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.
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() ); } } }