public RubyToken Decode(string file, RubyDecoderOptions options) { using (var stream = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) { return(Decode(stream, options)); } }
public RubyToken Decode(Stream stream, RubyDecoderOptions options) { using (var reader = new BinaryReader(stream)) { if (reader.ReadInt16() == 0x0804) { return(ReadEntry(new ReadContext { Reader = reader }, options ?? new RubyDecoderOptions())); } } return(null); }
internal static RubyToken ReadEntry(ReadContext context, RubyDecoderOptions options) { RubyClass rubyClass; int fieldCount; RubyObject objectEntry; var type = (RubyType)context.Reader.ReadByte(); switch (type) { case RubyType.Nil: return(new RubyValue((object)null)); case RubyType.TypeLink: var dataId = (int)ReadNumber(context); if (context.Data.Count > dataId) { return(context.Data[dataId]); } else { throw new Exception("Failed to locate linked data"); } case RubyType.Array: var length = (int)ReadNumber(context); var arrayEntry = new RubyArray { Array = new List <RubyToken>(length) }; context.Data.Add(arrayEntry); for (var index = 0; index < length; index++) { arrayEntry.Array.Add(ReadEntry(context, options)); } return(arrayEntry); case RubyType.Object: rubyClass = ReadSymbolDefinition(context); fieldCount = (int)ReadNumber(context); objectEntry = new RubyObject { RubyClass = rubyClass, Properties = new Dictionary <string, RubyToken>(fieldCount) }; context.Data.Add(objectEntry); for (var fieldIndex = 0; fieldIndex < fieldCount; fieldIndex++) { var keySymbol = ReadSymbolDefinition(context); var value = ReadEntry(context, options); objectEntry.Properties.Add(keySymbol.Name.TrimStart('@'), value); } return(objectEntry); case RubyType.UserDefined: rubyClass = ReadSymbolDefinition(context); if (options.UserDecoders != null && options.UserDecoders.TryGetValue(rubyClass.Name, out var decoder)) { ReadNumber(context); var userDefinedEntry = decoder(context.Reader); userDefinedEntry.RubyClass = rubyClass; context.Data.Add(userDefinedEntry); return(userDefinedEntry); } else { throw new Exception($"User defined type not handled: {rubyClass.Name}"); } case RubyType.Hash: fieldCount = (int)ReadNumber(context); objectEntry = new RubyObject { RubyClass = new RubyClass { Name = "encoder:Hash" }, Properties = new Dictionary <string, RubyToken>(fieldCount) }; context.Data.Add(objectEntry); for (var fieldIndex = 0; fieldIndex < fieldCount; fieldIndex++) { var key = ReadEntry(context, options); var value = ReadEntry(context, options); objectEntry.Properties.Add(key.ToString(), value); } return(objectEntry); case RubyType.Number: return(new RubyValue(ReadNumber(context))); case RubyType.Decimal: var rubyFloat = new RubyValue(ReadFloat(context)); context.Data.Add(rubyFloat); return(rubyFloat); case RubyType.String: var stringEntry = ReadStringValue(context); context.Data.Add(stringEntry); return(stringEntry); case RubyType.True: return(new RubyValue(true)); case RubyType.False: return(new RubyValue(false)); default: throw new Exception($"Unknown type: {type}"); } }