Beispiel #1
0
 public static DataStructures.GameData.GameData GetGameDataFromFiles(IEnumerable<string> files)
 {
     var gd = new DataStructures.GameData.GameData();
     foreach (var d in files.Select(GetGameDataFromFile))
     {
         gd.MapSizeHigh = d.MapSizeHigh;
         gd.MapSizeLow = d.MapSizeLow;
         gd.Classes.AddRange(d.Classes);
     }
     gd.CreateDependencies();
     return gd;
 }
Beispiel #2
0
        public static DataStructures.GameData.GameData GetGameDataFromFiles(IEnumerable <string> files)
        {
            var gd = new DataStructures.GameData.GameData();

            foreach (var d in files.Select(GetGameDataFromFile))
            {
                gd.MapSizeHigh = d.MapSizeHigh;
                gd.MapSizeLow  = d.MapSizeLow;
                gd.Classes.AddRange(d.Classes);
                gd.MaterialExclusions.AddRange(d.MaterialExclusions);
            }
            gd.CreateDependencies();
            gd.RemoveDuplicates();
            return(gd);
        }
Beispiel #3
0
        private DataStructures.GameData.GameData Parse(IEnumerable <LexObject> lex)
        {
            var gd       = new DataStructures.GameData.GameData();
            var iterator = lex.GetEnumerator();

            while (true)
            {
                if (!iterator.MoveNext())
                {
                    break;
                }
                if (iterator.Current.Type == LexType.At)
                {
                    ParseAt(gd, iterator);
                }
            }
            return(gd);
        }
        public DataStructures.GameData.GameData GetGameDataFromFiles(IEnumerable <string> files)
        {
            var gd = new DataStructures.GameData.GameData();

            foreach (var f in files.Where(IsValidForFile))
            {
                var provider = new FgdProvider();
                var d        = provider.OpenFile(f);

                gd.MapSizeHigh = d.MapSizeHigh;
                gd.MapSizeLow  = d.MapSizeLow;
                gd.Classes.AddRange(d.Classes);
                gd.MaterialExclusions.AddRange(d.MaterialExclusions);
            }
            gd.CreateDependencies();
            gd.RemoveDuplicates();
            return(gd);
        }
Beispiel #5
0
 protected abstract void SaveToStream(Stream stream, DataStructures.MapObjects.Map map, DataStructures.GameData.GameData gameData, TextureCollection textureCollection);
Beispiel #6
0
 protected virtual void SaveToFile(string filename, DataStructures.MapObjects.Map map, DataStructures.GameData.GameData gameData, TextureCollection textureCollection)
 {
     using (var strm = new FileStream(filename, FileMode.Create, FileAccess.Write)) {
         SaveToStream(strm, map, gameData, textureCollection);
     }
 }
Beispiel #7
0
        public static void SaveMapToFile(string filename, DataStructures.MapObjects.Map map, DataStructures.GameData.GameData gameData, TextureCollection textureCollection = null)
        {
            var provider = RegisteredProviders.FirstOrDefault(p => p.IsValidForFileName(filename));

            if (provider != null)
            {
                provider.SaveToFile(filename, map, gameData, textureCollection);
                return;
            }
            throw new ProviderNotFoundException("No map provider was found for this file format.");
        }
Beispiel #8
0
        private void ParseAt(DataStructures.GameData.GameData gd, IEnumerator <LexObject> iterator)
        {
            iterator.MoveNext();
            var type = iterator.Current.Value;

            if (type.Equals("include", StringComparison.InvariantCultureIgnoreCase))
            {
                Expect(iterator, LexType.String);
                if (CurrentFile != null)
                {
                    var filename = iterator.Current.GetValue();
                    var path     = Path.GetDirectoryName(CurrentFile) ?? "";
                    var incfile  = Path.Combine(path, filename);

                    var current = CurrentFile;
                    var incgd   = GetGameDataFromFile(incfile);
                    CurrentFile = current;

                    if (!gd.Includes.Any(x => String.Equals(x, filename, StringComparison.InvariantCultureIgnoreCase)))
                    {
                        gd.Includes.Add(filename);
                    }

                    // Merge the included gamedata into the current one
                    gd.MapSizeHigh = Math.Max(incgd.MapSizeHigh, gd.MapSizeHigh);
                    gd.MapSizeLow  = Math.Min(incgd.MapSizeLow, gd.MapSizeLow);
                    gd.Includes.AddRange(incgd.Includes.Where(x => !gd.Includes.Contains(x)));
                    gd.Classes.AddRange(incgd.Classes.Where(x => !gd.Classes.Any(y => String.Equals(x.Name, y.Name, StringComparison.InvariantCultureIgnoreCase))));
                    gd.AutoVisgroups.AddRange(incgd.AutoVisgroups.Where(x => !gd.AutoVisgroups.Any(y => String.Equals(x.Name, y.Name, StringComparison.InvariantCultureIgnoreCase))));
                    gd.MaterialExclusions.AddRange(incgd.MaterialExclusions.Where(x => !gd.MaterialExclusions.Any(y => String.Equals(x, y, StringComparison.InvariantCultureIgnoreCase))));
                }
                else
                {
                    throw new ProviderException("Unable to include a file when not reading from a file.");
                }
            }
            else if (type.Equals("mapsize", StringComparison.InvariantCultureIgnoreCase))
            {
                Expect(iterator, LexType.OpenParen);
                Expect(iterator, LexType.Value);
                gd.MapSizeLow = Int32.Parse(iterator.Current.Value);
                Expect(iterator, LexType.Comma);
                Expect(iterator, LexType.Value);
                gd.MapSizeHigh = Int32.Parse(iterator.Current.Value);
                Expect(iterator, LexType.CloseParen);
            }
            else if (type.Equals("materialexclusion", StringComparison.InvariantCultureIgnoreCase))
            {
                Expect(iterator, LexType.OpenBracket);
                iterator.MoveNext();
                while (iterator.Current.Type != LexType.CloseBracket)
                {
                    Assert(iterator.Current, iterator.Current.IsValueOrString(), "Expected value type, got " + iterator.Current.Type + ".");
                    var exclusion = iterator.Current.GetValue();
                    gd.MaterialExclusions.Add(exclusion);
                    iterator.MoveNext();
                }
            }
            else if (type.Equals("autovisgroup", StringComparison.InvariantCultureIgnoreCase))
            {
                Expect(iterator, LexType.Equals);

                iterator.MoveNext();
                Assert(iterator.Current, iterator.Current.IsValueOrString(), "Expected value type, got " + iterator.Current.Type + ".");
                var sectionName = iterator.Current.GetValue();
                var sect        = new AutoVisgroupSection {
                    Name = sectionName
                };

                Expect(iterator, LexType.OpenBracket);
                iterator.MoveNext();
                while (iterator.Current.Type != LexType.CloseBracket)
                {
                    Assert(iterator.Current, iterator.Current.IsValueOrString(), "Expected value type, got " + iterator.Current.Type + ".");
                    var groupName = iterator.Current.GetValue();
                    var grp       = new AutoVisgroup {
                        Name = groupName
                    };

                    Expect(iterator, LexType.OpenBracket);
                    iterator.MoveNext();
                    while (iterator.Current.Type != LexType.CloseBracket)
                    {
                        Assert(iterator.Current, iterator.Current.IsValueOrString(), "Expected value type, got " + iterator.Current.Type + ".");
                        var entity = iterator.Current.GetValue();
                        grp.EntityNames.Add(entity);
                        iterator.MoveNext();
                    }

                    sect.Groups.Add(grp);
                }

                gd.AutoVisgroups.Add(sect);
            }
            else
            {
                // Parsing:
                // @TypeClass name(param, param) name()
                var ct  = ParseClassType(type, iterator.Current);
                var gdo = new GameDataObject("", "", ct);
                iterator.MoveNext();
                while (iterator.Current.Type == LexType.Value)
                {
                    // Parsing:
                    // @TypeClass {name(param, param) name()}
                    var name = iterator.Current.Value;
                    var bh   = new Behaviour(name);
                    iterator.MoveNext();
                    if (iterator.Current.Type == LexType.Value)
                    {
                        // Allow for the following (first seen in hl2 base):
                        // @PointClass {halfgridsnap} base(Targetname)
                        continue;
                    }
                    Assert(iterator.Current, iterator.Current.Type == LexType.OpenParen, "Unexpected " + iterator.Current.Type);
                    iterator.MoveNext();
                    while (iterator.Current.Type != LexType.CloseParen)
                    {
                        // Parsing:
                        // name({param, param})
                        if (iterator.Current.Type != LexType.Comma)
                        {
                            Assert(iterator.Current, iterator.Current.Type == LexType.Value || iterator.Current.Type == LexType.String,
                                   "Unexpected " + iterator.Current.Type + ".");
                            var value = iterator.Current.Value;
                            if (iterator.Current.Type == LexType.String)
                            {
                                value = value.Trim('"');
                            }
                            bh.Values.Add(value);
                        }
                        iterator.MoveNext();
                    }
                    Assert(iterator.Current, iterator.Current.Type == LexType.CloseParen, "Unexpected " + iterator.Current.Type);
                    // Treat base behaviour as a special case
                    if (bh.Name == "base")
                    {
                        gdo.BaseClasses.AddRange(bh.Values);
                    }
                    else
                    {
                        gdo.Behaviours.Add(bh);
                    }
                    iterator.MoveNext();
                }
                // = class_name : "Descr" + "iption" [
                Assert(iterator.Current, iterator.Current.Type == LexType.Equals, "Expected equals, got " + iterator.Current.Type);
                Expect(iterator, LexType.Value);
                gdo.Name = iterator.Current.Value;
                iterator.MoveNext();
                if (iterator.Current.Type == LexType.Colon)
                {
                    // Parsing:
                    // : {"Descr" + "iption"} [
                    iterator.MoveNext();
                    gdo.Description = ParsePlusString(iterator);
                }
                Assert(iterator.Current, iterator.Current.Type == LexType.OpenBracket, "Unexpected " + iterator.Current.Type);

                // Parsing:
                // name(type) : "Desc" : "Default" : "Long Desc" = [ ... ]
                // input name(type) : "Description"
                // output name(type) : "Description"
                iterator.MoveNext();
                while (iterator.Current.Type != LexType.CloseBracket)
                {
                    Assert(iterator.Current, iterator.Current.Type == LexType.Value, "Unexpected " + iterator.Current.Type);
                    var pt = iterator.Current.Value;
                    if (pt == "input" || pt == "output") // IO
                    {
                        // input name(type) : "Description"
                        var io = new IO();
                        Expect(iterator, LexType.Value);
                        io.IOType = (IOType)Enum.Parse(typeof(IOType), pt, true);
                        io.Name   = iterator.Current.Value;
                        Expect(iterator, LexType.OpenParen);
                        Expect(iterator, LexType.Value);
                        io.VariableType = ParseVariableType(iterator.Current);
                        Expect(iterator, LexType.CloseParen);
                        iterator.MoveNext(); // if not colon, this will be the value of the next io/property, or close
                        if (iterator.Current.Type == LexType.Colon)
                        {
                            iterator.MoveNext();
                            io.Description = ParsePlusString(iterator);
                        }
                        gdo.InOuts.Add(io);
                    }
                    else // Property
                    {
                        Expect(iterator, LexType.OpenParen);
                        Expect(iterator, LexType.Value);
                        var vartype = ParseVariableType(iterator.Current);
                        Expect(iterator, LexType.CloseParen);
                        var prop = new Property(pt, vartype);
                        iterator.MoveNext();
                        // if not colon or equals, this will be the value of the next io/property, or close
                        if (iterator.Current.Type == LexType.Value)
                        {
                            // Check for additional flags on the property
                            // e.g.: name(type) readonly : "This is a read only value"
                            //       name(type) report   : "This value will show in the entity report"
                            switch (iterator.Current.Value)
                            {
                            case "readonly":
                                prop.ReadOnly = true;
                                iterator.MoveNext();
                                break;

                            case "report":
                                prop.ShowInEntityReport = true;
                                iterator.MoveNext();
                                break;
                            }
                        }
                        do // Using do/while(false) so I can break out - reduces nesting.
                        {
                            // Short description
                            if (iterator.Current.Type != LexType.Colon)
                            {
                                break;
                            }
                            iterator.MoveNext();
                            prop.ShortDescription = ParsePlusString(iterator);

                            // Default value
                            if (iterator.Current.Type != LexType.Colon)
                            {
                                break;
                            }
                            iterator.MoveNext();
                            if (iterator.Current.Type != LexType.Colon) // Allow for ': :' structure (no default)
                            {
                                if (iterator.Current.Type == LexType.String)
                                {
                                    prop.DefaultValue = iterator.Current.Value.Trim('"');
                                }
                                else
                                {
                                    Assert(iterator.Current, iterator.Current.Type == LexType.Value, "Unexpected " + iterator.Current.Type);
                                    prop.DefaultValue = iterator.Current.Value;
                                }
                                iterator.MoveNext();
                            }

                            // Long description
                            if (iterator.Current.Type != LexType.Colon)
                            {
                                break;
                            }
                            iterator.MoveNext();
                            prop.Description = ParsePlusString(iterator);
                        } while (false);
                        if (iterator.Current.Type == LexType.Equals)
                        {
                            Expect(iterator, LexType.OpenBracket);
                            // Parsing property options:
                            // value : description
                            // value : description : 0
                            iterator.MoveNext();
                            while (iterator.Current.IsValueOrString())
                            {
                                var opt = new Option
                                {
                                    Key = iterator.Current.GetValue()
                                };
                                Expect(iterator, LexType.Colon);

                                // Some FGDs use values for property descriptions instead of strings
                                iterator.MoveNext();
                                Assert(iterator.Current, iterator.Current.IsValueOrString(), "Choices value must be value or string type.");
                                if (iterator.Current.Type == LexType.String)
                                {
                                    opt.Description = ParsePlusString(iterator);
                                }
                                else
                                {
                                    opt.Description = iterator.Current.GetValue();
                                    iterator.MoveNext();
                                    // ParsePlusString moves next once it's complete, need to do the same here
                                }

                                prop.Options.Add(opt);
                                if (iterator.Current.Type != LexType.Colon)
                                {
                                    continue;
                                }
                                Expect(iterator, LexType.Value);
                                opt.On = iterator.Current.Value == "1";
                                iterator.MoveNext();
                            }
                            Assert(iterator.Current, iterator.Current.Type == LexType.CloseBracket, "Unexpected " + iterator.Current.Type);
                            iterator.MoveNext();
                        }
                        gdo.Properties.Add(prop);
                    }
                }
                Assert(iterator.Current, iterator.Current.Type == LexType.CloseBracket, "Unexpected " + iterator.Current.Type);
                gd.Classes.Add(gdo);
            }
        }
Beispiel #9
0
 private DataStructures.GameData.GameData Parse(IEnumerable<LexObject> lex)
 {
     var gd = new DataStructures.GameData.GameData();
     var iterator = lex.GetEnumerator();
     while (true)
     {
         if (!iterator.MoveNext()) break;
         if (iterator.Current.Type == LexType.At)
         {
             ParseAt(gd, iterator);
         }
     }
     return gd;
 }
Beispiel #10
0
        protected override void SaveToStream(Stream stream, DataStructures.MapObjects.Map map, DataStructures.GameData.GameData gameData, TextureCollection textureCollection)
        {
            var groups = new List <Group>();
            var solids = new List <Solid>();
            var ents   = new List <Entity>();

            FlattenTree(map.WorldSpawn, solids, ents, groups);

            var fvi         = FileVersionInfo.GetVersionInfo(typeof(VmfProvider).Assembly.Location);
            var versioninfo = new GenericStructure("versioninfo");

            versioninfo.AddProperty("editorname", "CBRE");
            versioninfo.AddProperty("editorversion", fvi.ProductMajorPart.ToString() + "." + fvi.ProductMinorPart.ToString());
            versioninfo.AddProperty("editorbuild", fvi.ProductBuildPart.ToString());
            versioninfo.AddProperty("mapversion", map.Version.ToString());
            versioninfo.AddProperty("formatversion", "100");
            versioninfo.AddProperty("prefab", "0");

            var visgroups = new GenericStructure("visgroups");

            foreach (var visgroup in map.Visgroups.OrderBy(x => x.ID).Where(x => !x.IsAutomatic))
            {
                visgroups.Children.Add(WriteVisgroup(visgroup));
            }

            var viewsettings = new GenericStructure("viewsettings");

            viewsettings.AddProperty("bSnapToGrid", map.SnapToGrid ? "1" : "0");
            viewsettings.AddProperty("bShowGrid", map.Show2DGrid ? "1" : "0");
            viewsettings.AddProperty("bShow3DGrid", map.Show3DGrid ? "1" : "0");
            viewsettings.AddProperty("nGridSpacing", map.GridSpacing.ToString());
            viewsettings.AddProperty("bIgnoreGrouping", map.IgnoreGrouping ? "1" : "0");
            viewsettings.AddProperty("bHideFaceMask", map.HideFaceMask ? "1" : "0");
            viewsettings.AddProperty("bHideNullTextures", map.HideNullTextures ? "1" : "0");
            viewsettings.AddProperty("bTextureLock", map.TextureLock ? "1" : "0");
            viewsettings.AddProperty("bTextureScalingLock", map.TextureScalingLock ? "1" : "0");

            var world = WriteWorld(map, solids, groups);

            var entities = ents.OrderBy(x => x.ID).Select(WriteEntity).ToList();

            var cameras = new GenericStructure("cameras");

            cameras.AddProperty("activecamera", map.Cameras.IndexOf(map.ActiveCamera).ToString());
            foreach (var cam in map.Cameras)
            {
                var camera = new GenericStructure("camera");
                camera.AddProperty("position", "[" + FormatCoordinate(cam.EyePosition) + "]");
                camera.AddProperty("look", "[" + FormatCoordinate(cam.LookPosition) + "]");
                cameras.Children.Add(camera);
            }

            var cordon = new GenericStructure("cordon");

            cordon.AddProperty("mins", map.CordonBounds.Start.ToString());
            cordon.AddProperty("maxs", map.CordonBounds.End.ToString());
            cordon.AddProperty("active", map.Cordon ? "1" : "0");

            using (var sw = new StreamWriter(stream)) {
                versioninfo.PrintToStream(sw);
                visgroups.PrintToStream(sw);
                viewsettings.PrintToStream(sw);
                world.PrintToStream(sw);
                entities.ForEach(e => e.PrintToStream(sw));
                cameras.PrintToStream(sw);
                cordon.PrintToStream(sw);
            }
        }
Beispiel #11
0
 protected override void SaveToStream(Stream stream, DataStructures.MapObjects.Map map, DataStructures.GameData.GameData gameData, TextureCollection textureCollection)
 {
     throw new NotImplementedException("don't save to 3dw, ew");
 }
Beispiel #12
0
        protected override void SaveToStream(Stream stream, DataStructures.MapObjects.Map map, DataStructures.GameData.GameData gameData, TextureCollection textureCollection)
        {
            BinaryWriter writer = new BinaryWriter(stream);

            writer.WriteFixedLengthString(Encoding.ASCII, 3, "CBR");
            writer.Write(revision);

            // Lightmaps
            bool lightmapped = textureCollection != null && textureCollection.Lightmaps[0] != null;

            if (lightmapped)
            {
                writer.Write((byte)Lightmapped.Fully); // TODO: Determine changes from last render
                for (int i = 0; i < 4; i++)
                {
                    long prevPos = writer.Seek(0, SeekOrigin.Current);
                    writer.Write(0);
                    writer.Flush();
                    textureCollection.Lightmaps[i].Save(stream, ImageFormat.Png);
                    int imageOffset = (int)(writer.Seek(0, SeekOrigin.Current) - prevPos);
                    writer.Seek(-imageOffset, SeekOrigin.Current);
                    writer.Write(imageOffset - sizeof(int));
                    writer.Seek(0, SeekOrigin.End);
                }
            }
            else
            {
                writer.Write((byte)Lightmapped.No);
            }

            // Texture dictionary
            Dictionary <string, int> texDic     = new Dictionary <string, int>();
            StringBuilder            texBuilder = new StringBuilder();
            int texCount = 0;
            IEnumerator <string> textures = map.GetAllTextures().GetEnumerator();

            while (textures.MoveNext())
            {
                texBuilder.Append(textures.Current);
                texBuilder.Append('\0');
                texDic.Add(textures.Current, texCount);
                texCount++;
            }
            writer.Write(texCount);
            writer.WriteFixedLengthString(Encoding.UTF8, texBuilder.Length, texBuilder.ToString());

            // Solids
            List <MapObject> solids = map.WorldSpawn.Find(x => x is Solid);

            writer.Write(solids.Count);
            foreach (Solid s in solids)
            {
                writer.Write(s.Faces.Count);
                foreach (Face f in s.Faces)
                {
                    writer.Write(texDic[f.Texture.Name]);
                    writer.WriteCoordinate(f.Texture.UAxis);
                    writer.WriteCoordinate(f.Texture.VAxis);
                    writer.Write(f.Texture.XShift);
                    writer.Write(f.Texture.YShift);
                    writer.Write(f.Texture.XScale);
                    writer.Write(f.Texture.YScale);
                    writer.Write(f.Texture.Rotation);
                    writer.Write(f.Vertices.Count);
                    foreach (Vertex v in f.Vertices)
                    {
                        writer.WriteCoordinate(v.Location);
                        if (lightmapped)
                        {
                            writer.Write(v.LMU);
                            writer.Write(v.LMV);
                            writer.Write((float)v.TextureU);
                            writer.Write((float)v.TextureV);
                        }
                    }
                }
            }

            // Entities
            ISet <string>         foundEntityTypes = new HashSet <string>();
            List <GameDataObject> entityTypes      = new List <GameDataObject>();
            List <MapObject>      entites          = map.WorldSpawn.Find(x => x is Entity && x.ClassName != "");

            foreach (Entity e in entites)
            {
                Console.WriteLine(e.ClassName);
                Console.WriteLine(e.GameData.Name);
                if (!foundEntityTypes.Contains(e.ClassName))
                {
                    GameDataObject gdo = gameData.Classes.Find(x => x.Name == e.ClassName);
                    entityTypes.Add(gdo);
                    foundEntityTypes.Add(gdo.Name);
                }
            }
            // Move brush entities to front
            entityTypes.Sort((x, y) => (x.ClassType == ClassType.Solid ? -1 : 0));
            // For later use with groups
            Dictionary <Entity, int> entityIndices = new Dictionary <Entity, int>();
            int  entityIndicesCounter = 0;
            bool reachedRegular       = false;

            foreach (GameDataObject gdo in entityTypes)
            {
                if (!reachedRegular && gdo.ClassType != ClassType.Solid)
                {
                    reachedRegular = true;
                    writer.WriteNullTerminatedString("");
                }
                writer.WriteNullTerminatedString(gdo.Name);
                foreach (DataStructures.GameData.Property p in gdo.Properties)
                {
                    writer.Write((byte)p.VariableType);
                    writer.WriteNullTerminatedString(p.Name); // Switch from brush to regular entities
                }
                writer.Write((byte)255);                      // Property end byte

                // Entries
                List <MapObject> entitiesOfType = entites.FindAll(x => x.ClassName == gdo.Name);
                writer.Write(entitiesOfType.Count);
                foreach (Entity e in entitiesOfType)
                {
                    entityIndices.Add(e, entityIndicesCounter++);
                    if (e.GameData.ClassType == ClassType.Solid)
                    {
                        IEnumerable <MapObject> children = e.GetChildren();
                        writer.Write(children.Count());
                        foreach (MapObject mo in children)
                        {
                            int index = solids.FindIndex(x => x == mo);
                            writer.Write(index);
                        }
                    }
                    for (int i = 0; i < gdo.Properties.Count; i++)
                    {
                        DataStructures.MapObjects.Property property;
                        if (i < e.EntityData.Properties.Count && gdo.Properties[i].Name == e.EntityData.Properties[i].Key)
                        {
                            property = e.EntityData.Properties[i];
                        }
                        else
                        {
                            property = e.EntityData.Properties.Find(x => x.Key == gdo.Properties[i].Name);
                            if (property == null)
                            {
                                property       = new DataStructures.MapObjects.Property();
                                property.Key   = gdo.Properties[i].Name;
                                property.Value = gdo.Properties[i].DefaultValue;
                            }
                        }
                        switch (gdo.Properties[i].VariableType)
                        {
                        case VariableType.Bool:
                            writer.Write(property.Value == "Yes");
                            break;

                        case VariableType.Color255:
                            writer.WriteRGBAColour(property.GetColour(Color.Black));
                            break;

                        case VariableType.Float:
                            writer.Write(float.Parse(property.Value));
                            break;

                        case VariableType.Integer:
                            writer.Write(int.Parse(property.Value));
                            break;

                        case VariableType.String:
                            // TODO: Implement dictionary.
                            writer.WriteNullTerminatedString(property.Value);
                            break;

                        case VariableType.Vector:
                            writer.WriteCoordinate(property.GetCoordinate(Coordinate.Zero));
                            break;

                        case VariableType.Choices:
                            bool found = false;
                            for (int j = 0; j < gdo.Properties[i].Options.Count; j++)
                            {
                                if (property.Value == gdo.Properties[i].Options[j].Key)
                                {
                                    writer.Write((byte)j);
                                    found = true;
                                    break;
                                }
                            }
                            if (!found)
                            {
                                writer.Write((byte)255);
                            }
                            break;
                        }
                    }
                }
            }
            writer.WriteNullTerminatedString("");

            // CBRE ONLY

            // Visgroup dictionary
            // TODO: Visgroup IDs to indices
            Stack <IEnumerator <Visgroup> > visStack = new Stack <IEnumerator <Visgroup> >();

            visStack.Push(map.Visgroups.GetEnumerator());
            while (visStack.Count > 0)
            {
                IEnumerator <Visgroup> v = visStack.Pop();
                while (v.MoveNext())
                {
                    if (!v.Current.IsAutomatic)
                    {
                        writer.Write(HIERARCHY_PROCCEED);
                        writer.Write(v.Current.ID);
                        writer.WriteNullTerminatedString(v.Current.Name);
                        if (v.Current.Children.Count != 0)
                        {
                            writer.Write(HIERARCHY_DOWN);
                            visStack.Push(v);
                            v = v.Current.Children.GetEnumerator();
                        }
                    }
                }
                writer.Write(HIERARCHY_UP);
            }

            // Solid visgroups
            foreach (Solid s in map.WorldSpawn.FindAll().OfType <Solid>())
            {
                WriteVisgroups(writer, s);
            }

            // Entity visgroups
            foreach (GameDataObject entityType in entityTypes)
            {
                foreach (Entity e in entites.FindAll(x => x.ClassName == entityType.Name))
                {
                    WriteVisgroups(writer, e);
                }
            }

            // Groups
            IEnumerable <Group> groups = map.WorldSpawn.GetChildren().OfType <Group>();

            writer.Write(groups.Count());
            foreach (Group g in groups)
            {
                Stack <IEnumerator <MapObject> > groupStack = new Stack <IEnumerator <MapObject> >();
                groupStack.Push(g.GetChildren().GetEnumerator());
                while (groupStack.Count > 0)
                {
                    IEnumerator <MapObject> gg = groupStack.Pop();
                    while (gg.MoveNext())
                    {
                        if (gg.Current is Group)
                        {
                            writer.Write(HIERARCHY_DOWN);
                            groupStack.Push(gg);
                            gg = gg.Current.GetChildren().GetEnumerator();
                        }
                        else if (gg.Current is Entity)
                        {
                            writer.Write(IDENTIFIER_ENTITY);
                            writer.Write(entityIndices[(Entity)gg.Current]);
                        }
                        else
                        {
                            writer.Write(IDENTIFIER_SOLID);
                            writer.Write(solids.FindIndex(x => x == gg.Current));
                        }
                    }
                    writer.Write(HIERARCHY_UP);
                }
            }

            stream.Close();
        }