コード例 #1
0
ファイル: AppInfo.cs プロジェクト: paddymahoney/depressurizer
        public int Parent; // 0 if none

        public AppInfo( int id, string name = null, AppTypes type = AppTypes.Unknown, AppPlatforms platforms = AppPlatforms.All ) {
            this.Id = id;
            this.Name = name;
            this.AppType = type;

            this.Platforms = platforms;
        }
コード例 #2
0
        public int Parent; // 0 if none

        public AppInfo(int id, string name = null, AppTypes type = AppTypes.Unknown, AppPlatforms platforms = AppPlatforms.All)
        {
            this.Id      = id;
            this.Name    = name;
            this.AppType = type;

            this.Platforms = platforms;
        }
コード例 #3
0
ファイル: AppInfo.cs プロジェクト: Thundernerd/Depressurizer
        public AppInfo(int id, string name = null, AppTypes type = AppTypes.Unknown, AppPlatforms platforms = AppPlatforms.All)
        {
            Id      = id;
            Name    = name;
            AppType = type;

            Platforms = platforms;
        }
コード例 #4
0
        public void DifferentLanguagesShouldHaveSamePlatforms()
        {
            const AppPlatforms supportedPlatforms = AppPlatforms.All;

            _entry1.Platforms.Should().Be(supportedPlatforms);
            _entry2.Platforms.Should().Be(supportedPlatforms);
            _entry3.Platforms.Should().Be(supportedPlatforms);
        }
コード例 #5
0
 public void AppPlatformsTest()
 {
     // get test
     Assert.That(AppPlatforms.Ios.ToString(), Is.EqualTo("ios"));
     Assert.That(AppPlatforms.Android.ToString(), Is.EqualTo("android"));
     Assert.That(AppPlatforms.WinPhone.ToString(), Is.EqualTo("winphone"));
     Assert.That(AppPlatforms.Web.ToString(), Is.EqualTo("web"));
     // parse test
     Assert.That(AppPlatforms.FromJsonString("ios"), Is.EqualTo(AppPlatforms.Ios));
     Assert.That(AppPlatforms.FromJsonString("android"), Is.EqualTo(AppPlatforms.Android));
     Assert.That(AppPlatforms.FromJsonString("winphone"), Is.EqualTo(AppPlatforms.WinPhone));
     Assert.That(AppPlatforms.FromJsonString("web"), Is.EqualTo(AppPlatforms.Web));
 }
コード例 #6
0
        public void AppPlatformsTest()
        {
            // get test
            Assert.That(actual: AppPlatforms.Ios.ToString(), expression: Is.EqualTo(expected: "ios"));
            Assert.That(actual: AppPlatforms.Android.ToString(), expression: Is.EqualTo(expected: "android"));
            Assert.That(actual: AppPlatforms.WinPhone.ToString(), expression: Is.EqualTo(expected: "winphone"));
            Assert.That(actual: AppPlatforms.Web.ToString(), expression: Is.EqualTo(expected: "web"));

            // parse test
            Assert.That(actual: AppPlatforms.FromJsonString(response: "ios"), expression: Is.EqualTo(expected: AppPlatforms.Ios));
            Assert.That(actual: AppPlatforms.FromJsonString(response: "android"), expression: Is.EqualTo(expected: AppPlatforms.Android));
            Assert.That(actual: AppPlatforms.FromJsonString(response: "winphone"), expression: Is.EqualTo(expected: AppPlatforms.WinPhone));
            Assert.That(actual: AppPlatforms.FromJsonString(response: "web"), expression: Is.EqualTo(expected: AppPlatforms.Web));
        }
コード例 #7
0
        public override AutoCatResult CategorizeGame(GameInfo game, Filter filter)
        {
            if (games == null)
            {
                Logger.Error(GlobalStrings.Log_AutoCat_GamelistNull);

                throw new ApplicationException(GlobalStrings.AutoCatGenre_Exception_NoGameList);
            }

            if (game == null)
            {
                Logger.Error(GlobalStrings.Log_AutoCat_GameNull);

                return(AutoCatResult.Failure);
            }

            if (!game.IncludeGame(filter))
            {
                return(AutoCatResult.Filtered);
            }

            if (!db.Contains(game.Id) || (db.Games[game.Id].LastStoreScrape == 0))
            {
                return(AutoCatResult.NotInDatabase);
            }

            AppPlatforms platforms = db.Games[game.Id].Platforms;

            if (Windows && ((platforms & AppPlatforms.Windows) != 0))
            {
                game.AddCategory(games.GetCategory(GetProcessedString("Windows")));
            }

            if (Windows && ((platforms & AppPlatforms.Mac) != 0))
            {
                game.AddCategory(games.GetCategory(GetProcessedString("Mac")));
            }

            if (Windows && ((platforms & AppPlatforms.Linux) != 0))
            {
                game.AddCategory(games.GetCategory(GetProcessedString("Linux")));
            }

            if (Windows && ((platforms & AppPlatforms.Linux) != 0))
            {
                game.AddCategory(games.GetCategory(GetProcessedString("SteamOS")));
            }

            return(AutoCatResult.Success);
        }
コード例 #8
0
        /// <inheritdoc />
        public override AutoCatResult CategorizeGame(GameInfo game, Filter filter)
        {
            if (games == null)
            {
                Logger.Error(GlobalStrings.Log_AutoCat_GamelistNull);
                throw new ApplicationException(GlobalStrings.AutoCatGenre_Exception_NoGameList);
            }

            if (game == null)
            {
                Logger.Error(GlobalStrings.Log_AutoCat_GameNull);
                return(AutoCatResult.Failure);
            }

            if (!game.IncludeGame(filter))
            {
                return(AutoCatResult.Filtered);
            }

            if (!db.Contains(game.Id, out DatabaseEntry entry) || entry.LastStoreScrape == 0)
            {
                return(AutoCatResult.NotInDatabase);
            }

            AppPlatforms platforms = entry.Platforms;

            if (Windows && platforms.HasFlag(AppPlatforms.Windows))
            {
                game.AddCategory(games.GetCategory(GetCategoryName("Windows")));
            }

            if (Mac && platforms.HasFlag(AppPlatforms.Mac))
            {
                game.AddCategory(games.GetCategory(GetCategoryName("Mac")));
            }

            if (Linux && platforms.HasFlag(AppPlatforms.Linux))
            {
                game.AddCategory(games.GetCategory(GetCategoryName("Linux")));
            }

            if (Linux && platforms.HasFlag(AppPlatforms.Linux))
            {
                game.AddCategory(games.GetCategory(GetCategoryName("SteamOS")));
            }

            return(AutoCatResult.Success);
        }
コード例 #9
0
        /// <summary>
        /// Merges in data from another entry. Useful for merging scrape results, but could also merge data from a different database.
        /// Uses newer data when there is a conflict.
        /// Does NOT perform deep copies of list fields.
        /// </summary>
        /// <param name="other">GameDBEntry containing info to be merged into this entry.</param>
        public void MergeIn(GameDBEntry other)
        {
            bool useAppInfoFields    = other.LastAppInfoUpdate > this.LastAppInfoUpdate || (this.LastAppInfoUpdate == 0 && other.LastStoreScrape >= this.LastStoreScrape);
            bool useScrapeOnlyFields = other.LastStoreScrape >= this.LastStoreScrape;

            if (other.AppType != AppTypes.Unknown && (this.AppType == AppTypes.Unknown || useAppInfoFields))
            {
                this.AppType = other.AppType;
            }

            if (other.LastAppInfoUpdate >= this.LastAppInfoUpdate)
            {
                this.Platforms = other.Platforms;
            }

            if (useAppInfoFields)
            {
                if (!string.IsNullOrEmpty(other.Name))
                {
                    Name = other.Name;
                }
                if (other.ParentId > 0)
                {
                    ParentId = other.ParentId;
                }
            }

            if (useScrapeOnlyFields)
            {
                if (other.Genres != null && other.Genres.Count > 0)
                {
                    Genres = other.Genres;
                }
                if (other.Flags != null && other.Flags.Count > 0)
                {
                    Flags = other.Flags;
                }
                if (other.Tags != null && other.Tags.Count > 0)
                {
                    Tags = other.Tags;
                }
                if (other.Developers != null && other.Developers.Count > 0)
                {
                    Developers = other.Developers;
                }
                if (other.Publishers != null && other.Publishers.Count > 0)
                {
                    Publishers = other.Publishers;
                }
                if (!string.IsNullOrEmpty(other.SteamReleaseDate))
                {
                    SteamReleaseDate = other.SteamReleaseDate;
                }

                if (other.ReviewTotal != 0)
                {
                    this.ReviewTotal = other.ReviewTotal;
                    this.ReviewPositivePercentage = other.ReviewPositivePercentage;
                }

                if (!string.IsNullOrEmpty(other.MC_Url))
                {
                    MC_Url = other.MC_Url;
                }
            }

            if (other.LastStoreScrape > this.LastStoreScrape)
            {
                this.LastStoreScrape = other.LastStoreScrape;
            }
            if (other.LastAppInfoUpdate > this.LastAppInfoUpdate)
            {
                this.LastAppInfoUpdate = other.LastAppInfoUpdate;
            }
        }
コード例 #10
0
        public static AppInfo FromVdfNode(VdfFileNode commonNode)
        {
            if (commonNode == null || commonNode.NodeType != ValueType.Array)
            {
                return(null);
            }

            AppInfo result = null;

            VdfFileNode idNode = commonNode.GetNodeAt(new string[] { "gameid" }, false);
            int         id     = -1;

            if (idNode != null)
            {
                if (idNode.NodeType == ValueType.Int)
                {
                    id = idNode.NodeInt;
                }
                else if (idNode.NodeType == ValueType.String)
                {
                    if (!int.TryParse(idNode.NodeString, out id))
                    {
                        id = -1;
                    }
                }
            }

            if (id >= 0)
            {
                // Get name
                string      name     = null;
                VdfFileNode nameNode = commonNode.GetNodeAt(new string[] { "name" }, false);
                if (nameNode != null)
                {
                    name = nameNode.NodeData.ToString();
                }

                // Get type
                string      typeStr  = null;
                AppTypes    type     = AppTypes.Unknown;
                VdfFileNode typeNode = commonNode.GetNodeAt(new string[] { "type" }, false);
                if (typeNode != null)
                {
                    typeStr = typeNode.NodeData.ToString();
                }

                if (typeStr != null)
                {
                    if (!Enum.TryParse <AppTypes>(typeStr, true, out type))
                    {
                        type = AppTypes.Other;
                    }
                }

                // Get platforms
                string       oslist     = null;
                AppPlatforms platforms  = AppPlatforms.None;
                VdfFileNode  oslistNode = commonNode.GetNodeAt(new string[] { "oslist" }, false);
                if (oslistNode != null)
                {
                    oslist = oslistNode.NodeData.ToString();
                    if (oslist.IndexOf("windows", StringComparison.OrdinalIgnoreCase) != -1)
                    {
                        platforms |= AppPlatforms.Windows;
                    }
                    if (oslist.IndexOf("mac", StringComparison.OrdinalIgnoreCase) != -1)
                    {
                        platforms |= AppPlatforms.Mac;
                    }
                    if (oslist.IndexOf("linux", StringComparison.OrdinalIgnoreCase) != -1)
                    {
                        platforms |= AppPlatforms.Linux;
                    }
                }

                result = new AppInfo(id, name, type, platforms);

                // Get parent
                VdfFileNode parentNode = commonNode.GetNodeAt(new string[] { "parent" }, false);
                if (parentNode != null)
                {
                    result.Parent = parentNode.NodeInt;
                }
            }
            return(result);
        }
コード例 #11
0
        private void GetAllDataFromPage(string page)
        {
            // Genres
            Match m = RegexGenre.Match(page);

            if (m.Success)
            {
                Genres = new List <string>();
                foreach (Capture cap in m.Groups[2].Captures)
                {
                    Genres.Add(cap.Value);
                }
            }

            // Flags
            MatchCollection matches = RegexFlags.Matches(page);

            if (matches.Count > 0)
            {
                Flags = new List <string>();
                foreach (Match ma in matches)
                {
                    string flag = ma.Groups[1].Value;
                    if (!string.IsNullOrWhiteSpace(flag))
                    {
                        Flags.Add(flag);
                    }
                }
            }

            //Tags
            matches = RegexTags.Matches(page);
            if (matches.Count > 0)
            {
                Tags = new List <string>();
                foreach (Match ma in matches)
                {
                    string tag = WebUtility.HtmlDecode(ma.Groups[1].Value.Trim());
                    if (!string.IsNullOrWhiteSpace(tag))
                    {
                        Tags.Add(tag);
                    }
                }
            }

            //Get VR Support headsets
            m = RegexVrSupportHeadsetsSection.Match(page);
            if (m.Success)
            {
                matches            = RegexVrSupportFlagMatch.Matches(m.Groups[1].Value.Trim());
                VrSupport.Headsets = new List <string>();
                foreach (Match ma in matches)
                {
                    string headset = WebUtility.HtmlDecode(ma.Groups[1].Value.Trim());
                    if (!string.IsNullOrWhiteSpace(headset))
                    {
                        VrSupport.Headsets.Add(headset);
                    }
                }
            }

            //Get VR Support Input
            m = RegexVrSupportInputSection.Match(page);
            if (m.Success)
            {
                matches         = RegexVrSupportFlagMatch.Matches(m.Groups[1].Value.Trim());
                VrSupport.Input = new List <string>();
                foreach (Match ma in matches)
                {
                    string input = WebUtility.HtmlDecode(ma.Groups[1].Value.Trim());
                    if (!string.IsNullOrWhiteSpace(input))
                    {
                        VrSupport.Input.Add(input);
                    }
                }
            }

            //Get VR Support Play Area
            m = RegexVrSupportPlayAreaSection.Match(page);
            if (m.Success)
            {
                matches            = RegexVrSupportFlagMatch.Matches(m.Groups[1].Value.Trim());
                VrSupport.PlayArea = new List <string>();
                foreach (Match ma in matches)
                {
                    string playArea = WebUtility.HtmlDecode(ma.Groups[1].Value.Trim());
                    if (!string.IsNullOrWhiteSpace(playArea))
                    {
                        VrSupport.PlayArea.Add(playArea);
                    }
                }
            }

            //Get Language Support
            matches = RegexLanguageSupport.Matches(page);
            if (matches.Count > 0)
            {
                LanguageSupport = new LanguageSupport
                {
                    Interface = new List <string>(),
                    FullAudio = new List <string>(),
                    Subtitles = new List <string>()
                };

                foreach (Match ma in matches)
                {
                    string language = WebUtility.HtmlDecode(ma.Groups[1].Value.Trim());
                    if (language.StartsWith("#lang") || language.StartsWith("("))
                    {
                        continue;                         //Some store pages on steam are bugged.
                    }

                    if (WebUtility.HtmlDecode(ma.Groups[2].Value.Trim()) != "")                     //Interface
                    {
                        LanguageSupport.Interface.Add(language);
                    }

                    if (WebUtility.HtmlDecode(ma.Groups[3].Value.Trim()) != "")                     //Full Audio
                    {
                        LanguageSupport.FullAudio.Add(language);
                    }

                    if (WebUtility.HtmlDecode(ma.Groups[4].Value.Trim()) != "")                     //Subtitles
                    {
                        LanguageSupport.Subtitles.Add(language);
                    }
                }
            }

            //Get Achievement number
            m = RegexAchievements.Match(page);
            if (m.Success)
            {
                //sometimes games have achievements but don't have the "Steam Achievements" flag in the store
                if (!Flags.Contains("Steam Achievements"))
                {
                    Flags.Add("Steam Achievements");
                }

                if (int.TryParse(m.Groups[1].Value, out int num))
                {
                    TotalAchievements = num;
                }
            }

            // Get Developer
            m = RegexDevelopers.Match(page);
            if (m.Success)
            {
                Developers = new List <string>();
                foreach (Capture cap in m.Groups[3].Captures)
                {
                    Developers.Add(WebUtility.HtmlDecode(cap.Value));
                }
            }

            // Get Publishers
            m = RegexPublishers.Match(page);
            if (m.Success)
            {
                Publishers = new List <string>();
                foreach (Capture cap in m.Groups[3].Captures)
                {
                    Publishers.Add(WebUtility.HtmlDecode(cap.Value));
                }
            }

            // Get release date
            m = RegexReleaseDate.Match(page);
            if (m.Success)
            {
                SteamReleaseDate = m.Groups[1].Captures[0].Value;
            }

            // Get user review data
            m = RegexReviews.Match(page);
            if (m.Success)
            {
                if (int.TryParse(m.Groups[1].Value, out int num))
                {
                    ReviewPositivePercentage = num;
                }

                if (int.TryParse(m.Groups[2].Value, NumberStyles.AllowThousands, CultureInfo.InvariantCulture, out num))
                {
                    ReviewTotal = num;
                }
            }

            // Get metacritic url
            m = RegexMetacriticLink.Match(page);
            if (m.Success)
            {
                MetacriticUrl = m.Groups[1].Captures[0].Value;
            }

            // Get Platforms
            m = RegexPlatformWindows.Match(page);
            if (m.Success)
            {
                Platforms |= AppPlatforms.Windows;
            }

            m = RegexPlatformMac.Match(page);
            if (m.Success)
            {
                Platforms |= AppPlatforms.Mac;
            }

            m = RegexPlatformLinux.Match(page);
            if (m.Success)
            {
                Platforms |= AppPlatforms.Linux;
            }
        }
コード例 #12
0
        public void ScrapeTrueSteamAchievements()
        {
            // We can only scrape TrueSteamAchievements in English
            if (Database.Language != StoreLanguage.English)
            {
                Logger.Warn("Scraping TSA {0}: Database langauge is not English, aborting scraping", Id);

                return;
            }

            if (string.IsNullOrWhiteSpace(Name))
            {
                Logger.Warn("Scraping TSA {0}: Entry name is empty, aborting scraping", Id);

                return;
            }

            string page;
            string name    = Name.Replace(" ", "-").Replace(":", "").Replace("'", "");
            string hubPage = string.Format(CultureInfo.InvariantCulture, "https://truesteamachievements.com/game/{0}", name);

            try
            {
                using (WebClient webClient = new WebClient())
                {
                    page = webClient.DownloadString(hubPage);
                }
            }
            catch (Exception e)
            {
                Logger.Warn("Scraping TSA {0}: Page read failed. {1}", Id, e.Message);
                LastStoreScrape = 0;

                return;
            }

            MatchCollection matches = RegexPlatformTSA.Matches(page);

            if (matches.Count > 0)
            {
                Platforms = AppPlatforms.None;

                foreach (Match m in matches)
                {
                    string platform = m.Groups[1].Value;

                    if (platform.Contains("Windows"))
                    {
                        Platforms |= AppPlatforms.Windows;
                    }

                    if (platform.Contains("Mac"))
                    {
                        Platforms |= AppPlatforms.Mac;
                    }

                    if (platform.Contains("Linux"))
                    {
                        Platforms |= AppPlatforms.Linux;
                    }
                }
            }

            Match match = RegexDevelopersTSA.Match(page);

            if (match.Success)
            {
                string developer = match.Groups[1].Value;
                if (!string.IsNullOrWhiteSpace(developer))
                {
                    Developers = new List <string>();

                    string[] developers = developer.Split(',');
                    foreach (string dev in developers)
                    {
                        if (!string.IsNullOrWhiteSpace(dev))
                        {
                            Developers.Add(dev);
                        }
                    }
                }
            }

            match = RegexPublisherTSA.Match(page);
            if (match.Success)
            {
                string publisher = match.Groups[1].Value;
                if (!string.IsNullOrWhiteSpace(publisher))
                {
                    Publishers = new List <string>();

                    string[] publishers = publisher.Split(',');
                    foreach (string pub in publishers)
                    {
                        if (!string.IsNullOrWhiteSpace(pub))
                        {
                            Publishers.Add(pub);
                        }
                    }
                }
            }

            matches = RegexGenreTSA.Matches(page);
            if (matches.Count > 0)
            {
                Genres = new List <string>();
                foreach (Match m in matches)
                {
                    string genre = WebUtility.HtmlDecode(m.Groups[1].Value.Trim());
                    if (!string.IsNullOrWhiteSpace(genre))
                    {
                        Genres.Add(genre);
                    }
                }
            }

            LastStoreScrape = DateTimeOffset.UtcNow.ToUnixTimeSeconds();

            Logger.Info("Scraping TSA {0}: Parsed. Genre: {1}", Id, string.Join(",", Genres));
        }
コード例 #13
0
        /// <summary>
        ///     Merges in data from another entry. Useful for merging scrape results, but could also merge data from a different
        ///     database.
        ///     Uses newer data when there is a conflict.
        ///     Does NOT perform deep copies of list fields.
        /// </summary>
        /// <param name="other">GameDBEntry containing info to be merged into this entry.</param>
        public void MergeIn(DatabaseEntry other)
        {
            bool useAppInfoFields    = (other.LastAppInfoUpdate > LastAppInfoUpdate) || ((LastAppInfoUpdate == 0) && (other.LastStoreScrape >= LastStoreScrape));
            bool useScrapeOnlyFields = other.LastStoreScrape >= LastStoreScrape;

            if ((other.AppType != AppType.Unknown) && ((AppType == AppType.Unknown) || useAppInfoFields))
            {
                AppType = other.AppType;
            }

            if ((other.LastStoreScrape >= LastStoreScrape) || ((LastStoreScrape == 0) && (other.LastAppInfoUpdate > LastAppInfoUpdate)) || (Platforms == AppPlatforms.None))
            {
                Platforms = other.Platforms;
            }

            if (useAppInfoFields)
            {
                if (!string.IsNullOrEmpty(other.Name))
                {
                    Name = other.Name;
                }

                if (other.ParentId > 0)
                {
                    ParentId = other.ParentId;
                }
            }

            if (useScrapeOnlyFields)
            {
                if ((other.Genres != null) && (other.Genres.Count > 0))
                {
                    Genres = other.Genres;
                }

                if ((other.Flags != null) && (other.Flags.Count > 0))
                {
                    Flags = other.Flags;
                }

                if ((other.Tags != null) && (other.Tags.Count > 0))
                {
                    Tags = other.Tags;
                }

                if ((other.Developers != null) && (other.Developers.Count > 0))
                {
                    Developers = other.Developers;
                }

                if ((other.Publishers != null) && (other.Publishers.Count > 0))
                {
                    Publishers = other.Publishers;
                }

                if (!string.IsNullOrEmpty(other.SteamReleaseDate))
                {
                    SteamReleaseDate = other.SteamReleaseDate;
                }

                if (other.TotalAchievements != 0)
                {
                    TotalAchievements = other.TotalAchievements;
                }

                //VR Support
                if ((other.VrSupport.Headsets != null) && (other.VrSupport.Headsets.Count > 0))
                {
                    VrSupport.Headsets = other.VrSupport.Headsets;
                }

                if ((other.VrSupport.Input != null) && (other.VrSupport.Input.Count > 0))
                {
                    VrSupport.Input = other.VrSupport.Input;
                }

                if ((other.VrSupport.PlayArea != null) && (other.VrSupport.PlayArea.Count > 0))
                {
                    VrSupport.PlayArea = other.VrSupport.PlayArea;
                }

                //Language Support
                if ((other.LanguageSupport.FullAudio != null) && (other.LanguageSupport.FullAudio.Count > 0))
                {
                    LanguageSupport.FullAudio = other.LanguageSupport.FullAudio;
                }

                if ((other.LanguageSupport.Interface != null) && (other.LanguageSupport.Interface.Count > 0))
                {
                    LanguageSupport.Interface = other.LanguageSupport.Interface;
                }

                if ((other.LanguageSupport.Subtitles != null) && (other.LanguageSupport.Subtitles.Count > 0))
                {
                    LanguageSupport.Subtitles = other.LanguageSupport.Subtitles;
                }

                if (other.ReviewTotal != 0)
                {
                    ReviewTotal = other.ReviewTotal;
                    ReviewPositivePercentage = other.ReviewPositivePercentage;
                }

                if (!string.IsNullOrEmpty(other.MetacriticUrl))
                {
                    MetacriticUrl = other.MetacriticUrl;
                }
            }

            if (other.LastStoreScrape > LastStoreScrape)
            {
                LastStoreScrape = other.LastStoreScrape;
            }

            if (other.LastAppInfoUpdate > LastAppInfoUpdate)
            {
                LastAppInfoUpdate = other.LastAppInfoUpdate;
            }
        }
コード例 #14
0
        public void ScrapeTrueSteamAchievements()
        {
            // We can only scrape TrueSteamAchievements in English
            if (Settings.Instance.StoreLanguage != StoreLanguage.English)
            {
                return;
            }

            if (string.IsNullOrWhiteSpace(Name))
            {
                return;
            }

            string page;
            string name    = Name.Replace(" ", "-").Replace(":", "").Replace("'", "");
            string hubPage = string.Format(CultureInfo.InvariantCulture, "https://truesteamachievements.com/game/{0}", name);

            using (WebClient webClient = new WebClient())
            {
                page = webClient.DownloadString(hubPage);
            }

            MatchCollection matches = RegexPlatformTSA.Matches(page);

            if (matches.Count > 0)
            {
                Platforms = AppPlatforms.None;

                foreach (Match m in matches)
                {
                    string platform = m.Groups[1].Value;

                    if (platform.Contains("Windows"))
                    {
                        Platforms |= AppPlatforms.Windows;
                    }

                    if (platform.Contains("Mac"))
                    {
                        Platforms |= AppPlatforms.Mac;
                    }

                    if (platform.Contains("Linux"))
                    {
                        Platforms |= AppPlatforms.Linux;
                    }
                }
            }

            Match match = RegexDevelopersTSA.Match(page);

            if (match.Success)
            {
                string developer = match.Groups[1].Value;
                if (!string.IsNullOrWhiteSpace(developer))
                {
                    Developers = new List <string>();

                    string[] developers = developer.Split(',');
                    foreach (string dev in developers)
                    {
                        if (!string.IsNullOrWhiteSpace(dev))
                        {
                            Developers.Add(dev);
                        }
                    }
                }
            }

            match = RegexPublisherTSA.Match(page);
            if (match.Success)
            {
                string publisher = match.Groups[1].Value;
                if (!string.IsNullOrWhiteSpace(publisher))
                {
                    Publishers = new List <string>();

                    string[] publishers = publisher.Split(',');
                    foreach (string pub in publishers)
                    {
                        if (!string.IsNullOrWhiteSpace(pub))
                        {
                            Publishers.Add(pub);
                        }
                    }
                }
            }

            matches = RegexGenreTSA.Matches(page);
            if (matches.Count > 0)
            {
                Genres = new List <string>();
                foreach (Match m in matches)
                {
                    string genre = WebUtility.HtmlDecode(m.Groups[1].Value.Trim());
                    if (!string.IsNullOrWhiteSpace(genre))
                    {
                        Genres.Add(genre);
                    }
                }
            }

            LastStoreScrape = Utility.GetCurrentUTime();
        }