205 lines
7.6 KiB
C#
205 lines
7.6 KiB
C#
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<XomString> xomStrings = new List<XomString>();
|
|
private List<XomType> xomTypes = new List<XomType>();
|
|
|
|
public XomString[] XomStrings
|
|
{
|
|
get
|
|
{
|
|
return xomStrings.ToArray();
|
|
}
|
|
}
|
|
|
|
public XomType[] XomTypes
|
|
{
|
|
get
|
|
{
|
|
return xomTypes.ToArray();
|
|
}
|
|
}
|
|
|
|
public XomContainer[] XomContainers
|
|
{
|
|
get
|
|
{
|
|
List<XomContainer> xomContainers = new List<XomContainer>();
|
|
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;
|
|
}
|
|
|
|
public XomString AddOrGetString(string value)
|
|
{
|
|
XomString[] strings = XomStrings;
|
|
for (int i = 0; i < strings.Length; i++)
|
|
if (strings[i].Value == value) return strings[i];
|
|
|
|
XomString str = new XomString(this, value);
|
|
xomStrings.Add(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 GetTypeByName(string typeName)
|
|
{
|
|
foreach(XomType type in XomTypes)
|
|
if (type.Name.Equals(typeName, StringComparison.InvariantCulture)) return type;
|
|
|
|
throw new XomTypeNotFoundException("Type \"" + typeName + "\" was not found in XOM.");
|
|
}
|
|
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 = XomStrings;
|
|
|
|
List<XomBlock> blocks = new List<XomBlock>();
|
|
// 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.Md5, 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<string, int> stringOffsets = new Dictionary<string, int>();
|
|
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 it corrupted?");
|
|
if (schemeBlock is not SchmBlock) throw new XomBlockNotFoundException("XOM contained no SCHM block!, Is it corrupted?");
|
|
if (stringBlock is not StrsBlock) throw new XomBlockNotFoundException("XOM contained no STRS block!, Is it 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<int, string> stringOffsets = new Dictionary<int, string>();
|
|
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[] md5 = type.Md5;
|
|
int numContainers = type.NumCtnr;
|
|
// Create a list of containers, inside this type.
|
|
List<XomContainer> xomContainers = new List<XomContainer>();
|
|
// 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, md5, typeName, xomContainers.ToArray()));
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|