2025-06-27 19:51:53 +08:00
using System.IO ;
using System.Collections ;
using System.Collections.Generic ;
using System ;
using System.ComponentModel ;
using Convention.EasySave.Types ;
using Convention.EasySave.Internal ;
2025-06-29 01:46:32 +08:00
namespace Convention.EasySave
2025-06-27 19:51:53 +08:00
{
2025-06-29 01:46:32 +08:00
public abstract class EasySaveReader : System . IDisposable
2025-06-27 19:51:53 +08:00
{
2025-06-29 01:46:32 +08:00
/// <summary>The settings used to create this reader.</summary>
public EasySaveSettings settings ;
protected int serializationDepth = 0 ;
#region EasySaveReader 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." ) ;
2025-06-27 19:51:53 +08:00
2025-06-29 01:46:32 +08:00
string currentKey ;
while ( ( currentKey = ReadPropertyName ( ) ) ! = key )
{
if ( currentKey = = null )
return false ;
Skip ( ) ;
}
return true ;
}
internal virtual bool StartReadObject ( )
2025-06-27 19:51:53 +08:00
{
2025-06-29 01:46:32 +08:00
serializationDepth + + ;
return false ;
2025-06-27 19:51:53 +08:00
}
2025-06-29 01:46:32 +08:00
internal virtual void EndReadObject ( )
2025-06-27 19:51:53 +08:00
{
2025-06-29 01:46:32 +08:00
serializationDepth - - ;
2025-06-27 19:51:53 +08:00
}
2025-06-29 01:46:32 +08:00
internal abstract bool StartReadDictionary ( ) ;
internal abstract void EndReadDictionary ( ) ;
internal abstract bool StartReadDictionaryKey ( ) ;
internal abstract void EndReadDictionaryKey ( ) ;
internal abstract void StartReadDictionaryValue ( ) ;
internal abstract bool EndReadDictionaryValue ( ) ;
2025-06-27 19:51:53 +08:00
2025-06-29 01:46:32 +08:00
internal abstract bool StartReadCollection ( ) ;
internal abstract void EndReadCollection ( ) ;
internal abstract bool StartReadCollectionItem ( ) ;
internal abstract bool EndReadCollectionItem ( ) ;
2025-06-27 19:51:53 +08:00
2025-06-29 01:46:32 +08:00
#endregion
2025-06-27 19:51:53 +08:00
2025-06-29 01:46:32 +08:00
internal EasySaveReader ( EasySaveSettings settings , bool readHeaderAndFooter = true )
{
this . settings = settings ;
}
2025-06-27 19:51:53 +08:00
2025-06-29 01:46:32 +08:00
// 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 EasySaveType file.</summary>
public virtual EasySaveReaderPropertyEnumerator Properties
{
get
{
return new EasySaveReaderPropertyEnumerator ( this ) ;
}
}
2025-06-27 19:51:53 +08:00
2025-06-29 01:46:32 +08:00
internal virtual EasySaveReaderRawEnumerator RawEnumerator
{
get
{
return new EasySaveReaderRawEnumerator ( this ) ;
}
}
2025-06-27 19:51:53 +08:00
2025-06-29 01:46:32 +08:00
/ *
* 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 ) ;
}
2025-06-27 19:51:53 +08:00
2025-06-29 01:46:32 +08:00
/// <summary>Reads a value of type T from the reader.</summary>
public virtual T Read < T > ( )
{
return Read < T > ( EasySaveTypeMgr . GetOrCreateEasySaveType ( typeof ( T ) ) ) ;
}
2025-06-27 19:51:53 +08:00
2025-06-29 01:46:32 +08:00
/// <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 , EasySaveTypeMgr . GetOrCreateEasySaveType ( typeof ( T ) ) ) ;
}
2025-06-27 19:51:53 +08:00
2025-06-29 01:46:32 +08:00
/// <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 > ( EasySaveTypeMgr . GetOrCreateEasySaveType ( typeof ( T ) ) ) ;
}
2025-06-27 19:51:53 +08:00
2025-06-29 01:46:32 +08:00
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
public T ReadProperty < T > ( EasySaveType type )
{
ReadPropertyName ( ) ;
return Read < T > ( type ) ;
}
2025-06-27 19:51:53 +08:00
2025-06-29 01:46:32 +08:00
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
public long ReadRefProperty ( )
{
ReadPropertyName ( ) ;
return Read_ref ( ) ;
}
2025-06-27 19:51:53 +08:00
2025-06-29 01:46:32 +08:00
internal Type ReadType ( )
{
return EasySaveReflection . GetType ( Read < string > ( EasySaveType_string . Instance ) ) ;
}
2025-06-27 19:51:53 +08:00
2025-06-29 01:46:32 +08:00
/// <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>
/// <returns>The objectContainingProperty object. This is helpful if you're setting a private property on a struct or other immutable type and need to return the boxed value.</returns>
public object SetPrivateProperty ( string name , object value , object objectContainingProperty )
{
var property = EasySaveReflection . GetEasySaveReflectedProperty ( 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 ) ;
return objectContainingProperty ;
}
2025-06-27 19:51:53 +08:00
2025-06-29 01:46:32 +08:00
/// <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="objectContainingField">The object containing the field we want to set.</param>
/// <returns>The objectContainingField object. This is helpful if you're setting a private property on a struct or other immutable type and need to return the boxed value.</returns>
public object SetPrivateField ( string name , object value , object objectContainingField )
{
var field = EasySaveReflection . GetEasySaveReflectedMember ( 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 ) ;
return objectContainingField ;
}
2025-06-27 19:51:53 +08:00
2025-06-29 01:46:32 +08:00
#region Read ( key ) & Read ( key , obj ) methods
2025-06-27 19:51:53 +08:00
2025-06-29 01:46:32 +08:00
/// <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." ) ;
2025-06-27 19:51:53 +08:00
2025-06-29 01:46:32 +08:00
Type type = ReadTypeFromHeader < T > ( ) ;
2025-06-27 19:51:53 +08:00
2025-06-29 01:46:32 +08:00
T obj = Read < T > ( EasySaveTypeMgr . GetOrCreateEasySaveType ( type ) ) ;
2025-06-27 19:51:53 +08:00
2025-06-29 01:46:32 +08:00
//ReadKeySuffix(); //No need to read key suffix as we're returning. Doing so would throw an error at this point for BinaryReaders.
return obj ;
}
2025-06-27 19:51:53 +08:00
2025-06-29 01:46:32 +08:00
/// <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 ;
2025-06-27 19:51:53 +08:00
2025-06-29 01:46:32 +08:00
Type type = ReadTypeFromHeader < T > ( ) ;
T obj = Read < T > ( EasySaveTypeMgr . GetOrCreateEasySaveType ( type ) ) ;
2025-06-27 19:51:53 +08:00
2025-06-29 01:46:32 +08:00
//ReadKeySuffix(); //No need to read key suffix as we're returning. Doing so would throw an error at this point for BinaryReaders.
return obj ;
}
2025-06-27 19:51:53 +08:00
2025-06-29 01:46:32 +08:00
/// <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 + "\"" ) ;
2025-06-27 19:51:53 +08:00
2025-06-29 01:46:32 +08:00
Type type = ReadTypeFromHeader < T > ( ) ;
2025-06-27 19:51:53 +08:00
2025-06-29 01:46:32 +08:00
ReadInto < T > ( obj , EasySaveTypeMgr . GetOrCreateEasySaveType ( type ) ) ;
2025-06-27 19:51:53 +08:00
2025-06-29 01:46:32 +08:00
//ReadKeySuffix(); //No need to read key suffix as we're returning. Doing so would throw an error at this point for BinaryReaders.
}
2025-06-27 19:51:53 +08:00
2025-06-29 01:46:32 +08:00
protected virtual void ReadObject < T > ( object obj , EasySaveType type )
{
// Check for null.
if ( StartReadObject ( ) )
return ;
2025-06-27 19:51:53 +08:00
2025-06-29 01:46:32 +08:00
type . ReadInto < T > ( this , obj ) ;
2025-06-27 19:51:53 +08:00
2025-06-29 01:46:32 +08:00
EndReadObject ( ) ;
}
protected virtual T ReadObject < T > ( EasySaveType type )
2025-06-27 19:51:53 +08:00
{
2025-06-29 01:46:32 +08:00
if ( StartReadObject ( ) )
return default ( T ) ;
object obj = type . Read < T > ( this ) ;
EndReadObject ( ) ;
return ( T ) obj ;
2025-06-27 19:51:53 +08:00
}
2025-06-29 01:46:32 +08:00
#endregion
#region Read ( EasySaveType ) & Read ( obj , EasySaveType ) 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 > ( EasySaveType type )
2025-06-27 19:51:53 +08:00
{
2025-06-29 01:46:32 +08:00
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 ) ( ( ECollectionType ) type ) . Read ( this ) ;
else if ( type . isDictionary )
return ( T ) ( ( EasySaveDictionaryType ) type ) . Read ( this ) ;
else
return ReadObject < T > ( type ) ;
2025-06-27 19:51:53 +08:00
}
2025-06-29 01:46:32 +08:00
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
public virtual void ReadInto < T > ( object obj , EasySaveType 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 )
( ( ECollectionType ) type ) . ReadInto ( this , obj ) ;
else if ( type . isDictionary )
( ( EasySaveDictionaryType ) type ) . ReadInto ( this , obj ) ;
else
ReadObject < T > ( obj , type ) ;
}
2025-06-27 19:51:53 +08:00
2025-06-29 01:46:32 +08:00
#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 ) ;
}
}
2025-06-27 19:51:53 +08:00
2025-06-29 01:46:32 +08:00
/// <summary>Creates a new EasySaveReader and loads the default file into it.</summary>
public static EasySaveReader Create ( )
{
return Create ( new EasySaveSettings ( ) ) ;
}
2025-06-27 19:51:53 +08:00
2025-06-29 01:46:32 +08:00
/// <summary>Creates a new EasySaveReader 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 EasySaveReader Create ( string filePath )
{
return Create ( new EasySaveSettings ( filePath ) ) ;
}
2025-06-27 19:51:53 +08:00
2025-06-29 01:46:32 +08:00
/// <summary>Creates a new EasySaveReader 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 EasySaveReader Create ( string filePath , EasySaveSettings settings )
{
return Create ( new EasySaveSettings ( filePath , settings ) ) ;
}
2025-06-27 19:51:53 +08:00
2025-06-29 01:46:32 +08:00
/// <summary>Creates a new EasySaveReader 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 EasySaveReader Create ( EasySaveSettings settings )
{
Stream stream = EasySaveStream . CreateStream ( settings , EasySaveFileMode . Read ) ;
if ( stream = = null )
return null ;
// Get the baseWriter using the given Stream.
if ( settings . format = = EasySave . Format . JSON )
return new EasySaveJSONReader ( stream , settings ) ;
2025-06-27 19:51:53 +08:00
return null ;
2025-06-29 01:46:32 +08:00
}
2025-06-27 19:51:53 +08:00
2025-06-29 01:46:32 +08:00
/// <summary>Creates a new EasySaveReader and loads the bytes provided into it.</summary>
public static EasySaveReader Create ( byte [ ] bytes )
{
return Create ( bytes , new EasySaveSettings ( ) ) ;
}
2025-06-27 19:51:53 +08:00
2025-06-29 01:46:32 +08:00
/// <summary>Creates a new EasySaveReader 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 EasySaveReader Create ( byte [ ] bytes , EasySaveSettings settings )
{
Stream stream = EasySaveStream . CreateStream ( new MemoryStream ( bytes ) , settings , EasySaveFileMode . Read ) ;
if ( stream = = null )
return null ;
2025-06-27 19:51:53 +08:00
2025-06-29 01:46:32 +08:00
// Get the baseWriter using the given Stream.
if ( settings . format = = EasySave . Format . JSON )
return new EasySaveJSONReader ( stream , settings ) ;
return null ;
}
2025-06-27 19:51:53 +08:00
2025-06-29 01:46:32 +08:00
internal static EasySaveReader Create ( Stream stream , EasySaveSettings settings )
{
stream = EasySaveStream . CreateStream ( stream , settings , EasySaveFileMode . Read ) ;
2025-06-27 19:51:53 +08:00
2025-06-29 01:46:32 +08:00
// Get the baseWriter using the given Stream.
if ( settings . format = = EasySave . Format . JSON )
return new EasySaveJSONReader ( stream , settings ) ;
return null ;
}
2025-06-27 19:51:53 +08:00
2025-06-29 01:46:32 +08:00
internal static EasySaveReader Create ( Stream stream , EasySaveSettings settings , bool readHeaderAndFooter )
2025-06-27 19:51:53 +08:00
{
2025-06-29 01:46:32 +08:00
// Get the baseWriter using the given Stream.
if ( settings . format = = EasySave . Format . JSON )
return new EasySaveJSONReader ( stream , settings , readHeaderAndFooter ) ;
return null ;
2025-06-27 19:51:53 +08:00
}
2025-06-29 01:46:32 +08:00
[EditorBrowsable(EditorBrowsableState.Never)]
public class EasySaveReaderPropertyEnumerator
2025-06-27 19:51:53 +08:00
{
2025-06-29 01:46:32 +08:00
public EasySaveReader reader ;
public EasySaveReaderPropertyEnumerator ( EasySaveReader reader )
2025-06-27 19:51:53 +08:00
{
2025-06-29 01:46:32 +08:00
this . reader = reader ;
}
public IEnumerator GetEnumerator ( )
{
string propertyName ;
while ( true )
2025-06-27 19:51:53 +08:00
{
2025-06-29 01:46:32 +08:00
// 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 ;
}
2025-06-27 19:51:53 +08:00
}
}
}
2025-06-29 01:46:32 +08:00
[EditorBrowsable(EditorBrowsableState.Never)]
public class EasySaveReaderRawEnumerator
2025-06-27 19:51:53 +08:00
{
2025-06-29 01:46:32 +08:00
public EasySaveReader reader ;
2025-06-27 19:51:53 +08:00
2025-06-29 01:46:32 +08:00
public EasySaveReaderRawEnumerator ( EasySaveReader reader )
{
this . reader = reader ;
}
public IEnumerator GetEnumerator ( )
2025-06-27 19:51:53 +08:00
{
2025-06-29 01:46:32 +08:00
while ( true )
{
string key = reader . ReadPropertyName ( ) ;
if ( key = = null )
yield break ;
2025-06-27 19:51:53 +08:00
2025-06-29 01:46:32 +08:00
Type type = reader . ReadTypeFromHeader < object > ( ) ;
2025-06-27 19:51:53 +08:00
2025-06-29 01:46:32 +08:00
byte [ ] bytes = reader . ReadElement ( ) ;
2025-06-27 19:51:53 +08:00
2025-06-29 01:46:32 +08:00
reader . ReadKeySuffix ( ) ;
2025-06-27 19:51:53 +08:00
2025-06-29 01:46:32 +08:00
if ( type ! = null )
yield return new KeyValuePair < string , EasySaveData > ( key , new EasySaveData ( type , bytes ) ) ;
}
2025-06-27 19:51:53 +08:00
}
}
}
}