Exemple #1
0
        private object ExportGeneric(ExportState state)
        {
            var input  = this.Data;
            var type   = input.ReadValueU16(LittleEndian);
            var flags  = (GFF.FieldFlags)input.ReadValueU16(LittleEndian);
            var offset = input.ReadValueU32(LittleEndian);

            if (state.References.ContainsKey(offset) == true)
            {
                return(state.References[offset]);
            }

            var def = new GFF.FieldDefinition();

            def.Id     = 0;
            def.Offset = 0;

            if ((flags & GFF.FieldFlags.IsStructure) != 0)
            {
                flags          &= ~GFF.FieldFlags.IsStructure;
                def.Type        = GFF.FieldType.Structure;
                def.StructureId = type;
            }
            else
            {
                def.Type = (GFF.FieldType)type;
            }

            def.Flags = flags;

            var instance = this.ExportField(def, offset, state);

            state.References.Add(offset, instance);
            return(instance);
        }
 private static bool ValidateField(
     GFF.FieldDefinition def,
     Type nativeType)
 {
     // i'm too lazy to write this code right now :)
     return(true);
 }
        private void ImportTypeToStructures(Type root)
        {
            var structs = new List <GFF.StructureDefinition>();
            var map     = new Dictionary <Type, GFF.StructureDefinition>();

            var types = new List <Type>();
            var queue = new Queue <Type>();

            queue.Enqueue(root);

            // discover non-native types we need to add
            while (queue.Count > 0)
            {
                var type = queue.Dequeue();
                types.Add(type);

                foreach (var field in type.GetFields())
                {
                    var subtype = field.FieldType;

                    if (GFF.Builtin.FromNativeType(subtype) != GFF.FieldType.Structure)
                    {
                        continue;
                    }

                    if (subtype.IsGenericType == true &&
                        subtype.GetGenericTypeDefinition() == typeof(List <>))
                    {
                        subtype = subtype.GetGenericArguments()[0];
                    }

                    if (types.Contains(subtype) == true ||
                        queue.Contains(subtype) == true)
                    {
                        continue;
                    }

                    queue.Enqueue(subtype);
                }
            }

            if (types.Count > 0xFFFF)
            {
                throw new InvalidOperationException();
            }

            // now define them
            foreach (var type in types)
            {
                var structDef = new GFF.StructureDefinition();
                var reflected = GFF.ReflectedStructureType.For(type);

                map.Add(type, structDef);
                structs.Add(structDef);

                structDef.Id = reflected.Id;

                uint offset = 0;
                foreach (var kvp in reflected.Fields)
                {
                    var subtype = kvp.Value.Field.FieldType;

                    var fieldDef = new GFF.FieldDefinition();

                    if (subtype.IsGenericType == true &&
                        subtype.GetGenericTypeDefinition() == typeof(List <>))
                    {
                        subtype         = subtype.GetGenericArguments()[0];
                        fieldDef.Flags |= GFF.FieldFlags.IsList;
                    }
                    else if (subtype.IsArray == true)
                    {
                        fieldDef.Flags |= GFF.FieldFlags.IsList;
                    }

                    fieldDef.Id   = kvp.Key;
                    fieldDef.Type = GFF.Builtin
                                    .FromNativeType(subtype, kvp.Value.Type);
                    fieldDef.Offset     = offset;
                    fieldDef.NativeType = subtype;

                    structDef.Fields.Add(fieldDef);

                    if ((fieldDef.Flags & GFF.FieldFlags.IsList) != 0)
                    {
                        offset += 4;
                    }
                    else if (fieldDef.Type == GFF.FieldType.Structure)
                    {
                        throw new NotImplementedException();
                    }
                    else
                    {
                        offset += GFF.Builtin.SizeOf(fieldDef.Type);
                    }
                }

                structDef.DataSize = offset;
            }

            // update ids
            foreach (var type in types)
            {
                var structDef = map[type];

                foreach (var fieldDef in structDef.Fields)
                {
                    if (fieldDef.Type == GFF.FieldType.Structure)
                    {
                        var structRef = map[fieldDef.NativeType];

                        int index = structs.IndexOf(structRef);
                        if (index < 0)
                        {
                            throw new InvalidOperationException();
                        }

                        fieldDef.StructureReference = structRef;
                        fieldDef.StructureId        = (ushort)index;
                    }
                }
            }

            this.Structures = structs;
        }
        private object ExportTypeField(GFF.FieldDefinition def, Type type, long offset)
        {
            if (ValidateField(def, type) == false)
            {
                throw new FormatException("field validation failed");
            }

            var endian = this.Endian;

            var input = this.Data;

            input.Seek(offset + def.Offset, SeekOrigin.Begin);

            if (def.IsReference == true)
            {
                throw new NotSupportedException();
            }

            if (def.IsList == true)
            {
                var listOffset = input.ReadValueU32(endian);
                if (listOffset == 0xFFFFFFFF)
                {
                    if (def.Type == GFF.FieldType.UInt8 &&
                        type == typeof(byte[]))
                    {
                        return(new byte[0]);
                    }
                    else
                    {
                        return(Activator.CreateInstance(type));
                    }
                }

                input.Seek(listOffset, SeekOrigin.Begin);
                var count = input.ReadValueU32(endian);

                switch (def.Type)
                {
                case GFF.FieldType.String:
                {
                    long itemOffset = input.Position;
                    var  list       = new List <string>();
                    for (uint i = 0; i < count; i++)
                    {
                        var dataOffset = input.ReadValueU32(endian);
                        if (dataOffset == 0xFFFFFFFF)
                        {
                            return("");
                        }

                        if (this.FileVersion < 1)
                        {
                            input.Seek(dataOffset, SeekOrigin.Begin);
                            var length = input.ReadValueU32(endian);
                            list.Add(input.ReadString(length * 2, true,
                                                      endian == Endian.Little ? Encoding.Unicode : Encoding.BigEndianUnicode));
                        }
                        else
                        {
                            input.Seek(dataOffset, SeekOrigin.Begin);
                            list.Add(this.StringTable[(int)dataOffset]);
                        }

                        itemOffset += 4;
                        input.Seek(itemOffset, SeekOrigin.Begin);
                    }

                    return(list);
                }

                case GFF.FieldType.Structure:
                {
                    long itemOffset = input.Position;
                    var  subtype    = type.GetGenericArguments()[0];
                    var  subdef     = this.Structures[def.StructureId];
                    var  list       = (IList)Activator.CreateInstance(type);
                    for (uint i = 0; i < count; i++)
                    {
                        list.Add(this.ExportTypeStructure(
                                     subdef,
                                     subtype,
                                     itemOffset));
                        itemOffset += subdef.DataSize;
                    }
                    return(list);
                }

                default:
                {
                    if (def.Type == GFF.FieldType.UInt8 &&
                        type == typeof(byte[]))
                    {
                        var list = new byte[count];
                        input.Read(list, 0, list.Length);
                        return(list);
                    }
                    else
                    {
                        var list = (IList)Activator.CreateInstance(type);
                        for (uint i = 0; i < count; i++)
                        {
                            list.Add(GFF.Builtin.Deserialize(input, def.Type, endian));
                        }
                        return(list);
                    }
                }
                }
            }
            else
            {
                switch (def.Type)
                {
                case GFF.FieldType.String:
                {
                    var dataOffset = input.ReadValueU32(endian);
                    if (dataOffset == 0xFFFFFFFF)
                    {
                        return("");
                    }

                    if (this.FileVersion < 1)
                    {
                        input.Seek(dataOffset, SeekOrigin.Begin);
                        var count = input.ReadValueU32(endian);
                        return(input.ReadString(count * 2, true,
                                                endian == Endian.Little ? Encoding.Unicode : Encoding.BigEndianUnicode));
                    }
                    else
                    {
                        return(this.StringTable[(int)dataOffset]);
                    }
                }

                case GFF.FieldType.TalkString:
                {
                    var tlk = new GFF.Builtins.TalkString();
                    tlk.Id = input.ReadValueU32(endian);

                    var dataOffset = input.ReadValueU32(endian);
                    if (dataOffset == 0xFFFFFFFF)
                    {
                        tlk.String = null;
                    }
                    else if (dataOffset == 0)
                    {
                        tlk.String = "";
                    }
                    else
                    {
                        if (this.FileVersion < 1)
                        {
                            input.Seek(dataOffset, SeekOrigin.Begin);
                            var count = input.ReadValueU32(endian);
                            tlk.String = input.ReadString(count * 2, true,
                                                          endian == Endian.Little ? Encoding.Unicode : Encoding.BigEndianUnicode);
                        }
                        else
                        {
                            tlk.String = this.StringTable[(int)dataOffset];
                        }
                    }

                    return(tlk);
                }

                case GFF.FieldType.Structure:
                {
                    var subdef = this.Structures[def.StructureId];
                    return(this.ExportTypeStructure(subdef, type, input.Position));
                }

                default:
                {
                    return(GFF.Builtin.Deserialize(input, def.Type, endian));
                }
                }
            }
        }
        private void ImportTypeField(GFF.FieldDefinition def, Type type, object value, long offset, ref long newOffset)
        {
            if (ValidateField(def, type) == false)
            {
                throw new FormatException("field validation failed");
            }

            var endian = this.Endian;

            var output = this.Data;

            output.Seek(offset + def.Offset, SeekOrigin.Begin);

            if (def.IsList == true)
            {
                output.WriteValueU32((uint)newOffset, endian);

                output.Seek(newOffset, SeekOrigin.Begin);
                output.WriteValueS32(((IList)value).Count, endian);
                newOffset += 4;

                uint itemSize;
                if (def.Type == GFF.FieldType.Structure)
                {
                    var subdef = this.Structures[def.StructureId];
                    itemSize = subdef.DataSize;
                }
                else
                {
                    itemSize = GFF.Builtin.SizeOf(def.Type);
                }

                var list = (IList)value;
                newOffset += list.Count * itemSize;

                switch (def.Type)
                {
                case GFF.FieldType.String:
                {
                    throw new NotImplementedException();
                }

                case GFF.FieldType.Structure:
                {
                    var subtype = type.GetGenericArguments()[0];
                    var subdef  = this.Structures[def.StructureId];

                    long itemOffset = output.Position;
                    foreach (var item in list)
                    {
                        this.ImportTypeStructure(
                            subdef,
                            item,
                            itemOffset,
                            ref newOffset);
                        itemOffset += itemSize;
                    }

                    break;
                }

                default:
                {
                    if (def.Type == GFF.FieldType.UInt8 &&
                        type == typeof(byte[]))
                    {
                        var bytes = (byte[])list;
                        output.Write(bytes, 0, bytes.Length);
                    }
                    else
                    {
                        long itemOffset = output.Position;
                        foreach (var item in list)
                        {
                            GFF.Builtin.Serialize(output, def.Type, item, endian);
                            itemOffset += itemSize;
                        }
                    }

                    break;
                }
                }
            }
            else
            {
                switch (def.Type)
                {
                case GFF.FieldType.String:
                {
                    var s = (string)value;

                    if (s.Length == 0)
                    {
                        output.WriteValueU32(0xFFFFFFFF, endian);
                    }
                    else
                    {
                        var length = s.Length + 1;

                        output.WriteValueU32((uint)newOffset, endian);

                        output.Seek(newOffset, SeekOrigin.Begin);
                        output.WriteValueS32(length, endian);
                        output.WriteString(s, endian == Endian.Little ? Encoding.Unicode : Encoding.BigEndianUnicode);
                        output.WriteValueU16(0, endian);
                        newOffset += 4 + (2 * length);
                    }

                    break;
                }

                case GFF.FieldType.Structure:
                {
                    var subdef = this.Structures[def.StructureId];
                    this.ImportTypeStructure(
                        subdef,
                        value,
                        output.Position,
                        ref newOffset);
                    break;
                }

                default:
                {
                    GFF.Builtin.Serialize(output, def.Type, value, endian);
                    break;
                }
                }
            }
        }
        public void Deserialize(Stream input)
        {
            input.Seek(0, SeekOrigin.Begin);

            var magic = input.ReadValueU32(Endian.Big);

            if (magic != 0x47464620)
            {
                throw new FormatException();
            }

            var version = input.ReadValueU32(Endian.Big);

            if (version != 0x56342E30 && // 4.0
                version != 0x56342E31)   // 4.1
            {
                throw new FormatException("unsupported version");
            }

            this.FileVersion   = (byte)(version - 0x56342E30);
            this.FilePlatform  = input.ReadValueEnum <GFF.FilePlatform>(Endian.Big);
            this.FormatType    = input.ReadValueEnum <GFF.FormatType>(Endian.Big);
            this.FormatVersion = input.ReadValueU32(Endian.Big);

            var endian = this.FilePlatform == GFF.FilePlatform.PC ? Endian.Little : Endian.Big;

            var structCount  = input.ReadValueU32(endian);
            var stringCount  = this.FileVersion < 1 ? 0 : input.ReadValueU32(endian);
            var stringOffset = this.FileVersion < 1 ? 0 : input.ReadValueU32(endian);
            var dataOffset   = input.ReadValueU32(endian);

            if (this.FileVersion < 1)
            {
                stringOffset = dataOffset;
            }
            else
            {
                if (dataOffset < stringOffset)
                {
                    throw new FormatException();
                }
            }

            this.Structures.Clear();
            for (uint i = 0; i < structCount; i++)
            {
                var structDef = new GFF.StructureDefinition();
                //structDef.Id = input.ReadValueU32(endian);
                structDef.Id = input.ReadValueU32(Endian.Big);
                var fieldCount  = input.ReadValueU32(endian);
                var fieldOffset = input.ReadValueU32(endian);
                structDef.DataSize = input.ReadValueU32(endian);

                long nextOffset = input.Position;

                structDef.Fields.Clear();
                input.Seek(fieldOffset, SeekOrigin.Begin);
                for (uint j = 0; j < fieldCount; j++)
                {
                    var fieldDef = new GFF.FieldDefinition();
                    fieldDef.Id = input.ReadValueS32(endian);
                    var rawFlags = input.ReadValueU32(endian);
                    fieldDef.Offset = input.ReadValueU32(endian);

                    var type  = (ushort)(rawFlags & 0xFFFF);
                    var flags = (GFF.FieldFlags)((rawFlags >> 16) & 0xFFFF);

                    if ((flags & GFF.FieldFlags.IsStructure) != 0)
                    {
                        flags               &= ~GFF.FieldFlags.IsStructure;
                        fieldDef.Type        = GFF.FieldType.Structure;
                        fieldDef.StructureId = type;
                    }
                    else
                    {
                        fieldDef.Type = (GFF.FieldType)type;
                    }

                    fieldDef.Flags = flags;
                    structDef.Fields.Add(fieldDef);
                }

                this.Structures.Add(structDef);
                input.Seek(nextOffset, SeekOrigin.Begin);
            }

            if (this.FileVersion >= 1)
            {
                input.Seek(stringOffset, SeekOrigin.Begin);
                this.StringTable = new List <string>();
                for (uint i = 0; i < stringCount; i++)
                {
                    this.StringTable.Add(input.ReadStringZ(Encoding.UTF8));
                }
            }

            input.Seek(dataOffset, SeekOrigin.Begin);
            this.Data = input.ReadToMemoryStream(input.Length - dataOffset);
        }
        private object ExportGeneric(ExportState state)
        {
            var input = this.Data;
            var type = input.ReadValueU16(LittleEndian);
            var flags = (GFF.FieldFlags)input.ReadValueU16(LittleEndian);
            var offset = input.ReadValueU32(LittleEndian);

            if (state.References.ContainsKey(offset) == true)
            {
                return state.References[offset];
            }

            var def = new GFF.FieldDefinition();
            def.Id = 0;
            def.Offset = 0;

            if ((flags & GFF.FieldFlags.IsStructure) != 0)
            {
                flags &= ~GFF.FieldFlags.IsStructure;
                def.Type = GFF.FieldType.Structure;
                def.StructureId = type;
            }
            else
            {
                def.Type = (GFF.FieldType)type;
            }

            def.Flags = flags;

            var instance = this.ExportField(def, offset, state);
            state.References.Add(offset, instance);
            return instance;
        }
Exemple #8
0
        private void ImportField(GFF.FieldDefinition def, GenericKeyValue data, long offset, ref long newOffset, ImportState state)
        {
            var endian = this.Endian;

            var output = this.Data;

            output.Seek(offset + def.Offset, SeekOrigin.Begin);

            if (def.IsList == true)
            {
                if (def.IsReference == true &&
                    (data.Value != null || def.Type != GFF.FieldType.Generic))
                {
                    throw new NotSupportedException();
                }

                var list = (IList)data.Value;
                if (list == null)
                {
                    output.WriteValueU32(0xFFFFFFFF, endian);
                }
                else
                {
                    output.WriteValueU32((uint)newOffset, endian);

                    output.Seek(newOffset, SeekOrigin.Begin);
                    output.WriteValueS32(list.Count, endian);
                    newOffset += 4;

                    uint itemSize;
                    if (def.Type == GFF.FieldType.Structure)
                    {
                        var subdef = this.Structures[def.StructureId];
                        itemSize = subdef.DataSize;
                    }
                    else
                    {
                        itemSize = GFF.Builtin.SizeOf(def.Type);
                    }

                    newOffset += list.Count * itemSize;

                    switch (def.Type)
                    {
                    case GFF.FieldType.String:
                    {
                        throw new NotImplementedException();
                    }

                    case GFF.FieldType.Structure:
                    {
                        var subdef = this.Structures[def.StructureId];

                        long itemOffset = output.Position;
                        foreach (var item in list)
                        {
                            this.ImportStructure(
                                subdef,
                                (GenericKeyValue)item,
                                itemOffset,
                                ref newOffset,
                                state);
                            itemOffset += itemSize;
                        }

                        break;
                    }

                    default:
                    {
                        if (def.Type == GFF.FieldType.UInt8 &&
                            list.GetType() == typeof(byte[]))
                        {
                            var bytes = (byte[])list;
                            output.Write(bytes, 0, bytes.Length);
                        }
                        else
                        {
                            long itemOffset = output.Position;
                            foreach (var item in list)
                            {
                                GFF.Builtin.Serialize(output, def.Type, item, endian);
                                itemOffset += itemSize;
                            }
                        }

                        break;
                    }
                    }
                }
            }
            else
            {
                if (def.IsReference == true &&
                    def.Type != GFF.FieldType.Structure)
                {
                    throw new NotSupportedException();
                }

                switch (def.Type)
                {
                case GFF.FieldType.String:
                {
                    var s = data.As <string>();

                    if (s == null || s.Length == 0)
                    {
                        output.WriteValueU32(0xFFFFFFFF, endian);
                    }
                    else
                    {
                        var length = s.Length + 1;

                        output.WriteValueU32((uint)newOffset, endian);

                        output.Seek(newOffset, SeekOrigin.Begin);
                        output.WriteValueS32(length, endian);
                        output.WriteString(s, endian == Endian.Little ? Encoding.Unicode : Encoding.BigEndianUnicode);
                        output.WriteValueU16(0, endian);
                        newOffset += 4 + (2 * length);
                    }

                    break;
                }

                case GFF.FieldType.TalkString:
                {
                    var s = data.As <GFF.Builtins.TalkString>();
                    output.WriteValueU32(s.Id, endian);

                    if (s.String == null)
                    {
                        output.WriteValueU32(0xFFFFFFFF, endian);
                    }
                    else if (s.String.Length == 0)
                    {
                        output.WriteValueU32(0, endian);
                    }
                    else
                    {
                        var length = s.String.Length + 1;

                        output.WriteValueU32((uint)newOffset, endian);

                        output.Seek(newOffset, SeekOrigin.Begin);
                        output.WriteValueS32(length, endian);
                        output.WriteString(s.String, endian == Endian.Little ? Encoding.Unicode : Encoding.BigEndianUnicode);
                        output.WriteValueU16(0, endian);
                        newOffset += 4 + (2 * length);
                    }

                    break;
                }

                case GFF.FieldType.Structure:
                {
                    if (def.IsReference == true)
                    {
                        if (data == null || data.Values == null)
                        {
                            output.WriteValueU32(0xFFFFFFFF, endian);
                        }
                        else
                        {
                            output.WriteValueU32((uint)newOffset, endian);
                            output.Seek(newOffset, SeekOrigin.Begin);

                            var subdef = this.Structures[def.StructureId];
                            newOffset += subdef.DataSize;

                            this.ImportStructure(
                                subdef,
                                data,
                                output.Position,
                                ref newOffset,
                                state);
                        }
                    }
                    else
                    {
                        var subdef = this.Structures[def.StructureId];
                        this.ImportStructure(
                            subdef,
                            data,
                            output.Position,
                            ref newOffset,
                            state);
                    }
                    break;
                }

                default:
                {
                    GFF.Builtin.Serialize(output, def.Type, data.Value, endian);
                    break;
                }
                }
            }
        }
Exemple #9
0
        private object ExportField(GFF.FieldDefinition def, long offset, ExportState state)
        {
            var endian = this.Endian;

            var input = this.Data;

            input.Seek(offset + def.Offset, SeekOrigin.Begin);

            if (def.IsReference == true && def.IsList == true)
            {
                var listOffset = input.ReadValueU32(endian);
                if (listOffset == 0xFFFFFFFF)
                {
                    return(null);
                }

                input.Seek(listOffset, SeekOrigin.Begin);
                var count = input.ReadValueU32(endian);

                long itemOffset = input.Position;
                var  list       = new List <object>();
                for (uint i = 0; i < count; i++)
                {
                    list.Add(this.ExportGeneric(state));
                    itemOffset += 8;
                    input.Seek(itemOffset, SeekOrigin.Begin);
                }
                return(list);
            }
            else if (def.IsList == true)
            {
                //var type = GFF.Builtin.ToNativeType(def.Type);

                var listOffset = input.ReadValueU32(endian);
                if (listOffset == 0xFFFFFFFF)
                {
                    /*if (def.Type == GFF.FieldType.UInt8)
                     * {
                     *  return new byte[0];
                     * }
                     * else
                     * {
                     *  throw new NotSupportedException();
                     * }*/
                    return(null);
                }

                input.Seek(listOffset, SeekOrigin.Begin);
                var count = input.ReadValueU32(endian);

                switch (def.Type)
                {
                case GFF.FieldType.String:
                {
                    long itemOffset = input.Position;
                    var  list       = new List <string>();
                    for (uint i = 0; i < count; i++)
                    {
                        var dataOffset = input.ReadValueU32(endian);
                        if (dataOffset == 0xFFFFFFFF)
                        {
                            list.Add(null);
                        }
                        else
                        {
                            if (this.FileVersion < 1)
                            {
                                input.Seek(dataOffset, SeekOrigin.Begin);
                                var length = input.ReadValueU32(endian);
                                list.Add(input.ReadString(length * 2, true,
                                                          endian == Endian.Little ? Encoding.Unicode : Encoding.BigEndianUnicode));
                            }
                            else
                            {
                                list.Add(this.StringTable[(int)dataOffset]);
                            }
                        }

                        itemOffset += 4;
                        input.Seek(itemOffset, SeekOrigin.Begin);
                    }

                    return(list);
                }

                case GFF.FieldType.Structure:
                {
                    long itemOffset = input.Position;
                    var  subdef     = this.Structures[def.StructureId];
                    var  list       = new List <GenericKeyValue>();
                    for (uint i = 0; i < count; i++)
                    {
                        list.Add(this.ExportStructure(
                                     subdef, itemOffset, state));
                        itemOffset += subdef.DataSize;
                    }
                    return(list);
                }

                default:
                {
                    if (def.Type == GFF.FieldType.UInt8)
                    {
                        var list = new byte[count];
                        input.Read(list, 0, list.Length);
                        return(list);
                    }
                    else
                    {
                        var type = GFF.Builtin.ToNativeType(def.Type);
                        var list = (IList)Activator.CreateInstance(typeof(List <>).MakeGenericType(type));
                        for (uint i = 0; i < count; i++)
                        {
                            list.Add(GFF.Builtin.Deserialize(input, def.Type, endian));
                        }
                        return(list);
                    }
                }
                }
            }
            else
            {
                if (def.IsReference == true &&
                    def.Type == GFF.FieldType.Generic)
                {
                    return(this.ExportGeneric(state));
                }
                else if (def.IsReference == true)
                {
                    var referenceOffset = input.ReadValueU32(endian);
                    if (referenceOffset == 0xFFFFFFFF)
                    {
                        return(null);
                    }
                    else if (state.References.ContainsKey(referenceOffset) == true)
                    {
                        return(state.References[referenceOffset]);
                    }

                    input.Seek(referenceOffset, SeekOrigin.Begin);
                }

                switch (def.Type)
                {
                case GFF.FieldType.Generic:
                {
                    throw new FormatException();
                }

                case GFF.FieldType.String:
                {
                    var dataOffset = input.ReadValueU32(endian);
                    if (dataOffset == 0xFFFFFFFF)
                    {
                        return("");
                    }

                    if (this.FileVersion < 1)
                    {
                        input.Seek(dataOffset, SeekOrigin.Begin);
                        var count = input.ReadValueU32(endian);
                        return(input.ReadString(count * 2, true,
                                                endian == Endian.Little ? Encoding.Unicode : Encoding.BigEndianUnicode));
                    }
                    else
                    {
                        return(this.StringTable[(int)dataOffset]);
                    }
                }

                case GFF.FieldType.TalkString:
                {
                    var tlk = new GFF.Builtins.TalkString();
                    tlk.Id = input.ReadValueU32(endian);

                    var dataOffset = input.ReadValueU32(endian);
                    if (dataOffset == 0xFFFFFFFF)
                    {
                        tlk.String = null;
                    }
                    else if (dataOffset == 0)
                    {
                        tlk.String = "";
                    }
                    else
                    {
                        if (this.FileVersion < 1)
                        {
                            input.Seek(dataOffset, SeekOrigin.Begin);
                            var count = input.ReadValueU32(endian);
                            tlk.String = input.ReadString(count * 2, true,
                                                          endian == Endian.Little ? Encoding.Unicode : Encoding.BigEndianUnicode);
                        }
                        else
                        {
                            tlk.String = this.StringTable[(int)dataOffset];
                        }
                    }

                    return(tlk);
                }

                case GFF.FieldType.Structure:
                {
                    var subdef = this.Structures[def.StructureId];
                    return(this.ExportStructure(subdef, input.Position, state));
                }

                default:
                {
                    return(GFF.Builtin.Deserialize(input, def.Type, endian));
                }
                }
            }
        }
        public void Deserialize(Stream input)
        {
            input.Seek(0, SeekOrigin.Begin);

            var magic = input.ReadValueU32(false);
            if (magic != 0x47464620)
            {
                throw new FormatException();
            }

            var version = input.ReadValueU32(false);
            if (version != 0x56342E30 && // 4.0
                version != 0x56342E31) // 4.1
            {
                throw new FormatException("unsupported version");
            }

            this.FileVersion = (byte)(version - 0x56342E30);
            this.FilePlatform = input.ReadValueEnum<GFF.FilePlatform>(false);
            this.FormatType = input.ReadValueEnum<GFF.FormatType>(false);
            this.FormatVersion = input.ReadValueU32(false);

            var littleEndian = this.FilePlatform == GFF.FilePlatform.PC;

            var structCount = input.ReadValueU32(littleEndian);
            var stringCount = this.FileVersion < 1 ? 0 : input.ReadValueU32(littleEndian);
            var stringOffset = this.FileVersion < 1 ? 0 : input.ReadValueU32(littleEndian);
            var dataOffset = input.ReadValueU32(littleEndian);

            if (this.FileVersion < 1)
            {
                stringOffset = dataOffset;
            }
            else
            {
                if (dataOffset < stringOffset)
                {
                    throw new FormatException();
                }
            }

            this.Structures.Clear();
            for (uint i = 0; i < structCount; i++)
            {
                var structDef = new GFF.StructureDefinition();
                //structDef.Id = input.ReadValueU32(littleEndian);
                structDef.Id = input.ReadValueU32(false);
                var fieldCount = input.ReadValueU32(littleEndian);
                var fieldOffset = input.ReadValueU32(littleEndian);
                structDef.DataSize = input.ReadValueU32(littleEndian);

                long nextOffset = input.Position;

                structDef.Fields.Clear();
                input.Seek(fieldOffset, SeekOrigin.Begin);
                for (uint j = 0; j < fieldCount; j++)
                {
                    var fieldDef = new GFF.FieldDefinition();
                    fieldDef.Id = input.ReadValueS32(littleEndian);
                    var rawFlags = input.ReadValueU32(littleEndian);
                    fieldDef.Offset = input.ReadValueU32(littleEndian);

                    var type = (ushort)(rawFlags & 0xFFFF);
                    var flags = (GFF.FieldFlags)((rawFlags >> 16) & 0xFFFF);

                    if ((flags & GFF.FieldFlags.IsStructure) != 0)
                    {
                        flags &= ~GFF.FieldFlags.IsStructure;
                        fieldDef.Type = GFF.FieldType.Structure;
                        fieldDef.StructureId = type;
                    }
                    else
                    {
                        fieldDef.Type = (GFF.FieldType)type;
                    }

                    fieldDef.Flags = flags;
                    structDef.Fields.Add(fieldDef);
                }

                this.Structures.Add(structDef);
                input.Seek(nextOffset, SeekOrigin.Begin);
            }

            if (this.FileVersion >= 1)
            {
                input.Seek(stringOffset, SeekOrigin.Begin);
                this.StringTable = new List<string>();
                for (uint i = 0; i < stringCount; i++)
                {
                    this.StringTable.Add(input.ReadStringZ(Encoding.UTF8));
                }
            }

            input.Seek(dataOffset, SeekOrigin.Begin);
            this.Data = input.ReadToMemoryStream(input.Length - dataOffset);
        }