コード例 #1
0
        public static Vdf ParseVdf(string filename)
        {
            using (Bwd2Reader br = new Bwd2Reader(filename))
            {
                Vdf vdf = new Vdf();
                br.FindNext("VDFC");

                vdf.Name                = br.ReadCString(20);
                vdf.VehicleType         = br.ReadUInt32();
                vdf.VehicleSize         = br.ReadUInt32();
                vdf.LODDistance1        = br.ReadSingle();
                vdf.LODDistance2        = br.ReadSingle();
                vdf.LODDistance3        = br.ReadSingle();
                vdf.LODDistance4        = br.ReadSingle();
                vdf.LODDistance5        = br.ReadSingle();
                vdf.Mass                = br.ReadSingle();
                vdf.CollisionMultiplier = br.ReadSingle();
                vdf.DragCoefficient     = br.ReadSingle();
                vdf.Unknown             = br.ReadUInt32();
                if (br.Current.DataLength == 77)
                {
                    vdf.EltFile = br.ReadCString(13);
                }

                br.FindNext("SOBJ");
                vdf.SOBJGeoName = br.ReadCString(8);

                vdf.VLocs = new List <VLoc>();
                br.FindNext("VLOC");
                while (br.Current != null && br.Current.Name != "EXIT")
                {
                    VLoc vloc = new VLoc
                    {
                        Number   = br.ReadUInt32(),
                        Right    = new Vector3(br.ReadSingle(), br.ReadSingle(), br.ReadSingle()),
                        Up       = new Vector3(br.ReadSingle(), br.ReadSingle(), br.ReadSingle()),
                        Forward  = new Vector3(br.ReadSingle(), br.ReadSingle(), br.ReadSingle()),
                        Position = new Vector3(br.ReadSingle(), br.ReadSingle(), br.ReadSingle())
                    };
                    vdf.VLocs.Add(vloc);

                    br.Next();
                }

                br.FindNext("VGEO");
                uint numParts = br.ReadUInt32();
                vdf.PartsThirdPerson = new List <SdfPart[]>(4);
                for (int damageState = 0; damageState < 4; damageState++)
                {
                    SdfPart[] parts = new SdfPart[numParts];
                    for (int i = 0; i < numParts; i++)
                    {
                        SdfPart sdfPart = new SdfPart();
                        sdfPart.Name       = br.ReadCString(8);
                        sdfPart.Right      = new Vector3(br.ReadSingle(), br.ReadSingle(), br.ReadSingle());
                        sdfPart.Up         = new Vector3(br.ReadSingle(), br.ReadSingle(), br.ReadSingle());
                        sdfPart.Forward    = new Vector3(br.ReadSingle(), br.ReadSingle(), br.ReadSingle());
                        sdfPart.Position   = new Vector3(br.ReadSingle(), br.ReadSingle(), br.ReadSingle());
                        sdfPart.ParentName = br.ReadCString(8);
                        br.Position       += 36;

                        parts[i] = sdfPart;
                    }
                    vdf.PartsThirdPerson.Add(parts);
                }
                br.Position += 100 * numParts * 12;

                vdf.PartsFirstPerson = new SdfPart[numParts];
                for (int i = 0; i < numParts; i++)
                {
                    SdfPart sdfPart = new SdfPart();
                    sdfPart.Name       = br.ReadCString(8);
                    sdfPart.Right      = new Vector3(br.ReadSingle(), br.ReadSingle(), br.ReadSingle());
                    sdfPart.Up         = new Vector3(br.ReadSingle(), br.ReadSingle(), br.ReadSingle());
                    sdfPart.Forward    = new Vector3(br.ReadSingle(), br.ReadSingle(), br.ReadSingle());
                    sdfPart.Position   = new Vector3(br.ReadSingle(), br.ReadSingle(), br.ReadSingle());
                    sdfPart.ParentName = br.ReadCString(8);
                    br.Position       += 36;

                    vdf.PartsFirstPerson[i] = sdfPart;
                }

                br.FindNext("COLP");

                float zMaxOuter = br.ReadSingle();
                float zMaxInner = br.ReadSingle();
                float zMinInner = br.ReadSingle();
                float zMinOuter = br.ReadSingle();

                float xMaxOuter = br.ReadSingle();
                float xMaxInner = br.ReadSingle();
                float xMinInner = br.ReadSingle();
                float xMinOuter = br.ReadSingle();

                float yMaxOuter = br.ReadSingle();
                float yMaxInner = br.ReadSingle();
                float yMinInner = br.ReadSingle();
                float yMinOuter = br.ReadSingle();

                Bounds innerBounds = new Bounds();
                innerBounds.SetMinMax(new Vector3(xMinInner, yMinInner, zMinInner), new Vector3(xMaxInner, yMaxInner, zMaxInner));
                vdf.BoundsInner = innerBounds;

                Bounds outerBounds = new Bounds();
                outerBounds.SetMinMax(new Vector3(xMinOuter, yMinOuter, zMinOuter), new Vector3(xMaxOuter, yMaxOuter, zMaxOuter));
                vdf.BoundsOuter = outerBounds;

                br.FindNext("WLOC");
                vdf.WheelLoc = new WheelLoc[6];
                for (int i = 0; i < 6; i++)
                {
                    WheelLoc wheelLoc = vdf.WheelLoc[i] = new WheelLoc();
                    uint     unk1     = br.ReadUInt32();
                    wheelLoc.Right    = new Vector3(br.ReadSingle(), br.ReadSingle(), br.ReadSingle());
                    wheelLoc.Up       = new Vector3(br.ReadSingle(), br.ReadSingle(), br.ReadSingle());
                    wheelLoc.Forward  = new Vector3(br.ReadSingle(), br.ReadSingle(), br.ReadSingle());
                    wheelLoc.Position = new Vector3(br.ReadSingle(), br.ReadSingle(), br.ReadSingle());
                    float unk2 = br.ReadSingle();
                }

                vdf.HLocs = new List <HLoc>();
                br.FindNext("HLOC");
                while (br.Current != null && br.Current.Name != "EXIT")
                {
                    HLoc hloc = new HLoc
                    {
                        Label           = br.ReadCString(16),
                        HardpointIndex  = br.ReadUInt32(),
                        FacingDirection = br.ReadUInt32(),
                        MeshType        = (HardpointMeshType)br.ReadUInt32(),
                        Right           = new Vector3(br.ReadSingle(), br.ReadSingle(), br.ReadSingle()),
                        Up       = new Vector3(br.ReadSingle(), br.ReadSingle(), br.ReadSingle()),
                        Forward  = new Vector3(br.ReadSingle(), br.ReadSingle(), br.ReadSingle()),
                        Position = new Vector3(br.ReadSingle(), br.ReadSingle(), br.ReadSingle()),
                        Unk      = br.ReadSingle()
                    };
                    vdf.HLocs.Add(hloc);

                    br.Next();
                }

                if (!SpriteManager.Instance.Initialised)
                {
                    if (vdf.EltFile == null)
                    {
                        vdf.Etbls = new List <ETbl>();
                        br.FindNext("ETBL");

                        while (br.Current != null && br.Current.Name != "EXIT")
                        {
                            long tableEnd = br.Current.DataPosition + br.Current.DataLength;
                            while (br.Position < tableEnd)
                            {
                                ETbl etbl = new ETbl
                                {
                                    MapFile          = br.ReadCString(13),
                                    IsReferenceImage = br.ReadUInt32() == 1,
                                    ItemCount        = br.ReadUInt32(),
                                };

                                uint itemCount = etbl.ItemCount;
                                etbl.Items = new Dictionary <string, ETbl.ETblItem>((int)itemCount);
                                for (uint i = 0; i < itemCount; ++i)
                                {
                                    ETbl.ETblItem etblItem = new ETbl.ETblItem
                                    {
                                        Name    = br.ReadCString(16),
                                        XOffset = br.ReadInt32(),
                                        YOffset = br.ReadInt32(),
                                        Width   = br.ReadInt32(),
                                        Height  = br.ReadInt32()
                                    };

                                    etbl.Items.Add(etblItem.Name, etblItem);
                                }

                                vdf.Etbls.Add(etbl);
                            }

                            br.Next();
                        }
                    }
                    else
                    {
                        vdf.Etbls = EltParser.ParseElt(vdf.EltFile);
                    }

                    if (vdf.Etbls != null && vdf.Etbls.Count > 0)
                    {
                        SpriteManager.Instance.Initialise(vdf);
                    }
                }

                return(vdf);
            }
        }
コード例 #2
0
        public static Vcf ParseVcf(string filename)
        {
            Vcf vcf = new Vcf();

            using (Bwd2Reader br = new Bwd2Reader(filename))
            {
                br.FindNext("VCFC");

                vcf.VariantName             = br.ReadCString(16);
                vcf.VdfFilename             = br.ReadCString(13);
                vcf.VtfFilename             = br.ReadCString(13);
                vcf.EngineType              = br.ReadUInt32();
                vcf.SuspensionType          = br.ReadUInt32();
                vcf.BrakesType              = br.ReadUInt32();
                vcf.WdfFrontFilename        = br.ReadCString(13);
                vcf.WdfMidFilename          = br.ReadCString(13);
                vcf.WdfBackFilename         = br.ReadCString(13);
                vcf.ArmorFront              = br.ReadUInt32();
                vcf.ArmorLeft               = br.ReadUInt32();
                vcf.ArmorRight              = br.ReadUInt32();
                vcf.ArmorRear               = br.ReadUInt32();
                vcf.ChassisFront            = br.ReadUInt32();
                vcf.ChassisLeft             = br.ReadUInt32();
                vcf.ChassisRight            = br.ReadUInt32();
                vcf.ChassisRear             = br.ReadUInt32();
                vcf.ArmorOrChassisLeftToAdd = br.ReadUInt32();

                vcf.Specials = new List <SpecialType>();
                if (br.TryFindNext("SPEC"))
                {
                    vcf.Specials.Add((SpecialType)br.ReadInt32());
                }
                if (br.TryFindNext("SPEC"))
                {
                    vcf.Specials.Add((SpecialType)br.ReadInt32());
                }
                if (br.TryFindNext("SPEC"))
                {
                    vcf.Specials.Add((SpecialType)br.ReadInt32());
                }

                br.FindNext("WEPN");
                vcf.Weapons = new List <VcfWeapon>();
                while (br.Current != null && br.Current.Name != "EXIT")
                {
                    VcfWeapon vcfWeapon = new VcfWeapon
                    {
                        MountPoint  = br.ReadInt32(),
                        GdfFilename = br.ReadCString(13)
                    };

                    vcfWeapon.Gdf = GdfParser.ParseGdf(vcfWeapon.GdfFilename);

                    vcf.Weapons.Add(vcfWeapon);
                    br.Next();
                }
            }

            if (vcf.WdfFrontFilename.ToUpper() != "NULL")
            {
                vcf.FrontWheelDef = WdfParser.ParseWdf(vcf.WdfFrontFilename);
            }
            if (vcf.WdfMidFilename.ToUpper() != "NULL")
            {
                vcf.MidWheelDef = WdfParser.ParseWdf(vcf.WdfMidFilename);
            }
            if (vcf.WdfBackFilename.ToUpper() != "NULL")
            {
                vcf.BackWheelDef = WdfParser.ParseWdf(vcf.WdfBackFilename);
            }

            return(vcf);
        }
コード例 #3
0
ファイル: MsnMissionParser.cs プロジェクト: r1sc/Open76
        public static MissonDefinition ReadMsnMission(string filename)
        {
            MissonDefinition mdef = new MissonDefinition();

            using (Bwd2Reader msn = new Bwd2Reader(filename))
            {
                msn.FindNext("WDEF");
                using (Bwd2Reader wdef = new Bwd2Reader(msn))
                {
                    wdef.FindNext("WRLD");
                    wdef.Position               += 30;
                    mdef.PaletteFilePath         = wdef.ReadCString(13);
                    mdef.LumaTableFilePath       = wdef.ReadCString(13);
                    mdef.XlucencyTableFilePath   = wdef.ReadCString(13);
                    mdef.ObjectiveFilePath       = wdef.ReadCString(13);
                    mdef.SkyTextureFilePath      = wdef.ReadCString(13);
                    mdef.ScroungeTextureFilePath = wdef.ReadCString(13);
                    mdef.SurfaceTextureFilePath  = wdef.ReadCString(13);
                    mdef.LevelMapFilePath        = wdef.ReadCString(13);
                    mdef.HzdFilePath             = wdef.ReadCString(13);
                }
                msn.FindNext("TDEF");
                List <float[, ]> heights = new List <float[, ]>();
                using (Bwd2Reader tdef = new Bwd2Reader(msn))
                {
                    tdef.FindNext("ZMAP");
                    byte   numUniqueTerrainPatches = tdef.ReadByte();
                    byte[] patchConfig             = tdef.ReadBytes(80 * 80);

                    tdef.FindNext("ZONE");
                    byte   unk             = tdef.ReadByte();
                    string terrainFilePath = tdef.ReadCString(13);

                    using (FastBinaryReader terr = VirtualFilesystem.Instance.GetFileStream(terrainFilePath))
                    {
                        for (int i = 0; i < numUniqueTerrainPatches; i++)
                        {
                            float[,] h = new float[129, 129];
                            for (int z = 0; z < 128; z++)
                            {
                                for (int x = 0; x < 128; x++)
                                {
                                    ushort tpoint = terr.ReadUInt16();
                                    float  height = (tpoint & 0xFFF) / 4096.0f;
                                    h[z, x] = height;
                                }
                            }

                            heights.Add(h);
                        }
                    }

                    Vector2 botLeft  = new Vector2(80, 80);
                    Vector2 topRight = new Vector2(0, 0);

                    float[,] defaultHeights = new float[129, 129];
                    for (int z = 0; z < 80; z++)
                    {
                        for (int x = 0; x < 80; x++)
                        {
                            byte patchIdx = patchConfig[z * 80 + x];
                            if (patchIdx == 0xFF)
                            {
                                //mdef.TerrainPatches[x, z] = new TerrainPatch(defaultTerrainData);
                            }
                            else
                            {
                                if (x < botLeft.x)
                                {
                                    botLeft.x = x;
                                }
                                if (z < botLeft.y)
                                {
                                    botLeft.y = z;
                                }
                                if (x > topRight.x)
                                {
                                    topRight.x = x;
                                }
                                if (z > topRight.y)
                                {
                                    topRight.y = z;
                                }

                                float[,] h = heights[patchIdx];
                                int rightPatchIdx = x == 79 ? 0xFF : patchConfig[z * 80 + x + 1];
                                float[,] rightHeights = rightPatchIdx == 0xFF ? defaultHeights : heights[rightPatchIdx];
                                for (int xx = 0; xx < 129; xx++)
                                {
                                    h[xx, 128] = rightHeights[xx, 0];
                                }

                                int bottomPatchIdx = z == 79 ? 0xFF : patchConfig[(z + 1) * 80 + x];
                                float[,] bottomHeights = bottomPatchIdx == 0xFF ? defaultHeights : heights[bottomPatchIdx];
                                for (int zz = 0; zz < 129; zz++)
                                {
                                    h[128, zz] = bottomHeights[0, zz];
                                }

                                int bottomRightPatchIdx = z == 79 || x == 79 ? 0xFF : patchConfig[(z + 1) * 80 + x + 1];
                                float[,] bottomRightHeights = bottomRightPatchIdx == 0xFF
                                    ? defaultHeights
                                    : heights[bottomRightPatchIdx];
                                h[128, 128] = bottomRightHeights[0, 0];

                                TerrainData tdata = CreateTerrainData();
                                tdata.SetHeights(0, 0, h);
                                mdef.TerrainPatches[x, z] = new TerrainPatch(tdata);
                            }
                        }
                    }
                    mdef.Middle = (topRight + botLeft) / 2.0f;
                }

                msn.FindNext("RDEF");
                using (Bwd2Reader rdef = new Bwd2Reader(msn))
                {
                    rdef.FindNext("RSEG");
                    while (rdef.Current != null && rdef.Current.Name != "EXIT")
                    {
                        uint segmentType       = rdef.ReadUInt32();
                        uint segmentPieceCount = rdef.ReadUInt32();
                        Road road = new Road
                        {
                            SegmentType  = (RoadSegmentType)segmentType,
                            RoadSegments = new RoadSegment[segmentPieceCount]
                        };

                        for (int i = 0; i < segmentPieceCount; i++)
                        {
                            RoadSegment roadSegment = new RoadSegment
                            {
                                Left  = new Vector3(rdef.ReadSingle(), rdef.ReadSingle(), rdef.ReadSingle()),
                                Right = new Vector3(rdef.ReadSingle(), rdef.ReadSingle(), rdef.ReadSingle())
                            };

                            Vector3 pos            = roadSegment.Left;
                            int     patchPosX      = (int)(pos.x / 640.0f);
                            int     patchPosZ      = (int)(pos.z / 640.0f);
                            float   localPositionX = (pos.x % 640) / 640.0f;
                            float   localPositionZ = (pos.z % 640) / 640.0f;
                            float   y =
                                mdef.TerrainPatches[patchPosX, patchPosZ].TerrainData.GetInterpolatedHeight(localPositionX,
                                                                                                            localPositionZ) + 0.1f;
                            pos.y            = y;
                            roadSegment.Left = pos;

                            pos            = roadSegment.Right;
                            patchPosX      = (int)(pos.x / 640.0f);
                            patchPosZ      = (int)(pos.z / 640.0f);
                            localPositionX = (pos.x % 640) / 640.0f;
                            localPositionZ = (pos.z % 640) / 640.0f;
                            y =
                                mdef.TerrainPatches[patchPosX, patchPosZ].TerrainData.GetInterpolatedHeight(localPositionX,
                                                                                                            localPositionZ) + 0.1f;
                            pos.y             = y;
                            roadSegment.Right = pos;

                            road.RoadSegments[i] = roadSegment;
                            //TODO: Figure out
                        }
                        mdef.Roads.Add(road);

                        rdef.Next();
                    }
                }

                msn.FindNext("ODEF");
                using (Bwd2Reader odef = new Bwd2Reader(msn))
                {
                    odef.FindNext("OBJ");
                    while (odef.Current.Name != "EXIT")
                    {
                        byte[]        rawlabel     = odef.ReadBytes(8);
                        int           labelhigh    = 0;
                        StringBuilder labelBuilder = new StringBuilder();
                        for (int i = 0; i < 8; i++)
                        {
                            byte v = rawlabel[i];
                            if (v > 0x7f)
                            {
                                labelhigh = (labelhigh << 1) | 0x01;
                            }
                            else
                            {
                                labelhigh = (labelhigh << 1) & 0xfe;
                            }
                            v = (byte)(v & 0x7f);
                            if (v != 0)
                            {
                                labelBuilder.Append((char)v);
                            }
                        }
                        string  label   = labelBuilder.ToString();
                        Vector3 right   = new Vector3(odef.ReadSingle(), odef.ReadSingle(), odef.ReadSingle());
                        Vector3 upwards = new Vector3(odef.ReadSingle(), odef.ReadSingle(), odef.ReadSingle());
                        Vector3 forward = new Vector3(odef.ReadSingle(), odef.ReadSingle(), odef.ReadSingle());
                        Vector3 pos     = new Vector3(odef.ReadSingle(), odef.ReadSingle(), odef.ReadSingle());

                        odef.Position += 36;
                        ClassId classId = (ClassId)odef.ReadUInt32();
                        ushort  flags   = odef.ReadUInt16();
                        ushort  teamId  = odef.ReadUInt16();

                        Vector3 localPosition = new Vector3(pos.x % 640, pos.y, pos.z % 640);
                        int     patchPosX     = (int)(pos.x / 640.0f);
                        int     patchPosZ     = (int)(pos.z / 640.0f);
                        mdef.TerrainPatches[patchPosX, patchPosZ].Objects.Add(new Odef
                        {
                            Label         = label,
                            Id            = labelhigh,
                            LocalPosition = localPosition,
                            ClassId       = classId,
                            TeamId        = teamId,
                            IsPlayer      = flags == 16,
                            LocalRotation = Quaternion.LookRotation(forward, upwards)
                        });

                        odef.Next();
                    }
                }

                msn.FindNext("LDEF");
                using (Bwd2Reader ldef = new Bwd2Reader(msn))
                {
                    ldef.FindNext("OBJ");
                    while (ldef.Current != null && ldef.Current.Name != "EXIT")
                    {
                        string label = ldef.ReadCString(8);
                        ldef.Position += 84;
                        ClassId classId = (ClassId)ldef.ReadUInt32();
                        ldef.ReadUInt32();
                        uint numStrings = ldef.ReadUInt32();

                        Vector3[] stringPositions = new Vector3[numStrings];
                        for (int i = 0; i < numStrings; i++)
                        {
                            Vector3 stringPos = new Vector3(ldef.ReadSingle(), ldef.ReadSingle(), ldef.ReadSingle());
                            stringPositions[i] = stringPos;
                        }

                        mdef.StringObjects.Add(new Ldef
                        {
                            Label           = label,
                            StringPositions = stringPositions
                        });

                        ldef.Next();
                    }
                }

                msn.FindNext("ADEF");
                using (Bwd2Reader adef = new Bwd2Reader(msn))
                {
                    adef.FindNext("FSM");
                    if (adef.Current != null && adef.Current.DataLength > 0)
                    {
                        mdef.FSM = new FSM();

                        mdef.FSM.ActionTable = new string[adef.ReadUInt32()];
                        for (int i = 0; i < mdef.FSM.ActionTable.Length; i++)
                        {
                            mdef.FSM.ActionTable[i] = adef.ReadCString(40);
                        }

                        uint numEntities = adef.ReadUInt32();
                        mdef.FSM.EntityTable = new FSMEntity[numEntities];

                        for (int i = 0; i < numEntities; i++)
                        {
                            string label    = adef.ReadCString(40);
                            byte[] rawlabel = adef.ReadBytes(8);

                            int           labelhigh    = 0;
                            StringBuilder labelBuilder = new StringBuilder();
                            for (int j = 0; j < 8; j++)
                            {
                                byte v = rawlabel[j];
                                if (v > 0x7f)
                                {
                                    labelhigh = (labelhigh << 1) | 0x01;
                                }
                                else
                                {
                                    labelhigh = (labelhigh << 1) & 0xfe;
                                }

                                v = (byte)(v & 0x7f);
                                if (v != 0)
                                {
                                    labelBuilder.Append((char)v);
                                }
                            }

                            mdef.FSM.EntityTable[i] = new FSMEntity
                            {
                                Label = label,
                                Value = labelBuilder.ToString(),
                                Id    = labelhigh
                            };
                        }

                        mdef.FSM.SoundClipTable = new string[adef.ReadUInt32()];
                        for (int i = 0; i < mdef.FSM.SoundClipTable.Length; i++)
                        {
                            mdef.FSM.SoundClipTable[i] = adef.ReadCString(40);
                        }

                        uint numPaths = adef.ReadUInt32();
                        mdef.FSM.Paths = new FSMPath[numPaths];
                        for (int i = 0; i < numPaths; i++)
                        {
                            string       name  = adef.ReadCString(40);
                            I76Vector3[] nodes = new I76Vector3[adef.ReadUInt32()];
                            for (int p = 0; p < nodes.Length; p++)
                            {
                                nodes[p] = new I76Vector3(adef.ReadSingle(), adef.ReadSingle(), adef.ReadSingle());
                            }

                            mdef.FSM.Paths[i] = new FSMPath
                            {
                                Name  = name,
                                Nodes = nodes
                            };
                        }

                        uint numMachines = adef.ReadUInt32();
                        mdef.FSM.StackMachines = new StackMachine[numMachines];
                        for (int i = 0; i < numMachines; i++)
                        {
                            long next = adef.Position + 168;

                            StackMachine machine = new StackMachine();
                            machine.StartAddress     = adef.ReadUInt32();
                            machine.InitialArguments = new int[adef.ReadUInt32()];

                            for (int j = 0; j < machine.InitialArguments.Length; j++)
                            {
                                machine.InitialArguments[j] = adef.ReadInt32();
                            }

                            adef.Position = next;

                            mdef.FSM.StackMachines[i] = machine;
                        }

                        mdef.FSM.Constants = new IntRef[adef.ReadUInt32()];
                        for (int i = 0; i < mdef.FSM.Constants.Length; i++)
                        {
                            mdef.FSM.Constants[i] = new IntRef(adef.ReadInt32());
                        }

                        mdef.FSM.ByteCode = new ByteCode[adef.ReadUInt32()];
                        for (int i = 0; i < mdef.FSM.ByteCode.Length; i++)
                        {
                            ByteCode byteCode = mdef.FSM.ByteCode[i] = new ByteCode();
                            byteCode.OpCode = (OpCode)adef.ReadUInt32();
                            byteCode.Value  = adef.ReadInt32();
                        }
                    }
                }
            }
            return(mdef);
        }
コード例 #4
0
        public static Gdf ParseGdf(string filename)
        {
            using (Bwd2Reader br = new Bwd2Reader(filename))
            {
                Gdf gdf = new Gdf();

                br.FindNext("REV");
                int rev = br.ReadInt32();

                br.FindNext("GDFC");
                {
                    gdf.Name = br.ReadCString(16);
                    int   unk1 = br.ReadInt32();
                    int   unk2 = br.ReadInt32();
                    float unk3 = br.ReadSingle();
                    float unk4 = br.ReadSingle();
                    float unk5 = br.ReadSingle();
                    float unk6 = br.ReadSingle();
                    br.ReadBytes(4);
                    gdf.Damage     = br.ReadInt32();
                    gdf.Health     = br.ReadInt32();
                    gdf.WeaponMass = br.ReadSingle();
                    string unk7 = br.ReadCString(12);
                    ushort unk8 = br.ReadUInt16();
                    float  unk9 = br.ReadSingle();
                    gdf.BurstRate      = 1f / br.ReadSingle();
                    gdf.FiringRate     = 1f / br.ReadSingle();
                    gdf.FireAmount     = br.ReadInt32();
                    gdf.BulletVelocity = br.ReadSingle();
                    gdf.WeaponGroup    = br.ReadInt32();
                    gdf.AmmoCount      = br.ReadInt32();
                    float unk10 = br.ReadSingle();

                    gdf.FireSpriteName = br.ReadCString(13);
                    gdf.SoundName      = br.ReadCString(13);
                    if (rev == 8)
                    {
                        int unk11 = br.ReadInt32(); // Always 1?
                        gdf.EnabledSpriteName  = br.ReadCString(16);
                        gdf.DisabledSpriteName = br.ReadCString(16);
                    }
                }

                br.FindNext("GPOF"); // 192 bytes

                SdfPart[] parts = new SdfPart[4];
                for (int i = 0; i < 4; ++i)
                {
                    SdfPart part = new SdfPart();
                    part.Right    = new Vector3(br.ReadSingle(), br.ReadSingle(), br.ReadSingle());
                    part.Up       = new Vector3(br.ReadSingle(), br.ReadSingle(), br.ReadSingle());
                    part.Forward  = new Vector3(br.ReadSingle(), br.ReadSingle(), br.ReadSingle());
                    part.Position = new Vector3(br.ReadSingle(), br.ReadSingle(), br.ReadSingle());
                    parts[i]      = part;
                }

                br.FindNext("GGEO"); // 4 bytes
                {
                    int numParts = br.ReadInt32();
                    if (numParts > 0)
                    {
                        int topPartsIndex    = 0;
                        int sidePartsIndex   = 0;
                        int turretPartsIndex = 0;
                        int insidePartsIndex = 0;

                        const int weaponSlots = 4;
                        int       totalSlots  = weaponSlots * numParts;
                        for (int i = 0; i < totalSlots; ++i)
                        {
                            string partName = br.ReadCString(8);
                            if (partName == "NULL")
                            {
                                br.Position += 92;
                            }
                            else
                            {
                                SdfPart sdfPart = new SdfPart
                                {
                                    Name       = partName,
                                    Right      = new Vector3(br.ReadSingle(), br.ReadSingle(), br.ReadSingle()),
                                    Up         = new Vector3(br.ReadSingle(), br.ReadSingle(), br.ReadSingle()),
                                    Forward    = new Vector3(br.ReadSingle(), br.ReadSingle(), br.ReadSingle()),
                                    Position   = new Vector3(br.ReadSingle(), br.ReadSingle(), br.ReadSingle()),
                                    ParentName = br.ReadCString(8)
                                };

                                char partType = partName[3];
                                switch (partType)
                                {
                                case 'P':
                                    if (gdf.TopParts == null)
                                    {
                                        gdf.TopParts = new SdfPart[numParts];
                                    }

                                    gdf.TopParts[topPartsIndex++] = sdfPart;
                                    break;

                                case 'S':
                                    if (gdf.SideParts == null)
                                    {
                                        gdf.SideParts = new SdfPart[numParts];
                                    }

                                    gdf.SideParts[sidePartsIndex++] = sdfPart;
                                    break;

                                case 'T':
                                    if (gdf.TurretParts == null)
                                    {
                                        gdf.TurretParts = new SdfPart[numParts];
                                    }

                                    gdf.TurretParts[turretPartsIndex++] = sdfPart;
                                    break;

                                case 'I':
                                    if (gdf.InsideParts == null)
                                    {
                                        gdf.InsideParts = new SdfPart[numParts];
                                    }

                                    gdf.InsideParts[insidePartsIndex++] = sdfPart;
                                    break;

                                default:
                                    Debug.LogWarningFormat("Unknown part type '{0}' for part name '{1}'.", partType, partName);
                                    break;
                                }

                                br.Position += 36;
                            }

                            // Skip Lower LOD levels - do we want to use these at all?
                            br.Position += 200;
                        }
                    }
                }

                br.FindNext("ORDF"); // 133 bytes


                br.FindNext("OGEO"); // 104 bytes

                br.ReadInt32();

                gdf.Projectile = new SdfPart
                {
                    Name       = br.ReadCString(8),
                    Right      = new Vector3(br.ReadSingle(), br.ReadSingle(), br.ReadSingle()),
                    Up         = new Vector3(br.ReadSingle(), br.ReadSingle(), br.ReadSingle()),
                    Forward    = new Vector3(br.ReadSingle(), br.ReadSingle(), br.ReadSingle()),
                    Position   = new Vector3(br.ReadSingle(), br.ReadSingle(), br.ReadSingle()),
                    ParentName = br.ReadCString(8)
                };

                return(gdf);
            }
        }