User:Maven/entitybot

From Valve Developer Community
< User:Maven
Revision as of 18:17, 8 October 2005 by Maven (talk | contribs) (another minor file)
Jump to navigation Jump to search

Here is code for a small app to convert FGD entries into markup for an entity entry in this wiki.

Notes

This code is in the public domain.

It uses C# 2.0, and comes without support. Please excuse its ugly kuldgeness. The correct way would have been to use Flex/Bison.

To use this, copy an entity entry from the FGD into the top box, click the convert button, and paste the resulting bottom-box text into the appropriate VDC edit page. You will need to fill in some of the Availability section.

Form1.cs

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

namespace FGD2Wiki
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void fgd2wikiButton_Click( object sender, EventArgs e )
        {
            this.ConvertFGD2Wiki();
        }

        private enum EntityType
        {
            Error = 0,
            PointBased,
            BrushBased,
        };

        private class EntityInput
        {
            public string name;
            public string type;
            public string desc;

            public string ToWiki()
            {
                StringBuilder sb = new StringBuilder();
                sb.Append( "*'''" );
                sb.Append( this.name );
                if ( this.type != "void" )
                {
                    sb.Append( " <" );
                    sb.Append( this.type );
                    sb.Append( ">" );
                }
                sb.AppendLine( "'''" );
                sb.Append( ":" );
                sb.AppendLine( this.desc );
                return sb.ToString();
            }
        };

        private class EntityOutput 
        {
            public string name;
            public string type;
            public string desc;

            public string ToWiki()
            {
                StringBuilder sb = new StringBuilder();
                sb.Append( "*'''" );
                sb.Append( this.name );
                if ( this.type != "void" )
                {
                    sb.Append( " <" );
                    sb.Append( this.type );
                    sb.Append( ">" );
                }
                sb.AppendLine( "'''" );
                sb.Append( ":" );
                sb.AppendLine( this.desc );
                return sb.ToString();
            }
        };

        private struct Pair<A,B>
        {
            public Pair( A a, B b ) { this.a = a; this.b = b; }
            public A a;
            public B b;
        }

        private class EntityKeyvalue
        {
            public string name;
            public string type;
            public string name2;    // the SmartEdit name
            public string desc;
            public string def;
            public List<Pair<int, string>> choices = new List<Pair<int, string>>();

            public string ToWiki()
            {
                StringBuilder sb = new StringBuilder();

                // Add the name
                sb.Append( "*'''" );
                sb.Append( this.name );
                sb.AppendLine( "'''" );

                // Add the type
                sb.Append( ":<" );
                switch ( this.type )
                {
                    case "angle":
                    case "angles":
                    case "vecline":
                    case "byte":
                    case "color255":
                        sb.Append( "[[" );
                        sb.Append( this.type );
                        sb.Append( "]]" );
                        break;
                    case "boolean":
                    case "bool":
                        sb.Append( "[[Wikipedia:boolean|boolean]]" );
                        break;
                    default:
                        sb.Append( this.type );
                        break;
                }
                sb.Append( "> " );

                // Add the SmartEdit name to the desc if the desc is shortish
                if ( this.desc.Length < 12 )
                {
                    sb.Append( this.name2 );
                    sb.Append( ". " );
                }

                // Add the description
                sb.Append( this.desc );
                // Add a period if there isn't one aleady
                if ( this.desc.Length > 0 && !this.desc.EndsWith( "." ) )
                    sb.Append( "." );

                /*
                // Give the default value
                if ( this.def != "" )
                {
                    sb.Append( " Default: " );
                    sb.Append( this.def );
                }
                */
                sb.AppendLine();

                // If this is a 'choices' variable, create the choice table
                if ( this.type == "choices" )
                {
                    sb.AppendLine( ":{|" );
                    sb.AppendLine( "! Literal value || Description" );
                    foreach ( Pair<int, string> pair in choices )
                    {
                        sb.AppendLine( "|-" );
                        sb.Append( "| " );
                        sb.Append( pair.a );
                        sb.Append( " || " );
                        sb.AppendLine( pair.b );
                    }
                    sb.AppendLine( "|}" );
                }

                return sb.ToString();
            }
        };

        private class EntitySpawnflag
        {
            public int value;
            public string desc;
            public int def;
        };

        private string StripTextUntil( ref string s, char c )
        {
            int i = s.IndexOf( c );
            if ( i < 0 )
            {
                string m = s.Trim();
                s = "";
                return m;
            }
            string n = s.Substring( 0, i );
            s = s.Substring( i + 1 ).TrimStart();
            return n.Trim();
        }

        private void ConvertFGD2Wiki()
        {
            // Get the (trimmed) input string
            string input = this.fgdTextbox.Text.Trim();
            string token;

            //------------------
            // Parse the header
            //------------------

            // Read the entity type: point-based or brush-based
            EntityType eEntityType = EntityType.Error;
            token = StripTextUntil( ref input, ' ' );
            switch ( token )
            {
                case "@PointClass":
                    eEntityType = EntityType.PointBased;
                    break;
                case "@SolidClass":
                    eEntityType = EntityType.BrushBased;
                    break;
                case "@BaseClass":
                    this.wikiTextbox.Text = "@BaseClass not supported";
                    return;
                default:
                    eEntityType = EntityType.Error;
                    break;
            }

            // Read everything until the equal sign
            string[] basetypes = new string[0];
            string sEntityParams = StripTextUntil( ref input, '=' );
            while ( sEntityParams.Length > 0 )
            {
                // Get the param name
                string sParamName = StripTextUntil( ref sEntityParams, '(' );
                string sParamData = StripTextUntil( ref sEntityParams, ')' );

                switch ( sParamName )
                {
                    case "base":
                    {
                        // Split into individual items
                        basetypes = sParamData.Split( ',' );
                        // Remove whitespace
                        for ( int j = 0; j < basetypes.Length; ++j )
                            basetypes[j] = basetypes[j].Trim().ToLower();

                        // Place targetname and parentname go first, if they exist, followed by the other templates
                        List<string> llBaseTypes = new List<string>( basetypes );   // temporary linked list
                        // TODO: this way is cheesy and inefficient; fix it
                        if ( llBaseTypes.Contains( "parentname" ) )
                        {
                            // Move parentname to the front
                            llBaseTypes.Remove( "parentname" );
                            llBaseTypes.Insert( 0, "parentname" );
                        }
                        if ( llBaseTypes.Contains( "targetname" ) )
                        {
                            // Move targetname to the front
                            llBaseTypes.Remove( "targetname" );
                            llBaseTypes.Insert( 0, "targetname" );
                        }
                        basetypes = llBaseTypes.ToArray();  // convert back to array

                        break;
                    }
                    default:
                        /* ignore it */
                        break;
                }
            }

            // TODO: not all entities have a description; handle that case

            // Read the entity name
            string sEntityName = StripTextUntil( ref input, ':' );

            // Read the entity description
            string sEntityDescriptionUnparsed = StripTextUntil( ref input, '[' );
            string sEntityDescription = "";
            while ( sEntityDescriptionUnparsed.Length > 0 )
            {
                // Ignore everything until a quote
                StripTextUntil( ref sEntityDescriptionUnparsed, '"' );
                // Take everything until the next quote
                sEntityDescription = sEntityDescription + StripTextUntil( ref sEntityDescriptionUnparsed, '"' ) + " ";

                // TODO: this will choke on escaped quotes
            }

            //---------------
            // Read the body
            //---------------

            string sBody = StripTextUntil( ref input, '@' );
            List<EntityInput> llInputs = new List<EntityInput>();
            List<EntityOutput> llOutputs = new List<EntityOutput>();
            List<EntityKeyvalue> llKeyvalues = new List<EntityKeyvalue>();
            List<EntitySpawnflag> llSpawnflags = new List<EntitySpawnflag>();
            while ( sBody.Length > 0 && !sBody.StartsWith( "]" ) )
            {
                // Check for line comment
                while ( sBody.StartsWith( "//" ) )
                {
                    // Eat everything until the next newline
                    StripTextUntil( ref sBody, '\n' );
                }

                // Get the item part of the next item:value pair
                string sItem = StripTextUntil( ref sBody, ')' );

                // Input
                if ( sItem.StartsWith( "input " ) )
                {
                    // Create a new input
                    EntityInput ei = new EntityInput();
                    // Discard the "input" part
                    StripTextUntil( ref sItem, ' ' );
                    // Read its information
                    ei.name = StripTextUntil( ref sItem, '(' );
                    ei.type = sItem;
                    // Read the desc
                    StripTextUntil( ref sBody, '"' );
                    ei.desc = StripTextUntil( ref sBody, '"' );
                    // Add it to the list of inputs
                    llInputs.Add( ei );
                }

                // Output
                else if ( sItem.StartsWith( "output " ) )
                {
                    // Create a new output
                    EntityOutput eo = new EntityOutput();
                    // Discard the "output" part
                    StripTextUntil( ref sItem, ' ' );
                    // Read its information
                    eo.name = StripTextUntil( ref sItem, '(' );
                    eo.type = StripTextUntil( ref sItem, ')' );
                    // Read the desc
                    StripTextUntil( ref sBody, '"' );
                    eo.desc = StripTextUntil( ref sBody, '"' );
                    // Add it to the list of outputs
                    llOutputs.Add( eo );
                }

                // Keyvalue
                else
                {
                    // Read the name
                    string sName = StripTextUntil( ref sItem, '(' );

                    // Is it the spawnflags field?
                    if ( sName == "spawnflags" )
                    {
                        StripTextUntil( ref sBody, '[' );
                        string sFlags = StripTextUntil( ref sBody, ']' );
                        while ( sFlags.Length > 0 )
                        {
                            // Create a new spawnflag
                            EntitySpawnflag fl = new EntitySpawnflag();
                            // Read its data
                            fl.value = int.Parse( StripTextUntil( ref sFlags, ':' ) );
                            StripTextUntil( ref sFlags, '"' );
                            fl.desc = StripTextUntil( ref sFlags, '"' );
                            StripTextUntil( ref sFlags, ':' );
                            fl.def = int.Parse( StripTextUntil( ref sFlags, '\n' ) );
                            // Add it to the list of spawnflags
                            llSpawnflags.Add( fl );
                        }
                    }

                    // Not spawnflag
                    else
                    {
                        // Create a new keyvalue
                        EntityKeyvalue kv = new EntityKeyvalue();
                        // Read its information
                        kv.name = sName;
                        kv.type = StripTextUntil( ref sItem, ')' );
                        // Read the SmartEdit name
                        StripTextUntil( ref sBody, '"' );
                        kv.name2 = StripTextUntil( ref sBody, '"' );
                        StripTextUntil( ref sBody, ':' );

                        // Is this a 'choices' keyvalue?
                        if ( kv.type == "choices" )
                        {
                            // Read the default value
                            kv.def = StripTextUntil( ref sBody, '=' );

                            // Parse the choices
                            StripTextUntil( ref sBody, '[' );
                            string sChoices = StripTextUntil( ref sBody, ']' );
                            while ( sChoices.Length > 0 )
                            {
                                // Create a new int,string pair
                                Pair<int,string> pair = new Pair<int,string>();
                                // Parse the choice info
                                pair.a = int.Parse( StripTextUntil( ref sChoices, ':' ) );
                                StripTextUntil( ref sChoices, '"' );
                                pair.b = StripTextUntil( ref sChoices, '"' );
                                // Add the pair
                                kv.choices.Add( pair );
                            }

                            // No desc (the SmartEdit name will be used)
                            kv.desc = "";
                        }
                        // Not a 'choices' keyvalue
                        else 
                        {
                            // Read the rest of the line
                            String sLine = StripTextUntil( ref sBody, '\n' );

                            // Read the default value
                            if ( sLine.StartsWith( "\"" ) )
                            {
                                // This is the case where it's in quotes
                                StripTextUntil( ref sLine, '"' );
                                kv.def = StripTextUntil( ref sLine, '"' );
                            }
                            else
                            {
                                // This is the case where it isn't (it's an integer or blank)
                                kv.def = StripTextUntil( ref sLine, ':' );
                            }

                            // Read the desc
                            StripTextUntil( ref sLine, '"' );
                            kv.desc = StripTextUntil( ref sLine, '"' );
                        }

                        // Add it to the list of keyvalues
                        llKeyvalues.Add( kv );
                    }
                }
            }

            //------------------------
            // Create the output text
            //------------------------

            StringBuilder sb = new StringBuilder();

            // Create the wrongtitle line
            sb.Append( "{{wrongtitle|title=" );
            sb.Append( sEntityName );
            sb.AppendLine( "}}" );
            sb.AppendLine();

            // Create the Entity Description section
            sb.AppendLine( "==Entity Description==" );
            sb.AppendLine( sEntityDescription );
            sb.AppendLine();

            // Create the Availability section
            sb.AppendLine( "==Availability==" );
            sb.Append( "{{in game|" );
            switch ( eEntityType )
            {
                case EntityType.PointBased:
                    sb.Append( "point" );
                    break;
                case EntityType.BrushBased:
                    sb.Append( "brush" );
                    break;
                default:
                    this.wikiTextbox.Text = "Unexpected entity type: " + eEntityType;
                    return;
            }
            sb.AppendLine( "}} ???" );
            sb.AppendLine( "{{in code|class=???|file=???}}" );
            sb.AppendLine();

            // Create the Keyvalues section
            sb.AppendLine( "==Keyvalues==" );
            foreach ( string template in basetypes )
            {
                sb.Append( "*{{kv " );
                sb.Append( template );
                sb.AppendLine( "}}" );
            }
            foreach ( EntityKeyvalue kv in llKeyvalues )
            {
                sb.Append( kv.ToWiki() );
            }
            sb.AppendLine();

            // Create the Flags section
            StringBuilder sbFlags = new StringBuilder();
            foreach ( string template in basetypes )
            {
                if ( IsEmptyFlag( template ) )
                    continue;
                sbFlags.Append( "*{{fl " );
                sbFlags.Append( template );
                sbFlags.AppendLine( "}}" );
            }
            foreach ( EntitySpawnflag fl in llSpawnflags )
            {
                sbFlags.Append( "*" );
                sbFlags.Append( fl.value );
                sbFlags.Append( " : " );
                sbFlags.AppendLine( fl.desc );
            }
            // If flag data is not empty, add the Flags section
            if ( sbFlags.ToString().Length > 0 )
            {
                sb.AppendLine( "==Flags==" );
                sb.Append( sbFlags.ToString() );
                sb.AppendLine();
            }

            // Create the Inputs section
            StringBuilder sbInputs = new StringBuilder();
            foreach ( string template in basetypes )
            {
                if ( IsEmptyInput( template ) )
                    continue;
                sbInputs.Append( "*{{i " );
                sbInputs.Append( template );
                sbInputs.AppendLine( "}}" );
            }
            foreach ( EntityInput ei in llInputs )
            {
                sbInputs.Append( ei.ToWiki() );
            }
            // If input data is not empty, add the Inputs section
            if ( sbInputs.ToString().Length > 0 )
            {
                sb.AppendLine( "==Inputs==" );
                sb.Append( sbInputs.ToString() );
                sb.AppendLine();
            }

            // Create the Outputs sections
            StringBuilder sbOutputs = new StringBuilder();
            foreach ( string template in basetypes )
            {
                if ( IsEmptyOutput( template ) )
                    continue;
                sbOutputs.Append( "*{{o " );
                sbOutputs.Append( template );
                sbOutputs.AppendLine( "}}" );
            }
            foreach ( EntityOutput eo in llOutputs )
            {
                sbOutputs.Append( eo.ToWiki() );
            }
            // If output data is not empty, add the Outputs section
            if ( sbOutputs.ToString().Length > 0 )
            {
                sb.AppendLine( "==Outputs==" );
                sb.Append( sbOutputs.ToString() );
                sb.AppendLine();
            }

            // Add the Category:Entities line
            sb.AppendLine( "[[Category:Entities]]" );
            sb.AppendLine();
            
            /*
            // TEMP: dump the tokens to the output box
            foreach ( string token in tokens )
                sb.AppendLine( token );
            */

            // Output the results
            this.wikiTextbox.Text = sb.ToString();
        }

        // TODO: IsEmptyKeyvalues

        private bool IsEmptyInput( string s )
        {
            // TODO: efficiency
            switch ( s.ToLower() )
            {
                case "angles":
                case "renderfxchoices":
                    return true;
                default:
                    return false;
            }
        }

        private bool IsEmptyOutput( string s )
        {
            // TODO: efficiency
            switch ( s.ToLower() )
            {
                case "angles":
                case "parentname":
                case "renderfxchoices":
                    return true;
                default:
                    return false;
            }
        }

        private bool IsEmptyFlag( string s )
        {
            // TODO: efficiency
            switch ( s.ToLower() )
            {
                case "angles":
                case "parentname":
                case "targetname":
                case "renderfxchoices":
                case "basevehicle":
                    return true;
                default:
                    return false;
            }
        }
    }
}

Form1.Designer.cs

namespace FGD2Wiki
{
    partial class Form1
    {
        /// <summary>
        /// Required designer variable.
        /// </summary>
        private System.ComponentModel.IContainer components = null;

        /// <summary>
        /// Clean up any resources being used.
        /// </summary>
        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
        protected override void Dispose( bool disposing )
        {
            if ( disposing && (components != null) )
            {
                components.Dispose();
            }
            base.Dispose( disposing );
        }

        #region Windows Form Designer generated code

        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
            this.splitContainer1 = new System.Windows.Forms.SplitContainer();
            this.fgdTextbox = new System.Windows.Forms.TextBox();
            this.wikiTextbox = new System.Windows.Forms.TextBox();
            this.panel1 = new System.Windows.Forms.Panel();
            this.fgd2wikiButton = new System.Windows.Forms.Button();
            this.splitContainer1.Panel1.SuspendLayout();
            this.splitContainer1.Panel2.SuspendLayout();
            this.splitContainer1.SuspendLayout();
            this.panel1.SuspendLayout();
            this.SuspendLayout();
            // 
            // splitContainer1
            // 
            this.splitContainer1.Dock = System.Windows.Forms.DockStyle.Fill;
            this.splitContainer1.Location = new System.Drawing.Point( 0, 0 );
            this.splitContainer1.Name = "splitContainer1";
            this.splitContainer1.Orientation = System.Windows.Forms.Orientation.Horizontal;
            // 
            // splitContainer1.Panel1
            // 
            this.splitContainer1.Panel1.Controls.Add( this.fgdTextbox );
            // 
            // splitContainer1.Panel2
            // 
            this.splitContainer1.Panel2.Controls.Add( this.wikiTextbox );
            this.splitContainer1.Panel2.Controls.Add( this.panel1 );
            this.splitContainer1.Size = new System.Drawing.Size( 522, 449 );
            this.splitContainer1.SplitterDistance = 205;
            this.splitContainer1.TabIndex = 0;
            this.splitContainer1.Text = "splitContainer1";
            // 
            // fgdTextbox
            // 
            this.fgdTextbox.Dock = System.Windows.Forms.DockStyle.Fill;
            this.fgdTextbox.Font = new System.Drawing.Font( "Courier New", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte) (0)) );
            this.fgdTextbox.Location = new System.Drawing.Point( 0, 0 );
            this.fgdTextbox.Multiline = true;
            this.fgdTextbox.Name = "fgdTextbox";
            this.fgdTextbox.ScrollBars = System.Windows.Forms.ScrollBars.Both;
            this.fgdTextbox.Size = new System.Drawing.Size( 522, 205 );
            this.fgdTextbox.TabIndex = 0;
            // 
            // wikiTextbox
            // 
            this.wikiTextbox.Dock = System.Windows.Forms.DockStyle.Fill;
            this.wikiTextbox.Font = new System.Drawing.Font( "Courier New", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte) (0)) );
            this.wikiTextbox.Location = new System.Drawing.Point( 0, 25 );
            this.wikiTextbox.Multiline = true;
            this.wikiTextbox.Name = "wikiTextbox";
            this.wikiTextbox.ReadOnly = true;
            this.wikiTextbox.ScrollBars = System.Windows.Forms.ScrollBars.Both;
            this.wikiTextbox.Size = new System.Drawing.Size( 522, 215 );
            this.wikiTextbox.TabIndex = 1;
            // 
            // panel1
            // 
            this.panel1.Controls.Add( this.fgd2wikiButton );
            this.panel1.Dock = System.Windows.Forms.DockStyle.Top;
            this.panel1.Location = new System.Drawing.Point( 0, 0 );
            this.panel1.Name = "panel1";
            this.panel1.Size = new System.Drawing.Size( 522, 25 );
            this.panel1.TabIndex = 0;
            // 
            // fgd2wikiButton
            // 
            this.fgd2wikiButton.Dock = System.Windows.Forms.DockStyle.Left;
            this.fgd2wikiButton.Location = new System.Drawing.Point( 0, 0 );
            this.fgd2wikiButton.Name = "fgd2wikiButton";
            this.fgd2wikiButton.Size = new System.Drawing.Size( 101, 25 );
            this.fgd2wikiButton.TabIndex = 0;
            this.fgd2wikiButton.Text = "&Convert";
            this.fgd2wikiButton.Click += new System.EventHandler( this.fgd2wikiButton_Click );
            // 
            // Form1
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF( 6F, 13F );
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size( 522, 449 );
            this.Controls.Add( this.splitContainer1 );
            this.Name = "Form1";
            this.Text = "FGD to Wiki";
            this.splitContainer1.Panel1.ResumeLayout( false );
            this.splitContainer1.Panel1.PerformLayout();
            this.splitContainer1.Panel2.ResumeLayout( false );
            this.splitContainer1.Panel2.PerformLayout();
            this.splitContainer1.ResumeLayout( false );
            this.panel1.ResumeLayout( false );
            this.ResumeLayout( false );

        }

        #endregion

        private System.Windows.Forms.SplitContainer splitContainer1;
        private System.Windows.Forms.Panel panel1;
        private System.Windows.Forms.Button fgd2wikiButton;
        private System.Windows.Forms.TextBox fgdTextbox;
        private System.Windows.Forms.TextBox wikiTextbox;
    }
}

Program.cs

If it's not auto-generated for you, you'll need this too.

using System;
using System.Collections.Generic;
using System.Windows.Forms;

namespace FGD2Wiki
{
    static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.Run( new Form1() );
        }
    }
}