コード例 #1
0
 public bool AddItem(string path, CacheMethod m = CacheMethod.Default)
 {
     // directory
     if (Directory.Exists(path))
     {
         AllArchives.Add(new DirArchive(path));
         Logger.Trace("Added <DirArchive> {0} to VFS", path);
         return(true);
     }
     // regular file
     else if (File.Exists(path))
     {
         var fi = new FileInfo(path);
         // mix file
         if (FormatHelper.MixArchiveExtensions.Contains(fi.Extension, StringComparer.InvariantCultureIgnoreCase))
         {
             var mf = new MixFile(File.Open(path, FileMode.Open, FileAccess.Read, FileShare.Read));
             mf.FileName = path;
             AllArchives.Add(mf);
             Logger.Trace("Added <MixFile> {0} to VFS", path);
             return(true);
         }
     }
     // virtual mix file
     else if (FileExists(path))
     {
         var mx = Open(path, FileFormat.Mix) as MixFile;
         AllArchives.Add(mx);
         Logger.Trace("Added <VirtualMixFile> {0} to VFS", path);
         return(true);
     }
     return(false);
 }
コード例 #2
0
        void IUtilityCommand.Run(Utility utility, string[] args)
        {
            var filename = Path.GetFileName(args[1]);
            var path     = Path.GetDirectoryName(args[1]);

            var fs = new FileSystem.FileSystem(utility.Mods);

            // Needed to access the global mix database
            fs.LoadFromManifest(utility.ModData.Manifest);

            fs.Mount(path, "parent");
            var package = new MixFile(fs, "parent|" + filename);

            foreach (var kv in package.Index.OrderBy(kv => kv.Value.Offset))
            {
                Console.WriteLine("{0}:", kv.Key);
                Console.WriteLine("\tOffset: {0}", kv.Value.Offset);
                Console.WriteLine("\tLength: {0}", kv.Value.Length);
            }
        }
コード例 #3
0
ファイル: MixFile.cs プロジェクト: AttacqueSuperior/Engine
        bool IPackageLoader.TryParsePackage(Stream s, string filename, FS context, out IReadOnlyPackage package)
        {
            if (!filename.EndsWith(".mix", StringComparison.InvariantCultureIgnoreCase))
            {
                package = null;
                return(false);
            }

            // Load the global mix database
            if (globalFilenames == null)
            {
                if (context.TryOpen("global mix database.dat", out var mixDatabase))
                {
                    using (var db = new XccGlobalDatabase(mixDatabase))
                        globalFilenames = db.Entries.Distinct().ToArray();
                }
            }

            package = new MixFile(s, filename, globalFilenames ?? Array.Empty <string>());
            return(true);
        }
コード例 #4
0
ファイル: MixFile.cs プロジェクト: dnqbob/OpenRA
        bool IPackageLoader.TryParsePackage(Stream s, string filename, FS context, out IReadOnlyPackage package)
        {
            if (!filename.EndsWith(".mix", StringComparison.InvariantCultureIgnoreCase))
            {
                package = null;
                return(false);
            }

            // Load the global mix database
            var allPossibleFilenames = new HashSet <string>();

            if (context.TryOpen("global mix database.dat", out var mixDatabase))
            {
                using (var db = new XccGlobalDatabase(mixDatabase))
                    foreach (var e in db.Entries)
                    {
                        allPossibleFilenames.Add(e);
                    }
            }

            package = new MixFile(s, filename, allPossibleFilenames);
            return(true);
        }
コード例 #5
0
ファイル: Program.cs プロジェクト: Palmke/ccmaps-net
        /// <summary>Gets the determine map name. </summary>
        /// <returns>The filename to save the map as</returns>
        public static string DetermineMapName(MapFile map, EngineType engine)
        {
            string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(map.FileName);

            IniFile.IniSection basic = map.GetSection("Basic");
            if (basic.ReadBool("Official") == false)
            {
                return(StripPlayersFromName(basic.ReadString("Name", fileNameWithoutExtension)));
            }

            string mapExt      = Path.GetExtension(Settings.InputFile);
            string missionName = "";
            string mapName     = "";

            PktFile.PktMapEntry       pktMapEntry  = null;
            MissionsFile.MissionEntry missionEntry = null;

            // campaign mission
            if (!basic.ReadBool("MultiplayerOnly") && basic.ReadBool("Official"))
            {
                string missionsFile;
                switch (engine)
                {
                case EngineType.TiberianSun:
                case EngineType.RedAlert2:
                    missionsFile = "mission.ini";
                    break;

                case EngineType.Firestorm:
                    missionsFile = "mission1.ini";
                    break;

                case EngineType.YurisRevenge:
                    missionsFile = "missionmd.ini";
                    break;

                default:
                    throw new ArgumentOutOfRangeException("engine");
                }
                var mf = VFS.Open <MissionsFile>(missionsFile);
                missionEntry = mf.GetMissionEntry(Path.GetFileName(map.FileName));
                if (missionEntry != null)
                {
                    missionName = (engine >= EngineType.RedAlert2) ? missionEntry.UIName : missionEntry.Name;
                }
            }

            else
            {
                // multiplayer map
                string  pktEntryName = fileNameWithoutExtension;
                PktFile pkt          = null;

                if (FormatHelper.MixArchiveExtensions.Contains(mapExt))
                {
                    // this is an 'official' map 'archive' containing a PKT file with its name
                    try {
                        var mix = new MixFile(File.Open(Settings.InputFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite));
                        pkt = mix.OpenFile(fileNameWithoutExtension + ".pkt", FileFormat.Pkt) as PktFile;
                        // pkt file is cached by default, so we can close the handle to the file
                        mix.Close();

                        if (pkt != null && pkt.MapEntries.Count > 0)
                        {
                            pktEntryName = pkt.MapEntries.First().Key;
                        }
                    }
                    catch (ArgumentException) { }
                }

                else
                {
                    // determine pkt file based on engine
                    switch (engine)
                    {
                    case EngineType.TiberianSun:
                    case EngineType.RedAlert2:
                        pkt = VFS.Open <PktFile>("missions.pkt");
                        break;

                    case EngineType.Firestorm:
                        pkt = VFS.Open <PktFile>("multi01.pkt");
                        break;

                    case EngineType.YurisRevenge:
                        pkt = VFS.Open <PktFile>("missionsmd.pkt");
                        break;

                    default:
                        throw new ArgumentOutOfRangeException("engine");
                    }
                }


                // fallback for multiplayer maps with, .map extension,
                // no YR objects so assumed to be ra2, but actually meant to be used on yr
                if (mapExt == ".map" && pkt != null && !pkt.MapEntries.ContainsKey(pktEntryName) && engine >= EngineType.RedAlert2)
                {
                    var vfs = new VFS();
                    vfs.AddFile(Settings.InputFile);
                    pkt = vfs.OpenFile <PktFile>("missionsmd.pkt");
                }

                if (pkt != null && !string.IsNullOrEmpty(pktEntryName))
                {
                    pktMapEntry = pkt.GetMapEntry(pktEntryName);
                }
            }

            // now, if we have a map entry from a PKT file,
            // for TS we are done, but for RA2 we need to look in the CSV file for the translated mapname
            if (engine <= EngineType.Firestorm)
            {
                if (pktMapEntry != null)
                {
                    mapName = pktMapEntry.Description;
                }
                else if (missionEntry != null)
                {
                    if (engine == EngineType.TiberianSun)
                    {
                        string campaignSide;
                        string missionNumber;

                        if (missionEntry.Briefing.Length >= 3)
                        {
                            campaignSide  = missionEntry.Briefing.Substring(0, 3);
                            missionNumber = missionEntry.Briefing.Length > 3 ? missionEntry.Briefing.Substring(3) : "";
                            missionName   = "";
                            mapName       = string.Format("{0} {1} - {2}", campaignSide, missionNumber.TrimEnd('A').PadLeft(2, '0'), missionName);
                        }
                        else if (missionEntry.Name.Length >= 10)
                        {
                            mapName = missionEntry.Name;
                        }
                    }
                    else
                    {
                        // FS map names are constructed a bit easier
                        mapName = missionName.Replace(":", " - ");
                    }
                }
                else if (!string.IsNullOrEmpty(basic.ReadString("Name")))
                {
                    mapName = basic.ReadString("Name", fileNameWithoutExtension);
                }
            }

            // if this is a RA2/YR mission (csfEntry set) or official map with valid pktMapEntry
            else if (missionEntry != null || pktMapEntry != null)
            {
                string csfEntryName = missionEntry != null ? missionName : pktMapEntry.Description;

                string csfFile = engine == EngineType.YurisRevenge ? "ra2md.csf" : "ra2.csf";
                Logger.Info("Loading csf file {0}", csfFile);
                var csf = VFS.Open <CsfFile>(csfFile);
                mapName = csf.GetValue(csfEntryName.ToLower());

                if (missionEntry != null)
                {
                    if (mapName.Contains("Operation: "))
                    {
                        string missionMapName = Path.GetFileName(map.FileName);
                        if (char.IsDigit(missionMapName[3]) && char.IsDigit(missionMapName[4]))
                        {
                            string missionNr = Path.GetFileName(map.FileName).Substring(3, 2);
                            mapName = mapName.Substring(0, mapName.IndexOf(":")) + " " + missionNr + " -" +
                                      mapName.Substring(mapName.IndexOf(":") + 1);
                        }
                    }
                }
                else
                {
                    // not standard map
                    if ((pktMapEntry.GameModes & PktFile.GameMode.Standard) == 0)
                    {
                        if ((pktMapEntry.GameModes & PktFile.GameMode.Megawealth) == PktFile.GameMode.Megawealth)
                        {
                            mapName += " (Megawealth)";
                        }
                        if ((pktMapEntry.GameModes & PktFile.GameMode.Duel) == PktFile.GameMode.Duel)
                        {
                            mapName += " (Land Rush)";
                        }
                        if ((pktMapEntry.GameModes & PktFile.GameMode.NavalWar) == PktFile.GameMode.NavalWar)
                        {
                            mapName += " (Naval War)";
                        }
                    }
                }
            }

            // not really used, likely empty, but if this is filled in it's probably better than guessing
            if (mapName == "" && basic.SortedEntries.ContainsKey("Name"))
            {
                mapName = basic.ReadString("Name");
            }

            if (mapName == "")
            {
                Logger.Warn("No valid mapname given or found, reverting to default filename {0}", fileNameWithoutExtension);
                mapName = fileNameWithoutExtension;
            }
            else
            {
                Logger.Info("Mapname found: {0}", mapName);
            }

            mapName = StripPlayersFromName(MakeValidFileName(mapName)).Replace("  ", " ");
            return(mapName);
        }
コード例 #6
0
ファイル: Program.cs プロジェクト: Palmke/ccmaps-net
        public static int Main(string[] args)
        {
            InitLoggerConfig();
            InitSettings(args);

            // DumpTileProperties();

            if (!ValidateSettings())
            {
                return(2);
            }

            try {
                Logger.Info("Initializing virtual filesystem");

                var         mapStream = File.Open(Settings.InputFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
                VirtualFile vmapFile;
                var         mixMap = new MixFile(mapStream, Settings.InputFile, 0, mapStream.Length, false, false);
                if (mixMap.IsValid())                   // input max is a mix
                {
                    var mapArchive = new MixFile(mapStream, Path.GetFileName(Settings.InputFile), true);
                    // grab the largest file in the archive
                    var mixEntry = mapArchive.Index.OrderByDescending(me => me.Value.Length).First();
                    vmapFile = mapArchive.OpenFile(mixEntry.Key);
                }
                else
                {
                    vmapFile = new VirtualFile(mapStream, Path.GetFileName(Settings.InputFile), true);
                }
                var mapFile = new MapFile(vmapFile, Path.GetFileName(Settings.InputFile));

                if (!string.IsNullOrEmpty(Settings.ModConfig))
                {
                    if (File.Exists(Settings.ModConfig))
                    {
                        ModConfig cfg;
                        try {
                            using (FileStream f = File.OpenRead(Settings.ModConfig))
                                cfg = ModConfig.Deserialize(f);
                            ModConfig.ActiveConfig = cfg;
                            if (Settings.Engine != EngineType.AutoDetect)
                            {
                                if (Settings.Engine != cfg.Engine)
                                {
                                    Logger.Warn("Provided engine override does not match mod config.");
                                }
                            }
                            else
                            {
                                Settings.Engine = ModConfig.ActiveConfig.Engine;
                            }
                        }
                        catch (IOException) {
                            Logger.Error("IOException while loading mod config");
                        }
                        catch (XmlException) {
                            Logger.Error("XmlException while loading mod config");
                        }
                        catch (SerializationException) {
                            Logger.Error("Serialization exception while loading mod config");
                        }
                    }
                    else
                    {
                        Logger.Error("Invalid mod config file specified");
                    }
                }

                if (Settings.Engine == EngineType.AutoDetect)
                {
                    Settings.Engine = EngineDetector.DetectEngineType(mapFile);
                    Logger.Info("Engine autodetect result: {0}", Settings.Engine);
                }

                // ---------------------------------------------------------------
                // Code to organize moving of maps in a directory for themselves

                /*
                 * string mapName = DetermineMapName(mapFile, Settings.Engine);
                 * string ndir = Path.Combine(Path.GetDirectoryName(Settings.InputFile), mapName);
                 * if (!Directory.Exists(ndir)) Directory.CreateDirectory(ndir);
                 * mapFile.Close();
                 * mapFile.Dispose();
                 * File.Move(Settings.InputFile, Path.Combine(ndir, Path.GetFileName(mapFile.FileName)));
                 * return 0;*/
                // ---------------------------------------------------------------

                // enginetype is now definitive, load mod config
                if (ModConfig.ActiveConfig == null)
                {
                    ModConfig.LoadDefaultConfig(Settings.Engine);
                }

                // first add the dirs, then load the extra mixes, then scan the dirs
                foreach (string modDir in ModConfig.ActiveConfig.Directories)
                {
                    VFS.Add(modDir);
                }

                // add mixdir to VFS (if it's not included in the mod config)
                if (!ModConfig.ActiveConfig.Directories.Any())
                {
                    string mixDir = VFS.DetermineMixDir(Settings.MixFilesDirectory, Settings.Engine);
                    VFS.Add(mixDir);
                }
                foreach (string mixFile in ModConfig.ActiveConfig.ExtraMixes)
                {
                    VFS.Add(mixFile);
                }
                foreach (var dir in VFS.Instance.AllArchives.OfType <DirArchive>().Select(d => d.Directory).ToList())
                {
                    VFS.Instance.ScanMixDir(dir, Settings.Engine);
                }

                var map = new Map {
                    IgnoreLighting  = Settings.IgnoreLighting,
                    StartPosMarking = Settings.StartPositionMarking,
                    MarkOreFields   = Settings.MarkOreFields
                };

                if (!map.Initialize(mapFile, Settings.Engine))
                {
                    Logger.Error("Could not successfully load this map. Try specifying the engine type manually.");
                    return(1);
                }

                if (!map.LoadTheater())
                {
                    Logger.Error("Could not successfully load all required components for this map. Aborting.");
                    return(1);
                }

                if (Settings.StartPositionMarking == StartPositionMarking.Tiled)
                {
                    map.MarkTiledStartPositions();
                }

                if (Settings.MarkOreFields)
                {
                    map.MarkOreAndGems();
                }

                map.Draw();

#if DEBUG
                // ====================================================================================
                using (var form = new DebugDrawingSurfaceWindow(map.GetDrawingSurface(), map.GetTiles(), map.GetTheater(), map)) {
                    form.RequestTileEvaluate += map.DebugDrawTile; form.ShowDialog();
                }
                // ====================================================================================
#endif

                if (Settings.StartPositionMarking == StartPositionMarking.Squared)
                {
                    map.DrawSquaredStartPositions();
                }

                if (Settings.OutputFile == "")
                {
                    Settings.OutputFile = DetermineMapName(mapFile, Settings.Engine);
                }

                if (Settings.OutputDir == "")
                {
                    Settings.OutputDir = Path.GetDirectoryName(Settings.InputFile);
                }

                // free up as much memory as possible before saving the large images
                Rectangle      saveRect = map.GetSizePixels(Settings.SizeMode);
                DrawingSurface ds       = map.GetDrawingSurface();
                // if we don't need this data anymore, we can try to save some memory
                if (!Settings.GeneratePreviewPack)
                {
                    ds.FreeNonBitmap();
                    map.FreeUseless();
                    GC.Collect();
                }

                if (Settings.SaveJPEG)
                {
                    ds.SaveJPEG(Path.Combine(Settings.OutputDir, Settings.OutputFile + ".jpg"), Settings.JPEGCompression, saveRect);
                }

                if (Settings.SavePNG)
                {
                    ds.SavePNG(Path.Combine(Settings.OutputDir, Settings.OutputFile + ".png"), Settings.PNGQuality, saveRect);
                }

                Regex reThumb = new Regex(@"(\+)?\((\d+),(\d+)\)");
                var   match   = reThumb.Match(Settings.ThumbnailSettings);
                if (match.Success)
                {
                    Size dimensions = new Size(
                        int.Parse(match.Groups[2].Captures[0].Value),
                        int.Parse(match.Groups[3].Captures[0].Value));
                    var cutRect = map.GetSizePixels(Settings.SizeMode);

                    if (match.Groups[1].Captures[0].Value == "+")
                    {
                        // + means maintain aspect ratio
                        double aspectRatio = cutRect.Width / (double)cutRect.Height;
                        if (dimensions.Width / (double)dimensions.Height > aspectRatio)
                        {
                            dimensions.Height = (int)(dimensions.Width / aspectRatio);
                        }
                        else
                        {
                            dimensions.Width = (int)(dimensions.Height / aspectRatio);
                        }
                    }
                    Logger.Info("Saving thumbnail with dimensions {0}x{1}", dimensions.Width, dimensions.Height);
                    ds.SaveThumb(dimensions, cutRect, Path.Combine(Settings.OutputDir, "thumb_" + Settings.OutputFile + ".jpg"));
                }

                if (Settings.GeneratePreviewPack)
                {
                    if (mapFile.BaseStream is MixFile)
                    {
                        Logger.Error("Cannot inject thumbnail into an archive (.mmx/.yro/.mix)!");
                    }
                    else
                    {
                        map.GeneratePreviewPack(Settings.OmitPreviewPackMarkers, Settings.SizeMode, mapFile);
                        Logger.Info("Saving map");
                        mapFile.Save(Settings.InputFile);
                    }
                }
            }
            catch (Exception exc) {
                Logger.Error(string.Format("An unknown fatal exception occured: {0}", exc), exc);
#if DEBUG
                throw;
#else
                return(1);
#endif
            }

            LogManager.Configuration = null;             // required for mono release to flush possible targets
            return(0);
        }
コード例 #7
0
 public bool AddMix(MixFile mix)
 {
     AllArchives.Add(mix);
     return(true);
 }
コード例 #8
0
        public EngineResult Execute()
        {
            try {
                _logger.Info("Initializing virtual filesystem");

                var         mapStream = File.Open(_settings.InputFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
                VirtualFile vmapFile;
                var         mixMap = new MixFile(mapStream, _settings.InputFile, 0, mapStream.Length, false, false);
                if (mixMap.IsValid())                   // input max is a mix
                {
                    var mapArchive = new MixFile(mapStream, Path.GetFileName(_settings.InputFile), true);
                    // grab the largest file in the archive
                    var mixEntry = mapArchive.Index.OrderByDescending(me => me.Value.Length).First();
                    vmapFile = mapArchive.OpenFile(mixEntry.Key);
                }
                else
                {
                    vmapFile = new VirtualFile(mapStream, Path.GetFileName(_settings.InputFile), true);
                }
                var mapFile = new MapFile(vmapFile, Path.GetFileName(_settings.InputFile));

                ModConfig modConfig = null;
                if (!string.IsNullOrEmpty(_settings.ModConfig))
                {
                    if (File.Exists(_settings.ModConfig))
                    {
                        try {
                            using (FileStream f = File.OpenRead(_settings.ModConfig))
                                modConfig = ModConfig.Deserialize(f);
                        }
                        catch (IOException) {
                            _logger.Fatal("IOException while loading mod config");
                        }
                        catch (XmlException) {
                            _logger.Fatal("XmlException while loading mod config");
                        }
                        catch (SerializationException) {
                            _logger.Fatal("Serialization exception while loading mod config");
                        }
                    }
                    else
                    {
                        _logger.Fatal("Invalid mod config file specified");
                    }
                }

                if (_settings.Engine == EngineType.AutoDetect)
                {
                    _settings.Engine = EngineDetector.DetectEngineType(mapFile);
                    _logger.Info("Engine autodetect result: {0}", _settings.Engine);
                }

                // Engine type is now definitive, load mod config
                if (modConfig == null)
                {
                    modConfig = ModConfig.GetDefaultConfig(_settings.Engine);
                }

                var map = new Map.Map {
                    IgnoreLighting  = _settings.IgnoreLighting,
                    StartPosMarking = _settings.StartPositionMarking,
                    StartMarkerSize = _settings.MarkerStartSize,
                    MarkOreFields   = _settings.MarkOreFields
                };

                using (var vfs = new VirtualFileSystem()) {
                    // first add the dirs, then load the extra mixes, then scan the dirs
                    foreach (string modDir in modConfig.Directories)
                    {
                        vfs.Add(modDir);
                    }

                    // add mixdir to VFS (if it's not included in the mod config)
                    if (!modConfig.Directories.Any())
                    {
                        string mixDir =
                            VirtualFileSystem.DetermineMixDir(_settings.MixFilesDirectory, _settings.Engine);
                        vfs.Add(mixDir);
                    }

                    foreach (string mixFile in modConfig.ExtraMixes)
                    {
                        vfs.Add(mixFile);
                    }

                    vfs.LoadMixes(_settings.Engine);

                    if (!map.Initialize(mapFile, modConfig, vfs))
                    {
                        _logger.Error("Could not successfully load this map. Try specifying the engine type manually.");
                        return(EngineResult.LoadRulesFailed);
                    }

                    if (!map.LoadTheater())
                    {
                        _logger.Error("Could not successfully load all required components for this map. Aborting.");
                        return(EngineResult.LoadTheaterFailed);
                    }

                    if (_settings.MarkStartPos && _settings.StartPositionMarking == StartPositionMarking.Tiled)
                    {
                        map.MarkTiledStartPositions();
                    }

                    if (_settings.MarkOreFields)
                    {
                        map.MarkOreAndGems();
                    }

                    if ((_settings.GeneratePreviewPack || _settings.FixupTiles || _settings.FixOverlays ||
                         _settings.CompressTiles) && _settings.Backup)
                    {
                        if (mapFile.BaseStream is MixFile)
                        {
                            _logger.Error("Cannot generate a map file backup into an archive (.mmx/.yro/.mix)!");
                        }
                        else
                        {
                            try {
                                string timestamp = DateTime.Now.ToString("yyyyMMddHHmmss");
                                string fileInput = Path.Combine(Path.GetDirectoryName(_settings.InputFile),
                                                                Path.GetFileName(_settings.InputFile));
                                fileInput = fileInput.TrimEnd(Path.DirectorySeparatorChar,
                                                              Path.AltDirectorySeparatorChar);
                                string fileInputNoExtn = Path.Combine(Path.GetDirectoryName(_settings.InputFile),
                                                                      Path.GetFileNameWithoutExtension(_settings.InputFile));
                                fileInputNoExtn = fileInputNoExtn.TrimEnd(Path.DirectorySeparatorChar,
                                                                          Path.AltDirectorySeparatorChar);
                                string fileBackup = fileInputNoExtn + "_" + timestamp + ".bkp";
                                File.Copy(fileInput, fileBackup, true);
                                _logger.Info("Creating map backup: " + fileBackup);
                            }
                            catch (Exception) {
                                _logger.Error("Unable to generate a map file backup!");
                            }
                        }
                    }

                    if (_settings.FixupTiles)
                    {
                        map.FixupTileLayer();
                    }

                    map.Draw();

                    if (_settings.MarkIceGrowth)
                    {
                        map.MarkIceGrowth();
                    }

                    if (_settings.TunnelPaths)
                    {
                        map.PlotTunnels(_settings.TunnelPosition);
                    }

                    if (_settings.MarkStartPos && (_settings.StartPositionMarking == StartPositionMarking.Squared ||
                                                   _settings.StartPositionMarking == StartPositionMarking.Circled ||
                                                   _settings.StartPositionMarking == StartPositionMarking.Diamond ||
                                                   _settings.StartPositionMarking == StartPositionMarking.Ellipsed ||
                                                   _settings.StartPositionMarking == StartPositionMarking.Starred))
                    {
                        map.DrawStartPositions();
                    }

                    if (_settings.OutputFile == "")
                    {
                        _settings.OutputFile = DetermineMapName(mapFile, _settings.Engine, vfs);
                    }

                    if (_settings.OutputDir == "")
                    {
                        _settings.OutputDir = Path.GetDirectoryName(_settings.InputFile);
                    }

                    if (_settings.DiagnosticWindow)
                    {
                        using (var form = new DebugDrawingSurfaceWindow(map.GetDrawingSurface(), map.GetTiles(),
                                                                        map.GetTheater(), map)) {
                            form.RequestTileEvaluate += map.DebugDrawTile;
                            form.ShowDialog();
                        }
                    }
                }                 // VFS resources can now be released

                // free up as much memory as possible before saving the large images
                Rectangle      saveRect = map.GetSizePixels(_settings.SizeMode);
                DrawingSurface ds       = map.GetDrawingSurface();
                saveRect.Intersect(new Rectangle(0, 0, ds.Width, ds.Height));
                // if we don't need this data anymore, we can try to save some memory
                if (!_settings.GeneratePreviewPack)
                {
                    ds.FreeNonBitmap();
                    map.FreeUseless();
                    GC.Collect();
                }

                if (_settings.SaveJPEG)
                {
                    ds.SaveJPEG(Path.Combine(_settings.OutputDir, _settings.OutputFile + ".jpg"),
                                _settings.JPEGCompression, saveRect);
                }

                if (_settings.SavePNG)
                {
                    ds.SavePNG(Path.Combine(_settings.OutputDir, _settings.OutputFile + ".png"),
                               _settings.PNGQuality, saveRect);
                }

                Regex reThumb = new Regex(@"(\+|)?\((\d+),(\d+)\)");
                var   match   = reThumb.Match(_settings.ThumbnailConfig);
                if (match.Success)
                {
                    Size dimensions = new Size(
                        int.Parse(match.Groups[2].Captures[0].Value),
                        int.Parse(match.Groups[3].Captures[0].Value));
                    var cutRect = map.GetSizePixels(_settings.SizeMode);

                    if (match.Groups[1].Captures[0].Value == "+")
                    {
                        // + means maintain aspect ratio

                        if (dimensions.Width > 0 && dimensions.Height > 0)
                        {
                            float scaleHeight = (float)dimensions.Height / (float)cutRect.Height;
                            float scaleWidth  = (float)dimensions.Width / (float)cutRect.Width;
                            float scale       = Math.Min(scaleHeight, scaleWidth);
                            dimensions.Width  = Math.Max((int)(cutRect.Width * scale), 1);
                            dimensions.Height = Math.Max((int)(cutRect.Height * scale), 1);
                        }
                        else
                        {
                            double aspectRatio = cutRect.Width / (double)cutRect.Height;
                            if (dimensions.Width / (double)dimensions.Height > aspectRatio)
                            {
                                dimensions.Height = (int)(dimensions.Width / aspectRatio);
                            }
                            else
                            {
                                dimensions.Width = (int)(dimensions.Height * aspectRatio);
                            }
                        }
                    }

                    _logger.Info("Saving thumbnail with dimensions {0}x{1}", dimensions.Width, dimensions.Height);

                    if (!_settings.SavePNGThumbnails)
                    {
                        ds.SaveThumb(dimensions, cutRect,
                                     Path.Combine(_settings.OutputDir, "thumb_" + _settings.OutputFile + ".jpg"));
                    }
                    else
                    {
                        ds.SaveThumb(dimensions, cutRect,
                                     Path.Combine(_settings.OutputDir, "thumb_" + _settings.OutputFile + ".png"), true);
                    }
                }

                if (_settings.GeneratePreviewPack || _settings.FixupTiles || _settings.FixOverlays ||
                    _settings.CompressTiles)
                {
                    if (mapFile.BaseStream is MixFile)
                    {
                        _logger.Error(
                            "Cannot fix tile layer or inject thumbnail into an archive (.mmx/.yro/.mix)!");
                    }
                    else
                    {
                        if (_settings.GeneratePreviewPack)
                        {
                            map.GeneratePreviewPack(_settings.PreviewMarkers, _settings.SizeMode, mapFile,
                                                    _settings.FixPreviewDimensions);
                        }

                        if (_settings.FixOverlays)
                        {
                            map.FixupOverlays();                             // fixing is done earlier, it now creates overlay and its data pack
                        }
                        // Keep this last in tiles manipulation
                        if (_settings.CompressTiles)
                        {
                            map.CompressIsoMapPack5();
                        }

                        _logger.Info("Saving map to " + _settings.InputFile);
                        mapFile.Save(_settings.InputFile);
                    }
                }
            }
            catch (Exception exc) {
                _logger.Error(string.Format("An unknown fatal exception occurred: {0}", exc), exc);
#if DEBUG
                throw;
#endif
                return(EngineResult.Exception);
            }
            return(EngineResult.RenderedOk);
        }