diff --git a/RevokeMsgPatcher.Assistant/JsonData.cs b/RevokeMsgPatcher.Assistant/JsonData.cs index d1ec3b83..7e1c9ca7 100644 --- a/RevokeMsgPatcher.Assistant/JsonData.cs +++ b/RevokeMsgPatcher.Assistant/JsonData.cs @@ -1380,7 +1380,7 @@ public App QQNT() { Search = ByteUtil.HexStringToByteArray("48 89 CE 48 8B 11 4C 8B 41 08 49 29 D0 48 8B 49 18 E8 3F 3F 3F 3F"), Replace = ByteUtil.HexStringToByteArray("48 89 CE 48 8B 11 4C 8B 41 08 49 29 D0 48 8B 49 18 B8 01 00 00 00"), - Category = "LiteLoaderQQNT+插件列表+防撤回" + Category = "请在新窗口内安装LiteLoaderQQNT" } } } diff --git a/RevokeMsgPatcher/FormMain.cs b/RevokeMsgPatcher/FormMain.cs index bef9c022..2cee3035 100644 --- a/RevokeMsgPatcher/FormMain.cs +++ b/RevokeMsgPatcher/FormMain.cs @@ -32,6 +32,8 @@ public partial class FormMain : Form Bag bag = null; + FormLiteLoaderQQNT formLiteLoader = null; + public void InitModifier() { // 从配置文件中读取配置 @@ -422,6 +424,7 @@ private void radioButtons_CheckedChanged(object sender, EventArgs e) else if (rbtQQNT.Checked) { modifier = (QQNTModifier)rbtQQNT.Tag; + ShowOrFocusFormLiteLoaderQQNT(); } EnableAllButton(true); @@ -432,6 +435,24 @@ private void radioButtons_CheckedChanged(object sender, EventArgs e) ga.RequestPageView($"{GetCheckedRadioButtonNameEn()}/{lblVersion.Text}/switch", "切换标签页"); } + private void ShowOrFocusFormLiteLoaderQQNT() + { + if (formLiteLoader == null || formLiteLoader.IsDisposed) + { + formLiteLoader = new FormLiteLoaderQQNT(); + formLiteLoader.Show(); + } + else + { + if (formLiteLoader.WindowState == FormWindowState.Minimized) + { + formLiteLoader.WindowState = FormWindowState.Normal; + } + formLiteLoader.BringToFront(); + formLiteLoader.Focus(); + } + } + private string GetCheckedRadioButtonNameEn() { if (rbtWechat.Checked) diff --git a/RevokeMsgPatcher/Forms/FormLiteLoaderQQNT.Designer.cs b/RevokeMsgPatcher/Forms/FormLiteLoaderQQNT.Designer.cs new file mode 100644 index 00000000..52425eb6 --- /dev/null +++ b/RevokeMsgPatcher/Forms/FormLiteLoaderQQNT.Designer.cs @@ -0,0 +1,162 @@ +namespace RevokeMsgPatcher.Forms +{ + partial class FormLiteLoaderQQNT + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(FormLiteLoaderQQNT)); + this.splitContainer1 = new System.Windows.Forms.SplitContainer(); + this.dataGridView1 = new System.Windows.Forms.DataGridView(); + this.btnCheckUpdateAll = new System.Windows.Forms.Button(); + this.btnPath = new System.Windows.Forms.Button(); + this.btnRestore = new System.Windows.Forms.Button(); + this.txtQQNTPath = new System.Windows.Forms.TextBox(); + this.label1 = new System.Windows.Forms.Label(); + ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit(); + this.splitContainer1.Panel1.SuspendLayout(); + this.splitContainer1.Panel2.SuspendLayout(); + this.splitContainer1.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.dataGridView1)).BeginInit(); + this.SuspendLayout(); + // + // splitContainer1 + // + this.splitContainer1.Dock = System.Windows.Forms.DockStyle.Fill; + this.splitContainer1.Location = new System.Drawing.Point(0, 0); + this.splitContainer1.Name = "splitContainer1"; + this.splitContainer1.Orientation = System.Windows.Forms.Orientation.Horizontal; + // + // splitContainer1.Panel1 + // + this.splitContainer1.Panel1.Controls.Add(this.dataGridView1); + // + // splitContainer1.Panel2 + // + this.splitContainer1.Panel2.Controls.Add(this.btnCheckUpdateAll); + this.splitContainer1.Panel2.Controls.Add(this.btnPath); + this.splitContainer1.Panel2.Controls.Add(this.btnRestore); + this.splitContainer1.Panel2.Controls.Add(this.txtQQNTPath); + this.splitContainer1.Panel2.Controls.Add(this.label1); + this.splitContainer1.Size = new System.Drawing.Size(446, 284); + this.splitContainer1.SplitterDistance = 193; + this.splitContainer1.TabIndex = 0; + // + // dataGridView1 + // + this.dataGridView1.AllowUserToAddRows = false; + this.dataGridView1.AllowUserToDeleteRows = false; + this.dataGridView1.BackgroundColor = System.Drawing.Color.White; + this.dataGridView1.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize; + this.dataGridView1.Dock = System.Windows.Forms.DockStyle.Fill; + this.dataGridView1.GridColor = System.Drawing.Color.White; + this.dataGridView1.Location = new System.Drawing.Point(0, 0); + this.dataGridView1.Name = "dataGridView1"; + this.dataGridView1.ReadOnly = true; + this.dataGridView1.RowTemplate.Height = 23; + this.dataGridView1.Size = new System.Drawing.Size(446, 193); + this.dataGridView1.TabIndex = 2; + // + // btnCheckUpdateAll + // + this.btnCheckUpdateAll.Location = new System.Drawing.Point(12, 49); + this.btnCheckUpdateAll.Name = "btnCheckUpdateAll"; + this.btnCheckUpdateAll.Size = new System.Drawing.Size(75, 23); + this.btnCheckUpdateAll.TabIndex = 4; + this.btnCheckUpdateAll.Text = "更新所有"; + this.btnCheckUpdateAll.UseVisualStyleBackColor = true; + this.btnCheckUpdateAll.Click += new System.EventHandler(this.btnCheckUpdateAll_Click); + // + // btnPath + // + this.btnPath.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.btnPath.Location = new System.Drawing.Point(359, 52); + this.btnPath.Name = "btnPath"; + this.btnPath.Size = new System.Drawing.Size(75, 23); + this.btnPath.TabIndex = 3; + this.btnPath.Text = "安装"; + this.btnPath.UseVisualStyleBackColor = true; + this.btnPath.Click += new System.EventHandler(this.btnPath_Click); + // + // btnRestore + // + this.btnRestore.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.btnRestore.Location = new System.Drawing.Point(278, 52); + this.btnRestore.Name = "btnRestore"; + this.btnRestore.Size = new System.Drawing.Size(75, 23); + this.btnRestore.TabIndex = 2; + this.btnRestore.Text = "备份还原"; + this.btnRestore.UseVisualStyleBackColor = true; + this.btnRestore.Click += new System.EventHandler(this.btnRestore_Click); + // + // txtQQNTPath + // + this.txtQQNTPath.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.txtQQNTPath.Location = new System.Drawing.Point(97, 14); + this.txtQQNTPath.Name = "txtQQNTPath"; + this.txtQQNTPath.Size = new System.Drawing.Size(337, 21); + this.txtQQNTPath.TabIndex = 1; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(12, 17); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(89, 12); + this.label1.TabIndex = 0; + this.label1.Text = "QQNT安装路径:"; + // + // FormLiteLoaderQQNT + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(446, 284); + this.Controls.Add(this.splitContainer1); + this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); + this.Name = "FormLiteLoaderQQNT"; + this.Text = "LiteLoaderQQNT安装器"; + this.splitContainer1.Panel1.ResumeLayout(false); + this.splitContainer1.Panel2.ResumeLayout(false); + this.splitContainer1.Panel2.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).EndInit(); + this.splitContainer1.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.dataGridView1)).EndInit(); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.SplitContainer splitContainer1; + private System.Windows.Forms.DataGridView dataGridView1; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.Button btnPath; + private System.Windows.Forms.Button btnRestore; + private System.Windows.Forms.TextBox txtQQNTPath; + private System.Windows.Forms.Button btnCheckUpdateAll; + } +} \ No newline at end of file diff --git a/RevokeMsgPatcher/Forms/FormLiteLoaderQQNT.cs b/RevokeMsgPatcher/Forms/FormLiteLoaderQQNT.cs new file mode 100644 index 00000000..52a1368e --- /dev/null +++ b/RevokeMsgPatcher/Forms/FormLiteLoaderQQNT.cs @@ -0,0 +1,175 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; +using RevokeMsgPatcher.Model; + +namespace RevokeMsgPatcher.Forms +{ + public partial class FormLiteLoaderQQNT : Form + { + List data = new List(); + + public FormLiteLoaderQQNT() + { + InitializeComponent(); + InitializeDataGridView(); + } + + + private void InitializeDataGridView() + { + dataGridView1.RowHeadersVisible = false; + // 设置 DataGridView 的列 + dataGridView1.Columns.Add(new DataGridViewLinkColumn { Name = "NameColumn", HeaderText = "名称", AutoSizeMode = DataGridViewAutoSizeColumnMode.AllCells }); + dataGridView1.Columns.Add(new DataGridViewLinkColumn { Name = "AuthorColumn", HeaderText = "作者", AutoSizeMode = DataGridViewAutoSizeColumnMode.AllCells }); + dataGridView1.Columns.Add(new DataGridViewButtonColumn { Name = "UpdateButtonColumn", HeaderText = "更新", Text = "更新", UseColumnTextForButtonValue = true, AutoSizeMode = DataGridViewAutoSizeColumnMode.AllCells }); + dataGridView1.Columns.Add(new DataGridViewTextBoxColumn { Name = "StatusColumn", HeaderText = "状态", AutoSizeMode = DataGridViewAutoSizeColumnMode.AllCells }); + + // 初始化数据并添加行 + data = InitData(); + foreach (var item in data) + { + AddRow(item); + } + + + // 处理单元格点击事件 + dataGridView1.CellClick += DataGridView1_CellClick; + } + + private List InitData() + { + var data = new List + { + new LiteLoaderRowData + { + Name = "本体 LiteLoaderQQNT", + NameLink = "https://github.com/LiteLoaderQQNT/LiteLoaderQQNT", + Author = "mo-jinran", + AuthorLink = "https://github.com/mo-jinran", + Status = "未检查", + MainBranchName = "main", + LocalPath = Path.Combine(Application.StartupPath, "LiteLoaderQQNT"), + DownloadUrl = "https://github.com/LiteLoaderQQNT/LiteLoaderQQNT/releases/download/#{version}/LiteLoaderQQNT.zip" + }, + new LiteLoaderRowData + { + Name = "补丁 DLLHijackMethod", + NameLink = "https://github.com/LiteLoaderQQNT/QQNTFileVerifyPatch/tree/DLLHijackMethod", + Author = "sysrom", + AuthorLink = "https://github.com/sysrom", + LocalPath = Path.Combine(Application.StartupPath, "Public"), + Status = "无需更新" + }, + new LiteLoaderRowData + { + Name = "列表插件 LL-plugin-list-viewer", + NameLink = "https://github.com/ltxhhz/LL-plugin-list-viewer", + Author = "ltxhhz", + AuthorLink = "https://github.com/ltxhhz", + Status = "未检查", + MainBranchName = "main", + LocalPath = Path.Combine(Application.StartupPath, "LiteLoaderQQNT/plugins/list-viewer"), + DownloadUrl = "https://github.com/ltxhhz/LL-plugin-list-viewer/releases/download/v#{version}/list-viewer.zip" + }, + new LiteLoaderRowData + { + Name = "防撤回插件 LiteLoaderQQNT-Anti-Recall", + NameLink = "https://github.com/xh321/LiteLoaderQQNT-Anti-Recall", + Author = "xh321", + AuthorLink = "https://github.com/xh321", + Status = "未检查", + MainBranchName = "master", + LocalPath = Path.Combine(Application.StartupPath, "LiteLoaderQQNT/plugins/qq-anti-recall"), + DownloadUrl = "https://github.com/xh321/LiteLoaderQQNT-Anti-Recall/releases/download/#{version}/qq-anti-recall.zip" + } + }; + + return data; + } + + private void AddRow(LiteLoaderRowData rowData) + { + int rowIndex = dataGridView1.Rows.Add(); + DataGridViewRow row = dataGridView1.Rows[rowIndex]; + rowData.Row = row; + + // 设置名称列 + DataGridViewLinkCell nameCell = (DataGridViewLinkCell)row.Cells["NameColumn"]; + nameCell.Value = rowData.Name; + nameCell.Tag = rowData.NameLink; + + // 设置作者列 + DataGridViewLinkCell authorCell = (DataGridViewLinkCell)row.Cells["AuthorColumn"]; + authorCell.Value = rowData.Author; + authorCell.Tag = rowData.AuthorLink; + + // 设置状态列 + row.Cells["StatusColumn"].Value = rowData.Status; + + // 订阅状态更新事件 + rowData.StatusUpdated += (newStatus) => + { + if (dataGridView1.InvokeRequired) + { + dataGridView1.Invoke(new Action(() => row.Cells["StatusColumn"].Value = newStatus)); + } + else + { + row.Cells["StatusColumn"].Value = newStatus; + } + }; + + rowData.GetLocalVersionAndUpdateStatus(); + } + + + private void DataGridView1_CellClick(object sender, DataGridViewCellEventArgs e) + { + if (e.RowIndex >= 0) + { + if (e.ColumnIndex == dataGridView1.Columns["UpdateButtonColumn"].Index) + { + if (data[e.RowIndex].NameLink.Contains("QQNTFileVerifyPatch")) + { + MessageBox.Show("此项无需更新!"); + return; + } + + data[e.RowIndex].Row.Cells["StatusColumn"].Value = "正在更新"; + Task.Run(() => data[e.RowIndex].CheckAndUpdate()); + } + else if (e.ColumnIndex == dataGridView1.Columns["NameColumn"].Index || e.ColumnIndex == dataGridView1.Columns["AuthorColumn"].Index) + { + string url = dataGridView1.Rows[e.RowIndex].Cells[e.ColumnIndex].Tag.ToString(); + System.Diagnostics.Process.Start(url); + } + } + } + + private void btnCheckUpdateAll_Click(object sender, EventArgs e) + { + foreach (var item in data) + { + Task.Run(() => item.CheckAndUpdate()); + } + } + + private void btnRestore_Click(object sender, EventArgs e) + { + + } + + private void btnPath_Click(object sender, EventArgs e) + { + + } + } +} \ No newline at end of file diff --git a/RevokeMsgPatcher/Forms/FormLiteLoaderQQNT.resx b/RevokeMsgPatcher/Forms/FormLiteLoaderQQNT.resx new file mode 100644 index 00000000..629e2340 --- /dev/null +++ b/RevokeMsgPatcher/Forms/FormLiteLoaderQQNT.resx @@ -0,0 +1,408 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + + AAABAAEAQEAAAAEAIAAoQgAAFgAAACgAAABAAAAAgAAAAAEAIAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAE9FOAJPRTgOT0U4KE5EN1BPRTh2T0U4jU9FN5FPRTiRT0U4kU9F + OJFPRTiRT0Q4j09FN35PRTdcT0U4ME9FOBRPRTgET0U4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABPRTcATkQ4Ek9FOEZORTiHT0U3yU9FN+VPRDf3T0Q3/09F + Nv9PRDb/T0Q1/09ENf9PRDX/T0Q1/09FNv9PRDf/T0Q3+09FN+tPRTfTTkU4m09FOFhPRTgcT0U4Ak9F + OAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE9FOAJPRTcqT0U4eE9FOM1ORDf1T0Q2/09E + Nv9ORTn/TkZD/05IVf9NSmP/TUxt/01Ndv9NTnr/TU13/01Mb/9NS2X/TklY/05HR/9ORTr/T0U2/09E + Nv9PRTf7T0U4209FOJVPRTg8T0Q4CE9FOAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAT0U4Ak5FOCRPRTiNT0Q4409F + N/1PRDX/TkU8/01JVv9MTn//S1Ol/0pWvP9KWM7/SlnU/0pZ1/9KWtj/SlrZ/0pa2P9KWtf/SlnV/0pZ + 0f9KV8L/S1Ss/0xQi/9NSl//TkZC/09ENv9PRDb/T0U48U9FOKtPRTg6T0U4BAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABPRTcATkU3Ek5F + OHJPRTjfT0Q3/05FN/9OR0n/TE59/0tUrv9KWdD/SlrY/0pa2P9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa + 1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa2P9KWtj/SlnU/0pWuv9MUIz/TUhV/05FOf9PRDb/T0Q3709F + OJdPRTgkT0U4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AABPRTgCT0U4Mk9FOLtPRDf7T0Q2/05HR/9MT4f/SlfC/0pZ1/9KWtj/SlrX/0pa1/9KWtf/SlrX/0pa + 1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtj/SlnY/0pY + y/9MUZj/TUhV/05FN/9PRDf9T0U4105FN1RPRTgGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAABPRTgGTkU3XE9FN+FPRDf/TkU8/01Mc/9KVr3/SlnX/0pa1/9KWtf/SlrX/0pa + 1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa + 1/9KWtf/SlrX/0pa1/9KWtf/SlrY/0pYyv9MT4j/TkZF/05ENv9ORTfxTkU3h09FOA5PRTcAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABPRTgIT0U4ek9FN/FPRTb/TkdM/0tSn/9KWdT/SlrY/0pa + 1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa + 1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlnX/0tVtf9NSl//TkU3/09F + N/tPRTelT0U4GE5ENwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABPRTgKT0U4iU9FN/dORTj/TUpg/0pW + vP9KWdj/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa + 1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa + 1/9KWtj/SljK/01NeP9ORTn/T0Q3/U9FOLdPRTgcT0U4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABPRTgIT0U4hU9F + N/lPRTf/TkdJ/0pWv/9KWtn/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa + 1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa + 1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWdP/TE+L/05FO/9PRDf/T0U4s09FNxgAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AABPRTgCT0U4bE9FN/VPRTf/T0U3/05FOP9NTXP/SljO/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa + 1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa + 1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pZ1f9MT4z/TkU7/09E + N/1PRTihT0U3DgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAABPRTgAT0U4Rk9FN+1ORTb/TUtn/0xNe/9ORTn/TkU3/01Mcf9KWM3/SlrX/0pa + 1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa + 1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa + 1/9KWtf/SlnV/0xPiP9ORTj/T0U3+09FOHxPRTgEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAT0U4Ik9FONFPRTb/TUlX/0pXxP9KWdL/TE6A/05F + Of9ORTf/TUxv/0pYzf9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa + 1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa + 1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWdL/TUxz/05FN/9PRTftT0U4SE9FOAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATkU4Bk5FOJ1PRDf9TkZD/0tU + sP9KWtj/SlrX/0pZ0v9MToD/TkU5/05FN/9NTHD/SljN/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa + 1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa + 1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pXx/9NSVn/T0Q2/09F + OM1PRTgcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAT0U4AE9F + OEhPRTfzTkU4/0xQjP9KWdf/SlrX/0pa1/9KWtf/SlnS/0xOgP9ORTn/TkU3/01McP9KWM3/SlrX/0pa + 1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa + 1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa + 1/9KWtj/S1St/05GQP9PRTf9T0U4g09FOAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAE9FOA5PRTi9T0Q2/01JWv9KWM3/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWdL/TE6B/05F + OP9ORTf/TUxx/0pYzf9KWdf/SlrX/0pa1/9KWtf/SlrX/0pZ1P9KWcz/SlrW/0pa1/9KWtf/SlrX/0pa + 1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa + 1/9KWtf/SlrX/0pa1/9KWtf/SlnW/0pZzf9MTXv/TkQ2/09FN+VPRDgsT0U4AAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAE9FOABORThYT0U4905FPP9LU6X/SlrY/0pa1/9KWtf/SlrX/0pa + 1/9KWtf/SlrX/0pZ0/9MToL/TkU5/05FN/9NTG//SljN/0pa1/9KWtf/SlrX/0pZ1P9LToL/TlOQ/0pa + 1v9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa + 1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pYzf9QUHH/Sla//05HTP9PRDf/T0U4k09F + OAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABPRTgKT0U4vU9ENv9NSmT/SlnS/0pa + 1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlnS/0xOgv9ORTj/TkU2/01Mb/9KWMz/SlrX/0pZ + 0/9LToP/TkQ1/01Tlv9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa + 1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pZ0v9NTXL/T0xh/0pa + 1v9MT4n/TkU3/09FN+VPRTgmAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABPRTgAT0U4PE9F + N/FORTr/S1Ol/0pZ2P9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWdP/TE+C/05F + Of9ORTf/TUxt/0pXyf9LTX//TkQ2/05ENP9NUpb/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa + 1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrW/0pZ + 0v9PUoX/T0U2/05Mav9KWtf/SlfC/05HSv9PRTf9T0U4eE9FOAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAATkU3Ak9FOI1PRTf/TkhV/0pYzv9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa + 1/9KWtf/SlrX/0pZ0v9MToL/TkU5/05FNv9NR0//TkQ1/09FNf9ORDT/TVKW/0pa1/9KWtf/SlrX/0pa + 1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa + 1/9KWtf/SlrX/0pYzv9OUHr/T0U3/05ENf9MTXX/SlrX/0pZ1/9NTXX/T0Q2/05FOMVORTgOAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAE9FNxRPRTfRTkQ2/0xOgP9KWtj/SlrX/0pa1/9KWtf/SlrX/0pa + 1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlnS/0xOgP9NRDb/TkQ1/09FNf9PRTX/TkQ0/01S + lv9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa + 1/9KWtf/SlrX/0pa1/9KWtf/SlnW/0tYw/9NSmH/TkU1/09FNf9ORDX/T1Wb/0pa1/9KWtj/S1Oq/05F + Ov9PRTfzT0U4OAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABPRTg8T0U38U5FPP9LVLH/SlrX/0pa + 1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pZ0v9MTHT/TkQ1/09F + Nf9PRTX/T0U1/05ENP9NU5b/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa + 1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtb/SlnP/01SlP9PR0b/TkQ0/09FNf9PRTX/TkQ3/0xX + uf9KWtf/SlrX/0pYyv9OSE7/T0Q3/U9FOHpPRTcAAAAAAAAAAAAAAAAAAAAAAAAAAABPRTgAT0U4eE5E + N/1OSFD/SljK/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pZ + 0v9MTHP/TkQ1/09FNf9PRTX/T0U1/09FNf9ORDT/TVKV/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa + 1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWc3/TFOc/09KVv9PRTb/T0U1/09F + Nf9PRTX/T0U1/09KUv9LWcz/SlrX/0pa1/9KWdf/TExz/09ENv9PRTixT0U4CAAAAAAAAAAAAAAAAAAA + AAAAAAAAT0U4Bk9FOKVPRTb/TUxt/0pZ1v9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa + 1/9KWtf/SlrX/0pZ0P9MS2//TkQ1/09FNf9PRTX/T0U1/09FNf9PRTX/TkQ0/09MZP9KWMv/SlrX/0pa + 1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1v9KWdP/S1nO/0tUp/9UVof/iYaO/7u3 + sv9ORDT/T0U1/2phVP/Mycb/YllL/05ENP9OTnP/SlrW/0pa1/9KWtf/SlrZ/0tQkP9ORTb/T0U41U9F + OBoAAAAAAAAAAAAAAAAAAAAAAAAAAE5FOBBPRTjFTkQ1/0xPhP9KWtn/SlrX/0pa1/9KWtf/SlrX/0pa + 1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pZz/9MS27/TkQ1/09FNf9PRTX/T0U1/09FNf9PRTX/T0U1/05E + NP9PRTf/UU1g/0xRlP9LUpv/TFat/01Yuv9NWcH/TVnB/01Zvv9NV7L/S1Oh/0tRlv9PUon/Tkpb/09I + Sf9iWk7/zsvH/9jW0/+qpZ7/T0U1/8K/u//Kx8T/0c/M/1NKOv9ORDT/TVex/0pa1/9KWtf/SlrX/0pa + 2P9LVLD/TkU8/09FN/FPRTg4AAAAAAAAAAAAAAAAAAAAAAAAAABPRTgkTkU35U5FN/9LU6H/SlrY/0pa + 1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pYz/9MS2r/TkQ1/09FNf9PRTX/T0U1/09F + Nf9PRTX/T0U1/09FNf9PRTX/T0Q0/05ENP9ORDT/TkQ1/09GOf9PRj7/T0dA/09HQP9PRz//T0Y7/05F + Nv9ORDT/TkQ0/05ENP9PRTX/mJKJ/6unoP+HgXb/zcrG/2ZeUP/Ewb3/nJeP/66qo/9waFz/T0pX/0pZ + zv9KWtf/SlrX/0pa1/9KWtf/SlfC/05GRP9PRTf5TkU4TgAAAAAAAAAAAAAAAAAAAAAAAAAAT0U4Mk9F + N+9ORTv/S1W0/0pa2P9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pYzv9MSmf/TkQ1/09F + Nf9PRTX/T0U1/09FNf9PRTX/T0U1/09FNf9PRTX/T0U1/09FNf9PRTX/T0U1/09FNf9PRTX/T0U1/09F + Nf9PRTX/T0U1/09FNf9PRTX/T0U1/09FNf9PRTX/T0U1/6Kdlf9waFv/T0U1/8TBvP9vZ1r/o56X/09F + Nf+gm5P/cmpf/0xSl/9KWtb/SlrX/0pa1/9KWtf/SlrX/0pYyv9OR0z/T0U3+09FOFgAAAAAAAAAAAAA + AAAAAAAAAAAAAE9FODpPRTfzTkU+/0tVuv9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pY + zf9MSmX/TkQ1/09FNf9PRTX/T0U1/09FNf9PRTX/T0U1/09FNf9PRTX/T0U1/09FNf9PRTX/T0U1/09F + Nf9PRTX/T0U1/09FNf9PRTX/T0U1/09FNf9PRTX/T0U1/09FNf9PRTX/T0U1/09FNf+QioD/jIZ8/09F + Nf/DwLv/YFdI/7y5s/9PRTX/sa2m/2FcZv9KWMr/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWM7/TkhT/09E + NvtPRThYAAAAAAAAAAAAAAAAAAAAAAAAAABPRTdCT0U39U5GQP9KVr7/SlrX/0pa1/9KWtf/SlrX/0pa + 1/9KWtf/SlrX/0pYzP94eJT/XFNE/09FNf9+d2z/VEo7/09FNf9SSTn/f3ht/4mDeP9bUkP/T0U1/1NJ + Of+AeW7/gntw/09FNf9yal3/YFdJ/09FNf91bmH/XVRF/1ZNPv+HgHb/c2tf/3t0af9PRTX/Ukk5/4iB + d/9qYVT/WVBB/7Wxq/+uqaL/enJn/09FNf+inZX/qaWe/5yXkf9MU5z/SlrX/0pa1/9KWtf/SlrX/0pa + 1/9KWtf/SlnQ/01JV/9PRDb7T0U4WAAAAAAAAAAAAAAAAAAAAAAAAAAAT0U3Qk9FN/VORkD/Sla+/0pa + 1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pYy/9UUWn/9PT0/6GclP9dU0X/+Pj4/4mDeP9PRTX/raih/9XT + 0P+3s63/wL24/09FNf/LyMT/1NHO/8fEwP95cWX/trKt/4N8cf9PRTX/wb25/3lxZf+tqaL/zszI/9nX + 1f/T0c7/T0U1/4uEev/d3Nr/nJaO/09FNf9PRTX/T0U1/09FNf9PRTX/T0U1/05ENP9OTGX/SlnT/0pa + 1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pZ0P9NSVf/T0Q2+09FOFgAAAAAAAAAAAAAAAAAAAAAAAAAAE9F + ODpPRTfzTkY+/0tWuv9KWtf/SlrX/0pa1/9KWtf/SlrX/0pYyf9MSVz/h4B2/8XCvv/PzMn/koyC/9LQ + zf/Bvrn/T0U1/+Xj4v+2sq3/qqWf/6mknf9kW03/5+bl/1BGNv9PRTX/T0U1/7ayrf+DfHH/T0U1/8G9 + uf95cWX/rKeg/7ayrf+dmI//09HO/09FNf+Ykon/opyV/09FNf9PRTX/T0U1/09FNf9PRTX/T0U1/09E + NP9PSUr/S1jD/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWM7/TkhT/09ENvtPRThYAAAAAAAA + AAAAAAAAAAAAAAAAAABPRTcyT0Q3705FO/9LVbT/SlrY/0pa1/9KWtf/SlrX/0pXyP9NSVv/TkQ1/8G+ + uf+IgXf/ycbC/97c2/94cWX/6Ofm/1VMPP/f3dv/qaSd/5SOhf/x8fH/YFdJ/+rp6f9WTD3/T0U1/09F + Nf+2sq3/mZSL/09FNf/IxcD/eXFl/1hPQP+WkIf/wLy3/9PRzv9PRTX/mJKJ/6Kclf9PRTX/T0U1/09F + Nf9PRTX/T0U1/09FNf9ORTr/TFOg/0pa1v9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SljK/05H + TP9PRTf7T0U4WAAAAAAAAAAAAAAAAAAAAAAAAAAAT0U4JE9FOOdORDj/S1Kj/0pa2f9KWtf/SlrX/0pZ + z/9NSVj/TkQ1/1hOP//o5+b/U0o6/4uEev/r6un/UEY2/7y4s/+Ignf/hn90/+fl5P/b2db/sq2n/09F + Nf+uqqP/5uXk/9LQzf95cWX/trKt/+vq6f/Qzcr/7e3s/1xSRP9kXE7/5ePi/93b2f+rpp//W1JD/+De + 3P/i4d//m5WN/09FNf9PRTX/T0U1/09FNf9ORTn/TlOX/0pZ1v9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa + 1/9KWtf/SlrX/0pXw/9ORkT/T0U3+U9FOE4AAAAAAAAAAAAAAAAAAAAAAAAAAE9EOBBPRTjHTkQ1/0xP + hf9KWtn/SlrX/0pa1/9KWdP/TE5+/05FNv9SSDn/aF9S/09FNf9USjv/Zl1Q/09FNf9dVEX/XVRF/09F + Nf9fVkf/aWFT/09FNf9PRTX/T0U1/2deUf9uZln/UEY2/7ayrf+EfXL/bmZZ/1lQQf9PRTX/T0U1/15V + R/9uZln/UUc4/1JIOP+qpZ7/sq6o/2JZS/9PRTX/T0U1/09FNf9ORTj/T1ON/0pa1f9KWtf/SlrX/0pa + 1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa2P9LVbL/TkU9/09FN/NPRTg6AAAAAAAAAAAAAAAAAAAAAAAA + AABPRTgGT0U4pU9FNv9NTG7/SlrX/0pa1/9KWtf/SlrX/0pZ0/9LToL/TkU2/09FNf9PRTX/T0U1/09F + Nf9PRTX/T0U1/09FNf9PRTX/T0U1/09FNf9PRTX/T0U1/09FNf9PRTX/T0U1/09FNf+2sqz/g3xx/09F + Nf9PRTX/T0U1/09FNf9PRTX/T0U1/09FNf9PRTX/XVRF/2lgU/9PRTX/T0U1/09FNf9ORTr/T1SU/0pZ + 1P9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtn/TFGR/05FN/9PRTjXT0U4HAAA + AAAAAAAAAAAAAAAAAAAAAAAAT0U4AE9FOHpPRDf9TkhS/0pYyv9KWtf/SlrX/0pa1/9KWtf/SlnV/0tP + hv9ORTf/T0U1/09FNf9PRTX/T0U1/09FNf9PRTX/T0U1/09FNf9PRTX/T0U1/09FNf9PRTX/T0U1/09F + Nf9PRTX/T0U1/09FNf9PRTX/T0U1/09FNf9PRTX/T0U1/09FNf9PRTX/T0U1/09FNf9PRTX/T0U1/05E + NP9ORkH/TVSh/0pa1v9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlnX/01N + dP9PRDb/T0U4tU9FOAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABPRTg+TkQ3805FPf9LVbP/SlrY/0pa + 1/9KWtf/SlrX/0pa1/9KWdT/S0+J/05FN/9PRTX/T0U1/09FNf9PRTX/xMG9/2JZS/9WTD3/2dfU/46H + fv+EfXL/gHlu/5+akv+Nh33/m5aN/4uFev9PRTX/T0U1/09FNf9PRTX/T0U1/09FNf9PRTX/T0U1/09F + Nf9PRTX/T0U1/05ENP9QTF3/S1a3/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa + 1/9KWtf/SlrX/0pYy/9OSFD/T0Q2/U9FOHxPRTgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAT0U3Fk9F + N9VPRDb/TE+E/0pa2P9KWtf/SlrX/0pa1/9KWtf/SlrX/0pZ1f9LT4v/TkU4/09FNf9PRTX/T0U1/9XT + 0P9lXE7/T0U1/7i0r/9aUUL/i4V6/4qDef/g393/1NLP/6ijnP+UjoT/T0U1/09FNf9PRTX/T0U1/09F + Nf9PRTX/T0U1/09FNf9ORDT/TkQ0/05ENf9QSk7/S1e//0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa + 1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa2P9LVK7/TkU7/09FN/VPRTg6AAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAE9FOAJPRTiRT0U2/05JVv9KWM7/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlnV/0tQ + j/9ORTj/T0U1/09FNf/V09D/ZVxO/09FNf+4tK//WlFC/4uFev/Fwr7/p6Ka/7Svqf/V0s//lI6E/09F + Nf9PRTX/T0U1/09FNf9PRTX/T0U1/09FNf9ORDX/TkdB/05MYf9PRjz/TkU2/01LZv9KWMn/SlrY/0pa + 1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWdf/TU13/09ENv9PRTjJT0U4EAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAT0U4QE9FN/NORTv/S1Oo/0pa2P9KWtf/SlrX/0pa + 1/9KWtf/SlrX/0pa1/9KWdX/S1CS/05FOP9TSjr/1tTR/2lhU/9QRjb/uLSv/1pRQv+LhXr/+Pj4/2Rb + Tf9xaV3/+vn6/5SOhP9PRTX/T0U1/05ENP9ORDT/TkU1/05ENv9PSlX/TVCF/0tXwv9KWdT/TE+J/05F + Ov9ORTb/TUto/0pXyf9KWtj/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlfE/05H + TP9PRTf9T0U4fE9FOAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE9FOAxPRTjBT0Q2/01L + Z/9KWdL/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pZ1f9LUZT/19bY/+Xk4//l5OP/fXZq/9jW + 1P+Oh37/g3xy/7OvqP9PRTX/TkU1/7y4s/+LhXz/TkU4/05FOv9ORkL/T01o/01PgP9MV7b/SlnP/0pa + 1/9KWtf/SlrX/0pZ1P9MUIv/TkU7/09FNv9NS2b/SlfK/0pa2P9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa + 1/9KWtf/SlrY/0xQjf9ORTf/T0U3509FOCgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AABPRTgAT0U4Xk9FN/lORT3/S1Op/0pa2P9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlnV/0tR + mP9ORTn/T0U1/09FNf9PRTX/T0U1/09FNf9PRTX/T0U1/0xLZf9NUYv/TlSY/05Ysf9KV8H/SljH/0pZ + 1P9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlnU/0xPiv9ORTr/TkQ2/01LaP9KV8n/SlrX/0pa + 1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pXxf9OSE7/T0U3/09FOJtPRTgGAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAE9FOBBPRTjBTkQ2/01JXf9KWc7/SlrX/0pa1/9KWtf/SlrX/0pa + 1/9KWtf/SlrX/0pa1/9KWdb/S1Gb/05FO/9PRTX/T0U1/09FNf9PRTX/T0U1/05ENP9NUpf/SlrX/0pa + 1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWdX/TFCM/05F + PP9ORTb/TUpl/0pXyP9KWtf/SlrX/0pa1/9KWtf/SlrX/0pZ1/9MTn//TkU2/09FN+lPRTgwT0U4AAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABPRTcAT0U4Tk9FN/VORTn/TFCR/0pa + 2P9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pZ1v9KUp3/TkU7/09FNf9PRTX/T0U1/09F + Nf9ORDT/TVKW/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa + 1/9KWtf/SlrX/0pZ1P9MUIv/TkU7/09FNv9NSmb/SlfH/0pa2P9KWtf/SlrX/0pa2P9LVbL/TkZC/09F + N/1PRDiJT0U4AgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE5F + OAhPRDihT0Q3/05GRf9LVbP/SlnY/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlnW/0tS + oP9ORT3/T0U1/09FNf9PRTX/TkQ0/01Slv9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa + 1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlnV/0xPjP9ORTz/TkU2/01KZf9KV8j/SlrX/0pa + 1/9KWMn/TUlb/05FNv9PRTjRT0U4HgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAT0U3Jk9FONdORDb/TUlb/0pXyP9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa + 1/9KWtf/SlrX/0pa1/9KWdb/SlOj/05FPv9PRTX/T0U1/05ENP9NUpb/SlrX/0pa1/9KWtf/SlrX/0pa + 1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWdT/TFCK/05F + O/9ORTb/TUpl/0pXyP9KWdT/TE15/05FN/9PRTfxT0U4UE9FOAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE9FNwBPRTdOT0U3705FN/9NS2z/SljO/0pa + 1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pZ1v9KU6b/TkU+/05FNf9ORDT/TVKW/0pa + 1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa + 1/9KWtf/SlrX/0pZ1f9MUI3/TkU7/05FNv9NS2b/TE+D/05FOv9PRTf7T0U4g09FOAQAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAT0U4BE9F + N3RPRTf3TkU3/01Ndf9KWM//SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlnW/0pT + qP9ORkD/TkQ0/01Slv9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa + 1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlnV/0xQjf9ORj7/T0Q3/09FN/9PRTf9T0U4qU9F + OBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAABPRTgIT0U4jU9FN/tORTj/TUxy/0pYzP9KWtj/SlrX/0pa1/9KWtf/SlrX/0pa + 1/9KWtf/SlrX/0pa1/9KWdb/SlSr/01FP/9NU5b/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa + 1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWdH/TUtn/09F + Nv9PRTf/T0U3u09FOBxPRTcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE9FOA5PRTiVT0U3+U5FNv9NS2b/SlbA/0pZ + 2P9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pZ1v9KU6f/TlOT/0pa1v9KWtf/SlrX/0pa + 1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa + 2P9KWM3/TE6A/05FPP9PRDf9T0U4v09FOCBPRTgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAT0U4Ck9F + OINPRTf1TkU2/05IT/9LU6X/SlnW/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa + 1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa + 1/9KWtf/SlrX/0pa2P9KVrr/TUpk/05EOP9PRDf9T0U3r09FNx5ORDcAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAABPRTgGT0U4ZE9FN+VPRDb/TkU+/0xNe/9KV8H/SlrY/0pa1/9KWtf/SlrX/0pa + 1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa + 1/9KWtf/SlrX/0pa1/9KWtf/SlrY/0pYzf9MUJD/TkdI/05ENv9PRTfzT0U4kU9FOBJPRTcAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE9FNwJPRTc6T0U4w09EN/tPRDb/TkdK/0xQ + jf9KV8X/SlrY/0pa2P9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa + 1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtj/SljO/0tSn/9OSVr/TkU4/09EN/9PRTjdT0U3XE9F + OAYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAT0U4AE9F + NxRPRTd+T0U35U9FN/9ORDf/TkdO/0xOg/9KVbf/SlnR/0pa2f9KWtj/SlrX/0pa1/9KWtf/SlrX/0pa + 1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtj/SlrZ/0pZ1f9KV8D/S1GU/01JWv9ORTn/T0Q2/09F + N/FPRTehT0U4Kk9FOAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAT0U4Ak9FOCxORTeVT0U36U9FN/9PRDb/TkY+/01KXf9MT4P/S1St/0pX + xP9KWM7/SlnV/0pZ2P9KWtj/SlnY/0pZ2P9KWtj/SlnW/0pZ0P9KV8f/S1W2/0xQjv9NS2j/TkZD/09E + N/9PRTf/T0U3809FN7NPRThET0U3BgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAT0U4BE9FODBPRTiHT0U31U9F + N/dPRDb/T0U2/05FO/9ORkb/TUhV/01Mbf9NTXr/TE6A/0xPhP9MToH/TE58/01Mcf9NSVz/TkdJ/05G + Pf9ORTf/T0Q2/09EN/tORDflT0U4nU9EOEZPRTgKT0U4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAT0U4Ak9FNxZPRTdMT0U4mU9FOMtPRTfvT0U3+U9ENv1PRDb/T0U2/09FNv9PRTb/T0U2/09F + Nv9PRDb/T0Q2/09EN/lPRTfzT0U32U9FOKdPRTdgT0U4Ik9FOARPRTgAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE9FOARPRTgST0U4ME9FN1JPRTh+TkU3o09F + OLFPRTi9T0U4wU9FOL9PRTi1T0U4qU9FOItPRThaT0U4Ok5FNxhPRTcGT0U4AAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AABPRTgAT0U4Ak9FOARPRTgET0U4Bk9FOAZPRTgGT0U4BE9FOARPRTgCT0U4AE9FOAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAA////////////////////////////////////8B////////4AAP//////+AAAH//////AAAAH + /////4AAAAH////+AAAAAP////wAAAAAP///+AAAAAAf///gAAAAAA///8AAAAAAB///wAAAAAAD//+A + AAAAAAP//wAAAAAAAf/+AAAAAAAA//4AAAAAAAB//AAAAAAAAH/8AAAAAAAAP/gAAAAAAAA/+AAAAAAA + AD/wAAAAAAAAH/AAAAAAAAAf8AAAAAAAAB/wAAAAAAAAD+AAAAAAAAAP4AAAAAAAAA/gAAAAAAAAD+AA + AAAAAAAP4AAAAAAAAA/gAAAAAAAAD+AAAAAAAAAP4AAAAAAAAA/gAAAAAAAAD+AAAAAAAAAP4AAAAAAA + AA/gAAAAAAAAD/AAAAAAAAAP8AAAAAAAAB/wAAAAAAAAH/AAAAAAAAAf+AAAAAAAAD/4AAAAAAAAP/wA + AAAAAAA//AAAAAAAAH/+AAAAAAAAf/4AAAAAAAD//wAAAAAAAf//gAAAAAAB///AAAAAAAP//8AAAAAA + B///4AAAAAAP///wAAAAAB////wAAAAAP////gAAAAD/////gAAAAf/////AAAAH//////AAAB////// + /gAA////////8A////////////////////////////////////8= + + + \ No newline at end of file diff --git a/RevokeMsgPatcher/Model/Json/LiteLoaderPackage.cs b/RevokeMsgPatcher/Model/Json/LiteLoaderPackage.cs new file mode 100644 index 00000000..7c4d076f --- /dev/null +++ b/RevokeMsgPatcher/Model/Json/LiteLoaderPackage.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace RevokeMsgPatcher.Model.Json +{ + internal class LiteLoaderPackage + { + public string Name { get; set; } + public string Version { get; set; } + public bool Private { get; set; } + public string Description { get; set; } + public string ProductName { get; set; } + public string Homepage { get; set; } + public bool SideEffects { get; set; } + public string Main { get; set; } + public string BuildVersion { get; set; } + public bool IsPureShell { get; set; } + public bool IsByteCodeShell { get; set; } + public string Platform { get; set; } + public string EleArch { get; set; } + } +} diff --git a/RevokeMsgPatcher/Model/Json/LiteLoaderPluginsManifest.cs b/RevokeMsgPatcher/Model/Json/LiteLoaderPluginsManifest.cs new file mode 100644 index 00000000..9954ad8c --- /dev/null +++ b/RevokeMsgPatcher/Model/Json/LiteLoaderPluginsManifest.cs @@ -0,0 +1,17 @@ +namespace RevokeMsgPatcher.Model.Json +{ + + /// + /// 只有部分信息,主要是拿版本号 + /// https://github.com/xh321/LiteLoaderQQNT-Anti-Recall/blob/master/manifest.json + /// + internal class LiteLoaderPluginsManifest + { + public string Type { get; set; } + public string Name { get; set; } + public string Slug { get; set; } + public string Description { get; set; } + public string Version { get; set; } + public string Icon { get; set; } + } +} diff --git a/RevokeMsgPatcher/Model/Json/ReleaseApiRes.cs b/RevokeMsgPatcher/Model/Json/ReleaseApiRes.cs new file mode 100644 index 00000000..9237651d --- /dev/null +++ b/RevokeMsgPatcher/Model/Json/ReleaseApiRes.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; + +namespace RevokeMsgPatcher.Model.Json +{ + internal class ReleaseApiRes + { + + public string Url { get; set; } + public string AssetsUrl { get; set; } + public string UploadUrl { get; set; } + public string HtmlUrl { get; set; } + public int Id { get; set; } + public string NodeId { get; set; } + public string TagName { get; set; } + public string TargetCommitish { get; set; } + public string Name { get; set; } + public bool Draft { get; set; } + public bool Prerelease { get; set; } + public DateTime CreatedAt { get; set; } + public DateTime PublishedAt { get; set; } + public List Assets { get; set; } + public string TarballUrl { get; set; } + public string ZipballUrl { get; set; } + public string Body { get; set; } + } + + public class Asset + { + public string Url { get; set; } + public int Id { get; set; } + public string NodeId { get; set; } + public string Name { get; set; } + public object Label { get; set; } + public string ContentType { get; set; } + public string State { get; set; } + public int Size { get; set; } + public int DownloadCount { get; set; } + public string CreatedAt { get; set; } + public string UpdatedAt { get; set; } + public string BrowserDownloadUrl { get; set; } + } +} diff --git a/RevokeMsgPatcher/Model/Json/VersionJson.cs b/RevokeMsgPatcher/Model/Json/VersionJson.cs new file mode 100644 index 00000000..e374e322 --- /dev/null +++ b/RevokeMsgPatcher/Model/Json/VersionJson.cs @@ -0,0 +1,8 @@ +namespace RevokeMsgPatcher.Model.Json +{ + internal class VersionJson + { + public string Name { get; set; } + public string Version { get; set; } + } +} diff --git a/RevokeMsgPatcher/Model/LiteLoaderRowData.cs b/RevokeMsgPatcher/Model/LiteLoaderRowData.cs new file mode 100644 index 00000000..6b2f8de3 --- /dev/null +++ b/RevokeMsgPatcher/Model/LiteLoaderRowData.cs @@ -0,0 +1,297 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using RevokeMsgPatcher.Model.Json; +using System; +using System.Diagnostics; +using System.IO; +using System.IO.Compression; +using System.Net.Http; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; +using Application = System.Windows.Forms.Application; + +namespace RevokeMsgPatcher.Model +{ + internal class LiteLoaderRowData + { + public static JsonSerializerSettings SerializerSettings = new JsonSerializerSettings + { + ContractResolver = new CamelCasePropertyNamesContractResolver() + }; + + public string Name { get; set; } + + public string NameLink { get; set; } + + public string Author { get; set; } + + public string AuthorLink { get; set; } + + public string Status { get; set; } + + public event Action StatusUpdated; + + public DataGridViewRow Row { get; set; } + + /// + /// 本地存储的目录 + /// + public string LocalPath { get; set; } + + /// + /// 主干名称 + /// + public string MainBranchName { get; set; } + + private void UpdateStatus(string newStatus) + { + Status = newStatus; + StatusUpdated?.Invoke(newStatus); + } + + + /// + /// 由于这个 api.github.com 没有加速的方式,所以不用了 + /// + [Obsolete("由于这个 api.github.com 没有加速的方式,所以不用了")] + public string ReleasesApi + { + get + { + var repo = NameLink.Replace(@"https://github.com", @"https://api.github.com/repos"); + return repo + @"/releases/latest"; + } + } + + public string DownloadUrl { get; set; } + + public string VersionJsonUrl + { + get + { + var repo = NameLink.Replace(@"https://github.com", @"https://raw.githubusercontent.com"); + if (NameLink == "https://github.com/LiteLoaderQQNT/LiteLoaderQQNT") + { + return repo + $@"/refs/heads/{MainBranchName}/package.json"; + } + else + { + return repo + $@"/refs/heads/{MainBranchName}/manifest.json"; + } + } + } + + public string GetLocalVersion() + { + if (NameLink.Contains("QQNTFileVerifyPatch")) + { + return null; + } + + if (!Directory.Exists(LocalPath)) + { + Directory.CreateDirectory(LocalPath); + } + + string path = null; + path = Path.Combine(LocalPath, NameLink == "https://github.com/LiteLoaderQQNT/LiteLoaderQQNT" ? "package.json" : "manifest.json"); + + if (File.Exists(path)) + { + var json = File.ReadAllText(path); + var package = JsonConvert.DeserializeObject(json, SerializerSettings); + return package.Version; + } + + return null; + } + + public void GetLocalVersionAndUpdateStatus() + { + var localVersion = GetLocalVersion(); + if (localVersion != null) + { + UpdateStatus($"当前版本{localVersion}"); + } + else + { + UpdateStatus("未检查"); + } + } + + public async Task GetRemoteVersion() + { + using (var client = new HttpClient()) + { + var response = await client.GetAsync(VersionJsonUrl); + Debug.WriteLine(response.Content); + if (response.IsSuccessStatusCode) + { + var json = await response.Content.ReadAsStringAsync(); + var package = JsonConvert.DeserializeObject(json, SerializerSettings); + return package.Version; + } + + return null; + } + } + + public async Task CheckAndUpdate() + { + try + { + if (NameLink.Contains("QQNTFileVerifyPatch")) + { + return; + } + + string localVersion = GetLocalVersion(); + string remoteVersion = await GetRemoteVersion(); + + if (localVersion == null || new Version(remoteVersion) > new Version(localVersion)) + { + UpdateStatus($"存在新版本{remoteVersion},正在下载..."); + Debug.WriteLine("发现新版本,正在下载..."); + string downloadedFilePath = await DownloadLatestPackage(DownloadUrl.Replace("#{version}", remoteVersion), Path.Combine(Application.StartupPath, "Public/Download")); + Debug.WriteLine("下载到:" + downloadedFilePath); + UpdateStatus($"下载成功,解压中..."); + + // 解压 + string zipFileName = Path.GetFileNameWithoutExtension(downloadedFilePath); + string extractPath = Path.Combine(Application.StartupPath, "Public/Extracted", zipFileName); + if (Directory.Exists(extractPath)) + { + Directory.Delete(extractPath, true); + } + Directory.CreateDirectory(extractPath); + ZipFile.ExtractToDirectory(downloadedFilePath, extractPath); + + Debug.WriteLine("解压至:" + extractPath); + UpdateStatus($"解压成功,替换中..."); + + // 找到根目录 + string pluginPath = FindDirectoryWithJson(extractPath); + Debug.WriteLine("解压后的插件/本体目录:" + pluginPath); + + // 拷贝 + DirectoryCopy(pluginPath, LocalPath); + Debug.WriteLine("拷贝至:" + LocalPath); + + + // 清理 + if (File.Exists(downloadedFilePath)) + { + File.Delete(downloadedFilePath); + } + if (Directory.Exists(extractPath)) + { + Directory.Delete(extractPath, true); + } + Debug.WriteLine("清理完成。"); + UpdateStatus($"{remoteVersion}更新完成"); + } + else + { + UpdateStatus($"已是最新版本{localVersion}"); + Debug.WriteLine("当前已是最新版本。"); + } + } + catch (Exception e) + { + Debug.WriteLine(e.ToString()); + UpdateStatus(Status + " 后发生异常:" + e.Message); + } + + } + + private string FindDirectoryWithJson(string extractPath, int maxDepth = 2) + { + return FindDirectoryWithJsonRecursive(extractPath, maxDepth, 0); + } + + private string FindDirectoryWithJsonRecursive(string currentPath, int maxDepth, int currentDepth) + { + if (currentDepth > maxDepth) + { + return null; + } + + string[] jsonFiles = { "package.json", "manifest.json" }; + foreach (var jsonFile in jsonFiles) + { + if (File.Exists(Path.Combine(currentPath, jsonFile))) + { + return currentPath; + } + } + + if (currentDepth < maxDepth) + { + foreach (var directory in Directory.GetDirectories(currentPath)) + { + var result = FindDirectoryWithJsonRecursive(directory, maxDepth, currentDepth + 1); + if (result != null) + { + return result; + } + } + } + + return null; + } + + private async Task DownloadLatestPackage(string url, string localDirectory) + { + using (HttpClient client = new HttpClient()) + { + var response = await client.GetAsync(url); + if (response.IsSuccessStatusCode) + { + var data = await response.Content.ReadAsByteArrayAsync(); + var fileName = Path.GetFileName(url); + var localPath = Path.Combine(localDirectory, fileName); + Directory.CreateDirectory(localDirectory); // 确保目录存在 + File.WriteAllBytes(localPath, data); + return localPath; + } + else + { + throw new Exception("下载失败"); + } + } + } + + private void DirectoryCopy(string sourceDirName, string destDirName, bool copySubDirs = true) + { + DirectoryInfo dir = new DirectoryInfo(sourceDirName); + DirectoryInfo[] dirs = dir.GetDirectories(); + + if (!dir.Exists) + { + throw new DirectoryNotFoundException("源目录不存在: " + sourceDirName); + } + + if (!Directory.Exists(destDirName)) + { + Directory.CreateDirectory(destDirName); + } + + FileInfo[] files = dir.GetFiles(); + foreach (FileInfo file in files) + { + string tempPath = Path.Combine(destDirName, file.Name); + file.CopyTo(tempPath, true); + } + + if (copySubDirs) + { + foreach (DirectoryInfo subdir in dirs) + { + string tempPath = Path.Combine(destDirName, subdir.Name); + DirectoryCopy(subdir.FullName, tempPath, copySubDirs); + } + } + } + } +} \ No newline at end of file diff --git a/RevokeMsgPatcher/RevokeMsgPatcher.csproj b/RevokeMsgPatcher/RevokeMsgPatcher.csproj index 50d4c00b..636b2763 100644 --- a/RevokeMsgPatcher/RevokeMsgPatcher.csproj +++ b/RevokeMsgPatcher/RevokeMsgPatcher.csproj @@ -39,8 +39,12 @@ app.manifest + + ..\packages\Newtonsoft.Json.13.0.3\lib\net45\Newtonsoft.Json.dll + + @@ -61,6 +65,12 @@ FormMain.cs + + Form + + + FormLiteLoaderQQNT.cs + Form @@ -75,6 +85,11 @@ + + + + + @@ -97,6 +112,9 @@ FormMain.cs + + FormLiteLoaderQQNT.cs + FormPatchInfo.cs @@ -111,6 +129,7 @@ True + SettingsSingleFileGenerator Settings.Designer.cs @@ -127,5 +146,6 @@ + \ No newline at end of file diff --git a/RevokeMsgPatcher/packages.config b/RevokeMsgPatcher/packages.config new file mode 100644 index 00000000..46471ce6 --- /dev/null +++ b/RevokeMsgPatcher/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file