Compare commits

..

1 Commits

Author SHA1 Message Date
Lightczx
c8b06b8008 parse interpolated syntax 2024-02-23 17:29:46 +08:00
193 changed files with 2256 additions and 3443 deletions

View File

@@ -35,8 +35,7 @@ Install with Snap Hutao MSIX package, can be installed with Windows built-in App
* [向我们提交 PR / Make Pull Requests](https://github.com/DGP-Studio/Snap.Hutao/pulls)
* [在 Crowdin 上进行本地化 / Translate Project on Crowdin](https://translate.hut.ao/)
* [为我们更新文档 / Enhance our Document](https://github.com/DGP-Studio/Snap.Hutao.Docs)
* [帮助我们测试程序 / Test Binary Package](https://hut.ao/development/contribute.html)
* [为我们更新文档 / Enhance our Document ](https://github.com/DGP-Studio/Snap.Hutao.Docs)
## 特别感谢 / Special Thanks

View File

@@ -69,15 +69,6 @@ else if (AppVeyor.IsRunningOnAppVeyor)
})[..^2];
Information($"Version: {version}");
}
else // Local
{
repoDir = System.Environment.CurrentDirectory;
outputPath = System.IO.Path.Combine(repoDir, "src", "output");
version = System.DateTime.Now.ToString("yyyy.M.d.") + ((int)((System.DateTime.Now - System.DateTime.Today).TotalSeconds / 86400 * 65535)).ToString();
Information($"Version: {version}");
}
Task("Build")
.IsDependentOn("Build binary package")
@@ -121,17 +112,6 @@ Task("Generate AppxManifest")
Information("Using Release configuration");
content = System.Text.RegularExpressions.Regex.Replace(content, " Publisher=\"([^\"]*)\"", " Publisher=\"CN=SignPath Foundation, O=SignPath Foundation, L=Lewes, S=Delaware, C=US\"");
}
else
{
Information("Using Local configuration.");
content = content
.Replace("Snap Hutao", "Snap Hutao Local")
.Replace("胡桃", "胡桃 Local")
.Replace("DGP Studio", "DGP Studio CI");
content = System.Text.RegularExpressions.Regex.Replace(content, " Name=\"([^\"]*)\"", " Name=\"E8B6E2B3-D2A0-4435-A81D-2A16AAF405C7\"");
content = System.Text.RegularExpressions.Regex.Replace(content, " Publisher=\"([^\"]*)\"", " Publisher=\"E=admin@dgp-studio.cn, CN=DGP Studio CI, OU=CI, O=DGP-Studio, L=San Jose, S=CA, C=US\"");
content = System.Text.RegularExpressions.Regex.Replace(content, " Version=\"([0-9\\.]+)\"", $" Version=\"{version}\"");
}
System.IO.File.WriteAllText(manifest, content);
@@ -193,10 +173,6 @@ Task("Build MSIX")
{
arguments = "pack /d " + binPath + " /p " + System.IO.Path.Combine(outputPath, $"Snap.Hutao-{version}.msix");
}
else
{
arguments = "pack /d " + binPath + " /p " + System.IO.Path.Combine(outputPath, $"Snap.Hutao.Local-{version}.msix");
}
var p = StartProcess(
"makeappx.exe",
new ProcessSettings

View File

@@ -13,9 +13,9 @@
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
<PackageReference Include="MSTest.TestAdapter" Version="3.2.2" />
<PackageReference Include="MSTest.TestFramework" Version="3.2.2" />
<PackageReference Include="coverlet.collector" Version="6.0.1">
<PackageReference Include="MSTest.TestAdapter" Version="3.2.1" />
<PackageReference Include="MSTest.TestFramework" Version="3.2.1" />
<PackageReference Include="coverlet.collector" Version="6.0.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>

View File

@@ -6,7 +6,6 @@
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<XamlControlsResources/>
<ResourceDictionary Source="ms-appx:///CommunityToolkit.WinUI.Controls.TokenizingTextBox/TokenizingTextBox.xaml"/>
<ResourceDictionary Source="ms-appx:///Control/Loading.xaml"/>
<ResourceDictionary Source="ms-appx:///Control/Image/CachedImage.xaml"/>
<ResourceDictionary Source="ms-appx:///Control/Theme/Card.xaml"/>
@@ -23,13 +22,11 @@
<ResourceDictionary Source="ms-appx:///Control/Theme/PageOverride.xaml"/>
<ResourceDictionary Source="ms-appx:///Control/Theme/PivotOverride.xaml"/>
<ResourceDictionary Source="ms-appx:///Control/Theme/ScrollViewer.xaml"/>
<ResourceDictionary Source="ms-appx:///Control/Theme/SegmentedOverride.xaml"/>
<ResourceDictionary Source="ms-appx:///Control/Theme/SettingsStyle.xaml"/>
<ResourceDictionary Source="ms-appx:///Control/Theme/Thickness.xaml"/>
<ResourceDictionary Source="ms-appx:///Control/Theme/TransitionCollection.xaml"/>
<ResourceDictionary Source="ms-appx:///Control/Theme/Uri.xaml"/>
<ResourceDictionary Source="ms-appx:///Control/Theme/WindowOverride.xaml"/>
<ResourceDictionary Source="ms-appx:///View/Card/Primitive/CardProgressBar.xaml"/>
</ResourceDictionary.MergedDictionaries>
<Style

View File

@@ -1,79 +0,0 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using CommunityToolkit.WinUI;
using CommunityToolkit.WinUI.Controls;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Controls.Primitives;
using Snap.Hutao.Control.Extension;
namespace Snap.Hutao.Control.AutoSuggestBox;
[DependencyProperty("FilterCommand", typeof(ICommand))]
[DependencyProperty("FilterCommandParameter", typeof(object))]
[DependencyProperty("AvailableTokens", typeof(IReadOnlyDictionary<string, SearchToken>))]
internal sealed partial class AutoSuggestTokenBox : TokenizingTextBox
{
public AutoSuggestTokenBox()
{
DefaultStyleKey = typeof(TokenizingTextBox);
TextChanged += OnFilterSuggestionRequested;
QuerySubmitted += OnQuerySubmitted;
TokenItemAdding += OnTokenItemAdding;
TokenItemAdded += OnTokenItemModified;
TokenItemRemoved += OnTokenItemModified;
Loaded += OnLoaded;
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
if (this.FindDescendant("SuggestionsPopup") is Popup { Child: Border { Child: ListView listView } border })
{
IAppResourceProvider appResourceProvider = Ioc.Default.GetRequiredService<IAppResourceProvider>();
listView.Background = null;
listView.Margin = appResourceProvider.GetResource<Thickness>("AutoSuggestListPadding");
border.Background = appResourceProvider.GetResource<Microsoft.UI.Xaml.Media.Brush>("AutoSuggestBoxSuggestionsListBackground");
border.CornerRadius = new(0, 0, 8, 8);
}
}
private void OnFilterSuggestionRequested(Microsoft.UI.Xaml.Controls.AutoSuggestBox sender, AutoSuggestBoxTextChangedEventArgs args)
{
if (string.IsNullOrWhiteSpace(Text))
{
return;
}
if (args.Reason == AutoSuggestionBoxTextChangeReason.UserInput)
{
sender.ItemsSource = AvailableTokens.Values.Where(q => q.Value.Contains(Text, StringComparison.OrdinalIgnoreCase));
}
}
private void OnQuerySubmitted(Microsoft.UI.Xaml.Controls.AutoSuggestBox sender, AutoSuggestBoxQuerySubmittedEventArgs args)
{
if (args.ChosenSuggestion is not null)
{
return;
}
CommandInvocation.TryExecute(FilterCommand, FilterCommandParameter);
}
private void OnTokenItemAdding(TokenizingTextBox sender, TokenItemAddingEventArgs args)
{
if (string.IsNullOrWhiteSpace(args.TokenText))
{
return;
}
args.Item = AvailableTokens.GetValueOrDefault(args.TokenText) ?? new SearchToken(SearchTokenKind.None, args.TokenText);
}
private void OnTokenItemModified(TokenizingTextBox sender, object args)
{
CommandInvocation.TryExecute(FilterCommand, FilterCommandParameter);
}
}

View File

@@ -1,33 +0,0 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Windows.UI;
namespace Snap.Hutao.Control.AutoSuggestBox;
internal sealed class SearchToken
{
public SearchToken(SearchTokenKind kind, string value, Uri? iconUri = null, Uri? sideIconUri = null, Color? quality = null)
{
Value = value;
Kind = kind;
IconUri = iconUri;
SideIconUri = sideIconUri;
Quality = quality;
}
public SearchTokenKind Kind { get; }
public string Value { get; set; } = default!;
public Uri? IconUri { get; }
public Uri? SideIconUri { get; }
public Color? Quality { get; }
public override string ToString()
{
return Value;
}
}

View File

@@ -1,17 +0,0 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
namespace Snap.Hutao.Control.AutoSuggestBox;
internal enum SearchTokenKind
{
None,
AssociationType,
Avatar,
BodyType,
ElementName,
FightProperty,
ItemQuality,
Weapon,
WeaponType,
}

View File

@@ -3,7 +3,6 @@
using CommunityToolkit.WinUI.Behaviors;
using Microsoft.UI.Xaml;
using Snap.Hutao.Control.Extension;
namespace Snap.Hutao.Control.Behavior;
@@ -46,6 +45,10 @@ internal sealed partial class InvokeCommandOnLoadedBehavior : BehaviorBase<UIEle
return;
}
executed = Command.TryExecute(CommandParameter);
if (Command is not null && Command.CanExecute(CommandParameter))
{
Command.Execute(CommandParameter);
executed = true;
}
}
}

View File

@@ -3,7 +3,6 @@
using CommunityToolkit.WinUI.Behaviors;
using Microsoft.UI.Xaml;
using Snap.Hutao.Control.Extension;
namespace Snap.Hutao.Control.Behavior;
@@ -50,7 +49,10 @@ internal sealed partial class PeriodicInvokeCommandOrOnActualThemeChangedBehavio
return;
}
Command.TryExecute(CommandParameter);
if (Command is not null && Command.CanExecute(CommandParameter))
{
Command.Execute(CommandParameter);
}
}
private async ValueTask RunCoreAsync()

View File

@@ -1,18 +0,0 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
namespace Snap.Hutao.Control.Extension;
internal static class CommandInvocation
{
public static bool TryExecute(this ICommand? command, object? parameter = null)
{
if (command is not null && command.CanExecute(parameter))
{
command.Execute(parameter);
return true;
}
return false;
}
}

View File

@@ -2,13 +2,11 @@
// Licensed under the MIT license.
using Microsoft.UI.Xaml;
using System.Runtime.CompilerServices;
namespace Snap.Hutao.Control.Extension;
internal static class DependencyObjectExtension
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static IServiceProvider ServiceProvider(this DependencyObject obj)
{
return Ioc.Default;

View File

@@ -1,24 +0,0 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Microsoft.UI.Xaml;
namespace Snap.Hutao.Control.Helper;
[SuppressMessage("", "SH001")]
[DependencyProperty("VisibilityObject", typeof(object), null, nameof(OnVisibilityObjectChanged), IsAttached = true, AttachedType = typeof(UIElement))]
[DependencyProperty("OpacityObject", typeof(object), null, nameof(OnOpacityObjectChanged), IsAttached = true, AttachedType = typeof(UIElement))]
public sealed partial class UIElementHelper
{
private static void OnVisibilityObjectChanged(DependencyObject dp, DependencyPropertyChangedEventArgs e)
{
UIElement element = (UIElement)dp;
element.Visibility = e.NewValue is null ? Visibility.Collapsed : Visibility.Visible;
}
private static void OnOpacityObjectChanged(DependencyObject dp, DependencyPropertyChangedEventArgs e)
{
UIElement element = (UIElement)dp;
element.Opacity = e.NewValue is null ? 0D : 1D;
}
}

View File

@@ -3,9 +3,7 @@
using Microsoft.UI.Xaml.Media;
using Microsoft.UI.Xaml.Media.Imaging;
using Snap.Hutao.Control.Extension;
using Snap.Hutao.Core.Caching;
using Snap.Hutao.Core.ExceptionService;
using System.Runtime.InteropServices;
namespace Snap.Hutao.Control.Image;
@@ -28,11 +26,12 @@ internal sealed class CachedImage : Implementation.ImageEx
/// <inheritdoc/>
protected override async Task<ImageSource?> ProvideCachedResourceAsync(Uri imageUri, CancellationToken token)
{
IImageCache imageCache = this.ServiceProvider().GetRequiredService<IImageCache>();
// We can only use Ioc to retrieve IImageCache, no IServiceProvider is available.
IImageCache imageCache = Ioc.Default.GetRequiredService<IImageCache>();
try
{
HutaoException.ThrowIf(string.IsNullOrEmpty(imageUri.Host), HutaoExceptionKind.ImageCacheInvalidUri, SH.ControlImageCachedImageInvalidResourceUri);
Verify.Operation(!string.IsNullOrEmpty(imageUri.Host), SH.ControlImageCachedImageInvalidResourceUri);
string file = await imageCache.GetFileFromCacheAsync(imageUri).ConfigureAwait(true); // BitmapImage need to be created by main thread.
token.ThrowIfCancellationRequested(); // check token state to determine whether the operation should be canceled.
return new BitmapImage(file.ToUri()); // BitmapImage initialize with a uri will increase image quality and loading speed.

View File

@@ -168,7 +168,6 @@ internal abstract partial class CompositionImage : Microsoft.UI.Xaml.Controls.Co
if (surface.DecodedPhysicalSize.Size() <= 0D)
{
await Task.WhenAny(surfaceLoadTaskCompletionSource.Task, Task.Delay(5000, token)).ConfigureAwait(true);
await Task.Delay(50, token).ConfigureAwait(true);
}
LoadImageSurfaceCompleted(surface);

View File

@@ -7,14 +7,21 @@ using Windows.Media.Casting;
namespace Snap.Hutao.Control.Image.Implementation;
[DependencyProperty("NineGrid", typeof(Thickness))]
internal partial class ImageEx : ImageExBase
internal class ImageEx : ImageExBase
{
private static readonly DependencyProperty NineGridProperty = DependencyProperty.Register(nameof(NineGrid), typeof(Thickness), typeof(ImageEx), new PropertyMetadata(default(Thickness)));
public ImageEx()
: base()
{
}
public Thickness NineGrid
{
get => (Thickness)GetValue(NineGridProperty);
set => SetValue(NineGridProperty, value);
}
public override CompositionBrush GetAlphaMask()
{
if (IsInitialized && Image is Microsoft.UI.Xaml.Controls.Image image)

View File

@@ -6,7 +6,6 @@ using Microsoft.UI.Composition;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Media;
using Microsoft.UI.Xaml.Media.Imaging;
using Snap.Hutao.Win32;
using System.IO;
using Windows.Foundation;
@@ -159,6 +158,7 @@ internal abstract partial class ImageExBase : Microsoft.UI.Xaml.Controls.Control
if (value)
{
control.LayoutUpdated += control.OnImageExBaseLayoutUpdated;
control.InvalidateLazyLoading();
}
else
@@ -169,7 +169,7 @@ internal abstract partial class ImageExBase : Microsoft.UI.Xaml.Controls.Control
private static void LazyLoadingThresholdChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is ImageExBase { EnableLazyLoading: true } control)
if (d is ImageExBase control && control.EnableLazyLoading)
{
control.InvalidateLazyLoading();
}
@@ -229,6 +229,9 @@ internal abstract partial class ImageExBase : Microsoft.UI.Xaml.Controls.Control
private void AttachPlaceholderSource(ImageSource? source)
{
// Setting the source at this point should call ImageExOpened/VisualStateManager.GoToState
// as we register to both the ImageOpened/ImageFailed events of the underlying control.
// We only need to call those methods if we fail in other cases before we get here.
if (PlaceholderImage is Microsoft.UI.Xaml.Controls.Image image)
{
image.Source = source;
@@ -237,15 +240,6 @@ internal abstract partial class ImageExBase : Microsoft.UI.Xaml.Controls.Control
{
brush.ImageSource = source;
}
if (source is null)
{
VisualStateManager.GoToState(this, UnloadedState, true);
}
else if (source is BitmapSource { PixelHeight: > 0, PixelWidth: > 0 })
{
VisualStateManager.GoToState(this, LoadedState, true);
}
}
private async void SetSource(object? source)
@@ -317,7 +311,8 @@ internal abstract partial class ImageExBase : Microsoft.UI.Xaml.Controls.Control
}
tokenSource?.Cancel();
tokenSource = new();
tokenSource = new CancellationTokenSource();
AttachPlaceholderSource(null);
@@ -448,10 +443,9 @@ internal abstract partial class ImageExBase : Microsoft.UI.Xaml.Controls.Control
return;
}
Rect controlRect = TransformToVisual(hostElement).TransformBounds(StructMarshal.Rect(ActualSize));
Rect controlRect = TransformToVisual(hostElement)
.TransformBounds(new Rect(0, 0, ActualWidth, ActualHeight));
double lazyLoadingThreshold = LazyLoadingThreshold;
// Left/Top 1 Threshold, Right/Bottom 2 Threshold
Rect hostRect = new(
0 - lazyLoadingThreshold,
0 - lazyLoadingThreshold,

View File

@@ -0,0 +1,151 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Microsoft.UI.Composition;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Hosting;
using System.Numerics;
using Windows.Foundation;
namespace Snap.Hutao.Control.Layout;
internal sealed class DefaultItemCollectionTransitionProvider : ItemCollectionTransitionProvider
{
private const double DefaultAnimationDurationInMs = 300.0;
static DefaultItemCollectionTransitionProvider()
{
AnimationSlowdownFactor = 1.0;
}
public static double AnimationSlowdownFactor { get; set; }
protected override bool ShouldAnimateCore(ItemCollectionTransition transition)
{
return true;
}
protected override void StartTransitions(IList<ItemCollectionTransition> transitions)
{
List<ItemCollectionTransition> addTransitions = [];
List<ItemCollectionTransition> removeTransitions = [];
List<ItemCollectionTransition> moveTransitions = [];
foreach (ItemCollectionTransition transition in addTransitions)
{
switch (transition.Operation)
{
case ItemCollectionTransitionOperation.Add:
addTransitions.Add(transition);
break;
case ItemCollectionTransitionOperation.Remove:
removeTransitions.Add(transition);
break;
case ItemCollectionTransitionOperation.Move:
moveTransitions.Add(transition);
break;
}
}
StartAddTransitions(addTransitions, removeTransitions.Count > 0, moveTransitions.Count > 0);
StartRemoveTransitions(removeTransitions);
StartMoveTransitions(moveTransitions, removeTransitions.Count > 0);
}
private static void StartAddTransitions(IList<ItemCollectionTransition> transitions, bool hasRemoveTransitions, bool hasMoveTransitions)
{
foreach (ItemCollectionTransition transition in transitions)
{
ItemCollectionTransitionProgress progress = transition.Start();
Visual visual = ElementCompositionPreview.GetElementVisual(progress.Element);
Compositor compositor = visual.Compositor;
ScalarKeyFrameAnimation fadeInAnimation = compositor.CreateScalarKeyFrameAnimation();
fadeInAnimation.InsertKeyFrame(0.0f, 0.0f);
if (hasMoveTransitions && hasRemoveTransitions)
{
fadeInAnimation.InsertKeyFrame(0.66f, 0.0f);
}
else if (hasMoveTransitions || hasRemoveTransitions)
{
fadeInAnimation.InsertKeyFrame(0.5f, 0.0f);
}
fadeInAnimation.InsertKeyFrame(1.0f, 1.0f);
fadeInAnimation.Duration = TimeSpan.FromMilliseconds(
DefaultAnimationDurationInMs * ((hasRemoveTransitions ? 1 : 0) + (hasMoveTransitions ? 1 : 0) + 1) * AnimationSlowdownFactor);
CompositionScopedBatch batch = compositor.CreateScopedBatch(CompositionBatchTypes.Animation);
visual.StartAnimation("Opacity", fadeInAnimation);
batch.End();
batch.Completed += (_, _) => progress.Complete();
}
}
private static void StartRemoveTransitions(IList<ItemCollectionTransition> transitions)
{
foreach (ItemCollectionTransition transition in transitions)
{
ItemCollectionTransitionProgress progress = transition.Start();
Visual visual = ElementCompositionPreview.GetElementVisual(progress.Element);
Compositor compositor = visual.Compositor;
ScalarKeyFrameAnimation fadeOutAnimation = compositor.CreateScalarKeyFrameAnimation();
fadeOutAnimation.InsertExpressionKeyFrame(0.0f, "this.CurrentValue");
fadeOutAnimation.InsertKeyFrame(1.0f, 0.0f);
fadeOutAnimation.Duration = TimeSpan.FromMilliseconds(DefaultAnimationDurationInMs * AnimationSlowdownFactor);
CompositionScopedBatch batch = compositor.CreateScopedBatch(CompositionBatchTypes.Animation);
visual.StartAnimation(nameof(Visual.Opacity), fadeOutAnimation);
batch.End();
batch.Completed += (_, _) =>
{
visual.Opacity = 1.0f;
progress.Complete();
};
}
}
private static void StartMoveTransitions(IList<ItemCollectionTransition> transitions, bool hasRemoveAnimations)
{
foreach (ItemCollectionTransition transition in transitions)
{
ItemCollectionTransitionProgress progress = transition.Start();
Visual visual = ElementCompositionPreview.GetElementVisual(progress.Element);
Compositor compositor = visual.Compositor;
CompositionScopedBatch batch = compositor.CreateScopedBatch(CompositionBatchTypes.Animation);
// Animate offset.
if (transition.OldBounds.X != transition.NewBounds.X ||
transition.OldBounds.Y != transition.NewBounds.Y)
{
AnimateOffset(visual, compositor, transition.OldBounds, transition.NewBounds, hasRemoveAnimations);
}
batch.End();
batch.Completed += (_, _) => progress.Complete();
}
}
private static void AnimateOffset(Visual visual, Compositor compositor, Rect oldBounds, Rect newBounds, bool hasRemoveAnimations)
{
Vector2KeyFrameAnimation offsetAnimation = compositor.CreateVector2KeyFrameAnimation();
offsetAnimation.SetVector2Parameter("delta", new Vector2(
(float)(oldBounds.X - newBounds.X),
(float)(oldBounds.Y - newBounds.Y)));
offsetAnimation.SetVector2Parameter("final", default);
offsetAnimation.InsertExpressionKeyFrame(0.0f, "this.CurrentValue + delta");
if (hasRemoveAnimations)
{
offsetAnimation.InsertExpressionKeyFrame(0.5f, "delta");
}
offsetAnimation.InsertExpressionKeyFrame(1.0f, "final");
offsetAnimation.Duration = TimeSpan.FromMilliseconds(
DefaultAnimationDurationInMs * ((hasRemoveAnimations ? 1 : 0) + 1) * AnimationSlowdownFactor);
visual.StartAnimation("TransformMatrix._41_42", offsetAnimation);
}
}

View File

@@ -4,7 +4,6 @@
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using System.Collections.Specialized;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Windows.Foundation;
@@ -19,12 +18,14 @@ internal sealed partial class UniformStaggeredLayout : VirtualizingLayout
protected override void InitializeForContextCore(VirtualizingLayoutContext context)
{
context.LayoutState = new UniformStaggeredLayoutState(context);
base.InitializeForContextCore(context);
}
/// <inheritdoc/>
protected override void UninitializeForContextCore(VirtualizingLayoutContext context)
{
context.LayoutState = null;
base.UninitializeForContextCore(context);
}
/// <inheritdoc/>
@@ -81,10 +82,16 @@ internal sealed partial class UniformStaggeredLayout : VirtualizingLayout
(int numberOfColumns, double columnWidth) = GetNumberOfColumnsAndWidth(availableWidth, MinItemWidth, MinColumnSpacing);
if (columnWidth != state.ColumnWidth)
{
// The items will need to be remeasured
state.Clear();
}
state.ColumnWidth = columnWidth;
double totalWidth = ((state.ColumnWidth + MinColumnSpacing) * numberOfColumns) - MinColumnSpacing;
// adjust for column spacing on all columns expect the first
double totalWidth = state.ColumnWidth + ((numberOfColumns - 1) * (state.ColumnWidth + MinColumnSpacing));
if (totalWidth > availableWidth)
{
numberOfColumns--;
@@ -96,6 +103,7 @@ internal sealed partial class UniformStaggeredLayout : VirtualizingLayout
if (numberOfColumns != state.NumberOfColumns)
{
// The items will not need to be remeasured, but they will need to go into new columns
state.ClearColumns();
}
@@ -162,7 +170,7 @@ internal sealed partial class UniformStaggeredLayout : VirtualizingLayout
item.Element.Measure(new Size(state.ColumnWidth, availableHeight));
if (item.Height != item.Element.DesiredSize.Height)
{
// this item changed size; we need to recalculate layout for everything after this item
// this item changed size; we need to recalculate layout for everything after this
state.RemoveFromIndex(i + 1);
item.Height = item.Element.DesiredSize.Height;
columnHeights[columnIndex] = item.Top + item.Height;
@@ -193,16 +201,16 @@ internal sealed partial class UniformStaggeredLayout : VirtualizingLayout
// Cycle through each column and arrange the items that are within the realization bounds
for (int columnIndex = 0; columnIndex < state.NumberOfColumns; columnIndex++)
{
foreach (ref readonly UniformStaggeredItem item in CollectionsMarshal.AsSpan(state.GetColumnLayout(columnIndex)))
UniformStaggeredColumnLayout layout = state.GetColumnLayout(columnIndex);
foreach (ref readonly UniformStaggeredItem item in CollectionsMarshal.AsSpan(layout))
{
double bottom = item.Top + item.Height;
if (bottom < context.RealizationRect.Top)
{
// Element is above the realization bounds
// element is above the realization bounds
continue;
}
// Partial or fully in the view
if (item.Top <= context.RealizationRect.Bottom)
{
double itemHorizontalOffset = (state.ColumnWidth * columnIndex) + (MinColumnSpacing * columnIndex);
@@ -221,22 +229,21 @@ internal sealed partial class UniformStaggeredLayout : VirtualizingLayout
return finalSize;
}
private static (int NumberOfColumns, double ColumnWidth) GetNumberOfColumnsAndWidth(double availableWidth, double minItemWidth, double columnSpacing)
private static (int NumberOfColumns, double ColumnWidth) GetNumberOfColumnsAndWidth(double availableWidth, double minItemWidth, double minColumnSpacing)
{
// test if the width can fit in 2 items
if ((2 * minItemWidth) + columnSpacing > availableWidth)
if ((2 * minItemWidth) + minColumnSpacing > availableWidth)
{
return (1, availableWidth);
}
int columnCount = Math.Max(1, (int)((availableWidth + columnSpacing) / (minItemWidth + columnSpacing)));
double columnWidthWithSpacing = (availableWidth + columnSpacing) / columnCount;
return (columnCount, columnWidthWithSpacing - columnSpacing);
int columnCount = Math.Max(1, (int)((availableWidth + minColumnSpacing) / (minItemWidth + minColumnSpacing)));
double columnWidthAddSpacing = (availableWidth + minColumnSpacing) / columnCount;
return (columnCount, columnWidthAddSpacing - minColumnSpacing);
}
private static int GetLowestColumnIndex(in ReadOnlySpan<double> columnHeights)
{
// We want to find the leftest column with the lowest height
int columnIndex = 0;
double height = columnHeights[0];
for (int j = 1; j < columnHeights.Length; j++)
@@ -253,11 +260,13 @@ internal sealed partial class UniformStaggeredLayout : VirtualizingLayout
private static void OnMinItemWidthChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((UniformStaggeredLayout)d).InvalidateMeasure();
UniformStaggeredLayout panel = (UniformStaggeredLayout)d;
panel.InvalidateMeasure();
}
private static void OnSpacingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((UniformStaggeredLayout)d).InvalidateMeasure();
UniformStaggeredLayout panel = (UniformStaggeredLayout)d;
panel.InvalidateMeasure();
}
}

View File

@@ -1,6 +1,7 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using System.Runtime.InteropServices;
@@ -66,6 +67,46 @@ internal sealed class UniformStaggeredLayoutState
return columnLayout[columnIndex];
}
/// <summary>
/// Clear everything that has been calculated.
/// </summary>
internal void Clear()
{
// https://github.com/DGP-Studio/Snap.Hutao/issues/1079
// The first element must be force refreshed otherwise
// it will use the old one realized
// https://github.com/DGP-Studio/Snap.Hutao/issues/1099
// Now we need to refresh the first element of each column
// https://github.com/DGP-Studio/Snap.Hutao/issues/1099
// Finally we need to refresh the whole layout when we reset
if (context.ItemCount > 0)
{
for (int i = 0; i < context.ItemCount; i++)
{
RecycleElementAt(i);
}
}
columnLayout.Clear();
items.Clear();
}
/// <summary>
/// Clear the layout columns so they will be recalculated.
/// </summary>
internal void ClearColumns()
{
columnLayout.Clear();
}
/// <summary>
/// Gets the estimated height of the layout.
/// </summary>
/// <returns>The estimated height of the layout.</returns>
/// <remarks>
/// If all of the items have been calculated then the actual height will be returned.
/// If all of the items have not been calculated then an estimated height will be calculated based on the average height of the items.
/// </remarks>
internal double GetHeight()
{
double desiredHeight = columnLayout.Values.Max(c => c.Height);
@@ -98,37 +139,10 @@ internal sealed class UniformStaggeredLayoutState
return desiredHeight;
}
internal void Clear()
{
RecycleElements();
ClearColumns();
ClearItems();
}
internal void ClearColumns()
{
columnLayout.Clear();
}
internal void ClearItems()
{
items.Clear();
}
internal void RecycleElements()
{
if (context.ItemCount > 0)
{
for (int i = 0; i < items.Count; i++)
{
RecycleElementAt(i);
}
}
}
internal void RecycleElementAt(int index)
{
context.RecycleElement(context.GetOrCreateElementAt(index));
UIElement element = context.GetOrCreateElementAt(index);
context.RecycleElement(element);
}
internal void RemoveFromIndex(int index)
@@ -161,7 +175,7 @@ internal sealed class UniformStaggeredLayoutState
{
for (int i = startIndex; i <= endIndex; i++)
{
if (i >= items.Count)
if (i > items.Count)
{
break;
}
@@ -170,7 +184,7 @@ internal sealed class UniformStaggeredLayoutState
item.Height = 0;
item.Top = 0;
// We must recycle all removed elements to ensure that it gets the correct context
// We must recycle all elements to ensure that it gets the correct context
RecycleElementAt(i);
}

View File

@@ -1,89 +0,0 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Microsoft.UI.Xaml;
using System.Data;
using System.Runtime.InteropServices;
using Windows.Foundation;
namespace Snap.Hutao.Control.Panel;
[DependencyProperty("Spacing", typeof(double), default(double), nameof(OnSpacingChanged))]
internal partial class EqualPanel : Microsoft.UI.Xaml.Controls.Panel
{
private double maxItemWidth;
private double maxItemHeight;
private int visibleItemsCount;
public EqualPanel()
{
RegisterPropertyChangedCallback(HorizontalAlignmentProperty, OnHorizontalAlignmentChanged);
}
protected override Size MeasureOverride(Size availableSize)
{
maxItemWidth = 0;
maxItemHeight = 0;
List<UIElement> elements = [.. Children.Where(element => element.Visibility == Visibility.Visible)];
visibleItemsCount = elements.Count;
foreach (ref readonly UIElement child in CollectionsMarshal.AsSpan(elements))
{
child.Measure(availableSize);
maxItemWidth = Math.Max(maxItemWidth, child.DesiredSize.Width);
maxItemHeight = Math.Max(maxItemHeight, child.DesiredSize.Height);
}
if (visibleItemsCount > 0)
{
// Return equal widths based on the widest item
// In very specific edge cases the AvailableWidth might be infinite resulting in a crash.
if (HorizontalAlignment is not HorizontalAlignment.Stretch || double.IsInfinity(availableSize.Width))
{
return new Size((maxItemWidth * visibleItemsCount) + (Spacing * (visibleItemsCount - 1)), maxItemHeight);
}
else
{
// Equal columns based on the available width, adjust for spacing
double totalWidth = availableSize.Width - (Spacing * (visibleItemsCount - 1));
maxItemWidth = totalWidth / visibleItemsCount;
return new Size(availableSize.Width, maxItemHeight);
}
}
else
{
return new Size(0, 0);
}
}
protected override Size ArrangeOverride(Size finalSize)
{
double x = 0;
// Check if there's more (little) width available - if so, set max item width to the maximum possible as we have an almost perfect height.
if (finalSize.Width > (visibleItemsCount * maxItemWidth) + (Spacing * (visibleItemsCount - 1)))
{
maxItemWidth = (finalSize.Width - (Spacing * (visibleItemsCount - 1))) / visibleItemsCount;
}
IEnumerable<UIElement> elements = Children.Where(static e => e.Visibility == Visibility.Visible);
foreach (UIElement child in elements)
{
child.Arrange(new Rect(x, 0, maxItemWidth, maxItemHeight));
x += maxItemWidth + Spacing;
}
return finalSize;
}
private static void OnSpacingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
(d as EqualPanel)?.InvalidateMeasure();
}
private void OnHorizontalAlignmentChanged(DependencyObject sender, DependencyProperty dp)
{
InvalidateMeasure();
}
}

View File

@@ -1,58 +0,0 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Microsoft.UI.Xaml;
using Windows.Foundation;
namespace Snap.Hutao.Control.Panel;
[DependencyProperty("MinItemWidth", typeof(double))]
[DependencyProperty("Spacing", typeof(double))]
internal partial class HorizontalEqualPanel : Microsoft.UI.Xaml.Controls.Panel
{
public HorizontalEqualPanel()
{
Loaded += OnLoaded;
SizeChanged += OnSizeChanged;
}
protected override Size MeasureOverride(Size availableSize)
{
foreach (UIElement child in Children)
{
// ScrollViewer will always return an Infinity Size, we should use ActualWidth for this situation.
double availableWidth = double.IsInfinity(availableSize.Width) ? ActualWidth : availableSize.Width;
double childAvailableWidth = (availableWidth + Spacing) / Children.Count;
double childMaxAvailableWidth = Math.Max(MinItemWidth, childAvailableWidth);
child.Measure(new(childMaxAvailableWidth - Spacing, ActualHeight));
}
return base.MeasureOverride(availableSize);
}
protected override Size ArrangeOverride(Size finalSize)
{
int itemCount = Children.Count;
double availableWidthPerItem = (finalSize.Width - (Spacing * (itemCount - 1))) / itemCount;
double actualItemWidth = Math.Max(MinItemWidth, availableWidthPerItem);
double offset = 0;
foreach (UIElement child in Children)
{
child.Arrange(new Rect(offset, 0, actualItemWidth, finalSize.Height));
offset += actualItemWidth + Spacing;
}
return finalSize;
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
MinWidth = (MinItemWidth * Children.Count) + (Spacing * (Children.Count - 1));
}
private void OnSizeChanged(object sender, SizeChangedEventArgs e)
{
InvalidateMeasure();
}
}

View File

@@ -6,7 +6,6 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:shcm="using:Snap.Hutao.Control.Markup"
Style="{StaticResource DefaultSegmentedStyle}"
mc:Ignorable="d">
<cwc:SegmentedItem

View File

@@ -4,7 +4,6 @@
using CommunityToolkit.WinUI.Controls;
using Microsoft.UI.Xaml;
using Snap.Hutao.Core.Setting;
using System.Collections.Frozen;
namespace Snap.Hutao.Control.Panel;
@@ -20,11 +19,11 @@ internal sealed partial class PanelSelector : Segmented
public const string List = nameof(List);
public const string Grid = nameof(Grid);
private static readonly FrozenDictionary<int, string> IndexTypeMap = FrozenDictionary.ToFrozenDictionary(
[
KeyValuePair.Create(0, List),
KeyValuePair.Create(1, Grid),
]);
private static readonly Dictionary<int, string> IndexTypeMap = new()
{
[0] = List,
[1] = Grid,
};
private readonly RoutedEventHandler loadedEventHandler;
private readonly RoutedEventHandler unloadedEventHandler;

View File

@@ -72,11 +72,7 @@ internal class ScopedPage : Page
DisposeViewModel();
}
if (this.IsDisposed())
{
return;
}
DataContext = null;
Unloaded -= unloadEventHandler;
}

View File

@@ -5,12 +5,6 @@ namespace Snap.Hutao.Control.Text.Syntax.MiHoYo;
internal sealed class MiHoYoColorTextSyntax : MiHoYoXmlElementSyntax
{
public MiHoYoColorTextSyntax(MiHoYoColorKind colorKind, string text, int start, int end)
: base(MiHoYoSyntaxKind.ColorText, text, start, end)
{
ColorKind = colorKind;
}
public MiHoYoColorTextSyntax(MiHoYoColorKind colorKind, string text, in TextPosition position)
: base(MiHoYoSyntaxKind.ColorText, text, position)
{
@@ -45,5 +39,5 @@ internal sealed class MiHoYoColorTextSyntax : MiHoYoXmlElementSyntax
}
}
public ReadOnlySpan<char> ColorSpan { get => Text.AsSpan()[ColorPosition.Start..ColorPosition.End]; }
public ReadOnlySpan<char> ColorSpan { get => Text.AsSpan(ColorPosition); }
}

View File

@@ -0,0 +1,16 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
namespace Snap.Hutao.Control.Text.Syntax.MiHoYo;
internal sealed class MiHoYoInterpolatedContentTextSyntax : MiHoYoInterpolatedTextSyntax
{
public MiHoYoInterpolatedContentTextSyntax(MiHoYoInterpolationKind interpolationKind, string text, in TextPosition position)
: base(interpolationKind, text, position)
{
}
public TextPosition ContentPosition { get; }
public ReadOnlySpan<char> ContentSpan => Text.AsSpan(ContentPosition);
}

View File

@@ -0,0 +1,32 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
namespace Snap.Hutao.Control.Text.Syntax.MiHoYo;
internal class MiHoYoInterpolatedTextSyntax : MiHoYoSyntaxNode
{
public MiHoYoInterpolatedTextSyntax(MiHoYoInterpolationKind interpolationKind, string text, in TextPosition position)
: base(MiHoYoSyntaxKind.InterpolatedText, text, position)
{
InterpolationKind = interpolationKind;
}
public MiHoYoInterpolationKind InterpolationKind { get; }
}
internal enum MiHoYoInterpolationKind
{
None,
Female,
LayoutMobile,
LayoutPC,
LayoutPS,
Male,
MateAvatarSexProInfoMaleFemalePronounBoyGirlD,
MateAvatarSexProInfoMaleFemalePronounBoyGirlFirst,
Nickname,
NonBreakSpace,
PlayerAvatarSexProInfoMaleFemalePronounHeShe,
RealNameId1,
RealNameId1HostOnlyTrue,
}

View File

@@ -5,11 +5,6 @@ namespace Snap.Hutao.Control.Text.Syntax.MiHoYo;
internal sealed class MiHoYoItalicTextSyntax : MiHoYoXmlElementSyntax
{
public MiHoYoItalicTextSyntax(string text, int start, int end)
: base(MiHoYoSyntaxKind.ItalicText, text, start, end)
{
}
public MiHoYoItalicTextSyntax(string text, in TextPosition position)
: base(MiHoYoSyntaxKind.ItalicText, text, position)
{

View File

@@ -5,11 +5,6 @@ namespace Snap.Hutao.Control.Text.Syntax.MiHoYo;
internal sealed class MiHoYoPlainTextSyntax : MiHoYoSyntaxNode
{
public MiHoYoPlainTextSyntax(string text, int start, int end)
: base(MiHoYoSyntaxKind.PlainText, text, start, end)
{
}
public MiHoYoPlainTextSyntax(string text, in TextPosition position)
: base(MiHoYoSyntaxKind.PlainText, text, position)
{

View File

@@ -9,4 +9,8 @@ internal sealed class MiHoYoRootSyntax : MiHoYoSyntaxNode
: base(MiHoYoSyntaxKind.Root, text, start, end)
{
}
public bool IsInterpolated { get => Text.AsSpan()[0] is '#'; }
public TextPosition ContentPosition { get => IsInterpolated ? new(Position.Start + 1, Position.End) : Position; }
}

View File

@@ -8,6 +8,7 @@ internal enum MiHoYoSyntaxKind
None,
Root,
PlainText,
InterpolatedText,
ColorText,
ItalicText,
}

View File

@@ -7,8 +7,6 @@ internal sealed class MiHoYoSyntaxTree
{
public MiHoYoSyntaxNode Root { get; set; } = default!;
public string Text { get; set; } = default!;
public static MiHoYoSyntaxTree Parse(string text)
{
MiHoYoRootSyntax root = new(text, 0, text.Length);
@@ -16,7 +14,6 @@ internal sealed class MiHoYoSyntaxTree
MiHoYoSyntaxTree tree = new()
{
Text = text,
Root = root,
};
@@ -27,43 +24,50 @@ internal sealed class MiHoYoSyntaxTree
{
TextPosition contentPosition = syntax switch
{
MiHoYoRootSyntax rootSyntax => rootSyntax.ContentPosition,
MiHoYoXmlElementSyntax xmlSyntax => xmlSyntax.ContentPosition,
_ => syntax.Position,
};
ReadOnlySpan<char> contentSpan = text.AsSpan().Slice(contentPosition.Start, contentPosition.Length);
ReadOnlySpan<char> contentSpan = text.AsSpan(contentPosition);
int endOfProcessedAtContent = 0;
while (true)
{
if (endOfProcessedAtContent >= contentSpan.Length)
{
break;
return;
}
// <tag>some text</tag>
// ↑ ↑ ↑ ↑
// | | | right closing
// | | right opening
// | left closing
// left opening
int indexOfXmlLeftOpeningAtUnprocessedContent = contentSpan[endOfProcessedAtContent..].IndexOf('<');
// End of content
if (indexOfXmlLeftOpeningAtUnprocessedContent < 0)
{
TextPosition position = new(contentPosition.Start + endOfProcessedAtContent, contentPosition.End);
MiHoYoPlainTextSyntax plainText = new(text, position);
syntax.Children.Add(plainText);
break;
TextPosition positionAtText = new(contentPosition.Start + endOfProcessedAtContent, contentPosition.End);
ParseInterpolatedOrPlainText(text, syntax, positionAtText);
return;
}
// We have plain text between xml elements
// Text between xml elements
if (indexOfXmlLeftOpeningAtUnprocessedContent > 0)
{
TextPosition position = new(0, indexOfXmlLeftOpeningAtUnprocessedContent);
TextPosition positionAtContent = position.RightShift(endOfProcessedAtContent);
TextPosition positionAtText = positionAtContent.RightShift(contentPosition.Start);
MiHoYoPlainTextSyntax plainText = new(text, positionAtText);
syntax.Children.Add(plainText);
TextPosition positionAtContent = position >> endOfProcessedAtContent;
TextPosition positionAtText = positionAtContent >> contentPosition.Start;
ParseInterpolatedOrPlainText(text, syntax, positionAtText);
endOfProcessedAtContent = positionAtContent.End;
continue;
}
// Peek the next character after '<'
// Peek the next character after left opening
int indexOfXmlLeftOpeningAtContent = endOfProcessedAtContent + indexOfXmlLeftOpeningAtUnprocessedContent;
switch (contentSpan[indexOfXmlLeftOpeningAtContent + 1])
{
@@ -79,8 +83,8 @@ internal sealed class MiHoYoSyntaxTree
};
TextPosition position = new(0, endOfXmlColorRightClosingAtUnprocessedContent);
TextPosition positionAtContent = position.RightShift(endOfProcessedAtContent);
TextPosition positionAtText = positionAtContent.RightShift(contentPosition.Start);
TextPosition positionAtContent = position >> endOfProcessedAtContent;
TextPosition positionAtText = positionAtContent >> contentPosition.Start;
MiHoYoColorTextSyntax colorText = new(colorKind, text, positionAtText);
ParseComponents(text, colorText);
@@ -94,8 +98,8 @@ internal sealed class MiHoYoSyntaxTree
int endOfXmlItalicRightClosingAtUnprocessedContent = EndOfXmlClosing(contentSpan[indexOfXmlLeftOpeningAtContent..], out _);
TextPosition position = new(0, endOfXmlItalicRightClosingAtUnprocessedContent);
TextPosition positionAtContent = position.RightShift(endOfProcessedAtContent);
TextPosition positionAtText = positionAtContent.RightShift(contentPosition.Start);
TextPosition positionAtContent = position >> endOfProcessedAtContent;
TextPosition positionAtText = positionAtContent >> contentPosition.Start;
MiHoYoItalicTextSyntax italicText = new(text, positionAtText);
ParseComponents(text, italicText);
@@ -107,6 +111,174 @@ internal sealed class MiHoYoSyntaxTree
}
}
private static void ParseInterpolatedOrPlainText(string text, MiHoYoSyntaxNode syntax, in TextPosition spanPosition)
{
if (text.AsSpan()[0] is not '#')
{
MiHoYoPlainTextSyntax plainText = new(text, spanPosition);
syntax.Children.Add(plainText);
return;
}
ReadOnlySpan<char> span = text.AsSpan(spanPosition);
int endOfProcessedAtSpan = 0;
while (true)
{
if (endOfProcessedAtSpan >= span.Length)
{
return;
}
int indexOfOpeningAtUnprocessedSpan = span[endOfProcessedAtSpan..].IndexOf('{');
// End of content
if (indexOfOpeningAtUnprocessedSpan < 0)
{
TextPosition positionAtText = new(spanPosition.Start + endOfProcessedAtSpan, spanPosition.End);
MiHoYoPlainTextSyntax plainText = new(text, positionAtText);
syntax.Children.Add(plainText);
break;
}
// Text between interpolation
if (indexOfOpeningAtUnprocessedSpan > 0)
{
TextPosition position = new(0, indexOfOpeningAtUnprocessedSpan);
TextPosition positionAtSpan = position >> endOfProcessedAtSpan;
TextPosition positionAtText = positionAtSpan >> spanPosition.Start;
MiHoYoPlainTextSyntax plainText = new(text, positionAtText);
syntax.Children.Add(plainText);
endOfProcessedAtSpan = positionAtSpan.End;
continue;
}
// Peek the next character after left opening
int indexOfOpeningAtSpan = endOfProcessedAtSpan + indexOfOpeningAtUnprocessedSpan;
int endOfClosingAtUnprocessedSpan = span[indexOfOpeningAtSpan..].IndexOf('}') + 1;
TextPosition position2 = new(0, endOfClosingAtUnprocessedSpan);
TextPosition positionAtSpan2 = position2 >> endOfProcessedAtSpan;
TextPosition positionAtText2 = positionAtSpan2 >> spanPosition.Start;
switch (span[indexOfOpeningAtSpan + 1])
{
// {F#(.*?)}
case 'F':
{
MiHoYoInterpolatedContentTextSyntax contentTextSyntax = new(MiHoYoInterpolationKind.Female, text, positionAtText2);
syntax.Children.Add(contentTextSyntax);
endOfProcessedAtSpan += endOfClosingAtUnprocessedSpan;
break;
}
// {LAYOUT_MOBILE#(.+?)}
// {LAYOUT_PC#(.+?)}
// {LAYOUT_PS#(.+?)}
case 'L':
{
MiHoYoInterpolationKind kind = span[indexOfOpeningAtSpan + 9] switch
{
'O' => MiHoYoInterpolationKind.LayoutMobile,
'P' => MiHoYoInterpolationKind.LayoutPC,
'S' => MiHoYoInterpolationKind.LayoutPS,
_ => throw Must.NeverHappen($"Unexpected span content {span}"),
};
MiHoYoInterpolatedContentTextSyntax contentTextSyntax = new(kind, text, positionAtText2);
syntax.Children.Add(contentTextSyntax);
endOfProcessedAtSpan += endOfClosingAtUnprocessedSpan;
break;
}
// {MATEAVATAR#SEXPRO[INFO_MALE_PRONOUN_BOYD|INFO_FEMALE_PRONOUN_GIRLD]}
// {MATEAVATAR#SEXPRO[INFO_MALE_PRONOUN_BOYFIRST|INFO_FEMALE_PRONOUN_GIRLFIRST]}
// {M#(.*?)}
case 'M':
{
switch (span[indexOfOpeningAtSpan + 2])
{
case 'A':
{
MiHoYoInterpolationKind kind = endOfClosingAtUnprocessedSpan switch
{
69 => MiHoYoInterpolationKind.MateAvatarSexProInfoMaleFemalePronounBoyGirlD,
77 => MiHoYoInterpolationKind.MateAvatarSexProInfoMaleFemalePronounBoyGirlFirst,
_ => throw Must.NeverHappen($"Unexpected span content {span}"),
};
MiHoYoInterpolatedTextSyntax textSyntax = new(kind, text, positionAtText2);
syntax.Children.Add(textSyntax);
endOfProcessedAtSpan += endOfClosingAtUnprocessedSpan;
break;
}
case '#':
{
MiHoYoInterpolatedContentTextSyntax contentTextSyntax = new(MiHoYoInterpolationKind.Male, text, positionAtText2);
syntax.Children.Add(contentTextSyntax);
endOfProcessedAtSpan += endOfClosingAtUnprocessedSpan;
break;
}
default:
throw Must.NeverHappen($"Unexpected span content {span}");
}
break;
}
// {NICKNAME}
// {NON_BREAK_SPACE}
case 'N':
{
MiHoYoInterpolationKind kind = span[indexOfOpeningAtSpan + 2] switch
{
'I' => MiHoYoInterpolationKind.Nickname,
'O' => MiHoYoInterpolationKind.NonBreakSpace,
_ => throw Must.NeverHappen($"Unexpected span content {span}"),
};
MiHoYoInterpolatedTextSyntax textSyntax = new(kind, text, positionAtText2);
syntax.Children.Add(textSyntax);
endOfProcessedAtSpan += endOfClosingAtUnprocessedSpan;
break;
}
// {PLAYERAVATAR#SEXPRO[INFO_MALE_PRONOUN_HE|INFO_FEMALE_PRONOUN_SHE]}
case 'P':
{
MiHoYoInterpolatedContentTextSyntax contentTextSyntax = new(MiHoYoInterpolationKind.PlayerAvatarSexProInfoMaleFemalePronounHeShe, text, positionAtText2);
syntax.Children.Add(contentTextSyntax);
endOfProcessedAtSpan += endOfClosingAtUnprocessedSpan;
break;
}
// {REALNAME[ID(1)|HOSTONLY(true)]}
// {REALNAME[ID(1)]}
case 'R':
{
MiHoYoInterpolationKind kind = endOfClosingAtUnprocessedSpan switch
{
32 => MiHoYoInterpolationKind.RealNameId1HostOnlyTrue,
17 => MiHoYoInterpolationKind.RealNameId1,
_ => throw Must.NeverHappen($"Unexpected span content {span}"),
};
MiHoYoInterpolatedTextSyntax textSyntax = new(kind, text, positionAtText2);
syntax.Children.Add(textSyntax);
endOfProcessedAtSpan += endOfClosingAtUnprocessedSpan;
break;
}
default:
throw Must.NeverHappen($"Unexpected span content {span}");
}
}
}
private static int EndOfXmlClosing(in ReadOnlySpan<char> span, out int endOfLeftClosing)
{
endOfLeftClosing = 0;

View File

@@ -5,11 +5,6 @@ namespace Snap.Hutao.Control.Text.Syntax.MiHoYo;
internal abstract class MiHoYoXmlElementSyntax : MiHoYoSyntaxNode
{
public MiHoYoXmlElementSyntax(MiHoYoSyntaxKind kind, string text, int start, int end)
: base(kind, text, start, end)
{
}
public MiHoYoXmlElementSyntax(MiHoYoSyntaxKind kind, string text, in TextPosition position)
: base(kind, text, position)
{

View File

@@ -22,13 +22,13 @@ internal readonly struct TextPosition
get => End - Start;
}
public TextPosition LeftShift(int offset)
public static TextPosition operator <<(TextPosition position, int offset)
{
return new(Start - offset, End - offset);
return new(position.Start - offset, position.End - offset);
}
public TextPosition RightShift(int offset)
public static TextPosition operator >>(TextPosition position, int offset)
{
return new(Start + offset, End + offset);
return new(position.Start + offset, position.End + offset);
}
}

View File

@@ -0,0 +1,12 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
namespace Snap.Hutao.Control.Text.Syntax;
internal static class TextPositionExtension
{
public static ReadOnlySpan<char> AsSpan(this string? text, TextPosition position)
{
return text.AsSpan(position.Start, position.Length);
}
}

View File

@@ -4,19 +4,21 @@
xmlns:cwm="using:CommunityToolkit.WinUI.Media">
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Light">
<x:Double x:Key="CompatShadowThemeOpacity">0.14</x:Double>
<cwm:AttachedCardShadow
x:Key="CompatCardShadow"
BlurRadius="8"
Opacity="0.14"
Offset="0,4,0"/>
</ResourceDictionary>
<ResourceDictionary x:Key="Dark">
<x:Double x:Key="CompatShadowThemeOpacity">0.28</x:Double>
<cwm:AttachedCardShadow
x:Key="CompatCardShadow"
BlurRadius="8"
Opacity="0.28"
Offset="0,4,0"/>
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
<cwm:AttachedCardShadow
x:Key="CompatCardShadow"
BlurRadius="8"
Opacity="{ThemeResource CompatShadowThemeOpacity}"
Offset="0,4,0"/>
<Style x:Key="BorderCardStyle" TargetType="Border">
<Setter Property="Background" Value="{ThemeResource CardBackgroundFillColorDefaultBrush}"/>
<Setter Property="BorderBrush" Value="{ThemeResource CardStrokeColorDefaultBrush}"/>

View File

@@ -8,9 +8,6 @@
<ItemsPanelTemplate x:Key="WrapPanelSpacing0Template">
<cwcont:WrapPanel/>
</ItemsPanelTemplate>
<ItemsPanelTemplate x:Key="WrapPanelSpacing2Template">
<cwcont:WrapPanel HorizontalSpacing="2" VerticalSpacing="2"/>
</ItemsPanelTemplate>
<ItemsPanelTemplate x:Key="WrapPanelSpacing4Template">
<cwcont:WrapPanel HorizontalSpacing="4" VerticalSpacing="4"/>
</ItemsPanelTemplate>
@@ -20,9 +17,6 @@
<ItemsPanelTemplate x:Key="HorizontalStackPanelSpacing2Template">
<StackPanel Orientation="Horizontal" Spacing="2"/>
</ItemsPanelTemplate>
<ItemsPanelTemplate x:Key="HorizontalStackPanelSpacing4Template">
<StackPanel Orientation="Horizontal" Spacing="4"/>
</ItemsPanelTemplate>
<ItemsPanelTemplate x:Key="StackPanelSpacing4Template">
<StackPanel Spacing="4"/>
</ItemsPanelTemplate>

View File

@@ -11,6 +11,4 @@ internal static class KnownColors
public static readonly Color Orange = StructMarshal.Color(0xFFBC6932);
public static readonly Color Purple = StructMarshal.Color(0xFFA156E0);
public static readonly Color Blue = StructMarshal.Color(0xFF5180CB);
public static readonly Color Green = StructMarshal.Color(0xFF2A8F72);
public static readonly Color White = StructMarshal.Color(0xFF72778B);
}

View File

@@ -1,115 +0,0 @@
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:cw="using:CommunityToolkit.WinUI"
xmlns:cwc="using:CommunityToolkit.WinUI.Controls"
xmlns:shcp="using:Snap.Hutao.Control.Panel"
xmlns:win="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="ms-appx:///CommunityToolkit.WinUI.Controls.Segmented/SegmentedItem/SegmentedItem.xaml"/>
</ResourceDictionary.MergedDictionaries>
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Default">
<StaticResource x:Key="SegmentedBackground" ResourceKey="ControlAltFillColorSecondaryBrush"/>
<StaticResource x:Key="SegmentedBorderBrush" ResourceKey="ControlStrokeColorDefaultBrush"/>
<Thickness x:Key="SegmentedBorderThickness">1</Thickness>
</ResourceDictionary>
<ResourceDictionary x:Key="Light">
<StaticResource x:Key="SegmentedBackground" ResourceKey="ControlAltFillColorSecondaryBrush"/>
<StaticResource x:Key="SegmentedBorderBrush" ResourceKey="ControlStrokeColorDefaultBrush"/>
<Thickness x:Key="SegmentedBorderThickness">1</Thickness>
</ResourceDictionary>
<ResourceDictionary x:Key="HighContrast">
<StaticResource x:Key="SegmentedBackground" ResourceKey="SystemColorButtonFaceColor"/>
<StaticResource x:Key="SegmentedBorderBrush" ResourceKey="SystemColorHighlightColorBrush"/>
<Thickness x:Key="SegmentedBorderThickness">1</Thickness>
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
<x:Double x:Key="SegmentedItemSpacing">1</x:Double>
<x:Double x:Key="ButtonItemSpacing">2</x:Double>
<Style BasedOn="{StaticResource DefaultSegmentedStyle}" TargetType="cwc:Segmented"/>
<Style x:Key="DefaultSegmentedStyle" TargetType="cwc:Segmented">
<Style.Setters>
<Setter Property="CornerRadius" Value="{ThemeResource ControlCornerRadius}"/>
<Setter Property="Background" Value="{ThemeResource SegmentedBackground}"/>
<Setter Property="BorderBrush" Value="{ThemeResource SegmentedBorderBrush}"/>
<Setter Property="BorderThickness" Value="{ThemeResource SegmentedBorderThickness}"/>
<Setter Property="HorizontalAlignment" Value="Left"/>
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="SelectionMode" Value="Single"/>
<Setter Property="IsItemClickEnabled" Value="False"/>
<win:Setter Property="SingleSelectionFollowsFocus" Value="False"/>
<Setter Property="IsTabStop" Value="False"/>
<Setter Property="TabNavigation" Value="Once"/>
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<shcp:EqualPanel
HorizontalAlignment="{Binding (cw:FrameworkElementExtensions.Ancestor).HorizontalAlignment, RelativeSource={RelativeSource Self}}"
cw:FrameworkElementExtensions.AncestorType="cwc:Segmented"
Spacing="{ThemeResource SegmentedItemSpacing}"/>
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="cwc:Segmented">
<Grid>
<Border
VerticalAlignment="Stretch"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="{TemplateBinding CornerRadius}"/>
<ItemsPresenter Margin="{TemplateBinding Padding}"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style.Setters>
</Style>
<Style
x:Key="PivotSegmentedStyle"
BasedOn="{StaticResource DefaultSegmentedStyle}"
TargetType="cwc:Segmented">
<Style.Setters>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderBrush" Value="Transparent"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Padding" Value="0"/>
<Setter Property="ItemContainerStyle" Value="{StaticResource PivotSegmentedItemStyle}"/>
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" Spacing="{ThemeResource SegmentedItemSpacing}"/>
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
</Style.Setters>
</Style>
<Style
x:Key="ButtonSegmentedStyle"
BasedOn="{StaticResource DefaultSegmentedStyle}"
TargetType="cwc:Segmented">
<Style.Setters>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderBrush" Value="Transparent"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Padding" Value="0"/>
<Setter Property="ItemContainerStyle" Value="{StaticResource ButtonSegmentedItemStyle}"/>
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" Spacing="{ThemeResource ButtonItemSpacing}"/>
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
</Style.Setters>
</Style>
</ResourceDictionary>

View File

@@ -1,25 +0,0 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Win32;
using Windows.UI;
namespace Snap.Hutao.Control.Theme;
internal static class SystemColors
{
public static Color BaseLowColor(bool isDarkMode)
{
return isDarkMode ? StructMarshal.Color(0x33FFFFFF) : StructMarshal.Color(0x33000000);
}
public static Color BaseMediumLowColor(bool isDarkMode)
{
return isDarkMode ? StructMarshal.Color(0x66FFFFFF) : StructMarshal.Color(0x66000000);
}
public static Color BaseHighColor(bool isDarkMode)
{
return isDarkMode ? StructMarshal.Color(0xFFFFFFFF) : StructMarshal.Color(0xFF000000);
}
}

View File

@@ -26,12 +26,12 @@ internal sealed partial class ImageCache : IImageCache, IImageCacheFilePathOpera
{
private const string CacheFolderName = nameof(ImageCache);
private readonly FrozenDictionary<int, TimeSpan> retryCountToDelay = FrozenDictionary.ToFrozenDictionary(
[
KeyValuePair.Create(0, TimeSpan.FromSeconds(4)),
KeyValuePair.Create(1, TimeSpan.FromSeconds(16)),
KeyValuePair.Create(2, TimeSpan.FromSeconds(64)),
]);
private readonly FrozenDictionary<int, TimeSpan> retryCountToDelay = new Dictionary<int, TimeSpan>()
{
[0] = TimeSpan.FromSeconds(4),
[1] = TimeSpan.FromSeconds(16),
[2] = TimeSpan.FromSeconds(64),
}.ToFrozenDictionary();
private readonly ConcurrentDictionary<string, Task> concurrentTasks = new();

View File

@@ -25,7 +25,6 @@ internal static partial class IocHttpClientConfiguration
.ConfigurePrimaryHttpMessageHandler((handler, provider) =>
{
HttpClientHandler clientHandler = (HttpClientHandler)handler;
clientHandler.AllowAutoRedirect = true;
clientHandler.UseProxy = true;
clientHandler.Proxy = provider.GetRequiredService<DynamicHttpProxy>();
});

View File

@@ -23,16 +23,8 @@ internal sealed partial class ExceptionRecorder
public void Record(Application app)
{
app.UnhandledException += OnAppUnhandledException;
app.DebugSettings.FailFastOnErrors = false;
app.DebugSettings.IsBindingTracingEnabled = true;
app.DebugSettings.BindingFailed += OnXamlBindingFailed;
app.DebugSettings.IsXamlResourceReferenceTracingEnabled = true;
app.DebugSettings.XamlResourceReferenceFailed += OnXamlResourceReferenceFailed;
app.DebugSettings.LayoutCycleTracingLevel = LayoutCycleTracingLevel.High;
app.DebugSettings.LayoutCycleDebugBreakLevel = LayoutCycleDebugBreakLevel.High;
}
[SuppressMessage("", "CA2012")]

View File

@@ -32,23 +32,9 @@ internal sealed class HutaoException : Exception
}
}
public static void ThrowIfNot(bool condition, HutaoExceptionKind kind, string message, Exception? innerException = default)
{
if (!condition)
{
throw new HutaoException(kind, message, innerException);
}
}
public static HutaoException ServiceTypeCastFailed<TFrom, TTo>(string name, Exception? innerException = default)
{
string message = $"This instance of '{typeof(TFrom).FullName}' '{name}' doesn't implement '{typeof(TTo).FullName}'";
throw new HutaoException(HutaoExceptionKind.ServiceTypeCastFailed, message, innerException);
}
public static HutaoException GachaStatisticsInvalidItemId(uint id, Exception? innerException = default)
{
string message = SH.FormatServiceGachaStatisticsFactoryItemIdInvalid(id);
throw new HutaoException(HutaoExceptionKind.GachaStatisticsInvalidItemId, message, innerException);
}
}

View File

@@ -6,16 +6,7 @@ namespace Snap.Hutao.Core.ExceptionService;
internal enum HutaoExceptionKind
{
None,
// Foundation
ServiceTypeCastFailed,
ImageCacheInvalidUri,
// IO
FileSystemCreateFileInsufficientPermissions,
PrivateNamedPipeContentHashIncorrect,
// Service
GachaStatisticsInvalidItemId,
GameFpsUnlockingFailed,
}

View File

@@ -1,7 +1,6 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections.Frozen;
using System.Text;
namespace Snap.Hutao.Core;
@@ -15,25 +14,25 @@ internal static class TypeNameHelper
{
private const char DefaultNestedTypeDelimiter = '+';
private static readonly FrozenDictionary<Type, string> BuiltInTypeNames = FrozenDictionary.ToFrozenDictionary(
[
KeyValuePair.Create(typeof(void), "void"),
KeyValuePair.Create(typeof(bool), "bool"),
KeyValuePair.Create(typeof(byte), "byte"),
KeyValuePair.Create(typeof(char), "char"),
KeyValuePair.Create(typeof(decimal), "decimal"),
KeyValuePair.Create(typeof(double), "double"),
KeyValuePair.Create(typeof(float), "float"),
KeyValuePair.Create(typeof(int), "int"),
KeyValuePair.Create(typeof(long), "long"),
KeyValuePair.Create(typeof(object), "object"),
KeyValuePair.Create(typeof(sbyte), "sbyte"),
KeyValuePair.Create(typeof(short), "short"),
KeyValuePair.Create(typeof(string), "string"),
KeyValuePair.Create(typeof(uint), "uint"),
KeyValuePair.Create(typeof(ulong), "ulong"),
KeyValuePair.Create(typeof(ushort), "ushort"),
]);
private static readonly Dictionary<Type, string> BuiltInTypeNames = new()
{
{ typeof(void), "void" },
{ typeof(bool), "bool" },
{ typeof(byte), "byte" },
{ typeof(char), "char" },
{ typeof(decimal), "decimal" },
{ typeof(double), "double" },
{ typeof(float), "float" },
{ typeof(int), "int" },
{ typeof(long), "long" },
{ typeof(object), "object" },
{ typeof(sbyte), "sbyte" },
{ typeof(short), "short" },
{ typeof(string), "string" },
{ typeof(uint), "uint" },
{ typeof(ulong), "ulong" },
{ typeof(ushort), "ushort" },
};
/// <summary>
/// 获取对象类型的显示名称

View File

@@ -9,7 +9,6 @@ namespace Snap.Hutao.Core.Validation;
/// 封装验证方法,简化微软验证
/// </summary>
[HighQuality]
[Obsolete("Use HutaoException instead")]
internal static class Must
{
/// <summary>

View File

@@ -13,7 +13,6 @@ using Snap.Hutao.Win32;
using Snap.Hutao.Win32.Foundation;
using Snap.Hutao.Win32.Graphics.Dwm;
using Snap.Hutao.Win32.UI.WindowsAndMessaging;
using System.Collections.Frozen;
using System.IO;
using Windows.Graphics;
using Windows.UI;
@@ -57,22 +56,25 @@ internal sealed class WindowController
private void InitializeCore()
{
RuntimeOptions runtimeOptions = serviceProvider.GetRequiredService<RuntimeOptions>();
AppOptions appOptions = serviceProvider.GetRequiredService<AppOptions>();
window.AppWindow.Title = SH.FormatAppNameAndVersion(runtimeOptions.Version);
window.AppWindow.SetIcon(Path.Combine(runtimeOptions.InstalledLocation, "Assets/Logo.ico"));
ExtendsContentIntoTitleBar();
RecoverOrInitWindowSize();
UpdateElementTheme(appOptions.ElementTheme);
UpdateImmersiveDarkMode(options.TitleBar, default!);
// appWindow.Show(true);
// appWindow.Show can't bring window to top.
window.Activate();
options.BringToForeground();
UpdateSystemBackdrop(appOptions.BackdropType);
appOptions.PropertyChanged += OnOptionsPropertyChanged;
if (options.UseSystemBackdrop)
{
AppOptions appOptions = serviceProvider.GetRequiredService<AppOptions>();
UpdateSystemBackdrop(appOptions.BackdropType);
appOptions.PropertyChanged += OnOptionsPropertyChanged;
}
subclass.Initialize();
@@ -120,17 +122,13 @@ internal sealed class WindowController
private void OnOptionsPropertyChanged(object? sender, PropertyChangedEventArgs e)
{
if (sender is not AppOptions options)
if (e.PropertyName is nameof(AppOptions.BackdropType))
{
return;
if (sender is AppOptions options)
{
UpdateSystemBackdrop(options.BackdropType);
}
}
_ = e.PropertyName switch
{
nameof(AppOptions.BackdropType) => UpdateSystemBackdrop(options.BackdropType),
nameof(AppOptions.ElementTheme) => UpdateElementTheme(options.ElementTheme),
_ => false,
};
}
private void OnWindowClosed(object sender, WindowEventArgs args)
@@ -160,7 +158,7 @@ internal sealed class WindowController
}
}
private bool UpdateSystemBackdrop(BackdropType backdropType)
private void UpdateSystemBackdrop(BackdropType backdropType)
{
window.SystemBackdrop = backdropType switch
{
@@ -170,15 +168,6 @@ internal sealed class WindowController
BackdropType.Acrylic => new DesktopAcrylicBackdrop(),
_ => null,
};
return true;
}
private bool UpdateElementTheme(ElementTheme theme)
{
((FrameworkElement)window.Content).RequestedTheme = theme;
return true;
}
private void UpdateTitleButtonColor()
@@ -188,12 +177,12 @@ internal sealed class WindowController
appTitleBar.ButtonBackgroundColor = Colors.Transparent;
appTitleBar.ButtonInactiveBackgroundColor = Colors.Transparent;
bool isDarkMode = Control.Theme.ThemeHelper.IsDarkMode(options.TitleBar.ActualTheme);
IAppResourceProvider resourceProvider = serviceProvider.GetRequiredService<IAppResourceProvider>();
Color systemBaseLowColor = Control.Theme.SystemColors.BaseLowColor(isDarkMode);
Color systemBaseLowColor = resourceProvider.GetResource<Color>("SystemBaseLowColor");
appTitleBar.ButtonHoverBackgroundColor = systemBaseLowColor;
Color systemBaseMediumLowColor = Control.Theme.SystemColors.BaseMediumLowColor(isDarkMode);
Color systemBaseMediumLowColor = resourceProvider.GetResource<Color>("SystemBaseMediumLowColor");
appTitleBar.ButtonPressedBackgroundColor = systemBaseMediumLowColor;
// The Foreground doesn't accept Alpha channel. So we translate it to gray.
@@ -201,7 +190,7 @@ internal sealed class WindowController
byte result = (byte)((systemBaseMediumLowColor.A / 255.0) * light);
appTitleBar.ButtonInactiveForegroundColor = Color.FromArgb(0xFF, result, result, result);
Color systemBaseHighColor = Control.Theme.SystemColors.BaseHighColor(isDarkMode);
Color systemBaseHighColor = resourceProvider.GetResource<Color>("SystemBaseHighColor");
appTitleBar.ButtonForegroundColor = systemBaseHighColor;
appTitleBar.ButtonHoverForegroundColor = systemBaseHighColor;
appTitleBar.ButtonPressedForegroundColor = systemBaseHighColor;

View File

@@ -41,18 +41,21 @@ internal readonly struct WindowOptions
/// </summary>
public readonly bool PersistSize;
public readonly bool UseSystemBackdrop;
/// <summary>
/// 是否使用 Win UI 3 自带的拓展标题栏实现
/// </summary>
public readonly bool UseLegacyDragBarImplementation = !AppWindowTitleBar.IsCustomizationSupported();
public WindowOptions(Window window, FrameworkElement titleBar, SizeInt32 initSize, bool persistSize = false)
public WindowOptions(Window window, FrameworkElement titleBar, SizeInt32 initSize, bool persistSize = false, bool useSystemBackdrop = true)
{
Hwnd = WindowNative.GetWindowHandle(window);
InputNonClientPointerSource = InputNonClientPointerSource.GetForWindowId(window.AppWindow.Id);
TitleBar = titleBar;
InitSize = initSize;
PersistSize = persistSize;
UseSystemBackdrop = useSystemBackdrop;
}
/// <summary>

View File

@@ -15,12 +15,6 @@ namespace Snap.Hutao.Extension;
[HighQuality]
internal static partial class EnumerableExtension
{
public static void Deconstruct<TKey, TElement>(this IGrouping<TKey, TElement> grouping, out TKey key, out IEnumerable<TElement> elements)
{
key = grouping.Key;
elements = grouping;
}
public static TElement? ElementAtOrLastOrDefault<TElement>(this IEnumerable<TElement> source, int index)
{
return source.ElementAtOrDefault(index) ?? source.LastOrDefault();

View File

@@ -41,30 +41,6 @@
"Equatable": true,
"EqualityOperators": true
},
{
"Name": "FurnitureId",
"Documentation": "家具 Id",
"Equatable": true,
"EqualityOperators": true
},
{
"Name": "FurnitureMakeId",
"Documentation": "家具配方 Id",
"Equatable": true,
"EqualityOperators": true
},
{
"Name": "FurnitureSuiteId",
"Documentation": "家具套装 Id",
"Equatable": true,
"EqualityOperators": true
},
{
"Name": "FurnitureTypeId",
"Documentation": "家具分类 Id",
"Equatable": true,
"EqualityOperators": true
},
{
"Name": "Level",
"Documentation": "等级 1 - 90",

View File

@@ -42,14 +42,14 @@ internal sealed partial class GachaItem
/// <summary>
/// 祈愿记录分类
/// </summary>
public GachaType GachaType { get; set; }
public GachaConfigType GachaType { get; set; }
/// <summary>
/// 祈愿记录查询分类
/// 合并保底的卡池使用此属性
/// 仅4种不含400
/// </summary>
public GachaType QueryType { get; set; }
public GachaConfigType QueryType { get; set; }
/// <summary>
/// 物品Id

View File

@@ -13,7 +13,6 @@ internal sealed partial class SettingEntry
public const string Culture = "Culture";
public const string SystemBackdropType = "SystemBackdropType";
public const string ElementTheme = "ElementTheme";
public const string BackgroundImageType = "BackgroundImageType";
public const string AnnouncementRegion = "AnnouncementRegion";

View File

@@ -17,7 +17,7 @@ internal sealed class UIGF : IJsonOnSerializing, IJsonOnDeserialized
/// <summary>
/// 当前版本
/// </summary>
public const string CurrentVersion = "v3.0";
public const string CurrentVersion = "v2.4";
/// <summary>
/// 信息
@@ -61,7 +61,6 @@ internal sealed class UIGF : IJsonOnSerializing, IJsonOnDeserialized
"v2.2" => UIGFVersion.Major2Minor2OrLower,
"v2.3" => UIGFVersion.Major2Minor3OrHigher,
"v2.4" => UIGFVersion.Major2Minor3OrHigher,
"v3.0" => UIGFVersion.Major2Minor3OrHigher,
_ => UIGFVersion.NotSupported,
};

View File

@@ -20,7 +20,7 @@ internal sealed class UIGFItem : GachaLogItem, IMappingFrom<UIGFItem, GachaItem,
/// </summary>
[JsonPropertyName("uigf_gacha_type")]
[JsonEnum(JsonSerializeType.NumberString)]
public GachaType UIGFGachaType { get; set; } = default!;
public GachaConfigType UIGFGachaType { get; set; } = default!;
public static UIGFItem From(GachaItem item, INameQuality nameQuality)
{

View File

@@ -1,25 +0,0 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
namespace Snap.Hutao.Model.Intrinsic;
internal enum FurnitureDeploySurfaceType
{
Ground = 0,
Wall = 1,
Ceil = 2,
StackObjPlane = 3,
Door = 4,
Chandelier = 5,
Floor = 6,
WallBody = 7,
Carpet = 8,
LegoRockery = 9,
Stair = 10,
NPC = 11,
Animal = 12,
Apartment = 13,
FurnitureSuite = 14,
Road = 15,
Terrain = 16,
}

View File

@@ -1,13 +0,0 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
namespace Snap.Hutao.Model.Intrinsic;
internal enum FurnitureDeployType
{
Interior,
Exterior,
InteriorRoom,
InteriorHall,
Skybox,
}

View File

@@ -1,14 +0,0 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
namespace Snap.Hutao.Model.Intrinsic;
internal enum GroupRecordType
{
None,
Racing,
Balloon,
Stake,
Seek,
Explosion,
}

View File

@@ -1,26 +0,0 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
namespace Snap.Hutao.Model.Intrinsic;
internal enum SpecialFurnitureType
{
NormalFurniture,
BlockDependent,
FarmField,
TeleportPoint,
Fishpond,
Npc,
Apartment,
FurnitureSuite,
Paimon,
Fish,
CustomBaseFurniture,
CustomNodeFurniture,
VirtualFurniture,
GroupFurniture,
CoopPictureFrame,
ChangeBgmFurniture,
ServerGadget,
Fishtank,
}

View File

@@ -1,9 +1,22 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Model.Primitive;
namespace Snap.Hutao.Model.Metadata.Achievement;
/// <summary>
/// 奖励
/// </summary>
internal sealed class Reward : IdCount;
internal sealed class Reward
{
/// <summary>
/// Id
/// </summary>
public MaterialId Id { get; set; }
/// <summary>
/// 数量
/// </summary>
public uint Count { get; set; }
}

View File

@@ -1,55 +0,0 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Control;
using Snap.Hutao.Model.Intrinsic;
using System.Collections.Frozen;
namespace Snap.Hutao.Model.Metadata.Converter;
internal sealed class AssociationTypeIconConverter : ValueConverter<AssociationType, Uri?>
{
private static readonly FrozenDictionary<string, AssociationType> LocalizedNameToAssociationType = FrozenDictionary.ToFrozenDictionary(
[
KeyValuePair.Create(SH.ModelIntrinsicAssociationTypeMondstadt, AssociationType.ASSOC_TYPE_MONDSTADT),
KeyValuePair.Create(SH.ModelIntrinsicAssociationTypeLiyue, AssociationType.ASSOC_TYPE_LIYUE),
KeyValuePair.Create(SH.ModelIntrinsicAssociationTypeFatui, AssociationType.ASSOC_TYPE_FATUI),
KeyValuePair.Create(SH.ModelIntrinsicAssociationTypeInazuma, AssociationType.ASSOC_TYPE_INAZUMA),
KeyValuePair.Create(SH.ModelIntrinsicAssociationTypeRanger, AssociationType.ASSOC_TYPE_RANGER),
KeyValuePair.Create(SH.ModelIntrinsicAssociationTypeSumeru, AssociationType.ASSOC_TYPE_SUMERU),
KeyValuePair.Create(SH.ModelIntrinsicAssociationTypeFontaine, AssociationType.ASSOC_TYPE_FONTAINE),
KeyValuePair.Create(SH.ModelIntrinsicAssociationTypeNatlan, AssociationType.ASSOC_TYPE_NATLAN),
KeyValuePair.Create(SH.ModelIntrinsicAssociationTypeSnezhnaya, AssociationType.ASSOC_TYPE_SNEZHNAYA),
]);
public static Uri? AssociationTypeNameToIconUri(string associationTypeName)
{
return AssociationTypeToIconUri(LocalizedNameToAssociationType.GetValueOrDefault(associationTypeName));
}
public static Uri? AssociationTypeToIconUri(AssociationType type)
{
string? association = type switch
{
AssociationType.ASSOC_TYPE_MONDSTADT => "Mengde",
AssociationType.ASSOC_TYPE_LIYUE => "Liyue",
AssociationType.ASSOC_TYPE_FATUI => default,
AssociationType.ASSOC_TYPE_INAZUMA => "Inazuma",
AssociationType.ASSOC_TYPE_RANGER => default,
AssociationType.ASSOC_TYPE_SUMERU => "Sumeru",
AssociationType.ASSOC_TYPE_FONTAINE => "Fontaine",
AssociationType.ASSOC_TYPE_NATLAN => default,
AssociationType.ASSOC_TYPE_SNEZHNAYA => default,
_ => throw Must.NeverHappen(),
};
return association is null
? default
: Web.HutaoEndpoints.StaticRaw("ChapterIcon", $"UI_ChapterIcon_{association}.png").ToUri();
}
public override Uri? Convert(AssociationType from)
{
return AssociationTypeToIconUri(from);
}
}

View File

@@ -3,7 +3,6 @@
using Snap.Hutao.Control;
using Snap.Hutao.Model.Intrinsic;
using System.Collections.Frozen;
namespace Snap.Hutao.Model.Metadata.Converter;
@@ -13,27 +12,27 @@ namespace Snap.Hutao.Model.Metadata.Converter;
[HighQuality]
internal sealed class ElementNameIconConverter : ValueConverter<string, Uri>
{
private static readonly FrozenDictionary<string, string> LocalizedNameToElementIconName = FrozenDictionary.ToFrozenDictionary(
[
KeyValuePair.Create(SH.ModelIntrinsicElementNameElec, "Electric"),
KeyValuePair.Create(SH.ModelIntrinsicElementNameFire, "Fire"),
KeyValuePair.Create(SH.ModelIntrinsicElementNameGrass, "Grass"),
KeyValuePair.Create(SH.ModelIntrinsicElementNameIce, "Ice"),
KeyValuePair.Create(SH.ModelIntrinsicElementNameRock, "Rock"),
KeyValuePair.Create(SH.ModelIntrinsicElementNameWater, "Water"),
KeyValuePair.Create(SH.ModelIntrinsicElementNameWind, "Wind"),
]);
private static readonly Dictionary<string, string> LocalizedNameToElementIconName = new()
{
[SH.ModelIntrinsicElementNameElec] = "Electric",
[SH.ModelIntrinsicElementNameFire] = "Fire",
[SH.ModelIntrinsicElementNameGrass] = "Grass",
[SH.ModelIntrinsicElementNameIce] = "Ice",
[SH.ModelIntrinsicElementNameRock] = "Rock",
[SH.ModelIntrinsicElementNameWater] = "Water",
[SH.ModelIntrinsicElementNameWind] = "Wind",
};
private static readonly FrozenDictionary<string, ElementType> LocalizedNameToElementType = FrozenDictionary.ToFrozenDictionary(
[
KeyValuePair.Create(SH.ModelIntrinsicElementNameElec, ElementType.Electric),
KeyValuePair.Create(SH.ModelIntrinsicElementNameFire, ElementType.Fire),
KeyValuePair.Create(SH.ModelIntrinsicElementNameGrass, ElementType.Grass),
KeyValuePair.Create(SH.ModelIntrinsicElementNameIce, ElementType.Ice),
KeyValuePair.Create(SH.ModelIntrinsicElementNameRock, ElementType.Rock),
KeyValuePair.Create(SH.ModelIntrinsicElementNameWater, ElementType.Water),
KeyValuePair.Create(SH.ModelIntrinsicElementNameWind, ElementType.Wind),
]);
private static readonly Dictionary<string, ElementType> LocalizedNameToElementType = new()
{
[SH.ModelIntrinsicElementNameElec] = ElementType.Electric,
[SH.ModelIntrinsicElementNameFire] = ElementType.Fire,
[SH.ModelIntrinsicElementNameGrass] = ElementType.Grass,
[SH.ModelIntrinsicElementNameIce] = ElementType.Ice,
[SH.ModelIntrinsicElementNameRock] = ElementType.Rock,
[SH.ModelIntrinsicElementNameWater] = ElementType.Water,
[SH.ModelIntrinsicElementNameWind] = ElementType.Wind,
};
/// <summary>
/// 将中文元素名称转换为图标链接

View File

@@ -3,9 +3,8 @@
using Microsoft.UI;
using Snap.Hutao.Control;
using Snap.Hutao.Control.Theme;
using Snap.Hutao.Model.Intrinsic;
using System.Collections.Frozen;
using Snap.Hutao.Win32;
using Windows.UI;
namespace Snap.Hutao.Model.Metadata.Converter;
@@ -16,39 +15,17 @@ namespace Snap.Hutao.Model.Metadata.Converter;
[HighQuality]
internal sealed class QualityColorConverter : ValueConverter<QualityType, Color>
{
private static readonly FrozenDictionary<string, QualityType> LocalizedNameToQualityType = FrozenDictionary.ToFrozenDictionary(
[
KeyValuePair.Create(SH.ModelIntrinsicItemQualityWhite, QualityType.QUALITY_WHITE),
KeyValuePair.Create(SH.ModelIntrinsicItemQualityGreen, QualityType.QUALITY_GREEN),
KeyValuePair.Create(SH.ModelIntrinsicItemQualityBlue, QualityType.QUALITY_BLUE),
KeyValuePair.Create(SH.ModelIntrinsicItemQualityPurple, QualityType.QUALITY_PURPLE),
KeyValuePair.Create(SH.ModelIntrinsicItemQualityOrange, QualityType.QUALITY_ORANGE),
KeyValuePair.Create(SH.ModelIntrinsicItemQualityRed, QualityType.QUALITY_ORANGE_SP),
]);
private static readonly FrozenDictionary<QualityType, Color> QualityTypeToColor = FrozenDictionary.ToFrozenDictionary(
[
KeyValuePair.Create(QualityType.QUALITY_WHITE, KnownColors.White),
KeyValuePair.Create(QualityType.QUALITY_GREEN, KnownColors.Green),
KeyValuePair.Create(QualityType.QUALITY_BLUE, KnownColors.Blue),
KeyValuePair.Create(QualityType.QUALITY_PURPLE, KnownColors.Purple),
KeyValuePair.Create(QualityType.QUALITY_ORANGE, KnownColors.Orange),
KeyValuePair.Create(QualityType.QUALITY_ORANGE_SP, KnownColors.Orange),
]);
public static Color QualityNameToColor(string qualityName)
{
return QualityToColor(LocalizedNameToQualityType.GetValueOrDefault(qualityName));
}
public static Color QualityToColor(QualityType quality)
{
return QualityTypeToColor.GetValueOrDefault(quality, Colors.Transparent);
}
/// <inheritdoc/>
public override Color Convert(QualityType from)
{
return QualityToColor(from);
return from switch
{
QualityType.QUALITY_WHITE => StructMarshal.Color(0xFF72778B),
QualityType.QUALITY_GREEN => StructMarshal.Color(0xFF2A8F72),
QualityType.QUALITY_BLUE => StructMarshal.Color(0xFF5180CB),
QualityType.QUALITY_PURPLE => StructMarshal.Color(0xFFA156E0),
QualityType.QUALITY_ORANGE or QualityType.QUALITY_ORANGE_SP => StructMarshal.Color(0xFFBC6932),
_ => Colors.Transparent,
};
}
}

View File

@@ -3,7 +3,6 @@
using Snap.Hutao.Control;
using Snap.Hutao.Model.Intrinsic;
using System.Collections.Frozen;
namespace Snap.Hutao.Model.Metadata.Converter;
@@ -13,20 +12,6 @@ namespace Snap.Hutao.Model.Metadata.Converter;
[HighQuality]
internal sealed class WeaponTypeIconConverter : ValueConverter<WeaponType, Uri>
{
private static readonly FrozenDictionary<string, WeaponType> LocalizedNameToWeaponType = FrozenDictionary.ToFrozenDictionary(
[
KeyValuePair.Create(SH.ModelIntrinsicWeaponTypeSwordOneHand, WeaponType.WEAPON_SWORD_ONE_HAND),
KeyValuePair.Create(SH.ModelIntrinsicWeaponTypeBow, WeaponType.WEAPON_BOW),
KeyValuePair.Create(SH.ModelIntrinsicWeaponTypePole, WeaponType.WEAPON_POLE),
KeyValuePair.Create(SH.ModelIntrinsicWeaponTypeClaymore, WeaponType.WEAPON_CLAYMORE),
KeyValuePair.Create(SH.ModelIntrinsicWeaponTypeCatalyst, WeaponType.WEAPON_CATALYST),
]);
public static Uri WeaponTypeNameToIconUri(string weaponTypeName)
{
return WeaponTypeToIconUri(LocalizedNameToWeaponType.GetValueOrDefault(weaponTypeName));
}
/// <summary>
/// 将武器类型转换为图标链接
/// </summary>

View File

@@ -1,50 +0,0 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Model.Intrinsic;
using Snap.Hutao.Model.Primitive;
namespace Snap.Hutao.Model.Metadata.Furniture;
internal sealed class Furniture
{
public List<FurnitureTypeId> Types { get; set; } = default!;
public FurnitureDeploySurfaceType SurfaceType { get; set; }
public bool IsSpecial { get; set; }
public SpecialFurnitureType SpecialType { get; set; }
public uint Comfort { get; set; }
public uint Cost { get; set; }
public uint DiscountCost { get; set; }
public bool CanFloat { get; set; }
public bool IsUnique { get; set; }
public string? ItemIcon { get; set; }
public string? EffectIcon { get; set; }
public QualityType RankLevel { get; set; }
public List<FurnitureId> GruopUnits { get; set; } = default!;
public GroupRecordType GroupRecordType { get; set; }
public List<string> SourceTexts { get; set; } = default!;
public FurnitureId Id { get; set; }
public string Name { get; set; } = default!;
public string Description { get; set; } = default!;
public string? Icon { get; set; }
public uint Rank { get; set; }
}

View File

@@ -1,17 +0,0 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Model.Primitive;
namespace Snap.Hutao.Model.Metadata.Furniture;
internal sealed class FurnitureMake
{
public FurnitureMakeId Id { get; set; }
public FurnitureId ItemId { get; set; }
public uint Experience { get; set; }
public List<IdCount> Materials { get; set; } = default!;
}

View File

@@ -1,25 +0,0 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Model.Primitive;
namespace Snap.Hutao.Model.Metadata.Furniture;
internal sealed class FurnitureSuite
{
public FurnitureSuiteId Id { get; set; }
public List<FurnitureTypeId> Types { get; set; } = default!;
public string Name { get; set; } = default!;
public string Description { get; set; } = default!;
public string ItemIcon { get; set; } = default!;
public string? MapIcon { get; set; }
public List<AvatarId>? FavoriteNpcs { get; set; }
public List<FurnitureId> Units { get; set; } = default!;
}

View File

@@ -1,28 +0,0 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Model.Intrinsic;
using Snap.Hutao.Model.Primitive;
namespace Snap.Hutao.Model.Metadata.Furniture;
internal sealed class FurnitureType
{
public FurnitureTypeId Id { get; set; }
public uint Category { get; set; }
public string Name { get; set; } = default!;
public string Name2 { get; set; } = default!;
public string TabIcon { get; set; } = default!;
public FurnitureDeployType SceneType { get; set; }
public bool BagPageOnly { get; set; }
public bool IsShowInBag { get; set; }
public uint Sort { get; set; }
}

View File

@@ -49,7 +49,7 @@ internal sealed class GachaEvent
/// <summary>
/// 卡池类型
/// </summary>
public GachaType Type { get; set; }
public GachaConfigType Type { get; set; }
/// <summary>
/// 五星列表

View File

@@ -1,19 +0,0 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Model.Primitive;
namespace Snap.Hutao.Model.Metadata;
internal class IdCount
{
/// <summary>
/// Id
/// </summary>
public MaterialId Id { get; set; }
/// <summary>
/// 数量
/// </summary>
public uint Count { get; set; }
}

View File

@@ -13,7 +13,7 @@
<Identity
Name="60568DGPStudio.SnapHutao"
Publisher="CN=35C8E923-85DF-49A7-9172-B39DC6312C52"
Version="1.9.8.0" />
Version="1.9.7.0" />
<Properties>
<DisplayName>Snap Hutao</DisplayName>

View File

@@ -13,7 +13,7 @@
<Identity
Name="60568DGPStudio.SnapHutaoDev"
Publisher="CN=35C8E923-85DF-49A7-9172-B39DC6312C52"
Version="1.9.8.0" />
Version="1.9.7.0" />
<Properties>
<DisplayName>Snap Hutao Dev</DisplayName>

View File

@@ -2,6 +2,7 @@
// Licensed under the MIT license.
using Microsoft.UI.Xaml;
using Snap.Hutao.Win32.Foundation;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using WinRT;
@@ -33,7 +34,7 @@ public static partial class Program
}
[LibraryImport("Microsoft.WindowsAppRuntime.dll", EntryPoint = "WindowsAppRuntime_EnsureIsLoaded")]
private static partial int WindowsAppRuntimeEnsureIsLoaded();
private static partial HRESULT WindowsAppRuntimeEnsureIsLoaded();
[LibraryImport("Microsoft.ui.xaml.dll")]
private static partial void XamlCheckProcessRequirements();

View File

@@ -60,45 +60,45 @@
: and then encoded with base64 encoding.
-->
<xsd:schema xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" id="root">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace"/>
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0"/>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string"/>
<xsd:attribute name="type" type="xsd:string"/>
<xsd:attribute name="mimetype" type="xsd:string"/>
<xsd:attribute ref="xml:space"/>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string"/>
<xsd:attribute name="name" type="xsd:string"/>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2"/>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1"/>
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3"/>
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4"/>
<xsd:attribute ref="xml:space"/>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required"/>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
@@ -186,18 +186,6 @@
<data name="CoreWebView2HelperVersionUndetected" xml:space="preserve">
<value>No WebView2 Runtime detected</value>
</data>
<data name="CoreWindowHotkeyCombinationRegisterFailed" xml:space="preserve">
<value>Register [{0}] hotkey [{1}] failed</value>
</data>
<data name="CoreWindowThemeDark" xml:space="preserve">
<value>Dark</value>
</data>
<data name="CoreWindowThemeLight" xml:space="preserve">
<value>Light</value>
</data>
<data name="CoreWindowThemeSystem" xml:space="preserve">
<value>System</value>
</data>
<data name="FilePickerExportCommit" xml:space="preserve">
<value>Export</value>
</data>
@@ -210,10 +198,10 @@
<data name="LaunchGameTitle" xml:space="preserve">
<value>Select Account to Launch</value>
</data>
<data name="MetadataSpecialNameMetaAvatarSexProInfoPronounBoyGirlD" xml:space="preserve">
<data name="MetadataSpecialNameMateAvatarSexProInfoPronounBoyGirlD" xml:space="preserve">
<value>&lt;color=#1E90FF&gt;Prince&lt;/color&gt;/&lt;color=#FFB6C1&gt;Princess&lt;/color&gt;</value>
</data>
<data name="MetadataSpecialNameMetaAvatarSexProInfoPronounBoyGirlFirst" xml:space="preserve">
<data name="MetadataSpecialNameMateAvatarSexProInfoPronounBoyGirlFirst" xml:space="preserve">
<value>&lt;color=#1E90FF&gt;I&lt;/color&gt;/&lt;color=#FFB6C1&gt;I&lt;/color&gt;</value>
</data>
<data name="MetadataSpecialNameNickname" xml:space="preserve">
@@ -869,9 +857,6 @@
<data name="ServiceGachaLogFactoryAvatarWishName" xml:space="preserve">
<value>Character Event Wish</value>
</data>
<data name="ServiceGachaLogFactoryChronicledWishName" xml:space="preserve">
<value>Chronicled Wish</value>
</data>
<data name="ServiceGachaLogFactoryPermanentWishName" xml:space="preserve">
<value>Wanderlust Invocation</value>
</data>
@@ -2229,7 +2214,7 @@
<value>Auto start Better GUI for automation tasks after game launched</value>
</data>
<data name="ViewPageLaunchGameBetterGIHeader" xml:space="preserve">
<value>Automated Tasks</value>
<value>Better GI</value>
</data>
<data name="ViewPageLaunchGameCommonHeader" xml:space="preserve">
<value>General</value>
@@ -2247,7 +2232,7 @@
<value>File</value>
</data>
<data name="ViewPageLaunchGameInterProcessHeader" xml:space="preserve">
<value>Process Linkage</value>
<value>InterProcess</value>
</data>
<data name="ViewPageLaunchGameMonitorsDescription" xml:space="preserve">
<value>Run the software on the selected monitor</value>
@@ -2268,7 +2253,7 @@
<value>Try to start Starward after the game is started for game duration statistics</value>
</data>
<data name="ViewPageLaunchGamePlayTimeHeader" xml:space="preserve">
<value>Game Hours Record</value>
<value>Hours Played</value>
</data>
<data name="ViewPageLaunchGameProcessHeader" xml:space="preserve">
<value>Progress</value>
@@ -2381,9 +2366,6 @@
<data name="ViewPageSettingBackgroundImageHeader" xml:space="preserve">
<value>Wallpaper Image</value>
</data>
<data name="ViewPageSettingBackgroundImageLocalFolderCopyrightHeader" xml:space="preserve">
<value>Using local images as the background</value>
</data>
<data name="ViewPageSettingCacheFolderDescription" xml:space="preserve">
<value>Images cache are saved here</value>
</data>
@@ -2639,12 +2621,6 @@
<data name="ViewPageSettingStoreReviewNavigate" xml:space="preserve">
<value>Rate Snap Hutao</value>
</data>
<data name="ViewPageSettingThemeDescription" xml:space="preserve">
<value>Change window theme</value>
</data>
<data name="ViewPageSettingThemeHeader" xml:space="preserve">
<value>Theme</value>
</data>
<data name="ViewPageSettingTranslateNavigate" xml:space="preserve">
<value>Contribute Translations</value>
</data>
@@ -2654,12 +2630,6 @@
<data name="ViewPageSettingWebview2Header" xml:space="preserve">
<value>Webview2 Runtime</value>
</data>
<data name="ViewPageSpiralAbyssTeamAppearanceDownHeader" xml:space="preserve">
<value>Second Half</value>
</data>
<data name="ViewPageSpiralAbyssTeamAppearanceUpHeader" xml:space="preserve">
<value>First Half</value>
</data>
<data name="ViewPageWiKiAvatarArtifactSetCombinationHeader" xml:space="preserve">
<value>Artifact Set Combination</value>
</data>
@@ -2766,7 +2736,7 @@
<value>Character Appearance Rate = Character Appearance in this Floor (only count for 1 if repeated) / Total Number of Abyss Record of this Floor</value>
</data>
<data name="ViewSpiralAbyssAvatarUsageRankDescription" xml:space="preserve">
<value>Character Usage Rate = Character Appearance in this Floor (only count for 1 if repeated) / Number of Player who Own this Character</value>
<value>Character Usage Rate = Character Appearance in this Floor (only count for 1 is repeated) / Number of Player who Own this Character</value>
</data>
<data name="ViewSpiralAbyssBattleHeader" xml:space="preserve">
<value>Battle Statistics</value>
@@ -2922,7 +2892,7 @@
<value>〓Event Duration〓.*?\d\.\d Available throughout the entirety of Version</value>
</data>
<data name="WebAnnouncementMatchTransientActivityTime" xml:space="preserve">
<value>(?:〓Event Duration〓|Event Wish Duration|【Availability Duration】|〓Discount Period〓).*?(\d\.\dAfter the Version update).*?~.*?&amp;lt;t class="t_(?:gl|lc)".*?&amp;gt;(.*?)&amp;lt;/t&amp;gt;</value>
<value>(?:〓Event Duration〓|Event Wish Duration|【Availability Duration】).*?(\d\.\dAfter the Version update).*?~.*?&amp;lt;t class="t_(?:gl|lc)".*?&amp;gt;(.*?)&amp;lt;/t&amp;gt;</value>
</data>
<data name="WebAnnouncementMatchVersionUpdateTime" xml:space="preserve">
<value>〓Update Maintenance Duration〓.+?&amp;lt;t class=\"t_(?:gl|lc)\".*?&amp;gt;(.*?)&amp;lt;/t&amp;gt;</value>
@@ -3074,9 +3044,6 @@
<data name="WebGachaConfigTypeAvatarEventWish2" xml:space="preserve">
<value>Character Event Wish-2</value>
</data>
<data name="WebGachaConfigTypeChronicledWish" xml:space="preserve">
<value>Chronicled Wish</value>
</data>
<data name="WebGachaConfigTypeNoviceWish" xml:space="preserve">
<value>Beginners' Wish</value>
</data>

View File

@@ -60,45 +60,45 @@
: and then encoded with base64 encoding.
-->
<xsd:schema xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" id="root">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace"/>
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0"/>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string"/>
<xsd:attribute name="type" type="xsd:string"/>
<xsd:attribute name="mimetype" type="xsd:string"/>
<xsd:attribute ref="xml:space"/>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string"/>
<xsd:attribute name="name" type="xsd:string"/>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2"/>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1"/>
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3"/>
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4"/>
<xsd:attribute ref="xml:space"/>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required"/>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
@@ -186,18 +186,6 @@
<data name="CoreWebView2HelperVersionUndetected" xml:space="preserve">
<value>WebView2 Runtime tidak terdeteksi.</value>
</data>
<data name="CoreWindowHotkeyCombinationRegisterFailed" xml:space="preserve">
<value>[{0}] Hotkey [{1}] gagal terdaftar</value>
</data>
<data name="CoreWindowThemeDark" xml:space="preserve">
<value>深色</value>
</data>
<data name="CoreWindowThemeLight" xml:space="preserve">
<value>浅色</value>
</data>
<data name="CoreWindowThemeSystem" xml:space="preserve">
<value>跟随系统</value>
</data>
<data name="FilePickerExportCommit" xml:space="preserve">
<value>Ekspor</value>
</data>
@@ -210,20 +198,20 @@
<data name="LaunchGameTitle" xml:space="preserve">
<value>Pilih akun untuk memulai</value>
</data>
<data name="MetadataSpecialNameMetaAvatarSexProInfoPronounBoyGirlD" xml:space="preserve">
<value>&lt;color=#1E90FF&gt;Prince&lt;/color&gt;/&lt;color=#FFB6C1&gt;Princess&lt;/color&gt;</value>
<data name="MetadataSpecialNameMateAvatarSexProInfoPronounBoyGirlD" xml:space="preserve">
<value>&lt;color=#1E90FF&gt;王子&lt;/color&gt;/&lt;color=#FFB6C1&gt;公主&lt;/color&gt;</value>
</data>
<data name="MetadataSpecialNameMetaAvatarSexProInfoPronounBoyGirlFirst" xml:space="preserve">
<value>&lt;color=#1E90FF&gt;I&lt;/color&gt;/&lt;color=#FFB6C1&gt;I&lt;/color&gt;</value>
<data name="MetadataSpecialNameMateAvatarSexProInfoPronounBoyGirlFirst" xml:space="preserve">
<value>&lt;color=#1E90FF&gt;&lt;/color&gt;/&lt;color=#FFB6C1&gt;&lt;/color&gt;</value>
</data>
<data name="MetadataSpecialNameNickname" xml:space="preserve">
<value>Pengembara</value>
</data>
<data name="MetadataSpecialNamePlayerAvatarSexProInfoPronounHeShe" xml:space="preserve">
<value>&lt;color=#1E90FF&gt;He&lt;/color&gt;/&lt;color=#FFB6C1&gt;She&lt;/color&gt;</value>
<value>&lt;color=#1E90FF&gt;&lt;/color&gt;/&lt;color=#FFB6C1&gt;&lt;/color&gt;</value>
</data>
<data name="MetadataSpecialNameRealNameId1" xml:space="preserve">
<value>Pengembara</value>
<value>流浪者</value>
</data>
<data name="ModelBindingAvatarPropertyWeaponAffixFormat" xml:space="preserve">
<value>Perbaiki {0}</value>
@@ -525,7 +513,7 @@
<value>Gelombang 4: Gelombang akan muncul hanya setelah membunuh semua musuh di gelombang sebelumnya</value>
</data>
<data name="ModelMetadataTowerWaveTypeWave99999" xml:space="preserve">
<value>Tetapkan 4 Perampok Harta Karun di lapangan, langsung muncul kembali setelah mati. Total 12.</value>
<value>维持场上 4 个盗宝团怪物,击杀后立即替换,总数 12</value>
</data>
<data name="ModelNameValueDefaultDescription" xml:space="preserve">
<value>Silakan perbarui data tampilan.</value>
@@ -780,19 +768,19 @@
<value>Pameran Karakter: {0:MM-dd HH:mm}</value>
</data>
<data name="ServiceBackgroundImageTypeBing" xml:space="preserve">
<value>Wallpaper Harian Bing</value>
<value>必应每日一图</value>
</data>
<data name="ServiceBackgroundImageTypeDaily" xml:space="preserve">
<value>Wallpaper Harian Hutao</value>
<value>胡桃每日一图</value>
</data>
<data name="ServiceBackgroundImageTypeLauncher" xml:space="preserve">
<value>Wallpaper Resmi Peluncur Genshin</value>
<value>官方启动器壁纸</value>
</data>
<data name="ServiceBackgroundImageTypeLocalFolder" xml:space="preserve">
<value>Gambar Acak Lokal</value>
<value>本地随机图片</value>
</data>
<data name="ServiceBackgroundImageTypeNone" xml:space="preserve">
<value>Tanpa Gambar Latar</value>
<value>无背景图片</value>
</data>
<data name="ServiceCultivationProjectCurrentUserdataCourrpted" xml:space="preserve">
<value>Gagal menyimpan status rencana pengembangan</value>
@@ -852,7 +840,7 @@
<value>Parametric Transformer telah siap</value>
</data>
<data name="ServiceDiscordActivityElevationRequiredHint" xml:space="preserve">
<value>Izin hilang, tidak dapat mengatur Aktivitas Discord Anda.</value>
<value>权限不足,将无法为您设置 Discord Activity 状态</value>
</data>
<data name="ServiceDiscordGameActivityDetails" xml:space="preserve">
<value>Menjelajahi di Teyvat</value>
@@ -869,9 +857,6 @@
<data name="ServiceGachaLogFactoryAvatarWishName" xml:space="preserve">
<value>Event Wish Karakter</value>
</data>
<data name="ServiceGachaLogFactoryChronicledWishName" xml:space="preserve">
<value>集录祈愿</value>
</data>
<data name="ServiceGachaLogFactoryPermanentWishName" xml:space="preserve">
<value>Wanderlust Invocation</value>
</data>
@@ -1239,10 +1224,10 @@
<value>Catatan Realtime Webhook URL</value>
</data>
<data name="ViewDialogFeedbackEnableLoopbackContent" xml:space="preserve">
<value>Anda memerlukan alat lain untuk mengembalikan loopback setelah dikecualikan</value>
<value>解除限制后需要使用其他工具恢复限制</value>
</data>
<data name="ViewDialogFeedbackEnableLoopbackTitle" xml:space="preserve">
<value>Konfirmasi untuk Mengonfigurasi Pengecualian Loopback</value>
<value>是否解除 Loopback 限制</value>
</data>
<data name="ViewDialogGachaLogImportTitle" xml:space="preserve">
<value>Mengimpor riwayat wish</value>
@@ -1425,7 +1410,7 @@
<value>Game Launcher</value>
</data>
<data name="ViewListViewDragElevatedHint" xml:space="preserve">
<value>Tidak dapat mengurutkan dalam Mode Administrator</value>
<value>管理员模式下无法拖动排序</value>
</data>
<data name="ViewModelAchievementArchiveAdded" xml:space="preserve">
<value>Arsip [{0}] berhasil ditambahkan</value>
@@ -1707,7 +1692,7 @@
<value>Selesai</value>
</data>
<data name="ViewModelWelcomeDownloadSummaryContentTypeNotMatch" xml:space="preserve">
<value>Stream respons tidak berisi tipe konten yang valid</value>
<value>响应内容不是有效的文件字节流</value>
</data>
<data name="ViewModelWelcomeDownloadSummaryDefault" xml:space="preserve">
<value>Mengantre</value>
@@ -1926,16 +1911,16 @@
<value>Tautan Berguna</value>
</data>
<data name="ViewPageFeedbackCurrentProxyHeader" xml:space="preserve">
<value>Proxy Saat ini</value>
<value>当前代理</value>
</data>
<data name="ViewPageFeedbackCurrentProxyNoProxyDescription" xml:space="preserve">
<value>Tidak ada Proxy</value>
<value>无代理</value>
</data>
<data name="ViewPageFeedbackEnableLoopbackEnabledDescription" xml:space="preserve">
<value>Dikecualikan</value>
<value>已解除</value>
</data>
<data name="ViewPageFeedbackEnableLoopbackHeader" xml:space="preserve">
<value>Konfigurasikan Pengecualian Loopback</value>
<value>解除 Loopback 限制</value>
</data>
<data name="ViewPageFeedbackEngageWithUsDescription" xml:space="preserve">
<value>Tetap berhubungan dengan kami</value>
@@ -2226,10 +2211,10 @@
<value>Argumen Awalan</value>
</data>
<data name="ViewPageLaunchGameBetterGIDescription" xml:space="preserve">
<value>Mulai Otomatis Antarmuka Pengguna yang Lebih Baik untuk tugas otomatis setelah game diluncurkan</value>
<value>在游戏启动后尝试启动并使用 Better GI 进行自动化任务</value>
</data>
<data name="ViewPageLaunchGameBetterGIHeader" xml:space="preserve">
<value>自动化任务</value>
<value>Better GI</value>
</data>
<data name="ViewPageLaunchGameCommonHeader" xml:space="preserve">
<value>Umum</value>
@@ -2373,16 +2358,13 @@
<value>Backdrop Material</value>
</data>
<data name="ViewPageSettingBackgroundImageCopyrightHeader" xml:space="preserve">
<value>Informasi Hak Cipta Gambar</value>
<value>图片版权信息</value>
</data>
<data name="ViewPageSettingBackgroundImageDescription" xml:space="preserve">
<value>Ubah sumber gambar latar, restart Snap Hutao untuk menerapkan perubahan</value>
<value>更改窗体的背景图片来源,重启胡桃以尽快生效</value>
</data>
<data name="ViewPageSettingBackgroundImageHeader" xml:space="preserve">
<value>Gambar Latar</value>
</data>
<data name="ViewPageSettingBackgroundImageLocalFolderCopyrightHeader" xml:space="preserve">
<value>当前背景为本地图片,如遇版权问题由用户个人负责</value>
<value>背景图片</value>
</data>
<data name="ViewPageSettingCacheFolderDescription" xml:space="preserve">
<value>Cache gambar disimpan di sini</value>
@@ -2391,7 +2373,7 @@
<value>Berkas Cache</value>
</data>
<data name="ViewPageSettingCardHutaoPassportHeader" xml:space="preserve">
<value>Hutao Passport</value>
<value>胡桃通行证</value>
</data>
<data name="ViewPageSettingCopyDeviceIdAction" xml:space="preserve">
<value>Salin</value>
@@ -2586,10 +2568,10 @@
<value>Website Resmi</value>
</data>
<data name="ViewPageSettingOpenBackgroundImageFolderDescription" xml:space="preserve">
<value>Latar Belakang Kustom, format didukung bmp/gif/ico/jpg/jpeg/png/tiff/webp</value>
<value>自定义背景图片,支持 bmp / gif / ico / jpg / jpeg / png / tiff / webp 格式</value>
</data>
<data name="ViewPageSettingOpenBackgroundImageFolderHeader" xml:space="preserve">
<value>Buka Berkas Latar Belakang</value>
<value>打开背景图片文件夹</value>
</data>
<data name="ViewPageSettingResetAction" xml:space="preserve">
<value>Reset</value>
@@ -2601,10 +2583,10 @@
<value>Setel ulang Sumber Daya Gambar</value>
</data>
<data name="ViewPageSettingsAdvancedOptionsLaunchUnlockFpsDescription" xml:space="preserve">
<value>Tambahkan Opsi Batasan FPS di Bagian Proses Peluncur Permainan</value>
<value>在启动游戏页面的进程部分加入解锁帧率限制选项</value>
</data>
<data name="ViewPageSettingsAdvancedOptionsLaunchUnlockFpsHeader" xml:space="preserve">
<value>Peluncur Permainan - Buka Limit FPS</value>
<value>启动游戏-解锁帧率限制</value>
</data>
<data name="ViewPageSettingSetDataFolderDescription" xml:space="preserve">
<value>Anda perlu memindahkan data di direktori secara manual, jika tidak, data pengguna baru akan dibuat.</value>
@@ -2639,12 +2621,6 @@
<data name="ViewPageSettingStoreReviewNavigate" xml:space="preserve">
<value>Beri Rating untuk Snap Hutao</value>
</data>
<data name="ViewPageSettingThemeDescription" xml:space="preserve">
<value>更改窗体的颜色主题</value>
</data>
<data name="ViewPageSettingThemeHeader" xml:space="preserve">
<value>颜色主题</value>
</data>
<data name="ViewPageSettingTranslateNavigate" xml:space="preserve">
<value>Kontribusi penerjemahan</value>
</data>
@@ -2654,12 +2630,6 @@
<data name="ViewPageSettingWebview2Header" xml:space="preserve">
<value>Webview2 Runtime</value>
</data>
<data name="ViewPageSpiralAbyssTeamAppearanceDownHeader" xml:space="preserve">
<value>下半</value>
</data>
<data name="ViewPageSpiralAbyssTeamAppearanceUpHeader" xml:space="preserve">
<value>上半</value>
</data>
<data name="ViewPageWiKiAvatarArtifactSetCombinationHeader" xml:space="preserve">
<value>Kombinasi Set Artefak</value>
</data>
@@ -2922,7 +2892,7 @@
<value>〓Durasi Event〓.*?\d\.\d Tersedia selama versi ini</value>
</data>
<data name="WebAnnouncementMatchTransientActivityTime" xml:space="preserve">
<value>(?:〓Waktu Acara〓|Waktu Menginginkan|【Waktu Peluncuran】|〓Waktu Diskon〓).*?(\d\.\d Setelah Pembaruan Versi).*?~.*?&amp;lt;t class="t_(?:gl|lc)".*?&amp;gt;(.*?)&amp;lt;/t&amp;gt;</value>
<value>(?:〓Durasi Event〓|Durasi Event Wish|【Ketersediaan DUrasi】).*?(\d\.\dSetelah pembaruan versi).*?~.*?&amp;lt;t class="t_(?:gl|lc)".*?&amp;gt;(.*?)&amp;lt;/t&amp;gt;</value>
</data>
<data name="WebAnnouncementMatchVersionUpdateTime" xml:space="preserve">
<value>〓Durasi Pemeliharaan Pembaruan.+?&amp;lt;t class=\"t_(?:gl|lc)\".*?&amp;gt;(.*?)&amp;lt;/t&amp;gt;</value>
@@ -3074,9 +3044,6 @@
<data name="WebGachaConfigTypeAvatarEventWish2" xml:space="preserve">
<value>Event WIsh Karakter-2</value>
</data>
<data name="WebGachaConfigTypeChronicledWish" xml:space="preserve">
<value>集录祈愿</value>
</data>
<data name="WebGachaConfigTypeNoviceWish" xml:space="preserve">
<value>Wish Pemula</value>
</data>

View File

@@ -60,45 +60,45 @@
: and then encoded with base64 encoding.
-->
<xsd:schema xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" id="root">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace"/>
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0"/>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string"/>
<xsd:attribute name="type" type="xsd:string"/>
<xsd:attribute name="mimetype" type="xsd:string"/>
<xsd:attribute ref="xml:space"/>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string"/>
<xsd:attribute name="name" type="xsd:string"/>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2"/>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1"/>
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3"/>
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4"/>
<xsd:attribute ref="xml:space"/>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required"/>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
@@ -186,18 +186,6 @@
<data name="CoreWebView2HelperVersionUndetected" xml:space="preserve">
<value>WebView2 ランタイムが検出されませんでした</value>
</data>
<data name="CoreWindowHotkeyCombinationRegisterFailed" xml:space="preserve">
<value>[{0}] 热键 [{1}] 注册失败</value>
</data>
<data name="CoreWindowThemeDark" xml:space="preserve">
<value>深色</value>
</data>
<data name="CoreWindowThemeLight" xml:space="preserve">
<value>浅色</value>
</data>
<data name="CoreWindowThemeSystem" xml:space="preserve">
<value>跟随系统</value>
</data>
<data name="FilePickerExportCommit" xml:space="preserve">
<value>エクスポート</value>
</data>
@@ -210,10 +198,10 @@
<data name="LaunchGameTitle" xml:space="preserve">
<value>アカウントを選択して開始します</value>
</data>
<data name="MetadataSpecialNameMetaAvatarSexProInfoPronounBoyGirlD" xml:space="preserve">
<data name="MetadataSpecialNameMateAvatarSexProInfoPronounBoyGirlD" xml:space="preserve">
<value>&lt;color=#1E90FF&gt;王子&lt;/color&gt;/&lt;color=#FFB6C1&gt;公主&lt;/color&gt;</value>
</data>
<data name="MetadataSpecialNameMetaAvatarSexProInfoPronounBoyGirlFirst" xml:space="preserve">
<data name="MetadataSpecialNameMateAvatarSexProInfoPronounBoyGirlFirst" xml:space="preserve">
<value>&lt;color=#1E90FF&gt;我&lt;/color&gt;/&lt;color=#FFB6C1&gt;我&lt;/color&gt;</value>
</data>
<data name="MetadataSpecialNameNickname" xml:space="preserve">
@@ -869,9 +857,6 @@
<data name="ServiceGachaLogFactoryAvatarWishName" xml:space="preserve">
<value>イベント祈願・キャラクター</value>
</data>
<data name="ServiceGachaLogFactoryChronicledWishName" xml:space="preserve">
<value>集录祈愿</value>
</data>
<data name="ServiceGachaLogFactoryPermanentWishName" xml:space="preserve">
<value>奔走世間</value>
</data>
@@ -2229,7 +2214,7 @@
<value>在游戏启动后尝试启动并使用 Better GI 进行自动化任务</value>
</data>
<data name="ViewPageLaunchGameBetterGIHeader" xml:space="preserve">
<value>自动化任务</value>
<value>Better GI</value>
</data>
<data name="ViewPageLaunchGameCommonHeader" xml:space="preserve">
<value>一般</value>
@@ -2381,9 +2366,6 @@
<data name="ViewPageSettingBackgroundImageHeader" xml:space="preserve">
<value>背景图片</value>
</data>
<data name="ViewPageSettingBackgroundImageLocalFolderCopyrightHeader" xml:space="preserve">
<value>当前背景为本地图片,如遇版权问题由用户个人负责</value>
</data>
<data name="ViewPageSettingCacheFolderDescription" xml:space="preserve">
<value>イメージキャッシュはここに格納されます</value>
</data>
@@ -2639,12 +2621,6 @@
<data name="ViewPageSettingStoreReviewNavigate" xml:space="preserve">
<value>レビューや感想</value>
</data>
<data name="ViewPageSettingThemeDescription" xml:space="preserve">
<value>更改窗体的颜色主题</value>
</data>
<data name="ViewPageSettingThemeHeader" xml:space="preserve">
<value>颜色主题</value>
</data>
<data name="ViewPageSettingTranslateNavigate" xml:space="preserve">
<value>和訳を提供</value>
</data>
@@ -2654,12 +2630,6 @@
<data name="ViewPageSettingWebview2Header" xml:space="preserve">
<value>Webview2 ランタイム</value>
</data>
<data name="ViewPageSpiralAbyssTeamAppearanceDownHeader" xml:space="preserve">
<value>下半</value>
</data>
<data name="ViewPageSpiralAbyssTeamAppearanceUpHeader" xml:space="preserve">
<value>上半</value>
</data>
<data name="ViewPageWiKiAvatarArtifactSetCombinationHeader" xml:space="preserve">
<value>おすすめ聖遺物</value>
</data>
@@ -2925,10 +2895,10 @@
<value>(?:〓イベント期間〓|祈願期間|【開始日時】).*?(\d\.\dバージョンアップ完了後).*?~.*?&amp;lt;t class="t_(?:gl|lc)".*?&amp;gt;(.*?)&amp;lt;/t&amp;gt;</value>
</data>
<data name="WebAnnouncementMatchVersionUpdateTime" xml:space="preserve">
<value>〓メンテナンス時間〓.+?&amp;lt;t class=\"t_(?:gl|lc)\".*?&amp;gt;(.*?)&amp;lt;/t&amp;gt;</value>
<value>〓更新日時〓.+?&amp;lt;t class=\"t_(?:gl|lc)\".*?&amp;gt;(.*?)&amp;lt;/t&amp;gt;</value>
</data>
<data name="WebAnnouncementMatchVersionUpdateTitle" xml:space="preserve">
<value>Ver.\d\.\d.+正式リリース</value>
<value>Ver.\d\.\d 更新内容</value>
</data>
<data name="WebAnnouncementTimeDaysBeginFormat" xml:space="preserve">
<value>{0} 日後に開始</value>
@@ -3074,9 +3044,6 @@
<data name="WebGachaConfigTypeAvatarEventWish2" xml:space="preserve">
<value>イベント祈願・キャラクター2</value>
</data>
<data name="WebGachaConfigTypeChronicledWish" xml:space="preserve">
<value>集录祈愿</value>
</data>
<data name="WebGachaConfigTypeNoviceWish" xml:space="preserve">
<value>初心者祈願</value>
</data>

View File

@@ -60,45 +60,45 @@
: and then encoded with base64 encoding.
-->
<xsd:schema xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" id="root">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace"/>
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0"/>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string"/>
<xsd:attribute name="type" type="xsd:string"/>
<xsd:attribute name="mimetype" type="xsd:string"/>
<xsd:attribute ref="xml:space"/>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string"/>
<xsd:attribute name="name" type="xsd:string"/>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2"/>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1"/>
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3"/>
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4"/>
<xsd:attribute ref="xml:space"/>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required"/>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
@@ -186,18 +186,6 @@
<data name="CoreWebView2HelperVersionUndetected" xml:space="preserve">
<value>WebView2 런타임이 감지되지 않음</value>
</data>
<data name="CoreWindowHotkeyCombinationRegisterFailed" xml:space="preserve">
<value>[{0}] 热键 [{1}] 注册失败</value>
</data>
<data name="CoreWindowThemeDark" xml:space="preserve">
<value>深色</value>
</data>
<data name="CoreWindowThemeLight" xml:space="preserve">
<value>浅色</value>
</data>
<data name="CoreWindowThemeSystem" xml:space="preserve">
<value>跟随系统</value>
</data>
<data name="FilePickerExportCommit" xml:space="preserve">
<value>내보내기</value>
</data>
@@ -210,10 +198,10 @@
<data name="LaunchGameTitle" xml:space="preserve">
<value>계정 선택 및 시작</value>
</data>
<data name="MetadataSpecialNameMetaAvatarSexProInfoPronounBoyGirlD" xml:space="preserve">
<data name="MetadataSpecialNameMateAvatarSexProInfoPronounBoyGirlD" xml:space="preserve">
<value>&lt;color=#1E90FF&gt;王子&lt;/color&gt;/&lt;color=#FFB6C1&gt;公主&lt;/color&gt;</value>
</data>
<data name="MetadataSpecialNameMetaAvatarSexProInfoPronounBoyGirlFirst" xml:space="preserve">
<data name="MetadataSpecialNameMateAvatarSexProInfoPronounBoyGirlFirst" xml:space="preserve">
<value>&lt;color=#1E90FF&gt;我&lt;/color&gt;/&lt;color=#FFB6C1&gt;我&lt;/color&gt;</value>
</data>
<data name="MetadataSpecialNameNickname" xml:space="preserve">
@@ -869,9 +857,6 @@
<data name="ServiceGachaLogFactoryAvatarWishName" xml:space="preserve">
<value>캐릭터 이벤트</value>
</data>
<data name="ServiceGachaLogFactoryChronicledWishName" xml:space="preserve">
<value>集录祈愿</value>
</data>
<data name="ServiceGachaLogFactoryPermanentWishName" xml:space="preserve">
<value>세상 여행</value>
</data>
@@ -2229,7 +2214,7 @@
<value>在游戏启动后尝试启动并使用 Better GI 进行自动化任务</value>
</data>
<data name="ViewPageLaunchGameBetterGIHeader" xml:space="preserve">
<value>自动化任务</value>
<value>Better GI</value>
</data>
<data name="ViewPageLaunchGameCommonHeader" xml:space="preserve">
<value>보통</value>
@@ -2381,9 +2366,6 @@
<data name="ViewPageSettingBackgroundImageHeader" xml:space="preserve">
<value>背景图片</value>
</data>
<data name="ViewPageSettingBackgroundImageLocalFolderCopyrightHeader" xml:space="preserve">
<value>当前背景为本地图片,如遇版权问题由用户个人负责</value>
</data>
<data name="ViewPageSettingCacheFolderDescription" xml:space="preserve">
<value>여기에 저장된 이미지 캐시</value>
</data>
@@ -2639,12 +2621,6 @@
<data name="ViewPageSettingStoreReviewNavigate" xml:space="preserve">
<value>评价软件</value>
</data>
<data name="ViewPageSettingThemeDescription" xml:space="preserve">
<value>更改窗体的颜色主题</value>
</data>
<data name="ViewPageSettingThemeHeader" xml:space="preserve">
<value>颜色主题</value>
</data>
<data name="ViewPageSettingTranslateNavigate" xml:space="preserve">
<value>번역에 기여하기</value>
</data>
@@ -2654,12 +2630,6 @@
<data name="ViewPageSettingWebview2Header" xml:space="preserve">
<value>Webview2 런타임</value>
</data>
<data name="ViewPageSpiralAbyssTeamAppearanceDownHeader" xml:space="preserve">
<value>下半</value>
</data>
<data name="ViewPageSpiralAbyssTeamAppearanceUpHeader" xml:space="preserve">
<value>上半</value>
</data>
<data name="ViewPageWiKiAvatarArtifactSetCombinationHeader" xml:space="preserve">
<value>搭配圣遗物</value>
</data>
@@ -3074,9 +3044,6 @@
<data name="WebGachaConfigTypeAvatarEventWish2" xml:space="preserve">
<value>캐릭터 이벤트 기원-2</value>
</data>
<data name="WebGachaConfigTypeChronicledWish" xml:space="preserve">
<value>集录祈愿</value>
</data>
<data name="WebGachaConfigTypeNoviceWish" xml:space="preserve">
<value>초심자 기원</value>
</data>

View File

@@ -60,45 +60,45 @@
: and then encoded with base64 encoding.
-->
<xsd:schema xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" id="root">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace"/>
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0"/>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string"/>
<xsd:attribute name="type" type="xsd:string"/>
<xsd:attribute name="mimetype" type="xsd:string"/>
<xsd:attribute ref="xml:space"/>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string"/>
<xsd:attribute name="name" type="xsd:string"/>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2"/>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1"/>
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3"/>
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4"/>
<xsd:attribute ref="xml:space"/>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required"/>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
@@ -186,18 +186,6 @@
<data name="CoreWebView2HelperVersionUndetected" xml:space="preserve">
<value>O WebView2 Runtime não foi detectado</value>
</data>
<data name="CoreWindowHotkeyCombinationRegisterFailed" xml:space="preserve">
<value>[{0}] 热键 [{1}] 注册失败</value>
</data>
<data name="CoreWindowThemeDark" xml:space="preserve">
<value>深色</value>
</data>
<data name="CoreWindowThemeLight" xml:space="preserve">
<value>浅色</value>
</data>
<data name="CoreWindowThemeSystem" xml:space="preserve">
<value>跟随系统</value>
</data>
<data name="FilePickerExportCommit" xml:space="preserve">
<value>Exportar</value>
</data>
@@ -210,10 +198,10 @@
<data name="LaunchGameTitle" xml:space="preserve">
<value>Selecionar conta para iniciar</value>
</data>
<data name="MetadataSpecialNameMetaAvatarSexProInfoPronounBoyGirlD" xml:space="preserve">
<data name="MetadataSpecialNameMateAvatarSexProInfoPronounBoyGirlD" xml:space="preserve">
<value>&lt;color=#1E90FF&gt;王子&lt;/color&gt;/&lt;color=#FFB6C1&gt;公主&lt;/color&gt;</value>
</data>
<data name="MetadataSpecialNameMetaAvatarSexProInfoPronounBoyGirlFirst" xml:space="preserve">
<data name="MetadataSpecialNameMateAvatarSexProInfoPronounBoyGirlFirst" xml:space="preserve">
<value>&lt;color=#1E90FF&gt;我&lt;/color&gt;/&lt;color=#FFB6C1&gt;我&lt;/color&gt;</value>
</data>
<data name="MetadataSpecialNameNickname" xml:space="preserve">
@@ -869,9 +857,6 @@
<data name="ServiceGachaLogFactoryAvatarWishName" xml:space="preserve">
<value>Evento de oração de personagem</value>
</data>
<data name="ServiceGachaLogFactoryChronicledWishName" xml:space="preserve">
<value>集录祈愿</value>
</data>
<data name="ServiceGachaLogFactoryPermanentWishName" xml:space="preserve">
<value>Invocação do Mochileiro</value>
</data>
@@ -1137,7 +1122,7 @@
<value>Sorte média</value>
</data>
<data name="ViewControlStatisticsCardUpText" xml:space="preserve">
<value>Em destaque</value>
<value>Acima</value>
</data>
<data name="ViewControlStatisticsSegmentedItemContentPrediction" xml:space="preserve">
<value>Previsão</value>
@@ -1155,7 +1140,7 @@
<value>Planejamento</value>
</data>
<data name="ViewDailyNoteHeader" xml:space="preserve">
<value>Lembretes</value>
<value>Lembrete em tempo real</value>
</data>
<data name="ViewDataHeader" xml:space="preserve">
<value>Dados</value>
@@ -2049,7 +2034,7 @@
<value>Visão geral</value>
</data>
<data name="ViewPageGahcaLogPivotStatistics" xml:space="preserve">
<value>Estatísticas globais</value>
<value>全球祈愿统计</value>
</data>
<data name="ViewPageGahcaLogPivotWeapon" xml:space="preserve">
<value>Arma</value>
@@ -2229,7 +2214,7 @@
<value>在游戏启动后尝试启动并使用 Better GI 进行自动化任务</value>
</data>
<data name="ViewPageLaunchGameBetterGIHeader" xml:space="preserve">
<value>自动化任务</value>
<value>Better GI</value>
</data>
<data name="ViewPageLaunchGameCommonHeader" xml:space="preserve">
<value>Geral</value>
@@ -2381,9 +2366,6 @@
<data name="ViewPageSettingBackgroundImageHeader" xml:space="preserve">
<value>背景图片</value>
</data>
<data name="ViewPageSettingBackgroundImageLocalFolderCopyrightHeader" xml:space="preserve">
<value>当前背景为本地图片,如遇版权问题由用户个人负责</value>
</data>
<data name="ViewPageSettingCacheFolderDescription" xml:space="preserve">
<value>O cache de imagens é salvo aqui</value>
</data>
@@ -2639,12 +2621,6 @@
<data name="ViewPageSettingStoreReviewNavigate" xml:space="preserve">
<value>Avalie o Snap Hutao</value>
</data>
<data name="ViewPageSettingThemeDescription" xml:space="preserve">
<value>更改窗体的颜色主题</value>
</data>
<data name="ViewPageSettingThemeHeader" xml:space="preserve">
<value>颜色主题</value>
</data>
<data name="ViewPageSettingTranslateNavigate" xml:space="preserve">
<value>Contribuir com traduções</value>
</data>
@@ -2654,12 +2630,6 @@
<data name="ViewPageSettingWebview2Header" xml:space="preserve">
<value>Webview2 Runtime</value>
</data>
<data name="ViewPageSpiralAbyssTeamAppearanceDownHeader" xml:space="preserve">
<value>下半</value>
</data>
<data name="ViewPageSpiralAbyssTeamAppearanceUpHeader" xml:space="preserve">
<value>上半</value>
</data>
<data name="ViewPageWiKiAvatarArtifactSetCombinationHeader" xml:space="preserve">
<value>Combinação de conjuntos de artefatos</value>
</data>
@@ -2991,7 +2961,7 @@
<value>Recompensa de comissão diária resgatada</value>
</data>
<data name="WebDailyNoteHomeCoinRecoveryFormat" xml:space="preserve">
<value>Estará cheio {0} às {1:HH:mm}</value>
<value>Estará cheio em {0} {1:HH:mm}</value>
</data>
<data name="WebDailyNoteHomeLocked" xml:space="preserve">
<value>Bule de Relachá não desbloqueado</value>
@@ -3074,9 +3044,6 @@
<data name="WebGachaConfigTypeAvatarEventWish2" xml:space="preserve">
<value>Evento de oração de personagem-2</value>
</data>
<data name="WebGachaConfigTypeChronicledWish" xml:space="preserve">
<value>集录祈愿</value>
</data>
<data name="WebGachaConfigTypeNoviceWish" xml:space="preserve">
<value>Oração de Novatos</value>
</data>

View File

@@ -189,15 +189,6 @@
<data name="CoreWindowHotkeyCombinationRegisterFailed" xml:space="preserve">
<value>[{0}] 热键 [{1}] 注册失败</value>
</data>
<data name="CoreWindowThemeDark" xml:space="preserve">
<value>深色</value>
</data>
<data name="CoreWindowThemeLight" xml:space="preserve">
<value>浅色</value>
</data>
<data name="CoreWindowThemeSystem" xml:space="preserve">
<value>跟随系统</value>
</data>
<data name="FilePickerExportCommit" xml:space="preserve">
<value>导出</value>
</data>
@@ -210,10 +201,10 @@
<data name="LaunchGameTitle" xml:space="preserve">
<value>选择账号并启动</value>
</data>
<data name="MetadataSpecialNameMetaAvatarSexProInfoPronounBoyGirlD" xml:space="preserve">
<data name="MetadataSpecialNameMateAvatarSexProInfoPronounBoyGirlD" xml:space="preserve">
<value>&lt;color=#1E90FF&gt;王子&lt;/color&gt;/&lt;color=#FFB6C1&gt;公主&lt;/color&gt;</value>
</data>
<data name="MetadataSpecialNameMetaAvatarSexProInfoPronounBoyGirlFirst" xml:space="preserve">
<data name="MetadataSpecialNameMateAvatarSexProInfoPronounBoyGirlFirst" xml:space="preserve">
<value>&lt;color=#1E90FF&gt;我&lt;/color&gt;/&lt;color=#FFB6C1&gt;我&lt;/color&gt;</value>
</data>
<data name="MetadataSpecialNameNickname" xml:space="preserve">
@@ -869,9 +860,6 @@
<data name="ServiceGachaLogFactoryAvatarWishName" xml:space="preserve">
<value>角色活动</value>
</data>
<data name="ServiceGachaLogFactoryChronicledWishName" xml:space="preserve">
<value>集录祈愿</value>
</data>
<data name="ServiceGachaLogFactoryPermanentWishName" xml:space="preserve">
<value>奔行世间</value>
</data>
@@ -2229,7 +2217,7 @@
<value>在游戏启动后尝试启动并使用 Better GI 进行自动化任务</value>
</data>
<data name="ViewPageLaunchGameBetterGIHeader" xml:space="preserve">
<value>自动化任务</value>
<value>Better GI</value>
</data>
<data name="ViewPageLaunchGameCommonHeader" xml:space="preserve">
<value>常规</value>
@@ -2247,7 +2235,7 @@
<value>文件</value>
</data>
<data name="ViewPageLaunchGameInterProcessHeader" xml:space="preserve">
<value>进程联动</value>
<value>进程</value>
</data>
<data name="ViewPageLaunchGameMonitorsDescription" xml:space="preserve">
<value>在指定的显示器上运行</value>
@@ -2381,9 +2369,6 @@
<data name="ViewPageSettingBackgroundImageHeader" xml:space="preserve">
<value>背景图片</value>
</data>
<data name="ViewPageSettingBackgroundImageLocalFolderCopyrightHeader" xml:space="preserve">
<value>当前背景为本地图片,如遇版权问题由用户个人负责</value>
</data>
<data name="ViewPageSettingCacheFolderDescription" xml:space="preserve">
<value>图片缓存 在此处存放</value>
</data>
@@ -2639,12 +2624,6 @@
<data name="ViewPageSettingStoreReviewNavigate" xml:space="preserve">
<value>评价软件</value>
</data>
<data name="ViewPageSettingThemeDescription" xml:space="preserve">
<value>更改窗体的颜色主题</value>
</data>
<data name="ViewPageSettingThemeHeader" xml:space="preserve">
<value>颜色主题</value>
</data>
<data name="ViewPageSettingTranslateNavigate" xml:space="preserve">
<value>贡献翻译</value>
</data>
@@ -2654,12 +2633,6 @@
<data name="ViewPageSettingWebview2Header" xml:space="preserve">
<value>Webview2 运行时</value>
</data>
<data name="ViewPageSpiralAbyssTeamAppearanceDownHeader" xml:space="preserve">
<value>下半</value>
</data>
<data name="ViewPageSpiralAbyssTeamAppearanceUpHeader" xml:space="preserve">
<value>上半</value>
</data>
<data name="ViewPageWiKiAvatarArtifactSetCombinationHeader" xml:space="preserve">
<value>搭配圣遗物</value>
</data>
@@ -2922,7 +2895,7 @@
<value>〓活动时间〓.*?\d\.\d版本期间持续开放</value>
</data>
<data name="WebAnnouncementMatchTransientActivityTime" xml:space="preserve">
<value>(?:〓活动时间〓|祈愿时间|【上架时间】|〓折扣时间〓).*?(\d\.\d版本更新后).*?~.*?&amp;lt;t class="t_(?:gl|lc)".*?&amp;gt;(.*?)&amp;lt;/t&amp;gt;</value>
<value>(?:〓活动时间〓|祈愿时间|【上架时间】).*?(\d\.\d版本更新后).*?~.*?&amp;lt;t class="t_(?:gl|lc)".*?&amp;gt;(.*?)&amp;lt;/t&amp;gt;</value>
</data>
<data name="WebAnnouncementMatchVersionUpdateTime" xml:space="preserve">
<value>〓更新时间〓.+?&amp;lt;t class=\"t_(?:gl|lc)\".*?&amp;gt;(.*?)&amp;lt;/t&amp;gt;</value>
@@ -3074,9 +3047,6 @@
<data name="WebGachaConfigTypeAvatarEventWish2" xml:space="preserve">
<value>角色活动祈愿-2</value>
</data>
<data name="WebGachaConfigTypeChronicledWish" xml:space="preserve">
<value>集录祈愿</value>
</data>
<data name="WebGachaConfigTypeNoviceWish" xml:space="preserve">
<value>新手祈愿</value>
</data>

View File

@@ -60,45 +60,45 @@
: and then encoded with base64 encoding.
-->
<xsd:schema xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" id="root">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace"/>
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0"/>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string"/>
<xsd:attribute name="type" type="xsd:string"/>
<xsd:attribute name="mimetype" type="xsd:string"/>
<xsd:attribute ref="xml:space"/>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string"/>
<xsd:attribute name="name" type="xsd:string"/>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2"/>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1"/>
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3"/>
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4"/>
<xsd:attribute ref="xml:space"/>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required"/>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
@@ -186,18 +186,6 @@
<data name="CoreWebView2HelperVersionUndetected" xml:space="preserve">
<value>Среда выполнения WebView2 не обнаружена</value>
</data>
<data name="CoreWindowHotkeyCombinationRegisterFailed" xml:space="preserve">
<value>[{0}] 热键 [{1}] 注册失败</value>
</data>
<data name="CoreWindowThemeDark" xml:space="preserve">
<value>深色</value>
</data>
<data name="CoreWindowThemeLight" xml:space="preserve">
<value>浅色</value>
</data>
<data name="CoreWindowThemeSystem" xml:space="preserve">
<value>跟随系统</value>
</data>
<data name="FilePickerExportCommit" xml:space="preserve">
<value>Экспорт</value>
</data>
@@ -210,10 +198,10 @@
<data name="LaunchGameTitle" xml:space="preserve">
<value>Выберите учетную запись для запуска</value>
</data>
<data name="MetadataSpecialNameMetaAvatarSexProInfoPronounBoyGirlD" xml:space="preserve">
<data name="MetadataSpecialNameMateAvatarSexProInfoPronounBoyGirlD" xml:space="preserve">
<value>&lt;color=#1E90FF&gt;Принц&lt;/color&gt;/&lt;color=#FFB6C1&gt;Принцесса&lt;/color&gt;</value>
</data>
<data name="MetadataSpecialNameMetaAvatarSexProInfoPronounBoyGirlFirst" xml:space="preserve">
<data name="MetadataSpecialNameMateAvatarSexProInfoPronounBoyGirlFirst" xml:space="preserve">
<value>&lt;color=#1E90FF&gt;I&lt;/color&gt;/&lt;color=#FFB6C1&gt;I&lt;/color&gt;</value>
</data>
<data name="MetadataSpecialNameNickname" xml:space="preserve">
@@ -869,9 +857,6 @@
<data name="ServiceGachaLogFactoryAvatarWishName" xml:space="preserve">
<value>角色活动</value>
</data>
<data name="ServiceGachaLogFactoryChronicledWishName" xml:space="preserve">
<value>集录祈愿</value>
</data>
<data name="ServiceGachaLogFactoryPermanentWishName" xml:space="preserve">
<value>奔行世间</value>
</data>
@@ -2229,7 +2214,7 @@
<value>在游戏启动后尝试启动并使用 Better GI 进行自动化任务</value>
</data>
<data name="ViewPageLaunchGameBetterGIHeader" xml:space="preserve">
<value>自动化任务</value>
<value>Better GI</value>
</data>
<data name="ViewPageLaunchGameCommonHeader" xml:space="preserve">
<value>常规</value>
@@ -2381,9 +2366,6 @@
<data name="ViewPageSettingBackgroundImageHeader" xml:space="preserve">
<value>背景图片</value>
</data>
<data name="ViewPageSettingBackgroundImageLocalFolderCopyrightHeader" xml:space="preserve">
<value>当前背景为本地图片,如遇版权问题由用户个人负责</value>
</data>
<data name="ViewPageSettingCacheFolderDescription" xml:space="preserve">
<value>图片缓存 在此处存放</value>
</data>
@@ -2639,12 +2621,6 @@
<data name="ViewPageSettingStoreReviewNavigate" xml:space="preserve">
<value>评价软件</value>
</data>
<data name="ViewPageSettingThemeDescription" xml:space="preserve">
<value>更改窗体的颜色主题</value>
</data>
<data name="ViewPageSettingThemeHeader" xml:space="preserve">
<value>颜色主题</value>
</data>
<data name="ViewPageSettingTranslateNavigate" xml:space="preserve">
<value>贡献翻译</value>
</data>
@@ -2654,12 +2630,6 @@
<data name="ViewPageSettingWebview2Header" xml:space="preserve">
<value>Webview2 Runtime</value>
</data>
<data name="ViewPageSpiralAbyssTeamAppearanceDownHeader" xml:space="preserve">
<value>下半</value>
</data>
<data name="ViewPageSpiralAbyssTeamAppearanceUpHeader" xml:space="preserve">
<value>上半</value>
</data>
<data name="ViewPageWiKiAvatarArtifactSetCombinationHeader" xml:space="preserve">
<value>搭配圣遗物</value>
</data>
@@ -3074,9 +3044,6 @@
<data name="WebGachaConfigTypeAvatarEventWish2" xml:space="preserve">
<value>Молитва события персонажа - 2</value>
</data>
<data name="WebGachaConfigTypeChronicledWish" xml:space="preserve">
<value>集录祈愿</value>
</data>
<data name="WebGachaConfigTypeNoviceWish" xml:space="preserve">
<value>Молитва новичка</value>
</data>

View File

@@ -60,45 +60,45 @@
: and then encoded with base64 encoding.
-->
<xsd:schema xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" id="root">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace"/>
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0"/>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string"/>
<xsd:attribute name="type" type="xsd:string"/>
<xsd:attribute name="mimetype" type="xsd:string"/>
<xsd:attribute ref="xml:space"/>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string"/>
<xsd:attribute name="name" type="xsd:string"/>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2"/>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1"/>
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3"/>
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4"/>
<xsd:attribute ref="xml:space"/>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required"/>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
@@ -186,18 +186,6 @@
<data name="CoreWebView2HelperVersionUndetected" xml:space="preserve">
<value>未檢測到 WebView2 運行時</value>
</data>
<data name="CoreWindowHotkeyCombinationRegisterFailed" xml:space="preserve">
<value>[{0}] 鍵盤快速鍵 [{1}] 註冊失敗</value>
</data>
<data name="CoreWindowThemeDark" xml:space="preserve">
<value>深色</value>
</data>
<data name="CoreWindowThemeLight" xml:space="preserve">
<value>淺色</value>
</data>
<data name="CoreWindowThemeSystem" xml:space="preserve">
<value>跟隨系統</value>
</data>
<data name="FilePickerExportCommit" xml:space="preserve">
<value>匯出</value>
</data>
@@ -210,14 +198,14 @@
<data name="LaunchGameTitle" xml:space="preserve">
<value>選擇帳號並啟動</value>
</data>
<data name="MetadataSpecialNameMetaAvatarSexProInfoPronounBoyGirlD" xml:space="preserve">
<data name="MetadataSpecialNameMateAvatarSexProInfoPronounBoyGirlD" xml:space="preserve">
<value>&lt;color=#1E90FF&gt;王子&lt;/color&gt;/&lt;color=#FFB6C1&gt;公主&lt;/color&gt;</value>
</data>
<data name="MetadataSpecialNameMetaAvatarSexProInfoPronounBoyGirlFirst" xml:space="preserve">
<data name="MetadataSpecialNameMateAvatarSexProInfoPronounBoyGirlFirst" xml:space="preserve">
<value>&lt;color=#1E90FF&gt;我&lt;/color&gt;/&lt;color=#FFB6C1&gt;我&lt;/color&gt;</value>
</data>
<data name="MetadataSpecialNameNickname" xml:space="preserve">
<value>旅行者</value>
<value>旅</value>
</data>
<data name="MetadataSpecialNamePlayerAvatarSexProInfoPronounHeShe" xml:space="preserve">
<value>&lt;color=#1E90FF&gt;他&lt;/color&gt;/&lt;color=#FFB6C1&gt;她&lt;/color&gt;</value>
@@ -525,7 +513,7 @@
<value>第四波:擊敗所有怪物,下一波才會出現</value>
</data>
<data name="ModelMetadataTowerWaveTypeWave99999" xml:space="preserve">
<value>維持場上 4 個盜寶團怪物,擊殺後立即替換,總數 12 </value>
<value>维持场上 4 个盗宝团怪物,击杀后立即替换,总数 12 </value>
</data>
<data name="ModelNameValueDefaultDescription" xml:space="preserve">
<value>請更新角色櫥窗數據</value>
@@ -780,19 +768,19 @@
<value>角色櫥窗:{0:MM-dd HH:mm}</value>
</data>
<data name="ServiceBackgroundImageTypeBing" xml:space="preserve">
<value>Bing每日一</value>
<value>必应每日一</value>
</data>
<data name="ServiceBackgroundImageTypeDaily" xml:space="preserve">
<value>胡桃每日一</value>
<value>胡桃每日一</value>
</data>
<data name="ServiceBackgroundImageTypeLauncher" xml:space="preserve">
<value>官方啟動器壁</value>
<value>官方启动器壁</value>
</data>
<data name="ServiceBackgroundImageTypeLocalFolder" xml:space="preserve">
<value>本地隨機圖片</value>
<value>本地随机图片</value>
</data>
<data name="ServiceBackgroundImageTypeNone" xml:space="preserve">
<value>背景片</value>
<value>背景片</value>
</data>
<data name="ServiceCultivationProjectCurrentUserdataCourrpted" xml:space="preserve">
<value>保存養成計劃狀態失敗</value>
@@ -852,7 +840,7 @@
<value>參量質變儀已準備完成</value>
</data>
<data name="ServiceDiscordActivityElevationRequiredHint" xml:space="preserve">
<value>限不足,將無法為您設定 Discord Activity 狀態</value>
<value>限不足,将无法为您设置 Discord Activity 状态</value>
</data>
<data name="ServiceDiscordGameActivityDetails" xml:space="preserve">
<value>正在提瓦特大陸中探索</value>
@@ -869,9 +857,6 @@
<data name="ServiceGachaLogFactoryAvatarWishName" xml:space="preserve">
<value>角色活動</value>
</data>
<data name="ServiceGachaLogFactoryChronicledWishName" xml:space="preserve">
<value>集錄祈願</value>
</data>
<data name="ServiceGachaLogFactoryPermanentWishName" xml:space="preserve">
<value>奔行世間</value>
</data>
@@ -1239,7 +1224,7 @@
<value>即時便箋 Webhook Url</value>
</data>
<data name="ViewDialogFeedbackEnableLoopbackContent" xml:space="preserve">
<value>解除限制後需使用其他工具恢限制</value>
<value>解除限制后需要使用其他工具恢限制</value>
</data>
<data name="ViewDialogFeedbackEnableLoopbackTitle" xml:space="preserve">
<value>是否解除 Loopback 限制</value>
@@ -1425,7 +1410,7 @@
<value>啟動遊戲</value>
</data>
<data name="ViewListViewDragElevatedHint" xml:space="preserve">
<value>管理模式下法拖排序</value>
<value>管理模式下法拖排序</value>
</data>
<data name="ViewModelAchievementArchiveAdded" xml:space="preserve">
<value>存檔 [{0}] 添加成功</value>
@@ -1707,7 +1692,7 @@
<value>完成</value>
</data>
<data name="ViewModelWelcomeDownloadSummaryContentTypeNotMatch" xml:space="preserve">
<value>回應內容不是有效的檔案位元組</value>
<value>响应内容不是有效的文件字节流</value>
</data>
<data name="ViewModelWelcomeDownloadSummaryDefault" xml:space="preserve">
<value>待處理</value>
@@ -1926,16 +1911,16 @@
<value>常用連結</value>
</data>
<data name="ViewPageFeedbackCurrentProxyHeader" xml:space="preserve">
<value>前代理</value>
<value>前代理</value>
</data>
<data name="ViewPageFeedbackCurrentProxyNoProxyDescription" xml:space="preserve">
<value>代理</value>
<value>代理</value>
</data>
<data name="ViewPageFeedbackEnableLoopbackEnabledDescription" xml:space="preserve">
<value>已解除</value>
</data>
<data name="ViewPageFeedbackEnableLoopbackHeader" xml:space="preserve">
<value>是否解除 Loopback 限制</value>
<value>解除 Loopback 限制</value>
</data>
<data name="ViewPageFeedbackEngageWithUsDescription" xml:space="preserve">
<value>與我們密切聯繫</value>
@@ -2226,10 +2211,10 @@
<value>啟動參數</value>
</data>
<data name="ViewPageLaunchGameBetterGIDescription" xml:space="preserve">
<value>在遊戲啟動後嘗試啟動並使用 Better GI 行自化任</value>
<value>在游戏启动后尝试启动并使用 Better GI 行自化任</value>
</data>
<data name="ViewPageLaunchGameBetterGIHeader" xml:space="preserve">
<value>自動化任務</value>
<value>Better GI</value>
</data>
<data name="ViewPageLaunchGameCommonHeader" xml:space="preserve">
<value>一般</value>
@@ -2247,7 +2232,7 @@
<value>檔案</value>
</data>
<data name="ViewPageLaunchGameInterProcessHeader" xml:space="preserve">
<value>進程聯動</value>
<value>行程間</value>
</data>
<data name="ViewPageLaunchGameMonitorsDescription" xml:space="preserve">
<value>在指定的屏幕上運行</value>
@@ -2373,16 +2358,13 @@
<value>背景材質</value>
</data>
<data name="ViewPageSettingBackgroundImageCopyrightHeader" xml:space="preserve">
<value>片版權訊息</value>
<value>片版权信息</value>
</data>
<data name="ViewPageSettingBackgroundImageDescription" xml:space="preserve">
<value>更改窗的背景圖片來源,重胡桃以快生效</value>
<value>更改窗的背景图片来源,重胡桃以快生效</value>
</data>
<data name="ViewPageSettingBackgroundImageHeader" xml:space="preserve">
<value>背景片</value>
</data>
<data name="ViewPageSettingBackgroundImageLocalFolderCopyrightHeader" xml:space="preserve">
<value>當前背景為本地圖片,如遇版權問題由用戶個人負責</value>
<value>背景片</value>
</data>
<data name="ViewPageSettingCacheFolderDescription" xml:space="preserve">
<value>圖片暫存存放在此</value>
@@ -2391,7 +2373,7 @@
<value>暫存檔案夾</value>
</data>
<data name="ViewPageSettingCardHutaoPassportHeader" xml:space="preserve">
<value>胡桃通行</value>
<value>胡桃通行</value>
</data>
<data name="ViewPageSettingCopyDeviceIdAction" xml:space="preserve">
<value>複製</value>
@@ -2586,10 +2568,10 @@
<value>前往官網</value>
</data>
<data name="ViewPageSettingOpenBackgroundImageFolderDescription" xml:space="preserve">
<value>自定背景片,支 bmp / gif / ico / jpg / jpeg / png / tiff / webp 格式</value>
<value>自定背景片,支 bmp / gif / ico / jpg / jpeg / png / tiff / webp 格式</value>
</data>
<data name="ViewPageSettingOpenBackgroundImageFolderHeader" xml:space="preserve">
<value>打背景圖片資料夾</value>
<value>打背景图片文件夹</value>
</data>
<data name="ViewPageSettingResetAction" xml:space="preserve">
<value>重設</value>
@@ -2601,10 +2583,10 @@
<value>重設圖片資源</value>
</data>
<data name="ViewPageSettingsAdvancedOptionsLaunchUnlockFpsDescription" xml:space="preserve">
<value>在啟動遊戲頁面的程序部分加入解鎖幀率限制選項</value>
<value>在启动游戏页面的进程部分加入解锁帧率限制选项</value>
</data>
<data name="ViewPageSettingsAdvancedOptionsLaunchUnlockFpsHeader" xml:space="preserve">
<value>啟動遊戲-解鎖幀率限制</value>
<value>启动游戏-解锁帧率限制</value>
</data>
<data name="ViewPageSettingSetDataFolderDescription" xml:space="preserve">
<value>更改目錄后需要手動移動目錄内的數據,否則會重新創建用戶數據</value>
@@ -2639,12 +2621,6 @@
<data name="ViewPageSettingStoreReviewNavigate" xml:space="preserve">
<value>評價軟體</value>
</data>
<data name="ViewPageSettingThemeDescription" xml:space="preserve">
<value>更改窗體的顏色主題</value>
</data>
<data name="ViewPageSettingThemeHeader" xml:space="preserve">
<value>顏色主題</value>
</data>
<data name="ViewPageSettingTranslateNavigate" xml:space="preserve">
<value>貢獻翻譯</value>
</data>
@@ -2654,12 +2630,6 @@
<data name="ViewPageSettingWebview2Header" xml:space="preserve">
<value>WebView 2 運行時</value>
</data>
<data name="ViewPageSpiralAbyssTeamAppearanceDownHeader" xml:space="preserve">
<value>下半</value>
</data>
<data name="ViewPageSpiralAbyssTeamAppearanceUpHeader" xml:space="preserve">
<value>上半</value>
</data>
<data name="ViewPageWiKiAvatarArtifactSetCombinationHeader" xml:space="preserve">
<value>搭配聖遺物</value>
</data>
@@ -2922,7 +2892,7 @@
<value>〓活動時間〓.*?\d\.\d版本期間持續開放</value>
</data>
<data name="WebAnnouncementMatchTransientActivityTime" xml:space="preserve">
<value>(?:〓活動時間〓|祈願時間|【上架時間】|〓折扣時間〓).*?(\d\.\d版本更新後).*?~.*?&amp;lt;t class="t_(?:gl|lc)".*?&amp;gt;(.*?)&amp;lt;/t&amp;gt;</value>
<value>(?:〓活動時間〓|祈願時間|【上架時間】).*?(\d\.\d版本更新後).*?~.*?&amp;lt;t class="t_(?:gl|lc)".*?&amp;gt;(.*?)&amp;lt;/t&amp;gt;</value>
</data>
<data name="WebAnnouncementMatchVersionUpdateTime" xml:space="preserve">
<value>〓更新時間〓.+?&amp;lt;t class=\"t_(?:gl|lc)\".*?&amp;gt;(.*?)&amp;lt;/t&amp;gt;</value>
@@ -3074,9 +3044,6 @@
<data name="WebGachaConfigTypeAvatarEventWish2" xml:space="preserve">
<value>角色活動祈願-2</value>
</data>
<data name="WebGachaConfigTypeChronicledWish" xml:space="preserve">
<value>集錄祈願</value>
</data>
<data name="WebGachaConfigTypeNoviceWish" xml:space="preserve">
<value>新手祈願</value>
</data>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.0 KiB

After

Width:  |  Height:  |  Size: 5.5 KiB

View File

@@ -85,13 +85,8 @@ internal sealed partial class AnnouncementService : IAnnouncementService
{
foreach (ref readonly Announcement item in CollectionsMarshal.AsSpan(listWrapper.List))
{
item.Subtitle = new StringBuilder(item.Subtitle)
.Replace("\r<br>", string.Empty)
.Replace("<br />", string.Empty)
.ToString();
item.Content = AnnouncementRegex
.XmlTimeTagRegex()
.Replace(item.Content, x => x.Groups[1].Value);
item.Subtitle = new StringBuilder(item.Subtitle).Replace("\r<br>", string.Empty).ToString();
item.Content = AnnouncementRegex.XmlTimeTagRegex().Replace(item.Content, x => x.Groups[1].Value);
}
}
}

View File

@@ -1,7 +1,6 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Microsoft.UI.Xaml;
using Snap.Hutao.Core.Windowing;
using Snap.Hutao.Model;
using Snap.Hutao.Model.Entity;
@@ -17,7 +16,6 @@ internal sealed partial class AppOptions : DbStoreOptions
{
private bool? isEmptyHistoryWishVisible;
private BackdropType? backdropType;
private ElementTheme? elementTheme;
private BackgroundImageType? backgroundImageType;
private Region? region;
private string? geetestCustomCompositeUrl;
@@ -36,19 +34,6 @@ internal sealed partial class AppOptions : DbStoreOptions
set => SetOption(ref backdropType, SettingEntry.SystemBackdropType, value, EnumToStringOrEmpty);
}
public Lazy<List<NameValue<ElementTheme>>> LazyElementThemes { get; } = new(() =>
[
new(SH.CoreWindowThemeLight, ElementTheme.Light),
new(SH.CoreWindowThemeDark, ElementTheme.Dark),
new(SH.CoreWindowThemeSystem, ElementTheme.Default),
]);
public ElementTheme ElementTheme
{
get => GetOption(ref elementTheme, SettingEntry.ElementTheme, EnumParse<ElementTheme>, ElementTheme.Default).Value;
set => SetOption(ref elementTheme, SettingEntry.ElementTheme, value, EnumToStringOrEmpty);
}
public List<NameValue<BackgroundImageType>> BackgroundImageTypes { get; } = CollectionsNameValue.FromEnum<BackgroundImageType>(type => type.GetLocalizedDescription());
public BackgroundImageType BackgroundImageType

View File

@@ -28,16 +28,16 @@ internal sealed partial class BackgroundImageService : IBackgroundImageService
private HashSet<string> currentBackgroundPathSet;
public async ValueTask<ValueResult<bool, BackgroundImage?>> GetNextBackgroundImageAsync(BackgroundImage? previous)
public async ValueTask<ValueResult<bool, BackgroundImage>> GetNextBackgroundImageAsync(BackgroundImage? previous)
{
HashSet<string> backgroundSet = await SkipOrInitBackgroundAsync().ConfigureAwait(false);
if (backgroundSet.Count <= 0)
{
return new(true, default!);
return new(false, default!);
}
string path = System.Random.Shared.GetItems([.. backgroundSet], 1)[0];
string path = System.Random.Shared.GetItems([..backgroundSet], 1)[0];
backgroundSet.Remove(path);
if (string.Equals(path, previous?.Path, StringComparison.OrdinalIgnoreCase))
@@ -109,9 +109,6 @@ internal sealed partial class BackgroundImageService : IBackgroundImageService
case BackgroundImageType.HutaoOfficialLauncher:
await SetCurrentBackgroundPathSetAsync(client => client.GetLauncherWallpaperAsync()).ConfigureAwait(false);
break;
default:
currentBackgroundPathSet = [];
break;
}
currentBackgroundPathSet ??= [];

View File

@@ -5,5 +5,5 @@ namespace Snap.Hutao.Service.BackgroundImage;
internal interface IBackgroundImageService
{
ValueTask<ValueResult<bool, BackgroundImage?>> GetNextBackgroundImageAsync(BackgroundImage? previous);
ValueTask<ValueResult<bool, BackgroundImage>> GetNextBackgroundImageAsync(BackgroundImage? previous);
}

View File

@@ -39,7 +39,7 @@ internal sealed partial class CultivationService : ICultivationService
List<InventoryItem> entities = cultivationDbService.GetInventoryItemListByProjectId(projectId);
List<InventoryItemView> results = [];
foreach (Material meta in context.EnumerateInventoryMaterial())
foreach (Material meta in context.EnumerateInventroyMaterial())
{
InventoryItem entity = entities.SingleOrDefault(e => e.ItemId == meta.Id) ?? InventoryItem.From(projectId, meta.Id);
results.Add(new(entity, meta, saveCommand));

View File

@@ -41,33 +41,39 @@ internal sealed partial class DiscordService : IDiscordService, IDisposable
private bool IsSupported()
{
// Actually requires a discord client to be running on Windows platform.
// If not, discord core creation code will throw.
Process[] discordProcesses = Process.GetProcessesByName("Discord");
if (discordProcesses.Length <= 0)
try
{
return false;
}
// Actually requires a discord client to be running on Windows platform.
// If not, discord core creation code will throw.
Process[] discordProcesses = Process.GetProcessesByName("Discord");
foreach (Process process in discordProcesses)
{
try
if (discordProcesses.Length <= 0)
{
_ = process.Handle;
}
catch (Exception)
{
if (!isInitialized)
{
isInitialized = true;
infoBarService.Warning(SH.ServiceDiscordActivityElevationRequiredHint);
}
return false;
}
}
return true;
foreach (Process process in discordProcesses)
{
try
{
_ = process.Handle;
}
catch (Exception)
{
if (!isInitialized)
{
infoBarService.Warning(SH.ServiceDiscordActivityElevationRequiredHint);
}
return false;
}
}
return true;
}
finally
{
isInitialized = true;
}
}
}

View File

@@ -0,0 +1,41 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Web.Hoyolab.Hk4e.Event.GachaInfo;
using System.Collections.Frozen;
using System.Runtime.CompilerServices;
namespace Snap.Hutao.Service.GachaLog.Factory;
/// <summary>
/// 祈愿配置类型比较器
/// </summary>
internal sealed class GachaConfigTypeComparer : IComparer<GachaConfigType>
{
private static readonly Lazy<GachaConfigTypeComparer> LazyShared = new(() => new());
private static readonly FrozenDictionary<GachaConfigType, int> OrderMap = new Dictionary<GachaConfigType, int>()
{
[GachaConfigType.AvatarEventWish] = 0,
[GachaConfigType.AvatarEventWish2] = 1,
[GachaConfigType.WeaponEventWish] = 2,
[GachaConfigType.StandardWish] = 3,
[GachaConfigType.NoviceWish] = 4,
}.ToFrozenDictionary();
/// <summary>
/// 共享的比较器
/// </summary>
public static GachaConfigTypeComparer Shared { get => LazyShared.Value; }
/// <inheritdoc/>
public int Compare(GachaConfigType x, GachaConfigType y)
{
return OrderOf(x) - OrderOf(y);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static int OrderOf(GachaConfigType type)
{
return OrderMap.GetValueOrDefault(type, 0);
}
}

View File

@@ -3,7 +3,6 @@
using Snap.Hutao.Model.Metadata.Abstraction;
using Snap.Hutao.ViewModel.GachaLog;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Text;
using Windows.UI;
@@ -26,7 +25,7 @@ internal static class GachaStatisticsExtension
bool isPreviousUp = true;
// mark the IsGuarantee
foreach (ref readonly SummaryItem item in CollectionsMarshal.AsSpan(summaryItems))
foreach (SummaryItem item in summaryItems)
{
if (item.IsUp && (!isPreviousUp))
{
@@ -63,4 +62,4 @@ internal static class GachaStatisticsExtension
ReadOnlySpan<byte> codes = MD5.HashData(Encoding.UTF8.GetBytes(name));
return Color.FromArgb(255, codes.Slice(0, 5).Average(), codes.Slice(5, 5).Average(), codes.Slice(10, 5).Average());
}
}
}

View File

@@ -3,10 +3,10 @@
using Snap.Hutao.Core.ExceptionService;
using Snap.Hutao.Model.Intrinsic;
using Snap.Hutao.Model.Metadata;
using Snap.Hutao.Model.Metadata.Avatar;
using Snap.Hutao.Model.Metadata.Weapon;
using Snap.Hutao.Service.Metadata;
using Snap.Hutao.Service.Metadata.ContextAbstraction;
using Snap.Hutao.ViewModel.GachaLog;
using Snap.Hutao.Web.Hoyolab.Hk4e.Event.GachaInfo;
using Snap.Hutao.Web.Hutao.GachaLog;
@@ -31,8 +31,9 @@ internal sealed partial class GachaStatisticsFactory : IGachaStatisticsFactory
public async ValueTask<GachaStatistics> CreateAsync(List<Model.Entity.GachaItem> items, GachaLogServiceMetadataContext context)
{
await taskContext.SwitchToBackgroundAsync();
List<GachaEvent> gachaEvents = await metadataService.GetGachaEventListAsync().ConfigureAwait(false);
List<HistoryWishBuilder> historyWishBuilders = gachaEvents.SelectList(gachaEvent => new HistoryWishBuilder(gachaEvent, context));
List<HistoryWishBuilder> historyWishBuilders = context.GachaEvents.SelectList(gachaEvent => new HistoryWishBuilder(gachaEvent, context));
return CreateCore(taskContext, homaGachaLogClient, items, historyWishBuilders, context, options.IsEmptyHistoryWishVisible);
}
@@ -53,9 +54,6 @@ internal sealed partial class GachaStatisticsFactory : IGachaStatisticsFactory
TypedWishSummaryBuilderContext weaponContext = TypedWishSummaryBuilderContext.WeaponEventWish(taskContext, gachaLogClient);
TypedWishSummaryBuilder weaponWishBuilder = new(weaponContext);
TypedWishSummaryBuilderContext chronicledContext = TypedWishSummaryBuilderContext.ChronicledWish(taskContext, gachaLogClient);
TypedWishSummaryBuilder chronicledWishBuilder = new(chronicledContext);
Dictionary<Avatar, int> orangeAvatarCounter = [];
Dictionary<Avatar, int> purpleAvatarCounter = [];
Dictionary<Weapon, int> orangeWeaponCounter = [];
@@ -63,25 +61,24 @@ internal sealed partial class GachaStatisticsFactory : IGachaStatisticsFactory
Dictionary<Weapon, int> blueWeaponCounter = [];
// Pre group builders
Dictionary<GachaType, List<HistoryWishBuilder>> historyWishBuilderMap = historyWishBuilders
Dictionary<GachaConfigType, List<HistoryWishBuilder>> historyWishBuilderMap = historyWishBuilders
.GroupBy(b => b.ConfigType)
.ToDictionary(g => g.Key, g => g.ToList().SortBy(b => b.From));
// Items are ordered by precise time, first is oldest
// 'ref' is not allowed here because we have lambda below
foreach (ref readonly Model.Entity.GachaItem item in CollectionsMarshal.AsSpan(items))
foreach (Model.Entity.GachaItem item in CollectionsMarshal.AsSpan(items))
{
// Find target history wish to operate. // banner.From <= item.Time <= banner.To
Model.Entity.GachaItem pinned = item;
HistoryWishBuilder? targetHistoryWishBuilder = item.GachaType is not (GachaType.Standard or GachaType.NewBie)
? historyWishBuilderMap[item.GachaType].BinarySearch(banner => pinned.Time < banner.From ? -1 : pinned.Time > banner.To ? 1 : 0)
// Find target history wish to operate. // w.From <= item.Time <= w.To
HistoryWishBuilder? targetHistoryWishBuilder = item.GachaType is not (GachaConfigType.StandardWish or GachaConfigType.NoviceWish)
? historyWishBuilderMap[item.GachaType].BinarySearch(w => item.Time < w.From ? -1 : item.Time > w.To ? 1 : 0)
: default;
switch (item.ItemId.StringLength())
{
case 8U:
{
Avatar avatar = context.GetAvatar(item.ItemId);
Avatar avatar = context.IdAvatarMap[item.ItemId];
bool isUp = false;
switch (avatar.Quality)
@@ -101,7 +98,6 @@ internal sealed partial class GachaStatisticsFactory : IGachaStatisticsFactory
standardWishBuilder.Track(item, avatar, isUp);
avatarWishBuilder.Track(item, avatar, isUp);
weaponWishBuilder.Track(item, avatar, isUp);
chronicledWishBuilder.Track(item, avatar, isUp);
break;
}
@@ -131,18 +127,17 @@ internal sealed partial class GachaStatisticsFactory : IGachaStatisticsFactory
standardWishBuilder.Track(item, weapon, isUp);
avatarWishBuilder.Track(item, weapon, isUp);
weaponWishBuilder.Track(item, weapon, isUp);
chronicledWishBuilder.Track(item, weapon, isUp);
break;
}
default:
// ItemId string length not correct.
HutaoException.GachaStatisticsInvalidItemId(item.ItemId);
ThrowHelper.UserdataCorrupted(SH.FormatServiceGachaStatisticsFactoryItemIdInvalid(item.ItemId), default!);
break;
}
}
AsyncBarrier barrier = new(4);
AsyncBarrier barrier = new(3);
return new()
{
@@ -150,7 +145,7 @@ internal sealed partial class GachaStatisticsFactory : IGachaStatisticsFactory
HistoryWishes = historyWishBuilders
.Where(b => isEmptyHistoryWishVisible || (!b.IsEmpty))
.OrderByDescending(builder => builder.From)
.ThenBy(builder => builder.ConfigType, GachaTypeComparer.Shared)
.ThenBy(builder => builder.ConfigType, GachaConfigTypeComparer.Shared)
.Select(builder => builder.ToHistoryWish())
.ToList(),
@@ -167,7 +162,6 @@ internal sealed partial class GachaStatisticsFactory : IGachaStatisticsFactory
StandardWish = standardWishBuilder.ToTypedWishSummary(barrier),
AvatarWish = avatarWishBuilder.ToTypedWishSummary(barrier),
WeaponWish = weaponWishBuilder.ToTypedWishSummary(barrier),
ChronicledWish = chronicledWishBuilder.ToTypedWishSummary(barrier),
};
}
}

View File

@@ -62,29 +62,22 @@ internal sealed partial class GachaStatisticsSlimFactory : IGachaStatisticsSlimF
int weaponPurpleTracker = 0;
TypedWishSummarySlim weaponWish = new(SH.ServiceGachaLogFactoryWeaponWishName, 80, 10);
int chronicledOrangeTracker = 0;
int chronicledPurpleTracker = 0;
TypedWishSummarySlim chronicledWish = new(SH.ServiceGachaLogFactoryChronicledWishName, 90, 10);
// O(n) operation
foreach (ref readonly GachaItem item in CollectionsMarshal.AsSpan(items))
{
INameQuality nameQuality = context.GetNameQualityByItemId(item.ItemId);
switch (item.QueryType)
{
case GachaType.Standard:
case GachaConfigType.StandardWish:
Track(nameQuality, ref standardOrangeTracker, ref standardPurpleTracker);
break;
case GachaType.ActivityAvatar:
case GachaType.SpecialActivityAvatar:
case GachaConfigType.AvatarEventWish:
case GachaConfigType.AvatarEventWish2:
Track(nameQuality, ref avatarOrangeTracker, ref avatarPurpleTracker);
break;
case GachaType.ActivityWeapon:
case GachaConfigType.WeaponEventWish:
Track(nameQuality, ref weaponOrangeTracker, ref weaponPurpleTracker);
break;
case GachaType.ActivityCity:
Track(nameQuality, ref chronicledOrangeTracker, ref chronicledPurpleTracker);
break;
default:
break;
}
@@ -92,16 +85,11 @@ internal sealed partial class GachaStatisticsSlimFactory : IGachaStatisticsSlimF
standardWish.LastOrangePull = standardOrangeTracker;
standardWish.LastPurplePull = standardPurpleTracker;
avatarWish.LastOrangePull = avatarOrangeTracker;
avatarWish.LastPurplePull = avatarPurpleTracker;
weaponWish.LastOrangePull = weaponOrangeTracker;
weaponWish.LastPurplePull = weaponPurpleTracker;
chronicledWish.LastOrangePull = chronicledOrangeTracker;
chronicledWish.LastPurplePull = chronicledPurpleTracker;
return new()
{
Uid = uid,

View File

@@ -1,42 +0,0 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Web.Hoyolab.Hk4e.Event.GachaInfo;
using System.Collections.Frozen;
using System.Runtime.CompilerServices;
namespace Snap.Hutao.Service.GachaLog.Factory;
/// <summary>
/// 祈愿配置类型比较器
/// </summary>
internal sealed class GachaTypeComparer : IComparer<GachaType>
{
private static readonly Lazy<GachaTypeComparer> LazyShared = new(() => new());
private static readonly FrozenDictionary<GachaType, int> OrderMap = FrozenDictionary.ToFrozenDictionary(
[
KeyValuePair.Create(GachaType.ActivityAvatar, 0),
KeyValuePair.Create(GachaType.SpecialActivityAvatar, 1),
KeyValuePair.Create(GachaType.ActivityWeapon, 2),
KeyValuePair.Create(GachaType.ActivityCity, 3),
KeyValuePair.Create(GachaType.Standard, 4),
KeyValuePair.Create(GachaType.NewBie, 5),
]);
/// <summary>
/// 共享的比较器
/// </summary>
public static GachaTypeComparer Shared { get => LazyShared.Value; }
/// <inheritdoc/>
public int Compare(GachaType x, GachaType y)
{
return OrderOf(x) - OrderOf(y);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static int OrderOf(GachaType type)
{
return OrderMap.GetValueOrDefault(type, 0);
}
}

View File

@@ -29,6 +29,7 @@ internal sealed class HistoryWishBuilder
/// </summary>
/// <param name="gachaEvent">卡池配置</param>
/// <param name="context">祈愿记录上下文</param>
[SuppressMessage("", "SH002")]
public HistoryWishBuilder(GachaEvent gachaEvent, GachaLogServiceMetadataContext context)
{
this.gachaEvent = gachaEvent;
@@ -36,27 +37,21 @@ internal sealed class HistoryWishBuilder
switch (ConfigType)
{
case GachaType.ActivityAvatar or GachaType.SpecialActivityAvatar:
case GachaConfigType.AvatarEventWish or GachaConfigType.AvatarEventWish2:
orangeUpCounter = gachaEvent.UpOrangeList.Select(id => context.IdAvatarMap[id]).ToDictionary(a => (IStatisticsItemSource)a, a => 0);
purpleUpCounter = gachaEvent.UpPurpleList.Select(id => context.IdAvatarMap[id]).ToDictionary(a => (IStatisticsItemSource)a, a => 0);
break;
case GachaType.ActivityWeapon:
case GachaConfigType.WeaponEventWish:
orangeUpCounter = gachaEvent.UpOrangeList.Select(id => context.IdWeaponMap[id]).ToDictionary(w => (IStatisticsItemSource)w, w => 0);
purpleUpCounter = gachaEvent.UpPurpleList.Select(id => context.IdWeaponMap[id]).ToDictionary(w => (IStatisticsItemSource)w, w => 0);
break;
case GachaType.ActivityCity:
// Avatars are less than weapons, so we try to get the value from avatar map first
orangeUpCounter = gachaEvent.UpOrangeList.Select(id => (IStatisticsItemSource?)context.IdAvatarMap.GetValueOrDefault(id) ?? context.IdWeaponMap[id]).ToDictionary(c => c, c => 0);
purpleUpCounter = gachaEvent.UpPurpleList.Select(id => (IStatisticsItemSource?)context.IdAvatarMap.GetValueOrDefault(id) ?? context.IdWeaponMap[id]).ToDictionary(c => c, c => 0);
break;
}
}
/// <summary>
/// 祈愿配置类型
/// </summary>
public GachaType ConfigType { get; }
public GachaConfigType ConfigType { get; }
/// <inheritdoc cref="GachaEvent.From"/>
public DateTimeOffset From { get => gachaEvent.From; }
@@ -111,13 +106,13 @@ internal sealed class HistoryWishBuilder
{
HistoryWish historyWish = new()
{
// Base
// base
Name = gachaEvent.Name,
From = gachaEvent.From,
To = gachaEvent.To,
TotalCount = totalCountTracker,
// Fill
// fill
Version = gachaEvent.Version,
BannerImage = gachaEvent.Banner,
OrangeUpList = orangeUpCounter.ToStatisticsList(),

View File

@@ -5,7 +5,6 @@ using Snap.Hutao.Core.ExceptionService;
using Snap.Hutao.Model.Intrinsic;
using Snap.Hutao.Model.Metadata;
using Snap.Hutao.Model.Metadata.Abstraction;
using Snap.Hutao.Service.Metadata.ContextAbstraction;
using Snap.Hutao.ViewModel.GachaLog;
using Snap.Hutao.Web.Hoyolab.Hk4e.Event.GachaInfo;
using Snap.Hutao.Web.Hutao.GachaLog;
@@ -19,20 +18,18 @@ internal sealed class HutaoStatisticsFactory
private readonly GachaEvent avatarEvent;
private readonly GachaEvent avatarEvent2;
private readonly GachaEvent weaponEvent;
private readonly GachaEvent chronicledEvent;
public HutaoStatisticsFactory(in HutaoStatisticsFactoryMetadataContext context)
{
this.context = context;
// when in new verion
// TODO: when in new verion
// due to lack of newer metadata
// this can crash
DateTimeOffset now = DateTimeOffset.UtcNow;
avatarEvent = context.GachaEvents.Single(g => g.From < now && g.To > now && g.Type == GachaType.ActivityAvatar);
avatarEvent2 = context.GachaEvents.Single(g => g.From < now && g.To > now && g.Type == GachaType.SpecialActivityAvatar);
weaponEvent = context.GachaEvents.Single(g => g.From < now && g.To > now && g.Type == GachaType.ActivityWeapon);
chronicledEvent = context.GachaEvents.Single(g => g.From < now && g.To > now && g.Type == GachaType.ActivityCity);
avatarEvent = context.GachaEvents.Single(g => g.From < now && g.To > now && g.Type == GachaConfigType.AvatarEventWish);
avatarEvent2 = context.GachaEvents.Single(g => g.From < now && g.To > now && g.Type == GachaConfigType.AvatarEventWish2);
weaponEvent = context.GachaEvents.Single(g => g.From < now && g.To > now && g.Type == GachaConfigType.WeaponEventWish);
}
public HutaoStatistics Create(GachaEventStatistics raw)
@@ -41,8 +38,7 @@ internal sealed class HutaoStatisticsFactory
{
AvatarEvent = CreateWishSummary(avatarEvent, raw.AvatarEvent),
AvatarEvent2 = CreateWishSummary(avatarEvent2, raw.AvatarEvent2),
WeaponEvent = CreateWishSummary(weaponEvent, raw.WeaponEvent),
Chronicled = CreateWishSummary(chronicledEvent, raw.Chronicled),
WeaponWish = CreateWishSummary(weaponEvent, raw.WeaponEvent),
};
}
@@ -57,13 +53,12 @@ internal sealed class HutaoStatisticsFactory
{
IStatisticsItemSource source = item.Item.StringLength() switch
{
8U => context.GetAvatar(item.Item),
5U => context.GetWeapon(item.Item),
_ => throw HutaoException.GachaStatisticsInvalidItemId(item.Item),
8U => context.IdAvatarMap[item.Item],
5U => context.IdWeaponMap[item.Item],
_ => throw ThrowHelper.UserdataCorrupted(SH.FormatServiceGachaStatisticsFactoryItemIdInvalid(item.Item), default!),
};
StatisticsItem statisticsItem = source.ToStatisticsItem(unchecked((int)item.Count));
// Put UP items to a separate list
if (gachaEvent.UpOrangeList.Contains(item.Item) || gachaEvent.UpPurpleList.Contains(item.Item))
{
upItems.Add(statisticsItem);

View File

@@ -5,18 +5,19 @@ using Snap.Hutao.Model.Metadata;
using Snap.Hutao.Model.Metadata.Avatar;
using Snap.Hutao.Model.Metadata.Weapon;
using Snap.Hutao.Model.Primitive;
using Snap.Hutao.Service.Metadata.ContextAbstraction;
namespace Snap.Hutao.Service.GachaLog.Factory;
internal sealed class HutaoStatisticsFactoryMetadataContext : IMetadataContext,
IMetadataDictionaryIdAvatarSource,
IMetadataDictionaryIdWeaponSource,
IMetadataListGachaEventSource
internal readonly struct HutaoStatisticsFactoryMetadataContext
{
public Dictionary<AvatarId, Avatar> IdAvatarMap { get; set; } = default!;
public readonly Dictionary<AvatarId, Avatar> IdAvatarMap;
public readonly Dictionary<WeaponId, Weapon> IdWeaponMap;
public readonly List<GachaEvent> GachaEvents;
public Dictionary<WeaponId, Weapon> IdWeaponMap { get; set; } = default!;
public List<GachaEvent> GachaEvents { get; set; } = default!;
public HutaoStatisticsFactoryMetadataContext(Dictionary<AvatarId, Avatar> idAvatarMap, Dictionary<WeaponId, Weapon> idWeaponMap, List<GachaEvent> gachaEvents)
{
IdAvatarMap = idAvatarMap;
IdWeaponMap = idWeaponMap;
GachaEvents = gachaEvents;
}
}

View File

@@ -38,10 +38,6 @@ internal sealed class PullPrediction
typedWishSummary.PredictedPullLeftToOrange = result.PredictedPullLeftToOrange;
typedWishSummary.IsPredictPullAvailable = true;
}
else
{
await barrier.SignalAndWaitAsync().ConfigureAwait(false);
}
}
private static PredictResult PredictCore(List<PullCount> distribution, TypedWishSummary typedWishSummary)

View File

@@ -5,6 +5,7 @@ using Snap.Hutao.Model.Entity;
using Snap.Hutao.Model.Intrinsic;
using Snap.Hutao.Model.Metadata.Abstraction;
using Snap.Hutao.ViewModel.GachaLog;
using Snap.Hutao.Web.Hoyolab.Hk4e.Event.GachaInfo;
namespace Snap.Hutao.Service.GachaLog.Factory;
@@ -14,6 +15,21 @@ namespace Snap.Hutao.Service.GachaLog.Factory;
[HighQuality]
internal sealed class TypedWishSummaryBuilder
{
/// <summary>
/// 常驻祈愿
/// </summary>
public static readonly Func<GachaConfigType, bool> IsStandardWish = type => type is GachaConfigType.StandardWish;
/// <summary>
/// 角色活动
/// </summary>
public static readonly Func<GachaConfigType, bool> IsAvatarEventWish = type => type is GachaConfigType.AvatarEventWish or GachaConfigType.AvatarEventWish2;
/// <summary>
/// 武器活动
/// </summary>
public static readonly Func<GachaConfigType, bool> IsWeaponEventWish = type => type is GachaConfigType.WeaponEventWish;
private readonly TypedWishSummaryBuilderContext context;
private readonly List<int> averageOrangePullTracker = [];
@@ -46,54 +62,52 @@ internal sealed class TypedWishSummaryBuilder
/// <param name="isUp">是否为Up物品</param>
public void Track(GachaItem item, ISummaryItemSource source, bool isUp)
{
if (!context.TypeEvaluator(item.GachaType))
if (context.TypeEvaluator(item.GachaType))
{
return;
}
++lastOrangePullTracker;
++lastPurplePullTracker;
++lastUpOrangePullTracker;
++lastOrangePullTracker;
++lastPurplePullTracker;
++lastUpOrangePullTracker;
// track total pulls
++totalCountTracker;
TrackFromToTime(item.Time);
// track total pulls
++totalCountTracker;
TrackFromToTime(item.Time);
switch (source.Quality)
{
case QualityType.QUALITY_ORANGE:
{
TrackMinMaxOrangePull(lastOrangePullTracker);
averageOrangePullTracker.Add(lastOrangePullTracker);
if (isUp)
switch (source.Quality)
{
case QualityType.QUALITY_ORANGE:
{
averageUpOrangePullTracker.Add(lastUpOrangePullTracker);
lastUpOrangePullTracker = 0;
TrackMinMaxOrangePull(lastOrangePullTracker);
averageOrangePullTracker.Add(lastOrangePullTracker);
if (isUp)
{
averageUpOrangePullTracker.Add(lastUpOrangePullTracker);
lastUpOrangePullTracker = 0;
}
summaryItems.Add(source.ToSummaryItem(lastOrangePullTracker, item.Time, isUp));
lastOrangePullTracker = 0;
++totalOrangePullTracker;
break;
}
summaryItems.Add(source.ToSummaryItem(lastOrangePullTracker, item.Time, isUp));
case QualityType.QUALITY_PURPLE:
{
lastPurplePullTracker = 0;
++totalPurplePullTracker;
break;
}
lastOrangePullTracker = 0;
++totalOrangePullTracker;
case QualityType.QUALITY_BLUE:
{
++totalBluePullTracker;
break;
}
default:
break;
}
case QualityType.QUALITY_PURPLE:
{
lastPurplePullTracker = 0;
++totalPurplePullTracker;
break;
}
case QualityType.QUALITY_BLUE:
{
++totalBluePullTracker;
break;
}
default:
break;
}
}
}

View File

@@ -14,13 +14,12 @@ internal readonly struct TypedWishSummaryBuilderContext
public readonly string Name;
public readonly int GuaranteeOrangeThreshold;
public readonly int GuaranteePurpleThreshold;
public readonly Func<GachaType, bool> TypeEvaluator;
public readonly Func<GachaConfigType, bool> TypeEvaluator;
public readonly GachaDistributionType DistributionType;
private static readonly Func<GachaType, bool> IsStandardWish = type => type is GachaType.Standard;
private static readonly Func<GachaType, bool> IsAvatarEventWish = type => type is GachaType.ActivityAvatar or GachaType.SpecialActivityAvatar;
private static readonly Func<GachaType, bool> IsWeaponEventWish = type => type is GachaType.ActivityWeapon;
private static readonly Func<GachaType, bool> IsChronicledWish = type => type is GachaType.ActivityCity;
private static readonly Func<GachaConfigType, bool> IsStandardWish = type => type is GachaConfigType.StandardWish;
private static readonly Func<GachaConfigType, bool> IsAvatarEventWish = type => type is GachaConfigType.AvatarEventWish or GachaConfigType.AvatarEventWish2;
private static readonly Func<GachaConfigType, bool> IsWeaponEventWish = type => type is GachaConfigType.WeaponEventWish;
public TypedWishSummaryBuilderContext(
ITaskContext taskContext,
@@ -28,7 +27,7 @@ internal readonly struct TypedWishSummaryBuilderContext
string name,
int guaranteeOrangeThreshold,
int guaranteePurpleThreshold,
Func<GachaType, bool> typeEvaluator,
Func<GachaConfigType, bool> typeEvaluator,
GachaDistributionType distributionType)
{
TaskContext = taskContext;
@@ -55,11 +54,6 @@ internal readonly struct TypedWishSummaryBuilderContext
return new(taskContext, gachaLogClient, SH.ServiceGachaLogFactoryWeaponWishName, 80, 10, IsWeaponEventWish, GachaDistributionType.WeaponEvent);
}
public static TypedWishSummaryBuilderContext ChronicledWish(ITaskContext taskContext, HomaGachaLogClient gachaLogClient)
{
return new(taskContext, gachaLogClient, SH.ServiceGachaLogFactoryChronicledWishName, 90, 10, IsChronicledWish, GachaDistributionType.Chronicled);
}
public ValueTask<HutaoResponse<GachaDistribution>> GetGachaDistributionAsync()
{
return GachaLogClient.GetGachaDistributionAsync(DistributionType);

View File

@@ -15,14 +15,12 @@ internal static class GachaArchiveOperation
{
archive = archives.SingleOrDefault(a => a.Uid == uid);
if (archive is not null)
if (archive is null)
{
return;
GachaArchive created = GachaArchive.From(uid);
gachaLogDbService.AddGachaArchive(created);
taskContext.InvokeOnMainThread(() => archives.Add(created));
archive = created;
}
GachaArchive created = GachaArchive.From(uid);
gachaLogDbService.AddGachaArchive(created);
taskContext.InvokeOnMainThread(() => archives.Add(created));
archive = created;
}
}

Some files were not shown because too many files have changed in this diff Show More