Upload inital code
This commit is contained in:
parent
da262270fc
commit
b271d97983
|
@ -0,0 +1,4 @@
|
|||
Worms4Editor/obj/*
|
||||
Worms4Editor/bin/*
|
||||
LibXom/obj/*
|
||||
LibXom/bin/*
|
|
@ -0,0 +1,26 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LibXom.Blocks
|
||||
{
|
||||
public class CtnrBlock : XomBlock
|
||||
{
|
||||
private byte[] data;
|
||||
public byte[] Data
|
||||
{
|
||||
get
|
||||
{
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
internal CtnrBlock(byte[] data)
|
||||
{
|
||||
this.name = "CTNR";
|
||||
this.data = data;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LibXom.Blocks
|
||||
{
|
||||
public class GuidBlock : XomBlock
|
||||
{
|
||||
|
||||
private int unk0;
|
||||
private int unk1;
|
||||
private int unk2;
|
||||
|
||||
public int Unk0
|
||||
{
|
||||
get
|
||||
{
|
||||
return unk0;
|
||||
}
|
||||
}
|
||||
public int Unk1
|
||||
{
|
||||
get
|
||||
{
|
||||
return unk1;
|
||||
}
|
||||
}
|
||||
|
||||
public int Unk2
|
||||
{
|
||||
get
|
||||
{
|
||||
return unk2;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
internal GuidBlock(int unk0, int unk1, int unk2)
|
||||
{
|
||||
this.name = "GUID";
|
||||
this.unk0 = unk0;
|
||||
this.unk1 = unk1;
|
||||
this.unk2 = unk2;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LibXom.Blocks
|
||||
{
|
||||
public class MoikBlock : XomBlock
|
||||
{
|
||||
|
||||
private int version;
|
||||
private int numCtnr;
|
||||
private int numTypes;
|
||||
|
||||
public int Version
|
||||
{
|
||||
get
|
||||
{
|
||||
return version;
|
||||
}
|
||||
}
|
||||
|
||||
public int NumCtnr
|
||||
{
|
||||
get
|
||||
{
|
||||
return numCtnr;
|
||||
}
|
||||
}
|
||||
|
||||
public int NumTypes
|
||||
{
|
||||
get
|
||||
{
|
||||
return numTypes;
|
||||
}
|
||||
}
|
||||
|
||||
internal MoikBlock(int version, int numCtnr, int numTypes)
|
||||
{
|
||||
this.name = "MOIK";
|
||||
this.version = version;
|
||||
this.numCtnr = numCtnr;
|
||||
this.numTypes = numTypes;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LibXom.Blocks
|
||||
{
|
||||
public class SchmBlock : XomBlock
|
||||
{
|
||||
private int unk0;
|
||||
private int unk1;
|
||||
private int unk2;
|
||||
|
||||
public int Unk0
|
||||
{
|
||||
get
|
||||
{
|
||||
return unk0;
|
||||
}
|
||||
}
|
||||
public int Unk1
|
||||
{
|
||||
get
|
||||
{
|
||||
return unk1;
|
||||
}
|
||||
}
|
||||
|
||||
public int Unk2
|
||||
{
|
||||
get
|
||||
{
|
||||
return unk2;
|
||||
}
|
||||
}
|
||||
internal SchmBlock(int unk0, int unk1, int unk2)
|
||||
{
|
||||
this.name = "SCHM";
|
||||
this.unk0 = unk0;
|
||||
this.unk1 = unk1;
|
||||
this.unk2 = unk2;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LibXom.Blocks
|
||||
{
|
||||
public class StrsBlock : XomBlock
|
||||
{
|
||||
private int strsBufferSz;
|
||||
private int numStrs;
|
||||
private List<int> offsetList = new List<int>();
|
||||
private List<string> stringList = new List<string>();
|
||||
|
||||
public int StringsSectionSz
|
||||
{
|
||||
get
|
||||
{
|
||||
return strsBufferSz;
|
||||
}
|
||||
}
|
||||
public int NumStrs
|
||||
{
|
||||
get
|
||||
{
|
||||
return numStrs;
|
||||
}
|
||||
}
|
||||
public int[] OffsetList
|
||||
{
|
||||
get
|
||||
{
|
||||
return offsetList.ToArray();
|
||||
}
|
||||
}
|
||||
public string[] StringList
|
||||
{
|
||||
get
|
||||
{
|
||||
return stringList.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
internal StrsBlock(int numStrs, int strsBufferSz, int[] offsetList, string[] stringList)
|
||||
{
|
||||
this.name = "STRS";
|
||||
this.numStrs = numStrs;
|
||||
this.strsBufferSz = strsBufferSz;
|
||||
|
||||
this.offsetList.AddRange(offsetList);
|
||||
this.stringList.AddRange(stringList);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LibXom.Blocks
|
||||
{
|
||||
public class TypeBlock : XomBlock
|
||||
{
|
||||
private int numCtnr;
|
||||
private byte[] md5;
|
||||
private string typeName;
|
||||
public int NumCtnr
|
||||
{
|
||||
get
|
||||
{
|
||||
return numCtnr;
|
||||
}
|
||||
}
|
||||
public byte[] Md5
|
||||
{
|
||||
get
|
||||
{
|
||||
return md5;
|
||||
}
|
||||
}
|
||||
public string TypeName
|
||||
{
|
||||
get
|
||||
{
|
||||
return typeName;
|
||||
}
|
||||
}
|
||||
|
||||
internal TypeBlock(int numCtnr, byte[] md5, string typeName)
|
||||
{
|
||||
this.name = "TYPE";
|
||||
this.numCtnr = numCtnr;
|
||||
this.md5 = md5;
|
||||
this.typeName = typeName;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LibXom.Blocks
|
||||
{
|
||||
public class XomBlock
|
||||
{
|
||||
internal string? name = null;
|
||||
internal string Name
|
||||
{
|
||||
get
|
||||
{
|
||||
if (name == null) return "";
|
||||
return name;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
using LibXom.Data;
|
||||
|
||||
namespace LibXom.Blocks
|
||||
{
|
||||
internal class XomBlockHandler
|
||||
{
|
||||
public static XomBlock[] GetBlocksByName(XomBlock[] xomBlocks, string name)
|
||||
{
|
||||
List<XomBlock> sortedXomBlocksList = new List<XomBlock>();
|
||||
foreach (XomBlock xomBlock in xomBlocks)
|
||||
{
|
||||
if (xomBlock.Name.ToLower().Equals(name.ToLower()))
|
||||
{
|
||||
sortedXomBlocksList.Add(xomBlock);
|
||||
}
|
||||
}
|
||||
return sortedXomBlocksList.ToArray();
|
||||
}
|
||||
public static XomBlock? GetBlockByName(XomBlock[] xomBlocks, string name)
|
||||
{
|
||||
XomBlock[] blocks = GetBlocksByName(xomBlocks, name);
|
||||
if (blocks.Length <= 0) return null;
|
||||
return blocks[0];
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,145 @@
|
|||
using LibXom.Exceptions;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LibXom.Data
|
||||
{
|
||||
public class XomCompressor
|
||||
{
|
||||
|
||||
internal static int readCompressedIntFromStream(MemoryStream ms)
|
||||
{
|
||||
byte[] buffer = new byte[4];
|
||||
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
int b = ms.ReadByte();
|
||||
buffer[i] = Convert.ToByte(b);
|
||||
if (b > 0x7F) continue;
|
||||
else break;
|
||||
}
|
||||
|
||||
return DecompressInt(BitConverter.ToInt32(buffer));
|
||||
}
|
||||
public static int[] Decompress(byte[] input)
|
||||
{
|
||||
List<int> decompressedData = new List<int>();
|
||||
|
||||
using (MemoryStream inputStream = new MemoryStream(input))
|
||||
{
|
||||
while (inputStream.Position < inputStream.Length)
|
||||
{
|
||||
int n = readCompressedIntFromStream(inputStream);
|
||||
decompressedData.Add(n);
|
||||
}
|
||||
|
||||
}
|
||||
return decompressedData.ToArray();
|
||||
}
|
||||
private static int getNumberByteCount(int num)
|
||||
{
|
||||
uint unum = Convert.ToUInt32(num);
|
||||
if (unum <= 0xFF) return 1;
|
||||
if (unum <= 0xFFFF) return 2;
|
||||
if (unum <= 0xFFFFFF) return 3;
|
||||
if (unum <= 0xFFFFFFFF) return 4;
|
||||
throw new XomException("Number is too large or too small.");
|
||||
}
|
||||
private static int decompressInt16(int compressedInt)
|
||||
{
|
||||
int mul = (compressedInt >> 8);
|
||||
int add = (compressedInt & 0xFF);
|
||||
|
||||
return (mul * 0x80) + (add % 0x80);
|
||||
}
|
||||
private static int decompressInt24(int compressedInt)
|
||||
{
|
||||
int mul = (compressedInt >> 8) & 0xFF;
|
||||
int mul2 = (compressedInt >> 16);
|
||||
|
||||
int add = (compressedInt & 0xFF);
|
||||
|
||||
return ((mul2 * 0x80) * mul) + (add % 0x80);
|
||||
}
|
||||
|
||||
private static int decompressInt32(int compressedInt)
|
||||
{
|
||||
int mul = (compressedInt >> 8) & 0xFF;
|
||||
int mul2 = (compressedInt >> 16) & 0xFF;
|
||||
int mul3 = (compressedInt >> 24);
|
||||
|
||||
int add = (compressedInt & 0xFF);
|
||||
|
||||
return (((mul3 * 0x80) * mul2 * 0x80) * mul * 0x80) + (add % 0x80);
|
||||
}
|
||||
|
||||
public static int DecompressInt(int compressedInt)
|
||||
{
|
||||
switch (getNumberByteCount(compressedInt))
|
||||
{
|
||||
case 1:
|
||||
return compressedInt;
|
||||
case 2:
|
||||
return decompressInt16(compressedInt);
|
||||
case 3:
|
||||
return decompressInt24(compressedInt);
|
||||
case 4:
|
||||
return decompressInt32(compressedInt);
|
||||
default:
|
||||
throw new XomException("Number is too large or too small.");
|
||||
}
|
||||
}
|
||||
public static int NextCompressedInterger(int compressedInt)
|
||||
{
|
||||
// Some parts of XOM follow a particular pattern,
|
||||
|
||||
// 0x7b, 0x7d, 0x7e, 0x7f, 0x180
|
||||
// 0x1fb, 0x1fd, 0x1fe, 0x1ff 0x280
|
||||
// 0x2fb, 0x2fd, 0x2fe, 0x2ff 0x380
|
||||
|
||||
// This function will take any given int,
|
||||
// and give you the next following this sequence.
|
||||
|
||||
// This is done by XOM to "compress" intergers,
|
||||
// you can read a int and, if its > 0x7f you know theres another byte to be read
|
||||
// otherwise you've read the entire interger, and can read the next one
|
||||
|
||||
// add 1 to current int
|
||||
int nextId = compressedInt + 1;
|
||||
int byteCount = getNumberByteCount(nextId);
|
||||
|
||||
// if its 0, just return 0
|
||||
if (nextId <= 0) return nextId;
|
||||
|
||||
// if its a multiple of 0x80..
|
||||
if (nextId % 0x8000 == 0)
|
||||
{
|
||||
int div = nextId / 0x8000;
|
||||
// check if that divisor is also divisible by 2
|
||||
if (div % 0x2 == 0)
|
||||
nextId += (0x8000); // add 0x8000
|
||||
}
|
||||
if (nextId % 0x80 == 0)
|
||||
{
|
||||
int div = nextId / 0x80;
|
||||
// check if that divisor is also divisible by 2
|
||||
if (div % 0x2 == 0)
|
||||
nextId += (0x80); // add 0x80
|
||||
}
|
||||
|
||||
byte[] buffer = BitConverter.GetBytes(nextId);
|
||||
for(int i = 0; i < 4; i++)
|
||||
{
|
||||
if (buffer[i] > 0x7F) continue;
|
||||
if (buffer[i] == 0) buffer[i]++;
|
||||
break;
|
||||
}
|
||||
nextId = BitConverter.ToInt32(buffer);
|
||||
|
||||
return nextId;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LibXom.Data
|
||||
{
|
||||
public class XomContainer : XomFileComponent
|
||||
{
|
||||
|
||||
private string typeBelongs;
|
||||
internal byte[] data;
|
||||
|
||||
public XomType Type
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.fileBelongs.GetTypeByName(typeBelongs);
|
||||
}
|
||||
}
|
||||
public override int Id
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.fileBelongs.calculateIdForXomFileComponent(this.uuid, fileBelongs.XomContainers);
|
||||
}
|
||||
}
|
||||
public byte[] Data
|
||||
{
|
||||
get
|
||||
{
|
||||
return data;
|
||||
}
|
||||
set
|
||||
{
|
||||
this.Type.ReplaceContainerData(this, value);
|
||||
}
|
||||
}
|
||||
|
||||
public int[] Decompress()
|
||||
{
|
||||
byte[] compressedData = new byte[Data.Length - 3];
|
||||
Array.ConstrainedCopy(Data, 3, compressedData, 0, compressedData.Length);
|
||||
return XomCompressor.Decompress(compressedData);
|
||||
}
|
||||
|
||||
internal XomContainer(XomFile fromFile, string fromType, byte[] data)
|
||||
{
|
||||
fileBelongs = fromFile;
|
||||
typeBelongs = fromType;
|
||||
this.data = data;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,143 @@
|
|||
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();
|
||||
}
|
||||
}
|
||||
|
||||
public XomContainer GetContainerById(int containerId)
|
||||
{
|
||||
foreach(XomContainer container in XomContainers)
|
||||
if (container.Id.Equals(containerId)) return container;
|
||||
throw new XomContainerNotFoundException("No container with id " + containerId + " was found.");
|
||||
}
|
||||
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)
|
||||
{
|
||||
int id = 0;
|
||||
foreach(XomFileComponent component in components)
|
||||
{
|
||||
if (component.uuid.Equals(searchUuid, StringComparison.InvariantCultureIgnoreCase)) return id;
|
||||
id = XomCompressor.NextCompressedInterger(id);
|
||||
}
|
||||
return id;
|
||||
}
|
||||
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 += str.Length + 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];
|
||||
// Were storing the offset here so that i can figure out how to best order the values again
|
||||
// when reconstructing the value (eg make it as close to the actual game)
|
||||
// ideally this wouldnt be needed.
|
||||
// TODO: Figure out how strings are ordered, so i can ditch the offset variable.
|
||||
xomStrings.Add(new XomString(this, offset, 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()));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LibXom.Data
|
||||
{
|
||||
public class XomFileComponent
|
||||
{
|
||||
internal Guid guid = Guid.NewGuid();
|
||||
internal XomFile fileBelongs;
|
||||
public virtual int Id { get; }
|
||||
internal string uuid
|
||||
{
|
||||
get
|
||||
{
|
||||
return guid.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
using LibXom.Exceptions;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LibXom.Data
|
||||
{
|
||||
public class XomString : XomFileComponent
|
||||
{
|
||||
private int offset;
|
||||
private string value;
|
||||
|
||||
public override int Id
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.fileBelongs.calculateIdForXomFileComponent(this.uuid, fileBelongs.XomStrings);
|
||||
}
|
||||
}
|
||||
public int Offset
|
||||
{
|
||||
get
|
||||
{
|
||||
return offset;
|
||||
}
|
||||
}
|
||||
public string Value
|
||||
{
|
||||
get
|
||||
{
|
||||
return value;
|
||||
}
|
||||
}
|
||||
internal XomString(XomFile fromFile, int offset, string value)
|
||||
{
|
||||
this.fileBelongs = fromFile;
|
||||
this.offset = offset;
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
using LibXom.Exceptions;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LibXom.Data
|
||||
{
|
||||
public class XomType : XomFileComponent
|
||||
{
|
||||
private byte[] md5;
|
||||
private string name;
|
||||
private List<XomContainer> xomContainers = new List<XomContainer>();
|
||||
public override int Id
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.fileBelongs.calculateIdForXomFileComponent(this.uuid, fileBelongs.XomTypes);
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] Md5
|
||||
{
|
||||
get
|
||||
{
|
||||
return md5;
|
||||
}
|
||||
}
|
||||
public string Name
|
||||
{
|
||||
get
|
||||
{
|
||||
return name;
|
||||
}
|
||||
}
|
||||
public XomContainer[] Containers
|
||||
{
|
||||
get
|
||||
{
|
||||
return xomContainers.ToArray();
|
||||
}
|
||||
}
|
||||
private int getContainerIndex(XomContainer container)
|
||||
{
|
||||
for (int i = 0; i < xomContainers.Count; i++)
|
||||
{
|
||||
if (xomContainers[i].uuid.Equals(container.uuid, StringComparison.InvariantCulture))
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
throw new XomContainerNotFoundException("Could not find Xom Container with uuid: " + container.uuid + " in type: " + this.Name);
|
||||
}
|
||||
public void ReplaceContainerData(XomContainer container, byte[] newData)
|
||||
{
|
||||
int indx = this.getContainerIndex(container);
|
||||
this.xomContainers[indx].data = newData;
|
||||
}
|
||||
public void DeleteContainer(XomContainer container)
|
||||
{
|
||||
int indx = this.getContainerIndex(container);
|
||||
this.xomContainers.RemoveAt(indx);
|
||||
}
|
||||
|
||||
internal XomType(XomFile fileFrom, byte[] md5, string name, XomContainer[] xomContainers)
|
||||
{
|
||||
fileBelongs = fileFrom;
|
||||
this.md5 = md5;
|
||||
this.name = name;
|
||||
this.xomContainers.AddRange(xomContainers);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LibXom.Exceptions
|
||||
{
|
||||
public class XomBlockNotFoundException : XomException
|
||||
{
|
||||
internal XomBlockNotFoundException(string message) : base(message)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LibXom.Exceptions
|
||||
{
|
||||
public class XomContainerNotFoundException : XomException
|
||||
{
|
||||
internal XomContainerNotFoundException(string message) : base(message)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LibXom.Exceptions
|
||||
{
|
||||
public class XomException : Exception
|
||||
{
|
||||
internal XomException(string message)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LibXom.Exceptions
|
||||
{
|
||||
public class XomFileComponentNotFoundException : XomException
|
||||
{
|
||||
internal XomFileComponentNotFoundException(string message) : base(message)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LibXom.Exceptions
|
||||
{
|
||||
public class XomTypeNotFoundException : XomException
|
||||
{
|
||||
internal XomTypeNotFoundException(string message) : base(message)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
|
@ -0,0 +1,206 @@
|
|||
using LibXom.Blocks;
|
||||
using LibXom.Data;
|
||||
using System.Text;
|
||||
|
||||
namespace LibXom
|
||||
{
|
||||
public class XomReader
|
||||
{
|
||||
private Stream xomStream;
|
||||
private byte[] readBytes(int amt)
|
||||
{
|
||||
byte[] buffer = new byte[amt];
|
||||
xomStream.Read(buffer, 0, amt);
|
||||
return buffer;
|
||||
}
|
||||
private byte readByte()
|
||||
{
|
||||
return Convert.ToByte(xomStream.ReadByte());
|
||||
}
|
||||
private string readStrLen(int len)
|
||||
{
|
||||
byte[] buf = readBytes(len);
|
||||
|
||||
int rlen = 0;
|
||||
for (rlen = 0; rlen < len; rlen++)
|
||||
if (buf[rlen] == 0) break;
|
||||
|
||||
return Encoding.UTF8.GetString(buf, 0, rlen);
|
||||
}
|
||||
private string readCStr()
|
||||
{
|
||||
StringBuilder cstr = new StringBuilder();
|
||||
|
||||
while (true)
|
||||
{
|
||||
char c = (char)readByte();
|
||||
if (c == 0) break;
|
||||
cstr.Append(c);
|
||||
}
|
||||
|
||||
return cstr.ToString();
|
||||
}
|
||||
private int readInt32BE()
|
||||
{
|
||||
byte[] buffer = readBytes(0x4);
|
||||
buffer.Reverse();
|
||||
return BitConverter.ToInt32(buffer);
|
||||
}
|
||||
private void skip(int amt)
|
||||
{
|
||||
xomStream.Seek(amt, SeekOrigin.Current);
|
||||
}
|
||||
private int readInt32()
|
||||
{
|
||||
return BitConverter.ToInt32(readBytes(0x4));
|
||||
}
|
||||
|
||||
public bool bufferEndsWith(List<byte> buffer, byte[] search)
|
||||
{
|
||||
int len = search.Length;
|
||||
|
||||
if (buffer.Count < len) return false;
|
||||
|
||||
byte[] lastSection = new byte[len];
|
||||
|
||||
int ii = 0;
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
int pos = (buffer.Count - len) + i;
|
||||
lastSection[ii] = buffer[pos];
|
||||
ii++;
|
||||
}
|
||||
|
||||
if (lastSection.SequenceEqual(search))
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
public CtnrBlock readCtnr()
|
||||
{
|
||||
List<byte> buffer = new List<byte>();
|
||||
while (xomStream.Position < xomStream.Length)
|
||||
{
|
||||
buffer.Add(readByte());
|
||||
if (bufferEndsWith(buffer, Encoding.UTF8.GetBytes("CTNR")))
|
||||
{
|
||||
skip(-4);
|
||||
int i = buffer.Count - 1;
|
||||
int endAt = i - 4;
|
||||
for (; i != endAt; i--)
|
||||
buffer.RemoveAt(i);
|
||||
return new CtnrBlock(buffer.ToArray());
|
||||
}
|
||||
}
|
||||
return new CtnrBlock(buffer.ToArray());
|
||||
}
|
||||
|
||||
public MoikBlock readMoik()
|
||||
{
|
||||
int version = readInt32BE();
|
||||
skip(0x10);
|
||||
int numTypes = readInt32();
|
||||
int numCtnr = readInt32();
|
||||
int numCtnr2 = readInt32();
|
||||
skip(0x1C);
|
||||
return new MoikBlock(version, numCtnr, numTypes);
|
||||
}
|
||||
public TypeBlock readType()
|
||||
{
|
||||
skip(0x4);
|
||||
int numCtnr = readInt32();
|
||||
skip(0x4);
|
||||
byte[] md5 = readBytes(0x10);
|
||||
string typeName = readStrLen(0x20);
|
||||
return new TypeBlock(numCtnr, md5, typeName);
|
||||
}
|
||||
public SchmBlock readSchm()
|
||||
{
|
||||
int unk0 = readInt32();
|
||||
int unk1 = readInt32();
|
||||
int unk2 = readInt32();
|
||||
return new SchmBlock(unk0, unk1, unk2);
|
||||
}
|
||||
public GuidBlock readGuid()
|
||||
{
|
||||
int unk0 = readInt32();
|
||||
int unk1 = readInt32();
|
||||
int unk2 = readInt32();
|
||||
return new GuidBlock(unk0, unk1, unk2);
|
||||
}
|
||||
public StrsBlock readStrs()
|
||||
{
|
||||
int numStrs = readInt32();
|
||||
int strsSz = readInt32();
|
||||
|
||||
int[] offsets = new int[numStrs];
|
||||
string[] strings = new string[numStrs];
|
||||
|
||||
for (int i = 0; i < numStrs; i++)
|
||||
{
|
||||
offsets[i] = readInt32();
|
||||
}
|
||||
for (int i = 0; i < numStrs; i++)
|
||||
{
|
||||
strings[i] = readCStr();
|
||||
}
|
||||
return new StrsBlock(numStrs, strsSz, offsets, strings); ;
|
||||
}
|
||||
public XomBlock? readBlock()
|
||||
{
|
||||
string hdr = readStrLen(0x4);
|
||||
|
||||
switch (hdr)
|
||||
{
|
||||
case "MOIK":
|
||||
return readMoik();
|
||||
case "TYPE":
|
||||
return readType();
|
||||
case "SCHM":
|
||||
return readSchm();
|
||||
case "GUID":
|
||||
return readGuid();
|
||||
case "STRS":
|
||||
return readStrs();
|
||||
case "CTNR":
|
||||
return readCtnr();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
private XomBlock[] readAllBlocks()
|
||||
{
|
||||
List<XomBlock> xomBlocks = new List<XomBlock>();
|
||||
while (xomStream.Position < xomStream.Length)
|
||||
{
|
||||
XomBlock? block = readBlock();
|
||||
if (block == null) break;
|
||||
xomBlocks.Add(block);
|
||||
}
|
||||
return xomBlocks.ToArray();
|
||||
}
|
||||
public static XomFile ReadXomFile(string xomFilename)
|
||||
{
|
||||
using(MemoryStream ms = new MemoryStream())
|
||||
{
|
||||
using(FileStream fs = File.OpenRead(xomFilename))
|
||||
fs.CopyTo(ms);
|
||||
|
||||
ms.Seek(0, SeekOrigin.Begin);
|
||||
return ReadXomFile(ms);
|
||||
}
|
||||
}
|
||||
public static XomFile ReadXomFile(Stream xomStream)
|
||||
{
|
||||
XomReader reader = new XomReader(xomStream);
|
||||
// Read all blocks
|
||||
XomBlock[] xomBlocks = reader.readAllBlocks();
|
||||
// Create the file object
|
||||
return new XomFile(xomBlocks);
|
||||
}
|
||||
internal XomReader(Stream xom)
|
||||
{
|
||||
this.xomStream = xom;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,162 @@
|
|||
using LibXom.Blocks;
|
||||
using LibXom.Data;
|
||||
using System.Text;
|
||||
|
||||
namespace LibXom
|
||||
{
|
||||
public class XomWriter
|
||||
{
|
||||
private Stream xomStream;
|
||||
private XomFile xomFile;
|
||||
|
||||
private void writeByte(byte b)
|
||||
{
|
||||
xomStream.WriteByte(b);
|
||||
}
|
||||
private int pos()
|
||||
{
|
||||
return Convert.ToInt32(xomStream.Position);
|
||||
}
|
||||
private void rewind(int amt)
|
||||
{
|
||||
xomStream.Seek(-amt, SeekOrigin.Current);
|
||||
}
|
||||
private void skip(int amt)
|
||||
{
|
||||
int cpos = pos();
|
||||
int len = Convert.ToInt32(xomStream.Length);
|
||||
int remain = len - cpos;
|
||||
|
||||
if(amt > remain)
|
||||
{
|
||||
xomStream.Seek(remain, SeekOrigin.Current);
|
||||
amt -= remain;
|
||||
writePadding(0, amt);
|
||||
}
|
||||
else
|
||||
{
|
||||
xomStream.Seek(amt, SeekOrigin.Current);
|
||||
}
|
||||
}
|
||||
private void writePadding(byte pad, int len)
|
||||
{
|
||||
byte[] buf = new byte[len];
|
||||
if(pad != 0)
|
||||
for(int i = 0; i < len; i++)
|
||||
buf[i] = pad;
|
||||
writeBytes(buf);
|
||||
}
|
||||
private void writeBytes(byte[] bytes)
|
||||
{
|
||||
xomStream.Write(bytes, 0, bytes.Length);
|
||||
}
|
||||
private void writeInt32(int value)
|
||||
{
|
||||
byte[] buffer = BitConverter.GetBytes(value);
|
||||
writeBytes(buffer);
|
||||
}
|
||||
private void writeInt32BE(int value)
|
||||
{
|
||||
byte[] buffer = BitConverter.GetBytes(value);
|
||||
buffer.Reverse();
|
||||
writeBytes(buffer);
|
||||
}
|
||||
|
||||
private void writeStrLen(string str, int len)
|
||||
{
|
||||
writeStr(str);
|
||||
|
||||
int padLen = (len - str.Length);
|
||||
if (padLen > 0)
|
||||
skip(padLen);
|
||||
}
|
||||
|
||||
private void writeStr(string str)
|
||||
{
|
||||
byte[] buffer = Encoding.UTF8.GetBytes(str);
|
||||
writeBytes(buffer);
|
||||
}
|
||||
|
||||
private void writeCStr(string str)
|
||||
{
|
||||
writeStr(str);
|
||||
writeByte(0);
|
||||
}
|
||||
private void writeMoik(MoikBlock moikBlock)
|
||||
{
|
||||
writeStr(moikBlock.Name);
|
||||
writeInt32BE(moikBlock.Version);
|
||||
skip(0x10);
|
||||
writeInt32(moikBlock.NumTypes);
|
||||
writeInt32(moikBlock.NumCtnr);
|
||||
writeInt32(moikBlock.NumCtnr);
|
||||
skip(0x1C);
|
||||
}
|
||||
|
||||
private void writeType(TypeBlock typeBlock)
|
||||
{
|
||||
writeStr(typeBlock.Name);
|
||||
skip(0x4);
|
||||
writeInt32(typeBlock.NumCtnr);
|
||||
skip(0x4);
|
||||
writeBytes(typeBlock.Md5);
|
||||
writeStrLen(typeBlock.TypeName, 0x20);
|
||||
}
|
||||
|
||||
|
||||
private void writeGuid(GuidBlock guidBlock)
|
||||
{
|
||||
writeStr(guidBlock.Name);
|
||||
writeInt32(guidBlock.Unk0);
|
||||
writeInt32(guidBlock.Unk1);
|
||||
writeInt32(guidBlock.Unk2);
|
||||
}
|
||||
private void writeSchm(SchmBlock schmBlock)
|
||||
{
|
||||
writeStr(schmBlock.Name);
|
||||
writeInt32(schmBlock.Unk0);
|
||||
writeInt32(schmBlock.Unk1);
|
||||
writeInt32(schmBlock.Unk2);
|
||||
}
|
||||
private void writeStrs(StrsBlock strsBlock)
|
||||
{
|
||||
writeStr(strsBlock.Name);
|
||||
writeInt32(strsBlock.NumStrs);
|
||||
writeInt32(strsBlock.StringsSectionSz);
|
||||
|
||||
foreach (int offset in strsBlock.OffsetList)
|
||||
writeInt32(offset);
|
||||
foreach (string str in strsBlock.StringList)
|
||||
writeCStr(str);
|
||||
}
|
||||
|
||||
private void writeCtnr(CtnrBlock ctnrBlock)
|
||||
{
|
||||
writeStr(ctnrBlock.Name);
|
||||
writeBytes(ctnrBlock.Data);
|
||||
}
|
||||
|
||||
private void writeBlocks(XomBlock[] blocks)
|
||||
{
|
||||
foreach(XomBlock block in blocks)
|
||||
{
|
||||
if (block is MoikBlock) writeMoik(block as MoikBlock);
|
||||
if (block is GuidBlock) writeGuid(block as GuidBlock);
|
||||
if (block is SchmBlock) writeSchm(block as SchmBlock);
|
||||
if (block is StrsBlock) writeStrs(block as StrsBlock);
|
||||
if (block is TypeBlock) writeType(block as TypeBlock);
|
||||
if (block is CtnrBlock) writeCtnr(block as CtnrBlock);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public void WriteXom()
|
||||
{
|
||||
}
|
||||
internal XomWriter(Stream xomStream, XomFile xomFile)
|
||||
{
|
||||
this.xomStream = xomStream;
|
||||
this.xomFile = xomFile;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.4.33205.214
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Worms4Editor", "Worms4Editor\Worms4Editor.csproj", "{FBAA43A5-824F-4C9A-97BC-7B18A42413B9}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LibXom", "LibXom\LibXom.csproj", "{7B60E17C-780E-44D3-BF02-9F5712DD3AE2}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{FBAA43A5-824F-4C9A-97BC-7B18A42413B9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{FBAA43A5-824F-4C9A-97BC-7B18A42413B9}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{FBAA43A5-824F-4C9A-97BC-7B18A42413B9}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{FBAA43A5-824F-4C9A-97BC-7B18A42413B9}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{7B60E17C-780E-44D3-BF02-9F5712DD3AE2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{7B60E17C-780E-44D3-BF02-9F5712DD3AE2}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{7B60E17C-780E-44D3-BF02-9F5712DD3AE2}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{7B60E17C-780E-44D3-BF02-9F5712DD3AE2}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {C7DC1F46-D074-4C13-B78C-AC959EB35C29}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
|
@ -0,0 +1,36 @@
|
|||
using LibXom;
|
||||
using LibXom.Blocks;
|
||||
using LibXom.Data;
|
||||
using System.Security.Cryptography;
|
||||
|
||||
namespace Worms4Editor
|
||||
{
|
||||
internal class Program
|
||||
{
|
||||
static void Main(string[] args)
|
||||
{
|
||||
Console.WriteLine(XomCompressor.DecompressInt(32767).ToString("X"));
|
||||
Console.WriteLine(XomCompressor.DecompressInt(98432).ToString("X"));
|
||||
Console.WriteLine(XomCompressor.DecompressInt(98433).ToString("X"));
|
||||
Console.WriteLine(XomCompressor.DecompressInt(8388607).ToString("X"));
|
||||
int id = 0;
|
||||
for (int i = 0; i < 0x9000; i++) {
|
||||
int d = XomCompressor.DecompressInt(id);
|
||||
if (d != i) Console.WriteLine("FAIL; " + i.ToString("X") + " id " + id.ToString("X") + " d " + d.ToString("X"));
|
||||
else Console.WriteLine("PASS; " + i.ToString("X") + " id " + id.ToString("X") + " d " + d.ToString("X"));
|
||||
id = XomCompressor.NextCompressedInterger(id);
|
||||
}
|
||||
XomFile xfile = XomReader.ReadXomFile(@"SaveGame.xom");
|
||||
XomFile ps2file = XomReader.ReadXomFile(@"ps2.xom");
|
||||
|
||||
XomType type = xfile.GetTypeByName("StoredStatsCollective");
|
||||
File.WriteAllBytes("StoredStatsCollective.bin", type.Containers.First().Data);
|
||||
|
||||
/*foreach(int d in data)
|
||||
{
|
||||
Console.WriteLine(d + ": "+ BitConverter.ToString(ps2file.GetContainerById(d).Data).Replace("-", " "));
|
||||
}*/
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\LibXom\LibXom.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
Loading…
Reference in New Issue