");
+
+ ///
+ /// Regex matching a double-layer disc ringcode information
+ ///
+ private Regex ringCodeDoubleRegex = new Regex(@""); // Varies based on available fields, like Addtional Mould
+
+ ///
+ /// Regex matching a single-layer disc ringcode information
+ ///
+ private Regex ringCodeSingleRegex = new Regex(@""); // Varies based on available fields, like Addtional Mould
+
+ ///
+ /// Regex matching the serial field on a disc page
+ ///
+ private Regex serialRegex = new Regex(@"Serial (.*?) ");
+
+ ///
+ /// Regex matching the system field on a disc page
+ ///
+ private Regex systemRegex = new Regex(@"System ");
+
+ ///
+ /// Regex matching the title field on a disc page
+ ///
+ private Regex titleRegex = new Regex(@"(.*?) ");
+
+ ///
+ /// Regex matching the current nonce token for login
+ ///
+ private Regex tokenRegex = new Regex(@" ");
+
+ ///
+ /// Regex matching a single track on a disc page
+ ///
+ private Regex trackRegex = new Regex(@"(?.*?) (?.*?) (?.*?) (?.*?) (?.*?) (?.*?) (?.*?) (?.*?) (?.*?) ");
+
+ ///
+ /// Regex matching the track count on a disc page
+ ///
+ private Regex trackCountRegex = new Regex(@"Number of tracks (.*?) ");
+
+ ///
+ /// Regex matching the version field on a disc page
+ ///
+ private Regex versionRegex = new Regex(@"Version (.*?) ");
+
+ ///
+ /// Regex matching the write offset field on a disc page
+ ///
+ private Regex writeOffsetRegex = new Regex(@"Write offset (.*?) ");
+
+ #endregion
+
+ #region URLs
+
+ ///
+ /// Redump disc page URL template
+ ///
+ private const string discPageUrl = @"http://redump.org/disc/{0}/";
+
+ ///
+ /// Redump last modified search URL
+ ///
+ private const string lastModifiedUrl = @"http://redump.org/discs/sort/modified/dir/desc?page={0}";
+
+ ///
+ /// Redump login page URL
+ ///
+ private const string loginUrl = "http://forum.redump.org/login/";
+
+ ///
+ /// Redump CUE pack URL template
+ ///
+ private const string packCuesUrl = @"http://redump.org/cues/{0}/";
+
+ ///
+ /// Redump DAT pack URL template
+ ///
+ private const string packDatfileUrl = @"http://redump.org/datfile/{0}/";
+
+ ///
+ /// Redump DKEYS pack URL template
+ ///
+ private const string packDkeysUrl = @"http://redump.org/dkeys/{0}/";
+
+ ///
+ /// Redump GDI pack URL template
+ ///
+ private const string packGdiUrl = @"http://redump.org/gdi/{0}/";
+
+ ///
+ /// Redump KEYS pack URL template
+ ///
+ private const string packKeysUrl = @"http://redump.org/keys/{0}/";
+
+ ///
+ /// Redump LSD pack URL template
+ ///
+ private const string packLsdUrl = @"http://redump.org/lsd/{0}/";
+
+ ///
+ /// Redump SBI pack URL template
+ ///
+ private const string packSbiUrl = @"http://redump.org/sbi/{0}/";
+
+ ///
+ /// Redump quicksearch URL template
+ ///
+ private const string quickSearchUrl = @"http://redump.org/discs/quicksearch/{0}/?page={1}";
+
+ ///
+ /// Redump user dumps URL template
+ ///
+ private const string userDumpsUrl = @"http://redump.org/discs/dumper/{0}/?page={1}";
+
+ ///
+ /// Redump WIP disc page URL template
+ ///
+ private const string wipDiscPageUrl = @"http://redump.org/newdisc/{0}/";
+
+ ///
+ /// Redump WIP dumps queue URL
+ ///
+ private const string wipDumpsUrl = @"http://redump.org/discs-wip/";
+
+ #endregion
+
+ #region URL Extensions
+
+ private const string changesExt = "/changes/";
+ private const string cueExt = "cue/";
+ private const string gdiExt = "gdi/";
+ private const string keyExt = "key/";
+ private const string lsdExt = "lsd/";
+ private const string md5Ext = "md5/";
+ private const string sbiExt = "sbi/";
+ private const string sfvExt = "sfv/";
+ private const string sha1Ext = "sha1/";
+
+ #endregion
+
+ private readonly CookieContainer m_container = new CookieContainer();
+
+ ///
+ /// Determines if user is logged into Redump
+ ///
+ public bool LoggedIn { get; set; } = false;
+
+ ///
+ /// Determines if the user is a staff member
+ ///
+ public bool IsStaff { get; set; } = false;
+
+ ///
+ /// Get the last downloaded filename, if possible
+ ///
+ ///
+ public string GetLastFilename()
+ {
+ // Try to extract the filename from the Content-Disposition header
+ if (!String.IsNullOrEmpty(this.ResponseHeaders["Content-Disposition"]))
+ return this.ResponseHeaders["Content-Disposition"].Substring(this.ResponseHeaders["Content-Disposition"].IndexOf("filename=") + 9).Replace("\"", "");
+
+ return null;
+ }
+
+ protected override WebRequest GetWebRequest(Uri address)
+ {
+ WebRequest request = base.GetWebRequest(address);
+ HttpWebRequest webRequest = request as HttpWebRequest;
+ if (webRequest != null)
+ {
+ webRequest.CookieContainer = m_container;
+ }
+
+ return request;
+ }
+
+ #region Features
+
+ ///
+ /// Login to Redump, if possible
+ ///
+ /// Redump username
+ /// Redump password
+ /// True if the user could be logged in, false otherwise, false otherwise
+ public bool Login(string username, string password)
+ {
+ // Credentials verification
+ if (!string.IsNullOrWhiteSpace(username) && !string.IsNullOrWhiteSpace(password))
+ {
+ Console.WriteLine("Credentials entered, will attempt Redump login...");
+ return false;
+ }
+ else if (!string.IsNullOrWhiteSpace(username) && string.IsNullOrWhiteSpace(password))
+ {
+ Console.WriteLine("Only a username was specified, will not attempt Redump login...");
+ return false;
+ }
+ else if (string.IsNullOrWhiteSpace(username))
+ {
+ Console.WriteLine("No credentials entered, will not attempt Redump login...");
+ return false;
+ }
+
+ // Get the current token from the login page
+ var loginPage = DownloadString(loginUrl);
+ string token = this.tokenRegex.Match(loginPage).Groups[1].Value;
+
+ // Construct the login request
+ Headers[HttpRequestHeader.ContentType] = "application/x-www-form-urlencoded";
+ Encoding = Encoding.UTF8;
+ var response = UploadString(loginUrl, $"form_sent=1&redirect_url=&csrf_token={token}&req_username={username}&req_password={password}&save_pass=0");
+
+ if (response.Contains("Incorrect username and/or password."))
+ {
+ Console.WriteLine("Invalid credentials entered, continuing without logging in...");
+ return false;
+ }
+ else
+ {
+ Console.WriteLine("Credentials accepted! Logged into Redump...");
+ }
+
+ // If the user is a moderator or staff, set accordingly
+ if (response.Contains("http://forum.redump.org/forum/9/staff/"))
+ IsStaff = true;
+
+ return true;
+ }
+
+ ///
+ /// Download the last modified disc pages, until first failure
+ ///
+ /// Output directory to save data to
+ public void DownloadLastModified(string outDir)
+ {
+ // Keep getting last modified pages until there are none left
+ int pageNumber = 1;
+ while (true)
+ {
+ if (!CheckSingleSitePage(string.Format(lastModifiedUrl, pageNumber++), outDir, true))
+ break;
+ }
+ }
+
+ ///
+ /// Download the last submitted WIP disc pages
+ ///
+ /// Output directory to save data to
+ public void DownloadLastSubmitted(string outDir)
+ {
+ CheckSingleWIPPage(wipDumpsUrl, outDir, false);
+ }
+
+ ///
+ /// Download premade packs
+ ///
+ /// Output directory to save data to
+ /// True to use named subfolders to store downloads, false to store directly in the output directory
+ public void DownloadPacks(string outDir, bool useSubfolders)
+ {
+ this.DownloadPacks(packCuesUrl, Extensions.HasCues, "CUEs", outDir, useSubfolders ? "cue" : null);
+ this.DownloadPacks(packDatfileUrl, Extensions.HasDat, "DATs", outDir, useSubfolders ? "dat" : null);
+ this.DownloadPacks(packDkeysUrl, Extensions.HasDkeys, "Decrypted KEYS", outDir, useSubfolders ? "dkey" : null);
+ this.DownloadPacks(packGdiUrl, Extensions.HasGdi, "GDIs", outDir, useSubfolders ? "gdi" : null);
+ this.DownloadPacks(packKeysUrl, Extensions.HasKeys, "KEYS", outDir, useSubfolders ? "keys" : null);
+ this.DownloadPacks(packLsdUrl, Extensions.HasKeys, "LSD", outDir, useSubfolders ? "lsd" : null);
+ this.DownloadPacks(packSbiUrl, Extensions.HasSbi, "SBIs", outDir, useSubfolders ? "sbi" : null);
+ }
+
+ ///
+ /// Download premade packs for an individual system
+ ///
+ /// RedumpSystem to get all possible packs for
+ /// Output directory to save data to
+ /// True to use named subfolders to store downloads, false to store directly in the output directory
+ public void DownloadPacksForSystem(RedumpSystem system, string outDir, bool useSubfolders)
+ {
+ RedumpSystem?[] systemAsArray = new RedumpSystem?[] { system };
+
+ if (Extensions.HasCues.Contains(system))
+ this.DownloadPacks(packCuesUrl, systemAsArray, "CUEs", outDir, useSubfolders ? "cue" : null);
+
+ if (Extensions.HasDat.Contains(system))
+ this.DownloadPacks(packCuesUrl, Extensions.HasDat, "DATs", outDir, useSubfolders ? "dat" : null);
+
+ if (Extensions.HasDkeys.Contains(system))
+ this.DownloadPacks(packCuesUrl, Extensions.HasDkeys, "Decrypted KEYS", outDir, useSubfolders ? "dkey" : null);
+
+ if (Extensions.HasGdi.Contains(system))
+ this.DownloadPacks(packCuesUrl, Extensions.HasGdi, "GDIs", outDir, useSubfolders ? "gdi" : null);
+
+ if (Extensions.HasKeys.Contains(system))
+ this.DownloadPacks(packCuesUrl, Extensions.HasKeys, "KEYS", outDir, useSubfolders ? "keys" : null);
+
+ if (Extensions.HasLsd.Contains(system))
+ this.DownloadPacks(packCuesUrl, Extensions.HasKeys, "LSD", outDir, useSubfolders ? "lsd" : null);
+
+ if (Extensions.HasSbi.Contains(system))
+ this.DownloadPacks(packCuesUrl, Extensions.HasSbi, "SBIs", outDir, useSubfolders ? "sbi" : null);
+ }
+
+ ///
+ /// Download the disc pages associated with a given quicksearch query
+ ///
+ /// Query string to attempt to search for
+ public Dictionary DownloadSearchResults(string query)
+ {
+ Dictionary resultPages = new Dictionary();
+
+ // Strip quotes
+ query = query.Trim('"', '\'');
+
+ // Special characters become dashes
+ query = query.Replace(' ', '-');
+ query = query.Replace('/', '-');
+ query = query.Replace('\\', '/');
+
+ // Lowercase is defined per language
+ query = query.ToLowerInvariant();
+
+ // Keep getting quicksearch pages until there are none left
+ int pageNumber = 1;
+ while (true)
+ {
+ List pageIds = CheckSingleSitePage(string.Format(quickSearchUrl, query, pageNumber++));
+ foreach (int pageId in pageIds)
+ {
+ resultPages[pageId] = DownloadSingleSiteID(pageId);
+ }
+
+ if (pageIds.Count <= 1)
+ break;
+ }
+
+ return resultPages;
+ }
+
+ ///
+ /// Download the disc pages associated with a given quicksearch query
+ ///
+ /// Query string to attempt to search for
+ /// Output directory to save data to
+ public void DownloadSearchResults(string query, string outDir)
+ {
+ // Strip quotes
+ query = query.Trim('"', '\'');
+
+ // Special characters become dashes
+ query = query.Replace(' ', '-');
+ query = query.Replace('/', '-');
+ query = query.Replace('\\', '/');
+
+ // Lowercase is defined per language
+ query = query.ToLowerInvariant();
+
+ // Keep getting quicksearch pages until there are none left
+ int pageNumber = 1;
+ while (true)
+ {
+ if (!CheckSingleSitePage(string.Format(quickSearchUrl, query, pageNumber++), outDir, false))
+ break;
+ }
+ }
+
+ ///
+ /// Download the specified range of site disc pages
+ ///
+ /// Output directory to save data to
+ /// Starting ID for the range
+ /// Ending ID for the range (inclusive)
+ public void DownloadSiteRange(string outDir, int minId = 0, int maxId = 0)
+ {
+ if (!LoggedIn)
+ {
+ Console.WriteLine("Site download functionality is only available to Redump members");
+ return;
+ }
+
+ for (int id = minId; id <= maxId; id++)
+ {
+ if (DownloadSingleSiteID(id, outDir, true))
+ Thread.Sleep(5 * 1000); // Intentional sleep here so we don't flood the server
+ }
+ }
+
+ ///
+ /// Download the disc pages associated with the given user
+ ///
+ /// Username to check discs for
+ /// Output directory to save data to
+ public void DownloadUser(string username, string outDir)
+ {
+ if (!LoggedIn)
+ {
+ Console.WriteLine("User download functionality is only available to Redump members");
+ return;
+ }
+
+ // Keep getting user pages until there are none left
+ int pageNumber = 1;
+ while (true)
+ {
+ if (!CheckSingleSitePage(string.Format(userDumpsUrl, username, pageNumber++), outDir, false))
+ break;
+ }
+ }
+
+ ///
+ /// Download the specified range of WIP disc pages
+ ///
+ /// RedumpWebClient for all access
+ /// Output directory to save data to
+ /// Starting ID for the range
+ /// Ending ID for the range (inclusive)
+ public void DownloadWIPRange(string outDir, int minId = 0, int maxId = 0)
+ {
+ if (!LoggedIn || !IsStaff)
+ {
+ Console.WriteLine("WIP download functionality is only available to Redump moderators");
+ return;
+ }
+
+ for (int id = minId; id <= maxId; id++)
+ {
+ if (DownloadSingleWIPID(id, outDir, true))
+ Thread.Sleep(5 * 1000); // Intentional sleep here so we don't flood the server
+ }
+ }
+
+ ///
+ /// List the disc IDs associated with a given quicksearch query
+ ///
+ /// Query string to attempt to search for
+ /// All disc IDs for the given query, empty on error
+ public List ListSearchResults(string query)
+ {
+ List ids = new List();
+
+ // Strip quotes
+ query = query.Trim('"', '\'');
+
+ // Special characters become dashes
+ query = query.Replace(' ', '-');
+ query = query.Replace('/', '-');
+ query = query.Replace('\\', '/');
+
+ // Lowercase is defined per language
+ query = query.ToLowerInvariant();
+
+ // Keep getting quicksearch pages until there are none left
+ int pageNumber = 1;
+ while (true)
+ {
+ List pageIds = CheckSingleSitePage(string.Format(quickSearchUrl, query, pageNumber++));
+ ids.AddRange(pageIds);
+ if (pageIds.Count <= 1)
+ break;
+ }
+
+ return ids;
+ }
+
+ ///
+ /// List the disc IDs associated with the given user
+ ///
+ /// Username to check discs for
+ /// All disc IDs for the given user, empty on error
+ public List ListUser(RedumpWebClient wc, string username)
+ {
+ List ids = new List();
+
+ if (!wc.LoggedIn)
+ {
+ Console.WriteLine("User download functionality is only available to Redump members");
+ return ids;
+ }
+
+ // Keep getting user pages until there are none left
+ int pageNumber = 1;
+ while (true)
+ {
+ List pageIds = wc.CheckSingleSitePage(string.Format(userDumpsUrl, username, pageNumber++));
+ ids.AddRange(pageIds);
+ if (pageIds.Count <= 1)
+ break;
+ }
+
+ return ids;
+ }
+
+ #endregion
+
+ #region Single Page Helpers
+
+ ///
+ /// Process a Redump site page as a list of possible IDs or disc page
+ ///
+ /// Base URL to download using
+ /// List of IDs from the page, empty on error
+ internal List CheckSingleSitePage(string url)
+ {
+ List ids = new List();
+ var dumpsPage = DownloadString(url);
+
+ // If we have no dumps left
+ if (dumpsPage.Contains("No discs found."))
+ return ids;
+
+ // If we have a single disc page already
+ if (dumpsPage.Contains("Download: "))
+ {
+ var value = Regex.Match(dumpsPage, @"/disc/(\d+)/sfv/").Groups[1].Value;
+ if (int.TryParse(value, out int id))
+ ids.Add(id);
+
+ return ids;
+ }
+
+ // Otherwise, traverse each dump on the page
+ var matches = discRegex.Matches(dumpsPage);
+ foreach (Match match in matches)
+ {
+ try
+ {
+ if (int.TryParse(match.Groups[1].Value, out int value))
+ ids.Add(value);
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"An exception has occurred: {ex}");
+ continue;
+ }
+ }
+
+ return ids;
+ }
+
+ ///
+ /// Process a Redump site page as a list of possible IDs or disc page
+ ///
+ /// Base URL to download using
+ /// Output directory to save data to
+ /// True to return on first error, false otherwise
+ /// True if the page could be downloaded, false otherwise
+ internal bool CheckSingleSitePage(string url, string outDir, bool failOnSingle)
+ {
+ var dumpsPage = DownloadString(url);
+
+ // If we have no dumps left
+ if (dumpsPage.Contains("No discs found."))
+ return false;
+
+ // If we have a single disc page already
+ if (dumpsPage.Contains("Download: "))
+ {
+ var value = Regex.Match(dumpsPage, @"/disc/(\d+)/sfv/").Groups[1].Value;
+ if (int.TryParse(value, out int id))
+ {
+ bool downloaded = DownloadSingleSiteID(id, outDir, false);
+ if (!downloaded && failOnSingle)
+ return false;
+ }
+
+ return false;
+ }
+
+ // Otherwise, traverse each dump on the page
+ var matches = discRegex.Matches(dumpsPage);
+ foreach (Match match in matches)
+ {
+ try
+ {
+ if (int.TryParse(match.Groups[1].Value, out int value))
+ {
+ bool downloaded = DownloadSingleSiteID(value, outDir, false);
+ if (!downloaded && failOnSingle)
+ return false;
+ }
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"An exception has occurred: {ex}");
+ continue;
+ }
+ }
+
+ return true;
+ }
+
+ ///
+ /// Process a Redump WIP page as a list of possible IDs or disc page
+ ///
+ /// RedumpWebClient to access the packs
+ /// List of IDs from the page, empty on error
+ internal List CheckSingleWIPPage(string url)
+ {
+ List ids = new List();
+ var dumpsPage = DownloadString(url);
+
+ // If we have no dumps left
+ if (dumpsPage.Contains("No discs found."))
+ return ids;
+
+ // Otherwise, traverse each dump on the page
+ var matches = newDiscRegex.Matches(dumpsPage);
+ foreach (Match match in matches)
+ {
+ try
+ {
+ if (int.TryParse(match.Groups[1].Value, out int value))
+ ids.Add(value);
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"An exception has occurred: {ex}");
+ continue;
+ }
+ }
+
+ return ids;
+ }
+
+ ///
+ /// Process a Redump WIP page as a list of possible IDs or disc page
+ ///
+ /// RedumpWebClient to access the packs
+ /// Output directory to save data to
+ /// True to return on first error, false otherwise
+ /// True if the page could be downloaded, false otherwise
+ internal bool CheckSingleWIPPage(string url, string outDir, bool failOnSingle)
+ {
+ var dumpsPage = DownloadString(url);
+
+ // If we have no dumps left
+ if (dumpsPage.Contains("No discs found."))
+ return false;
+
+ // Otherwise, traverse each dump on the page
+ var matches = newDiscRegex.Matches(dumpsPage);
+ foreach (Match match in matches)
+ {
+ try
+ {
+ if (int.TryParse(match.Groups[1].Value, out int value))
+ {
+ bool downloaded = DownloadSingleWIPID(value, outDir, false);
+ if (!downloaded && failOnSingle)
+ return false;
+ }
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"An exception has occurred: {ex}");
+ continue;
+ }
+ }
+
+ return true;
+ }
+
+ #endregion
+
+ #region Download Helpers
+
+ ///
+ /// Download a single pack
+ ///
+ /// Base URL to download using
+ /// System to download packs for
+ /// Byte array containing the downloaded pack, null on error
+ internal byte[] DownloadSinglePack(string url, RedumpSystem? system)
+ {
+ try
+ {
+ return DownloadData(string.Format(url, system.ShortName()));
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"An exception has occurred: {ex}");
+ return null;
+ }
+ }
+
+ ///
+ /// Download a single pack
+ ///
+ /// Base URL to download using
+ /// System to download packs for
+ /// Output directory to save data to
+ /// Named subfolder for the pack, used optionally
+ internal void DownloadSinglePack(string url, RedumpSystem? system, string outDir, string subfolder)
+ {
+ try
+ {
+ string tempfile = Path.Combine(outDir, "tmp" + Guid.NewGuid().ToString());
+ DownloadFile(string.Format(url, system.ShortName()), tempfile);
+ MoveOrDelete(tempfile, GetLastFilename(), outDir, subfolder);
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"An exception has occurred: {ex}");
+ }
+ }
+
+ ///
+ /// Download an individual site ID data, if possible
+ ///
+ /// Redump disc ID to retrieve
+ /// String containing the page contents if successful, null on error
+ internal string DownloadSingleSiteID(int id)
+ {
+ string paddedId = id.ToString().PadLeft(5, '0');
+ Console.WriteLine($"Processing ID: {paddedId}");
+ try
+ {
+ string discPage = DownloadString(string.Format(discPageUrl, +id));
+ if (discPage.Contains($"Disc with ID \"{id}\" doesn't exist"))
+ {
+ Console.WriteLine($"ID {paddedId} could not be found!");
+ return null;
+ }
+
+ Console.WriteLine($"ID {paddedId} has been successfully downloaded");
+ return discPage;
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"An exception has occurred: {ex}");
+ return null;
+ }
+ }
+
+ ///
+ /// Download an individual site ID data, if possible
+ ///
+ /// Redump disc ID to retrieve
+ /// Output directory to save data to
+ /// True to rename deleted entries, false otherwise
+ /// True if all data was downloaded, false otherwise
+ internal bool DownloadSingleSiteID(int id, string outDir, bool rename)
+ {
+ string paddedId = id.ToString().PadLeft(5, '0');
+ string paddedIdDir = Path.Combine(outDir, paddedId);
+ Console.WriteLine($"Processing ID: {paddedId}");
+ try
+ {
+ string discPage = DownloadString(string.Format(discPageUrl, +id));
+ if (discPage.Contains($"Disc with ID \"{id}\" doesn't exist"))
+ {
+ try
+ {
+ if (rename)
+ {
+ if (Directory.Exists(paddedIdDir) && rename)
+ Directory.Move(paddedIdDir, paddedIdDir + "-deleted");
+ else
+ Directory.CreateDirectory(paddedIdDir + "-deleted");
+ }
+ }
+ catch { }
+
+ Console.WriteLine($"ID {paddedId} could not be found!");
+ return false;
+ }
+
+ // Check if the page has been updated since the last time it was downloaded, if possible
+ if (File.Exists(Path.Combine(paddedIdDir, "disc.html")))
+ {
+ // Read in the cached file
+ var oldDiscPage = File.ReadAllText(Path.Combine(paddedIdDir, "disc.html"));
+
+ // Check for the last modified date in both pages
+ var oldResult = lastModifiedRegex.Match(oldDiscPage);
+ var newResult = lastModifiedRegex.Match(discPage);
+
+ // If both pages contain the same modified date, skip it
+ if (oldResult.Success && newResult.Success && oldResult.Groups[1].Value == newResult.Groups[1].Value)
+ {
+ Console.WriteLine($"ID {paddedId} has not been changed since last download");
+ return false;
+ }
+
+ // If neither page contains a modified date, skip it
+ else if (!oldResult.Success && !newResult.Success)
+ {
+ Console.WriteLine($"ID {paddedId} has not been changed since last download");
+ return false;
+ }
+ }
+
+ // Create ID subdirectory
+ Directory.CreateDirectory(paddedIdDir);
+
+ // HTML
+ using (var discStreamWriter = File.CreateText(Path.Combine(paddedIdDir, "disc.html")))
+ {
+ discStreamWriter.Write(discPage);
+ }
+
+ DownloadFile(string.Format(discPageUrl, +id) + changesExt, Path.Combine(paddedIdDir, "changes.html"));
+
+ // CUE
+ if (discPage.Contains($"
+ /// Download an individual WIP ID data, if possible
+ ///
+ /// Redump WIP disc ID to retrieve
+ /// String containing the page contents if successful, null on error
+ internal string DownloadSingleWIPID(int id)
+ {
+ string paddedId = id.ToString().PadLeft(5, '0');
+ Console.WriteLine($"Processing ID: {paddedId}");
+ try
+ {
+ string discPage = DownloadString(string.Format(wipDiscPageUrl, +id));
+ if (discPage.Contains($"System \"{id}\" doesn't exist"))
+ {
+ Console.WriteLine($"ID {paddedId} could not be found!");
+ return null;
+ }
+
+ Console.WriteLine($"ID {paddedId} has been successfully downloaded");
+ return discPage;
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"An exception has occurred: {ex}");
+ return null;
+ }
+ }
+
+ ///
+ /// Download an individual WIP ID data, if possible
+ ///
+ /// Redump WIP disc ID to retrieve
+ /// Output directory to save data to
+ /// True to rename deleted entries, false otherwise
+ /// True if all data was downloaded, false otherwise
+ internal bool DownloadSingleWIPID(int id, string outDir, bool rename)
+ {
+ string paddedId = id.ToString().PadLeft(5, '0');
+ string paddedIdDir = Path.Combine(outDir, paddedId);
+ Console.WriteLine($"Processing ID: {paddedId}");
+ try
+ {
+ string discPage = DownloadString(string.Format(wipDiscPageUrl, +id));
+ if (discPage.Contains($"System \"{id}\" doesn't exist"))
+ {
+ try
+ {
+ if (rename)
+ {
+ if (Directory.Exists(paddedIdDir) && rename)
+ Directory.Move(paddedIdDir, paddedIdDir + "-deleted");
+ else
+ Directory.CreateDirectory(paddedIdDir + "-deleted");
+ }
+ }
+ catch { }
+
+ Console.WriteLine($"ID {paddedId} could not be found!");
+ return false;
+ }
+
+ // Check if the page has been updated since the last time it was downloaded, if possible
+ if (File.Exists(Path.Combine(paddedIdDir, "disc.html")))
+ {
+ // Read in the cached file
+ var oldDiscPage = File.ReadAllText(Path.Combine(paddedIdDir, "disc.html"));
+
+ // Check for the full match ID in both pages
+ var oldResult = fullMatchRegex.Match(oldDiscPage);
+ var newResult = fullMatchRegex.Match(discPage);
+
+ // If both pages contain the same ID, skip it
+ if (oldResult.Success && newResult.Success && oldResult.Groups[1].Value == newResult.Groups[1].Value)
+ {
+ Console.WriteLine($"ID {paddedId} has not been changed since last download");
+ return false;
+ }
+
+ // If neither page contains an ID, skip it
+ else if (!oldResult.Success && !newResult.Success)
+ {
+ Console.WriteLine($"ID {paddedId} has not been changed since last download");
+ return false;
+ }
+ }
+
+ // Create ID subdirectory
+ Directory.CreateDirectory(paddedIdDir);
+
+ // HTML
+ using (var discStreamWriter = File.CreateText(Path.Combine(paddedIdDir, "disc.html")))
+ {
+ discStreamWriter.Write(discPage);
+ }
+
+ Console.WriteLine($"ID {paddedId} has been successfully downloaded");
+ return true;
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"An exception has occurred: {ex}");
+ return false;
+ }
+ }
+
+ #endregion
+
+ #region Internal Helpers
+
+ ///
+ /// Download a set of packs
+ ///
+ /// Base URL to download using
+ /// Systems to download packs for
+ /// Name of the pack that is downloading
+ internal Dictionary DownloadPacks(string url, RedumpSystem?[] systems, string title)
+ {
+ var packsDictionary = new Dictionary();
+
+ // If we didn't have credentials
+ if (!LoggedIn)
+ systems = systems.Where(s => !Extensions.BannedSystems.Contains(s)).ToArray();
+
+ Console.WriteLine($"Downloading {title}");
+ foreach (var system in systems)
+ {
+ Console.Write($"\r{system.LongName()}{new string(' ', Console.BufferWidth - system.LongName().Length - 1)}");
+ byte[] pack = DownloadSinglePack(url, system);
+ if (pack != null)
+ packsDictionary.Add(system, pack);
+ }
+
+ Console.Write($"\rComplete!{new string(' ', Console.BufferWidth - 10)}");
+ Console.WriteLine();
+
+ return packsDictionary;
+ }
+
+ ///
+ /// Download a set of packs
+ ///
+ /// Base URL to download using
+ /// Systems to download packs for
+ /// Name of the pack that is downloading
+ /// Output directory to save data to
+ /// Named subfolder for the pack, used optionally
+ internal void DownloadPacks(string url, RedumpSystem?[] systems, string title, string outDir, string subfolder)
+ {
+ // If we didn't have credentials
+ if (!LoggedIn)
+ systems = systems.Where(s => !Extensions.BannedSystems.Contains(s)).ToArray();
+
+ Console.WriteLine($"Downloading {title}");
+ foreach (var system in systems)
+ {
+ Console.Write($"\r{system.LongName()}{new string(' ', Console.BufferWidth - system.LongName().Length - 1)}");
+ DownloadSinglePack(url, system, outDir, subfolder);
+ }
+
+ Console.Write($"\rComplete!{new string(' ', Console.BufferWidth - 10)}");
+ Console.WriteLine();
+ }
+
+ ///
+ /// Move a tempfile to a new name unless it aleady exists, in which case, delete the tempfile
+ ///
+ /// Path to existing temporary file
+ /// Path to new output file
+ /// Output directory to save data to
+ /// Optional subfolder to append to the path
+ private void MoveOrDelete(string tempfile, string newfile, string outDir, string subfolder)
+ {
+ if (!string.IsNullOrWhiteSpace(newfile))
+ {
+ if (!string.IsNullOrWhiteSpace(subfolder))
+ {
+ if (!Directory.Exists(Path.Combine(outDir, subfolder)))
+ Directory.CreateDirectory(Path.Combine(outDir, subfolder));
+
+ newfile = Path.Combine(subfolder, newfile);
+ }
+
+ if (File.Exists(Path.Combine(outDir, newfile)))
+ File.Delete(tempfile);
+ else
+ File.Move(tempfile, Path.Combine(outDir, newfile));
+ }
+ else
+ File.Delete(tempfile);
+ }
+
+ #endregion
+ }
+}
diff --git a/DICUI/ComboBoxItems/CategoryComboBoxItem.cs b/DICUI/ComboBoxItems/CategoryComboBoxItem.cs
index 0f207ab6..582f2bc3 100644
--- a/DICUI/ComboBoxItems/CategoryComboBoxItem.cs
+++ b/DICUI/ComboBoxItems/CategoryComboBoxItem.cs
@@ -1,5 +1,5 @@
-using DICUI.Data;
-using DICUI.Utilities;
+using DICUI.Utilities;
+using DICUI.Web;
namespace DICUI
{
diff --git a/DICUI/ComboBoxItems/LanguageComboBoxItem.cs b/DICUI/ComboBoxItems/LanguageComboBoxItem.cs
index c91f767b..4f84012e 100644
--- a/DICUI/ComboBoxItems/LanguageComboBoxItem.cs
+++ b/DICUI/ComboBoxItems/LanguageComboBoxItem.cs
@@ -1,5 +1,5 @@
-using DICUI.Data;
-using DICUI.Utilities;
+using DICUI.Utilities;
+using DICUI.Web;
namespace DICUI
{
diff --git a/DICUI/ComboBoxItems/RegionComboBoxItem.cs b/DICUI/ComboBoxItems/RegionComboBoxItem.cs
index 5450b62e..76e3c380 100644
--- a/DICUI/ComboBoxItems/RegionComboBoxItem.cs
+++ b/DICUI/ComboBoxItems/RegionComboBoxItem.cs
@@ -1,5 +1,5 @@
-using DICUI.Data;
-using DICUI.Utilities;
+using DICUI.Utilities;
+using DICUI.Web;
namespace DICUI
{
diff --git a/DICUI/Windows/DiscInformationWindow.xaml.cs b/DICUI/Windows/DiscInformationWindow.xaml.cs
index 66f9df20..0f8481cb 100644
--- a/DICUI/Windows/DiscInformationWindow.xaml.cs
+++ b/DICUI/Windows/DiscInformationWindow.xaml.cs
@@ -4,6 +4,7 @@ using System.Linq;
using System.Windows;
using System.Windows.Media;
using DICUI.Data;
+using DICUI.Web;
namespace DICUI.Windows
{
diff --git a/DICUI/Windows/MainWindow.xaml.cs b/DICUI/Windows/MainWindow.xaml.cs
index 8671b20b..6df41151 100644
--- a/DICUI/Windows/MainWindow.xaml.cs
+++ b/DICUI/Windows/MainWindow.xaml.cs
@@ -261,7 +261,7 @@ namespace DICUI.Windows
string version = $"{assemblyVersion.Major}.{assemblyVersion.Minor}" + (assemblyVersion.MajorRevision != 0 ? $".{assemblyVersion.MajorRevision}" : string.Empty);
// Get the latest tag from GitHub
- using (var client = new CookieAwareWebClient())
+ using (var client = new RedumpWebClient())
{
client.Headers["User-Agent"] = "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:64.0) Gecko/20100101 Firefox/64.0";
diff --git a/DICUI/Windows/OptionsWindow.xaml.cs b/DICUI/Windows/OptionsWindow.xaml.cs
index 8e7cad7b..3fe63ff6 100644
--- a/DICUI/Windows/OptionsWindow.xaml.cs
+++ b/DICUI/Windows/OptionsWindow.xaml.cs
@@ -123,10 +123,9 @@ namespace DICUI.Windows
///
private void OnRedumpTestClick(object sender, EventArgs e)
{
- using (CookieAwareWebClient wc = new CookieAwareWebClient())
+ using (RedumpWebClient wc = new RedumpWebClient())
{
- RedumpAccess access = new RedumpAccess();
- if (access.RedumpLogin(wc, RedumpUsernameTextBox.Text, RedumpPasswordBox.Password))
+ if (wc.Login(RedumpUsernameTextBox.Text, RedumpPasswordBox.Password))
System.Windows.MessageBox.Show("Redump login credentials accepted!", "Success", MessageBoxButton.OK, MessageBoxImage.Information);
else
System.Windows.MessageBox.Show("Redump login credentials denied!", "Failure", MessageBoxButton.OK, MessageBoxImage.Error);