From 8a907c890bfa70dd8eb7d63a795eb4722edc5841 Mon Sep 17 00:00:00 2001 From: Stavros Date: Mon, 19 Jan 2026 12:47:31 +0200 Subject: [PATCH 1/2] Configure a FileDataIniParser to allow and override duplicate keys in ini files, for cases like the Tank.ini where it has a duplicate key DEAD in its sound effects --- C7/Animations/AnimationManager.cs | 2 +- C7Engine/C7Settings.cs | 9 +++------ C7Engine/Util.cs | 18 ++++++++++++++++++ ConvertCiv3Media/Civ3UnitSprite.cs | 4 ++++ 4 files changed, 26 insertions(+), 7 deletions(-) create mode 100644 C7Engine/Util.cs diff --git a/C7/Animations/AnimationManager.cs b/C7/Animations/AnimationManager.cs index 0e93aebb6..467932015 100644 --- a/C7/Animations/AnimationManager.cs +++ b/C7/Animations/AnimationManager.cs @@ -63,7 +63,7 @@ public AnimationManager(AudioStreamPlayer audioPlayer) { public IniData getINIData(string pathKey) { if (!iniDatas.TryGetValue(pathKey, out IniData tr)) { string fullPath = Util.Civ3MediaPath(pathKey); - tr = new FileIniDataParser().ReadFile(fullPath); + tr = C7Engine.Util.GetFileIniDataParser().ReadFile(fullPath); iniDatas.Add(pathKey, tr); } return tr; diff --git a/C7Engine/C7Settings.cs b/C7Engine/C7Settings.cs index 254407405..5406f44d4 100644 --- a/C7Engine/C7Settings.cs +++ b/C7Engine/C7Settings.cs @@ -1,17 +1,14 @@ -using System.IO; +using IniParser.Model; using IniParser.Exceptions; namespace C7Engine { - using IniParser; - using IniParser.Model; - public class C7Settings { private const string SETTINGS_FILE_NAME = "C7.ini"; public static IniData settings; public static void LoadSettings() { try { - settings = new FileIniDataParser().ReadFile(SETTINGS_FILE_NAME); + settings = Util.GetFileIniDataParser().ReadFile(SETTINGS_FILE_NAME); } catch (ParsingException) { //First run. The file doesn't exist. That's okay. We'll use sensible defaults. settings = new IniData(); @@ -20,7 +17,7 @@ public static void LoadSettings() { } public static void SaveSettings() { - new FileIniDataParser().WriteFile(SETTINGS_FILE_NAME, settings); + Util.GetFileIniDataParser().WriteFile(SETTINGS_FILE_NAME, settings); } public static void SetValue(string section, string key, string value) { diff --git a/C7Engine/Util.cs b/C7Engine/Util.cs new file mode 100644 index 000000000..6cb9f40cc --- /dev/null +++ b/C7Engine/Util.cs @@ -0,0 +1,18 @@ +using IniParser; + +namespace C7Engine; + +public static class Util { + private static FileIniDataParser fileIniDataParser; + + public static FileIniDataParser GetFileIniDataParser() { + if (fileIniDataParser != null) return fileIniDataParser; + + FileIniDataParser parser = new FileIniDataParser(); + parser.Parser.Configuration.AllowDuplicateKeys = true; + parser.Parser.Configuration.OverrideDuplicateKeys = true; + + fileIniDataParser = parser; + return parser; + } +} diff --git a/ConvertCiv3Media/Civ3UnitSprite.cs b/ConvertCiv3Media/Civ3UnitSprite.cs index c7068b059..26c42cbbb 100644 --- a/ConvertCiv3Media/Civ3UnitSprite.cs +++ b/ConvertCiv3Media/Civ3UnitSprite.cs @@ -67,6 +67,7 @@ public class Civ3UnitSprite { public Civ3UnitSprite(Civ3UnitSprite civ3UnitSprite) { } + /* public Civ3UnitSprite(string unitPath, byte unitColor = 0) { // TODO: Parameterize this and/or take ini path and chop it up // string UnitIniPath = unitPath + "Warrior.INI"; @@ -101,7 +102,9 @@ public Civ3UnitSprite(string unitPath, byte unitColor = 0) { } } } + /* + /* // Returns all non-empty key, value pairs for the "Animations" section of a unit INI file. I'm copying this out of the Civ3UnitSprite // constructor for use by UnitLayer. public static Dictionary getINIAnimationsInfo(string iniPath) { @@ -115,6 +118,7 @@ public static Dictionary getINIAnimationsInfo(string iniPath) { } return tr; } + /* /* public virtual void InitDisplay() { From 7d89418e1bf21a566525e4c892982578d2eee32b Mon Sep 17 00:00:00 2001 From: Stavros Date: Mon, 19 Jan 2026 19:25:35 +0200 Subject: [PATCH 2/2] Address review comments --- C7Engine/Util.cs | 4 + ConvertCiv3Media/Civ3UnitSprite.cs | 143 ----------------------------- 2 files changed, 4 insertions(+), 143 deletions(-) delete mode 100644 ConvertCiv3Media/Civ3UnitSprite.cs diff --git a/C7Engine/Util.cs b/C7Engine/Util.cs index 6cb9f40cc..0aaaaeede 100644 --- a/C7Engine/Util.cs +++ b/C7Engine/Util.cs @@ -9,7 +9,11 @@ public static FileIniDataParser GetFileIniDataParser() { if (fileIniDataParser != null) return fileIniDataParser; FileIniDataParser parser = new FileIniDataParser(); + // The default behaviour of the parser is to throw an exception + // when it finds duplicate keys (e.x. Tank.ini has 'DEAD' two times in [Sound Effects]) + // so, we want to allow it so that it doesn't crash parser.Parser.Configuration.AllowDuplicateKeys = true; + // but we only want to keep the last value we encounter parser.Parser.Configuration.OverrideDuplicateKeys = true; fileIniDataParser = parser; diff --git a/ConvertCiv3Media/Civ3UnitSprite.cs b/ConvertCiv3Media/Civ3UnitSprite.cs deleted file mode 100644 index 26c42cbbb..000000000 --- a/ConvertCiv3Media/Civ3UnitSprite.cs +++ /dev/null @@ -1,143 +0,0 @@ -using System; -using System.Collections.Generic; -using IniParser; -using IniParser.Model; - -namespace ConvertCiv3Media { - // Under construction - - // The order of direction animations in unit FLC files - public enum Direction { - SW, - S, - SE, - E, - NE, - N, - NW, - W - } - public enum UnitAction { - BLANK, - DEFAULT, - WALK, - RUN, - ATTACK1, - ATTACK2, - ATTACK3, - DEFEND, - DEATH, - DEAD, - FORTIFY, - FORTIFYHOLD, - FIDGET, - VICTORY, - TURNLEFT, - TURNRIGHT, - BUILD, - ROAD, - MINE, - IRRIGATE, - FORTRESS, - CAPTURE, - JUNGLE, - FOREST, - PLANT, - STOP_AT_LAST_FRAME, - // *** The following three are causing null filenames that don't match "" on non-warrior INIs - // *** Oh, they don't exist in other INI's Animation sections! - // PauseROAD, - // PauseMINE, - // PauseIRRIGATE - - } - // will probably make these to-override methods in Civ3UnitSprite instead - public interface ISprite { - void Animation(UnitAction action, Direction direction); - void Play(); - void Stop(); - // public void Move(); - // public void PlaySound(UnitAction action); - // public void SetLocation(int X, int Y); - } - public class Civ3UnitSprite { - protected Flic[] Animations = new Flic[Enum.GetNames(typeof(UnitAction)).Length]; - // TODO: handle mismatched cases in ini file .. maybe try INI then ini ? - // unitColor must be from 0 - 31 - public Civ3UnitSprite(Civ3UnitSprite civ3UnitSprite) { - } - - /* - public Civ3UnitSprite(string unitPath, byte unitColor = 0) { - // TODO: Parameterize this and/or take ini path and chop it up - // string UnitIniPath = unitPath + "Warrior.INI"; - FileIniDataParser UnitIniFile = new FileIniDataParser(); - IniData UnitIniData = UnitIniFile.ReadFile(unitPath); - - // TODO: Fix this total hack - string[] foo = unitPath.Split(new char[]{'/','\\'}); - foo[foo.Length - 2] = "Palettes"; - foo[foo.Length - 1] = String.Format("ntp{0:D02}.pcx", unitColor); - Pcx UnitPal = new Pcx(String.Join("/", foo)); - - // TODO: Fix this total hack - string[] bar = unitPath.Split(new char[]{'/','\\'}); - foreach (UnitAction actn in Enum.GetValues(typeof(UnitAction))) { - if (UnitIniData["Animations"][actn.ToString()] != "" && UnitIniData["Animations"][actn.ToString()] != null) { - bar[bar.Length - 1] = UnitIniData["Animations"][actn.ToString()]; - Flic UnitFlic = new Flic(String.Join("/", bar)); - - byte[,] CivColorUnitPal = new byte[256,3]; - for (int i = 0, palSplit = 64; i < 256; i++) { - byte[,] TempPal = i < palSplit ? UnitPal.Palette : UnitFlic.Palette ; - for (int j = 0; j < 3; j++) { - CivColorUnitPal[i, j] = TempPal[i < palSplit ? i : i, j]; - } - } - // foreach(Direction dir in Enum.GetValues(typeof(Direction))) { - UnitFlic.Palette = CivColorUnitPal; - Animations[(int)actn] = UnitFlic; - // } - - } - } - } - /* - - /* - // Returns all non-empty key, value pairs for the "Animations" section of a unit INI file. I'm copying this out of the Civ3UnitSprite - // constructor for use by UnitLayer. - public static Dictionary getINIAnimationsInfo(string iniPath) { - FileIniDataParser UnitIniFile = new FileIniDataParser(); - IniData UnitIniData = UnitIniFile.ReadFile(iniPath); - var tr = new Dictionary(); - foreach (UnitAction actn in Enum.GetValues(typeof(UnitAction))) { - var fileName = UnitIniData["Animations"][actn.ToString()]; - if ((fileName != null) && (fileName != "")) - tr[actn.ToString()] = fileName; - } - return tr; - } - /* - - /* - public virtual void InitDisplay() { - // override this method in the display framework to convert media to display framework objects - } - */ - virtual public void Animation(UnitAction action, Direction direction) { - // override this method in the display framework - } - /* - virtual public void Play() { - // override this method in the display framework to start animation - } - virtual public void Stop() { - // override this method in the display framework to stop animation - } - */ - virtual public void Move(Direction direction, float speed = 1) { - } - // public void PlaySound(UnitAction action); - } -}