2009-04-19 18:07:22 +00:00
#region = = = = = = = = = = = = = = = = = = Copyright ( c ) 2007 Pascal vd Heiden
/ *
* Copyright ( c ) 2007 Pascal vd Heiden , www . codeimp . com
* This program is released under GNU General Public License
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* /
#endregion
#region = = = = = = = = = = = = = = = = = = Namespaces
2014-02-26 14:11:06 +00:00
using System ;
2009-04-19 18:07:22 +00:00
using System.Collections.Generic ;
2015-12-27 00:13:31 +00:00
using System.Globalization ;
2009-04-19 18:07:22 +00:00
using System.IO ;
#endregion
namespace CodeImp.DoomBuilder.ZDoom
{
public sealed class DecorateParser : ZDTextParser
{
#region = = = = = = = = = = = = = = = = = = Delegates
public delegate void IncludeDelegate ( DecorateParser parser , string includefile ) ;
public IncludeDelegate OnInclude ;
#endregion
#region = = = = = = = = = = = = = = = = = = Constants
#endregion
#region = = = = = = = = = = = = = = = = = = Variables
// These are actors we want to keep
private Dictionary < string , ActorStructure > actors ;
// These are all parsed actors, also those from other games
private Dictionary < string , ActorStructure > archivedactors ;
2015-12-21 14:17:47 +00:00
//mxd. Includes tracking
private readonly HashSet < string > parsedlumps ;
2009-04-19 18:07:22 +00:00
#endregion
#region = = = = = = = = = = = = = = = = = = Properties
2009-08-15 08:41:43 +00:00
/// <summary>
/// All actors that are supported by the current game.
/// </summary>
2009-04-19 18:07:22 +00:00
public ICollection < ActorStructure > Actors { get { return actors . Values ; } }
2009-08-15 08:41:43 +00:00
/// <summary>
/// All actors defined in the loaded DECORATE structures. This includes actors not supported in the current game.
/// </summary>
public ICollection < ActorStructure > AllActors { get { return archivedactors . Values ; } }
2015-04-14 11:33:57 +00:00
/// <summary>
/// mxd. All actors that are supported by the current game.
/// </summary>
internal Dictionary < string , ActorStructure > ActorsByClass { get { return actors ; } }
/// <summary>
/// mxd. All actors defined in the loaded DECORATE structures. This includes actors not supported in the current game.
/// </summary>
internal Dictionary < string , ActorStructure > AllActorsByClass { get { return archivedactors ; } }
2009-04-19 18:07:22 +00:00
#endregion
#region = = = = = = = = = = = = = = = = = = Constructor / Disposer
// Constructor
public DecorateParser ( )
{
// Syntax
2013-09-11 09:47:53 +00:00
whitespace = "\n \t\r\u00A0" ; //mxd. non-breaking space is also space :)
2009-08-15 08:41:43 +00:00
specialtokens = ":{}+-\n;," ;
2009-04-19 18:07:22 +00:00
// Initialize
2014-02-26 14:11:06 +00:00
actors = new Dictionary < string , ActorStructure > ( StringComparer . Ordinal ) ;
archivedactors = new Dictionary < string , ActorStructure > ( StringComparer . Ordinal ) ;
2015-12-21 14:17:47 +00:00
parsedlumps = new HashSet < string > ( StringComparer . OrdinalIgnoreCase ) ; //mxd
2009-04-19 18:07:22 +00:00
}
2010-08-19 15:06:15 +00:00
// Disposer
public void Dispose ( )
{
foreach ( KeyValuePair < string , ActorStructure > a in archivedactors )
a . Value . Dispose ( ) ;
actors = null ;
archivedactors = null ;
}
2009-04-19 18:07:22 +00:00
#endregion
#region = = = = = = = = = = = = = = = = = = Parsing
// This parses the given decorate stream
// Returns false on errors
2014-10-23 12:48:31 +00:00
public override bool Parse ( Stream stream , string sourcefilename , bool clearerrors )
2009-04-19 18:07:22 +00:00
{
2015-12-18 10:16:53 +00:00
if ( ! base . Parse ( stream , sourcefilename , clearerrors ) ) return false ;
2009-04-19 18:07:22 +00:00
// Keep local data
Stream localstream = datastream ;
string localsourcename = sourcename ;
BinaryReader localreader = datareader ;
// Continue until at the end of the stream
while ( SkipWhitespace ( true ) )
{
// Read a token
string objdeclaration = ReadToken ( ) ;
2015-06-18 19:45:39 +00:00
if ( ! string . IsNullOrEmpty ( objdeclaration ) )
2009-04-19 18:07:22 +00:00
{
objdeclaration = objdeclaration . ToLowerInvariant ( ) ;
2015-12-17 10:07:28 +00:00
switch ( objdeclaration )
2009-04-19 18:07:22 +00:00
{
2015-12-17 10:07:28 +00:00
case "actor" :
2009-04-19 18:07:22 +00:00
{
2015-12-17 10:07:28 +00:00
// Read actor structure
ActorStructure actor = new ActorStructure ( this ) ;
if ( this . HasError ) return false ;
// Add the actor
archivedactors [ actor . ClassName . ToLowerInvariant ( ) ] = actor ;
2009-04-19 18:07:22 +00:00
if ( actor . CheckActorSupported ( ) )
2015-12-17 10:07:28 +00:00
actors [ actor . ClassName . ToLowerInvariant ( ) ] = actor ;
// Replace an actor?
if ( actor . ReplacesClass ! = null )
2009-04-19 18:07:22 +00:00
{
2015-12-17 10:07:28 +00:00
if ( GetArchivedActorByName ( actor . ReplacesClass ) ! = null )
archivedactors [ actor . ReplacesClass . ToLowerInvariant ( ) ] = actor ;
else
LogWarning ( "Unable to find '" + actor . ReplacesClass + "' class to replace, while parsing '" + actor . ClassName + "'" ) ;
if ( actor . CheckActorSupported ( ) & & GetActorByName ( actor . ReplacesClass ) ! = null )
2009-04-19 18:07:22 +00:00
actors [ actor . ReplacesClass . ToLowerInvariant ( ) ] = actor ;
}
}
2015-12-17 10:07:28 +00:00
break ;
case "#include" :
2009-04-19 18:07:22 +00:00
{
2015-12-21 14:17:47 +00:00
//INFO: ZDoom DECORATE include paths can't be relative ("../actor.txt")
//or absolute ("d:/project/actor.txt")
2015-12-27 00:13:31 +00:00
//or have backward slashes ("info\actor.txt")
2015-12-21 14:17:47 +00:00
//include paths are relative to the first parsed entry, not the current one
//also include paths may or may not be quoted
2015-12-17 10:07:28 +00:00
SkipWhitespace ( true ) ;
2015-12-21 14:17:47 +00:00
string filename = StripTokenQuotes ( ReadToken ( false ) ) ; //mxd. Don't skip newline
2015-12-17 10:07:28 +00:00
//mxd. Sanity checks
2015-12-21 14:17:47 +00:00
if ( string . IsNullOrEmpty ( filename ) )
2015-12-17 10:07:28 +00:00
{
2015-12-21 14:17:47 +00:00
ReportError ( "Expected file name to include" ) ;
2015-12-17 10:07:28 +00:00
return false ;
}
2016-01-01 18:10:16 +00:00
//mxd. Check invalid path chars
if ( ! CheckInvalidPathChars ( filename ) ) return false ;
2015-12-21 14:17:47 +00:00
//mxd. Absolute paths are not supported...
if ( Path . IsPathRooted ( filename ) )
{
ReportError ( "Absolute include paths are not supported by ZDoom" ) ;
return false ;
}
2009-04-19 18:07:22 +00:00
2015-12-21 14:17:47 +00:00
//mxd. Relative paths are not supported
if ( filename . StartsWith ( RELATIVE_PATH_MARKER ) | | filename . StartsWith ( CURRENT_FOLDER_PATH_MARKER ) | |
filename . StartsWith ( ALT_RELATIVE_PATH_MARKER ) | | filename . StartsWith ( ALT_CURRENT_FOLDER_PATH_MARKER ) )
2015-12-17 10:07:28 +00:00
{
2015-12-21 14:17:47 +00:00
ReportError ( "Relative include paths are not supported by ZDoom" ) ;
return false ;
}
2015-12-27 00:13:31 +00:00
//mxd. Backward slashes are not supported
if ( filename . Contains ( Path . DirectorySeparatorChar . ToString ( CultureInfo . InvariantCulture ) ) )
2015-12-21 14:17:47 +00:00
{
2015-12-27 00:13:31 +00:00
ReportError ( "Only forward slashes are supported by ZDoom" ) ;
2015-12-21 14:17:47 +00:00
return false ;
}
//mxd. Already parsed?
if ( parsedlumps . Contains ( filename ) )
{
ReportError ( "Already parsed '" + filename + "'. Check your include directives" ) ;
2015-12-17 10:07:28 +00:00
return false ;
}
2015-12-21 14:17:47 +00:00
//mxd. Add to collection
parsedlumps . Add ( filename ) ;
2009-04-19 18:07:22 +00:00
// Callback to parse this file now
if ( OnInclude ! = null ) OnInclude ( this , filename ) ;
2015-12-21 14:17:47 +00:00
//mxd. Bail out on error
if ( this . HasError ) return false ;
2009-04-19 18:07:22 +00:00
// Set our buffers back to continue parsing
datastream = localstream ;
datareader = localreader ;
sourcename = localsourcename ;
}
2015-12-17 10:07:28 +00:00
break ;
case "enum" :
case "native" :
case "const" :
while ( SkipWhitespace ( true ) )
{
string t = ReadToken ( ) ;
if ( string . IsNullOrEmpty ( t ) | | t = = ";" ) break ;
}
2009-04-19 18:07:22 +00:00
break ;
2015-12-17 10:07:28 +00:00
case "$gzdb_skip" : break ;
default :
2009-04-19 18:07:22 +00:00
{
2015-12-17 10:07:28 +00:00
// Unknown structure!
// Best we can do now is just find the first { and then
// follow the scopes until the matching } is found
string token2 ;
do
{
if ( ! SkipWhitespace ( true ) ) break ;
token2 = ReadToken ( ) ;
if ( string . IsNullOrEmpty ( token2 ) ) break ;
}
while ( token2 ! = "{" ) ;
int scopelevel = 1 ;
do
{
if ( ! SkipWhitespace ( true ) ) break ;
token2 = ReadToken ( ) ;
if ( string . IsNullOrEmpty ( token2 ) ) break ;
if ( token2 = = "{" ) scopelevel + + ;
if ( token2 = = "}" ) scopelevel - - ;
}
while ( scopelevel > 0 ) ;
2009-04-19 18:07:22 +00:00
}
2015-03-05 08:51:12 +00:00
break ;
}
2009-04-19 18:07:22 +00:00
}
}
// Return true when no errors occurred
return ( ErrorDescription = = null ) ;
}
#endregion
#region = = = = = = = = = = = = = = = = = = Methods
2009-08-15 08:41:43 +00:00
/// <summary>
/// This returns a supported actor by name. Returns null when no supported actor with the specified name can be found. This operation is of O(1) complexity.
/// </summary>
2009-04-19 18:07:22 +00:00
public ActorStructure GetActorByName ( string name )
{
name = name . ToLowerInvariant ( ) ;
2015-12-17 10:07:28 +00:00
return actors . ContainsKey ( name ) ? actors [ name ] : null ;
2009-04-19 18:07:22 +00:00
}
2009-08-15 08:41:43 +00:00
/// <summary>
/// This returns a supported actor by DoomEdNum. Returns null when no supported actor with the specified name can be found. Please note that this operation is of O(n) complexity!
/// </summary>
public ActorStructure GetActorByDoomEdNum ( int doomednum )
{
2015-12-17 10:07:28 +00:00
foreach ( ActorStructure a in actors . Values )
if ( a . DoomEdNum = = doomednum ) return a ;
2009-08-15 08:41:43 +00:00
return null ;
}
2009-04-19 18:07:22 +00:00
// This returns an actor by name
// Returns null when actor cannot be found
internal ActorStructure GetArchivedActorByName ( string name )
{
name = name . ToLowerInvariant ( ) ;
2015-03-17 12:28:42 +00:00
return ( archivedactors . ContainsKey ( name ) ? archivedactors [ name ] : null ) ;
2009-04-19 18:07:22 +00:00
}
2015-12-17 10:07:28 +00:00
//mxd
protected override string GetLanguageType ( )
{
return "DECORATE" ;
}
2009-04-19 18:07:22 +00:00
#endregion
}
}