PageDownloadCompDetail.xaml.vb 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538
  1. Imports System.Collections.ObjectModel
  2. Public Class PageDownloadResourceDetail
  3. Private ResourceItem As MyResourceItem = Nothing
  4. ''' <summary>
  5. ''' 当前页面应展示的内容类别。
  6. ''' 若当前页面是在查看前置时进入,则为 Any;这也能用于指示当前是否在查看前置。
  7. ''' </summary>
  8. Private TargetResourceType As ResourceTypes
  9. Private Project As ResourceProject
  10. Private TargetVersion As String
  11. Private TargetLoaders As ModLoaders
  12. Public Sub LoadPageArguments() Handles Me.Loaded
  13. Project = FrmMain.PageCurrent.Additional(0)
  14. TargetVersion = FrmMain.PageCurrent.Additional(2)
  15. TargetLoaders = CType(FrmMain.PageCurrent.Additional(3), ModLoaders)
  16. If TargetLoaders <> ModLoaders.None AndAlso Not TargetLoaders.Flags.IsSingle Then TargetLoaders = TargetLoaders.Flags.First() '强制取第一个
  17. TargetResourceType = FrmMain.PageCurrent.Additional(4)
  18. End Sub
  19. #Region "加载器"
  20. Private ResourceFileLoader As New LoaderTask(Of Integer, List(Of ResourceVersion))(
  21. "Resource File",
  22. Sub(Task)
  23. LoadPageArguments()
  24. Dim Result = ResourceVersion.FromProjectId(Project.Id, Project.Platform)
  25. If Task.IsCanceled Then Return
  26. Task.Output = Result
  27. End Sub)
  28. '初始化加载器信息
  29. Private Sub PageDownloadResourceDetail_Inited(sender As Object, e As EventArgs) Handles Me.Initialized
  30. LoadPageArguments()
  31. PageLoaderInit(Load, PanLoad, PanMain, CardIntro, ResourceFileLoader, AddressOf Load_OnFinish)
  32. End Sub
  33. '自动重试
  34. Private Sub Load_State(sender As Object, state As MyLoading.MyLoadingState, oldState As MyLoading.MyLoadingState) Handles Load.StateChanged
  35. Select Case ResourceFileLoader.State
  36. Case LoadState.Failed
  37. Dim ErrorMessage As String = ""
  38. If ResourceFileLoader.Error IsNot Nothing Then ErrorMessage = ResourceFileLoader.Error.Message
  39. If ErrorMessage.Contains("不是有效的 json 文件") Then
  40. Logger.Warn("下载的文件 json 列表损坏,已自动重试")
  41. PageLoaderRestart()
  42. End If
  43. End Select
  44. End Sub
  45. '结果 UI 化
  46. Private Class CardSorter
  47. Implements IComparer(Of String)
  48. Public Topmost As String = ""
  49. Public Function Compare(x As String, y As String) As Integer Implements IComparer(Of String).Compare
  50. '相同
  51. If x = y Then Return 0
  52. '置顶
  53. If x = Topmost Then Return -1
  54. If y = Topmost Then Return 1
  55. '特殊版本
  56. Dim IsXSpecial As Boolean = Not x.Contains(".")
  57. Dim IsYSpecial As Boolean = Not y.Contains(".")
  58. If IsXSpecial AndAlso IsYSpecial Then Return x.CompareTo(y)
  59. If IsXSpecial Then Return 1
  60. If IsYSpecial Then Return -1
  61. '比较版本号
  62. Dim VersionCodeSort = -CompareVersion(x.Replace(x.BeforeFirst(" ") & " ", ""), y.Replace(y.BeforeFirst(" ") & " ", ""))
  63. If VersionCodeSort <> 0 Then Return VersionCodeSort
  64. '比较全部
  65. Return -CompareVersion(x, y)
  66. End Function
  67. Public Sub New(Optional Topmost As String = "")
  68. Me.Topmost = If(Topmost, "")
  69. End Sub
  70. End Class
  71. Private VersionFilter As String
  72. Private GroupedDrop As Boolean '是否按 Drop 筛选(1.21 / 1.20 / 1.19 / ...)而非小版本号(1.21.1 / 1.21 / 1.20.4 / ...)
  73. Private GroupedOld As Boolean '是否折叠远古版本为一个选项
  74. '筛选类型相同的结果(Modrinth 会返回 Mod、服务端插件、数据包混合的列表)
  75. Private Function GetResults() As List(Of ResourceVersion)
  76. Dim Results As List(Of ResourceVersion) = ResourceFileLoader.Output
  77. Select Case TargetResourceType
  78. Case ResourceTypes.Any
  79. Results = Results.Where(Function(r) r.ResourceType <> ResourceTypes.Plugin).ToList
  80. Case ResourceTypes.Shader, ResourceTypes.ResourcePack
  81. '不筛选光影和资源包,否则原版光影会因为是资源包格式而被过滤(#6473)
  82. Case Else
  83. Results = Results.Where(Function(r) r.ResourceType = TargetResourceType).ToList
  84. End Select
  85. Return Results
  86. End Function
  87. Private Sub Load_OnFinish()
  88. Dim Results = GetResults()
  89. '初始化筛选器
  90. Dim Filters As List(Of String) = Nothing
  91. Dim UpdateFilters =
  92. Sub()
  93. Filters = Results.SelectMany(Function(v) v.GameVersions).Select(Function(v) GetGroupedVersionName(v, GroupedDrop, GroupedOld)).
  94. Distinct.OrderByDescending(Function(s) s, New VersionComparer).ToList
  95. End Sub
  96. '确定分组方式
  97. GroupedDrop = False : GroupedOld = False
  98. UpdateFilters()
  99. If Filters.Count < 9 Then GoTo GroupDone
  100. GroupedDrop = True : GroupedOld = False
  101. UpdateFilters()
  102. If Filters.Count < 9 Then GoTo GroupDone
  103. GroupedDrop = False : GroupedOld = True
  104. UpdateFilters()
  105. If Filters.Count < 9 Then GoTo GroupDone
  106. GroupedDrop = True : GroupedOld = True
  107. UpdateFilters()
  108. GroupDone:
  109. 'UI 化筛选器
  110. PanFilter.Children.Clear()
  111. If Filters.Count < 2 Then
  112. CardFilter.Visibility = Visibility.Collapsed
  113. VersionFilter = Nothing
  114. Else
  115. CardFilter.Visibility = Visibility.Visible
  116. Filters.Insert(0, "全部")
  117. '转化为按钮
  118. For Each Version As String In Filters
  119. Dim NewButton As New MyRadioButton With {
  120. .Text = Version, .Margin = New Thickness(2, 0, 2, 0), .ColorType = MyRadioButton.ColorState.Highlight}
  121. NewButton.LabText.Margin = New Thickness(-2, 0, 10, 0)
  122. AddHandler NewButton.Check,
  123. Sub(sender As MyRadioButton, raiseByMouse As Boolean)
  124. VersionFilter = If(sender.Text = "全部", Nothing, sender.Text)
  125. UpdateFilterResult()
  126. End Sub
  127. PanFilter.Children.Add(NewButton)
  128. Next
  129. '自动选择
  130. Dim ToCheck As MyRadioButton = Nothing
  131. If TargetVersion <> "" Then
  132. Dim TargetFile = Results.FirstOrDefault(Function(v) v.GameVersions.Contains(TargetVersion))
  133. If TargetFile IsNot Nothing Then
  134. Dim TargetGroup = GetGroupedVersionName(TargetVersion, GroupedDrop, GroupedOld)
  135. For Each Button As MyRadioButton In PanFilter.Children
  136. If Button.Text <> TargetGroup Then Continue For
  137. ToCheck = Button
  138. Exit For
  139. Next
  140. End If
  141. End If
  142. If ToCheck Is Nothing Then ToCheck = PanFilter.Children(0)
  143. ToCheck.Checked = True
  144. End If
  145. '更新筛选结果(文件列表 UI 化)
  146. UpdateFilterResult()
  147. End Sub
  148. Private Sub UpdateFilterResult()
  149. Dim Results = GetResults()
  150. Dim TargetCardName As String = If(TargetVersion <> "" OrElse TargetLoaders <> ModLoaders.None,
  151. $"{If(TargetLoaders <> ModLoaders.None, TargetLoaders.ToString + " ", "")}{TargetVersion}(所选版本)", "")
  152. '归类到卡片下
  153. Dim Dict As New SortedDictionary(Of String, List(Of ResourceVersion))(New CardSorter(TargetCardName))
  154. Dict.Add("其他", New List(Of ResourceVersion))
  155. For Each Version As ResourceVersion In Results
  156. For Each GameVersion In Version.GameVersions
  157. '检查是否符合版本筛选器
  158. If VersionFilter IsNot Nothing AndAlso
  159. GetGroupedVersionName(GameVersion, GroupedDrop, GroupedOld) <> VersionFilter Then Continue For
  160. '决定添加到哪个卡片
  161. Dim VerName As String = GetGroupedVersionName(GameVersion, False, False)
  162. '遍历加入的加载器列表
  163. Dim Loaders As New List(Of String)
  164. If Project.ModLoaders.Flags.Count > 1 AndAlso '工程至少有两个加载器
  165. Version.ResourceType = ResourceTypes.Mod AndAlso '是 Mod
  166. McVersion.IsFormatFit(VerName) Then '不是 “快照版本” 之类的
  167. Loaders.AddRange(Version.ModLoaders.Flags.Select(Function(f) f.ToString & " "))
  168. End If
  169. If Not Loaders.Any() Then Loaders.Add("") '保底加一个空的,确保它在一张卡片里
  170. '实际添加
  171. For Each Loader In Loaders
  172. Dim TargetCard As String = Loader & VerName
  173. If Not Dict.ContainsKey(TargetCard) Then Dict.Add(TargetCard, New List(Of ResourceVersion))
  174. If Not Dict(TargetCard).Contains(Version) Then Dict(TargetCard).Add(Version)
  175. Next
  176. Next
  177. Next
  178. '添加筛选的版本的卡片
  179. If TargetCardName <> "" AndAlso (VersionFilter Is Nothing OrElse GetGroupedVersionName(TargetVersion, GroupedDrop, GroupedOld).StartsWithF(VersionFilter)) Then
  180. Dict.Add(TargetCardName, New List(Of ResourceVersion))
  181. For Each Version As ResourceVersion In Results
  182. If Version.GameVersions.Contains(TargetVersion) AndAlso
  183. (TargetLoaders = ModLoaders.None OrElse TargetLoaders.Flags.Intersect(Version.ModLoaders.Flags).Any) Then
  184. '检查是否符合版本筛选器
  185. If VersionFilter IsNot Nothing AndAlso
  186. Not Version.GameVersions.Any(Function(v) GetGroupedVersionName(v, GroupedDrop, GroupedOld) = VersionFilter) Then Continue For
  187. If Not Dict(TargetCardName).Contains(Version) Then Dict(TargetCardName).Add(Version)
  188. End If
  189. Next
  190. End If
  191. '转化为 UI
  192. Try
  193. PanResults.Children.Clear()
  194. For Each Pair As KeyValuePair(Of String, List(Of ResourceVersion)) In Dict
  195. If Not Pair.Value.Any() Then Continue For
  196. If Pair.Key = TargetCardName.Replace("(所选版本)", "") Then Continue For
  197. '增加卡片
  198. Dim NewCard As New MyCard With {.Title = Pair.Key, .Margin = New Thickness(0, 0, 0, 15), .SwapType = If(TargetResourceType = ResourceTypes.ModPack, 9, 8)} '9 是安装,8 是另存为
  199. Dim NewStack As New StackPanel With {.Margin = New Thickness(20, MyCard.SwapedHeight, 18, 0), .VerticalAlignment = VerticalAlignment.Top, .RenderTransform = New TranslateTransform(0, 0), .Tag = Pair.Value}
  200. NewCard.Children.Add(NewStack)
  201. NewCard.SwapControl = NewStack
  202. PanResults.Children.Add(NewCard)
  203. '确定卡片是否默认展开
  204. If Pair.Key = TargetCardName OrElse
  205. (FrmMain.PageCurrent.Additional IsNot Nothing AndAlso '#2761
  206. CType(FrmMain.PageCurrent.Additional(1), List(Of String)).Contains(NewCard.Title)) Then
  207. MyCard.StackInstall(NewStack, If(TargetResourceType = ResourceTypes.ModPack, 9, 8), Pair.Key) '9 是安装,8 是另存为
  208. Else
  209. NewCard.IsSwapped = True
  210. End If
  211. '增加提示
  212. If Pair.Key = "其他" Then
  213. NewStack.Children.Add(New MyHint With {.Text = "由于版本信息更新缓慢,可能无法识别刚更新的 MC 版本。几天后即可正常识别。", .Theme = MyHint.Themes.Yellow, .Margin = New Thickness(5, 0, 0, 8)})
  214. End If
  215. Next
  216. '如果只有一张卡片,展开第一张卡片
  217. If PanResults.Children.Count = 1 Then
  218. CType(PanResults.Children(0), MyCard).IsSwapped = False
  219. End If
  220. '替代提示
  221. If Project.Types = ResourceTypes.ModOrDataPack AndAlso (TargetResourceType = ResourceTypes.Mod OrElse TargetResourceType = ResourceTypes.DataPack) Then
  222. HintAlternative.Visibility = Visibility.Visible
  223. HintAlternative.Text = If(TargetResourceType = ResourceTypes.Mod,
  224. "以下是该项目的 Mod 版本。点击这里查看其数据包版本。", "以下是该项目的数据包版本。点击这里查看其 Mod 版本。")
  225. Else
  226. HintAlternative.Visibility = Visibility.Collapsed
  227. End If
  228. Catch ex As Exception
  229. Logger.Error(ex, "可视化工程下载列表出错")
  230. End Try
  231. End Sub
  232. Private Function GetGroupedVersionName(Name As String, GroupedByDrop As Boolean, FoldOld As Boolean) As String
  233. Dim Drop = McVersion.VersionToDrop(Name)
  234. If Name Is Nothing Then
  235. Return "其他"
  236. ElseIf Name.Contains("w") OrElse Drop = 209 Then
  237. Return "快照版"
  238. ElseIf Not McVersion.IsFormatFit(Name) OrElse (FoldOld AndAlso Drop < 120) Then
  239. Return "远古版"
  240. ElseIf GroupedByDrop Then
  241. Return McVersion.DropToVersion(Drop)
  242. Else
  243. Return Name
  244. End If
  245. End Function
  246. #End Region
  247. Private IsFirstInit As Boolean = True
  248. Public Sub Init() Handles Me.PageEnter
  249. AniControlEnabled += 1
  250. Project = FrmMain.PageCurrent.Additional(0)
  251. PanBack.ScrollToHome()
  252. '重启加载器
  253. If IsFirstInit Then
  254. '在 Me.Initialized 已经初始化了加载器,不再重复初始化
  255. IsFirstInit = False
  256. Else
  257. PageLoaderRestart(IsForceRestart:=True)
  258. End If
  259. '放置当前工程
  260. If ResourceItem IsNot Nothing Then PanIntro.Children.Remove(ResourceItem)
  261. ResourceItem = Project.ToResourceItem(True, True).Init()
  262. ResourceItem.CanInteraction = False
  263. ResourceItem.Margin = New Thickness(-7, -7, 0, 8)
  264. PanIntro.Children.Insert(0, ResourceItem)
  265. '决定按钮显示
  266. BtnIntroWeb.Text = $"转到 {Project.Platform}"
  267. BtnIntroWiki.Visibility = If(Project.WikiId = 0, Visibility.Collapsed, Visibility.Visible)
  268. AniControlEnabled -= 1
  269. End Sub
  270. '整合包安装
  271. Public Sub Install_Click(sender As MyListItem, e As EventArgs)
  272. Try
  273. '获取基本信息
  274. Dim File As ResourceVersion = sender.Tag
  275. Dim LoaderName As String = $"{Project.Platform} 整合包下载:{Project.TranslatedName} "
  276. '获取版本名
  277. Dim PackName As String = Project.TranslatedName.Replace(".zip", "").Replace(".rar", "").Replace(".mrpack", "").Replace("\", "\").Replace("/", "/").Replace("|", "|").Replace(":", ":").Replace("<", "<").Replace(">", ">").Replace("*", "*").Replace("?", "?").Replace("""", "").Replace(": ", ":")
  278. Dim Validate As New ValidateFolderName(McFolderSelected & "versions")
  279. If Validate.Validate(PackName) <> "" Then PackName = ""
  280. Dim InstanceName As String = MyMsgBoxInput("输入版本名称", "", PackName, New Collection(Of Validate) From {Validate})
  281. If String.IsNullOrEmpty(InstanceName) Then Return
  282. '构造步骤加载器
  283. Dim Loaders As New List(Of LoaderBase)
  284. Dim Target As String = $"{McFolderSelected}versions\{InstanceName}\原始整合包.{If(Project.Platform = ResourcePlatforms.CurseForge, "zip", "mrpack")}"
  285. Dim LogoFileAddress As String = ResourceItem.PathLogo.ActualSource
  286. Loaders.Add(New LoaderDownload("下载整合包文件", {
  287. File.ToNetFile(Target, ResourceVersion.DownloadReason.Standalone, File.GameVersions.FirstOrDefault, File.ModLoaders)
  288. }) With {.ProgressWeight = 10, .Block = True})
  289. Loaders.Add(New LoaderTask(Of Integer, Integer)("准备安装整合包",
  290. Sub() ModpackInstall(Target, InstanceName, If(FileUtils.Exists(LogoFileAddress), LogoFileAddress, Nothing))) With {.ProgressWeight = 0.1})
  291. '启动
  292. Dim Loader As New LoaderCombo(Of String)(LoaderName, Loaders) With {.OnStateChanged =
  293. Sub(MyLoader)
  294. Select Case MyLoader.State
  295. Case LoadState.Failed
  296. Hint(MyLoader.Name & "失败:" & MyLoader.Error.GetDisplay(False), HintType.Red)
  297. Case LoadState.Canceled
  298. Hint(MyLoader.Name & "已取消!", HintType.Blue)
  299. Case LoadState.Loading
  300. Return '不重新加载版本列表
  301. End Select
  302. McInstallFailedClearFolder(MyLoader)
  303. End Sub}
  304. Loader.Start(McFolderSelected & "versions\" & InstanceName & "\")
  305. LoaderTaskbarAdd(Loader)
  306. FrmMain.BtnExtraDownload.ShowRefresh()
  307. FrmMain.PageChange(FormMain.PageType.TaskManager)
  308. Catch ex As Exception
  309. Logger.Error(ex, "下载资源整合包失败")
  310. End Try
  311. End Sub
  312. '资源下载;整合包另存为
  313. Public Shared CachedFolder As New Dictionary(Of ResourceTypes, String) '仅在本次缓存的下载文件夹
  314. Public Sub Save_Click(sender As Object, e As EventArgs)
  315. Dim File As ResourceVersion = If(TypeOf sender Is MyListItem, sender, sender.Parent).Tag
  316. RunInNewThread(
  317. Sub()
  318. Try
  319. Dim Desc As String = Nothing
  320. Select Case File.ResourceType
  321. Case ResourceTypes.ModPack : Desc = "整合包"
  322. Case ResourceTypes.Mod : Desc = "Mod "
  323. Case ResourceTypes.ResourcePack : Desc = "资源包"
  324. Case ResourceTypes.Shader : Desc = "光影包"
  325. Case ResourceTypes.DataPack : Desc = "数据包"
  326. End Select
  327. '确认默认保存位置
  328. Dim DefaultFolder As String = Nothing
  329. If File.ResourceType <> ResourceTypes.ModPack Then
  330. Dim SubFolder As String = Nothing
  331. Select Case File.ResourceType
  332. Case ResourceTypes.Mod : SubFolder = "mods\"
  333. Case ResourceTypes.ResourcePack : SubFolder = "resourcepacks\"
  334. Case ResourceTypes.Shader : SubFolder = "shaderpacks\"
  335. Case ResourceTypes.DataPack : SubFolder = "" '导航到版本根目录
  336. End Select
  337. Dim IsInstanceSuitable As Func(Of McInstance, Boolean) = Nothing
  338. '获取资源所需的加载器
  339. Dim AllowedLoaders As ModLoaders = ModLoaders.None
  340. If File.ModLoaders <> ModLoaders.None Then
  341. AllowedLoaders = File.ModLoaders
  342. ElseIf Project.ModLoaders <> ModLoaders.None Then
  343. AllowedLoaders = Project.ModLoaders
  344. End If
  345. Logger.Info($"{Desc}要求的加载器种类:{AllowedLoaders}")
  346. '判断某个版本是否符合资源要求
  347. IsInstanceSuitable =
  348. Function(Instance As McInstance)
  349. If Not Instance.IsLoaded Then Instance.Load()
  350. '只对 Mod 和数据包进行版本检测
  351. If File.ResourceType = ResourceTypes.Mod OrElse File.ResourceType = ResourceTypes.DataPack Then
  352. If Not File.GameVersions.Any( '判断是否有任何一个版本匹配
  353. Function(FileVersion)
  354. Dim FileIsSnapshot As Boolean = FileVersion.Contains("预览版") OrElse Not FileVersion.Contains(".")
  355. Dim InstanceIsSnapshot As Boolean = Instance.Version.VanillaName.Contains("snapshot") OrElse Not Instance.Version.VanillaName.Contains(".")
  356. If FileIsSnapshot <> InstanceIsSnapshot Then
  357. '只有一种是预览版
  358. Return False
  359. ElseIf Not FileIsSnapshot AndAlso Not InstanceIsSnapshot Then
  360. '都不是预览版
  361. Return FileVersion = Instance.Version.VanillaName
  362. Else
  363. '都是预览版
  364. Dim FileIsNewFormat As Boolean = FileVersion.Contains(".") AndAlso Val(FileVersion.BeforeFirst(".")) > 1
  365. Dim InstanceIsNewFormat As Boolean = Instance.Version.Drop > 250
  366. If FileIsNewFormat <> InstanceIsNewFormat Then
  367. '只有一种是新格式快照
  368. Return False
  369. ElseIf FileIsNewFormat AndAlso InstanceIsNewFormat Then
  370. '都是新格式快照
  371. Return FileVersion.BeforeFirst(" ") = Instance.Version.VanillaName.BeforeFirst("-")
  372. Else
  373. '都是旧格式快照
  374. If FileVersion.Contains("w") AndAlso Instance.Version.VanillaName.Contains("w") Then
  375. '都是 w 快照
  376. Return FileVersion = Instance.Version.VanillaName
  377. Else
  378. '不全是 w 快照
  379. Return True '只能假定相同,无法判断
  380. End If
  381. End If
  382. End If
  383. End Function) Then Return False
  384. End If
  385. '加载器
  386. Return AllowedLoaders = ModLoaders.None OrElse AllowedLoaders.Flags.Intersect(Instance.Version.ModLoaders.Flags).Any
  387. End Function
  388. '获取常规资源默认下载位置
  389. If CachedFolder.ContainsKey(File.ResourceType) Then
  390. DefaultFolder = CachedFolder(File.ResourceType)
  391. Logger.Info($"使用上次下载时的文件夹作为默认下载位置:{DefaultFolder}")
  392. ElseIf McInstanceSelected IsNot Nothing AndAlso IsInstanceSuitable(McInstanceSelected) Then
  393. DefaultFolder = $"{McInstanceSelected.PathIndie}{SubFolder}"
  394. DirectoryUtils.Create(DefaultFolder)
  395. Logger.Info($"使用当前版本作为默认下载位置:{DefaultFolder}")
  396. Else
  397. '查找所有可能的版本
  398. Dim NeedLoad As Boolean = McInstanceListLoader.State <> LoadState.Finished
  399. If NeedLoad Then
  400. Hint("正在查找适合的游戏版本……")
  401. LoaderFolderRun(McInstanceListLoader, McFolderSelected, LoaderFolderRunType.ForceRun, MaxDepth:=1, ExtraPath:="versions\", WaitForExit:=True)
  402. End If
  403. Dim SuitableInstanceList = McInstanceList.Values.SelectMany(Function(l) l).Where(Function(v) IsInstanceSuitable(v)).
  404. Select(Function(v) DirectoryUtils.GetInfo($"{v.PathIndie}{SubFolder}"))
  405. If SuitableInstanceList.Any Then
  406. Dim SelectedInstance = SuitableInstanceList.
  407. OrderByDescending(Function(Dir) If(Dir.Exists, Dir.LastWriteTimeUtc, Date.MinValue)). '先按文件夹更改时间降序
  408. ThenByDescending(Function(Dir) If(Dir.Exists, Dir.GetFiles().Length, -1)). '再按文件夹中的文件数量降序
  409. First()
  410. DefaultFolder = PathUtils.RemoveExtendedPrefix(SelectedInstance.FullName)
  411. DirectoryUtils.Create(DefaultFolder)
  412. Logger.Info($"使用适合的游戏版本作为默认下载位置:{DefaultFolder}")
  413. Else
  414. DefaultFolder = McFolderSelected
  415. If NeedLoad Then
  416. Hint($"当前 MC 文件夹中没有找到合适的版本!")
  417. Else
  418. Logger.Info("由于当前版本不兼容,使用当前的 MC 文件夹作为默认下载位置")
  419. End If
  420. End If
  421. End If
  422. End If
  423. '获取基本信息
  424. Dim FileName As String
  425. If Project.TranslatedName = Project.RawName Then
  426. FileName = File.FileName
  427. Else
  428. Dim ChineseName As String = Project.TranslatedName.BeforeFirst(" (").BeforeFirst(" - ").
  429. Replace("\", "\").Replace("/", "/").Replace("|", "|").Replace(":", ":").Replace("<", "<").Replace(">", ">").Replace("*", "*").Replace("?", "?").Replace("""", "").Replace(": ", ":")
  430. Select Case Settings.Get(Of Integer)("ToolDownloadTranslateV2")
  431. Case 0
  432. FileName = $"【{ChineseName}】{File.FileName}"
  433. Case 1
  434. FileName = $"[{ChineseName}] {File.FileName}"
  435. Case 2
  436. FileName = $"{ChineseName}-{File.FileName}"
  437. Case 3
  438. FileName = $"{File.FileName}-{ChineseName}"
  439. Case Else
  440. FileName = File.FileName
  441. End Select
  442. End If
  443. If File.ResourceType = ResourceTypes.Mod Then FileName = FileName.Replace("~", "-") '~ 会导致 Mixin 加载失败
  444. RunInUi(
  445. Sub()
  446. '弹窗要求选择保存位置
  447. Dim Target As String
  448. Dim Ext As String = If(File.ResourceType = ResourceTypes.Mod,
  449. If(File.FileName.EndsWithF(".litemod"), "litemod", "jar"),
  450. If(File.FileName.EndsWithF(".mrpack"), "mrpack", "zip"))
  451. Target = Dialogs.SaveFile("选择保存位置", FileName, DefaultFolder, {(Ext, Desc & "文件")})
  452. If Target Is Nothing Then Return
  453. '构造步骤加载器
  454. Dim LoaderName As String = Desc & "下载:" & PathUtils.GetFileNameWithoutExtension(Target) & " "
  455. If Target <> DefaultFolder Then CachedFolder(File.ResourceType) = PathUtils.RemoveLastPart(Target)
  456. Dim Loaders As New List(Of LoaderBase)
  457. Loaders.Add(New LoaderDownload("下载文件", {
  458. File.ToNetFile(Target, If(TargetResourceType = ResourceTypes.Any, ResourceVersion.DownloadReason.Dependency, ResourceVersion.DownloadReason.Standalone))
  459. }) With {.ProgressWeight = 6, .Block = True})
  460. '启动
  461. Dim Loader As New LoaderCombo(Of Integer)(LoaderName, Loaders) With {.OnStateChanged = AddressOf LoaderStateChangedHintOnly}
  462. Loader.Start(1)
  463. LoaderTaskbarAdd(Loader)
  464. FrmMain.BtnExtraDownload.ShowRefresh()
  465. FrmMain.BtnExtraDownload.Ribble()
  466. End Sub)
  467. Catch ex As Exception
  468. Logger.Error(ex, "保存资源文件失败")
  469. End Try
  470. End Sub, "Download Resource Detail Save")
  471. End Sub
  472. Private Sub BtnIntroWeb_Click(sender As Object, e As EventArgs) Handles BtnIntroWeb.Click
  473. OpenWebsite(Project.Website)
  474. End Sub
  475. Private Sub BtnIntroWiki_Click(sender As Object, e As EventArgs) Handles BtnIntroWiki.Click
  476. OpenWebsite("https://www.mcmod.cn/class/" & Project.WikiId & ".html")
  477. End Sub
  478. Private Sub BtnIntroCopy_Click(sender As Object, e As EventArgs) Handles BtnIntroCopy.Click
  479. ClipboardSet(ResourceItem.LabTitle.Text & ResourceItem.LabTitleRaw.Text)
  480. End Sub
  481. 'Mod / 数据包 互相跳转
  482. Private Sub HintAlternative_Click() Handles HintAlternative.Click
  483. TargetResourceType = If(TargetResourceType = ResourceTypes.Mod, ResourceTypes.DataPack, ResourceTypes.Mod)
  484. FrmMain.PageCurrent.Additional(4) = TargetResourceType '加载器会从这里重新拿数据
  485. PageLoaderRestart(IsForceRestart:=True)
  486. End Sub
  487. ''' <summary>
  488. ''' 预载包含大量 ProjectVersion 的卡片,添加必要的元素和前置列表。
  489. ''' </summary>
  490. Public Shared Sub ResourceFilesCardPreload(Stack As StackPanel, Files As List(Of ResourceVersion))
  491. '获取卡片对应的前置 ID
  492. '如果为整合包就不会有 Dependencies 信息,所以不用管
  493. Dim Deps As List(Of String) = Files.SelectMany(Function(f) f.Dependencies).Distinct.ToList()
  494. Deps.Sort()
  495. If Not Deps.Any() Then Return
  496. Deps = Deps.Where(
  497. Function(dep)
  498. If Not ResourceProject.Cache.ContainsKey(dep) Then Logger.Warn($"未找到 ID {dep} 的前置信息")
  499. Return ResourceProject.Cache.ContainsKey(dep)
  500. End Function).ToList
  501. '添加开头间隔
  502. Stack.Children.Add(New TextBlock With {.Text = "前置资源", .FontSize = 14, .HorizontalAlignment = HorizontalAlignment.Left, .Margin = New Thickness(6, 2, 0, 5)})
  503. '添加前置列表
  504. For Each Dep In Deps
  505. Dim Item = ResourceProject.Cache(Dep).ToResourceItem(False, False)
  506. Stack.Children.Add(Item)
  507. Next
  508. '添加结尾间隔
  509. Stack.Children.Add(New TextBlock With {.Text = "版本列表", .FontSize = 14, .HorizontalAlignment = HorizontalAlignment.Left, .Margin = New Thickness(6, 12, 0, 5)})
  510. End Sub
  511. End Class