Compare commits

...

44 Commits

Author SHA1 Message Date
DismissedLight
a259750624 Merge pull request #1818 from DGP-Studio/develop 2024-07-17 13:50:08 +08:00
DismissedLight
4e7f8e2a97 Update AdvancedCollectionView.cs 2024-07-17 13:46:57 +08:00
Masterain
1ea92413f9 New translations sh.resx (English) (#1822) 2024-07-16 22:22:05 -07:00
Masterain
46c117edff New Crowdin updates (#1812) 2024-07-17 11:28:31 +08:00
DismissedLight
9c4a9fc09a bump version 2024-07-17 11:27:17 +08:00
DismissedLight
605fe5a3af Merge pull request #1816 from DGP-Studio/feat/unlockfps-loadexe
fix #1814
2024-07-17 11:18:26 +08:00
DismissedLight
7581cf8c8f fix pattern matching offset loop 2024-07-17 11:09:36 +08:00
DismissedLight
6b67811bae fix finding module 2024-07-17 10:49:57 +08:00
DismissedLight
6863cbb113 fix matching pattern 2024-07-17 10:44:19 +08:00
DismissedLight
6b03ccdacc try fix unlocking fps 2024-07-17 10:40:33 +08:00
DismissedLight
ad90c6b792 cultivation dialog spacing 2024-07-16 23:41:37 +08:00
DismissedLight
3ea7d59985 prevent showing update webview multiple times 2024-07-16 22:36:23 +08:00
DismissedLight
555043dfaa fix build 2024-07-16 17:30:50 +08:00
DismissedLight
84e05017ba impl #1720 2024-07-16 17:30:25 +08:00
DismissedLight
2c139a1ff6 cultivation save mode 2024-07-16 14:48:21 +08:00
DismissedLight
04114fb170 AdvancedCollectionView background creation 2024-07-15 23:38:08 +08:00
DismissedLight
a8065bf6e6 refine AdvancedCollectionView 2024-07-15 23:07:23 +08:00
DismissedLight
bfdb4b0060 refine AdvancedCollectionView 2024-07-15 17:31:14 +08:00
Masterain
6489f66d13 New Crowdin updates (#1800) 2024-07-15 13:38:19 +08:00
DismissedLight
eb57ac5952 limit ex/import uid selections height 2024-07-15 00:04:15 +08:00
DismissedLight
d57865fed9 append resource strings 2024-07-14 23:56:23 +08:00
DismissedLight
110af48385 uigf 4 support 2024-07-14 23:51:09 +08:00
DismissedLight
d30ef6daa0 [skip ci] uigf 4 support part 4 2024-07-14 21:41:07 +08:00
DismissedLight
e98bee8a9b [skip ci] uigf 4 support part 3 2024-07-13 22:56:55 +08:00
DismissedLight
71e0452c6e [skip ci] [skip ci] uigf 4 support part 2 2024-07-13 19:19:18 +08:00
qhy040404
d866c46646 fix #1810 2024-07-13 09:20:33 +08:00
DismissedLight
05d0faf131 [skip ci] uigf 4 support part 1 2024-07-12 17:06:59 +08:00
DismissedLight
83f5f25324 [skip ci] code style 2024-07-11 17:03:56 +08:00
DismissedLight
0c0290c446 impl #1497 2024-07-11 16:57:28 +08:00
DismissedLight
c3efd8d806 infobar view optimization 2024-07-10 16:52:50 +08:00
DismissedLight
e8d613d81f bump packages version 2024-07-10 13:54:12 +08:00
qhy040404
b7d03bee77 fix weapon wiki unclickable 2024-07-09 19:07:21 +08:00
DismissedLight
55799d0731 Update RoleCombatData.cs 2024-07-09 17:04:05 +08:00
DismissedLight
0a24e19625 try fix uid profilepicture duplicated 2024-07-09 14:05:47 +08:00
DismissedLight
a32b787352 Update InfoBarView.xaml 2024-07-08 23:45:14 +08:00
DismissedLight
f73b3af180 fix infobar view duplicate items 2024-07-08 17:19:00 +08:00
DismissedLight
bad60f1d65 refactor infobar view 2024-07-08 16:39:48 +08:00
DismissedLight
823ffdb5ad open default browser for update log content link 2024-07-08 13:22:57 +08:00
DismissedLight
544469f078 fix webview title text clipping 2024-07-08 11:13:38 +08:00
DismissedLight
04d4fa0c29 show updatelog after update 2024-07-08 10:48:10 +08:00
DismissedLight
f5dbabc586 optimization 2024-07-07 22:22:26 +08:00
DismissedLight
bfefbc58fa disable interaction when page is initializing 2024-07-06 19:12:50 +08:00
DismissedLight
5b98ba3fc4 dailynote auto refresh defaults to false 2024-07-06 18:20:44 +08:00
DismissedLight
32a22695e3 fix gachalog deletion not refresh view 2024-07-06 17:45:30 +08:00
200 changed files with 3852 additions and 2752 deletions

View File

@@ -9,7 +9,6 @@ using Snap.Hutao.Core.ExceptionService;
using Snap.Hutao.Core.LifeCycle;
using Snap.Hutao.Core.LifeCycle.InterProcess;
using Snap.Hutao.Core.Logging;
using Snap.Hutao.UI.Xaml;
using System.Diagnostics;
namespace Snap.Hutao;

View File

@@ -1,28 +0,0 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
namespace Snap.Hutao;
/// <summary>
/// 应用程序资源提供器
/// </summary>
[Injection(InjectAs.Transient, typeof(IAppResourceProvider))]
internal sealed class AppResourceProvider : IAppResourceProvider
{
private readonly App app;
/// <summary>
/// 构造一个新的应用程序资源提供器
/// </summary>
/// <param name="app">应用</param>
public AppResourceProvider(App app)
{
this.app = app;
}
/// <inheritdoc/>
public T GetResource<T>(string name)
{
return (T)app.Resources[name];
}
}

View File

@@ -18,7 +18,7 @@ internal sealed class AdvancedDbCollectionView<TEntity> : AdvancedCollectionView
private bool savingToDatabase = true;
public AdvancedDbCollectionView(IList<TEntity> source, IServiceProvider serviceProvider)
: base(source, true)
: base(source)
{
this.serviceProvider = serviceProvider;
}
@@ -30,14 +30,14 @@ internal sealed class AdvancedDbCollectionView<TEntity> : AdvancedCollectionView
protected override void OnCurrentChangedOverride()
{
if (serviceProvider is null || !savingToDatabase)
if (!savingToDatabase)
{
return;
}
TEntity? currentItem = CurrentItem;
foreach (TEntity item in Source)
foreach (TEntity item in SourceCollection)
{
item.IsSelected = ReferenceEquals(item, currentItem);
}
@@ -85,7 +85,7 @@ internal sealed class AdvancedDbCollectionView<TEntityAccess, TEntity> : Advance
private bool savingToDatabase = true;
public AdvancedDbCollectionView(IList<TEntityAccess> source, IServiceProvider serviceProvider)
: base(source, true)
: base(source)
{
this.serviceProvider = serviceProvider;
}
@@ -97,14 +97,14 @@ internal sealed class AdvancedDbCollectionView<TEntityAccess, TEntity> : Advance
protected override void OnCurrentChangedOverride()
{
if (serviceProvider is null || !savingToDatabase)
if (!savingToDatabase)
{
return;
}
TEntityAccess? currentItem = CurrentItem;
foreach (TEntityAccess item in Source)
foreach (TEntityAccess item in SourceCollection)
{
item.Entity.IsSelected = ReferenceEquals(item, currentItem);
}

View File

@@ -18,4 +18,11 @@ internal static class SelectableExtension
{
return source.SingleOrDefault(i => i.IsSelected);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TSource? SelectedOrFirstOrDefault<TSource>(this IEnumerable<TSource> source)
where TSource : ISelectable
{
return source.SingleOrDefault(i => i.IsSelected) ?? source.FirstOrDefault();
}
}

View File

@@ -73,6 +73,15 @@ internal sealed class HutaoException : Exception
throw new NotSupportedException(message, innerException);
}
[MethodImpl(MethodImplOptions.NoInlining)]
public static void NotSupportedIf(bool condition, string? message = default, Exception? innerException = default)
{
if (condition)
{
throw new NotSupportedException(message, innerException);
}
}
[DoesNotReturn]
[MethodImpl(MethodImplOptions.NoInlining)]
public static OperationCanceledException OperationCanceled(string message, Exception? innerException = default)

View File

@@ -18,9 +18,8 @@ internal static class ValueFileExtension
return new(true, t);
}
}
catch (Exception ex)
catch (Exception)
{
_ = ex;
return new(false, null);
}
}

View File

@@ -0,0 +1,26 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using System.Globalization;
namespace Snap.Hutao.Core.Json.Converter;
internal sealed class DateTimeConverter : JsonConverter<DateTime>
{
private const string Format = "yyyy-MM-dd HH:mm:ss";
public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (reader.GetString() is { } dataTimeString)
{
return DateTime.ParseExact(dataTimeString, Format, CultureInfo.InvariantCulture);
}
return default;
}
public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
{
writer.WriteStringValue(value.ToString(Format, CultureInfo.InvariantCulture));
}
}

View File

@@ -6,7 +6,7 @@ using System.Globalization;
namespace Snap.Hutao.Core.Json.Converter;
// 此转换器无法实现无损往返 必须在反序列化后调整 Offset
internal class DateTimeOffsetConverter : JsonConverter<DateTimeOffset>
internal sealed class DateTimeOffsetConverter : JsonConverter<DateTimeOffset>
{
private const string Format = "yyyy-MM-dd HH:mm:ss";

View File

@@ -39,6 +39,7 @@ internal sealed partial class AppActivation : IAppActivation, IAppActivationActi
private readonly ICurrentXamlWindowReference currentWindowReference;
private readonly IServiceProvider serviceProvider;
private readonly RuntimeOptions runtimeOptions;
private readonly ILogger<AppActivation> logger;
private readonly ITaskContext taskContext;
@@ -257,6 +258,12 @@ internal sealed partial class AppActivation : IAppActivation, IAppActivationActi
guideWindow.BringToForeground();
return;
}
if (Version.Parse(LocalSetting.Get(SettingKeys.LastVersion, "0.0.0.0")) < runtimeOptions.Version)
{
XamlApplicationLifetime.IsFirstRunAfterUpdate = true;
LocalSetting.Set(SettingKeys.LastVersion, $"{runtimeOptions.Version}");
}
}
await WaitMainWindowOrCurrentAsync().ConfigureAwait(false);

View File

@@ -22,6 +22,7 @@ internal static class SettingKeys
#endregion
#region Application
public const string LastVersion = "LastVersion";
public const string LaunchTimes = "LaunchTimes";
public const string DataFolderPath = "DataFolderPath";
public const string Major1Minor10Revision0GuideState = "Major1Minor10Revision0GuideState1";
@@ -63,6 +64,7 @@ internal static class SettingKeys
public const string OverrideElevationRequirement = "OverrideElevationRequirement";
public const string OverrideUpdateVersionComparison = "OverrideUpdateVersionComparison";
public const string OverridePackageConvertDirectoryPermissionsRequirement = "OverridePackageConvertDirectoryPermissionsRequirement";
public const string AlwaysIsFirstRunAfterUpdate = "AlwaysIsFirstRunAfterUpdate";
#endregion
#region Obsolete

View File

@@ -3,33 +3,14 @@
namespace Snap.Hutao.Core.Threading.Abstraction;
/// <summary>
/// 表示一个可等待对象,如果一个方法返回此类型的实例,则此方法可以使用 <see langword="await"/> 异步等待。
/// </summary>
/// <typeparam name="TAwaiter">用于给 await 确定返回时机的 IAwaiter 的实例。</typeparam>
internal interface IAwaitable<out TAwaiter>
where TAwaiter : IAwaiter
{
/// <summary>
/// 获取一个可用于 await 关键字异步等待的异步等待对象。
/// 此方法会被编译器自动调用。
/// </summary>
/// <returns>等待器</returns>
TAwaiter GetAwaiter();
}
/// <summary>
/// 表示一个包含返回值的可等待对象,如果一个方法返回此类型的实例,则此方法可以使用 <see langword="await"/> 异步等待返回值。
/// </summary>
/// <typeparam name="TAwaiter">用于给 await 确定返回时机的 <see cref="IAwaiter{TResult}"/> 的实例。</typeparam>
/// <typeparam name="TResult">异步返回的返回值类型。</typeparam>
internal interface IAwaitable<out TAwaiter, out TResult>
where TAwaiter : IAwaiter<TResult>
{
/// <summary>
/// 获取一个可用于 await 关键字异步等待的异步等待对象。
/// 此方法会被编译器自动调用。
/// </summary>
/// <returns>等待器</returns>
TAwaiter GetAwaiter();
}

View File

@@ -5,38 +5,16 @@ using System.Runtime.CompilerServices;
namespace Snap.Hutao.Core.Threading.Abstraction;
/// <summary>
/// 用于给 await 确定异步返回的时机。
/// </summary>
internal interface IAwaiter : INotifyCompletion
{
/// <summary>
/// 获取一个状态,该状态表示正在异步等待的操作已经完成(成功完成或发生了异常);此状态会被编译器自动调用。
/// 在实现中,为了达到各种效果,可以灵活应用其值:可以始终为 true或者始终为 false。
/// </summary>
bool IsCompleted { get; }
/// <summary>
/// 此方法会被编译器在 await 结束时自动调用以获取返回状态(包括异常)。
/// </summary>
void GetResult();
}
/// <summary>
/// 用于给 await 确定异步返回的时机,并获取到返回值。
/// </summary>
/// <typeparam name="TResult">异步返回的返回值类型。</typeparam>
internal interface IAwaiter<out TResult> : INotifyCompletion
{
/// <summary>
/// 获取一个状态,该状态表示正在异步等待的操作已经完成(成功完成或发生了异常);此状态会被编译器自动调用。
/// 在实现中,为了达到各种效果,可以灵活应用其值:可以始终为 true或者始终为 false。
/// </summary>
bool IsCompleted { get; }
/// <summary>
/// 获取此异步等待操作的返回值,此方法会被编译器在 await 结束时自动调用以获取返回值(包括异常)。
/// </summary>
/// <returns>异步操作的返回值。</returns>
TResult GetResult();
}

View File

@@ -5,19 +5,10 @@ using System.Runtime.CompilerServices;
namespace Snap.Hutao.Core.Threading.Abstraction;
/// <summary>
/// 当执行关键代码(此代码中的错误可能给应用程序中的其他状态造成负面影响)时,
/// 用于给 await 确定异步返回的时机。
/// </summary>
internal interface ICriticalAwaiter : IAwaiter, ICriticalNotifyCompletion
{
}
/// <summary>
/// 当执行关键代码(此代码中的错误可能给应用程序中的其他状态造成负面影响)时,
/// 用于给 await 确定异步返回的时机,并获取到返回值。
/// </summary>
/// <typeparam name="TResult">异步返回的返回值类型。</typeparam>
internal interface ICriticalAwaiter<out TResult> : IAwaiter<TResult>, ICriticalNotifyCompletion
{
}

View File

@@ -18,6 +18,7 @@ internal readonly struct DispatcherQueueSwitchOperation : IAwaitable<DispatcherQ
public bool IsCompleted
{
// Only yields when we are not on the DispatcherQueue thread.
get => dispatherQueue.HasThreadAccess;
}

View File

@@ -18,7 +18,7 @@ internal readonly struct ThreadPoolSwitchOperation : IAwaitable<ThreadPoolSwitch
public bool IsCompleted
{
// 如果已经处于后台就不再切换新的线程
// Only yields when we are on the DispatcherQueue thread.
get => !dispatherQueue.HasThreadAccess;
}

View File

@@ -9,7 +9,7 @@ namespace Snap.Hutao.Extension;
internal static class CollectionExtension
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool IsNullOrEmpty<TSource>([NotNullWhen(false)][MaybeNullWhen(true)] this Collection<TSource>? source)
public static bool IsNullOrEmpty<TSource>([NotNullWhen(false)][MaybeNullWhen(true)] this ICollection<TSource>? source)
{
if (source is { Count: > 0 })
{

View File

@@ -67,17 +67,6 @@ internal static class ListExtension
return list.GetRange(start, length);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool IsNullOrEmpty<TSource>([NotNullWhen(false)][MaybeNullWhen(true)] this List<TSource>? source)
{
if (source is { Count: > 0 })
{
return false;
}
return true;
}
public static void RemoveLast<T>(this IList<T> collection)
{
collection.RemoveAt(collection.Count - 1);

View File

@@ -1,18 +0,0 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
namespace Snap.Hutao;
/// <summary>
/// 应用程序资源提供器
/// </summary>
internal interface IAppResourceProvider
{
/// <summary>
/// 获取资源
/// </summary>
/// <typeparam name="T">资源的类型</typeparam>
/// <param name="key">资源的名称</param>
/// <returns>资源</returns>
T GetResource<T>(string key);
}

View File

@@ -3,26 +3,12 @@
namespace Snap.Hutao.Model.Calculable;
/// <summary>
/// 可计算物品选项
/// </summary>
internal readonly struct CalculableOptions
{
/// <summary>
/// 角色
/// </summary>
public readonly ICalculableAvatar? Avatar;
/// <summary>
/// 武器
/// </summary>
public readonly ICalculableWeapon? Weapon;
/// <summary>
/// 构造一个新的可计算物品选项
/// </summary>
/// <param name="avatar">角色</param>
/// <param name="weapon">武器</param>
public CalculableOptions(ICalculableAvatar? avatar, ICalculableWeapon? weapon)
{
Avatar = avatar;

View File

@@ -8,11 +8,17 @@ namespace Snap.Hutao.Model.Entity.Abstraction;
internal interface IDbMappingForeignKeyFrom<TSource, TFrom>
{
[Pure]
static abstract TSource From(in Guid foreignKey, in TFrom from);
static abstract TSource From(Guid foreignKey, TFrom from);
}
internal interface IDbMappingForeignKeyFrom<TSource, T1, T2>
{
[Pure]
static abstract TSource From(in Guid foreignKey, in T1 param1, in T2 param2);
static abstract TSource From(Guid foreignKey, T1 param1, T2 param2);
}
internal interface IDbMappingForeignKeyFrom<TSource, T1, T2, T3>
{
[Pure]
static abstract TSource From(Guid foreignKey, T1 param1, T2 param2, T3 param3);
}

View File

@@ -10,61 +10,30 @@ using System.ComponentModel.DataAnnotations.Schema;
namespace Snap.Hutao.Model.Entity;
/// <summary>
/// 成就
/// </summary>
[HighQuality]
[Table("achievements")]
internal sealed class Achievement : IAppDbEntityHasArchive,
IEquatable<Achievement>,
IDbMappingForeignKeyFrom<Achievement, AchievementId>,
IDbMappingForeignKeyFrom<Achievement, UIAFItem>
{
/// <summary>
/// 内部Id
/// </summary>
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid InnerId { get; set; }
/// <summary>
/// 存档 Id
/// </summary>
public Guid ArchiveId { get; set; }
/// <summary>
/// 存档
/// </summary>
[ForeignKey(nameof(ArchiveId))]
public AchievementArchive Archive { get; set; } = default!;
/// <summary>
/// Id
/// </summary>
public uint Id { get; set; }
/// <summary>
/// 当前进度
/// </summary>
public uint Current { get; set; }
/// <summary>
/// 完成时间
/// </summary>
public DateTimeOffset Time { get; set; }
/// <summary>
/// 状态
/// </summary>
public AchievementStatus Status { get; set; }
/// <summary>
/// 创建一个新的成就
/// </summary>
/// <param name="archiveId">对应的用户id</param>
/// <param name="id">成就Id</param>
/// <returns>新创建的成就</returns>
public static Achievement From(in Guid archiveId, in AchievementId id)
public static Achievement From(Guid archiveId, AchievementId id)
{
return new()
{
@@ -75,17 +44,11 @@ internal sealed class Achievement : IAppDbEntityHasArchive,
};
}
/// <summary>
/// 创建一个新的成就
/// </summary>
/// <param name="userId">对应的用户id</param>
/// <param name="uiaf">uiaf项</param>
/// <returns>新创建的成就</returns>
public static Achievement From(in Guid userId, in UIAFItem uiaf)
public static Achievement From(Guid archiveId, UIAFItem uiaf)
{
return new()
{
ArchiveId = userId,
ArchiveId = archiveId,
Id = uiaf.Id,
Current = uiaf.Current,
Status = uiaf.Status,
@@ -93,32 +56,27 @@ internal sealed class Achievement : IAppDbEntityHasArchive,
};
}
/// <inheritdoc/>
public bool Equals(Achievement? other)
{
if (other is null)
{
return false;
}
else
{
return ArchiveId == other.ArchiveId
&& Id == other.Id
&& Current == other.Current
&& Status == other.Status
&& Time == other.Time;
}
return ArchiveId == other.ArchiveId
&& Id == other.Id
&& Current == other.Current
&& Status == other.Status
&& Time == other.Time;
}
#region Object
/// <inheritdoc/>
public override bool Equals(object? obj)
{
return Equals(obj as Achievement);
}
/// <inheritdoc/>
public override int GetHashCode()
{
return HashCode.Combine(ArchiveId, Id, Current, Status, Time);

View File

@@ -13,7 +13,7 @@ namespace Snap.Hutao.Model.Entity;
/// 成就存档
/// </summary>
[Table("achievement_archives")]
internal sealed class AchievementArchive : ISelectable,
internal sealed partial class AchievementArchive : ISelectable,
IAdvancedCollectionViewItem,
IMappingFrom<AchievementArchive, string>
{
@@ -43,12 +43,4 @@ internal sealed class AchievementArchive : ISelectable,
{
return new() { Name = name };
}
public object? GetPropertyValue(string name)
{
return name switch
{
_ => default!,
};
}
}

View File

@@ -7,28 +7,15 @@ using System.ComponentModel.DataAnnotations.Schema;
namespace Snap.Hutao.Model.Entity;
/// <summary>
/// 角色信息表
/// </summary>
[HighQuality]
[Table("avatar_infos")]
internal sealed class AvatarInfo : IMappingFrom<AvatarInfo, string, Web.Enka.Model.AvatarInfo>
{
/// <summary>
/// 内部 Id
/// </summary>
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid InnerId { get; set; }
/// <summary>
/// Uid
/// </summary>
public string Uid { get; set; } = default!;
/// <summary>
/// 角色的信息
/// </summary>
public Web.Enka.Model.AvatarInfo Info { get; set; } = default!;
public DateTimeOffset ShowcaseRefreshTime { get; set; }
@@ -37,12 +24,6 @@ internal sealed class AvatarInfo : IMappingFrom<AvatarInfo, string, Web.Enka.Mod
public DateTimeOffset CalculatorRefreshTime { get; set; }
/// <summary>
/// 创建一个新的实体角色信息
/// </summary>
/// <param name="uid">uid</param>
/// <param name="info">角色信息</param>
/// <returns>实体角色信息</returns>
public static AvatarInfo From(string uid, Web.Enka.Model.AvatarInfo info)
{
return new() { Uid = uid, Info = info };

View File

@@ -6,18 +6,13 @@ using Snap.Hutao.Core.Json;
namespace Snap.Hutao.Model.Entity.Configuration;
/// <summary>
/// Json文本转换器
/// </summary>
/// <typeparam name="TProperty">实体类型</typeparam>
[HighQuality]
internal sealed class JsonTextValueConverter<TProperty> : ValueConverter<TProperty, string>
internal sealed class JsonTextValueConverter<TPropertyType> : ValueConverter<TPropertyType, string>
{
[SuppressMessage("", "SH007")]
public JsonTextValueConverter()
: base(
obj => JsonSerializer.Serialize(obj, JsonOptions.Default),
str => JsonSerializer.Deserialize<TProperty>(str, JsonOptions.Default)!)
str => JsonSerializer.Deserialize<TPropertyType>(str, JsonOptions.Default)!)
{
}
}

View File

@@ -8,52 +8,26 @@ using System.ComponentModel.DataAnnotations.Schema;
namespace Snap.Hutao.Model.Entity;
/// <summary>
/// 养成入口
/// </summary>
[HighQuality]
[Table("cultivate_entries")]
internal sealed class CultivateEntry : IAppDbEntity,
IDbMappingForeignKeyFrom<CultivateEntry, CultivateType, uint>
{
/// <summary>
/// 内部Id
/// </summary>
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid InnerId { get; set; }
/// <summary>
/// 项目Id
/// </summary>
public Guid ProjectId { get; set; }
/// <summary>
/// 项目
/// </summary>
[ForeignKey(nameof(ProjectId))]
public CultivateProject Project { get; set; } = default!;
public CultivateEntryLevelInformation? LevelInformation { get; set; }
/// <summary>
/// 养成类型
/// </summary>
public CultivateType Type { get; set; }
/// <summary>
/// 角色/武器/家具 Id
/// </summary>
public uint Id { get; set; }
/// <summary>
/// 创建一个新的养成入口点
/// </summary>
/// <param name="projectId">项目Id</param>
/// <param name="type">类型</param>
/// <param name="id">主Id</param>
/// <returns>养成入口点</returns>
public static CultivateEntry From(in Guid projectId, in CultivateType type, in uint id)
public static CultivateEntry From(Guid projectId, CultivateType type, uint id)
{
return new()
{
@@ -62,10 +36,4 @@ internal sealed class CultivateEntry : IAppDbEntity,
Id = id,
};
}
public static CultivateEntry Join(CultivateEntry entry, CultivateEntryLevelInformation levelInformation)
{
entry.LevelInformation = levelInformation;
return entry;
}
}

View File

@@ -7,53 +7,25 @@ using System.ComponentModel.DataAnnotations.Schema;
namespace Snap.Hutao.Model.Entity;
/// <summary>
/// 消耗物品
/// </summary>
[HighQuality]
[Table("cultivate_items")]
internal sealed class CultivateItem : IDbMappingForeignKeyFrom<CultivateItem, Web.Hoyolab.Takumi.Event.Calculate.Item>
{
/// <summary>
/// 内部Id
/// </summary>
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid InnerId { get; set; }
/// <summary>
/// 外键
/// </summary>
public Guid EntryId { get; set; }
/// <summary>
/// 入口名称
/// </summary>
[ForeignKey(nameof(EntryId))]
public CultivateEntry Entry { get; set; } = default!;
/// <summary>
/// 物品 Id
/// </summary>
public uint ItemId { get; set; }
/// <summary>
/// 物品个数
/// </summary>
public uint Count { get; set; }
/// <summary>
/// 是否完成此项
/// </summary>
public bool IsFinished { get; set; }
/// <summary>
/// 创建一个新的养成物品
/// </summary>
/// <param name="entryId">入口点 Id</param>
/// <param name="item">物品</param>
/// <returns>养成物品</returns>
public static CultivateItem From(in Guid entryId, in Web.Hoyolab.Takumi.Event.Calculate.Item item)
public static CultivateItem From(Guid entryId, Web.Hoyolab.Takumi.Event.Calculate.Item item)
{
return new()
{

View File

@@ -9,53 +9,21 @@ using System.ComponentModel.DataAnnotations.Schema;
namespace Snap.Hutao.Model.Entity;
/// <summary>
/// 培养计划
/// </summary>
[HighQuality]
[Table("cultivate_projects")]
internal sealed class CultivateProject : ISelectable,
internal sealed partial class CultivateProject : ISelectable,
IAdvancedCollectionViewItem,
IMappingFrom<CultivateProject, string, string>
IMappingFrom<CultivateProject, string>
{
/// <summary>
/// 内部Id
/// </summary>
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid InnerId { get; set; }
/// <summary>
/// 是否选中
/// </summary>
public bool IsSelected { get; set; }
/// <summary>
/// 名称
/// </summary>
public string Name { get; set; } = default!;
/// <summary>
/// 所属的Uid
/// </summary>
public string? AttachedUid { get; set; }
/// <summary>
/// 创建新的养成计划
/// </summary>
/// <param name="name">名称</param>
/// <param name="attachedUid">绑定的Uid</param>
/// <returns>新的养成计划</returns>
public static CultivateProject From(string name, string? attachedUid = null)
public static CultivateProject From(string name)
{
return new() { Name = name, AttachedUid = attachedUid };
}
public object? GetPropertyValue(string name)
{
return name switch
{
_ => default,
};
return new() { Name = name };
}
}

View File

@@ -42,12 +42,4 @@ internal sealed partial class GachaArchive : ISelectable,
{
return new() { Uid = uid };
}
public object? GetPropertyValue(string name)
{
return name switch
{
_ => default,
};
}
}

View File

@@ -6,74 +6,35 @@ using Snap.Hutao.Model.InterChange.GachaLog;
using Snap.Hutao.Web.Hoyolab.Hk4e.Event.GachaInfo;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Globalization;
namespace Snap.Hutao.Model.Entity;
/// <summary>
/// 抽卡记录物品
/// </summary>
[HighQuality]
[Table("gacha_items")]
internal sealed partial class GachaItem
: IDbMappingForeignKeyFrom<GachaItem, GachaLogItem, uint>,
IDbMappingForeignKeyFrom<GachaItem, UIGFItem, uint>,
IDbMappingForeignKeyFrom<GachaItem, UIGFItem>,
IDbMappingForeignKeyFrom<GachaItem, Web.Hutao.GachaLog.GachaItem>
IDbMappingForeignKeyFrom<GachaItem, Web.Hutao.GachaLog.GachaItem>,
IDbMappingForeignKeyFrom<GachaItem, Hk4eItem, int>
{
/// <summary>
/// 内部Id
/// </summary>
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid InnerId { get; set; }
/// <summary>
/// 存档
/// </summary>
[ForeignKey(nameof(ArchiveId))]
public GachaArchive Archive { get; set; } = default!;
/// <summary>
/// 存档Id
/// </summary>
public Guid ArchiveId { get; set; }
/// <summary>
/// 祈愿记录分类
/// </summary>
public GachaType GachaType { get; set; }
/// <summary>
/// 祈愿记录查询分类
/// 合并保底的卡池使用此属性
/// 仅4种不含400
/// </summary>
public GachaType QueryType { get; set; }
/// <summary>
/// 物品Id
/// </summary>
public uint ItemId { get; set; }
/// <summary>
/// 获取时间
/// </summary>
public DateTimeOffset Time { get; set; }
/// <summary>
/// 物品
/// </summary>
public long Id { get; set; }
/// <summary>
/// 构造一个新的数据库祈愿物品
/// </summary>
/// <param name="archiveId">存档Id</param>
/// <param name="item">祈愿物品</param>
/// <param name="itemId">物品Id</param>
/// <returns>新的祈愿物品</returns>
public static GachaItem From(in Guid archiveId, in GachaLogItem item, in uint itemId)
public static GachaItem From(Guid archiveId, GachaLogItem item, uint itemId)
{
return new()
{
@@ -86,52 +47,7 @@ internal sealed partial class GachaItem
};
}
/// <summary>
/// 构造一个新的数据库祈愿物品
/// </summary>
/// <param name="archiveId">存档Id</param>
/// <param name="item">祈愿物品</param>
/// <param name="itemId">物品Id</param>
/// <returns>新的祈愿物品</returns>
public static GachaItem From(in Guid archiveId, in UIGFItem item, in uint itemId)
{
return new()
{
ArchiveId = archiveId,
GachaType = item.GachaType,
QueryType = item.UIGFGachaType,
ItemId = itemId,
Time = item.Time,
Id = item.Id,
};
}
/// <summary>
/// 构造一个新的数据库祈愿物品
/// </summary>
/// <param name="archiveId">存档Id</param>
/// <param name="item">祈愿物品</param>
/// <returns>新的祈愿物品</returns>
public static GachaItem From(in Guid archiveId, in UIGFItem item)
{
return new()
{
ArchiveId = archiveId,
GachaType = item.GachaType,
QueryType = item.UIGFGachaType,
ItemId = uint.Parse(item.ItemId, CultureInfo.CurrentCulture),
Time = item.Time,
Id = item.Id,
};
}
/// <summary>
/// 构造一个新的数据库祈愿物品
/// </summary>
/// <param name="archiveId">存档Id</param>
/// <param name="item">祈愿物品</param>
/// <returns>新的祈愿物品</returns>
public static GachaItem From(in Guid archiveId, in Web.Hutao.GachaLog.GachaItem item)
public static GachaItem From(Guid archiveId, Web.Hutao.GachaLog.GachaItem item)
{
return new()
{
@@ -143,4 +59,17 @@ internal sealed partial class GachaItem
Id = item.Id,
};
}
public static GachaItem From(Guid archiveId, Hk4eItem item, int timezone)
{
return new()
{
ArchiveId = archiveId,
GachaType = item.GachaType,
QueryType = item.UIGFGachaType,
ItemId = item.ItemId,
Time = new(item.Time, new(timezone, 0, 0)),
Id = item.Id,
};
}
}

View File

@@ -17,7 +17,7 @@ namespace Snap.Hutao.Model.Entity;
/// </summary>
[HighQuality]
[Table("game_accounts")]
internal sealed class GameAccount : ObservableObject,
internal sealed partial class GameAccount : ObservableObject,
IAppDbEntity,
IReorderable,
IAdvancedCollectionViewItem,
@@ -82,12 +82,4 @@ internal sealed class GameAccount : ObservableObject,
Name = name;
OnPropertyChanged($"{nameof(Name)}");
}
public object? GetPropertyValue(string propertyName)
{
return propertyName switch
{
_ => default,
};
}
}

View File

@@ -15,41 +15,20 @@ namespace Snap.Hutao.Model.Entity;
internal sealed class InventoryItem : IDbMappingForeignKeyFrom<InventoryItem, uint>,
IDbMappingForeignKeyFrom<InventoryItem, uint, uint>
{
/// <summary>
/// 内部Id
/// </summary>
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid InnerId { get; set; }
/// <summary>
/// 培养计划Id
/// </summary>
public Guid ProjectId { get; set; }
/// <summary>
/// 所属的计划
/// </summary>
[ForeignKey(nameof(ProjectId))]
public CultivateProject Project { get; set; } = default!;
/// <summary>
/// 物品Id
/// </summary>
public uint ItemId { get; set; }
/// <summary>
/// 个数 4294967295
/// </summary>
public uint Count { get; set; }
/// <summary>
/// 构造一个新的个数为0的物品
/// </summary>
/// <param name="projectId">项目Id</param>
/// <param name="itemId">物品Id</param>
/// <returns>新的个数为0的物品</returns>
public static InventoryItem From(in Guid projectId, in uint itemId)
public static InventoryItem From(Guid projectId, uint itemId)
{
return new()
{
@@ -58,14 +37,7 @@ internal sealed class InventoryItem : IDbMappingForeignKeyFrom<InventoryItem, ui
};
}
/// <summary>
/// 构造一个新的个数不为0的物品
/// </summary>
/// <param name="projectId">项目Id</param>
/// <param name="itemId">物品Id</param>
/// <param name="count">物品个数</param>
/// <returns>新的个数不为0的物品</returns>
public static InventoryItem From(in Guid projectId, in uint itemId, in uint count)
public static InventoryItem From(Guid projectId, uint itemId, uint count)
{
return new()
{

View File

@@ -0,0 +1,43 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Core.Abstraction;
using Snap.Hutao.Core.Json.Annotation;
using Snap.Hutao.Model.Entity;
using Snap.Hutao.Web.Hoyolab.Hk4e.Event.GachaInfo;
namespace Snap.Hutao.Model.InterChange.GachaLog;
internal sealed class Hk4eItem : IMappingFrom<Hk4eItem, GachaItem>
{
[JsonPropertyName("uigf_gacha_type")]
[JsonEnum(JsonSerializeType.NumberString)]
public required GachaType UIGFGachaType { get; set; }
[JsonPropertyName("gacha_type")]
[JsonEnum(JsonSerializeType.NumberString)]
public required GachaType GachaType { get; set; }
[JsonPropertyName("item_id")]
public required uint ItemId { get; set; }
[JsonPropertyName("time")]
[JsonConverter(typeof(Core.Json.Converter.DateTimeConverter))]
public required DateTime Time { get; set; }
[JsonPropertyName("id")]
[JsonNumberHandling(JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString)]
public required long Id { get; set; } = default!;
public static Hk4eItem From(GachaItem item)
{
return new()
{
UIGFGachaType = item.QueryType,
GachaType = item.GachaType,
ItemId = item.ItemId,
Time = item.Time.UtcDateTime,
Id = item.Id,
};
}
}

View File

@@ -1,110 +1,14 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Core;
using Snap.Hutao.Web.Hoyolab;
using System.Runtime.InteropServices;
namespace Snap.Hutao.Model.InterChange.GachaLog;
/// <summary>
/// 统一可交换祈愿格式
/// https://uigf.org/standards/UIGF.html
/// </summary>
[HighQuality]
internal sealed class UIGF : IJsonOnSerializing, IJsonOnDeserialized
internal sealed class UIGF
{
/// <summary>
/// 当前版本
/// </summary>
public const string CurrentVersion = "v3.0";
/// <summary>
/// 信息
/// </summary>
[JsonRequired]
[JsonPropertyName("info")]
[JsonRequired]
public UIGFInfo Info { get; set; } = default!;
/// <summary>
/// 列表
/// </summary>
[JsonPropertyName("list")]
public List<UIGFItem> List { get; set; } = default!;
public void OnSerializing()
{
TimeSpan offset = GetRegionTimeZoneUtcOffset();
foreach (UIGFItem item in List)
{
item.Time = item.Time.ToOffset(offset);
}
}
public void OnDeserialized()
{
// Adjust items timezone
TimeSpan offset = GetRegionTimeZoneUtcOffset();
foreach (UIGFItem item in List)
{
item.Time = UnsafeDateTimeOffset.AdjustOffsetOnly(item.Time, offset);
}
}
public bool IsCurrentVersionSupported(out UIGFVersion version)
{
version = Info.UIGFVersion switch
{
"v2.1" => UIGFVersion.Major2Minor2OrLower,
"v2.2" => UIGFVersion.Major2Minor2OrLower,
"v2.3" => UIGFVersion.Major2Minor3OrHigher,
"v2.4" => UIGFVersion.Major2Minor3OrHigher,
"v3.0" => UIGFVersion.Major2Minor3OrHigher,
_ => UIGFVersion.NotSupported,
};
return version != UIGFVersion.NotSupported;
}
public bool IsMajor2Minor2OrLowerListValid([NotNullWhen(false)] out long id)
{
foreach (ref readonly UIGFItem item in CollectionsMarshal.AsSpan(List))
{
if (item.ItemType != SH.ModelInterchangeUIGFItemTypeAvatar && item.ItemType != SH.ModelInterchangeUIGFItemTypeWeapon)
{
id = item.Id;
return false;
}
}
id = 0;
return true;
}
public bool IsMajor2Minor3OrHigherListValid([NotNullWhen(false)] out long id)
{
foreach (ref readonly UIGFItem item in CollectionsMarshal.AsSpan(List))
{
if (string.IsNullOrEmpty(item.ItemId))
{
id = item.Id;
return false;
}
}
id = 0;
return true;
}
private TimeSpan GetRegionTimeZoneUtcOffset()
{
if (Info.RegionTimeZone is int offsetHours)
{
return new TimeSpan(offsetHours, 0, 0);
}
return PlayerUid.GetRegionTimeZoneUtcOffsetForUid(Info.Uid);
}
[JsonPropertyName("hk4e")]
public List<UIGFEntry<Hk4eItem>>? Hk4e { get; set; }
}

View File

@@ -0,0 +1,23 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
namespace Snap.Hutao.Model.InterChange.GachaLog;
internal sealed class UIGFEntry<TItem>
{
[JsonPropertyName("uid")]
[JsonNumberHandling(JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString)]
public required uint Uid { get; set; }
[JsonPropertyName("timezone")]
public required int TimeZone { get; set; }
[JsonPropertyName("lang")]
public string? Language { get; set; }
[JsonPropertyName("list")]
public required List<TItem> List { get; set; }
[JsonIgnore]
public bool IsSelected { get; set; }
}

View File

@@ -1,81 +1,19 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Core;
using Snap.Hutao.Core.Abstraction;
using Snap.Hutao.Service;
using Snap.Hutao.Web.Hoyolab;
namespace Snap.Hutao.Model.InterChange.GachaLog;
/// <summary>
/// UIGF格式的信息
/// </summary>
[HighQuality]
internal sealed class UIGFInfo : IMappingFrom<UIGFInfo, RuntimeOptions, CultureOptions, string>
internal sealed class UIGFInfo
{
/// <summary>
/// 用户Uid
/// </summary>
[JsonPropertyName("uid")]
public string Uid { get; set; } = default!;
/// <summary>
/// 语言
/// </summary>
[JsonPropertyName("lang")]
public string Language { get; set; } = default!;
/// <summary>
/// 导出的时间戳
/// </summary>
[JsonPropertyName("export_timestamp")]
public long? ExportTimestamp { get; set; }
public required long ExportTimestamp { get; set; }
/// <summary>
/// 导出时间
/// </summary>
[JsonIgnore]
public DateTimeOffset ExportDateTime
{
get => DateTimeOffsetExtension.UnsafeRelaxedFromUnixTime(ExportTimestamp, DateTimeOffset.MinValue);
}
/// <summary>
/// 导出的 App 名称
/// </summary>
[JsonPropertyName("export_app")]
public string ExportApp { get; set; } = default!;
public required string ExportApp { get; set; }
/// <summary>
/// 导出的 App 版本
/// </summary>
[JsonPropertyName("export_app_version")]
public string ExportAppVersion { get; set; } = default!;
public required string ExportAppVersion { get; set; }
/// <summary>
/// 使用的UIGF版本
/// </summary>
[JsonPropertyName("uigf_version")]
public string UIGFVersion { get; set; } = default!;
/// <summary>
/// 时区偏移
/// </summary>
[JsonPropertyName("region_time_zone")]
public int? RegionTimeZone { get; set; } = default!;
public static UIGFInfo From(RuntimeOptions runtimeOptions, CultureOptions cultureOptions, string uid)
{
return new()
{
Uid = uid,
Language = cultureOptions.LanguageCode,
ExportTimestamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds(),
ExportApp = SH.AppName,
ExportAppVersion = runtimeOptions.Version.ToString(),
UIGFVersion = UIGF.CurrentVersion,
RegionTimeZone = PlayerUid.GetRegionTimeZoneUtcOffsetForUid(uid).Hours,
};
}
[JsonPropertyName("version")]
public required string Version { get; set; }
}

View File

@@ -1,50 +0,0 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Core.Abstraction;
using Snap.Hutao.Core.Json.Annotation;
using Snap.Hutao.Model.Entity;
using Snap.Hutao.Model.Metadata.Abstraction;
using Snap.Hutao.Web.Hoyolab.Hk4e.Event.GachaInfo;
namespace Snap.Hutao.Model.InterChange.GachaLog;
/// <summary>
/// UIGF物品
/// </summary>
[HighQuality]
internal sealed class UIGFItem : GachaLogItem, IMappingFrom<UIGFItem, GachaItem, INameQualityAccess>
{
/// <summary>
/// 额外祈愿映射
/// </summary>
[JsonPropertyName("uigf_gacha_type")]
[JsonEnum(JsonSerializeType.NumberString)]
public GachaType UIGFGachaType { get; set; } = default!;
public static UIGFItem From(GachaItem item, INameQualityAccess nameQuality)
{
return new()
{
GachaType = item.GachaType,
ItemId = $"{item.ItemId:D}",
Count = 1,
Time = item.Time,
Name = nameQuality.Name,
ItemType = GetItemTypeStringByItemId(item.ItemId),
Rank = nameQuality.Quality,
Id = item.Id,
UIGFGachaType = item.QueryType,
};
}
private static string GetItemTypeStringByItemId(uint itemId)
{
return itemId.StringLength() switch
{
8U => SH.ModelInterchangeUIGFItemTypeAvatar,
5U => SH.ModelInterchangeUIGFItemTypeWeapon,
_ => SH.ModelInterchangeUIGFItemTypeUnknown,
};
}
}

View File

@@ -1,25 +0,0 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
namespace Snap.Hutao.Model.InterChange.GachaLog;
/// <summary>
/// UIGF版本
/// </summary>
internal enum UIGFVersion
{
/// <summary>
/// 不支持的版本
/// </summary>
NotSupported,
/// <summary>
/// v2.2以及之前的版本
/// </summary>
Major2Minor2OrLower,
/// <summary>
/// v2.3以及之后的版本
/// </summary>
Major2Minor3OrHigher,
}

View File

@@ -117,12 +117,4 @@ internal partial class Avatar : INameQualityAccess,
IsUp = isUp,
};
}
public object? GetPropertyValue(string propertyName)
{
return propertyName switch
{
_ => default,
};
}
}

View File

@@ -11,7 +11,7 @@ namespace Snap.Hutao.Model.Metadata.Monster;
/// <summary>
/// 敌对生物
/// </summary>
internal sealed class Monster : IAdvancedCollectionViewItem
internal sealed partial class Monster : IAdvancedCollectionViewItem
{
internal const uint MaxLevel = 100;
@@ -90,12 +90,4 @@ internal sealed class Monster : IAdvancedCollectionViewItem
/// </summary>
[JsonIgnore]
public List<DisplayItem>? DropsView { get; set; }
public object? GetPropertyValue(string propertyName)
{
return propertyName switch
{
_ => default,
};
}
}

View File

@@ -106,12 +106,4 @@ internal sealed partial class Weapon : INameQualityAccess,
IsUp = isUp,
};
}
public object? GetPropertyValue(string propertyName)
{
return propertyName switch
{
_ => default,
};
}
}

View File

@@ -13,7 +13,7 @@
<Identity
Name="60568DGPStudio.SnapHutao"
Publisher="CN=35C8E923-85DF-49A7-9172-B39DC6312C52"
Version="1.10.6.0" />
Version="1.10.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.10.6.0" />
Version="1.10.7.0" />
<Properties>
<DisplayName>Snap Hutao Dev</DisplayName>

View File

@@ -1247,6 +1247,18 @@
<data name="ViewDialogCultivatePromotionDeltaTitle" xml:space="preserve">
<value>Add or update to current Enhancement Progression Plan</value>
</data>
<data name="ViewDialogCultivationConsumptionSaveStrategyCreateNewEntry" xml:space="preserve">
<value>Always create new adopted target items</value>
</data>
<data name="ViewDialogCultivationConsumptionSaveStrategyHeader" xml:space="preserve">
<value>Save Method</value>
</data>
<data name="ViewDialogCultivationConsumptionSaveStrategyOverwriteExisting" xml:space="preserve">
<value>Overwrite existing adopted items</value>
</data>
<data name="ViewDialogCultivationConsumptionSaveStrategyPreserveExisting" xml:space="preserve">
<value>Keep existing target items</value>
</data>
<data name="ViewDialogDailyNoteNotificationDailyTaskNotify" xml:space="preserve">
<value>Daily Commission Availability Notification</value>
</data>
@@ -1274,6 +1286,12 @@
<data name="ViewDialogDailyNoteWebhookUrlTitle" xml:space="preserve">
<value>Realtime Note Webhook URL</value>
</data>
<data name="ViewDialogExportUIGFSubtitle" xml:space="preserve">
<value>Select UID to export records</value>
</data>
<data name="ViewDialogExportUIGFTitle" xml:space="preserve">
<value>Export UIGF file</value>
</data>
<data name="ViewDialogFeedbackEnableLoopbackContent" xml:space="preserve">
<value>You need other tools to restore the loopback after it is exempted</value>
</data>
@@ -1361,6 +1379,12 @@
<data name="ViewDialogImportUIGFExportUIGFVersion" xml:space="preserve">
<value>UIGF Version</value>
</data>
<data name="ViewDialogImportUIGFSubtitle" xml:space="preserve">
<value>Select UID to import records</value>
</data>
<data name="ViewDialogImportUIGFTitle" xml:space="preserve">
<value>Import UIGF file</value>
</data>
<data name="ViewDialogLaunchGameAccountInputPlaceholder" xml:space="preserve">
<value>Input name here</value>
</data>
@@ -1382,6 +1406,9 @@
<data name="ViewDialogQRCodeTitle" xml:space="preserve">
<value>Scan the QR code with MiHoYo BBS App</value>
</data>
<data name="ViewDialogReconfirmHint" xml:space="preserve">
<value>In order to ensure that functionaries have independent judgement capability, we will irregularly update the enabling conditions for these features. If you are a content creator, do not share specific activation methods with your audience.</value>
</data>
<data name="ViewDialogReconfirmTextHeader" xml:space="preserve">
<value>To avoid enabling unintentionally, please input the &lt;b&gt;title name&lt;/b&gt; of feature you are enabling</value>
</data>
@@ -1502,6 +1529,12 @@
<data name="ViewHutaoDatabaseHeader" xml:space="preserve">
<value>Abyss Stats</value>
</data>
<data name="ViewInfoBarPanelClearAllContent" xml:space="preserve">
<value>Clear all notifications</value>
</data>
<data name="ViewInfoBarPanelContractContent" xml:space="preserve">
<value>Collapse</value>
</data>
<data name="ViewInfoBarToggleTitle" xml:space="preserve">
<value>New notification</value>
</data>
@@ -1559,6 +1592,12 @@
<data name="ViewModelAvatarPropertyShowcaseNotOpen" xml:space="preserve">
<value>Character showcase is disabled. Please activate it in the game.</value>
</data>
<data name="ViewModelAvatatPropertyExportTextError" xml:space="preserve">
<value>Failed to copy role details</value>
</data>
<data name="ViewModelAvatatPropertyExportTextSuccess" xml:space="preserve">
<value>Successfully copied role details</value>
</data>
<data name="ViewModelComplexReliquarySetViewEmptyName" xml:space="preserve">
<value>No Artifact Set</value>
</data>
@@ -1571,6 +1610,15 @@
<data name="ViewModelCultivationBatchAddIncompletedFormat" xml:space="preserve">
<value>Operation not fully completed, added/updated {0}, skipped {1}</value>
</data>
<data name="ViewModelCultivationConsumptionSaveNoItemHint" xml:space="preserve">
<value>The selected level does not require ingredients</value>
</data>
<data name="ViewModelCultivationConsumptionSaveNoProjectHint" xml:space="preserve">
<value>No plan has been created and selected</value>
</data>
<data name="ViewModelCultivationConsumptionSaveSkippedHint" xml:space="preserve">
<value>There is already a foster project for this item</value>
</data>
<data name="ViewModelCultivationEntryAddSuccess" xml:space="preserve">
<value>Successfully added to current plan</value>
</data>
@@ -1787,6 +1835,27 @@
<data name="ViewModelSettingSetGamePathDatabaseFailedTitle" xml:space="preserve">
<value>Failed to save game path</value>
</data>
<data name="ViewModelUIGFExportError" xml:space="preserve">
<value>Export failed</value>
</data>
<data name="ViewModelUIGFExportSuccess" xml:space="preserve">
<value>Exported successfully</value>
</data>
<data name="ViewModelUIGFImportDuplicatedHk4eEntry" xml:space="preserve">
<value>Imported UIGF files contain UID duplicated wish items</value>
</data>
<data name="ViewModelUIGFImportError" xml:space="preserve">
<value>Import failed</value>
</data>
<data name="ViewModelUIGFImportNoHk4eEntry" xml:space="preserve">
<value>The imported UIGF file does not contain wish data</value>
</data>
<data name="ViewModelUIGFImportNoSelectedEntry" xml:space="preserve">
<value>Please select at least one UID to import data</value>
</data>
<data name="ViewModelUIGFImportSuccess" xml:space="preserve">
<value>Imported successfully</value>
</data>
<data name="ViewModelUserAdded" xml:space="preserve">
<value>User [{0}] added successfully</value>
</data>
@@ -1880,6 +1949,9 @@
<data name="ViewPageAvatarPropertyExportAsImage" xml:space="preserve">
<value>Export as Image</value>
</data>
<data name="ViewPageAvatarPropertyExportToTextLabel" xml:space="preserve">
<value>Export text to clipboard</value>
</data>
<data name="ViewPageAvatarPropertyHeader" xml:space="preserve">
<value>Character Attributes</value>
</data>
@@ -2783,8 +2855,20 @@
<data name="ViewPageSettingTranslateNavigate" xml:space="preserve">
<value>Contribute Translations</value>
</data>
<data name="ViewPageSettingUIGFExportContent" xml:space="preserve">
<value>Export</value>
</data>
<data name="ViewPageSettingUIGFExportImportDescription" xml:space="preserve">
<value>Export/Import UIGF 4 File</value>
</data>
<data name="ViewPageSettingUIGFExportImportHeader" xml:space="preserve">
<value>Data Migration</value>
</data>
<data name="ViewPageSettingUIGFImportContent" xml:space="preserve">
<value>Import</value>
</data>
<data name="ViewPageSettingUnobtainedWishItemVisibleDescription" xml:space="preserve">
<value>在「祈愿记录-角色」与「祈愿记录-武器」中显示未抽取到的祈愿物品</value>
<value>Display Undrawn Items in "Wish Export - Characters" and "Wish Export - Weapons"</value>
</data>
<data name="ViewPageSettingUnobtainedWishItemVisibleHeader" xml:space="preserve">
<value>Undrawn Wish Items</value>
@@ -3126,7 +3210,7 @@
<value>{0} mins</value>
</data>
<data name="WebDailyNoteExpeditionRemainTime" xml:space="preserve">
<value>{0} 时 {1} 分</value>
<value>{0} hour(s) {1} minute(s)</value>
</data>
<data name="WebDailyNoteExtraTaskRewardNotAllowed" xml:space="preserve">
<value>Daily Commissions are not Completed</value>

View File

@@ -1247,6 +1247,18 @@
<data name="ViewDialogCultivatePromotionDeltaTitle" xml:space="preserve">
<value>添加或更新到当前养成计划</value>
</data>
<data name="ViewDialogCultivationConsumptionSaveStrategyCreateNewEntry" xml:space="preserve">
<value>总是创建新的养成目标物品</value>
</data>
<data name="ViewDialogCultivationConsumptionSaveStrategyHeader" xml:space="preserve">
<value>保存方式</value>
</data>
<data name="ViewDialogCultivationConsumptionSaveStrategyOverwriteExisting" xml:space="preserve">
<value>覆盖存在的养成目标物品</value>
</data>
<data name="ViewDialogCultivationConsumptionSaveStrategyPreserveExisting" xml:space="preserve">
<value>保留存在的养成目标物品</value>
</data>
<data name="ViewDialogDailyNoteNotificationDailyTaskNotify" xml:space="preserve">
<value>每日委托上线提醒</value>
</data>
@@ -1274,6 +1286,12 @@
<data name="ViewDialogDailyNoteWebhookUrlTitle" xml:space="preserve">
<value>实时便笺 Webhook Url</value>
</data>
<data name="ViewDialogExportUIGFSubtitle" xml:space="preserve">
<value>选择要导出记录的 UID</value>
</data>
<data name="ViewDialogExportUIGFTitle" xml:space="preserve">
<value>导出 UIGF 文件</value>
</data>
<data name="ViewDialogFeedbackEnableLoopbackContent" xml:space="preserve">
<value>解除限制后需要使用其他工具恢复限制</value>
</data>
@@ -1361,6 +1379,12 @@
<data name="ViewDialogImportUIGFExportUIGFVersion" xml:space="preserve">
<value>UIGF 版本</value>
</data>
<data name="ViewDialogImportUIGFSubtitle" xml:space="preserve">
<value>选择要导入记录的 UID</value>
</data>
<data name="ViewDialogImportUIGFTitle" xml:space="preserve">
<value>导入 UIGF 文件</value>
</data>
<data name="ViewDialogLaunchGameAccountInputPlaceholder" xml:space="preserve">
<value>在此处输入名称</value>
</data>
@@ -1382,6 +1406,9 @@
<data name="ViewDialogQRCodeTitle" xml:space="preserve">
<value>使用米游社扫描二维码</value>
</data>
<data name="ViewDialogReconfirmHint" xml:space="preserve">
<value>为了确保功能的启用者有独立的判断能力,我们将不定期更新这些功能的启用条件,如果您是一位内容创造者,在创作胡桃相关的内容时请不要向你的受众介绍具体的启用方法。</value>
</data>
<data name="ViewDialogReconfirmTextHeader" xml:space="preserve">
<value>为防止你在无意间启用,请输入正在启用的功能开关的&lt;b&gt;标题名称&lt;/b&gt;</value>
</data>
@@ -1502,6 +1529,12 @@
<data name="ViewHutaoDatabaseHeader" xml:space="preserve">
<value>深渊统计</value>
</data>
<data name="ViewInfoBarPanelClearAllContent" xml:space="preserve">
<value>清除所有通知</value>
</data>
<data name="ViewInfoBarPanelContractContent" xml:space="preserve">
<value>收起</value>
</data>
<data name="ViewInfoBarToggleTitle" xml:space="preserve">
<value>有新的通知</value>
</data>
@@ -1559,6 +1592,12 @@
<data name="ViewModelAvatarPropertyShowcaseNotOpen" xml:space="preserve">
<value>角色展柜尚未开启,请前往游戏操作后重试</value>
</data>
<data name="ViewModelAvatatPropertyExportTextError" xml:space="preserve">
<value>复制角色详情失败</value>
</data>
<data name="ViewModelAvatatPropertyExportTextSuccess" xml:space="preserve">
<value>复制角色详情成功</value>
</data>
<data name="ViewModelComplexReliquarySetViewEmptyName" xml:space="preserve">
<value>无圣遗物或散件</value>
</data>
@@ -1571,6 +1610,15 @@
<data name="ViewModelCultivationBatchAddIncompletedFormat" xml:space="preserve">
<value>操作未全部完成:添加/更新:{0} 个,跳过 {1} 个</value>
</data>
<data name="ViewModelCultivationConsumptionSaveNoItemHint" xml:space="preserve">
<value>选定的等级不需要养成材料</value>
</data>
<data name="ViewModelCultivationConsumptionSaveNoProjectHint" xml:space="preserve">
<value>尚未创建并选择养成计划</value>
</data>
<data name="ViewModelCultivationConsumptionSaveSkippedHint" xml:space="preserve">
<value>已存在该物品的养成项目</value>
</data>
<data name="ViewModelCultivationEntryAddSuccess" xml:space="preserve">
<value>已成功添加至当前养成计划</value>
</data>
@@ -1787,6 +1835,27 @@
<data name="ViewModelSettingSetGamePathDatabaseFailedTitle" xml:space="preserve">
<value>保存游戏路径失败</value>
</data>
<data name="ViewModelUIGFExportError" xml:space="preserve">
<value>导出失败</value>
</data>
<data name="ViewModelUIGFExportSuccess" xml:space="preserve">
<value>导出成功</value>
</data>
<data name="ViewModelUIGFImportDuplicatedHk4eEntry" xml:space="preserve">
<value>导入的 UIGF 文件中包含 UID 重复的祈愿记录项</value>
</data>
<data name="ViewModelUIGFImportError" xml:space="preserve">
<value>导入失败</value>
</data>
<data name="ViewModelUIGFImportNoHk4eEntry" xml:space="preserve">
<value>导入的 UIGF 文件中不包含祈愿数据</value>
</data>
<data name="ViewModelUIGFImportNoSelectedEntry" xml:space="preserve">
<value>请选择至少一个 UID 以导入数据</value>
</data>
<data name="ViewModelUIGFImportSuccess" xml:space="preserve">
<value>导入成功</value>
</data>
<data name="ViewModelUserAdded" xml:space="preserve">
<value>用户 [{0}] 添加成功</value>
</data>
@@ -1880,6 +1949,9 @@
<data name="ViewPageAvatarPropertyExportAsImage" xml:space="preserve">
<value>导出图片</value>
</data>
<data name="ViewPageAvatarPropertyExportToTextLabel" xml:space="preserve">
<value>导出文本到剪贴板</value>
</data>
<data name="ViewPageAvatarPropertyHeader" xml:space="preserve">
<value>角色属性</value>
</data>
@@ -2783,6 +2855,18 @@
<data name="ViewPageSettingTranslateNavigate" xml:space="preserve">
<value>贡献翻译</value>
</data>
<data name="ViewPageSettingUIGFExportContent" xml:space="preserve">
<value>Exporter</value>
</data>
<data name="ViewPageSettingUIGFExportImportDescription" xml:space="preserve">
<value>导出/导入 UIGF 4 文件</value>
</data>
<data name="ViewPageSettingUIGFExportImportHeader" xml:space="preserve">
<value>数据迁移</value>
</data>
<data name="ViewPageSettingUIGFImportContent" xml:space="preserve">
<value>Importer</value>
</data>
<data name="ViewPageSettingUnobtainedWishItemVisibleDescription" xml:space="preserve">
<value>在祈愿记录页面角色与武器页签显示未抽取到的祈愿物品</value>
</data>

View File

@@ -1247,6 +1247,18 @@
<data name="ViewDialogCultivatePromotionDeltaTitle" xml:space="preserve">
<value>Tambahkan atau perbarui ke Rencana Dev saat ini</value>
</data>
<data name="ViewDialogCultivationConsumptionSaveStrategyCreateNewEntry" xml:space="preserve">
<value>总是创建新的养成目标物品</value>
</data>
<data name="ViewDialogCultivationConsumptionSaveStrategyHeader" xml:space="preserve">
<value>保存方式</value>
</data>
<data name="ViewDialogCultivationConsumptionSaveStrategyOverwriteExisting" xml:space="preserve">
<value>覆盖存在的养成目标物品</value>
</data>
<data name="ViewDialogCultivationConsumptionSaveStrategyPreserveExisting" xml:space="preserve">
<value>保留存在的养成目标物品</value>
</data>
<data name="ViewDialogDailyNoteNotificationDailyTaskNotify" xml:space="preserve">
<value>Pemberitahuan Ketersediaan Komisi Harian</value>
</data>
@@ -1274,6 +1286,12 @@
<data name="ViewDialogDailyNoteWebhookUrlTitle" xml:space="preserve">
<value>Catatan Realtime Webhook URL</value>
</data>
<data name="ViewDialogExportUIGFSubtitle" xml:space="preserve">
<value>选择要导出记录的 UID</value>
</data>
<data name="ViewDialogExportUIGFTitle" xml:space="preserve">
<value>导出 UIGF 文件</value>
</data>
<data name="ViewDialogFeedbackEnableLoopbackContent" xml:space="preserve">
<value>Anda memerlukan alat lain untuk mengembalikan loopback setelah dikecualikan</value>
</data>
@@ -1361,6 +1379,12 @@
<data name="ViewDialogImportUIGFExportUIGFVersion" xml:space="preserve">
<value>Versi UIGF</value>
</data>
<data name="ViewDialogImportUIGFSubtitle" xml:space="preserve">
<value>选择要导入记录的 UID</value>
</data>
<data name="ViewDialogImportUIGFTitle" xml:space="preserve">
<value>导入 UIGF 文件</value>
</data>
<data name="ViewDialogLaunchGameAccountInputPlaceholder" xml:space="preserve">
<value>Masukan nama disini</value>
</data>
@@ -1382,6 +1406,9 @@
<data name="ViewDialogQRCodeTitle" xml:space="preserve">
<value>Pindai kode QR dengan Aplikasi MiHoYo BBS</value>
</data>
<data name="ViewDialogReconfirmHint" xml:space="preserve">
<value>为了确保功能的启用者有独立的判断能力,我们将不定期更新这些功能的启用条件,如果您是一位内容创造者,在创作胡桃相关的内容时请不要向你的受众介绍具体的启用方法。</value>
</data>
<data name="ViewDialogReconfirmTextHeader" xml:space="preserve">
<value>Untuk menghindari mengaktifkan secara keliru, harap masukkan &lt;b&gt;nama judul&lt;/b&gt; dari fitur yang Anda aktifkan</value>
</data>
@@ -1502,6 +1529,12 @@
<data name="ViewHutaoDatabaseHeader" xml:space="preserve">
<value>Abyss Stats</value>
</data>
<data name="ViewInfoBarPanelClearAllContent" xml:space="preserve">
<value>清除所有通知</value>
</data>
<data name="ViewInfoBarPanelContractContent" xml:space="preserve">
<value>收起</value>
</data>
<data name="ViewInfoBarToggleTitle" xml:space="preserve">
<value>Notifikasi baru</value>
</data>
@@ -1559,6 +1592,12 @@
<data name="ViewModelAvatarPropertyShowcaseNotOpen" xml:space="preserve">
<value>Pameran karakter dinonaktifkan. Silakan aktifkan di dalam permainan.</value>
</data>
<data name="ViewModelAvatatPropertyExportTextError" xml:space="preserve">
<value>复制角色详情失败</value>
</data>
<data name="ViewModelAvatatPropertyExportTextSuccess" xml:space="preserve">
<value>复制角色详情成功</value>
</data>
<data name="ViewModelComplexReliquarySetViewEmptyName" xml:space="preserve">
<value>Tidak ada set artefak</value>
</data>
@@ -1571,6 +1610,15 @@
<data name="ViewModelCultivationBatchAddIncompletedFormat" xml:space="preserve">
<value>Operasi tidak sepenuhnya selesai, ditambahkan/diperbarui {0}, dilewati {1}</value>
</data>
<data name="ViewModelCultivationConsumptionSaveNoItemHint" xml:space="preserve">
<value>选定的等级不需要养成材料</value>
</data>
<data name="ViewModelCultivationConsumptionSaveNoProjectHint" xml:space="preserve">
<value>尚未创建并选择养成计划</value>
</data>
<data name="ViewModelCultivationConsumptionSaveSkippedHint" xml:space="preserve">
<value>已存在该物品的养成项目</value>
</data>
<data name="ViewModelCultivationEntryAddSuccess" xml:space="preserve">
<value>Berhasil ditambahkan ke rencana saat ini.</value>
</data>
@@ -1787,6 +1835,27 @@
<data name="ViewModelSettingSetGamePathDatabaseFailedTitle" xml:space="preserve">
<value>Gagal menyimpan path game</value>
</data>
<data name="ViewModelUIGFExportError" xml:space="preserve">
<value>Ekspor gagal</value>
</data>
<data name="ViewModelUIGFExportSuccess" xml:space="preserve">
<value>Ekspor berhasil.</value>
</data>
<data name="ViewModelUIGFImportDuplicatedHk4eEntry" xml:space="preserve">
<value>导入的 UIGF 文件中包含 UID 重复的祈愿记录项</value>
</data>
<data name="ViewModelUIGFImportError" xml:space="preserve">
<value>Gagal Impor</value>
</data>
<data name="ViewModelUIGFImportNoHk4eEntry" xml:space="preserve">
<value>导入的 UIGF 文件中不包含祈愿数据</value>
</data>
<data name="ViewModelUIGFImportNoSelectedEntry" xml:space="preserve">
<value>请选择至少一个 UID 以导入数据</value>
</data>
<data name="ViewModelUIGFImportSuccess" xml:space="preserve">
<value>导入成功</value>
</data>
<data name="ViewModelUserAdded" xml:space="preserve">
<value>Pengguna [{0}] added successfully</value>
</data>
@@ -1880,6 +1949,9 @@
<data name="ViewPageAvatarPropertyExportAsImage" xml:space="preserve">
<value>Ekspor sebagai gambar</value>
</data>
<data name="ViewPageAvatarPropertyExportToTextLabel" xml:space="preserve">
<value>导出文本到剪贴板</value>
</data>
<data name="ViewPageAvatarPropertyHeader" xml:space="preserve">
<value>Atribut Karakter</value>
</data>
@@ -2783,6 +2855,18 @@
<data name="ViewPageSettingTranslateNavigate" xml:space="preserve">
<value>Kontribusi penerjemahan</value>
</data>
<data name="ViewPageSettingUIGFExportContent" xml:space="preserve">
<value>Ekspor</value>
</data>
<data name="ViewPageSettingUIGFExportImportDescription" xml:space="preserve">
<value>导出/导入 UIGF 4 文件</value>
</data>
<data name="ViewPageSettingUIGFExportImportHeader" xml:space="preserve">
<value>数据迁移</value>
</data>
<data name="ViewPageSettingUIGFImportContent" xml:space="preserve">
<value>Impor</value>
</data>
<data name="ViewPageSettingUnobtainedWishItemVisibleDescription" xml:space="preserve">
<value>在祈愿记录页面角色与武器页签显示未抽取到的祈愿物品</value>
</data>

View File

@@ -1247,6 +1247,18 @@
<data name="ViewDialogCultivatePromotionDeltaTitle" xml:space="preserve">
<value>現在の育成計画に追加または更新する</value>
</data>
<data name="ViewDialogCultivationConsumptionSaveStrategyCreateNewEntry" xml:space="preserve">
<value>アイテムは常に新しいドリルを作成</value>
</data>
<data name="ViewDialogCultivationConsumptionSaveStrategyHeader" xml:space="preserve">
<value>次で保存</value>
</data>
<data name="ViewDialogCultivationConsumptionSaveStrategyOverwriteExisting" xml:space="preserve">
<value>既存の育成目的アイテム</value>
</data>
<data name="ViewDialogCultivationConsumptionSaveStrategyPreserveExisting" xml:space="preserve">
<value>対象のアイテムを予約する</value>
</data>
<data name="ViewDialogDailyNoteNotificationDailyTaskNotify" xml:space="preserve">
<value>まだデイリー依頼を行っていません</value>
</data>
@@ -1274,6 +1286,12 @@
<data name="ViewDialogDailyNoteWebhookUrlTitle" xml:space="preserve">
<value>リアルタイムートのWebhook URL</value>
</data>
<data name="ViewDialogExportUIGFSubtitle" xml:space="preserve">
<value>レコードをエクスポートする UID を選択します。</value>
</data>
<data name="ViewDialogExportUIGFTitle" xml:space="preserve">
<value>UIGFファイルをエクスポートします。</value>
</data>
<data name="ViewDialogFeedbackEnableLoopbackContent" xml:space="preserve">
<value>制限を解除した後にループバックを復元するには他のツールが必要です</value>
</data>
@@ -1361,6 +1379,12 @@
<data name="ViewDialogImportUIGFExportUIGFVersion" xml:space="preserve">
<value>UIAFバージョン</value>
</data>
<data name="ViewDialogImportUIGFSubtitle" xml:space="preserve">
<value>レコードをインポートする UID を選択してください</value>
</data>
<data name="ViewDialogImportUIGFTitle" xml:space="preserve">
<value>UI GF ファイルのインポート</value>
</data>
<data name="ViewDialogLaunchGameAccountInputPlaceholder" xml:space="preserve">
<value>ここに名前を入力してください</value>
</data>
@@ -1382,6 +1406,9 @@
<data name="ViewDialogQRCodeTitle" xml:space="preserve">
<value>MiHoYo BBS を使用して QR コードをスキャンします</value>
</data>
<data name="ViewDialogReconfirmHint" xml:space="preserve">
<value>この機能の強化者には、独立した判断力の専門家が必要となります。これらの機能を定期的に更新したくない場合は、コンテンツ作成者であり、それは作成者のための明確な記述にしないでください。</value>
</data>
<data name="ViewDialogReconfirmTextHeader" xml:space="preserve">
<value>謝って有効にしないようにするためには、有効にする機能の&lt;b&gt;タイトル名&lt;/b&gt;を入力してください</value>
</data>
@@ -1502,6 +1529,12 @@
<data name="ViewHutaoDatabaseHeader" xml:space="preserve">
<value>深境螺旋集計</value>
</data>
<data name="ViewInfoBarPanelClearAllContent" xml:space="preserve">
<value>すべての通知をクリア</value>
</data>
<data name="ViewInfoBarPanelContractContent" xml:space="preserve">
<value>たたむ</value>
</data>
<data name="ViewInfoBarToggleTitle" xml:space="preserve">
<value>新しい通知</value>
</data>
@@ -1559,6 +1592,12 @@
<data name="ViewModelAvatarPropertyShowcaseNotOpen" xml:space="preserve">
<value>キャラクターラインナップが未配置か非表示です。ゲーム内のプロフィール編集で設定してください</value>
</data>
<data name="ViewModelAvatatPropertyExportTextError" xml:space="preserve">
<value>キャラクター詳細をコピーできませんでした</value>
</data>
<data name="ViewModelAvatatPropertyExportTextSuccess" xml:space="preserve">
<value>ロール詳細をコピーしました</value>
</data>
<data name="ViewModelComplexReliquarySetViewEmptyName" xml:space="preserve">
<value>聖遺物なし</value>
</data>
@@ -1571,6 +1610,15 @@
<data name="ViewModelCultivationBatchAddIncompletedFormat" xml:space="preserve">
<value>操作の一部に失敗しました:追加/更新:{0}、スキップ{1}</value>
</data>
<data name="ViewModelCultivationConsumptionSaveNoItemHint" xml:space="preserve">
<value>選択したLvでは育成材料は必要ありません</value>
</data>
<data name="ViewModelCultivationConsumptionSaveNoProjectHint" xml:space="preserve">
<value>まだ作成されておらず、養成計画を選択してください</value>
</data>
<data name="ViewModelCultivationConsumptionSaveSkippedHint" xml:space="preserve">
<value>そのアイテムは既に適用されているアイテムがあります</value>
</data>
<data name="ViewModelCultivationEntryAddSuccess" xml:space="preserve">
<value>選択中の育成計画に正常に追加されました</value>
</data>
@@ -1787,6 +1835,27 @@
<data name="ViewModelSettingSetGamePathDatabaseFailedTitle" xml:space="preserve">
<value>ゲームパスの保存に失敗しました</value>
</data>
<data name="ViewModelUIGFExportError" xml:space="preserve">
<value>エクスポート失敗</value>
</data>
<data name="ViewModelUIGFExportSuccess" xml:space="preserve">
<value>エクスポートに成功しました</value>
</data>
<data name="ViewModelUIGFImportDuplicatedHk4eEntry" xml:space="preserve">
<value>UID の重複を避けるために、インポートされたUIGFファイルには UID の重複送信</value>
</data>
<data name="ViewModelUIGFImportError" xml:space="preserve">
<value>インポート失敗</value>
</data>
<data name="ViewModelUIGFImportNoHk4eEntry" xml:space="preserve">
<value>インポートされた UI GFファイルには、祈るデータが含まれていません。</value>
</data>
<data name="ViewModelUIGFImportNoSelectedEntry" xml:space="preserve">
<value>データをインポートするには UID を指定する必要があります。</value>
</data>
<data name="ViewModelUIGFImportSuccess" xml:space="preserve">
<value>インポート成功</value>
</data>
<data name="ViewModelUserAdded" xml:space="preserve">
<value>ユーザー [{0}] を正常に追加しました</value>
</data>
@@ -1880,6 +1949,9 @@
<data name="ViewPageAvatarPropertyExportAsImage" xml:space="preserve">
<value>画像をエクスポート</value>
</data>
<data name="ViewPageAvatarPropertyExportToTextLabel" xml:space="preserve">
<value>テキストをクリップボードにコピー</value>
</data>
<data name="ViewPageAvatarPropertyHeader" xml:space="preserve">
<value>キャラクターの元素タイプ</value>
</data>
@@ -2783,6 +2855,18 @@
<data name="ViewPageSettingTranslateNavigate" xml:space="preserve">
<value>和訳を提供</value>
</data>
<data name="ViewPageSettingUIGFExportContent" xml:space="preserve">
<value>エクスポート</value>
</data>
<data name="ViewPageSettingUIGFExportImportDescription" xml:space="preserve">
<value>UI GF 4 ファイルのエクスポート/インポート</value>
</data>
<data name="ViewPageSettingUIGFExportImportHeader" xml:space="preserve">
<value>データ移行</value>
</data>
<data name="ViewPageSettingUIGFImportContent" xml:space="preserve">
<value>インポート</value>
</data>
<data name="ViewPageSettingUnobtainedWishItemVisibleDescription" xml:space="preserve">
<value>在祈愿记录页面角色与武器页签显示未抽取到的祈愿物品</value>
</data>
@@ -3126,7 +3210,7 @@
<value>{0} 分</value>
</data>
<data name="WebDailyNoteExpeditionRemainTime" xml:space="preserve">
<value>{0} {1} 分</value>
<value>{0} 回の {1} 分</value>
</data>
<data name="WebDailyNoteExtraTaskRewardNotAllowed" xml:space="preserve">
<value>すべての依頼を完了していません</value>

View File

@@ -1247,6 +1247,18 @@
<data name="ViewDialogCultivatePromotionDeltaTitle" xml:space="preserve">
<value>添加或更新到当前养成计划</value>
</data>
<data name="ViewDialogCultivationConsumptionSaveStrategyCreateNewEntry" xml:space="preserve">
<value>总是创建新的养成目标物品</value>
</data>
<data name="ViewDialogCultivationConsumptionSaveStrategyHeader" xml:space="preserve">
<value>保存方式</value>
</data>
<data name="ViewDialogCultivationConsumptionSaveStrategyOverwriteExisting" xml:space="preserve">
<value>覆盖存在的养成目标物品</value>
</data>
<data name="ViewDialogCultivationConsumptionSaveStrategyPreserveExisting" xml:space="preserve">
<value>保留存在的养成目标物品</value>
</data>
<data name="ViewDialogDailyNoteNotificationDailyTaskNotify" xml:space="preserve">
<value>일일 의뢰 가능 알림</value>
</data>
@@ -1274,6 +1286,12 @@
<data name="ViewDialogDailyNoteWebhookUrlTitle" xml:space="preserve">
<value>实时便笺 Webhook Url</value>
</data>
<data name="ViewDialogExportUIGFSubtitle" xml:space="preserve">
<value>选择要导出记录的 UID</value>
</data>
<data name="ViewDialogExportUIGFTitle" xml:space="preserve">
<value>导出 UIGF 文件</value>
</data>
<data name="ViewDialogFeedbackEnableLoopbackContent" xml:space="preserve">
<value>解除限制后需要使用其他工具恢复限制</value>
</data>
@@ -1361,6 +1379,12 @@
<data name="ViewDialogImportUIGFExportUIGFVersion" xml:space="preserve">
<value>UIGF 버전</value>
</data>
<data name="ViewDialogImportUIGFSubtitle" xml:space="preserve">
<value>选择要导入记录的 UID</value>
</data>
<data name="ViewDialogImportUIGFTitle" xml:space="preserve">
<value>导入 UIGF 文件</value>
</data>
<data name="ViewDialogLaunchGameAccountInputPlaceholder" xml:space="preserve">
<value>이름을 입력하세요</value>
</data>
@@ -1382,6 +1406,9 @@
<data name="ViewDialogQRCodeTitle" xml:space="preserve">
<value>使用米游社扫描二维码</value>
</data>
<data name="ViewDialogReconfirmHint" xml:space="preserve">
<value>为了确保功能的启用者有独立的判断能力,我们将不定期更新这些功能的启用条件,如果您是一位内容创造者,在创作胡桃相关的内容时请不要向你的受众介绍具体的启用方法。</value>
</data>
<data name="ViewDialogReconfirmTextHeader" xml:space="preserve">
<value>为防止你在无意间启用,请输入正在启用的功能开关的&lt;b&gt;标题名称&lt;/b&gt;</value>
</data>
@@ -1502,6 +1529,12 @@
<data name="ViewHutaoDatabaseHeader" xml:space="preserve">
<value>나선 비경 통계</value>
</data>
<data name="ViewInfoBarPanelClearAllContent" xml:space="preserve">
<value>清除所有通知</value>
</data>
<data name="ViewInfoBarPanelContractContent" xml:space="preserve">
<value>收起</value>
</data>
<data name="ViewInfoBarToggleTitle" xml:space="preserve">
<value>有新的通知</value>
</data>
@@ -1559,6 +1592,12 @@
<data name="ViewModelAvatarPropertyShowcaseNotOpen" xml:space="preserve">
<value>캐릭터 상세정보 보기가 꺼져있습니다. 게임에서 설정 후 다시 시도하십시오.</value>
</data>
<data name="ViewModelAvatatPropertyExportTextError" xml:space="preserve">
<value>复制角色详情失败</value>
</data>
<data name="ViewModelAvatatPropertyExportTextSuccess" xml:space="preserve">
<value>复制角色详情成功</value>
</data>
<data name="ViewModelComplexReliquarySetViewEmptyName" xml:space="preserve">
<value>성유물 없음</value>
</data>
@@ -1571,6 +1610,15 @@
<data name="ViewModelCultivationBatchAddIncompletedFormat" xml:space="preserve">
<value>操作未全部完成:添加/更新:{0} 个,跳过 {1} 个</value>
</data>
<data name="ViewModelCultivationConsumptionSaveNoItemHint" xml:space="preserve">
<value>选定的等级不需要养成材料</value>
</data>
<data name="ViewModelCultivationConsumptionSaveNoProjectHint" xml:space="preserve">
<value>尚未创建并选择养成计划</value>
</data>
<data name="ViewModelCultivationConsumptionSaveSkippedHint" xml:space="preserve">
<value>已存在该物品的养成项目</value>
</data>
<data name="ViewModelCultivationEntryAddSuccess" xml:space="preserve">
<value>현재 육성 계획 추가에 성공했습니다</value>
</data>
@@ -1787,6 +1835,27 @@
<data name="ViewModelSettingSetGamePathDatabaseFailedTitle" xml:space="preserve">
<value>保存游戏路径失败</value>
</data>
<data name="ViewModelUIGFExportError" xml:space="preserve">
<value>내보내기 실패</value>
</data>
<data name="ViewModelUIGFExportSuccess" xml:space="preserve">
<value>내보내기 성공</value>
</data>
<data name="ViewModelUIGFImportDuplicatedHk4eEntry" xml:space="preserve">
<value>导入的 UIGF 文件中包含 UID 重复的祈愿记录项</value>
</data>
<data name="ViewModelUIGFImportError" xml:space="preserve">
<value>가져오기 실패</value>
</data>
<data name="ViewModelUIGFImportNoHk4eEntry" xml:space="preserve">
<value>导入的 UIGF 文件中不包含祈愿数据</value>
</data>
<data name="ViewModelUIGFImportNoSelectedEntry" xml:space="preserve">
<value>请选择至少一个 UID 以导入数据</value>
</data>
<data name="ViewModelUIGFImportSuccess" xml:space="preserve">
<value>导入成功</value>
</data>
<data name="ViewModelUserAdded" xml:space="preserve">
<value>사용자 [{0}]가 정상적으로 추가되었습니다</value>
</data>
@@ -1880,6 +1949,9 @@
<data name="ViewPageAvatarPropertyExportAsImage" xml:space="preserve">
<value>이미지 내보내기</value>
</data>
<data name="ViewPageAvatarPropertyExportToTextLabel" xml:space="preserve">
<value>导出文本到剪贴板</value>
</data>
<data name="ViewPageAvatarPropertyHeader" xml:space="preserve">
<value>캐릭터 속성</value>
</data>
@@ -2783,6 +2855,18 @@
<data name="ViewPageSettingTranslateNavigate" xml:space="preserve">
<value>번역에 기여하기</value>
</data>
<data name="ViewPageSettingUIGFExportContent" xml:space="preserve">
<value>내보내기</value>
</data>
<data name="ViewPageSettingUIGFExportImportDescription" xml:space="preserve">
<value>导出/导入 UIGF 4 文件</value>
</data>
<data name="ViewPageSettingUIGFExportImportHeader" xml:space="preserve">
<value>数据迁移</value>
</data>
<data name="ViewPageSettingUIGFImportContent" xml:space="preserve">
<value>가져오기</value>
</data>
<data name="ViewPageSettingUnobtainedWishItemVisibleDescription" xml:space="preserve">
<value>在祈愿记录页面角色与武器页签显示未抽取到的祈愿物品</value>
</data>

View File

@@ -1247,6 +1247,18 @@
<data name="ViewDialogCultivatePromotionDeltaTitle" xml:space="preserve">
<value>Adicionar ou atualizar ao planejamento atual</value>
</data>
<data name="ViewDialogCultivationConsumptionSaveStrategyCreateNewEntry" xml:space="preserve">
<value>总是创建新的养成目标物品</value>
</data>
<data name="ViewDialogCultivationConsumptionSaveStrategyHeader" xml:space="preserve">
<value>保存方式</value>
</data>
<data name="ViewDialogCultivationConsumptionSaveStrategyOverwriteExisting" xml:space="preserve">
<value>覆盖存在的养成目标物品</value>
</data>
<data name="ViewDialogCultivationConsumptionSaveStrategyPreserveExisting" xml:space="preserve">
<value>保留存在的养成目标物品</value>
</data>
<data name="ViewDialogDailyNoteNotificationDailyTaskNotify" xml:space="preserve">
<value>Notificação diária de disponibilidade de missão</value>
</data>
@@ -1274,6 +1286,12 @@
<data name="ViewDialogDailyNoteWebhookUrlTitle" xml:space="preserve">
<value>URL do webhook do lembrete de tempo real</value>
</data>
<data name="ViewDialogExportUIGFSubtitle" xml:space="preserve">
<value>选择要导出记录的 UID</value>
</data>
<data name="ViewDialogExportUIGFTitle" xml:space="preserve">
<value>导出 UIGF 文件</value>
</data>
<data name="ViewDialogFeedbackEnableLoopbackContent" xml:space="preserve">
<value>解除限制后需要使用其他工具恢复限制</value>
</data>
@@ -1361,6 +1379,12 @@
<data name="ViewDialogImportUIGFExportUIGFVersion" xml:space="preserve">
<value>Versão do UIGF</value>
</data>
<data name="ViewDialogImportUIGFSubtitle" xml:space="preserve">
<value>选择要导入记录的 UID</value>
</data>
<data name="ViewDialogImportUIGFTitle" xml:space="preserve">
<value>导入 UIGF 文件</value>
</data>
<data name="ViewDialogLaunchGameAccountInputPlaceholder" xml:space="preserve">
<value>Insira o nome aqui</value>
</data>
@@ -1382,6 +1406,9 @@
<data name="ViewDialogQRCodeTitle" xml:space="preserve">
<value>Escaneie o código QR com o aplicativo MiHoYo BBS</value>
</data>
<data name="ViewDialogReconfirmHint" xml:space="preserve">
<value>为了确保功能的启用者有独立的判断能力,我们将不定期更新这些功能的启用条件,如果您是一位内容创造者,在创作胡桃相关的内容时请不要向你的受众介绍具体的启用方法。</value>
</data>
<data name="ViewDialogReconfirmTextHeader" xml:space="preserve">
<value>Para evitar erros na ativação, insira o &lt;b&gt;nome do título&lt;/b&gt; do recurso que está ativando</value>
</data>
@@ -1502,6 +1529,12 @@
<data name="ViewHutaoDatabaseHeader" xml:space="preserve">
<value>Status do abismo</value>
</data>
<data name="ViewInfoBarPanelClearAllContent" xml:space="preserve">
<value>清除所有通知</value>
</data>
<data name="ViewInfoBarPanelContractContent" xml:space="preserve">
<value>收起</value>
</data>
<data name="ViewInfoBarToggleTitle" xml:space="preserve">
<value>Nova notificação</value>
</data>
@@ -1559,6 +1592,12 @@
<data name="ViewModelAvatarPropertyShowcaseNotOpen" xml:space="preserve">
<value>A exibição de personagens está desativada. Por favor, ative-a no jogo.</value>
</data>
<data name="ViewModelAvatatPropertyExportTextError" xml:space="preserve">
<value>复制角色详情失败</value>
</data>
<data name="ViewModelAvatatPropertyExportTextSuccess" xml:space="preserve">
<value>复制角色详情成功</value>
</data>
<data name="ViewModelComplexReliquarySetViewEmptyName" xml:space="preserve">
<value>Nenhum artefato definido</value>
</data>
@@ -1571,6 +1610,15 @@
<data name="ViewModelCultivationBatchAddIncompletedFormat" xml:space="preserve">
<value>Operação não totalmente concluída, adicionado/atualizado {0}, pulado {1}</value>
</data>
<data name="ViewModelCultivationConsumptionSaveNoItemHint" xml:space="preserve">
<value>选定的等级不需要养成材料</value>
</data>
<data name="ViewModelCultivationConsumptionSaveNoProjectHint" xml:space="preserve">
<value>尚未创建并选择养成计划</value>
</data>
<data name="ViewModelCultivationConsumptionSaveSkippedHint" xml:space="preserve">
<value>已存在该物品的养成项目</value>
</data>
<data name="ViewModelCultivationEntryAddSuccess" xml:space="preserve">
<value>Adicionado com sucesso ao planejamento</value>
</data>
@@ -1787,6 +1835,27 @@
<data name="ViewModelSettingSetGamePathDatabaseFailedTitle" xml:space="preserve">
<value>Falha ao salvar o caminho do jogo</value>
</data>
<data name="ViewModelUIGFExportError" xml:space="preserve">
<value>Falha na exportação</value>
</data>
<data name="ViewModelUIGFExportSuccess" xml:space="preserve">
<value>Exportado com sucesso</value>
</data>
<data name="ViewModelUIGFImportDuplicatedHk4eEntry" xml:space="preserve">
<value>导入的 UIGF 文件中包含 UID 重复的祈愿记录项</value>
</data>
<data name="ViewModelUIGFImportError" xml:space="preserve">
<value>Falha na importação</value>
</data>
<data name="ViewModelUIGFImportNoHk4eEntry" xml:space="preserve">
<value>导入的 UIGF 文件中不包含祈愿数据</value>
</data>
<data name="ViewModelUIGFImportNoSelectedEntry" xml:space="preserve">
<value>请选择至少一个 UID 以导入数据</value>
</data>
<data name="ViewModelUIGFImportSuccess" xml:space="preserve">
<value>导入成功</value>
</data>
<data name="ViewModelUserAdded" xml:space="preserve">
<value>Usuário [{0}] adicionado com sucesso</value>
</data>
@@ -1880,6 +1949,9 @@
<data name="ViewPageAvatarPropertyExportAsImage" xml:space="preserve">
<value>Exportar como imagem</value>
</data>
<data name="ViewPageAvatarPropertyExportToTextLabel" xml:space="preserve">
<value>导出文本到剪贴板</value>
</data>
<data name="ViewPageAvatarPropertyHeader" xml:space="preserve">
<value>Atributos do personagem</value>
</data>
@@ -2783,6 +2855,18 @@
<data name="ViewPageSettingTranslateNavigate" xml:space="preserve">
<value>Contribuir com traduções</value>
</data>
<data name="ViewPageSettingUIGFExportContent" xml:space="preserve">
<value>Exportar</value>
</data>
<data name="ViewPageSettingUIGFExportImportDescription" xml:space="preserve">
<value>导出/导入 UIGF 4 文件</value>
</data>
<data name="ViewPageSettingUIGFExportImportHeader" xml:space="preserve">
<value>数据迁移</value>
</data>
<data name="ViewPageSettingUIGFImportContent" xml:space="preserve">
<value>Importar</value>
</data>
<data name="ViewPageSettingUnobtainedWishItemVisibleDescription" xml:space="preserve">
<value>在祈愿记录页面角色与武器页签显示未抽取到的祈愿物品</value>
</data>

View File

@@ -1247,6 +1247,18 @@
<data name="ViewDialogCultivatePromotionDeltaTitle" xml:space="preserve">
<value>添加或更新到当前养成计划</value>
</data>
<data name="ViewDialogCultivationConsumptionSaveStrategyCreateNewEntry" xml:space="preserve">
<value>总是创建新的养成目标物品</value>
</data>
<data name="ViewDialogCultivationConsumptionSaveStrategyHeader" xml:space="preserve">
<value>保存方式</value>
</data>
<data name="ViewDialogCultivationConsumptionSaveStrategyOverwriteExisting" xml:space="preserve">
<value>覆盖存在的养成目标物品</value>
</data>
<data name="ViewDialogCultivationConsumptionSaveStrategyPreserveExisting" xml:space="preserve">
<value>保留存在的养成目标物品</value>
</data>
<data name="ViewDialogDailyNoteNotificationDailyTaskNotify" xml:space="preserve">
<value>每日委托上线提醒</value>
</data>
@@ -1274,6 +1286,12 @@
<data name="ViewDialogDailyNoteWebhookUrlTitle" xml:space="preserve">
<value>实时便笺 Webhook Url</value>
</data>
<data name="ViewDialogExportUIGFSubtitle" xml:space="preserve">
<value>选择要导出记录的 UID</value>
</data>
<data name="ViewDialogExportUIGFTitle" xml:space="preserve">
<value>导出 UIGF 文件</value>
</data>
<data name="ViewDialogFeedbackEnableLoopbackContent" xml:space="preserve">
<value>解除限制后需要使用其他工具恢复限制</value>
</data>
@@ -1361,6 +1379,12 @@
<data name="ViewDialogImportUIGFExportUIGFVersion" xml:space="preserve">
<value>UIGF 版本</value>
</data>
<data name="ViewDialogImportUIGFSubtitle" xml:space="preserve">
<value>选择要导入记录的 UID</value>
</data>
<data name="ViewDialogImportUIGFTitle" xml:space="preserve">
<value>导入 UIGF 文件</value>
</data>
<data name="ViewDialogLaunchGameAccountInputPlaceholder" xml:space="preserve">
<value>在此处输入名称</value>
</data>
@@ -1382,6 +1406,9 @@
<data name="ViewDialogQRCodeTitle" xml:space="preserve">
<value>使用米游社扫描二维码</value>
</data>
<data name="ViewDialogReconfirmHint" xml:space="preserve">
<value>为了确保功能的启用者有独立的判断能力,我们将不定期更新这些功能的启用条件,如果您是一位内容创造者,在创作胡桃相关的内容时请不要向你的受众介绍具体的启用方法。</value>
</data>
<data name="ViewDialogReconfirmTextHeader" xml:space="preserve">
<value>为防止你在无意间启用,请输入正在启用的功能开关的&lt;b&gt;标题名称&lt;/b&gt;</value>
</data>
@@ -1502,6 +1529,12 @@
<data name="ViewHutaoDatabaseHeader" xml:space="preserve">
<value>深渊统计</value>
</data>
<data name="ViewInfoBarPanelClearAllContent" xml:space="preserve">
<value>清除所有通知</value>
</data>
<data name="ViewInfoBarPanelContractContent" xml:space="preserve">
<value>收起</value>
</data>
<data name="ViewInfoBarToggleTitle" xml:space="preserve">
<value>有新的通知</value>
</data>
@@ -1559,6 +1592,12 @@
<data name="ViewModelAvatarPropertyShowcaseNotOpen" xml:space="preserve">
<value>角色展柜尚未开启,请前往游戏操作后重试</value>
</data>
<data name="ViewModelAvatatPropertyExportTextError" xml:space="preserve">
<value>复制角色详情失败</value>
</data>
<data name="ViewModelAvatatPropertyExportTextSuccess" xml:space="preserve">
<value>复制角色详情成功</value>
</data>
<data name="ViewModelComplexReliquarySetViewEmptyName" xml:space="preserve">
<value>无圣遗物或散件</value>
</data>
@@ -1571,6 +1610,15 @@
<data name="ViewModelCultivationBatchAddIncompletedFormat" xml:space="preserve">
<value>操作未全部完成:添加 / 更新:{0} 个,跳过 {1} 个</value>
</data>
<data name="ViewModelCultivationConsumptionSaveNoItemHint" xml:space="preserve">
<value>选定的等级不需要养成材料</value>
</data>
<data name="ViewModelCultivationConsumptionSaveNoProjectHint" xml:space="preserve">
<value>尚未创建并选择养成计划</value>
</data>
<data name="ViewModelCultivationConsumptionSaveSkippedHint" xml:space="preserve">
<value>已存在该物品的养成项目</value>
</data>
<data name="ViewModelCultivationEntryAddSuccess" xml:space="preserve">
<value>已成功添加至当前养成计划</value>
</data>
@@ -1787,6 +1835,27 @@
<data name="ViewModelSettingSetGamePathDatabaseFailedTitle" xml:space="preserve">
<value>保存游戏路径失败</value>
</data>
<data name="ViewModelUIGFExportError" xml:space="preserve">
<value>导出失败</value>
</data>
<data name="ViewModelUIGFExportSuccess" xml:space="preserve">
<value>导出成功</value>
</data>
<data name="ViewModelUIGFImportDuplicatedHk4eEntry" xml:space="preserve">
<value>导入的 UIGF 文件中包含 UID 重复的祈愿记录项</value>
</data>
<data name="ViewModelUIGFImportError" xml:space="preserve">
<value>导入失败</value>
</data>
<data name="ViewModelUIGFImportNoHk4eEntry" xml:space="preserve">
<value>导入的 UIGF 文件中不包含祈愿数据</value>
</data>
<data name="ViewModelUIGFImportNoSelectedEntry" xml:space="preserve">
<value>请选择至少一个 UID 以导入数据</value>
</data>
<data name="ViewModelUIGFImportSuccess" xml:space="preserve">
<value>导入成功</value>
</data>
<data name="ViewModelUserAdded" xml:space="preserve">
<value>用户 [{0}] 添加成功</value>
</data>
@@ -1880,6 +1949,9 @@
<data name="ViewPageAvatarPropertyExportAsImage" xml:space="preserve">
<value>导出图片</value>
</data>
<data name="ViewPageAvatarPropertyExportToTextLabel" xml:space="preserve">
<value>导出文本到剪贴板</value>
</data>
<data name="ViewPageAvatarPropertyHeader" xml:space="preserve">
<value>角色属性</value>
</data>
@@ -2783,6 +2855,18 @@
<data name="ViewPageSettingTranslateNavigate" xml:space="preserve">
<value>贡献翻译</value>
</data>
<data name="ViewPageSettingUIGFExportContent" xml:space="preserve">
<value>导出</value>
</data>
<data name="ViewPageSettingUIGFExportImportDescription" xml:space="preserve">
<value>导出/导入 UIGF 4 文件</value>
</data>
<data name="ViewPageSettingUIGFExportImportHeader" xml:space="preserve">
<value>数据迁移</value>
</data>
<data name="ViewPageSettingUIGFImportContent" xml:space="preserve">
<value>导入</value>
</data>
<data name="ViewPageSettingUnobtainedWishItemVisibleDescription" xml:space="preserve">
<value>在「祈愿记录-角色」与「祈愿记录-武器」中显示未抽取到的祈愿物品</value>
</data>

View File

@@ -1247,6 +1247,18 @@
<data name="ViewDialogCultivatePromotionDeltaTitle" xml:space="preserve">
<value>添加或更新到当前养成计划</value>
</data>
<data name="ViewDialogCultivationConsumptionSaveStrategyCreateNewEntry" xml:space="preserve">
<value>总是创建新的养成目标物品</value>
</data>
<data name="ViewDialogCultivationConsumptionSaveStrategyHeader" xml:space="preserve">
<value>保存方式</value>
</data>
<data name="ViewDialogCultivationConsumptionSaveStrategyOverwriteExisting" xml:space="preserve">
<value>覆盖存在的养成目标物品</value>
</data>
<data name="ViewDialogCultivationConsumptionSaveStrategyPreserveExisting" xml:space="preserve">
<value>保留存在的养成目标物品</value>
</data>
<data name="ViewDialogDailyNoteNotificationDailyTaskNotify" xml:space="preserve">
<value>每日委托上线提醒</value>
</data>
@@ -1274,6 +1286,12 @@
<data name="ViewDialogDailyNoteWebhookUrlTitle" xml:space="preserve">
<value>实时便笺 Webhook Url</value>
</data>
<data name="ViewDialogExportUIGFSubtitle" xml:space="preserve">
<value>选择要导出记录的 UID</value>
</data>
<data name="ViewDialogExportUIGFTitle" xml:space="preserve">
<value>导出 UIGF 文件</value>
</data>
<data name="ViewDialogFeedbackEnableLoopbackContent" xml:space="preserve">
<value>解除限制后需要使用其他工具恢复限制</value>
</data>
@@ -1361,6 +1379,12 @@
<data name="ViewDialogImportUIGFExportUIGFVersion" xml:space="preserve">
<value>UIGF 版本</value>
</data>
<data name="ViewDialogImportUIGFSubtitle" xml:space="preserve">
<value>选择要导入记录的 UID</value>
</data>
<data name="ViewDialogImportUIGFTitle" xml:space="preserve">
<value>导入 UIGF 文件</value>
</data>
<data name="ViewDialogLaunchGameAccountInputPlaceholder" xml:space="preserve">
<value>Введите имя</value>
</data>
@@ -1382,6 +1406,9 @@
<data name="ViewDialogQRCodeTitle" xml:space="preserve">
<value>Отсканируйте QR-код с помощью Miyusha</value>
</data>
<data name="ViewDialogReconfirmHint" xml:space="preserve">
<value>为了确保功能的启用者有独立的判断能力,我们将不定期更新这些功能的启用条件,如果您是一位内容创造者,在创作胡桃相关的内容时请不要向你的受众介绍具体的启用方法。</value>
</data>
<data name="ViewDialogReconfirmTextHeader" xml:space="preserve">
<value>为防止你在无意间启用,请输入正在启用的功能开关的&lt;b&gt;标题名称&lt;/b&gt;</value>
</data>
@@ -1502,6 +1529,12 @@
<data name="ViewHutaoDatabaseHeader" xml:space="preserve">
<value>深渊统计</value>
</data>
<data name="ViewInfoBarPanelClearAllContent" xml:space="preserve">
<value>清除所有通知</value>
</data>
<data name="ViewInfoBarPanelContractContent" xml:space="preserve">
<value>收起</value>
</data>
<data name="ViewInfoBarToggleTitle" xml:space="preserve">
<value>有新的通知</value>
</data>
@@ -1559,6 +1592,12 @@
<data name="ViewModelAvatarPropertyShowcaseNotOpen" xml:space="preserve">
<value>角色展柜尚未开启,请前往游戏操作后重试</value>
</data>
<data name="ViewModelAvatatPropertyExportTextError" xml:space="preserve">
<value>复制角色详情失败</value>
</data>
<data name="ViewModelAvatatPropertyExportTextSuccess" xml:space="preserve">
<value>复制角色详情成功</value>
</data>
<data name="ViewModelComplexReliquarySetViewEmptyName" xml:space="preserve">
<value>无圣遗物或散件</value>
</data>
@@ -1571,6 +1610,15 @@
<data name="ViewModelCultivationBatchAddIncompletedFormat" xml:space="preserve">
<value>操作未全部完成:添加/更新:{0} 个,跳过 {1} 个</value>
</data>
<data name="ViewModelCultivationConsumptionSaveNoItemHint" xml:space="preserve">
<value>选定的等级不需要养成材料</value>
</data>
<data name="ViewModelCultivationConsumptionSaveNoProjectHint" xml:space="preserve">
<value>尚未创建并选择养成计划</value>
</data>
<data name="ViewModelCultivationConsumptionSaveSkippedHint" xml:space="preserve">
<value>已存在该物品的养成项目</value>
</data>
<data name="ViewModelCultivationEntryAddSuccess" xml:space="preserve">
<value>已成功添加至当前养成计划</value>
</data>
@@ -1787,6 +1835,27 @@
<data name="ViewModelSettingSetGamePathDatabaseFailedTitle" xml:space="preserve">
<value>Ошибка сохранения игрового пути</value>
</data>
<data name="ViewModelUIGFExportError" xml:space="preserve">
<value>导出失败</value>
</data>
<data name="ViewModelUIGFExportSuccess" xml:space="preserve">
<value>导出成功</value>
</data>
<data name="ViewModelUIGFImportDuplicatedHk4eEntry" xml:space="preserve">
<value>导入的 UIGF 文件中包含 UID 重复的祈愿记录项</value>
</data>
<data name="ViewModelUIGFImportError" xml:space="preserve">
<value>Ошибка загрузки</value>
</data>
<data name="ViewModelUIGFImportNoHk4eEntry" xml:space="preserve">
<value>导入的 UIGF 文件中不包含祈愿数据</value>
</data>
<data name="ViewModelUIGFImportNoSelectedEntry" xml:space="preserve">
<value>请选择至少一个 UID 以导入数据</value>
</data>
<data name="ViewModelUIGFImportSuccess" xml:space="preserve">
<value>导入成功</value>
</data>
<data name="ViewModelUserAdded" xml:space="preserve">
<value>用户 [{0}] 添加成功</value>
</data>
@@ -1880,6 +1949,9 @@
<data name="ViewPageAvatarPropertyExportAsImage" xml:space="preserve">
<value>导出图片</value>
</data>
<data name="ViewPageAvatarPropertyExportToTextLabel" xml:space="preserve">
<value>导出文本到剪贴板</value>
</data>
<data name="ViewPageAvatarPropertyHeader" xml:space="preserve">
<value>角色属性</value>
</data>
@@ -2783,6 +2855,18 @@
<data name="ViewPageSettingTranslateNavigate" xml:space="preserve">
<value>贡献翻译</value>
</data>
<data name="ViewPageSettingUIGFExportContent" xml:space="preserve">
<value>Экспортировать</value>
</data>
<data name="ViewPageSettingUIGFExportImportDescription" xml:space="preserve">
<value>导出/导入 UIGF 4 文件</value>
</data>
<data name="ViewPageSettingUIGFExportImportHeader" xml:space="preserve">
<value>数据迁移</value>
</data>
<data name="ViewPageSettingUIGFImportContent" xml:space="preserve">
<value>Импорт</value>
</data>
<data name="ViewPageSettingUnobtainedWishItemVisibleDescription" xml:space="preserve">
<value>在祈愿记录页面角色与武器页签显示未抽取到的祈愿物品</value>
</data>

View File

@@ -1247,6 +1247,18 @@
<data name="ViewDialogCultivatePromotionDeltaTitle" xml:space="preserve">
<value>添加或更新到当前养成计划</value>
</data>
<data name="ViewDialogCultivationConsumptionSaveStrategyCreateNewEntry" xml:space="preserve">
<value>总是创建新的养成目标物品</value>
</data>
<data name="ViewDialogCultivationConsumptionSaveStrategyHeader" xml:space="preserve">
<value>保存方式</value>
</data>
<data name="ViewDialogCultivationConsumptionSaveStrategyOverwriteExisting" xml:space="preserve">
<value>覆盖存在的养成目标物品</value>
</data>
<data name="ViewDialogCultivationConsumptionSaveStrategyPreserveExisting" xml:space="preserve">
<value>保留存在的养成目标物品</value>
</data>
<data name="ViewDialogDailyNoteNotificationDailyTaskNotify" xml:space="preserve">
<value>每日委托上线提醒</value>
</data>
@@ -1274,6 +1286,12 @@
<data name="ViewDialogDailyNoteWebhookUrlTitle" xml:space="preserve">
<value>实时便笺 Webhook Url</value>
</data>
<data name="ViewDialogExportUIGFSubtitle" xml:space="preserve">
<value>选择要导出记录的 UID</value>
</data>
<data name="ViewDialogExportUIGFTitle" xml:space="preserve">
<value>导出 UIGF 文件</value>
</data>
<data name="ViewDialogFeedbackEnableLoopbackContent" xml:space="preserve">
<value>解除限制后需要使用其他工具恢复限制</value>
</data>
@@ -1361,6 +1379,12 @@
<data name="ViewDialogImportUIGFExportUIGFVersion" xml:space="preserve">
<value>UIGF 版本</value>
</data>
<data name="ViewDialogImportUIGFSubtitle" xml:space="preserve">
<value>选择要导入记录的 UID</value>
</data>
<data name="ViewDialogImportUIGFTitle" xml:space="preserve">
<value>导入 UIGF 文件</value>
</data>
<data name="ViewDialogLaunchGameAccountInputPlaceholder" xml:space="preserve">
<value>在此处输入名称</value>
</data>
@@ -1382,6 +1406,9 @@
<data name="ViewDialogQRCodeTitle" xml:space="preserve">
<value>使用米游社扫描二维码</value>
</data>
<data name="ViewDialogReconfirmHint" xml:space="preserve">
<value>为了确保功能的启用者有独立的判断能力,我们将不定期更新这些功能的启用条件,如果您是一位内容创造者,在创作胡桃相关的内容时请不要向你的受众介绍具体的启用方法。</value>
</data>
<data name="ViewDialogReconfirmTextHeader" xml:space="preserve">
<value>为防止你在无意间启用,请输入正在启用的功能开关的&lt;b&gt;标题名称&lt;/b&gt;</value>
</data>
@@ -1502,6 +1529,12 @@
<data name="ViewHutaoDatabaseHeader" xml:space="preserve">
<value>深渊统计</value>
</data>
<data name="ViewInfoBarPanelClearAllContent" xml:space="preserve">
<value>清除所有通知</value>
</data>
<data name="ViewInfoBarPanelContractContent" xml:space="preserve">
<value>收起</value>
</data>
<data name="ViewInfoBarToggleTitle" xml:space="preserve">
<value>有新的通知</value>
</data>
@@ -1559,6 +1592,12 @@
<data name="ViewModelAvatarPropertyShowcaseNotOpen" xml:space="preserve">
<value>角色展柜尚未开启,请前往游戏操作后重试</value>
</data>
<data name="ViewModelAvatatPropertyExportTextError" xml:space="preserve">
<value>复制角色详情失败</value>
</data>
<data name="ViewModelAvatatPropertyExportTextSuccess" xml:space="preserve">
<value>复制角色详情成功</value>
</data>
<data name="ViewModelComplexReliquarySetViewEmptyName" xml:space="preserve">
<value>无圣遗物或散件</value>
</data>
@@ -1571,6 +1610,15 @@
<data name="ViewModelCultivationBatchAddIncompletedFormat" xml:space="preserve">
<value>操作未全部完成:添加/更新:{0} 个,跳过 {1} 个</value>
</data>
<data name="ViewModelCultivationConsumptionSaveNoItemHint" xml:space="preserve">
<value>选定的等级不需要养成材料</value>
</data>
<data name="ViewModelCultivationConsumptionSaveNoProjectHint" xml:space="preserve">
<value>尚未创建并选择养成计划</value>
</data>
<data name="ViewModelCultivationConsumptionSaveSkippedHint" xml:space="preserve">
<value>已存在该物品的养成项目</value>
</data>
<data name="ViewModelCultivationEntryAddSuccess" xml:space="preserve">
<value>已成功添加至当前养成计划</value>
</data>
@@ -1787,6 +1835,27 @@
<data name="ViewModelSettingSetGamePathDatabaseFailedTitle" xml:space="preserve">
<value>保存游戏路径失败</value>
</data>
<data name="ViewModelUIGFExportError" xml:space="preserve">
<value>导出失败</value>
</data>
<data name="ViewModelUIGFExportSuccess" xml:space="preserve">
<value>导出成功</value>
</data>
<data name="ViewModelUIGFImportDuplicatedHk4eEntry" xml:space="preserve">
<value>导入的 UIGF 文件中包含 UID 重复的祈愿记录项</value>
</data>
<data name="ViewModelUIGFImportError" xml:space="preserve">
<value>导入失败</value>
</data>
<data name="ViewModelUIGFImportNoHk4eEntry" xml:space="preserve">
<value>导入的 UIGF 文件中不包含祈愿数据</value>
</data>
<data name="ViewModelUIGFImportNoSelectedEntry" xml:space="preserve">
<value>请选择至少一个 UID 以导入数据</value>
</data>
<data name="ViewModelUIGFImportSuccess" xml:space="preserve">
<value>导入成功</value>
</data>
<data name="ViewModelUserAdded" xml:space="preserve">
<value>用户 [{0}] 添加成功</value>
</data>
@@ -1880,6 +1949,9 @@
<data name="ViewPageAvatarPropertyExportAsImage" xml:space="preserve">
<value>导出图片</value>
</data>
<data name="ViewPageAvatarPropertyExportToTextLabel" xml:space="preserve">
<value>导出文本到剪贴板</value>
</data>
<data name="ViewPageAvatarPropertyHeader" xml:space="preserve">
<value>角色属性</value>
</data>
@@ -2783,6 +2855,18 @@
<data name="ViewPageSettingTranslateNavigate" xml:space="preserve">
<value>贡献翻译</value>
</data>
<data name="ViewPageSettingUIGFExportContent" xml:space="preserve">
<value>Xuất</value>
</data>
<data name="ViewPageSettingUIGFExportImportDescription" xml:space="preserve">
<value>导出/导入 UIGF 4 文件</value>
</data>
<data name="ViewPageSettingUIGFExportImportHeader" xml:space="preserve">
<value>数据迁移</value>
</data>
<data name="ViewPageSettingUIGFImportContent" xml:space="preserve">
<value>Nhập</value>
</data>
<data name="ViewPageSettingUnobtainedWishItemVisibleDescription" xml:space="preserve">
<value>在祈愿记录页面角色与武器页签显示未抽取到的祈愿物品</value>
</data>

View File

@@ -151,7 +151,7 @@
<value>刪除</value>
</data>
<data name="ControlAutoSuggestTokenBoxSelectAllMenuItem" xml:space="preserve">
<value>选择全部</value>
<value>選擇全部</value>
</data>
<data name="ControlImageCachedImageInvalidResourceUri" xml:space="preserve">
<value>無效的 URI</value>
@@ -557,10 +557,10 @@
<value>必須登入 米游社/ HoYoLAB 並選擇一個用戶與角色</value>
</data>
<data name="ServerGachaLogServiceDeleteEntrySucceed" xml:space="preserve">
<value>刪除了 UID{0} 的 {1} 筆祈願錄</value>
<value>刪除了 UID{0} 的 {1} 筆祈願錄</value>
</data>
<data name="ServerGachaLogServiceInsufficientRecordSlot" xml:space="preserve">
<value>胡桃雲儲存的祈願錄存檔數已達當前帳號上限</value>
<value>胡桃雲儲存的祈願錄存檔數已達當前帳號上限</value>
</data>
<data name="ServerGachaLogServiceInsufficientTime" xml:space="preserve">
<value>未開通祈願紀錄上傳服務或已到期</value>
@@ -572,7 +572,7 @@
<value>數據異常,無法儲存至雲端,請勿跨帳號上傳或嘗試删除雲端數據後重試</value>
</data>
<data name="ServerGachaLogServiceUploadEntrySucceed" xml:space="preserve">
<value>上傳了 UID{0} 的 {1} 筆祈願錄,儲存了 {2} 筆</value>
<value>上傳了 UID{0} 的 {1} 筆祈願錄,儲存了 {2} 筆</value>
</data>
<data name="ServerPassportLoginRequired" xml:space="preserve">
<value>請先登入或註冊胡桃帳號</value>
@@ -623,40 +623,40 @@
<value>驗證請求過快,請 1 分鐘後再試</value>
</data>
<data name="ServerRecordBannedUid" xml:space="preserve">
<value>上傳深淵錄失敗,當前 UID 已被胡桃數據庫封禁</value>
<value>上傳深淵錄失敗,當前 UID 已被胡桃數據庫封禁</value>
</data>
<data name="ServerRecordComputingStatistics" xml:space="preserve">
<value>上傳深淵錄失敗,正在計算統計數據</value>
<value>上傳深淵錄失敗,正在計算統計數據</value>
</data>
<data name="ServerRecordComputingStatistics2" xml:space="preserve">
<value>獲取數據失敗,正在計算統計數據</value>
</data>
<data name="ServerRecordInternalException" xml:space="preserve">
<value>上傳深淵錄失敗,伺服器異常,請盡快聯繫開發者解決</value>
<value>上傳深淵錄失敗,伺服器異常,請盡快聯繫開發者解決</value>
</data>
<data name="ServerRecordInvalidData" xml:space="preserve">
<value>上傳深淵錄失敗,存在無效的數據</value>
<value>上傳深淵錄失敗,存在無效的數據</value>
</data>
<data name="ServerRecordInvalidUid" xml:space="preserve">
<value>無效的 UID</value>
</data>
<data name="ServerRecordNotCurrentSchedule" xml:space="preserve">
<value>上傳深淵錄失敗,不是本期數據</value>
<value>上傳深淵錄失敗,不是本期數據</value>
</data>
<data name="ServerRecordPreviousRequestNotCompleted" xml:space="preserve">
<value>上傳深淵錄失敗,當前 UID 的紀錄仍在處理中,請勿重複操作</value>
<value>上傳深淵錄失敗,當前 UID 的紀錄仍在處理中,請勿重複操作</value>
</data>
<data name="ServerRecordUploadSuccessAndGachaLogServiceTimeExtended" xml:space="preserve">
<value>上傳深淵錄成功,獲贈祈願錄上傳服務時長</value>
<value>上傳深淵錄成功,獲贈祈願錄上傳服務時長</value>
</data>
<data name="ServerRecordUploadSuccessButNoPassport" xml:space="preserve">
<value>上傳深淵錄成功,但未登入胡桃通行證,無法獲贈祈願錄上傳服務時長</value>
<value>上傳深淵錄成功,但未登入胡桃通行證,無法獲贈祈願錄上傳服務時長</value>
</data>
<data name="ServerRecordUploadSuccessButNoSuchUser" xml:space="preserve">
<value>上傳深淵錄成功,但無法找到用戶,無法獲贈祈願錄上傳服務時長</value>
<value>上傳深淵錄成功,但無法找到用戶,無法獲贈祈願錄上傳服務時長</value>
</data>
<data name="ServerRecordUploadSuccessButNotFirstTimeAtCurrentSchedule" xml:space="preserve">
<value>上傳深淵錄成功,但不是本期首次提交,無法獲贈祈願錄上傳服務時長</value>
<value>上傳深淵錄成功,但不是本期首次提交,無法獲贈祈願錄上傳服務時長</value>
</data>
<data name="ServiceAchievementImportResultFormat" xml:space="preserve">
<value>新增:{0} 個成就 | 更新:{1} 個成就 | 删除:{2} 個成就</value>
@@ -878,10 +878,10 @@
<value>由 {0} 啟動</value>
</data>
<data name="ServiceGachaLogArchiveCollectionUserdataCorruptedMessage" xml:space="preserve">
<value>無法獲取祈願錄:{0}</value>
<value>無法獲取祈願錄:{0}</value>
</data>
<data name="ServiceGachaLogEndIdUserdataCorruptedMessage" xml:space="preserve">
<value>無法獲取祈願錄 End Id</value>
<value>無法獲取祈願錄 End Id</value>
</data>
<data name="ServiceGachaLogFactoryAvatarWishName" xml:space="preserve">
<value>角色活動</value>
@@ -899,7 +899,7 @@
<value>獲取雲端祈願紀錄失敗</value>
</data>
<data name="ServiceGachaLogHutaoCloudServiceNotAllowed" xml:space="preserve">
<value>祈願錄上傳服務不可用</value>
<value>祈願錄上傳服務不可用</value>
</data>
<data name="ServiceGachaLogUIGFImportItemInvalidFormat" xml:space="preserve">
<value>數據包含異常物品, Id{0}</value>
@@ -921,7 +921,7 @@
<value>提供的 URL 無效</value>
</data>
<data name="ServiceGachaLogUrlProviderStokenUnsupported" xml:space="preserve">
<value>HoYoLAB 號不支持使用 SToken 重新整理祈願錄</value>
<value>HoYoLAB 號不支持使用 SToken 重新整理祈願錄</value>
</data>
<data name="ServiceGachaLogUrlProviderUrlLanguageNotMatchCurrentLocale" xml:space="preserve">
<value>URL 中的語言:{0} 與胡桃的語言:{1} 不對應,請切換到對應語言重試</value>
@@ -978,16 +978,16 @@
<value>等待遊戲進程退出</value>
</data>
<data name="ServiceGameLaunchUnlockerKindIslandDescription" xml:space="preserve">
<value>解锁模块注入游戏进程,非常危,但容易成功</value>
<value>解鎖模組注入遊戲進程,非常危,但容易成功</value>
</data>
<data name="ServiceGameLaunchUnlockerKindIslandName" xml:space="preserve">
<value>注入</value>
</data>
<data name="ServiceGameLaunchUnlockerKindLegacyDescription" xml:space="preserve">
<value>典的程外存操作,较为危险,但容易失</value>
<value>典的程外存操作,較為危險,但容易失</value>
</data>
<data name="ServiceGameLaunchUnlockerKindLegacyName" xml:space="preserve">
<value>典</value>
<value>典</value>
</data>
<data name="ServiceGameLocatorFileOpenPickerCommitText" xml:space="preserve">
<value>請選擇遊戲本體</value>
@@ -1041,7 +1041,7 @@
<value>在尋找必要的模組時遇到問題:尋找模組超時,請重試</value>
</data>
<data name="ServiceGameUnlockerInterestedPatternNotFound" xml:space="preserve">
<value>在比對內存時遇到問題:無法比對到期望的內容</value>
<value>在匹配內存時遇到問題:無法匹配到期望的內容</value>
</data>
<data name="ServiceGameUnlockerReadModuleMemoryCopyVirtualMemoryFailed" xml:space="preserve">
<value>在讀取必要的模組內存時遇到問題:無法將模組內存複製到指定位置</value>
@@ -1247,6 +1247,18 @@
<data name="ViewDialogCultivatePromotionDeltaTitle" xml:space="preserve">
<value>添加或更新到當前養成計劃</value>
</data>
<data name="ViewDialogCultivationConsumptionSaveStrategyCreateNewEntry" xml:space="preserve">
<value>总是创建新的养成目标物品</value>
</data>
<data name="ViewDialogCultivationConsumptionSaveStrategyHeader" xml:space="preserve">
<value>保存方式</value>
</data>
<data name="ViewDialogCultivationConsumptionSaveStrategyOverwriteExisting" xml:space="preserve">
<value>覆盖存在的养成目标物品</value>
</data>
<data name="ViewDialogCultivationConsumptionSaveStrategyPreserveExisting" xml:space="preserve">
<value>保留存在的养成目标物品</value>
</data>
<data name="ViewDialogDailyNoteNotificationDailyTaskNotify" xml:space="preserve">
<value>每日委託上線提醒</value>
</data>
@@ -1274,6 +1286,12 @@
<data name="ViewDialogDailyNoteWebhookUrlTitle" xml:space="preserve">
<value>即時便箋 Webhook URL</value>
</data>
<data name="ViewDialogExportUIGFSubtitle" xml:space="preserve">
<value>選擇要匯出紀錄的 UID</value>
</data>
<data name="ViewDialogExportUIGFTitle" xml:space="preserve">
<value>匯出 UIGF 文件</value>
</data>
<data name="ViewDialogFeedbackEnableLoopbackContent" xml:space="preserve">
<value>解除限制後需使用其他工具恢復限制</value>
</data>
@@ -1281,10 +1299,10 @@
<value>是否解除 Loopback 限制</value>
</data>
<data name="ViewDialogGachaLogImportTitle" xml:space="preserve">
<value>匯入祈願錄</value>
<value>匯入祈願錄</value>
</data>
<data name="ViewDialogGachaLogRefreshProgressAuthkeyTimeout" xml:space="preserve">
<value>祈願錄 URL 已失效,請重新獲取</value>
<value>祈願錄 URL 已失效,請重新獲取</value>
</data>
<data name="ViewDialogGachaLogRefreshProgressDescription" xml:space="preserve">
<value>正在獲取 {0}</value>
@@ -1296,7 +1314,7 @@
<value>請輸入 URL</value>
</data>
<data name="ViewDialogGachaLogUrlTitle" xml:space="preserve">
<value>手動輸入祈願錄 URL</value>
<value>手動輸入祈願錄 URL</value>
</data>
<data name="ViewDialogGeetestCustomUrlCompositInputHint" xml:space="preserve">
<value>請輸入請求接口的 URL 複合模板</value>
@@ -1361,6 +1379,12 @@
<data name="ViewDialogImportUIGFExportUIGFVersion" xml:space="preserve">
<value>UIGF 版本</value>
</data>
<data name="ViewDialogImportUIGFSubtitle" xml:space="preserve">
<value>選擇要匯入紀錄的 UID</value>
</data>
<data name="ViewDialogImportUIGFTitle" xml:space="preserve">
<value>匯入 UIGF 檔案</value>
</data>
<data name="ViewDialogLaunchGameAccountInputPlaceholder" xml:space="preserve">
<value>在此處輸入名稱</value>
</data>
@@ -1382,6 +1406,9 @@
<data name="ViewDialogQRCodeTitle" xml:space="preserve">
<value>使用米游社掃描 QR 碼</value>
</data>
<data name="ViewDialogReconfirmHint" xml:space="preserve">
<value>為了確保功能的啟用者有獨立的判斷能力,我們將不定期更新這些功能的啟用條件,如果您是一位內容創造者,在創作胡桃相關的內容時請不要向你的受眾介紹具體的啟用方法。</value>
</data>
<data name="ViewDialogReconfirmTextHeader" xml:space="preserve">
<value>為防止你在無意間啟用,請輸入正在啟用的功能開關的&lt;b&gt;標題名稱&lt;/b&gt;</value>
</data>
@@ -1428,7 +1455,7 @@
<value>回饋中心</value>
</data>
<data name="ViewGachaLogHeader" xml:space="preserve">
<value>祈願錄</value>
<value>祈願錄</value>
</data>
<data name="ViewGuideStaticResourceDownloadSize" xml:space="preserve">
<value>預計下載大小:{0}</value>
@@ -1502,6 +1529,12 @@
<data name="ViewHutaoDatabaseHeader" xml:space="preserve">
<value>深淵統計</value>
</data>
<data name="ViewInfoBarPanelClearAllContent" xml:space="preserve">
<value>清除所有通知</value>
</data>
<data name="ViewInfoBarPanelContractContent" xml:space="preserve">
<value>收起</value>
</data>
<data name="ViewInfoBarToggleTitle" xml:space="preserve">
<value>有新的通知</value>
</data>
@@ -1559,6 +1592,12 @@
<data name="ViewModelAvatarPropertyShowcaseNotOpen" xml:space="preserve">
<value>角色展櫃尚未開啟,請前往遊戲操作後重試</value>
</data>
<data name="ViewModelAvatatPropertyExportTextError" xml:space="preserve">
<value>複製角色詳情失敗</value>
</data>
<data name="ViewModelAvatatPropertyExportTextSuccess" xml:space="preserve">
<value>複製角色詳情成功</value>
</data>
<data name="ViewModelComplexReliquarySetViewEmptyName" xml:space="preserve">
<value>無聖遺物或散件</value>
</data>
@@ -1571,6 +1610,15 @@
<data name="ViewModelCultivationBatchAddIncompletedFormat" xml:space="preserve">
<value>操作未全部完成:添加/更新:{0} 個,跳過 {1} 個</value>
</data>
<data name="ViewModelCultivationConsumptionSaveNoItemHint" xml:space="preserve">
<value>选定的等级不需要养成材料</value>
</data>
<data name="ViewModelCultivationConsumptionSaveNoProjectHint" xml:space="preserve">
<value>尚未创建并选择养成计划</value>
</data>
<data name="ViewModelCultivationConsumptionSaveSkippedHint" xml:space="preserve">
<value>已存在该物品的养成项目</value>
</data>
<data name="ViewModelCultivationEntryAddSuccess" xml:space="preserve">
<value>已成功新增至目前養成計劃</value>
</data>
@@ -1787,6 +1835,27 @@
<data name="ViewModelSettingSetGamePathDatabaseFailedTitle" xml:space="preserve">
<value>儲存遊戲路徑失敗</value>
</data>
<data name="ViewModelUIGFExportError" xml:space="preserve">
<value>匯出失敗</value>
</data>
<data name="ViewModelUIGFExportSuccess" xml:space="preserve">
<value>匯出成功</value>
</data>
<data name="ViewModelUIGFImportDuplicatedHk4eEntry" xml:space="preserve">
<value>匯入的 UIGF 檔案中包含 UID 重複的祈願紀錄項</value>
</data>
<data name="ViewModelUIGFImportError" xml:space="preserve">
<value>匯入失敗</value>
</data>
<data name="ViewModelUIGFImportNoHk4eEntry" xml:space="preserve">
<value>匯入的 UIGF 檔案中不包含祈願數據</value>
</data>
<data name="ViewModelUIGFImportNoSelectedEntry" xml:space="preserve">
<value>請選擇至少一個 UID 以匯入數據</value>
</data>
<data name="ViewModelUIGFImportSuccess" xml:space="preserve">
<value>匯入成功</value>
</data>
<data name="ViewModelUserAdded" xml:space="preserve">
<value>用戶 [{0}] 新增成功</value>
</data>
@@ -1880,6 +1949,9 @@
<data name="ViewPageAvatarPropertyExportAsImage" xml:space="preserve">
<value>圖像輸出</value>
</data>
<data name="ViewPageAvatarPropertyExportToTextLabel" xml:space="preserve">
<value>匯出文本到剪貼簿</value>
</data>
<data name="ViewPageAvatarPropertyHeader" xml:space="preserve">
<value>角色屬性</value>
</data>
@@ -1965,10 +2037,10 @@
<value>添加任意武器到養成計劃</value>
</data>
<data name="ViewPageDailyNoteAddEntry" xml:space="preserve">
<value>添加实时便笺</value>
<value>添加即時便籤</value>
</data>
<data name="ViewPageDailyNoteAddEntryHint" xml:space="preserve">
<value>添加实时便笺以定时刷新</value>
<value>添加即時便籤以定時重新整理</value>
</data>
<data name="ViewPageDailyNoteAddEntryToolTip" xml:space="preserve">
<value>新增</value>
@@ -2142,7 +2214,7 @@
<value>手動輸入 URL</value>
</data>
<data name="ViewPageGachaLogRefreshByManualInputDescription" xml:space="preserve">
<value>使用由你提供的 URL 重新整理祈願錄</value>
<value>使用由你提供的 URL 重新整理祈願錄</value>
</data>
<data name="ViewPageGachaLogRefreshBySToken" xml:space="preserve">
<value>SToken 重新整理</value>
@@ -2235,7 +2307,7 @@
<value>數據收集統計</value>
</data>
<data name="ViewPageHutaoDatabaseOverviewRecordTotal" xml:space="preserve">
<value>上傳錄總數</value>
<value>上傳錄總數</value>
</data>
<data name="ViewPageHutaoDatabaseOverviewRefreshTime" xml:space="preserve">
<value>數據重新整理時間</value>
@@ -2247,16 +2319,16 @@
<value>平均戰鬥次數</value>
</data>
<data name="ViewPageHutaoDatabaseOverviewSpiralAbyssFullStar" xml:space="preserve">
<value>滿星深淵錄</value>
<value>滿星深淵錄</value>
</data>
<data name="ViewPageHutaoDatabaseOverviewSpiralAbyssPassed" xml:space="preserve">
<value>通關深淵錄</value>
<value>通關深淵錄</value>
</data>
<data name="ViewPageHutaoDatabaseOverviewSpiralAbyssStarAverage" xml:space="preserve">
<value>平均獲取淵星</value>
</data>
<data name="ViewPageHutaoDatabaseOverviewSpiralAbyssTotal" xml:space="preserve">
<value>總計深淵錄</value>
<value>總計深淵錄</value>
</data>
<data name="ViewPageHutaoDatabaseOverviewTeamAppearance" xml:space="preserve">
<value>隊伍出場</value>
@@ -2355,7 +2427,7 @@
<value>一般</value>
</data>
<data name="ViewPageLaunchGameConfigurationSaveHint" xml:space="preserve">
<value>所有選項會在啟動遊戲成功後儲存</value>
<value>所有選項會在啟動遊戲成功後儲存</value>
</data>
<data name="ViewPageLaunchGameDiscordActivityDescription" xml:space="preserve">
<value>在我遊戲時設定 Discord Activity 狀態</value>
@@ -2448,10 +2520,10 @@
<value>解鎖 FPS 限制</value>
</data>
<data name="ViewPageLaunchGameUnlockFpsKindDescription" xml:space="preserve">
<value>更改解锁帧率的工作方式</value>
<value>更改解鎖 FPS 的工作方式</value>
</data>
<data name="ViewPageLaunchGameUnlockFpsKindHeader" xml:space="preserve">
<value>解方式</value>
<value>解方式</value>
</data>
<data name="ViewPageLaunchGameUnlockFpsOff" xml:space="preserve">
<value>停用</value>
@@ -2559,7 +2631,7 @@
<value>刪除遊戲内網頁快取</value>
</data>
<data name="ViewPageSettingDeleteUserDescription" xml:space="preserve">
<value>直接刪除用戶表的所有錄,用於修復特定的賬號衝突問題</value>
<value>直接刪除用戶表的所有錄,用於修復特定的賬號衝突問題</value>
</data>
<data name="ViewPageSettingDeleteUserHeader" xml:space="preserve">
<value>刪除所有用戶</value>
@@ -2601,7 +2673,7 @@
<value>前往反饋</value>
</data>
<data name="ViewPageSettingGachaLogHeader" xml:space="preserve">
<value>祈願錄</value>
<value>祈願錄</value>
</data>
<data name="ViewPageSettingGameHeader" xml:space="preserve">
<value>遊戲</value>
@@ -2736,7 +2808,7 @@
<value>重設圖片資源</value>
</data>
<data name="ViewPageSettingsAdvancedOptionsLaunchUnlockFpsDescription" xml:space="preserve">
<value>在啟動遊戲頁面的程序部分加入解鎖 FPS 限制選項</value>
<value>在啟動遊戲 - 進程」選項卡加入解鎖 FPS 限制選項</value>
</data>
<data name="ViewPageSettingsAdvancedOptionsLaunchUnlockFpsHeader" xml:space="preserve">
<value>啟動遊戲-解鎖 FPS 限制</value>
@@ -2783,8 +2855,20 @@
<data name="ViewPageSettingTranslateNavigate" xml:space="preserve">
<value>貢獻翻譯</value>
</data>
<data name="ViewPageSettingUIGFExportContent" xml:space="preserve">
<value>匯出</value>
</data>
<data name="ViewPageSettingUIGFExportImportDescription" xml:space="preserve">
<value>匯出/匯入 UIGF 4 檔案</value>
</data>
<data name="ViewPageSettingUIGFExportImportHeader" xml:space="preserve">
<value>數據遷移</value>
</data>
<data name="ViewPageSettingUIGFImportContent" xml:space="preserve">
<value>匯入</value>
</data>
<data name="ViewPageSettingUnobtainedWishItemVisibleDescription" xml:space="preserve">
<value>在祈願記錄頁面角色與武器頁籤顯示未抽到的祈願物品</value>
<value>在祈願紀錄 - 角色」與「祈願紀錄 - 武器」中顯示未抽到的祈願物品</value>
</data>
<data name="ViewPageSettingUnobtainedWishItemVisibleHeader" xml:space="preserve">
<value>未抽取到的祈願物品</value>
@@ -3126,7 +3210,7 @@
<value>{0} 分</value>
</data>
<data name="WebDailyNoteExpeditionRemainTime" xml:space="preserve">
<value>{0} {1} 分</value>
<value>{0} {1} 分</value>
</data>
<data name="WebDailyNoteExtraTaskRewardNotAllowed" xml:space="preserve">
<value>今日完成委託數量不足</value>

View File

@@ -2,6 +2,7 @@
// Licensed under the MIT license.
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Storage;
using Snap.Hutao.Core.Database;
using Snap.Hutao.Model.Entity.Database;
using System.Linq.Expressions;
@@ -32,6 +33,24 @@ internal static class AppDbServiceExtension
return service.Execute(dbset => dbset.AddRangeAndSave(entities));
}
public static int Delete<TEntity>(this IAppDbService<TEntity> service)
where TEntity : class
{
return service.Execute(dbset => dbset.ExecuteDelete());
}
public static int Delete<TEntity>(this IAppDbService<TEntity> service, TEntity entity)
where TEntity : class
{
return service.Execute(dbset => dbset.RemoveAndSave(entity));
}
public static int Delete<TEntity>(this IAppDbService<TEntity> service, Expression<Func<TEntity, bool>> predicate)
where TEntity : class
{
return service.Execute(dbset => dbset.Where(predicate).ExecuteDelete());
}
public static TResult Query<TEntity, TResult>(this IAppDbService<TEntity> service, Func<IQueryable<TEntity>, TResult> func)
where TEntity : class
{
@@ -50,27 +69,38 @@ internal static class AppDbServiceExtension
return service.Query(query => query.SingleOrDefault(predicate));
}
public static void TransactionalExecute<TEntity>(this IAppDbService<TEntity> service, Action<DbSet<TEntity>> action)
where TEntity : class
{
using (IServiceScope scope = service.ServiceProvider.CreateScope())
{
AppDbContext appDbContext = scope.GetAppDbContext();
using (IDbContextTransaction transaction = appDbContext.Database.BeginTransaction())
{
action(appDbContext.Set<TEntity>());
transaction.Commit();
}
}
}
public static TResult TransactionalExecute<TEntity, TResult>(this IAppDbService<TEntity> service, Func<DbSet<TEntity>, TResult> func)
where TEntity : class
{
using (IServiceScope scope = service.ServiceProvider.CreateScope())
{
AppDbContext appDbContext = scope.GetAppDbContext();
using (IDbContextTransaction transaction = appDbContext.Database.BeginTransaction())
{
TResult result = func(appDbContext.Set<TEntity>());
transaction.Commit();
return result;
}
}
}
public static int Update<TEntity>(this IAppDbService<TEntity> service, TEntity entity)
where TEntity : class
{
return service.Execute(dbset => dbset.UpdateAndSave(entity));
}
public static int Delete<TEntity>(this IAppDbService<TEntity> service)
where TEntity : class
{
return service.Execute(dbset => dbset.ExecuteDelete());
}
public static int Delete<TEntity>(this IAppDbService<TEntity> service, TEntity entity)
where TEntity : class
{
return service.Execute(dbset => dbset.RemoveAndSave(entity));
}
public static int Delete<TEntity>(this IAppDbService<TEntity> service, Expression<Func<TEntity, bool>> predicate)
where TEntity : class
{
return service.Execute(dbset => dbset.Where(predicate).ExecuteDelete());
}
}

View File

@@ -78,4 +78,19 @@ internal sealed partial class AchievementDbService : IAchievementDbService
{
return this.List<AchievementArchive>();
}
public AchievementArchive? GetAchievementArchiveById(Guid archiveId)
{
return this.SingleOrDefault<AchievementArchive>(a => a.InnerId == archiveId);
}
public AchievementArchive? GetAchievementArchiveByName(string name)
{
return this.SingleOrDefault<AchievementArchive>(a => a.Name == name);
}
public void AddAchievementArchive(AchievementArchive archive)
{
this.Add(archive);
}
}

View File

@@ -8,12 +8,10 @@ using Snap.Hutao.Model.Entity;
using Snap.Hutao.Model.InterChange.Achievement;
using Snap.Hutao.Model.Primitive;
using Snap.Hutao.ViewModel.Achievement;
using System.Collections.ObjectModel;
using EntityAchievement = Snap.Hutao.Model.Entity.Achievement;
namespace Snap.Hutao.Service.Achievement;
[HighQuality]
[ConstructorGenerated]
[Injection(InjectAs.Scoped, typeof(IAchievementService))]
internal sealed partial class AchievementService : IAchievementService
@@ -31,9 +29,7 @@ internal sealed partial class AchievementService : IAchievementService
if (archives is null)
{
await taskContext.SwitchToBackgroundAsync();
ObservableCollection<AchievementArchive> source = achievementDbService.GetAchievementArchiveCollection();
await taskContext.SwitchToMainThreadAsync();
archives = new(source, serviceProvider);
archives = new(achievementDbService.GetAchievementArchiveCollection(), serviceProvider);
}
return archives;

View File

@@ -5,14 +5,23 @@ using Snap.Hutao.Model.Primitive;
using Snap.Hutao.Service.Abstraction;
using System.Collections.ObjectModel;
using EntityAchievement = Snap.Hutao.Model.Entity.Achievement;
using EntityArchive = Snap.Hutao.Model.Entity.AchievementArchive;
namespace Snap.Hutao.Service.Achievement;
internal interface IAchievementDbService : IAppDbService<Model.Entity.AchievementArchive>, IAppDbService<EntityAchievement>
internal interface IAchievementDbService : IAppDbService<EntityArchive>, IAppDbService<EntityAchievement>
{
ObservableCollection<Model.Entity.AchievementArchive> GetAchievementArchiveCollection();
void AddAchievementArchive(EntityArchive archive);
List<Model.Entity.AchievementArchive> GetAchievementArchiveList();
ObservableCollection<EntityArchive> GetAchievementArchiveCollection();
List<EntityArchive> GetAchievementArchiveList();
EntityArchive? GetAchievementArchiveById(Guid archiveId);
EntityArchive? GetAchievementArchiveByName(string name);
void RemoveAchievementArchive(EntityArchive archive);
List<EntityAchievement> GetAchievementListByArchiveId(Guid archiveId);
@@ -23,6 +32,4 @@ internal interface IAchievementDbService : IAppDbService<Model.Entity.Achievemen
List<EntityAchievement> GetLatestFinishedAchievementListByArchiveId(Guid archiveId, int take);
void OverwriteAchievement(EntityAchievement achievement);
void RemoveAchievementArchive(Model.Entity.AchievementArchive archive);
}

View File

@@ -31,6 +31,28 @@ internal sealed partial class AvatarInfoDbBulkOperation
private readonly IServiceProvider serviceProvider;
private readonly ITaskContext taskContext;
public void UnsafeUpdateDbAvatarInfos(string uid, IEnumerable<EnkaAvatarInfo> webInfos)
{
List<EntityAvatarInfo> dbInfos = avatarInfoDbService.GetAvatarInfoListByUid(uid);
EnsureItemsAvatarIdUnique(ref dbInfos, uid, out Dictionary<AvatarId, EntityAvatarInfo> dbInfoMap);
using (IServiceScope scope = serviceProvider.CreateScope())
{
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
foreach (EnkaAvatarInfo webInfo in webInfos)
{
if (AvatarIds.IsPlayer(webInfo.AvatarId))
{
continue;
}
EntityAvatarInfo? entity = dbInfoMap.GetValueOrDefault(webInfo.AvatarId);
AddOrUpdateAvatarInfo(entity, uid, appDbContext, webInfo);
}
}
}
public async ValueTask<List<EntityAvatarInfo>> UpdateDbAvatarInfosByShowcaseAsync(string uid, IEnumerable<EnkaAvatarInfo> webInfos, CancellationToken token)
{
await taskContext.SwitchToBackgroundAsync();

View File

@@ -10,6 +10,13 @@ namespace Snap.Hutao.Service.AvatarInfo.Factory.Builder;
internal static class EquipViewBuilderExtension
{
public static TBuilder SetEquipType<TBuilder, T>(this TBuilder builder, EquipType equipType)
where TBuilder : IEquipViewBuilder<T>
where T : EquipView
{
return builder.Configure(b => b.View.EquipType = equipType);
}
public static TBuilder SetLevel<TBuilder, T>(this TBuilder builder, string level)
where TBuilder : IEquipViewBuilder<T>
where T : EquipView

View File

@@ -22,6 +22,12 @@ internal static class ReliquaryViewBuilderExtension
return builder.SetDescription<TBuilder, ReliquaryView>(description);
}
public static TBuilder SetEquipType<TBuilder>(this TBuilder builder, EquipType equipType)
where TBuilder : IReliquaryViewBuilder
{
return builder.SetEquipType<TBuilder, ReliquaryView>(equipType);
}
public static TBuilder SetIcon<TBuilder>(this TBuilder builder, Uri icon)
where TBuilder : IReliquaryViewBuilder
{
@@ -63,4 +69,10 @@ internal static class ReliquaryViewBuilderExtension
{
return builder.Configure(b => b.View.SecondarySubProperties = secondarySubProperties);
}
public static TBuilder SetSetName<TBuilder>(this TBuilder builder, string setName)
where TBuilder : IReliquaryViewBuilder
{
return builder.Configure(b => b.View.SetName = setName);
}
}

View File

@@ -37,6 +37,12 @@ internal static class WeaponViewBuilderExtension
return builder.SetDescription<TBuilder, WeaponView>(description);
}
public static TBuilder SetEquipType<TBuilder>(this TBuilder builder, EquipType equipType)
where TBuilder : IWeaponViewBuilder
{
return builder.SetEquipType<TBuilder, WeaponView>(equipType);
}
public static TBuilder SetIcon<TBuilder>(this TBuilder builder, Uri icon)
where TBuilder : IWeaponViewBuilder
{
@@ -85,7 +91,7 @@ internal static class WeaponViewBuilderExtension
return builder.SetQuality<TBuilder, WeaponView>(quality);
}
public static TBuilder SetSubProperty<TBuilder>(this TBuilder builder, NameDescription subProperty)
public static TBuilder SetSubProperty<TBuilder>(this TBuilder builder, NameValue<string> subProperty)
where TBuilder : IWeaponViewBuilder
{
return builder.Configure(b => b.View.SubProperty = subProperty);

View File

@@ -102,17 +102,17 @@ internal sealed class SummaryAvatarFactory
WeaponStat? mainStat = equip.Flat.WeaponStats?.ElementAtOrDefault(0);
WeaponStat? subStat = equip.Flat.WeaponStats?.ElementAtOrDefault(1);
NameDescription subProperty;
NameValue<string> subProperty;
if (subStat is null)
{
subProperty = NameDescription.Default;
subProperty = NameValueDefaults.String;
}
else
{
float statValue = subStat.AppendPropId.GetFormatMethod() is FormatMethod.Percent
? subStat.StatValue / 100F
: subStat.StatValue;
subProperty = FightPropertyFormat.ToNameDescription(subStat.AppendPropId, statValue);
subProperty = FightPropertyFormat.ToNameValue(subStat.AppendPropId, statValue);
}
ArgumentNullException.ThrowIfNull(equip.Weapon);
@@ -123,6 +123,7 @@ internal sealed class SummaryAvatarFactory
.SetDescription(weapon.Description)
.SetLevel(LevelFormat.Format(equip.Weapon.Level))
.SetQuality(weapon.Quality)
.SetEquipType(EquipType.EQUIP_WEAPON)
.SetMainProperty(mainStat)
.SetId(weapon.Id)
.SetLevelNumber(equip.Weapon.Level)

View File

@@ -4,6 +4,7 @@
using Snap.Hutao.Model.Metadata;
using Snap.Hutao.Service.Metadata;
using Snap.Hutao.Service.Metadata.ContextAbstraction;
using Snap.Hutao.UI.Xaml.Data;
using Snap.Hutao.ViewModel.AvatarProperty;
namespace Snap.Hutao.Service.AvatarInfo.Factory;
@@ -34,9 +35,11 @@ internal sealed partial class SummaryFactory : ISummaryFactory
.ThenBy(a => a.Weapon?.WeaponType)
.ThenByDescending(a => a.FetterLevel);
IList<AvatarView> views = [.. avatars];
return new()
{
Avatars = [.. avatars],
Avatars = views.ToAdvancedCollectionView(),
};
}
}

View File

@@ -17,6 +17,7 @@ internal sealed class SummaryFactoryMetadataContext : IMetadataContext,
IMetadataDictionaryIdWeaponSource,
IMetadataDictionaryIdReliquaryAffixWeightSource,
IMetadataDictionaryIdReliquaryMainPropertySource,
IMetadataDictionaryIdReliquarySetSource,
IMetadataDictionaryIdReliquarySubAffixSource,
IMetadataDictionaryIdReliquarySource,
IMetadataListReliquaryMainAffixLevelSource
@@ -31,6 +32,8 @@ internal sealed class SummaryFactoryMetadataContext : IMetadataContext,
public Dictionary<ReliquarySubAffixId, ReliquarySubAffix> IdReliquarySubAffixMap { get; set; } = default!;
public Dictionary<ReliquarySetId, ReliquarySet> IdReliquarySetMap { get; set; } = default!;
public List<ReliquaryMainAffixLevel> ReliquaryMainAffixLevels { get; set; } = default!;
public Dictionary<ReliquaryId, MetadataReliquary> IdReliquaryMap { get; set; } = default!;

View File

@@ -36,6 +36,7 @@ internal sealed class SummaryReliquaryFactory
public ReliquaryView Create()
{
MetadataReliquary reliquary = metadataContext.IdReliquaryMap[equip.ItemId];
ReliquarySet reliquarySet = metadataContext.IdReliquarySetMap[reliquary.SetId];
ArgumentNullException.ThrowIfNull(equip.Reliquary);
List<ReliquarySubProperty> subProperties = equip.Reliquary.AppendPropIdList.EmptyIfNull().SelectList(CreateSubProperty);
@@ -45,7 +46,9 @@ internal sealed class SummaryReliquaryFactory
.SetIcon(RelicIconConverter.IconNameToUri(reliquary.Icon))
.SetDescription(reliquary.Description)
.SetLevel($"+{equip.Reliquary.Level - 1U}")
.SetQuality(reliquary.RankLevel);
.SetQuality(reliquary.RankLevel)
.SetEquipType(reliquary.EquipType)
.SetSetName(reliquarySet.Name);
int affixCount = GetSecondaryAffixCount(reliquary, equip.Reliquary);

View File

@@ -0,0 +1,12 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
namespace Snap.Hutao.Service.Cultivation;
internal enum ConsumptionSaveResultKind
{
NoItem,
NoProject,
Skipped,
Added,
}

View File

@@ -0,0 +1,11 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
namespace Snap.Hutao.Service.Cultivation;
internal enum ConsumptionSaveStrategyKind
{
PreserveExisting,
OverwriteExisting,
CreateNewEntry,
}

View File

@@ -85,4 +85,9 @@ internal sealed partial class CultivationDbService : ICultivationDbService
{
this.Add(levelInformation);
}
public CultivateProject? GetCultivateProjectById(Guid projectId)
{
return this.SingleOrDefault<CultivateProject>(p => p.InnerId == projectId);
}
}

View File

@@ -8,7 +8,6 @@ using Snap.Hutao.Service.Inventory;
using Snap.Hutao.Service.Metadata.ContextAbstraction;
using Snap.Hutao.ViewModel.Cultivation;
using System.Collections.ObjectModel;
using CalculateItem = Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate.Item;
using ModelItem = Snap.Hutao.Model.Item;
namespace Snap.Hutao.Service.Cultivation;
@@ -115,45 +114,54 @@ internal sealed partial class CultivationService : ICultivationService
}
/// <inheritdoc/>
public async ValueTask<bool> SaveConsumptionAsync(CultivateType type, uint itemId, List<CalculateItem> items, LevelInformation levelInformation)
public async ValueTask<ConsumptionSaveResultKind> SaveConsumptionAsync(InputConsumption inputConsumption)
{
if (items.Count == 0)
if (inputConsumption.Items.Count == 0)
{
return true;
return ConsumptionSaveResultKind.NoItem;
}
await taskContext.SwitchToMainThreadAsync();
if (Projects.CurrentItem is null)
{
await taskContext.SwitchToMainThreadAsync();
Projects.MoveCurrentTo(Projects.SourceCollection.SelectedOrDefault());
if (Projects.CurrentItem is null)
{
return false;
return ConsumptionSaveResultKind.NoProject;
}
}
await taskContext.SwitchToBackgroundAsync();
CultivateEntry? entry = type is CultivateType.AvatarAndSkill
? cultivationDbService.GetCultivateEntryByProjectIdAndItemId(Projects.CurrentItem.InnerId, itemId)
: default;
CultivateEntry? entry = default;
if (inputConsumption.Strategy is ConsumptionSaveStrategyKind.PreserveExisting or ConsumptionSaveStrategyKind.OverwriteExisting)
{
entry = cultivationDbService.GetCultivateEntryByProjectIdAndItemId(Projects.CurrentItem.InnerId, inputConsumption.ItemId);
if (inputConsumption.Strategy is ConsumptionSaveStrategyKind.PreserveExisting && entry is not null)
{
return ConsumptionSaveResultKind.Skipped;
}
}
if (entry is null)
{
entry = CultivateEntry.From(Projects.CurrentItem.InnerId, type, itemId);
entry = CultivateEntry.From(Projects.CurrentItem.InnerId, inputConsumption.Type, inputConsumption.ItemId);
cultivationDbService.AddCultivateEntry(entry);
}
Guid entryId = entry.InnerId;
cultivationDbService.RemoveLevelInformationByEntryId(entryId);
CultivateEntryLevelInformation entryLevelInformation = CultivateEntryLevelInformation.From(entryId, type, levelInformation);
CultivateEntryLevelInformation entryLevelInformation = CultivateEntryLevelInformation.From(entryId, inputConsumption.Type, inputConsumption.LevelInformation);
cultivationDbService.AddLevelInformation(entryLevelInformation);
cultivationDbService.RemoveCultivateItemRangeByEntryId(entryId);
IEnumerable<CultivateItem> toAdd = items.Select(item => CultivateItem.From(entryId, item));
IEnumerable<CultivateItem> toAdd = inputConsumption.Items.Select(item => CultivateItem.From(entryId, item));
cultivationDbService.AddCultivateItemRange(toAdd);
return true;
return ConsumptionSaveResultKind.Added;
}
/// <inheritdoc/>

View File

@@ -28,6 +28,8 @@ internal interface ICultivationDbService : IAppDbService<CultivateEntryLevelInfo
ObservableCollection<CultivateProject> GetCultivateProjectCollection();
CultivateProject? GetCultivateProjectById(Guid projectId);
void AddCultivateEntry(CultivateEntry entry);
void AddCultivateItemRange(IEnumerable<CultivateItem> toAdd);

View File

@@ -3,22 +3,13 @@
using Snap.Hutao.Core.Database;
using Snap.Hutao.Model.Entity;
using Snap.Hutao.Model.Entity.Primitive;
using Snap.Hutao.ViewModel.Cultivation;
using Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate;
using System.Collections.ObjectModel;
namespace Snap.Hutao.Service.Cultivation;
/// <summary>
/// 养成计算服务
/// </summary>
[HighQuality]
internal interface ICultivationService
{
/// <summary>
/// 获取用于绑定的项目集合
/// </summary>
AdvancedDbCollectionView<CultivateProject> Projects { get; }
ValueTask<ObservableCollection<CultivateEntryView>> GetCultivateEntriesAsync(CultivateProject cultivateProject, ICultivationMetadataContext context);
@@ -26,32 +17,13 @@ internal interface ICultivationService
ValueTask<ObservableCollection<StatisticsCultivateItem>> GetStatisticsCultivateItemCollectionAsync(
CultivateProject cultivateProject, ICultivationMetadataContext context, CancellationToken token);
/// <summary>
/// 删除养成清单
/// </summary>
/// <param name="entryId">入口Id</param>
/// <returns>任务</returns>
ValueTask RemoveCultivateEntryAsync(Guid entryId);
/// <summary>
/// 异步移除项目
/// </summary>
/// <param name="project">项目</param>
/// <returns>任务</returns>
ValueTask RemoveProjectAsync(CultivateProject project);
ValueTask<bool> SaveConsumptionAsync(CultivateType type, uint itemId, List<Item> items, LevelInformation levelInformation);
ValueTask<ConsumptionSaveResultKind> SaveConsumptionAsync(InputConsumption inputConsumption);
/// <summary>
/// 保存养成物品状态
/// </summary>
/// <param name="item">养成物品</param>
void SaveCultivateItem(CultivateItemView item);
/// <summary>
/// 异步尝试添加新的项目
/// </summary>
/// <param name="project">项目</param>
/// <returns>添加操作的结果</returns>
ValueTask<ProjectAddResultKind> TryAddProjectAsync(CultivateProject project);
}

View File

@@ -0,0 +1,20 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Model.Entity.Primitive;
using CalculateItem = Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate.Item;
namespace Snap.Hutao.Service.Cultivation;
internal sealed class InputConsumption
{
public required CultivateType Type { get; init; }
public required uint ItemId { get; init; }
public required List<CalculateItem> Items { get; init; }
public required LevelInformation LevelInformation { get; init; }
public required ConsumptionSaveStrategyKind Strategy { get; init; }
}

View File

@@ -37,7 +37,7 @@ internal sealed partial class DailyNoteOptions : DbStoreOptions
public bool IsAutoRefreshEnabled
{
get => GetOption(ref isAutoRefreshEnabled, SettingEntry.DailyNoteIsAutoRefreshEnabled, true);
get => GetOption(ref isAutoRefreshEnabled, SettingEntry.DailyNoteIsAutoRefreshEnabled, false);
set
{
if (SetOption(ref isAutoRefreshEnabled, SettingEntry.DailyNoteIsAutoRefreshEnabled, value))

View File

@@ -203,7 +203,7 @@ internal sealed partial class GachaStatisticsFactory : IGachaStatisticsFactory
AsyncBarrier barrier = new(4);
List<HistoryWish> historyWishes = historyWishBuilders
.Where(b => appOptions.IsEmptyHistoryWishVisible || (!b.IsEmpty))
.Where(b => appOptions.IsEmptyHistoryWishVisible || !b.IsEmpty)
.OrderByDescending(builder => builder.From)
.ThenBy(builder => builder.ConfigType, GachaTypeComparer.Shared)
.Select(builder => builder.ToHistoryWish())
@@ -212,7 +212,7 @@ internal sealed partial class GachaStatisticsFactory : IGachaStatisticsFactory
return new()
{
// history
HistoryWishes = taskContext.InvokeOnMainThread(() => new AdvancedCollectionView<HistoryWish>(historyWishes, true)),
HistoryWishes = historyWishes.ToAdvancedCollectionView(),
// avatars
OrangeAvatars = orangeAvatarCounter.ToStatisticsList(),

View File

@@ -109,4 +109,9 @@ internal sealed partial class GachaLogDbService : IGachaLogDbService
{
this.Delete<GachaItem>(i => i.ArchiveId == archiveId && i.QueryType == queryType && i.Id >= endId);
}
public List<string> GetGachaArchiveUidList()
{
return this.List<GachaArchive, string>(query => query.Select(archive => archive.Uid));
}
}

View File

@@ -5,7 +5,6 @@ using Snap.Hutao.Core.Database;
using Snap.Hutao.Core.Diagnostics;
using Snap.Hutao.Core.ExceptionService;
using Snap.Hutao.Model.Entity;
using Snap.Hutao.Model.InterChange.GachaLog;
using Snap.Hutao.Service.GachaLog.Factory;
using Snap.Hutao.Service.GachaLog.QueryProvider;
using Snap.Hutao.Service.Metadata;
@@ -22,8 +21,6 @@ internal sealed partial class GachaLogService : IGachaLogService
{
private readonly IGachaStatisticsSlimFactory gachaStatisticsSlimFactory;
private readonly IGachaStatisticsFactory gachaStatisticsFactory;
private readonly IUIGFExportService gachaLogExportService;
private readonly IUIGFImportService gachaLogImportService;
private readonly IGachaLogDbService gachaLogDbService;
private readonly IServiceProvider serviceProvider;
private readonly IMetadataService metadataService;
@@ -50,7 +47,6 @@ internal sealed partial class GachaLogService : IGachaLogService
if (await metadataService.InitializeAsync().ConfigureAwait(false))
{
context = await metadataService.GetContextAsync<GachaLogServiceMetadataContext>(token).ConfigureAwait(false);
await taskContext.SwitchToMainThreadAsync();
Archives = new(gachaLogDbService.GetGachaArchiveCollection(), serviceProvider);
return true;
}
@@ -85,17 +81,6 @@ internal sealed partial class GachaLogService : IGachaLogService
return statistics;
}
public ValueTask<UIGF> ExportToUIGFAsync(GachaArchive archive)
{
return gachaLogExportService.ExportAsync(context, archive);
}
public async ValueTask ImportFromUIGFAsync(UIGF uigf)
{
ArgumentNullException.ThrowIfNull(Archives);
await gachaLogImportService.ImportAsync(context, uigf, Archives).ConfigureAwait(false);
}
public async ValueTask<bool> RefreshGachaLogAsync(GachaLogQuery query, RefreshStrategyKind kind, IProgress<GachaLogFetchStatus> progress, CancellationToken token)
{
bool isLazy = kind switch

View File

@@ -22,6 +22,8 @@ internal interface IGachaLogDbService : IAppDbService<GachaArchive>, IAppDbServi
GachaArchive? GetGachaArchiveByUid(string uid);
List<string> GetGachaArchiveUidList();
ObservableCollection<GachaArchive> GetGachaArchiveCollection();
List<GachaItem> GetGachaItemListByArchiveId(Guid archiveId);

View File

@@ -3,72 +3,24 @@
using Snap.Hutao.Core.Database;
using Snap.Hutao.Model.Entity;
using Snap.Hutao.Model.InterChange.GachaLog;
using Snap.Hutao.Service.GachaLog.QueryProvider;
using Snap.Hutao.ViewModel.GachaLog;
namespace Snap.Hutao.Service.GachaLog;
/// <summary>
/// 祈愿记录服务
/// </summary>
[HighQuality]
internal interface IGachaLogService
{
AdvancedDbCollectionView<GachaArchive>? Archives { get; }
ValueTask<GachaArchive> EnsureArchiveInCollectionAsync(Guid archiveId, CancellationToken token = default(CancellationToken));
/// <summary>
/// 导出为一个新的UIGF对象
/// </summary>
/// <param name="archive">存档</param>
/// <returns>UIGF对象</returns>
ValueTask<UIGF> ExportToUIGFAsync(GachaArchive archive);
/// <summary>
/// 获得对应的祈愿统计
/// </summary>
/// <param name="archive">存档</param>
/// <returns>祈愿统计</returns>
ValueTask<GachaStatistics> GetStatisticsAsync(GachaArchive archive);
/// <summary>
/// 异步获取简化的祈愿统计列表
/// </summary>
/// <param name="token">取消令牌</param>
/// <returns>简化的祈愿统计列表</returns>
ValueTask<List<GachaStatisticsSlim>> GetStatisticsSlimListAsync(CancellationToken token = default);
/// <summary>
/// 异步从UIGF导入数据
/// </summary>
/// <param name="uigf">信息</param>
/// <returns>任务</returns>
ValueTask ImportFromUIGFAsync(UIGF uigf);
/// <summary>
/// 异步初始化
/// </summary>
/// <param name="token">取消令牌</param>
/// <returns>是否初始化成功</returns>
ValueTask<bool> InitializeAsync(CancellationToken token = default);
/// <summary>
/// 刷新祈愿记录
/// 切换选中的存档
/// </summary>
/// <param name="query">查询语句</param>
/// <param name="strategy">刷新策略</param>
/// <param name="progress">进度</param>
/// <param name="token">取消令牌</param>
/// <returns>验证密钥是否有效</returns>
ValueTask<bool> RefreshGachaLogAsync(GachaLogQuery query, RefreshStrategyKind strategy, IProgress<GachaLogFetchStatus> progress, CancellationToken token);
/// <summary>
/// 删除存档
/// </summary>
/// <param name="archive">存档</param>
/// <returns>任务</returns>
ValueTask RemoveArchiveAsync(GachaArchive archive);
}

View File

@@ -1,21 +0,0 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Model.Entity;
using Snap.Hutao.Model.InterChange.GachaLog;
namespace Snap.Hutao.Service.GachaLog;
/// <summary>
/// 祈愿记录导出服务
/// </summary>
internal interface IUIGFExportService
{
/// <summary>
/// 异步导出存档到 UIGF
/// </summary>
/// <param name="context">元数据上下文</param>
/// <param name="archive">存档</param>
/// <returns>UIGF</returns>
ValueTask<UIGF> ExportAsync(GachaLogServiceMetadataContext context, GachaArchive archive);
}

View File

@@ -1,16 +0,0 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Core.Database;
using Snap.Hutao.Model.Entity;
using Snap.Hutao.Model.InterChange.GachaLog;
namespace Snap.Hutao.Service.GachaLog;
/// <summary>
/// 祈愿记录导入服务
/// </summary>
internal interface IUIGFImportService
{
ValueTask ImportAsync(GachaLogServiceMetadataContext context, UIGF uigf, AdvancedDbCollectionView<GachaArchive> archives);
}

View File

@@ -13,6 +13,7 @@ internal sealed partial class GachaLogQueryProviderFactory : IGachaLogQueryProvi
public IGachaLogQueryProvider Create(RefreshOption option)
{
// TODO: replace with keyed services
return option switch
{
RefreshOption.SToken => serviceProvider.GetRequiredService<GachaLogQuerySTokenProvider>(),

View File

@@ -1,37 +0,0 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Core;
using Snap.Hutao.Model.Entity;
using Snap.Hutao.Model.InterChange.GachaLog;
namespace Snap.Hutao.Service.GachaLog;
/// <summary>
/// 祈愿记录导出服务
/// </summary>
[ConstructorGenerated]
[Injection(InjectAs.Scoped, typeof(IUIGFExportService))]
internal sealed partial class UIGFExportService : IUIGFExportService
{
private readonly IGachaLogDbService gachaLogDbService;
private readonly RuntimeOptions runtimeOptions;
private readonly CultureOptions cultureOptions;
private readonly ITaskContext taskContext;
/// <inheritdoc/>
public async ValueTask<UIGF> ExportAsync(GachaLogServiceMetadataContext context, GachaArchive archive)
{
await taskContext.SwitchToBackgroundAsync();
List<GachaItem> entities = gachaLogDbService.GetGachaItemListByArchiveId(archive.InnerId);
List<UIGFItem> list = entities.SelectList(i => UIGFItem.From(i, context.GetNameQualityByItemId(i.ItemId)));
UIGF uigf = new()
{
Info = UIGFInfo.From(runtimeOptions, cultureOptions, archive.Uid),
List = list,
};
return uigf;
}
}

View File

@@ -1,101 +0,0 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Core.Database;
using Snap.Hutao.Core.ExceptionService;
using Snap.Hutao.Model.Entity;
using Snap.Hutao.Model.InterChange.GachaLog;
using Snap.Hutao.Web.Hoyolab.Hk4e.Event.GachaInfo;
namespace Snap.Hutao.Service.GachaLog;
[ConstructorGenerated]
[Injection(InjectAs.Scoped, typeof(IUIGFImportService))]
internal sealed partial class UIGFImportService : IUIGFImportService
{
private readonly IGachaLogDbService gachaLogDbService;
private readonly ILogger<UIGFImportService> logger;
private readonly CultureOptions cultureOptions;
private readonly ITaskContext taskContext;
public async ValueTask ImportAsync(GachaLogServiceMetadataContext context, UIGF uigf, AdvancedDbCollectionView<GachaArchive> archives)
{
await taskContext.SwitchToBackgroundAsync();
if (!uigf.IsCurrentVersionSupported(out UIGFVersion version))
{
HutaoException.InvalidOperation(SH.ServiceUIGFImportUnsupportedVersion);
}
// v2.3+ supports any locale
// v2.2 only supports matched locale
// v2.1 only supports CHS
if (version is UIGFVersion.Major2Minor2OrLower)
{
if (!cultureOptions.LanguageCodeFitsCurrentLocale(uigf.Info.Language))
{
string message = SH.FormatServiceGachaUIGFImportLanguageNotMatch(uigf.Info.Language, cultureOptions.LanguageCode);
HutaoException.InvalidOperation(message);
}
if (!uigf.IsMajor2Minor2OrLowerListValid(out long id))
{
string message = SH.FormatServiceGachaLogUIGFImportItemInvalidFormat(id);
HutaoException.InvalidOperation(message);
}
}
if (version is UIGFVersion.Major2Minor3OrHigher)
{
if (!uigf.IsMajor2Minor3OrHigherListValid(out long id))
{
string message = SH.FormatServiceGachaLogUIGFImportItemInvalidFormat(id);
HutaoException.InvalidOperation(message);
}
}
GachaArchiveOperation.GetOrAdd(gachaLogDbService, taskContext, uigf.Info.Uid, archives, out GachaArchive? archive);
Guid archiveId = archive.InnerId;
List<GachaItem> fullItems = [];
foreach (GachaType queryType in GachaLog.QueryTypes)
{
long trimId = gachaLogDbService.GetOldestGachaItemIdByArchiveIdAndQueryType(archiveId, queryType);
logger.LogInformation("Last Id to trim with: [{Id}]", trimId);
List<GachaItem> currentTypedList = version switch
{
UIGFVersion.Major2Minor3OrHigher => uigf.List
.Where(item => item.UIGFGachaType == queryType && item.Id < trimId)
.OrderByDescending(item => item.Id)
.Select(item => GachaItem.From(archiveId, item))
.ToList(),
UIGFVersion.Major2Minor2OrLower => uigf.List
.Where(item => item.UIGFGachaType == queryType && item.Id < trimId)
.OrderByDescending(item => item.Id)
.Select(item => GachaItem.From(archiveId, item, context.GetItemId(item)))
.ToList(),
_ => throw HutaoException.NotSupported(),
};
ThrowIfContainsInvalidItem(currentTypedList);
fullItems.AddRange(currentTypedList);
}
gachaLogDbService.AddGachaItemRange(fullItems);
await taskContext.SwitchToMainThreadAsync();
archives.MoveCurrentTo(archive);
}
private static void ThrowIfContainsInvalidItem(List<GachaItem> list)
{
// 越早的记录手工导入的可能性越高
// 错误率相对来说会更高
// 因此从尾部开始查找
if (list.LastOrDefault(item => item.ItemId is 0U) is { } item)
{
HutaoException.InvalidOperation(SH.FormatServiceGachaLogUIGFImportItemInvalidFormat(item.Id));
}
}
}

View File

@@ -21,9 +21,15 @@ internal sealed partial class GameAccountService : IGameAccountService
private ObservableReorderableDbCollection<GameAccount>? gameAccounts;
public ObservableReorderableDbCollection<GameAccount> GameAccountCollection
public async ValueTask<ObservableReorderableDbCollection<GameAccount>> GetGameAccountCollectionAsync()
{
get => gameAccounts ??= gameDbService.GetGameAccountCollection();
if (gameAccounts is null)
{
await taskContext.SwitchToBackgroundAsync();
gameAccounts = gameDbService.GetGameAccountCollection();
}
return gameAccounts;
}
public async ValueTask<GameAccount?> DetectGameAccountAsync(SchemeType schemeType)

View File

@@ -9,14 +9,14 @@ namespace Snap.Hutao.Service.Game.Account;
internal interface IGameAccountService
{
ObservableReorderableDbCollection<GameAccount> GameAccountCollection { get; }
ValueTask AttachGameAccountToUidAsync(GameAccount gameAccount, string uid);
GameAccount? DetectCurrentGameAccount(SchemeType schemeType);
ValueTask<GameAccount?> DetectGameAccountAsync(SchemeType schemeType);
ValueTask<ObservableReorderableDbCollection<GameAccount>> GetGameAccountCollectionAsync();
ValueTask ModifyGameAccountAsync(GameAccount gameAccount);
ValueTask RemoveGameAccountAsync(GameAccount gameAccount);

View File

@@ -11,10 +11,6 @@ using Snap.Hutao.Service.Game.PathAbstraction;
namespace Snap.Hutao.Service.Game;
/// <summary>
/// 游戏服务
/// </summary>
[HighQuality]
[ConstructorGenerated]
[Injection(InjectAs.Singleton, typeof(IGameServiceFacade))]
internal sealed partial class GameServiceFacade : IGameServiceFacade
@@ -23,55 +19,46 @@ internal sealed partial class GameServiceFacade : IGameServiceFacade
private readonly IGameAccountService gameAccountService;
private readonly IGamePathService gamePathService;
/// <inheritdoc/>
public ObservableReorderableDbCollection<GameAccount> GameAccountCollection
public ValueTask<ObservableReorderableDbCollection<GameAccount>> GetGameAccountCollectionAsync()
{
get => gameAccountService.GameAccountCollection;
return gameAccountService.GetGameAccountCollectionAsync();
}
/// <inheritdoc/>
public ValueTask<ValueResult<bool, string>> GetGamePathAsync()
{
return gamePathService.SilentGetGamePathAsync();
}
/// <inheritdoc/>
public ChannelOptions GetChannelOptions()
{
return gameChannelOptionsService.GetChannelOptions();
}
/// <inheritdoc/>
public ValueTask<GameAccount?> DetectGameAccountAsync(SchemeType scheme)
{
return gameAccountService.DetectGameAccountAsync(scheme);
}
/// <inheritdoc/>
public GameAccount? DetectCurrentGameAccount(SchemeType scheme)
{
return gameAccountService.DetectCurrentGameAccount(scheme);
}
/// <inheritdoc/>
public ValueTask AttachGameAccountToUidAsync(GameAccount gameAccount, string uid)
{
return gameAccountService.AttachGameAccountToUidAsync(gameAccount, uid);
}
/// <inheritdoc/>
public ValueTask ModifyGameAccountAsync(GameAccount gameAccount)
{
return gameAccountService.ModifyGameAccountAsync(gameAccount);
}
/// <inheritdoc/>
public ValueTask RemoveGameAccountAsync(GameAccount gameAccount)
{
return gameAccountService.RemoveGameAccountAsync(gameAccount);
}
/// <inheritdoc/>
public bool IsGameRunning()
{
return LaunchExecutionEnsureGameNotRunningHandler.IsGameRunning(out _);

View File

@@ -8,58 +8,23 @@ using Snap.Hutao.Service.Game.Configuration;
namespace Snap.Hutao.Service.Game;
/// <summary>
/// 游戏服务
/// </summary>
[HighQuality]
internal interface IGameServiceFacade
{
/// <summary>
/// 游戏内账号集合
/// </summary>
ObservableReorderableDbCollection<GameAccount> GameAccountCollection { get; }
/// <summary>
/// 将账号绑定到对应的Uid
/// 清除老账号的绑定状态
/// </summary>
/// <param name="gameAccount">游戏内账号</param>
/// <param name="uid">uid</param>
ValueTask AttachGameAccountToUidAsync(GameAccount gameAccount, string uid);
ValueTask<GameAccount?> DetectGameAccountAsync(SchemeType scheme);
/// <summary>
/// 异步获取游戏路径
/// </summary>
/// <returns>结果</returns>
ValueTask<ValueResult<bool, string>> GetGamePathAsync();
/// <summary>
/// 获取多通道值
/// </summary>
/// <returns>多通道值</returns>
ChannelOptions GetChannelOptions();
/// <summary>
/// 游戏是否正在运行
/// </summary>
/// <returns>是否正在运行</returns>
bool IsGameRunning();
/// <summary>
/// 异步修改游戏账号名称
/// </summary>
/// <param name="gameAccount">游戏账号</param>
/// <returns>任务</returns>
ValueTask ModifyGameAccountAsync(GameAccount gameAccount);
/// <summary>
/// 异步尝试移除账号
/// </summary>
/// <param name="gameAccount">账号</param>
/// <returns>任务</returns>
ValueTask RemoveGameAccountAsync(GameAccount gameAccount);
GameAccount? DetectCurrentGameAccount(SchemeType scheme);
ValueTask<ObservableReorderableDbCollection<GameAccount>> GetGameAccountCollectionAsync();
}

View File

@@ -1,10 +1,6 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Core.ExceptionService;
using Snap.Hutao.Win32.Foundation;
using static Snap.Hutao.Win32.Kernel32;
namespace Snap.Hutao.Service.Game.Unlocker;
/// <summary>
@@ -19,19 +15,34 @@ internal static class GameFpsAddress
public static unsafe void UnsafeFindFpsAddress(GameFpsUnlockerContext context, in RequiredRemoteModule remoteModule, in RequiredLocalModule localModule)
{
int offsetToUserAssembly = IndexOfPattern(localModule.UserAssembly.AsSpan());
HutaoException.ThrowIfNot(offsetToUserAssembly >= 0, SH.ServiceGameUnlockerInterestedPatternNotFound);
Span<byte> executableSpan = localModule.Executable.AsSpan();
int offsetToExecutable = 0;
nuint localVirtualAddress = 0;
do
{
int index = IndexOfPattern(executableSpan[offsetToExecutable..], out int patternLength);
if (index < 0)
{
break;
}
nuint rip = localModule.UserAssembly.Address + (uint)offsetToUserAssembly;
rip += 5U;
rip += (nuint)(*(int*)(rip + 2U) + 6);
offsetToExecutable += index;
nuint remoteVirtualAddress = remoteModule.UserAssembly.Address + (rip - localModule.UserAssembly.Address);
nuint rip = localModule.Executable.Address + (uint)offsetToExecutable;
rip += 5U;
rip += (nuint)(*(int*)(rip + 1U) + 5);
nuint ptr = 0;
SpinWait.SpinUntil(() => UnsafeReadProcessMemory(context.AllAccess, remoteVirtualAddress, out ptr) && ptr != 0);
if (*(byte*)rip is ASM_JMP)
{
localVirtualAddress = rip;
break;
}
nuint localVirtualAddress = ptr - remoteModule.UnityPlayer.Address + localModule.UnityPlayer.Address;
offsetToExecutable += patternLength;
}
while (true);
ArgumentOutOfRangeException.ThrowIfZero(localVirtualAddress);
while (*(byte*)localVirtualAddress is ASM_CALL or ASM_JMP)
{
@@ -39,22 +50,15 @@ internal static class GameFpsAddress
}
localVirtualAddress += *(uint*)(localVirtualAddress + 2) + 6;
nuint relativeVirtualAddress = localVirtualAddress - localModule.UnityPlayer.Address;
context.FpsAddress = remoteModule.UnityPlayer.Address + relativeVirtualAddress;
nuint relativeVirtualAddress = localVirtualAddress - localModule.Executable.Address;
context.FpsAddress = remoteModule.Executable.Address + relativeVirtualAddress;
}
private static int IndexOfPattern(in ReadOnlySpan<byte> memory)
private static int IndexOfPattern(in ReadOnlySpan<byte> span, out int patternLength)
{
// B9 3C 00 00 00 FF 15
ReadOnlySpan<byte> part = [0xB9, 0x3C, 0x00, 0x00, 0x00, 0xFF, 0x15];
return memory.IndexOf(part);
}
private static unsafe bool UnsafeReadProcessMemory(HANDLE hProcess, nuint baseAddress, out nuint value)
{
value = 0;
bool result = ReadProcessMemory(hProcess, (void*)baseAddress, ref value, out _);
HutaoException.ThrowIfNot(result, SH.ServiceGameUnlockerReadProcessMemoryPointerAddressFailed);
return result;
// B9 3C 00 00 00 E8
ReadOnlySpan<byte> part = [0xB9, 0x3C, 0x00, 0x00, 0x00, 0xE8];
patternLength = part.Length;
return span.IndexOf(part);
}
}

View File

@@ -51,12 +51,9 @@ internal abstract class GameFpsUnlocker : IGameFpsUnlocker
private static RequiredLocalModule LoadRequiredLocalModule(GameFileSystem gameFileSystem)
{
string gameFoler = gameFileSystem.GameDirectory;
string dataFoler = gameFileSystem.DataDirectory;
LOAD_LIBRARY_FLAGS flags = LOAD_LIBRARY_FLAGS.LOAD_LIBRARY_AS_IMAGE_RESOURCE;
HMODULE unityPlayerAddress = LoadLibraryExW(System.IO.Path.Combine(gameFoler, "UnityPlayer.dll"), default, flags);
HMODULE userAssemblyAddress = LoadLibraryExW(System.IO.Path.Combine(dataFoler, "Native", "UserAssembly.dll"), default, flags);
HMODULE executaleAddress = LoadLibraryExW(gameFileSystem.GameFilePath, default, flags);
return new(unityPlayerAddress, userAssemblyAddress);
return new(executaleAddress);
}
}

View File

@@ -42,16 +42,15 @@ internal static class GameProcessModule
private static FindModuleResult UnsafeGetGameModuleInfo(in HANDLE hProcess, out RequiredRemoteModule info)
{
FindModuleResult unityPlayerResult = UnsafeFindModule(hProcess, "UnityPlayer.dll", out Module unityPlayer);
FindModuleResult userAssemblyResult = UnsafeFindModule(hProcess, "UserAssembly.dll", out Module userAssembly);
FindModuleResult result = UnsafeFindModule(hProcess, GameConstants.YuanShenFileName, GameConstants.GenshinImpactFileName, out Module executable);
if (unityPlayerResult is FindModuleResult.Ok && userAssemblyResult is FindModuleResult.Ok)
if (result is FindModuleResult.Ok)
{
info = new(unityPlayer, userAssembly);
info = new(executable);
return FindModuleResult.Ok;
}
if (unityPlayerResult is FindModuleResult.NoModuleFound && userAssemblyResult is FindModuleResult.NoModuleFound)
if (result is FindModuleResult.NoModuleFound)
{
info = default;
return FindModuleResult.NoModuleFound;
@@ -61,7 +60,7 @@ internal static class GameProcessModule
return FindModuleResult.ModuleNotLoaded;
}
private static unsafe FindModuleResult UnsafeFindModule(in HANDLE hProcess, in ReadOnlySpan<char> moduleName, out Module module)
private static unsafe FindModuleResult UnsafeFindModule(in HANDLE hProcess, in ReadOnlySpan<char> moduleName1, in ReadOnlySpan<char> moduleName2, out Module module)
{
HMODULE[] buffer = new HMODULE[128];
if (!K32EnumProcessModules(hProcess, buffer, out uint actualSize))
@@ -86,7 +85,8 @@ internal static class GameProcessModule
fixed (char* lpBaseName = baseName)
{
if (!moduleName.SequenceEqual(MemoryMarshal.CreateReadOnlySpanFromNullTerminated(lpBaseName)))
ReadOnlySpan<char> baseNameSpan = MemoryMarshal.CreateReadOnlySpanFromNullTerminated(lpBaseName);
if (!(moduleName1.SequenceEqual(baseNameSpan) || moduleName2.SequenceEqual(baseNameSpan)))
{
continue;
}

View File

@@ -12,30 +12,24 @@ namespace Snap.Hutao.Service.Game.Unlocker;
internal readonly struct RequiredLocalModule : IDisposable
{
public readonly bool HasValue = false;
public readonly Module UnityPlayer;
public readonly Module UserAssembly;
public readonly Module Executable;
private readonly HMODULE hModuleUnityPlayer;
private readonly HMODULE hModuleUserAssembly;
private readonly HMODULE hModuleExecutable;
public RequiredLocalModule(HMODULE unityPlayer, HMODULE userAssembly)
public RequiredLocalModule(HMODULE executable)
{
hModuleUnityPlayer = unityPlayer;
hModuleUserAssembly = userAssembly;
hModuleExecutable = executable;
// Align the pointer
nint unityPlayerMappedView = (nint)(unityPlayer & ~0x3L);
nint userAssemblyMappedView = (nint)(userAssembly & ~0x3L);
nint executableMappedView = (nint)(executable & ~0x3L);
Executable = new((nuint)executableMappedView, GetImageSize(executableMappedView));
HasValue = true;
UnityPlayer = new((nuint)unityPlayerMappedView, GetImageSize(unityPlayerMappedView));
UserAssembly = new((nuint)userAssemblyMappedView, GetImageSize(userAssemblyMappedView));
}
public void Dispose()
{
FreeLibrary(hModuleUnityPlayer);
FreeLibrary(hModuleUserAssembly);
FreeLibrary(hModuleExecutable);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]

View File

@@ -6,13 +6,11 @@ namespace Snap.Hutao.Service.Game.Unlocker;
internal readonly struct RequiredRemoteModule
{
public readonly bool HasValue = false;
public readonly Module UnityPlayer;
public readonly Module UserAssembly;
public readonly Module Executable;
public RequiredRemoteModule(in Module unityPlayer, in Module userAssembly)
public RequiredRemoteModule(in Module executable)
{
HasValue = true;
UnityPlayer = unityPlayer;
UserAssembly = userAssembly;
Executable = executable;
}
}

View File

@@ -0,0 +1,12 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Model.Metadata.Reliquary;
using Snap.Hutao.Model.Primitive;
namespace Snap.Hutao.Service.Metadata.ContextAbstraction;
internal interface IMetadataDictionaryIdReliquarySetSource
{
Dictionary<ReliquarySetId, ReliquarySet> IdReliquarySetMap { get; set; }
}

View File

@@ -86,6 +86,11 @@ internal static class MetadataServiceContextExtension
dictionaryIdReliquaryMainPropertySource.IdReliquaryMainPropertyMap = await metadataService.GetIdToReliquaryMainPropertyMapAsync(token).ConfigureAwait(false);
}
if (context is IMetadataDictionaryIdReliquarySetSource dictionaryIdReliquarySetSource)
{
dictionaryIdReliquarySetSource.IdReliquarySetMap = await metadataService.GetIdToReliquarySetMapAsync(token).ConfigureAwait(false);
}
if (context is IMetadataDictionaryIdReliquarySubAffixSource dictionaryIdReliquarySubAffixSource)
{
dictionaryIdReliquarySubAffixSource.IdReliquarySubAffixMap = await metadataService.GetIdToReliquarySubAffixMapAsync(token).ConfigureAwait(false);

View File

@@ -86,6 +86,11 @@ internal static class MetadataServiceDictionaryExtension
return metadataService.FromCacheAsDictionaryAsync<ReliquaryMainAffix, ReliquaryMainAffixId, FightProperty>(FileNameReliquaryMainAffix, r => r.Id, r => r.Type, token);
}
public static ValueTask<Dictionary<ReliquarySetId, ReliquarySet>> GetIdToReliquarySetMapAsync(this IMetadataService metadataService, CancellationToken token = default)
{
return metadataService.FromCacheAsDictionaryAsync<ReliquarySetId, ReliquarySet>(FileNameReliquarySet, r => r.SetId, token);
}
public static ValueTask<Dictionary<ReliquarySubAffixId, ReliquarySubAffix>> GetIdToReliquarySubAffixMapAsync(this IMetadataService metadataService, CancellationToken token = default)
{
return metadataService.FromCacheAsDictionaryAsync<ReliquarySubAffixId, ReliquarySubAffix>(FileNameReliquarySubAffix, a => a.Id, token);

View File

@@ -8,20 +8,15 @@ namespace Snap.Hutao.Service.Notification;
/// <inheritdoc/>
[HighQuality]
[ConstructorGenerated]
[Injection(InjectAs.Singleton, typeof(IInfoBarService))]
internal sealed class InfoBarService : IInfoBarService
internal sealed partial class InfoBarService : IInfoBarService
{
private readonly ILogger<InfoBarService> logger;
private readonly ITaskContext taskContext;
private ObservableCollection<InfoBarOptions>? collection;
public InfoBarService(IServiceProvider serviceProvider)
{
logger = serviceProvider.GetRequiredService<ILogger<InfoBarService>>();
taskContext = serviceProvider.GetRequiredService<ITaskContext>();
}
/// <inheritdoc/>
public ObservableCollection<InfoBarOptions> Collection
{
@@ -45,6 +40,6 @@ internal sealed class InfoBarService : IInfoBarService
await taskContext.SwitchToMainThreadAsync();
ArgumentNullException.ThrowIfNull(collection);
collection.Add(builder.Options);
collection.Insert(0, builder.Options);
}
}

View File

@@ -10,7 +10,7 @@ internal interface ISpiralAbyssRecordDbService : IAppDbService<SpiralAbyssEntry>
{
void AddSpiralAbyssEntry(SpiralAbyssEntry entry);
Dictionary<uint, SpiralAbyssEntry> GetSpiralAbyssEntryListByUid(string uid);
Dictionary<uint, SpiralAbyssEntry> GetSpiralAbyssEntryMapByUid(string uid);
void UpdateSpiralAbyssEntry(SpiralAbyssEntry entry);
}

View File

@@ -14,7 +14,7 @@ internal sealed partial class SpiralAbyssRecordDbService : ISpiralAbyssRecordDbS
public IServiceProvider ServiceProvider { get => serviceProvider; }
public Dictionary<uint, SpiralAbyssEntry> GetSpiralAbyssEntryListByUid(string uid)
public Dictionary<uint, SpiralAbyssEntry> GetSpiralAbyssEntryMapByUid(string uid)
{
return this.Query(query => query
.Where(s => s.Uid == uid)

View File

@@ -34,6 +34,7 @@ internal sealed partial class SpiralAbyssRecordService : ISpiralAbyssRecordServi
{
if (await metadataService.InitializeAsync().ConfigureAwait(false))
{
// TODO replace to IMetadataContext
metadataContext = new()
{
IdScheduleMap = await metadataService.GetIdToTowerScheduleMapAsync().ConfigureAwait(false),
@@ -60,7 +61,7 @@ internal sealed partial class SpiralAbyssRecordService : ISpiralAbyssRecordServi
if (spiralAbysses is null)
{
await taskContext.SwitchToBackgroundAsync();
Dictionary<uint, SpiralAbyssEntry> entryMap = spiralAbyssRecordDbService.GetSpiralAbyssEntryListByUid(userAndUid.Uid.Value);
Dictionary<uint, SpiralAbyssEntry> entryMap = spiralAbyssRecordDbService.GetSpiralAbyssEntryMapByUid(userAndUid.Uid.Value);
ArgumentNullException.ThrowIfNull(metadataContext);
spiralAbysses = metadataContext.IdScheduleMap.Values

View File

@@ -0,0 +1,9 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
namespace Snap.Hutao.Service.UIGF;
internal interface IUIGFExportService
{
ValueTask ExportAsync(UIGFExportOptions exportOptions, CancellationToken token);
}

View File

@@ -0,0 +1,9 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
namespace Snap.Hutao.Service.UIGF;
internal interface IUIGFImportService
{
ValueTask ImportAsync(UIGFImportOptions importOptions, CancellationToken token);
}

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