/// <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; }
/// <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); }
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; }
/// <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.ScanMixDir(VFS.TSInstallDir, EngineType.TiberianSun); vfsFS.ScanMixDir(VFS.TSInstallDir, EngineType.Firestorm); } if (Directory.Exists(VFS.RA2InstallDir)) { vfsRA2.ScanMixDir(VFS.RA2InstallDir, EngineType.RedAlert2); vfsYR.ScanMixDir(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.AddFile(f); } } if (thsFS != null) { foreach (var f in thsFS.Mixes) { vfsFS.AddFile(f); } } if (thsRA2 != null) { foreach (var f in thsRA2.Mixes) { vfsRA2.AddFile(f); } } if (thsYR != null) { foreach (var f in thsYR.Mixes) { vfsYR.AddFile(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); }
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); }
/// <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; }