/***
*
* ASMEX by RiskCare Ltd.
*
* This source is copyright (C) 2002 RiskCare Ltd. All rights reserved.
*
* Disclaimer:
* This code is provided 'as is', with absolutely no warranty expressed or
* implied. Any use of this code is at your own risk.
*
* You are hereby granted the right to redistribute this source unmodified
* in its original archive.
* You are hereby granted the right to use this code, or code based on it,
* provided that you acknowledge RiskCare Ltd somewhere in the documentation
* of your application.
* You are hereby granted the right to distribute changes to this source,
* provided that:
*
* 1 -- This copyright notice is retained unchanged
* 2 -- Your changes are clearly marked
*
* Enjoy!
*
* --------------------------------------------------------------------
*
* If you use this code or have comments on it, please mail me at
* support@jbrowse.com or ben.peterson@riskcare.com
*
*/
using System;
using System.IO;
using System.Collections;
using System.Text;
namespace Asmex.FileViewer
{
/// <summary>
/// 'Types' in .NET metadata may be simple types, coded token types, or tables.
/// Thus this enum describes all tables and codedtoken types as well as describing all types.
/// </summary>
public enum Types
{
//Tables
Module = 0,
TypeRef = 1,
TypeDef = 2,
FieldPtr = 3,
Field = 4,
MethodPtr = 5,
Method = 6,
ParamPtr = 7,
Param = 8,
InterfaceImpl = 9,
MemberRef = 10,
Constant = 11,
CustomAttribute = 12,
FieldMarshal = 13,
Permission = 14,
ClassLayout = 15,
FieldLayout = 16,
StandAloneSig = 17,
EventMap = 18,
EventPtr = 19,
Event = 20,
PropertyMap = 21,
PropertyPtr = 22,
Property = 23,
MethodSemantics = 24,
MethodImpl = 25,
ModuleRef = 26,
TypeSpec = 27,
ImplMap = 28, //lidin book is wrong again here? It has enclog at 28
FieldRVA = 29,
ENCLog = 30,
ENCMap = 31,
Assembly = 32,
AssemblyProcessor = 33,
AssemblyOS = 34,
AssemblyRef = 35,
AssemblyRefProcessor = 36,
AssemblyRefOS = 37,
File = 38,
ExportedType = 39,
ManifestResource = 40,
NestedClass = 41,
TypeTyPar = 42,
MethodTyPar = 43,
//Coded Token Types
TypeDefOrRef = 64,
HasConstant = 65,
CustomAttributeType = 66,
HasSemantic = 67,
ResolutionScope = 68,
HasFieldMarshal = 69,
HasDeclSecurity = 70,
MemberRefParent = 71,
MethodDefOrRef = 72,
MemberForwarded = 73,
Implementation = 74,
HasCustomAttribute = 75,
//Simple
UInt16 = 97,
UInt32 = 99,
String = 101,
Blob = 102,
Guid = 103,
UserString = 112
}
/// <summary>
/// The information that specifies one MD Table column
/// </summary>
public class ColDesc
{
private Types _type;
private string _name;
public ColDesc(Types type, string name)
{
_type = type;
_name = name;
}
public string Name
{
get
{
return _name;
}
}
public Types Type
{
get
{
return _type;
}
}
}
/// <summary>
/// An MD table. Includes the schema (a coldesc array) and some rows that are
/// accessed via the indexer
/// </summary>
public class Table : IEnumerable
{
private Types _type;
private ColDesc[] _colDescs;
private MDTables _helper;
private Row[] _rows;
private int _rowSize;
private Byte[] data;
public Table(Types type, Types[] colTypes, String[] colNames, MDTables helper, BinaryReader reader)
{
_type = type;
_helper = helper;
_colDescs = new ColDesc[colTypes.Length];
for (int i = 0; i < _colDescs.Length; i++)
{
_colDescs[i] = new ColDesc(colTypes[i], colNames[i]);
}
_rows = new Row[helper.GetTableRows(type)];
_rowSize = 0;
foreach (ColDesc cd in _colDescs)
{
_rowSize += _helper.SizeOfType(cd.Type);
}
data = reader.ReadBytes(_rowSize*Count);
}
public Types Type
{
get
{
return _type;
}
}
public ColDesc[] ColDescs
{
get
{
return _colDescs;
}
}
public int Count
{
get
{
return _rows.Length;
}
}
public IEnumerator GetEnumerator()
{
return new Enumerator(this);
}
// Source code change by Andrea Bertolotto - 2006/08/22 - Start
public Byte[] RawData
{
get
{
return this.data;
}
}
public int RowSize
{
get
{
return this._rowSize;
}
}
// Source code change by Andrea Bertolotto - 2006/08/22 - End
public Row this[int index]
{
get
{
if (_rows[index] == null)
{
_rows[index] = GetRow(index);
}
return _rows[index];
}
}
private Row GetRow(int idx)
{
MemoryStream stream = new MemoryStream(data);
BinaryReader reader = new BinaryReader(stream);
reader.BaseStream.Position = _rowSize*idx;
TableCell[] cells = new TableCell[_colDescs.Length];
for (int i = 0; i < _colDescs.Length; i++)
{
//uint valu = ReadSingleValue(_colDescs[i].Type, reader);
//cells[i] = new TableCell(valu, _helper);
cells[i] = new TableCell(_colDescs[i].Type, reader, _helper);
}
stream.Close();
return new Row(cells, this);
}
public override String ToString()
{
StringBuilder sb = new StringBuilder(100);
sb.Append(_type.ToString());
sb.Append(" (");
sb.Append(_rows.Length);
sb.Append(") (");
for (int i = 0; i < this._colDescs.Length; ++i)
{
sb.Append(_colDescs[i].Name);
if (i < _colDescs.Length - 1)
{
sb.Append(" -- ");
}
}
sb.Append(")");
return sb.ToString();
}
private class Enumerator : IEnumerator
{
private Table _table;
private int _curr;
public Enumerator(Table table)
{
_table = table;
Reset();
}
public void Reset()
{
_curr = -1;
}
public Boolean MoveNext()
{
_curr++;
return (_curr < _table.Count);
}
public Object Current
{
get
{
return _table[_curr];
}
}
}
}
/// <summary>
/// An MD Table row
/// </summary>
public class Row
{
private Table _table;
private TableCell[] _cells;
public Row(TableCell[] cells, Table table)
{
_cells = cells;
_table = table;
}
public Table Table
{
get
{
return _table;
}
}
public TableCell this[int index]
{
get
{
return _cells[index];
}
}
public string RawString()
{
StringBuilder sb = new StringBuilder(100);
for (int i = 0; i < _cells.Length; ++i)
{
sb.Append(_cells[i].RawData.ToString("X8"));
if (i < _cells.Length - 1)
{
sb.Append(" -- ");
}
}
return sb.ToString();
}
public string CookedString()
{
StringBuilder sb = new StringBuilder(100);
for (int i = 0; i < this._cells.Length; ++i)
{
sb.Append(_cells[i].Data);
if (i < _cells.Length - 1)
{
sb.Append(" -- ");
}
}
return sb.ToString();
}
public override String ToString()
{
return RawString();
}
}
/// <summary>
/// A cell in an MD table
/// </summary>
public class TableCell
{
private uint _rawData;
private Types _type;
private object _data;
public TableCell(Types type, BinaryReader reader, MDTables helper)
{
_type = type;
try
{
// Fixed
if (type == Types.UInt16)
{
_rawData = reader.ReadUInt16();
}
if (type == Types.UInt32)
{
_rawData = reader.ReadUInt32();
}
// Heap
if (type == Types.String)
{
_rawData = ((helper.GetStringIndexSize() == 2) ? reader.ReadUInt16() : reader.ReadUInt32());
}
if (type == Types.Guid)
{
_rawData = ((helper.GetGuidIndexSize() == 2) ? reader.ReadUInt16() : reader.ReadUInt32());
}
if (type == Types.Blob)
{
_rawData = ((helper.GetBlobIndexSize() == 2) ? reader.ReadUInt16() : reader.ReadUInt32());
}
// Rid
if ((int) type < 64)
{
Table table = helper.GetTable(type);
_rawData = ((uint) type << 24) | ((table.Count < 65536) ? reader.ReadUInt16() : reader.ReadUInt32());
}
// Coded token (may need to be uncompressed from 2-byte form)
else if ((int) type < 97)
{
int codedToken = (helper.SizeOfType(type) == 2) ? reader.ReadUInt16() : reader.ReadInt32();
Types[] referredTables = helper.CodedTokenTypes(type);
int tableIndex = (codedToken & ~(-1 << helper.CodedTokenBits(referredTables.Length)));
int index = (codedToken >> helper.CodedTokenBits(referredTables.Length));
int token = helper.ToToken(referredTables[tableIndex], index - 1);
_rawData = (uint) token;
}
_data = CreateData(helper);
}
catch (Exception)
{
_type = Types.String;
_data = "Unreadable cell: " + type + " " + _rawData;
}
}
public uint RawData
{
get
{
return _rawData;
}
}
public object Data
{
get
{
return _data;
}
}
private object CreateData(MDTables helper)
{
switch (_type)
{
case Types.UInt16:
return (ushort) _rawData;
case Types.UInt32:
return _rawData;
case Types.String:
return helper.GetString((int) _rawData);
case Types.Guid:
return helper.GetGuid((int) _rawData);
case Types.Blob:
return helper.GetBlob((int) _rawData);
//case Types.UserString:
// return _helper.GetBlob((int)_rawData);
}
if ((int) _type < 64)
{
return new RID(_type, _rawData);
}
return new CodedToken(_rawData);
}
}
/// <summary>
/// An RID. 'type' is not part of the RID's actual content but rather a note saying what
/// sort of column the RID was found in and thus what table it must refer to
/// </summary>
public class RID
{
private uint _rawData;
private Types _type;
public RID(Types type, uint raw)
{
_rawData = raw;
_type = type;
}
public uint Raw
{
get
{
return _rawData;
}
}
public override string ToString()
{
return _type.ToString() + " " + _rawData.ToString("X8");
}
}
/// <summary>
/// A coded token. As with the RID class, 'type' is not actually data held in the coded token but a note
/// telling us what kind of column the ct was found in and thus what kind of ct it must be
/// </summary>
public class CodedToken
{
private uint _rawData;
public CodedToken(uint raw)
{
_rawData = raw;
}
public uint Raw
{
get
{
return _rawData;
}
}
public override string ToString()
{
Types t = (Types) ((_rawData & 0xff000000) >> 24);
return t.ToString() + " " + (_rawData & 0x00ffffff).ToString("X8");
}
}
/// <summary>
/// The collection of all md tables in the file
/// </summary>
public class MDTables
{
private Hashtable _ctok;
private Table[] _td;
private int[] _codedTokenBits;
private MModule _mod;
public MDTables(BinaryReader reader, MModule mod)
{
_mod = mod;
SetupSchema(reader);
//reader may now be accessed randomly to fill in actual strings etc.
}
public Table[] Tables
{
get
{
return _td;
}
}
public Types[] CodedTokenTypes(Types t)
{
return (Types[]) _ctok[t];
}
public int CodedTokenBits(Types t)
{
return _codedTokenBits[(int) t];
}
public int CodedTokenBits(int i)
{
return _codedTokenBits[i];
}
public Table GetTable(int token)
{
int idx = token >> 24;
if (idx >= _td.Length)
{
throw new ModException("MDTables: No such table");
}
return _td[idx];
}
public Table GetTable(Types type)
{
int idx = (int) type;
if (idx >= _td.Length)
{
throw new ModException("MDTables: No such table");
}
return _td[idx];
}
public uint GetTableRows(Types t)
{
int idx = (int) t;
if (idx < 0 || idx > _mod.ModHeaders.MetaDataTableHeader.TableLengths.Length)
{
throw new ModException("MDHelper: Tried to get length of nonexistant table");
}
return _mod.ModHeaders.MetaDataTableHeader.TableLengths[(int) t];
}
public int ToToken(Types tableType, int index)
{
int type = (int) tableType;
index++;
if (index < 0)
{
return -1;
}
return ((type << 24) | index);
}
public string GetString(int offset)
{
if (offset < 0 || offset > _mod.StringHeap.Length)
{
throw new ModException("MDTables: string offs out of range.");
}
string s = (string) _mod.StringHeap.GetByOffset(offset);
return s;
}
public MDBlob GetBlob(int offset)
{
if (offset < 0 || offset > _mod.BlobHeap.Length)
{
throw new ModException("MDTables: blob offs out of range.");
}
return (MDBlob) _mod.BlobHeap.GetByOffset(offset);
}
//hmm. 'offsets' in the guid heap actually seem to be 1-based indexes? And an index of 0 means empty?
//ITS NOT LIKE THIS IS ACTUALLY ***DOCUMENTED*** ANYWHERE AFTER ALL.
public MDGUID GetGuid(int offset)
{
if (offset == 0)
{
return MDGUID.Empty;
}
if (offset < 1 || offset > _mod.GUIDHeap.Length/16)
{
throw new ModException("MDTables: GUID offs out of range.");
}
return (MDGUID) _mod.GUIDHeap[offset - 1];
}
public int GetStringIndexSize()
{
return ((_mod.ModHeaders.MetaDataTableHeader.HeapOffsetSizes & 0x01) != 0) ? 4 : 2;
}
public int GetGuidIndexSize()
{
return ((_mod.ModHeaders.MetaDataTableHeader.HeapOffsetSizes & 0x02) != 0) ? 4 : 2;
}
public int GetBlobIndexSize()
{
return ((_mod.ModHeaders.MetaDataTableHeader.HeapOffsetSizes & 0x04) != 0) ? 4 : 2;
}
//Note that the size of a type cannot be known until at least table sizes are loaded from file
public int SizeOfType(Types type)
{
// Fixed
if (type == Types.UInt16)
{
return 2;
}
if (type == Types.UInt32)
{
return 4;
}
// Heap
if (type == Types.String)
{
return GetStringIndexSize();
}
if (type == Types.Blob)
{
return GetBlobIndexSize();
}
if (type == Types.Guid)
{
return GetGuidIndexSize();
}
// RID
if ((int) type < 64)
{
uint rows = GetTableRows(type);
return (rows < 65536) ? 2 : 4;
}
// CodedToken
Types[] referredTypes = CodedTokenTypes(type);
if (referredTypes != null)
{
uint maxRows = 0;
foreach (Types referredType in referredTypes)
{
if (referredType != Types.UserString) //but what if there is a large user string table?
{
uint rows = GetTableRows(referredType);
if (maxRows < rows)
{
maxRows = rows;
}
}
}
maxRows = maxRows << CodedTokenBits(referredTypes.Length);
return (maxRows < 65536) ? 2 : 4;
}
throw new ModException("Table: Sizeof invalid token type.");
}
/// <summary>
/// .NET expects the consumer of the metadata to know the schema of the metadata database.
/// that schema is represented here as an array of 'table' objs which will be filled with actual
/// rows elsewhere.
/// </summary>
/// <param name="reader"></param>
private void SetupSchema(BinaryReader reader)
{
//number of bits in coded token tag for a coded token that refers to n tables.
//values 5-17 are not used :I
_codedTokenBits = new[] {0, 1, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5};
//Hash telling us what tables a given coded token type can refer to
_ctok = new Hashtable();
_ctok[Types.TypeDefOrRef] = new[] {Types.TypeDef, Types.TypeRef, Types.TypeSpec};
_ctok[Types.HasConstant] = new[] {Types.Field, Types.Param, Types.Property};
_ctok[Types.CustomAttributeType] = new[] {Types.TypeRef, Types.TypeDef, Types.Method, Types.MemberRef, Types.UserString};
_ctok[Types.HasSemantic] = new[] {Types.Event, Types.Property};
_ctok[Types.ResolutionScope] = new[] {Types.Module, Types.ModuleRef, Types.AssemblyRef, Types.TypeRef};
_ctok[Types.HasFieldMarshal] = new[] {Types.Field, Types.Param};
_ctok[Types.HasDeclSecurity] = new[] {Types.TypeDef, Types.Method, Types.Assembly};
_ctok[Types.MemberRefParent] = new[] {Types.TypeDef, Types.TypeRef, Types.ModuleRef, Types.Method, Types.TypeSpec};
_ctok[Types.MethodDefOrRef] = new[] {Types.Method, Types.MemberRef};
_ctok[Types.MemberForwarded] = new[] {Types.Field, Types.Method};
_ctok[Types.Implementation] = new[] {Types.File, Types.AssemblyRef, Types.ExportedType};
_ctok[Types.HasCustomAttribute] = new[]
{
Types.Method, Types.Field, Types.TypeRef, Types.TypeDef, Types.Param, Types.InterfaceImpl,
Types.MemberRef, Types.Module, Types.Permission, Types.Property, Types.Event, Types.StandAloneSig,
Types.ModuleRef, Types.TypeSpec, Types.Assembly, Types.AssemblyRef, Types.File, Types.ExportedType,
Types.ManifestResource
};
//md db schema is represented in the arguments to table constructors...
_td = new Table[0x2C];
_td[0x00] = new Table(Types.Module, new[] {Types.UInt16, Types.String, Types.Guid, Types.Guid, Types.Guid},
new[] {"Generation", "Name", "Mvid", "EncId", "EncBaseId"}, this, reader);
_td[0x01] = new Table(Types.TypeRef, new[] {Types.ResolutionScope, Types.String, Types.String},
new[] {"ResolutionScope", "Name", "Namespace"}, this, reader);
_td[0x02] = new Table(Types.TypeDef, new[] {Types.UInt32, Types.String, Types.String, Types.TypeDefOrRef, Types.Field, Types.Method},
new[] {"Flags", "Name", "Namespace", "Extends", "FieldList", "MethodList"}, this, reader);
_td[0x03] = new Table(Types.FieldPtr, new[] {Types.Field}, new[] {"Field"}, this, reader);
_td[0x04] = new Table(Types.Field, new[] {Types.UInt16, Types.String, Types.Blob}, new[] {"Flags", "Name", "Signature"}, this, reader);
_td[0x05] = new Table(Types.MethodPtr, new[] {Types.Method}, new[] {"Method"}, this, reader);
_td[0x06] = new Table(Types.Method, new[] {Types.UInt32, Types.UInt16, Types.UInt16, Types.String, Types.Blob, Types.Param},
new[] {"RVA", "ImplFlags", "Flags", "Name", "Signature", "ParamList"}, this, reader);
_td[0x07] = new Table(Types.ParamPtr, new[] {Types.Param}, new[] {"Param"}, this, reader);
_td[0x08] = new Table(Types.Param, new[] {Types.UInt16, Types.UInt16, Types.String}, new[] {"Flags", "Sequence", "Name"}, this, reader);
_td[0x09] = new Table(Types.InterfaceImpl, new[] {Types.TypeDef, Types.TypeDefOrRef}, new[] {"Class", "Interface"}, this, reader);
_td[0x0A] = new Table(Types.MemberRef, new[] {Types.MemberRefParent, Types.String, Types.Blob}, new[] {"Class", "Name", "Signature"}, this,
reader);
_td[0x0B] = new Table(Types.Constant, new[] {Types.UInt16, Types.HasConstant, Types.Blob}, new[] {"Type", "Parent", "Value"}, this, reader);
_td[0x0C] = new Table(Types.CustomAttribute, new[] {Types.HasCustomAttribute, Types.CustomAttributeType, Types.Blob},
new[] {"Type", "Parent", "Value"}, this, reader);
_td[0x0D] = new Table(Types.FieldMarshal, new[] {Types.HasFieldMarshal, Types.Blob}, new[] {"Parent", "Native"}, this, reader);
_td[0x0E] = new Table(Types.Permission, new[] {Types.UInt16, Types.HasDeclSecurity, Types.Blob},
new[] {"Action", "Parent", "PermissionSet"}, this, reader);
_td[0x0F] = new Table(Types.ClassLayout, new[] {Types.UInt16, Types.UInt32, Types.TypeDef}, new[] {"PackingSize", "ClassSize", "Parent"},
this, reader);
_td[0x10] = new Table(Types.FieldLayout, new[] {Types.UInt32, Types.Field}, new[] {"Offset", "Field"}, this, reader);
_td[0x11] = new Table(Types.StandAloneSig, new[] {Types.Blob}, new[] {"Signature"}, this, reader);
_td[0x12] = new Table(Types.EventMap, new[] {Types.TypeDef, Types.Event}, new[] {"Parent", "EventList"}, this, reader);
_td[0x13] = new Table(Types.EventPtr, new[] {Types.Event}, new[] {"Event"}, this, reader);
_td[0x14] = new Table(Types.Event, new[] {Types.UInt16, Types.String, Types.TypeDefOrRef}, new[] {"EventFlags", "Name", "EventType"}, this,
reader);
_td[0x15] = new Table(Types.PropertyMap, new[] {Types.TypeDef, Types.Property}, new[] {"Parent", "PropertyList"}, this, reader);
_td[0x16] = new Table(Types.PropertyPtr, new[] {Types.Property}, new[] {"Property"}, this, reader);
_td[0x17] = new Table(Types.Property, new[] {Types.UInt16, Types.String, Types.Blob}, new[] {"PropFlags", "Name", "Type"}, this, reader);
_td[0x18] = new Table(Types.MethodSemantics, new[] {Types.UInt16, Types.Method, Types.HasSemantic},
new[] {"Semantic", "Method", "Association"}, this, reader);
_td[0x19] = new Table(Types.MethodImpl, new[] {Types.TypeDef, Types.MethodDefOrRef, Types.MethodDefOrRef},
new[] {"Class", "MethodBody", "MethodDeclaration"}, this, reader);
_td[0x1A] = new Table(Types.ModuleRef, new[] {Types.String}, new[] {"Name"}, this, reader);
_td[0x1B] = new Table(Types.TypeSpec, new[] {Types.Blob}, new[] {"Signature"}, this, reader);
_td[0x1C] = new Table(Types.ImplMap, new[] {Types.UInt16, Types.MemberForwarded, Types.String, Types.ModuleRef},
new[] {"MappingFlags", "MemberForwarded", "ImportName", "ImportScope"}, this, reader);
_td[0x1D] = new Table(Types.FieldRVA, new[] {Types.UInt32, Types.Field}, new[] {"RVA", "Field"}, this, reader);
_td[0x1E] = new Table(Types.ENCLog, new[] {Types.UInt32, Types.UInt32}, new[] {"Token", "FuncCode"}, this, reader);
_td[0x1F] = new Table(Types.ENCMap, new[] {Types.UInt32}, new[] {"Token"}, this, reader);
_td[0x20] = new Table(Types.Assembly,
new[]
{
Types.UInt32, Types.UInt16, Types.UInt16, Types.UInt16, Types.UInt16, Types.UInt32, Types.Blob, Types.String,
Types.String
},
new[]
{
"HashAlgId", "MajorVersion", "MinorVersion", "BuildNumber", "RevisionNumber", "Flags", "PublicKey", "Name",
"Locale"
}, this, reader);
_td[0x21] = new Table(Types.AssemblyProcessor, new[] {Types.UInt32}, new[] {"Processor"}, this, reader);
_td[0x22] = new Table(Types.AssemblyOS, new[] {Types.UInt32, Types.UInt32, Types.UInt32},
new[] {"OSPlatformId", "OSMajorVersion", "OSMinorVersion"}, this, reader);
_td[0x23] = new Table(Types.AssemblyRef,
new[]
{
Types.UInt16, Types.UInt16, Types.UInt16, Types.UInt16, Types.UInt32, Types.Blob, Types.String, Types.String,
Types.Blob
},
new[]
{
"MajorVersion", "MinorVersion", "BuildNumber", "RevisionNumber", "Flags", "PublicKeyOrToken", "Name", "Locale",
"HashValue"
}, this, reader);
_td[0x24] = new Table(Types.AssemblyRefProcessor, new[] {Types.UInt32, Types.AssemblyRef}, new[] {"Processor", "AssemblyRef"}, this,
reader);
_td[0x25] = new Table(Types.AssemblyRefOS, new[] {Types.UInt32, Types.UInt32, Types.UInt32, Types.AssemblyRef},
new[] {"OSPlatformId", "OSMajorVersion", "OSMinorVersion", "AssemblyRef"}, this, reader);
_td[0x26] = new Table(Types.File, new[] {Types.UInt32, Types.String, Types.Blob}, new[] {"Flags", "Name", "HashValue"}, this, reader);
_td[0x27] = new Table(Types.ExportedType, new[] {Types.UInt32, Types.UInt32, Types.String, Types.String, Types.Implementation},
new[] {"Flags", "TypeDefId", "TypeName", "TypeNamespace", "TypeImplementation"}, this, reader);
_td[0x28] = new Table(Types.ManifestResource, new[] {Types.UInt32, Types.UInt32, Types.String, Types.Implementation},
new[] {"Offset", "Flags", "Name", "Implementation"}, this, reader);
_td[0x29] = new Table(Types.NestedClass, new[] {Types.TypeDef, Types.TypeDef}, new[] {"NestedClass", "EnclosingClass"}, this, reader);
//unused TyPar tables taken from Roeder's reflector... are these documented anywhere? Since they are always empty, does it matter
_td[0x2A] = new Table(Types.TypeTyPar, new[] {Types.UInt16, Types.TypeDef, Types.TypeDefOrRef, Types.String},
new[] {"Number", "Class", "Bound", "Name"}, this, reader);
_td[0x2B] = new Table(Types.MethodTyPar, new[] {Types.UInt16, Types.Method, Types.TypeDefOrRef, Types.String},
new[] {"Number", "Method", "Bound", "Name"}, this, reader);
}
}
}