From 3d3d300f8e9b701fc183de5de105d02194c17a79 Mon Sep 17 00:00:00 2001 From: Dan <12039578+optimus-code@users.noreply.github.com> Date: Sun, 18 Aug 2024 22:23:08 +0100 Subject: [PATCH] Experimental version, can read and write a Ruby Marshal list of classes, needs more testing and more ruby token support as we go --- LICENSE | 4 +- README.md | 110 ++-- RmSharp.Tests/RmSharp.Tests.csproj | 12 +- RmSharp/Attributes/AddReferenceAttribute.cs | 11 - RmSharp/Attributes/RmNameAttribute.cs | 13 + RmSharp/Converters/ClassConverter.cs | 106 ++++ RmSharp/Converters/RmConverterFactory.cs | 24 + RmSharp/Converters/SymbolConverter.cs | 67 ++ RmSharp/Decoder.cs | 55 -- RmSharp/Extensions/BinaryReaderExtensions.cs | 68 +-- RmSharp/Extensions/BinaryWriterExtensions.cs | 45 +- RmSharp/JsonSerialzable.cs | 9 - RmSharp/Reader.cs | 107 ---- RmSharp/RmReader.cs | 20 +- RmSharp/RmSerialiser.cs | 19 +- RmSharp/RmSharp.csproj | 20 +- RmSharp/RmWriter.cs | 46 ++ RmSharp/Typedef.cs | 609 ------------------- 18 files changed, 430 insertions(+), 915 deletions(-) delete mode 100644 RmSharp/Attributes/AddReferenceAttribute.cs create mode 100644 RmSharp/Attributes/RmNameAttribute.cs create mode 100644 RmSharp/Converters/ClassConverter.cs create mode 100644 RmSharp/Converters/SymbolConverter.cs delete mode 100644 RmSharp/Decoder.cs delete mode 100644 RmSharp/JsonSerialzable.cs delete mode 100644 RmSharp/Reader.cs create mode 100644 RmSharp/RmWriter.cs delete mode 100644 RmSharp/Typedef.cs diff --git a/LICENSE b/LICENSE index fc263f5..9a06d14 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,8 @@ MIT License -Copyright (c) 2024 HNIdesu +RmSharp - Copyright (C) 2024 optimus-code + +RubyMarshal - Copyright (c) 2024 HNIdesu Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 24c5ea2..cb63621 100644 --- a/README.md +++ b/README.md @@ -1,34 +1,76 @@ -### All marshal types -| Token | Type | -| --- | --- | -| " | String | -| [ | Array | -| i | Fixnum/long | -| 0 | Nil | -| o | Object | -| : | Symbol | -| ; | Symbol Link | -| I | Instance Variable | -| T | True | -| F | False | -| f | Float | -| { | Hash | -| @ | Object References | -| } | Default Hash | -| / | Regexp | -| l | Bignum | -| S | Struct | -| c | Class | -| m | Module | -| C | User Class | -| e | Extended | -| U | User Marshal | -| d | Data | -| u | User Defined | -| e | Extended | - -### Not supported types -| Token | Type | -| --- | --- | -| U | User Marshal | -| d | Data | +# RmSharp + +**RmSharp** is a C# .NET class library designed for deserializing and serializing Ruby Marshal files. It enables seamless translation between C# models and Ruby Marshal files, allowing you to work with Ruby data structures directly in your C# applications. + +This library is built upon the work done in [HNIdesu's RubyMarshal project](https://github.com/HNIdesu/RubyMarshal). Please note that not all Ruby Marshal tokens are supported in the current version, making it unsuitable for production use at this time. + +## Features + +- **Deserialize** Ruby Marshal files directly into C# objects. +- **Serialize** C# objects into Ruby Marshal files. +- Support for Arrays, basic types, and objects. +- Attribute-based mapping to maintain Ruby naming conventions for classes and properties. + +## Installation + +You can install RmSharp via NuGet: + +```shell +dotnet add package RmSharp +``` + +## Usage + +### Deserializing Ruby Marshal Files + +You can deserialize a Ruby Marshal file into your C# model as shown below: + +```csharp +using (var stream = File.OpenRead(file)) +{ + var instance = RmSerialiser.Deserialise(stream); +} +``` + +### Serializing C# Objects to Ruby Marshal Files + +To serialize your C# object into a Ruby Marshal file: + +```csharp +using (var stream = File.OpenWrite(file)) +{ + RmSerialiser.Serialise(stream, instance); +} +``` + +### Attribute-Based Mapping + +To ensure that Ruby naming conventions are maintained in your C# classes, you need to use the `[RmName]` attribute: + +```csharp +[RmName("ModuleName::ClassName")] +public class YourClass +{ + [RmName("ruby_name")] + public string YourProperty { get; set; } +} +``` + +This will map your C# class and properties to the corresponding Ruby class and properties during serialization and deserialization. + +## Limitations + +- **Not Production-Ready**: This library is still in its early stages and is not ready for production use. +- **Limited Token Support**: Currently, only a subset of Ruby Marshal tokens is supported, including arrays, basic types, and objects. + +## Credits + +This library is based on the work done in the [RubyMarshal project by HNIdesu](https://github.com/HNIdesu/RubyMarshal). We are deeply grateful for their contributions to the Ruby Marshal serialization format. + +## Contributing + +Contributions are welcome! Feel free to open issues or submit pull requests to improve the library. + +## License + +RmSharp is licensed under the MIT License. See the [LICENSE](LICENSE) file for more details. \ No newline at end of file diff --git a/RmSharp.Tests/RmSharp.Tests.csproj b/RmSharp.Tests/RmSharp.Tests.csproj index 271527c..bbb52fc 100644 --- a/RmSharp.Tests/RmSharp.Tests.csproj +++ b/RmSharp.Tests/RmSharp.Tests.csproj @@ -3,7 +3,7 @@ net8.0 enable - enable + disable false true @@ -24,4 +24,14 @@ + + + + + + + PreserveNewest + + + diff --git a/RmSharp/Attributes/AddReferenceAttribute.cs b/RmSharp/Attributes/AddReferenceAttribute.cs deleted file mode 100644 index 7a72313..0000000 --- a/RmSharp/Attributes/AddReferenceAttribute.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System; - -namespace RmSharp.Attributes -{ - /// - /// This attribute is only used to remind the programmers to add the object to the ObjectList of the decoder. - /// - public class AddReferenceAttribute : Attribute - { - } -} diff --git a/RmSharp/Attributes/RmNameAttribute.cs b/RmSharp/Attributes/RmNameAttribute.cs new file mode 100644 index 0000000..71d6613 --- /dev/null +++ b/RmSharp/Attributes/RmNameAttribute.cs @@ -0,0 +1,13 @@ +using System; + +namespace RmSharp.Attributes +{ + public class RmNameAttribute( string name ) : Attribute + { + public string Name + { + get; + private set; + } = name; + } +} \ No newline at end of file diff --git a/RmSharp/Converters/ClassConverter.cs b/RmSharp/Converters/ClassConverter.cs new file mode 100644 index 0000000..322a490 --- /dev/null +++ b/RmSharp/Converters/ClassConverter.cs @@ -0,0 +1,106 @@ +using RmSharp.Attributes; +using RmSharp.Exceptions; +using RmSharp.Extensions; +using RmSharp.Tokens; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; + +namespace RmSharp.Converters +{ + public class ClassConverter : RmTypeConverter + { + private readonly List _readSymbols = new List(); + private readonly List _writeSymbols = new List( ); + private readonly RmNameAttribute _typeName; + private readonly List<(PropertyInfo, RmNameAttribute)> _instanceVariables = []; + private readonly SymbolConverter _symbolConverter = RmConverterFactory.SymbolConverter; + + public ClassConverter( Type type ) + : base( type ) + { + _typeName = type.GetCustomAttribute( ); + + if ( _typeName == null ) + throw new RmException( $"No RmName attribute on type '{type.FullName}'." ); + + var properties = type.GetProperties( BindingFlags.Instance | BindingFlags.Public ); + + if ( properties == null || !properties.Any() ) + throw new RmException( $"No properties accessible on type '{type.FullName}'." ); + + foreach ( var property in properties ) + { + var nameAttribute = property.GetCustomAttribute( ); + + if ( nameAttribute == null ) + continue; + + _instanceVariables.Add((property, nameAttribute)); + } + } + + public override object Read( BinaryReader reader ) + { + return reader.ReadValue( ( token ) => + { + var className = ( string ) _symbolConverter.Read( reader ); + + if ( className != _typeName.Name ) + throw new RmException( $"Class name '{className}' does not match expected type '{_typeName.Name}'." ); + + var instance = Activator.CreateInstance( Type ); + var count = reader.ReadFixNum( ); + + for ( var i = 0; i < count; i++ ) + { + var symbolName = ( string ) _symbolConverter.Read( reader ); + + var property = _instanceVariables + .Where( i => symbolName == "@" + i.Item2.Name ) + .Select( i => i.Item1 ) + .FirstOrDefault( ); + + if ( property == null ) + throw new RmException( $"Instance variable '{symbolName}' not found in type '{Type.FullName}'." ); + + var typeConverter = RmConverterFactory.GetConverter( property.PropertyType ); + + if ( typeConverter == null ) + throw new RmException( $"No type converter found for '{property.PropertyType.FullName}'." ); + + object value = typeConverter.Read( reader ); + property.SetValue( instance, value ); + } + + return instance; + }, RubyMarshalToken.Object ); + } + + public override void Write( BinaryWriter writer, object instance ) + { + writer.WriteValue( instance, RubyMarshalToken.Object, ( ) => + { + _symbolConverter.Write( writer, _typeName.Name ); + + writer.WriteFixNum( _instanceVariables.Count ); + + foreach ( var instanceVariable in _instanceVariables ) + { + var rubyName = "@" + instanceVariable.Item2.Name; + + _symbolConverter.Write( writer, rubyName ); + + var typeConverter = RmConverterFactory.GetConverter( instanceVariable.Item1.PropertyType ); + + if ( typeConverter == null ) + throw new RmException( $"No type converter found for '{instanceVariable.Item1.PropertyType.FullName}'." ); + + typeConverter.Write( writer, instanceVariable.Item1.GetValue( instance ) ); + } + } ); + } + } +} \ No newline at end of file diff --git a/RmSharp/Converters/RmConverterFactory.cs b/RmSharp/Converters/RmConverterFactory.cs index 9f3ea69..f62c14b 100644 --- a/RmSharp/Converters/RmConverterFactory.cs +++ b/RmSharp/Converters/RmConverterFactory.cs @@ -9,6 +9,12 @@ namespace RmSharp.Converters { public static class RmConverterFactory { + public static SymbolConverter SymbolConverter + { + get; + private set; + } = new( ); + private static readonly Dictionary _typeConverters = new Dictionary { @@ -26,6 +32,8 @@ public static class RmConverterFactory { typeof( ulong ), new UInt64Converter( ) }, }; + private static readonly Dictionary _classConverters = []; + public static RmTypeConverter GetConverter( Type type ) { if ( _typeConverters.TryGetValue( type, out var converter ) ) @@ -44,7 +52,23 @@ public static RmTypeConverter GetConverter( Type type ) { return new DictionaryConverter( type ); } + else if ( type.IsClass ) + { + if ( _classConverters.TryGetValue( type, out converter ) ) + { + return converter; + } + + converter = new ClassConverter( type ); + _classConverters.Add( type, converter ); + return converter; + } return null; } + + public static void Reset( ) + { + SymbolConverter.Reset( ); + } } } \ No newline at end of file diff --git a/RmSharp/Converters/SymbolConverter.cs b/RmSharp/Converters/SymbolConverter.cs new file mode 100644 index 0000000..5aaae76 --- /dev/null +++ b/RmSharp/Converters/SymbolConverter.cs @@ -0,0 +1,67 @@ +using RmSharp.Exceptions; +using RmSharp.Extensions; +using RmSharp.Tokens; +using System.Collections.Generic; +using System.IO; + +namespace RmSharp.Converters +{ + public class SymbolConverter : RmConverter + { + private readonly List _readSymbols = []; + private readonly List _writeSymbols = []; + + public override object Read( BinaryReader reader ) + { + var token = reader.ReadToken( RubyMarshalToken.Symbol, RubyMarshalToken.SymbolLink ); + var symbol = ""; + + if ( token == RubyMarshalToken.Symbol ) + { + symbol = reader.ReadRubyString( false ); + _readSymbols.Add( symbol ); + } + else + { + var symbolID = reader.ReadFixNum( ); + + if ( symbolID >= _readSymbols.Count ) + throw new RmException( $"No valid Symbol Link '{symbolID}''." ); + + symbol = _readSymbols[symbolID]; + } + + return symbol; + } + + public override void Write( BinaryWriter writer, object instance ) + { + var symbol = ( string ) instance; + + if ( HasWriteSymbol( symbol, out var id ) ) + { + writer.Write( RubyMarshalToken.SymbolLink ); + writer.WriteFixNum( id ); + } + else + { + writer.Write( RubyMarshalToken.Symbol ); + writer.WriteRubyString( symbol, false ); + _writeSymbols.Add( symbol ); + } + } + + private bool HasWriteSymbol( string symbol, out int id ) + { + id = _writeSymbols.IndexOf( symbol ); + + return id != -1; + } + + public void Reset( ) + { + _writeSymbols.Clear( ); + _readSymbols.Clear( ); + } + } +} \ No newline at end of file diff --git a/RmSharp/Decoder.cs b/RmSharp/Decoder.cs deleted file mode 100644 index 2af8a90..0000000 --- a/RmSharp/Decoder.cs +++ /dev/null @@ -1,55 +0,0 @@ -using RmSharp.Reader; -using RmSharp.Types; -using System; -using System.Collections.Generic; -using System.IO; - -namespace RmSharp -{ - public class Decoder - { - internal static Dictionary ReaderMap { get; private set; } = new( ) - { - {(byte)'[',new ArrayReader() }, - {(byte)'"',new Reader.StringReader() }, - {(byte)'l',new BignumReader() }, - {(byte)'i',new FixnumReader() }, - {(byte)'0',new NilReader() }, - {(byte)'o',new ObjectReader() }, - {(byte)':',new SymbolReader() }, - {(byte)';',new SymbolLinkReader() }, - {(byte)'I',new InstanceVariableReader() }, - {(byte)'T',new TrueReader() }, - {(byte)'F',new FalseReader() }, - {(byte)'e',new ExtendedReader()}, - {(byte)'f',new FloatReader() }, - {(byte)'d',new DataReader()}, - {(byte)'{',new HashReader() }, - {(byte)'@',new ObjectReferencesReader() }, - {(byte)'u',new UserDefinedReader() }, - {(byte)'/',new RegexpReader()}, - {(byte)'S',new StructReader() }, - {(byte)'C',new UserClassReader() }, - {(byte)'U',new UserMarshalReader() }, - {(byte)'}',new DefaultHashReader() }, - {(byte)'m',new ModuleReader() }, - {(byte)'c',new ClassReader() }, - }; - public List SymbolList { get; private set; } = []; - public List ObjectReferenceList { get; private set; } = []; - - public Decoder( ) { } - - public Base Decode( Stream stream ) - { - using ( var br = new BinaryReader( stream ) ) - { - var signature = br.ReadInt16( ); - if ( signature != 0x0804 ) - throw new Exception( "signature mismatch" ); - return ReaderMap[br.ReadByte( )].Read( this, br, true ); - } - - } - } -} diff --git a/RmSharp/Extensions/BinaryReaderExtensions.cs b/RmSharp/Extensions/BinaryReaderExtensions.cs index 4a4152e..1f5b7bc 100644 --- a/RmSharp/Extensions/BinaryReaderExtensions.cs +++ b/RmSharp/Extensions/BinaryReaderExtensions.cs @@ -1,13 +1,11 @@ using RmSharp.Exceptions; using RmSharp.Tokens; -using RmSharp.Types; using System; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; using System.Numerics; -using System.Reflection.PortableExecutable; using System.Text; using System.Text.RegularExpressions; @@ -66,14 +64,10 @@ public static object ReadValueRaw( this BinaryReader reader, Func private static object ReadBigNum( this BinaryReader br, Type type ) { - // Read the sign byte var sign = br.ReadByte( ); - - // Read the number of 16-bit "shorts" var numShorts = br.ReadFixNum( ); - // Prepare a list to collect the bytes var byteList = new List( ); - for ( int i = 0; i < numShorts; i++ ) + for ( var i = 0; i < numShorts; i++ ) { - // Read each short (2 bytes) - byteList.Add( br.ReadByte( ) ); // First byte of the short + byteList.Add( br.ReadByte( ) ); if ( br.BaseStream.Position + 1 > br.BaseStream.Length ) break; if ( i == numShorts - 1 && ( byteList.Count % 2 == 1 ) ) { - // If it's the last item and doesn't complete a short, peek to check if the next byte is a RubyMarshalToken - byte nextByte = ( byte ) br.PeekChar( ); + var nextByte = ( byte ) br.PeekChar( ); - // RubyMarshalToken values are typically small (e.g., Fixnum, String, Array, etc.), - // so if nextByte matches a known RubyMarshalToken, stop reading. - if ( Enum.IsDefined( ( RubyMarshalToken ) nextByte ) ) + if ( Enum.IsDefined( typeof( RubyMarshalToken ), nextByte ) ) { break; } } - // If it isn't the end or if the next byte doesn't match a token, read the second byte byteList.Add( br.ReadByte( ) ); } - // Convert the list to an array - byte[] magnitudeBytes = byteList.ToArray( ); - - // Create the BigInteger using the little-endian byte array - BigInteger temp = new BigInteger( magnitudeBytes ); + var buffer = byteList.ToArray( ); + var temp = new BigInteger( buffer ); - // Apply the sign if ( sign == ( byte ) '+' ) { - return type == typeof( BigInteger ) ? ( object ) temp : ( object ) ( ulong ) temp; + return type == typeof( BigInteger ) ? temp : ( ulong ) temp; } else if ( sign == ( byte ) '-' ) { @@ -335,11 +312,6 @@ private static object ReadBigNum( this BinaryReader br, Type type ) throw new NotSupportedException( "Invalid sign byte in Ruby Bignum." ); } - - - - - /// /// Read a Ruby BigNum of the specified type. /// @@ -360,24 +332,26 @@ public static double ReadDoubleCustom( this BinaryReader br ) { return br.ReadValue( ( token ) => { - // Read the length of the string (stored as a Fixnum) - int length = br.ReadFixNum( ); - - // Read the string bytes - byte[] stringBytes = br.ReadBytes( length ); + var length = br.ReadFixNum( ); + var buffer = br.ReadBytes( length ); - // Convert the string bytes to a string - var floatString = Encoding.ASCII.GetString( stringBytes ); + var text = Encoding.ASCII.GetString( buffer ); - int index = floatString.IndexOf( '\0' ); - floatString = index == -1 ? floatString : floatString[0..index]; + var index = text.IndexOf( '\0' ); + text = index == -1 ? text : text[0..index]; - return double.Parse( floatString, CultureInfo.InvariantCulture ); + return double.Parse( text, CultureInfo.InvariantCulture ); }, RubyMarshalToken.Double ); } - public static string ReadRubyString( this BinaryReader br ) + public static string ReadRubyString( this BinaryReader br, bool hasToken = true ) { + if ( !hasToken ) + { + var length = br.ReadFixNum( ); + return Encoding.UTF8.GetString( br.ReadBytes( length ) ); + } + return br.ReadValue( ( token ) => { if ( token == RubyMarshalToken.Nil ) @@ -390,7 +364,7 @@ public static string ReadRubyString( this BinaryReader br ) }, RubyMarshalToken.String ); } - public static System.Text.RegularExpressions.Regex ReadRegex( this BinaryReader br ) + public static Regex ReadRegex( this BinaryReader br ) { return br.ReadValue( ( token ) => { diff --git a/RmSharp/Extensions/BinaryWriterExtensions.cs b/RmSharp/Extensions/BinaryWriterExtensions.cs index 3d577c7..01b3e95 100644 --- a/RmSharp/Extensions/BinaryWriterExtensions.cs +++ b/RmSharp/Extensions/BinaryWriterExtensions.cs @@ -262,28 +262,21 @@ public static void WriteFixNum( this BinaryWriter bw, object value ) /// private static void WriteBigNum( this BinaryWriter bw, BigInteger value ) { - // Determine the sign and write the corresponding token bw.Write( value.Sign >= 0 ? ( byte ) '+' : ( byte ) '-' ); - // Convert to absolute value for writing - BigInteger absValue = BigInteger.Abs( value ); + var absValue = BigInteger.Abs( value ); - // Get the byte array representation of the BigInteger - byte[] magnitude = absValue.ToByteArray( ); + var buffer = absValue.ToByteArray( ); + + var lengthInBytes = buffer.Length; - // Adjust the length to account for rounding up to the nearest short - int lengthInBytes = magnitude.Length; if ( lengthInBytes % 2 != 0 ) - { - lengthInBytes++; // Ensure length is rounded up to the nearest even number - } - int lengthInShorts = lengthInBytes / 2; + lengthInBytes++; - // Write the length in shorts as a Fixnum - bw.WriteFixNum( lengthInShorts ); + var lengthInShorts = lengthInBytes / 2; - // Write the magnitude bytes - bw.Write( magnitude ); + bw.WriteFixNum( lengthInShorts ); + bw.Write( buffer ); } /// @@ -295,20 +288,24 @@ public static void WriteDouble( this BinaryWriter bw, double value ) { bw.Write( RubyMarshalToken.Double ); - // Convert the double to a string with invariant culture - var floatString = value.ToString( "R", System.Globalization.CultureInfo.InvariantCulture ); - byte[] stringBytes = System.Text.Encoding.ASCII.GetBytes( floatString ); - - // Write the length of the string (as a Fixnum) - Actual length of the string without padding - bw.WriteFixNum( stringBytes.Length ); + var text = value.ToString( "R", System.Globalization.CultureInfo.InvariantCulture ); + var buffer = Encoding.ASCII.GetBytes( text ); - // Write the string bytes - bw.Write( stringBytes ); + bw.WriteFixNum( buffer.Length ); + bw.Write( buffer ); } - public static void WriteRubyString( this BinaryWriter bw, string value ) + public static void WriteRubyString( this BinaryWriter bw, string value, bool writeToken = true ) { + if ( !writeToken ) + { + byte[] bytes = Encoding.UTF8.GetBytes( value ); + bw.WriteFixNum( bytes.Length ); + bw.Write( bytes ); + return; + } + bw.WriteValue( value, RubyMarshalToken.String, ( ) => { byte[] bytes = Encoding.UTF8.GetBytes( value ); diff --git a/RmSharp/JsonSerialzable.cs b/RmSharp/JsonSerialzable.cs deleted file mode 100644 index bdc2e48..0000000 --- a/RmSharp/JsonSerialzable.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System.Text.Json.Nodes; - -namespace RmSharp -{ - public interface IJsonSerialzable - { - public abstract JsonNode? ToJson( ); - } -} diff --git a/RmSharp/Reader.cs b/RmSharp/Reader.cs deleted file mode 100644 index df166ed..0000000 --- a/RmSharp/Reader.cs +++ /dev/null @@ -1,107 +0,0 @@ -using RmSharp.Types; -using System.IO; - -namespace RmSharp.Reader -{ - public abstract class BaseReader - { - public abstract Base Read( Decoder ctx, BinaryReader br, bool addToObjectReferenceList ); - } - public class ArrayReader : BaseReader - { - public override Base Read( Decoder ctx, BinaryReader br, bool addToObjectReferenceList ) => new Types.Array( ctx, br, addToObjectReferenceList ); - } - public class HashReader : BaseReader - { - public override Base Read( Decoder ctx, BinaryReader br, bool addToObjectReferenceList ) => new Hash( ctx, br, addToObjectReferenceList ); - } - public class ObjectReferencesReader : BaseReader - { - public override Base Read( Decoder ctx, BinaryReader br, bool addToObjectReferenceList ) => new ObjectReferences( ctx!, br ).Target; - } - public class NilReader : BaseReader - { - public override Base Read( Decoder ctx, BinaryReader br, bool addToObjectReferenceList ) => new Nil( ); - } - public class ObjectReader : BaseReader - { - public override Base Read( Decoder ctx, BinaryReader br, bool addToObjectReferenceList ) => new Types.Object( ctx, br, addToObjectReferenceList ); - } - public class SymbolReader : BaseReader - { - public override Base Read( Decoder ctx, BinaryReader br, bool addToObjectReferenceList ) => new Symbol( ctx, br ); - } - public class SymbolLinkReader : BaseReader - { - public override Base Read( Decoder ctx, BinaryReader br, bool addToObjectReferenceList ) => new SymbolLink( ctx!, br ).Target; - } - public class FixnumReader : BaseReader - { - public override Base Read( Decoder ctx, BinaryReader br, bool addToObjectReferenceList ) => new Fixnum( br ); - } - public class StringReader : BaseReader - { - public override Base Read( Decoder ctx, BinaryReader br, bool addToObjectReferenceList ) => new Types.String( ctx, br, addToObjectReferenceList ); - } - public class BignumReader : BaseReader - { - public override Base Read( Decoder ctx, BinaryReader br, bool addToObjectReferenceList ) => new Bignum( ctx, br, addToObjectReferenceList ); - } - public class InstanceVariableReader : BaseReader - { - public override Base Read( Decoder ctx, BinaryReader br, bool addToObjectReferenceList ) => new InstanceVariable( ctx, br, addToObjectReferenceList ); - } - public class TrueReader : BaseReader - { - public override Base Read( Decoder ctx, BinaryReader br, bool addToObjectReferenceList ) => new True( ); - } - public class DataReader : BaseReader - { - public override Base Read( Decoder ctx, BinaryReader br, bool addToObjectReferenceList ) => new Data( ctx, br, addToObjectReferenceList ); - } - public class FalseReader : BaseReader - { - public override Base Read( Decoder ctx, BinaryReader br, bool addToObjectReferenceList ) => new False( ); - } - public class FloatReader : BaseReader - { - public override Base Read( Decoder ctx, BinaryReader br, bool addToObjectReferenceList ) => new Float( ctx, br, addToObjectReferenceList ); - } - public class ExtendedReader : BaseReader - { - public override Base Read( Decoder ctx, BinaryReader br, bool addToObjectReferenceList ) => new Extended( ctx, br, addToObjectReferenceList ); - } - public class UserDefinedReader : BaseReader - { - public override Base Read( Decoder ctx, BinaryReader br, bool addToObjectReferenceList ) => new UserDefined( ctx, br, addToObjectReferenceList ); - } - public class RegexpReader : BaseReader - { - public override Base Read( Decoder ctx, BinaryReader br, bool addToObjectReferenceList ) => new Regexp( ctx, br, addToObjectReferenceList ); - } - public class StructReader : BaseReader - { - public override Base Read( Decoder ctx, BinaryReader br, bool addToObjectReferenceList ) => new Struct( ctx, br, addToObjectReferenceList ); - } - public class UserClassReader : BaseReader - { - public override Base Read( Decoder ctx, BinaryReader br, bool addToObjectReferenceList ) => new UserClass( ctx, br, addToObjectReferenceList ); - } - public class UserMarshalReader : BaseReader - { - public override Base Read( Decoder ctx, BinaryReader br, bool addToObjectReferenceList ) => new UserMarshal( ctx, br, addToObjectReferenceList ); - } - public class DefaultHashReader : BaseReader - { - public override Base Read( Decoder ctx, BinaryReader br, bool addToObjectReferenceList ) => new DefaultHash( ctx, br, addToObjectReferenceList ); - } - public class ModuleReader : BaseReader - { - public override Base Read( Decoder ctx, BinaryReader br, bool addToObjectReferenceList ) => new Module( ctx, br, addToObjectReferenceList ); - } - public class ClassReader : BaseReader - { - public override Base Read( Decoder ctx, BinaryReader br, bool addToObjectReferenceList ) => new Class( ctx, br, addToObjectReferenceList ); - } - -} diff --git a/RmSharp/RmReader.cs b/RmSharp/RmReader.cs index 539715f..82be42f 100644 --- a/RmSharp/RmReader.cs +++ b/RmSharp/RmReader.cs @@ -1,10 +1,14 @@ using System.IO; using System; +using RmSharp.Converters; +using RmSharp.Exceptions; namespace RmSharp { public class RmReader : IDisposable { + const short HEADER_SIGNATURE = 0x0804; + private readonly BinaryReader _reader; public RmReader( Stream stream ) @@ -17,12 +21,22 @@ public T Deserialise( ) return ( T ) Deserialise( typeof( T ) ); } - private object Deserialise( Type type ) + public object Deserialise( Type type ) { - throw new NotImplementedException(); + var header = ReadHeader( ); + + if ( header != HEADER_SIGNATURE ) + throw new RmException( $"Invalid header signature, expected {HEADER_SIGNATURE:X4} but got {header:X4}" ); + + var converter = RmConverterFactory.GetConverter( type ); + + if ( converter == null ) + throw new RmException( $"No converter for type '{type.FullName}'." ); + + return converter.Read( _reader ); } - public short ReadHeader( ) + private short ReadHeader( ) { return _reader.ReadInt16( ); } diff --git a/RmSharp/RmSerialiser.cs b/RmSharp/RmSerialiser.cs index ab0dc24..0831836 100644 --- a/RmSharp/RmSerialiser.cs +++ b/RmSharp/RmSerialiser.cs @@ -1,4 +1,5 @@ -using RmSharp.Exceptions; +using RmSharp.Converters; +using RmSharp.Exceptions; using System; using System.IO; @@ -6,24 +7,24 @@ namespace RmSharp { public static class RmSerialiser { - const short HEADER_SIGNATURE = 0x0804; - public static T Deserialise( Stream stream ) { + RmConverterFactory.Reset( ); + using ( var reader = new RmReader( stream ) ) { - var header = reader.ReadHeader( ); - - if ( header != HEADER_SIGNATURE ) - throw new RmException( $"Invalid header signature, expected {HEADER_SIGNATURE:X4} but got {header:X4}" ); - return reader.Deserialise( ); } } public static void Serialise( Stream stream, T value ) { - throw new NotImplementedException( ); + RmConverterFactory.Reset( ); + + using ( var writer = new RmWriter( stream ) ) + { + writer.Serialise( value ); + } } } } \ No newline at end of file diff --git a/RmSharp/RmSharp.csproj b/RmSharp/RmSharp.csproj index 97621ed..5dc94da 100644 --- a/RmSharp/RmSharp.csproj +++ b/RmSharp/RmSharp.csproj @@ -1,14 +1,24 @@  - net8.0 + net8.0;net6.0;netstandard2.1; disable disable latest - - - - + + + RmSharp + 0.0.1 + 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. + rmsharp;library;ruby;marshal + https://github.com/optimus-code/RmSharp + git + MIT + https://github.com/optimus-code/RmSharp + https://raw.githubusercontent.com/optimus-code/RmSharp/main/icon.png + diff --git a/RmSharp/RmWriter.cs b/RmSharp/RmWriter.cs new file mode 100644 index 0000000..571b490 --- /dev/null +++ b/RmSharp/RmWriter.cs @@ -0,0 +1,46 @@ +using System; +using System.IO; +using RmSharp.Converters; +using RmSharp.Exceptions; + +namespace RmSharp +{ + public class RmWriter : IDisposable + { + const short HEADER_SIGNATURE = 0x0804; + + private readonly BinaryWriter _writer; + + public RmWriter( Stream stream ) + { + _writer = new BinaryWriter( stream ); + } + + public void Serialise( T obj ) + { + Serialise( obj, typeof( T ) ); + } + + public void Serialise( object obj, Type type ) + { + WriteHeader( ); + + var converter = RmConverterFactory.GetConverter( type ); + + if ( converter == null ) + throw new RmException( $"No converter for type '{type.FullName}'." ); + + converter.Write( _writer, obj ); + } + + private void WriteHeader( ) + { + _writer.Write( HEADER_SIGNATURE ); + } + + public void Dispose( ) + { + _writer?.Dispose( ); + } + } +} \ No newline at end of file diff --git a/RmSharp/Typedef.cs b/RmSharp/Typedef.cs deleted file mode 100644 index 6fe8a18..0000000 --- a/RmSharp/Typedef.cs +++ /dev/null @@ -1,609 +0,0 @@ -using RmSharp.Attributes; -using System; -using System.Collections; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Numerics; -using System.Text; -using System.Text.Json.Nodes; -using System.Text.RegularExpressions; - -namespace RmSharp.Types -{ - public abstract class HashKey : Base - { - public abstract override int GetHashCode( ); - public abstract object GetUniqueValue( ); - public sealed override bool Equals( object? obj ) - { - var other = obj as HashKey; - if ( other == null ) - return false; - object uv1 = GetUniqueValue( ), uv2 = other.GetUniqueValue( ); - if ( uv1.GetType( ) != uv2.GetType( ) ) - return false; - return uv1.Equals( uv2 ); - } - } - public abstract class Base : IJsonSerialzable - { - public String? AsString( ) => this as String; - public Fixnum? AsFixnum( ) => this as Fixnum; - public Float? AsFloat( ) => this as Float; - public Symbol? AsSymbol( ) => this as Symbol; - public Array? AsArray( ) => this as Array; - public Object? AsObject( ) => this as Object; - public Data? AsData( ) => this as Data; - public SymbolLink? AsSymbolLink( ) => this as SymbolLink; - public ObjectReferences? AsObjectReferences( ) => this as ObjectReferences; - public Nil? AsNil( ) => this as Nil; - public Bignum? AsBignum( ) => this as Bignum; - public True? AsTrue( ) => this as True; - public False? AsFalse( ) => this as False; - public Hash? AsHash( ) => this as Hash; - public UserDefined? AsUserDefined( ) => this as UserDefined; - public Extended? AsExtended( ) => this as Extended; - public InstanceVariable? AsInstanceVariable( ) => this as InstanceVariable; - public Regexp? AsRegex( ) => this as Regexp; - public UserClass? AsUserClass( ) => this as UserClass; - public UserMarshal? AsUserMarshal( ) => this as UserMarshal; - public DefaultHash? AsDefaultHash( ) => this as DefaultHash; - public Module? AsModule( ) => this as Module; - public Class? AsClass( ) => this as Class; - public Struct? AsStruct( ) => this as Struct; - - public abstract JsonNode? ToJson( ); - } - [AddReference] - public class Object : Base, IEnumerable> - { - public bool ContainsKey( HashKey s ) => _Elements.ContainsKey( s ); - public bool ContainsKey( string s ) => _Elements.ContainsKey( new String( s ) ); - public Base Name { get; private set; } - private Dictionary _Elements { get; set; } = new( ); - public Base this[HashKey s] - { - get => _Elements[s]; - } - - public Base this[string s] - { - get => _Elements[new String( s )]; - } - - public int Count => _Elements.Count; - internal Object( Decoder ctx, BinaryReader br, bool addToObjectReferenceList ) - { - if ( addToObjectReferenceList ) - ctx.ObjectReferenceList.Add( this ); - Name = Decoder.ReaderMap[br.ReadByte( )].Read( ctx, br, true )!; - var count = new Fixnum( br ).ToInt32( ); - for ( int i = 0; i < count; i++ ) - { - var key = Decoder.ReaderMap[br.ReadByte( )].Read( ctx, br, true ) as HashKey; - var value = Decoder.ReaderMap[br.ReadByte( )].Read( ctx, br, true ); - if ( key != null ) - _Elements.Add( key, value ); - else - throw new NotSupportedException( ); - } - } - public override JsonNode ToJson( ) - { - var obj = new JsonObject - { - ["class"] = Name.ToJson( ) - }; - foreach ( var pair in _Elements ) - obj.Add( pair.Key.GetUniqueValue( ).ToString( )!, pair.Value!.ToJson( ) ); - return obj; - } - - public IEnumerator> GetEnumerator( ) => _Elements.GetEnumerator( ); - - IEnumerator IEnumerable.GetEnumerator( ) => _Elements.GetEnumerator( ); - } - public class ObjectReferences : Base - { - public Base Target { get; private set; } - internal ObjectReferences( Decoder ctx, BinaryReader br ) - { - var index = new Fixnum( br ).ToInt32( ); - Target = ctx.ObjectReferenceList[index]; - } - - public override JsonNode? ToJson( ) => Target.ToJson( ); - } - [AddReference] - public class Array : Base, IEnumerable - { - private List _Elements = new( 1 ); - public int Count => _Elements.Count; - internal Array( Decoder ctx, BinaryReader br, bool addToObjectReferenceList ) - { - if ( addToObjectReferenceList ) - ctx.ObjectReferenceList.Add( this ); - var count = new Fixnum( br ).ToInt32( ); - for ( int i = 0; i < count; i++ ) - _Elements.Add( Decoder.ReaderMap[br.ReadByte( )].Read( ctx, br, true ) ); - } - - public IEnumerator GetEnumerator( ) => _Elements.GetEnumerator( ); - IEnumerator IEnumerable.GetEnumerator( ) => _Elements.GetEnumerator( ); - - public override JsonNode ToJson( ) => new JsonArray( _Elements.Select( ele => ele.ToJson( ) ).ToArray( ) ); - - public Base this[int index] - { - get => _Elements[index]; - } - - } - public class SymbolLink : HashKey - { - public Symbol Target { get; private set; } - internal SymbolLink( Decoder ctx, BinaryReader br ) - { - var index = new Fixnum( br ).ToInt32( ); - Target = ctx.SymbolList[index]; - } - public override JsonNode? ToJson( ) => Target.ToJson( ); - - public override int GetHashCode( ) => Target.GetHashCode( ); - - public override object GetUniqueValue( ) => Target.GetUniqueValue( ); - - } - public class Symbol : HashKey - { - public string Name { get; private set; } - internal Symbol( Decoder ctx, BinaryReader br ) - { - ctx?.SymbolList.Add( this ); - var strlen = new Fixnum( br ).ToInt32( ); - Name = Encoding.UTF8.GetString( br.ReadBytes( strlen ) ); - } - public override int GetHashCode( ) => Name.GetHashCode( ); - public override JsonNode? ToJson( ) => Name; - public override object GetUniqueValue( ) => Name; - } - public class Nil : Base - { - internal Nil( ) { } - public override JsonNode? ToJson( ) => null; - } - [AddReference] - public class Bignum : Base - { - public BigInteger Value { get; private set; } - internal Bignum( Decoder ctx, BinaryReader br, bool addToObjectReferenceList ) - { - if ( addToObjectReferenceList ) - ctx.ObjectReferenceList.Add( this ); - var sign = br.ReadByte( ); - var length = new Fixnum( br ).ToInt32( ) * 2; - var temp = new BigInteger( br.ReadBytes( length ) ); - if ( sign == ( byte ) '+' ) - Value = temp; - else if ( sign == ( byte ) '-' ) - Value = -temp; - else - throw new NotSupportedException( ); - } - public override JsonNode? ToJson( ) => Value.ToString( ); - } - public class True : Base - { - public const bool Value = true; - internal True( ) { } - public override JsonNode? ToJson( ) => Value; - } - public class False : Base - { - public const bool Value = false; - internal False( ) { } - public override JsonNode? ToJson( ) => Value; - } - [AddReference] - public class InstanceVariable : HashKey, IEnumerable> - { - public bool ContainsKey( HashKey s ) => _Properties.ContainsKey( s ); - public bool ContainsKey( string s ) => _Properties.ContainsKey( new String( s ) ); - public Base Base { get; private set; } - private Dictionary _Properties { get; set; } = new( ); - public Base this[HashKey s] - { - get => _Properties[s]; - } - public Base this[string s] - { - get => _Properties[new String( s )]; - } - public int Count => _Properties.Count; - internal InstanceVariable( Decoder ctx, BinaryReader br, bool addToObjectReferenceList ) - { - if ( addToObjectReferenceList ) - ctx.ObjectReferenceList.Add( this ); - Base = Decoder.ReaderMap[br.ReadByte( )].Read( ctx, br, false )!; - var count = new Fixnum( br ).ToInt32( ); - for ( int i = 0; i < count; i++ ) - { - var key = Decoder.ReaderMap[br.ReadByte( )].Read( ctx, br, true ) as HashKey; - var value = Decoder.ReaderMap[br.ReadByte( )].Read( ctx, br, true ); - if ( key != null ) - _Properties.Add( key, value ); - else - throw new NotSupportedException( ); - - } - } - public override JsonNode? ToJson( ) - { - var obj = new JsonObject( ) - { - ["base"] = Base.ToJson( ) - }; - foreach ( var item in _Properties ) - obj[item.Key.GetUniqueValue( ).ToString( )!] = item.Value.ToJson( ); - return obj; - } - - public IEnumerator GetEnumerator( ) => _Properties.GetEnumerator( ); - - IEnumerator> IEnumerable>.GetEnumerator( ) => _Properties.GetEnumerator( ); - - public override int GetHashCode( ) => ( ( HashKey ) Base ).GetHashCode( ); - - public override object GetUniqueValue( ) => ( ( HashKey ) Base ).GetUniqueValue( ); - } - [AddReference] - public class Extended : Base - { - public Base Base { get; private set; } - public Base Value { get; private set; } - internal Extended( Decoder ctx, BinaryReader br, bool addToObjectReferenceList ) - { - if ( addToObjectReferenceList ) - ctx.ObjectReferenceList.Add( this ); - Base = Decoder.ReaderMap[br.ReadByte( )].Read( ctx, br, true ); - Value = Decoder.ReaderMap[br.ReadByte( )].Read( ctx, br, true ); - } - - public override JsonNode? ToJson( ) - { - return new JsonObject - { - ["base"] = Base.ToJson( ), - ["value"] = Value.ToJson( ) - }; - } - } - public class Fixnum : HashKey - { - public long Value { get; private set; } - internal Fixnum( BinaryReader br ) - { - byte b = br.ReadByte( ); - switch ( b ) - { - case 0: - Value = 0; - break; - case 1: - Value = br.ReadByte( ); - break; - case 0xff: - Value = -br.ReadByte( ); - break; - case 2: - Value = br.ReadUInt16( ); - break; - case 0xfe: - Value = -br.ReadUInt16( ); - break; - case 3: - { - byte[] buffer = new byte[4]; - br.Read( buffer, 0, 3 ); - Value = BitConverter.ToUInt32( buffer ); - break; - } - case 0xfd: - { - byte[] buffer = new byte[4]; - br.Read( buffer, 0, 3 ); - Value = -BitConverter.ToUInt32( buffer ); - break; - } - case 4: - Value = br.ReadUInt32( ); - break; - case 0xfc: - Value = -br.ReadUInt32( ); - break; - default: - { - var temp = ( sbyte ) b; - if ( temp > 0 ) - Value = temp - 5; - else - Value = temp + 5; - break; - } - } - } - public int ToInt32( ) - { - if ( Value > int.MaxValue || Value < int.MinValue ) - throw new InvalidCastException( ); - return ( int ) Value; - } - public uint ToUInt32( ) - { - if ( Value < 0 || Value > uint.MaxValue ) - throw new InvalidCastException( ); - return ( uint ) Value; - } - public ushort ToUInt16( ) - { - if ( Value < 0 || Value > ushort.MaxValue ) - throw new InvalidCastException( ); - return ( ushort ) Value; - } - public short ToInt16( ) - { - if ( Value > short.MaxValue || Value < short.MinValue ) - throw new InvalidCastException( ); - return ( short ) Value; - } - - public Fixnum( long val ) - { - Value = val; - } - public override JsonNode? ToJson( ) => Value; - public override int GetHashCode( ) => Value.GetHashCode( ); - public override object GetUniqueValue( ) => Value; - } - [AddReference] - public class String : HashKey - { - public string Value { get; private set; } - internal String( string str ) - { - Value = str; - } - internal String( Decoder ctx, BinaryReader br, bool addToObjectReferenceList ) - { - if ( addToObjectReferenceList ) - ctx.ObjectReferenceList.Add( this ); - var strlen = new Fixnum( br ).ToInt32( ); - Value = Encoding.UTF8.GetString( br.ReadBytes( strlen ) ); - } - - public override int GetHashCode( ) => Value.GetHashCode( ); - - public override JsonNode? ToJson( ) => Value; - - public override object GetUniqueValue( ) => Value; - } - [AddReference] - public class Float : Base - { - private string _Data; - public double Value { get; private set; } - internal Float( Decoder ctx, BinaryReader br, bool addToObjectReferenceList ) - { - if ( addToObjectReferenceList ) - ctx.ObjectReferenceList.Add( this ); - var length = new Fixnum( br ).ToInt32( ); - var str = Encoding.ASCII.GetString( br.ReadBytes( length ) ); - int index = str.IndexOf( '\0' ); - _Data = index == -1 ? str : str[0..index]; - Value = double.Parse( _Data ); - } - - public override JsonNode ToJson( ) => Value; - } - [AddReference] - public class Data : Base - { - internal Data( Decoder ctx, BinaryReader br, bool addToObjectReferenceList ) - { - throw new NotImplementedException( ); - } - public override JsonNode ToJson( ) - { - throw new NotImplementedException( ); - } - } - [AddReference] - public class Hash : Base, IEnumerable> - { - private Dictionary _Elements { get; set; } = new( ); - public bool ContainsKey( HashKey s ) => _Elements.ContainsKey( s ); - public bool ContainsKey( string s ) => _Elements.ContainsKey( new String( s ) ); - public bool ContainsKey( int s ) => _Elements.ContainsKey( new Fixnum( s ) ); - public virtual Base this[HashKey s] - { - get => _Elements[s]; - } - - public Base this[int index] - { - get => this[new Fixnum( index )]; - } - - public Base this[string s] - { - get => this[new String( s )]; - } - internal Hash( Decoder ctx, BinaryReader br, bool addToObjectReferenceList ) - { - if ( addToObjectReferenceList ) - ctx.ObjectReferenceList.Add( this ); - var count = new Fixnum( br ).ToInt32( ); - for ( int i = 0; i < count; i++ ) - { - var key = Decoder.ReaderMap[br.ReadByte( )].Read( ctx, br, true ) as HashKey; - var value = Decoder.ReaderMap[br.ReadByte( )].Read( ctx, br, true ); - if ( key != null ) - _Elements.Add( key, value ); - else - throw new NotSupportedException( ); - } - } - public override JsonNode? ToJson( ) - { - var obj = new JsonObject( ); - foreach ( var pair in _Elements ) - obj.Add( pair.Key.GetUniqueValue( ).ToString( )!, pair.Value?.ToJson( ) ); - return obj; - } - - public IEnumerator> GetEnumerator( ) => _Elements.GetEnumerator( ); - - IEnumerator IEnumerable.GetEnumerator( ) => _Elements.GetEnumerator( ); - } - [AddReference] - public class Regexp : Base - { - public Regex Value { get; private set; } - internal Regexp( Decoder ctx, BinaryReader br, bool addToObjectReferenceList ) - { - if ( addToObjectReferenceList ) - ctx.ObjectReferenceList.Add( this ); - var strlen = new Fixnum( br ).ToInt32( ); - var pattern = Encoding.UTF8.GetString( br.ReadBytes( strlen ) ); - var options = RegexOptions.None; - int b = br.ReadByte( ); - if ( ( b & 1 ) != 0 ) - options |= RegexOptions.IgnoreCase; - if ( ( b & 2 ) != 0 ) - options |= RegexOptions.IgnorePatternWhitespace; - if ( ( b & 4 ) != 0 ) - options |= RegexOptions.Multiline; - Value = new Regex( pattern, options ); - } - public override JsonNode? ToJson( ) - { - StringBuilder sb = new StringBuilder( $"/{Value}/" ); - var options = Value.Options; - if ( options.HasFlag( RegexOptions.Multiline ) ) - sb.Append( 'm' ); - if ( options.HasFlag( RegexOptions.IgnoreCase ) ) - sb.Append( 'i' ); - if ( options.HasFlag( RegexOptions.IgnorePatternWhitespace ) ) - sb.Append( 'x' ); - return sb.ToString( ); - } - } - [AddReference] - public class Struct : Object - { - internal Struct( Decoder ctx, BinaryReader br, bool addToObjectReferenceList ) : base( ctx, br, addToObjectReferenceList ) { } - } - [AddReference] - public class UserClass : Base - { - public Base Name { get; private set; } - public Base Base { get; private set; } - internal UserClass( Decoder ctx, BinaryReader br, bool addToObjectReferenceList ) - { - if ( addToObjectReferenceList ) - ctx.ObjectReferenceList.Add( this ); - Name = Decoder.ReaderMap[br.ReadByte( )].Read( ctx, br, true ); - Base = Decoder.ReaderMap[br.ReadByte( )].Read( ctx, br, true ); - } - - public override JsonNode? ToJson( ) - { - return new JsonObject( ) - { - ["class"] = Name.ToJson( ), - ["base"] = Base.ToJson( ) - }; - } - } - [AddReference] - public class UserMarshal : Base - { - internal UserMarshal( Decoder ctx, BinaryReader br, bool addToObjectReferenceList ) - { - throw new NotImplementedException( ); - } - public override JsonNode? ToJson( ) - { - throw new NotImplementedException( ); - } - } - [AddReference] - public class DefaultHash : Hash - { - public Base DefaultValue { get; private set; } - internal DefaultHash( Decoder ctx, BinaryReader br, bool addToObjectReferenceList ) : base( ctx, br, addToObjectReferenceList ) - { - DefaultValue = Decoder.ReaderMap[br.ReadByte( )].Read( ctx, br, true ); - } - public sealed override Base this[HashKey s] - { - get - { - if ( !ContainsKey( s ) ) - return DefaultValue; - else - return base[s]; - } - } - } - [AddReference] - public class Module : Class - { - internal Module( Decoder ctx, BinaryReader br, bool addToObjectReferenceList ) : base( ctx, br, addToObjectReferenceList ) { } - } - [AddReference] - public class Class : Base - { - public string Name { get; private set; } - internal Class( Decoder ctx, BinaryReader br, bool addToObjectReferenceList ) - { - if ( addToObjectReferenceList ) - ctx.ObjectReferenceList.Add( this ); - var strlen = new Fixnum( br ).ToInt32( ); - Name = Encoding.UTF8.GetString( br.ReadBytes( strlen ) ); - } - public override JsonNode? ToJson( ) - { - return new JsonObject( ) - { - ["class"] = Name - }; - } - } - [AddReference] - public class UserDefined : Base - { - public Base Name { get; private set; } - public byte[] RawData { get; private set; } - - internal UserDefined( Decoder ctx, BinaryReader br, bool addToObjectReferenceList ) - { - if ( addToObjectReferenceList ) - ctx.ObjectReferenceList.Add( this ); - Name = Decoder.ReaderMap[br.ReadByte( )].Read( ctx, br, true ); - RawData = br.ReadBytes( new Fixnum( br ).ToInt32( ) ); - } - - public override JsonNode? ToJson( ) - { - var obj = new JsonObject( ); - obj["class"] = Name.ToString( ); - var arr = new JsonArray( ); - foreach ( var b in RawData ) - arr.Add( b ); - obj["data"] = arr; - return obj; - } - } -}