/*==LICENSE==*

CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

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.

You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.

Additional permissions under GNU GPL version 3 section 7

If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.

You can contact Cyan Worlds, Inc. by email legal@cyan.com
 or by snail mail at:
      Cyan Worlds, Inc.
      14617 N Newport Hwy
      Mead, WA   99021

*==LICENSE==*/
#ifndef PL_SDL_inc
#define PL_SDL_inc

//
// Code for the State Description Language (SDL)
//

#include "plSDLDescriptor.h"
#include "hsUtils.h"
#include "hsStlUtils.h"

#include "../pnFactory/plCreatable.h"
#include "../pnKeyedObject/plKey.h"
#include "../pnKeyedObject/plUoid.h"

#include "../plUnifiedTime/plUnifiedTime.h"

namespace plSDL
{
	typedef std::list<plStateDescriptor*> DescriptorList;	
	enum	GeneralPurpose
	{
		kLatestVersion  = -1,		// used when requesting the latest version of a state descriptor
		kMaxListSize	= 9999		// maximum size of var lists
	};

	enum ContentsFlags // or saveFlags, 16 bits
	{
		kHasUoid		= 0x1,		
		kHasNotificationInfo = 0x2,
		kHasTimeStamp	= 0x4,
		kSameAsDefault	= 0x8,
		kHasDirtyFlag	= 0x10,
		kWantTimeStamp	= 0x20,

		kAddedVarLengthIO = 0x8000,		// using to establish a new version in the header, can delete in 8/03
		
		kHasMaximumValue= 0xffff,	
	};

	enum RWOptions
	{
		kDirtyOnly				= 1<< 0,			// write option
		kSkipNotificationInfo	= 1<< 1,			// read/write option
		kBroadcast				= 1<< 2,			// send option
		kWriteTimeStamps		= 1<< 3,			// write out time stamps	
		kTimeStampOnRead		= 1<< 4,			// read: timestamp each var when it gets read. write: request that the reader timestamp the dirty vars.
		kTimeStampOnWrite		= 1<< 5,			// read: n/a. write: timestamp each var when it gets written.
		kKeepDirty				= 1<< 6,			// don't clear dirty flag on read
		kDontWriteDirtyFlag		= 1<< 7,			// write option. don't write var dirty flag.
		kMakeDirty				= 1<< 8,			// read/write: set dirty flag on var read/write. 
		kDirtyNonDefaults		= 1<< 9,			// dirty the var if non default value.
		kForceConvert			= 1<<10,			// always try to convert rec to latest on read
	};

	enum BehaviorFlags
	{
		kDisallowTimeStamping = 0x1,
	};

	extern const char* kAgeSDLObjectName;
	void VariableLengthRead(hsStream* s, int size, int* val);
	void VariableLengthWrite(hsStream* s, int size, int val);
};

class plStateVarNotificationInfo
{
private:
	std::string fHintString;
public:
	void SetHintString(const char* c) { fHintString=c;	}
	const char* GetHintString() const { return fHintString.c_str();	}

	void Read(hsStream* s, UInt32 readOptions);
	void Write(hsStream* s, UInt32 writeOptions) const;
};

//
// Base class for a state variable.
// A state var is a var descriptor and it's value (contents)
//
class plSimpleStateVariable;
class plSDStateVariable;
class plStateVariable
{
public:
	enum Flags
	{
		kDirty	= 0x1,	// true when someone sets the value using Set(...), can be cleared after writing
		kUsed	= 0x2	// true when it contains some value (either by Set(...) or Read() )	
	};
protected:
	UInt32 fFlags;
	plStateVarNotificationInfo fNotificationInfo;
public:
	plStateVariable() : fFlags(0) {}
	virtual ~plStateVariable() {}

	const char* GetName() const { return GetVarDescriptor()->GetName(); }
	bool IsNamed(const char* n) const { return (n && !stricmp(GetName(), n)); }
	virtual int GetCount() const = 0;

	// conversion ops
	virtual plSimpleStateVariable* GetAsSimpleStateVar() = 0;
	virtual plSDStateVariable* GetAsSDStateVar() = 0;
	virtual plVarDescriptor* GetVarDescriptor() = 0;
	virtual const plVarDescriptor* GetVarDescriptor() const = 0;
	virtual void Alloc(int cnt=-1 /* -1 means don't change count */) = 0;
	
	virtual bool IsDirty() const { return (fFlags & kDirty) != 0; }
	virtual bool IsUsed() const  { return (fFlags & kUsed) != 0; }

	void SetDirty(bool d) { if (d) fFlags |= kDirty; else fFlags &= ~kDirty; }
	void SetUsed(bool d) { if (d) fFlags |= kUsed; else fFlags &= ~kUsed; }
	virtual void SetFromDefaults(bool timeStampNow) = 0;
	virtual void TimeStamp( const plUnifiedTime & ut=plUnifiedTime::GetCurrentTime() ) = 0;
	virtual const plUnifiedTime& GetTimeStamp() const = 0;

	plStateVarNotificationInfo& GetNotificationInfo() {	return fNotificationInfo; }
	const plStateVarNotificationInfo& GetNotificationInfo() const {	return fNotificationInfo; }
	
	virtual void DumpToObjectDebugger(bool dirtyOnly, int level) const {}
	virtual void DumpToStream(hsStream* stream, bool dirtyOnly, int level) const {}

	// IO
	virtual bool ReadData(hsStream* s, float timeConvert, UInt32 readOptions);
	virtual bool WriteData(hsStream* s, float timeConvert, UInt32 writeOptions) const;
};

//
// Change Notifier. 
// When a plSimpleStateVariable changes it's value by more than the given delta value,
// a notification msg will be sent to the objects that registered interest.
//
class plStateChangeNotifier
{
	friend class plSimpleStateVariable;
private:
	float fDelta;
	typedef std::vector<plKey> KeyList;
	KeyList fKeys;		// the objects to notify on change>delta
	static UInt32 fCurrentPlayerID;

	void IAddKey(plKey k);
	int IRemoveKey(plKey k);
public:
	plStateChangeNotifier();
	plStateChangeNotifier(float i, plKey k);

	void AddNotificationKey(plKey k) { IAddKey(k);	}
	void AddNotificationKeys(KeyList keys);
	int RemoveNotificationKey(plKey k);			// returns number of keys left after removal
	int RemoveNotificationKeys(KeyList keys);	// returns number of keys left after removal

	void SendNotificationMsg(const plSimpleStateVariable* srcVar, const plSimpleStateVariable* dstVar, const char* sdlName);
	
	bool GetValue(float* i) const;
	bool SetValue(float i);

	static UInt32 GetCurrentPlayerID() { return fCurrentPlayerID;	}
	static void SetCurrentPlayerID(UInt32 p) { fCurrentPlayerID=p;	}

	bool operator==(const plStateChangeNotifier &) const;
};

//
// A (non-nested) variable descriptor and its data contents.
//
class plUoid;
class plKey;
class plClientUnifiedTime;
typedef unsigned char byte;
class plSimpleStateVariable : public plStateVariable
{
protected:
	union
	{
		int*	fI;		// array of int
		short*	fS;		// array of short
		byte*	fBy;	// array of byte
		float*	fF;		// array of float
		double*	fD;		// array of double
		bool*	fB;		// array of bool
		plUoid*	fU;		// array of uoid
		plCreatable** fC;	// array of plCreatable ptrs
		plVarDescriptor::String32* fS32;	// array of strings
		plClientUnifiedTime* fT;	// array of Times
	};
	mutable plUnifiedTime	fTimeStamp;		// the last time the var was changed
	plSimpleVarDescriptor fVar;

	typedef std::vector<plStateChangeNotifier> StateChangeNotifiers;
	StateChangeNotifiers fChangeNotifiers;

	void IDeAlloc();
	void IInit();	// initize vars
	void IVarSet(bool timeStampNow=false);
	
	// converter fxns
	bool IConvertFromBool(plVarDescriptor::Type newType);
	bool IConvertFromInt(plVarDescriptor::Type newType);
	bool IConvertFromByte(plVarDescriptor::Type newType);
	bool IConvertFromShort(plVarDescriptor::Type newType);
	bool IConvertFromFloat(plVarDescriptor::Type newType);
	bool IConvertFromDouble(plVarDescriptor::Type newType);
	bool IConvertFromString(plVarDescriptor::Type newType);
	bool IConvertFromRGB(plVarDescriptor::Type newType);
	bool IConvertFromRGBA(plVarDescriptor::Type newType);
	bool IConvertFromRGB8(plVarDescriptor::Type newType);
	bool IConvertFromRGBA8(plVarDescriptor::Type newType);

	bool IReadData(hsStream* s, float timeConvert, int idx, UInt32 readOptions);	
	bool IWriteData(hsStream* s, float timeConvert, int idx, UInt32 writeOptions) const;

public:

	plSimpleStateVariable() { IInit(); }		
	plSimpleStateVariable(plVarDescriptor* vd) { IInit(); CopyFrom(vd); }	
	~plSimpleStateVariable() { IDeAlloc(); }
	
	// conversion ops
	plSimpleStateVariable* GetAsSimpleStateVar() { return this; }
	plSDStateVariable* GetAsSDStateVar() { return nil; }
	bool operator==(const plSimpleStateVariable &other) const;	// assumes matching var descriptors

	void TimeStamp( const plUnifiedTime & ut=plUnifiedTime::GetCurrentTime() );
	void CopyFrom(plVarDescriptor* v);
	void CopyData(const plSimpleStateVariable* other, UInt32 writeOptions=0);
	bool SetFromString(const char* value, int idx, bool timeStampNow);		// set value from string, type.  return false on err
	char* GetAsString(int idx) const;
	bool ConvertTo(plSimpleVarDescriptor* toVar, bool force=false);			// return false on err
	void Alloc(int cnt=-1 /* -1 means don't change count */);									// alloc memory after setting type
	void Reset();

	// setters
	bool Set(float v, int idx=0);
	bool Set(float* v, int idx=0);							// floatVector
	bool Set(double v, int idx=0);
	bool Set(double* v, int idx=0);							// doubleVector
	bool Set(int v, int idx=0);
	bool Set(int* v, int idx=0) { return Set(*v, idx);	}	// helper since there is no int vec type
	bool Set(byte v, int idx=0);
	bool Set(byte* v, int idx=0);							// byteVector
	bool Set(short v, int idx=0);
	bool Set(short* v, int idx=0) { return Set(*v, idx); }	// helper since there is no short vec type
	bool Set(bool v, int idx=0);
	bool Set(bool* v, int idx=0) { return Set(*v, idx);	}	// helper since there is no bool vec type
	bool Set(const char* v, int idx=0);						// string
	bool Set(const plKey& v, int idx=0);
	bool Set(plCreatable*, int idx=0);						// only SDL generated by the server is allowed to use this type.
	void SetFromDefaults(bool timeStampNow);
	
	// getters
	bool Get(int* value, int idx=0) const;
	bool Get(short* value, int idx=0) const;
	bool Get(byte* value, int idx=0) const;				// returns byte or byteVector
	bool Get(float* value, int idx=0) const;			// returns float or floatVector
	bool Get(double* value, int idx=0) const;			// returns double or doubleVector
	bool Get(bool* value, int idx=0) const;
	bool Get(char value[], int idx=0) const;
	bool Get(plKey* value, int idx=0) const;
	bool Get(plCreatable** value, int idx=0) const;
	const plUnifiedTime& GetTimeStamp() const { return fTimeStamp;	}

	// Special backdoor so the KI Manager can get the key name without having a ResMgr
	const char* GetKeyName(int idx=0) const;

	int GetCount() const { return fVar.GetCount(); }	// helper
	plVarDescriptor* GetVarDescriptor() { return &fVar; }
	plSimpleVarDescriptor* GetSimpleVarDescriptor() { return fVar.GetAsSimpleVarDescriptor(); }
	const plVarDescriptor* GetVarDescriptor() const { return &fVar; }
	const plSimpleVarDescriptor* GetSimpleVarDescriptor() const { return fVar.GetAsSimpleVarDescriptor(); }

	// State Change Notification
	void AddStateChangeNotification(plStateChangeNotifier& n);
	void RemoveStateChangeNotification(plKey notificationObj);		// remove all with this key 
	void RemoveStateChangeNotification(plStateChangeNotifier n);	// remove ones which match
	void NotifyStateChange(const plSimpleStateVariable* other, const char* sdlName);		// send notification msg if necessary, internal use

	void DumpToObjectDebugger(bool dirtyOnly, int level) const;
	void DumpToStream(hsStream* stream, bool dirtyOnly, int level) const;

	// IO
	bool ReadData(hsStream* s, float timeConvert, UInt32 readOptions);	
	bool WriteData(hsStream* s, float timeConvert, UInt32 writeOptions) const;
};

//
// A list of state data records, all of which are the same kind. 
// Corresponds to a SD var descriptor.
//
class plStateDataRecord;
class plSDStateVariable : public plStateVariable
{
public:
	typedef std::vector<const plStateDataRecord*> ConstDataRecList;	
protected:
	typedef std::vector<plStateDataRecord*> DataRecList;
	DataRecList fDataRecList;	
	plSDVarDescriptor* fVarDescriptor;

	void IDeInit();
public:
	plSDStateVariable(plSDVarDescriptor* sdvd);
	~plSDStateVariable();	// delete all records

	// conversion ops
	plSimpleStateVariable* GetAsSimpleStateVar() { return nil; }
	plSDStateVariable* GetAsSDStateVar() { return this; }
	bool operator==(const plSDStateVariable &other) const;	// assumes matching var descriptors

	void ConvertTo(plSDStateVariable* otherSDVar, bool force=false);
	void CopyFrom(plSDStateVariable* other, UInt32 writeOptions=0);
	void UpdateFrom(plSDStateVariable* other, UInt32 writeOptions=0);
	void AddStateDataRecord(plStateDataRecord *sdr) { fDataRecList.push_back(sdr); SetDirty(true); SetUsed(true); }
	void InsertStateDataRecord(plStateDataRecord *sdr, int i) { fDataRecList[i] = sdr; SetDirty(true); SetUsed(true);}
	void SetFromDefaults(bool timeStampNow);
	void TimeStamp( const plUnifiedTime & ut=plUnifiedTime::GetCurrentTime() );
	const plUnifiedTime& GetTimeStamp() const { static plUnifiedTime foo; return foo; }
	
	void Alloc(int cnt=-1 /* -1 means don't change count */);	// wipe and re-create
	void Alloc(plSDVarDescriptor* sdvd, int cnt=-1);			// wipe and re-create
	void Resize(int cnt);
	
	bool IsDirty() const;
	bool IsUsed() const;

	// getters
	plStateDataRecord* GetStateDataRecord(int i) { return fDataRecList[i]; }
	const plStateDataRecord* GetStateDataRecord(int i) const { return fDataRecList[i]; }
	int GetCount() const { return fDataRecList.size(); }
	int GetUsedCount() const;
	int GetDirtyCount() const;
	void GetUsedDataRecords(ConstDataRecList*) const;
	void GetDirtyDataRecords(ConstDataRecList*) const;
	plVarDescriptor* GetVarDescriptor() { return fVarDescriptor; }
	plSDVarDescriptor* GetSDVarDescriptor() { return fVarDescriptor->GetAsSDVarDescriptor(); }
	const plVarDescriptor* GetVarDescriptor() const { return fVarDescriptor; }
	const plSDVarDescriptor* GetSDVarDescriptor() const { return fVarDescriptor->GetAsSDVarDescriptor(); }
	void FlagNewerState(const plSDStateVariable&, bool respectAlwaysNew);
	void FlagAlwaysNewState();
	void DumpToObjectDebugger(bool dirtyOnly, int level) const;
	void DumpToStream(hsStream* stream, bool dirtyOnly, int level) const;
	
	// IO
	bool ReadData(hsStream* s, float timeConvert, UInt32 readOptions);	
	bool WriteData(hsStream* s, float timeConvert, UInt32 writeOptions) const;
};

//
// Contains the actual data contents and points to its associated descriptor
//
class plNetMsgSDLState;
class plStateDataRecord : public plCreatable
{
public:
	typedef std::vector<plSimpleStateVariable*> SimpleVarsList;
	typedef std::vector<plSDStateVariable*> SDVarsList;
	enum Flags
	{
		kVolatile = 0x1
	};
protected:
	typedef std::vector<plStateVariable*> VarsList;

	const plStateDescriptor* fDescriptor;
	plUoid		fAssocObject;		// optional
	VarsList	fVarsList;			// list of variables
	VarsList	fSDVarsList;		// list of nested data records
	UInt32		fFlags;
	static const UInt8 kIOVersion;	// I/O Version
	
	void IDeleteVarsList(VarsList& vars);
	void IInitDescriptor(const char* name, int version);	// or plSDL::kLatestVersion
	void IInitDescriptor(const plStateDescriptor* sd);
	
	void IReadHeader(hsStream* s);
	void IWriteHeader(hsStream* s) const;
	bool IConvertVar(plSimpleStateVariable* fromVar, plSimpleStateVariable* toVar, bool force);

	plStateVariable* IFindVar(const VarsList& vars, const char* name) const;
	int IGetNumUsedVars(const VarsList& vars) const;
	int IGetUsedVars(const VarsList& varsOut, VarsList *varsIn) const;	// build a list of vars that have data
	bool IHasUsedVars(const VarsList& vars) const;

	int IGetNumDirtyVars(const VarsList& vars) const;
	int IGetDirtyVars(const VarsList& varsOut, VarsList *varsIn) const;	// build a list of vars that are dirty
	bool IHasDirtyVars(const VarsList& vars) const;
public:
	CLASSNAME_REGISTER( plStateDataRecord );
	GETINTERFACE_ANY( plStateDataRecord, plCreatable);

	plStateDataRecord(const char* sdName, int version=plSDL::kLatestVersion);
	plStateDataRecord(plStateDescriptor* sd);
	plStateDataRecord(const plStateDataRecord &other, UInt32 writeOptions=0 ):fFlags(0) { CopyFrom(other, writeOptions); }
	plStateDataRecord():fFlags(0) {}
	~plStateDataRecord();
	
	bool ConvertTo(plStateDescriptor* other, bool force=false );
	bool operator==(const plStateDataRecord &other) const;	// assumes matching state descriptors

	UInt32 GetFlags() const { return fFlags;	}
	void SetFlags(UInt32 f) { fFlags =f;	}
	
	plSimpleStateVariable* FindVar(const char* name) const { return (plSimpleStateVariable*)IFindVar(fVarsList, name); }
	plSDStateVariable* FindSDVar(const char* name) const { return (plSDStateVariable*)IFindVar(fSDVarsList, name); }
	
	plStateDataRecord& operator=(const plStateDataRecord& other) { CopyFrom(other); }
	void CopyFrom(const plStateDataRecord& other, UInt32 writeOptions=0);
	void UpdateFrom(const plStateDataRecord& other, UInt32 writeOptions=0);
	void SetFromDefaults(bool timeStampNow);
	void TimeStampDirtyVars();
	
	int GetNumVars() const { return fVarsList.size();	}
	plSimpleStateVariable* GetVar(int i) const { return (plSimpleStateVariable*)fVarsList[i];	}
	int GetNumSDVars() const { return fSDVarsList.size();	}
	plSDStateVariable* GetSDVar(int i) const { return (plSDStateVariable*)fSDVarsList[i];	}

	// Used vars
	bool IsUsed() const { return (HasUsedVars() || HasUsedSDVars()); }
	
	int GetNumUsedVars() const { return IGetNumUsedVars(fVarsList); }
	int GetUsedVars(SimpleVarsList *vars) const { return IGetUsedVars(fVarsList, (VarsList*)vars); }	// build a list of vars that have data
	bool HasUsedVars() const { return IHasUsedVars(fVarsList); }

	int GetNumUsedSDVars() const { return IGetNumUsedVars(fSDVarsList); }
	int GetUsedSDVars(SDVarsList *vars) const { return IGetUsedVars(fSDVarsList, (VarsList*)vars); }	// build a list of SD vars that have data
	bool HasUsedSDVars() const { return IHasUsedVars(fSDVarsList); }

	// Dirty Vars
	bool IsDirty() const { return (HasDirtyVars() || HasDirtySDVars()); }	
	
	int GetNumDirtyVars() const { return IGetNumDirtyVars(fVarsList); }
	int GetDirtyVars(SimpleVarsList *vars) const { return IGetDirtyVars(fVarsList, (VarsList*)vars); }	// build a list of vars that are dirty
	bool HasDirtyVars() const { return IHasDirtyVars(fVarsList); }

	int GetNumDirtySDVars() const { return IGetNumDirtyVars(fSDVarsList); }
	int GetDirtySDVars(SDVarsList *vars) const { return IGetDirtyVars(fSDVarsList, (VarsList*)vars); };	// build a list of Sdvars that are dirty
	bool HasDirtySDVars() const { return IHasDirtyVars(fSDVarsList); }

	const plStateDescriptor* GetDescriptor() const { return fDescriptor; }
	void SetDescriptor(const char* sdName, int version);
	
	plNetMsgSDLState* PrepNetMsg(float timeConvert, UInt32 writeOptions) const;	// create/prep a net msg with this data
	
	void SetAssocObject(const plUoid& u) { fAssocObject=u; }		// optional 
	plUoid* GetAssocObject() { return &fAssocObject; }		// optional
	const plUoid* GetAssocObject() const { return &fAssocObject; }		// optional

	// utils
	void FlagDifferentState(const plStateDataRecord& other);	// mark items which differ from 'other' as dirty
	void FlagNewerState(const plStateDataRecord& other, bool respectAlwaysNew=false);	// mark items which are newer than 'other' as dirty
	void FlagAlwaysNewState();	// mark 'alwaysNew' items as dirty
	void DumpToObjectDebugger(const char* msg, bool dirtyOnly=false, int level=0) const;
	void DumpToStream(hsStream* stream, const char* msg, bool dirtyOnly=false, int level=0) const;

	// IO
	bool Read(hsStream* s, float timeConvert, UInt32 readOptions=0);
	void Write(hsStream* s, float timeConvert, UInt32 writeOptions=0) const;

	static bool ReadStreamHeader(hsStream* s, char** name, int* version, plUoid* objUoid=nil);
	void WriteStreamHeader(hsStream* s, plUoid* objUoid=nil) const;
};

//
// Simple SDL parser
//
class plSDLMgr;
class plSDLParser
{
private:
	bool IReadDescriptors() const;
	bool ILoadSDLFile(const char* fileName) const;
	bool IParseVarDesc(const char* fileName, hsStream* stream, char token[], plStateDescriptor*& curDesc, 
		plVarDescriptor*& curVar) const;
	bool IParseStateDesc(const char* fileName, hsStream* stream, char token[], plStateDescriptor*& curDesc) const;

	void DebugMsg(char* fmt, ...) const;
	void DebugMsgV(char* fmt, va_list args) const;

public:

	bool Parse() const;	// reads sdl folder, creates descriptor list
};

//
// Holds, loads and unloads all state descriptors from sdl files.
// Singleton.
//
class plNetApp;
class plSDLMgr
{
	friend class plSDLParser;
private:
	std::string fSDLDir;
	plSDL::DescriptorList fDescriptors;
	plNetApp*	fNetApp;
	UInt32		fBehaviorFlags;

	void IDeleteDescriptors(plSDL::DescriptorList* dl);
public:
	plSDLMgr();
	~plSDLMgr();

	static plSDLMgr* GetInstance();
	plStateDescriptor* FindDescriptor(const char* name, int version, const plSDL::DescriptorList * dl=nil) const;	// version or kLatestVersion
	
	const plSDL::DescriptorList * GetDescriptors( void ) const { return &fDescriptors;}

	void SetSDLDir(const char* s) { fSDLDir=s; }
	const char* GetSDLDir() const { return fSDLDir.c_str(); }

	void SetNetApp(plNetApp* a) { fNetApp=a; }
	plNetApp* GetNetApp() const { return fNetApp; }
	
	bool Init( UInt32 behaviorFlags=0 );	// parse sdl folder
	void DeInit();
	UInt32 GetBehaviorFlags() const { return fBehaviorFlags; }
	void SetBehaviorFlags(UInt32 v) { fBehaviorFlags=v; }
	bool AllowTimeStamping() const { return ! ( fBehaviorFlags&plSDL::kDisallowTimeStamping ); }

	// I/O - return # of bytes read/written
	int Write(hsStream* s, const plSDL::DescriptorList* dl=nil);	// write descriptors to a stream
	int Read(hsStream* s, plSDL::DescriptorList* dl=nil);		// read descriptors into provided list (use legacyList if nil)
};

#endif	// PL_SDL_inc