private static List <Nintendo3DSRelease> ParseTicketsFrom3DsDb(List <Nintendo3DSRelease> parsedTickets)
        {
            ConsoleUtils.PrintColorfulLine(ConsoleColor.Green, "Checking Title IDs against 3dsdb.com database");
            var xmlFile = XElement.Load(Files.Nintendo3DsDbPath);

            foreach (var xNode in xmlFile.Nodes())
            {
                var titleInfo = xNode as XElement;

                if (titleInfo == null)
                {
                    continue;
                }

                Func <string, string> titleData = tag => titleInfo.Element(tag).Value.Trim();
                var titleId = titleData("titleid");

                var matchedTitles =
                    parsedTickets.Where(ticket => string.Compare(ticket.TitleId, titleId, StringComparison.OrdinalIgnoreCase) == 0)
                    .ToList();

                foreach (var title in matchedTitles)
                {
                    var publisher = titleData("publisher");

                    string type;

                    switch (int.Parse(titleData("type")))
                    {
                    case 1:
                        type = "3DS Game";
                        break;

                    case 2:
                        type = "3DS Demo";
                        break;

                    case 3:
                        type = "3DSWare";
                        break;

                    case 4:
                        type = "EShop";
                        break;

                    default:
                        type = "Unknown";
                        break;
                    }

                    var sizeInMegabytes = Convert.ToInt32(decimal.Parse(titleData("trimmedsize")) / (int)Math.Pow(2, 20));

                    var foundTicket = new Nintendo3DSRelease(
                        title.Name,
                        publisher,
                        title.Region,
                        type,
                        title.Serial,
                        titleId,
                        title.DecTitleKey,
                        sizeInMegabytes);

                    if (!parsedTickets.Exists(a => Equals(a, foundTicket)))
                    {
                        parsedTickets.Add(foundTicket);
                    }
                    else
                    {
                        title.Type            = type;
                        title.Publisher       = publisher;
                        title.SizeInMegabytes = sizeInMegabytes;

                        // remove title and add updated one
                        parsedTickets.Remove(title);
                        parsedTickets.Add(title);
                    }
                }
            }

            return(parsedTickets);
        }
        public static void Main(string[] args)
        {
            ServicePointManager.ServerCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => true;
            Console.OutputEncoding = Encoding.UTF8;
#if !DEBUG
            try
            {
#endif
            PrintProgramVersion();


            ProcessArgs(args);

            if (!File.Exists(Files.DecTitleKeysPath))
            {
                ConsoleUtils.PrintColorfulLine(
                    ConsoleColor.Red,
                    "decTitleKeys.bin not found! Get it from Decrypt9. Press any key to exit.");
                Console.ReadKey();
                Environment.Exit(1);
            }

            var decTitleKeysFile = File.ReadAllBytes(Files.DecTitleKeysPath);
            var computedHash     = BitConversion.BytesToHex(MD5.Create().ComputeHash(decTitleKeysFile));

            var shouldRecheck = false;

            if (!File.Exists(Files.DecTitleKeysMd5))
            {
                File.WriteAllText(Files.DecTitleKeysMd5, computedHash);
            }
            else
            {
                var savedHash = File.ReadAllText(Files.DecTitleKeysMd5);

                if (savedHash != computedHash)
                {
                    Console.WriteLine("Different decTitleKeys.bin found!");
                    Console.Write("Recheck titles? y/n: ");

                    var keyChosen = Console.ReadKey().Key;
                    if (keyChosen == ConsoleKey.Y)
                    {
                        shouldRecheck = true;
                        File.WriteAllText(Files.DecTitleKeysMd5, computedHash);
                    }
                }
            }

            if (args != null && args.Contains("-update"))
            {
                UpdateDependencies();
            }
            else
            {
                Files.CheckFor3dsDb();
                Files.CheckForGroovyCiaDb();
            }

            var tickets = new Dictionary <string, string>();

            if ((!File.Exists(Files.ValidTicketsPath) || new FileInfo(Files.ValidTicketsPath).Length == 0) || shouldRecheck)
            {
                Console.WriteLine("Decoding valid tickets...");
                tickets = DecodeTickets();
                var ticketsAsStrings = tickets.Select(ticket => ticket.Key + " " + ticket.Value).ToList();

                File.WriteAllText(Files.ValidTicketsPath, string.Join(Environment.NewLine, ticketsAsStrings));
                Console.WriteLine("Wrote valid tickets to ValidTickets.txt");
            }

            var parsedTickets = ParseTickets(tickets).ToArray();

            var titlesFound = ParseTicketsFromGroovyCiaDb(parsedTickets, Files.GroovyCiaPath);
            titlesFound = ParseTicketsFrom3DsDb(titlesFound);

            var longestTitleLength     = titlesFound.Max(a => a.Name.Length) + 2;
            var longestPublisherLength = titlesFound.Max(a => a.Publisher.Length) + 2;

            PrintNumberOfTicketsFound(titlesFound, parsedTickets);

            Console.WriteLine(PrintTitleLegend(longestTitleLength, longestPublisherLength));

            // titlesFound = titlesFound.OrderBy(r => Nintendo3DSRelease.GetTitleType(r.TitleId)).ThenBy(r => r.Name).ToList();
            foreach (var title in titlesFound)
            {
                var fullWidthExtraPadName      = GetFullWidthExtraPad(title.Name);
                var fullWidthExtraPadPublisher = GetFullWidthExtraPad(title.Publisher);

                Console.WriteLine(
                    $"{title.TitleId} {title.DecTitleKey} | {title.Name.PadRight(longestTitleLength - fullWidthExtraPadName)}{title.Publisher.PadRight(longestPublisherLength - fullWidthExtraPadPublisher)}{title.Region}");
            }

            Console.WriteLine(
                "\r\nTitles which 3dsdb or the GroovyCIA db couldn't find but we'll look up from the Nintendo CDN:");

            Console.WriteLine(PrintTitleLegend(longestTitleLength, longestPublisherLength));

            var remainingTitles = parsedTickets.Except(titlesFound).ToList();
            remainingTitles =
                LookUpRemainingTitles(remainingTitles, longestTitleLength, longestPublisherLength)
                .OrderBy(r => Nintendo3DSRelease.GetTitleType(r.TitleId))
                .ThenBy(r => r.Name)
                .ToList();

            WriteOutputToFile(longestTitleLength, longestPublisherLength, titlesFound, remainingTitles);
            WriteOutputToCsv(titlesFound, remainingTitles);

            Console.Write("Done! Tickets and titles exported to ");
            ConsoleUtils.PrintColorfulLine(ConsoleColor.Green, Files.OutputFile);

            Console.Write("Detailed info exported to ");
            ConsoleUtils.PrintColorfulLine(ConsoleColor.Green, Files.DetailedOutputFile);

            // #if !DEBUG
            Console.Write("Press any key to exit...");
            Console.ReadKey();
            // #endif
#if !DEBUG
        }

        catch (Exception ex)
        {
            ConsoleUtils.PrintColorfulLine(ConsoleColor.Red, "Fatal Error: " + ex.Message);

            Console.WriteLine(ex.StackTrace);
        }
#endif
        }