diff --git a/.editorconfig b/.editorconfig
index ea0c3b57..b947be64 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -1,22 +1,167 @@
-[*.cs]
+; This file is for unifying the coding style for different editors and IDEs.
+; More information at http://editorconfig.org
-# CS8618: Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
-dotnet_diagnostic.CS8618.severity = silent
+# This file is the top-most EditorConfig file
+root = true
-dotnet_diagnostic.CS8604.severity = silent
+##########################################
+# Common Settings
+##########################################
-dotnet_diagnostic.CS8622.severity = silent
+[*]
+indent_style = space
+end_of_line = crlf
+trim_trailing_whitespace = true
+insert_final_newline = true
+charset = utf-8
-dotnet_diagnostic.CS8625.severity = silent
+##########################################
+# File Extension Settings
+##########################################
-# CS8602: Dereference of a possibly null reference.
-dotnet_diagnostic.CS8602.severity = silent
+[*.{yml,yaml}]
+indent_size = 2
-# CS8603: Possible null reference return.
-dotnet_diagnostic.CS8603.severity = silent
+[.vsconfig]
+indent_size = 2
+end_of_line = lf
-# CS0252: Possible unintended reference comparison; left hand side needs cast
-dotnet_diagnostic.CS0252.severity = silent
+[*.sln]
+indent_style = tab
+indent_size = 2
-# CS8600: Converting null literal or possible null value to non-nullable type.
-dotnet_diagnostic.CS8600.severity = silent
+[*.{csproj,proj,projitems,shproj}]
+indent_size = 2
+
+[*.{json,slnf}]
+indent_size = 2
+end_of_line = lf
+
+[*.{props,targets}]
+indent_size = 2
+
+[*.xaml]
+indent_size = 2
+charset = utf-8-bom
+
+[*.xml]
+indent_size = 2
+end_of_line = lf
+
+[*.plist]
+indent_size = 2
+indent_style = tab
+end_of_line = lf
+
+[*.manifest]
+indent_size = 2
+
+[*.appxmanifest]
+indent_size = 2
+
+[*.{json,css,webmanifest}]
+indent_size = 2
+end_of_line = lf
+
+[web.config]
+indent_size = 2
+end_of_line = lf
+
+[*.sh]
+indent_size = 2
+end_of_line = lf
+
+[*.cs]
+# EOL should be normalized by Git. See https://github.com/dotnet/format/issues/1099
+end_of_line = unset
+
+# See https://github.com/dotnet/roslyn/issues/20356#issuecomment-310143926
+trim_trailing_whitespace = false
+
+tab_width = 4
+indent_size = 4
+
+# Sort using and Import directives with System.* appearing first
+dotnet_sort_system_directives_first = true
+
+# Avoid "this." and "Me." if not necessary
+dotnet_style_qualification_for_field = false:suggestion
+dotnet_style_qualification_for_property = false:suggestion
+dotnet_style_qualification_for_method = false:suggestion
+dotnet_style_qualification_for_event = false:suggestion
+
+#### Naming styles ####
+
+# Naming rules
+
+dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion
+dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface
+dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i
+
+dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion
+dotnet_naming_rule.types_should_be_pascal_case.symbols = types
+dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case
+
+dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion
+dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members
+dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case
+
+# Symbol specifications
+
+dotnet_naming_symbols.interface.applicable_kinds = interface
+dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
+dotnet_naming_symbols.interface.required_modifiers =
+
+dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
+dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
+dotnet_naming_symbols.types.required_modifiers =
+
+dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
+dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
+dotnet_naming_symbols.non_field_members.required_modifiers =
+
+# Naming styles
+
+dotnet_naming_style.begins_with_i.required_prefix = I
+dotnet_naming_style.begins_with_i.required_suffix =
+dotnet_naming_style.begins_with_i.word_separator =
+dotnet_naming_style.begins_with_i.capitalization = pascal_case
+
+dotnet_naming_style.pascal_case.required_prefix =
+dotnet_naming_style.pascal_case.required_suffix =
+dotnet_naming_style.pascal_case.word_separator =
+dotnet_naming_style.pascal_case.capitalization = pascal_case
+
+dotnet_naming_style.pascal_case.required_prefix =
+dotnet_naming_style.pascal_case.required_suffix =
+dotnet_naming_style.pascal_case.word_separator =
+dotnet_naming_style.pascal_case.capitalization = pascal_case
+dotnet_style_operator_placement_when_wrapping = beginning_of_line
+dotnet_style_coalesce_expression = true:suggestion
+dotnet_style_null_propagation = true:suggestion
+dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion
+dotnet_style_prefer_auto_properties = true:silent
+dotnet_style_object_initializer = true:suggestion
+dotnet_style_collection_initializer = true:suggestion
+dotnet_style_prefer_simplified_boolean_expressions = true:suggestion
+dotnet_style_prefer_conditional_expression_over_assignment = true:silent
+dotnet_style_prefer_conditional_expression_over_return = true:silent
+dotnet_style_explicit_tuple_names = true:suggestion
+dotnet_style_prefer_inferred_tuple_names = true:suggestion
+
+csharp_indent_labels = one_less_than_current
+csharp_using_directive_placement = outside_namespace:silent
+csharp_prefer_simple_using_statement = true:suggestion
+csharp_prefer_braces = true:silent
+csharp_style_namespace_declarations = file_scoped:warning
+csharp_style_prefer_method_group_conversion = true:silent
+csharp_style_prefer_top_level_statements = true:silent
+csharp_style_prefer_primary_constructors = true:suggestion
+csharp_style_expression_bodied_methods = false:silent
+csharp_style_expression_bodied_constructors = false:silent
+csharp_style_expression_bodied_operators = false:silent
+csharp_style_expression_bodied_properties = true:silent
+csharp_style_expression_bodied_indexers = true:silent
+csharp_style_expression_bodied_accessors = true:silent
+csharp_style_expression_bodied_lambdas = true:silent
+csharp_style_expression_bodied_local_functions = false:silent
diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml
index 76877e92..f0774448 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.yml
+++ b/.github/ISSUE_TEMPLATE/bug_report.yml
@@ -1,52 +1,83 @@
-name: 🐛 Bug Report
-description: Create a bug report to help improve Emerald
-labels: [bug]
-title: "🐛 "
+name: 🐞 Bug
+title: '[BUG]
'
+description: File a bug report
+labels: ["bug", "to verify"]
+assignees: []
body:
- - type: checkboxes
+ - type: markdown
attributes:
- label: ⏱️ Before you start...
- options:
- - label: Are you on the latest version of Emerald? You might be using an old version.
- required: true
- - label: Have you checked whether or not a similar issue has already been reported?
- required: true
- - type: textarea
- attributes:
- label: 🪟 What build of Windows is this issue present? (optional)
- description: Windows key + R > winver.exe
- placeholder: build 25174.1010
+ value: |
+ Thanks for taking the time to fill out this bug report! Please make sure to add as much detail as you can.
- type: textarea
+ id: description
attributes:
- label: 🔢 What version of Emerald are you on?
- description: Go to Emerald > Settings > About
+ label: Description
+ description: Please give a detailed description of the issue that you're seeing. You can add screenshots and videos as well.
+ placeholder: Tell us what you see!
validations:
required: true
- type: textarea
+ id: repro-steps
attributes:
- label: 📄 Description
- description: A clear and concise description of what the bug is.
+ label: Steps to Reproduce
+ description: Describe all the steps we need to take to show the behavior that you have observed. Also, include what you expected to happen and what did actually happen.
+ placeholder: |
+ 1. Create a File > New App
+ 2. Add a `Button` like so: ``
+ 3. Click the added button and observe the bug 🐞
+
+ Expected outcome: a bug was added
+ Actual outcome: a ladybug appeared
validations:
required: true
- - type: textarea
+ - type: dropdown
+ id: platform-with-bug
attributes:
- label: 🪜 Steps To Reproduce
- description: Steps to reproduce the behavior.
- placeholder: |
- 1. Go to '....'
- 2. Click on '....'
- 3. Scroll down to '....'
- 4. See the error
+ label: Platform with bug
+ description: What Platform is this bug affecting?
+ options:
+ - Core
+ - Android
+ - iOS
+ - MacCatalyst
+ - Windows
+ - Skia
+ - WebAssembly
+ - Other
validations:
- required: false
- - type: textarea
+ required: true
+
+ - type: dropdown
+ id: platforms-affected
attributes:
- label: 🤔 Expected behavior
- description: A clear and concise description of what you expected to happen.
+ label: Affected platforms
+ description: Select all or any platform that you see this issue on. This helps us determine if it's something platform-specific or in the core. If you were only able to test on 1 platform, please check the last option to inform us about that.
+ multiple: true
+ options:
+ - Android
+ - iOS
+ - MacOS
+ - MacCatalyst
+ - Windows
+ - GTK
+ - Linux Framebuffer
+ - WPF
+ - Skia Desktop
+ - WebAssembly
+ - Other
+ - I was *not* able test on other platforms
validations:
required: true
+
+ - type: textarea
+ id: workaround
+ attributes:
+ label: Did you find any workaround?
+ description: Did you find any workaround for this issue? This can unblock other people while waiting for this issue to be resolved or even give us a hint on how to fix this.
+
- type: textarea
+ id: logs
attributes:
- label: 📸 Assets
- description: |
- A list of assets (errors, screenshots) relevant to the bug.
+ label: Relevant log output
+ description: Please copy and paste any relevant log output. This will be auomatically formatted into code, so no need for back ticks.
+ render: shell
diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml
index 96fd262d..47b3fe78 100644
--- a/.github/ISSUE_TEMPLATE/feature_request.yml
+++ b/.github/ISSUE_TEMPLATE/feature_request.yml
@@ -1,44 +1,39 @@
-name: ✨ Feature Request
-description: Request a new feature for Emerald
-labels: [feature request]
-title: "✨ "
+name: Feature Request
+description: Suggest an idea for this project
+labels: ["enhancement"]
+assignees: []
body:
- type: markdown
attributes:
value: |
- ##### ⏱️ Before you start...
- - [ ] Have you checked whether or not a similar feature request has already been reported?
+ Fill in this template with some detail. A detailed description, perhaps supported with some screenshots, mockups, and a (public) API design in pseudo-code.
- type: textarea
+ id: description
attributes:
- label: 📄 Description
- description: A clear and concise description of what your idea is. Include things like possible use cases, drawbacks, etc.
+ label: Description
+ description: Please give us a detailed description of the feature that you envision. Focus on _what_ this feature does, over the _why_ you want this feature. What would be the end-result when implemented? Feel free to add any diagrams, (mocked up) screenshots, or other materials that support your story.
+ placeholder: I would love to have a button that I can make shiny. This is something that is supported on all the underlaying platforms.
validations:
required: true
- type: textarea
+ id: api-changes
attributes:
- label: 🗃️ Alternative solutions
- description: Describe more ways this idea could be implemented.
- validations:
- required: false
- - type: textarea
- attributes:
- label: ✅ Tasks
- description: Give an overview of all the specific things you would like to be changed or implemented.
- value: |
- ```[tasklist]
- ### High Priority
- - [ ] Something
- - [ ] Another thing
- - [ ] https://github.com/link/to/an/issue
- ```
- ```[tasklist]
- ### Nice to have
- - [ ] Something
- - [ ] Another thing
- - [ ] https://github.com/link/to/an/issue
+ label: API Changes
+ description: Include a list of all API changes, additions, subtractions as would be required by your proposal. These APIs should be considered placeholders, so the naming is not as important as getting the concepts correct. If possible you should include some example (pseudo-)code of usage of your new API.
+ placeholder: |
+ ```csharp
+ var button = new Button ();
+ button.MakeShiny = true; // new API
```
+
+ The MakeShiny API works even if the button is already visible.
+ validations:
+ required: true
- type: textarea
+ id: use-case
attributes:
- label: 📸 Assets
- description: |
- A list of assets (screenshots, mockups) relevant to this feature request.
+ label: Intended Use-Case
+ description: Provide a detailed example of where your proposal would be used and for what purpose. Focus on _why_ you want this feature instead of _what_ the feature does.
+ placeholder: I have a situation where I would really want a shiny button to make it stand out from the rest of the plain and boring buttons.
+ validations:
+ required: true
\ No newline at end of file
diff --git a/.github/ISSUE_TEMPLATE/sample/bug_report.yml b/.github/ISSUE_TEMPLATE/sample/bug_report.yml
new file mode 100644
index 00000000..f0774448
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/sample/bug_report.yml
@@ -0,0 +1,83 @@
+name: 🐞 Bug
+title: '[BUG] '
+description: File a bug report
+labels: ["bug", "to verify"]
+assignees: []
+body:
+ - type: markdown
+ attributes:
+ value: |
+ Thanks for taking the time to fill out this bug report! Please make sure to add as much detail as you can.
+ - type: textarea
+ id: description
+ attributes:
+ label: Description
+ description: Please give a detailed description of the issue that you're seeing. You can add screenshots and videos as well.
+ placeholder: Tell us what you see!
+ validations:
+ required: true
+ - type: textarea
+ id: repro-steps
+ attributes:
+ label: Steps to Reproduce
+ description: Describe all the steps we need to take to show the behavior that you have observed. Also, include what you expected to happen and what did actually happen.
+ placeholder: |
+ 1. Create a File > New App
+ 2. Add a `Button` like so: ``
+ 3. Click the added button and observe the bug 🐞
+
+ Expected outcome: a bug was added
+ Actual outcome: a ladybug appeared
+ validations:
+ required: true
+ - type: dropdown
+ id: platform-with-bug
+ attributes:
+ label: Platform with bug
+ description: What Platform is this bug affecting?
+ options:
+ - Core
+ - Android
+ - iOS
+ - MacCatalyst
+ - Windows
+ - Skia
+ - WebAssembly
+ - Other
+ validations:
+ required: true
+
+ - type: dropdown
+ id: platforms-affected
+ attributes:
+ label: Affected platforms
+ description: Select all or any platform that you see this issue on. This helps us determine if it's something platform-specific or in the core. If you were only able to test on 1 platform, please check the last option to inform us about that.
+ multiple: true
+ options:
+ - Android
+ - iOS
+ - MacOS
+ - MacCatalyst
+ - Windows
+ - GTK
+ - Linux Framebuffer
+ - WPF
+ - Skia Desktop
+ - WebAssembly
+ - Other
+ - I was *not* able test on other platforms
+ validations:
+ required: true
+
+ - type: textarea
+ id: workaround
+ attributes:
+ label: Did you find any workaround?
+ description: Did you find any workaround for this issue? This can unblock other people while waiting for this issue to be resolved or even give us a hint on how to fix this.
+
+ - type: textarea
+ id: logs
+ attributes:
+ label: Relevant log output
+ description: Please copy and paste any relevant log output. This will be auomatically formatted into code, so no need for back ticks.
+ render: shell
diff --git a/.github/ISSUE_TEMPLATE/sample/config.yml b/.github/ISSUE_TEMPLATE/sample/config.yml
new file mode 100644
index 00000000..3ba13e0c
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/sample/config.yml
@@ -0,0 +1 @@
+blank_issues_enabled: false
diff --git a/.github/ISSUE_TEMPLATE/sample/feature_request.yml b/.github/ISSUE_TEMPLATE/sample/feature_request.yml
new file mode 100644
index 00000000..47b3fe78
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/sample/feature_request.yml
@@ -0,0 +1,39 @@
+name: Feature Request
+description: Suggest an idea for this project
+labels: ["enhancement"]
+assignees: []
+body:
+ - type: markdown
+ attributes:
+ value: |
+ Fill in this template with some detail. A detailed description, perhaps supported with some screenshots, mockups, and a (public) API design in pseudo-code.
+ - type: textarea
+ id: description
+ attributes:
+ label: Description
+ description: Please give us a detailed description of the feature that you envision. Focus on _what_ this feature does, over the _why_ you want this feature. What would be the end-result when implemented? Feel free to add any diagrams, (mocked up) screenshots, or other materials that support your story.
+ placeholder: I would love to have a button that I can make shiny. This is something that is supported on all the underlaying platforms.
+ validations:
+ required: true
+ - type: textarea
+ id: api-changes
+ attributes:
+ label: API Changes
+ description: Include a list of all API changes, additions, subtractions as would be required by your proposal. These APIs should be considered placeholders, so the naming is not as important as getting the concepts correct. If possible you should include some example (pseudo-)code of usage of your new API.
+ placeholder: |
+ ```csharp
+ var button = new Button ();
+ button.MakeShiny = true; // new API
+ ```
+
+ The MakeShiny API works even if the button is already visible.
+ validations:
+ required: true
+ - type: textarea
+ id: use-case
+ attributes:
+ label: Intended Use-Case
+ description: Provide a detailed example of where your proposal would be used and for what purpose. Focus on _why_ you want this feature instead of _what_ the feature does.
+ placeholder: I have a situation where I would really want a shiny button to make it stand out from the rest of the plain and boring buttons.
+ validations:
+ required: true
\ No newline at end of file
diff --git a/.github/ISSUE_TEMPLATE/sample/spec.yml b/.github/ISSUE_TEMPLATE/sample/spec.yml
new file mode 100644
index 00000000..6abc3944
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/sample/spec.yml
@@ -0,0 +1,80 @@
+name: Spec
+description: An official specification for enhancements.
+labels: ["enhancement"]
+assignees: []
+body:
+ - type: markdown
+ attributes:
+ value: |
+ Thank you for taking the time to provide us with a detailed specification of your idea.
+
+ In a spec you will give a detailed and complete representation of the (public) APIs that are implemented as part of this change. Additionally please think about backwards compatibility, breaking changes, supported platforms and the difficulty.
+ - type: textarea
+ id: description
+ attributes:
+ label: Description
+ description: Provide a concise description of the feature and the motivation for adding it. This can be a modified version from the feature request prior to this.
+ validations:
+ required: true
+ - type: textarea
+ id: api-changes
+ attributes:
+ label: (Public) API Changes
+ description: Include a complete list of all API changes, additions, subtractions as would be required by your proposal.
+ value: |
+ # [ class ]
+
+ ## Properties
+
+ | API | Description |
+ | ------------- | ------------- |
+ | [name] | Gets or sets [description]. |
+
+ ## Events
+
+ | API | Description |
+ | ------------- | ------------- |
+ | [name] | [API documentation/description] |
+ validations:
+ required: true
+ - type: textarea
+ id: usage-scenarios
+ attributes:
+ label: Usage Scenarios
+ description: Give us a couple of scenarios that demonstrate how developers would consume the above APIs.
+ placeholder: |
+ # C# Example
+ ```csharp
+ var thing = new MyNewControl();
+ thing.BeAwesome = true;
+ thing.Color = Colors.Cornsilk;
+ ```
+
+ # XAML Example
+ ```xaml
+
+ ```
+ validations:
+ required: true
+ - type: textarea
+ id: backwards-compatibility
+ attributes:
+ label: Backward Compatibility
+ description: Please describe here anything in terms of backwards compatibility. Will there be breaking changes? Do we need to update dependencies to support this? What are the minimum supported API/OS levels? And lastly, are there any platforms that can't support this and why?
+ placeholder: |
+ Minimum API levels?
+ Breaking changes?
+ Unsupported platforms?
+ validations:
+ required: true
+ - type: dropdown
+ id: difficulty
+ attributes:
+ label: Difficulty
+ description: What do you feel will be the difficulty of this change all things considering? No exact science, just your gut feeling.
+ options:
+ - Low
+ - Medium
+ - High
+ validations:
+ required: true
\ No newline at end of file
diff --git a/.github/Install-WindowsSdkISO.ps1 b/.github/Install-WindowsSdkISO.ps1
new file mode 100644
index 00000000..f4964d23
--- /dev/null
+++ b/.github/Install-WindowsSdkISO.ps1
@@ -0,0 +1,314 @@
+[CmdletBinding()]
+param([Parameter(Mandatory=$true)]
+ [string]$buildNumber)
+
+# Ensure the error action preference is set to the default for PowerShell3, 'Stop'
+$ErrorActionPreference = 'Stop'
+
+# Constants
+$WindowsSDKOptions = @("OptionId.UWPCpp")
+$WindowsSDKRegPath = "HKLM:\Software\Microsoft\Windows Kits\Installed Roots"
+$WindowsSDKRegRootKey = "KitsRoot10"
+$WindowsSDKVersion = "10.0.$buildNumber.0"
+$WindowsSDKInstalledRegPath = "$WindowsSDKRegPath\$WindowsSDKVersion\Installed Options"
+$StrongNameRegPath = "HKLM:\SOFTWARE\Microsoft\StrongName\Verification"
+$PublicKeyTokens = @("31bf3856ad364e35")
+
+function Download-File
+{
+ param ([string] $outDir,
+ [string] $downloadUrl,
+ [string] $downloadName)
+
+ $downloadPath = Join-Path $outDir "$downloadName.download"
+ $downloadDest = Join-Path $outDir $downloadName
+ $downloadDestTemp = Join-Path $outDir "$downloadName.tmp"
+
+ Write-Host -NoNewline "Downloading $downloadName..."
+
+ try
+ {
+ $webclient = new-object System.Net.WebClient
+ $webclient.DownloadFile($downloadUrl, $downloadPath)
+ }
+ catch [System.Net.WebException]
+ {
+ Write-Host
+ Write-Warning "Failed to fetch updated file from $downloadUrl"
+ if (!(Test-Path $downloadDest))
+ {
+ throw "$downloadName was not found at $downloadDest"
+ }
+ else
+ {
+ Write-Warning "$downloadName may be out of date"
+ }
+ }
+
+ Unblock-File $downloadPath
+
+ $downloadDestTemp = $downloadPath;
+
+ # Delete and rename to final dest
+ if (Test-Path -PathType Container $downloadDest)
+ {
+ [System.IO.Directory]::Delete($downloadDest, $true)
+ }
+
+ Move-Item -Force $downloadDestTemp $downloadDest
+ Write-Host "Done"
+
+ return $downloadDest
+}
+
+function Get-ISODriveLetter
+{
+ param ([string] $isoPath)
+
+ $diskImage = Get-DiskImage -ImagePath $isoPath
+ if ($diskImage)
+ {
+ $volume = Get-Volume -DiskImage $diskImage
+
+ if ($volume)
+ {
+ $driveLetter = $volume.DriveLetter
+ if ($driveLetter)
+ {
+ $driveLetter += ":"
+ return $driveLetter
+ }
+ }
+ }
+
+ return $null
+}
+
+function Mount-ISO
+{
+ param ([string] $isoPath)
+
+ # Check if image is already mounted
+ $isoDrive = Get-ISODriveLetter $isoPath
+
+ if (!$isoDrive)
+ {
+ Mount-DiskImage -ImagePath $isoPath -StorageType ISO | Out-Null
+ }
+
+ $isoDrive = Get-ISODriveLetter $isoPath
+ Write-Verbose "$isoPath mounted to ${isoDrive}:"
+}
+
+function Dismount-ISO
+{
+ param ([string] $isoPath)
+
+ $isoDrive = (Get-DiskImage -ImagePath $isoPath | Get-Volume).DriveLetter
+
+ if ($isoDrive)
+ {
+ Write-Verbose "$isoPath dismounted"
+ Dismount-DiskImage -ImagePath $isoPath | Out-Null
+ }
+}
+
+function Disable-StrongName
+{
+ param ([string] $publicKeyToken = "*")
+
+ reg ADD "HKLM\SOFTWARE\Microsoft\StrongName\Verification\*,$publicKeyToken" /f | Out-Null
+ if ($env:PROCESSOR_ARCHITECTURE -eq "AMD64")
+ {
+ reg ADD "HKLM\SOFTWARE\Wow6432Node\Microsoft\StrongName\Verification\*,$publicKeyToken" /f | Out-Null
+ }
+}
+
+function Test-Admin
+{
+ $identity = [Security.Principal.WindowsIdentity]::GetCurrent()
+ $principal = New-Object Security.Principal.WindowsPrincipal $identity
+ $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
+}
+
+function Test-RegistryPathAndValue
+{
+ param (
+ [parameter(Mandatory=$true)]
+ [ValidateNotNullOrEmpty()]
+ [string] $path,
+ [parameter(Mandatory=$true)]
+ [ValidateNotNullOrEmpty()]
+ [string] $value)
+
+ try
+ {
+ if (Test-Path $path)
+ {
+ Get-ItemProperty -Path $path | Select-Object -ExpandProperty $value -ErrorAction Stop | Out-Null
+ return $true
+ }
+ }
+ catch
+ {
+ }
+
+ return $false
+}
+
+function Test-InstallWindowsSDK
+{
+ $retval = $true
+
+ if (Test-RegistryPathAndValue -Path $WindowsSDKRegPath -Value $WindowsSDKRegRootKey)
+ {
+ # A Windows SDK is installed
+ # Is an SDK of our version installed with the options we need?
+ if (Test-RegistryPathAndValue -Path $WindowsSDKInstalledRegPath -Value "$WindowsSDKOptions")
+ {
+ # It appears we have what we need. Double check the disk
+ $sdkRoot = Get-ItemProperty -Path $WindowsSDKRegPath | Select-Object -ExpandProperty $WindowsSDKRegRootKey
+ if ($sdkRoot)
+ {
+ if (Test-Path $sdkRoot)
+ {
+ $refPath = Join-Path $sdkRoot "References\$WindowsSDKVersion"
+ if (Test-Path $refPath)
+ {
+ $umdPath = Join-Path $sdkRoot "UnionMetadata\$WindowsSDKVersion"
+ if (Test-Path $umdPath)
+ {
+ # Pretty sure we have what we need
+ $retval = $false
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return $retval
+}
+
+function Test-InstallStrongNameHijack
+{
+ foreach($publicKeyToken in $PublicKeyTokens)
+ {
+ $key = "$StrongNameRegPath\*,$publicKeyToken"
+ if (!(Test-Path $key))
+ {
+ return $true
+ }
+ }
+
+ return $false
+}
+
+Write-Host -NoNewline "Checking for installed Windows SDK $WindowsSDKVersion..."
+$InstallWindowsSDK = Test-InstallWindowsSDK
+if ($InstallWindowsSDK)
+{
+ Write-Host "Installation required"
+}
+else
+{
+ Write-Host "INSTALLED"
+}
+
+$StrongNameHijack = Test-InstallStrongNameHijack
+Write-Host -NoNewline "Checking if StrongName bypass required..."
+
+if ($StrongNameHijack)
+{
+ Write-Host "REQUIRED"
+}
+else
+{
+ Write-Host "Done"
+}
+
+if ($StrongNameHijack -or $InstallWindowsSDK)
+{
+ if (!(Test-Admin))
+ {
+ Write-Host
+ throw "ERROR: Elevation required"
+ }
+}
+
+if ($InstallWindowsSDK)
+{
+ # Static(ish) link for Windows SDK
+ # Note: there is a delay from Windows SDK announcements to availability via the static link
+ $uri = "https://go.microsoft.com/fwlink/?prd=11966&pver=1.0&plcid=0x409&clcid=0x409&ar=Flight&sar=Sdsurl&o1=$buildNumber"
+
+ if($buildNumber -eq 18362)
+ {
+ # Workaround for removed SDK
+ $uri = "https://go.microsoft.com/fwlink/?linkid=2083448";
+ }
+
+ if($buildNumber -eq 19041)
+ {
+ # Workaround for missing SDK
+ $uri = "https://software-download.microsoft.com/download/pr/19041.1.191206-1406.vb_release_WindowsSDK.iso";
+ }
+
+ if ($env:TEMP -eq $null)
+ {
+ $env:TEMP = Join-Path $env:SystemDrive 'temp'
+ }
+
+ $winsdkTempDir = Join-Path $env:TEMP "WindowsSDK"
+
+ if (![System.IO.Directory]::Exists($winsdkTempDir))
+ {
+ [void][System.IO.Directory]::CreateDirectory($winsdkTempDir)
+ }
+
+ $file = "winsdk_$buildNumber.iso"
+
+ Write-Verbose "Getting WinSDK from $uri"
+ $downloadFile = Download-File $winsdkTempDir $uri $file
+
+ # TODO Check if zip, exe, iso, etc.
+ try
+ {
+ Write-Host -NoNewline "Mounting ISO $file..."
+ Mount-ISO $downloadFile
+ Write-Host "Done"
+
+ $isoDrive = Get-ISODriveLetter $downloadFile
+
+ if (Test-Path $isoDrive)
+ {
+ Write-Host -NoNewLine "Installing WinSDK..."
+
+ $setupPath = Join-Path "$isoDrive" "WinSDKSetup.exe"
+ Start-Process -Wait $setupPath "/features $WindowsSDKOptions /q"
+ Write-Host "Done"
+ }
+ else
+ {
+ throw "Could not find mounted ISO at ${isoDrive}"
+ }
+ }
+ finally
+ {
+ Write-Host -NoNewline "Dismounting ISO $file..."
+ #Dismount-ISO $downloadFile
+ Write-Host "Done"
+ }
+}
+
+if ($StrongNameHijack)
+{
+ Write-Host -NoNewline "Disabling StrongName for Windows SDK..."
+
+ foreach($key in $PublicKeyTokens)
+ {
+ Disable-StrongName $key
+ }
+
+ Write-Host "Done"
+}
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 00000000..1147b3ac
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,11 @@
+version: 2
+updates:
+- package-ecosystem: nuget
+ directory: "/"
+ schedule:
+ interval: weekly
+ time: "10:00"
+ open-pull-requests-limit: 10
+ labels:
+ - kind/dependency
+ - do-not-merge/work-in-progress
diff --git a/.github/steps/install_dependencies/action.yml b/.github/steps/install_dependencies/action.yml
new file mode 100644
index 00000000..f8cd87b1
--- /dev/null
+++ b/.github/steps/install_dependencies/action.yml
@@ -0,0 +1,45 @@
+name: Install Dependencies
+description: ""
+
+inputs:
+ target-platform:
+ description: 'The platform to install dependencies for. #See available values at https://platform.uno/docs/articles/external/uno.check/doc/using-uno-check.html'
+ required: false
+ default: 'all'
+ dotnet-version:
+ description: 'Installs and sets the .NET SDK Version'
+ required: false
+ default: '8.0.x'
+ sdkVersion:
+ description: 'The version of the Windows Sdk'
+ required: false
+ default: '19041'
+
+runs:
+ using: "composite"
+ steps:
+ # Install .NET
+ - name: Setup .NET ${{ inputs.dotnet-version }}
+ uses: actions/setup-dotnet@v3
+ with:
+ dotnet-version: '${{ inputs.dotnet-version }}'
+
+ # Install Windows SDK
+ - name: Install Windows SDK ${{ inputs.sdkVersion }}
+ shell: pwsh
+ if: ${{ runner.os == 'Windows' }}
+ run: .\.github\Install-WindowsSdkISO.ps1 ${{ inputs.sdkVersion }}
+
+ # Run Uno.Check
+ - name: Install ${{ inputs.target-platform }} Workloads
+ shell: pwsh
+ run: |
+ dotnet tool install -g uno.check
+ ("${{ inputs.target-platform }} ".Split(' ') | ForEach-Object {
+ $target = $_.Replace("_win", "").Replace("_macos", "")
+ if (![string]::IsNullOrEmpty($target)) {
+ echo "target: $target"
+ uno-check -v --ci --non-interactive --fix --target $target --skip vswin --skip vsmac --skip xcode --skip vswinworkloads --skip androidemulator --skip dotnetnewunotemplates
+ echo "uno-check finished for target: $target "
+ }
+ })
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 00000000..7f5b9cbf
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,36 @@
+name: CI
+
+on:
+ push:
+ branches:
+ - main
+ - release/**
+
+ pull_request:
+ types: [opened, synchronize, reopened]
+ branches:
+ - main
+ - release/**
+env:
+ STEP_TIMEOUT_MINUTES: 60
+
+jobs:
+ smoke_test:
+ name: Uno
+ runs-on: windows-latest
+ steps:
+ - uses: actions/checkout@v3
+ with:
+ fetch-depth: 0
+
+ - name: Install Dependencies
+ timeout-minutes: ${{ fromJSON(env.STEP_TIMEOUT_MINUTES) }}
+ uses: "./.github/steps/install_dependencies"
+
+ # Add MSBuild to the PATH: https://github.com/microsoft/setup-msbuild
+ - name: Setup MSBuild
+ uses: microsoft/setup-msbuild@v1.3.1
+
+ - name: Build Emerald (Debug)
+ shell: pwsh
+ run: msbuild ./Emerald/Emerald.csproj /r
diff --git a/.gitignore b/.gitignore
index 163adce0..d65e71a2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,7 +1,7 @@
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
-## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
+## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore
# User-specific files
*.rsuser
@@ -29,7 +29,6 @@ x86/
bld/
[Bb]in/
[Oo]bj/
-[Oo]ut/
[Ll]og/
[Ll]ogs/
@@ -91,6 +90,7 @@ StyleCopReport.xml
*.tmp_proj
*_wpftmp.csproj
*.log
+*.tlog
*.vspscc
*.vssscc
.builds
@@ -294,6 +294,17 @@ node_modules/
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw
+# Visual Studio 6 auto-generated project file (contains which files were open etc.)
+*.vbp
+
+# Visual Studio 6 workspace and project file (working project files containing files to include in project)
+*.dsw
+*.dsp
+
+# Visual Studio 6 technical files
+*.ncb
+*.aps
+
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
@@ -350,6 +361,9 @@ ASALocalRun/
# Local History for Visual Studio
.localhistory/
+# Visual Studio History (VSHistory) files
+.vshistory/
+
# BeatPulse healthcheck temp database
healthchecksdb
@@ -361,6 +375,34 @@ MigrationBackup/
# Fody - auto-generated XML schema
FodyWeavers.xsd
+
+# VS Code files for those working on multiple tools
+.vscode/*
+!.vscode/settings.json
+!.vscode/tasks.json
+!.vscode/launch.json
+!.vscode/extensions.json
+*.code-workspace
+
+# Local History for Visual Studio Code
+.history/
+
+# Windows Installer files from build outputs
+*.cab
+*.msi
+*.msix
+*.msm
+*.msp
+
+# JetBrains Rider
+*.sln.iml
+
+# Single Target Config
+solution-config.props
+# Publish Profiles
+!**/Properties/PublishProfiles/*.pubxml
+
+# Azure Active Directory tokens
/Emerald.App/Emerald.App/MsalClientID.txt
/Emerald.Core/MsalClientID.txt
-/Emerald.App/Emerald.App/GithubClientID.txt
+Emerald.App/MsalClientID.txt
diff --git a/.vscode/extensions.json b/.vscode/extensions.json
new file mode 100644
index 00000000..a63ad400
--- /dev/null
+++ b/.vscode/extensions.json
@@ -0,0 +1,5 @@
+{
+ "recommendations": [
+ "unoplatform.vscode"
+ ],
+}
diff --git a/.vscode/launch.json b/.vscode/launch.json
new file mode 100644
index 00000000..1d6e6504
--- /dev/null
+++ b/.vscode/launch.json
@@ -0,0 +1,35 @@
+{
+ // Use IntelliSense to find out which attributes exist for C# debugging
+ // Use hover for the description of the existing attributes
+ // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md
+ "version": "0.2.0",
+ "configurations": [
+ {
+ "name": "Uno Platform Mobile",
+ "type": "Uno",
+ "request": "launch",
+ // any Uno* task will do, this is simply to satisfy vscode requirement when a launch.json is present
+ "preLaunchTask": "Uno: android | Debug | android-x64"
+ },
+ {
+ // Use IntelliSense to find out which attributes exist for C# debugging
+ // Use hover for the description of the existing attributes
+ // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md
+ "name": "Uno Platform Desktop (Debug)",
+ "type": "coreclr",
+ "request": "launch",
+ "preLaunchTask": "build-desktop",
+ // If you have changed target frameworks, make sure to update the program path.
+ "program": "${workspaceFolder}/Emerald/bin/Debug/net8.0-desktop/Emerald.dll",
+ "args": [],
+ "launchSettingsProfile": "Emerald (Desktop)",
+ "env": {
+ "DOTNET_MODIFIABLE_ASSEMBLIES": "debug"
+ },
+ "cwd": "${workspaceFolder}/Emerald",
+ // For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console
+ "console": "internalConsole",
+ "stopAtEntry": false
+ },
+ ]
+}
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 00000000..3405922d
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,10 @@
+{
+ "explorer.fileNesting.enabled": true,
+ "explorer.fileNesting.expand": false,
+ "explorer.fileNesting.patterns": {
+ "*.xaml": "$(capture).xaml.cs"
+ },
+ "files.associations": {
+ "global.json": "jsonc"
+ }
+}
diff --git a/.vscode/tasks.json b/.vscode/tasks.json
new file mode 100644
index 00000000..ac5be062
--- /dev/null
+++ b/.vscode/tasks.json
@@ -0,0 +1,31 @@
+{
+ "version": "2.0.0",
+ "tasks": [
+ {
+ "label": "build-desktop",
+ "command": "dotnet",
+ "type": "process",
+ "args": [
+ "build",
+ "${workspaceFolder}/Emerald/Emerald.csproj",
+ "/property:GenerateFullPaths=true",
+ "/property:TargetFramework=net8.0-desktop",
+ "/consoleloggerparameters:NoSummary"
+ ],
+ "problemMatcher": "$msCompile"
+ },
+ {
+ "label": "publish-desktop",
+ "command": "dotnet",
+ "type": "process",
+ "args": [
+ "publish",
+ "${workspaceFolder}/Emerald/Emerald.csproj",
+ "/property:GenerateFullPaths=true",
+ "/property:TargetFramework=net8.0-desktop",
+ "/consoleloggerparameters:NoSummary"
+ ],
+ "problemMatcher": "$msCompile"
+ }
+ ]
+}
diff --git a/.vsconfig b/.vsconfig
new file mode 100644
index 00000000..8e1dd5e5
--- /dev/null
+++ b/.vsconfig
@@ -0,0 +1,19 @@
+{
+ "version": "1.0",
+ "components": [
+ "Microsoft.VisualStudio.Component.CoreEditor",
+ "Microsoft.VisualStudio.Workload.CoreEditor",
+ "Microsoft.NetCore.Component.SDK",
+ "Microsoft.NetCore.Component.DevelopmentTools",
+ "Microsoft.Net.ComponentGroup.DevelopmentPrerequisites",
+ "Microsoft.VisualStudio.Component.TextTemplating",
+ "Microsoft.VisualStudio.ComponentGroup.MSIX.Packaging",
+ "Microsoft.VisualStudio.Component.ManagedDesktop.Prerequisites",
+ "Microsoft.VisualStudio.Component.Debugger.JustInTime",
+ "Microsoft.VisualStudio.Workload.ManagedDesktop",
+ "Microsoft.VisualStudio.Component.MonoDebugger",
+ "Microsoft.VisualStudio.ComponentGroup.Maui.All",
+ "Microsoft.VisualStudio.Workload.NetCrossPlat",
+ "Microsoft.VisualStudio.Workload.NetCoreTools"
+ ]
+}
diff --git a/Directory.Build.props b/Directory.Build.props
new file mode 100644
index 00000000..47ddeb81
--- /dev/null
+++ b/Directory.Build.props
@@ -0,0 +1,23 @@
+
+
+ enable
+ enable
+ true
+
+
+ $(NoWarn);NU1507;NETSDK1201;PRI257
+
+
+
+
+ 4.1.24
+ 6.3.7
+ 5.0.13
+ 5.2.14
+
+
diff --git a/Directory.Build.targets b/Directory.Build.targets
new file mode 100644
index 00000000..f75adf7e
--- /dev/null
+++ b/Directory.Build.targets
@@ -0,0 +1,2 @@
+
+
diff --git a/Directory.Packages.props b/Directory.Packages.props
new file mode 100644
index 00000000..086d7816
--- /dev/null
+++ b/Directory.Packages.props
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Emerald.App/Emerald.App.Package/Package.WinUI.wapproj b/Emerald.App/Emerald.App.Package/Package.WinUI.wapproj
index c76b14af..9920f1a8 100644
--- a/Emerald.App/Emerald.App.Package/Package.WinUI.wapproj
+++ b/Emerald.App/Emerald.App.Package/Package.WinUI.wapproj
@@ -158,12 +158,13 @@
-
+
build
-
+
build
+
diff --git a/Emerald.App/Emerald.App/Emerald.App.csproj b/Emerald.App/Emerald.App/Emerald.App.csproj
index b80e8e74..bfa64968 100644
--- a/Emerald.App/Emerald.App/Emerald.App.csproj
+++ b/Emerald.App/Emerald.App/Emerald.App.csproj
@@ -1,33 +1,27 @@
-
+
WinExe
- net7.0-windows10.0.22000.0
+ net8.0-windows10.0.22000.0
10.0.17763.0
Emerald.WinUI
app.manifest
x86;x64;arm64
- win10-x86;win10-x64;win10-arm64
+ win-x86;win-x64;win-arm64
true
Debug;Release
enable
10.0.19041.0
+ true
-
-
-
- Always
-
-
-
Always
@@ -38,16 +32,17 @@
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Emerald.App/Emerald.App/Helpers/Extensions.cs b/Emerald.App/Emerald.App/Helpers/Extensions.cs
index 2d61c177..5f0a94d0 100644
--- a/Emerald.App/Emerald.App/Helpers/Extensions.cs
+++ b/Emerald.App/Emerald.App/Helpers/Extensions.cs
@@ -1,4 +1,4 @@
-using Microsoft.UI.Xaml;
+using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.Windows.ApplicationModel.Resources;
using System;
@@ -112,15 +112,11 @@ public static string ToBinaryString(this string str)
public static string ToMD5(this string s)
{
StringBuilder sb = new();
+ byte[] hashValue = MD5.HashData(Encoding.UTF8.GetBytes(s));
- using (MD5 md5 = MD5.Create())
+ foreach (byte b in hashValue)
{
- byte[] hashValue = md5.ComputeHash(Encoding.UTF8.GetBytes(s));
-
- foreach (byte b in hashValue)
- {
- sb.Append($"{b:X2}");
- }
+ sb.Append($"{b:X2}");
}
return sb.ToString();
diff --git a/Emerald.App/Emerald.App/Helpers/MarkupExtensions/FontIcon.cs b/Emerald.App/Emerald.App/Helpers/MarkupExtensions/FontIcon.cs
index e32a4597..5b70d088 100644
--- a/Emerald.App/Emerald.App/Helpers/MarkupExtensions/FontIcon.cs
+++ b/Emerald.App/Emerald.App/Helpers/MarkupExtensions/FontIcon.cs
@@ -1,11 +1,11 @@
-using Microsoft.UI.Xaml.Markup;
+using Microsoft.UI.Xaml.Markup;
namespace Emerald.WinUI.Helpers
{
[MarkupExtensionReturnType(ReturnType = typeof(Microsoft.UI.Xaml.Controls.FontIcon))]
public sealed class FontIcon : MarkupExtension
{
- public string Glyph { get; set; }
+ public string Glyph { get; set; } = "\xe8a5";
public int FontSize { get; set; } = 16;
diff --git a/Emerald.App/Emerald.App/Helpers/Settings/JSON.cs b/Emerald.App/Emerald.App/Helpers/Settings/JSON.cs
index 19f0dc87..168a0c28 100644
--- a/Emerald.App/Emerald.App/Helpers/Settings/JSON.cs
+++ b/Emerald.App/Emerald.App/Helpers/Settings/JSON.cs
@@ -1,429 +1,424 @@
-using CmlLib.Core;
+using CmlLib.Core;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.WinUI.Helpers;
using Emerald.Core.Store.Enums;
using Microsoft.UI;
using Microsoft.UI.Xaml;
-using Newtonsoft.Json;
+using System.Text.Json;
+using System.Text.Json.Serialization;
using System;
using System.Collections.Generic;
using Windows.UI;
-namespace Emerald.WinUI.Helpers.Settings.JSON
+namespace Emerald.WinUI.Helpers.Settings.JSON;
+
+public class JSON : Models.Model
{
- public class JSON : Models.Model
- {
- public string Serialize()
- => JsonConvert.SerializeObject(this, Formatting.Indented);
- }
+ public string Serialize()
+ => JsonSerializer.Serialize(this, new JsonSerializerOptions { WriteIndented = true });
+}
- public class SettingsBackup : JSON
- {
- public string Backup { get; set; }
- public string Name { get; set; }
- public DateTime Time { get; set; }
- //ik there is Time.ToString() lol
- public string DateString => $"{Time.ToLongDateString()} {Time.ToShortTimeString()}";
- }
+public class SettingsBackup : JSON
+{
+ public string Backup { get; set; }
+ public string Name { get; set; }
+ public DateTime Time { get; set; }
+ // ik there is Time.ToString() lol
+ public string DateString => $"{Time.ToLongDateString()} {Time.ToShortTimeString()}";
+}
- public class Backups : JSON
- {
- public SettingsBackup[] AllBackups { get; set; } = Array.Empty();
- public string APIVersion { get; private set; } = "1.0";
- }
+public class Backups : JSON
+{
+ public SettingsBackup[] AllBackups { get; set; } = Array.Empty();
+ public string APIVersion { get; private set; } = "1.0";
+}
- public class Settings : JSON
+public class Settings : JSON
+{
+ public static Settings CreateNew() => new()
{
- public static Settings CreateNew() => new()
+ App = new()
{
- App = new()
+ Discord = new(),
+ Appearance = new()
{
- Discord = new(),
- Appearance = new()
- {
- MicaTintColor = (int)Enums.MicaTintColor.NoColor,
- Theme = (int)ElementTheme.Default
- }
- },
- Minecraft = new()
+ MicaTintColor = (int)Enums.MicaTintColor.NoColor,
+ Theme = (int)ElementTheme.Default
+ }
+ },
+ Minecraft = new()
+ {
+ Path = MinecraftPath.GetOSDefaultPath(),
+ RAM = DirectResoucres.MaxRAM / 2,
+ MCVerionsConfiguration = new(),
+ JVM = new(),
+ Downloader = new()
{
- Path = MinecraftPath.GetOSDefaultPath(),
- RAM = DirectResoucres.MaxRAM / 2,
- MCVerionsConfiguration = new(),
- JVM = new(),
- Downloader = new()
- {
- AssetsCheck = true,
- HashCheck = true
- }
+ AssetsCheck = true,
+ HashCheck = true
}
- };
+ }
+ };
- public string APIVersion { get; set; } = DirectResoucres.SettingsAPIVersion;
- public DateTime LastSaved { get; set; } = DateTime.Now;
- public Minecraft Minecraft { get; set; } = new();
+ public string APIVersion { get; set; } = DirectResoucres.SettingsAPIVersion;
+ public DateTime LastSaved { get; set; } = DateTime.Now;
+ public Minecraft Minecraft { get; set; } = new();
- public App App { get; set; } = new();
- }
+ public App App { get; set; } = new();
+}
- public partial class Minecraft : JSON
+public partial class Minecraft : JSON
+{
+ public Minecraft()
{
- public Minecraft()
+ JVM.PropertyChanged += (_, _)
+ => InvokePropertyChanged();
+ PropertyChanged += (_, e) =>
{
- JVM.PropertyChanged += (_, _)
- => InvokePropertyChanged();
- PropertyChanged += (_, e) =>
- {
- if (e.PropertyName != null)
- InvokePropertyChanged();
- };
- }
-
- [JsonIgnore]
- public double RAMinGB => Math.Round((RAM / 1024.00), 2);
+ if (e.PropertyName != null)
+ InvokePropertyChanged();
+ };
+ }
+ [JsonIgnore]
+ public double RAMinGB => Math.Round((RAM / 1024.00), 2);
- [ObservableProperty]
- private string _Path;
- [ObservableProperty]
- private int _RAM;
+ [ObservableProperty]
+ private string _Path;
- [ObservableProperty]
- private bool _IsAdmin;
+ [ObservableProperty]
+ private int _RAM;
- public Downloader Downloader { get; set; } = new();
+ [ObservableProperty]
+ private bool _IsAdmin;
- public MCVerionsConfiguration MCVerionsConfiguration { get; set; }
+ public Downloader Downloader { get; set; } = new();
- public JVM JVM { get; set; } = new();
+ public MCVerionsConfiguration MCVerionsConfiguration { get; set; }
- public bool ReadLogs()
- => JVM.GameLogs && !IsAdmin;
- }
+ public JVM JVM { get; set; } = new();
- public class Account : JSON
- {
- public string Type { get; set; }
- public string Username { get; set; }
- public string AccessToken { get; set; }
- public string ClientToken { get; set; }
- public string UUID { get; set; }
- public bool LastAccessed { get; set; }
- }
+ public bool ReadLogs()
+ => JVM.GameLogs && !IsAdmin;
+}
- public partial class Downloader : JSON
- {
+public class Account : JSON
+{
+ public string Type { get; set; }
+ public string Username { get; set; }
+ public string AccessToken { get; set; }
+ public string ClientToken { get; set; }
+ public string UUID { get; set; }
+ public bool LastAccessed { get; set; }
+}
- [ObservableProperty]
- private bool _HashCheck;
+public partial class Downloader : JSON
+{
+ [ObservableProperty]
+ private bool _HashCheck;
- [ObservableProperty]
- private bool _AssetsCheck;
- }
+ [ObservableProperty]
+ private bool _AssetsCheck;
+}
- public partial class JVM : JSON
+public partial class JVM : JSON
+{
+ public JVM()
{
- public JVM()
+ this.PropertyChanged += (_, e) =>
{
- this.PropertyChanged += (_, e) =>
- {
- if (e.PropertyName != null)
- this.InvokePropertyChanged();
- };
- }
-
- [ObservableProperty]
- private string[] _Arguments;
-
- [ObservableProperty]
- private double _ScreenWidth;
+ if (e.PropertyName != null)
+ this.InvokePropertyChanged();
+ };
+ }
- [ObservableProperty]
- private double _ScreenHeight;
+ [ObservableProperty]
+ private string[] _Arguments;
- [ObservableProperty]
- private bool _FullScreen;
+ [ObservableProperty]
+ private double _ScreenWidth;
- [ObservableProperty]
- private bool _GameLogs;
+ [ObservableProperty]
+ private double _ScreenHeight;
- [JsonIgnore]
- public string ScreenSizeStatus =>
- FullScreen ? "FullScreen".Localize() : ((ScreenWidth > 0 && ScreenHeight > 0) ? $"{ScreenWidth} × {ScreenHeight}" : "Default".Localize());
+ [ObservableProperty]
+ private bool _FullScreen;
- [JsonIgnore]
- public bool SetSize => !(ScreenSizeStatus == "FullScreen".Localize() || ScreenSizeStatus == "Default".Localize());
+ [ObservableProperty]
+ private bool _GameLogs;
- }
+ [JsonIgnore]
+ public string ScreenSizeStatus =>
+ FullScreen ? "FullScreen".Localize() : ((ScreenWidth > 0 && ScreenHeight > 0) ? $"{ScreenWidth} × {ScreenHeight}" : "Default".Localize());
- public class App : JSON
- {
- public Appearance Appearance { get; set; } = new();
- public bool AutoLogin { get; set; }
- public Discord Discord { get; set; } = new();
- public NewsFilter NewsFilter { get; set; } = new();
- public Store Store { get; set; } = new();
- public Updates Updates { get; set; } = new();
- public bool AutoClose { get; set; }
- public bool HideOnLaunch { get; set; }
- public bool WindowsHello { get; set; }
+ [JsonIgnore]
+ public bool SetSize => !(ScreenSizeStatus == "FullScreen".Localize() || ScreenSizeStatus == "Default".Localize());
+}
- }
- public class Updates : JSON
- {
-
- public bool CheckAtStartup { get; set; } = true;
- public bool AutoDownload { get; set; }
- public bool IncludePreReleases { get; set; }
- }
- public partial class StoreFilter : JSON
- {
- [ObservableProperty]
- private bool _Fabric;
+public class App : JSON
+{
+ public Appearance Appearance { get; set; } = new();
+ public bool AutoLogin { get; set; }
+ public Discord Discord { get; set; } = new();
+ public NewsFilter NewsFilter { get; set; } = new();
+ public Store Store { get; set; } = new();
+ public Updates Updates { get; set; } = new();
+ public bool AutoClose { get; set; }
+ public bool HideOnLaunch { get; set; }
+ public bool WindowsHello { get; set; }
+}
+public class Updates : JSON
+{
+ public bool CheckAtStartup { get; set; } = true;
+ public bool AutoDownload { get; set; }
+ public bool IncludePreReleases { get; set; }
+}
+public partial class StoreFilter : JSON
+{
+ [ObservableProperty]
+ private bool _Fabric;
- [ObservableProperty]
- private bool _Forge;
+ [ObservableProperty]
+ private bool _Forge;
- [ObservableProperty]
- private bool _Adventure;
+ [ObservableProperty]
+ private bool _Adventure;
- [ObservableProperty]
- private bool _Cursed;
+ [ObservableProperty]
+ private bool _Cursed;
- [ObservableProperty]
- private bool _Decoration;
+ [ObservableProperty]
+ private bool _Decoration;
- [ObservableProperty]
- private bool _Equipment;
+ [ObservableProperty]
+ private bool _Equipment;
- [ObservableProperty]
- private bool _Food;
+ [ObservableProperty]
+ private bool _Food;
- [ObservableProperty]
- private bool _Library;
+ [ObservableProperty]
+ private bool _Library;
- [ObservableProperty]
- private bool _Magic;
+ [ObservableProperty]
+ private bool _Magic;
- [ObservableProperty]
- private bool _Misc;
+ [ObservableProperty]
+ private bool _Misc;
- [ObservableProperty]
- private bool _Optimization;
+ [ObservableProperty]
+ private bool _Optimization;
- [ObservableProperty]
- private bool _Storage;
+ [ObservableProperty]
+ private bool _Storage;
- [ObservableProperty]
- private bool _Technology;
+ [ObservableProperty]
+ private bool _Technology;
- [ObservableProperty]
- private bool _Utility;
+ [ObservableProperty]
+ private bool _Utility;
- [ObservableProperty]
- private bool _Worldgen;
+ [ObservableProperty]
+ private bool _Worldgen;
- [JsonIgnore]
- public bool All
+ [JsonIgnore]
+ public bool All
+ {
+ get => GetResult().Length == 0;
+ set
{
- get => GetResult().Length == 0;
- set
- {
- _Fabric = _Forge = _Adventure = _Cursed = _Decoration = _Equipment = _Food = _Library = _Magic = _Misc = _Optimization = _Storage = _Technology = _Utility = _Worldgen = false;
- InvokePropertyChanged(null);
- }
+ Fabric = Forge = Adventure = Cursed = Decoration = Equipment = Food = Library = Magic = Misc = Optimization = Storage = Technology = Utility = Worldgen = false;
+ InvokePropertyChanged(null);
}
- public SearchCategories[] GetResult()
- {
- if (Fabric & Forge & Adventure & Cursed & Decoration & Equipment & Food & Library & Magic & Misc & Optimization & Storage & Technology & Utility & Worldgen)
- return Array.Empty();
+ }
+ public SearchCategories[] GetResult()
+ {
+ if (Fabric & Forge & Adventure & Cursed & Decoration & Equipment & Food & Library & Magic & Misc & Optimization & Storage & Technology & Utility & Worldgen)
+ return Array.Empty();
- var r = new List();
+ var r = new List();
- if (Fabric)
- r.Add(SearchCategories.Fabric);
+ if (Fabric)
+ r.Add(SearchCategories.Fabric);
- if (Forge)
- r.Add(SearchCategories.Forge);
+ if (Forge)
+ r.Add(SearchCategories.Forge);
- if (Adventure)
- r.Add(SearchCategories.Adventure);
+ if (Adventure)
+ r.Add(SearchCategories.Adventure);
- if (Cursed)
- r.Add(SearchCategories.Cursed);
+ if (Cursed)
+ r.Add(SearchCategories.Cursed);
- if (Decoration)
- r.Add(SearchCategories.Decoration);
+ if (Decoration)
+ r.Add(SearchCategories.Decoration);
- if (Equipment)
- r.Add(SearchCategories.Equipment);
+ if (Equipment)
+ r.Add(SearchCategories.Equipment);
- if (Food)
- r.Add(SearchCategories.Food);
+ if (Food)
+ r.Add(SearchCategories.Food);
- if (Library)
- r.Add(SearchCategories.Library);
+ if (Library)
+ r.Add(SearchCategories.Library);
- if (Magic)
- r.Add(SearchCategories.Magic);
+ if (Magic)
+ r.Add(SearchCategories.Magic);
- if (Misc)
- r.Add(SearchCategories.Misc);
+ if (Misc)
+ r.Add(SearchCategories.Misc);
- if (Optimization)
- r.Add(SearchCategories.Optimization);
+ if (Optimization)
+ r.Add(SearchCategories.Optimization);
- if (Storage)
- r.Add(SearchCategories.Storage);
+ if (Storage)
+ r.Add(SearchCategories.Storage);
- if (Technology)
- r.Add(SearchCategories.Technology);
+ if (Technology)
+ r.Add(SearchCategories.Technology);
- if (Utility)
- r.Add(SearchCategories.Utility);
+ if (Utility)
+ r.Add(SearchCategories.Utility);
- if (Worldgen)
- r.Add(SearchCategories.Worldgen);
+ if (Worldgen)
+ r.Add(SearchCategories.Worldgen);
- return r.ToArray();
- }
+ return r.ToArray();
}
- public class Store : JSON
- {
- public StoreFilter Filter { get; set; } = new();
- public StoreSortOptions SortOptions { get; set; } = new();
- }
-
- public partial class StoreSortOptions : JSON
- {
+}
+public class Store : JSON
+{
+ public StoreFilter Filter { get; set; } = new();
+ public StoreSortOptions SortOptions { get; set; } = new();
+}
- [ObservableProperty]
- private bool _Relevance = true;
+public partial class StoreSortOptions : JSON
+{
+ [ObservableProperty]
+ private bool _Relevance = true;
- [ObservableProperty]
- private bool _Downloads;
+ [ObservableProperty]
+ private bool _Downloads;
- [ObservableProperty]
- private bool _Follows;
+ [ObservableProperty]
+ private bool _Follows;
- [ObservableProperty]
- private bool _Updated;
+ [ObservableProperty]
+ private bool _Updated;
- [ObservableProperty]
- private bool _Newest;
+ [ObservableProperty]
+ private bool _Newest;
- public SearchSortOptions GetResult()
- {
- if (!(Relevance || Downloads || Follows || Updated || Newest))
- return SearchSortOptions.Relevance;
- else
- return Relevance ? SearchSortOptions.Relevance : (Downloads ? SearchSortOptions.Downloads : (Follows ? SearchSortOptions.Follows : (Updated ? SearchSortOptions.Updated : SearchSortOptions.Newest)));
- }
- }
- public partial class NewsFilter : JSON
+ public SearchSortOptions GetResult()
{
- [ObservableProperty]
- private bool _Java = false;
+ if (!(Relevance || Downloads || Follows || Updated || Newest))
+ return SearchSortOptions.Relevance;
+ else
+ return Relevance ? SearchSortOptions.Relevance : (Downloads ? SearchSortOptions.Downloads : (Follows ? SearchSortOptions.Follows : (Updated ? SearchSortOptions.Updated : SearchSortOptions.Newest)));
+ }
+}
+public partial class NewsFilter : JSON
+{
+ [ObservableProperty]
+ private bool _Java = false;
- [ObservableProperty]
- private bool _Bedrock = false;
+ [ObservableProperty]
+ private bool _Bedrock = false;
- [ObservableProperty]
- private bool _Dungeons = false;
+ [ObservableProperty]
+ private bool _Dungeons = false;
- [ObservableProperty]
- private bool _Legends = false;
+ [ObservableProperty]
+ private bool _Legends = false;
- [JsonIgnore]
- public bool All
+ [JsonIgnore]
+ public bool All
+ {
+ get => GetResult().Length == 4;
+ set
{
- get => GetResult().Length == 4;
- set
- {
- Java = Bedrock = Dungeons = Legends = false;
- InvokePropertyChanged(null);
- }
+ Java = Bedrock = Dungeons = Legends = false;
+ InvokePropertyChanged(null);
}
- public string[] GetResult()
- {
- var r = new List();
+ }
+ public string[] GetResult()
+ {
+ var r = new List();
- if (!Java && !Bedrock && !Dungeons && !Legends)
- {
- r.Add("Minecraft: Java Edition");
- r.Add("Minecraft for Windows");
- r.Add("Minecraft Dungeons");
- r.Add("Minecraft Legends");
- return r.ToArray();
- }
+ if (!Java && !Bedrock && !Dungeons && !Legends)
+ {
+ r.Add("Minecraft: Java Edition");
+ r.Add("Minecraft for Windows");
+ r.Add("Minecraft Dungeons");
+ r.Add("Minecraft Legends");
+ return r.ToArray();
+ }
- if (Java)
- r.Add("Minecraft: Java Edition");
+ if (Java)
+ r.Add("Minecraft: Java Edition");
- if (Bedrock)
- r.Add("Minecraft for Windows");
+ if (Bedrock)
+ r.Add("Minecraft for Windows");
- if (Dungeons)
- r.Add("Minecraft Dungeons");
+ if (Dungeons)
+ r.Add("Minecraft Dungeons");
- if (Legends)
- r.Add("Minecraft Legends");
+ if (Legends)
+ r.Add("Minecraft Legends");
- return r.ToArray();
- }
- }
- public class Discord : JSON
- {
+ return r.ToArray();
}
- public partial class MCVerionsConfiguration : JSON
- {
- [ObservableProperty]
- private bool _Release = true;
+}
+public class Discord : JSON
+{
+}
+public partial class MCVerionsConfiguration : JSON
+{
+ [ObservableProperty]
+ private bool _Release = true;
- [ObservableProperty]
- private bool _Custom = false;
+ [ObservableProperty]
+ private bool _Custom = false;
- [ObservableProperty]
- private bool _OldBeta = false;
+ [ObservableProperty]
+ private bool _OldBeta = false;
- [ObservableProperty]
- private bool _OldAlpha = false;
+ [ObservableProperty]
+ private bool _OldAlpha = false;
- [ObservableProperty]
- private bool _Snapshot = false;
- }
+ [ObservableProperty]
+ private bool _Snapshot = false;
+}
- public partial class Appearance : JSON
- {
- [ObservableProperty]
- private int _NavIconType = 1;
+public partial class Appearance : JSON
+{
+ [ObservableProperty]
+ private int _NavIconType = 1;
- public bool ShowFontIcons => NavIconType == 0;
+ public bool ShowFontIcons => NavIconType == 0;
- [ObservableProperty]
- private int _Theme;
+ [ObservableProperty]
+ private int _Theme;
- [ObservableProperty]
- private int _MicaTintColor;
+ [ObservableProperty]
+ private int _MicaTintColor;
- [ObservableProperty]
- private int _MicaType = 0;
+ [ObservableProperty]
+ private int _MicaType = 0;
- [ObservableProperty]
- private (int A, int R, int G, int B)? _CustomMicaTintColor;
+ [ObservableProperty]
+ private (int A, int R, int G, int B)? _CustomMicaTintColor;
- //I had to do this because whenever the app has no Mica it won't change the background color when requested theme changes unless the Windows main theme gets changed.
- [JsonIgnore]
- public Color Win10BackgroundColor => (SystemInformation.Instance.OperatingSystemVersion.Build < 22000) ? (Emerald.WinUI.App.Current.ActualTheme == ElementTheme.Light ? Colors.White : Colors.Black) : Colors.Transparent;
+ //I had to do this because whenever the app has no Mica it won't change the background color when requested theme changes unless the Windows main theme gets changed.
+ [JsonIgnore]
+ public Color Win10BackgroundColor => (SystemInformation.Instance.OperatingSystemVersion.Build < 22000) ? (Emerald.WinUI.App.Current.ActualTheme == ElementTheme.Light ? Colors.White : Colors.Black) : Colors.Transparent;
- public Appearance()
+ public Appearance()
+ {
+ this.PropertyChanged += (_, e) =>
{
- this.PropertyChanged += (_, e) =>
- {
- if (e.PropertyName != null)
- this.InvokePropertyChanged();
- };
- }
+ if (e.PropertyName != null)
+ this.InvokePropertyChanged();
+ };
}
}
diff --git a/Emerald.App/Emerald.App/Helpers/Settings/SettingsSystem.cs b/Emerald.App/Emerald.App/Helpers/Settings/SettingsSystem.cs
index e083f7d2..d47d9054 100644
--- a/Emerald.App/Emerald.App/Helpers/Settings/SettingsSystem.cs
+++ b/Emerald.App/Emerald.App/Helpers/Settings/SettingsSystem.cs
@@ -1,5 +1,5 @@
-using Emerald.WinUI.Helpers.Settings.JSON;
-using Newtonsoft.Json;
+using Emerald.WinUI.Helpers.Settings.JSON;
+using System.Text.Json;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -19,13 +19,12 @@ public static T GetSerializedFromSettings(string key, T def)
string json;
try
{
-
json = ApplicationData.Current.RoamingSettings.Values[key] as string;
- return JsonConvert.DeserializeObject(json);
+ return JsonSerializer.Deserialize(json);
}
catch
{
- json = JsonConvert.SerializeObject(def);
+ json = JsonSerializer.Serialize(def);
ApplicationData.Current.RoamingSettings.Values[key] = json;
return def;
}
@@ -39,14 +38,14 @@ public static void LoadData()
{
APINoMatch?.Invoke(null, ApplicationData.Current.RoamingSettings.Values["Settings"] as string);
ApplicationData.Current.RoamingSettings.Values["Settings"] = JSON.Settings.CreateNew().Serialize();
- Settings = JsonConvert.DeserializeObject(ApplicationData.Current.RoamingSettings.Values["Settings"] as string);
+ Settings = JsonSerializer.Deserialize(ApplicationData.Current.RoamingSettings.Values["Settings"] as string);
}
}
public static async Task CreateBackup(string system)
{
string json = await FileIO.ReadTextAsync(await ApplicationData.Current.LocalFolder.CreateFileAsync("backups.json", CreationCollisionOption.OpenIfExists));
- var l = json.IsNullEmptyOrWhiteSpace() ? new Backups() : JsonConvert.DeserializeObject(json);
+ var l = json.IsNullEmptyOrWhiteSpace() ? new Backups() : JsonSerializer.Deserialize(json);
var bl = l.AllBackups == null ? new List() : l.AllBackups.ToList();
bl.Add(new SettingsBackup() { Time = DateTime.Now, Backup = system });
@@ -59,7 +58,7 @@ public static async Task CreateBackup(string system)
public static async Task DeleteBackup(int Index)
{
string json = await FileIO.ReadTextAsync(await ApplicationData.Current.LocalFolder.CreateFileAsync("backups.json", CreationCollisionOption.OpenIfExists));
- var l = json.IsNullEmptyOrWhiteSpace() ? new Backups() : JsonConvert.DeserializeObject(json);
+ var l = json.IsNullEmptyOrWhiteSpace() ? new Backups() : JsonSerializer.Deserialize(json);
var bl = l.AllBackups == null ? new List() : l.AllBackups.ToList();
bl.RemoveAt(Index);
@@ -72,7 +71,7 @@ public static async Task DeleteBackup(int Index)
public static async Task DeleteBackup(DateTime time)
{
string json = await FileIO.ReadTextAsync(await ApplicationData.Current.LocalFolder.CreateFileAsync("backups.json", CreationCollisionOption.OpenIfExists));
- var l = json.IsNullEmptyOrWhiteSpace() ? new Backups() : JsonConvert.DeserializeObject(json);
+ var l = json.IsNullEmptyOrWhiteSpace() ? new Backups() : JsonSerializer.Deserialize(json);
var bl = l.AllBackups == null ? new List() : l.AllBackups.ToList();
bl.Remove(x => x.Time == time);
@@ -84,7 +83,7 @@ public static async Task DeleteBackup(DateTime time)
public static async Task RenameBackup(DateTime time, string name)
{
string json = await FileIO.ReadTextAsync(await ApplicationData.Current.LocalFolder.CreateFileAsync("backups.json", CreationCollisionOption.OpenIfExists));
- var l = json.IsNullEmptyOrWhiteSpace() ? new Backups() : JsonConvert.DeserializeObject(json);
+ var l = json.IsNullEmptyOrWhiteSpace() ? new Backups() : JsonSerializer.Deserialize(json);
var bl = l.AllBackups == null ? new List() : l.AllBackups.ToList();
bl.FirstOrDefault(x => x.Time == time).Name = name;
@@ -97,7 +96,7 @@ public static async Task RenameBackup(DateTime time, string name)
public static async Task> GetBackups()
{
string json = await FileIO.ReadTextAsync(await ApplicationData.Current.LocalFolder.CreateFileAsync("backups.json", CreationCollisionOption.OpenIfExists));
- var l = json.IsNullEmptyOrWhiteSpace() ? new Backups() : JsonConvert.DeserializeObject(json);
+ var l = json.IsNullEmptyOrWhiteSpace() ? new Backups() : JsonSerializer.Deserialize(json);
return l.AllBackups == null ? new List() : l.AllBackups.ToList();
}
@@ -106,7 +105,7 @@ public static void SaveData()
{
Settings.LastSaved = DateTime.Now;
ApplicationData.Current.RoamingSettings.Values["Settings"] = Settings.Serialize();
- ApplicationData.Current.RoamingSettings.Values["Accounts"] = JsonConvert.SerializeObject(Accounts);
+ ApplicationData.Current.RoamingSettings.Values["Accounts"] = JsonSerializer.Serialize(Accounts);
}
}
}
\ No newline at end of file
diff --git a/Emerald.App/Emerald.App/Helpers/Updater/Updater.cs b/Emerald.App/Emerald.App/Helpers/Updater/Updater.cs
index 246642bb..fc74e220 100644
--- a/Emerald.App/Emerald.App/Helpers/Updater/Updater.cs
+++ b/Emerald.App/Emerald.App/Helpers/Updater/Updater.cs
@@ -1,11 +1,11 @@
-using CmlLib.Core;
+using CmlLib.Core;
using CommunityToolkit.WinUI.Helpers;
using Emerald.Core;
using Emerald.Core.Tasks;
using Emerald.WinUI.Models;
using Microsoft.Extensions.Logging;
using Microsoft.UI.Xaml.Shapes;
-using Newtonsoft.Json;
+using System.Text.Json;
using Octokit;
using ProjBobcat.Class.Model;
using System;
@@ -39,9 +39,7 @@ public Updater()
bool IsInitialized = false;
public async System.Threading.Tasks.Task Initialize()
{
- var cId = await FileIO.ReadTextAsync(await StorageFile.GetFileFromPathAsync($"{Windows.ApplicationModel.Package.Current.InstalledPath}\\GithubClientID.txt"));
-
- Client = new GitHubClient(new Octokit.ProductHeaderValue(cId));
+ Client = new GitHubClient(new Octokit.ProductHeaderValue("Riverside.Emerald"));
IsInitialized = true;
}
private bool isRunning = false;
@@ -95,7 +93,6 @@ public async void CheckForUpdates(bool OnlyInformifHigherAvailable = false)
var msg = await MessageBox.Show("UpdateAvailable".Localize(), "## Version: " + ver.ToString() + "\n\n###ReleaseNotes".Localize() + "\n\n " + rel.Body,Enums.MessageBoxButtons.CustomWithCancel, "UpdateNow".Localize());
if(msg == Enums.MessageBoxResults.Cancel)
goto Return;
-
}
else if(ver < currentver)
{
@@ -106,8 +103,6 @@ public async void CheckForUpdates(bool OnlyInformifHigherAvailable = false)
var msg = await MessageBox.Show("DowngradeAvailable".Localize(), "DowngradeDescription".Localize(),Enums.MessageBoxButtons.Ok);
goto Return;
-
-
}
else if(ver == currentver)
{
@@ -164,4 +159,4 @@ private async void Install(string path)
Process.Start(startInfo);
}
}
-}
\ No newline at end of file
+}
diff --git a/Emerald.App/Emerald.App/Helpers/WindowManager.cs b/Emerald.App/Emerald.App/Helpers/WindowManager.cs
index 52cbc0ef..0519e1e3 100644
--- a/Emerald.App/Emerald.App/Helpers/WindowManager.cs
+++ b/Emerald.App/Emerald.App/Helpers/WindowManager.cs
@@ -1,4 +1,4 @@
-using CommunityToolkit.WinUI.Helpers;
+using CommunityToolkit.WinUI.Helpers;
using Microsoft.UI;
using Microsoft.UI.Composition;
using Microsoft.UI.Composition.SystemBackdrops;
@@ -48,7 +48,6 @@ public static MicaBackground IntializeWindow(Window window)
///
public static void SetTitleBar(Window window, UIElement AppTitleBar)
{
-
FrameworkElement RootUI = (FrameworkElement)window.Content;
if (AppWindowTitleBar.IsCustomizationSupported())
{
@@ -168,7 +167,6 @@ private void MicaBackground_ActualThemeChanged(FrameworkElement sender, object a
{
SetConfigurationSourceTheme();
}
-
}
private void SetConfigurationSourceTheme()
diff --git a/Emerald.App/Emerald.App/MainWindow.xaml.cs b/Emerald.App/Emerald.App/MainWindow.xaml.cs
index 7147aef9..080ee662 100644
--- a/Emerald.App/Emerald.App/MainWindow.xaml.cs
+++ b/Emerald.App/Emerald.App/MainWindow.xaml.cs
@@ -1,4 +1,4 @@
-using Emerald.Core;
+using Emerald.Core;
using Emerald.Core.Tasks;
using Emerald.WinUI.Helpers;
using Emerald.WinUI.Helpers.Settings.JSON;
@@ -87,7 +87,6 @@ public async void Initialize(object s, RoutedEventArgs e)
if (BackupState.WantBackup)
{
-
var r = await MessageBox.Show(
Localized.Error.Localize(),
Localized.LoadSettingsFailed.Localize(),
@@ -156,7 +155,6 @@ void Tasks()
TaskView.ChangeDescription(e.ID, string.Join(" ", (e.Message ?? "").Split(" ").Select(s => s.Localize())));
TaskView.ChangeSeverty(e.ID, e.Success ? InfoBarSeverity.Success : InfoBarSeverity.Error);
ShowMiniTask(c, string.Join(" ", (e.Message ?? "").Split(" ").Select(s => s.Localize())), e.Success ? InfoBarSeverity.Success : InfoBarSeverity.Error);
-
};
}
diff --git a/Emerald.App/Emerald.App/Models/Account.cs b/Emerald.App/Emerald.App/Models/Account.cs
index 9c09afdb..5efae4e5 100644
--- a/Emerald.App/Emerald.App/Models/Account.cs
+++ b/Emerald.App/Emerald.App/Models/Account.cs
@@ -1,4 +1,4 @@
-using CommunityToolkit.Mvvm.ComponentModel;
+using CommunityToolkit.Mvvm.ComponentModel;
using Emerald.Core;
using Emerald.WinUI.Enums;
using Emerald.WinUI.Helpers;
@@ -7,7 +7,6 @@ namespace Emerald.WinUI.Models
{
public partial class Account : Model
{
-
[ObservableProperty]
private string _UserName;
diff --git a/Emerald.App/Emerald.App/Models/NavViewHeader.cs b/Emerald.App/Emerald.App/Models/NavViewHeader.cs
index e725700e..a2fdde70 100644
--- a/Emerald.App/Emerald.App/Models/NavViewHeader.cs
+++ b/Emerald.App/Emerald.App/Models/NavViewHeader.cs
@@ -1,11 +1,10 @@
-using CommunityToolkit.Mvvm.ComponentModel;
+using CommunityToolkit.Mvvm.ComponentModel;
using Microsoft.UI.Xaml;
namespace Emerald.WinUI.Models
{
public partial class NavViewHeader : Model
{
-
[ObservableProperty]
private string _HeaderText;
diff --git a/Emerald.App/Emerald.App/UserControls/AdaptiveItemPane.xaml.cs b/Emerald.App/Emerald.App/UserControls/AdaptiveItemPane.xaml.cs
index 84ef0769..8aa58e65 100644
--- a/Emerald.App/Emerald.App/UserControls/AdaptiveItemPane.xaml.cs
+++ b/Emerald.App/Emerald.App/UserControls/AdaptiveItemPane.xaml.cs
@@ -4,13 +4,11 @@
namespace Emerald.WinUI.UserControls
{
-
///
/// A nice control from the RiseMP
///
public sealed partial class AdaptiveItemPane : UserControl
{
-
public static DependencyProperty LeftPaneProperty =
DependencyProperty.Register("LeftPane", typeof(object),
typeof(AdaptiveItemPane), new PropertyMetadata(null));
diff --git a/Emerald.App/Emerald.App/UserControls/ExpanderItem.xaml.cs b/Emerald.App/Emerald.App/UserControls/ExpanderItem.xaml.cs
index 8933a8c1..2d219a94 100644
--- a/Emerald.App/Emerald.App/UserControls/ExpanderItem.xaml.cs
+++ b/Emerald.App/Emerald.App/UserControls/ExpanderItem.xaml.cs
@@ -64,7 +64,5 @@ public Visibility BorderVisibility
// Using a DependencyProperty as the backing store for BorderVisibility. This enables animation, styling, binding, etc...
public static readonly DependencyProperty BorderVisibilityProperty =
DependencyProperty.Register("BorderVisibility", typeof(Visibility), typeof(ExpanderItem), new PropertyMetadata(null));
-
-
}
}
diff --git a/Emerald.App/Emerald.App/Views/Home/AccountsPage.xaml.cs b/Emerald.App/Emerald.App/Views/Home/AccountsPage.xaml.cs
index e21a2308..84f7669c 100644
--- a/Emerald.App/Emerald.App/Views/Home/AccountsPage.xaml.cs
+++ b/Emerald.App/Emerald.App/Views/Home/AccountsPage.xaml.cs
@@ -1,4 +1,4 @@
-using CmlLib.Core.Auth;
+using CmlLib.Core.Auth;
using Emerald.Core;
using Emerald.WinUI.Helpers;
using Emerald.WinUI.Models;
@@ -122,7 +122,6 @@ private void btnSelectAll_Click(object sender, RoutedEventArgs e)
item.IsChecked = val;
}
UpdateAll();
-
}
private void RemoveSelected() =>
Accounts.Remove(x => x.IsChecked);
@@ -161,7 +160,6 @@ private void CancelLogin_Click(object sender, RoutedEventArgs e)
var a = (sender as Button).DataContext as Account;
SetEditor(null);
Accounts.Add(a);
-
}
public void UpdateMainSource() =>
SS.Accounts = Accounts.Select(x =>
@@ -244,7 +242,6 @@ private async void mitMicrosoftAdd_Click(object sender, RoutedEventArgs e)
}
catch (Exception ex)
{
-
Core.Tasks.TasksHelper.CompleteTask(taskID, false, ex.Message);
}
}
diff --git a/Emerald.App/Emerald.App/Views/Home/HomePage.xaml.cs b/Emerald.App/Emerald.App/Views/Home/HomePage.xaml.cs
index ab24c351..f2c077a4 100644
--- a/Emerald.App/Emerald.App/Views/Home/HomePage.xaml.cs
+++ b/Emerald.App/Emerald.App/Views/Home/HomePage.xaml.cs
@@ -1,4 +1,4 @@
-using CmlLib.Core;
+using CmlLib.Core;
using CmlLib.Core.Auth;
using CmlLib.Core.Downloader;
using CommunityToolkit.WinUI.Helpers;
@@ -92,7 +92,6 @@ public async void Initialize()
var r = await MessageBox.Show("Error".Localize(), "MCPathFailed".Localize().Replace("{Path}", SS.Settings.Minecraft.Path), MessageBoxButtons.CustomWithCancel, "Yes".Localize(), "SetDifMCPath".Localize());
if (r == MessageBoxResults.Cancel)
Process.GetCurrentProcess().Kill(); // Application.Current.Exit() didn't kill the process
-
else if (r == MessageBoxResults.CustomResult2)
{
var fop = new FolderPicker
@@ -106,7 +105,6 @@ public async void Initialize()
SS.Settings.Minecraft.Path = f.Path;
}
}
-
}
App.Current.Launcher.InitializeLauncher(new MinecraftPath(SS.Settings.Minecraft.Path));
@@ -152,7 +150,6 @@ public async void Initialize()
if (SystemInformation.Instance.IsFirstRun)
ShowTips();
-
}
public void ShowTips()
@@ -262,7 +259,6 @@ void FindSubVers(MinecraftVersion ver)
var found = splitText.All((key) => v.Version.ToLower().Contains(key));
if (found)
suitableItems.Add(v);
-
}
}
@@ -463,7 +459,6 @@ private void AdaptiveItemPane_OnStretched(object sender, EventArgs e)
s.Children.OfType().ToList().ForEach(x => x.HorizontalAlignment = HorizontalAlignment.Left);
NewsButton.HorizontalContentAlignment = ChangelogsButton.HorizontalContentAlignment = HorizontalAlignment.Left;
-
}
}
}
diff --git a/Emerald.App/Emerald.App/Views/Home/NewsPage.xaml.cs b/Emerald.App/Emerald.App/Views/Home/NewsPage.xaml.cs
index 0bafae92..0298ea6f 100644
--- a/Emerald.App/Emerald.App/Views/Home/NewsPage.xaml.cs
+++ b/Emerald.App/Emerald.App/Views/Home/NewsPage.xaml.cs
@@ -27,7 +27,6 @@ public NewsPage()
await Task.Delay(300);
pnlEmpty.Visibility = App.Current.Launcher.News.Entries.Any() ? Visibility.Collapsed : Visibility.Visible;
};
-
}
private void BackButton_Click(object sender, RoutedEventArgs e) =>
BackRequested?.Invoke(this, new EventArgs());
diff --git a/Emerald.App/Emerald.App/Views/Settings/AboutPage.xaml.cs b/Emerald.App/Emerald.App/Views/Settings/AboutPage.xaml.cs
index 3632cc60..32cb10d4 100644
--- a/Emerald.App/Emerald.App/Views/Settings/AboutPage.xaml.cs
+++ b/Emerald.App/Emerald.App/Views/Settings/AboutPage.xaml.cs
@@ -1,4 +1,4 @@
-using CommunityToolkit.WinUI.UI.Controls;
+using CommunityToolkit.WinUI.UI.Controls;
using Emerald.Core;
using Emerald.WinUI.Enums;
using Emerald.WinUI.Helpers;
@@ -102,7 +102,6 @@ private async void ToggleSwitch_Toggled(object sender, RoutedEventArgs e)
{
SS.Settings.App.WindowsHello = true;
}
-
}
private async void DeleteBackup_Click(object sender, RoutedEventArgs e)
diff --git a/Emerald.App/Emerald.App/Views/Settings/AppearancePage.xaml.cs b/Emerald.App/Emerald.App/Views/Settings/AppearancePage.xaml.cs
index b2d95df1..189fda80 100644
--- a/Emerald.App/Emerald.App/Views/Settings/AppearancePage.xaml.cs
+++ b/Emerald.App/Emerald.App/Views/Settings/AppearancePage.xaml.cs
@@ -1,4 +1,4 @@
-using Emerald.Core;
+using Emerald.Core;
using Emerald.WinUI.Helpers;
using Microsoft.UI.Xaml.Controls;
using System.Collections.ObjectModel;
@@ -93,7 +93,6 @@ private void GVColorList_SelectionChanged(object sender, SelectionChangedEventAr
private void CustomTintColor_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
{
-
var c = SS.Settings.App.Appearance.CustomMicaTintColor;
var cp = new ColorPicker()
{
diff --git a/Emerald.App/Emerald.App/Views/Settings/GeneralPage.xaml b/Emerald.App/Emerald.App/Views/Settings/GeneralPage.xaml
index f55b5ec7..29886196 100644
--- a/Emerald.App/Emerald.App/Views/Settings/GeneralPage.xaml
+++ b/Emerald.App/Emerald.App/Views/Settings/GeneralPage.xaml
@@ -13,59 +13,59 @@
xmlns:uc="using:Emerald.WinUI.UserControls"
mc:Ignorable="d">
-
-
-
-
+
+
+
+
-
-
-
-
+
+
+
-
-
-
-
+
+
-
-
-
-
+
+
+
GB
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
-
-
-
+
+
+
-
-
-
+
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
-
-
+
+
-
-
+
+
-
-
-
-
-
+
+
-
-
-
-
-
+
+
+
+
-
-
-
+
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
-
+
+
-
-
-
-
-
-
-
\ No newline at end of file
+
+
+
+
+
diff --git a/Emerald.App/Emerald.App/Views/Settings/GeneralPage.xaml.cs b/Emerald.App/Emerald.App/Views/Settings/GeneralPage.xaml.cs
index 1fe2010a..be703784 100644
--- a/Emerald.App/Emerald.App/Views/Settings/GeneralPage.xaml.cs
+++ b/Emerald.App/Emerald.App/Views/Settings/GeneralPage.xaml.cs
@@ -1,4 +1,4 @@
-using CmlLib.Core;
+using CmlLib.Core;
using Emerald.WinUI.Enums;
using Emerald.WinUI.Helpers;
using Microsoft.UI.Xaml;
@@ -54,10 +54,8 @@ async void Try()
Try();
else
Start();
-
}
}
-
}
Start();
}
@@ -72,7 +70,5 @@ private void btnRamMinus_Click(object sender, RoutedEventArgs e) =>
private void btnAutoRAM_Click(object sender, RoutedEventArgs e) =>
SS.Settings.Minecraft.RAM = DirectResoucres.MaxRAM / 2;
-
-
}
}
diff --git a/Emerald.App/Emerald.App/Views/Store/InstallerPage.xaml.cs b/Emerald.App/Emerald.App/Views/Store/InstallerPage.xaml.cs
index a72cf7a9..1f580601 100644
--- a/Emerald.App/Emerald.App/Views/Store/InstallerPage.xaml.cs
+++ b/Emerald.App/Emerald.App/Views/Store/InstallerPage.xaml.cs
@@ -9,7 +9,7 @@ namespace Emerald.WinUI.Views.Store
public sealed partial class InstallerPage : Page
{
public StoreItem Item { get; set; }
- private ObservableCollection Versions = new();
+ private ObservableCollection Versions = new();
public InstallerPage()
{
InitializeComponent();
diff --git a/Emerald.Core/Emerald.Core.csproj b/Emerald.Core/Emerald.Core.csproj
index 8bb6d6e5..80509451 100644
--- a/Emerald.Core/Emerald.Core.csproj
+++ b/Emerald.Core/Emerald.Core.csproj
@@ -1,20 +1,18 @@
-
- net7.0-windows10.0.22000.0
- enable
- enable
- Emerald.Core
- Debug;Release
- 10.0.17763.0
- win10-x86;win10-x64
-
+
+ net8.0
+ enable
+ enable
+ false
+ true
+
-
-
-
-
-
-
+
+
+
+
+
+
-
+
\ No newline at end of file
diff --git a/Emerald.Core/Launcher.cs b/Emerald.Core/Launcher.cs
index 33b44c6b..901af079 100644
--- a/Emerald.Core/Launcher.cs
+++ b/Emerald.Core/Launcher.cs
@@ -1,4 +1,4 @@
-using CmlLib.Core;
+using CmlLib.Core;
using CmlLib.Core.Downloader;
using CmlLib.Core.Files;
using CmlLib.Core.Installer.FabricMC;
@@ -12,7 +12,6 @@
using Emerald.Core.Tasks;
using ProjBobcat.Class.Model.Optifine;
using System.ComponentModel;
-using Windows.System;
using ProgressChangedEventArgs = Emerald.Core.Args.ProgressChangedEventArgs;
namespace Emerald.Core
@@ -141,7 +140,7 @@ void ProgChange(object sender, System.ComponentModel.ProgressChangedEventArgs e)
prog = e.ProgressPercentage;
if (createTask)
TasksHelper.EditProgressTask(id, prog, message: message);
- };
+ }
void FileChange(DownloadFileChangedEventArgs e)
{
@@ -149,7 +148,7 @@ void FileChange(DownloadFileChangedEventArgs e)
if (createTask)
TasksHelper.EditProgressTask(id, prog, message: message);
- };
+ }
try
{
diff --git a/Emerald.Core/News/JSON/Dimensions.cs b/Emerald.Core/News/JSON/Dimensions.cs
index 7016bea8..54f415af 100644
--- a/Emerald.Core/News/JSON/Dimensions.cs
+++ b/Emerald.Core/News/JSON/Dimensions.cs
@@ -1,6 +1,5 @@
namespace Emerald.Core.News.JSON
{
-
public class Dimensions
{
public int width { get; set; }
diff --git a/Emerald.Core/News/JSON/Entry.cs b/Emerald.Core/News/JSON/Entry.cs
index 908cf4ea..4abbe8df 100644
--- a/Emerald.Core/News/JSON/Entry.cs
+++ b/Emerald.Core/News/JSON/Entry.cs
@@ -2,7 +2,6 @@
namespace Emerald.Core.News.JSON
{
-
public class Entry
{
[JsonPropertyName("title")]
diff --git a/Emerald.Core/News/NewsHelper.cs b/Emerald.Core/News/NewsHelper.cs
index 09718ca9..4965e924 100644
--- a/Emerald.Core/News/NewsHelper.cs
+++ b/Emerald.Core/News/NewsHelper.cs
@@ -1,4 +1,4 @@
-using Newtonsoft.Json;
+using System.Text.Json;
using System.Collections.ObjectModel;
using System.ComponentModel;
@@ -77,14 +77,13 @@ public ObservableCollection Entries
response = await wc.GetStringAsync(url);
}
- var json = JsonConvert.DeserializeObject(response);
+ var json = JsonSerializer.Deserialize(response);
AllEntries = json?.entries != null ? new(json.entries.ToList()) : new();
Entries.Clear();
foreach (var item in AllEntries.Where(x => filter == null || filter.Contains(x.Category)))
Entries.Add(item);
-
}
catch
{
diff --git a/Emerald.Core/Optifine.cs b/Emerald.Core/Optifine.cs
index 77eceba3..bc6b1147 100644
--- a/Emerald.Core/Optifine.cs
+++ b/Emerald.Core/Optifine.cs
@@ -1,5 +1,5 @@
-using CmlLib.Core;
-using Newtonsoft.Json;
+using CmlLib.Core;
+using System.Text.Json;
using ProjBobcat.Class.Helper;
using ProjBobcat.Class.Model.Optifine;
using ProjBobcat.DefaultComponent.Installer;
@@ -27,7 +27,7 @@ public async Task> GetOptifineVersions()
c.Dispose();
- return JsonConvert.DeserializeObject>(json);
+ return JsonSerializer.Deserialize>(json);
}
catch
{
@@ -49,7 +49,7 @@ public async Task> GetOptifineVersions()
javaResult.Add(java);
}
- if (!javaResult.Any())
+ if (javaResult.Count == 0)
return (false, "NoJRE");
ProgressChanged(this, 0);
diff --git a/Emerald.Core/Store/Labrinth.cs b/Emerald.Core/Store/Labrinth.cs
index 8a1cc2e5..2b5dadd9 100644
--- a/Emerald.Core/Store/Labrinth.cs
+++ b/Emerald.Core/Store/Labrinth.cs
@@ -1,6 +1,6 @@
-using CmlLib.Core;
+using CmlLib.Core;
using Emerald.Core.Tasks;
-using Newtonsoft.Json;
+using System.Text.Json;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Net.Http.Headers;
@@ -34,7 +34,6 @@ private async Task Get(string code)
return await response.Content.ReadAsStringAsync();
throw new Exception("Failed to GET: \"" + code + "\", StatusCode: " + response.StatusCode.ToString());
-
}
private int DownloadTaskID;
@@ -75,7 +74,7 @@ private async void ModrinthDownload(string link, string folderdir, string fileNa
{
int taskID = name == "" ? Tasks.TasksHelper.AddTask(Localized.GettingMods) : TasksHelper.AddTask(Localized.SearchStore, name);
- string categouriesString = (categories != null && categories.Any() && categories.Length != 15) ? $"[\"categories:{string.Join("\"],[\"categories:", categories)}\"],".ToLower() : "";
+ string categouriesString = (categories != null && categories.Length != 0 && categories.Length != 15) ? $"[\"categories:{string.Join("\"],[\"categories:", categories)}\"],".ToLower() : "";
Results.SearchResult s;
@@ -84,7 +83,7 @@ private async void ModrinthDownload(string link, string folderdir, string fileNa
var fn = string.IsNullOrEmpty(name) ? "" : $"query={name}";
var url = $"search?{fn}&index={sortOptions.ToString().ToLower()}&facets=[{categouriesString}[\"project_type:mod\"]]&limit={limit}";
var json = await Get(url);
- s = JsonConvert.DeserializeObject(json);
+ s = JsonSerializer.Deserialize(json);
Tasks.TasksHelper.CompleteTask(taskID, true, name);
return s;
@@ -104,7 +103,7 @@ private async void ModrinthDownload(string link, string folderdir, string fileNa
try
{
var json = await Get("project/" + id);
- s = JsonConvert.DeserializeObject(json);
+ s = JsonSerializer.Deserialize(json);
Tasks.TasksHelper.CompleteTask(taskID, true, name);
return s;
}
@@ -123,7 +122,7 @@ private async void ModrinthDownload(string link, string folderdir, string fileNa
try
{
var json = await Get("project/" + id + "/version");
- s = JsonConvert.DeserializeObject>(json);
+ s = JsonSerializer.Deserialize>(json);
Tasks.TasksHelper.CompleteTask(taskID, true);
return s;
}
@@ -163,15 +162,12 @@ public enum SearchCategories
Technology,
Utility,
Worldgen
-
}
}
namespace Emerald.Core.Store.Results
{
-
public class File
{
-
[JsonPropertyName("hashes")]
public Hashes Hashes { get; set; }
@@ -244,7 +240,6 @@ public class Version : INotifyPropertyChanged
public class Hashes
{
-
[JsonPropertyName("sha512")]
public string Sha512 { get; set; }
@@ -254,7 +249,6 @@ public class Hashes
public class SearchResult
{
-
[JsonPropertyName("hits")]
public Hit[] Hits { get; set; }
@@ -270,7 +264,6 @@ public class SearchResult
public class Hit
{
-
[JsonPropertyName("slug")]
public string Slug { get; set; }
@@ -421,7 +414,6 @@ public class License
public class Donation_Urls
{
-
[JsonPropertyName("id")]
public string ID { get; set; }
diff --git a/Emerald.Core/Util.cs b/Emerald.Core/Util.cs
index 6c459be6..56b7c2bc 100644
--- a/Emerald.Core/Util.cs
+++ b/Emerald.Core/Util.cs
@@ -1,4 +1,4 @@
-namespace Emerald.Core
+namespace Emerald.Core
{
public static class Util
{
@@ -91,7 +91,7 @@ private async Task ProcessContentStream(long? totalDownloadSize, Stream contentS
using var fileStream = new FileStream(_destinationFilePath, FileMode.Create, FileAccess.Write, FileShare.None, 8192, true);
do
{
- var bytesRead = await contentStream.ReadAsync(buffer, 0, buffer.Length);
+ var bytesRead = await contentStream.ReadAsync(buffer);
if (bytesRead == 0)
{
isMoreToRead = false;
diff --git a/Emerald.CoreX/Core.cs b/Emerald.CoreX/Core.cs
new file mode 100644
index 00000000..4f8ef55d
--- /dev/null
+++ b/Emerald.CoreX/Core.cs
@@ -0,0 +1,13 @@
+using Microsoft.Extensions.Logging;
+using CmlLib.Core;
+using CmlLib.Core.VersionMetadata;
+namespace Emerald.CoreX;
+
+public class Core(ILogger logger)
+{
+ public MinecraftLauncher Launcher { get; set; }
+ public void InitializeLauncher()
+ {
+ Launcher = new MinecraftLauncher();
+ }
+}
\ No newline at end of file
diff --git a/Emerald.CoreX/Emerald.CoreX.csproj b/Emerald.CoreX/Emerald.CoreX.csproj
new file mode 100644
index 00000000..60f221b0
--- /dev/null
+++ b/Emerald.CoreX/Emerald.CoreX.csproj
@@ -0,0 +1,19 @@
+
+
+
+ net8.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Emerald.CoreX/Enums/MCVersionType.cs b/Emerald.CoreX/Enums/MCVersionType.cs
new file mode 100644
index 00000000..6d90c3d0
--- /dev/null
+++ b/Emerald.CoreX/Enums/MCVersionType.cs
@@ -0,0 +1,18 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Emerald.CoreX.Enums
+{
+ public enum MCVersionType
+ {
+ Vanilla,
+ Fabric,
+ Lite,
+ Forge,
+ Optifine,
+ Undefined
+ }
+}
diff --git a/Emerald.CoreX/Helpers/FileDownloader.cs b/Emerald.CoreX/Helpers/FileDownloader.cs
new file mode 100644
index 00000000..8023e4bc
--- /dev/null
+++ b/Emerald.CoreX/Helpers/FileDownloader.cs
@@ -0,0 +1,124 @@
+using System;
+using System.IO;
+using System.Net.Http;
+using System.Security.Cryptography;
+using System.Text.Json.Serialization;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.Extensions.Logging;
+
+namespace Emerald.CoreX.Helpers;
+
+public class FileDownloader
+{
+ private readonly ILogger _logger;
+ private readonly HttpClient _httpClient;
+
+ public FileDownloader(ILogger logger, HttpClient httpClient)
+ {
+ _logger = logger;
+ _httpClient = httpClient;
+ }
+
+ ///
+ /// Downloads a file from the specified URL to the given file path.
+ ///
+ /// The URL of the file to download.
+ /// The path where the file should be saved.
+ /// Optional. The expected hashes for file integrity verification.
+ /// Optional. An IProgress{double} to report download progress.
+ /// Optional. A CancellationToken to support cancellation of the download.
+ /// A task representing the asynchronous download operation.
+ public async Task DownloadFileAsync(string url, string filePath, Hashes? expectedHashes = null,
+ IProgress? progress = null, CancellationToken cancellationToken = default)
+ {
+ _logger.LogInformation($"Downloading file from URL: {url}");
+
+ try
+ {
+ Directory.CreateDirectory(Path.GetDirectoryName(filePath));
+
+ // Ensure any existing file is closed before overwriting
+ if (File.Exists(filePath))
+ {
+ File.Delete(filePath);
+ }
+
+ using var response = await _httpClient.GetAsync(url, HttpCompletionOption.ResponseHeadersRead, cancellationToken);
+ response.EnsureSuccessStatusCode();
+
+ var totalBytes = response.Content.Headers.ContentLength ?? -1L;
+ using var contentStream = await response.Content.ReadAsStreamAsync(cancellationToken);
+ using var fileStream = new FileStream(filePath, FileMode.CreateNew, FileAccess.Write, FileShare.None, 8192, true);
+
+ var buffer = new byte[8192];
+ long totalBytesRead = 0;
+ int bytesRead;
+
+ while ((bytesRead = await contentStream.ReadAsync(buffer, cancellationToken)) > 0)
+ {
+ await fileStream.WriteAsync(buffer.AsMemory(0, bytesRead), cancellationToken);
+ totalBytesRead += bytesRead;
+ progress?.Report(totalBytes > 0 ? (double)totalBytesRead / totalBytes * 100 : 0);
+ }
+
+ fileStream.Dispose();
+ contentStream.Dispose();
+ _logger.LogInformation($"Successfully downloaded file to: {filePath}");
+
+ if (expectedHashes != null && !await VerifyFileIntegrityAsync(filePath, expectedHashes))
+ {
+ throw new Exception("Downloaded file failed integrity check");
+ }
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, $"Error occurred while downloading file from URL: {url}");
+ throw;
+ }
+ }
+
+
+ ///
+ /// Verifies the integrity of a file against expected hashes.
+ ///
+ /// The path of the file to verify.
+ /// The expected hashes for the file.
+ /// True if the file integrity is verified, false otherwise.
+ private async Task VerifyFileIntegrityAsync(string filePath, Hashes expectedHashes)
+ {
+ try
+ {
+ using var sha1 = SHA1.Create();
+ using var sha512 = SHA512.Create();
+
+ // Open the file with read-only access and allow other processes to read the file
+ using var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read);
+
+ // Compute SHA-1 hash
+ var sha1Hash = BitConverter.ToString(await sha1.ComputeHashAsync(stream)).Replace("-", "").ToLowerInvariant();
+
+ // Reset stream position for the next hash computation
+ stream.Position = 0;
+
+ // Compute SHA-512 hash
+ var sha512Hash = BitConverter.ToString(await sha512.ComputeHashAsync(stream)).Replace("-", "").ToLowerInvariant();
+
+ return sha1Hash == expectedHashes.Sha1 && sha512Hash == expectedHashes.Sha512;
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, $"Error occurred during file integrity verification for file: {filePath}");
+ throw;
+ }
+ }
+
+}
+
+
+public class Hashes
+{
+ [JsonPropertyName("sha512")] public string Sha512 { get; set; }
+
+ [JsonPropertyName("sha1")] public string Sha1 { get; set; }
+}
diff --git a/Emerald.CoreX/Installers/Fabric.cs b/Emerald.CoreX/Installers/Fabric.cs
new file mode 100644
index 00000000..53c693ed
--- /dev/null
+++ b/Emerald.CoreX/Installers/Fabric.cs
@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Emerald.CoreX.Installers
+{
+ internal class Fabric : IModLoaderInstaller
+ {
+ }
+}
diff --git a/Emerald.CoreX/Installers/Forge.cs b/Emerald.CoreX/Installers/Forge.cs
new file mode 100644
index 00000000..a51d3718
--- /dev/null
+++ b/Emerald.CoreX/Installers/Forge.cs
@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Emerald.CoreX.Installers
+{
+ internal class Forge : IModLoaderInstaller
+ {
+ }
+}
diff --git a/Emerald.CoreX/Installers/IModLoaderInstaller.cs b/Emerald.CoreX/Installers/IModLoaderInstaller.cs
new file mode 100644
index 00000000..a91b12c6
--- /dev/null
+++ b/Emerald.CoreX/Installers/IModLoaderInstaller.cs
@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Emerald.CoreX.Installers
+{
+ internal interface IModLoaderInstaller
+ {
+ }
+}
diff --git a/Emerald.CoreX/Installers/LiteLoader.cs b/Emerald.CoreX/Installers/LiteLoader.cs
new file mode 100644
index 00000000..8798c807
--- /dev/null
+++ b/Emerald.CoreX/Installers/LiteLoader.cs
@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Emerald.CoreX.Installers
+{
+ internal class LiteLoader: IModLoaderInstaller
+ {
+ }
+}
diff --git a/Emerald.CoreX/Installers/Optifine.cs b/Emerald.CoreX/Installers/Optifine.cs
new file mode 100644
index 00000000..dec37deb
--- /dev/null
+++ b/Emerald.CoreX/Installers/Optifine.cs
@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Emerald.CoreX.Installers
+{
+ internal class Optifine : IModLoaderInstaller
+ {
+ }
+}
diff --git a/Emerald.CoreX/Models/MCVersion.cs b/Emerald.CoreX/Models/MCVersion.cs
new file mode 100644
index 00000000..84248297
--- /dev/null
+++ b/Emerald.CoreX/Models/MCVersion.cs
@@ -0,0 +1,29 @@
+using CmlLib.Core.Version;
+using CmlLib.Core.VersionMetadata;
+using CommunityToolkit.Mvvm.ComponentModel;
+using Emerald.CoreX.Enums;
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Emerald.CoreX.Models
+{
+ public partial class MCVersion
+ {
+ public MCVersionType Type { get; private set; }
+ public MVersionType ReleaseType { get; private set; }
+
+ public string Name { get; private set; }
+
+ public string DisplayName { get; set; }
+
+ public IVersionMetadata Metadata { get; private set; }
+
+ public bool Local { get; private set; }
+
+ public MCVersion[] Subversions { get; private set; }
+ }
+}
diff --git a/Emerald.CoreX/Store/Modrinth/IModrinthStore.cs b/Emerald.CoreX/Store/Modrinth/IModrinthStore.cs
new file mode 100644
index 00000000..b764f6dc
--- /dev/null
+++ b/Emerald.CoreX/Store/Modrinth/IModrinthStore.cs
@@ -0,0 +1,19 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Emerald.CoreX.Store.Modrinth.JSON;
+
+namespace Emerald.CoreX.Store.Modrinth;
+
+public interface IModrinthStore
+{
+ public Task SearchAsync(string query, int limit = 15,
+ SearchSortOptions sortOptions = SearchSortOptions.Relevance, string[]? categories = null);
+ public Task LoadCategoriesAsync();
+ public Task GetItemAsync(string id);
+ public Task?> GetVersionsAsync(string id);
+ public Task DownloadItemAsync(ItemFile file, string projectType, IProgress? progress = null, CancellationToken cancellationToken = default);
+ public Category[] Categories { get; }
+}
diff --git a/Emerald.CoreX/Store/Modrinth/JSON.cs b/Emerald.CoreX/Store/Modrinth/JSON.cs
new file mode 100644
index 00000000..42bde2e5
--- /dev/null
+++ b/Emerald.CoreX/Store/Modrinth/JSON.cs
@@ -0,0 +1,207 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Text.Json;
+using System.Text.Json.Serialization;
+using Emerald.CoreX.Helpers;
+
+namespace Emerald.CoreX.Store.Modrinth.JSON;
+
+public class SearchResult
+{
+ [JsonPropertyName("hits")] public List Hits { get; set; }
+
+ [JsonPropertyName("offset")] public int Offset { get; set; }
+
+ [JsonPropertyName("limit")] public int Limit { get; set; }
+
+ [JsonPropertyName("total_hits")] public int TotalHits { get; set; }
+}
+
+public class Category
+{
+ public string icon { get; set; }
+ public string name { get; set; }
+ public string project_type { get; set; }
+ public string header { get; set; }
+}
+
+public class SearchHit
+{
+ [JsonPropertyName("slug")] public string Slug { get; set; }
+
+ [JsonPropertyName("title")] public string Title { get; set; }
+
+ [JsonPropertyName("description")] public string Description { get; set; }
+
+ [JsonPropertyName("categories")] public string[] Categories { get; set; }
+
+ [JsonPropertyName("client_side")] public string ClientSide { get; set; }
+
+ [JsonPropertyName("server_side")] public string ServerSide { get; set; }
+
+ [JsonPropertyName("project_type")] public string ProjectType { get; set; }
+
+ [JsonPropertyName("downloads")] public int Downloads { get; set; }
+
+ [JsonPropertyName("icon_url")] public string IconUrl { get; set; }
+
+ [JsonPropertyName("project_id")] public string ProjectId { get; set; }
+
+ [JsonPropertyName("author")] public string Author { get; set; }
+
+ [JsonPropertyName("versions")] public string[] Versions { get; set; }
+
+ [JsonPropertyName("follows")] public int Follows { get; set; }
+
+ [JsonPropertyName("date_created")] public DateTime DateCreated { get; set; }
+
+ [JsonPropertyName("date_modified")] public DateTime DateModified { get; set; }
+
+ [JsonPropertyName("latest_version")] public string LatestVersion { get; set; }
+
+ [JsonPropertyName("license")] public string License { get; set; }
+
+ [JsonPropertyName("gallery")] public string[] Gallery { get; set; }
+}
+
+public class StoreItem
+{
+ [JsonPropertyName("id")] public string ID { get; set; }
+
+ [JsonPropertyName("slug")] public string Slug { get; set; }
+
+ [JsonPropertyName("project_type")] public string ProjectType { get; set; }
+
+ [JsonPropertyName("team")] public string Team { get; set; }
+
+ [JsonPropertyName("title")] public string Title { get; set; }
+
+ [JsonPropertyName("description")] public string Description { get; set; }
+
+ [JsonPropertyName("body")] public string Body { get; set; }
+
+ [JsonPropertyName("body_url")] public string BodyUrl { get; set; }
+
+ [JsonPropertyName("published")] public DateTime PublishedDate { get; set; }
+
+ [JsonPropertyName("updated")] public DateTime UpdatedDate { get; set; }
+
+ [JsonPropertyName("status")] public string Status { get; set; }
+
+ [JsonPropertyName("moderator_message")] public object? ModeratorMessage { get; set; }
+
+ [JsonPropertyName("license")] public License License { get; set; }
+
+ [JsonPropertyName("client_side")] public string ClientSide { get; set; }
+
+ [JsonPropertyName("server_side")] public string ServerSide { get; set; }
+
+ [JsonPropertyName("downloads")] public int Downloads { get; set; }
+
+ [JsonPropertyName("followers")] public int Followers { get; set; }
+
+ [JsonPropertyName("categories")] public string[] Categories { get; set; }
+
+ [JsonPropertyName("versions")] public string[] Versions { get; set; }
+
+ [JsonPropertyName("icon_url")] public string IconUrl { get; set; }
+
+ [JsonPropertyName("issues_url")] public string IssuesUrl { get; set; }
+
+ [JsonPropertyName("source_url")] public string SourceUrl { get; set; }
+
+ [JsonPropertyName("wiki_url")] public object? WikiUrl { get; set; }
+
+ [JsonPropertyName("discord_url")] public string DiscordUrl { get; set; }
+
+ [JsonPropertyName("donation_urls")] public DonationUrls[] DonationUrls { get; set; }
+
+ [JsonPropertyName("gallery")] public object[] Gallery { get; set; }
+}
+
+public class ItemVersion : INotifyPropertyChanged
+{
+ public bool IsDetailsVisible { get; set; } = false;
+ public string? FileName => Files.FirstOrDefault(x => x.Primary)?.Filename;
+
+ [JsonPropertyName("id")] public string ID { get; set; }
+
+ [JsonPropertyName("project_id")] public string ProjectId { get; set; }
+
+ [JsonPropertyName("author_id")] public string AuthorId { get; set; }
+
+ [JsonPropertyName("featured")] public bool Featured { get; set; }
+
+ [JsonPropertyName("name")] public string Name { get; set; }
+
+ [JsonPropertyName("version_number")] public string VersionNumber { get; set; }
+
+ [JsonPropertyName("changelog")] public string Changelog { get; set; }
+
+ [JsonPropertyName("changelog_url")] public string? ChangelogUrl { get; set; }
+
+ [JsonPropertyName("date_published")] public DateTime DatePublished { get; set; }
+
+ [JsonPropertyName("downloads")] public int Downloads { get; set; }
+
+ [JsonPropertyName("version_type")] public string VersionType { get; set; }
+
+ [JsonPropertyName("files")] public ItemFile[] Files { get; set; }
+
+ [JsonPropertyName("dependencies")] public Dependency[] Dependencies { get; set; }
+
+ [JsonPropertyName("game_versions")] public string[] GameVersions { get; set; }
+
+ [JsonPropertyName("loaders")] public string[] Loaders { get; set; }
+
+ public event PropertyChangedEventHandler? PropertyChanged;
+
+ public void InvokePropertyChanged(string? propertyName = null) =>
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
+}
+
+public class Dependency
+{
+ [JsonPropertyName("version_id")] public string VersionId { get; set; }
+
+ [JsonPropertyName("project_id")] public string ProjectId { get; set; }
+
+ [JsonPropertyName("file_name")] public string FileName { get; set; }
+
+ [JsonPropertyName("dependency_type")] public string DependencyType { get; set; }
+}
+
+public class ItemFile
+{
+ [JsonPropertyName("hashes")] public Hashes Hashes { get; set; }
+
+ [JsonPropertyName("url")] public string Url { get; set; }
+
+ [JsonPropertyName("filename")] public string Filename { get; set; }
+
+ [JsonPropertyName("primary")] public bool Primary { get; set; }
+
+ [JsonPropertyName("size")] public int Size { get; set; }
+}
+
+public class License
+{
+ [JsonPropertyName("id")] public string ID { get; set; }
+
+ [JsonPropertyName("name")] public string Name { get; set; }
+
+ [JsonPropertyName("url")] public string Url { get; set; }
+}
+
+public class DonationUrls
+{
+ [JsonPropertyName("id")] public string ID { get; set; }
+
+ [JsonPropertyName("platform")] public string Platform { get; set; }
+
+ [JsonPropertyName("url")] public string Url { get; set; }
+}
diff --git a/Emerald.CoreX/Store/Modrinth/ModrinthStore.cs b/Emerald.CoreX/Store/Modrinth/ModrinthStore.cs
new file mode 100644
index 00000000..d905ee27
--- /dev/null
+++ b/Emerald.CoreX/Store/Modrinth/ModrinthStore.cs
@@ -0,0 +1,211 @@
+using CmlLib.Core;
+using System.Text.Json;
+using RestSharp;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.IO;
+using System.Linq;
+using System.Threading.Tasks;
+using Microsoft.Extensions.Logging;
+using Emerald.CoreX.Store.Modrinth.JSON;
+using Emerald.CoreX.Helpers;
+
+namespace Emerald.CoreX.Store.Modrinth;
+
+public abstract class ModrinthStore : IModrinthStore
+{
+ protected readonly RestClient _client;
+ protected readonly ILogger _logger;
+ protected readonly string _projectType;
+ protected readonly FileDownloader _fileDownloader;
+ public MinecraftPath MCPath { get; }
+ public Category[] Categories { get; private set; } = [];
+
+ ///
+ /// Initializes a new instance of the ModrinthStore class.
+ ///
+ /// The Minecraft path.
+ /// The logger instance.
+ /// The type of project (e.g., mod, plugin, resourcepack).
+ protected ModrinthStore(MinecraftPath path, ILogger logger, string projectType)
+ {
+ _client = new RestClient("https://api.modrinth.com/v2/");
+ _client.AddDefaultHeader("Accept", "application/json");
+ MCPath = path;
+ _logger = logger;
+ _projectType = projectType;
+ _fileDownloader = new FileDownloader(logger, new());
+ }
+
+
+ ///
+ /// Loads categories for the specified project type from the Modrinth API.
+ ///
+ /// A task representing the asynchronous operation.
+ public async Task LoadCategoriesAsync()
+ {
+ var request = new RestRequest("tag/category");
+
+ try
+ {
+ var response = await _client.ExecuteAsync(request);
+ if (response.IsSuccessful)
+ {
+ var all = JsonSerializer.Deserialize>(response.Content);
+
+ var _categories = all
+ .Where(i => i.header == "categories"
+ && i.project_type == _projectType
+ && !string.IsNullOrWhiteSpace(i.icon)
+ && !string.IsNullOrWhiteSpace(i.name))
+ .ToList();
+ Categories = _categories.ToArray();
+ _logger.LogInformation($"Loaded {_categories.Count} {_projectType} categories.");
+ }
+ else
+ {
+ _logger.LogError($"Failed to load {_projectType} categories. Status code: {response.StatusCode}");
+ }
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, $"Error occurred while loading {_projectType} categories.");
+ }
+ }
+
+ ///
+ /// Searches for items in the Modrinth store based on the provided query and options.
+ ///
+ /// The search query string.
+ /// The maximum number of results to return (default is 15).
+ /// The sorting options for the search results.
+ /// An optional array of category names to filter the results.
+ /// A task that represents the asynchronous operation. The task result contains the search results or null if an error occurred.
+ public virtual async Task SearchAsync(string query, int limit = 15,
+ SearchSortOptions sortOptions = SearchSortOptions.Relevance, string[]? categories = null)
+ {
+ _logger.LogInformation($"Searching store for {_projectType}s with query: {query}");
+
+ try
+ {
+ // Prepare the facets parameter correctly
+ string facets = "[[\"project_type:" + _projectType + "\"]";
+ if (categories != null && categories.Length != 0)
+ {
+ var categoryFacets = categories.Select(cat => $"\"categories:{cat}\"");
+ facets += ",[" + string.Join(",", categoryFacets) + "]";
+ }
+ facets += "]";
+
+ var request = new RestRequest("search")
+ .AddParameter("index", sortOptions.ToString().ToLowerInvariant())
+ .AddParameter("facets", facets)
+ .AddParameter("limit", limit);
+
+ if (!string.IsNullOrEmpty(query))
+ {
+ request.AddParameter("query", query);
+ }
+
+ var response = await _client.ExecuteAsync(request);
+ if (response.IsSuccessful)
+ {
+ var result = JsonSerializer.Deserialize(response.Content);
+ _logger.LogInformation($"Search completed successfully. Found {result.TotalHits} {_projectType}s.");
+ return result;
+ }
+ else
+ {
+ _logger.LogError($"API request failed: {response.ErrorMessage}");
+ throw new Exception($"API request failed: {response.ErrorMessage}");
+ }
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, $"Error occurred while searching for {_projectType}s");
+ return null;
+ }
+ }
+
+ ///
+ /// Retrieves detailed information about a specific item from the Modrinth store.
+ ///
+ /// The unique identifier of the item.
+ /// A task that represents the asynchronous operation. The task result contains the item details or null if an error occurred.
+ public virtual async Task GetItemAsync(string id)
+ {
+ _logger.LogInformation($"Fetching {_projectType} with ID: {id}");
+
+ try
+ {
+ var request = new RestRequest($"project/{id}");
+ var response = await _client.ExecuteAsync(request);
+
+ if (response.IsSuccessful)
+ {
+ var item = JsonSerializer.Deserialize(response.Content);
+ _logger.LogInformation($"Successfully fetched {_projectType} with ID: {id}");
+ return item;
+ }
+ else
+ {
+ _logger.LogError($"API request failed: {response.ErrorMessage}");
+ throw new Exception($"API request failed: {response.ErrorMessage}");
+ }
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, $"Error occurred while fetching {_projectType} with ID: {id}");
+ return null;
+ }
+ }
+
+ ///
+ /// Retrieves all versions of a specific item from the Modrinth store.
+ ///
+ /// The unique identifier of the item.
+ /// A task that represents the asynchronous operation. The task result contains a list of item versions or null if an error occurred.
+ public virtual async Task?> GetVersionsAsync(string id)
+ {
+ _logger.LogInformation($"Fetching versions for {_projectType} with ID: {id}");
+
+ try
+ {
+ var request = new RestRequest($"project/{id}/version");
+ var response = await _client.ExecuteAsync(request);
+
+ if (response.IsSuccessful)
+ {
+ var versions = JsonSerializer.Deserialize>(response.Content);
+ _logger.LogInformation($"Successfully fetched versions for {_projectType} with ID: {id}");
+ return versions;
+ }
+ else
+ {
+ _logger.LogError($"API request failed: {response.ErrorMessage}");
+ throw new Exception($"API request failed: {response.ErrorMessage}");
+ }
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, $"Error occurred while fetching versions for {_projectType} with ID: {id}");
+ return null;
+ }
+ }
+
+ ///
+ /// Downloads a specific file for an item from the Modrinth store.
+ ///
+ /// The file information object containing download details.
+ /// The type of project being downloaded (e.g., "mods", "resourcepacks").
+ /// Optional. An IProgress{double} to report download progress.
+ /// Optional. A CancellationToken to support cancellation of the download.
+ /// A task that represents the asynchronous download operation.
+ public virtual async Task DownloadItemAsync(ItemFile file, string projectType,
+ IProgress? progress = null, CancellationToken cancellationToken = default)
+ {
+ var filePath = Path.Combine(MCPath.BasePath, projectType, file.Filename);
+ await _fileDownloader.DownloadFileAsync(file.Url, filePath, file.Hashes, progress, cancellationToken);
+ }
+}
diff --git a/Emerald.CoreX/Store/Modrinth/SearchSortOptions.cs b/Emerald.CoreX/Store/Modrinth/SearchSortOptions.cs
new file mode 100644
index 00000000..416e8af9
--- /dev/null
+++ b/Emerald.CoreX/Store/Modrinth/SearchSortOptions.cs
@@ -0,0 +1,15 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Emerald.CoreX.Store.Modrinth;
+public enum SearchSortOptions
+{
+ Relevance,
+ Downloads,
+ Follows,
+ Updated,
+ Newest
+}
diff --git a/Emerald.CoreX/Store/Modrinth/Stores.cs b/Emerald.CoreX/Store/Modrinth/Stores.cs
new file mode 100644
index 00000000..9f2b48b0
--- /dev/null
+++ b/Emerald.CoreX/Store/Modrinth/Stores.cs
@@ -0,0 +1,197 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Emerald.CoreX.Store.Modrinth.JSON;
+using Microsoft.Extensions.Logging;
+using CmlLib.Core;
+namespace Emerald.CoreX.Store.Modrinth;
+
+public class ModStore : ModrinthStore
+{
+ ///
+ /// Initializes a new instance of the ModStore class with a custom Minecraft path.
+ ///
+ /// The custom Minecraft path.
+ /// The logger instance.
+ public ModStore(MinecraftPath path, ILogger logger) : base(path, logger, "mod")
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the ModStore class with the default Minecraft path.
+ ///
+ /// The logger instance.
+ public ModStore(ILogger logger) : this(new MinecraftPath(MinecraftPath.GetOSDefaultPath()), logger)
+ {
+ }
+
+ public override async Task GetItemAsync(string id)
+ {
+ // Implement mod-specific logic if needed
+ return await base.GetItemAsync(id);
+ }
+
+ public override async Task?> GetVersionsAsync(string id)
+ {
+ // Implement mod-specific logic if needed
+ return await base.GetVersionsAsync(id);
+ }
+
+ public override async Task DownloadItemAsync(ItemFile file, string projectType, IProgress? progress = null, CancellationToken cancellationToken = default)
+ {
+ // Implement mod-specific download logic
+ await base.DownloadItemAsync(file, "mods",progress, cancellationToken);
+ }
+}
+
+public class PluginStore : ModrinthStore
+{
+ ///
+ /// Initializes a new instance of the PluginStore class with a custom Minecraft path.
+ ///
+ /// The custom Minecraft path.
+ /// The logger instance.
+ public PluginStore(MinecraftPath path, ILogger logger) : base(path, logger, "plugin")
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the PluginStore class with the default Minecraft path.
+ ///
+ /// The logger instance.
+ public PluginStore(ILogger logger) : this(new MinecraftPath(MinecraftPath.GetOSDefaultPath()), logger)
+ {
+ }
+
+ public override async Task GetItemAsync(string id)
+ {
+ return await base.GetItemAsync(id);
+ }
+
+ public override async Task?> GetVersionsAsync(string id)
+ {
+ return await base.GetVersionsAsync(id);
+ }
+
+ public override async Task DownloadItemAsync(ItemFile file, string projectType,
+ IProgress? progress = null, CancellationToken cancellationToken = default)
+ {
+ await base.DownloadItemAsync(file, "mods", progress, cancellationToken);
+ }
+}
+
+public class ResourcePackStore : ModrinthStore
+{
+ ///
+ /// Initializes a new instance of the ResourcePackStore class with a custom Minecraft path.
+ ///
+ /// The custom Minecraft path.
+ /// The logger instance.
+ public ResourcePackStore(MinecraftPath path, ILogger logger) : base(path, logger, "resourcepack")
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the ResourcePackStore class with the default Minecraft path.
+ ///
+ /// The logger instance.
+ public ResourcePackStore(ILogger logger) : this(new MinecraftPath(MinecraftPath.GetOSDefaultPath()), logger)
+ {
+ }
+
+ public override async Task GetItemAsync(string id)
+ {
+ return await base.GetItemAsync(id);
+ }
+
+ public override async Task?> GetVersionsAsync(string id)
+ {
+ return await base.GetVersionsAsync(id);
+ }
+
+ public override async Task DownloadItemAsync(ItemFile file, string projectType,
+ IProgress? progress = null, CancellationToken cancellationToken = default)
+ {
+ await base.DownloadItemAsync(file, "resourcepacks", progress, cancellationToken);
+ }
+}
+
+public class ShaderStore : ModrinthStore
+{
+ ///
+ /// Initializes a new instance of the ShaderStore class with a custom Minecraft path.
+ ///
+ /// The custom Minecraft path.
+ /// The logger instance.
+ public ShaderStore(MinecraftPath path, ILogger logger) : base(path, logger, "shader")
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the ShaderStore class with the default Minecraft path.
+ ///
+ /// The logger instance.
+ public ShaderStore(ILogger logger) : this(new MinecraftPath(MinecraftPath.GetOSDefaultPath()), logger)
+ {
+ }
+
+ public override async Task GetItemAsync(string id)
+ {
+ // Implement shader-specific logic if needed
+ return await base.GetItemAsync(id);
+ }
+
+ public override async Task?> GetVersionsAsync(string id)
+ {
+ // Implement shader-specific logic if needed
+ return await base.GetVersionsAsync(id);
+ }
+
+ public override async Task DownloadItemAsync(ItemFile file, string projectType,
+ IProgress? progress = null, CancellationToken cancellationToken = default)
+ {
+ // Implement shader-specific download logic
+ await base.DownloadItemAsync(file, "shaderpacks", progress, cancellationToken);
+ }
+}
+
+public class ModpackStore : ModrinthStore
+{
+ ///
+ /// Initializes a new instance of the ModpackStore class with a custom Minecraft path.
+ ///
+ /// The custom Minecraft path.
+ /// The logger instance.
+ public ModpackStore(MinecraftPath path, ILogger logger) : base(path, logger, "modpack")
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the ModpackStore class with the default Minecraft path.
+ ///
+ /// The logger instance.
+ public ModpackStore(ILogger logger) : this(new MinecraftPath(MinecraftPath.GetOSDefaultPath()), logger)
+ {
+ }
+
+ public override async Task GetItemAsync(string id)
+ {
+ // Implement modpack-specific logic if needed
+ return await base.GetItemAsync(id);
+ }
+
+ public override async Task?> GetVersionsAsync(string id)
+ {
+ // Implement modpack-specific logic if needed
+ return await base.GetVersionsAsync(id);
+ }
+
+ public override async Task DownloadItemAsync(ItemFile file, string projectType,
+ IProgress? progress = null, CancellationToken cancellationToken = default)
+ {
+ // Implement modpack-specific download logic
+ await base.DownloadItemAsync(file, "modpacks", progress, cancellationToken);
+ }
+}
diff --git a/Emerald.sln b/Emerald.sln
index 053ff471..bb5184e0 100644
--- a/Emerald.sln
+++ b/Emerald.sln
@@ -1,87 +1,109 @@
+
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
-VisualStudioVersion = 17.2.32602.215
+VisualStudioVersion = 17.11.35103.136
MinimumVisualStudioVersion = 10.0.40219.1
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Emerald.Core", "Emerald.Core\Emerald.Core.csproj", "{90389A34-9F42-4489-871A-DC726D74854D}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Emerald", "Emerald\Emerald.csproj", "{9D3213F4-E514-4E7D-872A-725DB4872436}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Emerald.App", "Emerald.App\Emerald.App\Emerald.App.csproj", "{F2065381-1265-4FEB-841A-E5798E62761D}"
-EndProject
-Project("{C7167F0D-BC9F-4E6E-AFE1-012C56B48DB5}") = "Package.WinUI", "Emerald.App\Emerald.App.Package\Package.WinUI.wapproj", "{59A6F3D1-B6E1-48AD-80D3-215DF2791AC1}"
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "project", "project", "{2BB1A64C-5C5D-415D-B816-66E2152E2703}"
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{3142BDF3-E95C-4F0F-8701-CF981F6EA3C0}"
ProjectSection(SolutionItems) = preProject
- .editorconfig = .editorconfig
- .gitattributes = .gitattributes
.gitignore = .gitignore
- .github\azp\azp.yml = .github\azp\azp.yml
- README.md = README.md
+ Directory.Build.props = Directory.Build.props
+ Directory.Build.targets = Directory.Build.targets
+ Directory.Packages.props = Directory.Packages.props
+ global.json = global.json
EndProjectSection
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CmlLib", "CMLLib\CmlLib\CmlLib.csproj", "{7FAC2A8B-C2EF-47FA-9598-FB2B414A7156}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Emerald.App", "Emerald.App\Emerald.App\Emerald.App.csproj", "{196FD412-FCBA-4266-A75E-5648F6AADDFB}"
+EndProject
+Project("{C7167F0D-BC9F-4E6E-AFE1-012C56B48DB5}") = "Package.WinUI", "Emerald.App\Emerald.App.Package\Package.WinUI.wapproj", "{59A6F3D1-B6E1-48AD-80D3-215DF2791AC1}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Emerald.Core", "Emerald.Core\Emerald.Core.csproj", "{BE4AEE6B-3F2E-4FFB-940D-3E4916EAE913}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Emerald.CoreX", "Emerald.CoreX\Emerald.CoreX.csproj", "{D103EFF9-90EA-49C7-9DD9-636E6056E9C3}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
- Debug|ARM = Debug|ARM
- Debug|ARM64 = Debug|ARM64
+ Debug|arm64 = Debug|arm64
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
- Release|ARM = Release|ARM
- Release|ARM64 = Release|ARM64
+ Release|arm64 = Release|arm64
Release|x64 = Release|x64
Release|x86 = Release|x86
+ SkipOld|Any CPU = SkipOld|Any CPU
+ SkipOld|arm64 = SkipOld|arm64
+ SkipOld|x64 = SkipOld|x64
+ SkipOld|x86 = SkipOld|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {90389A34-9F42-4489-871A-DC726D74854D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {90389A34-9F42-4489-871A-DC726D74854D}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {90389A34-9F42-4489-871A-DC726D74854D}.Debug|ARM.ActiveCfg = Debug|Any CPU
- {90389A34-9F42-4489-871A-DC726D74854D}.Debug|ARM.Build.0 = Debug|Any CPU
- {90389A34-9F42-4489-871A-DC726D74854D}.Debug|ARM64.ActiveCfg = Debug|Any CPU
- {90389A34-9F42-4489-871A-DC726D74854D}.Debug|ARM64.Build.0 = Debug|Any CPU
- {90389A34-9F42-4489-871A-DC726D74854D}.Debug|x64.ActiveCfg = Debug|Any CPU
- {90389A34-9F42-4489-871A-DC726D74854D}.Debug|x64.Build.0 = Debug|Any CPU
- {90389A34-9F42-4489-871A-DC726D74854D}.Debug|x86.ActiveCfg = Debug|Any CPU
- {90389A34-9F42-4489-871A-DC726D74854D}.Debug|x86.Build.0 = Debug|Any CPU
- {90389A34-9F42-4489-871A-DC726D74854D}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {90389A34-9F42-4489-871A-DC726D74854D}.Release|Any CPU.Build.0 = Release|Any CPU
- {90389A34-9F42-4489-871A-DC726D74854D}.Release|ARM.ActiveCfg = Release|Any CPU
- {90389A34-9F42-4489-871A-DC726D74854D}.Release|ARM.Build.0 = Release|Any CPU
- {90389A34-9F42-4489-871A-DC726D74854D}.Release|ARM64.ActiveCfg = Release|Any CPU
- {90389A34-9F42-4489-871A-DC726D74854D}.Release|ARM64.Build.0 = Release|Any CPU
- {90389A34-9F42-4489-871A-DC726D74854D}.Release|x64.ActiveCfg = Release|Any CPU
- {90389A34-9F42-4489-871A-DC726D74854D}.Release|x64.Build.0 = Release|Any CPU
- {90389A34-9F42-4489-871A-DC726D74854D}.Release|x86.ActiveCfg = Release|Any CPU
- {90389A34-9F42-4489-871A-DC726D74854D}.Release|x86.Build.0 = Release|Any CPU
- {F2065381-1265-4FEB-841A-E5798E62761D}.Debug|Any CPU.ActiveCfg = Debug|x64
- {F2065381-1265-4FEB-841A-E5798E62761D}.Debug|Any CPU.Build.0 = Debug|x64
- {F2065381-1265-4FEB-841A-E5798E62761D}.Debug|ARM.ActiveCfg = Debug|x86
- {F2065381-1265-4FEB-841A-E5798E62761D}.Debug|ARM.Build.0 = Debug|x86
- {F2065381-1265-4FEB-841A-E5798E62761D}.Debug|ARM64.ActiveCfg = Debug|arm64
- {F2065381-1265-4FEB-841A-E5798E62761D}.Debug|ARM64.Build.0 = Debug|arm64
- {F2065381-1265-4FEB-841A-E5798E62761D}.Debug|x64.ActiveCfg = Debug|x64
- {F2065381-1265-4FEB-841A-E5798E62761D}.Debug|x64.Build.0 = Debug|x64
- {F2065381-1265-4FEB-841A-E5798E62761D}.Debug|x86.ActiveCfg = Debug|x86
- {F2065381-1265-4FEB-841A-E5798E62761D}.Debug|x86.Build.0 = Debug|x86
- {F2065381-1265-4FEB-841A-E5798E62761D}.Release|Any CPU.ActiveCfg = Release|x64
- {F2065381-1265-4FEB-841A-E5798E62761D}.Release|Any CPU.Build.0 = Release|x64
- {F2065381-1265-4FEB-841A-E5798E62761D}.Release|ARM.ActiveCfg = Release|x64
- {F2065381-1265-4FEB-841A-E5798E62761D}.Release|ARM.Build.0 = Release|x64
- {F2065381-1265-4FEB-841A-E5798E62761D}.Release|ARM64.ActiveCfg = Release|arm64
- {F2065381-1265-4FEB-841A-E5798E62761D}.Release|ARM64.Build.0 = Release|arm64
- {F2065381-1265-4FEB-841A-E5798E62761D}.Release|x64.ActiveCfg = Release|x64
- {F2065381-1265-4FEB-841A-E5798E62761D}.Release|x64.Build.0 = Release|x64
- {F2065381-1265-4FEB-841A-E5798E62761D}.Release|x86.ActiveCfg = Release|x86
- {F2065381-1265-4FEB-841A-E5798E62761D}.Release|x86.Build.0 = Release|x86
+ {9D3213F4-E514-4E7D-872A-725DB4872436}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {9D3213F4-E514-4E7D-872A-725DB4872436}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {9D3213F4-E514-4E7D-872A-725DB4872436}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
+ {9D3213F4-E514-4E7D-872A-725DB4872436}.Debug|arm64.ActiveCfg = Debug|Any CPU
+ {9D3213F4-E514-4E7D-872A-725DB4872436}.Debug|arm64.Build.0 = Debug|Any CPU
+ {9D3213F4-E514-4E7D-872A-725DB4872436}.Debug|arm64.Deploy.0 = Debug|Any CPU
+ {9D3213F4-E514-4E7D-872A-725DB4872436}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {9D3213F4-E514-4E7D-872A-725DB4872436}.Debug|x64.Build.0 = Debug|Any CPU
+ {9D3213F4-E514-4E7D-872A-725DB4872436}.Debug|x64.Deploy.0 = Debug|Any CPU
+ {9D3213F4-E514-4E7D-872A-725DB4872436}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {9D3213F4-E514-4E7D-872A-725DB4872436}.Debug|x86.Build.0 = Debug|Any CPU
+ {9D3213F4-E514-4E7D-872A-725DB4872436}.Debug|x86.Deploy.0 = Debug|Any CPU
+ {9D3213F4-E514-4E7D-872A-725DB4872436}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {9D3213F4-E514-4E7D-872A-725DB4872436}.Release|Any CPU.Build.0 = Release|Any CPU
+ {9D3213F4-E514-4E7D-872A-725DB4872436}.Release|Any CPU.Deploy.0 = Release|Any CPU
+ {9D3213F4-E514-4E7D-872A-725DB4872436}.Release|arm64.ActiveCfg = Release|Any CPU
+ {9D3213F4-E514-4E7D-872A-725DB4872436}.Release|arm64.Build.0 = Release|Any CPU
+ {9D3213F4-E514-4E7D-872A-725DB4872436}.Release|arm64.Deploy.0 = Release|Any CPU
+ {9D3213F4-E514-4E7D-872A-725DB4872436}.Release|x64.ActiveCfg = Release|Any CPU
+ {9D3213F4-E514-4E7D-872A-725DB4872436}.Release|x64.Build.0 = Release|Any CPU
+ {9D3213F4-E514-4E7D-872A-725DB4872436}.Release|x64.Deploy.0 = Release|Any CPU
+ {9D3213F4-E514-4E7D-872A-725DB4872436}.Release|x86.ActiveCfg = Release|Any CPU
+ {9D3213F4-E514-4E7D-872A-725DB4872436}.Release|x86.Build.0 = Release|Any CPU
+ {9D3213F4-E514-4E7D-872A-725DB4872436}.Release|x86.Deploy.0 = Release|Any CPU
+ {9D3213F4-E514-4E7D-872A-725DB4872436}.SkipOld|Any CPU.ActiveCfg = Release|Any CPU
+ {9D3213F4-E514-4E7D-872A-725DB4872436}.SkipOld|Any CPU.Build.0 = Release|Any CPU
+ {9D3213F4-E514-4E7D-872A-725DB4872436}.SkipOld|Any CPU.Deploy.0 = Release|Any CPU
+ {9D3213F4-E514-4E7D-872A-725DB4872436}.SkipOld|arm64.ActiveCfg = Release|Any CPU
+ {9D3213F4-E514-4E7D-872A-725DB4872436}.SkipOld|arm64.Build.0 = Release|Any CPU
+ {9D3213F4-E514-4E7D-872A-725DB4872436}.SkipOld|arm64.Deploy.0 = Release|Any CPU
+ {9D3213F4-E514-4E7D-872A-725DB4872436}.SkipOld|x64.ActiveCfg = Release|Any CPU
+ {9D3213F4-E514-4E7D-872A-725DB4872436}.SkipOld|x64.Build.0 = Release|Any CPU
+ {9D3213F4-E514-4E7D-872A-725DB4872436}.SkipOld|x64.Deploy.0 = Release|Any CPU
+ {9D3213F4-E514-4E7D-872A-725DB4872436}.SkipOld|x86.ActiveCfg = Release|Any CPU
+ {9D3213F4-E514-4E7D-872A-725DB4872436}.SkipOld|x86.Build.0 = Release|Any CPU
+ {9D3213F4-E514-4E7D-872A-725DB4872436}.SkipOld|x86.Deploy.0 = Release|Any CPU
+ {196FD412-FCBA-4266-A75E-5648F6AADDFB}.Debug|Any CPU.ActiveCfg = Debug|x64
+ {196FD412-FCBA-4266-A75E-5648F6AADDFB}.Debug|Any CPU.Build.0 = Debug|x64
+ {196FD412-FCBA-4266-A75E-5648F6AADDFB}.Debug|arm64.ActiveCfg = Debug|arm64
+ {196FD412-FCBA-4266-A75E-5648F6AADDFB}.Debug|arm64.Build.0 = Debug|arm64
+ {196FD412-FCBA-4266-A75E-5648F6AADDFB}.Debug|x64.ActiveCfg = Debug|x64
+ {196FD412-FCBA-4266-A75E-5648F6AADDFB}.Debug|x64.Build.0 = Debug|x64
+ {196FD412-FCBA-4266-A75E-5648F6AADDFB}.Debug|x86.ActiveCfg = Debug|x86
+ {196FD412-FCBA-4266-A75E-5648F6AADDFB}.Debug|x86.Build.0 = Debug|x86
+ {196FD412-FCBA-4266-A75E-5648F6AADDFB}.Release|Any CPU.ActiveCfg = Release|x64
+ {196FD412-FCBA-4266-A75E-5648F6AADDFB}.Release|Any CPU.Build.0 = Release|x64
+ {196FD412-FCBA-4266-A75E-5648F6AADDFB}.Release|arm64.ActiveCfg = Release|arm64
+ {196FD412-FCBA-4266-A75E-5648F6AADDFB}.Release|arm64.Build.0 = Release|arm64
+ {196FD412-FCBA-4266-A75E-5648F6AADDFB}.Release|x64.ActiveCfg = Release|x64
+ {196FD412-FCBA-4266-A75E-5648F6AADDFB}.Release|x64.Build.0 = Release|x64
+ {196FD412-FCBA-4266-A75E-5648F6AADDFB}.Release|x86.ActiveCfg = Release|x86
+ {196FD412-FCBA-4266-A75E-5648F6AADDFB}.Release|x86.Build.0 = Release|x86
+ {196FD412-FCBA-4266-A75E-5648F6AADDFB}.SkipOld|Any CPU.ActiveCfg = Debug|x64
+ {196FD412-FCBA-4266-A75E-5648F6AADDFB}.SkipOld|Any CPU.Build.0 = Debug|x64
+ {196FD412-FCBA-4266-A75E-5648F6AADDFB}.SkipOld|arm64.ActiveCfg = Debug|arm64
+ {196FD412-FCBA-4266-A75E-5648F6AADDFB}.SkipOld|arm64.Build.0 = Debug|arm64
+ {196FD412-FCBA-4266-A75E-5648F6AADDFB}.SkipOld|x64.ActiveCfg = Debug|x64
+ {196FD412-FCBA-4266-A75E-5648F6AADDFB}.SkipOld|x64.Build.0 = Debug|x64
+ {196FD412-FCBA-4266-A75E-5648F6AADDFB}.SkipOld|x86.ActiveCfg = Debug|x86
+ {196FD412-FCBA-4266-A75E-5648F6AADDFB}.SkipOld|x86.Build.0 = Debug|x86
{59A6F3D1-B6E1-48AD-80D3-215DF2791AC1}.Debug|Any CPU.ActiveCfg = Debug|x64
{59A6F3D1-B6E1-48AD-80D3-215DF2791AC1}.Debug|Any CPU.Build.0 = Debug|x64
{59A6F3D1-B6E1-48AD-80D3-215DF2791AC1}.Debug|Any CPU.Deploy.0 = Debug|x64
- {59A6F3D1-B6E1-48AD-80D3-215DF2791AC1}.Debug|ARM.ActiveCfg = Debug|x86
- {59A6F3D1-B6E1-48AD-80D3-215DF2791AC1}.Debug|ARM.Build.0 = Debug|x86
- {59A6F3D1-B6E1-48AD-80D3-215DF2791AC1}.Debug|ARM.Deploy.0 = Debug|x86
- {59A6F3D1-B6E1-48AD-80D3-215DF2791AC1}.Debug|ARM64.ActiveCfg = Debug|arm64
- {59A6F3D1-B6E1-48AD-80D3-215DF2791AC1}.Debug|ARM64.Build.0 = Debug|arm64
- {59A6F3D1-B6E1-48AD-80D3-215DF2791AC1}.Debug|ARM64.Deploy.0 = Debug|arm64
+ {59A6F3D1-B6E1-48AD-80D3-215DF2791AC1}.Debug|arm64.ActiveCfg = Debug|arm64
+ {59A6F3D1-B6E1-48AD-80D3-215DF2791AC1}.Debug|arm64.Build.0 = Debug|arm64
+ {59A6F3D1-B6E1-48AD-80D3-215DF2791AC1}.Debug|arm64.Deploy.0 = Debug|arm64
{59A6F3D1-B6E1-48AD-80D3-215DF2791AC1}.Debug|x64.ActiveCfg = Debug|x64
{59A6F3D1-B6E1-48AD-80D3-215DF2791AC1}.Debug|x64.Build.0 = Debug|x64
{59A6F3D1-B6E1-48AD-80D3-215DF2791AC1}.Debug|x64.Deploy.0 = Debug|x64
@@ -91,43 +113,80 @@ Global
{59A6F3D1-B6E1-48AD-80D3-215DF2791AC1}.Release|Any CPU.ActiveCfg = Release|x64
{59A6F3D1-B6E1-48AD-80D3-215DF2791AC1}.Release|Any CPU.Build.0 = Release|x64
{59A6F3D1-B6E1-48AD-80D3-215DF2791AC1}.Release|Any CPU.Deploy.0 = Release|x64
- {59A6F3D1-B6E1-48AD-80D3-215DF2791AC1}.Release|ARM.ActiveCfg = Release|x64
- {59A6F3D1-B6E1-48AD-80D3-215DF2791AC1}.Release|ARM.Build.0 = Release|x64
- {59A6F3D1-B6E1-48AD-80D3-215DF2791AC1}.Release|ARM.Deploy.0 = Release|x64
- {59A6F3D1-B6E1-48AD-80D3-215DF2791AC1}.Release|ARM64.ActiveCfg = Release|arm64
- {59A6F3D1-B6E1-48AD-80D3-215DF2791AC1}.Release|ARM64.Build.0 = Release|arm64
- {59A6F3D1-B6E1-48AD-80D3-215DF2791AC1}.Release|ARM64.Deploy.0 = Release|arm64
+ {59A6F3D1-B6E1-48AD-80D3-215DF2791AC1}.Release|arm64.ActiveCfg = Release|arm64
+ {59A6F3D1-B6E1-48AD-80D3-215DF2791AC1}.Release|arm64.Build.0 = Release|arm64
+ {59A6F3D1-B6E1-48AD-80D3-215DF2791AC1}.Release|arm64.Deploy.0 = Release|arm64
{59A6F3D1-B6E1-48AD-80D3-215DF2791AC1}.Release|x64.ActiveCfg = Release|x64
{59A6F3D1-B6E1-48AD-80D3-215DF2791AC1}.Release|x64.Build.0 = Release|x64
{59A6F3D1-B6E1-48AD-80D3-215DF2791AC1}.Release|x64.Deploy.0 = Release|x64
{59A6F3D1-B6E1-48AD-80D3-215DF2791AC1}.Release|x86.ActiveCfg = Release|x86
{59A6F3D1-B6E1-48AD-80D3-215DF2791AC1}.Release|x86.Build.0 = Release|x86
{59A6F3D1-B6E1-48AD-80D3-215DF2791AC1}.Release|x86.Deploy.0 = Release|x86
- {7FAC2A8B-C2EF-47FA-9598-FB2B414A7156}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {7FAC2A8B-C2EF-47FA-9598-FB2B414A7156}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {7FAC2A8B-C2EF-47FA-9598-FB2B414A7156}.Debug|ARM.ActiveCfg = Debug|Any CPU
- {7FAC2A8B-C2EF-47FA-9598-FB2B414A7156}.Debug|ARM.Build.0 = Debug|Any CPU
- {7FAC2A8B-C2EF-47FA-9598-FB2B414A7156}.Debug|ARM64.ActiveCfg = Debug|Any CPU
- {7FAC2A8B-C2EF-47FA-9598-FB2B414A7156}.Debug|ARM64.Build.0 = Debug|Any CPU
- {7FAC2A8B-C2EF-47FA-9598-FB2B414A7156}.Debug|x64.ActiveCfg = Debug|Any CPU
- {7FAC2A8B-C2EF-47FA-9598-FB2B414A7156}.Debug|x64.Build.0 = Debug|Any CPU
- {7FAC2A8B-C2EF-47FA-9598-FB2B414A7156}.Debug|x86.ActiveCfg = Debug|Any CPU
- {7FAC2A8B-C2EF-47FA-9598-FB2B414A7156}.Debug|x86.Build.0 = Debug|Any CPU
- {7FAC2A8B-C2EF-47FA-9598-FB2B414A7156}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {7FAC2A8B-C2EF-47FA-9598-FB2B414A7156}.Release|Any CPU.Build.0 = Release|Any CPU
- {7FAC2A8B-C2EF-47FA-9598-FB2B414A7156}.Release|ARM.ActiveCfg = Release|Any CPU
- {7FAC2A8B-C2EF-47FA-9598-FB2B414A7156}.Release|ARM.Build.0 = Release|Any CPU
- {7FAC2A8B-C2EF-47FA-9598-FB2B414A7156}.Release|ARM64.ActiveCfg = Release|Any CPU
- {7FAC2A8B-C2EF-47FA-9598-FB2B414A7156}.Release|ARM64.Build.0 = Release|Any CPU
- {7FAC2A8B-C2EF-47FA-9598-FB2B414A7156}.Release|x64.ActiveCfg = Release|Any CPU
- {7FAC2A8B-C2EF-47FA-9598-FB2B414A7156}.Release|x64.Build.0 = Release|Any CPU
- {7FAC2A8B-C2EF-47FA-9598-FB2B414A7156}.Release|x86.ActiveCfg = Release|Any CPU
- {7FAC2A8B-C2EF-47FA-9598-FB2B414A7156}.Release|x86.Build.0 = Release|Any CPU
+ {59A6F3D1-B6E1-48AD-80D3-215DF2791AC1}.SkipOld|Any CPU.ActiveCfg = SkipOld|x64
+ {59A6F3D1-B6E1-48AD-80D3-215DF2791AC1}.SkipOld|Any CPU.Build.0 = SkipOld|x64
+ {59A6F3D1-B6E1-48AD-80D3-215DF2791AC1}.SkipOld|Any CPU.Deploy.0 = SkipOld|x64
+ {59A6F3D1-B6E1-48AD-80D3-215DF2791AC1}.SkipOld|arm64.ActiveCfg = SkipOld|arm64
+ {59A6F3D1-B6E1-48AD-80D3-215DF2791AC1}.SkipOld|arm64.Build.0 = SkipOld|arm64
+ {59A6F3D1-B6E1-48AD-80D3-215DF2791AC1}.SkipOld|arm64.Deploy.0 = SkipOld|arm64
+ {59A6F3D1-B6E1-48AD-80D3-215DF2791AC1}.SkipOld|x64.ActiveCfg = SkipOld|x64
+ {59A6F3D1-B6E1-48AD-80D3-215DF2791AC1}.SkipOld|x64.Build.0 = SkipOld|x64
+ {59A6F3D1-B6E1-48AD-80D3-215DF2791AC1}.SkipOld|x64.Deploy.0 = SkipOld|x64
+ {59A6F3D1-B6E1-48AD-80D3-215DF2791AC1}.SkipOld|x86.ActiveCfg = SkipOld|x86
+ {59A6F3D1-B6E1-48AD-80D3-215DF2791AC1}.SkipOld|x86.Build.0 = SkipOld|x86
+ {59A6F3D1-B6E1-48AD-80D3-215DF2791AC1}.SkipOld|x86.Deploy.0 = SkipOld|x86
+ {BE4AEE6B-3F2E-4FFB-940D-3E4916EAE913}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {BE4AEE6B-3F2E-4FFB-940D-3E4916EAE913}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {BE4AEE6B-3F2E-4FFB-940D-3E4916EAE913}.Debug|arm64.ActiveCfg = Debug|Any CPU
+ {BE4AEE6B-3F2E-4FFB-940D-3E4916EAE913}.Debug|arm64.Build.0 = Debug|Any CPU
+ {BE4AEE6B-3F2E-4FFB-940D-3E4916EAE913}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {BE4AEE6B-3F2E-4FFB-940D-3E4916EAE913}.Debug|x64.Build.0 = Debug|Any CPU
+ {BE4AEE6B-3F2E-4FFB-940D-3E4916EAE913}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {BE4AEE6B-3F2E-4FFB-940D-3E4916EAE913}.Debug|x86.Build.0 = Debug|Any CPU
+ {BE4AEE6B-3F2E-4FFB-940D-3E4916EAE913}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {BE4AEE6B-3F2E-4FFB-940D-3E4916EAE913}.Release|Any CPU.Build.0 = Release|Any CPU
+ {BE4AEE6B-3F2E-4FFB-940D-3E4916EAE913}.Release|arm64.ActiveCfg = Release|Any CPU
+ {BE4AEE6B-3F2E-4FFB-940D-3E4916EAE913}.Release|arm64.Build.0 = Release|Any CPU
+ {BE4AEE6B-3F2E-4FFB-940D-3E4916EAE913}.Release|x64.ActiveCfg = Release|Any CPU
+ {BE4AEE6B-3F2E-4FFB-940D-3E4916EAE913}.Release|x64.Build.0 = Release|Any CPU
+ {BE4AEE6B-3F2E-4FFB-940D-3E4916EAE913}.Release|x86.ActiveCfg = Release|Any CPU
+ {BE4AEE6B-3F2E-4FFB-940D-3E4916EAE913}.Release|x86.Build.0 = Release|Any CPU
+ {BE4AEE6B-3F2E-4FFB-940D-3E4916EAE913}.SkipOld|Any CPU.ActiveCfg = Debug|Any CPU
+ {BE4AEE6B-3F2E-4FFB-940D-3E4916EAE913}.SkipOld|Any CPU.Build.0 = Debug|Any CPU
+ {BE4AEE6B-3F2E-4FFB-940D-3E4916EAE913}.SkipOld|arm64.ActiveCfg = Debug|Any CPU
+ {BE4AEE6B-3F2E-4FFB-940D-3E4916EAE913}.SkipOld|arm64.Build.0 = Debug|Any CPU
+ {BE4AEE6B-3F2E-4FFB-940D-3E4916EAE913}.SkipOld|x64.ActiveCfg = Debug|Any CPU
+ {BE4AEE6B-3F2E-4FFB-940D-3E4916EAE913}.SkipOld|x64.Build.0 = Debug|Any CPU
+ {BE4AEE6B-3F2E-4FFB-940D-3E4916EAE913}.SkipOld|x86.ActiveCfg = Debug|Any CPU
+ {BE4AEE6B-3F2E-4FFB-940D-3E4916EAE913}.SkipOld|x86.Build.0 = Debug|Any CPU
+ {D103EFF9-90EA-49C7-9DD9-636E6056E9C3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D103EFF9-90EA-49C7-9DD9-636E6056E9C3}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D103EFF9-90EA-49C7-9DD9-636E6056E9C3}.Debug|arm64.ActiveCfg = Debug|Any CPU
+ {D103EFF9-90EA-49C7-9DD9-636E6056E9C3}.Debug|arm64.Build.0 = Debug|Any CPU
+ {D103EFF9-90EA-49C7-9DD9-636E6056E9C3}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {D103EFF9-90EA-49C7-9DD9-636E6056E9C3}.Debug|x64.Build.0 = Debug|Any CPU
+ {D103EFF9-90EA-49C7-9DD9-636E6056E9C3}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {D103EFF9-90EA-49C7-9DD9-636E6056E9C3}.Debug|x86.Build.0 = Debug|Any CPU
+ {D103EFF9-90EA-49C7-9DD9-636E6056E9C3}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D103EFF9-90EA-49C7-9DD9-636E6056E9C3}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D103EFF9-90EA-49C7-9DD9-636E6056E9C3}.Release|arm64.ActiveCfg = Release|Any CPU
+ {D103EFF9-90EA-49C7-9DD9-636E6056E9C3}.Release|arm64.Build.0 = Release|Any CPU
+ {D103EFF9-90EA-49C7-9DD9-636E6056E9C3}.Release|x64.ActiveCfg = Release|Any CPU
+ {D103EFF9-90EA-49C7-9DD9-636E6056E9C3}.Release|x64.Build.0 = Release|Any CPU
+ {D103EFF9-90EA-49C7-9DD9-636E6056E9C3}.Release|x86.ActiveCfg = Release|Any CPU
+ {D103EFF9-90EA-49C7-9DD9-636E6056E9C3}.Release|x86.Build.0 = Release|Any CPU
+ {D103EFF9-90EA-49C7-9DD9-636E6056E9C3}.SkipOld|Any CPU.ActiveCfg = Debug|Any CPU
+ {D103EFF9-90EA-49C7-9DD9-636E6056E9C3}.SkipOld|Any CPU.Build.0 = Debug|Any CPU
+ {D103EFF9-90EA-49C7-9DD9-636E6056E9C3}.SkipOld|arm64.ActiveCfg = Debug|Any CPU
+ {D103EFF9-90EA-49C7-9DD9-636E6056E9C3}.SkipOld|arm64.Build.0 = Debug|Any CPU
+ {D103EFF9-90EA-49C7-9DD9-636E6056E9C3}.SkipOld|x64.ActiveCfg = Debug|Any CPU
+ {D103EFF9-90EA-49C7-9DD9-636E6056E9C3}.SkipOld|x64.Build.0 = Debug|Any CPU
+ {D103EFF9-90EA-49C7-9DD9-636E6056E9C3}.SkipOld|x86.ActiveCfg = Debug|Any CPU
+ {D103EFF9-90EA-49C7-9DD9-636E6056E9C3}.SkipOld|x86.Build.0 = Debug|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
- SolutionGuid = {4830E5AF-9C61-4A32-B34F-82CF4072248B}
+ SolutionGuid = {4D769F43-A252-4DC4-B54B-055DBC12610B}
EndGlobalSection
EndGlobal
diff --git a/Emerald/App.xaml b/Emerald/App.xaml
new file mode 100644
index 00000000..30649aad
--- /dev/null
+++ b/Emerald/App.xaml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Emerald/App.xaml.cs b/Emerald/App.xaml.cs
new file mode 100644
index 00000000..acd2d2c1
--- /dev/null
+++ b/Emerald/App.xaml.cs
@@ -0,0 +1,71 @@
+using Uno.Resizetizer;
+
+namespace Emerald;
+public partial class App : Application
+{
+ ///
+ /// Initializes the singleton application object. This is the first line of authored code
+ /// executed, and as such is the logical equivalent of main() or WinMain().
+ ///
+ public App()
+ {
+ this.InitializeComponent();
+ }
+
+ public Window? MainWindow { get; private set; }
+ protected IHost? Host { get; private set; }
+
+ protected override void OnLaunched(LaunchActivatedEventArgs args)
+ {
+ var builder = this.CreateBuilder(args)
+ .Configure(host => host
+#if DEBUG
+ // Switch to Development environment when running in DEBUG
+ .UseEnvironment(Environments.Development)
+#endif
+ .ConfigureServices((context, services) =>
+ {
+ // TODO: Register your services
+ //services.AddSingleton();
+ })
+ );
+ MainWindow = builder.Window;
+
+
+#if DEBUG
+ MainWindow.UseStudio();
+#endif
+ MainWindow.SetWindowIcon();
+
+ Host = builder.Build();
+
+ // Do not repeat app initialization when the Window already has content,
+ // just ensure that the window is active
+ if (MainWindow.Content is not Frame rootFrame)
+ {
+ // Create a Frame to act as the navigation context and navigate to the first page
+ rootFrame = new Frame();
+
+ // Place the frame in the current Window
+ MainWindow.Content = rootFrame;
+ }
+#if WINDOWS
+ var mica = Emerald.Helpers.WindowManager.IntializeWindow(MainWindow);
+#endif
+
+ if (rootFrame.Content == null)
+ {
+ // When the navigation stack isn't restored navigate to the first page,
+ // configuring the new page by passing required information as a navigation
+ // parameter
+ rootFrame.Navigate(typeof(MainPage), args.Arguments);
+ }
+ // Ensure the current window is active
+ MainWindow.Activate();
+ }
+
+ ///
+ /// Gets the current instance in use
+ ///
+ public new static App Current => (App)Application.Current;
+}
diff --git a/Emerald/Assets/Icons/icon.svg b/Emerald/Assets/Icons/icon.svg
new file mode 100644
index 00000000..a15af53a
--- /dev/null
+++ b/Emerald/Assets/Icons/icon.svg
@@ -0,0 +1,42 @@
+
+
diff --git a/Emerald/Assets/Icons/icon_foreground.svg b/Emerald/Assets/Icons/icon_foreground.svg
new file mode 100644
index 00000000..8ffc41ae
--- /dev/null
+++ b/Emerald/Assets/Icons/icon_foreground.svg
@@ -0,0 +1,137 @@
+
+
diff --git a/Emerald/Assets/SharedAssets.md b/Emerald/Assets/SharedAssets.md
new file mode 100644
index 00000000..1b84a74a
--- /dev/null
+++ b/Emerald/Assets/SharedAssets.md
@@ -0,0 +1,32 @@
+# Shared Assets
+
+See documentation about assets here: https://github.com/unoplatform/uno/blob/master/doc/articles/features/working-with-assets.md
+
+## Here is a cheat sheet
+
+1. Add the image file to the `Assets` directory of a shared project.
+2. Set the build action to `Content`.
+3. (Recommended) Provide an asset for various scales/dpi
+
+### Examples
+
+```text
+\Assets\Images\logo.scale-100.png
+\Assets\Images\logo.scale-200.png
+\Assets\Images\logo.scale-400.png
+
+\Assets\Images\scale-100\logo.png
+\Assets\Images\scale-200\logo.png
+\Assets\Images\scale-400\logo.png
+```
+
+### Table of scales
+
+| Scale | WinUI | iOS/MacCatalyst | Android |
+|-------|:-----------:|:---------------:|:-------:|
+| `100` | scale-100 | @1x | mdpi |
+| `125` | scale-125 | N/A | N/A |
+| `150` | scale-150 | N/A | hdpi |
+| `200` | scale-200 | @2x | xhdpi |
+| `300` | scale-300 | @3x | xxhdpi |
+| `400` | scale-400 | N/A | xxxhdpi |
diff --git a/Emerald/Assets/Splash/splash_screen.svg b/Emerald/Assets/Splash/splash_screen.svg
new file mode 100644
index 00000000..8ffc41ae
--- /dev/null
+++ b/Emerald/Assets/Splash/splash_screen.svg
@@ -0,0 +1,137 @@
+
+
diff --git a/Emerald/Assets/icon.png b/Emerald/Assets/icon.png
new file mode 100644
index 00000000..94000d94
Binary files /dev/null and b/Emerald/Assets/icon.png differ
diff --git a/Emerald/DirectResources.cs b/Emerald/DirectResources.cs
new file mode 100644
index 00000000..f2d25777
--- /dev/null
+++ b/Emerald/DirectResources.cs
@@ -0,0 +1,39 @@
+using CommunityToolkit.WinUI.Helpers;
+using Emerald.Helpers;
+using System.Reflection;
+using System.Runtime.InteropServices;
+using Windows.UI;
+
+namespace Emerald;
+
+public static class DirectResoucres
+{
+ public static int MaxRAM
+ => Extensions.GetMemoryGB() * 1024;
+
+ public static int MinRAM
+ => 512;
+
+ //Used this thing for major setting changes because older settings could crash the program
+ public static string SettingsAPIVersion
+ => "1.3";
+
+ public static string BuildType
+ {
+ get
+ {
+#if DEBUG
+ return "DEBUG";
+#else
+ return "RELEASE";
+#endif
+ }
+ }
+ public static Architecture Architecture => RuntimeInformation.ProcessArchitecture;
+
+ public static string AppVersion
+ => Assembly.GetExecutingAssembly().GetName().Version.ToString();
+
+ public static Color LayerFillColorDefaultColor
+ => (Color)App.Current.Resources["LayerFillColorDefault"];
+}
diff --git a/Emerald/Emerald.csproj b/Emerald/Emerald.csproj
new file mode 100644
index 00000000..523591d0
--- /dev/null
+++ b/Emerald/Emerald.csproj
@@ -0,0 +1,93 @@
+
+
+
+ net8.0-maccatalyst;
+ net8.0-windows10.0.22621;
+ net8.0-desktop;
+
+
+ Exe
+ true
+
+
+ Emerald
+
+ Riverside.Emerald
+
+ 1.0.0.0
+ 1
+
+
+
+
+ Lottie;
+ Hosting;
+ Toolkit;
+ Mvvm;
+ Localization;
+ ThemeService;
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
+
+
+
+
+ MSBuild:Compile
+
+
+
+
+
+ MSBuild:Compile
+
+
+
+
+
+
+
+
+
+ MSBuild:Compile
+
+
+
+
+
+
+
+
diff --git a/Emerald/GlobalUsings.cs b/Emerald/GlobalUsings.cs
new file mode 100644
index 00000000..6b345c18
--- /dev/null
+++ b/Emerald/GlobalUsings.cs
@@ -0,0 +1,7 @@
+global using System.Collections.Immutable;
+global using CommunityToolkit.Mvvm.ComponentModel;
+global using CommunityToolkit.Mvvm.Input;
+global using Microsoft.Extensions.DependencyInjection;
+global using Microsoft.Extensions.Hosting;
+global using Microsoft.Extensions.Logging;
+global using ApplicationExecutionState = Windows.ApplicationModel.Activation.ApplicationExecutionState;
diff --git a/Emerald/Helpers/Converters/BoolToVisibility.cs b/Emerald/Helpers/Converters/BoolToVisibility.cs
new file mode 100644
index 00000000..3cd23e81
--- /dev/null
+++ b/Emerald/Helpers/Converters/BoolToVisibility.cs
@@ -0,0 +1,18 @@
+
+using Microsoft.UI.Xaml.Data;
+
+namespace Emerald.Helpers.Converters;
+public class BoolToVisibility : IValueConverter
+ {
+ public bool Reversed { get; set; }
+
+ public object Convert(object value, Type targetType, object parameter, string language)
+ {
+ return (value is bool b) ? (((Reversed || parameter == "reversed") && parameter != "normal") ? (!b ? Visibility.Visible : Visibility.Collapsed) : (b ? Visibility.Visible : Visibility.Collapsed)) : Visibility.Collapsed;
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, string language)
+ {
+ throw new InvalidOperationException();
+ }
+ }
diff --git a/Emerald/Helpers/Extensions.cs b/Emerald/Helpers/Extensions.cs
new file mode 100644
index 00000000..62482347
--- /dev/null
+++ b/Emerald/Helpers/Extensions.cs
@@ -0,0 +1,155 @@
+using Microsoft.UI.Xaml;
+using Microsoft.UI.Xaml.Controls;
+using Microsoft.Windows.ApplicationModel.Resources;
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Linq;
+using System.Security.Cryptography;
+using System.Text;
+using Windows.System.Diagnostics;
+
+namespace Emerald.Helpers;
+
+public static class Extensions
+{
+ private static readonly ConcurrentDictionary cachedResources = new();
+
+ //public static void ShowAt(this TeachingTip tip, FrameworkElement element, TeachingTipPlacementMode placement = TeachingTipPlacementMode.Auto, bool closeWhenClick = true, bool addToMainGrid = true)
+ //{
+ // if (addToMainGrid)
+ // (App.Current.MainWindow.Content as Grid).Children.Add(tip);
+
+ // tip.Target = element;
+ // tip.PreferredPlacement = placement;
+ // tip.IsOpen = true;
+ // if (closeWhenClick)
+ // {
+ // tip.ActionButtonClick += (_, _) => tip.IsOpen = false;
+ // tip.CloseButtonClick += (_, _) => tip.IsOpen = false;
+ // }
+ //}
+
+ public static int GetMemoryGB()
+ {
+ SystemMemoryUsageReport systemMemoryUsageReport = SystemDiagnosticInfo.GetForCurrentSystem().MemoryUsage.GetReport();
+
+ long memkb = Convert.ToInt64(systemMemoryUsageReport.TotalPhysicalSizeInBytes);
+ return Convert.ToInt32(memkb / Math.Pow(1024, 3));
+ }
+
+ public static string KiloFormat(this int num)
+ {
+ if (num >= 100000000)
+ return (num / 1000000).ToString("#,0M");
+
+ if (num >= 10000000)
+ return (num / 1000000).ToString("0.#") + "M";
+
+ if (num >= 100000)
+ return (num / 1000).ToString("#,0K");
+
+ if (num >= 1000)
+ return (num / 100).ToString("0.#") + "K";
+
+ return num.ToString("#,0");
+ }
+
+ //public static ContentDialog ToContentDialog(this UIElement content, string title, string closebtnText = null, ContentDialogButton defaultButton = ContentDialogButton.Close)
+ //{
+ // ContentDialog dialog = new()
+ // {
+ // XamlRoot = App.Current.MainWindow.Content.XamlRoot,
+ // Style = Application.Current.Resources["DefaultContentDialogStyle"] as Style,
+ // Title = title,
+ // CloseButtonText = closebtnText,
+ // DefaultButton = defaultButton,
+ // Content = content,
+ // RequestedTheme = (ElementTheme)Settings.SettingsSystem.Settings.App.Appearance.Theme
+ // };
+ // return dialog;
+ //}
+
+ public static int Remove(this ObservableCollection coll, Func condition)
+ {
+ var itemsToRemove = coll.Where(condition).ToList();
+
+ foreach (var itemToRemove in itemsToRemove)
+ {
+ coll.Remove(itemToRemove);
+ }
+ return itemsToRemove.Count;
+ }
+
+ public static int Remove(this List coll, Func condition)
+ {
+ var itemsToRemove = coll.Where(condition).ToList();
+
+ foreach (var itemToRemove in itemsToRemove)
+ {
+ coll.Remove(itemToRemove);
+ }
+
+ return itemsToRemove.Count;
+ }
+ public static void AddRange(this ObservableCollection cll, IEnumerable items)
+ {
+ foreach (var item in items)
+ cll.Add(item);
+ }
+
+ public static string ToBinaryString(this string str)
+ {
+ var binary = "";
+ foreach (char ch in str)
+ {
+ binary += Convert.ToString((int)ch, 2);
+ }
+ return binary;
+ }
+
+ public static string ToMD5(this string s)
+ {
+ StringBuilder sb = new();
+ byte[] hashValue = MD5.HashData(Encoding.UTF8.GetBytes(s));
+
+ foreach (byte b in hashValue)
+ {
+ sb.Append($"{b:X2}");
+ }
+
+ return sb.ToString();
+ }
+
+ public static string Localize(this string resourceKey)
+ {
+ try
+ {
+ string s = Windows.ApplicationModel.Resources.ResourceLoader
+ .GetForViewIndependentUse()
+ .GetString(resourceKey);
+
+ return string.IsNullOrEmpty(s) ? resourceKey : s;
+ }
+ catch
+ {
+ return resourceKey;
+ }
+ }
+
+ //public static string Localize(this Core.Localized resourceKey) =>
+ // resourceKey.ToString().Localize();
+
+ //public static Models.Account ToAccount(this CmlLib.Core.Auth.MSession session, bool plusCount = true)
+ //{
+ // bool isOffline = session.AccessToken == "access_token";
+ // return new Models.Account(session.Username, isOffline ? null : session.AccessToken, isOffline ? null : session.UUID, plusCount ? MainWindow.HomePage.AccountsPage.AllCount++ : 0, false, session.ClientToken);
+ //}
+
+ //public static CmlLib.Core.Auth.MSession ToMSession(this Models.Account account)
+ //{
+ // bool isOffline = account.UUID == null;
+ // return new CmlLib.Core.Auth.MSession(account.UserName, isOffline ? "access_token" : account.AccessToken, isOffline ? Guid.NewGuid().ToString().Replace("-", "") : account.UUID) { ClientToken = account.ClientToken };
+ //}
+}
diff --git a/Emerald/Helpers/MarkupExtensions/ConditionString.cs b/Emerald/Helpers/MarkupExtensions/ConditionString.cs
new file mode 100644
index 00000000..da03abbe
--- /dev/null
+++ b/Emerald/Helpers/MarkupExtensions/ConditionString.cs
@@ -0,0 +1,21 @@
+using Microsoft.UI.Xaml.Markup;
+
+namespace Emerald.Helpers;
+
+[MarkupExtensionReturnType(ReturnType = typeof(string))]
+public class ConditionString : MarkupExtension
+{
+ public string TrueString { get; set; }
+
+ public string FalseString { get; set; }
+
+ public bool Condition { get; set; }
+
+ public string Result
+ => Condition ? TrueString : FalseString;
+
+ public override string ToString()
+ => Result;
+
+ protected override object ProvideValue() => Result;
+}
diff --git a/Emerald/Helpers/MarkupExtensions/FontIcon.cs b/Emerald/Helpers/MarkupExtensions/FontIcon.cs
new file mode 100644
index 00000000..5731c5f7
--- /dev/null
+++ b/Emerald/Helpers/MarkupExtensions/FontIcon.cs
@@ -0,0 +1,14 @@
+using Microsoft.UI.Xaml.Markup;
+
+namespace Emerald.Helpers;
+
+[MarkupExtensionReturnType(ReturnType = typeof(Microsoft.UI.Xaml.Controls.FontIcon))]
+public sealed class FontIcon : MarkupExtension
+{
+ public string Glyph { get; set; } = "\xe8a5";
+
+ public int FontSize { get; set; } = 16;
+
+ protected override object ProvideValue()
+ => new Microsoft.UI.Xaml.Controls.FontIcon() { Glyph = Glyph, FontSize = FontSize };
+}
diff --git a/Emerald/Helpers/MarkupExtensions/LocalizeString.cs b/Emerald/Helpers/MarkupExtensions/LocalizeString.cs
new file mode 100644
index 00000000..495182dc
--- /dev/null
+++ b/Emerald/Helpers/MarkupExtensions/LocalizeString.cs
@@ -0,0 +1,12 @@
+using Microsoft.UI.Xaml.Markup;
+using CommunityToolkit.Mvvm;
+namespace Emerald.Helpers;
+
+[MarkupExtensionReturnType(ReturnType = typeof(string))]
+public sealed class Localize : MarkupExtension
+{
+ public string Name { get; set; }
+
+ protected override object ProvideValue()
+ => Name.Localize();
+}
diff --git a/Emerald/Helpers/Settings/Enums/MicaTintColor.cs b/Emerald/Helpers/Settings/Enums/MicaTintColor.cs
new file mode 100644
index 00000000..a92be213
--- /dev/null
+++ b/Emerald/Helpers/Settings/Enums/MicaTintColor.cs
@@ -0,0 +1,8 @@
+namespace Emerald.Helpers.Settings.Enums;
+
+public enum MicaTintColor : int
+{
+ NoColor = 0,
+ AccentColor = 1,
+ CustomColor = 2
+}
diff --git a/Emerald/Helpers/Settings/JSON.cs b/Emerald/Helpers/Settings/JSON.cs
new file mode 100644
index 00000000..11b1815a
--- /dev/null
+++ b/Emerald/Helpers/Settings/JSON.cs
@@ -0,0 +1,365 @@
+using CmlLib.Core;
+using CommunityToolkit.Mvvm.ComponentModel;
+using Microsoft.UI;
+using Microsoft.UI.Xaml;
+using System.Text.Json;
+using System.Text.Json.Serialization;
+using System;
+using System.Collections.Generic;
+using Windows.UI;
+using Emerald.CoreX.Store.Modrinth;
+namespace Emerald.Helpers.Settings.JSON;
+
+public class JSON : Models.Model
+{
+ public string Serialize()
+ => JsonSerializer.Serialize(this, new JsonSerializerOptions { WriteIndented = true });
+}
+
+public class SettingsBackup : JSON
+{
+ public string Backup { get; set; }
+ public string Name { get; set; }
+ public DateTime Time { get; set; }
+ // ik there is Time.ToString() lol
+ public string DateString => $"{Time.ToLongDateString()} {Time.ToShortTimeString()}";
+}
+
+public class Backups : JSON
+{
+ public SettingsBackup[] AllBackups { get; set; } = Array.Empty();
+ public string APIVersion { get; private set; } = "1.0";
+}
+
+public class Settings : JSON
+{
+ public static Settings CreateNew() => new()
+ {
+ App = new()
+ {
+ Discord = new(),
+ Appearance = new()
+ {
+ MicaTintColor = (int)Enums.MicaTintColor.NoColor,
+ Theme = (int)ElementTheme.Default
+ }
+ },
+ Minecraft = new()
+ {
+ Path = MinecraftPath.GetOSDefaultPath(),
+ // RAM = DirectResoucres.MaxRAM / 2,
+ MCVerionsConfiguration = new(),
+ JVM = new(),
+ Downloader = new()
+ {
+ AssetsCheck = true,
+ HashCheck = true
+ }
+ }
+ };
+
+ // public string APIVersion { get; set; } = DirectResoucres.SettingsAPIVersion;
+ public DateTime LastSaved { get; set; } = DateTime.Now;
+ public Minecraft Minecraft { get; set; } = new();
+
+ public App App { get; set; } = new();
+}
+
+public partial class Minecraft : JSON
+{
+ public Minecraft()
+ {
+ JVM.PropertyChanged += (_, _)
+ => InvokePropertyChanged();
+ PropertyChanged += (_, e) =>
+ {
+ if (e.PropertyName != null)
+ InvokePropertyChanged();
+ };
+ }
+
+ [JsonIgnore]
+ public double RAMinGB => Math.Round((RAM / 1024.00), 2);
+
+
+ [ObservableProperty]
+ private string _Path;
+
+ [ObservableProperty]
+ private int _RAM;
+
+ [ObservableProperty]
+ private bool _IsAdmin;
+
+ public Downloader Downloader { get; set; } = new();
+
+ public MCVerionsConfiguration MCVerionsConfiguration { get; set; }
+
+ public JVM JVM { get; set; } = new();
+
+ public bool ReadLogs()
+ => JVM.GameLogs && !IsAdmin;
+}
+
+public class Account : JSON
+{
+ public string Type { get; set; }
+ public string Username { get; set; }
+ public string AccessToken { get; set; }
+ public string ClientToken { get; set; }
+ public string UUID { get; set; }
+ public bool LastAccessed { get; set; }
+}
+
+public partial class Downloader : JSON
+{
+ [ObservableProperty]
+ private bool _HashCheck;
+
+ [ObservableProperty]
+ private bool _AssetsCheck;
+}
+
+public partial class JVM : JSON
+{
+ public JVM()
+ {
+ this.PropertyChanged += (_, e) =>
+ {
+ if (e.PropertyName != null)
+ this.InvokePropertyChanged();
+ };
+ }
+
+ [ObservableProperty]
+ private string[] _Arguments;
+
+ [ObservableProperty]
+ private double _ScreenWidth;
+
+ [ObservableProperty]
+ private double _ScreenHeight;
+
+ [ObservableProperty]
+ private bool _FullScreen;
+
+ [ObservableProperty]
+ private bool _GameLogs;
+
+ [JsonIgnore]
+ public string ScreenSizeStatus =>
+ FullScreen ? "FullScreen".Localize() : ((ScreenWidth > 0 && ScreenHeight > 0) ? $"{ScreenWidth} × {ScreenHeight}" : "Default".Localize());
+
+ [JsonIgnore]
+ public bool SetSize => !(ScreenSizeStatus == "FullScreen".Localize() || ScreenSizeStatus == "Default".Localize());
+}
+
+public class App : JSON
+{
+ public Appearance Appearance { get; set; } = new();
+ public bool AutoLogin { get; set; }
+ public Discord Discord { get; set; } = new();
+ public NewsFilter NewsFilter { get; set; } = new();
+ public Store Store { get; set; } = new();
+ public Updates Updates { get; set; } = new();
+ public bool AutoClose { get; set; }
+ public bool HideOnLaunch { get; set; }
+ public bool WindowsHello { get; set; }
+}
+public class Updates : JSON
+{
+ public bool CheckAtStartup { get; set; } = true;
+ public bool AutoDownload { get; set; }
+ public bool IncludePreReleases { get; set; }
+}
+public partial class StoreFilter : JSON
+{
+ [ObservableProperty]
+ private bool _Fabric;
+
+ [ObservableProperty]
+ private bool _Forge;
+
+ [ObservableProperty]
+ private bool _Adventure;
+
+ [ObservableProperty]
+ private bool _Cursed;
+
+ [ObservableProperty]
+ private bool _Decoration;
+
+ [ObservableProperty]
+ private bool _Equipment;
+
+ [ObservableProperty]
+ private bool _Food;
+
+ [ObservableProperty]
+ private bool _Library;
+
+ [ObservableProperty]
+ private bool _Magic;
+
+ [ObservableProperty]
+ private bool _Misc;
+
+ [ObservableProperty]
+ private bool _Optimization;
+
+ [ObservableProperty]
+ private bool _Storage;
+
+ [ObservableProperty]
+ private bool _Technology;
+
+ [ObservableProperty]
+ private bool _Utility;
+
+ [ObservableProperty]
+ private bool _Worldgen;
+
+ [JsonIgnore]
+ public bool All
+ {
+ get =>true;
+ set
+ {
+ _Fabric = _Forge = _Adventure = _Cursed = _Decoration = _Equipment = _Food = _Library = _Magic = _Misc = _Optimization = _Storage = _Technology = _Utility = _Worldgen = false;
+ InvokePropertyChanged(null);
+ }
+ }
+}
+public class Store : JSON
+{
+ public StoreFilter Filter { get; set; } = new();
+ public StoreSortOptions SortOptions { get; set; } = new();
+}
+
+public partial class StoreSortOptions : JSON
+{
+ [ObservableProperty]
+ private bool _Relevance = true;
+
+ [ObservableProperty]
+ private bool _Downloads;
+
+ [ObservableProperty]
+ private bool _Follows;
+
+ [ObservableProperty]
+ private bool _Updated;
+
+ [ObservableProperty]
+ private bool _Newest;
+
+ public SearchSortOptions GetResult()
+ {
+ if (!(Relevance || Downloads || Follows || Updated || Newest))
+ return SearchSortOptions.Relevance;
+ else
+ return Relevance ? SearchSortOptions.Relevance : (Downloads ? SearchSortOptions.Downloads : (Follows ? SearchSortOptions.Follows : (Updated ? SearchSortOptions.Updated : SearchSortOptions.Newest)));
+ }
+}
+public partial class NewsFilter : JSON
+{
+ [ObservableProperty]
+ private bool _Java = false;
+
+ [ObservableProperty]
+ private bool _Bedrock = false;
+
+ [ObservableProperty]
+ private bool _Dungeons = false;
+
+ [ObservableProperty]
+ private bool _Legends = false;
+
+ [JsonIgnore]
+ public bool All
+ {
+ get => GetResult().Length == 4;
+ set
+ {
+ Java = Bedrock = Dungeons = Legends = false;
+ InvokePropertyChanged(null);
+ }
+ }
+ public string[] GetResult()
+ {
+ var r = new List();
+
+ if (!Java && !Bedrock && !Dungeons && !Legends)
+ {
+ r.Add("Minecraft: Java Edition");
+ r.Add("Minecraft for Windows");
+ r.Add("Minecraft Dungeons");
+ r.Add("Minecraft Legends");
+ return r.ToArray();
+ }
+
+ if (Java)
+ r.Add("Minecraft: Java Edition");
+
+ if (Bedrock)
+ r.Add("Minecraft for Windows");
+
+ if (Dungeons)
+ r.Add("Minecraft Dungeons");
+
+ if (Legends)
+ r.Add("Minecraft Legends");
+
+ return r.ToArray();
+ }
+}
+public class Discord : JSON
+{
+}
+public partial class MCVerionsConfiguration : JSON
+{
+ [ObservableProperty]
+ private bool _Release = true;
+
+ [ObservableProperty]
+ private bool _Custom = false;
+
+ [ObservableProperty]
+ private bool _OldBeta = false;
+
+ [ObservableProperty]
+ private bool _OldAlpha = false;
+
+ [ObservableProperty]
+ private bool _Snapshot = false;
+}
+
+public partial class Appearance : JSON
+{
+ [ObservableProperty]
+ private int _NavIconType = 1;
+
+ public bool ShowFontIcons => NavIconType == 0;
+
+ [ObservableProperty]
+ private int _Theme;
+
+ [ObservableProperty]
+ private int _MicaTintColor;
+
+ [ObservableProperty]
+ private int _MicaType = 0;
+
+ [ObservableProperty]
+ private (int A, int R, int G, int B)? _CustomMicaTintColor;
+
+
+ public Appearance()
+ {
+ this.PropertyChanged += (_, e) =>
+ {
+ if (e.PropertyName != null)
+ this.InvokePropertyChanged();
+ };
+ }
+}
diff --git a/Emerald/Helpers/Settings/SettingsSystem.cs b/Emerald/Helpers/Settings/SettingsSystem.cs
new file mode 100644
index 00000000..5a25b55a
--- /dev/null
+++ b/Emerald/Helpers/Settings/SettingsSystem.cs
@@ -0,0 +1,111 @@
+using Emerald.Helpers.Settings.JSON;
+using System.Text.Json;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using Windows.Storage;
+
+namespace Emerald.Helpers.Settings;
+
+public static class SettingsSystem
+{
+ public static JSON.Settings Settings { get; private set; } = JSON.Settings.CreateNew();
+ public static Account[] Accounts { get; set; }
+
+ public static event EventHandler? APINoMatch;
+// public static T GetSerializedFromSettings(string key, T def)
+// {
+// string json;
+// try
+// {
+// json = ApplicationData.Current.RoamingSettings.Values[key] as string;
+// return JsonSerializer.Deserialize(json);
+// }
+// catch
+// {
+// json = JsonSerializer.Serialize(def);
+// ApplicationData.Current.RoamingSettings.Values[key] = json;
+// return def;
+// }
+// }
+// public static void LoadData()
+// {
+// Settings = GetSerializedFromSettings("Settings", JSON.Settings.CreateNew());
+// Accounts = GetSerializedFromSettings("Accounts", Array.Empty());
+
+// if (Settings.APIVersion != DirectResoucres.SettingsAPIVersion)
+// {
+// APINoMatch?.Invoke(null, ApplicationData.Current.RoamingSettings.Values["Settings"] as string);
+// ApplicationData.Current.RoamingSettings.Values["Settings"] = JSON.Settings.CreateNew().Serialize();
+// Settings = JsonSerializer.Deserialize(ApplicationData.Current.RoamingSettings.Values["Settings"] as string);
+// }
+// }
+
+// public static async Task CreateBackup(string system)
+// {
+// string json = await FileIO.ReadTextAsync(await ApplicationData.Current.LocalFolder.CreateFileAsync("backups.json", CreationCollisionOption.OpenIfExists));
+// var l = json.IsNullEmptyOrWhiteSpace() ? new Backups() : JsonSerializer.Deserialize(json);
+
+// var bl = l.AllBackups == null ? new List() : l.AllBackups.ToList();
+// bl.Add(new SettingsBackup() { Time = DateTime.Now, Backup = system });
+// l.AllBackups = bl.ToArray();
+// json = l.Serialize();
+
+// await FileIO.WriteTextAsync(await ApplicationData.Current.LocalFolder.CreateFileAsync("backups.json", CreationCollisionOption.OpenIfExists), json);
+// }
+
+// public static async Task DeleteBackup(int Index)
+// {
+// string json = await FileIO.ReadTextAsync(await ApplicationData.Current.LocalFolder.CreateFileAsync("backups.json", CreationCollisionOption.OpenIfExists));
+// var l = json.IsNullEmptyOrWhiteSpace() ? new Backups() : JsonSerializer.Deserialize(json);
+
+// var bl = l.AllBackups == null ? new List() : l.AllBackups.ToList();
+// bl.RemoveAt(Index);
+// l.AllBackups = bl.ToArray();
+// json = l.Serialize();
+
+// await FileIO.WriteTextAsync(await ApplicationData.Current.LocalFolder.CreateFileAsync("backups.json", CreationCollisionOption.OpenIfExists), json);
+// }
+
+// public static async Task DeleteBackup(DateTime time)
+// {
+// string json = await FileIO.ReadTextAsync(await ApplicationData.Current.LocalFolder.CreateFileAsync("backups.json", CreationCollisionOption.OpenIfExists));
+// var l = json.IsNullEmptyOrWhiteSpace() ? new Backups() : JsonSerializer.Deserialize(json);
+
+// var bl = l.AllBackups == null ? new List() : l.AllBackups.ToList();
+// bl.Remove(x => x.Time == time);
+// l.AllBackups = bl.ToArray();
+// json = l.Serialize();
+
+// await FileIO.WriteTextAsync(await ApplicationData.Current.LocalFolder.CreateFileAsync("backups.json", CreationCollisionOption.OpenIfExists), json);
+// }
+// public static async Task RenameBackup(DateTime time, string name)
+// {
+// string json = await FileIO.ReadTextAsync(await ApplicationData.Current.LocalFolder.CreateFileAsync("backups.json", CreationCollisionOption.OpenIfExists));
+// var l = json.IsNullEmptyOrWhiteSpace() ? new Backups() : JsonSerializer.Deserialize(json);
+
+// var bl = l.AllBackups == null ? new List() : l.AllBackups.ToList();
+// bl.FirstOrDefault(x => x.Time == time).Name = name;
+// l.AllBackups = bl.ToArray();
+// json = l.Serialize();
+
+// await FileIO.WriteTextAsync(await ApplicationData.Current.LocalFolder.CreateFileAsync("backups.json", CreationCollisionOption.OpenIfExists), json);
+// }
+
+// public static async Task> GetBackups()
+// {
+// string json = await FileIO.ReadTextAsync(await ApplicationData.Current.LocalFolder.CreateFileAsync("backups.json", CreationCollisionOption.OpenIfExists));
+// var l = json.IsNullEmptyOrWhiteSpace() ? new Backups() : JsonSerializer.Deserialize(json);
+
+// return l.AllBackups == null ? new List() : l.AllBackups.ToList();
+// }
+
+// public static void SaveData()
+// {
+// Settings.LastSaved = DateTime.Now;
+// ApplicationData.Current.RoamingSettings.Values["Settings"] = Settings.Serialize();
+// ApplicationData.Current.RoamingSettings.Values["Accounts"] = JsonSerializer.Serialize(Accounts);
+// }
+//}
+}
diff --git a/Emerald/Helpers/WindowManager.cs b/Emerald/Helpers/WindowManager.cs
new file mode 100644
index 00000000..28629aee
--- /dev/null
+++ b/Emerald/Helpers/WindowManager.cs
@@ -0,0 +1,175 @@
+using Microsoft.UI;
+using Microsoft.UI.Composition;
+using Microsoft.UI.Composition.SystemBackdrops;
+using Microsoft.UI.Windowing;
+using Microsoft.UI.Xaml;
+using System;
+using System.Runtime.InteropServices;
+using Windows.ApplicationModel;
+using WinRT;
+using WinRT.Interop;
+
+namespace Emerald.Helpers;
+
+#if WINDOWS
+public static class WindowManager
+{
+ ///
+ /// Add mica and the icon to the
+ ///
+ public static MicaBackground IntializeWindow(Window window)
+ {
+ var s = new MicaBackground(window);
+ s.TrySetMicaBackdrop();
+ return s;
+ }
+
+ ///
+ /// Sets the customized titlebar if supported
+ ///
+ ///
+ public static void SetTitleBar(Window window, UIElement AppTitleBar)
+ {
+ FrameworkElement RootUI = (FrameworkElement)window.Content;
+ if (AppWindowTitleBar.IsCustomizationSupported())
+ {
+ var titlebar = window.AppWindow.TitleBar;
+ titlebar.ExtendsContentIntoTitleBar = true;
+
+ void SetColor(ElementTheme acualTheme)
+ {
+ titlebar.ButtonBackgroundColor = titlebar.ButtonInactiveBackgroundColor = titlebar.ButtonPressedBackgroundColor = Colors.Transparent;
+ switch (acualTheme)
+ {
+ case ElementTheme.Dark:
+ titlebar.ButtonHoverBackgroundColor = Colors.Black;
+ titlebar.ButtonForegroundColor = Colors.White;
+ titlebar.ButtonHoverForegroundColor = Colors.White;
+ titlebar.ButtonPressedForegroundColor = Colors.Silver;
+ break;
+ case ElementTheme.Light:
+ titlebar.ButtonHoverBackgroundColor = Colors.White;
+ titlebar.ButtonForegroundColor = Colors.Black;
+ titlebar.ButtonHoverForegroundColor = Colors.Black;
+ titlebar.ButtonPressedForegroundColor = Colors.DarkGray;
+ break;
+ }
+ }
+
+ RootUI.ActualThemeChanged += (s, _) => SetColor(s.ActualTheme);
+ window.SetTitleBar(AppTitleBar);
+ SetColor(RootUI.ActualTheme);
+ }
+ else
+ {
+ window.ExtendsContentIntoTitleBar = true;
+ window.SetTitleBar(AppTitleBar);
+ }
+ }
+}
+
+public class WindowsSystemDispatcherQueueHelper
+{
+ private object? _dispatcherQueueController;
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct DispatcherQueueOptions
+ {
+ internal int dwSize;
+ internal int threadType;
+ internal int apartmentType;
+ }
+
+ [DllImport("CoreMessaging.dll")]
+ private static extern int CreateDispatcherQueueController([In] DispatcherQueueOptions options, [In, Out, MarshalAs(UnmanagedType.IUnknown)] ref object? dispatcherQueueController);
+
+ public void EnsureWindowsSystemDispatcherQueueController()
+ {
+ if (Windows.System.DispatcherQueue.GetForCurrentThread() != null)
+ {
+ // one already exists, so we'll just use it.
+ return;
+ }
+
+ if (_dispatcherQueueController == null)
+ {
+ DispatcherQueueOptions options;
+ options.dwSize = Marshal.SizeOf(typeof(DispatcherQueueOptions));
+ options.threadType = 2;
+ options.apartmentType = 2;
+
+ CreateDispatcherQueueController(options, ref _dispatcherQueueController);
+ }
+ }
+}
+
+public class MicaBackground
+{
+ private readonly Window _window;
+ public readonly MicaController MicaController = new();
+ private SystemBackdropConfiguration _backdropConfiguration = new();
+ private readonly WindowsSystemDispatcherQueueHelper _dispatcherQueueHelper = new();
+
+ public MicaBackground(Window window)
+ {
+ _window = window;
+ }
+
+ public bool TrySetMicaBackdrop()
+ {
+ if (MicaController.IsSupported())
+ {
+ _dispatcherQueueHelper.EnsureWindowsSystemDispatcherQueueController();
+ _window.Activated += WindowOnActivated;
+ _window.Closed += WindowOnClosed;
+ ((FrameworkElement)_window.Content).ActualThemeChanged += MicaBackground_ActualThemeChanged;
+ _backdropConfiguration.IsInputActive = true;
+
+ _backdropConfiguration.Theme = _window.Content switch
+ {
+ FrameworkElement { ActualTheme: ElementTheme.Dark } => SystemBackdropTheme.Dark,
+ FrameworkElement { ActualTheme: ElementTheme.Light } => SystemBackdropTheme.Light,
+ FrameworkElement { ActualTheme: ElementTheme.Default } => SystemBackdropTheme.Default,
+ _ => throw new InvalidOperationException("Unknown theme")
+ };
+
+ MicaController.AddSystemBackdropTarget(_window.As());
+ MicaController.SetSystemBackdropConfiguration(_backdropConfiguration);
+
+ return true;
+ }
+
+ return false;
+ }
+
+ private void MicaBackground_ActualThemeChanged(FrameworkElement sender, object args)
+ {
+ if (_backdropConfiguration != null)
+ {
+ SetConfigurationSourceTheme();
+ }
+ }
+
+ private void SetConfigurationSourceTheme()
+ {
+ switch (((FrameworkElement)_window.Content).ActualTheme)
+ {
+ case ElementTheme.Dark: _backdropConfiguration.Theme = SystemBackdropTheme.Dark; break;
+ case ElementTheme.Light: _backdropConfiguration.Theme = SystemBackdropTheme.Light; break;
+ case ElementTheme.Default: _backdropConfiguration.Theme = SystemBackdropTheme.Default; break;
+ }
+ }
+
+ private void WindowOnClosed(object sender, WindowEventArgs args)
+ {
+ MicaController.Dispose();
+ _window.Activated -= WindowOnActivated;
+ _backdropConfiguration = null!;
+ }
+
+ private void WindowOnActivated(object sender, WindowActivatedEventArgs args)
+ {
+ _backdropConfiguration.IsInputActive = args.WindowActivationState is not WindowActivationState.Deactivated;
+ }
+}
+#endif
diff --git a/Emerald/MainPage.xaml b/Emerald/MainPage.xaml
new file mode 100644
index 00000000..63a41b90
--- /dev/null
+++ b/Emerald/MainPage.xaml
@@ -0,0 +1,131 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 4
+ 24
+ 4
+ 0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Emerald/MainPage.xaml.cs b/Emerald/MainPage.xaml.cs
new file mode 100644
index 00000000..a2496167
--- /dev/null
+++ b/Emerald/MainPage.xaml.cs
@@ -0,0 +1,125 @@
+using Emerald.Helpers;
+using Emerald.Models;
+using Emerald.Views.Settings;
+using Microsoft.UI.Xaml.Controls;
+using Microsoft.UI.Xaml.Media.Animation;
+
+namespace Emerald;
+
+public sealed partial class MainPage : Page
+{
+ public MainPage()
+ {
+ this.InitializeComponent();
+ this.Loaded += MainPage_Loaded;
+ NavView.ItemInvoked += MainNavigationView_ItemInvoked;
+ }
+ private void MainNavigationView_ItemInvoked(NavigationView sender, NavigationViewItemInvokedEventArgs args)
+ {
+ if (!args.IsSettingsInvoked && NavView.SelectedItem is SquareNavigationViewItem itm)
+ {
+ itm.InvokePropertyChanged();
+ }
+ }
+ private void MainPage_Loaded(object sender, RoutedEventArgs e)
+ {
+#if WINDOWS
+ Emerald.Helpers.WindowManager.SetTitleBar(App.Current.MainWindow, AppTitleBar);
+#endif
+ NavView.MenuItems.Add(new SquareNavigationViewItem("Home".Localize())
+ {
+ FontIconGlyph = "\xE80F",
+ Tag = "Home",
+ SolidFontIconGlyph = "\xEA8A",
+ IsSelected = true,
+ ShowFontIcons = true
+ });
+ NavView.MenuItems.Add(new SquareNavigationViewItem("Store".Localize())
+ {
+ Tag = "Store",
+ FontIconGlyph = "\xE7BF",
+ SolidFontIconGlyph = "\xE7BF",
+ IsSelected = false,
+ ShowFontIcons = true
+ });
+ NavView.MenuItems.Add(new SquareNavigationViewItem("News".Localize())
+ {
+ Tag = "News",
+ FontIconGlyph = "\xF57E",
+ SolidFontIconGlyph = "\xF57E",
+ IsSelected = false,
+ ShowFontIcons = true
+ });
+
+ NavView.FooterMenuItems.Add(new SquareNavigationViewItem("Tasks".Localize())
+ {
+ Tag = "Tasks",
+ FontIconGlyph = "\xE9D5",
+ SolidFontIconGlyph = "\xE9D5",
+ IsSelected = false,
+ ShowFontIcons = true
+ });
+ NavView.FooterMenuItems.Add(new SquareNavigationViewItem("Logs".Localize())
+ {
+ Tag = "Logs",
+ FontIconGlyph = "\xE756",
+ SolidFontIconGlyph = "\xE756",
+ IsSelected = false,
+ ShowFontIcons = true
+ });
+ NavView.FooterMenuItems.Add(new SquareNavigationViewItem("Settings".Localize())
+ {
+ Tag = "Settings",
+ FontIconGlyph = "\xE713",
+ SolidFontIconGlyph = "\xE713",
+ IsSelected = false,
+ ShowFontIcons = true
+ });
+
+ NavView.SelectedItem = NavView.MenuItems[0];
+
+ NavView.Header = new NavViewHeader() { HeaderText = "Home".Localize(), HeaderMargin = GetNavViewHeaderMargin() };
+ NavView.DisplayModeChanged += (_, _) => (NavView.Header as NavViewHeader).HeaderMargin = GetNavViewHeaderMargin();
+ Navigate(NavView.SelectedItem as SquareNavigationViewItem);
+ }
+
+ private Thickness GetNavViewHeaderMargin()
+ {
+ if (NavView.DisplayMode == NavigationViewDisplayMode.Minimal)
+ {
+ NavView.IsPaneToggleButtonVisible = true;
+ return new Thickness(35, -40, 0, 0);
+ }
+ else
+ {
+ NavView.IsPaneToggleButtonVisible = false;
+ return new Thickness(-30, -20, 0, 10);
+ }
+ }
+ private void navView_ItemInvoked(NavigationView sender, NavigationViewItemInvokedEventArgs args)
+ {
+ Navigate(NavView.SelectedItem as SquareNavigationViewItem);
+ }
+
+ private void Navigate(SquareNavigationViewItem itm)
+ {
+ switch (itm.Tag)
+ {
+ default:
+ NavigateOnce(typeof(SettingsPage));
+ break;
+ }
+ (NavView.Header as NavViewHeader).HeaderText = itm.Tag == "Tasks" ? (NavView.Header as NavViewHeader).HeaderText : itm.Name;
+ (NavView.Header as NavViewHeader).HeaderMargin = GetNavViewHeaderMargin();
+
+ }
+
+ private void NavigateOnce(Type type)
+ {
+
+ if (frame.Content == null || frame.Content.GetType() != type)
+ {
+ frame.Navigate(type, null, new EntranceNavigationTransitionInfo());
+ }
+ }
+}
diff --git a/Emerald/Models/ArgTemplate.cs b/Emerald/Models/ArgTemplate.cs
new file mode 100644
index 00000000..ee708800
--- /dev/null
+++ b/Emerald/Models/ArgTemplate.cs
@@ -0,0 +1,13 @@
+using CommunityToolkit.Mvvm.ComponentModel;
+
+namespace Emerald.Models;
+
+//Copied from Emerald.UWP
+[ObservableObject]
+public partial class ArgTemplate
+{
+ [ObservableProperty]
+ private string arg;
+
+ public int Count { get; set; }
+}
diff --git a/Emerald/Models/Model.cs b/Emerald/Models/Model.cs
new file mode 100644
index 00000000..552f279d
--- /dev/null
+++ b/Emerald/Models/Model.cs
@@ -0,0 +1,17 @@
+using CommunityToolkit.Mvvm.ComponentModel;
+using System.ComponentModel;
+
+namespace Emerald.Models;
+
+public partial class Model : ObservableObject
+{
+ internal void Set(ref T obj, T value, string name = null)
+ {
+ SetProperty(ref obj, value, name);
+ }
+
+ public void InvokePropertyChanged(string name = null)
+ {
+ OnPropertyChanged(new PropertyChangedEventArgs(name));
+ }
+}
diff --git a/Emerald/Models/NavViewHeader.cs b/Emerald/Models/NavViewHeader.cs
new file mode 100644
index 00000000..093bdbaa
--- /dev/null
+++ b/Emerald/Models/NavViewHeader.cs
@@ -0,0 +1,23 @@
+using CommunityToolkit.Mvvm.ComponentModel;
+
+namespace Emerald.Models;
+
+public partial class NavViewHeader : Model
+{
+ [ObservableProperty]
+ private string _HeaderText;
+
+ [ObservableProperty]
+ private string _CustomButtonText;
+
+ public Visibility CustomButtonVisibility { get => CustomButtonText == null ? Visibility.Collapsed : Visibility.Visible; }
+
+
+ [ObservableProperty]
+ private string _CustomContent;
+
+ public Visibility CustomContentVisibility { get => CustomContent == null ? Visibility.Collapsed : Visibility.Visible; }
+
+ [ObservableProperty]
+ private Thickness _HeaderMargin;
+}
diff --git a/Emerald/Models/SquareNavigationViewItem.cs b/Emerald/Models/SquareNavigationViewItem.cs
new file mode 100644
index 00000000..3f9f2ac5
--- /dev/null
+++ b/Emerald/Models/SquareNavigationViewItem.cs
@@ -0,0 +1,63 @@
+using System.ComponentModel;
+using CommunityToolkit.Mvvm.ComponentModel;
+
+namespace Emerald.Models;
+
+public partial class SquareNavigationViewItem : Model
+{
+ public SquareNavigationViewItem()
+ {
+ PropertyChanged += (_, e) =>
+ {
+ if (e.PropertyName == nameof(IsSelected) || e.PropertyName == nameof(ShowFontIcons))
+ {
+ InvokePropertyChanged(null);
+ }
+ };
+ }
+ public SquareNavigationViewItem(string name, bool isSelected = false, ImageSource image = null, InfoBadge infoBadge = null)
+ {
+ Name = name;
+ IsSelected = isSelected;
+ Thumbnail = image;
+ InfoBadge = infoBadge;
+ PropertyChanged += (_, e) =>
+ {
+ if (e.PropertyName == nameof(IsSelected) || e.PropertyName == nameof(ShowFontIcons))
+ {
+ InvokePropertyChanged(null);
+ }
+ };
+ }
+ public string Tag { get; set; }
+
+ [ObservableProperty]
+ private string _Name;
+
+ [ObservableProperty]
+ private string _FontIconGlyph;
+
+ [ObservableProperty]
+ private string _SolidFontIconGlyph;
+
+ [ObservableProperty]
+ private bool _IsSelected;
+
+ [ObservableProperty]
+ private bool _IsEnabled = true;
+
+ [ObservableProperty]
+ private ImageSource _Thumbnail;
+
+ [ObservableProperty]
+ private InfoBadge _InfoBadge;
+
+ [ObservableProperty]
+ private bool _ShowFontIcons;
+
+ public Visibility FontIconVisibility => ShowFontIcons && !IsSelected ? Visibility.Visible : Visibility.Collapsed;
+ public Visibility SolidFontIconVisibility => ShowFontIcons && IsSelected ? Visibility.Visible : Visibility.Collapsed;
+ public Visibility SelectionVisibility => IsSelected ? Visibility.Collapsed : Visibility.Visible;
+
+ public bool ShowThumbnail => !ShowFontIcons;
+}
diff --git a/Emerald/Package.appxmanifest b/Emerald/Package.appxmanifest
new file mode 100644
index 00000000..29558f1d
--- /dev/null
+++ b/Emerald/Package.appxmanifest
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+ Emerald
+ Emerald
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Emerald/Platforms/Desktop/Program.cs b/Emerald/Platforms/Desktop/Program.cs
new file mode 100644
index 00000000..505e2c1b
--- /dev/null
+++ b/Emerald/Platforms/Desktop/Program.cs
@@ -0,0 +1,19 @@
+using Uno.UI.Runtime.Skia;
+
+namespace Emerald;
+public class Program
+{
+ [STAThread]
+ public static void Main(string[] args)
+ {
+ var host = SkiaHostBuilder.Create()
+ .App(() => new App())
+ .UseX11()
+ .UseLinuxFrameBuffer()
+ .UseMacOS()
+ .UseWindows()
+ .Build();
+
+ host.Run();
+ }
+}
diff --git a/Emerald/Platforms/MacCatalyst/Entitlements.plist b/Emerald/Platforms/MacCatalyst/Entitlements.plist
new file mode 100644
index 00000000..24c31036
--- /dev/null
+++ b/Emerald/Platforms/MacCatalyst/Entitlements.plist
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/Emerald/Platforms/MacCatalyst/Info.plist b/Emerald/Platforms/MacCatalyst/Info.plist
new file mode 100644
index 00000000..1bb02ddc
--- /dev/null
+++ b/Emerald/Platforms/MacCatalyst/Info.plist
@@ -0,0 +1,26 @@
+
+
+
+
+ UIDeviceFamily
+
+ 2
+
+ LSApplicationCategoryType
+ public.app-category.utilities
+ UISupportedInterfaceOrientations
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ XSAppIconAssets
+ Assets.xcassets/icon.appiconset
+
+
+
+
diff --git a/Emerald/Platforms/MacCatalyst/Main.maccatalyst.cs b/Emerald/Platforms/MacCatalyst/Main.maccatalyst.cs
new file mode 100644
index 00000000..d26d16e3
--- /dev/null
+++ b/Emerald/Platforms/MacCatalyst/Main.maccatalyst.cs
@@ -0,0 +1,13 @@
+using UIKit;
+
+namespace Emerald.MacCatalyst;
+public class EntryPoint
+{
+ // This is the main entry point of the application.
+ public static void Main(string[] args)
+ {
+ // if you want to use a different Application Delegate class from "AppDelegate"
+ // you can specify it here.
+ UIApplication.Main(args, null, typeof(App));
+ }
+}
diff --git a/Emerald/Platforms/MacCatalyst/Media.xcassets/LaunchImages.launchimage/Contents.json b/Emerald/Platforms/MacCatalyst/Media.xcassets/LaunchImages.launchimage/Contents.json
new file mode 100644
index 00000000..69555e44
--- /dev/null
+++ b/Emerald/Platforms/MacCatalyst/Media.xcassets/LaunchImages.launchimage/Contents.json
@@ -0,0 +1,58 @@
+{
+ "images": [
+ {
+ "orientation": "portrait",
+ "extent": "full-screen",
+ "minimum-system-version": "7.0",
+ "scale": "2x",
+ "size": "640x960",
+ "idiom": "iphone"
+ },
+ {
+ "orientation": "portrait",
+ "extent": "full-screen",
+ "minimum-system-version": "7.0",
+ "subtype": "retina4",
+ "scale": "2x",
+ "size": "640x1136",
+ "idiom": "iphone"
+ },
+ {
+ "orientation": "portrait",
+ "extent": "full-screen",
+ "minimum-system-version": "7.0",
+ "scale": "1x",
+ "size": "768x1024",
+ "idiom": "ipad"
+ },
+ {
+ "orientation": "landscape",
+ "extent": "full-screen",
+ "minimum-system-version": "7.0",
+ "scale": "1x",
+ "size": "1024x768",
+ "idiom": "ipad"
+ },
+ {
+ "orientation": "portrait",
+ "extent": "full-screen",
+ "minimum-system-version": "7.0",
+ "scale": "2x",
+ "size": "1536x2048",
+ "idiom": "ipad"
+ },
+ {
+ "orientation": "landscape",
+ "extent": "full-screen",
+ "minimum-system-version": "7.0",
+ "scale": "2x",
+ "size": "2048x1536",
+ "idiom": "ipad"
+ }
+ ],
+ "properties": {},
+ "info": {
+ "version": 1,
+ "author": ""
+ }
+}
\ No newline at end of file
diff --git a/Emerald/Platforms/Windows/Properties/PublishProfiles/win-arm64.pubxml b/Emerald/Platforms/Windows/Properties/PublishProfiles/win-arm64.pubxml
new file mode 100644
index 00000000..d5147f10
--- /dev/null
+++ b/Emerald/Platforms/Windows/Properties/PublishProfiles/win-arm64.pubxml
@@ -0,0 +1,22 @@
+
+
+
+
+ FileSystem
+ arm64
+ win-arm64
+ bin\$(Configuration)\$(TargetFramework)\$(RuntimeIdentifier)\publish\
+ true
+ False
+ False
+ True
+
+
+
+
diff --git a/Emerald/Platforms/Windows/Properties/PublishProfiles/win-x64.pubxml b/Emerald/Platforms/Windows/Properties/PublishProfiles/win-x64.pubxml
new file mode 100644
index 00000000..4fea954e
--- /dev/null
+++ b/Emerald/Platforms/Windows/Properties/PublishProfiles/win-x64.pubxml
@@ -0,0 +1,22 @@
+
+
+
+
+ FileSystem
+ x64
+ win-x64
+ bin\$(Configuration)\$(TargetFramework)\$(RuntimeIdentifier)\publish\
+ true
+ False
+ False
+ True
+
+
+
+
diff --git a/Emerald/Platforms/Windows/Properties/PublishProfiles/win-x86.pubxml b/Emerald/Platforms/Windows/Properties/PublishProfiles/win-x86.pubxml
new file mode 100644
index 00000000..928cb25e
--- /dev/null
+++ b/Emerald/Platforms/Windows/Properties/PublishProfiles/win-x86.pubxml
@@ -0,0 +1,22 @@
+
+
+
+
+ FileSystem
+ x86
+ win-x86
+ bin\$(Configuration)\$(TargetFramework)\$(RuntimeIdentifier)\publish\
+ true
+ False
+ False
+ True
+
+
+
+
diff --git a/Emerald/Properties/launchSettings.json b/Emerald/Properties/launchSettings.json
new file mode 100644
index 00000000..a8c8aed9
--- /dev/null
+++ b/Emerald/Properties/launchSettings.json
@@ -0,0 +1,31 @@
+{
+ "iisSettings": {
+ "windowsAuthentication": false,
+ "anonymousAuthentication": true,
+ "iisExpress": {
+ "applicationUrl": "http://localhost:8080",
+ "sslPort": 0
+ }
+ },
+ "profiles": {
+ // Note: In order to select this profile, you'll need to comment the `Packaged` profile below until this is fixed: https://aka.platform.uno/wasdk-maui-debug-profile-issue
+ "Emerald (WinAppSDK Unpackaged)": {
+ "commandName": "Project",
+ "compatibleTargetFramework": "windows"
+ },
+ "Emerald (WinAppSDK Packaged)": {
+ "commandName": "MsixPackage",
+ "compatibleTargetFramework": "windows"
+ },
+ "Emerald (Desktop)": {
+ "commandName": "Project",
+ "compatibleTargetFramework": "desktop"
+ },
+ "Emerald (Desktop WSL2)": {
+ "commandName": "WSL2",
+ "commandLineArgs": "{ProjectDir}/bin/Debug/net8.0-desktop/Emerald.dll",
+ "distributionName": "",
+ "compatibleTargetFramework": "desktop"
+ }
+ }
+}
diff --git a/Emerald/ReadMe.md b/Emerald/ReadMe.md
new file mode 100644
index 00000000..93482da2
--- /dev/null
+++ b/Emerald/ReadMe.md
@@ -0,0 +1,7 @@
+# Getting Started
+
+Welcome to the Uno Platform!
+
+To discover how to get started with your new app: https://aka.platform.uno/get-started
+
+For more information on how to use the Uno.Sdk or upgrade Uno Platform packages in your solution: https://aka.platform.uno/using-uno-sdk
\ No newline at end of file
diff --git a/Emerald/Strings/en/Resources.resw b/Emerald/Strings/en/Resources.resw
new file mode 100644
index 00000000..7297f452
--- /dev/null
+++ b/Emerald/Strings/en/Resources.resw
@@ -0,0 +1,760 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ Emerald-en
+
+
+ About
+
+
+ Accent color
+
+
+ Access token
+
+
+ Account Details
+
+
+ Accounts
+
+
+ Account Type
+
+
+ Add
+
+
+ Advanced Settings
+
+
+ All
+
+
+ Alpha
+
+
+ Appearance
+
+
+ Assets check
+
+
+ Authenticating using Windows Hello...
+
+
+ Automatically download updates if available
+
+
+ Auto login
+
+
+ Automatically tries to login with the most recent account at startup
+
+
+ Please Login
+
+
+ Please enter a version
+
+
+ Beta
+
+
+ Build type
+
+
+ Changelogs
+
+
+ Check for updates
+
+
+ Check for updates when app starts
+
+
+ Checking for updates
+
+
+ Check Now
+
+
+ Choose an account
+
+
+ Choose a version
+
+
+ Clear
+
+
+ Clear All
+
+
+ Colorful
+
+
+ Color Mode
+
+
+ Choose your color
+
+
+ Configure Optifine
+
+
+ Connecting to github
+
+
+ Contribute
+
+
+ Copy version
+
+
+ Create a color
+
+
+ Create Backup
+
+
+ Create a backup of the old settings
+
+
+ Credits
+
+
+ Respect...
+
+
+ Custom color
+
+
+ Custom colors
+
+
+ Dark
+
+
+ Default
+
+
+ Delete
+
+
+ Using DeviceCode API
+
+
+ Discord
+
+
+ Downgrade
+
+
+ Downgrade Available
+
+
+ The current version is higher than the latest version in the servers. You must be using a private beta. Please wait until a higher stable version be available
+
+
+ Download
+
+
+ The mod downloader is busy
+
+
+ Downloading
+
+
+ Download Update
+
+
+ Download Mod
+
+
+ Downloads
+
+
+ Download Settings
+
+
+ Optional. Recommended to turn these on
+
+
+ Duplicate
+
+
+ Using an embeded webview
+
+
+ Empty
+
+
+ Error
+
+
+ Finish!
+
+
+ Followers
+
+
+ Full Screen
+
+
+ Game is running...
+
+
+ Read game logs
+
+
+ For debug perposes
+
+
+ General
+
+
+ General Settings
+
+
+ Get Fabric
+
+
+ Get Mods
+
+
+ Getting Fabric
+
+
+ Getting inherited version...
+
+
+ Load Available Mods
+
+
+ Getting Optifine...
+
+
+ Getting available versions...
+
+
+ Failed to get the version
+
+
+ Github
+
+
+ Good Evening
+
+
+ Good Luck
+
+
+ Good Morning
+
+
+ Got it!
+
+
+ Go to {Link} and enter the code below to sign-in
+
+
+ Hash check
+
+
+ Screen Height
+
+
+ Home
+
+
+ Include pre-releases
+
+
+ Initialize Launcher Core
+
+
+ Install
+
+
+ Install Update
+
+
+ JVM Arguments
+
+
+ For Endermans!
+
+
+ Latest
+
+
+ Launch!
+
+
+ Launching Minecraft...
+
+
+ Launch Minecraft
+
+
+ Light
+
+
+ Links
+
+
+ Keep up with the latest updates
+
+
+ Load ChangeLogs
+
+
+ Load Download Versions
+
+
+ Loading ChangeLog
+
+
+ Loading Download Versions
+
+
+ Loading Mod
+
+
+ Load Mod
+
+
+ Failed to load the app settings and accounts. The version of the setting system is different. You may have recently updated the app. Contact a developer of this app to recover the settings
+
+
+ Login
+
+
+ Login with Microsoft
+
+
+ Logs
+
+
+ Login
+
+
+ For a better RPC please add a small status!
+
+
+ Status:
+
+
+ Launch minecraft as admin
+
+
+ Do this at your own risk!
+
+
+ Minecraft for Windows
+
+
+ Minecraft Dungeons
+
+
+ Minecraft: Java Edition
+
+
+ Minecraft Legends
+
+
+ Failed to initialize Minecraft path at `{Path}`. Do you want to try again?
+
+
+ Minecraft Windows Size
+
+
+ Change width, height or full screen
+
+
+ Merge Account
+
+
+ {Count} Microsoft account with same UUID already exist(s) in the accounts. Do you want to merge them together?
+
+
+ Mica Type
+
+
+ Normal Mica or Mica Alt (Tabbed)
+
+
+ Microsoft Account
+
+
+ Microsoft or offline
+
+
+ Minecraft Path
+
+
+ The path of the minecraft client
+
+
+ Minecraft Settings
+
+
+ Cannot find mods with follwing name or you may got disconnected from the internet.
+
+
+ More
+
+
+ More version info
+
+
+ Navigation Buttons Icon Style
+
+
+ Select a beautiful icons set for your sidebar
+
+
+ New Account
+
+
+ Looks like you are new here. Shall we go on a quick check out?
+
+
+ News
+
+
+ Cannot find news with follwing title or you may got disconnected from the internet.
+
+
+ No color
+
+
+ Failed to find the installer package in the release for this architecture
+
+
+ Seems like you don't have good internet...
+
+
+ No Thanks
+
+
+ You are on the latest version
+
+
+ No Updates Available
+
+
+ Offline account
+
+
+ Offline Mode
+
+
+ Options
+
+
+ Pick a Version
+
+
+ Memory
+
+
+ The allocated memory of the minecraft client
+
+
+ Failed to get the RAM of the device.
+ Rarely Used
+
+
+ Ready
+
+
+ Failed to get the Minecraft versions
+
+
+ Refresh Versions
+
+
+ Release
+
+
+ Release notes
+
+
+ Remove
+
+
+ Retry
+
+
+ Save
+
+
+ Search
+
+
+ Searching Store
+
+
+ Search Store
+
+
+ Select
+
+
+ Select All
+
+
+ Select None
+
+
+ Change Path
+
+
+ Settings
+
+
+ Settings Backups
+
+
+ Create, delete, view and try to load backups of your settings
+
+
+ This will overwrite the current settings and restart the application. If the application failed to load the settings, the previous settings will be lost. Do you want to create a backup of this settings before load?
+
+
+ Settings
+
+
+ Sign-in with Microsoft
+
+
+ Store
+
+
+ Sure!
+
+
+ Switch to offline mode
+
+
+ System Default
+
+
+ If you clear a running task, it won't get stopped. It'll run in the background without showing a progress.
+
+
+ Tasks
+
+
+ That's it! Go to settings for more options
+
+
+ {Time} left
+
+
+ Try to load
+
+
+ Local
+
+
+ OldAlpha
+
+
+ OldBeta
+
+
+ Release
+
+
+ Snapshot
+
+
+ Some unexpected thing happend. Please restart the app!
+
+
+ Update Available
+
+
+ Update now
+
+
+ Updates
+
+
+ Auto update, include prerelease etc..
+
+
+ Username
+
+
+ Using the default browser
+
+
+ UUID
+
+
+ Fabric and Vanilla is officially supported
+
+
+ Version
+
+
+ View
+
+
+ Warning
+
+
+ Welcome!
+ Rarely Used
+
+
+ Whoops!
+
+
+ Screen Width
+
+
+ It seems your java setting has problem
+ Rarely Used
+
+
+ Windows Hello
+
+
+ Do not create, view, load, delete backups without Windows Hello authentication
+
+
+ Window tint color
+
+
+ Show a color overlay behind the app content
+
+
+ Unable to change the setting because you failed to authenticate with Windows Hello
+
+
+ Invalid RAM
+ Rarely Used
+
+
\ No newline at end of file
diff --git a/Emerald/UserControls/ArgumentsListView.xaml b/Emerald/UserControls/ArgumentsListView.xaml
new file mode 100644
index 00000000..ca819bcd
--- /dev/null
+++ b/Emerald/UserControls/ArgumentsListView.xaml
@@ -0,0 +1,62 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Emerald/UserControls/ArgumentsListView.xaml.cs b/Emerald/UserControls/ArgumentsListView.xaml.cs
new file mode 100644
index 00000000..6ac337f1
--- /dev/null
+++ b/Emerald/UserControls/ArgumentsListView.xaml.cs
@@ -0,0 +1,92 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Runtime.InteropServices.WindowsRuntime;
+using Windows.Foundation;
+using Windows.Foundation.Collections;
+using Microsoft.UI.Xaml;
+using Microsoft.UI.Xaml.Controls;
+using Microsoft.UI.Xaml.Controls.Primitives;
+using Microsoft.UI.Xaml.Data;
+using Microsoft.UI.Xaml.Input;
+using Microsoft.UI.Xaml.Media;
+using Microsoft.UI.Xaml.Navigation;
+using System.Collections.ObjectModel;
+using SS = Emerald.Helpers.Settings.SettingsSystem;
+using Emerald.Models;
+
+
+namespace Emerald.UserControls;
+
+ //Copied from Emerald.UWP
+ public sealed partial class ArgumentsListView : UserControl
+ {
+ private int count = 0;
+ public ArgumentsListView()
+ {
+ InitializeComponent();
+ view.ItemsSource = Source;
+ UpdateSource();
+ }
+
+ private ObservableCollection Source = new();
+ private void btnAdd_Click(object sender, RoutedEventArgs e)
+ {
+ count++;
+ var r = new ArgTemplate { Arg = "", Count = count };
+ Source.Add(r);
+ UpdateMainSource();
+ view.SelectedItem = r;
+ }
+ public void UpdateSource()
+ {
+ Source.Clear();
+ if (SS.Settings.Minecraft.JVM.Arguments != null)
+ {
+ foreach (var item in SS.Settings.Minecraft.JVM.Arguments)
+ {
+ count++;
+ var r = new ArgTemplate { Arg = item, Count = count };
+ r.PropertyChanged += (_, _) =>
+ {
+ UpdateMainSource();
+ };
+ Source.Add(r);
+ }
+ }
+ btnRemove.IsEnabled = Source.Any();
+ }
+ private void UpdateMainSource() =>
+ SS.Settings.Minecraft.JVM.Arguments = Source.Select(x => x.Arg).ToArray();
+
+ private void btnRemove_Click(object sender, RoutedEventArgs e)
+ {
+ foreach (var item in view.SelectedItems)
+ {
+ Source.Remove((ArgTemplate)item);
+ }
+ UpdateMainSource();
+ }
+
+ private void TextBox_PointerPressed(object sender, PointerRoutedEventArgs e)
+ {
+ view.SelectedIndex = Source.IndexOf(Source.FirstOrDefault(x => x.Count == ((sender as FrameworkElement).DataContext as ArgTemplate).Count));
+ }
+
+ private void TextBox_TextChanged(object sender, TextChangedEventArgs e)
+ {
+ view.SelectedIndex = Source.IndexOf(Source.FirstOrDefault(x => x.Count == ((sender as FrameworkElement).DataContext as ArgTemplate).Count));
+ UpdateMainSource();
+ }
+
+ private void TextBox_GotFocus(object sender, RoutedEventArgs e)
+ {
+ view.SelectedIndex = Source.IndexOf(Source.FirstOrDefault(x => x.Count == ((sender as FrameworkElement).DataContext as ArgTemplate).Count));
+ }
+
+ private void view_SelectionChanged(object sender, SelectionChangedEventArgs e)
+ {
+ btnRemove.IsEnabled = view.SelectedItems.Any();
+ }
+ }
diff --git a/Emerald/UserControls/Titlebar.xaml b/Emerald/UserControls/Titlebar.xaml
new file mode 100644
index 00000000..76be090a
--- /dev/null
+++ b/Emerald/UserControls/Titlebar.xaml
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Emerald/UserControls/Titlebar.xaml.cs b/Emerald/UserControls/Titlebar.xaml.cs
new file mode 100644
index 00000000..b134aa33
--- /dev/null
+++ b/Emerald/UserControls/Titlebar.xaml.cs
@@ -0,0 +1,25 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Runtime.InteropServices.WindowsRuntime;
+using Microsoft.UI.Xaml;
+using Microsoft.UI.Xaml.Controls;
+using Microsoft.UI.Xaml.Controls.Primitives;
+using Microsoft.UI.Xaml.Data;
+using Microsoft.UI.Xaml.Input;
+using Microsoft.UI.Xaml.Media;
+using Microsoft.UI.Xaml.Navigation;
+using Windows.Foundation;
+using Windows.Foundation.Collections;
+
+// The User Control item template is documented at https://go.microsoft.com/fwlink/?LinkId=234236
+
+namespace Emerald.UserControls;
+public sealed partial class TitleBar : UserControl
+{
+ public TitleBar()
+ {
+ this.InitializeComponent();
+ }
+}
diff --git a/Emerald/Views/Settings/GeneralPage.xaml b/Emerald/Views/Settings/GeneralPage.xaml
new file mode 100644
index 00000000..c998ca54
--- /dev/null
+++ b/Emerald/Views/Settings/GeneralPage.xaml
@@ -0,0 +1,149 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ GB
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Emerald/Views/Settings/GeneralPage.xaml.cs b/Emerald/Views/Settings/GeneralPage.xaml.cs
new file mode 100644
index 00000000..5f0e5161
--- /dev/null
+++ b/Emerald/Views/Settings/GeneralPage.xaml.cs
@@ -0,0 +1,84 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Runtime.InteropServices.WindowsRuntime;
+using CmlLib.Core;
+using CommunityToolkit.WinUI.Controls;
+using Emerald.Helpers;
+using Microsoft.UI.Xaml;
+using Microsoft.UI.Xaml.Controls;
+using Microsoft.UI.Xaml.Controls.Primitives;
+using Microsoft.UI.Xaml.Data;
+using Microsoft.UI.Xaml.Input;
+using Microsoft.UI.Xaml.Media;
+using Microsoft.UI.Xaml.Navigation;
+using Windows.Foundation;
+using Windows.Foundation.Collections;
+using Windows.Storage.Pickers;
+
+using SS = Emerald.Helpers.Settings.SettingsSystem;
+// The Blank Page item template is documented at https://go.microsoft.com/fwlink/?LinkId=234238
+
+namespace Emerald.Views.Settings;
+///
+/// An empty page that can be used on its own or navigated to within a Frame.
+///
+public sealed partial class GeneralPage : Page
+{
+ public GeneralPage()
+ {
+ this.InitializeComponent();
+ }
+ private void btnChangeMPath_Click(object sender, RoutedEventArgs e)
+ {
+ //MinecraftPath mcP;
+ //string path = "";
+ //async void Start()
+ //{
+ // var fop = new FolderPicker
+ // {
+ // CommitButtonText = "Select".Localize()
+ // };
+ // WinRT.Interop.InitializeWithWindow.Initialize(fop, WinRT.Interop.WindowNative.GetWindowHandle(App.Current.MainWindow));
+ // var f = await fop.PickSingleFolderAsync();
+
+ // if (f != null)
+ // path = f.Path;
+ // else
+ // return;
+
+ // Try();
+
+ // async void Try()
+ // {
+ // try
+ // {
+ // mcP = new(path);
+ // SS.Settings.Minecraft.Path = path;
+ // App.Current.Launcher.InitializeLauncher(mcP);
+ // }
+ // catch
+ // {
+ // var r = await MessageBox.Show("Error".Localize(), "MCPathFailed".Localize().Replace("{Path}", path), MessageBoxButtons.Custom, "Yes".Localize(), "SetDifMCPath".Localize());
+ // if (r == MessageBoxResults.Yes)
+ // Try();
+ // else
+ // Start();
+ // }
+ // }
+ //}
+ //Start();
+ }
+
+ private void btnRamPlus_Click(object sender, RoutedEventArgs e) =>
+ SS.Settings.Minecraft.RAM = SS.Settings.Minecraft.RAM + (SS.Settings.Minecraft.RAM >= DirectResoucres.MaxRAM ? 0 : (DirectResoucres.MaxRAM - SS.Settings.Minecraft.RAM >= 50 ? 50 : DirectResoucres.MaxRAM - SS.Settings.Minecraft.RAM));
+
+
+ private void btnRamMinus_Click(object sender, RoutedEventArgs e) =>
+ SS.Settings.Minecraft.RAM = SS.Settings.Minecraft.RAM - (SS.Settings.Minecraft.RAM <= DirectResoucres.MinRAM ? 0 : 50);
+
+
+ private void btnAutoRAM_Click(object sender, RoutedEventArgs e) =>
+ SS.Settings.Minecraft.RAM = DirectResoucres.MaxRAM / 2;
+}
diff --git a/Emerald/Views/Settings/SettingsPage.xaml b/Emerald/Views/Settings/SettingsPage.xaml
new file mode 100644
index 00000000..4d7b56dc
--- /dev/null
+++ b/Emerald/Views/Settings/SettingsPage.xaml
@@ -0,0 +1,68 @@
+
+
+
+
+
+
+
+ 0
+ 0
+ 40
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Emerald/Views/Settings/SettingsPage.xaml.cs b/Emerald/Views/Settings/SettingsPage.xaml.cs
new file mode 100644
index 00000000..4720b56c
--- /dev/null
+++ b/Emerald/Views/Settings/SettingsPage.xaml.cs
@@ -0,0 +1,44 @@
+using Microsoft.UI.Xaml.Controls;
+using Microsoft.UI.Xaml.Media.Animation;
+using System;
+
+namespace Emerald.Views.Settings;
+
+public sealed partial class SettingsPage : Page
+{
+ public SettingsPage()
+ {
+ InitializeComponent();
+
+ //Loaded += (_, _) => Navigate(navView.SelectedItem as NavigationViewItem);
+ }
+
+ private void navView_ItemInvoked(NavigationView sender, NavigationViewItemInvokedEventArgs args)
+ {
+ Navigate(navView.SelectedItem as NavigationViewItem);
+ }
+
+ private void Navigate(NavigationViewItem itm)
+ {
+ switch (itm.Tag)
+ {
+ case "Appearance":
+ NavigateOnce(typeof(SettingsPage));
+ break;
+ case "About":
+ NavigateOnce(typeof(SettingsPage));
+ break;
+ default:
+ NavigateOnce(typeof(GeneralPage));
+ break;
+ }
+ }
+
+ private void NavigateOnce(Type type)
+ {
+ if (contentframe.Content == null || contentframe.Content.GetType() != type)
+ {
+ contentframe.Navigate(type, null, new DrillInNavigationTransitionInfo());
+ }
+ }
+}
diff --git a/Emerald/app.manifest b/Emerald/app.manifest
new file mode 100644
index 00000000..7c69768c
--- /dev/null
+++ b/Emerald/app.manifest
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true/PM
+ PerMonitorV2, PerMonitor
+
+
+
diff --git a/README.md b/README.md
index f7139fd5..861c3709 100644
--- a/README.md
+++ b/README.md
@@ -61,7 +61,8 @@ There are multiple ways to participate in the community:
├──Emerald.App // Emerald app code and packager
| ├──Emerald.App // Emerald app code (such as code related to UI but not Minecraft)
| └──Emerald.App.Package // Package code for generating an uploadable MSIX bundle.
-└──Emerald.Core // Emerald core code (such as code related to launching and modifying Minecraft
+├──Emerald.Core // Emerald core code (such as code related to launching and modifying Minecraft)
+└──Emerald.CoreX // Emerald core code for the ability to run different Minecraft installation profiles and mods.
```
## 🔨 Building the Code
diff --git a/global.json b/global.json
new file mode 100644
index 00000000..d0de00bf
--- /dev/null
+++ b/global.json
@@ -0,0 +1,9 @@
+{
+ // To update the version of Uno please update the version of the Uno.Sdk here. See https://aka.platform.uno/upgrade-uno-packages for more information.
+ "msbuild-sdks": {
+ "Uno.Sdk": "5.5.49"
+ },
+ "sdk": {
+ "allowPrerelease": false
+ }
+}