Fs Update
This commit is contained in:
352
BMGEditor/FS/Bcsv.cs
Normal file
352
BMGEditor/FS/Bcsv.cs
Normal file
@@ -0,0 +1,352 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace BMGEditor
|
||||
{
|
||||
public class Bcsv
|
||||
{
|
||||
public Bcsv(FileBase file)
|
||||
{
|
||||
m_File = file;
|
||||
m_File.BigEndian = true;
|
||||
m_File.Encoding = Encoding.GetEncoding(20127);
|
||||
|
||||
Fields = new Dictionary<uint, Field>();
|
||||
Entries = new List<Entry>();
|
||||
|
||||
m_File.Stream.Position = 0;
|
||||
uint entrycount = m_File.Reader.ReadUInt32();
|
||||
uint fieldcount = m_File.Reader.ReadUInt32();
|
||||
uint dataoffset = m_File.Reader.ReadUInt32();
|
||||
uint entrydatasize = m_File.Reader.ReadUInt32();
|
||||
|
||||
uint stringtableoffset = (uint)(dataoffset + (entrycount * entrydatasize));
|
||||
|
||||
for (uint i = 0; i < fieldcount; i++)
|
||||
{
|
||||
Field field = new Field();
|
||||
m_File.Stream.Position = 0x10 + (0xC * i);
|
||||
|
||||
field.NameHash = m_File.Reader.ReadUInt32();
|
||||
field.Mask = m_File.Reader.ReadUInt32();
|
||||
field.EntryOffset = m_File.Reader.ReadUInt16();
|
||||
field.ShiftAmount = m_File.Reader.ReadByte();
|
||||
field.Type = m_File.Reader.ReadByte();
|
||||
|
||||
string fieldname = Bcsv.HashToFieldName(field.NameHash);
|
||||
Fields.Add(field.NameHash, field);
|
||||
}
|
||||
|
||||
for (uint i = 0; i < entrycount; i++)
|
||||
{
|
||||
Entry entry = new Entry();
|
||||
|
||||
foreach (Field field in Fields.Values)
|
||||
{
|
||||
m_File.Stream.Position = dataoffset + (i * entrydatasize) + field.EntryOffset;
|
||||
|
||||
object val = null;
|
||||
switch (field.Type)
|
||||
{
|
||||
case 0:
|
||||
case 3:
|
||||
val = (uint)((m_File.Reader.ReadUInt32() & field.Mask) >> field.ShiftAmount);
|
||||
break;
|
||||
|
||||
case 4:
|
||||
val = (ushort)((m_File.Reader.ReadUInt16() & field.Mask) >> field.ShiftAmount);
|
||||
break;
|
||||
|
||||
case 5:
|
||||
val = (byte)((m_File.Reader.ReadByte() & field.Mask) >> field.ShiftAmount);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
val = m_File.Reader.ReadSingle();
|
||||
break;
|
||||
|
||||
case 6:
|
||||
uint str_offset = m_File.Reader.ReadUInt32();
|
||||
m_File.Stream.Position = stringtableoffset + str_offset;
|
||||
val = m_File.ReadString();
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new NotImplementedException("Bcsv: unsupported data type " + field.Type.ToString());
|
||||
}
|
||||
|
||||
entry.Add(field.NameHash, val);
|
||||
}
|
||||
|
||||
Entries.Add(entry);
|
||||
}
|
||||
}
|
||||
|
||||
public void Flush()
|
||||
{
|
||||
int[] datasizes = { 4, -1, 4, 4, 2, 1, 4 };
|
||||
uint entrysize = 0;
|
||||
|
||||
foreach (Field field in Fields.Values)
|
||||
{
|
||||
ushort fieldend = (ushort)(field.EntryOffset + datasizes[field.Type]);
|
||||
if (fieldend > entrysize) entrysize = fieldend;
|
||||
}
|
||||
|
||||
uint dataoffset = (uint)(0x10 + (0xC * Fields.Count));
|
||||
uint stringtableoffset = (uint)(dataoffset + (Entries.Count * entrysize));
|
||||
uint curstring = 0;
|
||||
|
||||
m_File.Stream.SetLength(stringtableoffset);
|
||||
|
||||
m_File.Stream.Position = 0;
|
||||
m_File.Writer.Write((uint)Entries.Count);
|
||||
m_File.Writer.Write((uint)Fields.Count);
|
||||
m_File.Writer.Write(dataoffset);
|
||||
m_File.Writer.Write(entrysize);
|
||||
|
||||
foreach (Field field in Fields.Values)
|
||||
{
|
||||
m_File.Writer.Write(field.NameHash);
|
||||
m_File.Writer.Write(field.Mask);
|
||||
m_File.Writer.Write(field.EntryOffset);
|
||||
m_File.Writer.Write(field.ShiftAmount);
|
||||
m_File.Writer.Write(field.Type);
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
Dictionary<string, uint> stringoffsets = new Dictionary<string, uint>();
|
||||
|
||||
foreach (Entry entry in Entries)
|
||||
{
|
||||
foreach (Field field in Fields.Values)
|
||||
{
|
||||
uint valoffset = (uint)(dataoffset + (i * entrysize) + field.EntryOffset);
|
||||
m_File.Stream.Position = valoffset;
|
||||
|
||||
switch (field.Type)
|
||||
{
|
||||
case 0:
|
||||
case 3:
|
||||
{
|
||||
uint val = m_File.Reader.ReadUInt32();
|
||||
val &= ~field.Mask;
|
||||
val |= (((uint)entry[field.NameHash] << field.ShiftAmount) & field.Mask);
|
||||
|
||||
m_File.Stream.Position = valoffset;
|
||||
m_File.Writer.Write(val);
|
||||
}
|
||||
break;
|
||||
|
||||
case 4:
|
||||
{
|
||||
ushort val = m_File.Reader.ReadUInt16();
|
||||
val &= (ushort)(~field.Mask);
|
||||
val |= (ushort)(((ushort)entry[field.NameHash] << field.ShiftAmount) & field.Mask);
|
||||
|
||||
m_File.Stream.Position = valoffset;
|
||||
m_File.Writer.Write(val);
|
||||
}
|
||||
break;
|
||||
|
||||
case 5:
|
||||
{
|
||||
byte val = m_File.Reader.ReadByte();
|
||||
val &= (byte)(~field.Mask);
|
||||
val |= (byte)(((byte)entry[field.NameHash] << field.ShiftAmount) & field.Mask);
|
||||
|
||||
m_File.Stream.Position = valoffset;
|
||||
m_File.Writer.Write(val);
|
||||
}
|
||||
break;
|
||||
|
||||
case 2:
|
||||
m_File.Writer.Write((float)entry[field.NameHash]);
|
||||
break;
|
||||
|
||||
case 6:
|
||||
{
|
||||
string val = (string)entry[field.NameHash];
|
||||
if (stringoffsets.ContainsKey(val))
|
||||
m_File.Writer.Write(stringoffsets[val]);
|
||||
else
|
||||
{
|
||||
stringoffsets.Add(val, curstring);
|
||||
m_File.Writer.Write(curstring);
|
||||
m_File.Stream.Position = stringtableoffset + curstring;
|
||||
curstring += (uint)m_File.WriteString(val);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
m_File.Flush();
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
m_File.Close();
|
||||
}
|
||||
|
||||
|
||||
public Field AddField(string name, int offset, byte type, uint mask, int shift, object defaultval)
|
||||
{
|
||||
int[] datasizes = { 4, -1, 4, 4, 2, 1, 4 };
|
||||
|
||||
AddHash(name); // hehe
|
||||
|
||||
int nbytes = datasizes[type];
|
||||
|
||||
if (type == 2 || type == 6)
|
||||
{
|
||||
mask = 0xFFFFFFFF;
|
||||
shift = 0;
|
||||
}
|
||||
|
||||
if (offset == -1)
|
||||
{
|
||||
foreach (Field field in Fields.Values)
|
||||
{
|
||||
ushort fieldend = (ushort)(field.EntryOffset + datasizes[field.Type]);
|
||||
if (fieldend > offset) offset = fieldend;
|
||||
}
|
||||
}
|
||||
|
||||
Field newfield = new Field();
|
||||
newfield.Name = name;
|
||||
newfield.NameHash = Bcsv.FieldNameToHash(name);
|
||||
newfield.Mask = mask;
|
||||
newfield.ShiftAmount = (byte)shift;
|
||||
newfield.Type = type;
|
||||
newfield.EntryOffset = (ushort)offset;
|
||||
Fields.Add(newfield.NameHash, newfield);
|
||||
|
||||
foreach (Entry entry in Entries)
|
||||
{
|
||||
entry.Add(name, defaultval);
|
||||
}
|
||||
|
||||
return newfield;
|
||||
}
|
||||
|
||||
public void RemoveField(string name)
|
||||
{
|
||||
uint hash = Bcsv.FieldNameToHash(name);
|
||||
Fields.Remove(hash);
|
||||
|
||||
foreach (Entry entry in Entries)
|
||||
{
|
||||
entry.Remove(hash);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class Field
|
||||
{
|
||||
public uint NameHash;
|
||||
public uint Mask;
|
||||
public ushort EntryOffset;
|
||||
public byte ShiftAmount;
|
||||
public byte Type;
|
||||
|
||||
public string Name;
|
||||
}
|
||||
|
||||
public class Entry : Dictionary<uint, object>
|
||||
{
|
||||
public Entry()
|
||||
: base()
|
||||
{ }
|
||||
|
||||
public object this[string key]
|
||||
{
|
||||
get
|
||||
{
|
||||
return this[Bcsv.FieldNameToHash(key)];
|
||||
}
|
||||
set
|
||||
{
|
||||
this[Bcsv.FieldNameToHash(key)] = value;
|
||||
}
|
||||
}
|
||||
|
||||
public void Add(string key, object val)
|
||||
{
|
||||
this.Add(Bcsv.FieldNameToHash(key), val);
|
||||
}
|
||||
|
||||
public bool ContainsKey(string key)
|
||||
{
|
||||
return this.ContainsKey(Bcsv.FieldNameToHash(key));
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
string str = "BcsvEntry:";
|
||||
|
||||
foreach (KeyValuePair<uint, object> field in this)
|
||||
{
|
||||
str += " [" + field.Key.ToString("X8");
|
||||
if (Bcsv.m_HashTable.ContainsKey(field.Key))
|
||||
str += " (" + Bcsv.HashToFieldName(field.Key) + ")";
|
||||
str += "]=[" + field.Value.ToString() + "]";
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
private FileBase m_File;
|
||||
|
||||
public Dictionary<uint, Field> Fields;
|
||||
public List<Entry> Entries;
|
||||
|
||||
|
||||
// Field name hash support functions
|
||||
// the hash->string table is meant for debugging purposes and
|
||||
// shouldn't be used by proper code
|
||||
|
||||
public static uint FieldNameToHash(string field)
|
||||
{
|
||||
uint ret = 0;
|
||||
foreach (char ch in field)
|
||||
{
|
||||
ret *= 0x1F;
|
||||
ret += ch;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static string HashToFieldName(uint hash)
|
||||
{
|
||||
if (!m_HashTable.ContainsKey(hash))
|
||||
return string.Format("[{0:X8}]", hash);
|
||||
|
||||
return m_HashTable[hash];
|
||||
}
|
||||
|
||||
public static void AddHash(string field)
|
||||
{
|
||||
uint hash = FieldNameToHash(field);
|
||||
if (!m_HashTable.ContainsKey(hash))
|
||||
m_HashTable.Add(hash, field);
|
||||
}
|
||||
|
||||
public static void PopulateHashtable()
|
||||
{
|
||||
m_HashTable = new Dictionary<uint, string>();
|
||||
AddHash("wowHowDidYouFindThis");
|
||||
}
|
||||
|
||||
public static Dictionary<uint, string> m_HashTable;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user