Worms4Editor/LibXom/Data/XomFile.cs

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()));
}
}
}
}