User:Maven/entitybot
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.
(Feel free to change anything in this article, including the code.)
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() ); } } }