Upload inital code

This commit is contained in:
Li 2023-01-07 01:36:13 -08:00
parent da262270fc
commit b271d97983
26 changed files with 1338 additions and 0 deletions

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
Worms4Editor/obj/*
Worms4Editor/bin/*
LibXom/obj/*
LibXom/bin/*

View File

@ -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;
}
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}
}
}

View File

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

View File

@ -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;
}
}
}

21
LibXom/Blocks/XomBlock.cs Normal file
View File

@ -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;
}
}
}
}

View File

@ -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];
}
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}
}
}

143
LibXom/Data/XomFile.cs Normal file
View File

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

View File

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

43
LibXom/Data/XomString.cs Normal file
View File

@ -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;
}
}
}

74
LibXom/Data/XomType.cs Normal file
View File

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

View File

@ -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)
{
}
}
}

View File

@ -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)
{
}
}
}

View File

@ -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)
{
}
}
}

View File

@ -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)
{
}
}
}

View File

@ -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)
{
}
}
}

9
LibXom/LibXom.csproj Normal file
View File

@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>

206
LibXom/XomReader.cs Normal file
View File

@ -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;
}
}
}

162
LibXom/XomWriter.cs Normal file
View File

@ -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;
}
}
}

31
Worms4Editor.sln Normal file
View File

@ -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

36
Worms4Editor/Program.cs Normal file
View File

@ -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("-", " "));
}*/
}
}
}

View File

@ -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>