diff --git a/CVRMelonAssistant/CVRMelonAssistant.csproj b/CVRMelonAssistant/CVRMelonAssistant.csproj index 02ae264..434dc3f 100644 --- a/CVRMelonAssistant/CVRMelonAssistant.csproj +++ b/CVRMelonAssistant/CVRMelonAssistant.csproj @@ -308,12 +308,21 @@ - - - $(MSBuildThisFileDirectory)bin\$(Configuration)\$(TargetFramework) - - - - - + + $(MSBuildThisFileDirectory)bin\$(Configuration)\$(TargetFramework) + + + + + + + + + + + + + + + diff --git a/CVRMelonAssistant/Classes/InstallHandlers.cs b/CVRMelonAssistant/Classes/InstallHandlers.cs index 859a6f8..a0259f8 100644 --- a/CVRMelonAssistant/Classes/InstallHandlers.cs +++ b/CVRMelonAssistant/Classes/InstallHandlers.cs @@ -2,6 +2,8 @@ using System.IO; using System.IO.Compression; using System.Linq; +using System.Net.Http; +using System.Text.Json.Nodes; using System.Threading.Tasks; using System.Windows; using CVRMelonAssistant.Pages; @@ -41,6 +43,29 @@ public static bool RemoveMelonLoader() return true; } + private static async Task GetLatestWindowsMelonLoaderNightlyDownloadUrl() + { + const string apiUrl = + "https://api.github.com/repos/LavaGang/MelonLoader/actions/workflows/5411546/runs" + + "?branch=alpha-development&event=push&status=success&per_page=1&sort=created&direction=desc"; + + using var http = new HttpClient(); + + // GitHub requires a user-agent header + http.DefaultRequestHeaders.UserAgent.ParseAdd("Mozilla/5.0"); + + var resp = await http.GetStringAsync(apiUrl); + var json = JsonNode.Parse(resp)!["workflow_runs"]!.AsArray(); + + if (json.Count == 0) + return null; + + var run = json[0]; + var runId = run!["id"]!.ToString(); + + return $"https://nightly.link/LavaGang/MelonLoader/actions/runs/{runId}/MelonLoader.Windows.x64.CI.Release.zip"; + } + public static async Task InstallMelonLoader() { if (!RemoveMelonLoader()) return; @@ -49,7 +74,8 @@ public static async Task InstallMelonLoader() { MainWindow.Instance.MainText = $"{(string) App.Current.FindResource("Mods:DownloadingMelonLoader")}..."; - using var installerZip = await DownloadFileToMemory("https://github.com/LavaGang/MelonLoader/releases/latest/download/MelonLoader.x64.zip"); + string nightlyLink = await GetLatestWindowsMelonLoaderNightlyDownloadUrl(); + using var installerZip = await DownloadFileToMemory(nightlyLink); using var zipReader = new ZipArchive(installerZip, ZipArchiveMode.Read); MainWindow.Instance.MainText = $"{(string) App.Current.FindResource("Mods:UnpackingMelonLoader")}..."; diff --git a/CVRMelonAssistant/Classes/Mod.cs b/CVRMelonAssistant/Classes/Mod.cs index ed0ce2d..86d85f7 100644 --- a/CVRMelonAssistant/Classes/Mod.cs +++ b/CVRMelonAssistant/Classes/Mod.cs @@ -16,6 +16,7 @@ public class Mod public string installedVersion; public bool installedInBrokenDir; public bool installedInRetiredDir; + public int flag; public class ModVersion { @@ -38,4 +39,9 @@ public class ModVersion public bool IsPlugin => modType.Equals("plugin", StringComparison.InvariantCultureIgnoreCase); } } + public class FlagEntry + { + public int _id; + public int flag; + } } diff --git a/CVRMelonAssistant/Classes/Updater.cs b/CVRMelonAssistant/Classes/Updater.cs index ddeea9f..5bc3c3f 100644 --- a/CVRMelonAssistant/Classes/Updater.cs +++ b/CVRMelonAssistant/Classes/Updater.cs @@ -9,7 +9,7 @@ namespace CVRMelonAssistant { class Updater { - private static readonly string APILatestURL = "https://api.github.com/repos/knah/CVRMelonAssistant/releases/latest"; + private static readonly string APILatestURL = "https://api.github.com/repos/Nirv-git/CVRMelonAssistant/releases/latest"; private static Update LatestUpdate; private static Version CurrentVersion; diff --git a/CVRMelonAssistant/Classes/Utils.cs b/CVRMelonAssistant/Classes/Utils.cs index e4abaec..d765da7 100644 --- a/CVRMelonAssistant/Classes/Utils.cs +++ b/CVRMelonAssistant/Classes/Utils.cs @@ -26,6 +26,7 @@ public class Constants { public const string ChilloutVRAppId = "661130"; public const string CVRMGModsJson = "https://api.cvrmg.com/v1/mods"; + public const string CVRMGModsFlagsJson = "https://gist.githubusercontent.com/Nirv-git/1963e20d855c401349820a93b4d2639b/raw/cvrModFlags.json"; public const string WeebCDNAPIURL = "https://pat.assistant.moe/api/v1.0/"; public const string MD5Spacer = " "; public static readonly char[] IllegalCharacters = new char[] diff --git a/CVRMelonAssistant/Localisation/en.xaml b/CVRMelonAssistant/Localisation/en.xaml index 88436f1..4c150a5 100644 --- a/CVRMelonAssistant/Localisation/en.xaml +++ b/CVRMelonAssistant/Localisation/en.xaml @@ -38,7 +38,7 @@ ChilloutVR allows modding the game under certain conditions, specifically as long as mods are not used for evil. - ChilloutVR Modding Assistant and mods provided here are not associated with or endorsed by Alpha Blend Interactive (the devloper). + ChilloutVR Modding Assistant and mods provided here are not associated with or endorsed by ChilloutVR. Mods @@ -113,6 +113,8 @@ Failed to extract {0}, trying again in {1} seconds. ({2}/{3}) Failed to extract {0} after max attempts ({1}), skipping. This mod might not work properly so proceed at your own risk Search... + ★ - Recommend for most users ⓘ - Niche, make sure you understand what the mod does + About @@ -199,5 +201,5 @@ No description available Download link: Source code: - Internal ID: {0} {1} + Internal ID: {0} - {1} diff --git a/CVRMelonAssistant/ModInfoWindow.xaml b/CVRMelonAssistant/ModInfoWindow.xaml index 307bd32..3736d67 100644 --- a/CVRMelonAssistant/ModInfoWindow.xaml +++ b/CVRMelonAssistant/ModInfoWindow.xaml @@ -18,7 +18,7 @@ - + diff --git a/CVRMelonAssistant/ModInfoWindow.xaml.cs b/CVRMelonAssistant/ModInfoWindow.xaml.cs index 7df44b1..47a1dfb 100644 --- a/CVRMelonAssistant/ModInfoWindow.xaml.cs +++ b/CVRMelonAssistant/ModInfoWindow.xaml.cs @@ -1,6 +1,9 @@ using System; +using System.Text.RegularExpressions; using System.Windows; +using System.Windows.Controls; using System.Windows.Documents; +using static System.Windows.Forms.LinkLabel; namespace CVRMelonAssistant { @@ -16,6 +19,10 @@ public void SetMod(Mod mod) Title = string.Format((string) FindResource("ModInfoWindow:Title"), mod.versions[0].name); ModDescription.Text = mod.versions[0].description ?? (string) FindResource("ModInfoWindow:NoDescription"); + + var desc = mod.versions[0].description ?? (string)FindResource("ModInfoWindow:NoDescription"); + SetMarkdownText(ModDescription, desc); + ModName.Text = mod.versions[0].name; ModAuthor.Text = string.Format((string) FindResource("ModInfoWindow:Author"), mod.versions[0].author ?? FindResource("ModInfoWindow:NoAuthor")); ModVersion.Text = mod.versions[0].modVersion; @@ -39,9 +46,96 @@ public void SetMod(Mod mod) InternalIds.Text = string.Format((string) FindResource("ModInfoWindow:InternalIds"), mod._id, mod.versions[0]._version); } + private static readonly Regex Linkish = new( + @"\[(?[^\]]+)\]\((?https?://[^\s)]+)\)|<(?https?://[^>\s]+)>", + RegexOptions.Compiled | RegexOptions.IgnoreCase); + + private static readonly Regex Emphasis = new( + @"(?\*\*\*.+?\*\*\*|___.+?___)|(?\*\*.+?\*\*|__.+?__)|(?\*(?!\s).+?\*|_(?!\s).+?_)", + RegexOptions.Compiled); + + public void SetMarkdownText(TextBlock tb, string? text) + { + tb.Inlines.Clear(); + if (string.IsNullOrEmpty(text)) return; + + int last = 0; + foreach (Match m in Linkish.Matches(text)) + { + if (m.Index > last) + AddFormattedText(tb.Inlines, text.Substring(last, m.Index - last)); + + if (m.Groups["url"].Success) + { // [text](url) + var url = m.Groups["url"].Value; + var linkText = m.Groups["text"].Value; + var link = CreateHyperlink(linkText, url); + tb.Inlines.Add(link); + } + else + { // + string url = m.Groups["auto"].Value; + tb.Inlines.Add(CreateHyperlink(url)); + } + last = m.Index + m.Length; + } + + if (last < text.Length) + AddFormattedText(tb.Inlines, text.Substring(last)); + } + + // Parse **bold**, *italics*, and ***bold+italics*** + private static void AddFormattedText(InlineCollection inlines, string text) + { + int last = 0; + foreach (Match m in Emphasis.Matches(text)) + { + if (m.Index > last) + inlines.Add(new Run(text.Substring(last, m.Index - last))); + + Inline styled; + if (m.Groups["strongem"].Success) + { + string inner = StripOuter(m.Value, 3); + styled = new Run(inner) { FontWeight = FontWeights.Bold, FontStyle = FontStyles.Italic }; + } + else if (m.Groups["strong"].Success) + { + string inner = StripOuter(m.Value, 2); + styled = new Run(inner) { FontWeight = FontWeights.Bold }; + } + else // em + { + string inner = StripOuter(m.Value, 1); + styled = new Run(inner) { FontStyle = FontStyles.Italic }; + } + + inlines.Add(styled); + last = m.Index + m.Length; + } + + if (last < text.Length) + inlines.Add(new Run(text.Substring(last))); + } + + private static string StripOuter(string s, int n) + { + if (s.Length >= 2 * n) return s.Substring(n, s.Length - 2 * n); + return s; + } + private static Hyperlink CreateHyperlink(string uri) { - var link = new Hyperlink(new Run(uri)) {NavigateUri = new Uri(uri)}; + var link = new Hyperlink { NavigateUri = new Uri(uri) }; + link.Inlines.Add(new Run(uri)); + link.RequestNavigate += HyperlinkExtensions.Hyperlink_RequestNavigate; + return link; + } + + private static Hyperlink CreateHyperlink(string text, string uri) + { + var link = new Hyperlink { NavigateUri = new Uri(uri) }; + link.Inlines.Add(new Run(text)); link.RequestNavigate += HyperlinkExtensions.Hyperlink_RequestNavigate; return link; } diff --git a/CVRMelonAssistant/Pages/Mods.xaml b/CVRMelonAssistant/Pages/Mods.xaml index ed3e2ae..d2890a7 100644 --- a/CVRMelonAssistant/Pages/Mods.xaml +++ b/CVRMelonAssistant/Pages/Mods.xaml @@ -28,34 +28,45 @@ + Text="{DynamicResource Mods:SearchLabel}" + FontSize="20"/> + TextChanged="SearchBar_TextChanged" + FontSize="20"/> + MouseDoubleClick="ModsListView_OnMouseDoubleClick" + Margin="0,0,0,20" + ScrollViewer.CanContentScroll="True" + VirtualizingStackPanel.IsVirtualizing="True" + VirtualizingStackPanel.VirtualizationMode="Recycling" + VirtualizingPanel.IsVirtualizingWhenGrouping="True" + VirtualizingPanel.ScrollUnit="Pixel" + VirtualizingPanel.CacheLengthUnit="Item" + VirtualizingPanel.CacheLength="2"> + - +