451 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			C#
		
	
	
	
		
		
			
		
	
	
			451 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			C#
		
	
	
	
|  | using UnityEngine; | |||
|  | using System.IO; | |||
|  | using System.Collections; | |||
|  | using System.Collections.Generic; | |||
|  | using System; | |||
|  | using System.ComponentModel; | |||
|  | using ES3Types; | |||
|  | using ES3Internal; | |||
|  | 
 | |||
|  | public abstract class ES3Reader : System.IDisposable | |||
|  | { | |||
|  | 	/// <summary>The settings used to create this reader.</summary> | |||
|  | 	public ES3Settings settings; | |||
|  | 
 | |||
|  |     protected int serializationDepth = 0; | |||
|  | 
 | |||
|  | 	#region ES3Reader Abstract Methods | |||
|  | 
 | |||
|  | 	internal abstract int 		Read_int(); | |||
|  | 	internal abstract float 	Read_float(); | |||
|  | 	internal abstract bool 		Read_bool(); | |||
|  | 	internal abstract char 		Read_char(); | |||
|  | 	internal abstract decimal 	Read_decimal(); | |||
|  | 	internal abstract double 	Read_double(); | |||
|  | 	internal abstract long 		Read_long(); | |||
|  | 	internal abstract ulong 	Read_ulong(); | |||
|  | 	internal abstract byte 		Read_byte(); | |||
|  | 	internal abstract sbyte 	Read_sbyte(); | |||
|  | 	internal abstract short 	Read_short(); | |||
|  | 	internal abstract ushort 	Read_ushort(); | |||
|  | 	internal abstract uint 		Read_uint(); | |||
|  | 	internal abstract string 	Read_string(); | |||
|  | 	internal abstract byte[]	Read_byteArray(); | |||
|  |     internal abstract long      Read_ref(); | |||
|  | 
 | |||
|  |     [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] | |||
|  |     public abstract string ReadPropertyName(); | |||
|  | 
 | |||
|  | 	protected abstract Type ReadKeyPrefix(bool ignore = false); | |||
|  | 	protected abstract void ReadKeySuffix(); | |||
|  | 	internal abstract byte[] ReadElement(bool skip=false); | |||
|  | 
 | |||
|  | 	/// <summary>Disposes of the reader and it's underlying stream.</summary> | |||
|  | 	public abstract void Dispose(); | |||
|  | 
 | |||
|  | 	// Seeks to the given key. Note that the stream position will not be reset. | |||
|  |     internal virtual bool Goto(string key) | |||
|  |     { | |||
|  |         if (key == null) | |||
|  |             throw new ArgumentNullException("Key cannot be NULL when loading data."); | |||
|  | 
 | |||
|  |         string currentKey; | |||
|  |         while ((currentKey = ReadPropertyName()) != key) | |||
|  |         { | |||
|  |             if (currentKey == null) | |||
|  |                 return false; | |||
|  |             Skip(); | |||
|  |         } | |||
|  |         return true; | |||
|  |     } | |||
|  | 
 | |||
|  |     internal virtual bool StartReadObject() | |||
|  |     { | |||
|  |         serializationDepth++; | |||
|  |         return false; | |||
|  |     } | |||
|  | 
 | |||
|  | 	internal virtual void EndReadObject() | |||
|  |     { | |||
|  |         serializationDepth--; | |||
|  |     } | |||
|  | 
 | |||
|  | 	internal abstract bool StartReadDictionary(); | |||
|  | 	internal abstract void EndReadDictionary(); | |||
|  | 	internal abstract bool StartReadDictionaryKey(); | |||
|  | 	internal abstract void EndReadDictionaryKey(); | |||
|  | 	internal abstract void StartReadDictionaryValue(); | |||
|  | 	internal abstract bool EndReadDictionaryValue(); | |||
|  | 
 | |||
|  | 	internal abstract bool StartReadCollection(); | |||
|  | 	internal abstract void EndReadCollection(); | |||
|  | 	internal abstract bool StartReadCollectionItem(); | |||
|  | 	internal abstract bool EndReadCollectionItem(); | |||
|  | 
 | |||
|  | 	#endregion | |||
|  | 
 | |||
|  | 	internal ES3Reader(ES3Settings settings, bool readHeaderAndFooter = true) | |||
|  | 	{ | |||
|  | 		this.settings = settings; | |||
|  | 	} | |||
|  | 
 | |||
|  | 	// If this is not null, the next call to the Properties will return this name. | |||
|  | 	internal string overridePropertiesName = null; | |||
|  | 	/// <summary>Allows you to enumerate over each field name. This should only be used within an ES3Type file.</summary> | |||
|  | 	public virtual ES3ReaderPropertyEnumerator Properties | |||
|  | 	{ | |||
|  | 		get | |||
|  | 		{ | |||
|  | 			return new ES3ReaderPropertyEnumerator (this); | |||
|  | 		} | |||
|  | 	} | |||
|  | 
 | |||
|  | 	internal virtual ES3ReaderRawEnumerator RawEnumerator | |||
|  | 	{ | |||
|  | 		get | |||
|  | 		{ | |||
|  | 			return new ES3ReaderRawEnumerator (this); | |||
|  | 		} | |||
|  | 	} | |||
|  | 
 | |||
|  | 	/* | |||
|  | 	 * 	Skips the current object in the stream. | |||
|  | 	 * 	Stream position should be somewhere before the opening brace for the object. | |||
|  | 	 * 	When this method successfully exits, it will be on the closing brace for the object. | |||
|  | 	 */ | |||
|  | 	/// <summary>Skips the current object in the stream.</summary> | |||
|  | 	[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] | |||
|  | 	public virtual void Skip() | |||
|  | 	{ | |||
|  | 		ReadElement(true); | |||
|  | 	} | |||
|  | 
 | |||
|  | 	/// <summary>Reads a value of type T from the reader.</summary> | |||
|  | 	public virtual T Read<T>() | |||
|  | 	{  | |||
|  | 		return Read<T>(ES3TypeMgr.GetOrCreateES3Type(typeof(T)));  | |||
|  | 	} | |||
|  | 
 | |||
|  | 	/// <summary>Reads a value of type T from the reader into an existing object.</summary> | |||
|  | 	/// <param name="obj">The object we want to read our value into.</param> | |||
|  | 	public virtual void ReadInto<T>(object obj) | |||
|  | 	{  | |||
|  | 		ReadInto<T>(obj, ES3TypeMgr.GetOrCreateES3Type(typeof(T)));  | |||
|  | 	} | |||
|  | 
 | |||
|  | 	/// <summary>Reads a property (i.e. a property name and value) from the reader, ignoring the property name and only returning the value.</summary> | |||
|  | 	[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] | |||
|  | 	public T ReadProperty<T>() | |||
|  | 	{ | |||
|  | 		return ReadProperty<T>(ES3TypeMgr.GetOrCreateES3Type(typeof(T))); | |||
|  | 	} | |||
|  | 
 | |||
|  | 	[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] | |||
|  | 	public T ReadProperty<T>(ES3Type type) | |||
|  | 	{ | |||
|  | 		ReadPropertyName(); | |||
|  | 		return Read<T>(type); | |||
|  | 	} | |||
|  | 
 | |||
|  |     [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] | |||
|  |     public long ReadRefProperty() | |||
|  |     { | |||
|  |         ReadPropertyName(); | |||
|  |         return Read_ref(); | |||
|  |     } | |||
|  | 
 | |||
|  |     internal Type ReadType() | |||
|  | 	{ | |||
|  | 		return ES3Reflection.GetType(Read<string>(ES3Type_string.Instance)); | |||
|  | 	} | |||
|  | 
 | |||
|  | 	/// <summary>Sets the value of a private property on an object.</summary> | |||
|  | 	/// <param name="name">The name of the property we want to set.</param> | |||
|  | 	/// <param name="value">The value we want to set the property to.</param> | |||
|  | 	/// <param name="objectContainingProperty">The object containing the property we want to set.</param> | |||
|  | 	public void SetPrivateProperty(string name, object value, object objectContainingProperty) | |||
|  | 	{ | |||
|  | 		var property = ES3Reflection.GetES3ReflectedProperty(objectContainingProperty.GetType(), name); | |||
|  | 		if(property.IsNull) | |||
|  | 			throw new MissingMemberException("A private property named "+ name + " does not exist in the type "+objectContainingProperty.GetType()); | |||
|  | 		property.SetValue(objectContainingProperty, value); | |||
|  | 	} | |||
|  | 
 | |||
|  | 	/// <summary>Sets the value of a private field on an object.</summary> | |||
|  | 	/// <param name="name">The name of the field we want to set.</param> | |||
|  | 	/// <param name="value">The value we want to set the field to.</param> | |||
|  | 	/// <param name="objectContainingProperty">The object containing the field we want to set.</param> | |||
|  | 	public void SetPrivateField(string name, object value, object objectContainingField) | |||
|  | 	{ | |||
|  | 		var field = ES3Reflection.GetES3ReflectedMember(objectContainingField.GetType(), name); | |||
|  | 		if(field.IsNull) | |||
|  | 			throw new MissingMemberException("A private field named "+ name + " does not exist in the type "+objectContainingField.GetType()); | |||
|  | 		field.SetValue(objectContainingField, value); | |||
|  | 	} | |||
|  | 
 | |||
|  | 	#region Read(key) & Read(key, obj) methods | |||
|  | 
 | |||
|  | 	/// <summary>Reads a value from the reader with the given key.</summary> | |||
|  | 	/// <param name="key">The key which uniquely identifies our value.</param> | |||
|  | 	public virtual T Read<T>(string key) | |||
|  | 	{ | |||
|  | 		if(!Goto(key)) | |||
|  | 			throw new KeyNotFoundException("Key \"" + key + "\" was not found in file \""+settings.FullPath+"\". Use Load<T>(key, defaultValue) if you want to return a default value if the key does not exist."); | |||
|  | 
 | |||
|  | 		Type type = ReadTypeFromHeader<T>(); | |||
|  | 
 | |||
|  | 		T obj = Read<T>(ES3TypeMgr.GetOrCreateES3Type(type)); | |||
|  | 
 | |||
|  | 		//ReadKeySuffix(); //No need to read key suffix as we're returning. Doing so would throw an error at this point for BinaryReaders. | |||
|  | 		return obj; | |||
|  | 	} | |||
|  | 
 | |||
|  | 	/// <summary>Reads a value from the reader with the given key, returning the default value if the key does not exist.</summary> | |||
|  | 	/// <param name="key">The key which uniquely identifies our value.</param> | |||
|  | 	/// <param name="defaultValue">The value we want to return if this key does not exist in the reader.</param> | |||
|  | 	public virtual T Read<T>(string key, T defaultValue) | |||
|  | 	{ | |||
|  | 		if(!Goto(key)) | |||
|  | 			return defaultValue; | |||
|  | 
 | |||
|  | 		Type type = ReadTypeFromHeader<T>(); | |||
|  | 		T obj = Read<T>(ES3TypeMgr.GetOrCreateES3Type(type)); | |||
|  | 
 | |||
|  |         //ReadKeySuffix(); //No need to read key suffix as we're returning. Doing so would throw an error at this point for BinaryReaders. | |||
|  |         return obj; | |||
|  | 	} | |||
|  | 
 | |||
|  | 	/// <summary>Reads a value from the reader with the given key into the provided object.</summary> | |||
|  | 	/// <param name="key">The key which uniquely identifies our value.</param> | |||
|  | 	/// <param name="obj">The object we want to load the value into.</param> | |||
|  | 	public virtual void ReadInto<T>(string key, T obj) where T : class | |||
|  | 	{ | |||
|  | 		if(!Goto(key)) | |||
|  | 			throw new KeyNotFoundException("Key \"" + key + "\" was not found in file \""+settings.FullPath+"\""); | |||
|  | 
 | |||
|  | 		Type type = ReadTypeFromHeader<T>(); | |||
|  | 
 | |||
|  | 		ReadInto<T>(obj, ES3TypeMgr.GetOrCreateES3Type(type)); | |||
|  | 
 | |||
|  |         //ReadKeySuffix(); //No need to read key suffix as we're returning. Doing so would throw an error at this point for BinaryReaders. | |||
|  |     } | |||
|  | 
 | |||
|  |     protected virtual void ReadObject<T>(object obj, ES3Type type) | |||
|  | 	{ | |||
|  | 		// Check for null. | |||
|  | 		if(StartReadObject()) | |||
|  | 			return; | |||
|  | 
 | |||
|  | 		type.ReadInto<T>(this, obj); | |||
|  | 
 | |||
|  | 		EndReadObject(); | |||
|  | 	} | |||
|  | 
 | |||
|  | 	protected virtual T ReadObject<T>(ES3Type type) | |||
|  | 	{ | |||
|  | 		if(StartReadObject()) | |||
|  | 			return default(T); | |||
|  | 
 | |||
|  | 		object obj = type.Read<T>(this); | |||
|  | 
 | |||
|  | 		EndReadObject(); | |||
|  | 		return (T)obj; | |||
|  | 	} | |||
|  | 		 | |||
|  | 
 | |||
|  | 	#endregion | |||
|  | 
 | |||
|  | 	#region Read(ES3Type) & Read(obj,ES3Type) methods | |||
|  | 
 | |||
|  | 	/* | |||
|  | 	 * 	Parses the next JSON Object in the stream (i.e. must be between '{' and '}' chars). | |||
|  | 	 * 	If the first character in the Stream is not a '{', it will throw an error. | |||
|  | 	 * 	Will also read the terminating '}'. | |||
|  | 	 * 	If we have reached the end of stream, it will return null. | |||
|  | 	 */ | |||
|  | 	[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] | |||
|  | 	public virtual T Read<T>(ES3Type type) | |||
|  | 	{ | |||
|  | 		if(type == null || type.isUnsupported) | |||
|  | 			throw new NotSupportedException("Type of "+type+" is not currently supported, and could not be loaded using reflection."); | |||
|  | 		else if(type.isPrimitive) | |||
|  | 			return (T)type.Read<T>(this); | |||
|  | 		else if(type.isCollection) | |||
|  | 			return (T)((ES3CollectionType)type).Read(this); | |||
|  | 		else if(type.isDictionary) | |||
|  | 			return (T)((ES3DictionaryType)type).Read(this); | |||
|  | 		else | |||
|  | 			return ReadObject<T>(type); | |||
|  | 	} | |||
|  | 
 | |||
|  | 	[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] | |||
|  | 	public virtual void ReadInto<T>(object obj, ES3Type type) | |||
|  | 	{ | |||
|  | 		if(type == null || type.isUnsupported) | |||
|  | 			throw new NotSupportedException("Type of "+obj.GetType()+" is not currently supported, and could not be loaded using reflection."); | |||
|  | 
 | |||
|  | 		else if(type.isCollection) | |||
|  | 			((ES3CollectionType)type).ReadInto(this, obj); | |||
|  | 		else if(type.isDictionary) | |||
|  | 			((ES3DictionaryType)type).ReadInto(this, obj); | |||
|  | 		else | |||
|  | 			ReadObject<T>(obj, type); | |||
|  | 	} | |||
|  | 
 | |||
|  | 
 | |||
|  |     #endregion | |||
|  |     [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] | |||
|  |     internal Type ReadTypeFromHeader<T>() | |||
|  | 	{ | |||
|  | 		// Check whether we need to determine the type by reading the header. | |||
|  | 		if(typeof(T) == typeof(object)) | |||
|  | 			return ReadKeyPrefix(); | |||
|  | 		else if(settings.typeChecking) | |||
|  | 		{ | |||
|  | 			Type type = ReadKeyPrefix(); | |||
|  | 			if(type != typeof(T)) | |||
|  | 				throw new InvalidOperationException("Trying to load data of type "+typeof(T)+", but data contained in file is type of "+type+"."); | |||
|  | 			return type; | |||
|  | 		} | |||
|  | 		else | |||
|  | 		{ | |||
|  | 			ReadKeyPrefix(true); | |||
|  | 			return typeof(T); | |||
|  | 		} | |||
|  | 	} | |||
|  | 
 | |||
|  | 	/// <summary>Creates a new ES3Reader and loads the default file into it.</summary> | |||
|  | 	public static ES3Reader Create() | |||
|  | 	{ | |||
|  | 		return Create(new ES3Settings()); | |||
|  | 	} | |||
|  | 
 | |||
|  | 	/// <summary>Creates a new ES3Reader and loads a file in storage into it.</summary> | |||
|  | 	/// <param name="filePath">The relative or absolute path of the file we want to load into the reader.</param> | |||
|  | 	public static ES3Reader Create(string filePath) | |||
|  | 	{ | |||
|  | 		return Create(new ES3Settings(filePath)); | |||
|  | 	} | |||
|  | 
 | |||
|  | 	/// <summary>Creates a new ES3Reader and loads a file in storage into it.</summary> | |||
|  | 	/// <param name="filePath">The relative or absolute path of the file we want to load into the reader.</param> | |||
|  | 	/// <param name="settings">The settings we want to use to override the default settings.</param> | |||
|  | 	public static ES3Reader Create(string filePath, ES3Settings settings) | |||
|  | 	{ | |||
|  | 		return Create(new ES3Settings(filePath, settings)); | |||
|  | 	} | |||
|  | 
 | |||
|  | 	/// <summary>Creates a new ES3Reader and loads a file in storage into it.</summary> | |||
|  | 	/// <param name="settings">The settings we want to use to override the default settings.</param> | |||
|  | 	public static ES3Reader Create(ES3Settings settings) | |||
|  | 	{ | |||
|  | 		Stream stream = ES3Stream.CreateStream(settings, ES3FileMode.Read); | |||
|  | 		if(stream == null) | |||
|  | 			return null; | |||
|  | 
 | |||
|  |         // Get the baseWriter using the given Stream. | |||
|  |         if (settings.format == ES3.Format.JSON) | |||
|  |             return new ES3JSONReader(stream, settings); | |||
|  | 		return null; | |||
|  | 	} | |||
|  | 
 | |||
|  | 	/// <summary>Creates a new ES3Reader and loads the bytes provided into it.</summary> | |||
|  | 	public static ES3Reader Create(byte[] bytes) | |||
|  | 	{ | |||
|  | 		return Create(bytes, new ES3Settings()); | |||
|  | 	} | |||
|  | 
 | |||
|  | 	/// <summary>Creates a new ES3Reader and loads the bytes provided into it.</summary> | |||
|  | 	/// <param name="settings">The settings we want to use to override the default settings.</param> | |||
|  | 	public static ES3Reader Create(byte[] bytes, ES3Settings settings) | |||
|  | 	{ | |||
|  | 		Stream stream = ES3Stream.CreateStream(new MemoryStream(bytes), settings, ES3FileMode.Read); | |||
|  | 		if(stream == null) | |||
|  | 			return null; | |||
|  | 
 | |||
|  | 		// Get the baseWriter using the given Stream. | |||
|  | 		if(settings.format == ES3.Format.JSON) | |||
|  | 			return new ES3JSONReader(stream, settings); | |||
|  |         return null; | |||
|  | 	} | |||
|  | 
 | |||
|  | 	internal static ES3Reader Create(Stream stream, ES3Settings settings) | |||
|  | 	{ | |||
|  | 		stream = ES3Stream.CreateStream(stream, settings, ES3FileMode.Read); | |||
|  | 
 | |||
|  | 		// Get the baseWriter using the given Stream. | |||
|  | 		if(settings.format == ES3.Format.JSON) | |||
|  | 			return new ES3JSONReader(stream, settings); | |||
|  |         return null; | |||
|  | 	} | |||
|  | 
 | |||
|  | 	internal static ES3Reader Create(Stream stream, ES3Settings settings, bool readHeaderAndFooter) | |||
|  | 	{ | |||
|  | 		// Get the baseWriter using the given Stream. | |||
|  | 		if(settings.format == ES3.Format.JSON) | |||
|  | 			return new ES3JSONReader(stream, settings, readHeaderAndFooter); | |||
|  |         return null; | |||
|  | 	} | |||
|  | 
 | |||
|  | 	[EditorBrowsable(EditorBrowsableState.Never)] | |||
|  | 	public class ES3ReaderPropertyEnumerator | |||
|  | 	{ | |||
|  | 		public ES3Reader reader; | |||
|  | 
 | |||
|  | 		public ES3ReaderPropertyEnumerator(ES3Reader reader) | |||
|  | 		{ | |||
|  | 			this.reader = reader; | |||
|  | 		} | |||
|  | 
 | |||
|  | 		public IEnumerator GetEnumerator() | |||
|  | 		{ | |||
|  | 			string propertyName; | |||
|  | 			while(true) | |||
|  | 			{ | |||
|  | 				// Allows us to repeat a property name or insert one of our own. | |||
|  | 				if(reader.overridePropertiesName != null) | |||
|  | 				{ | |||
|  | 					string tempName = reader.overridePropertiesName; | |||
|  | 					reader.overridePropertiesName = null; | |||
|  | 					yield return tempName; | |||
|  | 				} | |||
|  | 				else | |||
|  | 				{ | |||
|  | 					if((propertyName = reader.ReadPropertyName()) == null) | |||
|  | 						yield break; | |||
|  | 					yield return propertyName; | |||
|  | 				} | |||
|  | 			} | |||
|  | 		} | |||
|  | 	} | |||
|  | 
 | |||
|  | 	[EditorBrowsable(EditorBrowsableState.Never)] | |||
|  | 	public class ES3ReaderRawEnumerator | |||
|  | 	{ | |||
|  | 		public ES3Reader reader; | |||
|  | 
 | |||
|  | 		public ES3ReaderRawEnumerator(ES3Reader reader) | |||
|  | 		{ | |||
|  | 			this.reader = reader; | |||
|  | 		} | |||
|  | 
 | |||
|  | 		public IEnumerator GetEnumerator() | |||
|  | 		{ | |||
|  | 			while(true) | |||
|  | 			{ | |||
|  | 				string key = reader.ReadPropertyName(); | |||
|  | 				if(key == null) | |||
|  | 					yield break; | |||
|  | 
 | |||
|  |                 Type type = reader.ReadTypeFromHeader<object>(); | |||
|  | 
 | |||
|  | 				byte[] bytes = reader.ReadElement(); | |||
|  | 
 | |||
|  |                 reader.ReadKeySuffix(); | |||
|  | 
 | |||
|  |                 if(type != null) | |||
|  | 				    yield return new KeyValuePair<string,ES3Data>(key, new ES3Data(type, bytes)); | |||
|  | 			} | |||
|  | 		} | |||
|  | 	} | |||
|  | } |