├── .editorconfig ├── .gitignore ├── BuildRelease.py ├── DivinityModManager.sln ├── DivinityModManagerCore ├── ApiKeys.cs ├── Attributes │ ├── MenuSettingsAttribute.cs │ ├── ScreenReaderAttribute.cs │ └── SettingsEntryAttribute.cs ├── DivinityApp.cs ├── DivinityInteractions.cs ├── DivinityModManagerCore.csproj ├── Enums │ ├── AlertType.cs │ ├── DivinityExtenderModStatus.cs │ ├── DivinityGameLaunchWindowAction.cs │ └── Steam │ │ └── EPublishedFileQueryType.cs ├── Extensions │ ├── DictionaryExtensions.cs │ ├── KeyExtensions.cs │ ├── ResourceExtensions.cs │ └── StringExtensions.cs ├── FodyWeavers.xml ├── FodyWeavers.xsd ├── Models │ ├── App │ │ ├── AppSettings.cs │ │ ├── DefaultPathwayData.cs │ │ ├── Hotkey.cs │ │ ├── IgnoredModsData.cs │ │ └── MultiReactiveCommandHotkey.cs │ ├── BaseHistoryObject.cs │ ├── Conflicts │ │ ├── DivinityConflictEntryData.cs │ │ └── DivinityConflictGroup.cs │ ├── DivinityBaseModData.cs │ ├── DivinityGameMasterCampaign.cs │ ├── DivinityLoadOrder.cs │ ├── DivinityMissingModData.cs │ ├── DivinityModData.cs │ ├── DivinityModDependencyData.cs │ ├── DivinityModFilterData.cs │ ├── DivinityModManagerCachedWorkshopData.cs │ ├── DivinityModManagerSettings.cs │ ├── DivinityModScriptExtenderConfig.cs │ ├── DivinityModUpdateData.cs │ ├── DivinityModVersion.cs │ ├── DivinityModWorkshopCachedData.cs │ ├── DivinityModWorkshopData.cs │ ├── DivinityPathwayData.cs │ ├── DivinityProfileActiveModData.cs │ ├── DivinityProfileData.cs │ ├── DivinitySerializedModData.cs │ ├── Extender │ │ ├── ScriptExtenderSettings.cs │ │ └── ScriptExtenderUpdateConfig.cs │ ├── ISelectable.cs │ ├── ModFileDeletionData.cs │ ├── Steam │ │ ├── IWorkshopPublishFileDetails.cs │ │ ├── PublishedFileDetails.cs │ │ └── QueryFilesResponseData.cs │ └── WindowSettings.cs ├── Properties │ ├── Resources.Designer.cs │ └── Resources.resx ├── Util │ ├── DateUtils.cs │ ├── DivinityFileUtils.cs │ ├── DivinityGlobalCommands.cs │ ├── DivinityGlobalEvents.cs │ ├── DivinityJsonUtils.cs │ ├── DivinityModDataLoader.cs │ ├── DivinityModSorter.cs │ ├── DivinityRegistryHelper.cs │ ├── DivinitySaveTools.cs │ ├── DivinityStreamUtils.cs │ ├── DivinityWorkshopDataLoader.cs │ ├── GithubHelper.cs │ ├── JunctionPoint.cs │ ├── RecycleBinHelper.cs │ └── WebHelper.cs └── ViewModels │ ├── BaseHistoryViewModel.cs │ ├── BaseViewModel.cs │ └── IDivinityAppViewModel.cs ├── GUI ├── App.config ├── App.xaml ├── App.xaml.cs ├── Controls │ ├── AlertBar.xaml │ ├── AlertBar.xaml.cs │ ├── AutoGrayableImage.cs │ ├── AutomationTooltip.cs │ ├── Behavior │ │ ├── GridViewAutosizeColumnsBehavior.cs │ │ ├── ScreenReaderHelperBehavior.cs │ │ ├── TextBlockSettingsEntryAttributeBehavior.cs │ │ └── ToolTipHelperBehavior.cs │ ├── BusyIndicator.xaml │ ├── BusyIndicator.xaml.cs │ ├── CircleDecorator.cs │ ├── Extensions │ │ ├── EnumExtension.cs │ │ └── HyperlinkExtensions.cs │ ├── HotkeyEditorControl.xaml │ ├── HotkeyEditorControl.xaml.cs │ ├── HyperlinkText.xaml │ ├── HyperlinkText.xaml.cs │ ├── Markdown.cs │ ├── ModEntryGrid.cs │ ├── ModListView.cs │ ├── SelectableTextBlock.cs │ ├── TemplateSelectors │ │ └── TagTemplateSelector.cs │ └── UnfocusableTextBox.cs ├── Converters │ ├── BoolToVisibilityConverter.cs │ ├── EnumToStringConverter.cs │ ├── IntToVisibilityConverter.cs │ ├── ModIsAvailableConverter.cs │ ├── StringNotEmptyToVisibilityConverter.cs │ ├── StringToLinearBrushConverter.cs │ ├── StringToSolidBrushConverter.cs │ └── StringToUriConverter.cs ├── DivinityModManager.ico ├── Extensions │ └── DependencyObjectExtensions.cs ├── FodyWeavers.xml ├── FodyWeavers.xsd ├── GUI.csproj ├── MainWindow.xaml ├── MainWindow.xaml.cs ├── Properties │ ├── AssemblyInfo.cs │ ├── Settings.Designer.cs │ └── Settings.settings ├── Resources │ ├── AppFeatures.json │ ├── DefaultPathways.json │ ├── Icons │ │ ├── AddItem_16x.png │ │ ├── AlertBar_Close.png │ │ ├── AlertBar_Close_Hover.png │ │ ├── AlertBar_Danger_16x.png │ │ ├── AlertBar_Information_16x.png │ │ ├── AlertBar_Success_16x.png │ │ ├── AlertBar_Warning_16x.png │ │ ├── BrowserLink_16x.png │ │ ├── CopyToClipboard_16x.png │ │ ├── DefaultIcon_16x.png │ │ ├── DeleteAzureResource_16x.png │ │ ├── DivinityEngine2_64x.png │ │ ├── DivinityEngineGlasses_64x.png │ │ ├── DivinityOS2_EoCApp_16x.png │ │ ├── ExpandChevronDown_16x.png │ │ ├── ExpandChevronDown_lightGray_16x.png │ │ ├── ExpandChevronUp_16x.png │ │ ├── ExpandChevronUp_lightGrey_16x.png │ │ ├── ExportData_16x.png │ │ ├── ExportScript_16x.png │ │ ├── Folder_16x.png │ │ ├── Github_16x.png │ │ ├── Kofi_16x.png │ │ ├── LoadCampaignOrder_16x.png │ │ ├── Log_Normal_32x.png │ │ ├── OpenFile_16x.png │ │ ├── OpenFolder_16x.png │ │ ├── Refresh_16x.png │ │ ├── Rename_16x.png │ │ ├── SaveAs_16x.png │ │ ├── SaveGrey_16x.png │ │ ├── Save_16x.png │ │ ├── StatusWarning_16x.png │ │ ├── Steam_16x.png │ │ ├── ZipFileAs_16x.png │ │ └── ZipFile_16x.png │ └── IgnoredMods.json ├── Themes │ ├── Dark.xaml │ ├── DefaultStyles │ │ ├── ComboBox.xaml │ │ └── MarkdownStyle.xaml │ ├── DivinityColors.cs │ ├── Light.xaml │ ├── MainResourceDictionary.xaml │ └── ResourceAliasHelper.cs ├── Util │ ├── AutomationTooltipPeer.cs │ ├── BindingHelper.cs │ ├── CustomPropertyResolver.cs │ ├── ElementHelper.cs │ ├── FocusHelper.cs │ ├── NativeLibraryHelper.cs │ ├── RxExceptionHandler.cs │ └── ScreenReader │ │ ├── AlertBarAutomationPeer.cs │ │ ├── CachedAutomationPeer.cs │ │ ├── MainWindowAutomationPeer.cs │ │ ├── ModEntryGridAutomationPeer.cs │ │ ├── ModListViewAutomationPeer.cs │ │ ├── ScreenReaderHelper.cs │ │ └── Tolk.cs ├── ViewModels │ ├── AppKeys.cs │ ├── DeleteFilesViewData.cs │ ├── MainWindowExceptionHandler.cs │ ├── MainWindowViewModel.cs │ ├── ModListDragHandler.cs │ ├── ModListDropHandler.cs │ └── ModUpdatesViewData.cs ├── Views │ ├── AboutWindow.xaml │ ├── AboutWindow.xaml.cs │ ├── AppUpdateWindow.xaml │ ├── AppUpdateWindow.xaml.cs │ ├── DeleteFilesConfirmationView.xaml │ ├── DeleteFilesConfirmationView.xaml.cs │ ├── HideWindowBase.cs │ ├── HorizontalModLayout.xaml │ ├── HorizontalModLayout.xaml.cs │ ├── LeaderLibSettingsWindow.xaml │ ├── LeaderLibSettingsWindow.xaml.cs │ ├── ModUpdatesLayout.xaml │ ├── ModUpdatesLayout.xaml.cs │ ├── SettingsWindow.xaml │ ├── SettingsWindow.xaml.cs │ ├── VersionGeneratorWindow.xaml │ ├── VersionGeneratorWindow.xaml.cs │ ├── VerticalModLayout.xaml │ └── VerticalModLayout.xaml.cs ├── WinForms │ └── WindowWrapper.cs └── packages.config ├── LICENSE ├── README.md └── Update.xml /.editorconfig: -------------------------------------------------------------------------------- 1 | [*.cs] 2 | 3 | # CS0168: Variable is declared but never used 4 | dotnet_diagnostic.CS0168.severity = silent 5 | 6 | # CS0618: Type or member is obsolete 7 | dotnet_diagnostic.CS0618.severity = silent -------------------------------------------------------------------------------- /BuildRelease.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | from pathlib import Path 4 | import shutil 5 | import zipfile 6 | from zipfile import ZipFile 7 | 8 | import contextlib 9 | 10 | def get_arg(arg, fallback): 11 | if len(sys.argv) > arg: 12 | val = sys.argv[arg] 13 | if val != None: 14 | return val 15 | return fallback 16 | 17 | script_dir = Path(os.path.dirname(os.path.abspath(__file__))) 18 | os.chdir(script_dir) 19 | 20 | version = get_arg(1, None) 21 | 22 | file_name = "DivinityModManager_v{}.zip".format(version) 23 | export_file = "DivinityModManager_Latest.zip" 24 | print("Writing release zip:{}".format(file_name)) 25 | 26 | def zipdir(src, zip_name): 27 | ziph = zipfile.ZipFile(zip_name, 'w') 28 | for root, dirs, files in os.walk(src): 29 | for file in files: 30 | ziph.write(os.path.join(root, file), arcname=os.path.join(root.replace(src, ""), file), compress_type=zipfile.ZIP_DEFLATED) 31 | ziph.close() 32 | 33 | def SilentRemove(f): 34 | try: 35 | if Path(f).is_dir(): 36 | shutil.rmtree(f, ignore_errors=True) 37 | else: 38 | os.remove(f) 39 | print("Removed {}".format(f)) 40 | except Exception as e: 41 | print(e) 42 | 43 | def SilentCopyAndRemove(source, dest): 44 | with contextlib.suppress(FileNotFoundError, PermissionError): 45 | shutil.copy(source, dest) 46 | if Path(source).is_dir(): 47 | shutil.rmtree(source, ignore_errors=True) 48 | else: 49 | os.remove(source) 50 | print("Removed {}".format(source)) 51 | 52 | import time 53 | time.sleep(3) 54 | 55 | SilentRemove("bin/Publish/Data") 56 | SilentRemove("bin/Publish/_Logs") 57 | SilentRemove(file_name) 58 | 59 | zipdir("bin/Publish", file_name) 60 | 61 | shutil.copy(file_name, export_file) -------------------------------------------------------------------------------- /DivinityModManager.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.29102.190 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DivinityModManagerCore", "DivinityModManagerCore\DivinityModManagerCore.csproj", "{E24CFB13-F468-4B1D-BC4D-245CD4BD1A23}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GUI", "GUI\GUI.csproj", "{14BD698D-2A4F-44BB-A41E-6E36D80A8459}" 9 | EndProject 10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{943120CC-2315-475E-9597-7136811228E6}" 11 | ProjectSection(SolutionItems) = preProject 12 | .editorconfig = .editorconfig 13 | EndProjectSection 14 | EndProject 15 | Global 16 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 17 | Debug|x64 = Debug|x64 18 | Publish|x64 = Publish|x64 19 | Release|x64 = Release|x64 20 | EndGlobalSection 21 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 22 | {E24CFB13-F468-4B1D-BC4D-245CD4BD1A23}.Debug|x64.ActiveCfg = Debug|x64 23 | {E24CFB13-F468-4B1D-BC4D-245CD4BD1A23}.Debug|x64.Build.0 = Debug|x64 24 | {E24CFB13-F468-4B1D-BC4D-245CD4BD1A23}.Publish|x64.ActiveCfg = Release|x64 25 | {E24CFB13-F468-4B1D-BC4D-245CD4BD1A23}.Publish|x64.Build.0 = Release|x64 26 | {E24CFB13-F468-4B1D-BC4D-245CD4BD1A23}.Release|x64.ActiveCfg = Release|x64 27 | {E24CFB13-F468-4B1D-BC4D-245CD4BD1A23}.Release|x64.Build.0 = Release|x64 28 | {14BD698D-2A4F-44BB-A41E-6E36D80A8459}.Debug|x64.ActiveCfg = Debug|x64 29 | {14BD698D-2A4F-44BB-A41E-6E36D80A8459}.Debug|x64.Build.0 = Debug|x64 30 | {14BD698D-2A4F-44BB-A41E-6E36D80A8459}.Publish|x64.ActiveCfg = Publish|x64 31 | {14BD698D-2A4F-44BB-A41E-6E36D80A8459}.Publish|x64.Build.0 = Publish|x64 32 | {14BD698D-2A4F-44BB-A41E-6E36D80A8459}.Release|x64.ActiveCfg = Release|x64 33 | {14BD698D-2A4F-44BB-A41E-6E36D80A8459}.Release|x64.Build.0 = Release|x64 34 | EndGlobalSection 35 | GlobalSection(SolutionProperties) = preSolution 36 | HideSolutionNode = FALSE 37 | EndGlobalSection 38 | GlobalSection(ExtensibilityGlobals) = postSolution 39 | SolutionGuid = {D06B5DA3-3018-43F3-B9F0-2D9D89DB489E} 40 | EndGlobalSection 41 | EndGlobal 42 | -------------------------------------------------------------------------------- /DivinityModManagerCore/ApiKeys.cs: -------------------------------------------------------------------------------- 1 | namespace DivinityModManager 2 | { 3 | public class ApiKeys 4 | { 5 | public const string STEAM_WEB_API=""; 6 | } 7 | } -------------------------------------------------------------------------------- /DivinityModManagerCore/Attributes/MenuSettingsAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace DivinityModManager 8 | { 9 | public class MenuSettingsAttribute : Attribute 10 | { 11 | public string DisplayName { get; set; } 12 | public string Parent { get; set; } 13 | public bool AddSeparator { get; set; } 14 | public string Tooltip { get; set; } 15 | public string Style { get; set; } 16 | public MenuSettingsAttribute(string parent = "", string displayName = "", bool addSeparatorAfter = false, string tooltip = "") 17 | { 18 | DisplayName = displayName; 19 | Parent = parent; 20 | AddSeparator = addSeparatorAfter; 21 | Tooltip = tooltip; 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /DivinityModManagerCore/Attributes/ScreenReaderAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace DivinityModManager 8 | { 9 | public class ScreenReaderHelperAttribute : Attribute 10 | { 11 | public string Name { get; set; } 12 | public string HelpText { get; set; } 13 | 14 | public ScreenReaderHelperAttribute(string name = "", string helpText = "") 15 | { 16 | Name = name; 17 | HelpText = HelpText; 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /DivinityModManagerCore/Attributes/SettingsEntryAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace DivinityModManager 8 | { 9 | public class SettingsEntryAttribute : Attribute 10 | { 11 | public string DisplayName { get; set; } 12 | public string Tooltip { get; set; } 13 | public bool IsDebug { get; set; } 14 | public SettingsEntryAttribute(string displayName = "", string tooltip = "", bool isDebug = false) 15 | { 16 | DisplayName = displayName; 17 | Tooltip = tooltip; 18 | IsDebug = isDebug; 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /DivinityModManagerCore/DivinityInteractions.cs: -------------------------------------------------------------------------------- 1 | using ReactiveUI; 2 | 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading; 8 | using System.Threading.Tasks; 9 | 10 | namespace DivinityModManager 11 | { 12 | public struct DeleteFilesViewConfirmationData 13 | { 14 | public int Total; 15 | public bool PermanentlyDelete; 16 | public CancellationToken Token; 17 | } 18 | 19 | public static class DivinityInteractions 20 | { 21 | public static readonly Interaction ConfirmModDeletion = new Interaction(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /DivinityModManagerCore/DivinityModManagerCore.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net472 5 | DivinityModManager 6 | AnyCPU;x64 7 | false 8 | en-US 9 | 10 | 11 | 12 | x64 13 | 14 | 15 | 16 | x64 17 | 18 | 19 | 20 | x64 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | $(SolutionDir)External\lslib\LSLib.dll 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | True 46 | True 47 | Resources.resx 48 | 49 | 50 | 51 | 52 | PublicResXFileCodeGenerator 53 | Resources.Designer.cs 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /DivinityModManagerCore/Enums/AlertType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace DivinityModManager 8 | { 9 | public enum AlertType 10 | { 11 | Info, 12 | Success, 13 | Warning, 14 | Danger 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /DivinityModManagerCore/Enums/DivinityExtenderModStatus.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace DivinityModManager 8 | { 9 | public enum DivinityExtenderModStatus 10 | { 11 | NONE, 12 | SUPPORTS, 13 | REQUIRED, 14 | REQUIRED_OLD, 15 | REQUIRED_MISSING, 16 | REQUIRED_DISABLED, 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /DivinityModManagerCore/Enums/DivinityGameLaunchWindowAction.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | 3 | namespace DivinityModManager 4 | { 5 | public enum DivinityGameLaunchWindowAction 6 | { 7 | [Description("None")] 8 | None, 9 | [Description("Minimize")] 10 | Minimize, 11 | [Description("Close")] 12 | Close 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /DivinityModManagerCore/Enums/Steam/EPublishedFileQueryType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace DivinityModManager.Enums.Steam 8 | { 9 | public enum EPublishedFileQueryType 10 | { 11 | RankedByVote = 0, 12 | RankedByPublicationDate = 1, 13 | AcceptedForGameRankedByAcceptanceDate = 2, 14 | RankedByTrend = 3, 15 | FavoritedByFriendsRankedByPublicationDate = 4, 16 | CreatedByFriendsRankedByPublicationDate = 5, 17 | RankedByNumTimesReported = 6, 18 | CreatedByFollowedUsersRankedByPublicationDate = 7, 19 | NotYetRated = 8, 20 | RankedByTotalUniqueSubscriptions = 9, 21 | RankedByTotalVotesAsc = 10, 22 | RankedByVotesUp = 11, 23 | RankedByTextSearch = 12, 24 | RankedByPlaytimeTrend = 13, 25 | RankedByTotalPlaytime = 14, 26 | RankedByAveragePlaytimeTrend = 15, 27 | RankedByLifetimeAveragePlaytime = 16, 28 | RankedByPlaytimeSessionsTrend = 17, 29 | RankedByLifetimePlaytimeSessions = 18, 30 | RankedByInappropriateContentRating = 19, 31 | } 32 | } -------------------------------------------------------------------------------- /DivinityModManagerCore/Extensions/DictionaryExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace DivinityModManager.Extensions 9 | { 10 | public static class DictionaryExtensions 11 | { 12 | public static object FindKeyValue(this Dictionary dict, string key, StringComparison stringComparison = StringComparison.OrdinalIgnoreCase) 13 | { 14 | foreach(KeyValuePair kvp in dict) 15 | { 16 | if(kvp.Key.Equals(key, stringComparison)) 17 | { 18 | return kvp.Value; 19 | } 20 | else if(kvp.Value.GetType() == typeof(Dictionary)) 21 | { 22 | var subDict = (Dictionary)kvp.Value; 23 | var val = subDict.FindKeyValue(key, stringComparison); 24 | if (val != null) return val; 25 | } 26 | } 27 | return null; 28 | } 29 | 30 | private static object FindKeyValue_Recursive(object obj, string key, StringComparison stringComparison = StringComparison.OrdinalIgnoreCase) 31 | { 32 | if (obj.GetType() == typeof(Dictionary)) 33 | { 34 | var subDict = (Dictionary)obj; 35 | var val = subDict.FindKeyValue(key, stringComparison); 36 | if (val != null) return val; 37 | } 38 | else if (obj is IList list) 39 | { 40 | foreach (var childobj in list) 41 | { 42 | var val = FindKeyValue_Recursive(childobj, key, stringComparison); 43 | if (val != null) return val; 44 | } 45 | } 46 | else if (obj is IEnumerable enumerable) 47 | { 48 | foreach (var childobj in enumerable) 49 | { 50 | var val = FindKeyValue_Recursive(childobj, key, stringComparison); 51 | if (val != null) return val; 52 | } 53 | } 54 | return null; 55 | } 56 | 57 | public static bool TryFindKeyValue(this Dictionary dict, string key, out object valObj, StringComparison stringComparison = StringComparison.OrdinalIgnoreCase) 58 | { 59 | foreach (KeyValuePair kvp in dict) 60 | { 61 | if (kvp.Key.Equals(key, stringComparison)) 62 | { 63 | valObj = kvp.Value; 64 | return true; 65 | } 66 | else 67 | { 68 | var val = FindKeyValue_Recursive(kvp.Value, key, stringComparison); 69 | if(val != null) 70 | { 71 | valObj = val; 72 | return true; 73 | } 74 | } 75 | } 76 | valObj = null; 77 | return false; 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /DivinityModManagerCore/Extensions/KeyExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Windows.Input; 7 | 8 | namespace DivinityModManager 9 | { 10 | public static class KeyExtensions 11 | { 12 | private static readonly Dictionary KeyToName = new Dictionary 13 | { 14 | {Key.Add, "+"}, 15 | {Key.D0, "0"}, 16 | {Key.D1, "1"}, 17 | {Key.D2, "2"}, 18 | {Key.D3, "3"}, 19 | {Key.D4, "4"}, 20 | {Key.D5, "5"}, 21 | {Key.D6, "6"}, 22 | {Key.D7, "7"}, 23 | {Key.D8, "8"}, 24 | {Key.D9, "9"}, 25 | {Key.Decimal, "."}, 26 | {Key.Divide, " / "}, 27 | {Key.Multiply, "*"}, 28 | {Key.Oem1, ";"}, 29 | {Key.Oem5, "\\"}, 30 | {Key.Oem6, "]"}, 31 | {Key.Oem7, "'"}, 32 | {Key.OemBackslash, "\\"}, 33 | {Key.OemComma, ","}, 34 | {Key.OemMinus, "-"}, 35 | {Key.OemOpenBrackets, "["}, 36 | {Key.OemPeriod, "."}, 37 | {Key.OemPlus, "="}, 38 | {Key.OemQuestion, "/"}, 39 | {Key.OemTilde, "`"}, 40 | {Key.Subtract, "-"} 41 | }; 42 | 43 | public static string GetKeyName(this Key key) 44 | { 45 | if(KeyToName.TryGetValue(key, out string name)) 46 | { 47 | return name; 48 | } 49 | return key.ToString(); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /DivinityModManagerCore/Extensions/StringExtensions.cs: -------------------------------------------------------------------------------- 1 | using Alphaleonis.Win32.Filesystem; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace DivinityModManager.Extensions 9 | { 10 | public static class StringExtensions 11 | { 12 | public static bool IsExistingDirectory(this string path) 13 | { 14 | return !String.IsNullOrWhiteSpace(path) && Directory.Exists(path); 15 | } 16 | 17 | public static bool IsExistingFile(this string path) 18 | { 19 | return !String.IsNullOrWhiteSpace(path) && File.Exists(path); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /DivinityModManagerCore/FodyWeavers.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | -------------------------------------------------------------------------------- /DivinityModManagerCore/FodyWeavers.xsd: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. 12 | 13 | 14 | 15 | 16 | A comma-separated list of error codes that can be safely ignored in assembly verification. 17 | 18 | 19 | 20 | 21 | 'false' to turn off automatic generation of the XML Schema file. 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /DivinityModManagerCore/Models/App/AppSettings.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Drawing; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace DivinityModManager.Models.App 9 | { 10 | public class AppSettings 11 | { 12 | public DefaultPathwayData DefaultPathways { get; set; } = new DefaultPathwayData(); 13 | 14 | public Dictionary Features { get; set; } = new Dictionary(); 15 | 16 | public bool FeatureEnabled(string id) 17 | { 18 | if (Features.TryGetValue(id.ToLower(), out bool v)) 19 | { 20 | return v == true; 21 | } 22 | return false; 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /DivinityModManagerCore/Models/App/DefaultPathwayData.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace DivinityModManager.Models.App 8 | { 9 | public class AppPathwayData 10 | { 11 | public string Registry_32 { get; set; } 12 | public string Registry_64 { get; set; } 13 | public string AppID { get; set; } 14 | public string RootFolderName { get; set; } 15 | public string ExePath { get; set; } 16 | } 17 | public class DefaultPathwayData 18 | { 19 | public AppPathwayData Steam { get; set; } = new AppPathwayData(); 20 | public AppPathwayData GOG { get; set; } = new AppPathwayData(); 21 | 22 | public string DocumentsGameFolder { get; set; } = ""; 23 | public string GameDataFolder { get; set; } = ""; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /DivinityModManagerCore/Models/App/IgnoredModsData.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace DivinityModManager.Models 8 | { 9 | public class IgnoredModsData 10 | { 11 | public List IgnoreDependencies { get; set; } = new List(); 12 | public List> Mods { get; set; } = new List>(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /DivinityModManagerCore/Models/App/MultiReactiveCommandHotkey.cs: -------------------------------------------------------------------------------- 1 | using DynamicData; 2 | using DynamicData.Binding; 3 | 4 | using ReactiveUI; 5 | using ReactiveUI.Fody.Helpers; 6 | 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Collections.ObjectModel; 10 | using System.Linq; 11 | using System.Reactive; 12 | using System.Reactive.Concurrency; 13 | using System.Reactive.Disposables; 14 | using System.Reactive.Linq; 15 | using System.Text; 16 | using System.Threading.Tasks; 17 | using System.Windows.Input; 18 | 19 | namespace DivinityModManager.Models.App 20 | { 21 | public class MultiReactiveCommandHotkey : ReactiveObject, IHotkey 22 | { 23 | [Reactive] public string DisplayName { get; set; } 24 | [Reactive] public Key Key { get; set; } 25 | 26 | [Reactive] public ModifierKeys Modifiers { get; set; } 27 | 28 | [Reactive] public ICommand Command { get; set; } 29 | 30 | [Reactive] public bool Enabled { get; set; } 31 | 32 | [Reactive] public IObservable CanExecute { get; private set; } 33 | 34 | private ObservableCollection> commands = new ObservableCollection>(); 35 | 36 | public ObservableCollection> Commands 37 | { 38 | get => commands; 39 | } 40 | 41 | public void Add(ReactiveCommand command) 42 | { 43 | Commands.Add(command); 44 | } 45 | 46 | public void Invoke() 47 | { 48 | foreach (var cmd in Commands) 49 | { 50 | Observable.Start(() => { }).InvokeCommand(cmd); 51 | } 52 | } 53 | 54 | private void Init(Key key, ModifierKeys modifiers) 55 | { 56 | Key = key; 57 | Modifiers = modifiers; 58 | 59 | var canExecuteInitial = this.WhenAnyValue(x => x.Enabled, (b) => b == true); 60 | var anyCommandsCanExecute = Commands.ToObservableChangeSet().AutoRefreshOnObservable(c => c.CanExecute).ToCollection().Select(x => x.Any()); 61 | CanExecute = Observable.Merge(new IObservable[2] { canExecuteInitial, anyCommandsCanExecute }); 62 | Command = ReactiveCommand.Create(Invoke, CanExecute); 63 | } 64 | 65 | 66 | public MultiReactiveCommandHotkey(Key key) 67 | { 68 | Init(key, ModifierKeys.None); 69 | } 70 | 71 | public MultiReactiveCommandHotkey(Key key, ModifierKeys modifiers) 72 | { 73 | Init(key, modifiers); 74 | } 75 | 76 | public override string ToString() 77 | { 78 | var str = new StringBuilder(); 79 | 80 | if (Modifiers.HasFlag(ModifierKeys.Control)) 81 | str.Append("Ctrl + "); 82 | if (Modifiers.HasFlag(ModifierKeys.Shift)) 83 | str.Append("Shift + "); 84 | if (Modifiers.HasFlag(ModifierKeys.Alt)) 85 | str.Append("Alt + "); 86 | if (Modifiers.HasFlag(ModifierKeys.Windows)) 87 | str.Append("Win + "); 88 | 89 | str.Append(Key); 90 | 91 | return str.ToString(); 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /DivinityModManagerCore/Models/BaseHistoryObject.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Reflection; 4 | using System.Runtime.CompilerServices; 5 | using System.Text; 6 | using DivinityModManager.Util; 7 | using ReactiveHistory; 8 | using ReactiveUI; 9 | 10 | namespace DivinityModManager.Models 11 | { 12 | public class BaseHistoryObject : ReactiveObject 13 | { 14 | public IHistory History { get; set; } 15 | 16 | public virtual void Snapshot(Action undo, Action redo) 17 | { 18 | History.Snapshot(undo, redo); 19 | } 20 | 21 | public bool UpdateWithHistory(ref T field, T value, [CallerMemberName] string propertyName = null) 22 | { 23 | if (!Equals(field, value)) 24 | { 25 | if (History != null) 26 | { 27 | var undoValue = field; 28 | var redoValue = value; 29 | 30 | History.Snapshot(() => 31 | { 32 | this.SetProperty(this, propertyName, undoValue); 33 | }, () => 34 | { 35 | this.SetProperty(this, propertyName, redoValue); 36 | }); 37 | } 38 | 39 | this.RaiseAndSetIfChanged(ref field, value, propertyName); 40 | return true; 41 | } 42 | return false; 43 | } 44 | 45 | public bool UpdateWithHistory(ref T field, T value, Action undo, Action redo, [CallerMemberName] string propertyName = null) 46 | { 47 | if (!Equals(field, value)) 48 | { 49 | if (History != null) 50 | { 51 | History.Snapshot(undo, redo); 52 | } 53 | 54 | this.RaiseAndSetIfChanged(ref field, value, propertyName); 55 | return true; 56 | } 57 | return false; 58 | } 59 | 60 | private bool SetProperty(object targetObject, string propertyName, T value) 61 | { 62 | var prop = this.GetType().GetProperty(propertyName, BindingFlags.Public | BindingFlags.SetProperty | BindingFlags.Instance); 63 | if (prop != null && prop.CanWrite) 64 | { 65 | prop.SetValue(this, value); 66 | return true; 67 | } 68 | return false; 69 | } 70 | 71 | private bool SetField(string fieldName, T value, string propertyName = null) 72 | { 73 | var field = this.GetType().GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Instance); 74 | if (field != null) 75 | { 76 | field.SetValue(this, value); 77 | return true; 78 | } 79 | return false; 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /DivinityModManagerCore/Models/Conflicts/DivinityConflictEntryData.cs: -------------------------------------------------------------------------------- 1 | using DivinityModManager.Util; 2 | using ReactiveUI; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | 7 | namespace DivinityModManager.Models.Conflicts 8 | { 9 | public class DivinityConflictEntryData : ReactiveObject 10 | { 11 | private string target; 12 | 13 | public string Target 14 | { 15 | get => target; 16 | set { this.RaiseAndSetIfChanged(ref target, value); } 17 | } 18 | 19 | private string name; 20 | 21 | public string Name 22 | { 23 | get => name; 24 | set { this.RaiseAndSetIfChanged(ref name, value); } 25 | } 26 | 27 | public List ConflictModDataList { get; set; } = new List(); 28 | } 29 | 30 | public class DivinityConflictModData : ReactiveObject 31 | { 32 | private readonly DivinityModData modData; 33 | public DivinityModData Mod => modData; 34 | 35 | public string Value { get; set; } 36 | 37 | public DivinityConflictModData(DivinityModData mod, string val = "") 38 | { 39 | modData = mod; 40 | Value = val; 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /DivinityModManagerCore/Models/Conflicts/DivinityConflictGroup.cs: -------------------------------------------------------------------------------- 1 | using DivinityModManager.Util; 2 | using ReactiveUI; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Reactive.Disposables; 7 | 8 | namespace DivinityModManager.Models.Conflicts 9 | { 10 | public class DivinityConflictGroup : ReactiveObject 11 | { 12 | private string header; 13 | 14 | public string Header 15 | { 16 | get => header; 17 | set { this.RaiseAndSetIfChanged(ref header, value); } 18 | } 19 | 20 | private int totalConflicts = 0; 21 | 22 | public int TotalConflicts 23 | { 24 | get => totalConflicts; 25 | set { this.RaiseAndSetIfChanged(ref totalConflicts, value); } 26 | } 27 | 28 | public List Conflicts { get; set; } = new List(); 29 | 30 | private int selectedConflictIndex = 0; 31 | 32 | public int SelectedConflictIndex 33 | { 34 | get => selectedConflictIndex; 35 | set { this.RaiseAndSetIfChanged(ref selectedConflictIndex, value); } 36 | } 37 | 38 | public void OnActivated(CompositeDisposable disposables) 39 | { 40 | this.WhenAnyValue(x => x.Conflicts.Count).Subscribe(c => TotalConflicts = c).DisposeWith(disposables); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /DivinityModManagerCore/Models/DivinityGameMasterCampaign.cs: -------------------------------------------------------------------------------- 1 | using Alphaleonis.Win32.Filesystem; 2 | 3 | using DivinityModManager.Extensions; 4 | using DivinityModManager.Util; 5 | 6 | using DynamicData; 7 | using DynamicData.Binding; 8 | 9 | using LSLib.LS; 10 | 11 | using Newtonsoft.Json; 12 | 13 | using ReactiveUI; 14 | using ReactiveUI.Fody.Helpers; 15 | 16 | using System; 17 | using System.Collections.Generic; 18 | using System.Collections.ObjectModel; 19 | using System.Linq; 20 | using System.Reactive.Disposables; 21 | using System.Reactive.Linq; 22 | using System.Windows; 23 | using System.Windows.Automation.Peers; 24 | using System.Windows.Input; 25 | 26 | namespace DivinityModManager.Models 27 | { 28 | [ScreenReaderHelper(Name = "DisplayName", HelpText = "HelpText")] 29 | public class DivinityGameMasterCampaign : DivinityBaseModData 30 | { 31 | public Resource MetaResource { get; set; } 32 | 33 | public List Dependencies = new List(); 34 | 35 | public bool Export(IEnumerable order) 36 | { 37 | try 38 | { 39 | var conversionParams = ResourceConversionParameters.FromGameVersion(DivinityApp.GAME); 40 | if (File.Exists(FilePath) && File.GetSize(FilePath) > 0) 41 | { 42 | var backupName = Path.Combine(Path.GetDirectoryName(FilePath), FileName + ".backup"); 43 | File.Copy(FilePath, backupName, true); 44 | } 45 | 46 | if (MetaResource.TryFindNode("Dependencies", out var dependenciesNode)) 47 | { 48 | if (dependenciesNode.Children.TryGetValue("ModuleShortDesc", out var nodeList)) 49 | { 50 | nodeList.Clear(); 51 | foreach (var m in order) 52 | { 53 | var attributes = new Dictionary() 54 | { 55 | { "UUID", new NodeAttribute(NodeAttribute.DataType.DT_FixedString) {Value = m.UUID}}, 56 | { "Name", new NodeAttribute(NodeAttribute.DataType.DT_FixedString) {Value = m.Name}}, 57 | { "Version", new NodeAttribute(NodeAttribute.DataType.DT_Int) {Value = m.Version.VersionInt}}, 58 | { "MD5", new NodeAttribute(NodeAttribute.DataType.DT_LSString) {Value = m.MD5}}, 59 | { "Folder", new NodeAttribute(NodeAttribute.DataType.DT_LSWString) {Value = m.Folder}}, 60 | }; 61 | var modNode = new Node() 62 | { 63 | Name = "ModuleShortDesc", 64 | Parent = dependenciesNode, 65 | Attributes = attributes, 66 | Children = new Dictionary>() 67 | }; 68 | dependenciesNode.AppendChild(modNode); 69 | //nodeList.Add(modNode); 70 | } 71 | } 72 | } 73 | ResourceUtils.SaveResource(MetaResource, FilePath, LSLib.LS.Enums.ResourceFormat.LSF, conversionParams); 74 | if (File.Exists(FilePath)) 75 | { 76 | File.SetLastWriteTime(FilePath, DateTime.Now); 77 | File.SetLastAccessTime(FilePath, DateTime.Now); 78 | LastModified = DateTime.Now; 79 | DivinityApp.Log($"Wrote GM campaign metadata to {FilePath}"); 80 | } 81 | return true; 82 | } 83 | catch(Exception ex) 84 | { 85 | DivinityApp.Log($"Error saving GM Campaign meta.lsf:\n{ex}"); 86 | } 87 | return false; 88 | } 89 | 90 | public DivinityGameMasterCampaign() : base() 91 | { 92 | 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /DivinityModManagerCore/Models/DivinityMissingModData.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace DivinityModManager.Models 8 | { 9 | public class DivinityMissingModData 10 | { 11 | public string Name { get; set; } 12 | public int Index { get; set; } 13 | public string UUID { get; set; } 14 | public string Author { get; set; } 15 | public bool Dependency { get; set; } 16 | 17 | public override string ToString() 18 | { 19 | var str = ""; 20 | if(Index > 0) 21 | { 22 | str += $"{Index}. "; 23 | } 24 | str += Name; 25 | if(!String.IsNullOrEmpty(Author)) 26 | { 27 | str += " by " + Author; 28 | } 29 | if (Dependency) str += (" (Dependency)"); 30 | return str; 31 | } 32 | 33 | public static DivinityMissingModData FromData(DivinityModData modData) 34 | { 35 | return new DivinityMissingModData 36 | { 37 | Name = modData.Name, 38 | UUID = modData.UUID, 39 | Index = modData.Index, 40 | Author = modData.Author 41 | }; 42 | } 43 | 44 | public static DivinityMissingModData FromData(DivinityLoadOrderEntry modData, List orderList) 45 | { 46 | return new DivinityMissingModData 47 | { 48 | Name = modData.Name, 49 | UUID = modData.UUID, 50 | Index = orderList.IndexOf(modData) 51 | }; 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /DivinityModManagerCore/Models/DivinityModDependencyData.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using ReactiveUI; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace DivinityModManager.Models 10 | { 11 | [JsonObject(MemberSerialization.OptIn)] 12 | public struct DivinityModDependencyData : IDivinityModData 13 | { 14 | [JsonProperty] public string UUID { get; set; } 15 | [JsonProperty] public string Name { get; set; } 16 | public string Folder { get; set; } 17 | public string MD5 { get; set; } 18 | [JsonProperty] public DivinityModVersion Version { get; set; } 19 | 20 | public override string ToString() 21 | { 22 | return $"Dependency|Name({Name}) UUID({UUID}) Version({Version?.Version})"; 23 | } 24 | 25 | public static DivinityModDependencyData FromModData(DivinityModData m) 26 | { 27 | return new DivinityModDependencyData 28 | { 29 | Folder = m.Folder, 30 | Name = m.Name, 31 | UUID = m.UUID, 32 | MD5 = m.MD5, 33 | Version = m.Version 34 | }; 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /DivinityModManagerCore/Models/DivinityModFilterData.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Globalization; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace DivinityModManager.Models 10 | { 11 | public struct DivinityModFilterData 12 | { 13 | public string FilterProperty { get; set; } 14 | public string FilterValue { get; set; } 15 | 16 | private static char[] separators = new char[1] { ' ' }; 17 | 18 | public bool ValueContains(string val, bool separateWhitespace = false) 19 | { 20 | if(separateWhitespace && val.IndexOf(" ") > 1) 21 | { 22 | var vals = val.Split(separators, StringSplitOptions.RemoveEmptyEntries); 23 | var findVals = FilterValue.Split(separators, StringSplitOptions.RemoveEmptyEntries); 24 | DivinityApp.Log($"Searching for '{String.Join("; ", findVals)}' in ({String.Join("; ", vals)}"); 25 | return vals.Any(x => findVals.Any(x2 => CultureInfo.CurrentCulture.CompareInfo.IndexOf(x, x2, CompareOptions.IgnoreCase) >= 0)); 26 | } 27 | else 28 | { 29 | return CultureInfo.CurrentCulture.CompareInfo.IndexOf(val, FilterValue, CompareOptions.IgnoreCase) >= 0; 30 | } 31 | } 32 | 33 | public bool PropertyContains(string val) 34 | { 35 | return CultureInfo.CurrentCulture.CompareInfo.IndexOf(FilterProperty, val, CompareOptions.IgnoreCase) >= 0; 36 | } 37 | 38 | public bool Match(DivinityModData mod) 39 | { 40 | if (String.IsNullOrWhiteSpace(FilterValue)) return true; 41 | 42 | if(PropertyContains("Author")) 43 | { 44 | if (ValueContains(mod.Author)) return true; 45 | } 46 | 47 | if (PropertyContains("Version")) 48 | { 49 | if (ValueContains(mod.Version.Version)) return true; 50 | } 51 | 52 | if (PropertyContains("Mode")) 53 | { 54 | foreach (var mode in mod.Modes) 55 | { 56 | if (ValueContains(mode)) 57 | { 58 | return true; 59 | } 60 | } 61 | } 62 | 63 | if (PropertyContains("Depend")) 64 | { 65 | foreach(var dependency in mod.Dependencies.Items) 66 | { 67 | if (ValueContains(dependency.Name) || FilterValue == dependency.UUID || ValueContains(dependency.Folder)) 68 | { 69 | return true; 70 | } 71 | } 72 | } 73 | 74 | if (PropertyContains("Name")) 75 | { 76 | //DivinityApp.LogMessage($"Searching for '{FilterValue}' in '{mod.Name}' | {mod.Name.IndexOf(FilterValue)}"); 77 | if (ValueContains(mod.Name)) return true; 78 | } 79 | 80 | if (PropertyContains("File")) 81 | { 82 | if (ValueContains(mod.FileName)) return true; 83 | } 84 | 85 | if (PropertyContains("Desc")) 86 | { 87 | if (ValueContains(mod.Description)) return true; 88 | } 89 | 90 | if (PropertyContains("Type")) 91 | { 92 | if (ValueContains(mod.ModType)) return true; 93 | } 94 | 95 | if (PropertyContains("UUID")) 96 | { 97 | if (ValueContains(mod.UUID)) return true; 98 | } 99 | 100 | if (PropertyContains("Selected")) 101 | { 102 | if (mod.IsSelected) return true; 103 | } 104 | 105 | if (PropertyContains("Editor")) 106 | { 107 | if (mod.IsEditorMod) return true; 108 | } 109 | 110 | if (PropertyContains("Modified") || PropertyContains("Updated")) 111 | { 112 | DateTime date = DateTime.Now; 113 | if(DateTime.TryParse(FilterValue, out date)) 114 | { 115 | if (mod.LastModified >= date) return true; 116 | } 117 | } 118 | 119 | if (PropertyContains("Tag")) 120 | { 121 | if(mod.Tags != null && mod.Tags.Count > 0) 122 | { 123 | var f = this; 124 | if (mod.Tags.Any(x => f.ValueContains(x))) return true; 125 | // GM, Story, Arena are technically tags as well 126 | foreach (var mode in mod.Modes) 127 | { 128 | if (ValueContains(mode)) 129 | { 130 | return true; 131 | } 132 | } 133 | } 134 | } 135 | 136 | /* 137 | * var propertyValue = (string)mod.GetType().GetProperty(FilterProperty).GetValue(mod, null); 138 | if(propertyValue != null) 139 | { 140 | return CultureInfo.CurrentCulture.CompareInfo.IndexOf(propertyValue, FilterValue, CompareOptions.IgnoreCase) >= 0; 141 | } 142 | */ 143 | return false; 144 | } 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /DivinityModManagerCore/Models/DivinityModManagerCachedWorkshopData.cs: -------------------------------------------------------------------------------- 1 | using DivinityModManager.Models.Steam; 2 | using Newtonsoft.Json; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Diagnostics; 6 | using System.IO; 7 | using System.Linq; 8 | using System.Runtime.Serialization; 9 | using System.Text; 10 | using System.Threading.Tasks; 11 | 12 | namespace DivinityModManager.Models 13 | { 14 | [DataContract] 15 | public class DivinityModManagerCachedWorkshopData 16 | { 17 | [DataMember] public long LastUpdated { get; set; } = -1; 18 | [DataMember] public string LastVersion { get; set; } = ""; 19 | 20 | [DataMember] public List Mods { get; set; } = new List(); 21 | [DataMember] public List NonWorkshopMods { get; set; } = new List(); 22 | 23 | public bool CacheUpdated { get; set; } 24 | 25 | public void AddOrUpdate(string uuid, IWorkshopPublishFileDetails d, List tags) 26 | { 27 | // Mods may have the same UUID, so use the WorkshopID instead. 28 | var cachedData = Mods.FirstOrDefault(x => x.WorkshopID == d.publishedfileid); 29 | if(cachedData != null) 30 | { 31 | cachedData.LastUpdated = d.time_updated; 32 | cachedData.Created = d.time_created; 33 | cachedData.Tags = tags; 34 | } 35 | else 36 | { 37 | Mods.Add(new DivinityModWorkshopCachedData() 38 | { 39 | Created = d.time_created, 40 | LastUpdated = d.time_updated, 41 | UUID = uuid, 42 | WorkshopID = d.publishedfileid, 43 | Tags = tags 44 | }); 45 | } 46 | NonWorkshopMods.Remove(uuid); 47 | CacheUpdated = true; 48 | } 49 | 50 | public void AddNonWorkshopMod(string uuid) 51 | { 52 | if(!NonWorkshopMods.Any(x => x == uuid)) 53 | { 54 | NonWorkshopMods.Add(uuid); 55 | } 56 | CacheUpdated = true; 57 | } 58 | 59 | public string Serialize() 60 | { 61 | StringBuilder sb = new StringBuilder(); 62 | StringWriter sw = new StringWriter(sb); 63 | 64 | using (JsonWriter writer = new JsonTextWriter(sw)) 65 | { 66 | try 67 | { 68 | writer.WriteStartObject(); 69 | 70 | writer.WritePropertyName("LastUpdated"); 71 | writer.WriteValue(LastUpdated); 72 | 73 | writer.WritePropertyName("LastVersion"); 74 | writer.WriteValue(LastVersion); 75 | 76 | writer.WritePropertyName("Mods"); 77 | writer.WriteStartArray(); 78 | 79 | foreach (var data in Mods) 80 | { 81 | writer.WriteStartObject(); 82 | 83 | writer.WritePropertyName("UUID"); 84 | writer.WriteValue(data.UUID); 85 | 86 | writer.WritePropertyName("WorkshopID"); 87 | writer.WriteValue(data.WorkshopID); 88 | 89 | //writer.WritePropertyName("Created"); 90 | //writer.WriteValue(data.Created); 91 | 92 | writer.WritePropertyName("LastUpdated"); 93 | writer.WriteValue(data.LastUpdated); 94 | 95 | writer.WritePropertyName("Tags"); 96 | writer.WriteStartArray(); 97 | 98 | if(data.Tags != null && data.Tags.Count > 0) 99 | { 100 | foreach (var tag in data.Tags) 101 | { 102 | writer.WriteValue(tag); 103 | } 104 | } 105 | 106 | writer.WriteEndArray(); 107 | 108 | writer.WriteEndObject(); 109 | } 110 | 111 | writer.WriteEndArray(); 112 | 113 | writer.WritePropertyName("NonWorkshopMods"); 114 | writer.WriteStartArray(); 115 | foreach(var uuid in NonWorkshopMods) 116 | { 117 | writer.WriteValue(uuid); 118 | } 119 | writer.WriteEndArray(); 120 | 121 | writer.WriteEndObject(); 122 | } 123 | catch(Exception ex) 124 | { 125 | DivinityApp.Log("Error serializing CachedWorkshopData:"); 126 | DivinityApp.Log(ex.ToString()); 127 | } 128 | } 129 | 130 | return sb.ToString(); 131 | } 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /DivinityModManagerCore/Models/DivinityModScriptExtenderConfig.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace DivinityModManager.Models 9 | { 10 | [JsonObject(MemberSerialization.OptIn)] 11 | public class DivinityModScriptExtenderConfig 12 | { 13 | [JsonProperty("RequiredExtensionVersion")] 14 | public int RequiredExtensionVersion { get; set; } = -1; 15 | 16 | [JsonProperty("FeatureFlags")] 17 | public List FeatureFlags { get; set; } = new List(); 18 | 19 | public bool HasAnySettings 20 | { 21 | get 22 | { 23 | return RequiredExtensionVersion > -1 || FeatureFlags?.Count > 0; 24 | } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /DivinityModManagerCore/Models/DivinityModUpdateData.cs: -------------------------------------------------------------------------------- 1 | using ReactiveUI; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using System.Windows; 8 | using System.Windows.Input; 9 | 10 | namespace DivinityModManager.Models 11 | { 12 | public class DivinityModUpdateData : ReactiveObject, ISelectable 13 | { 14 | private DivinityModData localMod; 15 | public DivinityModData LocalMod 16 | { 17 | get => localMod; 18 | set 19 | { 20 | this.RaiseAndSetIfChanged(ref localMod, value); 21 | } 22 | } 23 | 24 | private DivinityModData workshopMod; 25 | public DivinityModData WorkshopMod 26 | { 27 | get => workshopMod; 28 | set 29 | { 30 | this.RaiseAndSetIfChanged(ref workshopMod, value); 31 | } 32 | } 33 | 34 | private bool isSelected = false; 35 | 36 | public bool IsSelected 37 | { 38 | get => isSelected; 39 | set { this.RaiseAndSetIfChanged(ref isSelected, value); } 40 | } 41 | 42 | public bool IsEditorMod { get; set; } 43 | public bool CanDrag { get; set; } = true; 44 | public Visibility Visibility { get; set; } = Visibility.Visible; 45 | 46 | public DivinityModUpdateData() 47 | { 48 | 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /DivinityModManagerCore/Models/DivinityModVersion.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | using ReactiveUI; 4 | using ReactiveUI.Fody.Helpers; 5 | 6 | using System; 7 | using System.Collections.Generic; 8 | using System.Linq; 9 | using System.Reactive.Linq; 10 | using System.Text; 11 | using System.Threading.Tasks; 12 | 13 | namespace DivinityModManager.Models 14 | { 15 | [JsonObject(MemberSerialization.OptIn)] 16 | public class DivinityModVersion : ReactiveObject 17 | { 18 | [Reactive] public int Major { get; set; } 19 | [Reactive] public int Minor { get; set; } 20 | [Reactive] public int Revision { get; set; } 21 | [Reactive] public int Build { get; set; } 22 | 23 | private readonly ObservableAsPropertyHelper _version; 24 | public string Version => _version.Value; 25 | 26 | private readonly ObservableAsPropertyHelper _versionInt; 27 | public int VersionInt => _versionInt.Value; 28 | 29 | public int ToInt() 30 | { 31 | return (Major << 28) + (Minor << 24) + (Revision << 16) + (Build); 32 | } 33 | 34 | public override string ToString() 35 | { 36 | return Version; 37 | } 38 | 39 | public void ParseInt(int vInt) 40 | { 41 | Major = (vInt >> 28); 42 | Minor = (vInt >> 24) & 0x0F; 43 | Revision = (vInt >> 16) & 0xFF; 44 | Build = (vInt & 0xFFFF); 45 | } 46 | 47 | public static string StringFromIndividual(int major, int minor, int revision, int build) 48 | { 49 | return $"{major}.{minor}.{revision}.{build}"; 50 | } 51 | 52 | public static int IntFromIndividual(int major, int minor, int revision, int build) 53 | { 54 | return (major << 28) + (minor << 24) + (revision << 16) + (build); 55 | } 56 | 57 | public static DivinityModVersion FromInt(int vInt) 58 | { 59 | return new DivinityModVersion(vInt); 60 | } 61 | 62 | public DivinityModVersion() 63 | { 64 | var whenAnyNum = this.WhenAnyValue(x => x.Major, x => x.Minor, x => x.Revision, x => x.Build); 65 | _version = whenAnyNum.Select(v => StringFromIndividual(v.Item1, v.Item2, v.Item3, v.Item4)).ToProperty(this, nameof(Version)); 66 | _versionInt = whenAnyNum.Select(v => IntFromIndividual(v.Item1, v.Item2, v.Item3, v.Item4)).ToProperty(this, nameof(VersionInt)); 67 | } 68 | 69 | public DivinityModVersion(int vInt) : this() 70 | { 71 | ParseInt(vInt); 72 | } 73 | 74 | public static bool operator> (DivinityModVersion a, DivinityModVersion b) 75 | { 76 | return a.VersionInt > b.VersionInt; 77 | } 78 | 79 | public static bool operator< (DivinityModVersion a, DivinityModVersion b) 80 | { 81 | return a.VersionInt < b.VersionInt; 82 | } 83 | 84 | public static bool operator>= (DivinityModVersion a, DivinityModVersion b) 85 | { 86 | return a.VersionInt >= b.VersionInt; 87 | } 88 | 89 | public static bool operator<= (DivinityModVersion a, DivinityModVersion b) 90 | { 91 | return a.VersionInt <= b.VersionInt; 92 | } 93 | 94 | public DivinityModVersion(int headerMajor, int headerMinor, int headerRevision, int headerBuild) : this() 95 | { 96 | Major = headerMajor; 97 | Minor = headerMinor; 98 | Revision = headerRevision; 99 | Build = headerBuild; 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /DivinityModManagerCore/Models/DivinityModWorkshopCachedData.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using ReactiveUI; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.IO; 6 | using System.Text; 7 | 8 | namespace DivinityModManager.Models 9 | { 10 | public class DivinityModWorkshopCachedData 11 | { 12 | public string UUID { get; set; } 13 | public string WorkshopID { get; set; } 14 | public long Created { get; set; } 15 | public long LastUpdated { get; set; } 16 | public List Tags { get; set; } = new List(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /DivinityModManagerCore/Models/DivinityModWorkshopData.cs: -------------------------------------------------------------------------------- 1 | using ReactiveUI; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace DivinityModManager.Models 9 | { 10 | public class DivinityModWorkshopData : ReactiveObject 11 | { 12 | private string id = ""; 13 | 14 | public string ID 15 | { 16 | get => id; 17 | set { this.RaiseAndSetIfChanged(ref id, value); } 18 | } 19 | 20 | private DateTime createdDate; 21 | 22 | public DateTime CreatedDate 23 | { 24 | get => createdDate; 25 | set { this.RaiseAndSetIfChanged(ref createdDate, value); } 26 | } 27 | 28 | private DateTime updatedDate; 29 | 30 | public DateTime UpdatedDate 31 | { 32 | get => updatedDate; 33 | set { this.RaiseAndSetIfChanged(ref updatedDate, value); } 34 | } 35 | 36 | private List tags; 37 | 38 | public List Tags 39 | { 40 | get => tags; 41 | set 42 | { 43 | this.RaiseAndSetIfChanged(ref tags, value); 44 | } 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /DivinityModManagerCore/Models/DivinityPathwayData.cs: -------------------------------------------------------------------------------- 1 | using Alphaleonis.Win32.Filesystem; 2 | using DivinityModManager.Extensions; 3 | using ReactiveUI; 4 | using ReactiveUI.Fody.Helpers; 5 | 6 | using System; 7 | using System.Collections.Generic; 8 | using System.Linq; 9 | using System.Text; 10 | using System.Threading.Tasks; 11 | 12 | namespace DivinityModManager.Models 13 | { 14 | public class DivinityPathwayData : ReactiveObject 15 | { 16 | /// 17 | /// The path to the root game folder, i.e. SteamLibrary\steamapps\common\Divinity Original Sin 2 18 | /// 19 | [Reactive] public string InstallPath { get; set; } 20 | 21 | /// 22 | /// The path to Documents\Larian Studios\Divinity Original Sin 2 Definitive Edition 23 | /// 24 | [Reactive] public string LarianDocumentsFolder { get; set; } 25 | 26 | /// 27 | /// The path to Documents\Larian Studios\Divinity Original Sin 2 Definitive Edition\Mods 28 | /// 29 | [Reactive] public string DocumentsModsPath { get; set; } 30 | 31 | /// 32 | /// The path to Documents\Larian Studios\Divinity Original Sin 2 Definitive Edition\PlayerProfiles 33 | /// 34 | [Reactive] public string DocumentsProfilesPath { get; set; } 35 | 36 | /// 37 | /// The path to Documents\Larian Studios\Divinity Original Sin 2 Definitive Edition\GMCampaigns 38 | /// 39 | [Reactive] public string DocumentsGMCampaignsPath { get; set; } 40 | 41 | [Reactive] public string LastSaveFilePath { get; set; } 42 | 43 | [Reactive] public string ScriptExtenderLatestReleaseUrl { get; set; } 44 | [Reactive] public string ScriptExtenderLatestReleaseVersion { get; set; } 45 | 46 | public DivinityPathwayData() 47 | { 48 | InstallPath = ""; 49 | LarianDocumentsFolder = ""; 50 | DocumentsModsPath = ""; 51 | DocumentsGMCampaignsPath = ""; 52 | LastSaveFilePath = ""; 53 | ScriptExtenderLatestReleaseUrl = ""; 54 | ScriptExtenderLatestReleaseVersion = ""; 55 | } 56 | 57 | public string ScriptExtenderSettingsFile(DivinityModManagerSettings settings) 58 | { 59 | if(settings.GameExecutablePath.IsExistingFile()) 60 | { 61 | return Path.Combine(Path.GetDirectoryName(settings.GameExecutablePath), "ScriptExtenderSettings.json"); 62 | } 63 | return ""; 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /DivinityModManagerCore/Models/DivinityProfileActiveModData.cs: -------------------------------------------------------------------------------- 1 | using LSLib.LS; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Diagnostics; 5 | using System.Linq; 6 | using System.Reflection; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | namespace DivinityModManager.Models 11 | { 12 | public class DivinityProfileActiveModData 13 | { 14 | public string Folder { get; set; } 15 | public string MD5 { get; set; } 16 | public string Name { get; set; } 17 | public string UUID { get; set; } 18 | public int Version { get; set; } 19 | 20 | private T GetAttribute(Dictionary attributes, string name, T fallBack) 21 | { 22 | if (attributes.TryGetValue(name, out var attribute)) 23 | { 24 | var val = (T)attribute.Value; 25 | if (val != null) 26 | { 27 | return val; 28 | } 29 | } 30 | return fallBack; 31 | } 32 | 33 | public void LoadFromAttributes(Dictionary attributes) 34 | { 35 | Folder = GetAttribute(attributes, "Folder", ""); 36 | MD5 = GetAttribute(attributes, "MD5", ""); 37 | Name = GetAttribute(attributes, "Name", ""); 38 | UUID = GetAttribute(attributes, "UUID", ""); 39 | Version = GetAttribute(attributes, "Version", -1); 40 | 41 | //DivinityApp.LogMessage($"[DivinityProfileActiveModData] Name({Name}) UUID({UUID})"); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /DivinityModManagerCore/Models/DivinityProfileData.cs: -------------------------------------------------------------------------------- 1 | using Alphaleonis.Win32.Filesystem; 2 | using ReactiveUI; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Text; 6 | 7 | namespace DivinityModManager.Models 8 | { 9 | public class DivinityProfileData : ReactiveObject 10 | { 11 | public string Name { get; set; } 12 | 13 | /// 14 | /// The stored name in the profile.lsb file. 15 | /// 16 | public string ProfileName { get; set; } 17 | public string UUID { get; set; } 18 | 19 | private string folder; 20 | 21 | public string Folder 22 | { 23 | get => folder; 24 | set 25 | { 26 | if(value != folder) 27 | { 28 | ModSettingsFile = Path.Combine(value, "modsettings.lsx"); 29 | } 30 | this.RaiseAndSetIfChanged(ref folder, value); 31 | } 32 | } 33 | 34 | private string modSettingsFile; 35 | 36 | public string ModSettingsFile 37 | { 38 | get => modSettingsFile; 39 | set { this.RaiseAndSetIfChanged(ref modSettingsFile, value); } 40 | } 41 | 42 | /// 43 | /// The saved load order from modsettings.lsx 44 | /// 45 | public List ModOrder { get; set; } = new List(); 46 | 47 | /// 48 | /// The mod data under the Mods node, from modsettings.lsx. 49 | /// 50 | public List ActiveMods { get; set; } = new List(); 51 | 52 | /// 53 | /// The ModOrder transformed into a DivinityLoadOrder. This is the "Current" order. 54 | /// 55 | public DivinityLoadOrder SavedLoadOrder { get; set; } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /DivinityModManagerCore/Models/DivinitySerializedModData.cs: -------------------------------------------------------------------------------- 1 | using DynamicData; 2 | 3 | using ReactiveUI.Fody.Helpers; 4 | 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Linq; 8 | using System.Runtime.Serialization; 9 | using System.Text; 10 | using System.Threading.Tasks; 11 | 12 | namespace DivinityModManager.Models 13 | { 14 | [DataContract] 15 | public class DivinitySerializedModData : IDivinityModData 16 | { 17 | [DataMember] public int Index { get; set; } 18 | [DataMember] public string FileName { get; set; } 19 | [DataMember] public string UUID { get; set; } 20 | [DataMember] public string Folder { get; set; } 21 | [DataMember] public string Name { get; set; } 22 | [DataMember] public string Description { get; set; } 23 | [DataMember] public string Author { get; set; } 24 | 25 | [DataMember] public DivinityModVersion Version { get; set; } 26 | 27 | [DataMember] public string Type { get; set; } 28 | [DataMember] public List Modes { get; set; } 29 | 30 | [DataMember] public string Targets { get; set; } 31 | 32 | [DataMember] public DivinityModScriptExtenderConfig ScriptExtenderData { get; set; } 33 | [DataMember] public List Dependencies { get; set; } 34 | 35 | [DataMember] public string MD5 { get; set; } 36 | 37 | public static DivinitySerializedModData FromMod(DivinityModData mod) 38 | { 39 | return new DivinitySerializedModData 40 | { 41 | Author = mod.Author, 42 | Dependencies = mod.Dependencies.Items.ToList(), 43 | Description = mod.Description, 44 | FileName = mod.FileName, 45 | Folder = mod.Folder, 46 | Name = mod.Name, 47 | Version = mod.Version, 48 | Type = mod.ModType, 49 | Modes = mod.Modes, 50 | Targets = mod.Targets, 51 | Index = mod.Index, 52 | ScriptExtenderData = mod.ScriptExtenderData, 53 | UUID = mod.UUID, 54 | MD5 = mod.MD5 55 | }; 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /DivinityModManagerCore/Models/Extender/ScriptExtenderUpdateConfig.cs: -------------------------------------------------------------------------------- 1 | using ReactiveUI; 2 | using ReactiveUI.Fody.Helpers; 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.ComponentModel; 7 | using System.Linq; 8 | using System.Runtime.Serialization; 9 | using System.Text; 10 | using System.Threading.Tasks; 11 | 12 | namespace DivinityModManager.Models.Extender 13 | { 14 | [DataContract] 15 | public class ScriptExtenderUpdateConfig : ReactiveObject 16 | { 17 | [SettingsEntry("UpdateChannel", "Use a specific update channel (Release or Devel)")] 18 | [Reactive] 19 | [DataMember] 20 | [DefaultValue("")] 21 | public string UpdateChannel { get; set; } 22 | 23 | [SettingsEntry("TargetVersion", "Update to a specific version of the script extender (ex. '57.0.0.0')")] 24 | [Reactive] 25 | [DataMember] 26 | [DefaultValue("")] 27 | public string TargetVersion { get; set; } 28 | 29 | [SettingsEntry("Disable Updates", "Disable automatic updating to the latest extender version")] 30 | [Reactive] 31 | [DataMember] 32 | [DefaultValue(false)] 33 | public bool DisableUpdates { get; set; } 34 | 35 | [SettingsEntry("Debug", "Enable debug mode, which prints more messages to the console window")] 36 | [Reactive] 37 | [DataMember] 38 | [DefaultValue(false)] 39 | public bool Debug { get; set; } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /DivinityModManagerCore/Models/ISelectable.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Windows; 7 | 8 | namespace DivinityModManager.Models 9 | { 10 | public interface ISelectable 11 | { 12 | bool IsSelected { get; set; } 13 | Visibility Visibility { get; set; } 14 | bool CanDrag { get; } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /DivinityModManagerCore/Models/ModFileDeletionData.cs: -------------------------------------------------------------------------------- 1 | using ReactiveUI.Fody.Helpers; 2 | using ReactiveUI; 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | namespace DivinityModManager.Models 11 | { 12 | public class ModFileDeletionData : ReactiveObject 13 | { 14 | [Reactive] public bool IsSelected { get; set; } 15 | [Reactive] public bool IsWorkshop { get; set; } 16 | [Reactive] public string FilePath { get; set; } 17 | [Reactive] public string DisplayName { get; set; } 18 | [Reactive] public string UUID { get; set; } 19 | 20 | public static ModFileDeletionData FromMod(DivinityModData mod, bool isWorkshopMod = false) 21 | { 22 | return new ModFileDeletionData { FilePath = mod.FilePath, DisplayName = mod.DisplayName, IsSelected = true, UUID = mod.UUID, IsWorkshop = isWorkshopMod }; 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /DivinityModManagerCore/Models/Steam/IWorkshopPublishFileDetails.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace DivinityModManager.Models.Steam 8 | { 9 | public interface IWorkshopPublishFileDetails 10 | { 11 | string publishedfileid { get; set; } 12 | long time_created { get; set; } 13 | long time_updated { get; set; } 14 | 15 | List tags { get; set; } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /DivinityModManagerCore/Models/Steam/PublishedFileDetails.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace DivinityModManager.Models.Steam 8 | { 9 | public struct WorkshopTag 10 | { 11 | public string tag { get; set; } 12 | } 13 | 14 | public class PublishedFileDetailsResponse 15 | { 16 | public PublishedFileDetailsResponseData response { get; set; } 17 | } 18 | 19 | public class PublishedFileDetailsResponseData 20 | { 21 | public int result { get; set; } 22 | public int resultcount { get; set; } 23 | 24 | public List publishedfiledetails { get; set;} 25 | } 26 | 27 | 28 | public class PublishedFileDetails : IWorkshopPublishFileDetails 29 | { 30 | public string publishedfileid { get; set; } 31 | public int result { get; set; } 32 | public string creator { get; set; } 33 | public int creator_app_id { get; set; } 34 | public int consumer_app_id { get; set; } 35 | public string filename { get; set; } 36 | public string file_size { get; set; } 37 | public string file_url { get; set; } 38 | public string hcontent_file { get; set; } 39 | public string preview_url { get; set; } 40 | public string hcontent_preview { get; set; } 41 | public string title { get; set; } 42 | public string description { get; set; } 43 | public long time_created { get; set; } 44 | public long time_updated { get; set; } 45 | public int visibility { get; set; } 46 | public bool banned { get; set; } 47 | public string ban_reason { get; set; } 48 | public int subscriptions { get; set; } 49 | public int favorited { get; set; } 50 | public int lifetime_subscriptions { get; set; } 51 | public int lifetime_favorited { get; set; } 52 | public int views { get; set; } 53 | public List tags { get; set; } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /DivinityModManagerCore/Models/Steam/QueryFilesResponseData.cs: -------------------------------------------------------------------------------- 1 | using DivinityModManager.Util; 2 | using Newtonsoft.Json; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace DivinityModManager.Models.Steam 10 | { 11 | public class QueryFilesResponse 12 | { 13 | public QueryFilesResponseData response { get; set; } 14 | } 15 | 16 | public class QueryFilesResponseData 17 | { 18 | public int total { get; set; } 19 | 20 | public List publishedfiledetails { get; set; } 21 | } 22 | 23 | public class QueryFilesPublishedFileDetails : IWorkshopPublishFileDetails 24 | { 25 | public int result { get; set; } 26 | public string publishedfileid { get; set; } 27 | public string creator { get; set; } 28 | public string filename { get; set; } 29 | public string file_size { get; set; } 30 | public string file_url { get; set; } 31 | public string preview_url { get; set; } 32 | public string url { get; set; } 33 | public string title { get; set; } 34 | public string description { get; set; } 35 | public long time_created { get; set; } 36 | public long time_updated { get; set; } 37 | public int visibility { get; set; } 38 | public int flags { get; set; } 39 | public List tags { get; set; } 40 | public string metadata { get; set; } 41 | [JsonIgnore] public QueryFilesPublishedFileDivinityMetadataMain MetaData { get; set; } 42 | public int language { get; set; } 43 | public string revision_change_number { get; set; } 44 | public int revision { get; set; } 45 | 46 | public string GetGuid() 47 | { 48 | if (this.MetaData != null) 49 | { 50 | try 51 | { 52 | return this.MetaData.root.regions.MetaData.Guid.Value; 53 | } 54 | catch 55 | { 56 | } 57 | } 58 | return null; 59 | } 60 | 61 | public void DeserializeMetadata() 62 | { 63 | if (!String.IsNullOrEmpty(metadata)) 64 | { 65 | MetaData = DivinityJsonUtils.SafeDeserialize(metadata); 66 | } 67 | } 68 | } 69 | 70 | public class QueryFilesPublishedFileDivinityMetadataMain 71 | { 72 | public QueryFilesPublishedFileDivinityMetadataRoot root { get; set; } 73 | } 74 | 75 | public class QueryFilesPublishedFileDivinityMetadataRoot 76 | { 77 | public QueryFilesPublishedFileDivinityMetadataHeader header { get; set; } 78 | public QueryFilesPublishedFileDivinityMetadataRegions regions { get; set; } 79 | } 80 | 81 | public class QueryFilesPublishedFileDivinityMetadataHeader 82 | { 83 | public int time { get; set; } 84 | public string version { get; set; } 85 | } 86 | 87 | public class QueryFilesPublishedFileDivinityMetadataRegions 88 | { 89 | public QueryFilesPublishedFileDivinityMetadataEntry MetaData { get; set; } 90 | } 91 | 92 | public class QueryFilesPublishedFileDivinityMetadataEntry 93 | { 94 | public QueryFilesPublishedFileDivinityMetadataEntryAttribute Guid { get; set; } 95 | public QueryFilesPublishedFileDivinityMetadataEntryAttribute Type { get; set; } 96 | public QueryFilesPublishedFileDivinityMetadataEntryAttribute Version { get; set; } 97 | } 98 | public class QueryFilesPublishedFileDivinityMetadataEntryAttribute 99 | { 100 | public int type { get; set; } 101 | public object value { get; set; } 102 | 103 | [JsonIgnore] 104 | public T Value 105 | { 106 | get 107 | { 108 | return (T)value; 109 | } 110 | } 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /DivinityModManagerCore/Models/WindowSettings.cs: -------------------------------------------------------------------------------- 1 | using ReactiveUI; 2 | 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Runtime.Serialization; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | namespace DivinityModManager.Models 11 | { 12 | public class WindowSettings 13 | { 14 | public bool Maximized { get; set; } 15 | public double X { get; set; } 16 | public double Y { get; set; } 17 | public int Screen { get; set; } 18 | 19 | public WindowSettings() 20 | { 21 | Maximized = false; 22 | X = 0; 23 | Y = 0; 24 | Screen = -1; 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /DivinityModManagerCore/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 DivinityModManager.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", "16.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("DivinityModManager.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 | -------------------------------------------------------------------------------- /DivinityModManagerCore/Util/DateUtils.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace DivinityModManager.Util 8 | { 9 | public static class DateUtils 10 | { 11 | public static DateTime UnixTimeStampToDateTime(long unixTimeStamp) 12 | { 13 | // Unix timestamp is seconds past epoch 14 | System.DateTime dtDateTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, System.DateTimeKind.Utc); 15 | long unixTimeStampInTicks = (long)(unixTimeStamp * TimeSpan.TicksPerSecond); 16 | return new DateTime(dtDateTime.Ticks + unixTimeStampInTicks, DateTimeKind.Utc); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /DivinityModManagerCore/Util/DivinityGlobalCommands.cs: -------------------------------------------------------------------------------- 1 | using Alphaleonis.Win32.Filesystem; 2 | 3 | using DivinityModManager.Models; 4 | using DivinityModManager.ViewModels; 5 | 6 | using ReactiveUI; 7 | 8 | using System; 9 | using System.Diagnostics; 10 | using System.Reactive; 11 | using System.Windows; 12 | 13 | namespace DivinityModManager.Util 14 | { 15 | public class DivinityGlobalCommands : ReactiveObject 16 | { 17 | private IDivinityAppViewModel _viewModel; 18 | 19 | public IDivinityAppViewModel ViewModel => _viewModel; 20 | 21 | public void SetViewModel(IDivinityAppViewModel vm) 22 | { 23 | _viewModel = vm; 24 | this.RaisePropertyChanged(nameof(ViewModel)); 25 | } 26 | 27 | public ReactiveCommand OpenFileCommand { get; private set; } 28 | public ReactiveCommand OpenInFileExplorerCommand { get; private set; } 29 | public ReactiveCommand ClearMissingModsCommand { get; private set; } 30 | public ReactiveCommand ToggleNameDisplayCommand { get; private set; } 31 | public ReactiveCommand CopyToClipboardCommand { get; private set; } 32 | public ReactiveCommand DeleteModCommand { get; private set; } 33 | 34 | public void OpenFile(string path) 35 | { 36 | if (File.Exists(path)) 37 | { 38 | try 39 | { 40 | Process.Start(Path.GetFullPath(path)); 41 | } 42 | catch (System.ComponentModel.Win32Exception ex) // No File Association 43 | { 44 | Process.Start("explorer.exe", $"\"{Path.GetFullPath(path)}\""); 45 | } 46 | } 47 | else if (Directory.Exists(path)) 48 | { 49 | Process.Start("explorer.exe", $"\"{Path.GetFullPath(path)}\""); 50 | } 51 | else 52 | { 53 | _viewModel.ShowAlert($"Error opening '{path}': File does not exist!", AlertType.Danger, 10); 54 | } 55 | } 56 | 57 | public void OpenInFileExplorer(string path) 58 | { 59 | if (File.Exists(path)) 60 | { 61 | Process.Start("explorer.exe", $"/select, \"{Path.GetFullPath(path)}\""); 62 | } 63 | else if (Directory.Exists(path)) 64 | { 65 | Process.Start("explorer.exe", $"\"{Path.GetFullPath(path)}\""); 66 | } 67 | else 68 | { 69 | _viewModel.ShowAlert($"Error opening '{path}': File does not exist!", AlertType.Danger, 10); 70 | } 71 | } 72 | 73 | public void CopyToClipboard(string text) 74 | { 75 | try 76 | { 77 | Clipboard.SetText(text); 78 | _viewModel.ShowAlert("Copied text to clipboard.", 0, 10); 79 | } 80 | catch (Exception ex) 81 | { 82 | _viewModel.ShowAlert($"Error copying text to clipboard: {ex}", AlertType.Danger, 10); 83 | } 84 | } 85 | 86 | public void ClearMissingMods() 87 | { 88 | _viewModel.ClearMissingMods(); 89 | } 90 | 91 | public DivinityGlobalCommands() 92 | { 93 | var canExecuteViewModelCommands = this.WhenAnyValue(x => x.ViewModel, x => x.ViewModel.IsLocked, (vm, b) => vm != null && !b); 94 | 95 | OpenFileCommand = ReactiveCommand.Create(OpenFile, canExecuteViewModelCommands); 96 | OpenInFileExplorerCommand = ReactiveCommand.Create(OpenInFileExplorer, canExecuteViewModelCommands); 97 | ClearMissingModsCommand = ReactiveCommand.Create(ClearMissingMods, canExecuteViewModelCommands); 98 | 99 | ToggleNameDisplayCommand = ReactiveCommand.Create((mod) => 100 | { 101 | mod.DisplayFileForName = !mod.DisplayFileForName; 102 | var b = mod.DisplayFileForName; 103 | foreach (var m in _viewModel.Mods) 104 | { 105 | if (m.IsSelected) 106 | { 107 | m.DisplayFileForName = b; 108 | } 109 | } 110 | }, canExecuteViewModelCommands); 111 | 112 | CopyToClipboardCommand = ReactiveCommand.Create(CopyToClipboard, canExecuteViewModelCommands); 113 | 114 | DeleteModCommand = ReactiveCommand.Create((mod) => 115 | { 116 | if (mod.CanDelete && _viewModel != null) 117 | { 118 | _viewModel.DeleteMod(mod); 119 | } 120 | }, canExecuteViewModelCommands); 121 | } 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /DivinityModManagerCore/Util/DivinityGlobalEvents.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace DivinityModManager.Util 8 | { 9 | public class OrderNameChangedArgs : EventArgs 10 | { 11 | public string LastName { get; set; } 12 | public string NewName { get; set; } 13 | } 14 | 15 | public class DivinityGlobalEvents 16 | { 17 | public event EventHandler OrderNameChanged; 18 | 19 | public void OnOrderNameChanged(string lastName, string newName) 20 | { 21 | EventHandler handler = OrderNameChanged; 22 | if (handler != null) 23 | { 24 | handler(this, new OrderNameChangedArgs { LastName = lastName, NewName = newName }); 25 | } 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /DivinityModManagerCore/Util/DivinityJsonUtils.cs: -------------------------------------------------------------------------------- 1 | using Alphaleonis.Win32.Filesystem; 2 | using Newtonsoft.Json; 3 | using Newtonsoft.Json.Linq; 4 | using Newtonsoft.Json.Serialization; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Diagnostics; 8 | using System.Linq; 9 | using System.Text; 10 | using System.Threading.Tasks; 11 | 12 | namespace DivinityModManager.Util 13 | { 14 | public static class DivinityJsonUtils 15 | { 16 | public static T GetValue(this JToken jToken, string key, T defaultValue = default(T)) 17 | { 18 | dynamic ret = jToken[key]; 19 | if (ret == null) return defaultValue; 20 | if (ret is JObject) return JsonConvert.DeserializeObject(ret.ToString()); 21 | return (T)ret; 22 | } 23 | 24 | public static T SafeDeserialize(string text) 25 | { 26 | List errors = new List(); 27 | 28 | var result = JsonConvert.DeserializeObject(text, new JsonSerializerSettings 29 | { 30 | Error = delegate(object sender, ErrorEventArgs args) 31 | { 32 | errors.Add(args.ErrorContext.Error.Message); 33 | args.ErrorContext.Handled = true; 34 | } 35 | }); 36 | if(result != null) 37 | { 38 | return result; 39 | } 40 | else 41 | { 42 | DivinityApp.Log($"Error deserializing json:\n\n{text}\n\t" + String.Join("\n\t", errors)); 43 | return default(T); 44 | } 45 | } 46 | 47 | public static T SafeDeserializeFromPath(string path) 48 | { 49 | try 50 | { 51 | if (File.Exists(path)) 52 | { 53 | string contents = File.ReadAllText(path); 54 | return SafeDeserialize(contents); 55 | } 56 | else 57 | { 58 | DivinityApp.Log($"Error deserializing json: File '{path}' does not exist."); 59 | } 60 | } 61 | catch(Exception ex) 62 | { 63 | DivinityApp.Log("Error deserializing json:\n" + ex.ToString()); 64 | } 65 | return default(T); 66 | } 67 | 68 | public static bool TrySafeDeserialize(string text, out T result) 69 | { 70 | result = JsonConvert.DeserializeObject(text, new JsonSerializerSettings 71 | { 72 | Error = delegate (object sender, ErrorEventArgs args) 73 | { 74 | args.ErrorContext.Handled = true; 75 | } 76 | }); 77 | return result != null; 78 | } 79 | 80 | public static bool TrySafeDeserializeFromPath(string path, out T result) 81 | { 82 | if (File.Exists(path)) 83 | { 84 | string contents = File.ReadAllText(path); 85 | result = JsonConvert.DeserializeObject(contents, new JsonSerializerSettings 86 | { 87 | Error = delegate (object sender, ErrorEventArgs args) 88 | { 89 | args.ErrorContext.Handled = true; 90 | } 91 | }); 92 | return result != null; 93 | } 94 | result = default(T); 95 | return false; 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /DivinityModManagerCore/Util/DivinityModSorter.cs: -------------------------------------------------------------------------------- 1 | using DivinityModManager.Models; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace DivinityModManager.Util 9 | { 10 | public static class DivinityModSorter 11 | { 12 | public static IEnumerable SortAlphabetical(IEnumerable mods) 13 | { 14 | return mods.OrderBy(x => x.DisplayName, StringComparer.OrdinalIgnoreCase); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /DivinityModManagerCore/Util/DivinityStreamUtils.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace DivinityModManager.Util 8 | { 9 | public static class DivinityStreamUtils 10 | { 11 | public static byte[] ReadToEnd(System.IO.Stream stream) 12 | { 13 | long originalPosition = 0; 14 | 15 | if (stream.CanSeek) 16 | { 17 | originalPosition = stream.Position; 18 | stream.Position = 0; 19 | } 20 | 21 | try 22 | { 23 | byte[] readBuffer = new byte[4096]; 24 | 25 | int totalBytesRead = 0; 26 | int bytesRead; 27 | 28 | while ((bytesRead = stream.Read(readBuffer, totalBytesRead, readBuffer.Length - totalBytesRead)) > 0) 29 | { 30 | totalBytesRead += bytesRead; 31 | 32 | if (totalBytesRead == readBuffer.Length) 33 | { 34 | int nextByte = stream.ReadByte(); 35 | if (nextByte != -1) 36 | { 37 | byte[] temp = new byte[readBuffer.Length * 2]; 38 | Buffer.BlockCopy(readBuffer, 0, temp, 0, readBuffer.Length); 39 | Buffer.SetByte(temp, totalBytesRead, (byte)nextByte); 40 | readBuffer = temp; 41 | totalBytesRead++; 42 | } 43 | } 44 | } 45 | 46 | byte[] buffer = readBuffer; 47 | if (readBuffer.Length != totalBytesRead) 48 | { 49 | buffer = new byte[totalBytesRead]; 50 | Buffer.BlockCopy(readBuffer, 0, buffer, 0, totalBytesRead); 51 | } 52 | return buffer; 53 | } 54 | finally 55 | { 56 | if (stream.CanSeek) 57 | { 58 | stream.Position = originalPosition; 59 | } 60 | } 61 | } 62 | 63 | public static int IndexOf(this byte[] arrayToSearchThrough, byte[] patternToFind) 64 | { 65 | if (patternToFind.Length > arrayToSearchThrough.Length) 66 | return -1; 67 | for (int i = 0; i < arrayToSearchThrough.Length - patternToFind.Length; i++) 68 | { 69 | bool found = true; 70 | for (int j = 0; j < patternToFind.Length; j++) 71 | { 72 | if (arrayToSearchThrough[i + j] != patternToFind[j]) 73 | { 74 | found = false; 75 | break; 76 | } 77 | } 78 | if (found) 79 | { 80 | return i; 81 | } 82 | } 83 | return -1; 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /DivinityModManagerCore/Util/GithubHelper.cs: -------------------------------------------------------------------------------- 1 | using DivinityModManager.Extensions; 2 | using Newtonsoft.Json.Linq; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Diagnostics; 6 | using System.IO; 7 | using System.Linq; 8 | using System.Net; 9 | using System.Text; 10 | using System.Threading.Tasks; 11 | 12 | namespace DivinityModManager.Util 13 | { 14 | public static class GithubHelper 15 | { 16 | private static readonly string GIT_URL_REPO_LATEST = "https://api.github.com/repos/{0}/releases/latest"; 17 | 18 | public static async Task GetLatestReleaseDataAsync(string repo) 19 | { 20 | var response = await WebHelper.Client.GetAsync(String.Format(GIT_URL_REPO_LATEST, repo), System.Net.Http.HttpCompletionOption.ResponseContentRead); 21 | return await response.Content.ReadAsStringAsync(); 22 | } 23 | 24 | private static string GetBrowserDownloadUrl(string dataString) 25 | { 26 | var jsonData = DivinityJsonUtils.SafeDeserialize>(dataString); 27 | if (jsonData != null) 28 | { 29 | if (jsonData.TryGetValue("assets", out var assetsArray)) 30 | { 31 | JArray assets = (JArray)assetsArray; 32 | foreach (var obj in assets.Children()) 33 | { 34 | if (obj.TryGetValue("browser_download_url", StringComparison.OrdinalIgnoreCase, out var browserUrl)) 35 | { 36 | return browserUrl.ToString(); 37 | } 38 | } 39 | } 40 | #if DEBUG 41 | var lines = jsonData.Select(kvp => kvp.Key + ": " + kvp.Value.ToString()); 42 | DivinityApp.Log($"Can't find 'browser_download_url' in:\n{String.Join(Environment.NewLine, lines)}"); 43 | #endif 44 | } 45 | return ""; 46 | } 47 | 48 | public static async Task GetLatestReleaseLinkAsync(string repo) 49 | { 50 | var response = await WebHelper.Client.GetAsync(String.Format(GIT_URL_REPO_LATEST, repo), System.Net.Http.HttpCompletionOption.ResponseContentRead); 51 | return GetBrowserDownloadUrl(await response.Content.ReadAsStringAsync()); 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /DivinityModManagerCore/Util/RecycleBinHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Linq; 5 | using System.Runtime.InteropServices; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using Microsoft.VisualBasic.FileIO; 9 | 10 | namespace DivinityModManager.Util 11 | { 12 | public static class RecycleBinHelper 13 | { 14 | // Source: http://csharphelper.com/blog/2015/07/manage-the-recycle-bin-wastebasket-in-c/ 15 | // Structure used by SHQueryRecycleBin. 16 | [StructLayout(LayoutKind.Sequential)] 17 | private struct SHQUERYRBINFO 18 | { 19 | public int cbSize; 20 | public long i64Size; 21 | public long i64NumItems; 22 | } 23 | 24 | // Get information from recycle bin. 25 | [DllImport("shell32.dll")] 26 | private static extern int SHQueryRecycleBin(string pszRootPath, 27 | ref SHQUERYRBINFO pSHQueryRBInfo); 28 | 29 | // Empty the recycle bin. 30 | [DllImport("shell32.dll")] 31 | static extern int SHEmptyRecycleBin(IntPtr hWnd, 32 | string pszRootPath, uint dwFlags); 33 | 34 | // Return the number of items in the recycle bin. 35 | 36 | // Note: In Windows 2000, you need to supply the root 37 | // directory to the call to SHQueryRecycleBin so to get 38 | // the total number of files in the recycle you must add 39 | // up the results for each disk. See: 40 | // http://www.pinvoke.net/default.aspx/shell32/SHQueryRecycleBin.html 41 | public static int NumberOfFilesInRecycleBin() 42 | { 43 | SHQUERYRBINFO sqrbi = new SHQUERYRBINFO(); 44 | sqrbi.cbSize = Marshal.SizeOf(typeof(SHQUERYRBINFO)); 45 | int hresult = SHQueryRecycleBin(string.Empty, ref sqrbi); 46 | return (int)sqrbi.i64NumItems; 47 | } 48 | 49 | // Delete a file or move it to the recycle bin. 50 | public static bool DeleteFile(string filename, bool confirm, bool deletePermanently = false) 51 | { 52 | UIOption uiDisplayOptions = confirm ? UIOption.AllDialogs : UIOption.OnlyErrorDialogs; 53 | RecycleOption reyclingOptions = deletePermanently ? RecycleOption.DeletePermanently : RecycleOption.SendToRecycleBin; 54 | 55 | try 56 | { 57 | Microsoft.VisualBasic.FileIO.FileSystem.DeleteFile(filename, uiDisplayOptions, reyclingOptions); 58 | return true; 59 | } 60 | catch (Exception ex) 61 | { 62 | DivinityApp.Log("Error deleting file.\n" + ex.ToString()); 63 | } 64 | return false; 65 | } 66 | 67 | // Empty the wastebasket. 68 | [Flags] 69 | private enum RecycleFlags : uint 70 | { 71 | SHERB_NOCONFIRMATION = 0x1, 72 | SHERB_NOPROGRESSUI = 0x2, 73 | SHERB_NOSOUND = 0x4 74 | } 75 | public static void EmptyWastebasket(bool show_progress, 76 | bool play_sound, bool confirm) 77 | { 78 | RecycleFlags options = 0; 79 | if (!show_progress) options = 80 | options | RecycleFlags.SHERB_NOPROGRESSUI; 81 | if (!play_sound) options = 82 | options | RecycleFlags.SHERB_NOSOUND; 83 | if (!confirm) options = 84 | options | RecycleFlags.SHERB_NOCONFIRMATION; 85 | 86 | try 87 | { 88 | SHEmptyRecycleBin(IntPtr.Zero, null, (uint)options); 89 | } 90 | catch (Exception ex) 91 | { 92 | DivinityApp.Log("Error emptying wastebasket.\n" + ex.ToString()); 93 | } 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /DivinityModManagerCore/ViewModels/BaseHistoryViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | using System.Reactive.Disposables; 4 | using System.Runtime.CompilerServices; 5 | using ReactiveUI; 6 | using ReactiveHistory; 7 | using Reactive.Bindings.Extensions; 8 | using System.Reflection; 9 | using System.Windows.Input; 10 | using System.Collections.Generic; 11 | using DivinityModManager.Models; 12 | 13 | namespace DivinityModManager.ViewModels 14 | { 15 | public interface IHistoryViewModel 16 | { 17 | IHistory History { get; } 18 | 19 | void Undo(); 20 | void Redo(); 21 | } 22 | 23 | public static class HistoryViewModelExtensions 24 | { 25 | public static void ChangeListWithHistory(this IHistoryViewModel vm, IList source, IList oldValue, IList newValue) 26 | { 27 | if (vm.History != null) 28 | { 29 | void undo() => source = oldValue; 30 | void redo() => source = newValue; 31 | vm.History.Snapshot(undo, redo); 32 | source = newValue; 33 | } 34 | } 35 | 36 | public static void AddWithHistory(this IHistoryViewModel vm, IList source, T item) 37 | { 38 | if (vm.History != null) 39 | { 40 | int index = source.Count; 41 | void redo() => source.Insert(index, item); 42 | void undo() => source.RemoveAt(index); 43 | vm.History.Snapshot(undo, redo); 44 | redo(); 45 | } 46 | } 47 | 48 | public static void RemoveWithHistory(this IHistoryViewModel vm, IList source, T item) 49 | { 50 | if (vm.History != null) 51 | { 52 | int index = source.IndexOf(item); 53 | void redo() => source.RemoveAt(index); 54 | void undo() => source.Insert(index, item); 55 | vm.History.Snapshot(undo, redo); 56 | redo(); 57 | } 58 | } 59 | 60 | public static void CreateSnapshot(this IHistoryViewModel vm, Action undo, Action redo) 61 | { 62 | vm.History?.Snapshot(undo, redo); 63 | } 64 | } 65 | 66 | public abstract class BaseHistoryViewModel : BaseHistoryObject, IHistoryViewModel, IDisposable 67 | { 68 | public CompositeDisposable Disposables { get; internal set; } 69 | 70 | public ICommand UndoCommand { get; set; } 71 | public ICommand RedoCommand { get; set; } 72 | public ICommand ClearHistoryCommand { get; set; } 73 | 74 | public void Dispose() 75 | { 76 | this.Disposables?.Dispose(); 77 | } 78 | 79 | public void Undo() 80 | { 81 | History.Undo(); 82 | } 83 | 84 | public void Redo() 85 | { 86 | History.Redo(); 87 | } 88 | 89 | public BaseHistoryViewModel() 90 | { 91 | Disposables = new CompositeDisposable(); 92 | 93 | var history = new StackHistory().AddTo(Disposables); 94 | History = history; 95 | 96 | var undo = ReactiveCommand.Create(Undo, History.CanUndo); 97 | undo.Subscribe().DisposeWith(this.Disposables); 98 | UndoCommand = undo; 99 | 100 | var redo = ReactiveCommand.Create(Redo, History.CanRedo); 101 | redo.Subscribe().DisposeWith(this.Disposables); 102 | RedoCommand = redo; 103 | 104 | var clear = ReactiveCommand.Create(History.Clear, History.CanClear); 105 | clear.Subscribe().DisposeWith(this.Disposables); 106 | ClearHistoryCommand = clear; 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /DivinityModManagerCore/ViewModels/BaseViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | using System.Reactive.Disposables; 4 | using System.Runtime.CompilerServices; 5 | using ReactiveUI; 6 | using ReactiveHistory; 7 | using Reactive.Bindings.Extensions; 8 | using System.Reflection; 9 | using System.Windows.Input; 10 | using System.Collections.Generic; 11 | using DivinityModManager.Models; 12 | 13 | namespace DivinityModManager.ViewModels 14 | { 15 | public class BaseViewModel : ReactiveObject, IDisposable 16 | { 17 | public CompositeDisposable Disposables { get; private set; } 18 | 19 | public void Dispose() 20 | { 21 | this.Disposables?.Dispose(); 22 | } 23 | 24 | public BaseViewModel() 25 | { 26 | Disposables = new CompositeDisposable(); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /DivinityModManagerCore/ViewModels/IDivinityAppViewModel.cs: -------------------------------------------------------------------------------- 1 | using DivinityModManager.Models; 2 | 3 | using DynamicData.Binding; 4 | 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Collections.ObjectModel; 8 | using System.Linq; 9 | using System.Text; 10 | using System.Threading.Tasks; 11 | 12 | namespace DivinityModManager.ViewModels 13 | { 14 | public interface IDivinityAppViewModel 15 | { 16 | IEnumerable ActiveMods { get; } 17 | IEnumerable InactiveMods { get; } 18 | ObservableCollectionExtended Profiles { get; } 19 | ReadOnlyObservableCollection Mods { get; } 20 | ReadOnlyObservableCollection WorkshopMods { get; } 21 | 22 | bool IsDragging { get; } 23 | bool IsRefreshing { get; } 24 | bool IsLocked { get; } 25 | 26 | int ActiveSelected { get; } 27 | int InactiveSelected { get; } 28 | 29 | void ShowAlert(string message, AlertType alertType = AlertType.Info, int timeout = 0); 30 | void DeleteMod(DivinityModData mod); 31 | void ClearMissingMods(); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /GUI/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 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 | -------------------------------------------------------------------------------- /GUI/App.xaml: -------------------------------------------------------------------------------- 1 |  10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /GUI/App.xaml.cs: -------------------------------------------------------------------------------- 1 | using Alphaleonis.Win32.Filesystem; 2 | 3 | using DivinityModManager.Util; 4 | using DivinityModManager.Views; 5 | using ReactiveUI; 6 | using System; 7 | using System.Collections.Generic; 8 | using System.Configuration; 9 | using System.Data; 10 | using System.Globalization; 11 | using System.Linq; 12 | using System.Reflection; 13 | using System.Threading.Tasks; 14 | using System.Windows; 15 | using System.Windows.Input; 16 | using System.Windows.Markup; 17 | 18 | namespace DivinityModManager 19 | { 20 | /// 21 | /// Interaction logic for App.xaml 22 | /// 23 | public partial class App : Application 24 | { 25 | public App() 26 | { 27 | // Fix for loading C++ dlls from _Lib 28 | AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve; 29 | //AssemblyResolver.Hook("_Lib"); 30 | // POCO type warning suppression 31 | Splat.Locator.CurrentMutable.Register(() => new DivinityModManager.Util.CustomPropertyResolver(), typeof(ICreatesObservableForProperty)); 32 | WebHelper.SetupClient(); 33 | #if DEBUG 34 | RxApp.SuppressViewCommandBindingMessage = false; 35 | #else 36 | RxApp.DefaultExceptionHandler = new RxExceptionHandler(); 37 | RxApp.SuppressViewCommandBindingMessage = true; 38 | #endif 39 | } 40 | 41 | private static System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) 42 | { 43 | var assyName = new AssemblyName(args.Name); 44 | 45 | var newPath = Path.Combine("_Lib", assyName.Name); 46 | if (!newPath.EndsWith(".dll")) 47 | { 48 | newPath += ".dll"; 49 | } 50 | 51 | if (File.Exists(newPath)) 52 | { 53 | var assy = Assembly.LoadFile(newPath); 54 | return assy; 55 | } 56 | return null; 57 | } 58 | 59 | private void Application_Startup(object sender, StartupEventArgs e) 60 | { 61 | //For making date display use the current system's culture 62 | FrameworkElement.LanguageProperty.OverrideMetadata( 63 | typeof(FrameworkElement), 64 | new FrameworkPropertyMetadata(XmlLanguage.GetLanguage(CultureInfo.CurrentCulture.IetfLanguageTag))); 65 | 66 | EventManager.RegisterClassHandler(typeof(Window), Window.PreviewMouseDownEvent, new MouseButtonEventHandler(OnPreviewMouseDown)); 67 | } 68 | 69 | private static void OnPreviewMouseDown(object sender, MouseButtonEventArgs e) 70 | { 71 | DivinityApp.IsKeyboardNavigating = false; 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /GUI/Controls/AlertBar.xaml: -------------------------------------------------------------------------------- 1 |  12 | 13 | 82 | 83 | 84 | 85 | 86 | 87 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 104 | 110 | 117 | 118 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | -------------------------------------------------------------------------------- /GUI/Controls/AutoGrayableImage.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Diagnostics; 5 | using System.IO; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | using System.Windows; 10 | using System.Windows.Controls; 11 | using System.Windows.Media; 12 | using System.Windows.Media.Imaging; 13 | 14 | namespace DivinityModManager.Controls 15 | { 16 | /// 17 | /// Image that switches to monochrome when disabled. 18 | /// Source: https://www.engineeringsolutions.de/creating-an-automatic-adjusting-image-for-buttons-in-wpf/ 19 | /// 20 | public class AutoGrayableImage : Image 21 | { 22 | // References to original and grayscale ImageSources 23 | private ImageSource _sourceColor; 24 | private ImageSource _sourceGray; 25 | // Original and grayscale opacity masks 26 | private Brush _opacityMaskColor; 27 | private Brush _opacityMaskGray; 28 | 29 | static AutoGrayableImage() 30 | { 31 | DefaultStyleKeyProperty.OverrideMetadata(typeof(AutoGrayableImage), new FrameworkPropertyMetadata(typeof(AutoGrayableImage))); 32 | } 33 | 34 | /// 35 | /// Overwritten to handle changes of IsEnabled, Source and OpacityMask properties 36 | /// 37 | protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e) 38 | { 39 | if (e.Property.Name.Equals(nameof(IsEnabled))) 40 | { 41 | if (e.NewValue as bool? == false) 42 | { 43 | Source = _sourceGray; 44 | OpacityMask = _opacityMaskGray; 45 | } 46 | else if (e.NewValue as bool? == true) 47 | { 48 | Source = _sourceColor; 49 | OpacityMask = _opacityMaskColor; 50 | } 51 | } 52 | else if (e.Property.Name.Equals(nameof(Source)) && // only recache Source if it's the new one from outside 53 | !ReferenceEquals(Source, _sourceColor) && 54 | !ReferenceEquals(Source, _sourceGray)) 55 | { 56 | SetSources(); 57 | } 58 | else if (e.Property.Name.Equals(nameof(OpacityMask)) && // only recache opacityMask if it's the new one from outside 59 | !ReferenceEquals(OpacityMask, _opacityMaskColor) && 60 | !ReferenceEquals(OpacityMask, _opacityMaskGray)) 61 | { 62 | _opacityMaskColor = OpacityMask; 63 | } 64 | 65 | base.OnPropertyChanged(e); 66 | } 67 | 68 | /// 69 | /// Caches original ImageSource, creates and caches grayscale ImageSource and grayscale opacity mask 70 | /// 71 | private void SetSources() 72 | { 73 | // If grayscale image cannot be created set grayscale source to original Source first 74 | _sourceGray = _sourceColor = Source; 75 | 76 | // Create Opacity Mask for grayscale image as FormatConvertedBitmap does not keep transparency info 77 | _opacityMaskGray = new ImageBrush(_sourceColor); 78 | _opacityMaskGray.Opacity = 0.6; 79 | Uri uri = null; 80 | 81 | try 82 | { 83 | // Get the string Uri for the original image source first 84 | string stringUri = TypeDescriptor.GetConverter(Source).ConvertTo(Source, typeof(string)) as string; 85 | 86 | // Try to resolve it as an absolute Uri 87 | if (!Uri.TryCreate(stringUri, UriKind.Absolute, out uri)) 88 | { 89 | // Uri is relative => requested image is in the same assembly as this object 90 | stringUri = "pack://application:,,,/" + stringUri.TrimStart(new char[2] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar }); 91 | uri = new Uri(stringUri); 92 | } 93 | 94 | // create and cache grayscale ImageSource 95 | _sourceGray = new FormatConvertedBitmap(new BitmapImage(uri), PixelFormats.Gray8, null, 0); 96 | } 97 | catch (Exception e) 98 | { 99 | //Debug.Fail("The Image used cannot be grayed out.", "Use BitmapImage or URI as a Source in order to allow gray scaling. Make sure the absolute Uri is used as relative Uri may sometimes resolve incorrectly.\n\nException: " + e.Message); 100 | DivinityApp.Log($"Error greying out image '{uri}'({Source}).\n\nException: {e.Message}"); 101 | } 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /GUI/Controls/AutomationTooltip.cs: -------------------------------------------------------------------------------- 1 | using DivinityModManager.Util; 2 | 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using System.Windows.Automation.Peers; 9 | using System.Windows.Controls; 10 | 11 | namespace DivinityModManager.Controls 12 | { 13 | public class AutomationTooltip : ToolTip 14 | { 15 | protected override AutomationPeer OnCreateAutomationPeer() 16 | { 17 | return new AutomationTooltipPeer(this); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /GUI/Controls/Behavior/TextBlockSettingsEntryAttributeBehavior.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using System.Windows; 8 | using System.Windows.Controls; 9 | 10 | namespace DivinityModManager.Controls.Behavior 11 | { 12 | public class TextBlockSettingsEntryAttributeBehavior 13 | { 14 | public static string GetTarget(DependencyObject element) 15 | { 16 | return (string)element.GetValue(TargetProperty); 17 | } 18 | 19 | public static void SetTarget(DependencyObject element, string value) 20 | { 21 | element.SetValue(TargetProperty, value); 22 | } 23 | 24 | public static readonly DependencyProperty TargetProperty = 25 | DependencyProperty.RegisterAttached( 26 | "Target", 27 | typeof(string), 28 | typeof(ScreenReaderHelperBehavior), 29 | new UIPropertyMetadata("", OnTargetSet)); 30 | 31 | static void OnTargetSet(DependencyObject depObj, DependencyPropertyChangedEventArgs e) 32 | { 33 | if (depObj is TextBlock element && e.NewValue is string propName && !String.IsNullOrEmpty(propName)) 34 | { 35 | if(element.DataContext != null) 36 | { 37 | PropertyInfo prop = element.DataContext.GetType().GetProperty(propName); 38 | SettingsEntryAttribute settingsEntry = prop.GetCustomAttribute(); 39 | element.Text = settingsEntry.DisplayName; 40 | element.ToolTip = settingsEntry.Tooltip; 41 | } 42 | else 43 | { 44 | element.DataContextChanged += OnDataContextChanged; 45 | } 46 | } 47 | } 48 | 49 | private static void OnDataContextChanged(object sender, DependencyPropertyChangedEventArgs e) 50 | { 51 | if (sender is TextBlock element && e.NewValue != null) 52 | { 53 | string propName = (string)element.GetValue(TargetProperty); 54 | PropertyInfo prop = element.DataContext.GetType().GetProperty(propName); 55 | SettingsEntryAttribute settingsEntry = prop.GetCustomAttribute(); 56 | element.Text = settingsEntry.DisplayName; 57 | element.ToolTip = settingsEntry.Tooltip; 58 | } 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /GUI/Controls/Behavior/ToolTipHelperBehavior.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Windows; 7 | using System.Windows.Interop; 8 | 9 | namespace DivinityModManager.Controls.Behavior 10 | { 11 | public class ToolTipHelperBehavior 12 | { 13 | public static bool GetDisableMouseEvents(DependencyObject element) 14 | { 15 | return (bool)element.GetValue(DisableMouseEventsProperty); 16 | } 17 | 18 | public static void SetDisableMouseEvents(DependencyObject element, bool value) 19 | { 20 | element.SetValue(DisableMouseEventsProperty, value); 21 | } 22 | 23 | public static readonly DependencyProperty DisableMouseEventsProperty = 24 | DependencyProperty.RegisterAttached( 25 | "DisableMouseEvents", 26 | typeof(bool), 27 | typeof(ScreenReaderHelperBehavior), 28 | new UIPropertyMetadata(false, OnDisableMouseEvents)); 29 | 30 | static void OnDisableMouseEvents(DependencyObject depObj, DependencyPropertyChangedEventArgs e) 31 | { 32 | if (depObj is UIElement element) 33 | { 34 | if ((bool)e.NewValue == true) 35 | { 36 | element.PreviewMouseDown += OnPreviewMouseDown; 37 | } 38 | else 39 | { 40 | element.PreviewMouseDown -= OnPreviewMouseDown; 41 | } 42 | } 43 | } 44 | 45 | private static void OnPreviewMouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e) 46 | { 47 | e.Handled = true; 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /GUI/Controls/BusyIndicator.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Windows; 7 | using System.Windows.Controls; 8 | using System.Windows.Data; 9 | using System.Windows.Documents; 10 | using System.Windows.Input; 11 | using System.Windows.Media; 12 | using System.Windows.Media.Imaging; 13 | using System.Windows.Navigation; 14 | using System.Windows.Shapes; 15 | 16 | namespace DivinityModManager.Controls 17 | { 18 | /// 19 | /// Interaction logic for BusyIndicator.xaml 20 | /// 21 | public partial class BusyIndicator : UserControl 22 | { 23 | public BusyIndicator() 24 | { 25 | InitializeComponent(); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /GUI/Controls/CircleDecorator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Windows; 7 | using System.Windows.Controls; 8 | using System.Windows.Media; 9 | 10 | namespace DivinityModManager.Controls 11 | { 12 | public class CircleDecorator : Border 13 | { 14 | protected override void OnRender(System.Windows.Media.DrawingContext drawingContext) 15 | { 16 | Double width = this.ActualWidth; 17 | Double height = this.ActualHeight; 18 | Double a = width / 2; 19 | Double b = height / 2; 20 | Point centerPoint = new Point(a, b); 21 | Double thickness = this.BorderThickness.Left; 22 | EllipseGeometry ellipse = new EllipseGeometry(centerPoint, a, b); 23 | drawingContext.PushClip(ellipse); 24 | drawingContext.DrawGeometry( 25 | this.Background, 26 | new Pen(this.BorderBrush, thickness), 27 | ellipse); 28 | } 29 | 30 | protected override Size MeasureOverride(Size constraint) 31 | { 32 | return base.MeasureOverride(constraint); 33 | } 34 | 35 | protected override Size ArrangeOverride(Size finalSize) 36 | { 37 | Double a = finalSize.Width / 2; 38 | Double b = finalSize.Height / 2; 39 | Double PI = 3.1415926; 40 | Double x = a * Math.Cos(45 * PI / 180); 41 | Double y = b * Math.Sin(45 * PI / 180); 42 | Rect rect = new Rect(new Point(a - x, b - y), new Point(a + x, b + y)); 43 | if (base.Child != null) 44 | { 45 | base.Child.Arrange(rect); 46 | } 47 | 48 | return finalSize; 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /GUI/Controls/Extensions/EnumExtension.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using System.Windows.Markup; 8 | 9 | namespace DivinityModManager.Controls.Extensions 10 | { 11 | public class EnumExtension : MarkupExtension 12 | { 13 | private Type _enumType; 14 | 15 | 16 | public EnumExtension(Type enumType) 17 | { 18 | if (enumType == null) 19 | throw new ArgumentNullException("enumType"); 20 | 21 | EnumType = enumType; 22 | } 23 | 24 | public Type EnumType 25 | { 26 | get { return _enumType; } 27 | private set 28 | { 29 | if (_enumType == value) 30 | return; 31 | 32 | var enumType = Nullable.GetUnderlyingType(value) ?? value; 33 | 34 | if (enumType.IsEnum == false) 35 | throw new ArgumentException("Type must be an Enum."); 36 | 37 | _enumType = value; 38 | } 39 | } 40 | 41 | public override object ProvideValue(IServiceProvider serviceProvider) 42 | { 43 | var enumValues = Enum.GetValues(EnumType); 44 | 45 | return ( 46 | from object enumValue in enumValues 47 | select new EnumerationMember 48 | { 49 | Value = enumValue, 50 | Description = GetDescription(enumValue) 51 | }).ToArray(); 52 | } 53 | 54 | private string GetDescription(object enumValue) 55 | { 56 | var descriptionAttribute = EnumType 57 | .GetField(enumValue.ToString()) 58 | .GetCustomAttributes(typeof(DescriptionAttribute), false) 59 | .FirstOrDefault() as DescriptionAttribute; 60 | 61 | 62 | return descriptionAttribute != null 63 | ? descriptionAttribute.Description 64 | : enumValue.ToString(); 65 | } 66 | 67 | public class EnumerationMember 68 | { 69 | public string Description { get; set; } 70 | public object Value { get; set; } 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /GUI/Controls/Extensions/HyperlinkExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using System.Windows; 8 | using System.Windows.Documents; 9 | 10 | namespace DivinityModManager.Controls.Extensions 11 | { 12 | /// 13 | /// Source: https://stackoverflow.com/a/11433814 14 | /// 15 | public static class HyperlinkExtensions 16 | { 17 | public static bool GetIsExternal(DependencyObject obj) 18 | { 19 | return (bool)obj.GetValue(IsExternalProperty); 20 | } 21 | 22 | public static void SetIsExternal(DependencyObject obj, bool value) 23 | { 24 | obj.SetValue(IsExternalProperty, value); 25 | } 26 | public static readonly DependencyProperty IsExternalProperty = 27 | DependencyProperty.RegisterAttached("IsExternal", typeof(bool), typeof(HyperlinkExtensions), new UIPropertyMetadata(false, OnIsExternalChanged)); 28 | 29 | private static void OnIsExternalChanged(object sender, DependencyPropertyChangedEventArgs args) 30 | { 31 | var hyperlink = sender as Hyperlink; 32 | 33 | if ((bool)args.NewValue) 34 | hyperlink.RequestNavigate += Hyperlink_RequestNavigate; 35 | else 36 | hyperlink.RequestNavigate -= Hyperlink_RequestNavigate; 37 | } 38 | 39 | private static void Hyperlink_RequestNavigate(object sender, System.Windows.Navigation.RequestNavigateEventArgs e) 40 | { 41 | Process.Start(new ProcessStartInfo(e.Uri.AbsoluteUri)); 42 | e.Handled = true; 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /GUI/Controls/HotkeyEditorControl.xaml: -------------------------------------------------------------------------------- 1 |  13 | 14 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /GUI/Controls/HotkeyEditorControl.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Windows; 7 | using System.Windows.Controls; 8 | using System.Windows.Data; 9 | using System.Windows.Documents; 10 | using System.Windows.Input; 11 | using System.Windows.Media; 12 | using System.Windows.Media.Imaging; 13 | using System.Windows.Navigation; 14 | using System.Windows.Shapes; 15 | using DivinityModManager.Models.App; 16 | 17 | using ReactiveUI; 18 | using ReactiveUI.Wpf; 19 | 20 | namespace DivinityModManager.Controls 21 | { 22 | /// 23 | /// Interaction logic for HotkeyEditorControl.xaml 24 | /// 25 | public partial class HotkeyEditorControl : UserControl 26 | { 27 | public static readonly DependencyProperty HotkeyProperty = 28 | DependencyProperty.Register("Hotkey", typeof(Hotkey), typeof(HotkeyEditorControl)); 29 | 30 | public Hotkey Hotkey 31 | { 32 | get => (Hotkey)GetValue(HotkeyProperty); 33 | set => SetValue(HotkeyProperty, value); 34 | } 35 | 36 | public static readonly DependencyProperty FocusReturnTargetProperty = 37 | DependencyProperty.Register("FocusReturnTarget", typeof(IInputElement), typeof(HotkeyEditorControl)); 38 | 39 | public IInputElement FocusReturnTarget 40 | { 41 | get => (IInputElement)GetValue(FocusReturnTargetProperty); 42 | set => SetValue(FocusReturnTargetProperty, value); 43 | } 44 | 45 | public void StopEditing() 46 | { 47 | Keyboard.ClearFocus(); 48 | Keyboard.Focus(FocusReturnTarget); 49 | } 50 | 51 | private void SetKeybind(Key key = Key.None, ModifierKeys modifierKeys = ModifierKeys.None) 52 | { 53 | Hotkey.Key = key; 54 | Hotkey.Modifiers = modifierKeys; 55 | Hotkey.UpdateDisplayBindingText(); 56 | StopEditing(); 57 | } 58 | 59 | private void HotkeyTextBox_PreviewKeyUp(object sender, KeyEventArgs e) 60 | { 61 | // Don't let the event pass further 62 | // because we don't want standard textbox shortcuts working 63 | e.Handled = true; 64 | 65 | // Get modifiers and key data 66 | var modifiers = Keyboard.Modifiers; 67 | var key = e.Key; 68 | 69 | // When Alt is pressed, SystemKey is used instead 70 | if (key == Key.System) 71 | { 72 | key = e.SystemKey; 73 | } 74 | 75 | // Shortcut for resetting 76 | if(modifiers == ModifierKeys.Shift && key == Key.Back) 77 | { 78 | Hotkey.ResetToDefault(); 79 | StopEditing(); 80 | return; 81 | } 82 | 83 | // Pressing escape without modifiers clears the current value 84 | if (modifiers == ModifierKeys.None && key == Key.Escape) 85 | { 86 | SetKeybind(); 87 | return; 88 | } 89 | // Pressing enter without modifiers removes focus 90 | // If the hotkey's default key is Return, and it's set to Return, stop editing as well. 91 | if (modifiers == ModifierKeys.None && key == Key.Return && (Hotkey.DefaultKey != Key.Return || Hotkey.Key == Hotkey.DefaultKey)) 92 | { 93 | StopEditing(); 94 | return; 95 | } 96 | 97 | // If no actual key was pressed - return 98 | if (key == Key.LeftCtrl || 99 | key == Key.RightCtrl || 100 | key == Key.LeftAlt || 101 | key == Key.RightAlt || 102 | key == Key.LeftShift || 103 | key == Key.RightShift || 104 | key == Key.LWin || 105 | key == Key.RWin || 106 | key == Key.Clear || 107 | key == Key.OemClear || 108 | key == Key.Apps) 109 | { 110 | return; 111 | } 112 | 113 | // Update the value 114 | SetKeybind(key, modifiers); 115 | } 116 | 117 | public HotkeyEditorControl() 118 | { 119 | InitializeComponent(); 120 | } 121 | 122 | private void HotkeyTextBox_PreviewMouseRightButtonDown(object sender, MouseButtonEventArgs e) 123 | { 124 | // Disables focusing on right click; 125 | e.Handled = true; 126 | } 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /GUI/Controls/HyperlinkText.xaml: -------------------------------------------------------------------------------- 1 |  10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /GUI/Controls/HyperlinkText.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using System.Windows; 8 | using System.Windows.Controls; 9 | using System.Windows.Data; 10 | using System.Windows.Documents; 11 | using System.Windows.Input; 12 | using System.Windows.Media; 13 | using System.Windows.Media.Imaging; 14 | using System.Windows.Navigation; 15 | using System.Windows.Shapes; 16 | 17 | namespace DivinityModManager.Controls 18 | { 19 | /// 20 | /// Interaction logic for HyperlinkText.xaml 21 | /// 22 | public partial class HyperlinkText : TextBlock 23 | { 24 | public string URL 25 | { 26 | get { return (string)GetValue(URLProperty); } 27 | set { SetValue(URLProperty, value); } 28 | } 29 | 30 | // Using a DependencyProperty as the backing store for URL. This enables animation, styling, binding, etc... 31 | public static readonly DependencyProperty URLProperty = 32 | DependencyProperty.Register("URL", typeof(string), typeof(HyperlinkText), new FrameworkPropertyMetadata("", new PropertyChangedCallback(OnURLChanged))); 33 | 34 | private static void OnURLChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 35 | { 36 | string url = (string)e.NewValue; 37 | Uri uri = null; 38 | if(Uri.TryCreate(url, UriKind.RelativeOrAbsolute, out uri)) 39 | { 40 | HyperlinkText x = (HyperlinkText)d; 41 | x.Hyperlink.NavigateUri = uri; 42 | x.ToolTip = url; 43 | if(String.IsNullOrEmpty(x.DisplayText) || x.UseUrlForDisplayText) 44 | { 45 | x.DisplayText = url; 46 | } 47 | } 48 | } 49 | 50 | public string DisplayText 51 | { 52 | get { return (string)GetValue(DisplayTextProperty); } 53 | set { SetValue(DisplayTextProperty, value); } 54 | } 55 | 56 | // Using a DependencyProperty as the backing store for DisplayText. This enables animation, styling, binding, etc... 57 | public static readonly DependencyProperty DisplayTextProperty = 58 | DependencyProperty.Register("DisplayText", typeof(string), typeof(HyperlinkText), new PropertyMetadata("")); 59 | 60 | public bool UseUrlForDisplayText 61 | { 62 | get { return (bool)GetValue(UseUrlForDisplayTextProperty); } 63 | set { SetValue(UseUrlForDisplayTextProperty, value); } 64 | } 65 | 66 | public static readonly DependencyProperty UseUrlForDisplayTextProperty = 67 | DependencyProperty.Register("UseUrlForDisplayText", typeof(bool), typeof(HyperlinkText), new PropertyMetadata(false)); 68 | 69 | public HyperlinkText() 70 | { 71 | InitializeComponent(); 72 | 73 | Hyperlink.RequestNavigate += Hyperlink_RequestNavigate; 74 | } 75 | 76 | private void Hyperlink_RequestNavigate(object sender, RequestNavigateEventArgs e) 77 | { 78 | Process.Start(new ProcessStartInfo(e.Uri.AbsoluteUri)); 79 | e.Handled = true; 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /GUI/Controls/ModEntryGrid.cs: -------------------------------------------------------------------------------- 1 | using DivinityModManager.Util.ScreenReader; 2 | 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using System.Windows.Automation.Peers; 9 | using System.Windows.Controls; 10 | 11 | namespace DivinityModManager.Controls 12 | { 13 | public class ModEntryGrid : Grid 14 | { 15 | public ModEntryGrid() : base() { } 16 | 17 | protected override AutomationPeer OnCreateAutomationPeer() 18 | { 19 | return new ModEntryGridAutomationPeer(this); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /GUI/Controls/SelectableTextBlock.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Windows; 7 | using System.Windows.Controls; 8 | using System.Windows.Documents; 9 | using System.Windows.Input; 10 | using System.Windows.Media; 11 | 12 | namespace DivinityModManager.Controls 13 | { 14 | // Source: https://stackoverflow.com/a/32870521 15 | public partial class SelectableTextBlock : TextBlock 16 | { 17 | TextPointer StartSelectPosition; 18 | TextPointer EndSelectPosition; 19 | public String SelectedText = ""; 20 | 21 | public delegate void TextSelectedHandler(string SelectedText); 22 | public event TextSelectedHandler TextSelected; 23 | 24 | protected override void OnMouseDown(MouseButtonEventArgs e) 25 | { 26 | base.OnMouseDown(e); 27 | Point mouseDownPoint = e.GetPosition(this); 28 | StartSelectPosition = this.GetPositionFromPoint(mouseDownPoint, true); 29 | } 30 | 31 | protected override void OnMouseUp(MouseButtonEventArgs e) 32 | { 33 | base.OnMouseUp(e); 34 | Point mouseUpPoint = e.GetPosition(this); 35 | EndSelectPosition = this.GetPositionFromPoint(mouseUpPoint, true); 36 | 37 | TextRange otr = new TextRange(this.ContentStart, this.ContentEnd); 38 | otr.ApplyPropertyValue(TextElement.ForegroundProperty, new SolidColorBrush(Colors.GreenYellow)); 39 | 40 | TextRange ntr = new TextRange(StartSelectPosition, EndSelectPosition); 41 | ntr.ApplyPropertyValue(TextElement.ForegroundProperty, new SolidColorBrush(Colors.White)); 42 | 43 | SelectedText = ntr.Text; 44 | if (!(TextSelected == null)) 45 | { 46 | TextSelected(SelectedText); 47 | } 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /GUI/Controls/TemplateSelectors/TagTemplateSelector.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Windows; 4 | using System.Windows.Controls; 5 | 6 | namespace DivinityModManager.Controls.TemplateSelectors 7 | { 8 | public class TagTemplateSelector : DataTemplateSelector 9 | { 10 | private string[] modeValues = {"Story", "GM", "Arena" }; 11 | private string[] defaultWorkshopTags = { "Armors", "Balancing/Stats", "Classes", "Companions", "Consumables", "Maps", "Origins", "Overhauls", "Quality of Life", "Quests", "Races", "Runes/Boosts", "Skills", "Utility", "Visual Overrides", "Weapons" }; 12 | 13 | public override DataTemplate SelectTemplate(object item, DependencyObject container) 14 | { 15 | FrameworkElement element = container as FrameworkElement; 16 | 17 | if (element != null && item != null && item is string tag) 18 | { 19 | if(modeValues.Any(x => x.Equals(tag, StringComparison.OrdinalIgnoreCase))) 20 | { 21 | return element.FindResource("ModeTagTemplate") as DataTemplate; 22 | } 23 | else if (defaultWorkshopTags.Any(x => x.Equals(tag, StringComparison.OrdinalIgnoreCase))) 24 | { 25 | return element.FindResource("TagTemplate") as DataTemplate; 26 | } 27 | else 28 | { 29 | return element.FindResource("CustomTagTemplate") as DataTemplate; 30 | } 31 | } 32 | 33 | return null; 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /GUI/Controls/UnfocusableTextBox.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Windows; 7 | using System.Windows.Controls; 8 | using System.Windows.Data; 9 | using System.Windows.Input; 10 | 11 | namespace DivinityModManager.Controls 12 | { 13 | public class UnfocusableTextBox : TextBox 14 | { 15 | public bool CanUndoTextOnEscape 16 | { 17 | get { return (bool)GetValue(CanUndoTextOnEscapeProperty); } 18 | set { SetValue(CanUndoTextOnEscapeProperty, value); } 19 | } 20 | 21 | public static readonly DependencyProperty CanUndoTextOnEscapeProperty = 22 | DependencyProperty.Register("CanUndoTextOnEscape", 23 | typeof(bool), typeof(UnfocusableTextBox), 24 | new PropertyMetadata(false)); 25 | 26 | public bool UpdateBindingOnFocusLost 27 | { 28 | get { return (bool)GetValue(UpdateBindingOnFocusLostProperty); } 29 | set { SetValue(UpdateBindingOnFocusLostProperty, value); } 30 | } 31 | 32 | public static readonly DependencyProperty UpdateBindingOnFocusLostProperty = 33 | DependencyProperty.Register("UpdateBindingOnFocusLost", 34 | typeof(bool), typeof(UnfocusableTextBox), 35 | new PropertyMetadata(false)); 36 | 37 | public UnfocusableTextBox() 38 | { 39 | 40 | } 41 | 42 | private string lastText = ""; 43 | 44 | protected override void OnGotKeyboardFocus(KeyboardFocusChangedEventArgs e) 45 | { 46 | base.OnGotKeyboardFocus(e); 47 | lastText = Text; 48 | } 49 | 50 | protected override void OnKeyDown(KeyEventArgs e) 51 | { 52 | base.OnKeyDown(e); 53 | if (e.Key == Key.Return) 54 | { 55 | Keyboard.ClearFocus(); 56 | lastText = Text; 57 | if(UpdateBindingOnFocusLost) 58 | { 59 | var bindingExpression = BindingOperations.GetBindingExpression(this, TextBox.TextProperty); 60 | if(bindingExpression != null) 61 | { 62 | bindingExpression.UpdateSource(); 63 | } 64 | } 65 | } 66 | else if(e.Key == Key.Escape && CanUndoTextOnEscape) 67 | { 68 | if (CanUndoTextOnEscape && Text != lastText) Text = lastText; 69 | Keyboard.ClearFocus(); 70 | } 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /GUI/Converters/BoolToVisibilityConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Globalization; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using System.Windows.Controls; 8 | using System.Windows.Data; 9 | using System.Windows; 10 | 11 | namespace DivinityModManager.Converters 12 | { 13 | public class BoolToVisibilityConverter : IValueConverter 14 | { 15 | public static Visibility FromBool(bool b) => b ? Visibility.Visible : Visibility.Collapsed; 16 | 17 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 18 | { 19 | bool reverse = false; 20 | if (parameter != null) 21 | { 22 | if (parameter is int reverseInt) 23 | { 24 | reverse = reverseInt > 0; 25 | } 26 | else if (parameter is bool r) 27 | { 28 | reverse = r; 29 | } 30 | DivinityApp.Log($"BoolToVisibilityConverter param: {parameter} | {parameter.GetType()}"); 31 | } 32 | 33 | if (value is bool b) 34 | { 35 | if (!reverse) 36 | { 37 | return b ? Visibility.Visible : Visibility.Collapsed; 38 | } 39 | else 40 | { 41 | return !b ? Visibility.Visible : Visibility.Collapsed; 42 | } 43 | } 44 | return Visibility.Visible; 45 | } 46 | 47 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 48 | { 49 | if (value is Visibility visbility) 50 | { 51 | if (visbility == Visibility.Visible) 52 | { 53 | return true; 54 | } 55 | } 56 | return false; 57 | } 58 | } 59 | 60 | public class BoolToVisibilityConverterReversed : IValueConverter 61 | { 62 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 63 | { 64 | if (value is bool b) 65 | { 66 | return !b ? Visibility.Visible : Visibility.Collapsed; 67 | } 68 | return Visibility.Collapsed; 69 | } 70 | 71 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 72 | { 73 | if (value is Visibility visbility) 74 | { 75 | if (visbility == Visibility.Visible) 76 | { 77 | return false; 78 | } 79 | } 80 | return true; 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /GUI/Converters/EnumToStringConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Windows.Data; 7 | 8 | namespace DivinityModManager.Converters 9 | { 10 | class DivinityGameLaunchWindowActionToStringConverter : IValueConverter 11 | { 12 | public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 13 | { 14 | return value.ToString(); 15 | } 16 | 17 | public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 18 | { 19 | return (DivinityGameLaunchWindowAction)Enum.Parse(typeof(DivinityGameLaunchWindowAction), value.ToString(), true); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /GUI/Converters/IntToVisibilityConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Globalization; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using System.Windows; 8 | using System.Windows.Data; 9 | 10 | namespace DivinityModManager.Converters 11 | { 12 | public class IntToVisibilityConverter : IValueConverter 13 | { 14 | public static Visibility FromInt(int v) => v > 0 ? Visibility.Visible : Visibility.Collapsed; 15 | 16 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 17 | { 18 | if(value is int intVal) 19 | { 20 | return intVal == 0 ? Visibility.Collapsed : Visibility.Visible; 21 | } 22 | return Visibility.Visible; 23 | } 24 | 25 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 26 | { 27 | return null; 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /GUI/Converters/ModIsAvailableConverter.cs: -------------------------------------------------------------------------------- 1 | using DivinityModManager.Models; 2 | using DivinityModManager.Views; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Globalization; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | using System.Windows.Data; 10 | 11 | namespace DivinityModManager.Converters 12 | { 13 | public class ModIsAvailableConverter : IValueConverter 14 | { 15 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 16 | { 17 | if(value is IDivinityModData data) 18 | { 19 | return MainWindow.Self?.ViewModel.ModIsAvailable(data); 20 | } 21 | 22 | return false; 23 | } 24 | 25 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 26 | { 27 | return null; 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /GUI/Converters/StringNotEmptyToVisibilityConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Globalization; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using System.Windows; 8 | using System.Windows.Data; 9 | 10 | namespace DivinityModManager.Converters 11 | { 12 | public class StringNotEmptyToVisibilityConverter : IValueConverter 13 | { 14 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 15 | { 16 | bool reverse = false; 17 | if (parameter != null) 18 | { 19 | if (parameter is int reverseInt) 20 | { 21 | reverse = reverseInt > 0; 22 | } 23 | else if (parameter is bool r) 24 | { 25 | reverse = r; 26 | } 27 | } 28 | 29 | if (value is string v) 30 | { 31 | if (!reverse) 32 | { 33 | return !String.IsNullOrWhiteSpace(v) ? Visibility.Visible : Visibility.Collapsed; 34 | } 35 | else 36 | { 37 | return String.IsNullOrWhiteSpace(v) ? Visibility.Visible : Visibility.Collapsed; 38 | } 39 | } 40 | return Visibility.Visible; 41 | } 42 | 43 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 44 | { 45 | return null; 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /GUI/Converters/StringToLinearBrushConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Globalization; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using System.Windows.Data; 8 | using System.Windows.Media; 9 | 10 | namespace DivinityModManager.Converters 11 | { 12 | public class StringToLinearBrushConverter : IValueConverter 13 | { 14 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 15 | { 16 | if (value is string str) 17 | { 18 | var startColor = (Color)ColorConverter.ConvertFromString(str); 19 | var endColor = Color.FromArgb(startColor.A, (byte)(startColor.R * 0.7), (byte)(startColor.G * 0.7), (byte)(startColor.B * 0.7)); 20 | return new LinearGradientBrush(startColor, endColor, 90.0d); 21 | } 22 | return null; 23 | } 24 | 25 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 26 | { 27 | if (value is Uri uri) 28 | { 29 | return uri.OriginalString; 30 | } 31 | return ""; 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /GUI/Converters/StringToSolidBrushConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Globalization; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using System.Windows.Data; 8 | using System.Windows.Media; 9 | 10 | namespace DivinityModManager.Converters 11 | { 12 | public class StringToSolidBrushConverter : IValueConverter 13 | { 14 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 15 | { 16 | if (value is string str) 17 | { 18 | var color = (Color)ColorConverter.ConvertFromString(str); 19 | return new SolidColorBrush(color); 20 | } 21 | return null; 22 | } 23 | 24 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 25 | { 26 | return null; 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /GUI/Converters/StringToUriConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Globalization; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using System.Windows.Data; 8 | 9 | namespace DivinityModManager.Converters 10 | { 11 | public class StringToUriConverter : IValueConverter 12 | { 13 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 14 | { 15 | if(value is string str) 16 | { 17 | Uri result = null; 18 | if (Uri.TryCreate(str, UriKind.RelativeOrAbsolute, out result)) 19 | { 20 | return result; 21 | } 22 | } 23 | return null; 24 | } 25 | 26 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 27 | { 28 | if(value is Uri uri) 29 | { 30 | return uri.OriginalString; 31 | } 32 | return ""; 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /GUI/DivinityModManager.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LaughingLeader-DOS2-Mods/DivinityModManager/befc13677c7a90e4940fcc8dcee381a6236d78dc/GUI/DivinityModManager.ico -------------------------------------------------------------------------------- /GUI/Extensions/DependencyObjectExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Windows; 7 | using System.Windows.Media; 8 | 9 | namespace DivinityModManager 10 | { 11 | public static class DependencyObjectExtensions 12 | { 13 | public static IEnumerable FindVisualChildren(this DependencyObject depObj) where T : DependencyObject 14 | { 15 | if (depObj != null) 16 | { 17 | for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++) 18 | { 19 | DependencyObject child = VisualTreeHelper.GetChild(depObj, i); 20 | if (child != null && child is T) 21 | { 22 | yield return (T)child; 23 | } 24 | 25 | foreach (T childOfChild in FindVisualChildren(child)) 26 | { 27 | yield return childOfChild; 28 | } 29 | } 30 | } 31 | } 32 | public static T FindVisualParent(this DependencyObject depObj) where T : DependencyObject 33 | { 34 | if (depObj != null) 35 | { 36 | //get parent item 37 | DependencyObject parentObject = VisualTreeHelper.GetParent(depObj); 38 | 39 | //we've reached the end of the tree 40 | if (parentObject == null) return null; 41 | 42 | //check if the parent matches the type we're looking for 43 | T parent = parentObject as T; 44 | if (parent != null) 45 | return parent; 46 | else 47 | return FindVisualParent(parentObject); 48 | } 49 | return null; 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /GUI/FodyWeavers.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | -------------------------------------------------------------------------------- /GUI/FodyWeavers.xsd: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. 12 | 13 | 14 | 15 | 16 | A comma-separated list of error codes that can be safely ignored in assembly verification. 17 | 18 | 19 | 20 | 21 | 'false' to turn off automatic generation of the XML Schema file. 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /GUI/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 | [assembly: AssemblyTitle("DivinityModManager")] 8 | [assembly: AssemblyDescription("A mod manager for Divinity: Original Sin 2 - Definitive Edition.")] 9 | #if DEBUG 10 | [assembly: AssemblyConfiguration("Debug")] 11 | #else 12 | [assembly: AssemblyConfiguration("Release")] 13 | #endif 14 | [assembly: AssemblyCompany("LaughingLeader")] 15 | [assembly: AssemblyProduct("Divinity Mod Manager")] 16 | [assembly: AssemblyCopyright("Copyright © 2019")] 17 | [assembly: AssemblyTrademark("")] 18 | [assembly: NeutralResourcesLanguageAttribute("en")] 19 | [assembly: AssemblyCulture("")] 20 | 21 | [assembly: ComVisible(false)] 22 | 23 | [assembly: ThemeInfo( 24 | ResourceDictionaryLocation.None, 25 | ResourceDictionaryLocation.SourceAssembly 26 | )] 27 | 28 | [assembly: AssemblyVersion("1.10.6.1")] -------------------------------------------------------------------------------- /GUI/Properties/Settings.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 DivinityModManager.Properties { 12 | 13 | 14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.8.1.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 | -------------------------------------------------------------------------------- /GUI/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /GUI/Resources/AppFeatures.json: -------------------------------------------------------------------------------- 1 | { 2 | "ScriptExtender": true, 3 | "Workshop": true 4 | } 5 | -------------------------------------------------------------------------------- /GUI/Resources/DefaultPathways.json: -------------------------------------------------------------------------------- 1 | { 2 | "Steam": { 3 | "AppID": "435150", 4 | "Registry_32": "SOFTWARE\\Valve\\Steam\\Apps\\435150", 5 | "Registry_64": "SOFTWARE\\Wow6432Node\\Valve\\Steam\\Apps\\435150", 6 | "RootFolderName": "Divinity Original Sin 2", 7 | "ExePath": "DefEd\\bin\\EoCApp.exe" 8 | }, 9 | "GOG": { 10 | "AppID": "1584823040", 11 | "Registry_32": "SOFTWARE\\GOG.com\\Games\\1584823040", 12 | "Registry_64": "SOFTWARE\\Wow6432Node\\GOG.com\\Games\\1584823040", 13 | "RootFolderName": "Divinity Original Sin 2", 14 | "ExePath": "DefEd\\bin\\EoCApp.exe" 15 | }, 16 | "DocumentsGameFolder": "Larian Studios\\Divinity Original Sin 2 Definitive Edition", 17 | "GameDataFolder": "DefEd\\Data" 18 | } -------------------------------------------------------------------------------- /GUI/Resources/Icons/AddItem_16x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LaughingLeader-DOS2-Mods/DivinityModManager/befc13677c7a90e4940fcc8dcee381a6236d78dc/GUI/Resources/Icons/AddItem_16x.png -------------------------------------------------------------------------------- /GUI/Resources/Icons/AlertBar_Close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LaughingLeader-DOS2-Mods/DivinityModManager/befc13677c7a90e4940fcc8dcee381a6236d78dc/GUI/Resources/Icons/AlertBar_Close.png -------------------------------------------------------------------------------- /GUI/Resources/Icons/AlertBar_Close_Hover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LaughingLeader-DOS2-Mods/DivinityModManager/befc13677c7a90e4940fcc8dcee381a6236d78dc/GUI/Resources/Icons/AlertBar_Close_Hover.png -------------------------------------------------------------------------------- /GUI/Resources/Icons/AlertBar_Danger_16x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LaughingLeader-DOS2-Mods/DivinityModManager/befc13677c7a90e4940fcc8dcee381a6236d78dc/GUI/Resources/Icons/AlertBar_Danger_16x.png -------------------------------------------------------------------------------- /GUI/Resources/Icons/AlertBar_Information_16x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LaughingLeader-DOS2-Mods/DivinityModManager/befc13677c7a90e4940fcc8dcee381a6236d78dc/GUI/Resources/Icons/AlertBar_Information_16x.png -------------------------------------------------------------------------------- /GUI/Resources/Icons/AlertBar_Success_16x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LaughingLeader-DOS2-Mods/DivinityModManager/befc13677c7a90e4940fcc8dcee381a6236d78dc/GUI/Resources/Icons/AlertBar_Success_16x.png -------------------------------------------------------------------------------- /GUI/Resources/Icons/AlertBar_Warning_16x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LaughingLeader-DOS2-Mods/DivinityModManager/befc13677c7a90e4940fcc8dcee381a6236d78dc/GUI/Resources/Icons/AlertBar_Warning_16x.png -------------------------------------------------------------------------------- /GUI/Resources/Icons/BrowserLink_16x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LaughingLeader-DOS2-Mods/DivinityModManager/befc13677c7a90e4940fcc8dcee381a6236d78dc/GUI/Resources/Icons/BrowserLink_16x.png -------------------------------------------------------------------------------- /GUI/Resources/Icons/CopyToClipboard_16x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LaughingLeader-DOS2-Mods/DivinityModManager/befc13677c7a90e4940fcc8dcee381a6236d78dc/GUI/Resources/Icons/CopyToClipboard_16x.png -------------------------------------------------------------------------------- /GUI/Resources/Icons/DefaultIcon_16x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LaughingLeader-DOS2-Mods/DivinityModManager/befc13677c7a90e4940fcc8dcee381a6236d78dc/GUI/Resources/Icons/DefaultIcon_16x.png -------------------------------------------------------------------------------- /GUI/Resources/Icons/DeleteAzureResource_16x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LaughingLeader-DOS2-Mods/DivinityModManager/befc13677c7a90e4940fcc8dcee381a6236d78dc/GUI/Resources/Icons/DeleteAzureResource_16x.png -------------------------------------------------------------------------------- /GUI/Resources/Icons/DivinityEngine2_64x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LaughingLeader-DOS2-Mods/DivinityModManager/befc13677c7a90e4940fcc8dcee381a6236d78dc/GUI/Resources/Icons/DivinityEngine2_64x.png -------------------------------------------------------------------------------- /GUI/Resources/Icons/DivinityEngineGlasses_64x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LaughingLeader-DOS2-Mods/DivinityModManager/befc13677c7a90e4940fcc8dcee381a6236d78dc/GUI/Resources/Icons/DivinityEngineGlasses_64x.png -------------------------------------------------------------------------------- /GUI/Resources/Icons/DivinityOS2_EoCApp_16x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LaughingLeader-DOS2-Mods/DivinityModManager/befc13677c7a90e4940fcc8dcee381a6236d78dc/GUI/Resources/Icons/DivinityOS2_EoCApp_16x.png -------------------------------------------------------------------------------- /GUI/Resources/Icons/ExpandChevronDown_16x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LaughingLeader-DOS2-Mods/DivinityModManager/befc13677c7a90e4940fcc8dcee381a6236d78dc/GUI/Resources/Icons/ExpandChevronDown_16x.png -------------------------------------------------------------------------------- /GUI/Resources/Icons/ExpandChevronDown_lightGray_16x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LaughingLeader-DOS2-Mods/DivinityModManager/befc13677c7a90e4940fcc8dcee381a6236d78dc/GUI/Resources/Icons/ExpandChevronDown_lightGray_16x.png -------------------------------------------------------------------------------- /GUI/Resources/Icons/ExpandChevronUp_16x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LaughingLeader-DOS2-Mods/DivinityModManager/befc13677c7a90e4940fcc8dcee381a6236d78dc/GUI/Resources/Icons/ExpandChevronUp_16x.png -------------------------------------------------------------------------------- /GUI/Resources/Icons/ExpandChevronUp_lightGrey_16x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LaughingLeader-DOS2-Mods/DivinityModManager/befc13677c7a90e4940fcc8dcee381a6236d78dc/GUI/Resources/Icons/ExpandChevronUp_lightGrey_16x.png -------------------------------------------------------------------------------- /GUI/Resources/Icons/ExportData_16x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LaughingLeader-DOS2-Mods/DivinityModManager/befc13677c7a90e4940fcc8dcee381a6236d78dc/GUI/Resources/Icons/ExportData_16x.png -------------------------------------------------------------------------------- /GUI/Resources/Icons/ExportScript_16x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LaughingLeader-DOS2-Mods/DivinityModManager/befc13677c7a90e4940fcc8dcee381a6236d78dc/GUI/Resources/Icons/ExportScript_16x.png -------------------------------------------------------------------------------- /GUI/Resources/Icons/Folder_16x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LaughingLeader-DOS2-Mods/DivinityModManager/befc13677c7a90e4940fcc8dcee381a6236d78dc/GUI/Resources/Icons/Folder_16x.png -------------------------------------------------------------------------------- /GUI/Resources/Icons/Github_16x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LaughingLeader-DOS2-Mods/DivinityModManager/befc13677c7a90e4940fcc8dcee381a6236d78dc/GUI/Resources/Icons/Github_16x.png -------------------------------------------------------------------------------- /GUI/Resources/Icons/Kofi_16x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LaughingLeader-DOS2-Mods/DivinityModManager/befc13677c7a90e4940fcc8dcee381a6236d78dc/GUI/Resources/Icons/Kofi_16x.png -------------------------------------------------------------------------------- /GUI/Resources/Icons/LoadCampaignOrder_16x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LaughingLeader-DOS2-Mods/DivinityModManager/befc13677c7a90e4940fcc8dcee381a6236d78dc/GUI/Resources/Icons/LoadCampaignOrder_16x.png -------------------------------------------------------------------------------- /GUI/Resources/Icons/Log_Normal_32x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LaughingLeader-DOS2-Mods/DivinityModManager/befc13677c7a90e4940fcc8dcee381a6236d78dc/GUI/Resources/Icons/Log_Normal_32x.png -------------------------------------------------------------------------------- /GUI/Resources/Icons/OpenFile_16x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LaughingLeader-DOS2-Mods/DivinityModManager/befc13677c7a90e4940fcc8dcee381a6236d78dc/GUI/Resources/Icons/OpenFile_16x.png -------------------------------------------------------------------------------- /GUI/Resources/Icons/OpenFolder_16x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LaughingLeader-DOS2-Mods/DivinityModManager/befc13677c7a90e4940fcc8dcee381a6236d78dc/GUI/Resources/Icons/OpenFolder_16x.png -------------------------------------------------------------------------------- /GUI/Resources/Icons/Refresh_16x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LaughingLeader-DOS2-Mods/DivinityModManager/befc13677c7a90e4940fcc8dcee381a6236d78dc/GUI/Resources/Icons/Refresh_16x.png -------------------------------------------------------------------------------- /GUI/Resources/Icons/Rename_16x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LaughingLeader-DOS2-Mods/DivinityModManager/befc13677c7a90e4940fcc8dcee381a6236d78dc/GUI/Resources/Icons/Rename_16x.png -------------------------------------------------------------------------------- /GUI/Resources/Icons/SaveAs_16x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LaughingLeader-DOS2-Mods/DivinityModManager/befc13677c7a90e4940fcc8dcee381a6236d78dc/GUI/Resources/Icons/SaveAs_16x.png -------------------------------------------------------------------------------- /GUI/Resources/Icons/SaveGrey_16x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LaughingLeader-DOS2-Mods/DivinityModManager/befc13677c7a90e4940fcc8dcee381a6236d78dc/GUI/Resources/Icons/SaveGrey_16x.png -------------------------------------------------------------------------------- /GUI/Resources/Icons/Save_16x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LaughingLeader-DOS2-Mods/DivinityModManager/befc13677c7a90e4940fcc8dcee381a6236d78dc/GUI/Resources/Icons/Save_16x.png -------------------------------------------------------------------------------- /GUI/Resources/Icons/StatusWarning_16x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LaughingLeader-DOS2-Mods/DivinityModManager/befc13677c7a90e4940fcc8dcee381a6236d78dc/GUI/Resources/Icons/StatusWarning_16x.png -------------------------------------------------------------------------------- /GUI/Resources/Icons/Steam_16x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LaughingLeader-DOS2-Mods/DivinityModManager/befc13677c7a90e4940fcc8dcee381a6236d78dc/GUI/Resources/Icons/Steam_16x.png -------------------------------------------------------------------------------- /GUI/Resources/Icons/ZipFileAs_16x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LaughingLeader-DOS2-Mods/DivinityModManager/befc13677c7a90e4940fcc8dcee381a6236d78dc/GUI/Resources/Icons/ZipFileAs_16x.png -------------------------------------------------------------------------------- /GUI/Resources/Icons/ZipFile_16x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LaughingLeader-DOS2-Mods/DivinityModManager/befc13677c7a90e4940fcc8dcee381a6236d78dc/GUI/Resources/Icons/ZipFile_16x.png -------------------------------------------------------------------------------- /GUI/Themes/Dark.xaml: -------------------------------------------------------------------------------- 1 |  6 | 7 | 8 | 9 | 10 | #495588 11 | #497688 12 | #5e4988 13 | #33000000 14 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /GUI/Themes/DivinityColors.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Windows; 7 | 8 | namespace DivinityModManager.Themes 9 | { 10 | public static class DivinityColors 11 | { 12 | public static ComponentResourceKey TagBackgroundColor => new ComponentResourceKey(typeof(DivinityColors), "TagBackgroundColor"); 13 | public static ComponentResourceKey CustomTagBackgroundColor => new ComponentResourceKey(typeof(DivinityColors), "CustomTagBackgroundColor"); 14 | public static ComponentResourceKey ModeTagBackgroundColor => new ComponentResourceKey(typeof(DivinityColors), "ModeTagBackgroundColor"); 15 | public static ComponentResourceKey ListInactiveColor => new ComponentResourceKey(typeof(DivinityColors), "ListInactiveColor"); 16 | 17 | public static ComponentResourceKey TagBackgroundBrush => new ComponentResourceKey(typeof(DivinityColors), "TagBackgroundBrush"); 18 | public static ComponentResourceKey CustomTagBackgroundBrush => new ComponentResourceKey(typeof(DivinityColors), "CustomTagBackgroundBrush"); 19 | public static ComponentResourceKey ModeTagBackgroundBrush => new ComponentResourceKey(typeof(DivinityColors), "ModeTagBackgroundBrush"); 20 | public static ComponentResourceKey ListInactiveBrush => new ComponentResourceKey(typeof(DivinityColors), "ListInactiveBrush"); 21 | 22 | public static ComponentResourceKey ListInactiveRectangle => new ComponentResourceKey(typeof(DivinityColors), "ListInactiveRectangle"); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /GUI/Themes/Light.xaml: -------------------------------------------------------------------------------- 1 |  7 | 8 | 9 | 10 | 11 | #495588 12 | #497688 13 | #5e4988 14 | #10000000 15 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /GUI/Themes/ResourceAliasHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using System.Windows; 8 | using System.Windows.Markup; 9 | using System.Xaml; 10 | 11 | namespace DivinityModManager.Themes 12 | { 13 | internal class ResourceAliasHelper : MarkupExtension 14 | { 15 | public object ResourceKey { get; set; } 16 | 17 | public override object ProvideValue(IServiceProvider serviceProvider) 18 | { 19 | IRootObjectProvider rootObjectProvider = (IRootObjectProvider)serviceProvider.GetService(typeof(IRootObjectProvider)); 20 | IDictionary dictionary = rootObjectProvider?.RootObject as IDictionary; 21 | return dictionary?[ResourceKey]; 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /GUI/Util/AutomationTooltipPeer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Windows.Automation.Peers; 7 | using System.Windows.Controls; 8 | 9 | namespace DivinityModManager.Util 10 | { 11 | /// 12 | /// This simply disables the automation stuff for the tooltip, to prevent the ElementNotAvailableException from happening. 13 | /// 14 | public class AutomationTooltipPeer : ToolTipAutomationPeer 15 | { 16 | public AutomationTooltipPeer(ToolTip owner) : base(owner) { } 17 | 18 | protected override string GetNameCore() 19 | { 20 | return "AutomationTooltipPeer"; 21 | } 22 | 23 | protected override AutomationControlType GetAutomationControlTypeCore() 24 | { 25 | return AutomationControlType.ToolTip; 26 | } 27 | 28 | protected override List GetChildrenCore() 29 | { 30 | return new List(); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /GUI/Util/BindingHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Windows.Controls; 7 | using System.Windows.Data; 8 | 9 | namespace DivinityModManager.Util 10 | { 11 | public static class BindingHelper 12 | { 13 | public static void CreateCommandBinding(Button button, string vmProperty, object source) 14 | { 15 | Binding binding = new Binding(vmProperty); 16 | binding.Source = source; 17 | binding.Mode = BindingMode.OneWay; 18 | button.SetBinding(Button.CommandProperty, binding); 19 | } 20 | public static void CreateCommandBinding(MenuItem button, string vmProperty, object source) 21 | { 22 | Binding binding = new Binding(vmProperty); 23 | binding.Source = source; 24 | binding.Mode = BindingMode.OneWay; 25 | button.SetBinding(MenuItem.CommandProperty, binding); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /GUI/Util/CustomPropertyResolver.cs: -------------------------------------------------------------------------------- 1 | using ReactiveUI; 2 | 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Reactive.Concurrency; 7 | using System.Reactive.Linq; 8 | using System.Reflection; 9 | using System.Text; 10 | using System.Threading.Tasks; 11 | using System.Windows; 12 | 13 | namespace DivinityModManager.Util 14 | { 15 | public class CustomPropertyResolver : ICreatesObservableForProperty 16 | { 17 | public int GetAffinityForObject(Type type, string propertyName, bool beforeChanged = false) 18 | { 19 | if (!typeof(FrameworkElement).IsAssignableFrom(type)) 20 | return 0; 21 | var fi = type.GetTypeInfo().GetFields(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly) 22 | .FirstOrDefault(x => x.Name == propertyName); 23 | 24 | return fi != null ? 2 /* POCO affinity+1 */ : 0; 25 | } 26 | 27 | public IObservable> GetNotificationForProperty(object sender, System.Linq.Expressions.Expression expression, string propertyName, bool beforeChanged = false, bool suppressWarnings = false) 28 | { 29 | var foo = (FrameworkElement)sender; 30 | var value = sender.GetType().GetProperty(propertyName)?.GetValue(sender, null); 31 | return Observable.Return(new ObservedChange(sender, expression, value), new DispatcherScheduler(foo.Dispatcher)) 32 | .Concat(Observable.Never>()); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /GUI/Util/ElementHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Windows; 7 | using System.Windows.Media; 8 | 9 | namespace DivinityModManager.Util 10 | { 11 | public static class ElementHelper 12 | { 13 | /// 14 | /// Finds a Child of a given item in the visual tree. 15 | /// 16 | /// A direct parent of the queried item. 17 | /// The type of the queried item. 18 | /// x:Name or Name of child. 19 | /// The first parent item that matches the submitted type parameter. 20 | /// If not matching item can be found, 21 | /// a null parent is being returned. 22 | public static T FindChild(DependencyObject parent, string childName) 23 | where T : DependencyObject 24 | { 25 | // Confirm parent and childName are valid. 26 | if (parent == null) return null; 27 | 28 | T foundChild = null; 29 | 30 | int childrenCount = VisualTreeHelper.GetChildrenCount(parent); 31 | for (int i = 0; i < childrenCount; i++) 32 | { 33 | var child = VisualTreeHelper.GetChild(parent, i); 34 | // If the child is not of the request child type child 35 | T childType = child as T; 36 | if (childType == null) 37 | { 38 | // recursively drill down the tree 39 | foundChild = FindChild(child, childName); 40 | 41 | // If the child is found, break so we do not overwrite the found child. 42 | if (foundChild != null) break; 43 | } 44 | else if (!String.IsNullOrEmpty(childName)) 45 | { 46 | var frameworkElement = child as FrameworkElement; 47 | // If the child's name is set for search 48 | if (frameworkElement != null && frameworkElement.Name == childName) 49 | { 50 | // if the child's name is of the request name 51 | foundChild = (T)child; 52 | break; 53 | } 54 | } 55 | else 56 | { 57 | // child element found. 58 | foundChild = (T)child; 59 | break; 60 | } 61 | } 62 | 63 | return foundChild; 64 | } 65 | 66 | public static IEnumerable FindVisualChildren(DependencyObject depObj) where T : DependencyObject 67 | { 68 | if (depObj != null) 69 | { 70 | for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++) 71 | { 72 | DependencyObject child = VisualTreeHelper.GetChild(depObj, i); 73 | if (child != null && child is T) 74 | { 75 | yield return (T)child; 76 | } 77 | 78 | foreach (T childOfChild in FindVisualChildren(child)) 79 | { 80 | yield return childOfChild; 81 | } 82 | } 83 | } 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /GUI/Util/FocusHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Windows; 7 | 8 | namespace DivinityModManager.Util 9 | { 10 | public static class FocusHelper 11 | { 12 | public static bool HasKeyboardFocus(FrameworkElement element) 13 | { 14 | if (element == null) return false; 15 | if(element.IsKeyboardFocused || element.IsKeyboardFocusWithin) 16 | { 17 | return true; 18 | } 19 | foreach(var child in element.FindVisualChildren()) 20 | { 21 | if(HasKeyboardFocus(child)) 22 | { 23 | return true; 24 | } 25 | } 26 | return false; 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /GUI/Util/NativeLibraryHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Runtime.InteropServices; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace DivinityModManager.Util 9 | { 10 | public static class NativeLibraryHelper 11 | { 12 | [DllImport("kernel32.dll")] 13 | public static extern IntPtr LoadLibrary(string dllToLoad); 14 | 15 | [DllImport("kernel32.dll")] 16 | public static extern IntPtr GetProcAddress(IntPtr hModule, string procedureName); 17 | 18 | [DllImport("kernel32.dll")] 19 | public static extern bool FreeLibrary(IntPtr hModule); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /GUI/Util/RxExceptionHandler.cs: -------------------------------------------------------------------------------- 1 | using DivinityModManager.Views; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Diagnostics; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using System.Windows; 9 | 10 | namespace DivinityModManager.Util 11 | { 12 | class RxExceptionHandler : IObserver 13 | { 14 | public static MainWindow view { get; set; } 15 | public void OnNext(Exception value) 16 | { 17 | //if (Debugger.IsAttached) Debugger.Break(); 18 | 19 | var message = $"(OnNext) Exception encountered:\nType: {value.GetType().ToString()}\tMessage: {value.Message}\nSource: {value.Source}\nStackTrace: {value.StackTrace}"; 20 | DivinityApp.Log(message); 21 | MessageBox.Show(message, "Error Encountered", MessageBoxButton.OK, MessageBoxImage.Error); 22 | //MessageBoxResult result = Xceed.Wpf.Toolkit.MessageBox.Show(view, message, "Error Encountered", MessageBoxButton.OK, MessageBoxImage.Error, MessageBoxResult.OK, view.MainWindowMessageBox_OK.Style); 23 | //RxApp.MainThreadScheduler.Schedule(() => { throw value; }); 24 | } 25 | 26 | public void OnError(Exception value) 27 | { 28 | var message = $"(OnError) Exception encountered:\nType: {value.GetType().ToString()}\tMessage: {value.Message}\nSource: {value.Source}\nStackTrace: {value.StackTrace}"; 29 | DivinityApp.Log(message); 30 | //MessageBoxResult result = Xceed.Wpf.Toolkit.MessageBox.Show(view, message, "Error Encountered", MessageBoxButton.OK, MessageBoxImage.Error, MessageBoxResult.OK, view.MainWindowMessageBox_OK.Style); 31 | } 32 | 33 | public void OnCompleted() 34 | { 35 | //if (Debugger.IsAttached) Debugger.Break(); 36 | //RxApp.MainThreadScheduler.Schedule(() => { throw new NotImplementedException(); }); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /GUI/Util/ScreenReader/AlertBarAutomationPeer.cs: -------------------------------------------------------------------------------- 1 | using DivinityModManager.Controls; 2 | 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using System.Windows.Automation.Peers; 9 | using System.Windows.Controls; 10 | 11 | namespace DivinityModManager.Util.ScreenReader 12 | { 13 | public class AlertBarAutomationPeer : FrameworkElementAutomationPeer 14 | { 15 | private AlertBar alertBar; 16 | 17 | public AlertBarAutomationPeer(AlertBar owner) : base(owner) 18 | { 19 | alertBar = owner; 20 | } 21 | protected override string GetNameCore() 22 | { 23 | return alertBar.GetText(); 24 | } 25 | 26 | protected override AutomationControlType GetAutomationControlTypeCore() 27 | { 28 | return AutomationControlType.StatusBar; 29 | } 30 | 31 | protected override List GetChildrenCore() 32 | { 33 | List peers = new List(); 34 | var textElements = alertBar.GetTextElements(); 35 | if(textElements.Count > 0) 36 | { 37 | foreach(var element in textElements) 38 | { 39 | var peer = UIElementAutomationPeer.CreatePeerForElement(element); 40 | if(peer != null) 41 | { 42 | peers.Add(peer); 43 | } 44 | } 45 | } 46 | return peers; 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /GUI/Util/ScreenReader/CachedAutomationPeer.cs: -------------------------------------------------------------------------------- 1 | using DivinityModManager.Views; 2 | 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using System.Windows; 9 | using System.Windows.Automation.Peers; 10 | using System.Windows.Media; 11 | 12 | namespace DivinityModManager.Util.ScreenReader 13 | { 14 | public class CachedAutomationPeer : FrameworkElementAutomationPeer 15 | { 16 | public CachedAutomationPeer(FrameworkElement owner) : base(owner) { } 17 | 18 | private List _cachedAutomationPeers; 19 | 20 | private static AutomationPeer CreatePeerForElementSafe(UIElement element) 21 | { 22 | try 23 | { 24 | return FrameworkElementAutomationPeer.CreatePeerForElement(element); 25 | } 26 | catch(Exception) 27 | { 28 | return null; 29 | } 30 | } 31 | 32 | internal static List GetChildrenRecursively(UIElement uiElement) 33 | { 34 | List children = new List(); 35 | int childrenCount = VisualTreeHelper.GetChildrenCount(uiElement); 36 | 37 | for (int child = 0; child < childrenCount; child++) 38 | { 39 | if (!(VisualTreeHelper.GetChild(uiElement, child) is UIElement element)) 40 | continue; 41 | 42 | AutomationPeer peer = CreatePeerForElementSafe(element); 43 | if (peer != null) 44 | children.Add(peer); 45 | else 46 | { 47 | List returnedChildren = GetChildrenRecursively(element); 48 | if (returnedChildren != null) 49 | children.AddRange(returnedChildren); 50 | } 51 | } 52 | 53 | if (children.Count == 0) 54 | return null; 55 | 56 | return children; 57 | } 58 | 59 | public virtual bool HasNullChildElement() 60 | { 61 | foreach (var c in this.Owner.FindVisualChildren()) 62 | { 63 | if (c == null) 64 | { 65 | return true; 66 | } 67 | } 68 | return false; 69 | } 70 | 71 | public virtual List GetPeersFromElements() 72 | { 73 | return GetChildrenRecursively(Owner); 74 | } 75 | 76 | protected override List GetChildrenCore() 77 | { 78 | if(HasNullChildElement()) 79 | { 80 | return _cachedAutomationPeers; 81 | } 82 | else 83 | { 84 | _cachedAutomationPeers = GetPeersFromElements(); 85 | } 86 | return _cachedAutomationPeers; 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /GUI/Util/ScreenReader/MainWindowAutomationPeer.cs: -------------------------------------------------------------------------------- 1 | using DivinityModManager.Views; 2 | 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using System.Windows; 9 | using System.Windows.Automation.Peers; 10 | 11 | namespace DivinityModManager.Util.ScreenReader 12 | { 13 | public class MainWindowAutomationPeer : CachedAutomationPeer 14 | { 15 | private MainWindow mainWindow; 16 | public MainWindowAutomationPeer(MainWindow owner) : base(owner) 17 | { 18 | mainWindow = owner; 19 | } 20 | 21 | protected override string GetNameCore() 22 | { 23 | if(mainWindow.ViewModel != null) 24 | { 25 | return mainWindow.ViewModel.Title; 26 | } 27 | else 28 | { 29 | return "Divinity Mod Manager"; 30 | } 31 | } 32 | 33 | protected override AutomationControlType GetAutomationControlTypeCore() 34 | { 35 | return AutomationControlType.Window; 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /GUI/Util/ScreenReader/ModEntryGridAutomationPeer.cs: -------------------------------------------------------------------------------- 1 | using DivinityModManager.Controls; 2 | 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using System.Windows.Automation; 9 | using System.Windows.Automation.Peers; 10 | using System.Windows.Controls; 11 | 12 | namespace DivinityModManager.Util.ScreenReader 13 | { 14 | public class ModEntryGridAutomationPeer : CachedAutomationPeer 15 | { 16 | private ModEntryGrid grid; 17 | public ModEntryGridAutomationPeer(ModEntryGrid owner) : base(owner) 18 | { 19 | grid = owner; 20 | } 21 | 22 | protected override string GetNameCore() 23 | { 24 | return grid.GetValue(AutomationProperties.NameProperty) as string ?? string.Empty; 25 | } 26 | 27 | protected override AutomationControlType GetAutomationControlTypeCore() 28 | { 29 | return AutomationControlType.ListItem; 30 | } 31 | 32 | private AutomationPeer _textPeer; 33 | 34 | override public bool HasNullChildElement() 35 | { 36 | var text = ElementHelper.FindChild(grid, "ModNameText"); 37 | if (text != null) 38 | { 39 | var peer = UIElementAutomationPeer.CreatePeerForElement(text); 40 | if (peer != null) 41 | { 42 | _textPeer = peer; 43 | return true; 44 | } 45 | } 46 | return true; 47 | } 48 | 49 | override public List GetPeersFromElements() 50 | { 51 | return new List(1) { _textPeer }; 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /GUI/Util/ScreenReader/ModListViewAutomationPeer.cs: -------------------------------------------------------------------------------- 1 | using DivinityModManager.Controls; 2 | 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using System.Windows; 9 | using System.Windows.Automation; 10 | using System.Windows.Automation.Peers; 11 | using System.Windows.Controls; 12 | 13 | namespace DivinityModManager.Util.ScreenReader 14 | { 15 | public class ModListViewAutomationPeer : CachedAutomationPeer 16 | { 17 | private ModListView _listView; 18 | 19 | public ModListViewAutomationPeer(ModListView owner) : base(owner) 20 | { 21 | _listView = owner; 22 | } 23 | 24 | protected override string GetNameCore() 25 | { 26 | return Owner.GetValue(AutomationProperties.NameProperty) as string ?? string.Empty; 27 | } 28 | 29 | protected override AutomationControlType GetAutomationControlTypeCore() 30 | { 31 | return AutomationControlType.List; 32 | } 33 | 34 | override public bool HasNullChildElement() 35 | { 36 | foreach (var c in _listView.Items) 37 | { 38 | if (c == null) 39 | { 40 | DivinityApp.Log("Found a null entry in ModListViewAutomationPeer"); 41 | return true; 42 | } 43 | } 44 | return false; 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /GUI/Util/ScreenReader/ScreenReaderHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Runtime.InteropServices; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | using DivinityModManager.Util; 9 | using DivinityModManager.Util.ScreenReader; 10 | 11 | namespace DivinityModManager 12 | { 13 | 14 | 15 | public static class ScreenReaderHelper 16 | { 17 | private static bool isLoaded = false; 18 | private static bool loadedLibraries = false; 19 | 20 | private static string[] libraries = new string[3] 21 | { 22 | "nvdaControllerClient64.dll", 23 | "SAAPI64.dll", 24 | "Tolk.dll", 25 | }; 26 | public static void Init() 27 | { 28 | if(!loadedLibraries) 29 | { 30 | /* 31 | * Since the above DLLs are native, they need to be loaded manually from the _Lib directory since DLLImport and LoadLibrary in 32 | * Tolk.dll won't be able to find nvdaControllerClient64.dll inside the _Lib folder. 33 | */ 34 | foreach (var lib in libraries) 35 | { 36 | NativeLibraryHelper.LoadLibrary("_Lib/" + lib); 37 | } 38 | loadedLibraries = true; 39 | } 40 | 41 | if (!isLoaded) 42 | { 43 | Tolk.Load(); 44 | } 45 | isLoaded = Tolk.IsLoaded(); 46 | } 47 | 48 | public static void Speak(string text, bool interrupt = true) 49 | { 50 | if(DivinityApp.IsScreenReaderActive()) 51 | { 52 | if (!isLoaded) 53 | { 54 | Init(); 55 | } 56 | if (isLoaded) 57 | { 58 | if(!Tolk.HasSpeech()) 59 | { 60 | Tolk.TrySAPI(true); 61 | } 62 | Tolk.Output(text, interrupt); 63 | } 64 | //DivinityApp.Log($"Tolk.DetectScreenReader: {Tolk.DetectScreenReader()} Tolk.HasSpeech: {Tolk.HasSpeech()} Tolk.IsLoaded: {Tolk.IsLoaded()}"); 65 | } 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /GUI/Util/ScreenReader/Tolk.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Runtime.InteropServices; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace DivinityModManager.Util.ScreenReader 9 | { 10 | public sealed class Tolk 11 | { 12 | [DllImport("Tolk.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)] 13 | private static extern void Tolk_Load(); 14 | [DllImport("Tolk.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)] 15 | [return: MarshalAs(UnmanagedType.I1)] 16 | private static extern bool Tolk_IsLoaded(); 17 | [DllImport("Tolk.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)] 18 | private static extern void Tolk_Unload(); 19 | [DllImport("Tolk.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)] 20 | private static extern void Tolk_TrySAPI( 21 | [MarshalAs(UnmanagedType.I1)] bool trySAPI); 22 | [DllImport("Tolk.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)] 23 | private static extern void Tolk_PreferSAPI( 24 | [MarshalAs(UnmanagedType.I1)] bool preferSAPI); 25 | [DllImport("Tolk.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)] 26 | private static extern IntPtr Tolk_DetectScreenReader(); 27 | [DllImport("Tolk.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)] 28 | [return: MarshalAs(UnmanagedType.I1)] 29 | private static extern bool Tolk_HasSpeech(); 30 | [DllImport("Tolk.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)] 31 | [return: MarshalAs(UnmanagedType.I1)] 32 | private static extern bool Tolk_HasBraille(); 33 | [DllImport("Tolk.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)] 34 | [return: MarshalAs(UnmanagedType.I1)] 35 | private static extern bool Tolk_Output( 36 | [MarshalAs(UnmanagedType.LPWStr)] String str, 37 | [MarshalAs(UnmanagedType.I1)] bool interrupt); 38 | [DllImport("Tolk.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)] 39 | [return: MarshalAs(UnmanagedType.I1)] 40 | private static extern bool Tolk_Speak( 41 | [MarshalAs(UnmanagedType.LPWStr)] String str, 42 | [MarshalAs(UnmanagedType.I1)] bool interrupt); 43 | [DllImport("Tolk.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)] 44 | [return: MarshalAs(UnmanagedType.I1)] 45 | private static extern bool Tolk_Braille( 46 | [MarshalAs(UnmanagedType.LPWStr)] String str); 47 | [DllImport("Tolk.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)] 48 | [return: MarshalAs(UnmanagedType.I1)] 49 | private static extern bool Tolk_IsSpeaking(); 50 | [DllImport("Tolk.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)] 51 | [return: MarshalAs(UnmanagedType.I1)] 52 | private static extern bool Tolk_Silence(); 53 | 54 | // Prevent construction 55 | private Tolk() { } 56 | 57 | public static void Load() { Tolk_Load(); } 58 | public static bool IsLoaded() { return Tolk_IsLoaded(); } 59 | public static void Unload() { Tolk_Unload(); } 60 | public static void TrySAPI(bool trySAPI) { Tolk_TrySAPI(trySAPI); } 61 | public static void PreferSAPI(bool preferSAPI) { Tolk_PreferSAPI(preferSAPI); } 62 | // Prevent the marshaller from freeing the unmanaged string 63 | public static String DetectScreenReader() { return Marshal.PtrToStringUni(Tolk_DetectScreenReader()); } 64 | public static bool HasSpeech() { return Tolk_HasSpeech(); } 65 | public static bool HasBraille() { return Tolk_HasBraille(); } 66 | public static bool Output(String str, bool interrupt = false) { return Tolk_Output(str, interrupt); } 67 | public static bool Speak(String str, bool interrupt = false) { return Tolk_Speak(str, interrupt); } 68 | public static bool Braille(String str) { return Tolk_Braille(str); } 69 | public static bool IsSpeaking() { return Tolk_IsSpeaking(); } 70 | public static bool Silence() { return Tolk_Silence(); } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /GUI/ViewModels/MainWindowExceptionHandler.cs: -------------------------------------------------------------------------------- 1 | using ReactiveUI; 2 | using System.Reactive.Concurrency; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Diagnostics; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | namespace DivinityModManager.ViewModels 11 | { 12 | public class MainWindowExceptionHandler : IObserver 13 | { 14 | private MainWindowViewModel _viewModel; 15 | 16 | public MainWindowExceptionHandler(MainWindowViewModel vm) 17 | { 18 | _viewModel = vm; 19 | } 20 | 21 | public void OnNext(Exception value) 22 | { 23 | DivinityApp.Log($"Error: ({value.GetType().ToString()}: {value.Message}\n{value.StackTrace}"); 24 | //if (Debugger.IsAttached) Debugger.Break(); 25 | //RxApp.MainThreadScheduler.Schedule(() => { throw value; }); 26 | } 27 | 28 | public void OnError(Exception error) 29 | { 30 | DivinityApp.Log($"Error: ({error.GetType().ToString()}: {error.Message}\n{error.StackTrace}"); 31 | if (Debugger.IsAttached) Debugger.Break(); 32 | 33 | RxApp.MainThreadScheduler.Schedule(() => { 34 | if(_viewModel.MainProgressIsActive) 35 | { 36 | _viewModel.MainProgressIsActive = false; 37 | } 38 | _viewModel.View.AlertBar.SetDangerAlert(error.Message); 39 | //throw error; 40 | }); 41 | } 42 | 43 | public void OnCompleted() 44 | { 45 | if (Debugger.IsAttached) Debugger.Break(); 46 | //RxApp.MainThreadScheduler.Schedule(() => { throw new NotImplementedException(); }); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /GUI/ViewModels/ModListDragHandler.cs: -------------------------------------------------------------------------------- 1 | using DivinityModManager.Models; 2 | 3 | using GongSolutions.Wpf.DragDrop; 4 | 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Linq; 8 | using System.Reactive; 9 | using System.Reactive.Linq; 10 | using System.Windows; 11 | using System.Windows.Data; 12 | using System.Windows.Input; 13 | 14 | namespace DivinityModManager.ViewModels 15 | { 16 | public class ManualDragInfo : IDragInfo 17 | { 18 | public DataFormat DataFormat { get; set; } 19 | public object Data { get; set; } 20 | public Point DragStartPosition { get; } 21 | public Point PositionInDraggedItem { get; } 22 | public DragDropEffects Effects { get; set; } 23 | public MouseButton MouseButton { get; set; } 24 | public System.Collections.IEnumerable SourceCollection { get; set; } 25 | public int SourceIndex { get; set; } 26 | public object SourceItem { get; set; } 27 | public System.Collections.IEnumerable SourceItems { get; set; } 28 | public CollectionViewGroup SourceGroup { get; set; } 29 | public UIElement VisualSource { get; set; } 30 | public UIElement VisualSourceItem { get; set; } 31 | public FlowDirection VisualSourceFlowDirection { get; set; } 32 | public object DataObject { get; set; } 33 | public Func DragDropHandler { get; set; } 34 | public DragDropKeyStates DragDropCopyKeyState { get; set; } 35 | } 36 | 37 | public class ModListDragHandler : DefaultDragHandler 38 | { 39 | private MainWindowViewModel _viewModel; 40 | 41 | public ModListDragHandler(MainWindowViewModel vm) : base() 42 | { 43 | _viewModel = vm; 44 | } 45 | 46 | public override void StartDrag(IDragInfo dragInfo) 47 | { 48 | if (dragInfo != null) 49 | { 50 | dragInfo.Data = null; 51 | if (dragInfo.SourceCollection == _viewModel.ActiveMods) 52 | { 53 | var selected = _viewModel.ActiveMods.Where(x => x.IsSelected && x.Visibility == Visibility.Visible); 54 | dragInfo.Data = selected; 55 | } 56 | else if (dragInfo.SourceCollection == _viewModel.InactiveMods) 57 | { 58 | var selected = _viewModel.InactiveMods.Where(x => x.IsSelected && x.Visibility == Visibility.Visible && x.CanDrag); 59 | dragInfo.Data = selected; 60 | } 61 | if(dragInfo.Data != null) 62 | { 63 | _viewModel.IsDragging = true; 64 | } 65 | dragInfo.Effects = dragInfo.Data != null ? DragDropEffects.Copy | DragDropEffects.Move : DragDropEffects.None; 66 | } 67 | } 68 | 69 | public override void DragDropOperationFinished(DragDropEffects operationResult, IDragInfo dragInfo) 70 | { 71 | base.DragDropOperationFinished(operationResult, dragInfo); 72 | _viewModel.IsDragging = false; 73 | } 74 | 75 | public override bool CanStartDrag(IDragInfo dragInfo) 76 | { 77 | if(_viewModel.IsLoadingOrder || _viewModel.IsRefreshing) 78 | { 79 | return false; 80 | } 81 | if (dragInfo.Data is ISelectable d && !d.CanDrag) 82 | { 83 | return false; 84 | } 85 | else if (dragInfo.Data is IEnumerable modData) 86 | { 87 | if (modData.All(x => !x.CanDrag)) 88 | { 89 | return false; 90 | } 91 | } 92 | return true; 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /GUI/Views/AboutWindow.xaml: -------------------------------------------------------------------------------- 1 |  16 | 17 |