/// <summary>
        /// Get all xml files in a directory and its subdirectories
        /// </summary>
        /// <param name="source">Where to search for XML</param>
        /// <returns></returns>
        private static Tuple <string, SCType>[] GetFiles(string source, SCType filter)
        {
            List <Tuple <string, SCType> > files = new List <Tuple <string, SCType> >();

            foreach (var f in Directory.GetFiles(source, "*.xml", SearchOption.AllDirectories))
            {
                SCType type = CryXML.DetectType(f);

                // Dont process game.xml and not supported files
                if (f.ToLower().Equals("game.xml") || (filter & type) == SCType.None)
                {
                    continue;
                }

                files.Add(new Tuple <string, SCType>(f, type));
            }
            return(files.OrderBy(f => f.Item2).ToArray());
        }
        public static void Main(string[] args)
        {
            Console.OutputEncoding = System.Text.Encoding.UTF8;
            System.Text.StringBuilder sb = new System.Text.StringBuilder();
            bool hasException            = false;

            // Check if version or required arguments are passed
            if (args.Contains("-v") || args.Contains("--version"))
            {
                PrintVersion();
                return;
            }
            else
            if (args.Length < 2 || args.Contains("-h") || args.Contains("--help"))
            {
                PrintHelp();
                return;
            }

            string working_dir = Environment.CurrentDirectory;

            args[0] = args[0].Trim(new char[] { '\'', '\"' });             // Trim ' in source
            args[1] = args[1].Trim(new char[] { '\'', '\"' });             // Trim " in destination

            string source      = new DirectoryInfo(args[0]).FullName;
            var    destination = new DirectoryInfo((Directory.Exists(args[1])) ? args[1] : "./").FullName;

            // Ignore destination if not specified in arguments
            args = args.Skip(destination == "./" ? 1 : 2).ToArray();

            // Process arguments
            SCType filters = FindParameters(args);             // skip source and destination from args

            Logger.LogEmpty("Process has started.");
            Logger.LogDebug("DEBUG MODE ENABLED");
            Logger.LogDebug("Arguments: " + String.Join(' ', args));
            Logger.LogEmpty();
            Logger.LogEmpty("Parameters:");
            Logger.LogEmpty($"\tSource:\t\t{source}");
            Logger.LogEmpty($"\tDestination:\t{destination}");

            if (debug)
            {
                Logger.LogEmpty($"Filter:");
                Logger.LogEmpty("\tShips: " + ((filters & SCType.Ship) == SCType.None ? "No" : "Yes"));
                Logger.LogEmpty("\tShops: " + ((filters & SCType.Shop) == SCType.None ? "No" : "Yes"));
                Logger.LogEmpty("\tWeapons: " + ((filters & SCType.Weapon) == SCType.None ? "No" : "Yes"));
                Logger.LogEmpty("\tStations: " + ((filters & SCType.None) == SCType.None ? "No" : "Yes"));
                Logger.LogEmpty("\tCommodities: " + ((filters & SCType.Commodity) == SCType.None ? "No" : "Yes"));
                Logger.LogEmpty("\tManufacturers: " + ((filters & SCType.Manufacturer) == SCType.None ? "No" : "Yes"));
            }
            Logger.LogEmpty();

            // If no filter is provided, print message
            if (filters == SCType.None)
            {
                Logger.LogInfo("No filter(s) entered,  try to add a least one filter.");
                Logger.LogInfo("Type '--help' for help.");
                return;
            }

            Logger.Log("Loading directory (this can take some time).. ", end: "");
            Tuple <string, SCType>[] files = null;

            // Load all files form the source directory
#if DEBUG
            files = (Tuple <string, SCType>[])Progress.Process(() =>
                                                               GetFiles(source, filters),
                                                               "Done");
#else
            try
            {
                files = (Tuple <string, SCType>[])Progress.Process(() => GetFiles(source, filters), "Done");
            }
            catch (Exception ex)
            {
                Logger.LogError("Loading directory.. FAILED", ex, start: "\r");
                Exit(true, saveCache: false);
            }
#endif

            Logger.Log("Preparing resources.. ", end: "");
            CryXML cryXml = null;

            // Load some XML file in memory
#if DEBUG
            cryXml = (CryXML)Progress.Process(() => new CryXML(source, destination), "Done");
#else
            try
            {
                cryXml = (CryXML)Progress.Process(() => new CryXML(source, destination), "Done");
            }
            catch (Exception ex)
            {
                Logger.LogError("Preparing resources.. FAILED", ex, start: "\r");
                Exit(true, saveCache: false);
            }
#endif

            // Load cached data
            if (useCache)
            {
                try
                {
                    Logger.Log("Loading cache.. ", end: "");
                    var exist = (bool)Progress.Process(() => CryXML.game.LoadCache(), "Done");
                    if (!exist)
                    {
                        Logger.Log("Cache is empty");
                    }
                }
                catch (System.Runtime.Serialization.SerializationException ex)
                {
                    Logger.LogError("Loading cache.. Format error", ex, start: "\r");
                    Logger.LogWarning("Loading cache failed, the cache will rebuild.");
                    CryXML.game.DeleteCache();
                }
                catch (Exception ex)
                {
                    Logger.LogError("Loading cache.. FAILED", ex, start: "\r");
                    Exit(true, saveCache: false);
                }
            }

            Logger.Log("Loading localization.. ", end: "");
            Progress.Process(() => CryXML.localization.LoadLocalization(Language.ENGLISH), "Done");


            Logger.LogInfo($"Files to be converted: {files.Length}");
            Logger.LogEmpty();
            Logger.LogInfo("Starting..");

            var category = SCType.None;
            foreach (Tuple <string, SCType> file in new ProgressBar(files, "Converting", true))
            {
                FileInfo f = new FileInfo(file.Item1);

                // Print category section
                if (category != file.Item2)
                {
                    category = file.Item2;
                    Logger.LogEmpty();
                    Logger.LogInfo($"Category [{category.ToString()}]", clear_line: true);
                }

                Logger.Log($"Converting {f.Name}..  ", end: "");

                // Start XML conversion
#if DEBUG
                Progress.Process(() => cryXml.ConvertJSON(f, file.Item2), "Done");
#else
                try
                {
                    Progress.Process(() => cryXml.ConvertJSON(f, file.Item2), "Done");
                }
                catch (Exception ex)
                {
                    Logger.LogError($"Converting {f.Name}.. FAILED 🔥", start: "\r", exception: ex);
                    hasException = true;
                }
#endif
            }

            Exit(hasException);
        }