//
// Copyright (c) 2002
// Ronald Kevin Burton
//
// Z poniższym kodem nie jest związana żadna gwarancja poprawności działania.
// Program został dołączony do książki ".NET CLR. Księga eksperta" w celu
// ilustracji koncepcji i zasad przedstawionych w tej książce. Program może być 
// używany na własne ryzyko.
//
// Przyznaje się prawo do użycia lub kopiowania tego oprogramowania do dowolnego celu
// bez konieczności ponoszenia żadnych opłat pod warunkiem, że powyższe uwagi zostaną 
// zachowane we wszystkich kopiach. Przyznaje się także prawo do modyfikacji kodu
// i dystrybucji zmodyfikowanego kodu pod warunkiem zachowania powyższych uwag
// oraz dołączenia informacji mówiącej o modyfikacji kodu.
//
// 
// AssemblyView.cpp : plik implementacji
//

#include "stdafx.h"
#include "MDI.h"
#include "AssemblyDoc.h"
#include "AssemblyView.h"

#include <sstream>
#include <iostream>

// CAssemblyView
IMPLEMENT_DYNCREATE(CAssemblyView, CFormView)

CAssemblyView::CAssemblyView()
	: CFormView(CAssemblyView::IDD)
{
	compositeTable = NULL;
	stringsTable = NULL;
	userStringsTable = NULL;
	blobTable = NULL;
	guidTable = NULL;
	tableCount = 0;
	stringsTableSize = 0;
	userStringsTableSize = 0;
	blobTableSize = 0;
	guidTableSize = 0;
}

CAssemblyView::~CAssemblyView()
{
}

void CAssemblyView::DoDataExchange(CDataExchange* pDX)
{
	CFormView::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CAssemblyView, CFormView)
END_MESSAGE_MAP()


// Diagnostyka CAssemblyView

#ifdef _DEBUG
void CAssemblyView::AssertValid() const
{
	CFormView::AssertValid();
}

void CAssemblyView::Dump(CDumpContext& dc) const
{
	CFormView::Dump(dc);
}

CAssemblyDoc* CAssemblyView::GetDocument() // wersja non-debug jest wbudowana
{
	ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CAssemblyDoc)));
	return (CAssemblyDoc*)m_pDocument;
}
#endif //_DEBUG


// Procedury obsługi wiadomości CAssemblyView
void CAssemblyView::OnInitialUpdate()
{
	CFormView::OnInitialUpdate();

	m_dlgPropSheet.AddPage(&m_filePropertiesPage);
	m_dlgPropSheet.AddPage(&m_optionalHeaderPropertiesPage);
	m_dlgPropSheet.AddPage(&m_dataDirectoryPage);
	m_dlgPropSheet.AddPage(&m_sectionPage);
	m_dlgPropSheet.AddPage(&m_importPage);
	m_dlgPropSheet.AddPage(&m_importAddressPage);
	m_dlgPropSheet.AddPage(&m_cilHeaderPage);
	m_dlgPropSheet.AddPage(&m_blobPage);
	m_dlgPropSheet.AddPage(&m_stringPage);
	m_dlgPropSheet.AddPage(&m_userStringPage);
	m_dlgPropSheet.AddPage(&m_guidPage);
	m_dlgPropSheet.AddPage(&m_tablesPage);

	m_dlgPropSheet.Create(this, WS_CHILD | WS_VISIBLE, 0);
	m_dlgPropSheet.ModifyStyleEx (0, WS_EX_CONTROLPARENT);
	m_dlgPropSheet.ModifyStyle( 0, WS_TABSTOP );

	CRect rcSheet;
	GetDlgItem( IDC_PROPERTYSHEET )->GetWindowRect( &rcSheet );
	ScreenToClient( &rcSheet );
	m_dlgPropSheet.SetWindowPos( NULL, rcSheet.left-7, rcSheet.top-7, 0, 0, 
			SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE );

	CAssemblyDoc *pDoc = GetDocument();
	PBYTE pAssembly = pDoc->FileData();
	PIMAGE_DOS_HEADER dosHeader = (PIMAGE_DOS_HEADER)pAssembly;
    if ( dosHeader->e_magic == IMAGE_DOS_SIGNATURE )
    {
		PIMAGE_NT_HEADERS pNTHeader;
		PIMAGE_NT_HEADERS64 pNTHeader64;

		// Utworzenie wskaźników do 32- i 64-bitowej wersji nagłówka.
		pNTHeader = MakePtr( PIMAGE_NT_HEADERS,
			                 dosHeader,
							 dosHeader->e_lfanew );

		pNTHeader64 = (PIMAGE_NT_HEADERS64)pNTHeader;

		// Najpierw weryfikacja, czy pole e_lfanew przekazało właściwy wskaźnik,
		// a następnie weryfikacja sygnatury PE.
		if ( IsBadReadPtr( pNTHeader, sizeof(pNTHeader->Signature) ) )
		{
			ATLTRACE(L"To nie jest plik typu Portable Executable (PE) EXE\n");
			return;
		}

		if ( pNTHeader->Signature != IMAGE_NT_SIGNATURE )
		{
			ATLTRACE(L"To nie jest plik typu Portable Executable (PE) EXE\n");
			return;
		}

		bool bIs64Bit = ( pNTHeader->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC );
		if(bIs64Bit)
		{
			GetTableInformation( pAssembly, pNTHeader64 );
		}
		else
		{
			GetTableInformation( pAssembly, pNTHeader );
		}
	}

	CEdit* pText;
	TCHAR cBuffer[128];
	pText = (CEdit*)GetDlgItem(IDC_NUMTABLES);
	wsprintf(cBuffer, _T("%d"), tableCount);
	pText->SetWindowText(cBuffer);

	pText = (CEdit*)GetDlgItem(IDC_BLOBSIZE);
	wsprintf(cBuffer, _T("%d"), blobTableSize);
	pText->SetWindowText(cBuffer);
    TRACE("Blob: %d(%#x)\n", blobTableSize, blobTableSize);

	pText = (CEdit*)GetDlgItem(IDC_STRINGSIZE);
	wsprintf(cBuffer, _T("%d"), stringsTableSize);
	pText->SetWindowText(cBuffer);
    TRACE("Ciągi: %d(%#x)\n", stringsTableSize, stringsTableSize);

	pText = (CEdit*)GetDlgItem(IDC_USERSTRINGSIZE);
	wsprintf(cBuffer, _T("%d"), userStringsTableSize);
	pText->SetWindowText(cBuffer);
    TRACE("Ciąg użytkownika: %d(%#x)\n", userStringsTableSize, userStringsTableSize);

	pText = (CEdit*)GetDlgItem(IDC_GUIDHEAPSIZE);
	wsprintf(cBuffer, _T("%d"), guidTableSize);
	pText->SetWindowText(cBuffer);
    TRACE("GUID: %d(%#x)\n", guidTableSize, guidTableSize);
}

template <class T> void CAssemblyView::GetTableInformation( PBYTE pImageBase, T* pNTHeader )	// T = PIMAGE_NT_HEADERS
{
	// CIL RVA
    DWORD CILHdrRVA;

    CILHdrRVA = GetImgDirEntryRVA(pNTHeader, IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR );
    if ( !CILHdrRVA )
        return;

	PIMAGE_COR20_HEADER pCIL = (PIMAGE_COR20_HEADER)GetPtrFromRVA( CILHdrRVA, pNTHeader, pImageBase );


	PBYTE pMeta = (PBYTE)GetPtrFromRVA( pCIL->MetaData.VirtualAddress, pNTHeader, pImageBase );
	PBYTE pMetaRoot = pMeta;

	// Rozpoczęcie od sygnatury
	pMeta += 4;

	// Następnie wersja
	pMeta += 4;

	// 4 bajty są zarezerwowane
	pMeta += 4;

	// Długość ciągu wersji
	int versionLength = *((DWORD*)pMeta);
	pMeta += 4;

	// Ciąg wersji
	pMeta += versionLength;
	if((versionLength % 4) > 0)
		pMeta += 4 - (versionLength % 4);

	// 2 bajty są zarezerwowane dla znaczników
	pMeta += 2;

	// 2 dla licznika strumieni
	int metaCount = *((USHORT*)pMeta);
	pMeta += 2;

	size_t stringLength;
	// wchar_t cBuffer[128];
	PMETA_STREAM_HEADER pMetaStream;
	for(int i = 0;i < metaCount;i++)
	{
		pMetaStream = (PMETA_STREAM_HEADER)pMeta;
		stringLength = strlen(pMetaStream->Name);
		if(strcmp("#~", pMetaStream->Name) == 0)
		{
			compositeTable = (PMETA_COMPOSITE_HEADER)(pMetaRoot + pMetaStream->Offset);
			tableCount = 0;
			META_TABLE_ENTRY tableEntry;
			ULONGLONG valid = compositeTable->Valid;
			int tableID = 0;
			PDWORD rows = (PDWORD)(compositeTable + 1);
			while(valid)
			{
				if(valid & 0x1)
				{
					tableEntry.Address = NULL;
					tableEntry.Rows = rows[tableCount];
					tableEntry.RowSize = 0;
					tableEntry.Table = (TableType)tableID;
					tableEntry.HeapSizes = compositeTable->HeapSizes;
					tables[(TableType)tableID] = tableEntry;
					tableCount++;
				}
				valid = valid >> 1;
				tableID++;
			}

			ATLTRACE(L"Istnieje %d tablic\n", tableCount);

			// Funkcja TableRowSizes jest zależna od obecności co najmniej informacji
			// o liczbie wierszy, przez co jest to dwuetapowy proces.
			PBYTE address = (PBYTE)(rows + tableCount);
			valid = compositeTable->Valid;
			tableID = 0;
			while(valid)
			{
				if(valid & 0x1)
				{
					tables[(TableType)tableID].Address = address;
					tables[(TableType)tableID].RowSize = TableRowSizes((TableType)tableID);
					address += tables[(TableType)tableID].RowSize * tables[(TableType)tableID].Rows;
				}
				valid = valid >> 1;
				tableID++;
			}
		}
		else if(strcmp("#Ciągi", pMetaStream->Name) == 0)
		{
			stringsTable = pMetaRoot + pMetaStream->Offset;
			stringsTableSize = pMetaStream->Size;
		}
		else if(strcmp("#US", pMetaStream->Name) == 0)
		{
			userStringsTable = pMetaRoot + pMetaStream->Offset;
			userStringsTableSize = pMetaStream->Size;
		}
		else if(strcmp("#GUID", pMetaStream->Name) == 0)
		{
			guidTable = pMetaRoot + pMetaStream->Offset;
			guidTableSize = pMetaStream->Size;
		}
		else if(strcmp("#Blob", pMetaStream->Name) == 0)
		{
			blobTable = pMetaRoot + pMetaStream->Offset;
			blobTableSize = pMetaStream->Size;
		}
		pMeta += 8 + stringLength + 1;
		if((8 + stringLength + 1) % 4 > 0)
			pMeta += 4 - ((8 + stringLength + 1) % 4);
	}
}

DWORD CAssemblyView::TableRowSizes(TableType tableID)
{
	DWORD bytes;
	DWORD maxRows;
	MetaDataIterator it;
	byte heapSizes;
	it = tables.find(tableID);
	if(it == tables.end())
		return 0;
	heapSizes = it->second.HeapSizes;
	switch(tableID)
	{
	case Module:
		it->second.Name = L"Module";
		it->second.IndexSizes = 0;
		// Generacja (wartość dwubajtowa, zarezerowana, powinna być równa 0)
		// Nazwa (indeks sterty String)
		// Mvid  (indeks sterty Guid; jest to po prostu Guid używany do rozróżnienia między dwoma wersjami tego samego modułu)
		// EncId (indeks sterty Guid, zarezerwowany, powinien mieć wartość zero)
		// EncBaseId (indeks sterty Guid, zarezerwowany, powinien mieć wartość zero)
		bytes = 2;
		bytes += (heapSizes & 0x01) ? 4 : 2;
		bytes += (heapSizes & 0x02) ? 4 : 2;
		bytes += (heapSizes & 0x02) ? 4 : 2;
		bytes += (heapSizes & 0x02) ? 4 : 2;
		return bytes;

	case TypeRef:
		it->second.Name = L"TypeRef";
		// Zależność
		// ResolutionScope (indeks tablicy Module, ModuleRef, AssemblyRef, TypeRef
		//                  albo pusty; jest to zakodowany indeks ResolutionScope)
		// Nazwa (indeks sterty String)
		// Przestrzeń nazw (indeks sterty String)

		maxRows = 0;
		// Moduł
		it = tables.find(Module);
		if(it != tables.end() &&
		   it->second.Rows > maxRows)
		{
			maxRows = it->second.Rows;
		}
		// ModuleRef
		it = tables.find(ModuleRef);
		if(it != tables.end() &&
		   it->second.Rows > maxRows)
		{
			maxRows = it->second.Rows;
		}
		// AssemblyRef
		it = tables.find(AssemblyRef);
		if(it != tables.end() &&
		   it->second.Rows > maxRows)
		{
			maxRows = it->second.Rows;
		}
		// TypeRef
		it = tables.find(TypeRef);
		if(it != tables.end() &&
		   it->second.Rows > maxRows)
		{
			maxRows = it->second.Rows;
		}

		it = tables.find(tableID);
		if((maxRows << 2) > 0xffff)
		{
			bytes = 4;
			it->second.IndexSizes |=  0x01;
		}
		else
		{
			bytes = 2;
			it->second.IndexSizes &= ~0x01;
		}
		bytes += (heapSizes & 0x01) ? 4 : 2;
		bytes += (heapSizes & 0x01) ? 4 : 2;
		return bytes;

	case TypeDef:
		it->second.Name = L"TypeDef";
		// Zależność
		// Znaczniki (4-bajtowa maska bitowa typu TypeAttributes, sekcja 22.1.14)
		// Nazwa (indeks sterty String)
		// Przestrzeń nazw (indeks sterty String)
		// Extends (indeks tablicy TypeDef, TypeRef lub TypeSpec; jest to zakodowany indeks TypeDefOrRef)
		// FieldList (indeks tablicy Field). 
		// MethodList (indeks tablicy Method).
		bytes = 4;
		bytes += (heapSizes & 0x01) ? 4 : 2;
		bytes += (heapSizes & 0x01) ? 4 : 2;

		maxRows = 0;
		// TypeDef
		it = tables.find(TypeDef);
		if(it != tables.end() &&
		   it->second.Rows > maxRows)
		{
			maxRows = it->second.Rows;
		}
		// TypeSpec
		it = tables.find(TypeSpec);
		if(it != tables.end() &&
		   it->second.Rows > maxRows)
		{
			maxRows = it->second.Rows;
		}
		// TypeRef
		it = tables.find(TypeRef);
		if(it != tables.end() &&
		   it->second.Rows > maxRows)
		{
			maxRows = it->second.Rows;
		}
		it = tables.find(tableID);
		if((maxRows << 2) > 0xffff)
		{
			bytes += 4;
			it->second.IndexSizes |=  0x08;
		}
		else
		{
			bytes += 2;
			it->second.IndexSizes &= ~0x08;
		}

		maxRows = 0;
		// Pole
		it = tables.find(FieldDef);
		if(it != tables.end() &&
		   it->second.Rows > maxRows)
		{
			maxRows = it->second.Rows;
		}
		it = tables.find(tableID);
		if((maxRows << 2) > 0xffff)
		{
			it->second.IndexSizes |=  0x10;
			bytes += 4;
		}
		else
		{
			it->second.IndexSizes &= ~0x10;
			bytes += 2;
		}

		maxRows = 0;
		// Metoda
		it = tables.find(MethodDef);
		if(it != tables.end() &&
		   it->second.Rows > maxRows)
		{
			maxRows = it->second.Rows;
		}
		it = tables.find(tableID);
		if((maxRows << 2) > 0xffff)
		{
			it->second.IndexSizes |=  0x20;
			bytes += 4;
		}
		else
		{
			it->second.IndexSizes &= ~0x20;
			bytes += 2;
		}

		return bytes;

	case FieldDef:
		it->second.Name = L"FieldDef";
		it->second.IndexSizes = 0;
		// Znaczniki (dwubajtowa maska bitowa typu FieldAttributes, sekcja 22.1.5)
		// Nazwa (indeks sterty String)
		// Sygnatura (indeks sterty Blob)
		bytes = 2;
		bytes += (heapSizes & 0x01) ? 4 : 2;
		bytes += (heapSizes & 0x04) ? 4 : 2;
		return bytes;

	case MethodDef:
		it->second.Name = L"MethodDef";
		// Zależność
		// RVA (4-bajtowa stała)
		// ImplFlags (dwubajtowa maska bitowa typu MethodImplAttributes, sekcja 22.1.9)
		// Znaczniki (dwubajtowa maska bitowa typu MethodAttribute, sekcja 22.1.9)
		// Nazwa (indeks sterty String)
		// Sygnatura (indeks sterty Blob)
		// ParamList (indeks tablicy Param).
		bytes = 4;
		bytes += 2;
		bytes += 2;
		bytes += (heapSizes & 0x01) ? 4 : 2;
		bytes += (heapSizes & 0x04) ? 4 : 2;
		maxRows = 0;

		// ParamDef
		it = tables.find(ParamDef);
		if(it != tables.end() &&
		   it->second.Rows > maxRows)
		{
			maxRows = it->second.Rows;
		}
		it = tables.find(tableID);
		if(maxRows > 0xffff)
		{
			it->second.IndexSizes |=  0x20;
			bytes += 4;
		}
		else
		{
			it->second.IndexSizes &= ~0x20;
			bytes += 2;
		}
		return bytes;

	case ParamDef:
		it->second.Name = L"ParamDef";
		it->second.IndexSizes = 0;
		// Znaczniki (dwubajtowa maska bitowa typu ParamAttributes, sekcja 22.1.12)
		// Sekwencja (dwubajtowa stała)
		// Nazwa (indeks sterty String)
		bytes = 2;
		bytes += 2;
		bytes += (heapSizes & 0x01) ? 4 : 2;
		return bytes;

	case InterfaceImpl:
		it->second.Name = L"InterfaceImpl";
		// Zależność
		// Klasa (indeks tablicy TypeDef)
		// Interfejs (indeks tablicy TypeDef, TypeRef lub TypeSpec; jest to zakodowany indeks TypeDefOrRef)

		maxRows = 0;
		// TypeDef
		it = tables.find(TypeDef);
		if(it != tables.end() &&
		   it->second.Rows > maxRows)
		{
			maxRows = it->second.Rows;
		}
		it = tables.find(tableID);
		if(maxRows > 0xffff)
		{
			it->second.IndexSizes |=  0x01;
			bytes = 4;
		}
		else
		{
			it->second.IndexSizes &= ~0x01;
			bytes = 2;
		}

		maxRows = 0;
		// TypeDef
		it = tables.find(TypeDef);
		if(it != tables.end() &&
		   it->second.Rows > maxRows)
		{
			maxRows = it->second.Rows;
		}
		// TypeSpec
		it = tables.find(TypeSpec);
		if(it != tables.end() &&
		   it->second.Rows > maxRows)
		{
			maxRows = it->second.Rows;
		}
		// TypeRef
		it = tables.find(TypeRef);
		if(it != tables.end() &&
		   it->second.Rows > maxRows)
		{
			maxRows = it->second.Rows;
		}
		it = tables.find(tableID);
		if((maxRows << 2) > 0xffff)
		{
			it->second.IndexSizes |=  0x02;
			bytes += 4;
		}
		else
		{
			it->second.IndexSizes &= ~0x02;
			bytes += 2;
		}
		return bytes;

	case MemberRef:
		it->second.Name = L"MemberRef";
		// Zależność
		// Klasa (indeks tablicy TypeRef, ModuleRef, Method, TypeSpec lub TypeDef; jest to zakodowany indeks MemberRefParent)
		// Nazwa (indeks sterty String)
		// Sygnatura (indeks sterty Blob)
		maxRows = 0;
		// TypeRef
		it = tables.find(TypeRef);
		if(it != tables.end() &&
		   it->second.Rows > maxRows)
		{
			maxRows = it->second.Rows;
		}
		// ModuleRef
		it = tables.find(ModuleRef);
		if(it != tables.end() &&
		   it->second.Rows > maxRows)
		{
			maxRows = it->second.Rows;
		}
		// Method
		it = tables.find(MethodDef);
		if(it != tables.end() &&
		   it->second.Rows > maxRows)
		{
			maxRows = it->second.Rows;
		}
		// TypeSpec
		it = tables.find(TypeSpec);
		if(it != tables.end() &&
		   it->second.Rows > maxRows)
		{
			maxRows = it->second.Rows;
		}
		// TypeDef
		it = tables.find(TypeDef);
		if(it != tables.end() &&
		   it->second.Rows > maxRows)
		{
			maxRows = it->second.Rows;
		}
		it = tables.find(tableID);
		if((maxRows << 3) > 0xffff)
		{
			it->second.IndexSizes |=  0x01;
			bytes = 4;
		}
		else
		{
			it->second.IndexSizes &= ~0x01;
			bytes = 2;
		}
		bytes += (heapSizes & 0x01) ? 4 : 2;
		bytes += (heapSizes & 0x04) ? 4 : 2;
		return bytes;

	case Constant:
		it->second.Name = L"Constant";
		// Typ (jednobajtowa stała, po której następuje uzupełniający pusty bajt) : sekcja 22.1.15
		// Rodzic (indeks tablicy Param, Field lub Property; jest to zakodowany indeks HasConst)
		// Wartość (indeks sterty Blob)
		bytes  = 1;
		bytes += 1;

		maxRows = 0;
		// Param
		it = tables.find(ParamDef);
		if(it != tables.end() &&
		   it->second.Rows > maxRows)
		{
			maxRows = it->second.Rows;
		}
		// Pole
		it = tables.find(FieldDef);
		if(it != tables.end() &&
		   it->second.Rows > maxRows)
		{
			maxRows = it->second.Rows;
		}
		// Właściwość
		it = tables.find(Property);
		if(it != tables.end() &&
		   it->second.Rows > maxRows)
		{
			maxRows = it->second.Rows;
		}
		it = tables.find(tableID);
		if((maxRows << 2) > 0xffff)
		{
			it->second.IndexSizes |=  0x02;
			bytes += 4;
		}
		else
		{
			it->second.IndexSizes &= ~0x02;
			bytes += 2;
		}

		bytes += (heapSizes & 0x04) ? 4 : 2;

		return bytes;

	case CustomAttribute:
		it->second.Name = L"CustomAttribute";
		// Zależność
		// Rodzic (indeks tablicy metadanych z wyjątkiem samej tablicy CustomAttribute; jest to zakodowany indeks HasCustomAttribute)
		// Typ (indeks tablicy Method lub MethodRef; jest to zakodowany indeks CustomAttributeType)
		// Wartość (indeks sterty Blob)
		maxRows = 0;
		for(it = tables.begin();it != tables.end(); ++it)
		{
			// !CustomAttribute
			if(it->second.Table != CustomAttribute &&
			   it->second.Rows > maxRows)
			{
				maxRows = it->second.Rows;
			}
		}
		it = tables.find(tableID);
		if((maxRows << 5) > 0xffff)
		{
			it->second.IndexSizes |=  0x01;
			bytes = 4;
		}
		else
		{
			it->second.IndexSizes &= ~0x01;
			bytes = 2;
		}

		maxRows = 0;
		// Method
		it = tables.find(MethodDef);
		if(it != tables.end() &&
		   it->second.Rows > maxRows)
		{
			maxRows = it->second.Rows;
		}
		// MemberRef
		it = tables.find(MemberRef);
		if(it != tables.end() &&
		   it->second.Rows > maxRows)
		{
			maxRows = it->second.Rows;
		}
		it = tables.find(tableID);
		if((maxRows << 3) > 0xffff)
		{
			it->second.IndexSizes |=  0x02;
			bytes += 4;
		}
		else
		{
			it->second.IndexSizes &= ~0x02;
			bytes += 2;
		}
		bytes += (heapSizes & 0x04) ? 4 : 2;
		return bytes;

	case FieldMarshal:
		it->second.Name = L"FieldMarshal";
		// Rodzic (indeks tablicy Field lub Param; jest to zakodowany indeks HasFieldMarshal)
		// NativeType (indeks sterty Blob)
		maxRows = 0;
		// Param
		it = tables.find(ParamDef);
		if(it != tables.end() &&
		   it->second.Rows > maxRows)
		{
			maxRows = it->second.Rows;
		}
		// Pole
		it = tables.find(FieldDef);
		if(it != tables.end() &&
		   it->second.Rows > maxRows)
		{
			maxRows = it->second.Rows;
		}
		it = tables.find(tableID);
		if((maxRows << 1) > 0xffff)
		{
			it->second.IndexSizes |=  0x01;
			bytes = 4;
		}
		else
		{
			it->second.IndexSizes &= ~0x01;
			bytes = 2;
		}

		bytes += (heapSizes & 0x04) ? 4 : 2;

		return bytes;

	case DeclSecurity:
		it->second.Name = L"DeclSecurity";
		// Zależność
		// Akcja (dwubajtowa wartość)
		// Rodzic (indeks tablicy TypeDef, Method lub Assembly; jest to zakodowany indeks HasDeclSecurity)
		// PermissionSet (indeks sterty Blob) 
		bytes = 2;
		maxRows = 0;
		// TypeDef
		it = tables.find(TypeDef);
		if(it != tables.end() &&
		   it->second.Rows > maxRows)
		{
			maxRows = it->second.Rows;
		}
		// Metoda
		it = tables.find(MethodDef);
		if(it != tables.end() &&
		   it->second.Rows > maxRows)
		{
			maxRows = it->second.Rows;
		}
		// Podzespół
		it = tables.find(Assembly);
		if(it != tables.end() &&
		   it->second.Rows > maxRows)
		{
			maxRows = it->second.Rows;
		}
		it = tables.find(tableID);
		if((maxRows << 2) > 0xffff)
		{
			it->second.IndexSizes |=  0x02;
			bytes += 4;
		}
		else
		{
			it->second.IndexSizes &= ~0x02;
			bytes += 2;
		}
		bytes += (heapSizes & 0x04) ? 4 : 2;
		return bytes;

	case ClassLayout:
		it->second.Name = L"ClassLayout";
		// PackingSize (dwubajtowa stała)
		// ClassSize (4-bajtowa stała)
		// Rodzic (indeks tablicy TypeDef)
		bytes = 2;
		bytes += 4;

		maxRows = 0;
		// TypeDef
		it = tables.find(TypeDef);
		if(it != tables.end() &&
		   it->second.Rows > maxRows)
		{
			maxRows = it->second.Rows;
		}
		it = tables.find(tableID);
		if(maxRows > 0xffff)
		{
			it->second.IndexSizes |=  0x04;
			bytes += 4;
		}
		else
		{
			it->second.IndexSizes &= ~0x04;
			bytes += 2;
		}
		return bytes;

	case StandAloneSig:
		it->second.Name = L"StandAloneSig";
		it->second.IndexSizes = 0;
		// Sygnatura (indeks sterty Blob)
		bytes = (heapSizes & 0x04) ? 4 : 2;
		return bytes;

	case EventMap:
		it->second.Name = L"EventMap";
		// Zależność
		// Rodzic (indeks tablicy TypeDef)
		// EventList (indeks tablicy Event).
		maxRows = 0;
		// TypeDef
		it = tables.find(TypeDef);
		if(it != tables.end() &&
		   it->second.Rows > maxRows)
		{
			maxRows = it->second.Rows;
		}
		it = tables.find(tableID);
		if(maxRows > 0xffff)
		{
			it->second.IndexSizes |=  0x01;
			bytes = 4;
		}
		else
		{
			it->second.IndexSizes &= ~0x01;
			bytes = 2;
		}
		maxRows = 0;
		// Zdarzenie
		it = tables.find(Event);
		if(it != tables.end() &&
		   it->second.Rows > maxRows)
		{
			maxRows = it->second.Rows;
		}
		it = tables.find(tableID);
		if(maxRows > 0xffff)
		{
			it->second.IndexSizes |=  0x02;
			bytes += 4;
		}
		else
		{
			it->second.IndexSizes &= ~0x02;
			bytes += 2;
		}
		return bytes;

	case Event:
		it->second.Name = L"Event";
		// Zależność
		// EventFlags (dwubajtowa maska bitowa typu EventAttribute, sekcja 22.1.4)
		// Nazwa (indeks sterty String)
		// EventType (indeks tablicy TypeDef, TypeRef lub TypeSpec; jest to zakodowany indeks TypeDefOrRef)  [odpowiada to typowi zdarzenia; nie jest to typ, do którego należy to zdarzenie]
		bytes = 2;
		bytes += (heapSizes & 0x01) ? 4 : 2;
		maxRows = 0;
		// TypeDef
		it = tables.find(TypeDef);
		if(it != tables.end() &&
		   it->second.Rows > maxRows)
		{
			maxRows = it->second.Rows;
		}
		// TypeSpec
		it = tables.find(TypeSpec);
		if(it != tables.end() &&
		   it->second.Rows > maxRows)
		{
			maxRows = it->second.Rows;
		}
		// TypeRef
		it = tables.find(TypeRef);
		if(it != tables.end() &&
		   it->second.Rows > maxRows)
		{
			maxRows = it->second.Rows;
		}
		it = tables.find(tableID);
		if((maxRows << 2) > 0xffff)
		{
			it->second.IndexSizes |=  0x04;
			bytes += 4;
		}
		else
		{
			it->second.IndexSizes &= ~0x04;
			bytes += 2;
		}
		return bytes;

	case PropertyMap:
		it->second.Name = L"PropertyMap";
		// Rodzic (indeks tablicy TypeDef)
		// PropertyList (indeks tablicy Property). Znakuje pierwsze ciągłe uruchomienie właściwości należących do rodzica.
		maxRows = 0;
		// TypeDef
		it = tables.find(TypeDef);
		if(it != tables.end() &&
		   it->second.Rows > maxRows)
		{
			maxRows = it->second.Rows;
		}
		if(maxRows > 0xffff)
		{
			it->second.IndexSizes |=  0x01;
			bytes = 4;
		}
		else
		{
			it->second.IndexSizes &= ~0x01;
			bytes = 2;
		}
		maxRows = 0;
		// Właściwość
		it = tables.find(Property);
		if(it != tables.end() &&
		   it->second.Rows > maxRows)
		{
			maxRows = it->second.Rows;
		}
		if(maxRows > 0xffff)
		{
			it->second.IndexSizes |=  0x02;
			bytes += 4;
		}
		else
		{
			it->second.IndexSizes &= ~0x02;
			bytes += 2;
		}
		return bytes;

	case Property:
		it->second.Name = L"Property";
		it->second.IndexSizes = 0;
		// Znaczniki (dwubajtowa maska bitowa typu PropertyAttributes, sekcja 22.1.13)
		// Nazwa (indeks sterty String)
		// Typ (indeks sterty Blob)  [nazwa tej kolumny jest myląca, gdyż nie ma tu indeksów tablic TypeDef lub TypeRef, a jedynie indeksy sygnatury w stercie Blob właściwości)
		bytes = 2;
		bytes += (heapSizes & 0x01) ? 4 : 2;
		bytes += (heapSizes & 0x04) ? 4 : 2;
		return bytes;

	case MethodSemantics:
		it->second.Name = L"MethodSemantics";
		// Semantyka (dwubajtowa maska bitowa typu MethodSemanticsAttributes, sekcja 22.1.10)
		// Metoda (indeks tablicy Method)
		// Powiązanie (indeks tablicy Event lub Property; jest to zakodowany indeks HasSemantics) 

		bytes = 2;

		maxRows = 0;
		// MethodDef
		it = tables.find(MethodDef);
		if(it != tables.end() &&
		   it->second.Rows > maxRows)
		{
			maxRows = it->second.Rows;
		}
		if(maxRows > 0xffff)
		{
			it->second.IndexSizes |=  0x02;
			bytes += 4;
		}
		else
		{
			it->second.IndexSizes &= ~0x02;
			bytes += 2;
		}

		maxRows = 0;
		// Zdarzenie
		it = tables.find(Event);
		if(it != tables.end() &&
		   it->second.Rows > maxRows)
		{
			maxRows = it->second.Rows;
		}
		// Właściwość
		it = tables.find(Property);
		if(it != tables.end() &&
		   it->second.Rows > maxRows)
		{
			maxRows = it->second.Rows;
		}
		if((maxRows << 1) > 0xffff)
		{
			it->second.IndexSizes |=  0x02;
			bytes += 4;
		}
		else
		{
			it->second.IndexSizes &= ~0x02;
			bytes += 2;
		}
		return bytes;

	case ModuleRef:
		it->second.Name = L"ModuleRef";
		it->second.IndexSizes = 0;
		// Nazwa (indeks sterty String)
		bytes = (heapSizes & 0x01) ? 4 : 2;
		return bytes;

	case TypeSpec:
		it->second.Name = L"TypeSpec";
		it->second.IndexSizes = 0;
		// Sygnatura (indeks sterty Blob, gdzie obiekt blob jest sformatowany zgodnie z sekcją 22.2.14)
		bytes = (heapSizes & 0x04) ? 4 : 2;
		return bytes;

	case ImplMap:
		it->second.Name = L"ImplMap";
		// MappingFlags (dwubajtowa maska bitowa typu PInvokeAttributes, sekcja 22.1.7)
		// MemberForwarded (indeks tablicy Field lub Method table; jest to zakodowany indeks MemberForwarded. Indeksowana jest jedynie tablica Method, ponieważ eksport Field nie jest obsługiwany.
		// ImportName (indeks sterty String)
		// ImportScope (indeks tablicy ModuleRef)
		bytes = 2;

		maxRows = 0;
		// Pole
		it = tables.find(FieldDef);
		if(it != tables.end() &&
		   it->second.Rows > maxRows)
		{
			maxRows = it->second.Rows;
		}
		// MethodDef
		it = tables.find(MethodDef);
		if(it != tables.end() &&
		   it->second.Rows > maxRows)
		{
			maxRows = it->second.Rows;
		}
		it = tables.find(tableID);
		if((maxRows << 1) > 0xffff)
		{
			it->second.IndexSizes |=  0x02;
			bytes += 4;
		}
		else
		{
			it->second.IndexSizes &= ~0x02;
			bytes += 2;
		}

		bytes += (heapSizes & 0x01) ? 4 : 2;

		maxRows = 0;
		// ModuleRef
		it = tables.find(ModuleRef);
		if(it != tables.end() &&
		   it->second.Rows > maxRows)
		{
			maxRows = it->second.Rows;
		}
		it = tables.find(tableID);
		if((maxRows << 2) > 0xffff)
		{
			it->second.IndexSizes |=  0x08;
			bytes += 4;
		}
		else
		{
			it->second.IndexSizes &= ~0x08;
			bytes += 2;
		}
		return bytes;

	case FieldRVA:
		it->second.Name = L"FieldRVA";
		// RVA (4-bajtowa stała)
		// Pole (indeks tablicy Field)
		bytes = 4;
		maxRows = 0;
		// Pole
		it = tables.find(FieldDef);
		if(it != tables.end() &&
		   it->second.Rows > maxRows)
		{
			maxRows = it->second.Rows;
		}
		it = tables.find(tableID);
		if(maxRows > 0xffff)
		{
			it->second.IndexSizes |=  0x02;
			bytes += 4;
		}
		else
		{
			it->second.IndexSizes &= ~0x02;
			bytes += 2;
		}
		return bytes;

	case Assembly:
		it->second.Name = L"Assembly";
		it->second.IndexSizes = 0;
		// HashAlgId (4-bajtowa stała typu AssemblyHashAlgorithm, sekcja 22.1.1)
		// MajorVersion, MinorVersion, BuildNumber, RevisionNumber (dwubajtowe stałe)
		// Znaczniki (4-bajtowa maska bitowa typu AssemblyFlags, sekcja 22.1.2)
		// PublicKey (indeks sterty Blob)
		// Nazwa (indeks sterty String)
		// Kultura (indeks sterty String) 
		bytes = 4;
		bytes += 8;
		bytes += 4;
		bytes += (heapSizes & 0x04) ? 4 : 2;
		bytes += (heapSizes & 0x01) ? 4 : 2;
		bytes += (heapSizes & 0x01) ? 4 : 2;
		return bytes;

	case AssemblyProcessor:
		it->second.Name = L"AssemblyProcessor";
		it->second.IndexSizes = 0;
		bytes = 4;
		return bytes;

	case AssemblyOS:
		it->second.Name = L"AssemblyOS";
		it->second.IndexSizes = 0;
		// OSPlatformID  (4-bajtowa stała)
		// OSMajorVersion (4-bajtowa stała)
		// OSMinorVersion (4-bajtowa stała)
		bytes = 4;
		bytes += 4;
		bytes += 4;
		return bytes;

	case AssemblyRef:
		it->second.Name = L"AssemblyRef";
		it->second.IndexSizes = 0;
		// MajorVersion, MinorVersion, BuildNumber, RevisionNumber (dwubajtowe stałe)
		// Znaczniki (4-bajtowa maska bitowa typu AssemblyFlags, sekcja 22.1.2)
		// PublicKeyOrToken (indeks sterty Blob; jest to klucz publiczny lub żeton identyfikujący autora podzespołu)
		// Nazwa (indeks sterty String)
		// Kultura (indeks sterty String)
		// HashValue (indeks sterty Blob) 
		bytes = 8;
		bytes += 4;
		bytes += (heapSizes & 0x04) ? 4 : 2;
		bytes += (heapSizes & 0x01) ? 4 : 2;
		bytes += (heapSizes & 0x01) ? 4 : 2;
		bytes += (heapSizes & 0x04) ? 4 : 2;
		return bytes;

	case AssemblyRefProcessor:
		it->second.Name = L"AssemblyRefProcessor";
		// Procesor (4-bajtowa stała)
		// AssemblyRef  (indeks tablicy AssemblyRef)
		bytes = 4;

		maxRows = 0;
		// AssemblyRef
		it = tables.find(AssemblyRef);
		if(it != tables.end() &&
		   it->second.Rows > maxRows)
		{
			maxRows = it->second.Rows;
		}
		it = tables.find(tableID);
		if(maxRows > 0xffff)
		{
			it->second.IndexSizes |=  0x02;
			bytes += 4;
		}
		else
		{
			it->second.IndexSizes &= ~0x02;
			bytes += 2;
		}
		return bytes;

	case AssemblyRefOS:
		it->second.Name = L"AssemblyRefOS";
		// OSPlatformId  (4-bajtowa stała)
		// OSMajorVersion (4-bajtowa stała)
		// OSMinorVersion (4-bajtowa stała)
		// AssemblyRef  (indeks tablicy AssemblyRef)
		bytes = 4;
		bytes += 4;
		bytes += 4;

		maxRows = 0;
		// AssemblyRef
		it = tables.find(AssemblyRef);
		if(it != tables.end() &&
		   it->second.Rows > maxRows)
		{
			maxRows = it->second.Rows;
		}
		it = tables.find(tableID);
		if(maxRows > 0xffff)
		{
			it->second.IndexSizes |=  0x04;
			bytes += 4;
		}
		else
		{
			it->second.IndexSizes &= ~0x04;
			bytes += 2;
		}
		return bytes;

	case File:
		it->second.Name = L"File";
		it->second.IndexSizes = 0;
		// Znaczniki (4-bajtowa maska bitowa typu FileAttributes, sekcja 22.1.6)
		// Nazwa (indeks sterty String)
		// HashValue (indeks sterty Blob)
		bytes = 4;
		bytes += (heapSizes & 0x01) ? 4 : 2;
		bytes += (heapSizes & 0x04) ? 4 : 2;
		return bytes;

	case ExportedType:
		it->second.Name = L"ExportedType";
		// Zależność
		// Znaczniki (4-bajtowa maska bitowa typu TypeAttributes, sekcja 22.1.14)
		// TypeDefId (4-bajtowy indeks tablicy TypeDef dla innego modułu w danym podzespole). To pole służy tylko jako wskazówka. Jeśli wpis w docelowej tablicy TypeDef odpowiada wpisom TypeName i TypeNamespace w tej tablicy, to rozwiązywanie powiodło się. Niezgodność oznacza, iż CLI powinien powrócić do przeszukiwania docelowej tablicy TypeDef.
		// TypeName (indeks sterty String)
		// TypeNamespace (indeks sterty String)
		// Implementacja. Może to być indeks (a konkretnie zakodowany indeks implementacji) jednej z dwóch tablic:
		bytes = 4;
		bytes += 4;
		bytes += (heapSizes & 0x01) ? 4 : 2;
		bytes += (heapSizes & 0x01) ? 4 : 2;
		maxRows = 0;
		// Plik
		it = tables.find(File);
		if(it != tables.end() &&
		   it->second.Rows > maxRows)
		{
			maxRows = it->second.Rows;
		}
		// AssemblyRef
		it = tables.find(AssemblyRef);
		if(it != tables.end() &&
		   it->second.Rows > maxRows)
		{
			maxRows = it->second.Rows;
		}
		// ExportedType
		it = tables.find(ExportedType);
		if(it != tables.end() &&
		   it->second.Rows > maxRows)
		{
			maxRows = it->second.Rows;
		}
		it = tables.find(tableID);
		if((maxRows << 2) > 0xffff)
		{
			it->second.IndexSizes |=  0x10;
			bytes += 4;
		}
		else
		{
			it->second.IndexSizes &= ~0x10;
			bytes += 2;
		}
		return bytes;

	case ManifestResource:
		it->second.Name = L"ManifestResource";
		// Zależność
		// Przesunięcie  (4-bajtowa stała)
		// Znaczniki (4-bajtowa maska bitowa typu ManifestResourceAttributes, sekcja 22.1.8) 
		// Nazwa (indeks sterty String)
		// Implementacja (indeks tablicy File lub AssemblyRef albo pusta wartość; jest to zakodowany indeks implementacji).
		bytes = 4;
		bytes += 4;
		bytes += (heapSizes & 0x01) ? 4 : 2;
		maxRows = 0;
		// File
		it = tables.find(File);
		if(it != tables.end() &&
		   it->second.Rows > maxRows)
		{
			maxRows = it->second.Rows;
		}
		// AssemblyRef
		it = tables.find(AssemblyRef);
		if(it != tables.end() &&
		   it->second.Rows > maxRows)
		{
			maxRows = it->second.Rows;
		}
		// ExportedType
		it = tables.find(ExportedType);
		if(it != tables.end() &&
		   it->second.Rows > maxRows)
		{
			maxRows = it->second.Rows;
		}
		it = tables.find(tableID);
		if((maxRows << 2) > 0xffff)
		{
			it->second.IndexSizes |=  0x80;
			bytes += 4;
		}
		else
		{
			it->second.IndexSizes &= ~0x80;
			bytes += 2;
		}
		return bytes;

	case NestedClass:
		it->second.Name = L"NestedClass";
		// Zależność
		// NestedClass (indeks tablicy TypeDef)
		// EnclosingClass (indeks tablicy TypeDef)
		maxRows = 0;
		it = tables.find(TypeDef);
		if(it != tables.end() &&
		   it->second.Rows > maxRows)
		{
			maxRows = it->second.Rows;
		}
		it = tables.find(tableID);
		if(maxRows > 0xffff)
		{
			it->second.IndexSizes |=  0x01;
			it->second.IndexSizes |=  0x02;
			bytes = 4;
			bytes += 4;
		}
		else
		{
			it->second.IndexSizes &= ~0x01;
			it->second.IndexSizes &= ~0x02;
			bytes = 2;
			bytes += 2;
		}
		return bytes;
	default:
		return 0;
	}
}

std::wstring CAssemblyView::StringTableEntry(DWORD index) const
{
	if(index > stringsTableSize)
		return L"";
	PBYTE str = stringsTable + index;

	std::wstring ret;
	int nchars = MultiByteToWideChar(CP_UTF8, 0,		// Konwersja UTF8
		                             (const char *)str,	// Ciąg źródłowy
								     -1,				// Zakończony pustą wartością
								     NULL,				// Sprawdzenie liczby wymaganych bajtów
								     0);
	wchar_t* buffer = (wchar_t *)_alloca(nchars);
	int err = MultiByteToWideChar(CP_UTF8, 0,
		                          (const char *)str,
								  -1,
								  buffer,
								  nchars);
	if(err == 0)
	{
		ATLTRACE(L"Błąd konwersji\n");
		return L"";
	}
	ret = buffer;
	return ret;
}

std::wstring CAssemblyView::GUIDTableEntry(DWORD index) const
{
	if(index*sizeof(GUID) > guidTableSize)
		return L"";
	if(index == 0)
		return L"";
	// Z sekcji 21:
	// "Sterta Guid jest tablicą identyfikatorów GUID o długości 16 bajtów każdy.
	//  Pierwszy element ma numer 1, drugie element numer 2 itd."
	GUID *pguid = (GUID*)(guidTable + (index - 1) * 16);
	WCHAR lBuffer[64];
	StringFromGUID2(*pguid, lBuffer, sizeof(lBuffer));
	return lBuffer;
}

const PBYTE CAssemblyView::BlobTableEntry(DWORD index, PDWORD size) const
{
	if(size == NULL)
		return NULL;
	if(index > blobTableSize)
	{
		*size = 0;
		return NULL;
	}
	const PBYTE blob = blobTable + index;
	DWORD relindex = 0;
	relindex = CorSigUncompressData(blob, size);
	return blob + relindex;
}

const std::wstring CAssemblyView::UserStringTableEntry(DWORD index) const
{
	if(index > userStringsTableSize)
	{
		return L"";
	}
	DWORD size = 0;
	const PBYTE userString = userStringsTable + index;
	DWORD relindex = 0;
	relindex = CorSigUncompressData(userString, &size);

	// Zawsze nieparzyste
	size--;

	LPWSTR szUserString = (LPWSTR)(userString + relindex);
	std::wstring codedUserString;
	while (size)
	{   
		switch (*szUserString)
		{
		case 0:
			codedUserString += L"\\0";
			break;
		case L'\r':
			codedUserString += L"\\r";
			break;
		case L'\n':
			codedUserString += L"\\n";
			break;
		case L'\t':
			codedUserString += L"\\t";
			break;
		default:
			if (iswprint(*szUserString))
			{
				codedUserString += *szUserString;
			}
			else 
			{
				codedUserString += L".";
			}
			break;
		}
		++szUserString;
		size -= 2;
	}
	return codedUserString;
}

std::wstring CAssemblyView::TypeRefName(const META_TABLE_ENTRY& t, DWORD index) const
{
	PBYTE row;
	DWORD nameIndex;
	DWORD nameSpaceIndex;

	row = t.Address + t.RowSize * index;
	if(t.IndexSizes & 0x01)
	{
		row += 4;
	}
	else
	{
		row += 2;
	}
	if(t.HeapSizes & 0x01)
	{
		nameIndex = *((ULONG *)row);
		row += 4;
		nameSpaceIndex = *((ULONG *)row);
		row += 4;
	}
	else
	{
		nameIndex = *((USHORT *)row);
		row += 2;
		nameSpaceIndex = *((USHORT *)row);
		row += 2;
	}
	return StringTableEntry(nameSpaceIndex) +
		   std::wstring(L".") +
		   StringTableEntry(nameIndex);
}

std::wstring CAssemblyView::TypeDefName(const META_TABLE_ENTRY& t, DWORD index) const
{
	PBYTE row;
	DWORD nameIndex;
	DWORD nameSpaceIndex;

	row = t.Address + t.RowSize * index;
	row += 4;
	if(t.HeapSizes & 0x01)
	{
		nameIndex = *((ULONG *)row);
		row += 4;
		nameSpaceIndex = *((ULONG *)row);
		row += 4;
	}
	else
	{
		nameIndex = *((USHORT *)row);
		row += 2;
		nameSpaceIndex = *((USHORT *)row);
		row += 2;
	}
	return StringTableEntry(nameSpaceIndex) +
		   std::wstring(L".") +
		   StringTableEntry(nameIndex);
}

bool CAssemblyView::MetaDataTokenToString(mdToken t, std::wstring &s) const
{
	TableType table = (TableType)(t >> 24);
	MetaDataConstantIterator it;
	if(table != UserString)
	{
		it = tables.find(table);
		if(it == tables.end())
			return false;
	}
	std::wostringstream ret;
	DWORD index = (DWORD)(t & 0x00ffffff);
	switch(table)
	{
	case Module:
		ret << L"Module(" << index << L")";
		break;
	case TypeRef:
		ret << TypeRefName(it->second, index - 1);
		break;
	case TypeDef:
		ret << TypeDefName(it->second, index - 1);
		break;
	case FieldDef:
		ret << L"FieldDef(" << index << L")";
		break;
	case MethodDef:
		ret << L"MethodDef(" << index << L")";
		break;
	case ParamDef:
		ret << L"ParamDef(" << index << L")";
		break;
	case InterfaceImpl:
		ret << L"InterfaceImpl(" << index << L")";
		break;
	case MemberRef:
		ret << L"MemberRef(" << index << L")";
		break;
	case Constant:
		ret << L"Constant(" << index << L")";
		break;
	case CustomAttribute:
		ret << L"CustomAttribute(" << index << L")";
		break;
	case FieldMarshal:
		ret << L"FieldMarshal(" << index << L")";
		break;
	case DeclSecurity:
		ret << L"DeclSecurity(" << index << L")";
		break;
	case ClassLayout:
		ret << L"ClassLayout(" << index << L")";
		break;
	case StandAloneSig:
		ret << L"StandAloneSig(" << index << L")";
		break;
	case EventMap:
		ret << L"EventMap(" << index << L")";
		break;
	case Event:
		ret << L"Event(" << index << L")";
		break;
	case PropertyMap:
		ret << L"PropertyMap(" << index << L")";
		break;
	case Property:
		ret << L"Property(" << index << L")";
		break;
	case MethodSemantics:
		ret << L"MethodSemantics(" << index << L")";
		break;
	case ModuleRef:
		ret << L"ModuleRef(" << index << L")";
		break;
	case TypeSpec:
		ret << L"TypeSpec(" << index << L")";
		break;
	case ImplMap:
		ret << L"ImplMap(" << index << L")";
		break;
	case FieldRVA:
		ret << L"FieldRVA(" << index << L")";
		break;
	case Assembly:
		ret << L"Assembly(" << index << L")";
		break;
	case AssemblyProcessor:
		ret << L"AssemblyProcessor(" << index << L")";
		break;
	case AssemblyOS:
		ret << L"AssemblyOS(" << index << L")";
		break;
	case AssemblyRef:
		ret << L"AssemblyRef(" << index << L")";
		break;
	case AssemblyRefProcessor:
		ret << L"AssemblyRefProcessor(" << index << L")";
		break;
	case AssemblyRefOS:
		ret << L"AssemblyRefOS(" << index << L")";
		break;
	case File:
		ret << L"File(" << index << L")";
		break;
	case ExportedType:
		ret << L"ExportedType(" << index << L")";
		break;
	case ManifestResource:
		ret << L"ManifestResource(" << index << L")";
		break;
	case NestedClass:
		ret << L"NestedClass(" << index << L")";
		break;
	case UserString:
		ret << L"UserString(0x" << std::hex << index << L") \"" << UserStringTableEntry(index).c_str() << L"\"";
		break;
	default:
		return false;
	}
	s = ret.str();
	return true;
}
