Map file.
Inheritance: IniFile
Esempio n. 1
0
		public bool Initialize(MapFile mf, EngineType et, List<string> customRulesININames, List<string> customArtININames) {
			if (et == EngineType.AutoDetect) {
				Logger.Fatal("Engine type needs to be known by now!");
				return false;
			}
			Engine = et;
			TheaterType = Theater.TheaterTypeFromString(mf.ReadString("Map", "Theater"));
			FullSize = mf.FullSize;
			LocalSize = mf.LocalSize;

			_tiles = new TileLayer(FullSize.Size);

			LoadAllObjects(mf);

			if (!IgnoreLighting)
				_lighting = mf.Lighting;
			else
				_lighting = new Lighting();

			_wayPoints.AddRange(mf.Waypoints);

			if (!LoadInis(customRulesININames, customArtININames)) {
				Logger.Fatal("Ini files couldn't be loaded");
				return false;
			}

			Logger.Info("Overriding rules.ini with map INI entries");
			_rules.MergeWith(mf);

			return true;
		}
Esempio n. 2
0
		/// <summary>Reads all objects. </summary>
		private void LoadAllObjects(MapFile mf) {

			// import tiles
			foreach (var iso in mf.Tiles)
				_tiles[iso.Dx, iso.Dy / 2] = new MapTile(iso.Dx, iso.Dy, iso.Rx, iso.Ry, iso.Z, iso.TileNum, iso.SubTile, _tiles);

			// import terrain
			foreach (var terr in mf.Terrains) {
				var t = new TerrainObject(terr.Name);
				_terrainObjects.Add(t);
				_tiles.GetTile(terr.Tile).AddObject(t);
			}

			// import smudges
			foreach (var sm in mf.Smudges) {
				var s = new SmudgeObject(sm.Name);
				_tiles.GetTile(sm.Tile).AddObject(s);
				_smudgeObjects.Add(s);
			}

			// import overlays
			foreach (var o in mf.Overlays) {
				var ovl = new OverlayObject(o.OverlayID, o.OverlayValue);
				_tiles.GetTile(o.Tile).AddObject(ovl);
				_overlayObjects.Add(ovl);
			}

			// import infantry
			foreach (var i in mf.Infantries) {
				var inf = new InfantryObject(i.Owner, i.Name, i.Health, i.Direction, i.OnBridge);
				_tiles.GetTile(i.Tile).AddObject(inf);
				_infantryObjects.Add(inf);
			}

			foreach (var u in mf.Units) {
				var un = new UnitObject(u.Owner, u.Name, u.Health, u.Direction, u.OnBridge);
				_tiles.GetTile(u.Tile).AddObject(un);
				_unitObjects.Add(un);
			}

			foreach (var a in mf.Aircrafts) {
				var ac = new AircraftObject(a.Owner, a.Name, a.Health, a.Direction, a.OnBridge);
				_tiles.GetTile(a.Tile).AddObject(ac);
				_aircraftObjects.Add(ac);
			}

			foreach (var s in mf.Structures) {
				var str = new StructureObject(s.Owner, s.Name, s.Health, s.Direction);
				str.Upgrade1 = s.Upgrade1;
				str.Upgrade2 = s.Upgrade2;
				str.Upgrade3 = s.Upgrade3;
				_tiles.GetTile(s.Tile).AddObject(str);
				_structureObjects.Add(str);
			}
		}
Esempio n. 3
0
        /// <summary>Detect map type.</summary>
        /// <param name="rules">The rules.ini file to be used.</param>
        /// <returns>The engine to be used to render this map.</returns>
        public static EngineType DetectEngineType(MapFile mf)
        {
            var vfsTS = new VFS();
            var vfsFS = new VFS();
            var vfsRA2 = new VFS();
            var vfsYR = new VFS();

            if (Directory.Exists(VFS.TSInstallDir)) {
                vfsTS.LoadMixes(VFS.TSInstallDir, EngineType.TiberianSun);
                vfsFS.LoadMixes(VFS.TSInstallDir, EngineType.Firestorm);
            }

            if (Directory.Exists(VFS.RA2InstallDir)) {
                vfsRA2.LoadMixes(VFS.RA2InstallDir, EngineType.RedAlert2);
                vfsYR.LoadMixes(VFS.RA2InstallDir, EngineType.YurisRevenge);
            }

            IniFile rulesTS = vfsTS.OpenFile<IniFile>("rules.ini");
            IniFile rulesFS = vfsFS.OpenFile<IniFile>("rules.ini");
            if (rulesFS != null)
                rulesFS.MergeWith(vfsFS.OpenFile<IniFile>("firestrm.ini"));

            IniFile rulesRA2 = vfsRA2.OpenFile<IniFile>("rules.ini");
            IniFile rulesYR = vfsYR.OpenFile<IniFile>("rulesmd.ini");

            TheaterType theater = Theater.TheaterTypeFromString(mf.ReadString("Map", "Theater"));
            TheaterSettings thsTS = ModConfig.DefaultsTS.GetTheater(theater);
            TheaterSettings thsFS = ModConfig.DefaultsFS.GetTheater(theater);
            TheaterSettings thsRA2 = ModConfig.DefaultsRA2.GetTheater(theater);
            TheaterSettings thsYR = ModConfig.DefaultsYR.GetTheater(theater);

            if (thsTS != null)
                foreach (var f in thsTS.Mixes)
                    vfsTS.AddItem(f);

            if (thsFS != null)
                foreach (var f in thsFS.Mixes)
                    vfsFS.AddItem(f);

            if (thsRA2 != null)
                foreach (var f in thsRA2.Mixes)
                    vfsRA2.AddItem(f);

            if (thsYR != null)
                foreach (var f in thsYR.Mixes)
                    vfsYR.AddItem(f);

            var ret = DetectEngineFromRules(mf, rulesTS, rulesFS, rulesRA2, rulesYR, thsTS, thsFS, thsRA2, thsYR, vfsTS, vfsFS, vfsRA2, vfsYR);
            Logger.Debug("Engine type detected as {0}", ret);
            return ret;
        }
Esempio n. 4
0
		private static EngineType DetectEngineFromRules(MapFile mf,
			IniFile rulesTS, IniFile rulesFS, IniFile rulesRA2, IniFile rulesYR,
			TheaterSettings theaterTS, TheaterSettings theaterFS, TheaterSettings theaterRA2, TheaterSettings theaterYR,
			VFS vfsTS, VFS vfsFS, VFS vfsRA2, VFS vfsYR) {

			double tsScore = PercentageObjectsKnown(mf, vfsTS, rulesTS, theaterTS);
			double fsScore = PercentageObjectsKnown(mf, vfsFS, rulesFS, theaterFS);
			double ra2Score = PercentageObjectsKnown(mf, vfsRA2, rulesRA2, theaterRA2);
			double yrScore = PercentageObjectsKnown(mf, vfsYR, rulesYR, theaterYR);

			double maxScore = Math.Max(Math.Max(Math.Max(tsScore, fsScore), ra2Score), yrScore);
			if (maxScore == ra2Score) return EngineType.RedAlert2;
			else if (maxScore == yrScore) return EngineType.YurisRevenge;
			else if (maxScore == tsScore) return EngineType.TiberianSun;
			else if (maxScore == fsScore) return EngineType.Firestorm;
			return EngineType.YurisRevenge; // default
		}
Esempio n. 5
0
		private static double PercentageObjectsKnown(MapFile mf, VFS vfs, IniFile rules, TheaterSettings ths) {
			if (rules == null || ths == null) return 0.0;
			var theaterIni = vfs.OpenFile<IniFile>(ths.TheaterIni);
			if (theaterIni == null) return 0.0;

			Func<MapObject, IniFile.IniSection, bool> objectKnown = (obj, section) => {
				if (obj is NamedMapObject) {
					string name = (obj as NamedMapObject).Name;
					return section.OrderedEntries.Any(kvp => kvp.Value.ToString().Equals(name, StringComparison.InvariantCultureIgnoreCase));
				}
				else if (obj is NumberedMapObject) {
					int number = (obj as NumberedMapObject).Number;
					return section.HasKey(number.ToString());
				}
				return false; // should not happen
			};

			int known = 0;
			int total = 0;

			var tiles = mf.Tiles.Where(t => t != null).DistinctBy(t => t.TileNum);
			var tilesCollection = new TileCollection(ths, vfs.OpenFile<IniFile>(ths.TheaterIni));
			tilesCollection.InitTilesets();
			known += mf.Tiles.Count(o => o.TileNum <= tilesCollection.NumTiles);
			total += mf.Tiles.Count();

			var infs = mf.Infantries.DistinctBy(o => o.Name);
			known += infs.Count(o => objectKnown(o, rules.GetSection("InfantryTypes")));
			total += infs.Count();

			var terrains = mf.Infantries.DistinctBy(o => o.Name);
			known += terrains.Count(o => objectKnown(o, rules.GetSection("TerrainTypes")));
			total += terrains.Count();

			var units = mf.Infantries.DistinctBy(o => o.Name);
			known += units.Count(o => objectKnown(o, rules.GetSection("VehicleTypes")));
			total += units.Count();

			var aircrafts = mf.Aircrafts.DistinctBy(o => o.Name);
			known += aircrafts.Count(o => objectKnown(o, rules.GetSection("AircraftTypes")));
			total += aircrafts.Count();

			var smudges = mf.Smudges.DistinctBy(o => o.Name);
			known += smudges.Count(o => objectKnown(o, rules.GetSection("SmudgeTypes")));
			total += smudges.Count();

			var structures = mf.Structures.DistinctBy(o => o.Name);
			known += structures.Count(o => objectKnown(o, rules.GetSection("BuildingTypes"))
				|| objectKnown(o, rules.GetSection("OverlayTypes")));
			total += structures.Count();

			var overlays = mf.Overlays.DistinctBy(o => o.Number);
			known += overlays.Count(o => objectKnown(o, rules.GetSection("OverlayTypes")));
			total += overlays.Count();


			return known / (double)total;
		}
Esempio n. 6
0
        /// <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(MakeValidFileName(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);
                if (mf != null)
                    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.AddItem(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;
        }
Esempio n. 7
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));

                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.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);
                }

                // ---------------------------------------------------------------
                // 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);

                VFS.Instance.LoadMixes(Settings.Engine);

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

                if (!map.Initialize(mapFile, Settings.Engine, ModConfig.ActiveConfig.CustomRulesIniFiles, ModConfig.ActiveConfig.CustomArtIniFiles)) {
                    _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.StartPositionMarking == StartPositionMarking.Tiled)
                    map.MarkTiledStartPositions();

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

                if (Settings.FixupTiles) map.FixupTileLayer();
                map.Draw();

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

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

                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();
                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
                        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 || Settings.FixupTiles) {
                    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);

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