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
using System ;
using System.Collections.Generic ;
using System.Text ;
using System.IO ;
using CodeImp.DoomBuilder.Map ;
using CodeImp.DoomBuilder.Geometry ;
using System.Windows.Forms ;
using CodeImp.DoomBuilder.Config ;
using CodeImp.DoomBuilder.Types ;
#endregion
namespace CodeImp.DoomBuilder.IO
{
internal class UniversalStreamReader
{
#region = = = = = = = = = = = = = = = = = = Constants
// Name of the UDMF configuration file
private const string UDMF_CONFIG_NAME = "UDMF.cfg" ;
#endregion
#region = = = = = = = = = = = = = = = = = = Variables
private Configuration config ;
private bool setknowncustomtypes ;
private bool strictchecking = true ;
#endregion
#region = = = = = = = = = = = = = = = = = = Properties
public bool SetKnownCustomTypes { get { return setknowncustomtypes ; } set { setknowncustomtypes = value ; } }
public bool StrictChecking { get { return strictchecking ; } set { strictchecking = value ; } }
#endregion
#region = = = = = = = = = = = = = = = = = = Constructor / Disposer
// Constructor
public UniversalStreamReader ( )
{
// Make configuration
config = new Configuration ( ) ;
// Find a resource named UDMF.cfg
string [ ] resnames = General . ThisAssembly . GetManifestResourceNames ( ) ;
foreach ( string rn in resnames )
{
// Found it?
if ( rn . EndsWith ( UDMF_CONFIG_NAME , StringComparison . InvariantCultureIgnoreCase ) )
{
// Get a stream from the resource
Stream udmfcfg = General . ThisAssembly . GetManifestResourceStream ( rn ) ;
StreamReader udmfcfgreader = new StreamReader ( udmfcfg , Encoding . ASCII ) ;
// Load configuration from stream
config . InputConfiguration ( udmfcfgreader . ReadToEnd ( ) ) ;
// Now we add the linedef flags, activations and thing flags
// to this list, so that these don't show up in the custom
// fields list either. We use true as dummy value (it has no meaning)
// Add linedef flags
foreach ( KeyValuePair < string , string > flag in General . Map . Config . LinedefFlags )
config . WriteSetting ( "managedfields.linedef." + flag . Key , true ) ;
// Add linedef activations
foreach ( LinedefActivateInfo activate in General . Map . Config . LinedefActivates )
config . WriteSetting ( "managedfields.linedef." + activate . Key , true ) ;
2010-08-14 15:44:47 +00:00
// Add linedef flag translations
foreach ( FlagTranslation f in General . Map . Config . LinedefFlagsTranslation )
{
foreach ( string fn in f . Fields )
config . WriteSetting ( "managedfields.linedef." + fn , true ) ;
}
2009-04-19 18:07:22 +00:00
2013-07-10 08:59:17 +00:00
//mxd. Add sector flags
foreach ( KeyValuePair < string , string > flag in General . Map . Config . SectorFlags )
config . WriteSetting ( "managedfields.sector." + flag . Key , true ) ;
2009-04-19 18:07:22 +00:00
// Add thing flags
foreach ( KeyValuePair < string , string > flag in General . Map . Config . ThingFlags )
config . WriteSetting ( "managedfields.thing." + flag . Key , true ) ;
2010-08-14 15:44:47 +00:00
// Add thing flag translations
foreach ( FlagTranslation f in General . Map . Config . ThingFlagsTranslation )
{
foreach ( string fn in f . Fields )
config . WriteSetting ( "managedfields.thing." + fn , true ) ;
}
2009-04-19 18:07:22 +00:00
// Done
udmfcfgreader . Dispose ( ) ;
udmfcfg . Dispose ( ) ;
break ;
}
}
}
#endregion
#region = = = = = = = = = = = = = = = = = = Reading
// This reads from a stream
public MapSet Read ( MapSet map , Stream stream )
{
StreamReader reader = new StreamReader ( stream , Encoding . ASCII ) ;
Dictionary < int , Vertex > vertexlink ;
Dictionary < int , Sector > sectorlink ;
UniversalParser textmap = new UniversalParser ( ) ;
textmap . StrictChecking = strictchecking ;
2009-06-15 21:58:34 +00:00
try
2009-04-19 18:07:22 +00:00
{
// Read UDMF from stream
textmap . InputConfiguration ( reader . ReadToEnd ( ) ) ;
// Check for errors
if ( textmap . ErrorResult ! = 0 )
{
// Show parse error
General . ShowErrorMessage ( "Error on line " + textmap . ErrorLine + " while parsing UDMF map data:\n" + textmap . ErrorDescription , MessageBoxButtons . OK ) ;
}
else
{
// Read the map
vertexlink = ReadVertices ( map , textmap ) ;
sectorlink = ReadSectors ( map , textmap ) ;
ReadLinedefs ( map , textmap , vertexlink , sectorlink ) ;
ReadThings ( map , textmap ) ;
}
}
2009-06-15 21:58:34 +00:00
catch ( Exception e )
2009-04-19 18:07:22 +00:00
{
2009-06-15 21:58:34 +00:00
General . ShowErrorMessage ( "Unexpected error reading UDMF map data. " + e . GetType ( ) . Name + ": " + e . Message , MessageBoxButtons . OK ) ;
2009-04-19 18:07:22 +00:00
}
return map ;
}
// This reads the things
private void ReadThings ( MapSet map , UniversalParser textmap )
{
// Get list of entries
List < UniversalCollection > collections = GetNamedCollections ( textmap . Root , "thing" ) ;
// Go for all collections
2009-06-18 14:23:33 +00:00
map . SetCapacity ( 0 , 0 , 0 , 0 , map . Things . Count + collections . Count ) ;
2009-04-19 18:07:22 +00:00
for ( int i = 0 ; i < collections . Count ; i + + )
{
// Read fields
UniversalCollection c = collections [ i ] ;
int [ ] args = new int [ Linedef . NUM_ARGS ] ;
2009-06-15 21:58:34 +00:00
string where = "thing " + i ;
float x = GetCollectionEntry < float > ( c , "x" , true , 0.0f , where ) ;
float y = GetCollectionEntry < float > ( c , "y" , true , 0.0f , where ) ;
float height = GetCollectionEntry < float > ( c , "height" , false , 0.0f , where ) ;
int tag = GetCollectionEntry < int > ( c , "id" , false , 0 , where ) ;
int angledeg = GetCollectionEntry < int > ( c , "angle" , false , 0 , where ) ;
int type = GetCollectionEntry < int > ( c , "type" , true , 0 , where ) ;
int special = GetCollectionEntry < int > ( c , "special" , false , 0 , where ) ;
args [ 0 ] = GetCollectionEntry < int > ( c , "arg0" , false , 0 , where ) ;
args [ 1 ] = GetCollectionEntry < int > ( c , "arg1" , false , 0 , where ) ;
args [ 2 ] = GetCollectionEntry < int > ( c , "arg2" , false , 0 , where ) ;
args [ 3 ] = GetCollectionEntry < int > ( c , "arg3" , false , 0 , where ) ;
args [ 4 ] = GetCollectionEntry < int > ( c , "arg4" , false , 0 , where ) ;
2009-04-19 18:07:22 +00:00
// Flags
Dictionary < string , bool > stringflags = new Dictionary < string , bool > ( ) ;
foreach ( KeyValuePair < string , string > flag in General . Map . Config . ThingFlags )
2009-06-15 21:58:34 +00:00
stringflags [ flag . Key ] = GetCollectionEntry < bool > ( c , flag . Key , false , false , where ) ;
2009-04-19 18:07:22 +00:00
foreach ( FlagTranslation ft in General . Map . Config . ThingFlagsTranslation )
{
foreach ( string field in ft . Fields )
2009-06-15 21:58:34 +00:00
stringflags [ field ] = GetCollectionEntry < bool > ( c , field , false , false , where ) ;
2009-04-19 18:07:22 +00:00
}
// Create new item
Thing t = map . CreateThing ( ) ;
2010-08-15 19:43:00 +00:00
if ( t ! = null )
{
2010-10-05 08:31:27 +00:00
t . Update ( type , x , y , height , angledeg , stringflags , tag , special , args ) ;
2009-04-19 18:07:22 +00:00
2010-08-15 19:43:00 +00:00
// Custom fields
ReadCustomFields ( c , t , "thing" ) ;
}
2009-04-19 18:07:22 +00:00
}
}
// This reads the linedefs and sidedefs
private void ReadLinedefs ( MapSet map , UniversalParser textmap ,
Dictionary < int , Vertex > vertexlink , Dictionary < int , Sector > sectorlink )
{
// Get list of entries
List < UniversalCollection > linescolls = GetNamedCollections ( textmap . Root , "linedef" ) ;
List < UniversalCollection > sidescolls = GetNamedCollections ( textmap . Root , "sidedef" ) ;
// Go for all lines
2009-06-18 14:23:33 +00:00
map . SetCapacity ( 0 , map . Linedefs . Count + linescolls . Count , map . Sidedefs . Count + sidescolls . Count , 0 , 0 ) ;
2009-04-19 18:07:22 +00:00
for ( int i = 0 ; i < linescolls . Count ; i + + )
{
// Read fields
UniversalCollection lc = linescolls [ i ] ;
int [ ] args = new int [ Linedef . NUM_ARGS ] ;
2009-06-15 21:58:34 +00:00
string where = "linedef " + i ;
int tag = GetCollectionEntry < int > ( lc , "id" , false , 0 , where ) ;
int v1 = GetCollectionEntry < int > ( lc , "v1" , true , 0 , where ) ;
int v2 = GetCollectionEntry < int > ( lc , "v2" , true , 0 , where ) ;
int special = GetCollectionEntry < int > ( lc , "special" , false , 0 , where ) ;
args [ 0 ] = GetCollectionEntry < int > ( lc , "arg0" , false , 0 , where ) ;
args [ 1 ] = GetCollectionEntry < int > ( lc , "arg1" , false , 0 , where ) ;
args [ 2 ] = GetCollectionEntry < int > ( lc , "arg2" , false , 0 , where ) ;
args [ 3 ] = GetCollectionEntry < int > ( lc , "arg3" , false , 0 , where ) ;
args [ 4 ] = GetCollectionEntry < int > ( lc , "arg4" , false , 0 , where ) ;
int s1 = GetCollectionEntry < int > ( lc , "sidefront" , true , - 1 , where ) ;
int s2 = GetCollectionEntry < int > ( lc , "sideback" , false , - 1 , where ) ;
2009-04-19 18:07:22 +00:00
// Flags
Dictionary < string , bool > stringflags = new Dictionary < string , bool > ( ) ;
foreach ( KeyValuePair < string , string > flag in General . Map . Config . LinedefFlags )
2009-06-15 21:58:34 +00:00
stringflags [ flag . Key ] = GetCollectionEntry < bool > ( lc , flag . Key , false , false , where ) ;
2009-04-19 18:07:22 +00:00
foreach ( FlagTranslation ft in General . Map . Config . LinedefFlagsTranslation )
{
foreach ( string field in ft . Fields )
2009-06-15 21:58:34 +00:00
stringflags [ field ] = GetCollectionEntry < bool > ( lc , field , false , false , where ) ;
2009-04-19 18:07:22 +00:00
}
// Activations
foreach ( LinedefActivateInfo activate in General . Map . Config . LinedefActivates )
2009-06-15 21:58:34 +00:00
stringflags [ activate . Key ] = GetCollectionEntry < bool > ( lc , activate . Key , false , false , where ) ;
2009-04-19 18:07:22 +00:00
// Create new linedef
if ( vertexlink . ContainsKey ( v1 ) & & vertexlink . ContainsKey ( v2 ) )
{
2009-05-12 18:20:03 +00:00
// Check if not zero-length
if ( Vector2D . ManhattanDistance ( vertexlink [ v1 ] . Position , vertexlink [ v2 ] . Position ) > 0.0001f )
2009-04-19 18:07:22 +00:00
{
2009-05-12 18:20:03 +00:00
Linedef l = map . CreateLinedef ( vertexlink [ v1 ] , vertexlink [ v2 ] ) ;
2010-08-15 19:43:00 +00:00
if ( l ! = null )
2009-05-12 18:20:03 +00:00
{
2010-08-15 19:43:00 +00:00
l . Update ( stringflags , 0 , tag , special , args ) ;
l . UpdateCache ( ) ;
// Custom fields
ReadCustomFields ( lc , l , "linedef" ) ;
// Read sidedefs and connect them to the line
if ( s1 > - 1 )
{
if ( s1 < sidescolls . Count )
ReadSidedef ( map , sidescolls [ s1 ] , l , true , sectorlink , s1 ) ;
else
General . ErrorLogger . Add ( ErrorType . Warning , "Linedef " + i + " references invalid front sidedef " + s1 + ". Sidedef has been removed." ) ;
}
if ( s2 > - 1 )
{
if ( s2 < sidescolls . Count )
ReadSidedef ( map , sidescolls [ s2 ] , l , false , sectorlink , s2 ) ;
else
General . ErrorLogger . Add ( ErrorType . Warning , "Linedef " + i + " references invalid back sidedef " + s1 + ". Sidedef has been removed." ) ;
}
2009-05-12 18:20:03 +00:00
}
2009-04-19 18:07:22 +00:00
}
2009-05-12 18:20:03 +00:00
else
2009-04-19 18:07:22 +00:00
{
2009-05-12 18:20:03 +00:00
General . ErrorLogger . Add ( ErrorType . Warning , "Linedef " + i + " is zero-length. Linedef has been removed." ) ;
2009-04-19 18:07:22 +00:00
}
}
else
{
2009-05-12 18:20:03 +00:00
General . ErrorLogger . Add ( ErrorType . Warning , "Linedef " + i + " references one or more invalid vertices. Linedef has been removed." ) ;
2009-04-19 18:07:22 +00:00
}
}
}
// This reads a single sidedef and connects it to the given linedef
private void ReadSidedef ( MapSet map , UniversalCollection sc , Linedef ld ,
2009-06-15 21:58:34 +00:00
bool front , Dictionary < int , Sector > sectorlink , int index )
2009-04-19 18:07:22 +00:00
{
// Read fields
2009-06-15 21:58:34 +00:00
string where = "linedef " + ld . Index + ( front ? " front sidedef " : " back sidedef " ) + index ;
int offsetx = GetCollectionEntry < int > ( sc , "offsetx" , false , 0 , where ) ;
int offsety = GetCollectionEntry < int > ( sc , "offsety" , false , 0 , where ) ;
string thigh = GetCollectionEntry < string > ( sc , "texturetop" , false , "-" , where ) ;
string tlow = GetCollectionEntry < string > ( sc , "texturebottom" , false , "-" , where ) ;
string tmid = GetCollectionEntry < string > ( sc , "texturemiddle" , false , "-" , where ) ;
int sector = GetCollectionEntry < int > ( sc , "sector" , true , 0 , where ) ;
2009-04-19 18:07:22 +00:00
// Create sidedef
if ( sectorlink . ContainsKey ( sector ) )
{
Sidedef s = map . CreateSidedef ( ld , front , sectorlink [ sector ] ) ;
2010-08-15 19:43:00 +00:00
if ( s ! = null )
{
s . Update ( offsetx , offsety , thigh , tmid , tlow ) ;
2009-04-19 18:07:22 +00:00
2010-08-15 19:43:00 +00:00
// Custom fields
ReadCustomFields ( sc , s , "sidedef" ) ;
}
2009-04-19 18:07:22 +00:00
}
else
{
General . ErrorLogger . Add ( ErrorType . Warning , "Sidedef references invalid sector " + sector + ". Sidedef has been removed." ) ;
}
}
// This reads the sectors
private Dictionary < int , Sector > ReadSectors ( MapSet map , UniversalParser textmap )
{
Dictionary < int , Sector > link ;
// Get list of entries
List < UniversalCollection > collections = GetNamedCollections ( textmap . Root , "sector" ) ;
// Create lookup table
link = new Dictionary < int , Sector > ( collections . Count ) ;
// Go for all collections
2009-06-18 14:23:33 +00:00
map . SetCapacity ( 0 , 0 , 0 , map . Sectors . Count + collections . Count , 0 ) ;
2009-04-19 18:07:22 +00:00
for ( int i = 0 ; i < collections . Count ; i + + )
{
// Read fields
UniversalCollection c = collections [ i ] ;
2009-06-15 21:58:34 +00:00
string where = "sector " + i ;
int hfloor = GetCollectionEntry < int > ( c , "heightfloor" , false , 0 , where ) ;
int hceil = GetCollectionEntry < int > ( c , "heightceiling" , false , 0 , where ) ;
string tfloor = GetCollectionEntry < string > ( c , "texturefloor" , true , "-" , where ) ;
string tceil = GetCollectionEntry < string > ( c , "textureceiling" , true , "-" , where ) ;
int bright = GetCollectionEntry < int > ( c , "lightlevel" , false , 160 , where ) ;
int special = GetCollectionEntry < int > ( c , "special" , false , 0 , where ) ;
int tag = GetCollectionEntry < int > ( c , "id" , false , 0 , where ) ;
2009-04-19 18:07:22 +00:00
2013-07-10 08:59:17 +00:00
//mxd. Flags
Dictionary < string , bool > stringflags = new Dictionary < string , bool > ( ) ;
foreach ( KeyValuePair < string , string > flag in General . Map . Config . SectorFlags )
stringflags [ flag . Key ] = GetCollectionEntry < bool > ( c , flag . Key , false , false , where ) ;
2009-04-19 18:07:22 +00:00
// Create new item
Sector s = map . CreateSector ( ) ;
2010-08-15 19:43:00 +00:00
if ( s ! = null )
{
2013-07-10 08:59:17 +00:00
s . Update ( hfloor , hceil , tfloor , tceil , special , stringflags , tag , bright ) ;
2009-04-19 18:07:22 +00:00
2010-08-15 19:43:00 +00:00
// Custom fields
ReadCustomFields ( c , s , "sector" ) ;
2009-04-19 18:07:22 +00:00
2010-08-15 19:43:00 +00:00
// Add it to the lookup table
link . Add ( i , s ) ;
}
2009-04-19 18:07:22 +00:00
}
// Return lookup table
return link ;
}
// This reads the vertices
private Dictionary < int , Vertex > ReadVertices ( MapSet map , UniversalParser textmap )
{
Dictionary < int , Vertex > link ;
// Get list of entries
List < UniversalCollection > collections = GetNamedCollections ( textmap . Root , "vertex" ) ;
// Create lookup table
link = new Dictionary < int , Vertex > ( collections . Count ) ;
// Go for all collections
2009-06-18 14:23:33 +00:00
map . SetCapacity ( map . Vertices . Count + collections . Count , 0 , 0 , 0 , 0 ) ;
2009-04-19 18:07:22 +00:00
for ( int i = 0 ; i < collections . Count ; i + + )
{
// Read fields
UniversalCollection c = collections [ i ] ;
2009-06-15 21:58:34 +00:00
string where = "vertex " + i ;
float x = GetCollectionEntry < float > ( c , "x" , true , 0.0f , where ) ;
float y = GetCollectionEntry < float > ( c , "y" , true , 0.0f , where ) ;
2009-04-19 18:07:22 +00:00
// Create new item
Vertex v = map . CreateVertex ( new Vector2D ( x , y ) ) ;
2010-08-15 19:43:00 +00:00
if ( v ! = null )
{
2013-07-08 13:13:28 +00:00
//mxd. zoffsets
v . ZCeiling = GetCollectionEntry < float > ( c , "zceiling" , false , float . NaN , where ) ; //mxd
v . ZFloor = GetCollectionEntry < float > ( c , "zfloor" , false , float . NaN , where ) ; //mxd
2010-08-15 19:43:00 +00:00
// Custom fields
ReadCustomFields ( c , v , "vertex" ) ;
2009-04-19 18:07:22 +00:00
2010-08-15 19:43:00 +00:00
// Add it to the lookup table
link . Add ( i , v ) ;
}
2009-04-19 18:07:22 +00:00
}
// Return lookup table
return link ;
}
// This reads custom fields from a collection and adds them to a map element
private void ReadCustomFields ( UniversalCollection collection , MapElement element , string elementname )
{
2009-06-11 21:21:20 +00:00
element . Fields . BeforeFieldsChange ( ) ;
2009-04-19 18:07:22 +00:00
// Go for all the elements in the collection
foreach ( UniversalEntry e in collection )
{
// Check if not a managed field
if ( ! config . SettingExists ( "managedfields." + elementname + "." + e . Key ) )
{
int type = ( int ) UniversalType . Integer ;
// Try to find the type from configuration
2012-11-19 09:44:05 +00:00
if ( setknowncustomtypes ) {
type = General . Map . Options . GetUniversalFieldType ( elementname , e . Key , type ) ;
//mxd. Check type
object value = e . Value ;
// Let's be kind and cast any int to a float if needed
if ( type = = ( int ) UniversalType . Float & & e . Value . GetType ( ) = = typeof ( int ) ) {
value = ( float ) ( int ) e . Value ;
} else if ( ! e . IsValidType ( e . Value . GetType ( ) ) ) {
General . ErrorLogger . Add ( ErrorType . Warning , element . ToString ( ) + ": the value of entry '" + e . Key + "' is of incompatible type (expected " + e . GetType ( ) . Name + ", but got " + e . Value . GetType ( ) . Name + "). If you save the map, this value will be ignored." ) ;
continue ;
}
// Make custom field
element . Fields [ e . Key ] = new UniValue ( type , value ) ;
} else {
// Determine default type
if ( e . Value . GetType ( ) = = typeof ( int ) ) type = ( int ) UniversalType . Integer ;
else if ( e . Value . GetType ( ) = = typeof ( float ) ) type = ( int ) UniversalType . Float ;
else if ( e . Value . GetType ( ) = = typeof ( bool ) ) type = ( int ) UniversalType . Boolean ;
else if ( e . Value . GetType ( ) = = typeof ( string ) ) type = ( int ) UniversalType . String ;
// Make custom field
element . Fields [ e . Key ] = new UniValue ( type , e . Value ) ;
}
2009-04-19 18:07:22 +00:00
}
}
}
// This validates and returns an entry
2009-06-15 21:58:34 +00:00
private T GetCollectionEntry < T > ( UniversalCollection c , string entryname , bool required , T defaultvalue , string where )
2009-04-19 18:07:22 +00:00
{
T result = default ( T ) ;
bool found = false ;
// Find the entry
foreach ( UniversalEntry e in c )
{
// Check if matches
if ( e . Key = = entryname )
{
// Let's be kind and cast any int to a float if needed
if ( ( typeof ( T ) = = typeof ( float ) ) & &
( e . Value . GetType ( ) = = typeof ( int ) ) )
{
// Make it a float
2009-04-26 07:34:19 +00:00
object fvalue = ( float ) ( int ) e . Value ;
result = ( T ) fvalue ;
2009-04-19 18:07:22 +00:00
}
else
{
// Verify type
e . ValidateType ( typeof ( T ) ) ;
// Found it!
result = ( T ) e . Value ;
}
// Done
found = true ;
}
}
// Not found?
if ( ! found )
{
2009-06-15 21:58:34 +00:00
// Report error when entry is required!
2009-04-19 18:07:22 +00:00
if ( required )
2009-06-15 21:58:34 +00:00
General . ErrorLogger . Add ( ErrorType . Error , "Error while reading UDMF map data: Missing required field '" + entryname + "' at " + where + "." ) ;
// Make default entry
result = defaultvalue ;
2009-04-19 18:07:22 +00:00
}
// Return result
return result ;
}
// This makes a list of all collections with the given name
private List < UniversalCollection > GetNamedCollections ( UniversalCollection collection , string entryname )
{
List < UniversalCollection > list = new List < UniversalCollection > ( ) ;
// Make list
foreach ( UniversalEntry e in collection )
if ( ( e . Value is UniversalCollection ) & & ( e . Key = = entryname ) ) list . Add ( e . Value as UniversalCollection ) ;
return list ;
}
#endregion
}
}