using LibXom.Blocks; using LibXom.Exceptions; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace LibXom.Data { public class XomFile { private int version; private int unk0; private List xomStrings = new List(); private List xomTypes = new List(); public XomString[] XomStrings { get { return xomStrings.ToArray(); } } public XomType[] XomTypes { get { return xomTypes.ToArray(); } } public XomContainer[] XomContainers { get { List xomContainers = new List(); foreach(XomType type in xomTypes) foreach(XomContainer container in type.Containers) xomContainers.Add(container); return xomContainers.ToArray(); } } internal void updateStringById(int stringId, XomString newStr) { xomStrings[stringId] = newStr; } internal int addString(XomString xStr) { xomStrings.Add(xStr); return xomStrings.Count - 1; } public XomContainer CreateContainer(string typeName) { XomType type = GetTypeByName(typeName); return type.NewContainer(); } public void ClearAllStrings() { xomStrings.Clear(); } public XomString AddOrGetString(string value) { XomString[] strings = this.XomStrings; for (int i = 0; i < strings.Length; i++) if (strings[i].Equals(value)) return strings[i]; XomString str = new XomString(this, value); addString(str); return str; } public void DeleteString(XomString str) { xomStrings.RemoveAt(str.Id); } public XomString GetStringById(int stringId) { return XomStrings[stringId]; } public XomContainer GetContainerById(int containerId) { return XomContainers[containerId - 1]; } public XomType CreateXomType(byte[] xBytes, string typeName, int insertAt=-1) { if (insertAt == -1) insertAt = xomTypes.Count - 1; XomType newType = new XomType(this, xBytes, typeName, new XomContainer[0]); xomTypes.Insert(insertAt, newType); return newType; } public XomType GetTypeByName(string typeName) { foreach(XomType type in XomTypes) if (type.Name.Equals(typeName, StringComparison.InvariantCulture)) return type; throw new XomTypeNotFoundException("Could not find xom type: " + typeName); } public XomContainer GetContainerByUuid(string uuid) { foreach(XomContainer container in XomContainers) { if(container.Uuid.Equals(uuid, StringComparison.InvariantCultureIgnoreCase)) { return container; } } throw new XomContainerNotFoundException("No container was found with the uuid: " + uuid); } internal void deleteXomType(XomType type) { for(int i = 0; i < xomTypes.Count; i++) { if (xomTypes[i].Equals(type)) { xomTypes.RemoveAt(i); break; } } } internal int calculateIdForXomStrings(XomString searchString) { XomString[] stringsList = this.XomStrings; for (int i = 0; i < stringsList.Length; i++) if (stringsList[i].Equals(searchString)) return i; return addString(searchString); } internal int calculateIdForXomFileComponent(string searchUuid, XomFileComponent[] components) { for (int i = 0; i < components.Length; i++) if (components[i].Uuid.Equals(searchUuid, StringComparison.CurrentCultureIgnoreCase)) return i + 1; throw new XomFileComponentNotFoundException("A XOM Components ID could not be found in the Component List."); } internal XomBlock[] generateBlocks() { XomContainer[] containers = this.XomContainers; XomType[] types = this.XomTypes; XomString[] strings = this.XomStrings; List blocks = new List(); // add MOIK block blocks.Add(new MoikBlock(this.version, containers.Length, types.Length)); // add type blocks foreach (XomType type in types) blocks.Add(new TypeBlock(type.Containers.Length, type.XBytes, type.Name)); // Add guid and schm block blocks.Add(new GuidBlock(0, 0, 0)); blocks.Add(new SchmBlock(unk0, 0, 0)); // Create a list of all strings and the offset they would appear in the strs block. Dictionary stringOffsets = new Dictionary(); int loc = 0; XomString[] stringsSorted = strings.OrderBy(o => o.Value, StringComparer.Ordinal).ToArray(); string[] stringLst = new string[stringsSorted.Length]; for (int i = 0; i < stringsSorted.Length; i++) { XomString str = stringsSorted[i]; stringLst[i] = str.Value; stringOffsets.Add(str.Value, loc); loc += str.Length + 1; // str length, + \0 for terminator. } // now create the strs block int[] offsetLst = new int[strings.Length]; for(int i = 0; i < strings.Length; i++) { XomString str = strings[i]; offsetLst[i] = stringOffsets[str.Value]; } blocks.Add(new StrsBlock(strings.Length, loc, offsetLst, stringLst)); // Add containers foreach (XomContainer container in containers) blocks.Add(new CtnrBlock(container.GetData())); return blocks.ToArray(); } internal XomFile(XomBlock[] xomBlocks) { MoikBlock? moikBlock = XomBlockHandler.GetBlockByName(xomBlocks, "MOIK") as MoikBlock; SchmBlock? schemeBlock = XomBlockHandler.GetBlockByName(xomBlocks, "SCHM") as SchmBlock; StrsBlock? stringBlock = XomBlockHandler.GetBlockByName(xomBlocks, "STRS") as StrsBlock; // Get Types XomBlock[] typeBlocks = XomBlockHandler.GetBlocksByName(xomBlocks, "TYPE"); // Get Containers XomBlock[] containerBlocks = XomBlockHandler.GetBlocksByName(xomBlocks, "CTNR"); if (moikBlock is not MoikBlock) throw new XomBlockNotFoundException("XOM contained no MOIK block!, Is the XOM corrupted?"); if (schemeBlock is not SchmBlock) throw new XomBlockNotFoundException("XOM contained no SCHM block!, Is the XOM corrupted?"); if (stringBlock is not StrsBlock) throw new XomBlockNotFoundException("XOM contained no STRS block!, Is the XOM corrupted?"); version = moikBlock.Version; unk0 = schemeBlock.Unk0; /// Read Strings // Create a list of all strings and the offset they would appear in the strs block. Dictionary stringOffsets = new Dictionary(); int loc = 0; foreach (string str in stringBlock.StringList) { stringOffsets.Add(loc, str); loc += Encoding.UTF8.GetByteCount(str) + 1; // str length, + \0 for terminator. } // Now create the list of XomStrings ... for (int i = 0; i < stringBlock.NumStrs; i++) { int offset = stringBlock.OffsetList[i]; string value = stringOffsets[offset]; xomStrings.Add(new XomString(this, value)); } /// Read Types // keep a global count of how many container blocks have been processed int cnt = 0; foreach (TypeBlock type in typeBlocks) { string typeName = type.TypeName; byte[] xBytes = type.XBytes; int numContainers = type.NumCtnr; // Create a list of containers, inside this type. List xomContainers = new List(); // Read numContainers many containers from the container blocks list, // and then create XomContainer from it. for (int i = 0; i < numContainers; i++) { // Get the container block from the global count .. CtnrBlock? containerBlock = containerBlocks[cnt] as CtnrBlock; if (containerBlock is not CtnrBlock) continue; // Add the new container to the list xomContainers.Add(new XomContainer(this, typeName, containerBlock.Data)); cnt++; // Increment the global container block count. } this.xomTypes.Add(new XomType(this, xBytes, typeName, xomContainers.ToArray())); } } } }