using System; using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; namespace OpenVIII { /// /// Loads strings from FF8 files /// public partial class Strings : IReadOnlyDictionary { #region Fields private static ConcurrentDictionary _files; private static readonly object Lock = new object(); #endregion Fields #region Constructors public Strings() => Init(); #endregion Constructors #region Enums /// /// file names of files with strings and ID's for structs that hold the data. /// public enum FileID : uint { MenuGroup = 0, AreaNames = 2, Namedic = 3, Kernel = 4, } #endregion Enums #region Methods public FF8String GetName(Characters c, Saves.Data d = null) => GetName(c.ToFacesID(), d); public FF8String GetName(GFs gf, Saves.Data d = null) => GetName(gf.ToFacesID(), d); public FF8String GetName(Faces.ID id, Saves.Data d = null) { if (d == null) d = Memory.State; switch (id) { case Faces.ID.Squall_Leonhart: return d.SquallName ?? Read(FileID.MenuGroup, 2, 92); case Faces.ID.Rinoa_Heartilly: return d.RinoaName ?? Read(FileID.MenuGroup, 2, 93); case Faces.ID.Angelo: return d.AngeloName ?? Read(FileID.MenuGroup, 2, 94); case Faces.ID.Boko: return d.BokoName ?? Read(FileID.MenuGroup, 2, 135); case Faces.ID.Zell_Dincht: case Faces.ID.Irvine_Kinneas: case Faces.ID.Quistis_Trepe: case Faces.ID.Selphie_Tilmitt: case Faces.ID.Seifer_Almasy: case Faces.ID.Edea_Kramer: case Faces.ID.Laguna_Loire: case Faces.ID.Kiros_Seagill: case Faces.ID.Ward_Zabac: return Read(FileID.Kernel, 6, (int)id); case Faces.ID.Quezacotl: case Faces.ID.Shiva: case Faces.ID.Ifrit: case Faces.ID.Siren: case Faces.ID.Brothers: case Faces.ID.Diablos: case Faces.ID.Carbuncle: case Faces.ID.Leviathan: case Faces.ID.Pandemona: case Faces.ID.Cerberus: case Faces.ID.Alexander: case Faces.ID.Doomtrain: case Faces.ID.Bahamut: case Faces.ID.Cactuar: case Faces.ID.Tonberry: case Faces.ID.Eden: if (d.GFs != null && d.GFs.TryGetValue(id.ToGFs(), out var value) && value.Name != null && value.Name.Length > 0) return value.Name; return Read(FileID.MenuGroup, 2, 95 + (int)(id.ToGFs())); case Faces.ID.Griever: return d.GrieverName ?? Read(FileID.MenuGroup, 2, 135); case Faces.ID.MiniMog: return Read(FileID.Kernel, 0, 72); // also in KERNEL, 12, 36 case Faces.ID.Blank: return new FF8String(); default: throw new ArgumentOutOfRangeException(nameof(id), id, null); } } /// /// Remember to Close() if done using /// /// /// /// /// public FF8StringReference Read(FileID fileID, int sectionID, int stringID) => _files[fileID][sectionID, stringID]; public FF8StringReference ReadByOffset(FileID fileID, int sectionID, int offset) { var r = new FF8StringReference(Memory.Strings[FileID.Kernel].GetArchive(), Memory.Strings[FileID.Kernel].GetFileNames()[0], Memory.Strings[FileID.Kernel].GetFiles() .SubPositions[(int) ((Kernel) Memory.Strings[FileID.Kernel]).StringLocations[0].StringLocation] + offset, settings: (FF8StringReference.Settings.Namedic | FF8StringReference.Settings.MultiCharByte)); return r.Length>0 ? r : null; } public bool ContainsKey(FileID key) { return _files.ContainsKey(key); } public bool TryGetValue(FileID key, out StringsBase value) { return _files.TryGetValue(key, out value); } public StringsBase this[FileID id] => _files[id]; public IEnumerable Keys => ((IReadOnlyDictionary) _files).Keys; public IEnumerable Values => ((IReadOnlyDictionary) _files).Values; private void Init() { lock (Lock) if (_files == null) { Memory.Log.WriteLine($"{nameof(Strings)} :: {nameof(Init)}"); _files = new ConcurrentDictionary(); Func[] func = { () => _files.TryAdd(FileID.Namedic, Namedic.Load()), // area names and kernel require namedic //though there shouldn't be anything reading the strings till this is done processing //Task.WaitAll(tasks.ToArray()); () => _files.TryAdd(FileID.MenuGroup, MenuGroup.Load()), () => _files.TryAdd(FileID.AreaNames, Areames.Load()), () => _files.TryAdd(FileID.Kernel, Kernel.Load()), }; var tasks = Memory.ProcessFunctions(func); if (tasks.Any(x => !x)) throw new ArgumentException($"{this}::Failed to add to dictionary..."); } } #endregion Methods public IEnumerator> GetEnumerator() { return _files.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable) _files).GetEnumerator(); } public int Count => _files.Count; } }