Skip to content

Commit 8ee7e09

Browse files
committed
Steam编辑数据支持导入导出
修复Steam新成就格式无限加载问题
1 parent e464f28 commit 8ee7e09

File tree

5 files changed

+251
-105
lines changed

5 files changed

+251
-105
lines changed

src/BD.WTTS.Client.Plugins.Accelerator/Services/Mvvm/ProxyService.Operate.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,9 @@ async Task<OperateProxyServiceResult> StartProxyServiceCoreAsync()
7474
bool useDoh = ProxySettings.UseDoh.Value;
7575
string? customDohAddres = ProxySettings.CustomDohAddres2.Value;
7676

77+
string? proxyToken = proxyDomains.Any_Nullable(s => s.ProxyType == ProxyType.ServerAccelerate || s.Items.Any_Nullable(x => x.ProxyType == ProxyType.ServerAccelerate)) ?
78+
await TryRequestServerSideProxyToken() : null;
79+
7780
if (ProxySettings.ProxyBeforeDNSCheck.Value)
7881
{
7982
if (useDoh)
@@ -229,9 +232,6 @@ await EnableProxyScripts.ContinueWith(e =>
229232
}
230233
}
231234

232-
string? proxyToken = proxyDomains.Any_Nullable(s => s.ProxyType == ProxyType.ServerAccelerate || s.Items.Any_Nullable(x => x.ProxyType == ProxyType.ServerAccelerate)) ?
233-
await TryRequestServerSideProxyToken() : null;
234-
235235
ReverseProxySettings reverseProxySettings = new(proxyDomains, scripts,
236236
isEnableScript, isOnlyWorkSteamBrowser, proxyPort,
237237
proxyIp, proxyMode, isProxyGOG, onlyEnableProxyScript,

src/BD.WTTS.Client.Plugins.GameList/UI/ViewModels/Pages/EditAppsPageViewModel.cs

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ public sealed class EditAppsPageViewModel : ViewModelBase
1616

1717
public bool IsSteamEditedAppsEmpty => !SteamEditedApps.Any_Nullable();
1818

19+
[Reactive]
20+
public bool IsExportingBackup { get; set; }
21+
1922
public ICommand EditAppInfoClickCommand { get; }
2023

2124
public EditAppsPageViewModel()
@@ -56,6 +59,114 @@ public async Task SaveSteamEditedApps()
5659
}
5760
}
5861

62+
public async Task ExportSteamEditedAppsBackup()
63+
{
64+
if (IsExportingBackup)
65+
{
66+
return;
67+
}
68+
69+
if (!SteamEditedApps.Any())
70+
{
71+
Toast.Show(ToastIcon.Warning, "没有可导出的已修改游戏数据。");
72+
return;
73+
}
74+
75+
var result = await FilePicker2.SaveAsync(new PickOptions
76+
{
77+
PickerTitle = "导出编辑游戏数据备份",
78+
InitialFileName = $"steam-edited-apps-{DateTime.Now:yyyyMMddHHmmss}.stmbak",
79+
});
80+
81+
if (result == null)
82+
{
83+
return;
84+
}
85+
86+
try
87+
{
88+
IsExportingBackup = true;
89+
var exportData = await Task.Run(async () =>
90+
{
91+
var stmService = ISteamService.Instance;
92+
var applist = await stmService.GetAppInfos(true);
93+
var editApps = SteamConnectService.Current.SteamApps.Items
94+
.Where(s => s.IsEdited)
95+
.ToDictionary(s => s.AppId, s => s);
96+
var modifiedApps = new List<ModifiedApp>();
97+
98+
foreach (var app in applist)
99+
{
100+
if (editApps.TryGetValue(app.AppId, out var editApp))
101+
{
102+
app.SetEditProperty(editApp);
103+
modifiedApps.Add(new ModifiedApp(app));
104+
}
105+
}
106+
107+
return (ModifiedApps: modifiedApps, Bytes: Serializable.SMP(modifiedApps));
108+
});
109+
110+
if (!exportData.ModifiedApps.Any())
111+
{
112+
Toast.Show(ToastIcon.Error, "导出失败:没有可序列化的修改数据。");
113+
return;
114+
}
115+
116+
using var stream = result.OpenWrite();
117+
await stream.WriteAsync(exportData.Bytes, 0, exportData.Bytes.Length);
118+
await stream.FlushAsync();
119+
120+
Toast.Show(ToastIcon.Success, $"导出成功,共 {exportData.ModifiedApps.Count} 条修改数据。");
121+
}
122+
catch
123+
{
124+
Toast.Show(ToastIcon.Error, "导出失败,请检查文件权限后重试。");
125+
}
126+
finally
127+
{
128+
IsExportingBackup = false;
129+
}
130+
}
131+
132+
public async Task ImportSteamEditedAppsBackup()
133+
{
134+
var file = await FilePicker2.PickAsync(new PickOptions
135+
{
136+
PickerTitle = "导入编辑游戏数据备份",
137+
});
138+
139+
if (file == null)
140+
{
141+
return;
142+
}
143+
144+
try
145+
{
146+
await using var stream = await file.OpenReadAsync();
147+
var modifiedApps = Serializable.DMP<List<ModifiedApp>>(stream);
148+
if (!modifiedApps.Any_Nullable())
149+
{
150+
Toast.Show(ToastIcon.Warning, "导入文件中没有可还原的数据。");
151+
return;
152+
}
153+
154+
var applyCount = SteamConnectService.Current.ApplyModifiedApps(modifiedApps);
155+
if (applyCount <= 0)
156+
{
157+
Toast.Show(ToastIcon.Warning, "导入完成,但未匹配到可还原的游戏。\n请确认账号或游戏库与备份来源一致。");
158+
return;
159+
}
160+
161+
this.RaisePropertyChanged(nameof(IsSteamEditedAppsEmpty));
162+
Toast.Show(ToastIcon.Success, $"导入成功,已还原 {applyCount} 个游戏的修改数据。");
163+
}
164+
catch
165+
{
166+
Toast.Show(ToastIcon.Error, "导入失败,文件格式可能不正确。");
167+
}
168+
}
169+
59170
//public async Task ClearSteamEditedApps()
60171
//{
61172
// if (await MessageBox.ShowAsync("确定要重置所有的已修改数据吗?(该操作不可还原)", AssemblyInfo.Trademark, MessageBox.Button.OKCancel) == MessageBox.Result.OK)

src/BD.WTTS.Client.Plugins.GameList/UI/ViewModels/Windows/AchievementAppPageViewModel.cs

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -200,10 +200,32 @@ bool LoadUserGameStatsSchema()
200200
continue;
201201
}
202202

203-
var rawType = stat["type_int"].Valid
204-
? stat["type_int"].AsInteger(0)
205-
: stat["type"].AsInteger(0);
206-
var type = (UserStatType)rawType;
203+
UserStatType type;
204+
205+
// schema in the new format
206+
var typeNode = stat["type"];
207+
if (typeNode.Valid == true && typeNode.Type == KeyValueType.String)
208+
{
209+
if (Enum.TryParse((string)typeNode.Value, true, out type) == false)
210+
{
211+
type = UserStatType.Invalid;
212+
}
213+
}
214+
else
215+
{
216+
type = UserStatType.Invalid;
217+
}
218+
219+
// schema in the old format
220+
if (type == UserStatType.Invalid)
221+
{
222+
var typeIntNode = stat["type_int"];
223+
var rawType = typeIntNode.Valid == true
224+
? typeIntNode.AsInteger(0)
225+
: typeNode.AsInteger(0);
226+
type = (UserStatType)rawType;
227+
}
228+
207229
switch (type)
208230
{
209231
case UserStatType.Invalid:

src/BD.WTTS.Client.Plugins.GameList/UI/Views/Pages/EditAppsPage.axaml

Lines changed: 80 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,18 @@
6767
Theme="{StaticResource TransparentButton}">
6868
<DropDownButton.Flyout>
6969
<ui:FAMenuFlyout Placement="BottomEdgeAlignedRight">
70+
<ui:MenuFlyoutItem
71+
Command="{Binding ExportSteamEditedAppsBackup}"
72+
IsEnabled="{Binding !IsExportingBackup}"
73+
Text="{Binding Path=Res.Export,
74+
Mode=OneWay,
75+
Source={x:Static s:ResourceService.Current}}" />
76+
<ui:MenuFlyoutItem
77+
Command="{Binding ImportSteamEditedAppsBackup}"
78+
Text="{Binding Path=Res.Import,
79+
Mode=OneWay,
80+
Source={x:Static s:ResourceService.Current}}" />
81+
<ui:MenuFlyoutSeparator />
7082
<!--<ui:MenuFlyoutItem
7183
Command="{Binding DeleteAllButton_Click}"
7284
IconSource="Delete"
@@ -100,88 +112,76 @@
100112
IsLoading="false"
101113
IsShowNoResultText="{Binding !!!SteamEditedApps.Count, Mode=OneWay}"
102114
NoResultMessage="{Binding Path=Res.SaveEditedAppInfo_AppsEmpty, Mode=OneWay, Source={x:Static s:ResourceService.Current}}">
103-
<ItemsControl ItemsSource="{Binding SteamEditedApps}">
104-
<ItemsControl.ItemsPanel>
105-
<ItemsPanelTemplate>
106-
<WrapPanel />
107-
</ItemsPanelTemplate>
108-
</ItemsControl.ItemsPanel>
109-
<ItemsControl.ItemTemplate>
115+
<ui:ItemsRepeater ItemsSource="{Binding SteamEditedApps}">
116+
<ui:ItemsRepeater.Layout>
117+
<ui:UniformGridLayout
118+
ItemsJustification="Start"
119+
ItemsStretch="Uniform"
120+
MinColumnSpacing="10"
121+
MinItemHeight="310"
122+
MinItemWidth="150"
123+
MinRowSpacing="10" />
124+
</ui:ItemsRepeater.Layout>
125+
<ui:ItemsRepeater.ItemTemplate>
110126
<DataTemplate DataType="spp:SteamApp">
111-
<Border Margin="5" spp:Animations.EnableAnimations="False">
112-
<spp:AppItem
113-
Title="{Binding DisplayName}"
114-
Height="270"
115-
MaxWidth="120"
116-
Classes="Vertical"
117-
ClickCommand="{Binding $parent[spp:PageBase].((spp:EditAppsPageViewModel)DataContext).EditAppInfoClickCommand}"
118-
ClickCommandParameter="{Binding}"
119-
Cursor="Hand">
120-
<spp:AppItem.Status>
121-
<Border Classes="Status">
122-
<TextBlock Text="{Binding Path=Res.Edit, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
127+
<spp:AppItem
128+
Title="{Binding DisplayName}"
129+
Classes="Vertical"
130+
ClickCommand="{Binding $parent[spp:PageBase].((spp:EditAppsPageViewModel)DataContext).EditAppInfoClickCommand}"
131+
ClickCommandParameter="{Binding}"
132+
Cursor="Hand">
133+
<spp:AppItem.Status>
134+
<Border Classes="Status">
135+
<TextBlock Text="{Binding Path=Res.Edit, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
136+
</Border>
137+
</spp:AppItem.Status>
138+
<spp:AppItem.Image>
139+
<Panel Width="{Binding $parent[spp:AppItem].Bounds.Width}">
140+
<spp:Image2
141+
Name="AppImage"
142+
DecodeWidth="120"
143+
FallbackSource="avares://BD.WTTS.Client.Plugins.GameList/UI/Assets/defaultappimage.png"
144+
RenderOptions.BitmapInterpolationMode="HighQuality"
145+
Source="{Binding LibraryGridStream^}"
146+
Stretch="UniformToFill" />
147+
<TextBlock
148+
Margin="8,0"
149+
HorizontalAlignment="Center"
150+
VerticalAlignment="Center"
151+
Foreground="{DynamicResource TextFillColorPrimaryBrush}"
152+
IsVisible="{Binding #AppImage.IsFailed}"
153+
Text="{Binding DisplayName}"
154+
TextAlignment="Center"
155+
TextWrapping="WrapWithOverflow"
156+
Theme="{StaticResource BodyStrongTextBlockStyle}" />
157+
<Border
158+
Margin="10,0,10,-3"
159+
HorizontalAlignment="Center"
160+
VerticalAlignment="Bottom"
161+
Background="#3D4450"
162+
CornerRadius="3"
163+
IsVisible="{Binding IsInstalled}">
164+
<DockPanel Margin="10,3" HorizontalAlignment="Center">
165+
<TextBlock
166+
Margin="0,0,5,0"
167+
VerticalAlignment="Center"
168+
FontSize="11"
169+
Foreground="{DynamicResource TextFillColorPrimaryBrush}"
170+
IsVisible="{Binding InstalledDrive, Converter={StaticResource IsNullConverter}, ConverterParameter=invert}"
171+
Text="{Binding InstalledDrive}" />
172+
<TextBlock
173+
VerticalAlignment="Center"
174+
FontSize="11"
175+
Foreground="{DynamicResource TextFillColorPrimaryBrush}"
176+
Text="{Binding SizeOnDisk, Converter={StaticResource StringFormatConverter}, ConverterParameter=size}"
177+
TextWrapping="Wrap" />
178+
</DockPanel>
123179
</Border>
124-
</spp:AppItem.Status>
125-
<!--<spp:AppItem.ActionButton>
126-
<Button
127-
Command="{Binding $parent[spp:PageBase].((spp:EditAppsPageViewModel)DataContext).DeleteButtonCommand}"
128-
CommandParameter="{Binding}"
129-
Cursor="Hand"
130-
ToolTip.Tip="{Binding Path=Res.GameList_RemoveItemBtn, Mode=OneWay, Source={x:Static s:ResourceService.Current}}">
131-
<Viewbox>
132-
<ui:SymbolIcon Symbol="Delete" />
133-
</Viewbox>
134-
</Button>
135-
</spp:AppItem.ActionButton>-->
136-
<spp:AppItem.Image>
137-
<Panel MinWidth="{Binding $parent[spp:AppItem].MinWidth}" MaxWidth="{Binding $parent[spp:AppItem].MaxWidth}">
138-
<spp:Image2
139-
Name="AppImage"
140-
DecodeWidth="120"
141-
FallbackSource="avares://BD.WTTS.Client.Plugins.GameList/UI/Assets/defaultappimage.png"
142-
RenderOptions.BitmapInterpolationMode="HighQuality"
143-
Source="{Binding LibraryGridStream^}"
144-
Stretch="UniformToFill" />
145-
<TextBlock
146-
Margin="8,0"
147-
HorizontalAlignment="Center"
148-
VerticalAlignment="Center"
149-
Foreground="{DynamicResource TextFillColorPrimaryBrush}"
150-
IsVisible="{Binding #AppImage.IsFailed}"
151-
Text="{Binding DisplayName}"
152-
TextAlignment="Center"
153-
TextWrapping="WrapWithOverflow"
154-
Theme="{StaticResource BodyStrongTextBlockStyle}" />
155-
156-
<Border
157-
Margin="10,0,10,-3"
158-
HorizontalAlignment="Center"
159-
VerticalAlignment="Bottom"
160-
Background="#3D4450"
161-
CornerRadius="3"
162-
IsVisible="{Binding IsInstalled}">
163-
<DockPanel Margin="10,3" HorizontalAlignment="Center">
164-
<TextBlock
165-
Margin="0,0,5,0"
166-
VerticalAlignment="Center"
167-
FontSize="11"
168-
Foreground="{DynamicResource TextFillColorPrimaryBrush}"
169-
IsVisible="{Binding InstalledDrive, Converter={StaticResource IsNullConverter}, ConverterParameter=invert}"
170-
Text="{Binding InstalledDrive}" />
171-
<TextBlock
172-
VerticalAlignment="Center"
173-
FontSize="11"
174-
Foreground="{DynamicResource TextFillColorPrimaryBrush}"
175-
Text="{Binding SizeOnDisk, Converter={StaticResource StringFormatConverter}, ConverterParameter=size}"
176-
TextWrapping="Wrap" />
177-
</DockPanel>
178-
</Border>
179-
</Panel>
180-
</spp:AppItem.Image>
181-
</spp:AppItem>
182-
</Border>
180+
</Panel>
181+
</spp:AppItem.Image>
182+
</spp:AppItem>
183183
</DataTemplate>
184-
</ItemsControl.ItemTemplate>
185-
</ItemsControl>
184+
</ui:ItemsRepeater.ItemTemplate>
185+
</ui:ItemsRepeater>
186186
</spp:ContentLoader>
187187
</spp:PageBase>

0 commit comments

Comments
 (0)