MySkin.xaml.vb 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289
  1. Public Class MySkin
  2. '事件
  3. Public Event Click(sender As Object, e As MouseButtonEventArgs)
  4. '皮肤储存
  5. Private _Address As String
  6. Public Property Address As String
  7. Get
  8. Return _Address
  9. End Get
  10. Set(value As String)
  11. _Address = value
  12. ToolTip = If(_Address = "", "加载中", "点击更换皮肤(右键查看更多选项)")
  13. End Set
  14. End Property
  15. Public Loader As LoaderTask(Of (String, String), String)
  16. '控件动画
  17. Private Sub PanSkin_MouseEnter(sender As Object, e As MouseEventArgs) Handles Me.MouseEnter
  18. AniStart(AaOpacity(ShadowSkin, 0.8 - ShadowSkin.Opacity, 200, 100), "Skin Shadow")
  19. End Sub
  20. Private Sub PanSkin_MouseLeave(sender As Object, e As MouseEventArgs) Handles Me.MouseLeave
  21. AniStart(AaOpacity(ShadowSkin, 0.2 - ShadowSkin.Opacity, 200), "Skin Shadow")
  22. IsSkinMouseDown = False
  23. AniStart(AaScaleTransform(Me, 1 - CType(Me.RenderTransform, ScaleTransform).ScaleX, 60,, New AniEaseOutFluent), "Skin Scale")
  24. End Sub
  25. '点击
  26. Private IsSkinMouseDown As Boolean = False
  27. Private Sub PanSkin_MouseLeftButtonDown(sender As Object, e As MouseButtonEventArgs) Handles Me.MouseLeftButtonDown
  28. IsSkinMouseDown = True
  29. AniStart(AaScaleTransform(Me, 0.9 - CType(Me.RenderTransform, ScaleTransform).ScaleX, 60,, New AniEaseOutFluent), "Skin Scale")
  30. End Sub
  31. Private Sub PanSkin_MouseLeftButtonUp(sender As Object, e As MouseButtonEventArgs) Handles Me.MouseLeftButtonUp
  32. AniStart(AaScaleTransform(Me, 1 - CType(Me.RenderTransform, ScaleTransform).ScaleX, 60,, New AniEaseOutFluent), "Skin Scale")
  33. If IsSkinMouseDown Then
  34. IsSkinMouseDown = False
  35. RaiseEvent Click(sender, e)
  36. End If
  37. End Sub
  38. '保存皮肤
  39. Public Sub BtnSkinSave_Click() Handles BtnSkinSave.Click
  40. Save(Loader)
  41. End Sub
  42. Public Shared Sub Save(Loader As LoaderTask(Of (String, String), String))
  43. Dim Address = Loader.Output
  44. If Not Loader.State = LoadState.Finished Then
  45. Hint("皮肤正在获取中,请稍候!", HintType.Red)
  46. If Not Loader.State = LoadState.Loading Then Loader.Start()
  47. Return
  48. End If
  49. Try
  50. Dim FileAddress As String = Dialogs.SaveFile("选取保存皮肤的位置", PathUtils.GetLastPart(Address), filter:={("png", "皮肤图片文件")})
  51. If FileAddress Is Nothing Then Return
  52. If FileAddress.Contains("\") Then
  53. FileUtils.Delete(FileAddress)
  54. If Address.StartsWithF(PathImage) Then
  55. Dim Image As New MyBitmap(Address)
  56. Image.Save(FileAddress)
  57. Else
  58. FileUtils.Copy(Address, FileAddress)
  59. End If
  60. Hint("皮肤保存成功!", HintType.Green)
  61. End If
  62. Catch ex As Exception
  63. Logger.Error(ex, "保存皮肤失败", LogBehavior.Toast)
  64. End Try
  65. End Sub
  66. Private Sub BtnSkinSave_Checked(sender As MyMenuItem, e As RoutedEventArgs) Handles BtnSkinSave.Checked
  67. sender.IsEnabled = Address = ""
  68. End Sub
  69. ''' <summary>
  70. ''' 载入皮肤。
  71. ''' </summary>
  72. Public Sub Load()
  73. Try
  74. '检查文件存在
  75. Address = Loader.Output
  76. If String.IsNullOrEmpty(Address) Then Throw New Exception("皮肤加载器 " & Loader.Name & " 没有输出")
  77. If Not Address.StartsWithF(PathImage) AndAlso Not FileUtils.Exists(Address) Then Throw New FileNotFoundException("皮肤文件未找到", Address)
  78. '加载
  79. Dim Image As MyBitmap
  80. Try
  81. Image = New MyBitmap(Address)
  82. Catch ex As Exception '#2272
  83. Logger.Error(ex, $"皮肤文件已损坏:{Address}", LogBehavior.Toast)
  84. FileUtils.Delete(Address)
  85. Return
  86. End Try
  87. ImgBack.Tag = Address
  88. '大小检查
  89. Dim Scale As Integer = Image.Pic.Width / 64
  90. If Image.Pic.Width < 32 OrElse Image.Pic.Height < 32 Then
  91. ImgFore.Source = Nothing : ImgBack.Source = Nothing
  92. Throw New Exception("图片大小不足,长为 " & Image.Pic.Height & ",宽为 " & Image.Pic.Width)
  93. End If
  94. '头发层(附加层)
  95. If Image.Pic.Width >= 64 AndAlso Image.Pic.Height >= 32 Then
  96. If (Image.Pic.GetPixel(1, 1).A = 0 OrElse '如果图片中有任何透明像素(避免纯色白底)
  97. Image.Pic.GetPixel(Image.Pic.Width - 1, Image.Pic.Height - 1).A = 0 OrElse
  98. Image.Pic.GetPixel(Image.Pic.Width - 2, Image.Pic.Height / 2 - 2).A = 0) OrElse
  99. (Image.Pic.GetPixel(1, 1) <> Image.Pic.GetPixel(Scale * 41, Scale * 9) AndAlso '或是头部颜色和透明区均不一样
  100. Image.Pic.GetPixel(Image.Pic.Width - 1, Image.Pic.Height - 1) <> Image.Pic.GetPixel(Scale * 41, Scale * 9) AndAlso
  101. Image.Pic.GetPixel(Image.Pic.Width - 2, Image.Pic.Height / 2 - 2) <> Image.Pic.GetPixel(Scale * 41, Scale * 9)) Then
  102. ImgFore.Source = Image.Clip(Scale * 40, Scale * 8, Scale * 8, Scale * 8)
  103. Else
  104. ImgFore.Source = Nothing
  105. End If
  106. Else
  107. ImgFore.Source = Nothing
  108. End If
  109. '脸层
  110. ImgBack.Source = Image.Clip(Scale * 8, Scale * 8, Scale * 8, Scale * 8)
  111. Logger.Info($"载入头像成功:{Loader.Name}")
  112. Catch ex As Exception
  113. Logger.Error(ex, $"载入头像失败({If(Address, "null")},{Loader.Name})", LogBehavior.Toast)
  114. End Try
  115. End Sub
  116. ''' <summary>
  117. ''' 清空皮肤。
  118. ''' </summary>
  119. Public Sub Clear()
  120. Address = ""
  121. ImgFore.Source = Nothing
  122. ImgBack.Source = Nothing
  123. End Sub
  124. '刷新缓存
  125. Public Sub RefreshClick() Handles BtnSkinRefresh.Click
  126. RefreshCache(Loader)
  127. End Sub
  128. ''' <summary>
  129. ''' 刷新皮肤缓存。
  130. ''' </summary>
  131. Public Shared Sub RefreshCache(Optional sender As LoaderTask(Of (String, String), String) = Nothing)
  132. Dim HasLoaderRunning As Boolean = False
  133. For Each SkinLoader In PageLaunchLeft.SkinLoaders
  134. If SkinLoader.State = LoadState.Loading Then
  135. HasLoaderRunning = True : Exit For
  136. End If
  137. Next
  138. If FrmLaunchLeft IsNot Nothing AndAlso HasLoaderRunning Then
  139. '由于 Interrupt 不是实时的,暂时不会释放文件,会导致删除报错,故只能取消执行
  140. Hint("有正在获取中的皮肤,请稍后再试!", HintType.Blue)
  141. Else
  142. RunInThread(
  143. Sub()
  144. Try
  145. Hint("正在刷新头像……")
  146. '清空缓存
  147. Logger.Info("正在清空皮肤缓存")
  148. If DirectoryUtils.Exists(PathTemp & "Cache\Skin") Then DirectoryUtils.Delete(PathTemp & "Cache\Skin")
  149. If DirectoryUtils.Exists(PathTemp & "Cache\Uuid") Then DirectoryUtils.Delete(PathTemp & "Cache\Uuid")
  150. IniClearCache(PathTemp & "Cache\Skin\IndexMs.ini")
  151. IniClearCache(PathTemp & "Cache\Skin\IndexNide.ini")
  152. IniClearCache(PathTemp & "Cache\Skin\IndexAuth.ini")
  153. IniClearCache(PathTemp & "Cache\Uuid\Mojang.ini")
  154. '刷新控件
  155. For Each SkinLoader In If(sender IsNot Nothing, {sender}, {PageLaunchLeft.SkinLegacy, PageLaunchLeft.SkinMs})
  156. SkinLoader.WaitForExit(IsForceRestart:=True)
  157. Next
  158. Hint("已刷新头像!", HintType.Green)
  159. Catch ex As Exception
  160. Logger.Error(ex, "刷新皮肤缓存失败", LogBehavior.Alert)
  161. End Try
  162. End Sub)
  163. End If
  164. End Sub
  165. ''' <summary>
  166. ''' 在更换正版皮肤后,刷新正版皮肤。
  167. ''' </summary>
  168. ''' <param name="SkinAddress">新的正版皮肤完整地址。</param>
  169. Public Shared Sub ReloadCache(SkinAddress As String)
  170. RunInThread(
  171. Sub()
  172. Try
  173. '更新缓存
  174. WriteIni(PathTemp & "Cache\Skin\IndexMs.ini", Settings.Get(Of String)("CacheMsV2Uuid"), SkinAddress)
  175. Logger.Info($"已写入皮肤地址缓存 {Settings.Get(Of String)("CacheMsV2Uuid")} -> {SkinAddress}")
  176. '刷新控件
  177. For Each SkinLoader In {PageLaunchLeft.SkinMs, PageLaunchLeft.SkinLegacy}
  178. SkinLoader.WaitForExit(IsForceRestart:=True)
  179. Next
  180. '完成提示
  181. Hint("更改皮肤成功!", HintType.Green)
  182. Catch ex As Exception
  183. Logger.Error(ex, "更改正版皮肤后刷新皮肤失败")
  184. End Try
  185. End Sub)
  186. End Sub
  187. '披风
  188. Public Property HasCape As Boolean
  189. Get
  190. Return BtnSkinCape.Visibility = Visibility.Collapsed
  191. End Get
  192. Set(value As Boolean)
  193. If value Then
  194. BtnSkinCape.Visibility = Visibility.Visible
  195. Else
  196. BtnSkinCape.Visibility = Visibility.Collapsed
  197. End If
  198. End Set
  199. End Property
  200. Private IsChanging As Boolean = False
  201. Public Sub BtnSkinCape_Click() Handles BtnSkinCape.Click
  202. '检查条件,获取新披风
  203. If IsChanging Then
  204. Hint("正在更改披风中,请稍候!")
  205. Return
  206. End If
  207. If McLoginMsLoader.State = LoadState.Failed Then
  208. Hint("登录失败,无法更改披风!", HintType.Red)
  209. Return
  210. End If
  211. Hint("正在获取披风列表,请稍候……")
  212. IsChanging = True
  213. '开始实际获取
  214. RunInNewThread(
  215. Sub()
  216. Try
  217. Retry:
  218. '获取登录信息
  219. If McLoginMsLoader.State <> LoadState.Finished Then McLoginMsLoader.WaitForExit(PageLoginMsSkin.GetLoginData())
  220. If McLoginMsLoader.State <> LoadState.Finished Then
  221. Hint("登录失败,无法更改披风!", HintType.Red)
  222. Return
  223. End If
  224. Dim AccessToken As String = McLoginMsLoader.Output.AccessToken
  225. Dim Uuid As String = McLoginMsLoader.Output.Uuid
  226. Dim SkinData As JObject = GetJson(McLoginMsLoader.Output.ProfileJson)
  227. '获取玩家的所有披风
  228. Dim SelectedIndex As Integer? = Nothing
  229. RunInUiWait(
  230. Sub()
  231. Try
  232. Dim CapeNames As New Dictionary(Of String, String) From {
  233. {"Migrator", "迁移者披风"}, {"MapMaker", "Realms 地图制作者披风"}, {"Moderator", "Mojira 管理员披风"},
  234. {"Translator-Chinese", "Crowdin 中文翻译者披风"}, {"Translator", "Crowdin 翻译者披风"}, {"Cobalt", "Cobalt 披风"},
  235. {"Vanilla", "原版披风"}, {"Minecon2011", "Minecon 2011 参与者披风"}, {"Minecon2012", "Minecon 2012 参与者披风"},
  236. {"Minecon2013", "Minecon 2013 参与者披风"}, {"Minecon2015", "Minecon 2015 参与者披风"}, {"Minecon2016", "Minecon 2016 参与者披风"},
  237. {"Cherry Blossom", "樱花披风"}, {"15th Anniversary", "15 周年纪念披风"}, {"Purple Heart", "紫色心形披风"},
  238. {"Follower's", "追随者披风"}, {"MCC 15th Year", "MCC 15 周年披风"}, {"Minecraft Experience", "村民救援披风"},
  239. {"Mojang Office", "Mojang 办公室披风"}, {"Home", "家园披风"}, {"Menace", "入侵披风"}, {"Yearn", "渴望披风"},
  240. {"Common", "普通披风"}, {"Pan", "薄煎饼披风"}, {"Founder's", "创始人披风"}, {"Copper", "铜披风"}, {"Zombie Horse", "僵尸马披风"}
  241. }
  242. Dim SelectionControl As New List(Of IMyRadio) From {
  243. New MyRadioBox With {.Text = "无披风", .Checked = Not SkinData("capes").Any(Function(c) c("state")?.ToString = "ACTIVE")}}
  244. For Each Cape In SkinData("capes")
  245. Dim CapeName As String = Cape("alias").ToString
  246. If CapeNames.ContainsKey(CapeName) Then CapeName = CapeNames(CapeName)
  247. SelectionControl.Add(New MyRadioBox With {.Text = CapeName, .Checked = Cape("state")?.ToString = "ACTIVE"})
  248. Next
  249. SelectedIndex = MyMsgBoxSelect(SelectionControl, "选择披风", "确定", "取消")
  250. Catch ex As Exception
  251. Logger.Error(ex, "获取玩家皮肤列表失败")
  252. End Try
  253. End Sub)
  254. If SelectedIndex Is Nothing Then Return
  255. '发送请求
  256. Dim Result As String = NetRequestByClientRetry("https://api.minecraftservices.com/minecraft/profile/capes/active",
  257. If(SelectedIndex = 0, HttpMethod.Delete, HttpMethod.Put),
  258. Content:=If(SelectedIndex = 0, "", New JObject(New JProperty("capeId", SkinData("capes")(SelectedIndex - 1)("id"))).ToString(0)),
  259. ContentType:="application/json",
  260. Headers:={{"Authorization", "Bearer " & AccessToken}})
  261. If Result.Contains("""errorMessage""") Then
  262. Hint("更改披风失败:" & GetJson(Result)("errorMessage").ToString, HintType.Red)
  263. Return
  264. Else
  265. Hint("更改披风成功!", HintType.Green)
  266. '更新当前选择的披风
  267. For Each Cape In SkinData("capes")
  268. Cape("state") = "INACTIVE"
  269. Next
  270. If SelectedIndex > 0 Then SkinData("capes")(SelectedIndex - 1)("state") = "ACTIVE"
  271. McLoginMsLoader.Output.ProfileJson = SkinData.ToString()
  272. End If
  273. Catch ex As Exception
  274. Logger.Error(ex, "更改披风失败", LogBehavior.Toast)
  275. Finally
  276. IsChanging = False
  277. End Try
  278. End Sub, "Cape Change")
  279. End Sub
  280. End Class