console.c
author Darkvater
Sat, 02 Apr 2005 23:05:09 +0000
changeset 1632 10c391e108b7
parent 1588 7f0385ebdcfe
child 1725 e391da994698
permissions -rw-r--r--
(svn r2136) - Fix: [ 1174313 ] terrain hotkeys nonfunctional in scenario editor (D,Q,W,E,R,T,Y,U fltr)
- Fix: 'L' no longer opens ingame terraform bar in scenario editor bar, but the land generator one
- Feature: [ 1095110 ] Create Lake and draggable Create Desert tools (initial implementation GoneWacko), also added sticky buttons to land generator and town generator
- CodeChange: moved around some of the draggable tools, demystifying them
- CodeChange: change CmdBuildCanal to allow for XANDY dragging not only X or Y (only scenario editor)
- CodeChange: add some more enums to sprites.
- TODO: merge most of the ingame and scenario editor land terraform code. This can only be done after OnClickButton function is changed so it also includes the backreference to the widget being clicked, postponed to after 0.4.0
#include "stdafx.h"
#include "ttd.h"
#include "table/strings.h"
#include "window.h"
#include "gui.h"
#include "gfx.h"
#include "player.h"
#include "variables.h"
#include "string.h"
#include "hal.h"
#include <stdarg.h>
#include <string.h>
#include "console.h"
#include "network.h"
#include "network_data.h"
#include "network_server.h"

#define ICON_BUFFER 79
#define ICON_CMDBUF_SIZE 20
#define ICON_CMDLN_SIZE 255
#define ICON_LINE_HEIGHT 12
#define ICON_RIGHT_BORDERWIDTH 10
#define ICON_BOTTOM_BORDERWIDTH 12
#define ICON_MAX_ALIAS_LINES 40

// ** main console ** //
static Window *_iconsole_win; // Pointer to console window
static bool _iconsole_inited;
static char* _iconsole_buffer[ICON_BUFFER + 1];
static uint16 _iconsole_cbuffer[ICON_BUFFER + 1];
static Textbuf _iconsole_cmdline;
static byte _iconsole_scroll;

// ** stdlib ** //
byte _stdlib_developer = 1;
bool _stdlib_con_developer = false;
FILE* _iconsole_output_file;

// ** main console cmd buffer
static char* _iconsole_cmdbuffer[ICON_CMDBUF_SIZE];
static byte _iconsole_cmdbufferpos;

/* *************** */
/*  end of header  */
/* *************** */

static void IConsoleClearCommand(void)
{
	memset(_iconsole_cmdline.buf, 0, ICON_CMDLN_SIZE);
	_iconsole_cmdline.length = 0;
	_iconsole_cmdline.width = 0;
	_iconsole_cmdline.caretpos = 0;
	_iconsole_cmdline.caretxoffs = 0;
	SetWindowDirty(_iconsole_win);
}

// ** console window ** //
static void IConsoleWndProc(Window* w, WindowEvent* e)
{
	switch(e->event) {
		case WE_PAINT: {
			int i = _iconsole_scroll;
			int max = (w->height / ICON_LINE_HEIGHT) - 1;
			int delta = 0;
			GfxFillRect(w->left, w->top, w->width, w->height - 1, 0);
			while ((i > 0) && (i > _iconsole_scroll - max) && (_iconsole_buffer[i] != NULL)) {
				DoDrawString(_iconsole_buffer[i], 5,
					w->height - (_iconsole_scroll + 2 - i) * ICON_LINE_HEIGHT, _iconsole_cbuffer[i]);
				i--;
			}
			/* If the text is longer than the window, don't show the starting ']' */
			delta = w->width - 10 - _iconsole_cmdline.width - ICON_RIGHT_BORDERWIDTH;
			if (delta > 0) {
				DoDrawString("]", 5, w->height - ICON_LINE_HEIGHT, _iconsole_color_commands);
				delta = 0;
			}

			DoDrawString(_iconsole_cmdline.buf, 10 + delta, w->height - ICON_LINE_HEIGHT, _iconsole_color_commands);

			if (_iconsole_cmdline.caret)
				DoDrawString("_", 10 + delta + _iconsole_cmdline.caretxoffs, w->height - ICON_LINE_HEIGHT, 12);
			break;
		}
		case WE_MOUSELOOP:
			if (HandleCaret(&_iconsole_cmdline))
				SetWindowDirty(w);
			break;
		case WE_DESTROY:
			_iconsole_win = NULL;
			_iconsole_mode = ICONSOLE_CLOSED;
			break;
		case WE_KEYPRESS:
			e->keypress.cont = false;
			switch (e->keypress.keycode) {
				case WKC_UP:
					IConsoleCmdBufferNavigate(+1);
					SetWindowDirty(w);
					break;
				case WKC_DOWN:
					IConsoleCmdBufferNavigate(-1);
					SetWindowDirty(w);
					break;
				case WKC_SHIFT | WKC_PAGEUP:
					if (_iconsole_scroll - (w->height / ICON_LINE_HEIGHT) - 1 < 0)
						_iconsole_scroll = 0;
					else
						_iconsole_scroll -= (w->height / ICON_LINE_HEIGHT) - 1;
					SetWindowDirty(w);
					break;
				case WKC_SHIFT | WKC_PAGEDOWN:
					if (_iconsole_scroll + (w->height / ICON_LINE_HEIGHT) - 1 > ICON_BUFFER)
						_iconsole_scroll = ICON_BUFFER;
					else
						_iconsole_scroll += (w->height / ICON_LINE_HEIGHT) - 1;
					SetWindowDirty(w);
					break;
				case WKC_SHIFT | WKC_UP:
					if (_iconsole_scroll <= 0)
						_iconsole_scroll = 0;
					else
						--_iconsole_scroll;
					SetWindowDirty(w);
					break;
				case WKC_SHIFT | WKC_DOWN:
					if (_iconsole_scroll >= ICON_BUFFER)
						_iconsole_scroll = ICON_BUFFER;
					else
						++_iconsole_scroll;
					SetWindowDirty(w);
					break;
				case WKC_BACKQUOTE:
					IConsoleSwitch();
					break;
				case WKC_RETURN: case WKC_NUM_ENTER:
					IConsolePrintF(_iconsole_color_commands, "] %s", _iconsole_cmdline.buf);
					_iconsole_cmdbufferpos = ICON_CMDBUF_SIZE - 1;
					IConsoleCmdBufferAdd(_iconsole_cmdline.buf);

					IConsoleCmdExec(_iconsole_cmdline.buf);
					IConsoleClearCommand();
					break;
				case WKC_CTRL | WKC_RETURN:
					_iconsole_mode = (_iconsole_mode == ICONSOLE_FULL) ? ICONSOLE_OPENED : ICONSOLE_FULL;
					IConsoleResize();
					MarkWholeScreenDirty();
					break;
				case (WKC_CTRL | 'V'):
					if (InsertTextBufferClipboard(&_iconsole_cmdline))
						SetWindowDirty(w);
					break;
				case WKC_BACKSPACE: case WKC_DELETE:
					if (DeleteTextBufferChar(&_iconsole_cmdline, e->keypress.keycode))
						SetWindowDirty(w);
					_iconsole_cmdbufferpos = ICON_CMDBUF_SIZE - 1;
					break;
				case WKC_LEFT: case WKC_RIGHT: case WKC_END: case WKC_HOME:
					if (MoveTextBufferPos(&_iconsole_cmdline, e->keypress.keycode))
						SetWindowDirty(w);
					break;
				default:
					if (IsValidAsciiChar(e->keypress.ascii)) {
						_iconsole_scroll = ICON_BUFFER;
						InsertTextBufferChar(&_iconsole_cmdline, e->keypress.ascii);
						_iconsole_cmdbufferpos = ICON_CMDBUF_SIZE - 1;
						SetWindowDirty(w);
					} else
						e->keypress.cont = true;
			break;
		}
	}
}

static const Widget _iconsole_window_widgets[] = {
	{WIDGETS_END}
};

static const WindowDesc _iconsole_window_desc = {
	0, 0, 2, 2,
	WC_CONSOLE, 0,
	WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS,
	_iconsole_window_widgets,
	IConsoleWndProc,
};

extern const char _openttd_revision[];

void IConsoleInit(void)
{
	_iconsole_output_file = NULL;
	_iconsole_color_default = 1;
	_iconsole_color_error = 3;
	_iconsole_color_warning = 13;
	_iconsole_color_debug = 5;
	_iconsole_color_commands = 2;
	_iconsole_scroll = ICON_BUFFER;
	_iconsole_cmdbufferpos = ICON_CMDBUF_SIZE - 1;
	_iconsole_inited = true;
	_iconsole_mode = ICONSOLE_CLOSED;
	_iconsole_win = NULL;

#ifdef ENABLE_NETWORK /* Initialize network only variables */
	_redirect_console_to_client = 0;
#endif

	memset(_iconsole_cmdbuffer, 0, sizeof(_iconsole_cmdbuffer));
	memset(_iconsole_buffer, 0, sizeof(_iconsole_buffer));
	memset(_iconsole_cbuffer, 0, sizeof(_iconsole_cbuffer));
	_iconsole_cmdline.buf = calloc(ICON_CMDLN_SIZE, sizeof(*_iconsole_cmdline.buf)); // create buffer and zero it
	_iconsole_cmdline.maxlength = ICON_CMDLN_SIZE - 1;

	IConsoleStdLibRegister();
	IConsolePrintF(13, "OpenTTD Game Console Revision 6 - %s", _openttd_revision);
	IConsolePrint(12, "---------------------------------");
	IConsolePrint(12, "use \"help\" for more info");
	IConsolePrint(12, "");
	IConsoleClearCommand();
	IConsoleCmdBufferAdd("");
}

void IConsoleClear(void)
{
	uint i;
	for (i = 0; i <= ICON_BUFFER; i++)
		free(_iconsole_buffer[i]);

	free(_iconsole_cmdline.buf);
}

static void IConsoleWriteToLogFile(const char* string)
{
	if (_iconsole_output_file != NULL) {
		// if there is an console output file ... also print it there
		fwrite(string, strlen(string), 1, _iconsole_output_file);
		fwrite("\n", 1, 1, _iconsole_output_file);
	}
}

bool CloseConsoleLogIfActive(void)
{
	if (_iconsole_output_file != NULL) {
		IConsolePrintF(_iconsole_color_default, "file output complete");
		fclose(_iconsole_output_file);
		_iconsole_output_file = NULL;
		return true;
	}

	return false;
}

void IConsoleFree(void)
{
	_iconsole_inited = false;
	IConsoleClear();
	CloseConsoleLogIfActive();
}

void IConsoleResize(void)
{

	_iconsole_win = FindWindowById(WC_CONSOLE, 0);

	switch (_iconsole_mode) {
		case ICONSOLE_OPENED:
			_iconsole_win->height = _screen.height / 3;
			_iconsole_win->width = _screen.width;
			break;
		case ICONSOLE_FULL:
			_iconsole_win->height = _screen.height - ICON_BOTTOM_BORDERWIDTH;
			_iconsole_win->width = _screen.width;
			break;
		default:
			break;
	}

	MarkWholeScreenDirty();
}

void IConsoleSwitch(void)
{
	switch (_iconsole_mode) {
		case ICONSOLE_CLOSED:
			_iconsole_win = AllocateWindowDesc(&_iconsole_window_desc);
			_iconsole_win->height = _screen.height / 3;
			_iconsole_win->width = _screen.width;
			_iconsole_mode = ICONSOLE_OPENED;
			SETBIT(_no_scroll, SCROLL_CON);
			break;
		case ICONSOLE_OPENED: case ICONSOLE_FULL:
			DeleteWindowById(WC_CONSOLE, 0);
			_iconsole_win = NULL;
			_iconsole_mode = ICONSOLE_CLOSED;
			CLRBIT(_no_scroll, SCROLL_CON);
			break;
	}

	MarkWholeScreenDirty();
}

void IConsoleClose(void)
{
	if (_iconsole_mode == ICONSOLE_OPENED) IConsoleSwitch();
}

void IConsoleOpen(void)
{
	if (_iconsole_mode == ICONSOLE_CLOSED) IConsoleSwitch();
}

void IConsoleCmdBufferAdd(const char* cmd)
{
	int i;
	if (_iconsole_cmdbufferpos != (ICON_CMDBUF_SIZE - 1)) return;
	free(_iconsole_cmdbuffer[ICON_CMDBUF_SIZE - 2]);
	for (i = (ICON_CMDBUF_SIZE - 2); i > 0; i--) _iconsole_cmdbuffer[i] = _iconsole_cmdbuffer[i - 1];
	_iconsole_cmdbuffer[0] = strdup(cmd);
}

void IConsoleCmdBufferNavigate(signed char direction)
{
	int i;
	i = _iconsole_cmdbufferpos + direction;
	if (i < 0) i = ICON_CMDBUF_SIZE - 1;
	if (i >= ICON_CMDBUF_SIZE) i = 0;
	if (direction > 0)
		while (_iconsole_cmdbuffer[i] == NULL) {
			i++;
			if (i >= ICON_CMDBUF_SIZE) i = 0;
		}
	if (direction < 0)
		while (_iconsole_cmdbuffer[i] == NULL) {
			--i;
			if (i < 0) i = ICON_CMDBUF_SIZE - 1;
		}
	_iconsole_cmdbufferpos = i;
	IConsoleClearCommand();
	ttd_strlcpy(_iconsole_cmdline.buf, _iconsole_cmdbuffer[i], _iconsole_cmdline.maxlength);
	UpdateTextBufferSize(&_iconsole_cmdline);
}

/**
 * Handle the printing of text entered into the console or redirected there
 * by any other means. Text can be redirected to other players in a network game
 * as well as to a logfile. If the network server is a dedicated server, all activities
 * are also logged. All lines to print are added to a temporary buffer which can be
 * used as a history to print them onscreen
 * @param color_code the colour of the command. Red in case of errors, etc.
 * @param string the message entered or output on the console (notice, error, etc.)
 */
void IConsolePrint(uint16 color_code, const char* string)
{
	char *i;

#ifdef ENABLE_NETWORK
	if (_redirect_console_to_client != 0) {
		/* Redirect the string to the client */
		SEND_COMMAND(PACKET_SERVER_RCON)(NetworkFindClientStateFromIndex(_redirect_console_to_client), color_code, string);
		return;
	}
#endif

	if (_network_dedicated) {
		printf("%s\n", string);
		IConsoleWriteToLogFile(string);
		return;
	}

	if (!_iconsole_inited) return;

	/* move up all the strings in the buffer one place and do the same for colour
	 * to accomodate for the new command/message */
	free(_iconsole_buffer[0]);
	memmove(&_iconsole_buffer[0], &_iconsole_buffer[1], sizeof(_iconsole_buffer[0]) * ICON_BUFFER);
	_iconsole_buffer[ICON_BUFFER] = strdup(string);

	// filter out unprintable characters
	for (i = _iconsole_buffer[ICON_BUFFER]; *i != '\0'; i++)
		if (!IsValidAsciiChar((byte)*i)) *i = ' ';

	memmove(&_iconsole_cbuffer[0], &_iconsole_cbuffer[1], sizeof(_iconsole_cbuffer[0]) * ICON_BUFFER);
	_iconsole_cbuffer[ICON_BUFFER] = color_code;

	IConsoleWriteToLogFile(string);

	if (_iconsole_win != NULL) SetWindowDirty(_iconsole_win);
}


void CDECL IConsolePrintF(uint16 color_code, const char* s, ...)
{
	va_list va;
	char buf[1024];
	int len;

	va_start(va, s);
	len = vsnprintf(buf, sizeof(buf), s, va);
	va_end(va);

	IConsolePrint(color_code, buf);
}

void IConsoleDebug(const char* string)
{
	if (_stdlib_developer > 1)
		IConsolePrintF(_iconsole_color_debug, "dbg: %s", string);
}

void IConsoleError(const char* string)
{
	if (_stdlib_developer > 0)
		IConsolePrintF(_iconsole_color_error, "ERROR: %s", string);
}

void IConsoleWarning(const char* string)
{
	if (_stdlib_developer > 0)
		IConsolePrintF(_iconsole_color_warning, "WARNING: %s", string);
}

void IConsoleCmdRegister(const char* name, _iconsole_cmd_addr addr)
{
	char* _new;
	_iconsole_cmd* item;
	_iconsole_cmd* item_new;
	_iconsole_cmd* item_before;

	_new = strdup(name);

	item_new = malloc(sizeof(_iconsole_cmd));

	item_new->_next = NULL;
	item_new->addr = addr;
	item_new->name = _new;

	item_new->hook_access = NULL;
	item_new->hook_after_exec = NULL;
	item_new->hook_before_exec = NULL;

	item_before = NULL;
	item = _iconsole_cmds;

	if (item == NULL) {
		_iconsole_cmds = item_new;
	} else {
		while ((item->_next != NULL) && (strcmp(item->name,item_new->name)<=0)) {
			item_before = item;
			item = item->_next;
			}
// insertion sort
		if (item_before==NULL) {
			if (strcmp(item->name,item_new->name)<=0) {
				// appending
				item ->_next = item_new;
			} else {
				// inserting as startitem
				_iconsole_cmds = item_new;
				item_new ->_next = item;
			}
		} else {
			if (strcmp(item->name,item_new->name)<=0) {
				// appending
				item ->_next = item_new;
			} else {
				// inserting
				item_new ->_next = item_before->_next;
				item_before ->_next = item_new;
			}
		}
// insertion sort end
	}

}

_iconsole_cmd* IConsoleCmdGet(const char* name)
{
	_iconsole_cmd* item;

	item = _iconsole_cmds;
	while (item != NULL) {
		if (strcmp(item->name, name) == 0) return item;
		item = item->_next;
	}
	return NULL;
}

void IConsoleAliasRegister(const char* name, const char* cmdline)
{
	char* _new;
	char* _newcmd;
	_iconsole_alias* item;
	_iconsole_alias* item_new;
	_iconsole_alias* item_before;

	_new = strdup(name);
	_newcmd = strdup(cmdline);

	item_new = malloc(sizeof(_iconsole_alias));

	item_new->_next = NULL;
	item_new->cmdline = _newcmd;
	item_new->name = _new;

	item_before = NULL;
	item = _iconsole_aliases;

	if (item == NULL) {
		_iconsole_aliases = item_new;
	} else {
		while ((item->_next != NULL) && (strcmp(item->name,item_new->name)<=0)) {
			item_before = item;
			item = item->_next;
			}
// insertion sort
		if (item_before==NULL) {
			if (strcmp(item->name,item_new->name)<=0) {
				// appending
				item ->_next = item_new;
			} else {
				// inserting as startitem
				_iconsole_aliases = item_new;
				item_new ->_next = item;
			}
		} else {
			if (strcmp(item->name,item_new->name)<=0) {
				// appending
				item ->_next = item_new;
			} else {
				// inserting
				item_new ->_next = item_before->_next;
				item_before ->_next = item_new;
			}
		}
// insertion sort end
	}

}

_iconsole_alias* IConsoleAliasGet(const char* name)
{
	_iconsole_alias* item;

	item = _iconsole_aliases;
	while (item != NULL) {
		if (strcmp(item->name, name) == 0) return item;
		item = item->_next;
	}
	return NULL;
}

static void IConsoleAliasExec(const char* cmdline, char* tokens[20], byte tokentypes[20])
{
	char* lines[ICON_MAX_ALIAS_LINES];
	char* linestream;
	char* linestream_s;

	int c;
	int i;
	int l;
	int x;
	byte t;

	//** clearing buffer **//

	for (i = 0; i < 40; i++) {
		lines[0] = NULL;
	}
	linestream_s = linestream = malloc(1024*ICON_MAX_ALIAS_LINES);
	memset(linestream, 0, 1024*ICON_MAX_ALIAS_LINES);

	//** parsing **//

	l = strlen(cmdline);
	i = 0;
	c = 0;
	x = 0;
	t = 0;
	lines[c] = linestream;

	while (i < l && c < ICON_MAX_ALIAS_LINES - 1) {
		if (cmdline[i] == '%') {
			i++;
			if (cmdline[i] == '+') {
				// all params seperated: "[param 1]" "[param 2]"
				t=1;
				while ((tokens[t]!=NULL) && (t<20) &&
						((tokentypes[t] == ICONSOLE_VAR_STRING) || (tokentypes[t] == ICONSOLE_VAR_UNKNOWN))) {
					int l2 = strlen(tokens[t]);
					*linestream = '"';
					linestream++;
					memcpy(linestream,tokens[t],l2);
					linestream += l2;
					*linestream = '"';
					linestream++;
					*linestream = ' ';
					linestream++;
					x += l2+3;
					t++;
				}
			} else if (cmdline[i] == '!') {
				// merge the params to one: "[param 1] [param 2] [param 3...]"
				t=1;
				*linestream = '"';
				linestream++;
				while ((tokens[t]!=NULL) && (t<20) &&
						((tokentypes[t] == ICONSOLE_VAR_STRING) || (tokentypes[t] == ICONSOLE_VAR_UNKNOWN))) {
					int l2 = strlen(tokens[t]);
					memcpy(linestream,tokens[t],l2);
					linestream += l2;
					*linestream = ' ';
					linestream++;
					x += l2+1;
					t++;
				}
				linestream--;
				*linestream = '"';
				linestream++;
				x += 1;
			} else {
				// one specific parameter: %A = [param 1] %B = [param 2] ...
				int l2;
				t = ((byte)cmdline[i]) - 64;
				if ((t<20) && (tokens[t]!=NULL) &&
						((tokentypes[t] == ICONSOLE_VAR_STRING) || (tokentypes[t] == ICONSOLE_VAR_UNKNOWN))) {
					l2 = strlen(tokens[t]);
					*linestream = '"';
					linestream++;
					memcpy(linestream,tokens[t],l2);
					linestream += l2;
					*linestream = '"';
					linestream++;
					x += l2+2;
				}
			}
		} else if (cmdline[i] == '\\') {
			// \\ = \       \' = '      \% = %
			i++;
			if (cmdline[i] == '\\') {
				*linestream = '\\';
				linestream++;
			} else if (cmdline[i] == '\'') {
				*linestream = '\'';
				linestream++;
			} else if (cmdline[i] == '%') {
				*linestream = '%';
				linestream++;
			}
		} else if (cmdline[i] == '\'') {
			// ' = "
			*linestream = '"';
			linestream++;
		} else if (cmdline[i] == ';') {
			// ; = start a new line
			c++;
			*linestream = '\0';
			linestream += 1024 - (x % 1024);
			x += 1024 - (x % 1024);
			lines[c] = linestream;
		} else {
			*linestream = cmdline[i];
			linestream++;
			x++;
		}
		i++;
	}

	linestream--;
	if (*linestream != '\0') {
		c++;
		linestream++;
		*linestream = '\0';
	}

	for (i=0; i<c; i++)	{
		IConsoleCmdExec(lines[i]);
	}

	free(linestream_s);
}

void IConsoleVarInsert(_iconsole_var* item_new, const char* name)
{
	_iconsole_var* item;
	_iconsole_var* item_before;

	item_new->_next = NULL;

	item_new->name = malloc(strlen(name) + 2); /* XXX unchecked malloc */
	sprintf(item_new->name, "%s", name);

	item_before = NULL;
	item = _iconsole_vars;

	if (item == NULL) {
		_iconsole_vars = item_new;
	} else {
		while ((item->_next != NULL) && (strcmp(item->name,item_new->name)<=0)) {
			item_before = item;
			item = item->_next;
			}
// insertion sort
		if (item_before==NULL) {
			if (strcmp(item->name,item_new->name)<=0) {
				// appending
				item ->_next = item_new;
			} else {
				// inserting as startitem
				_iconsole_vars = item_new;
				item_new ->_next = item;
			}
		} else {
			if (strcmp(item->name,item_new->name)<=0) {
				// appending
				item ->_next = item_new;
			} else {
				// inserting
				item_new ->_next = item_before->_next;
				item_before ->_next = item_new;
			}
		}
// insertion sort end
	}
}

void IConsoleVarRegister(const char* name, void* addr, _iconsole_var_types type)
{
	_iconsole_var* item_new;

	item_new = malloc(sizeof(_iconsole_var)); /* XXX unchecked malloc */

	item_new->_next = NULL;

	switch (type) {
		case ICONSOLE_VAR_BOOLEAN:
			item_new->data.bool_ = addr;
			break;
		case ICONSOLE_VAR_BYTE:
		case ICONSOLE_VAR_UINT8:
			item_new->data.byte_ = addr;
			break;
		case ICONSOLE_VAR_UINT16:
			item_new->data.uint16_ = addr;
			break;
		case ICONSOLE_VAR_UINT32:
			item_new->data.uint32_ = addr;
			break;
		case ICONSOLE_VAR_INT16:
			item_new->data.int16_ = addr;
			break;
		case ICONSOLE_VAR_INT32:
			item_new->data.int32_ = addr;
			break;
		case ICONSOLE_VAR_STRING:
			item_new->data.string_ = addr;
			break;
		default:
			error("unknown console variable type");
			break;
	}

	IConsoleVarInsert(item_new, name);

	item_new->type = type;
	item_new->_malloc = false;

	item_new->hook_access = NULL;
	item_new->hook_after_change = NULL;
	item_new->hook_before_change = NULL;

}

void IConsoleVarMemRegister(const char* name, _iconsole_var_types type)
{
	_iconsole_var* item;
	item = IConsoleVarAlloc(type);
	IConsoleVarInsert(item, name);
}

_iconsole_var* IConsoleVarGet(const char* name)
{
	_iconsole_var* item;
	for (item = _iconsole_vars; item != NULL; item = item->_next)
		if (strcmp(item->name, name) == 0) return item;
	return NULL;
}

_iconsole_var* IConsoleVarAlloc(_iconsole_var_types type)
{
	_iconsole_var* item = malloc(sizeof(_iconsole_var)); /* XXX unchecked malloc */
	item->_next = NULL;
	item->name = NULL;
	item->type = type;
	switch (item->type) {
		case ICONSOLE_VAR_BOOLEAN:
			item->data.bool_ = malloc(sizeof(*item->data.bool_));
			*item->data.bool_ = false;
			item->_malloc = true;
			break;
		case ICONSOLE_VAR_BYTE:
		case ICONSOLE_VAR_UINT8:
			item->data.byte_ = malloc(sizeof(*item->data.byte_));
			*item->data.byte_ = 0;
			item->_malloc = true;
			break;
		case ICONSOLE_VAR_UINT16:
			item->data.uint16_ = malloc(sizeof(*item->data.uint16_));
			*item->data.uint16_ = 0;
			item->_malloc = true;
			break;
		case ICONSOLE_VAR_UINT32:
			item->data.uint32_ = malloc(sizeof(*item->data.uint32_));
			*item->data.uint32_ = 0;
			item->_malloc = true;
			break;
		case ICONSOLE_VAR_INT16:
			item->data.int16_ = malloc(sizeof(*item->data.int16_));
			*item->data.int16_ = 0;
			item->_malloc = true;
			break;
		case ICONSOLE_VAR_INT32:
			item->data.int32_ = malloc(sizeof(*item->data.int32_));
			*item->data.int32_ = 0;
			item->_malloc = true;
			break;
		case ICONSOLE_VAR_POINTER:
		case ICONSOLE_VAR_STRING:
			// needs no memory ... it gets memory when it is set to an value
			item->data.addr = NULL;
			item->_malloc = false;
			break;
		default:
			error("unknown console variable type");
			break;
	}

	item->hook_access = NULL;
	item->hook_after_change = NULL;
	item->hook_before_change = NULL;
	return item;
}


void IConsoleVarFree(_iconsole_var* var)
{
	if (var->_malloc)
		free(var->data.addr);
	free(var->name);
	free(var);
}

void IConsoleVarSetString(_iconsole_var* var, const char* string)
{
	if (string == NULL) return;

	if (var->_malloc)
		free(var->data.string_);

	var->data.string_ = strdup(string);
	var->_malloc = true;
}

void IConsoleVarSetValue(_iconsole_var* var, int value) {
	switch (var->type) {
		case ICONSOLE_VAR_BOOLEAN:
			*var->data.bool_ = (value != 0);
			break;
		case ICONSOLE_VAR_BYTE:
		case ICONSOLE_VAR_UINT8:
			*var->data.byte_ = value;
			break;
		case ICONSOLE_VAR_UINT16:
			*var->data.uint16_ = value;
			break;
		case ICONSOLE_VAR_UINT32:
			*var->data.uint32_ = value;
			break;
		case ICONSOLE_VAR_INT16:
			*var->data.int16_ = value;
			break;
		case ICONSOLE_VAR_INT32:
			*var->data.int32_ = value;
			break;
		default:
			assert(0);
			break;
	}
}

void IConsoleVarDump(const _iconsole_var* var, const char* dump_desc)
{
	if (var == NULL) return;
	if (dump_desc == NULL) dump_desc = var->name;

	switch (var->type) {
		case ICONSOLE_VAR_BOOLEAN:
			IConsolePrintF(_iconsole_color_default, "%s = %s",
				dump_desc, *var->data.bool_ ? "true" : "false");
			break;
		case ICONSOLE_VAR_BYTE:
		case ICONSOLE_VAR_UINT8:
			IConsolePrintF(_iconsole_color_default, "%s = %u",
				dump_desc, *var->data.byte_);
			break;
		case ICONSOLE_VAR_UINT16:
			IConsolePrintF(_iconsole_color_default, "%s = %u",
				dump_desc, *var->data.uint16_);
			break;
		case ICONSOLE_VAR_UINT32:
			IConsolePrintF(_iconsole_color_default, "%s = %u",
				dump_desc, *var->data.uint32_);
			break;
		case ICONSOLE_VAR_INT16:
			IConsolePrintF(_iconsole_color_default, "%s = %i",
				dump_desc, *var->data.int16_);
			break;
		case ICONSOLE_VAR_INT32:
			IConsolePrintF(_iconsole_color_default, "%s = %i",
				dump_desc, *var->data.int32_);
			break;
		case ICONSOLE_VAR_STRING:
			IConsolePrintF(_iconsole_color_default, "%s = %s",
				dump_desc, var->data.string_);
			break;
		case ICONSOLE_VAR_REFERENCE:
			IConsolePrintF(_iconsole_color_default, "%s = @%s",
				dump_desc, var->data.reference_);
		case ICONSOLE_VAR_UNKNOWN:
		case ICONSOLE_VAR_POINTER:
			IConsolePrintF(_iconsole_color_default, "%s = @%p",
				dump_desc, var->data.addr);
			break;
		case ICONSOLE_VAR_NONE:
			IConsolePrintF(_iconsole_color_default, "%s = [nothing]",
				dump_desc);
			break;
	}
}

// * ************************* * //
// * hooking code              * //
// * ************************* * //

void IConsoleVarHook(const char* name, _iconsole_hook_types type, iconsole_var_hook proc)
{
	_iconsole_var* hook_var = IConsoleVarGet(name);
	if (hook_var == NULL) return;
	switch (type) {
		case ICONSOLE_HOOK_BEFORE_CHANGE:
			hook_var->hook_before_change = proc;
			break;
		case ICONSOLE_HOOK_AFTER_CHANGE:
			hook_var->hook_after_change = proc;
			break;
		case ICONSOLE_HOOK_ACCESS:
			hook_var->hook_access = proc;
			break;
		case ICONSOLE_HOOK_BEFORE_EXEC:
		case ICONSOLE_HOOK_AFTER_EXEC:
			assert(0);
			break;
	}
}

bool IConsoleVarHookHandle(_iconsole_var* hook_var, _iconsole_hook_types type)
{
	iconsole_var_hook proc;
	if (hook_var == NULL) return false;

	proc = NULL;
	switch (type) {
		case ICONSOLE_HOOK_BEFORE_CHANGE:
			proc = hook_var->hook_before_change;
			break;
		case ICONSOLE_HOOK_AFTER_CHANGE:
			proc = hook_var->hook_after_change;
			break;
		case ICONSOLE_HOOK_ACCESS:
			proc = hook_var->hook_access;
			break;
		case ICONSOLE_HOOK_BEFORE_EXEC:
		case ICONSOLE_HOOK_AFTER_EXEC:
			assert(0);
			break;
	}
	return proc == NULL ? true : proc(hook_var);
}

void IConsoleCmdHook(const char* name, _iconsole_hook_types type, iconsole_cmd_hook proc)
{
	_iconsole_cmd* hook_cmd = IConsoleCmdGet(name);
	if (hook_cmd == NULL) return;
	switch (type) {
		case ICONSOLE_HOOK_AFTER_EXEC:
			hook_cmd->hook_after_exec = proc;
			break;
		case ICONSOLE_HOOK_BEFORE_EXEC:
			hook_cmd->hook_before_exec = proc;
			break;
		case ICONSOLE_HOOK_ACCESS:
			hook_cmd->hook_access = proc;
			break;
		case ICONSOLE_HOOK_BEFORE_CHANGE:
		case ICONSOLE_HOOK_AFTER_CHANGE:
			assert(0);
			break;
	}
}

bool IConsoleCmdHookHandle(_iconsole_cmd* hook_cmd, _iconsole_hook_types type)
{
	iconsole_cmd_hook proc = NULL;
	switch (type) {
		case ICONSOLE_HOOK_AFTER_EXEC:
			proc = hook_cmd->hook_after_exec;
			break;
		case ICONSOLE_HOOK_BEFORE_EXEC:
			proc = hook_cmd->hook_before_exec;
			break;
		case ICONSOLE_HOOK_ACCESS:
			proc = hook_cmd->hook_access;
			break;
		case ICONSOLE_HOOK_BEFORE_CHANGE:
		case ICONSOLE_HOOK_AFTER_CHANGE:
			assert(0);
			break;
	}
	return proc == NULL ? true : proc(hook_cmd);
}

void IConsoleCmdExec(const char* cmdstr)
{
	_iconsole_cmd_addr function;
	char* tokens[20];
	byte  tokentypes[20];
	char* tokenstream;
	char* tokenstream_s;
	byte  execution_mode;
	_iconsole_var* var     = NULL;
	_iconsole_var* result  = NULL;
	_iconsole_cmd* cmd     = NULL;
	_iconsole_alias* alias = NULL;

	bool longtoken;
	bool valid_token;
	bool skip_lt_change;

	uint c;
	uint i;
	uint l;

	for (; strchr("\n\r \t", *cmdstr) != NULL; ++cmdstr) {
		switch (*cmdstr) {
			case '\0':
			case '#':
				return;

			default:
				break;
		}
	}

	if (_stdlib_con_developer)
		IConsolePrintF(_iconsole_color_debug, "CONDEBUG: execution_cmdline: %s", cmdstr);

	//** clearing buffer **//

	for (i = 0; i < 20; i++) {
		tokens[i] = NULL;
		tokentypes[i] = ICONSOLE_VAR_NONE;
	}
	tokenstream_s = tokenstream = malloc(1024);
	memset(tokenstream, 0, 1024);

	//** parsing **//

	longtoken = false;
	valid_token = false;
	skip_lt_change = false;
	l = strlen(cmdstr);
	i = 0;
	c = 0;
	tokens[c] = tokenstream;
	tokentypes[c] = ICONSOLE_VAR_UNKNOWN;
	while (i < l && c < lengthof(tokens) - 1) {
		if (cmdstr[i] == '"') {
			if (longtoken) {
				if (cmdstr[i + 1] == '"') {
					i++;
					*tokenstream = '"';
					tokenstream++;
					skip_lt_change = true;
				} else {
					longtoken = !longtoken;
					tokentypes[c] = ICONSOLE_VAR_STRING;
				}
			} else {
				longtoken = !longtoken;
				tokentypes[c] = ICONSOLE_VAR_STRING;
			}
			if (!skip_lt_change) {
				if (!longtoken) {
					if (valid_token) {
						c++;
						*tokenstream = '\0';
						tokenstream++;
						tokens[c] = tokenstream;
						tokentypes[c] = ICONSOLE_VAR_UNKNOWN;
						valid_token = false;
					}
				}
				skip_lt_change=false;
			}
		} else if (!longtoken && cmdstr[i] == ' ') {
			if (valid_token) {
				c++;
				*tokenstream = '\0';
				tokenstream++;
				tokens[c] = tokenstream;
				tokentypes[c] = ICONSOLE_VAR_UNKNOWN;
				valid_token = false;
			}
		} else {
			valid_token = true;
			*tokenstream = cmdstr[i];
			tokenstream++;
		}
		i++;
	}

	tokenstream--;
	if (*tokenstream != '\0') {
		c++;
		tokenstream++;
		*tokenstream = '\0';
	}

	//** interpreting **//

	for (i = 0; i < c; i++) {
		if (tokens[i] != NULL && i > 0 && strlen(tokens[i]) > 0) {
			if (IConsoleVarGet((char *)tokens[i]) != NULL) {
				// change the variable to an pointer if execution_mode != 4 is
				// being prepared. execution_mode 4 is used to assign
				// one variables data to another one
				// [token 0 and 2]
				if (!((i == 2) && (tokentypes[1] == ICONSOLE_VAR_UNKNOWN) &&
					(strcmp(tokens[1], "<<") == 0))) {
					// only look for another variable if it isnt an longtoken == string with ""
					var = NULL;
					if (tokentypes[i]!=ICONSOLE_VAR_STRING) var = IConsoleVarGet(tokens[i]);
					if (var != NULL) {
						// pointer to the data --> token
						tokens[i] = (char *) var->data.addr; /* XXX: maybe someone finds an cleaner way to do this */
						tokentypes[i] = var->type;
					}
				}
			}
			if (tokens[i] != NULL && tokens[i][0] == '@' && (IConsoleVarGet(tokens[i]+1) != NULL)) {
				var = IConsoleVarGet(tokens[i]+1);
				if (var != NULL) {
					// pointer to the _iconsole_var struct --> token
					tokens[i] = (char *) var; /* XXX: maybe someone finds an cleaner way to do this */
					tokentypes[i] = ICONSOLE_VAR_REFERENCE;
				}
			}
		}
	}

	execution_mode=0;

	function = NULL;
	cmd = IConsoleCmdGet(tokens[0]);
	if (cmd != NULL) {
		function = cmd->addr;
	} else {
		alias = IConsoleAliasGet(tokens[0]);
		if (alias != NULL) execution_mode = 5; // alias handling
	}

	if (function != NULL) {
		execution_mode = 1; // this is a command
	} else {
		var = IConsoleVarGet(tokens[0]);
		if (var != NULL) {
			execution_mode = 2; // this is a variable
			if (c > 2 && strcmp(tokens[1], "<<") == 0) {
				// this is command to variable mode [normal]

				function = NULL;
				cmd = IConsoleCmdGet(tokens[2]);
				if (cmd != NULL) function = cmd->addr;

				if (function != NULL) {
					execution_mode = 3;
				} else {
					result = IConsoleVarGet(tokens[2]);
					if (result != NULL)
						execution_mode = 4;
				}
			}
		}
	}

	//** executing **//
	if (_stdlib_con_developer)
		IConsolePrintF(_iconsole_color_debug, "CONDEBUG: execution_mode: %i",
			execution_mode);
	switch (execution_mode) {
		case 0:
			// not found
			IConsoleError("command or variable not found");
			break;
		case 1:
			if (IConsoleCmdHookHandle(cmd, ICONSOLE_HOOK_ACCESS)) {
				// execution with command syntax
				IConsoleCmdHookHandle(cmd, ICONSOLE_HOOK_BEFORE_EXEC);
				result = function(c, tokens, tokentypes);
				if (result != NULL) {
					IConsoleVarDump(result, "result");
					IConsoleVarFree(result);
				}
				IConsoleCmdHookHandle(cmd, ICONSOLE_HOOK_AFTER_EXEC);
				break;
			}
		case 2:
		{
			// execution with variable syntax
			if (IConsoleVarHookHandle(var, ICONSOLE_HOOK_ACCESS) && (c == 2 || c == 3)) {
				// ** variable modifications ** //
				IConsoleVarHookHandle(var, ICONSOLE_HOOK_BEFORE_CHANGE);
				switch (var->type) {
					case ICONSOLE_VAR_BOOLEAN:
					{
						if (strcmp(tokens[1], "=") == 0) {
							if (c == 3) {
								*var->data.bool_ = (atoi(tokens[2]) != 0);
							} else {
								*var->data.bool_ = false;
							}
							IConsoleVarDump(var, NULL);
						} else if (strcmp(tokens[1], "++") == 0) {
							*var->data.bool_ = true;
							IConsoleVarDump(var, NULL);
						} else if (strcmp(tokens[1], "--") == 0) {
							*var->data.bool_ = false;
							IConsoleVarDump(var, NULL);
						}
						else
							IConsoleError("operation not supported");
						break;
					}
					case ICONSOLE_VAR_BYTE:
					case ICONSOLE_VAR_UINT8:
					{
						if (strcmp(tokens[1], "=") == 0) {
							if (c == 3)
								*var->data.byte_ = atoi(tokens[2]);
							else
								*var->data.byte_ = 0;
							IConsoleVarDump(var, NULL);
						} else if (strcmp(tokens[1], "++") == 0) {
							++*var->data.byte_;
							IConsoleVarDump(var, NULL);
						} else if (strcmp(tokens[1], "--")==0) {
							--*var->data.byte_;
							IConsoleVarDump(var, NULL);
						}
						else
							IConsoleError("operation not supported");
						break;
					}
					case ICONSOLE_VAR_UINT16:
					{
						if (strcmp(tokens[1], "=") == 0) {
							if (c == 3)
								*var->data.uint16_ = atoi(tokens[2]);
							else
								*var->data.uint16_ = 0;
							IConsoleVarDump(var, NULL);
						} else if (strcmp(tokens[1], "++") == 0) {
							++*var->data.uint16_;
							IConsoleVarDump(var, NULL);
						} else if (strcmp(tokens[1], "--") == 0) {
							--*var->data.uint16_;
							IConsoleVarDump(var, NULL);
						}
						else
							IConsoleError("operation not supported");
						break;
					}
					case ICONSOLE_VAR_UINT32:
					{
						if (strcmp(tokens[1], "=") == 0) {
							if (c == 3)
								*var->data.uint32_ = atoi(tokens[2]);
							else
								*var->data.uint32_ = 0;
							IConsoleVarDump(var, NULL);
						} else if (strcmp(tokens[1], "++") == 0) {
							++*var->data.uint32_;
							IConsoleVarDump(var, NULL);
						} else if (strcmp(tokens[1], "--") == 0) {
							--*var->data.uint32_;
							IConsoleVarDump(var, NULL);
						}
						else
							IConsoleError("operation not supported");
						break;
					}
					case ICONSOLE_VAR_INT16:
					{
						if (strcmp(tokens[1], "=") == 0) {
							if (c == 3)
								*var->data.int16_ = atoi(tokens[2]);
							else
								*var->data.int16_ = 0;
							IConsoleVarDump(var, NULL);
						} else if (strcmp(tokens[1], "++") == 0) {
							++*var->data.int16_;
							IConsoleVarDump(var, NULL);
						} else if (strcmp(tokens[1], "--") == 0) {
							--*var->data.int16_;
							IConsoleVarDump(var, NULL);
						}
						else
							IConsoleError("operation not supported");
						break;
					}
					case ICONSOLE_VAR_INT32:
					{
						if (strcmp(tokens[1], "=") == 0) {
							if (c == 3)
								*var->data.int32_ = atoi(tokens[2]);
							else
								*var->data.int32_ = 0;
							IConsoleVarDump(var, NULL);
						} else if (strcmp(tokens[1], "++") == 0) {
							++*var->data.int32_;
							IConsoleVarDump(var, NULL);
						} else if (strcmp(tokens[1], "--") == 0) {
							--*var->data.int32_;
							IConsoleVarDump(var, NULL);
						}
						else { IConsoleError("operation not supported"); }
						break;
					}
					case ICONSOLE_VAR_STRING:
					{
						if (strcmp(tokens[1], "=") == 0) {
							if (c == 3)
								IConsoleVarSetString(var, tokens[2]);
							else
								IConsoleVarSetString(var, "");
							IConsoleVarDump(var, NULL);
						}
						else
							IConsoleError("operation not supported");
						break;
					}
					case ICONSOLE_VAR_POINTER:
					{
						if (strcmp(tokens[1], "=") == 0) {
							if (c == 3) {
								if (tokentypes[2] == ICONSOLE_VAR_UNKNOWN)
									var->data.addr = (void*)atoi(tokens[2]); /* direct access on memory [by address] */
								else
									var->data.addr = (void*)tokens[2]; /* direct acces on memory [by variable] */
							} else
								var->data.addr = NULL;
							IConsoleVarDump(var, NULL);
						} else if (strcmp(tokens[1], "++") == 0) {
							++*(char*)&var->data.addr; /* change the address + 1 */
							IConsoleVarDump(var, NULL);
						} else if (strcmp(tokens[1], "--") == 0) {
							--*(char*)&var->data.addr; /* change the address - 1 */
							IConsoleVarDump(var, NULL);
						}
						else
							IConsoleError("operation not supported");
						break;
					}
					case ICONSOLE_VAR_NONE:
					case ICONSOLE_VAR_REFERENCE:
					case ICONSOLE_VAR_UNKNOWN:
						IConsoleError("operation not supported");
						break;
				}
				IConsoleVarHookHandle(var, ICONSOLE_HOOK_AFTER_CHANGE);
			}
			if (c == 1) // ** variable output ** //
				IConsoleVarDump(var, NULL);
			break;
		}
		case 3:
		case 4:
		{
			// execute command with result or assign a variable
			if (execution_mode == 3) {
				if (IConsoleCmdHookHandle(cmd, ICONSOLE_HOOK_ACCESS)) {
					int i;
					int diff;
					void* temp;
					byte temp2;

					// tokenshifting
					for (diff = 0; diff < 2; diff++) {
						temp = tokens[0];
						temp2 = tokentypes[0];
						for (i = 0; i < 19; i++) {
							tokens[i] = tokens[i + 1];
							tokentypes[i] = tokentypes[i + 1];
						}
						tokens[19] = temp;
						tokentypes[19] = temp2;
					}
					IConsoleCmdHookHandle(cmd, ICONSOLE_HOOK_BEFORE_EXEC);
					result = function(c, tokens, tokentypes);
					IConsoleCmdHookHandle(cmd, ICONSOLE_HOOK_AFTER_EXEC);
				} else
					execution_mode = 255;
			}

			if (IConsoleVarHookHandle(var, ICONSOLE_HOOK_ACCESS) && result != NULL) {
				if (result->type != var->type) {
					IConsoleError("variable type missmatch");
				} else {
					IConsoleVarHookHandle(var, ICONSOLE_HOOK_BEFORE_CHANGE);
					switch (result->type) {
						case ICONSOLE_VAR_BOOLEAN:
							*var->data.bool_ = *result->data.bool_;
							IConsoleVarDump(var, NULL);
							break;
						case ICONSOLE_VAR_BYTE:
						case ICONSOLE_VAR_UINT8:
							*var->data.byte_ = *result->data.byte_;
							IConsoleVarDump(var, NULL);
							break;
						case ICONSOLE_VAR_UINT16:
							*var->data.uint16_ = *result->data.uint16_;
							IConsoleVarDump(var, NULL);
							break;
						case ICONSOLE_VAR_UINT32:
							*var->data.uint32_ = *result->data.uint32_;
							IConsoleVarDump(var, NULL);
							break;
						case ICONSOLE_VAR_INT16:
							*var->data.int16_ = *result->data.int16_;
							IConsoleVarDump(var, NULL);
							break;
						case ICONSOLE_VAR_INT32:
							*var->data.int32_ = *result->data.int32_;
							IConsoleVarDump(var, NULL);
							break;
						case ICONSOLE_VAR_POINTER:
							var->data.addr = result->data.addr;
							IConsoleVarDump(var, NULL);
							break;
						case ICONSOLE_VAR_STRING:
							IConsoleVarSetString(var, result->data.string_);
							IConsoleVarDump(var, NULL);
							break;
						default:
							IConsoleError("variable type missmatch");
							break;
					}
					IConsoleVarHookHandle(var, ICONSOLE_HOOK_AFTER_CHANGE);
				}

				if (execution_mode == 3) {
					IConsoleVarFree(result);
				}
			}
			break;
		}
		case 5: {
			// execute an alias
			IConsoleAliasExec(alias->cmdline, tokens,tokentypes);
			}
			break;
		default:
			// execution mode invalid
			IConsoleError("invalid execution mode");
			break;
	}

	//** freeing the tokenstream **//
	free(tokenstream_s);
}