MySkin.xaml.vb 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  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 EqualableList(Of 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. Private Sub BtnSkinSave_Click(sender As Object, e As RoutedEventArgs) Handles BtnSkinSave.Click
  40. Save(Loader)
  41. End Sub
  42. Public Shared Sub Save(Loader As LoaderTask(Of EqualableList(Of String), String))
  43. Dim Address = Loader.Output
  44. If Not Loader.State = LoadState.Finished Then
  45. Hint("皮肤正在获取中,请稍候!", HintType.Critical)
  46. If Not Loader.State = LoadState.Loading Then Loader.Start()
  47. Exit Sub
  48. End If
  49. Try
  50. Dim FileAddress As String = SelectAs("选取保存皮肤的位置", GetFileNameFromPath(Address), "皮肤图片文件(*.png)|*.png")
  51. If FileAddress.Contains("\") Then
  52. File.Delete(FileAddress)
  53. If Address.StartsWith(PathImage) Then
  54. Dim Image As New MyBitmap(Address)
  55. Image.Save(FileAddress)
  56. Else
  57. CopyFile(Address, FileAddress)
  58. End If
  59. Hint("皮肤保存成功!", HintType.Finish)
  60. End If
  61. Catch ex As Exception
  62. Log(ex, "保存皮肤失败", LogLevel.Hint)
  63. End Try
  64. End Sub
  65. Private Sub BtnSkinSave_Checked(sender As MyMenuItem, e As RoutedEventArgs) Handles BtnSkinSave.Checked
  66. sender.IsEnabled = Address = ""
  67. End Sub
  68. ''' <summary>
  69. ''' 载入皮肤。
  70. ''' </summary>
  71. Public Sub Load()
  72. Try
  73. Address = Loader.Output
  74. '检查文件存在
  75. If Address = "" Then Throw New Exception("皮肤加载器 " & Loader.Name & " 没有输出")
  76. If Not Address.StartsWith(PathImage) AndAlso Not File.Exists(Address) Then Throw New FileNotFoundException("皮肤文件未找到", Address)
  77. '加载
  78. Dim Image As New MyBitmap(Address)
  79. ImgBack.Tag = Address
  80. '大小检查
  81. Dim Scale As Integer = Image.Pic.Width / 64
  82. If Image.Pic.Width < 32 OrElse Image.Pic.Height < 32 Then
  83. ImgFore.Source = Nothing : ImgBack.Source = Nothing
  84. Throw New Exception("图片大小不足,长为 " & Image.Pic.Height & ",宽为 " & Image.Pic.Width)
  85. End If
  86. '头发层(附加层)
  87. If Image.Pic.Width >= 64 AndAlso Image.Pic.Height >= 32 Then
  88. If (Image.Pic.GetPixel(1, 1).A = 0 OrElse '如果图片中有任何透明像素(避免纯色白底)
  89. Image.Pic.GetPixel(Image.Pic.Width - 1, Image.Pic.Height - 1).A = 0 OrElse
  90. Image.Pic.GetPixel(Image.Pic.Width - 2, Image.Pic.Height / 2 - 2).A = 0) OrElse
  91. (Image.Pic.GetPixel(1, 1) <> Image.Pic.GetPixel(Scale * 41, Scale * 9) AndAlso '或是头部颜色和透明区均不一样
  92. Image.Pic.GetPixel(Image.Pic.Width - 1, Image.Pic.Height - 1) <> Image.Pic.GetPixel(Scale * 41, Scale * 9) AndAlso
  93. Image.Pic.GetPixel(Image.Pic.Width - 2, Image.Pic.Height / 2 - 2) <> Image.Pic.GetPixel(Scale * 41, Scale * 9)) Then
  94. ImgFore.Source = New CroppedBitmap(Image, New Int32Rect(Scale * 40, Scale * 8, Scale * 8, Scale * 8))
  95. Else
  96. ImgFore.Source = Nothing
  97. End If
  98. Else
  99. ImgFore.Source = Nothing
  100. End If
  101. '脸层
  102. ImgBack.Source = New CroppedBitmap(Image, New Int32Rect(Scale * 8, Scale * 8, Scale * 8, Scale * 8))
  103. Log("[Skin] 载入头像成功:" & Loader.Name)
  104. Catch ex As Exception
  105. Log(ex, "载入头像失败(" & If(Address, "null") & "," & Loader.Name & ")", LogLevel.Hint)
  106. End Try
  107. End Sub
  108. ''' <summary>
  109. ''' 清空皮肤。
  110. ''' </summary>
  111. Public Sub Clear()
  112. Address = ""
  113. ImgFore.Source = Nothing
  114. ImgBack.Source = Nothing
  115. End Sub
  116. '刷新缓存
  117. Private Sub RefreshClick(sender As MyMenuItem, e As EventArgs) Handles BtnSkinRefresh.Click
  118. RefreshCache(Loader)
  119. End Sub
  120. ''' <summary>
  121. ''' 刷新皮肤缓存。
  122. ''' </summary>
  123. Public Shared Sub RefreshCache(Optional sender As LoaderTask(Of EqualableList(Of String), String) = Nothing)
  124. Dim HasLoaderRunning As Boolean = False
  125. For Each SkinLoader In PageLaunchLeft.SkinLoaders
  126. If SkinLoader.State = LoadState.Loading Then
  127. HasLoaderRunning = True : Exit For
  128. End If
  129. Next
  130. If FrmLaunchLeft IsNot Nothing AndAlso HasLoaderRunning Then
  131. '由于 Abort 不是实时的,暂时不会释放文件,会导致删除报错,故只能取消执行
  132. Hint("有正在获取中的皮肤,请稍后再试!", HintType.Info)
  133. Else
  134. RunInThread(Sub()
  135. Try
  136. Hint("正在刷新皮肤……")
  137. '清空缓存
  138. Log("[Skin] 正在清空皮肤缓存")
  139. If Directory.Exists(PathTemp & "Cache\Skin") Then DeleteDirectory(PathTemp & "Cache\Skin")
  140. If Directory.Exists(PathTemp & "Cache\Uuid") Then DeleteDirectory(PathTemp & "Cache\Uuid")
  141. IniClearCache(PathTemp & "Cache\Skin\IndexMojang.ini")
  142. IniClearCache(PathTemp & "Cache\Skin\IndexMs.ini")
  143. IniClearCache(PathTemp & "Cache\Skin\IndexNide.ini")
  144. IniClearCache(PathTemp & "Cache\Skin\IndexAuth.ini")
  145. IniClearCache(PathTemp & "Cache\Uuid\Mojang.ini")
  146. '刷新控件
  147. For Each SkinLoader In If(sender IsNot Nothing, {sender}, {PageLaunchLeft.SkinMojang, PageLaunchLeft.SkinLegacy, PageLaunchLeft.SkinMs})
  148. SkinLoader.WaitForExit(IsForceRestart:=True)
  149. Next
  150. Hint("已刷新皮肤!", HintType.Finish)
  151. Catch ex As Exception
  152. Log(ex, "刷新皮肤缓存失败", LogLevel.Msgbox)
  153. End Try
  154. End Sub)
  155. End If
  156. End Sub
  157. ''' <summary>
  158. ''' 在更换正版皮肤后,刷新正版皮肤。
  159. ''' </summary>
  160. ''' <param name="SkinAddress">新的正版皮肤完整地址。</param>
  161. Public Shared Sub ReloadCache(SkinAddress As String, IsMsLogin As Boolean)
  162. RunInThread(Sub()
  163. Try
  164. '更新缓存
  165. Dim LoginType As String = If(IsMsLogin, "Ms", "Mojang")
  166. WriteIni(PathTemp & "Cache\Skin\Index" & LoginType & ".ini", Setup.Get("Cache" & LoginType & "Uuid"), SkinAddress)
  167. Log(String.Format("[Skin] 已写入皮肤地址缓存 {0} -> {1}", Setup.Get("Cache" & LoginType & "Uuid"), SkinAddress))
  168. '刷新控件
  169. For Each SkinLoader In If(IsMsLogin, {PageLaunchLeft.SkinMs, PageLaunchLeft.SkinLegacy}, {PageLaunchLeft.SkinMojang, PageLaunchLeft.SkinLegacy})
  170. SkinLoader.WaitForExit(IsForceRestart:=True)
  171. Next
  172. '完成提示
  173. Hint("更改皮肤成功!", HintType.Finish)
  174. Catch ex As Exception
  175. Log(ex, "更改正版皮肤后刷新皮肤失败", LogLevel.Feedback)
  176. End Try
  177. End Sub)
  178. End Sub
  179. '披风
  180. Public Property HasCape As Boolean
  181. Get
  182. Return BtnSkinCape.Visibility = Visibility.Collapsed
  183. End Get
  184. Set(value As Boolean)
  185. If value Then
  186. BtnSkinCape.Visibility = Visibility.Visible
  187. Else
  188. BtnSkinCape.Visibility = Visibility.Collapsed
  189. End If
  190. End Set
  191. End Property
  192. Private IsChanging As Boolean = False
  193. Private Sub BtnSkinCape_Click(sender As Object, e As RoutedEventArgs) Handles BtnSkinCape.Click
  194. '检查条件,获取新披风
  195. If IsChanging Then
  196. Hint("正在更改披风中,请稍候!")
  197. Exit Sub
  198. End If
  199. If McLoginMsLoader.State = LoadState.Failed Then
  200. Hint("登录失败,无法更改披风!", HintType.Critical)
  201. Exit Sub
  202. End If
  203. Hint("正在获取披风列表,请稍候……")
  204. IsChanging = True
  205. '开始实际获取
  206. RunInNewThread(Sub()
  207. Try
  208. Retry:
  209. '获取登录信息
  210. If McLoginMsLoader.State <> LoadState.Finished Then McLoginMsLoader.WaitForExit(PageLoginMsSkin.GetLoginData())
  211. If McLoginMsLoader.State <> LoadState.Finished Then
  212. Hint("登录失败,无法更改披风!", HintType.Critical)
  213. Exit Sub
  214. End If
  215. Dim AccessToken As String = McLoginMsLoader.Output.AccessToken
  216. Dim Uuid As String = McLoginMsLoader.Output.Uuid
  217. Dim SkinData As JObject = GetJson(McLoginMsLoader.Output.ProfileJson)
  218. '获取玩家的所有披风
  219. Dim SelId As Integer? = Nothing
  220. RunInUiWait(Sub()
  221. Try
  222. Dim CapeNames As New Dictionary(Of String, String) From {
  223. {"Migrator", "迁移者披风"}, {"MapMaker", "Realms 地图制作者披风"}, {"Moderator", "Mojira 管理员披风"},
  224. {"Translator-Chinese", "Crowdin 中文翻译者披风"}, {"Translator", "Crowdin 翻译者披风"}, {"Cobalt", "Cobalt 披风"},
  225. {"Vanilla", "原版披风"}, {"Minecon2011", "Minecon 2011 参与者披风"}, {"Minecon2012", "Minecon 2012 参与者披风"},
  226. {"Minecon2013", "Minecon 2013 参与者披风"}, {"Minecon2015", "Minecon 2015 参与者披风"}, {"Minecon2016", "Minecon 2016 参与者披风"}
  227. }
  228. Dim SelectionControl As New List(Of IMyRadio) From {New MyRadioBox With {.Text = "无披风"}}
  229. For Each Cape In SkinData("capes")
  230. Dim CapeName As String = Cape("alias").ToString
  231. If CapeNames.ContainsKey(CapeName) Then CapeName = CapeNames(CapeName)
  232. SelectionControl.Add(New MyRadioBox With {.Text = CapeName})
  233. Next
  234. SelId = MyMsgBoxSelect(SelectionControl, "选择披风", "确定", "取消")
  235. Catch ex As Exception
  236. Log(ex, "获取玩家皮肤列表失败", LogLevel.Feedback)
  237. End Try
  238. End Sub)
  239. If SelId Is Nothing Then Exit Sub
  240. '发送请求
  241. Dim Result As String = NetRequestRetry("https://api.minecraftservices.com/minecraft/profile/capes/active",
  242. If(SelId = 0, "DELETE", "PUT"),
  243. If(SelId = 0, "", "{""capeId"": """ & SkinData("capes")(SelId - 1)("id").ToString & """}"),
  244. "application/json", Headers:=New Dictionary(Of String, String) From {{"Authorization", "Bearer " & AccessToken}})
  245. If Result.Contains("""errorMessage""") Then
  246. Hint("更改披风失败:" & GetJson(Result)("errorMessage"), HintType.Critical)
  247. Exit Sub
  248. Else
  249. Hint("更改披风成功!", HintType.Finish)
  250. End If
  251. Catch ex As Exception
  252. Log(ex, "更改披风失败", LogLevel.Hint)
  253. Finally
  254. IsChanging = False
  255. End Try
  256. End Sub, "Cape Change")
  257. End Sub
  258. End Class