about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorMagnus Auvinen <magnus.auvinen@gmail.com>2008-01-12 12:27:55 +0000
committerMagnus Auvinen <magnus.auvinen@gmail.com>2008-01-12 12:27:55 +0000
commit47a0525ab8f99180e1d7a1a74fb6ca620c08f7b5 (patch)
treebe5be984123acb9df49739c4daa50fc9ea92ff80 /src
parent1c1677f02300e5ab10bca9c74ce7f49d4605b9d6 (diff)
downloadzcatch-47a0525ab8f99180e1d7a1a74fb6ca620c08f7b5.tar.gz
zcatch-47a0525ab8f99180e1d7a1a74fb6ca620c08f7b5.zip
merged editor over to trunk
Diffstat (limited to 'src')
-rwxr-xr-xsrc/editor/array.h238
-rw-r--r--src/editor/ed_layer_game.cpp20
-rw-r--r--src/editor/ed_layer_quads.cpp361
-rw-r--r--src/editor/ed_layer_tiles.cpp294
-rw-r--r--src/editor/editor.cpp3307
-rw-r--r--src/editor/editor.hpp581
-rw-r--r--src/engine/client/ec_gfx.c49
-rw-r--r--src/engine/client/ec_ui.c271
-rw-r--r--src/engine/client/ec_ui.h48
-rw-r--r--src/engine/e_datafile.c37
-rw-r--r--src/engine/e_datafile.h2
-rw-r--r--src/engine/e_engine.c9
-rw-r--r--src/engine/e_interface.h9
-rw-r--r--src/engine/e_system.c2
-rw-r--r--src/game/client/gc_client.cpp6
-rw-r--r--src/game/client/gc_mapres_tilemap.cpp7
-rw-r--r--src/game/client/gc_menu.cpp21
-rw-r--r--src/game/server/gs_game.cpp5
18 files changed, 4060 insertions, 1207 deletions
diff --git a/src/editor/array.h b/src/editor/array.h
new file mode 100755
index 00000000..fe9f2739
--- /dev/null
+++ b/src/editor/array.h
@@ -0,0 +1,238 @@
+

+template <class T>

+class array

+{

+	// 

+	//

+	void init()

+	{

+		list = 0;

+		clear();

+	}

+

+public:

+	array()

+	{

+		init();

+	}

+	

+	//

+	array(const array &other)

+	{

+		init();

+		setsize(other.len());

+		for(int i = 0; i < len(); i++)

+			(*this)[i] = other[i];

+	}

+

+

+	// 

+	//

+	virtual ~array()

+	{

+		delete [] list;

+		list = 0;

+	}

+

+	//

+	//

+	void deleteall()

+	{

+		for(int i = 0; i < len(); i++)

+			delete list[i];

+

+		clear();

+	}

+

+

+	//

+	//

+	void clear()

+	{

+		delete [] list;

+		

+		list_size = 1;

+		list = new T[1];

+		num_elements = 0;

+	}

+

+	int find(T val)

+	{

+		for(int i = 0; i < len(); i++)

+			if((*this)[i] == val)

+				return i;

+		return -1;

+	}

+

+	bool exist(T val)

+	{

+		return find(val) != -1;

+	}

+

+	//

+	// returns the number of elements in the list

+	//

+	int len() const

+	{

+		return num_elements;

+	}

+

+	//

+	// This doesn't conserve the order in the list. Be careful

+	//

+	void removebyindexfast(int index)

+	{

+		//ASSUME(_Pos >= 0 && _Pos < num_elements);

+		list[index] = list[num_elements-1];

+		setsize(len()-1);

+	}

+

+	void removefast(const T& _Elem)

+	{

+		for(int i = 0; i < len(); i++)

+			if(list[i] == _Elem)

+			{

+				removebyindexfast(i);

+				return;

+			}

+	}

+

+	//

+	//

+	void removebyindex(int index)

+	{

+		//ASSUME(_Pos >= 0 && _Pos < num_elements);

+

+		for(int i = index+1; i < num_elements; i++)

+			list[i-1] = list[i];

+		

+		setsize(len()-1);

+	}

+

+	void insert(int index, const T& element)

+	{

+		int some_len = len();

+		if (index < some_len)

+			setsize(some_len+1);

+		else

+			setsize(index + 1);

+

+		for(int i = num_elements-2; i >= index; i--)

+			list[i+1] = list[i];

+

+		list[index] = element;

+	}

+

+	bool remove(const T& element)

+	{

+		for(int i = 0; i < len(); i++)

+			if(list[i] == element)

+			{

+				removebyindex(i);

+				return true;

+			}

+		return false;

+	}

+

+	// 

+	//

+	int add(const T& element)

+	{

+		//if(num_elements == list_size)

+		setsize(len()+1);

+		list[num_elements-1] = element;

+		return num_elements-1;

+	}

+

+	// 

+	//

+	int add(const T& elem, int index)

+	{

+		setsize(len()+1);

+		

+		for(int i = num_elements-1; i > index; i--)

+			list[i] = list[i-1];

+

+		list[index] = elem;

+		

+		//num_elements++;

+		return num_elements-1;

+	}

+

+	// 

+	//

+	T& operator[] (int index)

+	{

+		return list[index];

+	}

+

+	const T& operator[] (int index) const

+	{

+		return list[index];

+	}

+

+	//

+	//

+	T *getptr()

+	{

+		return list;

+	}

+

+	const T *getptr() const

+	{

+		return list;

+	}

+

+	//

+	//

+	//

+	void setsize(int new_len)

+	{

+		if (list_size < new_len)

+			allocsize(new_len);

+		num_elements = new_len;

+	}

+

+	// removes unnessasary data, returns how many bytes was earned

+	int optimize()

+	{

+		int Before = memoryusage();

+		setsize(num_elements);

+		return Before - memoryusage();

+	}

+

+	// returns how much memory this dynamic array is using

+	int memoryusage()

+	{

+		return sizeof(array) + sizeof(T)*list_size;

+	}

+

+	//

+	array &operator = (const array &other)

+	{

+		setsize(other.len());

+		for(int i = 0; i < len(); i++)

+			(*this)[i] = other[i];

+		return *this;

+	}		

+private:

+	void allocsize(int new_len)

+	{

+		list_size = new_len;

+		T *new_list = new T[list_size];

+		

+		long end = num_elements < list_size ? num_elements : list_size;

+		for(int i = 0; i < end; i++)

+			new_list[i] = list[i];

+		

+		delete [] list;

+		list = 0;

+		num_elements = num_elements < list_size ? num_elements : list_size;

+		list = new_list;

+	}

+

+	T *list;

+	long list_size;

+	long num_elements;

+};

+

diff --git a/src/editor/ed_layer_game.cpp b/src/editor/ed_layer_game.cpp
new file mode 100644
index 00000000..0e002559
--- /dev/null
+++ b/src/editor/ed_layer_game.cpp
@@ -0,0 +1,20 @@
+#include <game/client/gc_mapres_tilemap.h>
+#include "editor.hpp"
+
+
+LAYER_GAME::LAYER_GAME(int w, int h)
+: LAYER_TILES(w, h)
+{
+	type_name = "Game";
+	game = 1;
+}
+
+LAYER_GAME::~LAYER_GAME()
+{
+}
+
+void LAYER_GAME::render_properties(RECT *toolbox)
+{
+	LAYER_TILES::render_properties(toolbox);
+	image = -1;
+}
diff --git a/src/editor/ed_layer_quads.cpp b/src/editor/ed_layer_quads.cpp
new file mode 100644
index 00000000..d58a9e0f
--- /dev/null
+++ b/src/editor/ed_layer_quads.cpp
@@ -0,0 +1,361 @@
+#include "editor.hpp"
+#include <game/g_math.h>
+
+LAYER_QUADS::LAYER_QUADS()
+{
+	type = LAYERTYPE_QUADS;
+	type_name = "Quads";
+	image = -1;
+}
+
+LAYER_QUADS::~LAYER_QUADS()
+{
+}
+
+static void rotate(POINT *center, POINT *point, float rotation)
+{
+	int x = point->x - center->x;
+	int y = point->y - center->y;
+	point->x = (int)(x * cosf(rotation) - y * sinf(rotation) + center->x);
+	point->y = (int)(x * sinf(rotation) + y * cosf(rotation) + center->y);
+}
+
+static void render_quads(QUAD *quads, int num_quads)
+{
+	gfx_quads_begin();
+	float conv = 1/255.0f;
+	for(int i = 0; i < num_quads; i++)
+	{
+		QUAD *q = &quads[i];
+		
+		gfx_quads_setsubset_free(
+			fx2f(q->texcoords[0].x), fx2f(q->texcoords[0].y),
+			fx2f(q->texcoords[1].x), fx2f(q->texcoords[1].y),
+			fx2f(q->texcoords[2].x), fx2f(q->texcoords[2].y),
+			fx2f(q->texcoords[3].x), fx2f(q->texcoords[3].y)
+		);
+
+		float r=1, g=1, b=1, a=1;
+		float offset_x = 0;
+		float offset_y = 0;
+		float rot = 0;
+		
+		if(editor.animate)
+		{
+			if(q->pos_env >= 0 && q->pos_env < editor.map.envelopes.len())
+			{
+				ENVELOPE *e = editor.map.envelopes[q->pos_env];
+				float t = editor.animate_time+q->pos_env_offset/1000.0f;
+				offset_x = e->eval(t, 0);
+				offset_y = e->eval(t, 1);
+				rot = e->eval(t, 2);
+			}
+			
+			if(q->color_env >= 0 && q->color_env < editor.map.envelopes.len())
+			{
+				ENVELOPE *e = editor.map.envelopes[q->color_env];
+				float t = editor.animate_time+q->color_env_offset/1000.0f;
+				r = e->eval(t, 0);
+				g = e->eval(t, 1);
+				b = e->eval(t, 2);
+				a = e->eval(t, 3);
+			}
+		}
+		
+		gfx_setcolorvertex(0, q->colors[0].r*conv*r, q->colors[0].g*conv*g, q->colors[0].b*conv*b, q->colors[0].a*conv*a);
+		gfx_setcolorvertex(1, q->colors[1].r*conv*r, q->colors[1].g*conv*g, q->colors[1].b*conv*b, q->colors[1].a*conv*a);
+		gfx_setcolorvertex(2, q->colors[2].r*conv*r, q->colors[2].g*conv*g, q->colors[2].b*conv*b, q->colors[2].a*conv*a);
+		gfx_setcolorvertex(3, q->colors[3].r*conv*r, q->colors[3].g*conv*g, q->colors[3].b*conv*b, q->colors[3].a*conv*a);
+
+		POINT *points = q->points;
+	
+		if(rot != 0)
+		{
+			static POINT rotated[4];
+			rotated[0] = q->points[0];
+			rotated[1] = q->points[1];
+			rotated[2] = q->points[2];
+			rotated[3] = q->points[3];
+			points = rotated;
+			
+			rotate(&q->points[4], &rotated[0], rot);
+			rotate(&q->points[4], &rotated[1], rot);
+			rotate(&q->points[4], &rotated[2], rot);
+			rotate(&q->points[4], &rotated[3], rot);
+		}
+		
+		gfx_quads_draw_freeform(
+			fx2f(points[0].x)+offset_x, fx2f(points[0].y)+offset_y,
+			fx2f(points[1].x)+offset_x, fx2f(points[1].y)+offset_y,
+			fx2f(points[2].x)+offset_x, fx2f(points[2].y)+offset_y,
+			fx2f(points[3].x)+offset_x, fx2f(points[3].y)+offset_y
+		);
+	}
+	gfx_quads_end();	
+}
+
+void LAYER_QUADS::render()
+{
+	gfx_texture_set(-1);
+	if(image >= 0 && image < editor.map.images.len())
+		gfx_texture_set(editor.map.images[image]->tex_id);
+		
+	render_quads(quads.getptr(), quads.len());
+}
+
+QUAD *LAYER_QUADS::new_quad()
+{
+	QUAD *q = &quads[quads.add(QUAD())];
+
+	q->pos_env = -1;
+	q->color_env = -1;
+	q->pos_env_offset = 0;
+	q->color_env_offset = 0;
+	int x = 0, y = 0;
+	q->points[0].x = x;
+	q->points[0].y = y;
+	q->points[1].x = x+64;
+	q->points[1].y = y;
+	q->points[2].x = x;
+	q->points[2].y = y+64;
+	q->points[3].x = x+64;
+	q->points[3].y = y+64;
+
+	q->points[4].x = x+32; // pivot
+	q->points[4].y = y+32;
+	
+	for(int i = 0; i < 5; i++)
+	{
+		q->points[i].x <<= 10;
+		q->points[i].y <<= 10;
+	}
+	
+
+	q->texcoords[0].x = 0;
+	q->texcoords[0].y = 0;
+	
+	q->texcoords[1].x = 1<<10;
+	q->texcoords[1].y = 0;
+	
+	q->texcoords[2].x = 0;
+	q->texcoords[2].y = 1<<10;
+	
+	q->texcoords[3].x = 1<<10;
+	q->texcoords[3].y = 1<<10;
+	
+	q->colors[0].r = 255; q->colors[0].g = 255; q->colors[0].b = 255; q->colors[0].a = 255;
+	q->colors[1].r = 255; q->colors[1].g = 255; q->colors[1].b = 255; q->colors[1].a = 255;
+	q->colors[2].r = 255; q->colors[2].g = 255; q->colors[2].b = 255; q->colors[2].a = 255;
+	q->colors[3].r = 255; q->colors[3].g = 255; q->colors[3].b = 255; q->colors[3].a = 255;
+
+	return q;
+}
+
+void LAYER_QUADS::brush_selecting(RECT rect)
+{
+	// draw selection rectangle
+	gfx_texture_set(-1);
+	gfx_lines_begin();
+	gfx_lines_draw(rect.x, rect.y, rect.x+rect.w, rect.y);
+	gfx_lines_draw(rect.x+rect.w, rect.y, rect.x+rect.w, rect.y+rect.h);
+	gfx_lines_draw(rect.x+rect.w, rect.y+rect.h, rect.x, rect.y+rect.h);
+	gfx_lines_draw(rect.x, rect.y+rect.h, rect.x, rect.y);
+	gfx_lines_end();
+}
+
+int LAYER_QUADS::brush_grab(LAYERGROUP *brush, RECT rect)
+{
+	// create new layers
+	LAYER_QUADS *grabbed = new LAYER_QUADS();
+	grabbed->image = image;
+	brush->add_layer(grabbed);
+	
+	//dbg_msg("", "%f %f %f %f", rect.x, rect.y, rect.w, rect.h);
+	for(int i = 0; i < quads.len(); i++)
+	{
+		QUAD *q = &quads[i];
+		float px = fx2f(q->points[4].x);
+		float py = fx2f(q->points[4].y);
+		
+		if(px > rect.x && px < rect.x+rect.w && py > rect.y && py < rect.y+rect.h)
+		{
+			dbg_msg("", "grabbed one");
+			QUAD n;
+			n = *q;
+			
+			for(int p = 0; p < 5; p++)
+			{
+				n.points[p].x -= f2fx(rect.x);
+				n.points[p].y -= f2fx(rect.y);
+			}
+			
+			grabbed->quads.add(n);
+		}
+	}
+	
+	return grabbed->quads.len()?1:0;
+}
+
+void LAYER_QUADS::brush_place(LAYER *brush, float wx, float wy)
+{
+	LAYER_QUADS *l = (LAYER_QUADS *)brush;
+	for(int i = 0; i < l->quads.len(); i++)
+	{
+		QUAD n = l->quads[i];
+		
+		for(int p = 0; p < 5; p++)
+		{
+			n.points[p].x += f2fx(wx);
+			n.points[p].y += f2fx(wy);
+		}
+			
+		quads.add(n);
+	}
+}
+
+void LAYER_QUADS::brush_flip_x()
+{
+}
+
+void LAYER_QUADS::brush_flip_y()
+{
+}
+
+void LAYER_QUADS::get_size(float *w, float *h)
+{
+	*w = 0; *h = 0;
+	
+	for(int i = 0; i < quads.len(); i++)
+	{
+		for(int p = 0; p < 5; p++)
+		{
+			*w = max(*w, fx2f(quads[i].points[p].x));
+			*h = max(*h, fx2f(quads[i].points[p].y));
+		}
+	}
+}
+
+extern int selected_points;
+
+void LAYER_QUADS::render_properties(RECT *toolbox)
+{
+	// layer props
+	if(editor.props == PROPS_LAYER)
+	{
+		enum
+		{
+			PROP_IMAGE=0,
+			NUM_PROPS,
+		};
+		
+		PROPERTY props[] = {
+			{"Image", image, PROPTYPE_INT_STEP, -1, 0},
+			{0},
+		};
+		
+		static int ids[NUM_PROPS] = {0};
+		int new_val = 0;
+		int prop = editor.do_properties(toolbox, props, ids, &new_val);		
+		
+		if(prop == PROP_IMAGE)
+		{
+			if(new_val >= 0)
+				image = new_val%editor.map.images.len();
+			else
+				image = -1;
+		}
+	}
+
+	// quad props
+	QUAD *quad = editor.get_selected_quad();
+	if(editor.props == PROPS_QUAD)
+	{
+		if(quad)
+		{
+			RECT slot;
+			ui_hsplit_t(toolbox, 15.0f, &slot, toolbox);
+			ui_do_label(&slot, "Quad Props", 12.0f, -1, -1);
+			
+			enum
+			{
+				PROP_POS_ENV=0,
+				PROP_POS_ENV_OFFSET,
+				PROP_COLOR_ENV,
+				PROP_COLOR_ENV_OFFSET,
+				NUM_PROPS,
+			};
+			
+			PROPERTY props[] = {
+				{"Pos. Env", quad->pos_env, PROPTYPE_INT_STEP, -1, editor.map.envelopes.len()},
+				{"Pos. TO", quad->pos_env_offset, PROPTYPE_INT_SCROLL, -1000000, 1000000},
+				{"Color Env", quad->color_env, PROPTYPE_INT_STEP, -1, editor.map.envelopes.len()},
+				{"Color TO", quad->color_env_offset, PROPTYPE_INT_SCROLL, -1000000, 1000000},
+				
+				{0},
+			};
+			
+			static int ids[NUM_PROPS] = {0};
+			int new_val = 0;
+			int prop = editor.do_properties(toolbox, props, ids, &new_val);		
+			
+			if(prop == PROP_POS_ENV) quad->pos_env = new_val;
+			if(prop == PROP_POS_ENV_OFFSET) quad->pos_env_offset = new_val;
+			if(prop == PROP_COLOR_ENV) quad->color_env = new_val;
+			if(prop == PROP_COLOR_ENV_OFFSET) quad->color_env_offset = new_val;
+		}
+	}
+	
+	// point props
+	if(editor.props == PROPS_QUAD_POINT && quad && selected_points)
+	{
+		RECT slot;
+		ui_hsplit_t(toolbox, 15.0f, &slot, toolbox);
+		ui_do_label(&slot, "Point Props", 14.0f, -1, -1);
+		
+		enum
+		{
+			PROP_COLOR=0,
+			NUM_PROPS,
+		};
+		
+		int color = 0;
+	
+		for(int v = 0; v < 4; v++)
+		{
+			if(selected_points&(1<<v))
+			{
+				color = 0;
+				color |= quad->colors[v].r<<24;
+				color |= quad->colors[v].g<<16;
+				color |= quad->colors[v].b<<8;
+				color |= quad->colors[v].a;
+			}
+		}
+		
+		
+		PROPERTY props[] = {
+			{"Color", color, PROPTYPE_COLOR, -1, editor.map.envelopes.len()},
+			{0},
+		};
+		
+		static int ids[NUM_PROPS] = {0};
+		int new_val = 0;
+		int prop = editor.do_properties(toolbox, props, ids, &new_val);		
+		if(prop == PROP_COLOR)
+		{
+			for(int v = 0; v < 4; v++)
+			{
+				if(selected_points&(1<<v))
+				{
+					color = 0;
+					quad->colors[v].r = (new_val>>24)&0xff;
+					quad->colors[v].g = (new_val>>16)&0xff;
+					quad->colors[v].b = (new_val>>8)&0xff;
+					quad->colors[v].a = new_val&0xff;
+				}
+			}
+		}
+	}	
+}
+
+
diff --git a/src/editor/ed_layer_tiles.cpp b/src/editor/ed_layer_tiles.cpp
new file mode 100644
index 00000000..f697e56e
--- /dev/null
+++ b/src/editor/ed_layer_tiles.cpp
@@ -0,0 +1,294 @@
+#include <game/client/gc_mapres_tilemap.h>
+#include <game/g_math.h>
+#include "editor.hpp"
+
+static void render_tilemap(TILE *tiles, int w, int h, float scale)
+{
+			//gfx_texture_set(img_get(tmap->image));
+	float screen_x0, screen_y0, screen_x1, screen_y1;
+	gfx_getscreen(&screen_x0, &screen_y0, &screen_x1, &screen_y1);
+
+	// calculate the final pixelsize for the tiles	
+	float tile_pixelsize = 1024/32.0f;
+	float final_tilesize = scale/(screen_x1-screen_x0) * gfx_screenwidth();
+	float final_tilesize_scale = final_tilesize/tile_pixelsize;
+	
+	gfx_quads_begin();
+	
+	int starty = (int)(screen_y0/scale)-1;
+	int startx = (int)(screen_x0/scale)-1;
+	int endy = (int)(screen_y1/scale)+1;
+	int endx = (int)(screen_x1/scale)+1;
+	
+	// adjust the texture shift according to mipmap level
+	float texsize = 1024.0f;
+	float frac = (1.25f/texsize) * (1/final_tilesize_scale);
+	float nudge = (0.5f/texsize) * (1/final_tilesize_scale);
+
+	for(int y = starty; y < endy; y++)
+		for(int x = startx; x < endx; x++)
+		{
+			int mx = x;
+			int my = y;
+			if(mx<0)
+				continue; // mx = 0;
+			if(mx>=w)
+				continue; // mx = w-1;
+			if(my<0)
+				continue; // my = 0;
+			if(my>=h)
+				continue; // my = h-1;
+			
+			int c = mx + my*w;
+				
+			unsigned char index = tiles[c].index;
+			if(index)
+			{
+				unsigned char flags = tiles[c].flags;
+				int tx = index%16;
+				int ty = index/16;
+				int px0 = tx*(1024/16);
+				int py0 = ty*(1024/16);
+				int px1 = (tx+1)*(1024/16)-1;
+				int py1 = (ty+1)*(1024/16)-1;
+				
+				float u0 = nudge + px0/texsize+frac;
+				float v0 = nudge + py0/texsize+frac;
+				float u1 = nudge + px1/texsize-frac;
+				float v1 = nudge + py1/texsize-frac;
+				
+				if(flags&TILEFLAG_VFLIP)
+				{
+					float tmp = u0;
+					u0 = u1;
+					u1 = tmp;
+				}
+
+				if(flags&TILEFLAG_HFLIP)
+				{
+					float tmp = v0;
+					v0 = v1;
+					v1 = tmp;
+				}
+				
+				gfx_quads_setsubset(u0,v0,u1,v1);
+
+				gfx_quads_drawTL(x*scale, y*scale, scale, scale);
+			}
+		}
+	
+	gfx_quads_end();
+}
+
+LAYER_TILES::LAYER_TILES(int w, int h)
+{
+	type = LAYERTYPE_TILES;
+	type_name = "Tiles";
+	width = w;
+	height = h;
+	image = -1;
+	tex_id = -1;
+	game = 0;
+	
+	tiles = new TILE[width*height];
+	mem_zero(tiles, width*height*sizeof(TILE));
+}
+
+LAYER_TILES::~LAYER_TILES()
+{
+	delete [] tiles;
+}
+
+void LAYER_TILES::make_palette()
+{
+	for(int y = 0; y < height; y++)
+		for(int x = 0; x < width; x++)
+			tiles[y*width+x].index = y*16+x;
+}
+
+void LAYER_TILES::render()
+{
+	if(image >= 0 && image < editor.map.images.len())
+		tex_id = editor.map.images[image]->tex_id;
+	gfx_texture_set(tex_id);
+	render_tilemap(tiles, width, height, 32.0f);
+}
+
+int LAYER_TILES::convert_x(float x) const { return (int)(x/32.0f); }
+int LAYER_TILES::convert_y(float y) const { return (int)(y/32.0f); }
+
+void LAYER_TILES::convert(RECT rect, RECTi *out)
+{
+	out->x = convert_x(rect.x);
+	out->y = convert_y(rect.y);
+	out->w = convert_x(rect.x+rect.w+31) - out->x;
+	out->h = convert_y(rect.y+rect.h+31) - out->y;
+}
+
+void LAYER_TILES::snap(RECT *rect)
+{
+	RECTi out;
+	convert(*rect, &out);
+	rect->x = out.x*32.0f;
+	rect->y = out.y*32.0f;
+	rect->w = out.w*32.0f;
+	rect->h = out.h*32.0f;
+}
+
+void LAYER_TILES::clamp(RECTi *rect)
+{
+	if(rect->x < 0)
+	{
+		rect->w += rect->x;
+		rect->x = 0;
+	}
+		
+	if(rect->y < 0)
+	{
+		rect->h += rect->y;
+		rect->y = 0;
+	}
+	
+	if(rect->x+rect->w > width)
+		rect->w = width-rect->x;
+
+	if(rect->y+rect->h > height)
+		rect->h = height-rect->y;
+		
+	if(rect->h < 0)
+		rect->h = 0;
+	if(rect->w < 0)
+		rect->w = 0;
+}
+
+void LAYER_TILES::brush_selecting(RECT rect)
+{
+	gfx_texture_set(-1);
+	gfx_quads_begin();
+	gfx_setcolor(1,1,1,0.4f);
+	snap(&rect);
+	gfx_quads_drawTL(rect.x, rect.y, rect.w, rect.h);
+	gfx_quads_end();
+	
+}
+
+int LAYER_TILES::brush_grab(LAYERGROUP *brush, RECT rect)
+{
+	RECTi r;
+	convert(rect, &r);
+	clamp(&r);
+	
+	if(!r.w || !r.h)
+		return 0;
+	
+	// create new layers
+	LAYER_TILES *grabbed = new LAYER_TILES(r.w, r.h);
+	grabbed->tex_id = tex_id;
+	grabbed->image = image;
+	brush->add_layer(grabbed);
+	
+	// copy the tiles
+	for(int y = 0; y < r.h; y++)
+		for(int x = 0; x < r.w; x++)
+			grabbed->tiles[y*grabbed->width+x] = tiles[(r.y+y)*width+(r.x+x)];
+	
+	return 1;
+}
+
+void LAYER_TILES::brush_draw(LAYER *brush, float wx, float wy)
+{
+	//
+	LAYER_TILES *l = (LAYER_TILES *)brush;
+	int sx = convert_x(wx);
+	int sy = convert_y(wy);
+	
+	for(int y = 0; y < l->height; y++)
+		for(int x = 0; x < l->width; x++)
+		{
+			int fx = x+sx;
+			int fy = y+sy;
+			if(fx<0 || fx >= width || fy < 0 || fy >= height)
+				continue;
+				
+			tiles[fy*width+fx] = l->tiles[y*l->width+x];
+		}
+}
+
+void LAYER_TILES::brush_flip_x()
+{
+	for(int y = 0; y < height; y++)
+		for(int x = 0; x < width/2; x++)
+		{
+			TILE tmp = tiles[y*width+x];
+			tiles[y*width+x] = tiles[y*width+x+width-1-x];
+			tiles[y*width+x+width-1-x] = tmp;
+		}
+
+	for(int y = 0; y < height; y++)
+		for(int x = 0; x < width; x++)
+			tiles[y*width+x].flags ^= TILEFLAG_VFLIP;
+}
+
+void LAYER_TILES::brush_flip_y()
+{
+	for(int y = 0; y < height/2; y++)
+		for(int x = 0; x < width; x++)
+		{
+			TILE tmp = tiles[y*width+x];
+			tiles[y*width+x] = tiles[(height-1-y)*width+x];
+			tiles[(height-1-y)*width+x] = tmp;
+		}
+
+	for(int y = 0; y < height; y++)
+		for(int x = 0; x < width; x++)
+			tiles[y*width+x].flags ^= TILEFLAG_HFLIP;
+}
+
+void LAYER_TILES::resize(int new_w, int new_h)
+{
+	TILE *new_data = new TILE[new_w*new_h];
+	mem_zero(new_data, new_w*new_h*sizeof(TILE));
+
+	// copy old data	
+	for(int y = 0; y < min(new_h, height); y++)
+		mem_copy(&new_data[y*new_w], &tiles[y*width], min(width, new_w)*sizeof(TILE));
+	
+	// replace old
+	delete [] tiles;
+	tiles = new_data;
+	width = new_w;
+	height = new_h;
+}
+
+
+void LAYER_TILES::render_properties(RECT *toolbox)
+{
+	if(editor.props != PROPS_LAYER)
+		return;
+	
+	enum
+	{
+		PROP_IMAGE=0,
+		PROP_WIDTH,
+		PROP_HEIGHT,
+		NUM_PROPS,
+	};
+	
+	PROPERTY props[] = {
+		{"Image", image, PROPTYPE_INT_STEP, 0, 0},
+		{"Width", width, PROPTYPE_INT_STEP, 1, 1000000000},
+		{"Height", height, PROPTYPE_INT_STEP, 1, 1000000000},
+		{0},
+	};
+	
+	static int ids[NUM_PROPS] = {0};
+	int new_val = 0;
+	int prop = editor.do_properties(toolbox, props, ids, &new_val);		
+	
+	if(prop == PROP_IMAGE)
+		image = new_val%editor.map.images.len();
+	else if(prop == PROP_WIDTH && new_val > 1)
+		resize(new_val, height);
+	else if(prop == PROP_HEIGHT && new_val > 1)
+		resize(width, new_val);
+}
diff --git a/src/editor/editor.cpp b/src/editor/editor.cpp
index 33175f7f..161192b5 100644
--- a/src/editor/editor.cpp
+++ b/src/editor/editor.cpp
@@ -1,5 +1,5 @@
-/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
 #include <stdio.h>
+#include <stdlib.h>
 #include <string.h>
 
 extern "C" {
@@ -16,1398 +16,2603 @@ extern "C" {
 #include <game/g_mapres.h>
 #include <game/g_game.h>
 
-static int font_texture = 0;
-static int checker_texture = 0;
-
-struct ent_type
-{
-	const char *name;
-	int id;
-	int numfields;
-	int fields[8];
-};
+#include "editor.hpp"
 
-static ent_type ent_types[] = {
-	{"null", 0, 0, {0}},
-	{"---", 0, 0, {0}},
-	{"spawn", MAPRES_SPAWNPOINT, 0, {0}},
-	{"spawn_red", MAPRES_SPAWNPOINT_RED, 0, {0}},
-	{"spawn_blue", MAPRES_SPAWNPOINT_BLUE, 0, {0}},
-	{"---", 0, 0, {0}},
-	{"flagstand_red", MAPRES_FLAGSTAND_RED, 0, {0}},
-	{"flagstand_blue", MAPRES_FLAGSTAND_BLUE, 0, {0}},
-	{"---", 0, 0, {0}},
-	{"gun", MAPRES_ITEM, 1, {ITEM_WEAPON_GUN,0}},
-	{"shotgun", MAPRES_ITEM, 1, {ITEM_WEAPON_SHOTGUN,0}},
-	{"rocket", MAPRES_ITEM, 1, {ITEM_WEAPON_ROCKET,0}},
-	{"sniper", MAPRES_ITEM, 1, {ITEM_WEAPON_SNIPER,0}},
-	{"hammer", MAPRES_ITEM, 1, {ITEM_WEAPON_HAMMER,0}},
-	{"---", 0, 0, {0}},
-	{"health", MAPRES_ITEM, 1, {ITEM_HEALTH,0}},
-	{"armor", MAPRES_ITEM, 1, {ITEM_ARMOR,0}},
-	{"---", 0, 0, {0}},
-	{"ninja", MAPRES_ITEM, 1, {ITEM_NINJA,0}},
-	{0, 0, 0, {0}}
-};
+static int checker_texture = 0;
+static int background_texture = 0;
+static int cursor_texture = 0;
+static int entities_texture = 0;
 
 
-static int map_theme = 0;
+EDITOR editor;
 
-/********************************************************
- ENTITIES                                                
-*********************************************************/
-struct entity
+LAYERGROUP::LAYERGROUP()
 {
-	int type;
-	int x, y;
-};
-
-static const int MAX_ENTITIES = 1024;
-static entity entites[MAX_ENTITIES];
-static int num_entities = 0;
+	name = "";
+	visible = true;
+	game_group = false;
+	offset_x = 0;
+	offset_y = 0;
+	parallax_x = 100;
+	parallax_y = 100;
+}
 
-static int ents_count()
+LAYERGROUP::~LAYERGROUP()
 {
-	return num_entities;
+	clear();
 }
 
-static entity *ents_get(int index)
+void LAYERGROUP::convert(RECT *rect)
 {
-	return &entites[index];
+	rect->x += offset_x;
+	rect->y += offset_y;
 }
 
-static int ents_delete(int index)
+void LAYERGROUP::mapping(float *points)
 {
-	if(index < 0 || index >= ents_count())
-		return -1;
-	num_entities--;
-	entites[index] = entites[num_entities];
-	return 0;
+	mapscreen_to_world(
+		editor.world_offset_x, editor.world_offset_y,
+		parallax_x/100.0f, parallax_y/100.0f,
+		offset_x, offset_y,
+		gfx_screenaspect(), editor.world_zoom, points);
+		
+	points[0] += editor.editor_offset_x;
+	points[1] += editor.editor_offset_y;
+	points[2] += editor.editor_offset_x;
+	points[3] += editor.editor_offset_y;
 }
 
-static int ents_new(int type, int x, int y)
+void LAYERGROUP::mapscreen()
 {
-	entites[num_entities].type = type;
-	entites[num_entities].x = x;
-	entites[num_entities].y = y;
-	num_entities++;
-	return num_entities-1;
+	float points[4];
+	mapping(points);
+	gfx_mapscreen(points[0], points[1], points[2], points[3]);
 }
 
-/********************************************************
- TILEMAP                                                 
-*********************************************************/
-struct tile
+void LAYERGROUP::render()
 {
-	unsigned char index;
-	unsigned char flags;
-};
+	mapscreen();
+	
+	for(int i = 0; i < layers.len(); i++)
+	{
+		if(layers[i]->visible && layers[i] != editor.game_layer)
+			layers[i]->render();
+	}
+}
+
+bool LAYERGROUP::is_empty() const { return layers.len() == 0; }
+void LAYERGROUP::clear() { layers.deleteall(); }
+void LAYERGROUP::add_layer(LAYER *l) { layers.add(l); }
 
-struct tilemap
+void LAYERGROUP::delete_layer(int index)
 {
-	int width;
-	int height;
-	int render_flags;
-	tile *tiles;
-};
+	if(index < 0 || index >= layers.len()) return;
+	delete layers[index];
+	layers.removebyindex(index);
+}	
 
-// allocates a new tilemap and inits the structure
-static void tilemap_new(tilemap *tm, int width, int height)
+void LAYERGROUP::get_size(float *w, float *h)
 {
-	unsigned size = sizeof(tile)*width*height;
-	mem_zero(tm, sizeof(tilemap));
-	
-	tm->width = width;
-	tm->height = height;
-	tm->render_flags = 0;
-	tm->tiles = (tile *)mem_alloc(size, 1);
-	mem_zero(tm->tiles, size);
+	*w = 0; *h = 0;
+	for(int i = 0; i < layers.len(); i++)
+	{
+		float lw, lh;
+		layers[i]->get_size(&lw, &lh);
+		*w = max(*w, lw);
+		*h = max(*h, lh);
+	}
 }
 
-// resizes an tilemap, copies the old data
-static void tilemap_resize(tilemap *tm, int new_width, int new_height, int snap)
+
+int LAYERGROUP::swap_layers(int index0, int index1)
 {
-	if(new_width <= 0) new_width = 1;
-	if(new_height <= 0) new_height = 1;
-		
-	new_width = (new_width+snap-1)/snap*snap;
-	new_height = (new_height+snap-1)/snap*snap;
-	
-	unsigned size = sizeof(tile)*new_width*new_height;
-	tile *newtiles = (tile *)mem_alloc(size, 1);
-	mem_zero(newtiles, size);
-
-	// copy old tiles
-	int w = new_width < tm->width ? new_width : tm->width;
-	int h = new_height < tm->height ? new_height : tm->height;
-	for(int y = 0; y < h; y++)
-		for(int x = 0; x < w; x++)
-			newtiles[y*new_width+x] = tm->tiles[y*tm->width+x];
-		
-	// free old tiles and set new values
-	mem_free(tm->tiles);
-	tm->tiles = newtiles;
-	tm->width = new_width;
-	tm->height = new_height;
-}
+	if(index0 < 0 || index0 >= layers.len()) return index0;
+	if(index1 < 0 || index1 >= layers.len()) return index0;
+	if(index0 == index1) return index0;
+	swap(layers[index0], layers[index1]);
+	return index1;
+}	
 
-static void tilemap_destroy(tilemap *tm)
+/********************************************************
+ OTHER
+*********************************************************/
+extern void draw_round_rect_ext(float x, float y, float w, float h, float r, int corners);
+extern void draw_round_rect(float x, float y, float w, float h, float r);
+
+static void ui_draw_rect(const RECT *r, vec4 color, int corners, float rounding)
 {
-	mem_free(tm->tiles);
-	tm->tiles = 0;
-	tm->width = 0;
-	tm->height = 0;
+	gfx_texture_set(-1);
+	gfx_quads_begin();
+	gfx_setcolor(color.r, color.g, color.b, color.a);
+	draw_round_rect_ext(r->x,r->y,r->w,r->h,rounding*ui_scale(), corners);
+	gfx_quads_end();
 }
 
-static void tilemap_vflip(tilemap *tm)
+// copied from gc_menu.cpp, should be more generalized
+
+int ui_do_edit_box(void *id, const RECT *rect, char *str, int str_size, bool hidden=false)
 {
-	for(int iy = 0; iy < tm->height; iy++)
-		for(int ix = 0; ix < tm->width/2; ix++)
-		{
-			tile tmp = tm->tiles[iy*tm->width+ix];
-			tm->tiles[iy*tm->width+ix] = tm->tiles[iy*tm->width+tm->width-ix-1];
-			tm->tiles[iy*tm->width+tm->width-ix-1] = tmp;
-		}
-		
-	for(int iy = 0; iy < tm->height; iy++)
-		for(int ix = 0; ix < tm->width; ix++)
-			tm->tiles[iy*tm->width+ix].flags ^= TILEFLAG_VFLIP;
+    int inside = ui_mouse_inside(rect);
+	int r = 0;
+	static int at_index = 0;
 
-}
+	if(ui_last_active_item() == id)
+	{
+		int len = strlen(str);
 
-static void tilemap_hflip(tilemap *tm)
-{
-	for(int iy = 0; iy < tm->height/2; iy++)
-		for(int ix = 0; ix < tm->width; ix++)
+		if (inside && ui_mouse_button(0))
 		{
-			tile tmp = tm->tiles[iy*tm->width+ix];
-			tm->tiles[iy*tm->width+ix] = tm->tiles[tm->height*tm->width-tm->width-tm->width*iy+ix];
-			tm->tiles[tm->height*tm->width-tm->width-tm->width*iy+ix] = tmp;
-		}
+			int mx_rel = (int)(ui_mouse_x() - rect->x);
 
-	for(int iy = 0; iy < tm->height; iy++)
-		for(int ix = 0; ix < tm->width; ix++)
-			tm->tiles[iy*tm->width+ix].flags ^= TILEFLAG_HFLIP;
-}
+			for (int i = 1; i <= len; i++)
+			{
+				if (gfx_pretty_text_width(14.0f, str, i) + 10 > mx_rel)
+				{
+					at_index = i - 1;
+					break;
+				}
 
-static int tilemap_blit(tilemap *dst, tilemap *src, int x, int y)
-{
-	int count = 0;
-	// TODO: performance of this could be better
-	for(int iy = 0; iy < src->height; iy++)
-		for(int ix = 0; ix < src->width; ix++)
-		{
-			if(x+ix >= dst->width || y+iy >= dst->height)
-				continue;
-			if(x+ix < 0 || y+iy < 0)
-				continue;
-			
-			count++;
-			dst->tiles[(y+iy)*dst->width + x+ix] = src->tiles[iy*src->width + ix];
+				if (i == len)
+					at_index = len;
+			}
 		}
-	return count;
-}
 
-/********************************************************
- LAYERS                                                  
-*********************************************************/
-struct layer
-{
-	tilemap tm;
-	int tileset_id;
-	int visible;
-	int main_layer;
-};
+		if (at_index > len)
+			at_index = len;
+			
+		for(int i = 0; i < inp_num_events(); i++)
+		{
+			INPUTEVENT e = inp_get_event(i);
+			char c = e.ch;
+			int k = e.key;
 
-static const int MAX_LAYERS = 64;
-static layer layers[MAX_LAYERS];
-static int num_layers = 0;
-static int current_layer = 0;
-static int current_hover_layer = -1;
+			if (!(c >= 0 && c < 32))
+			{
+				if (len < str_size - 1 && at_index < str_size - 1)
+				{
+					memmove(str + at_index + 1, str + at_index, len - at_index + 1);
+					str[at_index] = c;
+					at_index++;
+				}
+			}
 
-static int layers_remove(int index)
-{
-	if(index < 0 || index >= num_layers)
-		return 0;
+			if (k == KEY_BACKSPACE && at_index > 0)
+			{
+				memmove(str + at_index - 1, str + at_index, len - at_index + 1);
+				at_index--;
+			}
+			else if (k == KEY_DEL && at_index < len)
+				memmove(str + at_index, str + at_index + 1, len - at_index);
+			else if (k == KEY_ENTER)
+				ui_clear_last_active_item();
+			else if (k == KEY_LEFT && at_index > 0)
+				at_index--;
+			else if (k == KEY_RIGHT && at_index < len)
+				at_index++;
+			else if (k == KEY_HOME)
+				at_index = 0;
+			else if (k == KEY_END)
+				at_index = len;
+		}
+		
+		r = 1;
+	}
+
+	bool just_got_active = false;
 	
-	// free the memory
-	mem_free(layers[index].tm.tiles);
+	if(ui_active_item() == id)
+	{
+		if(!ui_mouse_button(0))
+			ui_set_active_item(0);
+	}
+	else if(ui_hot_item() == id)
+	{
+		if(ui_mouse_button(0))
+		{
+			if (ui_last_active_item() != id)
+				just_got_active = true;
+			ui_set_active_item(id);
+		}
+	}
 	
-	// move the layers
-	for(int i = index; i < num_layers-1; i++)
-		layers[i] = layers[i+1];
-	num_layers--;
-	return 1;
+	if(inside)
+		ui_set_hot_item(id);
+
+	RECT textbox = *rect;
+	ui_draw_rect(&textbox, vec4(1,1,1,0.5f), CORNER_ALL, 5.0f);
+	ui_vmargin(&textbox, 5.0f, &textbox);
+	
+	const char *display_str = str;
+	char stars[128];
+	
+	if(hidden)
+	{
+		unsigned s = strlen(str);
+		if(s >= sizeof(stars))
+			s = sizeof(stars)-1;
+		memset(stars, '*', s);
+		stars[s] = 0;
+		display_str = stars;
+	}
+	
+	ui_do_label(&textbox, display_str, 14, -1, -1);
+
+	if (ui_last_active_item() == id && !just_got_active)
+	{
+		float w = gfx_pretty_text_width(14.0f, display_str, at_index);
+		textbox.x += w*ui_scale();
+		ui_do_label(&textbox, "_", 14, -1, -1);
+	}
+
+	return r;
 }
 
-static int layers_count()
+
+static vec4 get_button_color(const void *id, int checked)
 {
-	return num_layers;
+	if(checked < 0)
+		return vec4(0,0,0,0.5f);
+		
+	if(checked > 0)
+	{
+		if(ui_hot_item() == id)
+			return vec4(1,0,0,0.75f);
+		return vec4(1,0,0,0.5f);
+	}
+	
+	if(ui_hot_item() == id)
+		return vec4(1,1,1,0.75f);
+	return vec4(1,1,1,0.5f);
 }
 
-static layer *layers_get_current()
+static void draw_editor_button(const void *id, const char *text, int checked, const RECT *r, const void *extra)
 {
-	return &layers[current_layer];
+	if(ui_hot_item() == id) if(extra) editor.tooltip = (const char *)extra;
+	ui_draw_rect(r, get_button_color(id, checked), CORNER_ALL, 5.0f);
+	ui_do_label(r, text, 10, 0, -1);
 }
 
-static layer *layers_get(int index)
+static void draw_editor_button_l(const void *id, const char *text, int checked, const RECT *r, const void *extra)
 {
-	return &layers[index];
+	if(ui_hot_item() == id) if(extra) editor.tooltip = (const char *)extra;
+	ui_draw_rect(r, get_button_color(id, checked), CORNER_L, 5.0f);
+	ui_do_label(r, text, 10, 0, -1);
 }
 
-static int layers_new(int w, int h)
+static void draw_editor_button_m(const void *id, const char *text, int checked, const RECT *r, const void *extra)
 {
-	if(num_layers+1 >= MAX_LAYERS)
-		return -1;
-	
-	tilemap_new(&layers[num_layers].tm, w, h);
-	layers[num_layers].tileset_id = -1;
-	layers[num_layers].visible = 1;
-	layers[num_layers].main_layer = 0;
-	num_layers++;
-	return num_layers-1;
+	if(ui_hot_item() == id) if(extra) editor.tooltip = (const char *)extra;
+	ui_draw_rect(r, get_button_color(id, checked), 0, 5.0f);
+	ui_do_label(r, text, 10, 0, -1);
 }
 
-static int layers_swap(int index1, int index2)
+static void draw_editor_button_r(const void *id, const char *text, int checked, const RECT *r, const void *extra)
 {
-	// swap the two layers
-	layer temp = layers[index1];
-	layers[index1] = layers[index2];
-	layers[index2] = temp;
-	return -1;
+	if(ui_hot_item() == id) if(extra) editor.tooltip = (const char *)extra;
+	ui_draw_rect(r, get_button_color(id, checked), CORNER_R, 5.0f);
+	ui_do_label(r, text, 10, 0, -1);
 }
 
-static int layers_moveup(int index)
+static void draw_inc_button(const void *id, const char *text, int checked, const RECT *r, const void *extra)
 {
-	if(index < 0 || index >= num_layers)
-		return index;
-	
-	if(index == 0)
-		return 0;
-	
-	layers_swap(index, index-1);
-	return index-1;
+	if(ui_hot_item == id) if(extra) editor.tooltip = (const char *)extra;
+	ui_draw_rect(r, get_button_color(id, checked), CORNER_R, 5.0f);
+	ui_do_label(r, ">", 10, 0, -1);
 }
 
-static int layers_movedown(int index)
+static void draw_dec_button(const void *id, const char *text, int checked, const RECT *r, const void *extra)
 {
-	if(index < 0 || index >= num_layers)
-		return index;
-	
-	if(index+1 == num_layers)
-		return index;
-	
-	layers_swap(index, index+1);
-	return index+1;
+	if(ui_hot_item == id) if(extra) editor.tooltip = (const char *)extra;
+	ui_draw_rect(r, get_button_color(id, checked), CORNER_L, 5.0f);
+	ui_do_label(r, "<", 10, 0, -1);
 }
 
-/********************************************************
- TILESET                                                 
-*********************************************************/
-struct tileset
-{
-	int tex_id;
-	IMAGE_INFO img;
-};
 
-static const int MAX_TILESETS = 64;
-static tileset tilesets[MAX_TILESETS];
-static int num_tilesets = 0;
 
-static int tilesets_new()
+static void render_background(RECT view, int texture, float size, float brightness)
 {
-	tilesets[num_tilesets].img.width = 0;
-	tilesets[num_tilesets].img.height = 0;
-	tilesets[num_tilesets].img.data = 0;
-	tilesets[num_tilesets].tex_id = -1;// gfx_load_texture_raw(img.width, img.height, img.data);
-	num_tilesets++;
-	return num_tilesets-1;
+	gfx_texture_set(texture);
+	gfx_blend_normal();
+	gfx_quads_begin();
+	gfx_setcolor(brightness,brightness,brightness,1.0f);
+	gfx_quads_setsubset(0,0, view.w/size, view.h/size);
+	gfx_quads_drawTL(view.x, view.y, view.w, view.h);
+	gfx_quads_end();
 }
 
 
-static void tilesets_delete(int index)
+static int selected_layer = 0;
+static int selected_group = 0;
+static int selected_quad = -1;
+int selected_points = 0;
+static int selected_envelope = 0;
+
+static LAYERGROUP brush;
+static LAYER_TILES tileset_picker(16, 16);
+
+static int ui_do_value_selector(void *id, RECT *r, const char *label, int current, int min, int max, float scale)
 {
-	// remove the tileset
-	if(tilesets[index].tex_id >= 0)
-		gfx_unload_texture(tilesets[index].tex_id);
-		
-	for(int i = index; i < num_tilesets-1; i++)
-		tilesets[i] = tilesets[i+1];
-	num_tilesets--;
-	
-	// adjust
-	for(int i = 0; i < layers_count(); i++)
+    /* logic */
+    static float value;
+    int ret = 0;
+    int inside = ui_mouse_inside(r);
+
+	if(ui_active_item() == id)
 	{
-		if(layers_get(i)->tileset_id == index)
-			layers_get(i)->tileset_id = -1;
-		else if(layers_get(i)->tileset_id > index)
-			layers_get(i)->tileset_id--;
+		if(!ui_mouse_button(0))
+		{
+			if(inside)
+				ret = 1;
+			editor.lock_mouse = false;
+			ui_set_active_item(0);
+		}
+		else
+		{
+			if(inp_key_pressed(KEY_LSHIFT) || inp_key_pressed(KEY_RSHIFT))
+				value += editor.mouse_delta_x*0.05f;
+			else
+				value += editor.mouse_delta_x;
+			
+			if(fabs(value) > scale)
+			{
+				int count = (int)(value/scale);
+				value = fmod(value, scale);
+				current += count;
+				if(current < min)
+					current = min;
+				if(current > max)
+					current = max;
+			}
+		}
 	}
-}
-
-static void tilesets_clear()
-{
-	for(int i = 0; i < num_tilesets; i++)
+	else if(ui_hot_item() == id)
 	{
-		if(tilesets[i].tex_id >= 0)
-			gfx_unload_texture(tilesets[i].tex_id);
-		mem_free(tilesets[i].img.data);
+		if(ui_mouse_button(0))
+		{
+			editor.lock_mouse = true;
+			value = 0;
+			ui_set_active_item(id);
+		}
 	}
-	num_tilesets = 0;
 	
+	if(inside)
+		ui_set_hot_item(id);
+
+	// render
+	char buf[128];
+	sprintf(buf, "%s %d", label, current);
+	ui_draw_rect(r, get_button_color(id, 0), CORNER_ALL, 5.0f);
+	ui_do_label(r, buf, 12, 0, -1);
+	return current;
 }
 
-static int tilesets_set_img(int index, int w, int h, void *data)
+
+LAYERGROUP *EDITOR::get_selected_group()
 {
-	tilesets[index].img.width = w;
-	tilesets[index].img.height = h;
-	
-	if(tilesets[index].img.data)
-		mem_free(tilesets[index].img.data);
-	tilesets[index].img.data = data;
-	tilesets[index].tex_id = gfx_load_texture_raw(w, h, IMG_RGBA, data, IMG_RGBA);
-	return index;
+	if(selected_group >= 0 && selected_group < editor.map.groups.len())
+		return editor.map.groups[selected_group];
+	return 0x0;
 }
 
-static int tilesets_count()
+LAYER *EDITOR::get_selected_layer(int index)
 {
-	return num_tilesets;
+	LAYERGROUP *group = get_selected_group();
+	if(!group)
+		return 0x0;
+
+	if(selected_layer >= 0 && selected_layer < editor.map.groups[selected_group]->layers.len())
+		return group->layers[selected_layer];
+	return 0x0;
 }
 
-static tileset *tilesets_get(int index)
+LAYER *EDITOR::get_selected_layer_type(int index, int type)
 {
-	return &tilesets[index];
+	LAYER *p = get_selected_layer(index);
+	if(p && p->type == type)
+		return p;
+	return 0x0;
 }
 
-/********************************************************
- UI                                                      
-*********************************************************/
+QUAD *EDITOR::get_selected_quad()
+{
+	LAYER_QUADS *ql = (LAYER_QUADS *)get_selected_layer_type(0, LAYERTYPE_QUADS);
+	if(!ql)
+		return 0;
+	if(selected_quad >= 0 && selected_quad < ql->quads.len())
+		return &ql->quads[selected_quad];
+	return 0;
+}
 
-static const int TILEMAPFLAG_READONLY = 1;
-static const int TILEMAPFLAG_UISPACE = 2;
-static const int TILEMAPFLAG_HILIGHTED = 4;
 
-static void render_tilemap(tilemap *tm, float sx, float sy, float scale, int flags)
+static void do_toolbar(RECT toolbar)
 {
-	float frac = (1.0f/1024.0f); //2.0f;
-	
-	gfx_quads_begin();
-	
-	if(flags&TILEMAPFLAG_HILIGHTED)
-		gfx_setcolor(1.0f,0.5f,0.5f,1.0f);
+	RECT button;
+
+	ui_vsplit_l(&toolbar, 16.0f, &button, &toolbar);
+	static int zoom_out_button = 0;
+	if(ui_do_button(&zoom_out_button, "ZO", 0, &button, draw_editor_button_l, "[NumPad-] Zoom out") || inp_key_down(KEY_KP_SUBTRACT))
+		editor.zoom_level += 50;
+		
+	ui_vsplit_l(&toolbar, 16.0f, &button, &toolbar);
+	static int zoom_normal_button = 0;
+	if(ui_do_button(&zoom_normal_button, "1:1", 0, &button, draw_editor_button_m, "[NumPad*] Zoom to normal and remove editor offset") || inp_key_down(KEY_KP_MULTIPLY))
+	{
+		editor.editor_offset_x = 0;
+		editor.editor_offset_y = 0;
+		editor.zoom_level = 100;
+	}
+		
+	ui_vsplit_l(&toolbar, 16.0f, &button, &toolbar);
+	static int zoom_in_button = 0;
+	if(ui_do_button(&zoom_in_button, "ZI", 0, &button, draw_editor_button_r, "[NumPad+] Zoom in") || inp_key_down(KEY_KP_ADD))
+		editor.zoom_level -= 50;
 	
-	for(int y = 0; y < tm->height; y++)
-		for(int x = 0; x < tm->width; x++)
+	if(editor.zoom_level < 50)
+		editor.zoom_level = 50;
+	editor.world_zoom = editor.zoom_level/100.0f;
+
+	ui_vsplit_l(&toolbar, 10.0f, &button, &toolbar);
+
+
+	// brush manipulation
+	{	
+		int enabled = brush.is_empty()?-1:0;
+		
+		// flip buttons
+		ui_vsplit_l(&toolbar, 20.0f, &button, &toolbar);
+		static int flipx_button = 0;
+		if(ui_do_button(&flipx_button, "^X", enabled, &button, draw_editor_button_l, "Flip brush horizontal"))
+		{
+			for(int i = 0; i < brush.layers.len(); i++)
+				brush.layers[i]->brush_flip_x();
+		}
+			
+		ui_vsplit_l(&toolbar, 20.0f, &button, &toolbar);
+		static int flipy_button = 0;
+		if(ui_do_button(&flipy_button, "^Y", enabled, &button, draw_editor_button_r, "Flip brush vertical"))
 		{
-			unsigned char d = tm->tiles[y*tm->width+x].index;
-			unsigned char f = tm->tiles[y*tm->width+x].flags;
-			if(d)
+			for(int i = 0; i < brush.layers.len(); i++)
+				brush.layers[i]->brush_flip_y();
+		}
+	}
+
+	// quad manipulation
+	{
+		// do add button
+		ui_vsplit_l(&toolbar, 10.0f, &button, &toolbar);
+		ui_vsplit_l(&toolbar, 60.0f, &button, &toolbar);
+		static int new_button = 0;
+		
+		LAYER_QUADS *qlayer = (LAYER_QUADS *)editor.get_selected_layer_type(0, LAYERTYPE_QUADS);
+		LAYER_TILES *tlayer = (LAYER_TILES *)editor.get_selected_layer_type(0, LAYERTYPE_TILES);
+		if(ui_do_button(&new_button, "Add Quad", qlayer?0:-1, &button, draw_editor_button, "Adds a new quad"))
+		{
+			if(qlayer)
 			{
-				float u0 = (d%16)/16.0f+frac;
-				float v0 = (d/16)/16.0f+frac;
-				float u1 = (d%16)/16.0f+1.0f/16.0f-frac;
-				float v1 = (d/16)/16.0f+1.0f/16.0f-frac;
-				if(f&TILEFLAG_VFLIP)
+				float mapping[4];
+				LAYERGROUP *g = editor.get_selected_group();
+				g->mapping(mapping);
+				int add_x = f2fx(mapping[0] + (mapping[2]-mapping[0])/2);
+				int add_y = f2fx(mapping[1] + (mapping[3]-mapping[1])/2);
+				
+				QUAD *q = qlayer->new_quad();
+				for(int i = 0; i < 5; i++)
 				{
-					float tmp = u0;
-					u0 = u1;
-					u1 = tmp;
+					q->points[i].x += add_x;
+					q->points[i].y += add_y;
 				}
+			}
+		}
 
-				if(f&TILEFLAG_HFLIP)
+		ui_vsplit_l(&toolbar, 10.0f, &button, &toolbar);
+		ui_vsplit_l(&toolbar, 60.0f, &button, &toolbar);
+		static int sq_button = 0;
+		if(ui_do_button(&sq_button, "Sq. Quad", editor.get_selected_quad()?0:-1, &button, draw_editor_button, "Squares the current quad"))
+		{
+			QUAD *q = editor.get_selected_quad();
+			if(q)
+			{
+				int top = q->points[0].y;
+				int left = q->points[0].x;
+				int bottom = q->points[0].y;
+				int right = q->points[0].x;
+				
+				for(int k = 1; k < 4; k++)
 				{
-					float tmp = v0;
-					v0 = v1;
-					v1 = tmp;
+					if(q->points[k].y < top) top = q->points[k].y;
+					if(q->points[k].x < left) left = q->points[k].x;
+					if(q->points[k].y > bottom) bottom = q->points[k].y;
+					if(q->points[k].x > right) right = q->points[k].x;
 				}
 				
-				gfx_quads_setsubset(u0,v0,u1,v1);
-				gfx_quads_drawTL(sx+x*scale, sy+y*scale, scale, scale);
+				q->points[0].x = left; q->points[0].y = top;
+				q->points[1].x = right; q->points[1].y = top;
+				q->points[2].x = left; q->points[2].y = bottom;
+				q->points[3].x = right; q->points[3].y = bottom;
 			}
+		}
 
-			//gfx_quads_setsubset(x/16.0f,y/16.0f,(x+1)/16.0f,(y+1)/16.0f);
-			//gfx_quads_drawTL(sx+x*w,sy+y*h,w,h);
+		ui_vsplit_l(&toolbar, 20.0f, &button, &toolbar);
+		ui_vsplit_l(&toolbar, 60.0f, &button, &toolbar);
+		bool in_gamegroup = editor.game_group->layers.find(tlayer) != -1;
+		static int col_button = 0;
+		if(ui_do_button(&col_button, "Make Col", (in_gamegroup&&tlayer)?0:-1, &button, draw_editor_button, "Constructs collision from the current layer"))
+		{
+			LAYER_TILES *gl = editor.game_layer;
+			int w = min(gl->width, tlayer->width);
+			int h = min(gl->height, tlayer->height);
+			dbg_msg("", "w=%d h=%d", w, h);
+			for(int y = 0; y < h; y++)
+				for(int x = 0; x < w; x++)
+				{
+					if(gl->tiles[y*gl->width+x].index <= TILE_SOLID)
+						gl->tiles[y*gl->width+x].index = tlayer->tiles[y*tlayer->width+x].index?TILE_SOLID:TILE_AIR;
+				}
 		}
-	gfx_quads_end();
+	}
 }
 
-/********************************************************
- EDITOR                                                  
-*********************************************************/
-
-static void ui_do_frame(float x, float y, float w, float h)
+static void rotate(POINT *center, POINT *point, float rotation)
 {
-	static int frame_id = 0;
-    int inside = ui_mouse_inside(x,y,w,h);
-	if(inside)
-		ui_set_hot_item(&frame_id);
-		
-	gfx_texture_set(-1);
-	gfx_blend_normal();
-	gfx_quads_begin();
-	gfx_setcolor(0.0f, 0.0f, 0.0f, 1.0f);
-	gfx_quads_drawTL(x, y, w, h);
-	gfx_quads_end();
-	gfx_blend_normal();	
+	int x = point->x - center->x;
+	int y = point->y - center->y;
+	point->x = (int)(x * cosf(rotation) - y * sinf(rotation) + center->x);
+	point->y = (int)(x * sinf(rotation) + y * cosf(rotation) + center->y);
 }
 
-static tilemap brush = {0};
-static tilemap chooser = {0};
-static float world_offset_x = 0, world_offset_y = 0;
-static int world_zoom = 3;
-static char editor_filename[128] = {0};
-static int editor_mode = 0; // 0 == tiles, 1 == ents
-static int editor_selected_ent = -1;
-
-static int ui_do_tilemap(void *id, tilemap *tm, int flags, float x, float y, float scale)
+static void do_quad(QUAD *q, int index)
 {
-	/*
-	int do_input = 1;
-	if(inp_key_pressed(KEY_LALT) || inp_key_pressed(KEY_RALT))
-		do_input = 0;*/
-	
-	float mx = ui_mouse_world_x();
-	float my = ui_mouse_world_y();
-	if(flags&TILEMAPFLAG_UISPACE)
+	enum
+	{
+		OP_NONE=0,
+		OP_MOVE_ALL,
+		OP_MOVE_PIVOT,
+		OP_ROTATE,
+	};
+	
+	// some basic values
+	void *id = &q->points[4]; // use pivot addr as id
+	static POINT rotate_points[4];
+	static float last_wx;
+	static float last_wy;
+	static int operation = OP_NONE;
+	static float rotate_angle = 0;
+	float wx = ui_mouse_world_x();
+	float wy = ui_mouse_world_y();
+	
+	// get pivot
+	float center_x = fx2f(q->points[4].x);
+	float center_y = fx2f(q->points[4].y);
+
+	float dx = (center_x - wx);
+	float dy = (center_y - wy);
+	if(dx*dx+dy*dy < 10*10)
+		ui_set_hot_item(id);
+
+	// draw selection background	
+	if(selected_quad == index)
 	{
-		mx = ui_mouse_x();
-		my = ui_mouse_y();
+		gfx_setcolor(0,0,0,1);
+		gfx_quads_draw(center_x, center_y, 7.0f, 7.0f);
 	}
 	
-	int tmx = (int)((mx-x)/scale); // tilemap x
-	int tmy = (int)((my-y)/scale); // tilemap y
-	
-	static int start_tmx, start_tmy;
-	static int grabbing = 0;
-	
-	//float start_tmx_wx = start_tmx*scale+x;
-	//float start_tmx_wy = start_tmy*scale+y;
-	
-	int select_x = 0;
-	int select_y = 0;
-	int select_w = 1;
-	int select_h = 1;
-	float select_wx = 0;
-	float select_wy = 0;
-	float select_ww = 0;
-	float select_wh = 0;
-	
-	if(ui_hot_item() == id)
+	if(ui_active_item() == id)
 	{
-		int x0 = start_tmx;
-		int y0 = start_tmy;
-		int x1 = tmx;
-		int y1 = tmy;
-		
-		if(x1 < x0)
+		// check if we only should move pivot
+		if(operation == OP_MOVE_PIVOT)
 		{
-			int tmp = x1;
-			x1 = x0;
-			x0 = tmp;
+			q->points[4].x += f2fx(wx-last_wx);
+			q->points[4].y += f2fx(wy-last_wy);
 		}
-		
-		if(y1 < y0)
+		else if(operation == OP_MOVE_ALL)
+		{
+			// move all points including pivot
+			for(int v = 0; v < 5; v++)
+			{
+				q->points[v].x += f2fx(wx-last_wx);
+				q->points[v].y += f2fx(wy-last_wy);
+			}
+		}
+		else if(operation == OP_ROTATE)
 		{
-			int tmp = y1;
-			y1 = y0;
-			y0 = tmp;
+			for(int v = 0; v < 4; v++)
+			{
+				q->points[v] = rotate_points[v];
+				rotate(&q->points[4], &q->points[v], rotate_angle);
+			}
 		}
 		
-		select_w = x1-x0;
-		select_h = y1-y0;
-		select_w++;
-		select_h++;
-		select_x = x0;
-		select_y = y0;
-		
-		select_wx = select_x*scale+x;
-		select_wy = select_y*scale+y;
-		select_ww = select_w*scale;
-		select_wh = select_h*scale;
-	}
-	
-	// ui_do_tilemap always tries to steal the focus
-	ui_set_hot_item(id);
-	
-	// render the tilemap
-	render_tilemap(tm, x, y, scale, flags);
-	
-	if(ui_hot_item() == id)
-	{
-		if(brush.tiles != 0)
+		rotate_angle += (editor.mouse_delta_x) * 0.002f;
+		last_wx = wx;
+		last_wy = wy;
+		
+		if(!ui_mouse_button(0))
 		{
-			// draw brush
-			render_tilemap(&brush, (tmx-brush.width/2)*scale, (tmy-brush.height/2)*scale, scale, flags);
-			
-			gfx_texture_set(-1);
-			gfx_blend_additive();
-			gfx_quads_begin();
-			gfx_setcolor(1.0f, 0.0f, 0.0f, 0.25f);
-			gfx_quads_drawTL((tmx-brush.width/2)*scale, (tmy-brush.height/2)*scale, brush.width*scale, brush.height*scale);
-			gfx_quads_end();
-			gfx_blend_normal();
+			editor.lock_mouse = false;
+			operation = OP_NONE;
+			ui_set_active_item(0);
 		}
 
-		if(grabbing == 0)
+		gfx_setcolor(1,1,1,1);
+	}
+	else if(ui_hot_item() == id)
+	{
+		gfx_setcolor(1,1,1,1);
+		editor.tooltip = "Left mouse button to move. Hold shift to move pivot. Hold ctrl to rotate";
+		
+		if(ui_mouse_button(0))
 		{
-			if(ui_mouse_button(0))
+			if(inp_key_pressed(KEY_LSHIFT) || inp_key_pressed(KEY_RSHIFT))
+				operation = OP_MOVE_PIVOT;
+			else if(inp_key_pressed(KEY_LCTRL) || inp_key_pressed(KEY_RCTRL))
 			{
-				//grabbing = 1;
-				start_tmx = (int)((mx-x)/scale);
-				start_tmy = (int)((my-y)/scale);
-				if(brush.tiles == 0)
-				{
-					dbg_msg("editor", "grabbing...");
-					grabbing = 1; // grab tiles
-				}
-				else
-				{
-					// paint
-					if(!(flags&TILEMAPFLAG_READONLY))
-					{
-						layer *l = layers_get_current();
-						int px = tmx-brush.width/2;
-						int py = tmy-brush.height/2;
-						tilemap_blit(&l->tm, &brush, px, py);
-						//dbg_msg("editor", "painted %d tiles at (%d,%d)", c, px, py);
-					}
-				}
+				editor.lock_mouse = true;
+				operation = OP_ROTATE;
+				rotate_angle = 0;
+				rotate_points[0] = q->points[0];
+				rotate_points[1] = q->points[1];
+				rotate_points[2] = q->points[2];
+				rotate_points[3] = q->points[3];
 			}
-		}
-		else
-		{
-			gfx_texture_set(-1);
-			gfx_blend_additive();
-			gfx_quads_begin();
-			gfx_setcolor(1.0f, 1.0f, 1.0f, 0.25f);
-			gfx_quads_drawTL(select_wx, select_wy, select_ww, select_wh);
-			gfx_quads_end();
-			gfx_blend_normal();
-			
-			if(!ui_mouse_button(0))
-			{
-				grabbing = 0;
+			else
+				operation = OP_MOVE_ALL;
 				
-				if(brush.tiles == 0)
-				{
-					// create brush
-					dbg_msg("editor", "creating brush w=%d h=%d p0=(%d,%d)", select_w, select_h, select_x, select_y);
-					tilemap_new(&brush, select_w, select_h);
-					
-					// copy data
-					for(int y = 0; y < select_h; y++)
-						for(int x = 0; x < select_w; x++)
-							brush.tiles[y*select_w+x] = tm->tiles[(select_y+y)*tm->width + select_x+x];
-				}
-			}
+			ui_set_active_item(id);
+			selected_quad = index;
+			editor.props = PROPS_QUAD;
+			last_wx = wx;
+			last_wy = wy;
 		}
 	}
-	
-	// raw rect around tilemap
-	gfx_texture_set(-1);
-	gfx_quads_begin();
-	float w = tm->width*scale;
-	float h = tm->height*scale;
-	gfx_quads_drawTL(x-2, y-2, 2, h+4);
-	gfx_quads_drawTL(x-2, y-2, w+4, 2);
-	gfx_quads_drawTL(x+w, y-2, 2, h+4);
-	gfx_quads_drawTL(x-2, y+h, w+4, 2);
-	gfx_quads_end();
-	
-	return 0;
+	else
+		gfx_setcolor(0,1,0,1);
+
+	gfx_quads_draw(center_x, center_y, 5.0f, 5.0f);
 }
 
-static int ui_do_entity(void *id, entity *ent, int selected)
+static void do_quad_point(QUAD *q, int quad_index, int v)
 {
-	float x = (float)ent->x;
-	float y = (float)ent->y;
-	float w = 16;
-	float h = 16;
-	
-	float mx = ui_mouse_world_x();
-	float my = ui_mouse_world_y();
+	void *id = &q->points[v];
+
+	float wx = ui_mouse_world_x();
+	float wy = ui_mouse_world_y();
 	
-	int inside = 0;
-	int r = 0;
-	if(mx > x-w/2 && mx < x+w/2 && my > y-h/2 && my < y+h/2)
-		inside = 1;
+	float px = fx2f(q->points[v].x);
+	float py = fx2f(q->points[v].y);
 	
-	if(inside)
+	float dx = (px - wx);
+	float dy = (py - wy);
+	if(dx*dx+dy*dy < 10*10)
 		ui_set_hot_item(id);
-	
-	if(ui_hot_item() == id && ui_mouse_button(0))
+
+	// draw selection background	
+	if(selected_quad == quad_index && selected_points&(1<<v))
 	{
-		ui_set_active_item(id);
-		r = 1;
+		gfx_setcolor(0,0,0,1);
+		gfx_quads_draw(px, py, 7.0f, 7.0f);
 	}
 	
+	enum
+	{
+		OP_NONE=0,
+		OP_MOVEPOINT,
+		OP_MOVEUV
+	};
+	
+	static bool moved;
+	static int operation = OP_NONE;
+
 	if(ui_active_item() == id)
 	{
+		float dx = editor.mouse_delta_wx;
+		float dy = editor.mouse_delta_wy;
+		if(!moved)
+		{
+			if(dx*dx+dy*dy > 0.5f)
+				moved = true;
+		}
+		
+		if(moved)
+		{
+			if(operation == OP_MOVEPOINT)
+			{
+				for(int m = 0; m < 4; m++)
+					if(selected_points&(1<<m))
+					{
+						q->points[m].x += f2fx(dx);
+						q->points[m].y += f2fx(dy);
+					}
+			}
+			else if(operation == OP_MOVEUV)
+			{
+				for(int m = 0; m < 4; m++)
+					if(selected_points&(1<<m))
+					{
+						q->texcoords[m].x += f2fx(dx*0.001f);
+						q->texcoords[m].y += f2fx(dy*0.001f);
+					}
+			}
+		}
+		
 		if(!ui_mouse_button(0))
+		{
+			if(!moved)
+			{
+				if(inp_key_pressed(KEY_LSHIFT) || inp_key_pressed(KEY_RSHIFT))
+					selected_points ^= 1<<v;
+				else
+					selected_points = 1<<v;
+					
+				editor.props = PROPS_QUAD_POINT;
+			}
+			editor.lock_mouse = false;
 			ui_set_active_item(0);
-		ent->x = (int)ui_mouse_world_x();
-		ent->y = (int)ui_mouse_world_y();
-		ent->x = (ent->x/32)*32+16;
-		ent->y = (ent->y/32)*32+16;
+		}
+
+		gfx_setcolor(1,1,1,1);
 	}
-	
-	// raw rect around tilemap
-	gfx_texture_set(-1);
-	gfx_quads_begin();
-	if(selected)
-		gfx_setcolor(1.0f, 0.5f, 0.5f, 0.95f);
 	else if(ui_hot_item() == id)
-		gfx_setcolor(1.0f, 1.0f, 1.0f, 0.95f);
-	else
-		gfx_setcolor(0.75f, 0.75f, 0.75f, 0.95f);
+	{
+		gfx_setcolor(1,1,1,1);
+		editor.tooltip = "Left mouse button to move. Hold shift to move the texture.";
 		
-	gfx_quads_drawTL(x-w/2, y-w/2, w, h);
-	gfx_quads_end();
-	
-    gfx_texture_set(font_texture);
-	if(ent->type >= 0)
-		gfx_quads_text(x-4, y-h/2-4, 24.0f, ent_types[ent->type].name);
+		if(ui_mouse_button(0))
+		{
+			ui_set_active_item(id);
+			moved = false;
+			if(inp_key_pressed(KEY_LSHIFT) || inp_key_pressed(KEY_RSHIFT))
+			{
+				operation = OP_MOVEUV;
+				editor.lock_mouse = true;
+			}
+			else
+				operation = OP_MOVEPOINT;
+				
+			if(!(selected_points&(1<<v)))
+			{
+				if(inp_key_pressed(KEY_LSHIFT) || inp_key_pressed(KEY_RSHIFT))
+					selected_points |= 1<<v;
+				else
+					selected_points = 1<<v;
+				moved = true;
+			}
+															
+			editor.props = PROPS_QUAD_POINT;
+			selected_quad = quad_index;
+		}
+	}
+	else
+		gfx_setcolor(1,0,0,1);
 	
-	return r;
+	gfx_quads_draw(px, py, 5.0f, 5.0f);	
 }
 
-
-static int editor_reset()
+static void do_map_editor(RECT view, RECT toolbar)
 {
-	// delete all layers
-	while(layers_count())
-		layers_remove(layers_count()-1);
+	// do the toolbar
+	if(editor.gui_active)
+		do_toolbar(toolbar);
 	
-	tilemap_destroy(&brush);
-	current_layer = 0;
+	ui_clip_enable(&view);
 	
-	tilesets_clear();
-	
-	// init chooser
-	static tile chooser_tiles[256];
-	for(int i = 0; i < 256; i++)
+	bool show_picker = inp_key_pressed(KEY_SPACE) != 0;
+
+	// render all good stuff
+	if(!show_picker)
 	{
-		chooser_tiles[i].index = i;
-		chooser_tiles[i].flags = 0;
+		for(int g = 0; g < editor.map.groups.len(); g++)
+		{
+			if(editor.map.groups[g]->visible)
+				editor.map.groups[g]->render();
+		}
+		
+		// render the game above everything else
+		if(editor.game_group->visible && editor.game_layer->visible)
+		{
+			editor.game_group->mapscreen();
+			editor.game_layer->render();
+		}
 	}
-	
-	chooser.width = 16;
-	chooser.height = 16;
-	chooser.tiles = chooser_tiles;
-	
-	return 0;
-}
-
-void draw_editor_button(const void *id, const char *text, int checked, float x, float y, float w, float h, void *extra)
-{
-    gfx_blend_normal();
-    gfx_texture_set(-1);
-    gfx_quads_begin(); 
-    if(ui_hot_item() == id)
-        gfx_setcolor(1,1,1,1);
-    else if(checked)
-        gfx_setcolor(0.75f,0.5f,0.5f,1);
-    else
-        gfx_setcolor(0.5f,0.5f,0.5f,1);
-
-    gfx_quads_drawTL(x,y,w,h);
-    gfx_quads_end();
-    gfx_pretty_text(x+1, y-1, 6.5f, text, -1);
-}
 
+	static void *editor_id = (void *)&editor_id;
+	int inside = ui_mouse_inside(&view);
 
-
-int editor_load(const char *filename)
-{
-	editor_reset();
+	// fetch mouse position		
+	float wx = ui_mouse_world_x();
+	float wy = ui_mouse_world_y();
+	float mx = ui_mouse_x();
+	float my = ui_mouse_y();
 	
-	DATAFILE *df = datafile_load(filename);
-	if(!df)
-		return 0;
+	static float start_wx = 0;
+	static float start_wy = 0;
+	static float start_mx = 0;
+	static float start_my = 0;
 	
-	// load tilesets
+	enum
 	{
-		int start, count;
-		datafile_get_type(df, MAPRES_IMAGE, &start, &count);
-		for(int i = 0; i < count; i++)
+		OP_NONE=0,
+		OP_BRUSH_GRAB,
+		OP_BRUSH_DRAW,
+		OP_PAN_WORLD,
+		OP_PAN_EDITOR,
+	};
+
+	// remap the screen so it can display the whole tileset
+	if(show_picker)
+	{
+		RECT screen = *ui_screen();
+		float size = 32.0*16.0f;
+		float w = size*(screen.w/view.w);
+		float h = size*(screen.h/view.h);
+		float x = -(view.x/screen.w)*w;
+		float y = -(view.y/screen.h)*h;
+		wx = x+w*mx/screen.w;
+		wy = y+h*my/screen.h;
+		gfx_mapscreen(x, y, x+w, y+h);
+		LAYER_TILES *t = (LAYER_TILES *)editor.get_selected_layer_type(0, LAYERTYPE_TILES);
+		if(t)
 		{
-			mapres_image *img = (mapres_image *)datafile_get_item(df, start+i, 0, 0);
-			void *data = datafile_get_data(df, img->image_data);
-			int id = tilesets_new();
-			void *data_cpy = mem_alloc(img->width*img->height*4, 1);
-			mem_copy(data_cpy, data, img->width*img->height*4);
-			tilesets_set_img(id, img->width, img->height, data_cpy);
+			tileset_picker.image = t->image;
+			tileset_picker.tex_id = t->tex_id;
+			tileset_picker.render();
 		}
 	}
 	
-	// load tilemaps
+	static int operation = OP_NONE;
+	
+	// draw layer borders
+	LAYER *edit_layers[16];
+	int num_edit_layers = 0;
+	num_edit_layers = 0;
+	
+	if(show_picker)
 	{
-		int start, num;
-		datafile_get_type(df, MAPRES_TILEMAP, &start, &num);
-		for(int t = 0; t < num; t++)
+		edit_layers[0] = &tileset_picker;
+		num_edit_layers++;
+	}
+	else
+	{
+		edit_layers[0] = editor.get_selected_layer(0);
+		if(edit_layers[0])
+			num_edit_layers++;
+
+		LAYERGROUP *g = editor.get_selected_group();
+		if(g)
 		{
-			mapres_tilemap *tmap = (mapres_tilemap *)datafile_get_item(df, start+t,0,0);
-			//unsigned char *data = (unsigned char *)datafile_get_data(df, tmap->data);
+			g->mapscreen();
 			
-			layer *l = layers_get(layers_new(tmap->width, tmap->height));
-			mem_copy(l->tm.tiles, datafile_get_data(df, tmap->data), tmap->width*tmap->height*2);
-			l->tileset_id = tmap->image;
-			l->main_layer = tmap->main;
-			
-			// force a main layer
-			if(num == 1)
-				l->main_layer = 1;
+			for(int i = 0; i < num_edit_layers; i++)
+			{
+				if(edit_layers[i]->type != LAYERTYPE_TILES)
+					continue;
+					
+				float w, h;
+				edit_layers[i]->get_size(&w, &h);
+
+				gfx_texture_set(-1);
+				gfx_lines_begin();
+				gfx_lines_draw(0,0, w,0);
+				gfx_lines_draw(w,0, w,h);
+				gfx_lines_draw(w,h, 0,h);
+				gfx_lines_draw(0,h, 0,0);
+				gfx_lines_end();
+			}
 		}
 	}
-	
-	// load entities
-	for(int t = MAPRES_ENTS_START; t < MAPRES_ENTS_END; t++)
+		
+	if(inside)
 	{
-		// fetch entities of this class
-		int start, num;
-		datafile_get_type(df, t, &start, &num);
+		ui_set_hot_item(editor_id);
+		
+		// do global operations like pan and zoom
+		if(ui_active_item() == 0 && ui_mouse_button(0))
+		{
+			start_wx = wx;
+			start_wy = wy;
+			start_mx = mx;
+			start_my = my;
+					
+			if(inp_key_pressed(KEY_LALT))
+			{
+				if(inp_key_pressed(KEY_LSHIFT))
+					operation = OP_PAN_EDITOR;
+				else
+					operation = OP_PAN_WORLD;
+				ui_set_active_item(editor_id);
+			}
+		}
 
-		for(int i = 0; i < num; i++)
+		// brush editing
 		{
-			mapres_entity *e = (mapres_entity *)datafile_get_item(df, start+i,0,0);
-			
-			// map type	
-			int type = 0;
-			for(int k = 0; ent_types[k].name; k++)
+			if(brush.is_empty())
+				editor.tooltip = "Use left mouse button to drag and create a brush.";
+			else
+				editor.tooltip = "Use left mouse button to paint with the brush. Right button clears the brush.";
+
+			if(ui_active_item() == editor_id)
 			{
-				if(ent_types[k].id == t &&
-					memcmp(ent_types[k].fields, e->data, ent_types[k].numfields*sizeof(int)) == 0)
+				RECT r;
+				r.x = start_wx;
+				r.y = start_wy;
+				r.w = wx-start_wx;
+				r.h = wy-start_wy;
+				if(r.w < 0)
 				{
-					type = k;
-					break;
+					r.x += r.w;
+					r.w = -r.w;
+				}
+
+				if(r.h < 0)
+				{
+					r.y += r.h;
+					r.h = -r.h;
+				}
+				
+				if(operation == OP_BRUSH_DRAW)
+				{						
+					if(!brush.is_empty())
+					{
+						// draw with brush
+						for(int k = 0; k < num_edit_layers; k++)
+						{
+							if(edit_layers[k]->type == brush.layers[0]->type)
+								edit_layers[k]->brush_draw(brush.layers[0], wx, wy);
+						}
+					}
+				}
+				else if(operation == OP_BRUSH_GRAB)
+				{
+					if(!ui_mouse_button(0))
+					{
+						// grab brush
+						dbg_msg("editor", "grabbing %f %f %f %f", r.x, r.y, r.w, r.h);
+						
+						// TODO: do all layers
+						int grabs = 0;
+						for(int k = 0; k < num_edit_layers; k++)
+							grabs += edit_layers[k]->brush_grab(&brush, r);
+						if(grabs == 0)
+							brush.clear();
+					}
+					else
+					{
+						editor.map.groups[selected_group]->mapscreen();
+						for(int k = 0; k < num_edit_layers; k++)
+							edit_layers[k]->brush_selecting(r);
+						gfx_mapscreen(ui_screen()->x, ui_screen()->y, ui_screen()->w, ui_screen()->h);
+					}
+				}
+			}
+			else
+			{
+				if(ui_mouse_button(1))
+					brush.clear();
+					
+				if(ui_mouse_button(0) && operation == OP_NONE)
+				{
+					ui_set_active_item(editor_id);
+					
+					if(brush.is_empty())
+						operation = OP_BRUSH_GRAB;
+					else
+					{
+						operation = OP_BRUSH_DRAW;
+						for(int k = 0; k < num_edit_layers; k++)
+						{
+							if(edit_layers[k]->type == brush.layers[0]->type)
+								edit_layers[k]->brush_place(brush.layers[0], wx, wy);
+						}
+						
+					}
+				}
+				
+				if(!brush.is_empty())
+				{
+					brush.offset_x = -(int)wx;
+					brush.offset_y = -(int)wy;
+					for(int i = 0; i < brush.layers.len(); i++)
+					{
+						if(brush.layers[i]->type == LAYERTYPE_TILES)
+						{
+							brush.offset_x = -(int)(wx/32.0f)*32;
+							brush.offset_y = -(int)(wy/32.0f)*32;
+							break;
+						}
+					}
+				
+					LAYERGROUP *g = editor.get_selected_group();
+					brush.offset_x += g->offset_x;
+					brush.offset_y += g->offset_y;
+					brush.parallax_x = g->parallax_x;
+					brush.parallax_y = g->parallax_y;
+					brush.render();
+					float w, h;
+					brush.get_size(&w, &h);
+					
+					gfx_texture_set(-1);
+					gfx_lines_begin();
+					gfx_lines_draw(0,0, w,0);
+					gfx_lines_draw(w,0, w,h);
+					gfx_lines_draw(w,h, 0,h);
+					gfx_lines_draw(0,h, 0,0);
+					gfx_lines_end();
+					
+				}
+			}
+		}
+		
+		if(!show_picker && brush.is_empty())
+		{
+			// fetch layers
+			LAYERGROUP *g = editor.get_selected_group();
+			if(g)
+				g->mapscreen();
+				
+			for(int k = 0; k < num_edit_layers; k++)
+			{
+				if(edit_layers[k]->type == LAYERTYPE_QUADS)
+				{
+					LAYER_QUADS *layer = (LAYER_QUADS *)edit_layers[k];
+	
+					gfx_texture_set(-1);
+					gfx_quads_begin();				
+					for(int i = 0; i < layer->quads.len(); i++)
+					{
+						for(int v = 0; v < 4; v++)
+							do_quad_point(&layer->quads[i], i, v);
+							
+						do_quad(&layer->quads[i], i);
+					}
+					gfx_quads_end();
 				}
 			}
 			
-			//dbg_msg("editor", "ent type=%d pos=(%d,%d)", type, e->x, e->y);
-			ents_new(type, e->x, e->y);
+			gfx_mapscreen(ui_screen()->x, ui_screen()->y, ui_screen()->w, ui_screen()->h);
+		}		
+		
+		// do panning
+		if(ui_active_item() == editor_id)
+		{
+			if(operation == OP_PAN_WORLD)
+			{
+				editor.world_offset_x -= editor.mouse_delta_x*editor.world_zoom;
+				editor.world_offset_y -= editor.mouse_delta_y*editor.world_zoom;
+			}
+			else if(operation == OP_PAN_EDITOR)
+			{
+				editor.editor_offset_x -= editor.mouse_delta_x*editor.world_zoom;
+				editor.editor_offset_y -= editor.mouse_delta_y*editor.world_zoom;
+			}
 		}
+
+		
+		// release mouse
+		if(!ui_mouse_button(0))
+		{
+			operation = OP_NONE;
+			ui_set_active_item(0);
+		}
+		
 	}
 
-	// load theme	
+	// render screen sizes	
+	if(editor.proof_borders)
 	{
-		mapres_theme *e = (mapres_theme *)datafile_find_item(df, MAPRES_TEMP_THEME, 0);
-		map_theme = 0;
-		if(e)
-			map_theme = e->id;
+		LAYERGROUP *g = editor.game_group;
+		g->mapscreen();
+		
+		gfx_texture_set(-1);
+		gfx_lines_begin();
+		
+		float last_points[4];
+		float start = 1.0f; //9.0f/16.0f;
+		float end = 16.0f/9.0f;
+		const int num_steps = 20;
+		for(int i = 0; i <= num_steps; i++)
+		{
+			float points[4];
+			float aspect = start + (end-start)*(i/(float)num_steps);
+			
+			mapscreen_to_world(
+				editor.world_offset_x, editor.world_offset_y,
+				1.0f, 1.0f, 0.0f, 0.0f, aspect, 1.0f, points);
+			
+			if(i == 0)
+			{
+				gfx_lines_draw(points[0], points[1], points[2], points[1]);
+				gfx_lines_draw(points[0], points[3], points[2], points[3]);
+			}
+
+			if(i != 0)
+			{
+				gfx_lines_draw(points[0], points[1], last_points[0], last_points[1]);
+				gfx_lines_draw(points[2], points[1], last_points[2], last_points[1]);
+				gfx_lines_draw(points[0], points[3], last_points[0], last_points[3]);
+				gfx_lines_draw(points[2], points[3], last_points[2], last_points[3]);
+			}
+
+			if(i == num_steps)
+			{
+				gfx_lines_draw(points[0], points[1], points[0], points[3]);
+				gfx_lines_draw(points[2], points[1], points[2], points[3]);
+			}
+			
+			mem_copy(last_points, points, sizeof(points));
+		}
+
+		if(0)
+		{
+			gfx_setcolor(1.0f,0,0,1);		
+			for(int i = 0; i < 4; i++)
+			{
+				float points[4];
+				float aspects[] = {4.0f/3.0f, 5.0f/4.0f, 16.0f/10.0f, 16.0f/9.0f};
+				float aspect = aspects[i];
+				
+				mapscreen_to_world(
+					editor.world_offset_x, editor.world_offset_y,
+					1.0f, 1.0f, 0.0f, 0.0f, aspect, 1.0f, points);
+				
+				RECT r;
+				r.x = points[0];
+				r.y = points[1];
+				r.w = points[2]-points[0];
+				r.h = points[3]-points[1];
+				
+				gfx_lines_draw(r.x, r.y, r.x+r.w, r.y);
+				gfx_lines_draw(r.x+r.w, r.y, r.x+r.w, r.y+r.h);
+				gfx_lines_draw(r.x+r.w, r.y+r.h, r.x, r.y+r.h);
+				gfx_lines_draw(r.x, r.y+r.h, r.x, r.y);
+			}
+		}
+			
+		gfx_lines_end();
 	}
 	
-	datafile_unload(df);
-	return 1;
+	gfx_mapscreen(ui_screen()->x, ui_screen()->y, ui_screen()->w, ui_screen()->h);
+	ui_clip_disable();
 }
 
-int editor_save(const char *filename)
+int EDITOR::do_properties(RECT *toolbox, PROPERTY *props, int *ids, int *new_val)
 {
-	DATAFILE_OUT *df = datafile_create(filename);
+	int change = -1;
 
-	// add tilesets
-	for(int i = 0; i < tilesets_count(); i++)
+	for(int i = 0; props[i].name; i++)
 	{
-		mapres_image img;
-		tileset *ts = tilesets_get(i);
-		img.width = ts->img.width;
-		img.height = ts->img.height;
-		img.image_data = datafile_add_data(df, ts->img.width*ts->img.height*4, ts->img.data);
-		datafile_add_item(df, MAPRES_IMAGE, i, sizeof(img), &img);
+		RECT slot;
+		ui_hsplit_t(toolbox, 13.0f, &slot, toolbox);
+		RECT label, shifter;
+		ui_vsplit_mid(&slot, &label, &shifter);
+		ui_hmargin(&shifter, 1.0f, &shifter);
+		ui_do_label(&label, props[i].name, 10.0f, -1, -1);
+		
+		if(props[i].type == PROPTYPE_INT_STEP)
+		{
+			RECT inc, dec;
+			char buf[64];
+			
+			ui_vsplit_r(&shifter, 10.0f, &shifter, &inc);
+			ui_vsplit_l(&shifter, 10.0f, &dec, &shifter);
+			sprintf(buf, "%d", props[i].value);
+			ui_draw_rect(&shifter, vec4(1,1,1,0.5f), 0, 0.0f);
+			ui_do_label(&shifter, buf, 10.0f, 0, -1);
+			
+			if(ui_do_button(&ids[i], 0, 0, &dec, draw_dec_button, "Decrease"))
+			{
+				if(inp_key_pressed(KEY_LSHIFT) || inp_key_pressed(KEY_RSHIFT))
+					*new_val = props[i].value-5;
+				else
+					*new_val = props[i].value-1;
+				change = i;
+			}
+			if(ui_do_button(((char *)&ids[i])+1, 0, 0, &inc, draw_inc_button, "Increase"))
+			{
+				if(inp_key_pressed(KEY_LSHIFT) || inp_key_pressed(KEY_RSHIFT))
+					*new_val = props[i].value+5;
+				else
+					*new_val = props[i].value+1;
+				change = i;
+			}
+		}
+		else if(props[i].type == PROPTYPE_INT_SCROLL)
+		{
+			int new_value = ui_do_value_selector(&ids[i], &shifter, "", props[i].value, props[i].min, props[i].max, 1.0f);
+			if(new_value != props[i].value)
+			{
+				*new_val = new_value;
+				change = i;
+			}
+		}
+		else if(props[i].type == PROPTYPE_COLOR)
+		{
+			static const char *texts[4] = {"R", "G", "B", "A"};
+			static int shift[] = {24, 16, 8, 0};
+			int new_color = 0;
+			
+			for(int c = 0; c < 4; c++)
+			{
+				int v = (props[i].value >> shift[c])&0xff;
+				new_color |= ui_do_value_selector(((char *)&ids[i])+c, &shifter, texts[c], v, 0, 255, 1.0f)<<shift[c];
+
+				if(c != 3)
+				{
+					ui_hsplit_t(toolbox, 13.0f, &slot, toolbox);
+					ui_vsplit_mid(&slot, 0, &shifter);
+					ui_hmargin(&shifter, 1.0f, &shifter);
+				}
+			}
+			
+			if(new_color != props[i].value)
+			{
+				*new_val = new_color;
+				change = i;
+			}
+		}
 	}
 	
-	// add tilemaps
-	for(int i = 0; i < layers_count(); i++)
+	return change;
+}
+
+static void render_layers(RECT toolbox, RECT toolbar, RECT view)
+{
+	RECT layersbox = toolbox;
+	RECT propsbox;
+
+	do_map_editor(view, toolbar);
+	
+	if(!editor.gui_active)
+		return;
+			
+	RECT slot, button;
+	char buf[64];
+
+	int valid_group = 0;
+	int valid_layer = 0;
+	if(selected_group >= 0 && selected_group < editor.map.groups.len())
+		valid_group = 1;
+
+	if(valid_group && selected_layer >= 0 && selected_layer < editor.map.groups[selected_group]->layers.len())
+		valid_layer = 1;
+		
+	int valid_group_mask = valid_group ? 0 : -1;
+	int valid_layer_mask = valid_layer ? 0 : -1;
+	
 	{
-		layer *l = layers_get(i);
-		mapres_tilemap tm;
-		tm.image = l->tileset_id;
-		tm.width = l->tm.width;
-		tm.height = l->tm.height;
-		tm.x = 0;
-		tm.y = 0;
-		tm.main = l->main_layer;
-		tm.scale = 1<<16;
-		tm.data = datafile_add_data(df, l->tm.width*l->tm.height*2, l->tm.tiles);
-		datafile_add_item(df, MAPRES_TILEMAP, i, sizeof(tm), &tm);
+		ui_hsplit_t(&layersbox, 12.0f, &slot, &layersbox);
+		
+		// new layer group
+		ui_vsplit_l(&slot, 12.0f, &button, &slot);
+		static int new_layer_group_button = 0;
+		if(ui_do_button(&new_layer_group_button, "G+", 0, &button, draw_editor_button, "New group"))
+		{
+			editor.map.new_group();
+			selected_group = editor.map.groups.len()-1;
+			editor.props = PROPS_GROUP;
+		}
+			
+		// new tile layer
+		ui_vsplit_l(&slot, 10.0f, &button, &slot);
+		ui_vsplit_l(&slot, 12.0f, &button, &slot);
+		static int new_tile_layer_button = 0;
+		if(ui_do_button(&new_tile_layer_button, "T+", valid_group_mask, &button, draw_editor_button, "New tile layer"))
+		{
+			LAYER *l = new LAYER_TILES(50, 50);
+			editor.map.groups[selected_group]->add_layer(l);
+			selected_layer = editor.map.groups[selected_group]->layers.len()-1;
+			editor.props = PROPS_LAYER;
+		}
+
+		// new quad layer
+		ui_vsplit_l(&slot, 2.0f, &button, &slot);
+		ui_vsplit_l(&slot, 12.0f, &button, &slot);
+		static int new_quad_layer_button = 0;
+		if(ui_do_button(&new_quad_layer_button, "Q+", valid_group_mask, &button, draw_editor_button, "New quad layer"))
+		{
+			LAYER *l = new LAYER_QUADS;
+			editor.map.groups[selected_group]->add_layer(l);
+			selected_layer = editor.map.groups[selected_group]->layers.len()-1;
+			editor.props = PROPS_LAYER;
+		}
+
+		// remove layer
+		ui_vsplit_r(&slot, 12.0f, &slot, &button);
+		static int delete_layer_button = 0;
+		if(ui_do_button(&delete_layer_button, "L-", valid_layer_mask, &button, draw_editor_button, "Delete layer"))
+			editor.map.groups[selected_group]->delete_layer(selected_layer);
+
+		// remove group
+		ui_vsplit_r(&slot, 2.0f, &slot, &button);
+		ui_vsplit_r(&slot, 12.0f, &slot, &button);
+		static int delete_group_button = 0;
+		if(ui_do_button(&delete_group_button, "G-", valid_group_mask, &button, draw_editor_button, "Delete group"))
+			editor.map.delete_group(selected_group);
 	}
+
+	ui_hsplit_t(&layersbox, 5.0f, &slot, &layersbox);
 	
-	// add collision
-	char *collisiondata = 0x0;
-	for(int i = 0; i < layers_count(); i++)
+	// render layers	
 	{
-		layer *l = layers_get(i);
-		if(l->main_layer)
+		for(int g = 0; g < editor.map.groups.len(); g++)
 		{
-			mapres_collision col;
-			col.width = l->tm.width;
-			col.height = l->tm.height;
+			RECT visible_toggle;
+			ui_hsplit_t(&layersbox, 12.0f, &slot, &layersbox);
+			ui_vsplit_l(&slot, 12, &visible_toggle, &slot);
+			if(ui_do_button(&editor.map.groups[g]->visible, editor.map.groups[g]->visible?"V":"H", 0, &visible_toggle, draw_editor_button_l, "Toggle group visibility"))
+				editor.map.groups[g]->visible = !editor.map.groups[g]->visible;
+
+			sprintf(buf, "#%d %s", g, editor.map.groups[g]->name);
+			if(ui_do_button(&editor.map.groups[g], buf, g==selected_group, &slot, draw_editor_button_r, "Select group"))
+			{
+				selected_group = g;
+				selected_layer = 0;
+				editor.props = PROPS_GROUP;
+			}
+			
+			ui_hsplit_t(&layersbox, 2.0f, &slot, &layersbox);
 			
-			collisiondata = (char *)mem_alloc(col.width*col.height, 1);
-			for(int y = 0, c = 0; y < col.height; y++)
-				for(int x = 0; x < col.width; x++, c++)
+			for(int i = 0; i < editor.map.groups[g]->layers.len(); i++)
+			{
+				//visible
+				ui_hsplit_t(&layersbox, 12.0f, &slot, &layersbox);
+				ui_vsplit_l(&slot, 12.0f, 0, &button);
+				ui_vsplit_l(&button, 15, &visible_toggle, &button);
+
+				if(ui_do_button(&editor.map.groups[g]->layers[i]->visible, editor.map.groups[g]->layers[i]->visible?"V":"H", 0, &visible_toggle, draw_editor_button_l, "Toggle layer visibility"))
+					editor.map.groups[g]->layers[i]->visible = !editor.map.groups[g]->layers[i]->visible;
+
+				sprintf(buf, "#%d %s ", i, editor.map.groups[g]->layers[i]->type_name);
+				if(ui_do_button(editor.map.groups[g]->layers[i], buf, g==selected_group&&i==selected_layer, &button, draw_editor_button_r, "Select layer"))
 				{
-					if(l->tm.tiles[c].index)
-						collisiondata[c] = 1;
-					else
-						collisiondata[c] = 0;
+					selected_layer = i;
+					selected_group = g;
+					editor.props = PROPS_LAYER;
 				}
-			
-			col.data_index = datafile_add_data(df, col.width*col.height, collisiondata);
-			datafile_add_item(df, MAPRES_COLLISIONMAP, 0, sizeof(col), &col);
-			break;
+				ui_hsplit_t(&layersbox, 2.0f, &slot, &layersbox);
+			}
+			ui_hsplit_t(&layersbox, 5.0f, &slot, &layersbox);
 		}
 	}
 	
-	// add entities
-	for(int t = MAPRES_ENTS_START; t < MAPRES_ENTS_END; t++)
+	propsbox = layersbox;
+	
+	// group properties
+	if(editor.props == PROPS_GROUP && valid_group)
 	{
-		int id = 0;
-		for(int i = 0; i < ents_count(); i++)
+		ui_hsplit_t(&propsbox, 12.0f, &slot, &propsbox);
+		ui_do_label(&slot, "Group Props", 12.0f, -1, -1);
+	
+		enum
 		{
-			entity *ent = ents_get(i);
-			if(ent_types[ent->type].id != t)
-				continue;
-				
-			int savebuf[64];
-			mapres_entity *mapent = (mapres_entity *)savebuf;
-			mapent->x = ent->x;
-			mapent->y = ent->y;
-			dbg_msg("editor", "saving ent idx=%d pos=(%d,%d)", i, ent->x, ent->y);
-			memcpy(mapent->data, ent_types[ent->type].fields, ent_types[ent->type].numfields*sizeof(int));
-			datafile_add_item(df, t, id, (ent_types[ent->type].numfields+2)*sizeof(int), mapent);
-			id++;
+			PROP_ORDER=0,
+			PROP_POS_X,
+			PROP_POS_Y,
+			PROP_PARA_X,
+			PROP_PARA_Y,
+			NUM_PROPS,
+		};
+		
+		PROPERTY props[] = {
+			{"Order", selected_group, PROPTYPE_INT_STEP, 0, editor.map.groups.len()-1},
+			{"Pos X", -editor.map.groups[selected_group]->offset_x, PROPTYPE_INT_SCROLL, -1000000, 1000000},
+			{"Pos Y", -editor.map.groups[selected_group]->offset_y, PROPTYPE_INT_SCROLL, -1000000, 1000000},
+			{"Para X", editor.map.groups[selected_group]->parallax_x, PROPTYPE_INT_SCROLL, -1000000, 1000000},
+			{"Para Y", editor.map.groups[selected_group]->parallax_y, PROPTYPE_INT_SCROLL, -1000000, 1000000},
+			{0},
+		};
+		
+		static int ids[NUM_PROPS] = {0};
+		int new_val = 0;
+		
+		// cut the properties that isn't needed
+		if(editor.get_selected_group()->game_group)
+			props[PROP_POS_X].name = 0;
+			
+		int prop = editor.do_properties(&propsbox, props, ids, &new_val);
+		if(prop == PROP_ORDER)
+			selected_group = editor.map.swap_groups(selected_group, new_val);
+			
+		// these can not be changed on the game group
+		if(!editor.get_selected_group()->game_group)
+		{
+			if(prop == PROP_PARA_X)
+				editor.map.groups[selected_group]->parallax_x = new_val;
+			else if(prop == PROP_PARA_Y)
+				editor.map.groups[selected_group]->parallax_y = new_val;
+			else if(prop == PROP_POS_X)
+				editor.map.groups[selected_group]->offset_x = -new_val;
+			else if(prop == PROP_POS_Y)
+				editor.map.groups[selected_group]->offset_y = -new_val;
 		}
 	}
 	
-	// save theme
-	mapres_theme t;
-	t.id = map_theme;
-	datafile_add_item(df, MAPRES_TEMP_THEME, 0, sizeof(mapres_theme), &t);
-	
-	// finish and clean up
-	datafile_finish(df);
-	mem_free(collisiondata);
-	
-	return 0;
+	// layer properties
+	if(editor.get_selected_layer(0))
+	{
+		LAYERGROUP *current_group = editor.map.groups[selected_group];
+		LAYER *current_layer = editor.get_selected_layer(0);
+		
+		if(editor.props == PROPS_LAYER)
+		{
+			ui_hsplit_t(&propsbox, 15.0f, &slot, &propsbox);
+			ui_do_label(&slot, "Layer Props", 12.0f, -1, -1);
+			
+			enum
+			{
+				PROP_GROUP=0,
+				PROP_ORDER,
+				NUM_PROPS,
+			};
+			
+			PROPERTY props[] = {
+				{"Group", selected_group, PROPTYPE_INT_STEP, 0, editor.map.groups.len()-1},
+				{"Order", selected_layer, PROPTYPE_INT_STEP, 0, current_group->layers.len()},
+				{0},
+			};
+			
+			static int ids[NUM_PROPS] = {0};
+			int new_val = 0;
+			int prop = editor.do_properties(&propsbox, props, ids, &new_val);		
+			
+			if(prop == PROP_ORDER)
+				selected_layer = current_group->swap_layers(selected_layer, new_val);
+			else if(prop == PROP_GROUP && current_layer->type != LAYERTYPE_GAME)
+			{
+				if(new_val >= 0 && new_val < editor.map.groups.len())
+				{
+					current_group->layers.remove(current_layer);
+					editor.map.groups[new_val]->layers.add(current_layer);
+					selected_group = new_val;
+					selected_layer = editor.map.groups[new_val]->layers.len()-1;
+				}
+			}
+		}
+			
+		current_layer->render_properties(&propsbox);
+	}
 }
 
+static void render_images(RECT toolbox, RECT view)
+{
+	static int selected_image = 0;
+	
+	for(int i = 0; i < editor.map.images.len(); i++)
+	{
+		char buf[128];
+		sprintf(buf, "#%d %dx%d", i, editor.map.images[i]->width, editor.map.images[i]->height);
+		RECT slot;
+		ui_hsplit_t(&toolbox, 15.0f, &slot, &toolbox);
+		
+		if(ui_do_button(&editor.map.images[i], buf, selected_image == i, &slot, draw_editor_button, "Select image"))
+			selected_image = i;
+		
+		ui_hsplit_t(&toolbox, 2.0f, 0, &toolbox);
+		
+		// render image
+		if(selected_image == i)
+		{
+			RECT r;
+			ui_margin(&view, 10.0f, &r);
+			if(r.h < r.w)
+				r.w = r.h;
+			else
+				r.h = r.w;
+			gfx_texture_set(editor.map.images[i]->tex_id);
+			gfx_blend_normal();
+			gfx_quads_begin();
+			gfx_quads_drawTL(r.x, r.y, r.w, r.h);
+			gfx_quads_end();
+			
+		}
+	}
+	
+	RECT slot;
+	ui_hsplit_t(&toolbox, 5.0f, &slot, &toolbox);
+	ui_hsplit_t(&toolbox, 15.0f, &slot, &toolbox);
 
-static int editor_loadimage = -1;
-static int editor_loadmap = -1;
+	// new image
+	static int new_image_button = 0;
+	if(ui_do_button(&new_image_button, "(Load New Image)", 0, &slot, draw_editor_button, "Load a new image to use in the map"))
+		editor.dialog = DIALOG_LOAD_IMAGE;
 
+	ui_hsplit_t(&toolbox, 15.0f, &slot, &toolbox);
+}
+	
 static void editor_listdir_callback(const char *name, int is_dir, void *user)
 {
 	if(name[0] == '.') // skip this shit!
 		return;
 	
-	int *y = (int*)user;
-	if(ui_do_button((void*)(*y + 1), name, 0, 10, 10 + *y * 8, 100, 6, draw_editor_button, 0))
+	RECT *view = (RECT *)user;
+	RECT button;
+	ui_hsplit_t(view, 15.0f, &button, view);
+	ui_hsplit_t(view, 2.0f, 0, view);
+	
+	if(ui_do_button((void*)(10+(int)button.y), name, 0, &button, draw_editor_button, 0))
 	{
 		char buf[512];
 		sprintf(buf, "tilesets/%s", name);
 		
-		IMAGE_INFO img;
-		if(!gfx_load_png(&img, buf))
+		IMAGE imginfo;
+		if(!gfx_load_png(&imginfo, buf))
 			return;
 		
-		tilesets_set_img(editor_loadimage, img.width, img.height, img.data);
-		editor_loadimage = -1;
+		IMAGE *img = new IMAGE;
+		*img = imginfo;
+		img->tex_id = gfx_load_texture_raw(imginfo.width, imginfo.height, imginfo.format, imginfo.data, IMG_AUTO);
+		editor.map.images.add(img);
+		
+		//tilesets_set_img(tilesets_new(), img.width, img.height, img.data);
+		editor.dialog = DIALOG_NONE;
 	}
-	*y += 1;
 }
 
-static void editor_render_loadfile_dialog()
+static void render_dialog_load_image()
 {
 	// GUI coordsys
-	gfx_clear(0.5f,0.5f,0.5f);
+	gfx_clear(0.25f,0.25f,0.25f);
 		
-	gfx_mapscreen(0,0,400.0f,300.0f);
+	gfx_mapscreen(ui_screen()->x, ui_screen()->y, ui_screen()->w, ui_screen()->h);
 	
-	int index = 0;
-	fs_listdir("tilesets", editor_listdir_callback, &index);
+	RECT view = *ui_screen();
+	fs_listdir("tilesets", editor_listdir_callback, &view);
 	
 	if(inp_key_pressed(KEY_ESC))
-		editor_loadimage = -1;
+		editor.dialog = DIALOG_NONE;
 }
 
-static void editor_listdir_map_callback(const char *name, int is_dir, void *user)
+static void render_modebar(RECT view)
 {
-	if(name[0] == '.') // skip this shit!
-		return;
-	
-	int *y = (int*)user;
-	if(ui_do_button((void*)(*y + 1), name, 0, 10, 10 + *y * 8, 100, 6, draw_editor_button, 0))
+	RECT button;
+
+	// mode buttons
 	{
-		char buf[512];
-		sprintf(buf, "data/maps/%s", name);
+		ui_vsplit_l(&view, 40.0f, &button, &view);
+		static int map_button = 0;
+		if(ui_do_button(&map_button, "Map", editor.mode == MODE_MAP, &button, draw_editor_button_l, "Switch to edit global map settings"))
+			editor.mode = MODE_MAP;
 		
-		editor_load(buf);
-		strcpy(editor_filename, buf);
-		editor_loadmap = -1;
+		ui_vsplit_l(&view, 40.0f, &button, &view);
+		static int tile_button = 0;
+		if(ui_do_button(&tile_button, "Layers", editor.mode == MODE_LAYERS, &button, draw_editor_button_m, "Switch to edit layers"))
+			editor.mode = MODE_LAYERS;
+
+		ui_vsplit_l(&view, 40.0f, &button, &view);
+		static int img_button = 0;
+		if(ui_do_button(&img_button, "Images", editor.mode == MODE_IMAGES, &button, draw_editor_button_r, "Switch to manage images"))
+			editor.mode = MODE_IMAGES;
 	}
-	*y += 1;
-}
 
-static void editor_render_loadmap_dialog()
-{
-	// GUI coordsys
-	gfx_clear(0.2f,0.2f,0.8f);
-	gfx_mapscreen(0,0,400.0f,300.0f);
+	ui_vsplit_l(&view, 5.0f, 0, &view);
 	
-	int index = 0;
-	fs_listdir("data/maps", editor_listdir_map_callback, &index);
+	// animate button
+	ui_vsplit_l(&view, 30.0f, &button, &view);
+	static int animate_button = 0;
+	if(ui_do_button(&animate_button, "Anim", editor.animate, &button, draw_editor_button, "[ctrl+m] Toggle animation") ||
+		(inp_key_down('M') && (inp_key_pressed(KEY_LCTRL) || inp_key_pressed(KEY_RCTRL))))
+	{
+		editor.animate_start = time_get();
+		editor.animate = !editor.animate;
+	}
+
+	ui_vsplit_l(&view, 5.0f, 0, &view);
+
+	// proof button
+	ui_vsplit_l(&view, 30.0f, &button, &view);
+	static int proof_button = 0;
+	if(ui_do_button(&proof_button, "Proof", editor.proof_borders, &button, draw_editor_button, "[ctrl-p] Toggles proof borders. These borders represent what a player maximum can see.") ||
+		(inp_key_down('P') && (inp_key_pressed(KEY_LCTRL) || inp_key_pressed(KEY_RCTRL))))
+	{
+		editor.proof_borders = !editor.proof_borders;
+	}
 	
-	if(inp_key_pressed(KEY_ESC))
-		editor_loadmap = -1;
+	// spacing
+	//ui_vsplit_l(&view, 10.0f, 0, &view);
 }
 
-static void editor_render_normal()
+static void render_statusbar(RECT view)
 {
-	// background color
-	if(map_theme == 1)
-		gfx_clear(0x11/(float)0xff, 0x1a/(float)0xff, 0x21/(float)0xff);
-	else 
-		gfx_clear(0.65f,0.78f,0.9f);
-		
+	RECT button;
+	ui_vsplit_r(&view, 60.0f, &view, &button);
+	static int envelope_button = 0;
+	if(ui_do_button(&envelope_button, "Envelopes", editor.show_envelope_editor, &button, draw_editor_button, "Toggles the envelope editor"))
+		editor.show_envelope_editor = (editor.show_envelope_editor+1)%4;
+	
+	if(editor.tooltip)
+		ui_do_label(&view, editor.tooltip, 12.0f, -1, -1);
+}
+
+static void render_envelopeeditor(RECT view)
+{
+	if(selected_envelope < 0) selected_envelope = 0;
+	if(selected_envelope >= editor.map.envelopes.len()) selected_envelope--;
 
-	// world coordsys
-	float zoom = world_zoom;
-	gfx_mapscreen(world_offset_x,world_offset_y,world_offset_x+400.0f*zoom,world_offset_y+300.0f*zoom);
+	ENVELOPE *envelope = 0;
+	if(selected_envelope >= 0 && selected_envelope < editor.map.envelopes.len())
+		envelope = editor.map.envelopes[selected_envelope];
 
-	for(int i = 0; i < layers_count(); i++)
+	bool show_colorbar = false;
+	if(envelope && envelope->channels == 4)
+		show_colorbar = true;
+
+	RECT toolbar, curvebar, colorbar;
+	ui_hsplit_t(&view, 20.0f, &toolbar, &view);
+	ui_hsplit_t(&view, 20.0f, &curvebar, &view);
+	ui_margin(&toolbar, 2.0f, &toolbar);
+	ui_margin(&curvebar, 2.0f, &curvebar);
+
+	if(show_colorbar)
+	{
+		ui_hsplit_t(&view, 20.0f, &colorbar, &view);
+		ui_margin(&colorbar, 2.0f, &colorbar);
+		render_background(colorbar, checker_texture, 16.0f, 1.0f);
+	}
+
+	render_background(view, checker_texture, 32.0f, 0.1f);
+
+	// do the toolbar
 	{
-		layer *l = layers_get(i);
+		RECT button;
+		ENVELOPE *new_env = 0;
 		
-		gfx_texture_set(-1);
-		if(l->tileset_id >= 0 && l->tileset_id < tilesets_count())
-			gfx_texture_set(tilesets_get(l->tileset_id)->tex_id);
-			
-		int f = 0;
-		if(current_hover_layer == i)
-			f = TILEMAPFLAG_HILIGHTED;
+		ui_vsplit_r(&toolbar, 50.0f, &toolbar, &button);
+		static int new_4d_button = 0;
+		if(ui_do_button(&new_4d_button, "Color+", 0, &button, draw_editor_button, "Creates a new color envelope"))
+			new_env = editor.map.new_envelope(4);
+
+		ui_vsplit_r(&toolbar, 5.0f, &toolbar, &button);
+		ui_vsplit_r(&toolbar, 50.0f, &toolbar, &button);
+		static int new_2d_button = 0;
+		if(ui_do_button(&new_2d_button, "Pos.+", 0, &button, draw_editor_button, "Creates a new pos envelope"))
+			new_env = editor.map.new_envelope(3);
 		
-		if(editor_mode == 0)
+		if(new_env) // add the default points
 		{
-			if(l == layers_get_current())
+			if(new_env->channels == 4)
 			{
-				// do current layer
-				ui_do_tilemap(&l->tm, &l->tm, f, 0, 0, 32.0f);
+				new_env->add_point(0, 1,1,1,1);
+				new_env->add_point(1000, 1,1,1,1);
 			}
-			else if(l->visible)
+			else
 			{
-				// render layer
-				render_tilemap(&l->tm, 0, 0, 32.0f, f);
+				new_env->add_point(0, 0);
+				new_env->add_point(1000, 0);
 			}
 		}
-		else
-			render_tilemap(&l->tm, 0, 0, 32.0f, f);
-	}
-
-	if(editor_mode == 1)
-	{
-		// ents mode
-		for(int i = 0; i < ents_count(); i++)
-		{
-			if(ui_do_entity(ents_get(i), ents_get(i), i == editor_selected_ent))
-				editor_selected_ent = i;
+		
+		RECT shifter, inc, dec;
+		ui_vsplit_l(&toolbar, 60.0f, &shifter, &toolbar);
+		ui_vsplit_r(&shifter, 15.0f, &shifter, &inc);
+		ui_vsplit_l(&shifter, 15.0f, &dec, &shifter);
+		char buf[512];
+		sprintf(buf, "%d/%d", selected_envelope+1, editor.map.envelopes.len());
+		ui_draw_rect(&shifter, vec4(1,1,1,0.5f), 0, 0.0f);
+		ui_do_label(&shifter, buf, 14.0f, 0, -1);
+		
+		static int prev_button = 0;
+		if(ui_do_button(&prev_button, 0, 0, &dec, draw_dec_button, "Previous Envelope"))
+			selected_envelope--;
+		
+		static int next_button = 0;
+		if(ui_do_button(&next_button, 0, 0, &inc, draw_inc_button, "Next Envelope"))
+			selected_envelope++;
 			
+		if(envelope)
+		{
+			ui_vsplit_l(&toolbar, 15.0f, &button, &toolbar);
+			ui_vsplit_l(&toolbar, 35.0f, &button, &toolbar);
+			ui_do_label(&button, "Name:", 14.0f, -1, -1);
+
+			ui_vsplit_l(&toolbar, 80.0f, &button, &toolbar);
+			static int name_box = 0;
+			ui_do_edit_box(&name_box, &button, envelope->name, sizeof(envelope->name));
 		}
 	}
 	
-	// GUI coordsys
-	gfx_mapscreen(0,0,400.0f,300.0f);
-
-	// toolbox
-	float toolbox_width = 50.0f;
-	
-	ui_do_frame(0, 0, toolbox_width, 300);
-	
-	if(editor_mode == 0)
+	if(envelope)
 	{
-		float layerbox_x = 0;
-		float layerbox_y = 0;
-		int count = 1;
-		int main_layer = -1;
+		static array<int> selection;
+		static int envelope_editor_id = 0;
+		static int active_channels = 0xf;
 		
-		current_hover_layer = -1;
-		for(int i = 0; i < layers_count(); i++)
+		if(envelope)
 		{
-			layer *l = layers_get(i);
-			char buf[128];
-			if(l->main_layer)
-			{
-				main_layer = i;
-				sprintf(buf, "Main\n(%dx%d)", l->tm.width, l->tm.height);
-				count = 1;
-			}
-			else
-			{
-				if(main_layer == -1)
-					sprintf(buf, "Bg %d\n(%dx%d)", count, l->tm.width, l->tm.height);
-				else
-					sprintf(buf, "Fg %d\n(%dx%d)", count, l->tm.width, l->tm.height);
-				count++;
-			}
+			RECT button;	
 			
-			// show / hide layer
-			const char *text = " ";
-			if(layers_get(i)->visible)
-				text = "V";
-
-			if(ui_do_button(&layers_get(i)->visible, text, 0, layerbox_x, layerbox_y+i*14, 6, 6, draw_editor_button, 0))
-				layers_get(i)->visible = layers_get(i)->visible^1;
+			ui_vsplit_l(&toolbar, 15.0f, &button, &toolbar);
+
+			static const char *names[4][4] = {
+				{"X", "", "", ""},
+				{"X", "Y", "", ""},
+				{"X", "Y", "R", ""},
+				{"R", "G", "B", "A"},
+			};
 			
-			// layer bytton
-			if(ui_mouse_inside(layerbox_x+8, layerbox_y+i*14, toolbox_width-8, 12))
-				current_hover_layer = i;
+			static int channel_buttons[4] = {0};
+			int bit = 1;
+			ui_draw_button_func draw_func;
+			
+			for(int i = 0; i < envelope->channels; i++, bit<<=1)
+			{
+				ui_vsplit_l(&toolbar, 15.0f, &button, &toolbar);
 				
-			if(ui_do_button(&layers_get(i)->tileset_id, buf, current_layer == i, layerbox_x+8, layerbox_y+i*14, toolbox_width-8, 12, draw_editor_button, 0))
-				current_layer = i;
-		}
+				if(i == 0) draw_func = draw_editor_button_l;
+				else if(i == envelope->channels-1) draw_func = draw_editor_button_r;
+				else draw_func = draw_editor_button_m;
+				
+				if(ui_do_button(&channel_buttons[i], names[envelope->channels-1][i], active_channels&bit, &button, draw_func, 0))
+					active_channels ^= bit;
+			}
+		}		
 		
-		// draw buttons
-		{
-			static int push_button, pull_button;
-			float y = 150;
-			float x = 0;
-
-			const char *themes[] = {"theme: summer", "theme: winter"};
-			if(ui_do_button(&map_theme, themes[map_theme%2], 0, x, y, toolbox_width, 6, draw_editor_button, 0))
-				map_theme = (map_theme+1)%2;
-			y += 15;
-
-			if(ui_do_button(&push_button, "push", 0, x, y, toolbox_width, 6, draw_editor_button, 0))
-				current_layer = layers_moveup(current_layer);
-			y += 7;
+		float end_time = envelope->end_time();
+		if(end_time < 1)
+			end_time = 1;
+		
+		envelope->find_top_bottom();
+		float top = envelope->top;
+		float bottom = envelope->bottom;
+		
+		if(top < 1)
+			top = 1;
+		if(bottom >= 0)
+			bottom = 0;
+		
+		float timescale = end_time/view.w;
+		float valuescale = (top-bottom)/view.h;
+		
+		if(ui_mouse_inside(&view))
+			ui_set_hot_item(&envelope_editor_id);
 			
-			if(ui_do_button(&pull_button, "pull", 0, x, y, toolbox_width, 6, draw_editor_button, 0))
-				current_layer = layers_movedown(current_layer);
-			y += 10;
-
-			static int w_inc, w_dec;
-			int resize_amount = 10;
-			if(ui_do_button(&w_dec, "width-", 0, x, y, toolbox_width, 6, draw_editor_button, 0))
-				tilemap_resize(&layers_get_current()->tm, layers_get_current()->tm.width-resize_amount, layers_get_current()->tm.height, resize_amount);
-			y += 7;
-			if(ui_do_button(&w_inc, "width+", 0, x, y, toolbox_width, 6, draw_editor_button, 0))
-				tilemap_resize(&layers_get_current()->tm, layers_get_current()->tm.width+resize_amount, layers_get_current()->tm.height, resize_amount);
-			y += 10;
-
-			static int h_inc, h_dec;
-			if(ui_do_button(&h_dec, "height-", 0, x, y, toolbox_width, 6, draw_editor_button, 0))
-				tilemap_resize(&layers_get_current()->tm, layers_get_current()->tm.width, layers_get_current()->tm.height-resize_amount, resize_amount);
-			y += 7;
-			if(ui_do_button(&h_inc, "height+", 0, x, y, toolbox_width, 6, draw_editor_button, 0))
-				tilemap_resize(&layers_get_current()->tm, layers_get_current()->tm.width, layers_get_current()->tm.height+resize_amount, resize_amount);
-			y += 10;
+		if(ui_hot_item() == &envelope_editor_id)
+		{
+			// do stuff
+			if(envelope)
+			{
+				if(ui_mouse_button_clicked(1))
+				{
+					// add point
+					int time = (int)(((ui_mouse_x()-view.x)*timescale)*1000.0f);
+					//float env_y = (ui_mouse_y()-view.y)/timescale;
+					envelope->add_point(time,
+						f2fx(envelope->eval(time, 0)),
+						f2fx(envelope->eval(time, 1)),
+						f2fx(envelope->eval(time, 2)),
+						f2fx(envelope->eval(time, 3)));
+				}
+				
+				editor.tooltip = "Press right mouse button to create a new point";
+			}
 		}
 
+		vec3 colors[] = {vec3(1,0.2f,0.2f), vec3(0.2f,1,0.2f), vec3(0.2f,0.2f,1), vec3(1,1,0.2f)};
 
-		float tilesetsbox_x = 0;
-		float tilesetsbox_y = 230;
-		for(int i = 0; i < tilesets_count(); i++)
+		// render lines
 		{
-			char buf[128];
-			sprintf(buf, "#%d %dx%d", i, tilesets_get(i)->img.width, tilesets_get(i)->img.height);
-			if(ui_do_button(&tilesets_get(i)->img.width, "L", layers_get(current_layer)->tileset_id == i, tilesetsbox_x, tilesetsbox_y+i*7, 6, 6, draw_editor_button, 0))
+			gfx_texture_set(-1);
+			gfx_lines_begin();
+			for(int c = 0; c < envelope->channels; c++)
 			{
-				// load image
-				editor_loadimage = i;
+				if(active_channels&(1<<c))
+					gfx_setcolor(colors[c].r,colors[c].g,colors[c].b,1);
+				else
+					gfx_setcolor(colors[c].r*0.5f,colors[c].g*0.5f,colors[c].b*0.5f,1);
+				
+				float prev_x = 0;
+				float prev_value = envelope->eval(0.000001f, c);
+				int steps = (int)((view.w/ui_screen()->w) * gfx_screenwidth());
+				for(int i = 1; i <= steps; i++)
+				{
+					float a = i/(float)steps;
+					float v = envelope->eval(a*end_time, c);
+					v = (v-bottom)/(top-bottom);
+					
+					gfx_lines_draw(view.x + prev_x*view.w, view.y+view.h - prev_value*view.h, view.x + a*view.w, view.y+view.h - v*view.h);
+					prev_x = a;
+					prev_value = v;
+				}
 			}
-
-			if(ui_do_button(tilesets_get(i), buf, layers_get(current_layer)->tileset_id == i, tilesetsbox_x+16, tilesetsbox_y+i*7, toolbox_width-16, 6, draw_editor_button, 0))
+			gfx_lines_end();
+		}
+		
+		// render curve options
+		{
+			for(int i = 0; i < envelope->points.len()-1; i++)
 			{
-				// select tileset for layer
-				dbg_msg("editor", "settings tileset %d=%d", current_layer, i);
-				layers_get(current_layer)->tileset_id = i;
-			}
+				float t0 = envelope->points[i].time/1000.0f/end_time;
+				float t1 = envelope->points[i+1].time/1000.0f/end_time;
 
-			if(ui_do_button(&tilesets_get(i)->img.height, "D", layers_get(current_layer)->tileset_id == i, tilesetsbox_x+8, tilesetsbox_y+i*7, 6, 6, draw_editor_button, 0)
-				&& (inp_key_pressed(KEY_LCTRL) || inp_key_pressed(KEY_RCTRL)))
-			{
-				dbg_msg("editor", "deleting tileset %d", i);
-				tilesets_delete(i);
-				i--;
+				//dbg_msg("", "%f", end_time);
+				
+				RECT v;
+				v.x = curvebar.x + (t0+(t1-t0)*0.5f) * curvebar.w;
+				v.y = curvebar.y;
+				v.h = curvebar.h;
+				v.w = curvebar.h;
+				v.x -= v.w/2;
+				void *id = &envelope->points[i].curvetype;
+				const char *type_name[] = {
+					"N", "L", "S", "F", "M"
+					};
+				
+				if(ui_do_button(id, type_name[envelope->points[i].curvetype], 0, &v, draw_editor_button, "Switch curve type"))
+					envelope->points[i].curvetype = (envelope->points[i].curvetype+1)%NUM_CURVETYPES;
 			}
 		}
-
-		// (add) button for tilesets
-		static int add_tileset;
-		if(ui_do_button(&add_tileset, "(Add)", 0, tilesetsbox_x, tilesetsbox_y+tilesets_count()*7+3, toolbox_width, 6, draw_editor_button, 0))
-			tilesets_new();
 		
-		if(brush.tiles != 0)
+		// render colorbar
+		if(show_colorbar)
 		{
-			// right mouse button or C clears the brush
-			if(ui_mouse_button(1) || inp_key_pressed('C'))
-				tilemap_destroy(&brush);
+			gfx_texture_set(-1);
+			gfx_quads_begin();
+			for(int i = 0; i < envelope->points.len()-1; i++)
+			{
+				float r0 = fx2f(envelope->points[i].values[0]);
+				float g0 = fx2f(envelope->points[i].values[1]);
+				float b0 = fx2f(envelope->points[i].values[2]);
+				float a0 = fx2f(envelope->points[i].values[3]);
+				float r1 = fx2f(envelope->points[i+1].values[0]);
+				float g1 = fx2f(envelope->points[i+1].values[1]);
+				float b1 = fx2f(envelope->points[i+1].values[2]);
+				float a1 = fx2f(envelope->points[i+1].values[3]);
+
+				gfx_setcolorvertex(0, r0, g0, b0, a0);
+				gfx_setcolorvertex(1, r1, g1, b1, a1);
+				gfx_setcolorvertex(2, r1, g1, b1, a1);
+				gfx_setcolorvertex(3, r0, g0, b0, a0);
+
+				float x0 = envelope->points[i].time/1000.0f/end_time;
+//				float y0 = (fx2f(envelope->points[i].values[c])-bottom)/(top-bottom);
+				float x1 = envelope->points[i+1].time/1000.0f/end_time;
+				//float y1 = (fx2f(envelope->points[i+1].values[c])-bottom)/(top-bottom);
+				RECT v;
+				v.x = colorbar.x + x0*colorbar.w;
+				v.y = colorbar.y;
+				v.w = (x1-x0)*colorbar.w;
+				v.h = colorbar.h;
+				
+				gfx_quads_drawTL(v.x, v.y, v.w, v.h);
+			}
+			gfx_quads_end();
 		}
-
-		// flip buttons
-		if(inp_key_down('N') && brush.tiles)
-			tilemap_vflip(&brush);
-		if(inp_key_down('M') && brush.tiles)
-			tilemap_hflip(&brush);
 		
-		if(inp_key_pressed(KEY_SPACE))
+		// render handles
 		{
-			// render chooser
-			float chooser_x = toolbox_width+10.0f;
-			float chooser_y = 10.0f;
+			static bool move = false;
 			
+			int current_value = 0, current_time = 0;
 			
-			gfx_texture_set(checker_texture);
-			gfx_blend_normal();
+			gfx_texture_set(-1);
 			gfx_quads_begin();
-			gfx_setcolor(1.0f, 1.0f, 1.0f, 1.0f);
-			gfx_quads_setsubset(0,0,32.0f, 32.0f);
-			gfx_quads_drawTL(chooser_x, chooser_y, 16*16.0f, 16*16.0f);
-			gfx_quads_end();
-			gfx_blend_normal();	
+			for(int c = 0; c < envelope->channels; c++)
+			{
+				if(!(active_channels&(1<<c)))
+					continue;
 				
-			gfx_texture_set(-1);
-			layer *l = layers_get_current();
-			if(l && l->tileset_id >= 0 && l->tileset_id < tilesets_count())
-				gfx_texture_set(tilesets_get(l->tileset_id)->tex_id);
-			ui_do_tilemap(&chooser, &chooser, TILEMAPFLAG_READONLY|TILEMAPFLAG_UISPACE, chooser_x, chooser_y, 16.0f);
+				for(int i = 0; i < envelope->points.len(); i++)
+				{
+					float x0 = envelope->points[i].time/1000.0f/end_time;
+					float y0 = (fx2f(envelope->points[i].values[c])-bottom)/(top-bottom);
+					RECT final;
+					final.x = view.x + x0*view.w;
+					final.y = view.y+view.h - y0*view.h;
+					final.x -= 2.0f;
+					final.y -= 2.0f;
+					final.w = 4.0f;
+					final.h = 4.0f;
+					
+					void *id = &envelope->points[i].values[c];
+					
+					if(ui_mouse_inside(&final))
+						ui_set_hot_item(id);
+						
+					float colormod = 1.0f;
+
+					if(ui_active_item() == id)
+					{
+						if(!ui_mouse_button(0))
+						{
+							ui_set_active_item(0);
+							move = false;
+						}
+						else
+						{
+							envelope->points[i].values[c] -= f2fx(editor.mouse_delta_y*valuescale);
+							if(inp_key_pressed(KEY_LSHIFT) || inp_key_pressed(KEY_RSHIFT))
+							{
+								if(i != 0)
+								{
+									envelope->points[i].time += (int)((editor.mouse_delta_x*timescale)*1000.0f);
+									if(envelope->points[i].time < envelope->points[i-1].time)
+										envelope->points[i].time = envelope->points[i-1].time + 1;
+									if(i+1 != envelope->points.len() && envelope->points[i].time > envelope->points[i+1].time)
+										envelope->points[i].time = envelope->points[i+1].time - 1;
+								}
+							}
+						}
+						
+						colormod = 100.0f;
+						gfx_setcolor(1,1,1,1);
+					}
+					else if(ui_hot_item() == id)
+					{
+						if(ui_mouse_button(0))
+						{
+							selection.clear();
+							selection.add(i);
+							ui_set_active_item(id);
+						}
+
+						// remove point
+						if(ui_mouse_button_clicked(1))
+							envelope->points.removebyindex(i);
+							
+						colormod = 100.0f;
+						gfx_setcolor(1,0.75f,0.75f,1);
+						editor.tooltip = "Left mouse to drag. Hold shift to alter time point aswell. Right click to delete.";
+					}
+
+					if(ui_active_item() == id || ui_hot_item() == id)
+					{
+						current_time = envelope->points[i].time;
+						current_value = envelope->points[i].values[c];
+					}
+					
+					gfx_setcolor(colors[c].r*colormod, colors[c].g*colormod, colors[c].b*colormod, 1.0f);
+					gfx_quads_drawTL(final.x, final.y, final.w, final.h);
+				}
+			}
+			gfx_quads_end();
+
+			char buf[512];
+			sprintf(buf, "%.3f %.3f", current_time/1000.0f, fx2f(current_value));
+			ui_do_label(&toolbar, buf, 14.0f, 0, -1);
 		}
 	}
-	else
+}
+
+static void editor_render()
+{
+	// basic start
+	gfx_clear(1.0f,0.0f,1.0f);
+	RECT view = *ui_screen();
+	gfx_mapscreen(ui_screen()->x, ui_screen()->y, ui_screen()->w, ui_screen()->h);
+	
+	// reset tip
+	editor.tooltip = 0;
+	
+	// render checker
+	render_background(view, checker_texture, 32.0f, 1.0f);
+	
+	RECT modebar, toolbar, statusbar, envelope_editor, propsbar;
+	
+	if(editor.gui_active)
 	{
-		int current_type = -1;
-		if(editor_selected_ent >= 0 && editor_selected_ent < ents_count())
-			current_type = ents_get(editor_selected_ent)->type;
 		
-		float y = 0;
-		for(int i = 0; ent_types[i].name; i++)
-		{
-			if(ui_do_button(&ent_types[i], ent_types[i].name, current_type==i, 0, y, toolbox_width, 6, draw_editor_button, 0))
-			{
-				if(editor_selected_ent >= 0 && editor_selected_ent < ents_count())
-					ents_get(editor_selected_ent)->type = i;
-			}
-			y += 8;
-		}
+		ui_hsplit_t(&view, 16.0f, &toolbar, &view);
+		ui_vsplit_l(&view, 80.0f, &propsbar, &view);
+		ui_hsplit_b(&view, 16.0f, &view, &statusbar);
+
+				
+		float brightness = 0.25f;
+
+		render_background(propsbar, background_texture, 128.0f, brightness);
+		ui_margin(&propsbar, 2.0f, &propsbar);
+		
+		render_background(toolbar, background_texture, 128.0f, brightness);
+		ui_margin(&toolbar, 2.0f, &toolbar);
+		ui_vsplit_l(&toolbar, 220.0f, &modebar, &toolbar);
 
-		y += 8;
-		static int add, del;
-		if(ui_do_button(&add, "Add", 0, 0, y, toolbox_width, 6, draw_editor_button, 0))
+		render_background(statusbar, background_texture, 128.0f, brightness);
+		ui_margin(&statusbar, 2.0f, &statusbar);
+		
+		if(editor.show_envelope_editor)
 		{
-			int x = (int)(world_offset_x+400*zoom/2)/32*32+16;
-			int y = (int)(world_offset_y+300*zoom/2)/32*32+16;
-			
-			if(editor_selected_ent >= 0 && editor_selected_ent < ents_count())
-				ents_new(ents_get(editor_selected_ent)->type, x, y);
-			else
-				ents_new(0, x, y);
+			float size = 125.0f;
+			if(editor.show_envelope_editor == 2)
+				size *= 2.0f;
+			else if(editor.show_envelope_editor == 3)
+				size *= 3.0f;
+			ui_hsplit_b(&view, size, &view, &envelope_editor);
+			render_background(envelope_editor, background_texture, 128.0f, brightness);
+			ui_margin(&envelope_editor, 2.0f, &envelope_editor);
 		}
-		
-		y += 8;
-		if(ui_do_button(&del, "Del", 0, 0, y, toolbox_width, 6, draw_editor_button, 0))
-			ents_delete(editor_selected_ent);
 	}
 	
-}
+	if(editor.dialog == DIALOG_LOAD_IMAGE)
+		render_dialog_load_image();
+	else if(editor.mode == MODE_LAYERS)
+		render_layers(propsbar, toolbar, view);
+	else if(editor.mode == MODE_IMAGES)
+		render_images(propsbar, view);
+		
+	gfx_mapscreen(ui_screen()->x, ui_screen()->y, ui_screen()->w, ui_screen()->h);
 
-static void editor_render()
-{
-	if(editor_loadimage != -1)
-		editor_render_loadfile_dialog();
-	else if(editor_loadmap != -1)
-		editor_render_loadmap_dialog();
-	else
-		editor_render_normal();
+	if(editor.gui_active)
+	{
+		render_modebar(modebar);
+		if(editor.show_envelope_editor)
+			render_envelopeeditor(envelope_editor);
+		render_statusbar(statusbar);
+	}
 	
+	//do_propsdialog();
+
 	// render butt ugly mouse cursor
 	float mx = ui_mouse_x();
 	float my = ui_mouse_y();
-	gfx_texture_set(-1);
+	gfx_texture_set(cursor_texture);
 	gfx_quads_begin();
-	gfx_setcolor(0,0,0,1);
-	gfx_quads_draw_freeform(mx,my,mx,my,
-							mx+7,my,
-							mx,my+7);
-	gfx_setcolor(1,1,1,1);
-	gfx_quads_draw_freeform(mx+1,my+1,mx+1,my+1,
-							mx+5,my+1,
-							mx+1,my+5);
+	gfx_quads_drawTL(mx,my, 16.0f, 16.0f);
 	gfx_quads_end();	
 }
 
-extern "C" void editor_update_and_render()
+void editor_reset(bool create_default=true)
 {
-	static int mouse_x = 0;
-	static int mouse_y = 0;
-		
-	// handle mouse movement
-	float mx, my, mwx, mwy;
-	int rx, ry;
-	{
-		inp_mouse_relative(&rx, &ry);
-		mouse_x += rx;
-		mouse_y += ry;
-		if(mouse_x < 0) mouse_x = 0;
-		if(mouse_y < 0) mouse_y = 0;
-		if(mouse_x > gfx_screenwidth()) mouse_x = gfx_screenwidth();
-		if(mouse_y > gfx_screenheight()) mouse_y = gfx_screenheight();
-
-		// update the ui
-		mx = (mouse_x/(float)gfx_screenwidth())*400.0f;
-		my = (mouse_y/(float)gfx_screenheight())*300.0f;
-		mwx = world_offset_x+mx*world_zoom; // adjust to zoom and offset
-		mwy = world_offset_y+my*world_zoom; // adjust to zoom and offset
-		
-		int buttons = 0;
-		if(inp_key_pressed(KEY_MOUSE_1)) buttons |= 1;
-		if(inp_key_pressed(KEY_MOUSE_2)) buttons |= 2;
-		if(inp_key_pressed(KEY_MOUSE_3)) buttons |= 4;
-		
-		ui_update(mx,my,mwx,mwy,buttons);
-	}
+	editor.map.groups.deleteall();
+	editor.map.envelopes.deleteall();
+	editor.map.images.deleteall();
 	
-	// render the editor
-	editor_render();
+	editor.game_layer = 0;
+	editor.game_group = 0;
 
-	if(inp_key_pressed(KEY_LALT) || inp_key_pressed(KEY_RALT))
+	// create default layers
+	if(create_default)
 	{
-		// steal focus
-		static int moveid;
-		ui_set_hot_item(&moveid);
-
-		if(inp_key_pressed(KEY_MOUSE_1))
-		{
-			world_offset_x -= rx*2;
-			world_offset_y -= ry*2;
-		}
+		editor.make_game_group(editor.map.new_group());
+		editor.make_game_layer(new LAYER_GAME(50, 50));
+		editor.game_group->add_layer(editor.game_layer);
 	}
+}
 
-	//
-	if(inp_key_pressed(KEY_F1))
-		inp_mouse_mode_absolute();
-	if(inp_key_pressed(KEY_F2))
-		inp_mouse_mode_relative();
+void EDITOR::make_game_layer(LAYER *layer)
+{
+	editor.game_layer = (LAYER_GAME *)layer;
+	editor.game_layer->tex_id = entities_texture;
+	editor.game_layer->readonly = true;
+}
 
-	// mode switch
-	if(inp_key_down(KEY_TAB))
-		editor_mode ^= 1;
+void EDITOR::make_game_group(LAYERGROUP *group)
+{
+	editor.game_group = group;
+	editor.game_group->game_group = true;
+	editor.game_group->name = "Game";
+}
+
+typedef struct 
+{
+	int version;
+	int width;
+	int height;
+	int external;
+	int image_name;
+	int image_data;
+} MAPITEM_IMAGE;
+
+typedef struct
+{
+	int version;
+	int offset_x;
+	int offset_y;
+	int parallax_x;
+	int parallax_y;
+
+	int start_layer;
+	int num_layers;
+} MAPITEM_GROUP;
+
+typedef struct
+{
+	int version;
+	int type;
+	int flags;
+} MAPITEM_LAYER;
+
+typedef struct
+{
+	MAPITEM_LAYER layer;
+	int version;
+	
+	int width;
+	int height;
+	int flags;
+	
+	COLOR color;
+	int color_env;
+	int color_env_offset;
+	
+	int image;
+	int data;
+} MAPITEM_LAYER_TILEMAP;
+
+typedef struct
+{
+	MAPITEM_LAYER layer;
+	int version;
 	
-	// zoom in
-	if(inp_key_down(KEY_KP_ADD))
+	int num_quads;
+	int data;
+	int image;
+} MAPITEM_LAYER_QUADS;
+
+typedef struct
+{
+	int version;
+} MAPITEM_VERSION;
+
+template<typename T>
+static int make_version(int i, const T &v)
+{ return (i<<16)+sizeof(T); }
+
+enum
+{
+	MAPITEMTYPE_VERSION=0,
+	MAPITEMTYPE_INFO,
+	MAPITEMTYPE_IMAGE,
+	MAPITEMTYPE_ENVELOPE,
+	MAPITEMTYPE_GROUP,
+	MAPITEMTYPE_LAYER,
+};
+
+
+void editor_load_old(DATAFILE *df)
+{
+	// load tilemaps
+	int game_width = 0;
+	int game_height = 0;
 	{
-		world_zoom--;
-		if(world_zoom < 1)
-			world_zoom = 1;
+		int start, num;
+		datafile_get_type(df, MAPRES_TILEMAP, &start, &num);
+		for(int t = 0; t < num; t++)
+		{
+			mapres_tilemap *tmap = (mapres_tilemap *)datafile_get_item(df, start+t,0,0);
+			
+			LAYER_TILES *l = new LAYER_TILES(tmap->width, tmap->height);
+			
+			if(tmap->main)
+			{
+				// move game layer to correct position
+				for(int i = 0; i < editor.map.groups[0]->layers.len()-1; i++)
+				{
+					if(editor.map.groups[0]->layers[i] == editor.game_layer)
+						editor.map.groups[0]->swap_layers(i, i+1);
+				}
+				
+				game_width = tmap->width;
+				game_height = tmap->height;
+			}
+
+			// add new layer
+			editor.map.groups[0]->add_layer(l);
+
+			// process the data
+			unsigned char *src_data = (unsigned char *)datafile_get_data(df, tmap->data);
+			TILE *dst_data = l->tiles;
+			
+			for(int y = 0; y < tmap->height; y++)
+				for(int x = 0; x < tmap->width; x++, dst_data++, src_data+=2)
+				{
+					dst_data->index = src_data[0];
+					dst_data->flags = src_data[1];
+				}
+				
+			l->image = tmap->image;
+		}
 	}
 	
-	// zoom out
-	if(inp_key_down(KEY_KP_SUBTRACT))
+	// load images
 	{
-		world_zoom++;
-		if(world_zoom > 16)
-			world_zoom = 16;
+		int start, count;
+		datafile_get_type(df, MAPRES_IMAGE, &start, &count);
+		for(int i = 0; i < count; i++)
+		{
+			mapres_image *imgres = (mapres_image *)datafile_get_item(df, start+i, 0, 0);
+			void *data = datafile_get_data(df, imgres->image_data);
+
+			IMAGE *img = new IMAGE;
+			img->width = imgres->width;
+			img->height = imgres->height;
+			img->format = IMG_RGBA;
+			
+			// copy image data
+			img->data = mem_alloc(img->width*img->height*4, 1);
+			mem_copy(img->data, data, img->width*img->height*4);
+			img->tex_id = gfx_load_texture_raw(img->width, img->height, img->format, img->data, IMG_AUTO);
+			editor.map.images.add(img);
+			
+			// unload image
+			datafile_unload_data(df, imgres->image_data);
+		}
 	}
 	
-	if(inp_key_pressed(KEY_LCTRL) || inp_key_pressed(KEY_RCTRL))
+	// load entities
 	{
-		if(inp_key_down('L'))
+		LAYER_GAME *g = editor.game_layer;
+		g->resize(game_width, game_height);
+		for(int t = MAPRES_ENTS_START; t < MAPRES_ENTS_END; t++)
 		{
-			int w = 50, h = 50;
-			for(int i = 0; i < layers_count(); i++)
+			// fetch entities of this class
+			int start, num;
+			datafile_get_type(df, t, &start, &num);
+
+
+			for(int i = 0; i < num; i++)
 			{
-				layer *l = layers_get(i);	
-				if(l->main_layer)
+				mapres_entity *e = (mapres_entity *)datafile_get_item(df, start+i,0,0);
+				int x = e->x/32;
+				int y = e->y/32;
+				int id = -1;
+					
+				if(t == MAPRES_SPAWNPOINT) id = ENTITY_SPAWN;
+				else if(t == MAPRES_SPAWNPOINT_RED) id = ENTITY_SPAWN_RED;
+				else if(t == MAPRES_SPAWNPOINT_BLUE) id = ENTITY_SPAWN_BLUE;
+				else if(t == MAPRES_FLAGSTAND_RED) id = ENTITY_FLAGSTAND_RED;
+				else if(t == MAPRES_FLAGSTAND_BLUE) id = ENTITY_FLAGSTAND_BLUE;
+				else if(t == MAPRES_ITEM)
 				{
-					w = l->tm.width;
-					h = l->tm.height;
-					break;
+					if(e->data[0] == ITEM_WEAPON_SHOTGUN) id = ENTITY_WEAPON_SHOTGUN;
+					else if(e->data[0] == ITEM_WEAPON_ROCKET) id = ENTITY_WEAPON_ROCKET;
+					else if(e->data[0] == ITEM_NINJA) id = ENTITY_POWERUP_NINJA;
+					else if(e->data[0] == ITEM_ARMOR) id = ENTITY_ARMOR_1;
+					else if(e->data[0] == ITEM_HEALTH) id = ENTITY_HEATH_1;
 				}
-			}					
-			// copy main layer size
-			layers_new(w, h);
+						
+				if(id > 0 && x >= 0 && x < g->width && y >= 0 && y < g->height)
+					g->tiles[y*g->width+x].index = id;
+			}
 		}
+	}
+}
+
+int EDITOR::save(const char *filename)
+{
+	dbg_msg("editor", "saving to '%s'...", filename);
+	DATAFILE_OUT *df = datafile_create(filename);
+	if(!df)
+	{
+		dbg_msg("editor", "failed to open file '%s'...", filename);
+		return 0;
+	}
 		
-		if(inp_key_down('O'))
-			editor_loadmap = 1;
-		
-		if(inp_key_down('S'))
-		{
-			dbg_msg("editor", "save");
-			editor_save(editor_filename);
-			client_rcon("sv_map_reload=1");
-		}
+	// save version
+	{
+		MAPITEM_VERSION item;
+		item.version = 1;
+		datafile_add_item(df, MAPITEMTYPE_VERSION, 0, sizeof(item), &item);
+	}
 
+	// save images
+	for(int i = 0; i < map.images.len(); i++)
+	{
+		IMAGE *img = map.images[i];
+		MAPITEM_IMAGE item;
+		item.version = 1;
+		
+		item.width = img->width;
+		item.height = img->height;
+		item.external = 0;
+		item.image_name = -1;
+		item.image_data = datafile_add_data(df, item.width*item.height*4, img->data);
+		datafile_add_item(df, MAPITEMTYPE_IMAGE, i, sizeof(item), &item);
 	}
 	
-	if(inp_key_down(KEY_F5))
+	// save layers
+	int layer_count = 0;
+	for(int g = 0; g < map.groups.len(); g++)
 	{
-		dbg_msg("editor", "quick save");
-		editor_save("quicksave.map");
+		LAYERGROUP *group = map.groups[g];
+		MAPITEM_GROUP gitem;
+		gitem.version = 1;
+		
+		gitem.parallax_x = group->parallax_x;
+		gitem.parallax_y = group->parallax_y;
+		gitem.offset_x = group->offset_x;
+		gitem.offset_y = group->offset_y;
+		gitem.start_layer = layer_count;
+		gitem.num_layers = 0;
+		
+		for(int l = 0; l < group->layers.len(); l++)
+		{
+			if(group->layers[l]->type == LAYERTYPE_TILES)
+			{
+				dbg_msg("editor", "saving tiles layer");
+				LAYER_TILES *layer = (LAYER_TILES *)group->layers[l];
+				MAPITEM_LAYER_TILEMAP item;
+				item.version = 2;
+				
+				item.layer.flags = 0;
+				item.layer.type = layer->type;
+				
+				item.color.r = 255; // not in use right now
+				item.color.g = 255;
+				item.color.b = 255;
+				item.color.a = 255;
+				item.color_env = -1;
+				item.color_env_offset = 0;
+				
+				item.width = layer->width;
+				item.height = layer->height;
+				item.flags = layer->game;
+				item.image = layer->image;
+				item.data = datafile_add_data(df, layer->width*layer->height*sizeof(TILE), layer->tiles);
+				datafile_add_item(df, MAPITEMTYPE_LAYER, layer_count, sizeof(item), &item);
+				
+				gitem.num_layers++;
+				layer_count++;
+			}
+			else if(group->layers[l]->type == LAYERTYPE_QUADS)
+			{
+				dbg_msg("editor", "saving quads layer");
+				LAYER_QUADS *layer = (LAYER_QUADS *)group->layers[l];
+				if(layer->quads.len())
+				{
+					MAPITEM_LAYER_QUADS item;
+					item.version = 1;
+					item.layer.flags = 0;
+					item.layer.type = layer->type;
+					item.image = layer->image;
+					
+					// add the data
+					item.num_quads = layer->quads.len();
+					item.data = datafile_add_data_swapped(df, layer->quads.len()*sizeof(QUAD), layer->quads.getptr());
+					datafile_add_item(df, MAPITEMTYPE_LAYER, layer_count, sizeof(item), &item);
+					
+					// clean up
+					//mem_free(quads);
+
+					gitem.num_layers++;
+					layer_count++;
+				}
+			}
+		}
+		
+		datafile_add_item(df, MAPITEMTYPE_GROUP, g, sizeof(gitem), &gitem);
 	}
 	
-	if(inp_key_down(KEY_F8))
+	// finish the data file
+	datafile_finish(df);
+	dbg_msg("editor", "done");
+	return 1;
+}
+
+
+int EDITOR::load(const char *filename)
+{
+	DATAFILE *df = datafile_load(filename);
+	if(!df)
+		return 0;
+
+	// check version
+	MAPITEM_VERSION *item = (MAPITEM_VERSION *)datafile_find_item(df, MAPITEMTYPE_VERSION, 0);
+	if(!item)
 	{
-		dbg_msg("editor", "quick load");
-		int s = current_layer;
+		// import old map
 		editor_reset();
-		editor_load("quicksave.map");
-		current_layer = s;
-		if(current_layer >= layers_count())
-			current_layer = layers_count();
+		editor_load_old(df);
+	}
+	else if(item->version == 1)
+	{
+		editor_reset(false);
+		
+		// load images
+		{
+			int start, num;
+			datafile_get_type(df, MAPITEMTYPE_IMAGE, &start, &num);
+			for(int i = 0; i < num; i++)
+			{
+				MAPITEM_IMAGE *item = (MAPITEM_IMAGE *)datafile_get_item(df, start+i, 0, 0);
+				void *data = datafile_get_data(df, item->image_data);
+				
+				IMAGE *img = new IMAGE;
+				img->width = item->width;
+				img->height = item->height;
+				img->format = IMG_RGBA;
+				
+				// copy image data
+				img->data = mem_alloc(img->width*img->height*4, 1);
+				mem_copy(img->data, data, img->width*img->height*4);
+				img->tex_id = gfx_load_texture_raw(img->width, img->height, img->format, img->data, IMG_AUTO);
+				editor.map.images.add(img);
+				
+				// unload image
+				datafile_unload_data(df, item->image_data);
+			}
+		}
+		
+		// load groups
+		{
+			int layers_start, layers_num;
+			datafile_get_type(df, MAPITEMTYPE_LAYER, &layers_start, &layers_num);
 			
+			int start, num;
+			datafile_get_type(df, MAPITEMTYPE_GROUP, &start, &num);
+			for(int g = 0; g < num; g++)
+			{
+				MAPITEM_GROUP *gitem = (MAPITEM_GROUP *)datafile_get_item(df, start+g, 0, 0);
+				LAYERGROUP *group = map.new_group();
+				group->parallax_x = gitem->parallax_x;
+				group->parallax_y = gitem->parallax_y;
+				group->offset_x = gitem->offset_x;
+				group->offset_y = gitem->offset_y;
+				
+				for(int l = 0; l < gitem->num_layers; l++)
+				{
+					MAPITEM_LAYER *layer_item = (MAPITEM_LAYER *)datafile_get_item(df, layers_start+gitem->start_layer+l, 0, 0);
+					if(!layer_item)
+						continue;
+						
+					if(layer_item->type == LAYERTYPE_TILES)
+					{
+						MAPITEM_LAYER_TILEMAP *tilemap_item = (MAPITEM_LAYER_TILEMAP *)layer_item;
+						LAYER_TILES *tiles = 0;
+						
+						if(tilemap_item->flags&1)
+						{
+							tiles = new LAYER_GAME(tilemap_item->width, tilemap_item->height);
+							editor.make_game_layer(tiles);
+							make_game_group(group);
+						}
+						else
+							tiles = new LAYER_TILES(tilemap_item->width, tilemap_item->height);
+						
+						group->add_layer(tiles);
+						void *data = datafile_get_data(df, tilemap_item->data);
+						tiles->image = tilemap_item->image;
+						tiles->game = tilemap_item->flags&1;
+						
+						mem_copy(tiles->tiles, data, tiles->width*tiles->height*sizeof(TILE));
+						
+						if(tiles->game && tilemap_item->version == make_version(1, *tilemap_item))
+						{
+							for(int i = 0; i < tiles->width*tiles->height; i++)
+							{
+								if(tiles->tiles[i].index)
+									tiles->tiles[i].index += ENTITY_OFFSET;
+							}
+						}
+						
+						datafile_unload_data(df, tilemap_item->data);
+					}
+					else if(layer_item->type == LAYERTYPE_QUADS)
+					{
+						MAPITEM_LAYER_QUADS *quads_item = (MAPITEM_LAYER_QUADS *)layer_item;
+						LAYER_QUADS *layer = new LAYER_QUADS;
+						layer->image = quads_item->image;
+						if(layer->image < -1 || layer->image >= map.images.len())
+							layer->image = -1;
+						void *data = datafile_get_data_swapped(df, quads_item->data);
+						group->add_layer(layer);
+						layer->quads.setsize(quads_item->num_quads);
+						mem_copy(layer->quads.getptr(), data, sizeof(QUAD)*quads_item->num_quads);
+						datafile_unload_data(df, quads_item->data);
+					}
+				}
+			}
+		}
 	}
+	
+	datafile_unload(df);
+	
+	return 0;
 }
 
+
 extern "C" void editor_init()
 {
-	// reset and start
-	font_texture = gfx_load_texture("data/debug_font.png", IMG_AUTO);
-	checker_texture = gfx_load_texture("data/checker.png", IMG_AUTO);
+	checker_texture = gfx_load_texture("data/editor/checker.png", IMG_AUTO);
+	background_texture = gfx_load_texture("data/editor/background.png", IMG_AUTO);
+	cursor_texture = gfx_load_texture("data/editor/cursor.png", IMG_AUTO);
+	entities_texture = gfx_load_texture("data/editor/entities.png", IMG_AUTO);
+	
+	tileset_picker.make_palette();
+	tileset_picker.readonly = true;
+	
 	editor_reset();
+	//editor.load("debug_test.map");
+	
+#if 0
+	IMAGE *img = new IMAGE;
+	gfx_load_png(img, "tilesets/grassland_main.png");
+	img->tex_id = gfx_load_texture_raw(img->width, img->height, img->format, img->data);
+	editor.map.images.add(img);
 	
-	layer *l = layers_get(layers_new(50, 50));
-	l->main_layer = 1;
-}
 
-/*
-extern void modmenu_init();
+	ENVELOPE *e = editor.map.new_envelope(4);
+	e->add_point(0, 0, 0);
+	e->add_point(1000, f2fx(1), f2fx(0.75f));
+	e->add_point(2000, f2fx(0.75f), f2fx(1));
+	e->add_point(3000, 0, 0);
+	
+	editor.animate = true;
+	editor.animate_start = time_get();
+	
+	editor.show_envelope_editor = 1;
+#endif
 
-extern "C" int editor_main(int argc, char **argv)
+	if(1)
+	{
+		float w, h;
+		float amount = 1300*1000;
+		float max = 1500;
+		dbg_msg("", "%f %f %f %f", (900*(5/4.0f))*900.0f, (900*(4/3.0f))*900.0f, (900*(16/9.0f))*900.0f, (900*(16/10.0f))*900.0f);
+		dbg_msg("", "%f", 900*(16/9.0f));
+		calc_screen_params(amount, max, max, 5.0f/4.0f, &w, &h); dbg_msg("", "5:4 %f %f %f", w, h, w*h);
+		calc_screen_params(amount, max, max, 4.0f/3.0f, &w, &h); dbg_msg("", "4:3 %f %f %f", w, h, w*h);
+		calc_screen_params(amount, max, max, 16.0f/9.0f, &w, &h); dbg_msg("", "16:9 %f %f %f", w, h, w*h);
+		calc_screen_params(amount, max, max, 16.0f/10.0f, &w, &h); dbg_msg("", "16:10 %f %f %f", w, h, w*h);
+		
+		calc_screen_params(amount, max, max, 9.0f/16.0f, &w, &h); dbg_msg("", "%f %f %f", w, h, w*h);
+		calc_screen_params(amount, max, max, 16.0f/3.0f, &w, &h); dbg_msg("", "%f %f %f", w, h, w*h);
+		calc_screen_params(amount, max, max, 3.0f/16.0f, &w, &h); dbg_msg("", "%f %f %f", w, h, w*h);
+	}
+}
+
+extern "C" void editor_update_and_render()
 {
-	dbg_msg("editor", "starting...");
-#ifdef CONF_PLATFORM_MACOSX	
-	config_load("~/teewars");
-#else
-	config_load("default.cfg");
-#endif
-	
-	// parse arguments
-	for(int i = 1; i < argc; i++)
+	static int mouse_x = 0;
+	static int mouse_y = 0;
+
+	editor.animate_time = (time_get()-editor.animate_start)/(float)time_freq();
+
+	// handle mouse movement
+	float mx, my, mwx, mwy;
+	int rx, ry;
 	{
-		if(argv[i][0] == '-' && argv[i][1] == 'e' && argv[i][2] == 0 && argc - i > 1)
+		inp_mouse_relative(&rx, &ry);
+		editor.mouse_delta_x = rx;
+		editor.mouse_delta_y = ry;
+		
+		if(!editor.lock_mouse)
 		{
-			// -e NAME
-			i++;
-			editor_filename = argv[i];
+			mouse_x += rx;
+			mouse_y += ry;
 		}
+		
+		if(mouse_x < 0) mouse_x = 0;
+		if(mouse_y < 0) mouse_y = 0;
+		if(mouse_x > ui_screen()->w) mouse_x = (int)ui_screen()->w;
+		if(mouse_y > ui_screen()->h) mouse_y = (int)ui_screen()->h;
+
+		// update the ui
+		mx = mouse_x;
+		my = mouse_y;
+		mwx = 0;
+		mwy = 0;
+		
+		// fix correct world x and y
+		LAYERGROUP *g = editor.get_selected_group();
+		if(g)
+		{
+			float points[4];
+			g->mapping(points);
+
+			float world_width = points[2]-points[0];
+			float world_height = points[3]-points[1];
+			
+			mwx = points[0] + world_width * (mouse_x/ui_screen()->w);
+			mwy = points[1] + world_height * (mouse_y/ui_screen()->h);
+			editor.mouse_delta_wx = editor.mouse_delta_x*(world_width / ui_screen()->w);
+			editor.mouse_delta_wy = editor.mouse_delta_y*(world_height / ui_screen()->h);
+		}
+		
+		int buttons = 0;
+		if(inp_key_pressed(KEY_MOUSE_1)) buttons |= 1;
+		if(inp_key_pressed(KEY_MOUSE_2)) buttons |= 2;
+		if(inp_key_pressed(KEY_MOUSE_3)) buttons |= 4;
+		
+		ui_update(mx,my,mwx,mwy,buttons);
 	}
 	
-	if(!editor_filename)
-	{
-		dbg_msg("editor", "no filename given");
-		return -1;
-	}
-	
-	if(!gfx_init())
-		return -1;
-	
-	// load or new
-	if(!editor_load(editor_filename))
-	{
-		layer *l = layers_get(layers_new(50, 50));
-		l->main_layer = 1;
-	}
+	// toggle gui
+	if(inp_key_down(KEY_TAB))
+		editor.gui_active = !editor.gui_active;
+
+	if(inp_key_down(KEY_F5))
+		editor.save("data/maps/debug_test2.map");
 	
-	editor_loop();
+	if(inp_key_down(KEY_F8))
+		editor.load("data/maps/debug_test.map");
 	
-	return 0;
-}*/
+	editor_render();
+	inp_clear_events();
+}
+
diff --git a/src/editor/editor.hpp b/src/editor/editor.hpp
new file mode 100644
index 00000000..8630cc0c
--- /dev/null
+++ b/src/editor/editor.hpp
@@ -0,0 +1,581 @@
+#include <stdlib.h>
+#include <math.h>
+#include "array.h"
+
+extern "C" {
+	#include <engine/e_system.h>
+	#include <engine/client/ec_ui.h>
+	#include <engine/e_interface.h>
+	#include <engine/e_datafile.h>
+	#include <engine/e_config.h>
+}
+
+// EDITOR SPECIFIC
+template<typename T>
+void swap(T &a, T &b)
+{
+	T tmp = a;
+	a = b;
+	b = tmp;
+}
+
+enum
+{
+	MODE_MAP=0,
+	MODE_LAYERS,
+	MODE_IMAGES,
+	
+	DIALOG_NONE=0,
+	DIALOG_LOAD_IMAGE,
+	
+	LAYERTYPE_INVALID=0,
+	LAYERTYPE_GAME,
+	LAYERTYPE_TILES,
+	LAYERTYPE_QUADS,
+};
+
+typedef struct
+{
+	int x, y; // 22.10 fixed point
+} POINT;
+
+// float to fixed
+inline int f2fx(float v) { return (int)(v*(float)(1<<10)); }
+inline float fx2f(int v) { return v*(1.0f/(1<<10)); }
+
+typedef struct // as in file
+{
+	int r, g, b, a;
+} COLOR;
+
+typedef struct // as in file
+{
+	POINT points[5];
+	COLOR colors[4];
+	POINT texcoords[4];
+	
+	int pos_env;
+	int pos_env_offset;
+	
+	int color_env;
+	int color_env_offset;
+} QUAD;
+
+typedef struct // as in file
+{
+	POINT position;
+	int type;
+} ENTITY;
+
+enum
+{
+	ENTITY_NULL=0,
+	ENTITY_SPAWN,
+	ENTITY_SPAWN_RED,
+	ENTITY_SPAWN_BLUE,
+	ENTITY_FLAGSTAND_RED,
+	ENTITY_FLAGSTAND_BLUE,
+	ENTITY_ARMOR_1,
+	ENTITY_HEATH_1,
+	ENTITY_WEAPON_SHOTGUN,
+	ENTITY_WEAPON_ROCKET,
+	ENTITY_POWERUP_NINJA,
+	NUM_ENTITIES,
+	
+	TILE_AIR=0,
+	TILE_SOLID,
+	TILE_NOHOOK,
+	
+	ENTITY_OFFSET=255-16*4,
+};
+
+typedef struct // as in file
+{
+	unsigned char index;
+	unsigned char flags;
+	unsigned char reserved1;
+	unsigned char reserved2;
+} TILE;
+
+enum
+{
+	CURVETYPE_STEP=0,
+	CURVETYPE_LINEAR,
+	CURVETYPE_SLOW,
+	CURVETYPE_FAST,
+	CURVETYPE_SMOOTH,
+	NUM_CURVETYPES,
+	
+};
+
+typedef struct // as in file
+{
+	int time; // in ms
+	int curvetype;
+	int values[4]; // 1-4 depending on envelope (22.10 fixed point)
+} ENVPOINT;
+
+class ENVELOPE
+{
+public:
+	int channels;
+	array<ENVPOINT> points;
+	char name[32];
+	float bottom, top;
+	
+	ENVELOPE(int chan)
+	{
+		channels = chan;
+		name[0] = 0;
+		bottom = 0;
+		top = 0;
+	}
+	
+	static int sort_comp(const void *v0, const void *v1)
+	{
+		const ENVPOINT *p0 = (const ENVPOINT *)v0;
+		const ENVPOINT *p1 = (const ENVPOINT *)v1;
+		if(p0->time < p1->time)
+			return -1;
+		if(p0->time > p1->time)
+			return 1;
+		return 0;
+	}
+	
+	void resort()
+	{
+		qsort(points.getptr(), points.len(), sizeof(ENVPOINT), sort_comp);
+		find_top_bottom();
+	}
+
+	void find_top_bottom()
+	{
+		top = -1000000000.0f;
+		bottom = 1000000000.0f;
+		for(int i = 0; i < points.len(); i++)
+		{
+			for(int c = 0; c < channels; c++)
+			{
+				float v = fx2f(points[i].values[c]);
+				if(v > top) top = v;
+				if(v < bottom) bottom = v;
+			}
+		}
+	}
+	
+	float eval(float time, int channel)
+	{
+		if(channel >= channels)
+			return 0;
+		if(points.len() == 0)
+			return 0;
+		if(points.len() == 1)
+			return points[0].values[channel];
+		
+		time = fmod(time, end_time())*1000.0f;
+		for(int i = 0; i < points.len() - 1; i++)
+		{
+			if(time >= points[i].time && time <= points[i+1].time)
+			{
+				float delta = points[i+1].time-points[i].time;
+				float a = (time-points[i].time)/delta;
+				
+				float v0 = fx2f(points[i].values[channel]);
+				float v1 = fx2f(points[i+1].values[channel]);
+				
+				if(points[i].curvetype == CURVETYPE_SMOOTH)
+					a = -2*a*a*a + 3*a*a; // second hermite basis
+				else if(points[i].curvetype == CURVETYPE_SLOW)
+					a = a*a*a;
+				else if(points[i].curvetype == CURVETYPE_FAST)
+				{
+					a = 1-a;
+					a = 1-a*a*a;
+				}
+				else if (points[i].curvetype == CURVETYPE_STEP)
+					a = 0;
+				else
+				{
+					// linear
+				}
+		
+				return v0 + (v1-v0) * a;
+			}
+		}
+		
+		return points[points.len()-1].values[channel];
+	}
+	
+	void add_point(int time, int v0, int v1=0, int v2=0, int v3=0)
+	{
+		ENVPOINT p;
+		p.time = time;
+		p.values[0] = v0;
+		p.values[1] = v1;
+		p.values[2] = v2;
+		p.values[3] = v3;
+		p.curvetype = CURVETYPE_LINEAR;
+		points.add(p);
+		resort();
+	}
+	
+	float end_time()
+	{
+		if(points.len())
+			return points[points.len()-1].time*(1.0f/1000.0f);
+		return 0;
+	}
+};
+
+
+class LAYER;
+class LAYERGROUP;
+class MAP;
+
+class LAYER
+{
+public:
+	LAYER()
+	{
+		type = LAYERTYPE_INVALID;
+		type_name = "(invalid)";
+		visible = true;
+		readonly = false;
+	}
+	
+	virtual ~LAYER()
+	{
+	}
+	
+	
+	virtual void brush_selecting(RECT rect) {}
+	virtual int brush_grab(LAYERGROUP *brush, RECT rect) { return 0; }
+	virtual void brush_draw(LAYER *brush, float x, float y) {}
+	virtual void brush_place(LAYER *brush, float x, float y) {}
+	virtual void brush_flip_x() {}
+	virtual void brush_flip_y() {}
+	
+	virtual void render() {}
+	virtual void render_properties(RECT *toolbox) {}
+	
+	virtual void get_size(float *w, float *h) { *w = 0; *h = 0;}
+	
+	const char *type_name;
+	int type;
+
+	bool readonly;
+	bool visible;
+};
+
+class LAYERGROUP
+{
+public:
+	array<LAYER*> layers;
+	
+	int offset_x;
+	int offset_y;
+
+	int parallax_x;
+	int parallax_y;
+	
+	const char *name;
+	bool game_group;
+	bool visible;
+	
+	LAYERGROUP();
+	~LAYERGROUP();
+	
+	void convert(RECT *rect);
+	void render();
+	void mapscreen();
+	void mapping(float *points);
+	
+	bool is_empty() const;
+	void clear();
+	void add_layer(LAYER *l);
+
+	void get_size(float *w, float *h);
+	
+	void delete_layer(int index);
+	int swap_layers(int index0, int index1);
+};
+
+class IMAGE : public IMAGE_INFO
+{
+public:
+	IMAGE()
+	{
+		tex_id = -1;
+		name[0] = 0;
+	}
+	
+	int tex_id;
+	char name[128];
+};
+
+class MAP
+{
+public:
+	array<LAYERGROUP*> groups;
+	array<IMAGE*> images;
+	array<ENVELOPE*> envelopes;
+	
+	ENVELOPE *new_envelope(int channels)
+	{
+		ENVELOPE *e = new ENVELOPE(channels);
+		envelopes.add(e);
+		return e;
+	}
+	
+	LAYERGROUP *new_group()
+	{
+		LAYERGROUP *g = new LAYERGROUP;
+		groups.add(g);
+		return g;
+	}
+	
+	int swap_groups(int index0, int index1)
+	{
+		if(index0 < 0 || index0 >= groups.len()) return index0;
+		if(index1 < 0 || index1 >= groups.len()) return index0;
+		if(index0 == index1) return index0;
+		swap(groups[index0], groups[index1]);
+		return index1;
+	}
+	
+	void delete_group(int index)
+	{
+		if(index < 0 || index >= groups.len()) return;
+		delete groups[index];
+		groups.removebyindex(index);
+	}
+};
+
+
+struct PROPERTY
+{
+	const char *name;
+	int value;
+	int type;
+	int min;
+	int max;
+};
+
+enum
+{
+	PROPTYPE_NULL=0,
+	PROPTYPE_INT_STEP,
+	PROPTYPE_INT_SCROLL,
+	PROPTYPE_COLOR,
+	
+	PROPS_NONE=0,
+	PROPS_GROUP,
+	PROPS_LAYER,
+	PROPS_QUAD,
+	PROPS_QUAD_POINT,
+};
+
+class EDITOR
+{
+public:	
+	EDITOR()
+	{
+		mode = MODE_LAYERS;
+		dialog = 0;
+		tooltip = 0;
+
+		world_offset_x = 0;
+		world_offset_y = 0;
+		editor_offset_x = 0.0f;
+		editor_offset_y = 0.0f;
+		
+		world_zoom = 1.0f;
+		zoom_level = 100;
+		lock_mouse = false;
+		mouse_delta_x = 0;
+		mouse_delta_y = 0;
+		mouse_delta_wx = 0;
+		mouse_delta_wy = 0;
+		
+		gui_active = true;
+		proof_borders = false;
+		
+		
+		animate = false;
+		animate_start = 0;
+		animate_time = 0;
+		
+		props = PROPS_NONE;
+		
+		show_envelope_editor = 0;
+	}
+	
+	void make_game_group(LAYERGROUP *group);
+	void make_game_layer(LAYER *layer);
+	
+	int save(const char *filename);
+	int load(const char *filename);
+
+	QUAD *get_selected_quad();
+	LAYER *get_selected_layer_type(int index, int type);
+	LAYER *get_selected_layer(int index);
+	LAYERGROUP *get_selected_group();
+	
+	class LAYER_GAME *game_layer;
+	LAYERGROUP *game_group;
+	
+	int do_properties(RECT *toolbox, PROPERTY *props, int *ids, int *new_val);
+	
+	int mode;
+	int dialog;
+	const char *tooltip;
+
+	float world_offset_x;
+	float world_offset_y;
+	float editor_offset_x;
+	float editor_offset_y;
+	float world_zoom;
+	int zoom_level;
+	bool lock_mouse;
+	bool gui_active;
+	bool proof_borders;
+	float mouse_delta_x;
+	float mouse_delta_y;
+	float mouse_delta_wx;
+	float mouse_delta_wy;
+	
+	bool animate;
+	int64 animate_start;
+	float animate_time;
+	
+	int props;
+	
+	int show_envelope_editor;
+	
+	MAP map;
+};
+
+extern EDITOR editor;
+
+typedef struct
+{
+	int x, y;
+	int w, h;
+} RECTi;
+
+class LAYER_TILES : public LAYER
+{
+public:
+	LAYER_TILES(int w, int h);
+	~LAYER_TILES();
+
+	void resize(int new_w, int new_h);
+
+	void make_palette();
+	virtual void render();
+
+	int convert_x(float x) const;
+	int convert_y(float y) const;
+	void convert(RECT rect, RECTi *out);
+	void snap(RECT *rect);
+	void clamp(RECTi *rect);
+
+	virtual void brush_selecting(RECT rect);
+	virtual int brush_grab(LAYERGROUP *brush, RECT rect);
+	virtual void brush_draw(LAYER *brush, float wx, float wy);
+	virtual void brush_flip_x();
+	virtual void brush_flip_y();
+	
+	virtual void render_properties(RECT *toolbox);
+
+	void get_size(float *w, float *h) { *w = width*32.0f;  *h = height*32.0f; }
+	
+	int tex_id;
+	int game;
+	int image;
+	int width;
+	int height;
+	TILE *tiles;
+};
+
+class LAYER_QUADS : public LAYER
+{
+public:
+	LAYER_QUADS();
+	~LAYER_QUADS();
+
+	virtual void render();
+	QUAD *new_quad();
+
+	virtual void brush_selecting(RECT rect);
+	virtual int brush_grab(LAYERGROUP *brush, RECT rect);
+	virtual void brush_place(LAYER *brush, float wx, float wy);
+	virtual void brush_flip_x();
+	virtual void brush_flip_y();
+	
+	virtual void render_properties(RECT *toolbox);
+	
+	void get_size(float *w, float *h);
+	
+	int image;
+	array<QUAD> quads;
+};
+
+
+class LAYER_GAME : public LAYER_TILES
+{
+public:
+	LAYER_GAME(int w, int h);
+	~LAYER_GAME();
+
+	virtual void render_properties(RECT *toolbox);
+};
+
+
+inline void calc_screen_params(float amount, float wmax, float hmax, float aspect, float *w, float *h)
+{
+	float f = sqrt(amount) / sqrt(aspect);
+	*w = f*aspect;
+	*h = f;
+	
+	// limit the view
+	if(*w > wmax)
+	{
+		*w = wmax;
+		*h = *w/aspect;
+	}
+	
+	if(*h > hmax)
+	{
+		*h = hmax;
+		*w = *h*aspect;
+	}
+}
+
+inline void mapscreen_to_world(float center_x, float center_y, float parallax_x, float parallax_y,
+	float offset_x, float offset_y, float aspect, float zoom, float *points)
+{
+	float width, height;
+	calc_screen_params(1300*1000, 1500, 1050, aspect, &width, &height);
+	center_x *= parallax_x;
+	center_y *= parallax_y;
+	width *= zoom;
+	height *= zoom;
+	points[0] = offset_x+center_x-width/2;
+	points[1] = offset_x+center_y-height/2;
+	points[2] = offset_x+center_x+width/2;
+	points[3] = offset_x+center_y+height/2;
+}
+/*
+inline void mapscreen_to_world(float center_x, float center_y, float parallax_x, float parallax_y,
+	float offset_x, float offset_y, float aspect, float zoom, float *points)
+{
+	float width = 300*3*zoom*aspect;
+	float height = 300*3*zoom;
+	center_x *= parallax_x;
+	center_y *= parallax_y;
+	points[0] = offset_x+center_x-width/2;
+	points[1] = offset_y+center_y-height/2;
+	points[2] = offset_x+center_x+width/2;
+	points[3] = offset_y+center_y+height/2;
+}*/
diff --git a/src/engine/client/ec_gfx.c b/src/engine/client/ec_gfx.c
index 70712ded..a87818bc 100644
--- a/src/engine/client/ec_gfx.c
+++ b/src/engine/client/ec_gfx.c
@@ -18,6 +18,8 @@
 #define GL_COMPRESSED_RGB_ARB 0x84ED
 #define GL_COMPRESSED_RGBA_ARB 0x84EE
 
+#define TEXTURE_MAX_ANISOTROPY_EXT 0x84FE
+
 enum
 {
 	DRAWING_QUADS=1,
@@ -612,8 +614,31 @@ void gfx_screenshot()
 	do_screenshot = 1;
 }
 
+static int64 next_frame = 0;
+static int record = 0;
+
 void gfx_swap()
 {
+	if(record)
+	{
+		int w = screen_width;
+		int h = screen_height;
+		unsigned char *pixel_data = (unsigned char *)mem_alloc(w*(h+1)*3, 1);
+		/*unsigned char *temp_row = pixel_data+w*h*3;*/
+		glReadPixels(0,0, w, h, GL_RGB, GL_UNSIGNED_BYTE, pixel_data);
+
+		/* clean up */
+		mem_free(pixel_data);
+
+		if(next_frame)
+			next_frame += time_freq()/30;
+		else
+			next_frame = time_get() + time_freq()/30;
+
+		while(time_get() < next_frame)
+			(void)0;
+	}
+	
 	if(do_screenshot)
 	{
 		/* fetch image data */
@@ -642,8 +667,8 @@ void gfx_swap()
 			for(; index < 1000; index++)
 			{
 				IOHANDLE io;
-				sprintf(filename, "screenshots/screenshot%04d.png", index);
-				engine_savepath(filename, wholepath, sizeof(wholepath));
+				sprintf(wholepath, "/home/kma/Desktop/prq/blogimg/editor/screenshot%04d.png", index);
+				/*engine_savepath(filename, wholepath, sizeof(wholepath));*/
 				
 				io = io_open(wholepath, IOFLAG_READ);
 				if(io)
@@ -783,6 +808,26 @@ void gfx_quads_setsubset(float tl_u, float tl_v, float br_u, float br_v)
 	texture[3].v = br_v;
 }
 
+void gfx_quads_setsubset_free(
+	float x0, float y0,
+	float x1, float y1,
+	float x2, float y2,
+	float x3, float y3)
+{
+	texture[0].u = x0;
+	texture[0].v = y0;
+
+	texture[1].u = x1;
+	texture[1].v = y1;
+
+	texture[2].u = x2;
+	texture[2].v = y2;
+
+	texture[3].u = x3;
+	texture[3].v = y3;
+}
+
+
 static void rotate(VEC3 *center, VEC3 *point)
 {
 	float x = point->x - center->x;
diff --git a/src/engine/client/ec_ui.c b/src/engine/client/ec_ui.c
index 5ba9c0d9..3ce40f9c 100644
--- a/src/engine/client/ec_ui.c
+++ b/src/engine/client/ec_ui.c
@@ -24,12 +24,14 @@ static const void *becomming_hot_item = 0;
 static float mouse_x, mouse_y; /* in gui space */
 static float mouse_wx, mouse_wy; /* in world space */
 static unsigned mouse_buttons = 0;
+static unsigned last_mouse_buttons = 0;
 
 float ui_mouse_x() { return mouse_x; }
 float ui_mouse_y() { return mouse_y; }
 float ui_mouse_world_x() { return mouse_wx; }
 float ui_mouse_world_y() { return mouse_wy; }
 int ui_mouse_button(int index) { return (mouse_buttons>>index)&1; }
+int ui_mouse_button_clicked(int index) { return ui_mouse_button(index) && !((last_mouse_buttons>>index)&1) ; }
 
 void ui_set_hot_item(const void *id) { becomming_hot_item = id; }
 void ui_set_active_item(const void *id) { active_item = id; if (id) last_active_item = id; }
@@ -44,6 +46,7 @@ int ui_update(float mx, float my, float mwx, float mwy, int buttons)
     mouse_y = my;
     mouse_wx = mwx;
     mouse_wy = mwy;
+    last_mouse_buttons = mouse_buttons;
     mouse_buttons = buttons;
     hot_item = becomming_hot_item;
     if(active_item)
@@ -52,226 +55,238 @@ int ui_update(float mx, float my, float mwx, float mwy, int buttons)
     return 0;
 }
 
-int ui_mouse_inside(float x, float y, float w, float h)
+int ui_mouse_inside(const RECT *r)
 {
-    if(mouse_x >= x && mouse_x <= x+w && mouse_y >= y && mouse_y <= y+h)
+    if(mouse_x >= r->x && mouse_x <= r->x+r->w && mouse_y >= r->y && mouse_y <= r->y+r->h)
         return 1;
     return 0;
 }
 
-void ui_do_image(int texture, float x, float y, float w, float h)
-{
-    gfx_blend_normal();
-    gfx_texture_set(texture);
-    gfx_quads_begin();
-    gfx_setcolor(1,1,1,1);
-    gfx_quads_setsubset(
-        0.0f, /* startx */
-        0.0f, /* starty */
-        1.0f, /* endx */
-        1.0f); /* endy */                              
-    gfx_quads_drawTL(x,y,w,h);
-    gfx_quads_end();
-}
+static RECT screen = { 0.0f, 0.0f, 848.0f, 480.0f };
 
-void ui_do_label(float x, float y, const char *text, float size)
+RECT *ui_screen()
 {
-    gfx_blend_normal();
-    gfx_texture_set(current_font->font_texture);
-    gfx_pretty_text(x, y, size, text, -1);
-}
+    float aspect = gfx_screenaspect();
+    float w, h;
 
-int ui_do_button(const void *id, const char *text, int checked, float x, float y, float w, float h, draw_button_callback draw_func, void *extra)
-{
-    /* logic */
-    int r = 0;
-    int inside = ui_mouse_inside(x,y,w,h);
+    h = 600;
+    w = aspect*h;
 
-	if(ui_active_item() == id)
-	{
-		if(!ui_mouse_button(0))
-		{
-			if(inside)
-				r = 1;
-			ui_set_active_item(0);
-		}
-	}
-	else if(ui_hot_item() == id)
-	{
-		if(ui_mouse_button(0))
-			ui_set_active_item(id);
-	}
-	
-	if(inside)
-		ui_set_hot_item(id);
+    screen.w = w;
+    screen.h = h;
 
-    draw_func(id, text, checked, x, y, w, h, extra);
-    return r;
+    return &screen;
 }
 
-static float scale = 1.0f;
-#define MEMORY_SIZE 10*1024
-static struct rect memory[MEMORY_SIZE];
-static int memory_used = 0;
-static struct rect screen = { 0.0f, 0.0f, 848.0f, 480.0f };
-
-void ui_foreach_rect(rect_fun fun)
+void ui_set_scale(float s)
 {
-    int hrm;
-    for (hrm = 0; hrm < memory_used; hrm++)
-        fun(&memory[hrm]);
+    config.ui_scale = (int)(s*100.0f);
 }
 
-static void add_rect(struct rect *r)
+float ui_scale()
 {
-    if (memory_used < MEMORY_SIZE)
-        memory[memory_used++] = *r;
-    else
-        dbg_msg("ui", "rect memory full.");
+    return config.ui_scale/100.0f;
 }
 
-struct rect *ui_screen()
+void ui_clip_enable(const RECT *r)
 {
-    if (config.debug)
-    {
-        memory_used = 0;
-    }
-
-    return &screen;
+	float xscale = gfx_screenwidth()/ui_screen()->w;
+	float yscale = gfx_screenheight()/ui_screen()->h;
+	gfx_clip_enable((int)(r->x*xscale), (int)(r->y*yscale), (int)(r->w*xscale), (int)(r->h*yscale));
 }
 
-void ui_scale(float s)
+void ui_clip_disable()
 {
-    scale = s;
+	gfx_clip_disable();
 }
 
-void ui_hsplit_t(const struct rect *original, int pixels, struct rect *top, struct rect *bottom)
+void ui_hsplit_t(const RECT *original, float cut, RECT *top, RECT *bottom)
 {
-    struct rect r = *original;
-    pixels *= scale;
+    RECT r = *original;
+    cut *= ui_scale();
 
     if (top)
     {
         top->x = r.x;
         top->y = r.y;
         top->w = r.w;
-        top->h = pixels;
+        top->h = cut;
     }
 
     if (bottom)
     {
         bottom->x = r.x;
-        bottom->y = r.y + pixels;
+        bottom->y = r.y + cut;
         bottom->w = r.w;
-        bottom->h = r.h - pixels;
-    }
-
-    if (config.debug)
-    {
-        if (top)
-            add_rect(top);
-        if (bottom)
-            add_rect(bottom);
+        bottom->h = r.h - cut;
     }
 }
 
-void ui_hsplit_b(const struct rect *original, int pixels, struct rect *top, struct rect *bottom)
+void ui_hsplit_b(const RECT *original, float cut, RECT *top, RECT *bottom)
 {
-    struct rect r = *original;
-    pixels *= scale;
+    RECT r = *original;
+    cut *= ui_scale();
 
     if (top)
     {
         top->x = r.x;
         top->y = r.y;
         top->w = r.w;
-        top->h = r.h - pixels;
+        top->h = r.h - cut;
     }
 
     if (bottom)
     {
         bottom->x = r.x;
-        bottom->y = r.y + r.h - pixels;
+        bottom->y = r.y + r.h - cut;
         bottom->w = r.w;
-        bottom->h = pixels;
-    }
-
-    if (config.debug)
-    {
-        if (top)
-            add_rect(top);
-        if (bottom)
-            add_rect(bottom);
+        bottom->h = cut;
     }
 }
 
-void ui_vsplit_l(const struct rect *original, int pixels, struct rect *left, struct rect *right)
+
+void ui_vsplit_mid(const RECT *original, RECT *left, RECT *right)
 {
-    struct rect r = *original;
-    pixels *= scale;
+    RECT r = *original;
+    float cut = r.w/2;
 
     if (left)
     {
         left->x = r.x;
         left->y = r.y;
-        left->w = pixels;
+        left->w = cut;
         left->h = r.h;
     }
 
     if (right)
     {
-        right->x = r.x + pixels;
+        right->x = r.x + cut;
         right->y = r.y;
-        right->w = r.w - pixels;
+        right->w = r.w - cut;
         right->h = r.h;
     }
+}
+
+void ui_vsplit_l(const RECT *original, float cut, RECT *left, RECT *right)
+{
+    RECT r = *original;
+    cut *= ui_scale();
+
+    if (left)
+    {
+        left->x = r.x;
+        left->y = r.y;
+        left->w = cut;
+        left->h = r.h;
+    }
 
-    if (config.debug)
+    if (right)
     {
-        if (left)
-            add_rect(left);
-        if (right)
-            add_rect(right);
+        right->x = r.x + cut;
+        right->y = r.y;
+        right->w = r.w - cut;
+        right->h = r.h;
     }
 }
 
-void ui_vsplit_r(const struct rect *original, int pixels, struct rect *left, struct rect *right)
+void ui_vsplit_r(const RECT *original, float cut, RECT *left, RECT *right)
 {
-    struct rect r = *original;
-    pixels *= scale;
+    RECT r = *original;
+    cut *= ui_scale();
 
     if (left)
     {
         left->x = r.x;
         left->y = r.y;
-        left->w = r.w - pixels;
+        left->w = r.w - cut;
         left->h = r.h;
     }
 
     if (right)
     {
-        right->x = r.x + r.w - pixels;
+        right->x = r.x + r.w - cut;
         right->y = r.y;
-        right->w = pixels;
+        right->w = cut;
         right->h = r.h;
     }
+}
 
-    if (config.debug)
-    {
-        if (left)
-            add_rect(left);
-        if (right)
-            add_rect(right);
-    }
+void ui_margin(const RECT *original, float cut, RECT *other_rect)
+{
+    RECT r = *original;
+	cut *= ui_scale();
+
+    other_rect->x = r.x + cut;
+    other_rect->y = r.y + cut;
+    other_rect->w = r.w - 2*cut;
+    other_rect->h = r.h - 2*cut;
+}
+
+void ui_vmargin(const RECT *original, float cut, RECT *other_rect)
+{
+    RECT r = *original;
+	cut *= ui_scale();
+
+    other_rect->x = r.x + cut;
+    other_rect->y = r.y;
+    other_rect->w = r.w - 2*cut;
+    other_rect->h = r.h;
 }
 
-void ui_margin(const struct rect *original, int pixels, struct rect *other_rect)
+void ui_hmargin(const RECT *original, float cut, RECT *other_rect)
 {
-    struct rect r = *original;
-    pixels *= scale;
+    RECT r = *original;
+	cut *= ui_scale();
 
-    other_rect->x = r.x + pixels;
-    other_rect->y = r.y + pixels;
-    other_rect->w = r.w - 2*pixels;
-    other_rect->h = r.h - 2*pixels;
+    other_rect->x = r.x;
+    other_rect->y = r.y + cut;
+    other_rect->w = r.w;
+    other_rect->h = r.h - 2*cut;
+}
+
+int ui_do_button(const void *id, const char *text, int checked, const RECT *r, ui_draw_button_func draw_func, const void *extra)
+{
+    /* logic */
+    int ret = 0;
+    int inside = ui_mouse_inside(r);
+
+	if(ui_active_item() == id)
+	{
+		if(!ui_mouse_button(0))
+		{
+			if(inside && checked >= 0)
+				ret = 1;
+			ui_set_active_item(0);
+		}
+	}
+	else if(ui_hot_item() == id)
+	{
+		if(ui_mouse_button(0))
+			ui_set_active_item(id);
+	}
+	
+	if(inside)
+		ui_set_hot_item(id);
+
+	if(draw_func)
+    	draw_func(id, text, checked, r, extra);
+    return ret;
+}
+
+void ui_do_label(const RECT *r, const char *text, float size, int align, int max_width)
+{
+    gfx_blend_normal();
+    size *= ui_scale();
+    if(align == 0)
+    {
+    	float tw = gfx_pretty_text_width(size, text, max_width);
+    	gfx_pretty_text(r->x + r->w/2-tw/2, r->y, size, text, max_width);
+	}
+	else if(align < 0)
+    	gfx_pretty_text(r->x, r->y, size, text, max_width);
+	else if(align > 0)
+	{
+    	float tw = gfx_pretty_text_width(size, text, max_width);
+    	gfx_pretty_text(r->x + r->w-tw, r->y, size, text, max_width);
+	}
 }
diff --git a/src/engine/client/ec_ui.h b/src/engine/client/ec_ui.h
index 15c63b90..83dbe37d 100644
--- a/src/engine/client/ec_ui.h
+++ b/src/engine/client/ec_ui.h
@@ -6,6 +6,28 @@
 extern "C" {
 #endif
 
+
+
+typedef struct 
+{
+    float x, y, w, h;
+} RECT;
+
+enum
+{
+	CORNER_TL=1,
+	CORNER_TR=2,
+	CORNER_BL=4,
+	CORNER_BR=8,
+	
+	CORNER_T=CORNER_TL|CORNER_TR,
+	CORNER_B=CORNER_BL|CORNER_BR,
+	CORNER_R=CORNER_TR|CORNER_BR,
+	CORNER_L=CORNER_TL|CORNER_BL,
+	
+	CORNER_ALL=CORNER_T|CORNER_B
+};
+
 int ui_update(float mx, float my, float mwx, float mwy, int buttons);
 
 float ui_mouse_x();
@@ -13,6 +35,7 @@ float ui_mouse_y();
 float ui_mouse_world_x();
 float ui_mouse_world_y();
 int ui_mouse_button(int index);
+int ui_mouse_button_clicked(int index);
 
 void ui_set_hot_item(const void *id);
 void ui_set_active_item(const void *id);
@@ -21,13 +44,28 @@ const void *ui_hot_item();
 const void *ui_active_item();
 const void *ui_last_active_item();
 
-int ui_mouse_inside(float x, float y, float w, float h);
+int ui_mouse_inside(const RECT *r);
+
+RECT *ui_screen();
+void ui_set_scale(float s);
+float ui_scale();
+void ui_clip_enable(const RECT *r);
+void ui_clip_disable();
+
+void ui_hsplit_t(const RECT *original, float cut, RECT *top, RECT *bottom);
+void ui_hsplit_b(const RECT *original, float cut, RECT *top, RECT *bottom);
+void ui_vsplit_mid(const RECT *original, RECT *left, RECT *right);
+void ui_vsplit_l(const RECT *original, float cut, RECT *left, RECT *right);
+void ui_vsplit_r(const RECT *original, float cut, RECT *left, RECT *right);
+
+void ui_margin(const RECT *original, float cut, RECT *other_rect);
+void ui_vmargin(const RECT *original, float cut, RECT *other_rect);
+void ui_hmargin(const RECT *original, float cut, RECT *other_rect);
 
-typedef void (*draw_button_callback)(const void *id, const char *text, int checked, float x, float y, float w, float h, void *extra);
+typedef void (*ui_draw_button_func)(const void *id, const char *text, int checked, const RECT *r, const void *extra);
+int ui_do_button(const void *id, const char *text, int checked, const RECT *r, ui_draw_button_func draw_func, const void *extra);
+void ui_do_label(const RECT *r, const char *text, float size, int align, int max_width);
 
-void ui_do_image(int texture, float x, float y, float w, float h);
-void ui_do_label(float x, float y, const char *text, float size);
-int ui_do_button(const void *id, const char *text, int checked, float x, float y, float w, float h, draw_button_callback draw_func, void *extra);
 
 #ifdef __cplusplus
 }
diff --git a/src/engine/e_datafile.c b/src/engine/e_datafile.c
index 14138ed9..f0c70f58 100644
--- a/src/engine/e_datafile.c
+++ b/src/engine/e_datafile.c
@@ -251,6 +251,23 @@ void *datafile_get_data(DATAFILE *df, int index)
 	return df->data_ptrs[index];
 }
 
+void *datafile_get_data_swapped(DATAFILE *df, int index)
+{
+	void *ptr = datafile_get_data(df, index);
+	int size = datafile_get_datasize(df, index);
+	(void)size; /* not used on LE machines */
+	
+	if(!ptr)
+		return ptr;
+
+#if defined(CONF_ARCH_ENDIAN_BIG)
+	swap_endian(ptr, sizeof(int), size);
+#endif
+
+	return ptr;
+}
+
+
 void datafile_unload_data(DATAFILE *df, int index)
 {
 	/* */
@@ -429,8 +446,8 @@ int datafile_add_item(DATAFILE_OUT *df, int type, int id, int size, void *data)
 int datafile_add_data(DATAFILE_OUT *df, int size, void *data)
 {
 	DATA_INFO *info = &df->datas[df->num_datas];
-	void *compdata = mem_alloc(size, 1); /* temporary buffer that we use duing compression */
-	unsigned long s = size;
+	unsigned long s = size*2;
+	void *compdata = mem_alloc(s, 1); /* temporary buffer that we use duing compression */
 
 	info->uncompressed_size = size;
 	if(compress((Bytef*)compdata, &s, (Bytef*)data, size) != Z_OK)
@@ -444,6 +461,22 @@ int datafile_add_data(DATAFILE_OUT *df, int size, void *data)
 	return df->num_datas-1;
 }
 
+int datafile_add_data_swapped(DATAFILE_OUT *df, int size, void *data)
+{
+#if defined(CONF_ARCH_ENDIAN_BIG)
+	void *swapped = mem_alloc(size, 1); /* temporary buffer that we use duing compression */
+	int index;
+	mem_copy(swapped, data, size);
+	swap_endian(&swapped, sizeof(int), size/sizeof(int));
+	index = datafile_add_data(df, size, swapped);
+	mem_free(swapped);
+	return index;
+#else
+	return datafile_add_data(df, size, data);
+#endif
+}
+
+
 int datafile_finish(DATAFILE_OUT *df)
 {
 	int itemsize = 0;
diff --git a/src/engine/e_datafile.h b/src/engine/e_datafile.h
index 7cc7b209..203f415c 100644
--- a/src/engine/e_datafile.h
+++ b/src/engine/e_datafile.h
@@ -7,6 +7,7 @@ typedef struct DATAFILE_t DATAFILE;
 DATAFILE *datafile_load(const char *filename);
 DATAFILE *datafile_load_old(const char *filename);
 void *datafile_get_data(DATAFILE *df, int index);
+void *datafile_get_data_swapped(DATAFILE *df, int index); /* makes sure that the data is 32bit LE ints when saved */
 int datafile_get_datasize(DATAFILE *df, int index);
 void datafile_unload_data(DATAFILE *df, int index);
 void *datafile_get_item(DATAFILE *df, int index, int *type, int *id);
@@ -23,5 +24,6 @@ int datafile_crc(const char *filename);
 typedef struct DATAFILE_OUT_t DATAFILE_OUT;
 DATAFILE_OUT *datafile_create(const char *filename);
 int datafile_add_data(DATAFILE_OUT *df, int size, void *data);
+int datafile_add_data_swapped(DATAFILE_OUT *df, int size, void *data);
 int datafile_add_item(DATAFILE_OUT *df, int type, int id, int size, void *data);
 int datafile_finish(DATAFILE_OUT *df);
diff --git a/src/engine/e_engine.c b/src/engine/e_engine.c
index b1cf345f..3a3f6333 100644
--- a/src/engine/e_engine.c
+++ b/src/engine/e_engine.c
@@ -16,6 +16,15 @@ const char *engine_savepath(const char *filename, char *buffer, int max)
 
 void engine_init(const char *appname, int argc, char **argv)
 {
+	dbg_msg("engine", "running on %s-%s-%s", CONF_FAMILY_STRING, CONF_PLATFORM_STRING, CONF_ARCH_STRING);
+#ifdef CONF_ARCH_ENDIAN_LITTLE
+	dbg_msg("engine", "arch is little endian");
+#elif defined(CONF_ARCH_ENDIAN_BIG)
+	dbg_msg("engine", "arch is big endian");
+#else
+	dbg_msg("engine", "unknown endian");
+#endif
+
 	/* init the network */
 	net_init();
 
diff --git a/src/engine/e_interface.h b/src/engine/e_interface.h
index 666d15e5..612b2432 100644
--- a/src/engine/e_interface.h
+++ b/src/engine/e_interface.h
@@ -110,6 +110,7 @@ void perf_start(PERFORMACE_INFO *info);
 void perf_end();
 void perf_dump();
 
+/*
 struct rect
 {
     float x, y, w, h;
@@ -123,7 +124,7 @@ void ui_hsplit_t(const struct rect *original, int pixels, struct rect *top, stru
 void ui_hsplit_b(const struct rect *original, int pixels, struct rect *top, struct rect *bottom);
 void ui_vsplit_l(const struct rect *original, int pixels, struct rect *left, struct rect *right);
 void ui_vsplit_r(const struct rect *original, int pixels, struct rect *left, struct rect *right);
-void ui_margin(const struct rect *original, int pixels, struct rect *new_rect);
+void ui_margin(const struct rect *original, int pixels, struct rect *new_rect);*/
 
 /* image loaders */
 int gfx_load_png(IMAGE_INFO *img, const char *filename);
@@ -894,6 +895,12 @@ void gfx_clear_mask(int fill);
 void gfx_clip_enable(int x, int y, int w, int h);
 void gfx_clip_disable();
 
+void gfx_quads_setsubset_free(
+	float x0, float y0,
+	float x1, float y1,
+	float x2, float y2,
+	float x3, float y3);
+
 /* server snap id */
 int snap_new_id();
 void snap_free_id(int id);
diff --git a/src/engine/e_system.c b/src/engine/e_system.c
index 90aeccc5..bf445c9c 100644
--- a/src/engine/e_system.c
+++ b/src/engine/e_system.c
@@ -106,7 +106,7 @@ struct memtail
 	int guard;
 };
 
-struct memheader *first = 0;
+static struct memheader *first = 0;
 
 int mem_allocated()
 {
diff --git a/src/game/client/gc_client.cpp b/src/game/client/gc_client.cpp
index 68c6db00..9c70bd86 100644
--- a/src/game/client/gc_client.cpp
+++ b/src/game/client/gc_client.cpp
@@ -47,10 +47,6 @@ enum
 	CHATMODE_REMOTECONSOLE,
 };
 
-typedef struct 
-{
-    float x, y, w, h;
-} RECT;
 RECT *ui2_screen();
 
 static int chat_mode = CHATMODE_NONE;
@@ -1885,7 +1881,7 @@ int emoticon_selector_render()
 		emoticon_selector_active = false;
 	}
 
-    RECT screen = *ui2_screen();
+    RECT screen = *ui_screen();
 
 	gfx_mapscreen(screen.x, screen.y, screen.w, screen.h);
 
diff --git a/src/game/client/gc_mapres_tilemap.cpp b/src/game/client/gc_mapres_tilemap.cpp
index 6e687495..d94add17 100644
--- a/src/game/client/gc_mapres_tilemap.cpp
+++ b/src/game/client/gc_mapres_tilemap.cpp
@@ -9,6 +9,13 @@ int tilemap_init()
 {
 	return 0;
 }
+/*
+struct render_info
+{
+	unsigned char *data;
+};
+
+void tilemap_render_one()*/
 
 void tilemap_render(float scale, int fg)
 {
diff --git a/src/game/client/gc_menu.cpp b/src/game/client/gc_menu.cpp
index 17716bd6..5062b15b 100644
--- a/src/game/client/gc_menu.cpp
+++ b/src/game/client/gc_menu.cpp
@@ -70,6 +70,7 @@ static vec4 color_tabbar_active = color_tabbar_active_outgame;
 
 enum
 {
+	/*
 	CORNER_TL=1,
 	CORNER_TR=2,
 	CORNER_BL=4,
@@ -81,6 +82,7 @@ enum
 	CORNER_L=CORNER_TL|CORNER_BL,
 	
 	CORNER_ALL=CORNER_T|CORNER_B,
+	*/
 	
 	PAGE_NEWS=0,
 	PAGE_INTERNET,
@@ -90,11 +92,11 @@ enum
 	//PAGE_GAME, // not a real page
 	PAGE_SYSTEM,
 };
-
+/*
 typedef struct 
 {
     float x, y, w, h;
-} RECT;
+} RECT;*/
 
 static RECT screen = { 0.0f, 0.0f, 848.0f, 480.0f };
 
@@ -286,7 +288,7 @@ int ui2_do_button(const void *id, const char *text, int checked, const RECT *r,
 {
     /* logic */
     int ret = 0;
-    int inside = ui_mouse_inside(r->x,r->y,r->w,r->h);
+    int inside = ui_mouse_inside(r);
 
 	if(ui_active_item() == id)
 	{
@@ -461,7 +463,7 @@ static void ui2_draw_checkbox_number(const void *id, const char *text, int check
 
 int ui2_do_edit_box(void *id, const RECT *rect, char *str, int str_size, bool hidden=false)
 {
-    int inside = ui_mouse_inside(rect->x,rect->y,rect->w,rect->h);
+    int inside = ui_mouse_inside(rect);
 	int r = 0;
 	static int at_index = 0;
 
@@ -592,7 +594,7 @@ float ui2_do_scrollbar_v(const void *id, const RECT *rect, float current)
 
 	/* logic */
     float ret = current;
-    int inside = ui_mouse_inside(handle.x,handle.y,handle.w,handle.h);
+    int inside = ui_mouse_inside(&handle);
 
 	if(ui_active_item() == id)
 	{
@@ -648,7 +650,7 @@ float ui2_do_scrollbar_h(const void *id, const RECT *rect, float current)
 
 	/* logic */
     float ret = current;
-    int inside = ui_mouse_inside(handle.x,handle.y,handle.w,handle.h);
+    int inside = ui_mouse_inside(&handle);
 
 	if(ui_active_item() == id)
 	{
@@ -696,7 +698,7 @@ int ui2_do_key_reader(void *id, const RECT *rect, int key)
 {
 	// process
 	static bool mouse_released = true;
-	int inside = ui_mouse_inside(rect->x, rect->y, rect->w, rect->h);
+	int inside = ui_mouse_inside(rect);
 	int new_key = key;
 	
 	if(!ui_mouse_button(0))
@@ -895,7 +897,10 @@ void render_loading(float percent)
 	const char *caption = "Loading";
 
 	tw = gfx_pretty_text_width(48.0f, caption, -1);
-	ui_do_label(x+w/2-tw/2, y+20, caption, 48.0f);
+	RECT r;
+	r.x = x+w/2;
+	r.y = y+20;
+	ui_do_label(&r, caption, 48.0f, 0, -1);
 
 	gfx_texture_set(-1);
 	gfx_quads_begin();
diff --git a/src/game/server/gs_game.cpp b/src/game/server/gs_game.cpp
index badabee6..2b3af530 100644
--- a/src/game/server/gs_game.cpp
+++ b/src/game/server/gs_game.cpp
@@ -49,10 +49,7 @@ void gameobject::resetgame()
 	world->reset_requested = true;
 }
 
-static bool is_separator(char c)
-{
-	return c == ';' || c == ' ' || c == ',' || c == '\t';
-}
+static bool is_separator(char c) { return c == ';' || c == ' ' || c == ',' || c == '\t'; }
 
 void gameobject::startround()
 {