2017-09-21 18:33:05 +08:00
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
using System.Text;
|
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
|
|
|
|
|
namespace CodeWalker.GameFiles
|
|
|
|
|
{
|
|
|
|
|
public class MetaBuilder
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
List<MetaBuilderBlock> Blocks = new List<MetaBuilderBlock>();
|
|
|
|
|
|
2017-09-26 22:54:05 +08:00
|
|
|
|
int MaxBlockLength = 0x4000; //TODO: figure what this should be!
|
2017-09-21 18:33:05 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public MetaBuilderBlock EnsureBlock(MetaName type)
|
|
|
|
|
{
|
|
|
|
|
foreach (var block in Blocks)
|
|
|
|
|
{
|
|
|
|
|
if (block.StructureNameHash == type)
|
|
|
|
|
{
|
|
|
|
|
if (block.TotalSize < MaxBlockLength)
|
|
|
|
|
{
|
|
|
|
|
return block;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
MetaBuilderBlock b = new MetaBuilderBlock();
|
|
|
|
|
b.StructureNameHash = type;
|
|
|
|
|
b.Index = Blocks.Count;
|
|
|
|
|
Blocks.Add(b);
|
|
|
|
|
return b;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public MetaBuilderPointer AddItem<T>(MetaName type, T item) where T : struct
|
|
|
|
|
{
|
|
|
|
|
MetaBuilderBlock block = EnsureBlock(type);
|
|
|
|
|
byte[] data = MetaTypes.ConvertToBytes(item);
|
|
|
|
|
int brem = data.Length % 16;
|
|
|
|
|
if (brem > 0)
|
|
|
|
|
{
|
|
|
|
|
int newlen = data.Length - brem + 16;
|
|
|
|
|
byte[] newdata = new byte[newlen];
|
|
|
|
|
Buffer.BlockCopy(data, 0, newdata, 0, data.Length);
|
|
|
|
|
data = newdata; //make sure item size is multiple of 16... so pointers don't need sub offsets!
|
|
|
|
|
}
|
|
|
|
|
int idx = block.AddItem(data);
|
|
|
|
|
MetaBuilderPointer r = new MetaBuilderPointer();
|
2017-09-28 12:28:09 +08:00
|
|
|
|
r.BlockID = block.Index + 1;
|
|
|
|
|
r.Offset = (idx * data.Length);
|
2017-09-21 18:33:05 +08:00
|
|
|
|
r.Length = data.Length;
|
|
|
|
|
return r;
|
|
|
|
|
}
|
|
|
|
|
public MetaBuilderPointer AddItemArray<T>(MetaName type, T[] items) where T : struct
|
|
|
|
|
{
|
|
|
|
|
MetaBuilderBlock block = EnsureBlock(type);
|
|
|
|
|
byte[] data = MetaTypes.ConvertArrayToBytes(items);
|
|
|
|
|
int datalen = data.Length;
|
|
|
|
|
int newlen = datalen;
|
|
|
|
|
int lenrem = newlen % 16;
|
|
|
|
|
if (lenrem != 0)
|
|
|
|
|
{
|
|
|
|
|
newlen += (16 - lenrem);
|
|
|
|
|
}
|
|
|
|
|
byte[] newdata = new byte[newlen];
|
|
|
|
|
Buffer.BlockCopy(data, 0, newdata, 0, datalen);
|
2017-09-28 12:28:09 +08:00
|
|
|
|
int offs = block.TotalSize;
|
2017-09-21 18:33:05 +08:00
|
|
|
|
int idx = block.AddItem(newdata);
|
|
|
|
|
MetaBuilderPointer r = new MetaBuilderPointer();
|
2017-09-28 12:28:09 +08:00
|
|
|
|
r.BlockID = block.Index + 1;
|
|
|
|
|
r.Offset = offs; //(idx * data.Length);;
|
2017-09-21 18:33:05 +08:00
|
|
|
|
r.Length = items.Length;
|
|
|
|
|
return r;
|
|
|
|
|
}
|
|
|
|
|
public MetaBuilderPointer AddString(string str)
|
|
|
|
|
{
|
|
|
|
|
MetaBuilderBlock block = EnsureBlock(MetaName.STRING);
|
|
|
|
|
byte[] data = Encoding.ASCII.GetBytes(str);
|
|
|
|
|
int datalen = data.Length;
|
|
|
|
|
int newlen = datalen;
|
|
|
|
|
int lenrem = newlen % 16;
|
|
|
|
|
if (lenrem != 0) //need to pad the data length up to multiple of 16.
|
|
|
|
|
{
|
|
|
|
|
newlen += (16 - lenrem);
|
|
|
|
|
}
|
|
|
|
|
byte[] newdata = new byte[newlen];
|
|
|
|
|
Buffer.BlockCopy(data, 0, newdata, 0, datalen);
|
2017-09-28 12:28:09 +08:00
|
|
|
|
int offs = block.TotalSize;
|
2017-09-21 18:33:05 +08:00
|
|
|
|
int idx = block.AddItem(newdata);
|
|
|
|
|
MetaBuilderPointer r = new MetaBuilderPointer();
|
2017-09-28 12:28:09 +08:00
|
|
|
|
r.BlockID = block.Index + 1;
|
|
|
|
|
r.Offset = offs;// (idx * data.Length);
|
2017-09-21 18:33:05 +08:00
|
|
|
|
r.Length = datalen; //actual length of string.
|
|
|
|
|
return r;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public MetaPOINTER AddItemPtr<T>(MetaName type, T item) where T : struct //helper method for AddItem<T>
|
|
|
|
|
{
|
|
|
|
|
var ptr = AddItem(type, item);
|
2017-09-28 12:28:09 +08:00
|
|
|
|
return new MetaPOINTER(ptr.BlockID, ptr.Offset, 0);
|
2017-09-21 18:33:05 +08:00
|
|
|
|
}
|
|
|
|
|
public Array_Structure AddItemArrayPtr<T>(MetaName type, T[] items) where T : struct //helper method for AddItemArray<T>
|
|
|
|
|
{
|
|
|
|
|
if ((items == null) || (items.Length == 0)) return new Array_Structure();
|
|
|
|
|
var ptr = AddItemArray(type, items);
|
|
|
|
|
return new Array_Structure(ptr);
|
|
|
|
|
}
|
|
|
|
|
public Array_uint AddHashArrayPtr(MetaHash[] items)
|
|
|
|
|
{
|
|
|
|
|
if ((items == null) || (items.Length == 0)) return new Array_uint();
|
|
|
|
|
var ptr = AddItemArray(MetaName.HASH, items);
|
|
|
|
|
return new Array_uint(ptr);
|
|
|
|
|
}
|
|
|
|
|
public Array_ushort AddUshortArrayPtr(ushort[] items)
|
|
|
|
|
{
|
|
|
|
|
if ((items == null) || (items.Length == 0)) return new Array_ushort();
|
|
|
|
|
var ptr = AddItemArray(MetaName.USHORT, items);
|
|
|
|
|
return new Array_ushort(ptr);
|
|
|
|
|
}
|
|
|
|
|
public CharPointer AddStringPtr(string str) //helper method for AddString
|
|
|
|
|
{
|
|
|
|
|
var ptr = AddString(str);
|
|
|
|
|
return new CharPointer(ptr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public Array_StructurePointer AddPointerArray(MetaPOINTER[] arr)
|
|
|
|
|
{
|
|
|
|
|
if ((arr == null) || (arr.Length == 0)) return new Array_StructurePointer();
|
|
|
|
|
var ptr = AddItemArray(MetaName.POINTER, arr);
|
|
|
|
|
Array_StructurePointer sp = new Array_StructurePointer();
|
|
|
|
|
sp.Count1 = (ushort)arr.Length;
|
|
|
|
|
sp.Count2 = sp.Count1;
|
|
|
|
|
sp.Pointer = ptr.Pointer;
|
|
|
|
|
return sp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public Array_StructurePointer AddItemPointerArrayPtr<T>(MetaName type, T[] items) where T : struct
|
|
|
|
|
{
|
|
|
|
|
//helper method for creating a pointer array
|
|
|
|
|
if ((items == null) || (items.Length == 0)) return new Array_StructurePointer();
|
|
|
|
|
|
|
|
|
|
MetaPOINTER[] ptrs = new MetaPOINTER[items.Length];
|
|
|
|
|
for (int i = 0; i < items.Length; i++)
|
|
|
|
|
{
|
|
|
|
|
ptrs[i] = AddItemPtr(type, items[i]);
|
|
|
|
|
}
|
|
|
|
|
return AddPointerArray(ptrs);
|
|
|
|
|
|
|
|
|
|
//Array_StructurePointer sp = new Array_StructurePointer();
|
|
|
|
|
//sp.Count1 = (ushort)items.Length;
|
|
|
|
|
//sp.Count2 = sp.Count1;
|
|
|
|
|
//for (int i = 0; i < items.Length; i++)
|
|
|
|
|
//{
|
|
|
|
|
// var item = items[i];
|
|
|
|
|
// var meptr = AddItemPtr(type, item);
|
|
|
|
|
// var mptr = AddItem(MetaName.POINTER, meptr);
|
|
|
|
|
// if (i == 0)
|
|
|
|
|
// {
|
|
|
|
|
// sp.Pointer = mptr.Pointer; //main pointer points to the first item.
|
|
|
|
|
// }
|
|
|
|
|
//}
|
|
|
|
|
//return sp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public Array_StructurePointer AddWrapperArrayPtr(MetaWrapper[] items)
|
|
|
|
|
{
|
|
|
|
|
if ((items == null) || (items.Length == 0)) return new Array_StructurePointer();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
MetaPOINTER[] ptrs = new MetaPOINTER[items.Length];
|
|
|
|
|
for (int i = 0; i < items.Length; i++)
|
|
|
|
|
{
|
|
|
|
|
ptrs[i] = items[i].Save(this);
|
|
|
|
|
}
|
|
|
|
|
return AddPointerArray(ptrs);
|
|
|
|
|
|
|
|
|
|
//Array_StructurePointer sp = new Array_StructurePointer();
|
|
|
|
|
//sp.Count1 = (ushort)items.Length;
|
|
|
|
|
//sp.Count2 = sp.Count1;
|
|
|
|
|
//for (int i = 0; i < items.Length; i++)
|
|
|
|
|
//{
|
|
|
|
|
// var item = items[i];
|
|
|
|
|
// var meptr = item.Save(this);
|
|
|
|
|
// var mptr = AddItem(MetaName.POINTER, meptr);
|
|
|
|
|
// if (i == 0)
|
|
|
|
|
// {
|
|
|
|
|
// sp.Pointer = mptr.Pointer; //main pointer points to the first item.
|
|
|
|
|
// }
|
|
|
|
|
//}
|
|
|
|
|
//return sp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public Array_Structure AddWrapperArray(MetaWrapper[] items)
|
|
|
|
|
{
|
|
|
|
|
if ((items == null) || (items.Length == 0)) return new Array_Structure();
|
|
|
|
|
|
|
|
|
|
var sa = new Array_Structure();
|
|
|
|
|
sa.Count1 = (ushort)items.Length;
|
|
|
|
|
sa.Count2 = sa.Count1;
|
|
|
|
|
for (int i = 0; i < items.Length; i++)
|
|
|
|
|
{
|
|
|
|
|
var item = items[i];
|
|
|
|
|
var meptr = item.Save(this);
|
|
|
|
|
if (i == 0)
|
|
|
|
|
{
|
|
|
|
|
MetaBuilderPointer mbp = new MetaBuilderPointer();
|
2017-09-28 12:28:09 +08:00
|
|
|
|
mbp.BlockID = meptr.BlockID;
|
|
|
|
|
mbp.Offset = meptr.Offset;
|
2017-09-21 18:33:05 +08:00
|
|
|
|
sa.Pointer = mbp.Pointer;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return sa;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public byte[] GetData()
|
|
|
|
|
{
|
|
|
|
|
int totlen = 0;
|
|
|
|
|
for (int i = 0; i < Blocks.Count; i++)
|
|
|
|
|
{
|
|
|
|
|
totlen += Blocks[i].TotalSize;
|
|
|
|
|
}
|
|
|
|
|
byte[] data = new byte[totlen];
|
|
|
|
|
int offset = 0;
|
|
|
|
|
for (int i = 0; i < Blocks.Count; i++)
|
|
|
|
|
{
|
|
|
|
|
var block = Blocks[i];
|
|
|
|
|
for (int j = 0; j < block.Items.Count; j++)
|
|
|
|
|
{
|
|
|
|
|
var bdata = block.Items[j];
|
|
|
|
|
Buffer.BlockCopy(bdata, 0, data, offset, bdata.Length);
|
|
|
|
|
offset += bdata.Length;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (offset != data.Length)
|
|
|
|
|
{ }
|
|
|
|
|
return data;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Dictionary<MetaName, MetaStructureInfo> StructureInfos = new Dictionary<MetaName, MetaStructureInfo>();
|
|
|
|
|
Dictionary<MetaName, MetaEnumInfo> EnumInfos = new Dictionary<MetaName, MetaEnumInfo>();
|
|
|
|
|
|
|
|
|
|
public void AddStructureInfo(MetaName name)
|
|
|
|
|
{
|
|
|
|
|
if (!StructureInfos.ContainsKey(name))
|
|
|
|
|
{
|
|
|
|
|
MetaStructureInfo si = MetaTypes.GetStructureInfo(name);
|
|
|
|
|
if (si != null)
|
|
|
|
|
{
|
|
|
|
|
StructureInfos[name] = si;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
public void AddEnumInfo(MetaName name)
|
|
|
|
|
{
|
|
|
|
|
if (!EnumInfos.ContainsKey(name))
|
|
|
|
|
{
|
|
|
|
|
MetaEnumInfo ei = MetaTypes.GetEnumInfo(name);
|
|
|
|
|
if (ei != null)
|
|
|
|
|
{
|
|
|
|
|
EnumInfos[name] = ei;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public Meta GetMeta()
|
|
|
|
|
{
|
|
|
|
|
Meta m = new Meta();
|
|
|
|
|
m.FileVFT = 0x405bc808;
|
|
|
|
|
m.FileUnknown = 1;
|
|
|
|
|
m.Unknown_10h = 0x50524430;
|
|
|
|
|
m.Unknown_14h = 0x0079;
|
|
|
|
|
|
|
|
|
|
m.RootBlockIndex = 1; //assume first block is root. todo: make adjustable?
|
|
|
|
|
|
|
|
|
|
m.StructureInfos = new ResourceSimpleArray<MetaStructureInfo>();
|
|
|
|
|
foreach (var si in StructureInfos.Values)
|
|
|
|
|
{
|
|
|
|
|
m.StructureInfos.Add(si);
|
|
|
|
|
}
|
|
|
|
|
m.StructureInfosCount = (short)m.StructureInfos.Count;
|
|
|
|
|
|
|
|
|
|
m.EnumInfos = new ResourceSimpleArray<MetaEnumInfo>();
|
|
|
|
|
foreach (var ei in EnumInfos.Values)
|
|
|
|
|
{
|
|
|
|
|
m.EnumInfos.Add(ei);
|
|
|
|
|
}
|
|
|
|
|
m.EnumInfosCount = (short)m.EnumInfos.Count;
|
|
|
|
|
|
|
|
|
|
m.DataBlocks = new ResourceSimpleArray<MetaDataBlock>();
|
|
|
|
|
foreach (var bb in Blocks)
|
|
|
|
|
{
|
|
|
|
|
m.DataBlocks.Add(bb.GetMetaDataBlock());
|
|
|
|
|
}
|
|
|
|
|
m.DataBlocksCount = (short)m.DataBlocks.Count;
|
|
|
|
|
|
|
|
|
|
return m;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public class MetaBuilderBlock
|
|
|
|
|
{
|
|
|
|
|
public MetaName StructureNameHash { get; set; }
|
|
|
|
|
public List<byte[]> Items { get; set; } = new List<byte[]>();
|
|
|
|
|
public int TotalSize { get; set; } = 0;
|
|
|
|
|
public int Index { get; set; } = 0;
|
|
|
|
|
|
|
|
|
|
public int AddItem(byte[] item)
|
|
|
|
|
{
|
|
|
|
|
int idx = Items.Count;
|
|
|
|
|
Items.Add(item);
|
|
|
|
|
TotalSize += item.Length;
|
|
|
|
|
return idx;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public uint BasePointer
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
return (((uint)Index + 1) & 0xFFF);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public MetaDataBlock GetMetaDataBlock()
|
|
|
|
|
{
|
|
|
|
|
if (TotalSize <= 0) return null;
|
|
|
|
|
|
|
|
|
|
byte[] data = new byte[TotalSize];
|
|
|
|
|
int offset = 0;
|
|
|
|
|
for (int j = 0; j < Items.Count; j++)
|
|
|
|
|
{
|
|
|
|
|
var bdata = Items[j];
|
|
|
|
|
Buffer.BlockCopy(bdata, 0, data, offset, bdata.Length);
|
|
|
|
|
offset += bdata.Length;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
MetaDataBlock db = new MetaDataBlock();
|
|
|
|
|
db.StructureNameHash = StructureNameHash;
|
|
|
|
|
db.DataLength = TotalSize;
|
|
|
|
|
db.Data = data;
|
|
|
|
|
|
|
|
|
|
return db;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public struct MetaBuilderPointer
|
|
|
|
|
{
|
2017-09-28 12:28:09 +08:00
|
|
|
|
public int BlockID { get; set; } //1-based id
|
|
|
|
|
public int Offset { get; set; } //byte offset
|
2017-09-21 18:33:05 +08:00
|
|
|
|
public int Length { get; set; } //for temp use...
|
|
|
|
|
public uint Pointer
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
2017-09-28 12:28:09 +08:00
|
|
|
|
uint bidx = (((uint)BlockID) & 0xFFF);
|
|
|
|
|
uint offs = (((uint)Offset) & 0xFFFFF) << 12;
|
2017-09-21 18:33:05 +08:00
|
|
|
|
return bidx + offs;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|