2015-04-14 11:33:57 +00:00
#region = = = = = = = = = = = = = = = = = = Namespaces
using System.Collections.Generic ;
using System.Drawing ;
2012-06-01 10:17:47 +00:00
using System.Globalization ;
using System.IO ;
using SlimDX ;
using CodeImp.DoomBuilder.ZDoom ;
using CodeImp.DoomBuilder.GZBuilder.Data ;
2015-04-14 11:33:57 +00:00
#endregion
namespace CodeImp.DoomBuilder.GZBuilder.GZDoom
{
internal sealed class MapinfoParser : ZDTextParser
{
#region = = = = = = = = = = = = = = = = = = Delegates
public delegate void IncludeDelegate ( MapinfoParser parser , string includefile ) ;
public IncludeDelegate OnInclude ;
#endregion
#region = = = = = = = = = = = = = = = = = = Variables
private MapInfo mapinfo ;
private readonly Dictionary < int , string > spawnnums ;
private readonly Dictionary < int , string > doomednums ; // <DoomEdNum, <lowercase classname, List of default arguments>>
#endregion
#region = = = = = = = = = = = = = = = = = = Properties
public MapInfo MapInfo { get { return mapinfo ; } }
public Dictionary < int , string > SpawnNums { get { return spawnnums ; } }
public Dictionary < int , string > DoomEdNums { get { return doomednums ; } }
#endregion
#region = = = = = = = = = = = = = = = = = = Constructor
public MapinfoParser ( )
{
2015-04-29 08:36:10 +00:00
// Syntax
whitespace = "\n \t\r\u00A0" ;
specialtokens = ",{}\n" ;
2015-04-14 11:33:57 +00:00
mapinfo = new MapInfo ( ) ;
spawnnums = new Dictionary < int , string > ( ) ;
doomednums = new Dictionary < int , string > ( ) ;
}
#endregion
2012-06-01 10:17:47 +00:00
2015-04-14 11:33:57 +00:00
#region = = = = = = = = = = = = = = = = = = Parsing
2012-06-01 10:17:47 +00:00
2015-04-14 11:33:57 +00:00
public bool Parse ( Stream stream , string sourcefilename , string mapname )
2014-12-03 23:15:26 +00:00
{
2013-09-11 09:47:53 +00:00
base . Parse ( stream , sourcefilename ) ;
2015-04-14 11:33:57 +00:00
mapname = mapname . ToLowerInvariant ( ) ;
2012-06-01 10:17:47 +00:00
2014-12-03 23:15:26 +00:00
while ( SkipWhitespace ( true ) )
{
2013-09-11 09:47:53 +00:00
string token = ReadToken ( ) ;
2014-12-03 23:15:26 +00:00
if ( token ! = null )
{
2013-09-11 09:47:53 +00:00
token = token . ToLowerInvariant ( ) ;
2015-04-14 11:33:57 +00:00
if ( ParseBlock ( token , mapname ) ) break ;
2013-09-11 09:47:53 +00:00
}
}
2012-06-01 10:17:47 +00:00
2013-09-11 09:47:53 +00:00
//check values
2015-04-14 11:33:57 +00:00
if ( mapinfo . FadeColor . Red > 0 | | mapinfo . FadeColor . Green > 0 | | mapinfo . FadeColor . Blue > 0 )
mapinfo . HasFadeColor = true ;
2012-06-01 10:17:47 +00:00
2015-04-14 11:33:57 +00:00
if ( mapinfo . OutsideFogColor . Red > 0 | | mapinfo . OutsideFogColor . Green > 0 | | mapinfo . OutsideFogColor . Blue > 0 )
mapinfo . HasOutsideFogColor = true ;
2012-06-01 10:17:47 +00:00
2013-09-11 09:47:53 +00:00
//Cannot fail here
return true ;
}
2012-06-01 10:17:47 +00:00
2013-09-11 09:47:53 +00:00
//returns true if parsing is finished
2014-12-03 23:15:26 +00:00
private bool ParseBlock ( string token , string mapName )
{
2015-04-14 11:33:57 +00:00
// Keep local data
Stream localstream = datastream ;
string localsourcename = sourcename ;
BinaryReader localreader = datareader ;
2014-12-03 23:15:26 +00:00
if ( token = = "map" | | token = = "defaultmap" | | token = = "adddefaultmap" )
{
2015-03-21 19:41:54 +00:00
string curBlockName = token ;
switch ( token )
2014-12-03 23:15:26 +00:00
{
2015-03-21 19:41:54 +00:00
case "map" :
//get map name
SkipWhitespace ( true ) ;
token = ReadToken ( ) . ToLowerInvariant ( ) ;
if ( token ! = mapName ) return false ; //not finished, search for next "map", "defaultmap" or "adddefaultmap" block
break ;
case "defaultmap" :
//reset MapInfo
2015-04-14 11:33:57 +00:00
mapinfo = new MapInfo ( ) ;
2015-03-21 19:41:54 +00:00
break ;
2013-09-11 09:47:53 +00:00
}
2012-06-01 10:17:47 +00:00
2013-09-11 09:47:53 +00:00
//search for required keys
2014-12-03 23:15:26 +00:00
while ( SkipWhitespace ( true ) )
{
2013-09-11 09:47:53 +00:00
token = ReadToken ( ) . ToLowerInvariant ( ) ;
2013-09-11 08:49:45 +00:00
//sky1 or sky2
2014-12-03 23:15:26 +00:00
if ( token = = "sky1" | | token = = "sky2" )
{
2013-09-11 09:47:53 +00:00
string skyType = token ;
SkipWhitespace ( true ) ;
token = StripTokenQuotes ( ReadToken ( ) ) . ToLowerInvariant ( ) ;
//new format
2014-12-03 23:15:26 +00:00
if ( token = = "=" )
{
2013-09-11 09:47:53 +00:00
SkipWhitespace ( true ) ;
//should be sky texture name
token = StripTokenQuotes ( ReadToken ( ) ) ;
bool gotComma = ( token . IndexOf ( "," ) ! = - 1 ) ;
2013-09-12 09:51:56 +00:00
if ( gotComma ) token = token . Replace ( "," , "" ) ;
2013-09-11 09:47:53 +00:00
string skyTexture = StripTokenQuotes ( token ) . ToLowerInvariant ( ) ;
2014-12-03 23:15:26 +00:00
if ( ! string . IsNullOrEmpty ( skyTexture ) )
{
2013-09-11 09:47:53 +00:00
if ( skyType = = "sky1" )
2015-04-14 11:33:57 +00:00
mapinfo . Sky1 = skyTexture ;
2013-09-11 09:47:53 +00:00
else
2015-04-14 11:33:57 +00:00
mapinfo . Sky2 = skyTexture ;
2013-09-11 09:47:53 +00:00
//check if we have scrollspeed
SkipWhitespace ( true ) ;
token = StripTokenQuotes ( ReadToken ( ) ) ;
2014-12-03 23:15:26 +00:00
if ( ! gotComma & & token = = "," )
{
2013-09-11 09:47:53 +00:00
gotComma = true ;
SkipWhitespace ( true ) ;
token = ReadToken ( ) ;
}
2014-12-03 23:15:26 +00:00
if ( gotComma )
{
2013-09-11 09:47:53 +00:00
float scrollSpeed = 0 ;
2014-12-03 23:15:26 +00:00
if ( ! ReadSignedFloat ( token , ref scrollSpeed ) )
{
2013-09-11 09:47:53 +00:00
// Not numeric!
General . ErrorLogger . Add ( ErrorType . Warning , "Unexpected token found in '" + sourcename + "' at line " + GetCurrentLineNumber ( ) + ": expected " + skyType + " scroll speed value, but got '" + token + "'" ) ;
datastream . Seek ( - token . Length - 1 , SeekOrigin . Current ) ; //step back and try parsing this token again
continue ;
}
if ( skyType = = "sky1" )
2015-04-14 11:33:57 +00:00
mapinfo . Sky1ScrollSpeed = scrollSpeed ;
2013-09-11 09:47:53 +00:00
else
2015-04-14 11:33:57 +00:00
mapinfo . Sky2ScrollSpeed = scrollSpeed ;
2014-12-03 23:15:26 +00:00
}
else
{
2013-09-11 09:47:53 +00:00
datastream . Seek ( - token . Length - 1 , SeekOrigin . Current ) ; //step back and try parsing this token again
}
2014-12-03 23:15:26 +00:00
}
else
{
2013-09-11 09:47:53 +00:00
datastream . Seek ( - token . Length - 1 , SeekOrigin . Current ) ; //step back and try parsing this token again
General . ErrorLogger . Add ( ErrorType . Error , "Unexpected token found in '" + sourcename + "' at line " + GetCurrentLineNumber ( ) + ": expected " + skyType + " texture name." ) ;
}
2014-12-03 23:15:26 +00:00
}
//old format
else
{
2013-09-11 09:47:53 +00:00
//token should be sky1/2 name
2014-12-03 23:15:26 +00:00
if ( ! string . IsNullOrEmpty ( token ) )
{
2013-09-11 09:47:53 +00:00
if ( skyType = = "sky1" )
2015-04-14 11:33:57 +00:00
mapinfo . Sky1 = token ;
2013-09-11 09:47:53 +00:00
else
2015-04-14 11:33:57 +00:00
mapinfo . Sky2 = token ;
2013-09-11 09:47:53 +00:00
//try to read scroll speed
SkipWhitespace ( true ) ;
token = StripTokenQuotes ( ReadToken ( ) ) ;
float scrollSpeed = 0 ;
2014-12-03 23:15:26 +00:00
if ( ! ReadSignedFloat ( token , ref scrollSpeed ) )
{
2013-09-11 09:47:53 +00:00
// Not numeric!
datastream . Seek ( - token . Length - 1 , SeekOrigin . Current ) ; //step back and try parsing this token again
continue ;
}
if ( skyType = = "sky1" )
2015-04-14 11:33:57 +00:00
mapinfo . Sky1ScrollSpeed = scrollSpeed ;
2013-09-11 09:47:53 +00:00
else
2015-04-14 11:33:57 +00:00
mapinfo . Sky2ScrollSpeed = scrollSpeed ;
2013-09-11 09:47:53 +00:00
2014-12-03 23:15:26 +00:00
}
else
{
2013-09-11 09:47:53 +00:00
datastream . Seek ( - token . Length - 1 , SeekOrigin . Current ) ; //step back and try parsing this token again
General . ErrorLogger . Add ( ErrorType . Error , "Unexpected token found in '" + sourcename + "' at line " + GetCurrentLineNumber ( ) + ": expected " + skyType + " texture name." ) ;
}
}
2014-12-03 23:15:26 +00:00
}
2013-09-11 08:49:45 +00:00
//fade or outsidefog
2014-12-03 23:15:26 +00:00
else if ( token = = "fade" | | token = = "outsidefog" )
{
2013-09-11 09:47:53 +00:00
string fadeType = token ;
SkipWhitespace ( true ) ;
token = StripTokenQuotes ( ReadToken ( ) ) . ToLowerInvariant ( ) ;
2013-09-12 09:51:56 +00:00
//new format?
2014-12-03 23:15:26 +00:00
if ( token = = "=" )
{
2013-09-11 09:47:53 +00:00
SkipWhitespace ( true ) ;
token = ReadToken ( ) ;
2013-09-12 09:51:56 +00:00
}
2013-09-11 09:47:53 +00:00
2013-09-12 09:51:56 +00:00
//get the color value
string colorVal = StripTokenQuotes ( token ) . ToLowerInvariant ( ) . Replace ( " " , "" ) ;
2014-12-03 23:15:26 +00:00
if ( ! string . IsNullOrEmpty ( colorVal ) )
{
2013-09-12 09:51:56 +00:00
Color4 color = new Color4 ( ) ;
//try to get the color...
2014-12-03 23:15:26 +00:00
if ( GetColor ( colorVal , ref color ) )
{
2013-09-12 09:51:56 +00:00
if ( fadeType = = "fade" )
2015-04-14 11:33:57 +00:00
mapinfo . FadeColor = color ;
2013-09-11 09:47:53 +00:00
else
2015-04-14 11:33:57 +00:00
mapinfo . OutsideFogColor = color ;
2014-12-03 23:15:26 +00:00
}
else //...or not
{
2014-01-03 10:33:45 +00:00
General . ErrorLogger . Add ( ErrorType . Error , "Failed to parse " + fadeType + " value from string '" + colorVal + "' in '" + sourcename + "' at line " + GetCurrentLineNumber ( ) ) ;
2013-09-11 09:47:53 +00:00
datastream . Seek ( - token . Length - 1 , SeekOrigin . Current ) ; //step back and try parsing this token again
}
2014-12-03 23:15:26 +00:00
}
else
{
2013-09-12 09:51:56 +00:00
General . ErrorLogger . Add ( ErrorType . Error , "Unexpected token found in '" + sourcename + "' at line " + GetCurrentLineNumber ( ) + ": expected " + fadeType + " color value." ) ;
datastream . Seek ( - token . Length - 1 , SeekOrigin . Current ) ; //step back and try parsing this token again
2013-09-11 09:47:53 +00:00
}
2014-12-03 23:15:26 +00:00
}
2013-09-11 08:49:45 +00:00
//vertwallshade or horizwallshade
2014-12-03 23:15:26 +00:00
else if ( token = = "vertwallshade" | | token = = "horizwallshade" )
{
2013-09-11 08:49:45 +00:00
string shadeType = token ;
SkipWhitespace ( true ) ;
token = StripTokenQuotes ( ReadToken ( ) ) ;
//new format
2014-12-03 23:15:26 +00:00
if ( token = = "=" )
{
2013-09-11 08:49:45 +00:00
SkipWhitespace ( true ) ;
token = StripTokenQuotes ( ReadToken ( ) ) ;
}
int val = 0 ;
2014-12-03 23:15:26 +00:00
if ( ! ReadSignedInt ( token , ref val ) )
{
2013-09-11 08:49:45 +00:00
// Not numeric!
General . ErrorLogger . Add ( ErrorType . Error , "Unexpected token found in '" + sourcename + "' at line " + GetCurrentLineNumber ( ) + ": expected " + shadeType + " value, but got '" + token + "'" ) ;
datastream . Seek ( - token . Length - 1 , SeekOrigin . Current ) ; //step back and try parsing this token again
continue ;
}
if ( shadeType = = "vertwallshade" )
2015-04-14 11:33:57 +00:00
mapinfo . VertWallShade = General . Clamp ( val , - 255 , 255 ) ;
2013-09-11 08:49:45 +00:00
else
2015-04-14 11:33:57 +00:00
mapinfo . HorizWallShade = General . Clamp ( val , - 255 , 255 ) ;
2014-12-03 23:15:26 +00:00
}
2013-09-11 08:49:45 +00:00
//fogdensity or outsidefogdensity
2014-12-03 23:15:26 +00:00
else if ( token = = "fogdensity" | | token = = "outsidefogdensity" )
{
2013-09-11 08:49:45 +00:00
string densityType = token ;
SkipWhitespace ( true ) ;
token = StripTokenQuotes ( ReadToken ( ) ) ;
//new format
2014-12-03 23:15:26 +00:00
if ( token = = "=" )
{
2013-09-11 08:49:45 +00:00
SkipWhitespace ( true ) ;
token = StripTokenQuotes ( ReadToken ( ) ) ;
}
int val ;
2014-12-03 23:15:26 +00:00
if ( ! int . TryParse ( token , NumberStyles . Integer , CultureInfo . InvariantCulture , out val ) )
{
2013-09-11 08:49:45 +00:00
// Not numeric!
General . ErrorLogger . Add ( ErrorType . Error , "Unexpected token found in '" + sourcename + "' at line " + GetCurrentLineNumber ( ) + ": expected " + densityType + " value, but got '" + token + "'" ) ;
datastream . Seek ( - token . Length - 1 , SeekOrigin . Current ) ; //step back and try parsing this token again
continue ;
}
2014-12-03 23:15:26 +00:00
if ( densityType = = "fogdensity" )
2015-04-14 11:33:57 +00:00
mapinfo . FogDensity = ( int ) ( 1024 * ( 256.0f / val ) ) ;
2014-12-03 23:15:26 +00:00
else
2015-04-14 11:33:57 +00:00
mapinfo . OutsideFogDensity = ( int ) ( 1024 * ( 256.0f / val ) ) ;
2014-12-03 23:15:26 +00:00
}
//doublesky
else if ( token = = "doublesky" )
{
2015-04-14 11:33:57 +00:00
mapinfo . DoubleSky = true ;
2014-12-03 23:15:26 +00:00
}
2013-09-11 08:49:45 +00:00
//evenlighting
2014-12-03 23:15:26 +00:00
else if ( token = = "evenlighting" )
{
2015-04-14 11:33:57 +00:00
mapinfo . EvenLighting = true ;
2014-12-03 23:15:26 +00:00
}
2013-09-11 08:49:45 +00:00
//smoothlighting
2014-12-03 23:15:26 +00:00
else if ( token = = "smoothlighting" )
{
2015-04-14 11:33:57 +00:00
mapinfo . SmoothLighting = true ;
2014-12-03 23:15:26 +00:00
}
//block end
else if ( token = = "}" )
{
return ( curBlockName = = "map" | | ParseBlock ( token , mapName ) ) ;
2013-09-11 09:47:53 +00:00
}
}
2015-04-14 11:33:57 +00:00
}
else if ( token = = "#include" )
{
SkipWhitespace ( true ) ;
string includeLump = StripTokenQuotes ( ReadToken ( ) ) . ToLowerInvariant ( ) ;
if ( ! string . IsNullOrEmpty ( includeLump ) )
{
// Callback to parse this file
if ( OnInclude ! = null )
OnInclude ( this , includeLump . Replace ( Path . AltDirectorySeparatorChar , Path . DirectorySeparatorChar ) ) ;
// Set our buffers back to continue parsing
datastream = localstream ;
datareader = localreader ;
sourcename = localsourcename ;
}
else
{
General . ErrorLogger . Add ( ErrorType . Error , "Error in '" + sourcename + "' at line " + GetCurrentLineNumber ( ) + ": got #include directive with missing or incorrect path: '" + includeLump + "'." ) ;
}
}
else if ( token = = "doomednums" )
{
if ( ! NextTokenIs ( "{" ) ) return true ; // Finished with this file
while ( SkipWhitespace ( true ) )
{
token = ReadToken ( ) ;
if ( string . IsNullOrEmpty ( token ) )
{
General . ErrorLogger . Add ( ErrorType . Error , "Error while parisng '" + sourcename + "' at line " + GetCurrentLineNumber ( ) + ": failed to find the end of DoomEdNums block" ) ;
return true ; // Finished with this file
}
if ( token = = "}" ) break ;
// First must be a number
int id ;
if ( ! int . TryParse ( token , NumberStyles . Integer , CultureInfo . InvariantCulture , out id ) )
{
// Not numeric!
General . ErrorLogger . Add ( ErrorType . Error , "Unexpected token found in '" + sourcename + "' at line " + GetCurrentLineNumber ( ) + ": expected DoomEdNums entry number, but got '" + token + "'" ) ;
return true ; // Finished with this file
}
// Then "="
if ( ! NextTokenIs ( "=" ) ) return true ; // Finished with this file
// Then actor class
SkipWhitespace ( false ) ;
string classname = StripTokenQuotes ( ReadToken ( ) ) ;
if ( string . IsNullOrEmpty ( classname ) )
{
General . ErrorLogger . Add ( ErrorType . Error , "Error while parisng '" + sourcename + "' at line " + GetCurrentLineNumber ( ) + ": unable to get DoomEdNums entry class definition" ) ;
return true ; // Finished with this file
}
2015-04-29 08:36:10 +00:00
// Possible special and args. We'll skip them
for ( int i = 0 ; i < 6 ; i + + )
2015-04-14 11:33:57 +00:00
{
2015-04-29 08:36:10 +00:00
if ( ! NextTokenIs ( "," , false ) ) break ;
2015-04-14 11:33:57 +00:00
2015-04-29 08:36:10 +00:00
// Read special name or arg value
if ( ! SkipWhitespace ( true ) | | string . IsNullOrEmpty ( ReadToken ( ) ) ) return false ;
2015-04-14 11:33:57 +00:00
}
// Add to collection?
if ( id ! = 0 ) doomednums [ id ] = classname . ToLowerInvariant ( ) ;
}
}
else if ( token = = "spawnnums" )
{
if ( ! NextTokenIs ( "{" ) ) return true ; // Finished with this file
while ( SkipWhitespace ( true ) )
{
token = ReadToken ( ) ;
if ( string . IsNullOrEmpty ( token ) )
{
General . ErrorLogger . Add ( ErrorType . Error , "Error while parisng '" + sourcename + "' at line " + GetCurrentLineNumber ( ) + ": failed to find the end of SpawnNums block" ) ;
return true ; // Finished with this file
}
if ( token = = "}" ) break ;
// First must be a number
int id ;
if ( ! int . TryParse ( token , NumberStyles . Integer , CultureInfo . InvariantCulture , out id ) )
{
// Not numeric!
General . ErrorLogger . Add ( ErrorType . Error , "Unexpected token found in '" + sourcename + "' at line " + GetCurrentLineNumber ( ) + ": expected SpawnNums number, but got '" + token + "'" ) ;
return true ; // Finished with this file
}
// Then "="
if ( ! NextTokenIs ( "=" ) ) return true ; // Finished with this file
// Then actor class
SkipWhitespace ( false ) ;
token = StripTokenQuotes ( ReadToken ( ) ) ;
if ( string . IsNullOrEmpty ( token ) )
{
General . ErrorLogger . Add ( ErrorType . Error , "Error while parisng '" + sourcename + "' at line " + GetCurrentLineNumber ( ) + ": unable to get SpawnNums entry class definition" ) ;
return true ;
}
// Add to collection
spawnnums [ id ] = token . ToLowerInvariant ( ) ;
}
}
else if ( token = = "$gzdb_skip" )
{
return true ; // Finished with this file
2013-09-11 09:47:53 +00:00
}
2015-04-14 11:33:57 +00:00
return false ; // Not done yet
2013-09-11 09:47:53 +00:00
}
2015-04-14 11:33:57 +00:00
#endregion
#region = = = = = = = = = = = = = = = = = = Methods
2014-12-03 23:15:26 +00:00
private static bool GetColor ( string name , ref Color4 color )
{
2013-09-11 09:47:53 +00:00
if ( name = = "black" ) return true ;
2013-09-11 08:49:45 +00:00
//probably it's a hex color (like FFCC11)?
int ci ;
2014-12-03 23:15:26 +00:00
if ( int . TryParse ( name , NumberStyles . HexNumber , CultureInfo . InvariantCulture , out ci ) )
{
2013-09-11 08:49:45 +00:00
color = new Color4 ( ci ) { Alpha = 1.0f } ;
return true ;
}
2012-06-01 10:17:47 +00:00
2013-09-11 08:49:45 +00:00
//probably it's a color name?
2013-09-11 09:47:53 +00:00
Color c = Color . FromName ( name ) ; //should be similar to C++ color name detection, I suppose
2014-12-03 23:15:26 +00:00
if ( c . IsKnownColor )
{
2013-09-11 09:47:53 +00:00
color = new Color4 ( c ) ;
return true ;
}
return false ;
}
2015-04-14 11:33:57 +00:00
#endregion
2013-09-11 09:47:53 +00:00
}
2012-06-01 10:17:47 +00:00
}