using System.Collections; using System.Collections.Generic; using TMPro; using UnityEngine; using UnityEngine.Networking; using UnityEngine.UI; using Newtonsoft.Json; using System.Text.RegularExpressions; using System; using System.IO; public class DownloadMod : MonoBehaviour { [Header("\tResources")] [SerializeField] public OnlineAccountHandler OAH; [SerializeField] public Controller Con; [SerializeField] private ModList ML; [Header("\tImage handler")] [SerializeField] private ImportImages ImageCatcher; [SerializeField] private GameObject ImagePrefab; [SerializeField] public Transform ImageContainer; [SerializeField] public List ImagePrefabList; [Header("\tMod Info handler")] [SerializeField] private GameObject ModInfoPrefab; [SerializeField] private Transform ModInfoContainer; [SerializeField] private List ModInfoPrefabList; [Header("\tMod Download handler")] [SerializeField] private GameObject ModDownload; [SerializeField] private GameObject ModDownloadPrefab; [SerializeField] private GameObject GoToDownloadPagePrefab; [SerializeField] private Transform ModDownloadContainer; [SerializeField] private List ModDownloadPrefabList; [Header("\tBig Iage")] [SerializeField] private GameObject BigImageObject; [SerializeField] private RawImage BigImage; [Header("\tUser")] [SerializeField] private RawImage ModAuthorImage; [SerializeField] private string ModAuthorLink; [SerializeField] private TextMeshProUGUI AboutMe; [Header("\tMod")] [SerializeField] private string Version; [SerializeField] public string ModID; [SerializeField] private string FileID; [SerializeField] private TextMeshProUGUI Title; [SerializeField] private TextMeshProUGUI Description; [SerializeField] private string ChangeLog; [Header("\tOther")] [SerializeField] private GameObject UpdateImagesButton; [SerializeField] private List Share; [SerializeField] private Transform UserInfoContainer; [SerializeField] private string ModInfoDir; [SerializeField] private GameObject FileDownloadPrefab; [SerializeField] public SchakenMods CurrentJSON; void Start() { ModInfoDir = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData)+@"\Schaken-ModsĀ®\Singularity\Mods\"; if (!Directory.Exists(ModInfoDir)) { Directory.CreateDirectory(ModInfoDir); } } public void RemoveMe(string A) { for (int i = 0; i < ML.ModPrefabList.Count; i++) { if (ML.ModPrefabList[i].GetComponent().ID == A) { DestroyImmediate(ML.ModPrefabList[i]); ML.ModPrefabList.RemoveAt(i); } } } public void FillMe(SchakenMods A) { if (A != null) { CurrentJSON = A; if (A.Mod.Url.ToLower().Contains("nexusmods.com")) { UpdateImagesButton.SetActive(true); } else { UpdateImagesButton.SetActive(false); } Dictionary User = new(); Dictionary Mod = new(); Dictionary extra = new(); if (A.Mod.ScreenshotThumbnails != null) { // Debug.Log(A.Mod.ScreenshotThumbnails.Count+" Images"); for (int i = 0; i < A.Mod.ScreenshotThumbnails.Count; i++) { StartCoroutine(DownloadImage(A.Mod.ScreenshotThumbnails[i].Url, A.Mod.Screenshots[i].Url)); } } else { Debug.LogError("Screenshots are null"); } User.Add("Name", A.Mod.ModAuthor.Name); User.Add("Joined", FormatDate(A.Mod.ModAuthor.Joined.ToString())); User.Add("Posts", A.Mod.ModAuthor.Posts.ToString()); User.Add("Last Activity", FormatDate(A.Mod.ModAuthor.LastActivity.ToString())); User.Add("Profile Views", A.Mod.ModAuthor.ProfileViews.ToString()); User.Add("Reputation Points", A.Mod.ModAuthor.ReputationPoints.ToString()); User.Add("Achievements Points", A.Mod.ModAuthor.AchievementsPoints.ToString()); Mod.Add("category", A.Mod.ModCategory.Name); Mod.Add("Posted Date", FormatDate(A.Mod.Date.ToString())); Mod.Add("Last Updated", FormatDate(A.Mod.Updated.ToString())); Mod.Add("Last Downloaded", FormatDate(A.Mod.Downloaded.ToString())); Mod.Add("Version", A.Mod.Version.ToString()); Mod.Add("Downloads", A.Mod.Downloads.ToString()); Mod.Add("Comments", A.Mod.Comments.ToString()); Mod.Add("Reviews", A.Mod.Reviews.ToString()); Mod.Add("Views", A.Mod.Views.ToString()); Mod.Add("Hidden", A.Mod.Hidden.ToString()); if (A.Mod.IsPaid.ToString() == "true") { Mod.Add("Can Purchase", A.Mod.IsPurchasable.ToString()); Mod.Add("canBuy", A.Mod.CanBuy.ToString()); Mod.Add("price", A.Mod.Prices.ToString()); } // Mod.Add("Can Download", A.Mod.CanDownload.ToString()); Mod.Add("Rating", A.Mod.Rating.ToString()); extra.Add("id", A.Mod.Id.ToString()); // extra.Add("categoryUrl", A.Mod.ModCategory.Url.ToString()); extra.Add("url", A.Mod.Url.ToString()); extra.Add("primaryScreenshot", A.Mod.PrimaryScreenshot.Url.ToString()); Title.text = A.Mod.Title; Description.text = CleanString(A.Mod.Description); if (A.Mod.ModAuthor != null) { AboutMe.text = "Nothing here..."; if (A.Mod.ModAuthor.CustomFields != null) { if (A.Mod.ModAuthor.CustomFields.TryGetValue("1", out var customField1) && customField1 != null) { if (customField1.Fields.TryGetValue("1", out var field1) && field1 != null) { if (field1.Value != null) { AboutMe.text = CleanString(field1.Value.Replace("&", "&") ?? "Empty..."); } else { AboutMe.text = "Empty..."; } } } } } StartCoroutine(DownloadBigImage(A.Mod.ModAuthor.PhotoUrl, ModAuthorImage)); ClearList(ImagePrefabList); ClearList(ModInfoPrefabList); ClearList(ModDownloadPrefabList); foreach (KeyValuePair info in Mod) { GameObject NewInfo = Instantiate(ModInfoPrefab, ModInfoContainer); NewInfo.GetComponent().FillMe(info.Key, info.Value); ModInfoPrefabList.Add(NewInfo); } foreach (KeyValuePair user in User) { GameObject NewInfo = Instantiate(ModInfoPrefab, UserInfoContainer); NewInfo.GetComponent().FillMe(user.Key, user.Value); ModInfoPrefabList.Add(NewInfo); } Version = A.Mod.Version; ModID = A.Mod.Id.ToString(); ChangeLog = CleanString(A.Mod.Changelog ?? "Empty..."); if (A.Mod.ModAuthor != null) { ModAuthorLink = A.Mod.ModAuthor.ProfileUrl; } for (int i = 0; i < Share.Count; i++) { Share[i].ModURL = A.Mod.Url; Share[i].ModDescription = CleanString(A.Mod.Description).Replace("&", "&"); Share[i].ModImage = A.Mod.PrimaryScreenshot.Url; } } else { Debug.LogError("SchakenMods Object is null"); } } public void UpdateImages() { StartCoroutine(UpdateJustImages()); } private string S(string A) { return A; } IEnumerator UpdateJustImages() { Debug.Log("calling: "+CurrentJSON.Mod.Url); using (UnityWebRequest uwr = UnityWebRequest.Get(CurrentJSON.Mod.Url)) { yield return uwr.SendWebRequest(); if (uwr.result == UnityWebRequest.Result.ProtocolError) { Debug.LogError($"ERROR: {uwr.error}"); ImageCatcher.gameObject.SetActive(true); } else { string result = uwr.downloadHandler.text; if (!result.Contains("This mod contains adult content. You can turn adult content on in your preference, if you wish")) { string chunkString = result.Split(S("
    "))[0]; Debug.Log("Chunk: "+chunkString); string[] images = chunkString.Split(S("
  • ().FillMe(imageUrl, this); using (UnityWebRequest www = UnityWebRequestTexture.GetTexture(imageUrl)) { yield return www.SendWebRequest(); if (www.result != UnityWebRequest.Result.ConnectionError) { byte[] webPData = www.downloadHandler.data; if (webPData == null || webPData.Length == 0) { Debug.LogError("No WebP data received."); yield break; } WebpImporter.ByteWebpTexture2D(webPData, (texture) => { A.texture = texture; }); A.SetNativeSize(); A.SizeToParent(); if (!A.gameObject.activeSelf) { A.gameObject.SetActive(true); } } else { Debug.LogError("Error downloading image: " + www.error); } } if (!A.gameObject.activeSelf) { A.gameObject.SetActive(true); } } } } else { ImageCatcher.gameObject.SetActive(true); Debug.LogError("Adult Content Blocked"); // Here lets make a Violent Monkey script and add a custom http to put images in clipboard. grab them and load them into page. } } } } public void Download(string A, int B) { StartCoroutine(DownloadA(A, B)); } IEnumerator DownloadA(string id, int B) { FileID = id; if (B == 1) { // Schaken-Mods using (UnityWebRequest uwr = UnityWebRequest.Get($"https://schaken-mods.com/api/downloads/files/{id}/download")) { uwr.SetRequestHeader("Authorization", "Bearer " + OAH.BearerID); yield return uwr.SendWebRequest(); if (uwr.result == UnityWebRequest.Result.ProtocolError) { Debug.LogError($"ERROR: {uwr.error}"); } else { string result = uwr.downloadHandler.text; // Debug.Log(result); SchakenMods.VersionFilesResponse response = JsonConvert.DeserializeObject(result); List versions = response.Files; for (int i = 0; i < ModDownloadPrefabList.Count; i++) { DestroyImmediate(ModDownloadPrefabList[i]); } ModDownloadPrefabList.Clear(); for (int i = 0; i < versions.Count; i++) { GameObject NewFile = Instantiate(FileDownloadPrefab, ModDownloadContainer); NewFile.GetComponent().FillMe(versions[i].Name, BytesToString(versions[i].Size), versions[i].URL, this); ModDownloadPrefabList.Add(NewFile); } } } } else if (B == 2) { // Nexus string uri = $"https://api.nexusmods.com/v1/games/{CurrentJSON.Mod.ModCategory.Name}/mods/{CurrentJSON.Mod.Id}/files/{id}/download_link.json"; string ErrorMessage = "There was a problem communicating with Nexus, please visit their site"; using (UnityWebRequest uwr = UnityWebRequest.Get(uri)) { uwr.SetRequestHeader("accept", "application/json"); uwr.SetRequestHeader("apikey", OAH.NMKey); yield return uwr.SendWebRequest(); if (uwr.result == UnityWebRequest.Result.ConnectionError) { //Debug.LogError($"Error While Sending GetNexusUserIDA({ID}): {uwr.error}"); GoToNexusPage(ErrorMessage, CurrentJSON.Mod.Url+"?tab=files"); } else { string result = uwr.downloadHandler.text; // Debug.Log(result); if (!result.Contains("No File found for mod")) { List cdnInfos = JsonConvert.DeserializeObject>(result); for (int i = 0; i < ModDownloadPrefabList.Count; i++) { DestroyImmediate(ModDownloadPrefabList[i]); } ModDownloadPrefabList.Clear(); foreach (var cdnInfo in cdnInfos) { GameObject NewFile = Instantiate(FileDownloadPrefab, ModDownloadContainer); string FileName = ExtractFileNameFromUrl(cdnInfo.Url); NewFile.GetComponent().FillMe(cdnInfo.ShortName, "X", cdnInfo.Url, this, FileName); ModDownloadPrefabList.Add(NewFile); } } else { GoToNexusPage(ErrorMessage, CurrentJSON.Mod.Url+"?tab=files"); } } } } } public void DownloadFile(string A, string fileName) { Con.DownloadFile(A, fileName, ML.AddMod(CurrentJSON)); Close(); } private void Close() { gameObject.SetActive(false); ModDownload.SetActive(false); } private string BytesToString(long byteCount) { // string[] suf = { " B", " KB", " MB", " GB", " TB", " PB", " EB" }; //Longs run out around EB if (byteCount == 0) return "0" + suf[0]; long bytes = Math.Abs(byteCount); int place = Convert.ToInt32(Math.Floor(Math.Log(bytes, 1024))); double num = Math.Round(bytes / Math.Pow(1024, place), 1); return (Math.Sign(byteCount) * num).ToString() + suf[place]; } public string SanitizeFileName(string fileName) { string invalidChars = new string(Path.GetInvalidFileNameChars()) + new string(Path.GetInvalidPathChars()); foreach (char c in invalidChars) { fileName = fileName.Replace(c.ToString(), string.Empty); } return fileName; } public void VisitModAuthor() { Application.OpenURL(ModAuthorLink); } public void EnlargeImage(string A) { BigImage.gameObject.SetActive(false); StartCoroutine(DownloadBigImage(A, BigImage)); BigImageObject.SetActive(true); } public void ViewDownloads() { StartCoroutine(GetVersionsA()); } IEnumerator GetVersionsA() { for (int i = 0; i < ModDownloadPrefabList.Count; i++) { DestroyImmediate(ModDownloadPrefabList[i]); } ModDownloadPrefabList.Clear(); if (CurrentJSON.Mod.Url.ToLower().Contains("schaken-mods.com")) { using (UnityWebRequest uwr = UnityWebRequest.Get($"https://schaken-mods.com/api/downloads/files/{CurrentJSON.Mod.Id}/history")) { GameObject NewDownload2 = Instantiate(ModDownloadPrefab, ModDownloadContainer); NewDownload2.GetComponent().FillMe(ModID, Version, ChangeLog, this); ModDownloadPrefabList.Add(NewDownload2); uwr.SetRequestHeader("Authorization", "Bearer " + OAH.BearerID); yield return uwr.SendWebRequest(); if (uwr.result == UnityWebRequest.Result.ConnectionError) { Debug.LogError("Error While Sending TEST: " + uwr.error); } else { string result = uwr.downloadHandler.text; // Debug.Log(result); List versions = JsonConvert.DeserializeObject>(result); for (int i = 0; i < versions.Count; i++) { GameObject NewDownload = Instantiate(ModDownloadPrefab, ModDownloadContainer); ModDownloadPrefabList.Add(NewDownload); string A = CleanString(versions[i].Changelog); string Header = $"Version: {versions[i].Version} Changelog"; NewDownload.GetComponent().FillMe(versions[i].Id.ToString(), Header, A, this); } } } } else if (CurrentJSON.Mod.Url.ToLower().Contains("nexusmods.com")) { string Message = "Nexus requires you to have premium membership to directly download using their API. Please click the button below to open the download page and select to download using Singularity."; string ErrorMessage = "It seems NexusMods ia not responding correctly, please continue using the browser."; if (OAH.NMSettings != null) { if (OAH.NMSettings.IsSupporter) { // Lets get the API for the Download string uri = $"https://api.nexusmods.com/v1/games/{CurrentJSON.Mod.ModCategory.Name}/mods/{CurrentJSON.Mod.Id}/files.json"; UnityWebRequest uwr = UnityWebRequest.Get(uri); uwr.SetRequestHeader("accept", "application/json"); uwr.SetRequestHeader("apikey", OAH.NMKey); yield return uwr.SendWebRequest(); if (uwr.result == UnityWebRequest.Result.ConnectionError) { //Debug.LogError($"Error While Sending GetNexusUserIDA({ID}): {uwr.error}"); GoToNexusPage(ErrorMessage, CurrentJSON.Mod.Url+"?tab=files"); } else { string result = uwr.downloadHandler.text; // Assuming the JSON response is stored in the 'result' variable NexusMods response = JsonConvert.DeserializeObject(result); // Debug.Log(result); List files = response.NMFiles; for (int i = 0; i < files.Count; i++) { if (files[i].CategoryName != null) { GameObject NewDownload = Instantiate(ModDownloadPrefab, ModDownloadContainer); ModDownloadPrefabList.Add(NewDownload); string A = CleanString(files[i].ChangelogHtml); if (A == "") { A = "No descriotion given."; } NewDownload.GetComponent().FillMe(files[i].FileId.ToString(), files[i].Name, "Version: "+files[i].Version+" - "+A, this, 2); } } } } else { GoToNexusPage(Message, CurrentJSON.Mod.Url+"?tab=files"); } } else { GoToNexusPage(Message, CurrentJSON.Mod.Url+"?tab=files"); } } } private void GoToNexusPage(string Message, string Link) { GameObject NewPrefab = Instantiate(GoToDownloadPagePrefab, ModDownloadContainer); NewPrefab.GetComponent().FillMe(Message, Link); ModDownloadPrefabList.Add(NewPrefab); } IEnumerator DownloadBigImage(string imageUrl, RawImage A) { // Debug.Log(imageUrl); if (imageUrl.Contains("nexusmods.com")) { using (UnityWebRequest www = UnityWebRequestTexture.GetTexture(imageUrl)) { yield return www.SendWebRequest(); if (www.result != UnityWebRequest.Result.ConnectionError) { byte[] webPData = www.downloadHandler.data; if (webPData == null || webPData.Length == 0) { Debug.LogError("No WebP data received."); yield break; } WebpImporter.ByteWebpTexture2D(webPData, (texture) => { A.texture = texture; }); A.SetNativeSize(); A.SizeToParent(); if (!A.gameObject.activeSelf) { A.gameObject.SetActive(true); } } else { Debug.LogError("Error downloading image: " + www.error); } } } else { using (UnityWebRequest www = UnityWebRequestTexture.GetTexture(imageUrl)) { yield return www.SendWebRequest(); if (www.result != UnityWebRequest.Result.ConnectionError) { A.texture = DownloadHandlerTexture.GetContent(www); A.SetNativeSize(); A.SizeToParent(); if (!A.gameObject.activeSelf) { A.gameObject.SetActive(true); } } else { Debug.LogError("Error downloading image: " + www.error); } } } } IEnumerator DownloadImage(string imageUrl, string FullURL) { if (imageUrl.Contains("nexusmods.com")) { using (UnityWebRequest www = UnityWebRequestTexture.GetTexture(imageUrl)) { yield return www.SendWebRequest(); if (www.result != UnityWebRequest.Result.ConnectionError) { GameObject NewImage = Instantiate(ImagePrefab, ImageContainer); ImagePrefabList.Add(NewImage); RawImage A = NewImage.GetComponent().FillMe(FullURL, this); byte[] webPData = www.downloadHandler.data; if (webPData == null || webPData.Length == 0) { Debug.LogError("No WebP data received."); yield break; } WebpImporter.ByteWebpTexture2D(webPData, (texture) => { A.texture = texture; }); A.SetNativeSize(); A.SizeToParent(); if (!A.gameObject.activeSelf) { A.gameObject.SetActive(true); } } else { Debug.LogError("Error downloading image: " + www.error); } } } else { using (UnityWebRequest www = UnityWebRequestTexture.GetTexture(imageUrl)) { yield return www.SendWebRequest(); if (www.result != UnityWebRequest.Result.ConnectionError) { GameObject NewImage = Instantiate(ImagePrefab, ImageContainer); ImagePrefabList.Add(NewImage); RawImage A = NewImage.GetComponent().FillMe(FullURL, this); A.texture = DownloadHandlerTexture.GetContent(www); A.SetNativeSize(); A.SizeToParent(); } else { Debug.LogError("Error downloading image: " + www.error); } } } } public void UpdateImagesJSON() { List Screenshots = new(); List ScreenshotThumbs = new(); string file = ""; for (int i = 0; i < ImagePrefabList.Count; i++) { string Image = ImagePrefabList[i].GetComponent().FullImage; Texture2D texture = ImagePrefabList[i].GetComponent().Image.texture as Texture2D; long ImageSize = 0; if (texture != null) { byte[] rawData = texture.GetRawTextureData(); ImageSize = rawData.Length; } else { Debug.LogError("The texture is not a Texture2D."); } if (Image.ToLower().Contains("nexusmods.com")) { SchakenMods.Screenshot NewScreenShot = new() { Url = Image, Name = "NewScreenshot", Size = ImageSize }; SchakenMods.ScreenshotThumbnail NewScreenShotThumb = new() { Url = Image.Split(S("images"))[0]+"images/thumbnails"+Image.Split(S("images"))[1], Name = "NewScreenshot" }; Screenshots.Add(NewScreenShot); ScreenshotThumbs.Add(NewScreenShotThumb); file = ModInfoDir+$"\\NM{CurrentJSON.Mod.Id}.sing"; } } CurrentJSON.Mod.Screenshots = Screenshots; CurrentJSON.Mod.ScreenshotThumbnails = ScreenshotThumbs; UpdateJSON(CurrentJSON, file); } public void UpdateJSON(SchakenMods SM, string filePath) { string prettyJson = JsonConvert.SerializeObject(SM.Mod, Formatting.Indented); if (File.Exists(filePath)) { File.Delete(filePath); } File.WriteAllText(filePath, prettyJson); } public void CheckForModUpdates(SchakenMods SM, ModListPrefab MLPrefab) { ML.CheckForModUpdates(SM, MLPrefab); } private string CleanString (string A) { if (A != string.Empty) { A = Regex.Replace(A ?? "", @"^\s*$\n|\r", string.Empty, RegexOptions.Multiline); A = Regex.Replace(A, @"<[^>]*>", string.Empty); } return A; } private string FormatDate(string dateString) { return dateString.Split(" ")[0]; } private void ClearList(List A) { for (int i = 0; i < A.Count; i++) { DestroyImmediate(A[i]); } A.Clear(); } public string ExtractFileNameFromUrl(string url) { Uri uri = new(url); string path = uri.AbsolutePath; string pattern = @"[^/]+$"; Match match = Regex.Match(path, pattern); return match.Success ? match.Value : string.Empty; } }