diff --git a/RmSharp.Tests/Converters/Double.cs b/RmSharp.Tests/Converters/Double.cs index d525e4b..86c54ae 100644 --- a/RmSharp.Tests/Converters/Double.cs +++ b/RmSharp.Tests/Converters/Double.cs @@ -8,11 +8,7 @@ public class Double { private readonly byte[] _rubyMarshalData = new byte[] { - (byte)RubyMarshalToken.Double, // Token indicating a double - 0x08, // Length of the string "1.2\0\0\0\0" (8 bytes total) - 0x31, // '1' in ASCII - 0x2E, // '.' in ASCII - 0x32, // '2' in ASCII + ( byte ) RubyMarshalToken.Double, 0x08, 0x31, 0x2E, 0x32 }; private readonly double _expectedValue = 1.2; diff --git a/RmSharp.Tests/Converters/Dynamic.cs b/RmSharp.Tests/Converters/Dynamic.cs new file mode 100644 index 0000000..799153a --- /dev/null +++ b/RmSharp.Tests/Converters/Dynamic.cs @@ -0,0 +1,41 @@ +using RmSharp.Converters; +using RmSharp.Tokens; + +namespace RmSharp.Tests.Converters +{ + [TestClass] + public class Dynamic + { + private readonly byte[] _rubyMarshalData = new byte[] { ( byte ) RubyMarshalToken.Fixnum, 0x02, 0xD2, 0x04 }; + private readonly dynamic _expectedValue = 1234; + + [TestMethod] + public void Read( ) + { + var converter = RmConverterFactory.GetConverter( _expectedValue.GetType( ) ); + using ( var memoryStream = new MemoryStream( _rubyMarshalData ) ) + using ( var reader = new BinaryReader( memoryStream ) ) + { + var result = converter.Read( reader ); + + Assert.IsNotNull( result ); + Assert.IsInstanceOfType( result, _expectedValue.GetType( ) ); + Assert.AreEqual( _expectedValue, result ); + } + } + + [TestMethod] + public void Write( ) + { + var converter = RmConverterFactory.GetConverter( _expectedValue.GetType( ) ); + using ( var memoryStream = new MemoryStream( ) ) + using ( var writer = new BinaryWriter( memoryStream ) ) + { + converter.Write( writer, _expectedValue ); + + byte[] writtenData = memoryStream.ToArray( ); + CollectionAssert.AreEqual( _rubyMarshalData, writtenData ); + } + } + } +} \ No newline at end of file diff --git a/RmSharp.Tests/Converters/DynamicArray.cs b/RmSharp.Tests/Converters/DynamicArray.cs new file mode 100644 index 0000000..0ae2b46 --- /dev/null +++ b/RmSharp.Tests/Converters/DynamicArray.cs @@ -0,0 +1,55 @@ +using RmSharp.Converters; +using RmSharp.Tokens; + +namespace RmSharp.Tests.Converters +{ + [TestClass] + public class DyamicArray + { + private readonly byte[] _rubyMarshalData = new byte[] { + + ( byte ) RubyMarshalToken.Array, 0x0A, + ( byte ) RubyMarshalToken.Fixnum, 0x02, 0xD2, 0x04, + ( byte ) RubyMarshalToken.String, 0x10, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x57, 0x6f, 0x72, 0x6c, 0x64, + ( byte ) RubyMarshalToken.Fixnum, 0x02, 0xD2, 0x04, + ( byte ) RubyMarshalToken.Fixnum, 0x02, 0xD2, 0x04, + ( byte ) RubyMarshalToken.Double, 0x08, 0x31, 0x2E, 0x32 + }; + + private readonly dynamic[] _expectedValue = [1234, "Hello World", 1234, 1234, 1.2]; + + [TestMethod] + public void Read( ) + { + var converter = RmConverterFactory.GetConverter( _expectedValue.GetType() ); + using ( var memoryStream = new MemoryStream( _rubyMarshalData ) ) + using ( var reader = new BinaryReader( memoryStream ) ) + { + var result = ( dynamic[] ) converter.Read( reader ); + + Assert.IsNotNull( result ); + Assert.IsInstanceOfType( result, _expectedValue.GetType( ) ); + + for ( var i = 0; i < result.Length; i++ ) + { + if ( result[i] != _expectedValue[i] ) + Assert.Fail( $"Item at index '{i}' is not equal to '{_expectedValue[i]}' got '{result[i]}'." ); + } + } + } + + [TestMethod] + public void Write( ) + { + var converter = RmConverterFactory.GetConverter( _expectedValue.GetType( ) ); + using ( var memoryStream = new MemoryStream( ) ) + using ( var writer = new BinaryWriter( memoryStream ) ) + { + converter.Write( writer, _expectedValue ); + + byte[] writtenData = memoryStream.ToArray( ); + CollectionAssert.AreEqual( _rubyMarshalData, writtenData ); + } + } + } +} \ No newline at end of file diff --git a/RmSharp.Tests/RmSharp.Tests.csproj b/RmSharp.Tests/RmSharp.Tests.csproj index bbb52fc..d9c55ed 100644 --- a/RmSharp.Tests/RmSharp.Tests.csproj +++ b/RmSharp.Tests/RmSharp.Tests.csproj @@ -9,6 +9,12 @@ true + + + + + + @@ -24,14 +30,4 @@ - - - - - - - PreserveNewest - - - diff --git a/RmSharp/Converters/DynamicConverter.cs b/RmSharp/Converters/DynamicConverter.cs new file mode 100644 index 0000000..e2f4c0a --- /dev/null +++ b/RmSharp/Converters/DynamicConverter.cs @@ -0,0 +1,39 @@ +using RmSharp.Extensions; +using RmSharp.Tokens; +using System; +using System.IO; + +namespace RmSharp.Converters +{ + public class DynamicConverter : RmConverter + { + public override object Read( BinaryReader reader ) + { + // Peek the byte to find out a token to process + if ( reader.PeekByte( out var byteValue ) ) + { + if ( Enum.IsDefined( typeof( RubyMarshalToken ), byteValue ) ) + { + var tokenConverter = RmConverterFactory.GetConverter( ( RubyMarshalToken ) byteValue ); + + if ( tokenConverter != null ) + { + return tokenConverter.Read( reader ); + } + } + } + + return null; + } + + public override void Write( BinaryWriter writer, object instance ) + { + var converter = RmConverterFactory.GetConverter( instance.GetType() ); + + if ( converter != null ) + { + converter.Write( writer, instance ); + } + } + } +} diff --git a/RmSharp/Converters/RmConverterFactory.cs b/RmSharp/Converters/RmConverterFactory.cs index f62c14b..aab7c08 100644 --- a/RmSharp/Converters/RmConverterFactory.cs +++ b/RmSharp/Converters/RmConverterFactory.cs @@ -1,7 +1,9 @@ using RmSharp.Converters.Types; using RmSharp.Converters.Types.Basic; +using RmSharp.Tokens; using System; using System.Collections.Generic; +using System.Dynamic; using System.Numerics; using System.Text.RegularExpressions; @@ -32,11 +34,58 @@ public static SymbolConverter SymbolConverter { typeof( ulong ), new UInt64Converter( ) }, }; + + private static readonly Dictionary _tokenMap = + new Dictionary + { + { RubyMarshalToken.Bignum, _typeConverters[typeof( BigInteger )]}, + { RubyMarshalToken.True, _typeConverters[typeof( bool )]}, + { RubyMarshalToken.False, _typeConverters[typeof( bool )]}, + { RubyMarshalToken.Double, _typeConverters[typeof( double )]}, + { RubyMarshalToken.Fixnum, _typeConverters[typeof( long )]}, + { RubyMarshalToken.String, _typeConverters[typeof( string )]} + }; + private static readonly Dictionary _classConverters = []; + private static readonly DynamicConverter _dynamicConverter = new( ); + + public static RmTypeConverter GetConverter( RubyMarshalToken token ) + { + if ( _tokenMap.TryGetValue( token, out var converter ) ) + return converter; + + if ( token == RubyMarshalToken.Array ) + { + return new ListConverter( typeof( List ) ); + } + else if ( token == RubyMarshalToken.Hash ) + { + return new DictionaryConverter( typeof( Dictionary ) ); + } + else if ( token == RubyMarshalToken.Object ) + { + // Handle this, it'll need to find the appropriate type based on name... + + //if ( _classConverters.TryGetValue( type, out converter ) ) + //{ + // return converter; + //} - public static RmTypeConverter GetConverter( Type type ) + //converter = new ClassConverter( type ); + //_classConverters.Add( type, converter ); + //return converter; + } + + return null; + } + + public static RmConverter GetConverter( Type type ) { - if ( _typeConverters.TryGetValue( type, out var converter ) ) + if ( typeof( object ) == type || typeof( IDynamicMetaObjectProvider ).IsAssignableFrom( type ) ) + { + return _dynamicConverter; + } + else if ( _typeConverters.TryGetValue( type, out var converter ) ) { return converter; } @@ -63,6 +112,7 @@ public static RmTypeConverter GetConverter( Type type ) _classConverters.Add( type, converter ); return converter; } + return null; } diff --git a/RmSharp/Converters/Types/ArrayConverter.cs b/RmSharp/Converters/Types/ArrayConverter.cs index c42c827..fdffb41 100644 --- a/RmSharp/Converters/Types/ArrayConverter.cs +++ b/RmSharp/Converters/Types/ArrayConverter.cs @@ -8,7 +8,7 @@ namespace RmSharp.Converters.Types { public class ArrayConverter : RmTypeConverter { - private readonly RmTypeConverter _elementConverter; + private readonly RmConverter _elementConverter; public ArrayConverter( Type type ) : base( type ) { diff --git a/RmSharp/Converters/Types/DictionaryConverter.cs b/RmSharp/Converters/Types/DictionaryConverter.cs index 10a334e..20cae46 100644 --- a/RmSharp/Converters/Types/DictionaryConverter.cs +++ b/RmSharp/Converters/Types/DictionaryConverter.cs @@ -8,8 +8,8 @@ namespace RmSharp.Converters.Types { public class DictionaryConverter : RmTypeConverter { - private readonly RmTypeConverter _keyConverter; - private readonly RmTypeConverter _valueConverter; + private readonly RmConverter _keyConverter; + private readonly RmConverter _valueConverter; public DictionaryConverter( Type type ) : base( type ) { diff --git a/RmSharp/Converters/Types/ListConverter.cs b/RmSharp/Converters/Types/ListConverter.cs index 88bebc1..31e84a8 100644 --- a/RmSharp/Converters/Types/ListConverter.cs +++ b/RmSharp/Converters/Types/ListConverter.cs @@ -10,7 +10,7 @@ namespace RmSharp.Converters.Types { public class ListConverter : RmTypeConverter { - private readonly RmTypeConverter _elementConverter; + private readonly RmConverter _elementConverter; public ListConverter( Type type ) : base( type ) { diff --git a/RmSharp/RmSharp.csproj b/RmSharp/RmSharp.csproj index 5dc94da..bf8eee5 100644 --- a/RmSharp/RmSharp.csproj +++ b/RmSharp/RmSharp.csproj @@ -9,7 +9,7 @@ RmSharp - 0.0.1 + 0.0.2 optimus-code;HNIdesu RmSharp RmSharp is a .NET library for handling Ruby Marshal data structures, providing essential functionalities for reading, writing, and manipulating Ruby Marshal files.