/* ILEngineer - Crummy .NET Decompiler
 * Copyright (C) 2001-2002  Jay Freeman (saurik)
*/

/*
 *        Redistribution and use in source and binary
 * forms, with or without modification, are permitted
 * provided that the following conditions are met:
 * 
 * 1. Redistributions of source code must retain the
 *    above copyright notice, this list of conditions
 *    and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the
 *    above copyright notice, this list of conditions
 *    and the following disclaimer in the documentation
 *    and/or other materials provided with the
 *    distribution.
 * 3. The name of the author may not be used to endorse
 *    or promote products derived from this software
 *    without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS''
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#include "stdafx.h"
#include "ilengineer/Block.h"

#include "ilengineer/Blocks/Catch.h"
#include "ilengineer/Blocks/Finally.h"
#include "ilengineer/Blocks/Method.h"
#include "ilengineer/Blocks/Try.h"

#include "ilengineer/Ops/MSIL.h"

#include "ilengineer/ParseData/Operations.h"
#include "ilengineer/ParseData/Operators.h"

#include <sstream>

void ILEngineer::Block::Populate(uint8_t * &offset, ULONG length, Metallurgy::ILMethodImpl::ExceptionVector &exs, bool disregard) {
	uint8_t *end = offset + length, *orig = offset;
	uint8_t *current, *start = offset;

	if (isOpen) std::cout << std::endl << "< (" << (void *) offset << ':' << length << ") : populate >" << std::endl;

	#define Push(op) \
		if (isOpen) std::cout << "<--: - - >" << std::endl << std::endl; \
		m_Statements.push_back(new Statement(getMethod(), start, op)); \
		start = offset;

	const Metallurgy::Module *import = m_Method->getImport();
	const Metallurgy::Method &def = m_Method->getMethodDef();
	const Metallurgy::ILMethodImpl *impl = dynamic_cast<const Metallurgy::ILMethodImpl *>(def.getImpl());
	const Metallurgy::Signature *sig = def.getSignature();
	const Metallurgy::ILMethodImpl::Exception *except;
	Metallurgy::ILMethodImpl::Exception *ex;

	Stack stack;

	while (offset < end) {
		if (!disregard && !m_Statements.empty() && (except = impl->isHandler(offset)) != NULL) {
			Statement *hope = m_Statements.back();

			if (dynamic_cast<Blocks::Try *>(hope) == NULL && dynamic_cast<Blocks::Catch *>(hope) == NULL) {
				std::cerr << std::endl << "Please don't tell me this is C#... please?" << std::endl;
				exit(1);
			}

			if (except->Flags & Chordata::ILMethod::Section::Exception::Flags::Finally) {
				if (isOpen) std::cout << std::endl << "< (" << (void *) except->TryOffset << ':' << (int) except->TryLength << ") / (" << (void *) except->HandlerOffset << ':' << (int) except->HandlerLength << ") [" << except->Flags << "]: finally >";
				//triumph->AddFinally(new Blocks::Finally(m_Method, offset, ex, exs));
				m_Statements.push_back(new Blocks::Finally(m_Method, offset, except, exs));
			} else {
				if (isOpen) std::cout << std::endl << "< (" << (void *) except->TryOffset << ':' << (int) except->TryLength << ") / (" << (void *) except->HandlerOffset << ':' << (int) except->HandlerLength << ") [" << except->Flags << "]: catch >";
				//triumph->AddCatch(new Blocks::Catch(m_Method, offset, ex, exs));*
				m_Statements.push_back(new Blocks::Catch(m_Method, offset, except, exs));
			}

			start = offset;
			disregard = false;
			continue;
		} else if ((ex = impl->remException(exs, offset)) != NULL) {
			Metallurgy::ILMethodImpl::Exception *old = NULL;
			if (typeid(*this) == typeid(Blocks::Try))
				old = dynamic_cast<Blocks::Try *>(this)->getException();
			if (isOpen) std::cout << std::endl << "< (" << (void *) ex->TryOffset << ':' << (int) ex->TryLength << ") / (" << (void *) ex->HandlerOffset << ':' << (int) ex->HandlerLength << ") [" << ex->Flags << "]: try >";
			if (old == NULL || old->TryOffset != ex->TryOffset || old->TryLength != ex->TryLength) {
				m_Statements.push_back(new Blocks::Try(m_Method, offset, ex, exs));
				start = offset;
				disregard = false;
				continue;
			} else
				delete ex;
		}

		if (isOpen && *offset != 0xFE)
			std::cout << "< (" << (void *) offset << ") " << std::setbase(16) << std::setw(2) << (int) *offset << std::setbase(10) << ": " << stack.size() << " - " << Data::GlobalTable[*offset].name << ">" << std::endl;
		switch (*(current = offset++)) {

			case 0x00: { // nop
			} break;
			case 0x01: { // break
				Push(new Ops::MSIL::Break(m_Method, current));
			} break;
			case 0x02: { // ldarg.0
				stack.push(new Ops::MSIL::LoadArgument(m_Method, current, 0));
				if (isOpen) {
					if (sig->getHasThis())
						std::wcout << L"  { this }" << std::endl;
					else
						std::wcout << L"  { " << def.getArgName(0) << L" }" << std::endl;
				}
			} break;
			case 0x03: { // ldarg.1
				stack.push(new Ops::MSIL::LoadArgument(m_Method, current, 1));
				if (isOpen) std::wcout << L"  { " << def.getArgName(1) << L" }" << std::endl;
			} break;
			case 0x04: { // ldarg.2
				stack.push(new Ops::MSIL::LoadArgument(m_Method, current, 2));
				if (isOpen) std::wcout << L"  { " << def.getArgName(2) << L" }" << std::endl;
			} break;
			case 0x05: { // ldarg.3
				stack.push(new Ops::MSIL::LoadArgument(m_Method, current, 3));
				if (isOpen) std::wcout << L"  { " << def.getArgName(3) << L" }" << std::endl;
			} break;
			case 0x06: { // ldloc.0
				stack.push(new Ops::MSIL::LoadLocal(m_Method, current, 0));
			} break;
			case 0x07: { // ldloc.1
				stack.push(new Ops::MSIL::LoadLocal(m_Method, current, 1));
			} break;
			case 0x08: { // ldloc.2
				stack.push(new Ops::MSIL::LoadLocal(m_Method, current, 2));
			} break;
			case 0x09: { // ldloc.3
				stack.push(new Ops::MSIL::LoadLocal(m_Method, current, 3));
			} break;
			case 0x0A: { // stloc.0
				Operation *value = stack.top(); stack.pop();
				Push(new Ops::MSIL::StoreLocal(m_Method, current, value, 0));
			} break;
			case 0x0B: { // stloc.1
				Operation *value = stack.top(); stack.pop();
				Push(new Ops::MSIL::StoreLocal(m_Method, current, value, 1));
			} break;
			case 0x0C: { // stloc.2
				Operation *value = stack.top(); stack.pop();
				Push(new Ops::MSIL::StoreLocal(m_Method, current, value, 2));
			} break;
			case 0x0D: { // stloc.3
				Operation *value = stack.top(); stack.pop();
				Push(new Ops::MSIL::StoreLocal(m_Method, current, value, 3));
			} break;
			case 0x0E: { // ldarg.s <U1>
				stack.push(new Ops::MSIL::LoadArgument(m_Method, current, *(offset++)));
				if (isOpen) std::wcout << L"  { " << def.getArgName(*(offset - 1)) << L" }" << std::endl;
			} break;
			case 0x0F: { // ldarga.s <U1>
				stack.push(new Ops::MSIL::LoadArgumentAddress(m_Method, current, *(offset++)));
				if (isOpen) std::wcout << L"  { " << def.getArgName(*(offset - 1)) << L" }" << std::endl;
			} break;
			case 0x10: { // starg.s <U1>
				Operation *value = stack.top(); stack.pop();
				if (isOpen) std::wcout << L"  { " << def.getArgName(*offset) << L" }" << std::endl;
				Push(new Ops::MSIL::StoreArgument(m_Method, current, *(offset++), value));
			} break;
			case 0x11: { // ldloc.s <U1>
				stack.push(new Ops::MSIL::LoadLocal(m_Method, current, *(offset++)));
			} break;
			case 0x12: { // ldloca.s <U1>
				stack.push(new Ops::MSIL::LoadLocalAddress(m_Method, current, *(offset++)));
			} break;
			case 0x13: { // stloc.s <U1>
				Operation *value = stack.top(); stack.pop();
				Push(new Ops::MSIL::StoreLocal(m_Method, current, value, *(offset++)));
			} break;
			case 0x14: { // ldnull
				stack.push(new Ops::MSIL::LoadNull(m_Method, current));
			} break;
			case 0x15: { // ldc.i4.m1
				stack.push(new Ops::MSIL::LoadI4Constant(m_Method, current, -1));
			} break;
			case 0x16: { // ldc.i4.0
				stack.push(new Ops::MSIL::LoadI4Constant(m_Method, current, 0));
			} break;
			case 0x17: { // ldc.i4.1
				stack.push(new Ops::MSIL::LoadI4Constant(m_Method, current, 1));
			} break;
			case 0x18: { // ldc.i4.2
				stack.push(new Ops::MSIL::LoadI4Constant(m_Method, current, 2));
			} break;
			case 0x19: { // ldc.i4.3
				stack.push(new Ops::MSIL::LoadI4Constant(m_Method, current, 3));
			} break;
			case 0x1A: { // ldc.i4.4
				stack.push(new Ops::MSIL::LoadI4Constant(m_Method, current, 4));
			} break;
			case 0x1B: { // ldc.i4.5
				stack.push(new Ops::MSIL::LoadI4Constant(m_Method, current, 5));
			} break;
			case 0x1C: { // ldc.i4.6
				stack.push(new Ops::MSIL::LoadI4Constant(m_Method, current, 6));
			} break;
			case 0x1D: { // ldc.i4.7
				stack.push(new Ops::MSIL::LoadI4Constant(m_Method, current, 7));
			} break;
			case 0x1E: { // ldc.i4.8
				stack.push(new Ops::MSIL::LoadI4Constant(m_Method, current, 8));
			} break;
			case 0x1F: { // ldc.i4.s
				stack.push(new Ops::MSIL::LoadI4Constant(m_Method, current, * (int8_t *) offset++));
			} break;
			case 0x20: { // ldc.i4
				stack.push(new Ops::MSIL::LoadI4Constant(m_Method, current, * (int32_t *) offset));
				offset += 4;
			} break;
			case 0x21: { // ldc.i8
				stack.push(new Ops::MSIL::LoadI8Constant(m_Method, current, * (__int64 *) offset));
				offset += 8;
			} break;
			case 0x22: { // ldc.r4
				stack.push(new Ops::MSIL::LoadR4Constant(m_Method, current, * (float *) offset));
				offset += 4;
			} break;
			case 0x23: { // ldc.r8
				stack.push(new Ops::MSIL::LoadR8Constant(m_Method, current, * (double *) offset));
				offset += 8;
			} break;
			case 0x25: { // dup
				Operation *value = stack.top(); stack.pop();
				stack.push(new Ops::MSIL::Duplicate(m_Method, current, value, true));
				stack.push(new Ops::MSIL::Duplicate(m_Method, current, value, false));
			} break;
			case 0x26: { // pop
				Operation *op = stack.top(); stack.pop();
				Push(op);
			} break;

			/* End Brick */

			/* Start Brick */

			case 0x28:   // call <T>
			case 0x6F:   // callvirt <T>
			{
				mdToken token(* (mdToken *) offset);
				Metallurgy::Member *member = dynamic_cast<Metallurgy::Member *>(import->ResolveToken(token));
				const Metallurgy::Signature *signature = member->getSignature();

				if (isOpen)
					std::wcout << L"  { " << member->getName().getFull() << L"() }" << std::endl;
				offset += 4;

				Stack args;

				for (ULONG i(0); i < signature->getCount(); i++) {
					args.push(stack.top()); stack.pop();
				}

				if (signature->getHasThis()) {
					args.push(stack.top()); stack.pop();
				}

				Ops::MSIL::Call *call = new Ops::MSIL::Call(m_Method, current, token, args);
				if (signature->getReturn()->isVoid()) {
					Push(call);
				} else
					stack.push(call);
			} break;
			case 0x29: { // calli <T>
				/*Ops::MSIL::CallIndirect *calli = new Ops::MSIL::CallIndirect(m_Method, current, token, args);
				if (signature->getReturn()->isVoid()) {
					Push(calli);
				} else
					stack.push(calli);*/
			} break;
			case 0x2A: { // ret
				if (sig->getReturn()->isVoid()) {
					Push(new Ops::MSIL::Return(m_Method, current));
				} else {
					Operation *retVal = stack.top(); stack.pop();
					Push(new Ops::MSIL::ReturnValue(m_Method, current, retVal));
				}
			} break;
			case 0x2B: { // br.s <I1>
				if (isOpen) std::cout << "  { " << (void *) (current + 2 + * (int8_t *) offset) << " }" << std::endl;
				Push(new Ops::MSIL::Branch(m_Method, current, current + 2 + * (int8_t *) (offset++)));
			} break;
			case 0x2C: { // brfalse.s <I1>
				Operation *value = stack.top(); stack.pop();
				Push(new Ops::MSIL::BranchFalse(m_Method, current, value, current + 2 + * (int8_t *) (offset++)));
			} break;
			case 0x2D: { // brtrue.s <I1>
				Operation *value = stack.top(); stack.pop();
				Push(new Ops::MSIL::BranchTrue(m_Method, current, value, current + 2 + * (int8_t *) (offset++)));
			} break;
			case 0x2E: { // beq.s <I1>
				Operation *value2 = stack.top(); stack.pop();
				Operation *value1 = stack.top(); stack.pop();
				Ops::MSIL::CompareEqual *cmp = new Ops::MSIL::CompareEqual(m_Method, current, value1, value2);
				Push(new Ops::MSIL::BranchTrue(m_Method, current, cmp, current + 2 + * (int8_t *) (offset++)));
			} break;
			case 0x2F: { // bge.s <I1>
				Operation *value2 = stack.top(); stack.pop();
				Operation *value1 = stack.top(); stack.pop();
				Ops::MSIL::CompareLessThan *cmp = new Ops::MSIL::CompareLessThan(m_Method, current, value1, value2);
				Push(new Ops::MSIL::BranchFalse(m_Method, current, cmp, current + 2 + * (int8_t *) (offset++)));
			} break;
			case 0x30: { // bgt.s <I1>
				Operation *value2 = stack.top(); stack.pop();
				Operation *value1 = stack.top(); stack.pop();
				Ops::MSIL::CompareGreaterThan *cmp = new Ops::MSIL::CompareGreaterThan(m_Method, current, value1, value2);
				Push(new Ops::MSIL::BranchTrue(m_Method, current, cmp, current + 2 + * (int8_t *) (offset++)));
			} break;
			case 0x31: { // ble.s <I1>
				Operation *value2 = stack.top(); stack.pop();
				Operation *value1 = stack.top(); stack.pop();
				Ops::MSIL::CompareGreaterThan *cmp = new Ops::MSIL::CompareGreaterThan(m_Method, current, value1, value2);
				Push(new Ops::MSIL::BranchFalse(m_Method, current, cmp, current + 2 + * (int8_t *) (offset++)));
			} break;
			case 0x32: { // blt.s <I1>
				Operation *value2 = stack.top(); stack.pop();
				Operation *value1 = stack.top(); stack.pop();
				Ops::MSIL::CompareLessThan *cmp = new Ops::MSIL::CompareLessThan(m_Method, current, value1, value2);
				Push(new Ops::MSIL::BranchTrue(m_Method, current, cmp, current + 2 + * (int8_t *) (offset++)));
			} break;
			case 0x33: { // bne.un.s <I1>
				Operation *value2 = stack.top(); stack.pop();
				Operation *value1 = stack.top(); stack.pop();
				Ops::MSIL::CompareEqual *cmp = new Ops::MSIL::CompareEqual(m_Method, current, value1, value2);
				Push(new Ops::MSIL::BranchFalse(m_Method, current, cmp, current + 2 + * (int8_t *) (offset++)));
			} break;
			case 0x34: { // bge.un.s <I1>
				Operation *value2 = stack.top(); stack.pop();
				Operation *value1 = stack.top(); stack.pop();
				Ops::MSIL::CompareLessThan *cmp = new Ops::MSIL::CompareLessThan(m_Method, current, value1, value2);
				Push(new Ops::MSIL::BranchFalse(m_Method, current, cmp, current + 2 + * (int8_t *) (offset++)));
			} break;
			case 0x35: { // bgt.un.s <I1>
				Operation *value2 = stack.top(); stack.pop();
				Operation *value1 = stack.top(); stack.pop();
				Ops::MSIL::CompareGreaterThan *cmp = new Ops::MSIL::CompareGreaterThan(m_Method, current, value1, value2);
				Push(new Ops::MSIL::BranchTrue(m_Method, current, cmp, current + 2 + * (int8_t *) (offset++)));
			} break;
			case 0x36: { // ble.un.s <I1>
				Operation *value2 = stack.top(); stack.pop();
				Operation *value1 = stack.top(); stack.pop();
				Ops::MSIL::CompareGreaterThan *cmp = new Ops::MSIL::CompareGreaterThan(m_Method, current, value1, value2);
				Push(new Ops::MSIL::BranchFalse(m_Method, current, cmp, current + 2 + * (int8_t *) (offset++)));
			} break;
			case 0x37: { // blt.un.s <I1>
				Operation *value2 = stack.top(); stack.pop();
				Operation *value1 = stack.top(); stack.pop();
				Ops::MSIL::CompareLessThan *cmp = new Ops::MSIL::CompareLessThan(m_Method, current, value1, value2);
				Push(new Ops::MSIL::BranchTrue(m_Method, current, cmp, current + 2 + * (int8_t *) (offset++)));
			} break;
			case 0x38: { // br <I4>
				Ops::MSIL::Branch *branch = new Ops::MSIL::Branch(m_Method, current, current + 5 + * (int32_t *) offset);
				offset += 4;
				Push(branch);
			} break;
			case 0x39: { // brfalse <I4>
				Operation *value = stack.top(); stack.pop();
				Ops::MSIL::BranchFalse *branch = new Ops::MSIL::BranchFalse(m_Method, current, value, current + 5 + * (int32_t *) offset);
				offset += 4;
				Push(branch);
			} break;
			case 0x3A: { // brtrue <I4>
				Operation *value = stack.top(); stack.pop();
				Ops::MSIL::BranchTrue *branch = new Ops::MSIL::BranchTrue(m_Method, current, value, current + 5 + * (int32_t *) offset);
				offset += 4;
				Push(branch);
			} break;
			case 0x3B: { // beq <I4>
				Operation *value2 = stack.top(); stack.pop();
				Operation *value1 = stack.top(); stack.pop();
				Ops::MSIL::CompareEqual *cmp = new Ops::MSIL::CompareEqual(m_Method, current, value1, value2);
				Ops::MSIL::BranchTrue *branch = new Ops::MSIL::BranchTrue(m_Method, current, cmp, current + 5 + * (int32_t *) offset);
				offset += 4;
				Push(branch);
			} break;
			case 0x3C: { // bge <I4>
				Operation *value2 = stack.top(); stack.pop();
				Operation *value1 = stack.top(); stack.pop();
				Ops::MSIL::CompareLessThan *cmp = new Ops::MSIL::CompareLessThan(m_Method, current, value1, value2);
				Ops::MSIL::BranchFalse *branch = new Ops::MSIL::BranchFalse(m_Method, current, cmp, current + 5 + * (int32_t *) offset);
				offset += 4;
				Push(branch);
			} break;
			case 0x3D: { // bgt <I4>
				Operation *value2 = stack.top(); stack.pop();
				Operation *value1 = stack.top(); stack.pop();
				Ops::MSIL::CompareGreaterThan *cmp = new Ops::MSIL::CompareGreaterThan(m_Method, current, value1, value2);
				Ops::MSIL::BranchTrue *branch = new Ops::MSIL::BranchTrue(m_Method, current, cmp, current + 5 + * (int32_t *) offset);
				offset += 4;
				Push(branch);
			} break;
			case 0x3E: { // ble <I4>
				Operation *value2 = stack.top(); stack.pop();
				Operation *value1 = stack.top(); stack.pop();
				Ops::MSIL::CompareGreaterThan *cmp = new Ops::MSIL::CompareGreaterThan(m_Method, current, value1, value2);
				Ops::MSIL::BranchFalse *branch = new Ops::MSIL::BranchFalse(m_Method, current, cmp, current + 5 + * (int32_t *) offset);
				offset += 4;
				Push(branch);
			} break;
			case 0x3F: { // blt <I4>
				Operation *value2 = stack.top(); stack.pop();
				Operation *value1 = stack.top(); stack.pop();
				Ops::MSIL::CompareLessThan *cmp = new Ops::MSIL::CompareLessThan(m_Method, current, value1, value2);
				Ops::MSIL::BranchTrue *branch = new Ops::MSIL::BranchTrue(m_Method, current, cmp, current + 5 + * (int32_t *) offset);
				offset += 4;
				Push(branch);
			} break;
			case 0x40: { // bne.un <I4>
				Operation *value2 = stack.top(); stack.pop();
				Operation *value1 = stack.top(); stack.pop();
				Ops::MSIL::CompareEqual *cmp = new Ops::MSIL::CompareEqual(m_Method, current, value1, value2);
				Ops::MSIL::BranchFalse *branch = new Ops::MSIL::BranchFalse(m_Method, current, cmp, current + 5 + * (int32_t *) offset);
				offset += 4;
				Push(branch);
			} break;
			case 0x41: { // bge.un <I4>
				Operation *value2 = stack.top(); stack.pop();
				Operation *value1 = stack.top(); stack.pop();
				Ops::MSIL::CompareLessThan *cmp = new Ops::MSIL::CompareLessThan(m_Method, current, value1, value2);
				Ops::MSIL::BranchFalse *branch = new Ops::MSIL::BranchFalse(m_Method, current, cmp, current + 5 + * (int32_t *) offset);
				offset += 4;
				Push(branch);
			} break;
			case 0x42: { // bgt.un <I4>
				Operation *value2 = stack.top(); stack.pop();
				Operation *value1 = stack.top(); stack.pop();
				Ops::MSIL::CompareGreaterThan *cmp = new Ops::MSIL::CompareGreaterThan(m_Method, current, value1, value2);
				Ops::MSIL::BranchTrue *branch = new Ops::MSIL::BranchTrue(m_Method, current, cmp, current + 5 + * (int32_t *) offset);
				offset += 4;
				Push(branch);
			} break;
			case 0x43: { // ble.un <I4>
				Operation *value2 = stack.top(); stack.pop();
				Operation *value1 = stack.top(); stack.pop();
				Ops::MSIL::CompareGreaterThan *cmp = new Ops::MSIL::CompareGreaterThan(m_Method, current, value1, value2);
				Ops::MSIL::BranchFalse *branch = new Ops::MSIL::BranchFalse(m_Method, current, cmp, current + 5 + * (int32_t *) offset);
				offset += 4;
				Push(branch);
			} break;
			case 0x44: { // blt.un <I4>
				Operation *value2 = stack.top(); stack.pop();
				Operation *value1 = stack.top(); stack.pop();
				Ops::MSIL::CompareLessThan *cmp = new Ops::MSIL::CompareLessThan(m_Method, current, value1, value2);
				Ops::MSIL::BranchTrue *branch = new Ops::MSIL::BranchTrue(m_Method, current, cmp, current + 5 + * (int32_t *) offset);
				offset += 4;
				Push(branch);
			} break;
			case 0x45: { // switch <U4> <I4>... <I4>
				Operation *value = stack.top(); stack.pop();
				ULONG many = * (ULONG *) offset;
				offset += 4;
				Push(new Ops::MSIL::Switch(m_Method, current, offset, many, value));
			} break;
			case 0x46:   // ldind.i1
			case 0x47:   // ldind.u1
			case 0x48:   // ldind.i2
			case 0x49:   // ldind.u2
			case 0x4A:   // ldind.i4
			case 0x4B:   // ldind.u4
			case 0x4C:   // ldind.i8
			case 0x4D:   // ldind.i
			case 0x4E:   // ldind.r4
			case 0x4F:   // ldind.r8
			case 0x50:   // ldind.ref
			{
				// XXX: Anyone know what this does?
				// Seriously... doing NOTHING seems
				// to be the "right" thing to do...
			} break;
			case 0x51: { // stind.ref
				Operation *val = stack.top(); stack.pop();
				Operation *addr = stack.top(); stack.pop();
				Push(new Ops::MSIL::StoreIndirectRef(m_Method, current, addr, val));
			} break;
			case 0x52: { // stind.i1
				Operation *val = stack.top(); stack.pop();
				Operation *addr = stack.top(); stack.pop();
				Push(new Ops::MSIL::StoreIndirectI1(m_Method, current, addr, val));
			} break;
			case 0x53: { // stind.i2
				Operation *val = stack.top(); stack.pop();
				Operation *addr = stack.top(); stack.pop();
				Push(new Ops::MSIL::StoreIndirectI2(m_Method, current, addr, val));
			} break;
			case 0x54: { // stind.i4
				Operation *val = stack.top(); stack.pop();
				Operation *addr = stack.top(); stack.pop();
				Push(new Ops::MSIL::StoreIndirectI4(m_Method, current, addr, val));
			} break;

			/* End Brick */

			/* Start Brick */

			case 0x58: { // add
				Operation *value2 = stack.top(); stack.pop();
				Operation *value1 = stack.top(); stack.pop();
				stack.push(new Ops::MSIL::Add(m_Method, current, value1, value2));
			} break;
			case 0x59: { // sub
				Operation *value2 = stack.top(); stack.pop();
				Operation *value1 = stack.top(); stack.pop();
				stack.push(new Ops::MSIL::Sub(m_Method, current, value1, value2));
			} break;
			case 0x5A: { // mul
				Operation *value2 = stack.top(); stack.pop();
				Operation *value1 = stack.top(); stack.pop();
				stack.push(new Ops::MSIL::Mul(m_Method, current, value1, value2));
			} break;
			case 0x5B: { // div
				Operation *value2 = stack.top(); stack.pop();
				Operation *value1 = stack.top(); stack.pop();
				stack.push(new Ops::MSIL::Div(m_Method, current, value1, value2));
			} break;

			/* End Brick */

			case 0x5D: { // rem
				Operation *value2 = stack.top(); stack.pop();
				Operation *value1 = stack.top(); stack.pop();
				stack.push(new Ops::MSIL::Rem(m_Method, current, value1, value2));
			} break;

			/* Start Brick */

			case 0x5F: { // and
				Operation *value2 = stack.top(); stack.pop();
				Operation *value1 = stack.top(); stack.pop();
				stack.push(new Ops::MSIL::And(m_Method, current, value1, value2));
			} break;
			case 0x60: { // or
				Operation *value2 = stack.top(); stack.pop();
				Operation *value1 = stack.top(); stack.pop();
				stack.push(new Ops::MSIL::Or(m_Method, current, value1, value2));
			} break;
			case 0x61: { // xor
				Operation *value2 = stack.top(); stack.pop();
				Operation *value1 = stack.top(); stack.pop();
				stack.push(new Ops::MSIL::XOr(m_Method, current, value1, value2));
			} break;
			case 0x62: { // shl
				Operation *amount = stack.top(); stack.pop();
				Operation *value = stack.top(); stack.pop();
				stack.push(new Ops::MSIL::ShL(m_Method, current, value, amount));
			} break;
			case 0x63: { // shr
				Operation *amount = stack.top(); stack.pop();
				Operation *value = stack.top(); stack.pop();
				stack.push(new Ops::MSIL::ShR(m_Method, current, value, amount, Chordata::Element::U));
			} break;
			case 0x64: { // shr.un
				Operation *amount = stack.top(); stack.pop();
				Operation *value = stack.top(); stack.pop();
				stack.push(new Ops::MSIL::ShR(m_Method, current, value, amount, Chordata::Element::U4));
			} break;
			case 0x65: { // neg
				Operation *value = stack.top(); stack.pop();
				stack.push(new Ops::MSIL::Neg(m_Method, current, value));
			} break;
			case 0x66: { // not
				Operation *value = stack.top(); stack.pop();
				stack.push(new Ops::MSIL::Not(m_Method, current, value));
			} break;
			case 0x67: { // conv.i1
				Operation *value = stack.top(); stack.pop();
				stack.push(new Ops::MSIL::Convert(m_Method, current, value, Chordata::Element::I1));
			} break;
			case 0x68: { // conv.i2
				Operation *value = stack.top(); stack.pop();
				stack.push(new Ops::MSIL::Convert(m_Method, current, value, Chordata::Element::I2));
			} break;
			case 0x69: { // conv.i4
				Operation *value = stack.top(); stack.pop();
				stack.push(new Ops::MSIL::Convert(m_Method, current, value, Chordata::Element::I4));
			} break;
			case 0x6A: { // conv.i8
				Operation *value = stack.top(); stack.pop();
				stack.push(new Ops::MSIL::Convert(m_Method, current, value, Chordata::Element::I8));
			} break;
			case 0x6B: { // conv.r4
				Operation *value = stack.top(); stack.pop();
				stack.push(new Ops::MSIL::Convert(m_Method, current, value, Chordata::Element::R4));
			} break;
			case 0x6C: { // conv.r8
				Operation *value = stack.top(); stack.pop();
				stack.push(new Ops::MSIL::Convert(m_Method, current, value, Chordata::Element::R8));
			} break;
			case 0x6D: { // conv.i4
				Operation *value = stack.top(); stack.pop();
				stack.push(new Ops::MSIL::Convert(m_Method, current, value, Chordata::Element::I4));
			} break;
			case 0x6E: { // conv.u8
				Operation *value = stack.top(); stack.pop();
				stack.push(new Ops::MSIL::Convert(m_Method, current, value, Chordata::Element::U8));
			} break;

			/* End Brick */

			case 0x71: { // ldobj
				mdToken type(* (mdToken *) offset);
				offset += 4;

				// XXX: Anyone know what this does?
				// Seriously... doing NOTHING seems
				// to be the "right" thing to do...
			} break;
			case 0x72: { // ldstr
				stack.push(new Ops::MSIL::LoadString(m_Method, current, * (mdToken *) offset));
				offset += 4;
			} break;
			case 0x73: { // newobj <T>
				Metallurgy::Member *member = dynamic_cast<Metallurgy::Member *>(import->ResolveToken(* (mdToken *) offset));
				Ops::MSIL::NewObject *newobj = new Ops::MSIL::NewObject(m_Method, current, * (mdToken *) offset);
				const Metallurgy::Signature *signature = member->getSignature();

                const Metallurgy::IName *name = dynamic_cast<const Metallurgy::IName *>(member->getType());
				if (isOpen)
					std::wcout << L"  { " << name->getName().getShort() << L" }" << std::endl;

				Stack args;

				for (ULONG i(0); i < signature->getCount(); i++) {
					args.push(stack.top()); stack.pop();
				}

				while (args.size()) {
					newobj->getOperations().push_back(args.top()); args.pop();
				}

				offset += 4;
				stack.push(newobj);
			} break;
			case 0x74: { // castclass <T>
				Operation *value = stack.top(); stack.pop();
				stack.push(new Ops::MSIL::CastClass(m_Method, current, value, * (mdToken *) offset));
				offset += 4;
			} break;
			case 0x75: { // isinst <T>
				Operation *value = stack.top(); stack.pop();
				stack.push(new Ops::MSIL::IsInstance(m_Method, current, value, * (mdToken *) offset));
				offset += 4;
			} break;
			case 0x79: { // unbox <T>
				Operation *value = stack.top(); stack.pop();
				stack.push(new Ops::MSIL::Unbox(m_Method, current, value, * (mdToken *) offset));
				offset += 4;
			} break;
			case 0x7A: { // throw
				Operation *object = stack.top(); stack.pop();
				Push(new Ops::MSIL::Throw(m_Method, current, object));
			} break;
			case 0x7B: { // ldfld <T>
				Metallurgy::Member *member = dynamic_cast<Metallurgy::Member *>(import->ResolveToken(* (mdToken *) offset));
				if (isOpen)
					std::wcout << L"  { " << member->getName().getFull() << L" }" << std::endl;
				Operation *obj = stack.top(); stack.pop();
				stack.push(new Ops::MSIL::LoadField(m_Method, current, obj, * (mdToken *) offset));
				offset += 4;
			} break;
			case 0x7C: { // ldflda <T>
				Metallurgy::Member *member = dynamic_cast<Metallurgy::Member *>(import->ResolveToken(* (mdToken *) offset));
				if (isOpen)
					std::wcout << L"  { " << member->getName().getFull() << L" }" << std::endl;
				Operation *obj = stack.top(); stack.pop();
				stack.push(new Ops::MSIL::LoadFieldAddress(m_Method, current, obj, * (mdToken *) offset));
				offset += 4;
			} break;
			case 0x7D: { // stfld <T>
				Operation *value = stack.top(); stack.pop();
				Operation *obj = stack.top(); stack.pop();

				mdToken field(* (mdToken *) offset);
				offset += 4;

				Push(new Ops::MSIL::StoreField(m_Method, current, obj, value, field));
			} break;
			case 0x7E: { // ldsfld <T>
				Metallurgy::Member *member = dynamic_cast<Metallurgy::Member *>(import->ResolveToken(* (mdToken *) offset));
				stack.push(new Ops::MSIL::LoadStaticField(m_Method, current, * (mdToken *) offset));
				offset += 4;
			} break;
			case 0x7F: { // ldsflda <T>
				Metallurgy::Member *member = dynamic_cast<Metallurgy::Member *>(import->ResolveToken(* (mdToken *) offset));
				stack.push(new Ops::MSIL::LoadStaticFieldAddress(m_Method, current, * (mdToken *) offset));
				offset += 4;
			} break;
			case 0x80: { // stsfld <T>
				Operation *value = stack.top(); stack.pop();

				mdToken field(* (mdToken *) offset);
				offset += 4;

				Push(new Ops::MSIL::StoreStaticField(m_Method, current, value, field));
			} break;
			case 0x81: { // stobj <T>
				Operation *value = stack.top(); stack.pop();
				Operation *addr = stack.top(); stack.pop();
				mdToken field(* (mdToken *) offset); offset += 4;

				Push(new Ops::MSIL::StoreObject(m_Method, current, addr, value, field));
			} break;

			/* Start Brick */

			case 0x8C: { // box <T>
				Operation *value = stack.top(); stack.pop();
				stack.push(new Ops::MSIL::Box(m_Method, current, value, * (mdToken *) offset));
				offset += 4;
			} break;
			case 0x8D: { // newarr <T>
				Operation *numElems = stack.top(); stack.pop();
				stack.push(new Ops::MSIL::NewArray(m_Method, current, numElems, * (mdToken *) offset));
				offset += 4;
			} break;
			case 0x8E: { // ldlen
				Operation *array = stack.top(); stack.pop();
				stack.push(new Ops::MSIL::LoadLength(m_Method, current, array));
			} break;
			case 0x8F: { // ldelema <T>
				Operation *index = stack.top(); stack.pop();
				Operation *array = stack.top(); stack.pop();
				mdToken type(* (mdToken *) offset); offset += 4;

				stack.push(new Ops::MSIL::LoadElementAddress(m_Method, current, array, index, type));
			} break;
			case 0x90: { // ldelem.i1
				Operation *index = stack.top(); stack.pop();
				Operation *array = stack.top(); stack.pop();
				stack.push(new Ops::MSIL::LoadElement(m_Method, current, array, index, Chordata::Element::I1));
			} break;
			case 0x91: { // ldelem.u1
				Operation *index = stack.top(); stack.pop();
				Operation *array = stack.top(); stack.pop();
				stack.push(new Ops::MSIL::LoadElement(m_Method, current, array, index, Chordata::Element::U1));
			} break;
			case 0x92: { // ldelem.i2
				Operation *index = stack.top(); stack.pop();
				Operation *array = stack.top(); stack.pop();
				stack.push(new Ops::MSIL::LoadElement(m_Method, current, array, index, Chordata::Element::I2));
			} break;
			case 0x93: { // ldelem.u2
				Operation *index = stack.top(); stack.pop();
				Operation *array = stack.top(); stack.pop();
				stack.push(new Ops::MSIL::LoadElement(m_Method, current, array, index, Chordata::Element::U2));
			} break;
			case 0x94: { // ldelem.i4
				Operation *index = stack.top(); stack.pop();
				Operation *array = stack.top(); stack.pop();
				stack.push(new Ops::MSIL::LoadElement(m_Method, current, array, index, Chordata::Element::I4));
			} break;
			case 0x95: { // ldelem.u4
				Operation *index = stack.top(); stack.pop();
				Operation *array = stack.top(); stack.pop();
				stack.push(new Ops::MSIL::LoadElement(m_Method, current, array, index, Chordata::Element::U4));
			} break;
			case 0x96: { // ldelem.i8
				Operation *index = stack.top(); stack.pop();
				Operation *array = stack.top(); stack.pop();
				stack.push(new Ops::MSIL::LoadElement(m_Method, current, array, index, Chordata::Element::I8));
			} break;
			case 0x97: { // ldelem.i
				Operation *index = stack.top(); stack.pop();
				Operation *array = stack.top(); stack.pop();
				stack.push(new Ops::MSIL::LoadElement(m_Method, current, array, index, Chordata::Element::I));
			} break;
			case 0x98: { // ldelem.r4
				Operation *index = stack.top(); stack.pop();
				Operation *array = stack.top(); stack.pop();
				stack.push(new Ops::MSIL::LoadElement(m_Method, current, array, index, Chordata::Element::R4));
			} break;
			case 0x99: { // ldelem.r8
				Operation *index = stack.top(); stack.pop();
				Operation *array = stack.top(); stack.pop();
				stack.push(new Ops::MSIL::LoadElement(m_Method, current, array, index, Chordata::Element::R8));
			} break;
			case 0x9A: { // ldelem.ref
				Operation *index = stack.top(); stack.pop();
				Operation *array = stack.top(); stack.pop();
				stack.push(new Ops::MSIL::LoadElement(m_Method, current, array, index, Chordata::Element::Object));
			} break;
			case 0x9B: // stelem.i
			case 0x9C: // stelem.i1
			case 0x9D: // stelem.i2
			case 0x9E: // stelem.i4
			case 0x9F: // stelem.i8
			case 0xA0: // stelem.r4
			case 0xA1: // stelem.r8
			case 0xA2: // stelem.ref
			{
				Operation *value = stack.top(); stack.pop();
				Operation *index = stack.top(); stack.pop();
				Operation *array = stack.top(); stack.pop();
				Push(new Ops::MSIL::StoreElement(m_Method, current, array, index, value));
			} break;
			case 0xB3: { // conv.ovf.i1
				Operation *value = stack.top(); stack.pop();
				stack.push(new Ops::MSIL::Convert(m_Method, current, value, Chordata::Element::I1, true));
			} break;
			case 0xB4: { // conv.ovf.u1
				Operation *value = stack.top(); stack.pop();
				stack.push(new Ops::MSIL::Convert(m_Method, current, value, Chordata::Element::U1, true));
			} break;
			case 0xB5: { // conv.ovf.i2
				Operation *value = stack.top(); stack.pop();
				stack.push(new Ops::MSIL::Convert(m_Method, current, value, Chordata::Element::I2, true));
			} break;
			case 0xB6: { // conv.ovf.u2
				Operation *value = stack.top(); stack.pop();
				stack.push(new Ops::MSIL::Convert(m_Method, current, value, Chordata::Element::U2, true));
			} break;
			case 0xB7: { // conv.ovf.i4
				Operation *value = stack.top(); stack.pop();
				stack.push(new Ops::MSIL::Convert(m_Method, current, value, Chordata::Element::I4, true));
			} break;
			case 0xB8: { // conv.ovf.u4
				Operation *value = stack.top(); stack.pop();
				stack.push(new Ops::MSIL::Convert(m_Method, current, value, Chordata::Element::U4, true));
			} break;
			case 0xB9: { // conv.ovf.i8
				Operation *value = stack.top(); stack.pop();
				stack.push(new Ops::MSIL::Convert(m_Method, current, value, Chordata::Element::I8, true));
			} break;
			case 0xBA: { // conv.ovf.u8
				Operation *value = stack.top(); stack.pop();
				stack.push(new Ops::MSIL::Convert(m_Method, current, value, Chordata::Element::U8, true));
			} break;

			/* End Brick */

			case 0xD0: { // ldtoken <T>
				stack.push(new Ops::MSIL::LoadToken(m_Method, current, * (mdToken *) offset));
				offset += 4;
			} break;
			case 0xD1: { // conv.u2
				Operation *value = stack.top(); stack.pop();
				stack.push(new Ops::MSIL::Convert(m_Method, current, value, Chordata::Element::U2));
			} break;
			case 0xD2: { // conv.u1
				Operation *value = stack.top(); stack.pop();
				stack.push(new Ops::MSIL::Convert(m_Method, current, value, Chordata::Element::U1));
			} break;
			case 0xD3: { // conv.i
				Operation *value = stack.top(); stack.pop();
				stack.push(new Ops::MSIL::Convert(m_Method, current, value, Chordata::Element::I));
			} break;
			case 0xD4: { // conv.ovf.i
				Operation *value = stack.top(); stack.pop();
				stack.push(new Ops::MSIL::Convert(m_Method, current, value, Chordata::Element::I, true));
			} break;
			case 0xD5: { // conv.ovf.u
				Operation *value = stack.top(); stack.pop();
				stack.push(new Ops::MSIL::Convert(m_Method, current, value, Chordata::Element::U, true));
			} break;
			case 0xD6: { // add.ovf
				Operation *value2 = stack.top(); stack.pop();
				Operation *value1 = stack.top(); stack.pop();
				stack.push(new Ops::MSIL::Add(m_Method, current, value1, value2));
			} break;
			case 0xD7: { // add.ovf.un
				Operation *value2 = stack.top(); stack.pop();
				Operation *value1 = stack.top(); stack.pop();
				stack.push(new Ops::MSIL::Add(m_Method, current, value1, value2));
			} break;
			case 0xD8: { // mul.ovf
				Operation *value2 = stack.top(); stack.pop();
				Operation *value1 = stack.top(); stack.pop();
				stack.push(new Ops::MSIL::Mul(m_Method, current, value1, value2));
			} break;
			case 0xD9: { // mul.ovf.un
				Operation *value2 = stack.top(); stack.pop();
				Operation *value1 = stack.top(); stack.pop();
				stack.push(new Ops::MSIL::Mul(m_Method, current, value1, value2));
			} break;
			case 0xDA: { // sub.ovf
				Operation *value2 = stack.top(); stack.pop();
				Operation *value1 = stack.top(); stack.pop();
				stack.push(new Ops::MSIL::Sub(m_Method, current, value1, value2));
			} break;
			case 0xDB: { // sub.ovf.un
				Operation *value2 = stack.top(); stack.pop();
				Operation *value1 = stack.top(); stack.pop();
				stack.push(new Ops::MSIL::Sub(m_Method, current, value1, value2));
			} break;
			case 0xDC: { // endfinally
				Push(new Ops::MSIL::EndFinally(m_Method, current));
			} break;
			case 0xDD: { // leave
				Push(new Ops::MSIL::Leave(m_Method, current, current + 5 + * (int32_t *) offset));
				offset += 4;
			} break;
			case 0xDE: { // leave.s
				Push(new Ops::MSIL::Leave(m_Method, current, current + 2 + * (int8_t *) (offset++)));
			} break;
			case 0xE0: { // conv.u
				Operation *value = stack.top(); stack.pop();
				stack.push(new Ops::MSIL::Convert(m_Method, current, value, Chordata::Element::U));
			} break;
			case 0xFE: 
				if (isOpen)
					std::cout << "< (" << (void *) offset << ")+" << std::setbase(16) << std::setw(2) << (int) *offset << std::setbase(10) << ": " << stack.size() << " - " << Data::Prefix1Table[*offset].name << ">" << std::endl;
				switch (*(offset++)) {
					case 0x01: { // ceq
						Operation *value2 = stack.top(); stack.pop();
						Operation *value1 = stack.top(); stack.pop();
						stack.push(new Ops::MSIL::CompareEqual(m_Method, current, value1, value2));
					} break;
					case 0x02: { // cgt
						Operation *value2 = stack.top(); stack.pop();
						Operation *value1 = stack.top(); stack.pop();
						stack.push(new Ops::MSIL::CompareGreaterThan(m_Method, current, value1, value2));
					} break;
					case 0x03: { // cgt.un
						Operation *value2 = stack.top(); stack.pop();
						Operation *value1 = stack.top(); stack.pop();
						stack.push(new Ops::MSIL::CompareGreaterThan(m_Method, current, value1, value2));
					} break;
					case 0x04: { // clt
						Operation *value2 = stack.top(); stack.pop();
						Operation *value1 = stack.top(); stack.pop();
						stack.push(new Ops::MSIL::CompareLessThan(m_Method, current, value1, value2));
					} break;
					case 0x05: { // clt.un
						Operation *value2 = stack.top(); stack.pop();
						Operation *value1 = stack.top(); stack.pop();
						stack.push(new Ops::MSIL::CompareLessThan(m_Method, current, value1, value2));
					} break;
					case 0x06: { // ldftn <T>
						stack.push(new Ops::MSIL::LoadFunction(m_Method, current, * (mdToken *) offset));
						offset += 4;
					} break;
					case 0x07: { // ldvirtfn <T>
						Operation *obj = stack.top(); stack.pop();
						stack.push(new Ops::MSIL::LoadVirtualFunction(m_Method, current, obj, * (mdToken *) offset));
						offset += 4;
					} break;
					case 0x0A: { // ldarga <U2>
						uint16_t indx(* (uint16_t *) offset); offset += 2;
						stack.push(new Ops::MSIL::LoadArgumentAddress(m_Method, current, indx));
						if (isOpen) std::wcout << L"  { " << def.getArgName(indx) << L" }" << std::endl;
					} break;
					case 0x15: { // initobj
						mdToken token(* (mdToken *) offset); offset += 4;
						Operation *addr = stack.top(); stack.pop();
						Push(new Ops::MSIL::InitObj(m_Method, current, addr, token));
					} break;
					case 0x1A: { // rethrow
						Push(new Ops::MSIL::ReThrow(m_Method, current));
					} break;
					default:
						std::wostringstream serr;
						Data::OpCode &code = Data::Prefix1Table[*(offset-1)];
						serr << L"unsupported operation: [" << std::setbase(16) << std::setw(2) << code.code << std::setbase(10) << L" = " << (code.name == NULL ? L"????" : code.name) << L"]";
						throw std::wstring(serr.str());
					break;
				}
			break;
			default: {
				std::wostringstream serr;
				Data::OpCode &code = Data::GlobalTable[*(offset-1)];
				serr << L"unsupported operation: [" << std::setbase(16) << std::setw(2) << code.code << std::setbase(10) << L" = " << (code.name == NULL ? L"????" : code.name) << L"]";
				throw std::wstring(serr.str());
			} break;
		}
	}

	if (isOpen) std::cout << "<??: ? - depopulate>" << std::endl;
}