PageDownloadCompDetail.xaml.vb 30 KB

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