//
// 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.
//
// 
#include "stdafx.h"
#include "cilopcode.h"

bool CCILOpcode::initialized = false;
ILCodeMap CCILOpcode::oneByteOpcodes;
ILCodeMap CCILOpcode::twoByteOpcodes;
void CCILOpcode::BuildMap(ILCodeMap& m, ILCodeMap& n)
{
	m[0x00] = opcode(L"nop");
	m[0x01] = opcode(L"break");
	m[0x02] = opcode(L"ldarg.0");
	m[0x03] = opcode(L"ldarg.1");
	m[0x04] = opcode(L"ldarg.2");
	m[0x05] = opcode(L"ldarg.3");
	m[0x06] = opcode(L"ldloc.0");
	m[0x07] = opcode(L"ldloc.1");
	m[0x08] = opcode(L"ldloc.2");
	m[0x09] = opcode(L"ldloc.3");
	m[0x0a] = opcode(L"stloc.0");
	m[0x0b] = opcode(L"stloc.1");
	m[0x0c] = opcode(L"stloc.2");
	m[0x0d] = opcode(L"stloc.3");
	m[0x0e] = opcode(L"ldarg.s", 1, Index);
	m[0x0f] = opcode(L"ldarga.s", 1, Index);
	m[0x10] = opcode(L"starg.s", 1, Index);
	m[0x11] = opcode(L"ldloc.s", 1, Index);
	m[0x12] = opcode(L"ldloca.s", 1, Index);
	m[0x13] = opcode(L"stloc.s", 1, Index);
	m[0x14] = opcode(L"ldnull");
	m[0x15] = opcode(L"ldc.i4.m1");
	m[0x16] = opcode(L"ldc.i4.0");
	m[0x17] = opcode(L"ldc.i4.1");
	m[0x18] = opcode(L"ldc.i4.2");
	m[0x19] = opcode(L"ldc.i4.3");
	m[0x1a] = opcode(L"ldc.i4.4");
	m[0x1b] = opcode(L"ldc.i4.5");
	m[0x1c] = opcode(L"ldc.i4.6");
	m[0x1d] = opcode(L"ldc.i4.7");
	m[0x1e] = opcode(L"ldc.i4.8");
	m[0x1f] = opcode(L"ldc.i4.s", 1, Value);
	m[0x20] = opcode(L"ldc.i4", 4, Value);
	m[0x21] = opcode(L"ldc.i8", 8, Value);
	m[0x22] = opcode(L"ldc.r4", 4, Value);
	m[0x23] = opcode(L"ldc.r8", 8, Value);
	m[0x25] = opcode(L"dup");
	m[0x26] = opcode(L"pop");
	m[0x27] = opcode(L"jmp", 4, Token);
	m[0x28] = opcode(L"call", 4, Token);
	m[0x29] = opcode(L"calli", 4, Token);
	m[0x2a] = opcode(L"ret");
	m[0x2b] = opcode(L"br.s", 1, Branch);
	m[0x2c] = opcode(L"brfalse.s", 1, Branch);
	m[0x2d] = opcode(L"brtrue.s", 1, Branch);
	m[0x2e] = opcode(L"beq.s", 1, Branch);
	m[0x2f] = opcode(L"bge.s", 1, Branch);
	m[0x30] = opcode(L"bgt.s", 1, Branch);
	m[0x31] = opcode(L"ble.s", 1, Branch);
	m[0x32] = opcode(L"blt.s", 1, Branch);
	m[0x33] = opcode(L"bne.un.s", 1, Branch);
	m[0x34] = opcode(L"bge.un.s", 1, Branch);
	m[0x35] = opcode(L"bgt.un.s", 1, Branch);
	m[0x36] = opcode(L"ble.un.s", 1, Branch);
	m[0x37] = opcode(L"blt.un.s", 1, Branch);
	m[0x38] = opcode(L"br", 4, Branch);
	m[0x39] = opcode(L"brfalse", 4, Branch);
	m[0x3a] = opcode(L"brtrue", 4, Branch);
	m[0x3b] = opcode(L"beq", 4, Branch);
	m[0x3c] = opcode(L"bge", 4, Branch);
	m[0x3d] = opcode(L"bgt", 4, Branch);
	m[0x3e] = opcode(L"ble", 4, Branch);
	m[0x3f] = opcode(L"blt", 4, Branch);
	m[0x40] = opcode(L"bne.un", 4, Branch);
	m[0x41] = opcode(L"bge.un", 4, Branch);
	m[0x42] = opcode(L"bgt.un", 4, Branch);
	m[0x43] = opcode(L"ble.un", 4, Branch);
	m[0x44] = opcode(L"blt.un", 4, Branch);
	m[0x45] = opcode(L"switch", 4, Switch);
	m[0x46] = opcode(L"ldind.i1");
	m[0x47] = opcode(L"ldind.u1");
	m[0x48] = opcode(L"ldind.i2");
	m[0x49] = opcode(L"ldind.u2");
	m[0x4a] = opcode(L"ldind.i4");
	m[0x4b] = opcode(L"ldind.u4");
	m[0x4c] = opcode(L"ldind.i8");
	m[0x4d] = opcode(L"ldind.i");
	m[0x4e] = opcode(L"ldind.r4");
	m[0x4f] = opcode(L"ldind.r8");
	m[0x50] = opcode(L"ldind.ref");
	m[0x51] = opcode(L"stind.ref");
	m[0x52] = opcode(L"stind.i1");
	m[0x53] = opcode(L"stind.i2");
	m[0x54] = opcode(L"stind.i4");
	m[0x55] = opcode(L"stind.i8");
	m[0x56] = opcode(L"stind.r4");
	m[0x57] = opcode(L"stind.r8");
	m[0x58] = opcode(L"add");
	m[0x59] = opcode(L"sub");
	m[0x5a] = opcode(L"mul");
	m[0x5b] = opcode(L"div");
	m[0x5c] = opcode(L"div.un");
	m[0x5d] = opcode(L"rem");
	m[0x5e] = opcode(L"rem.un");
	m[0x5f] = opcode(L"and");
	m[0x60] = opcode(L"or");
	m[0x61] = opcode(L"xor");
	m[0x62] = opcode(L"shl");
	m[0x63] = opcode(L"shr");
	m[0x64] = opcode(L"shr.un");
	m[0x65] = opcode(L"neg");
	m[0x66] = opcode(L"not");
	m[0x67] = opcode(L"conv.i1");
	m[0x68] = opcode(L"conv.i2");
	m[0x69] = opcode(L"conv.i4");
	m[0x6a] = opcode(L"conv.i8");
	m[0x6b] = opcode(L"conv.r4");
	m[0x6c] = opcode(L"conv.r8");
	m[0x6d] = opcode(L"conv.u4");
	m[0x6e] = opcode(L"conv.u8");
	m[0x6f] = opcode(L"callvirt", 4, Token);
	m[0x70] = opcode(L"cpobj", 4, Token);
	m[0x71] = opcode(L"ldobj", 4, Token);
	m[0x72] = opcode(L"ldstr", 4, Token);
	m[0x73] = opcode(L"newobj", 4, Token);
	m[0x74] = opcode(L"castclass", 4, Token);
	m[0x75] = opcode(L"isinst", 4, Token);
	m[0x76] = opcode(L"conv.r.un");
	m[0x79] = opcode(L"unbox", 4, Token);
	m[0x7a] = opcode(L"throw");
	m[0x7b] = opcode(L"ldfld", 4, Token);
	m[0x7c] = opcode(L"ldflda", 4, Token);
	m[0x7d] = opcode(L"stfld", 4, Token);
	m[0x7e] = opcode(L"ldsfld", 4, Token);
	m[0x7f] = opcode(L"ldsflda", 4, Token);
	m[0x80] = opcode(L"stsfld", 4, Token);
	m[0x81] = opcode(L"stobj", 4, Token);
	m[0x82] = opcode(L"conv.ovf.i1.un");
	m[0x83] = opcode(L"conv.ovf.i2.un");
	m[0x84] = opcode(L"conv.ovf.i4.un");
	m[0x85] = opcode(L"conv.ovf.i8.un");
	m[0x86] = opcode(L"conv.ovf.u1.un");
	m[0x87] = opcode(L"conv.ovf.u2.un");
	m[0x88] = opcode(L"conv.ovf.u4.un");
	m[0x89] = opcode(L"conv.ovf.u8.un");
	m[0x8a] = opcode(L"conv.ovf.i.un");
	m[0x8b] = opcode(L"conv.ovf.u.un");
	m[0x8c] = opcode(L"box", 4, Token);
	m[0x8d] = opcode(L"newarr", 4, Token);
	m[0x8e] = opcode(L"ldlen");
	m[0x8f] = opcode(L"ldelema", 4, Token);
	m[0x90] = opcode(L"ldelem.i1");
	m[0x91] = opcode(L"ldelem.u1");
	m[0x92] = opcode(L"ldelem.i2");
	m[0x93] = opcode(L"ldelem.u2");
	m[0x94] = opcode(L"ldelem.i4");
	m[0x95] = opcode(L"ldelem.u4");
	m[0x96] = opcode(L"ldelem.i8");
	m[0x97] = opcode(L"ldelem.i");
	m[0x98] = opcode(L"ldelem.r4");
	m[0x99] = opcode(L"ldelem.r8");
	m[0x9a] = opcode(L"ldelem.ref");
	m[0x9b] = opcode(L"stelem.i");
	m[0x9c] = opcode(L"stelem.i1");
	m[0x9d] = opcode(L"stelem.i2");
	m[0x9e] = opcode(L"stelem.i4");
	m[0x9f] = opcode(L"stelem.i8");
	m[0xa0] = opcode(L"stelem.r4");
	m[0xa1] = opcode(L"stelem.r8");
	m[0xa2] = opcode(L"stelem.ref");
	m[0xb3] = opcode(L"conv.ovf.i1");
	m[0xb4] = opcode(L"conv.ovf.u1");
	m[0xb5] = opcode(L"conv.ovf.i2");
	m[0xb6] = opcode(L"conv.ovf.u2");
	m[0xb7] = opcode(L"conv.ovf.i4");
	m[0xb8] = opcode(L"conv.ovf.u4");
	m[0xb9] = opcode(L"conv.ovf.i8");
	m[0xba] = opcode(L"conv.ovf.u8");
	m[0xc2] = opcode(L"refanyval", 4, Token);
	m[0xc3] = opcode(L"ckfinite");
	m[0xc6] = opcode(L"mkrefany", 4, Token);
	m[0xd0] = opcode(L"ldtoken", 4, Token);
	m[0xd1] = opcode(L"conv.u2");
	m[0xd2] = opcode(L"conv.u1");
	m[0xd3] = opcode(L"conv.i");
	m[0xd4] = opcode(L"conv.ovf.i");
	m[0xd5] = opcode(L"conv.ovf.u");
	m[0xd6] = opcode(L"add.ovf");
	m[0xd7] = opcode(L"add.ovf.un");
	m[0xd8] = opcode(L"mul.ovf");
	m[0xd9] = opcode(L"mul.ovf.un");
	m[0xda] = opcode(L"sub.ovf");
	m[0xdb] = opcode(L"sub.ovf.un");
	m[0xdc] = opcode(L"endfinally");
	m[0xdd] = opcode(L"leave", 4, Branch);
	m[0xde] = opcode(L"leave.s", 1, Branch);
	m[0xdf] = opcode(L"stind.i");
	m[0xe0] = opcode(L"conv.u");

	n[0x00] =  opcode(L"arglist");
	n[0x01] =  opcode(L"ceq");
	n[0x02] =  opcode(L"cgt");
	n[0x03] =  opcode(L"cgt.un");
	n[0x04] =  opcode(L"clt");
	n[0x05] =  opcode(L"clt.un");
	n[0x06] =  opcode(L"ldftn", 4, Token);
	n[0x07] =  opcode(L"ldvirtftn", 4, Token);
	n[0x09] =  opcode(L"ldarg", 2, Index);
	n[0x0a] =  opcode(L"ldarga", 2, Index);
	n[0x0b] =  opcode(L"starg", 2, Index);
	n[0x0c] =  opcode(L"ldloc", 2, Index);
	n[0x0d] =  opcode(L"ldloca", 2, Index);
	n[0x0e] =  opcode(L"stloc", 2, Index);
	n[0x0f] =  opcode(L"localloc");
	n[0x11] =  opcode(L"endfilter");
	n[0x12] =  opcode(L"unaligned.");
	n[0x13] =  opcode(L"volatile.");
	n[0x14] =  opcode(L"tail.");
	n[0x15] =  opcode(L"initobj", 4, Token);
	n[0x17] =  opcode(L"cpblk");
	n[0x18] =  opcode(L"initblk");
	n[0x1a] =  opcode(L"rethrow");
	n[0x1c] =  opcode(L"sizeof", 4, Token);
	n[0x1d] =  opcode(L"refanytype");
}

CCILOpcode::CCILOpcode(PBYTE source, int count)
{
	if(!initialized)
	{
		BuildMap(oneByteOpcodes, twoByteOpcodes);
		initialized = true;
	}
	this->source = source;
	this->count = count;
	index = 0;
}

bool CCILOpcode::Next()
{
	std::wstring instructionLabel;
	if(count <= 0)
		return false;
	if(index >= count)
		return false;

	WCHAR lBuffer[256];
	WCHAR oBuffer[64];
	wsprintf(lBuffer, L"IL_%08X: ", index);

	BYTE b = source[index];
	index++;
	if(IsPrefix(b))
	{
		b = source[index];
		index++;
		current = (opcode)twoByteOpcodes[b];
	}
	else
	{
		current = (opcode)oneByteOpcodes[b];
	}

	wcscat(lBuffer, current.Instruction().c_str());
	switch(current.Type())
	{
	// Konieczność obsługi przełącznika w specjalny sposób.
	// Może istnieć wiele operandów z przełącznikiem.
	// Pierwszy operand informuje po prostu o liczbie
	// następujących po nim operandów.
	// Założono, że kod operacji ma wielkość jednego bajta.
	// Nie istnieje podwójny kod operacji,
	// dla którego wartość drugiego kodu wynosi 0x45.
	case Switch:
		switchCount = *((DWORD*)&source[index]);
		index += 4;
		operand = &source[index];
		index += switchCount * sizeof(DWORD);
		isSwitch = true;
		break;
	case Branch:
		operand = &source[index];
		index += current.OperandSize();
		isSwitch = false;
		switch(current.OperandSize())
		{
		case 1:
			wsprintf(oBuffer, L" IL_%08X", index + *operand);
			break;
		case 2:
			wsprintf(oBuffer, L" IL_%08X", index + *((USHORT *)operand));
			break;
		case 4:
			wsprintf(oBuffer, L" IL_%08X", index + *((ULONG *)operand));
			break;
		}
		wcscat(lBuffer, oBuffer);
		break;
	case Index:
		operand = &source[index];
		index += current.OperandSize();
		isSwitch = false;
		switch(current.OperandSize())
		{
		case 1:
			wsprintf(oBuffer, L" %d", *operand);
			break;
		case 2:
			wsprintf(oBuffer, L" %d", *((USHORT *)operand));
			break;
		case 4:
			wsprintf(oBuffer, L" %d", *((ULONG *)operand));
			break;
		}
		wcscat(lBuffer, oBuffer);
		break;
	case Value:
		operand = &source[index];
		index += current.OperandSize();
		isSwitch = false;
		switch(current.OperandSize())
		{
		case 1:
			wsprintf(oBuffer, L" 0x%02X", *operand);
			break;
		case 2:
			wsprintf(oBuffer, L" 0x%04X", *((USHORT *)operand));
			break;
		case 4:
			wsprintf(oBuffer, L" 0x%08X", *((ULONG *)operand));
			break;
		}
		wcscat(lBuffer, oBuffer);
		break;
	default:
		operand = &source[index];
		index += current.OperandSize();
		isSwitch = false;
		break;
	}
	instructionString = lBuffer;
	return true;
}

CCILOpcode::~CCILOpcode(void)
{
}
