/* Copyright (C) 2014-2019 de4dot@gmail.com This file is part of dnSpy dnSpy is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. dnSpy is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with dnSpy. If not, see . */ using System; namespace dnSpy.Contracts.Hex.Files.DotNet { /// /// .NET metadata token /// public class TokenData : UInt32Data { /// /// Constructor /// /// Data span public TokenData(HexBufferSpan span) : base(span) { } /// /// Constructor /// /// Buffer /// Position public TokenData(HexBuffer buffer, HexPosition position) : this(new HexBufferSpan(buffer, new HexSpan(position, 4))) { } /// /// Writes the value /// /// Formatter public override void WriteValue(HexFieldFormatter formatter) => formatter.WriteToken(ReadValue()); /// /// Returns the span the field value references or null. The span can be empty. /// /// File /// public override HexSpan? GetFieldReferenceSpan(HexBufferFile file) { var mdHeaders = file.GetHeaders(); if (mdHeaders is null) return null; var tablesStream = mdHeaders.TablesStream; if (tablesStream is null) return null; var token = new MDToken(ReadValue()); if ((uint)token.Table >= (uint)tablesStream.MDTables.Count) return null; var mdTable = tablesStream.MDTables[(int)token.Table]; if (!mdTable.IsValidRID(token.Rid)) return null; return new HexSpan(mdTable.Span.Start + (token.Rid - 1) * mdTable.RowSize, mdTable.RowSize); } } /// /// Coded .NET metadata token /// public abstract class CodedTokenData : SimpleData { readonly CodedToken codedToken; /// /// Constructor /// /// Data span /// Coded token info protected CodedTokenData(HexBufferSpan span, CodedToken codedToken) : base(span) => this.codedToken = codedToken ?? throw new ArgumentNullException(nameof(codedToken)); /// /// Reads the token value /// /// protected abstract uint ReadTokenValue(); /// /// Writes an error /// /// Formatter protected abstract void WriteValueError(HexFieldFormatter formatter); MDToken? ReadToken() { if (!codedToken.Decode(ReadTokenValue(), out MDToken token)) return null; return token; } /// /// Writes the value /// /// Formatter public override void WriteValue(HexFieldFormatter formatter) { var token = ReadToken(); if (token is not null) formatter.WriteToken(token.Value.Raw); else WriteValueError(formatter); } /// /// Returns the span the field value references or null. The span can be empty. /// /// File /// public override HexSpan? GetFieldReferenceSpan(HexBufferFile file) { var tablesStream = file.GetHeaders()?.TablesStream; if (tablesStream is null) return null; var token = ReadToken(); if (token is null) return null; if ((uint)token.Value.Table >= (uint)tablesStream.MDTables.Count) return null; var mdTable = tablesStream.MDTables[(int)token.Value.Table]; if (!mdTable.IsValidRID(token.Value.Rid)) return null; return new HexSpan(mdTable.Span.Start + (token.Value.Rid - 1) * mdTable.RowSize, mdTable.RowSize); } } /// /// 16-bit coded .NET metadata token /// public class CodedToken16Data : CodedTokenData { /// /// Constructor /// /// Data span /// Coded token info public CodedToken16Data(HexBufferSpan span, CodedToken codedToken) : base(span, codedToken) { if (span.Length != 2) throw new ArgumentOutOfRangeException(nameof(span)); } /// /// Constructor /// /// Buffer /// Position /// Coded token info public CodedToken16Data(HexBuffer buffer, HexPosition position, CodedToken codedToken) : this(new HexBufferSpan(buffer, new HexSpan(position, 2)), codedToken) { } /// /// Reads the token value /// /// protected override uint ReadTokenValue() => Span.Buffer.ReadUInt16(Span.Start); /// /// Writes an error /// /// Formatter protected override void WriteValueError(HexFieldFormatter formatter) => formatter.WriteUInt16((ushort)ReadTokenValue()); } /// /// 32-bit coded .NET metadata token /// public class CodedToken32Data : CodedTokenData { /// /// Constructor /// /// Data span /// Coded token info public CodedToken32Data(HexBufferSpan span, CodedToken codedToken) : base(span, codedToken) { if (span.Length != 4) throw new ArgumentOutOfRangeException(nameof(span)); } /// /// Constructor /// /// Buffer /// Position /// Coded token info public CodedToken32Data(HexBuffer buffer, HexPosition position, CodedToken codedToken) : this(new HexBufferSpan(buffer, new HexSpan(position, 4)), codedToken) { } /// /// Reads the token value /// /// protected override uint ReadTokenValue() => Span.Buffer.ReadUInt32(Span.Start); /// /// Writes an error /// /// Formatter protected override void WriteValueError(HexFieldFormatter formatter) => formatter.WriteUInt32(ReadTokenValue()); } /// /// .NET table rid /// public abstract class RidData : SimpleData { readonly Table table; /// /// Constructor /// /// Data span /// Table protected RidData(HexBufferSpan span, Table table) : base(span) => this.table = table; /// /// Reads the rid /// /// protected abstract uint ReadRidValue(); MDToken ReadToken() => new MDToken(table, ReadRidValue()); /// /// Writes the value /// /// Formatter public override void WriteValue(HexFieldFormatter formatter) => formatter.WriteToken(ReadToken().Raw); /// /// Returns the span the field value references or null. The span can be empty. /// /// File /// public override HexSpan? GetFieldReferenceSpan(HexBufferFile file) { var tablesStream = file.GetHeaders()?.TablesStream; if (tablesStream is null) return null; var token = ReadToken(); if ((uint)token.Table >= (uint)tablesStream.MDTables.Count) return null; var mdTable = tablesStream.MDTables[(int)token.Table]; if (!mdTable.IsValidRID(token.Rid)) return null; return new HexSpan(mdTable.Span.Start + (token.Rid - 1) * mdTable.RowSize, mdTable.RowSize); } } /// /// 16-bit .NET table rid /// public class Rid16Data : RidData { /// /// Constructor /// /// Data span /// Table public Rid16Data(HexBufferSpan span, Table table) : base(span, table) { if (span.Length != 2) throw new ArgumentOutOfRangeException(nameof(span)); } /// /// Constructor /// /// Buffer /// Position /// Table public Rid16Data(HexBuffer buffer, HexPosition position, Table table) : this(new HexBufferSpan(buffer, new HexSpan(position, 2)), table) { } /// /// Reads the token value /// /// protected override uint ReadRidValue() => Span.Buffer.ReadUInt16(Span.Start); } /// /// 32-bit .NET table rid /// public class Rid32Data : RidData { /// /// Constructor /// /// Data span /// Table public Rid32Data(HexBufferSpan span, Table table) : base(span, table) { if (span.Length != 4) throw new ArgumentOutOfRangeException(nameof(span)); } /// /// Constructor /// /// Buffer /// Position /// Table public Rid32Data(HexBuffer buffer, HexPosition position, Table table) : this(new HexBufferSpan(buffer, new HexSpan(position, 4)), table) { } /// /// Reads the token value /// /// protected override uint ReadRidValue() => Span.Buffer.ReadUInt32(Span.Start); } }