Index: elec/CrO2/software/Makefile
===================================================================
--- elec/CrO2/software/Makefile	(revision 32c7682511a44bdb2ad4bdfc6948421a4892c4d5)
+++ elec/CrO2/software/Makefile	(revision c7b42180dbb79565876daf11c904fa544c18956c)
@@ -14,5 +14,5 @@
 
 CC		= g++
-CPPFLAGS	= $(USBFLAGS) -O -Wall -I/usr/include/IUP/
+CPPFLAGS	= $(USBFLAGS) -O -Wall -I/usr/include/IUP/ -g
 LIBS	= $(USBLIBS) -L/usr/lib/ -mwindows -liup -lcomctl32 -lole32
 
@@ -23,5 +23,5 @@
 all: $(PROGRAM)
 
-$(PROGRAM): $(PROJECT).o device.o poller.o
+$(PROGRAM): $(PROJECT).o device.o gui.o k5.o
 	$(CC) -o $(PROGRAM) $^ $(LIBS)
 
Index: elec/CrO2/software/cro2.cpp
===================================================================
--- elec/CrO2/software/cro2.cpp	(revision 32c7682511a44bdb2ad4bdfc6948421a4892c4d5)
+++ elec/CrO2/software/cro2.cpp	(revision c7b42180dbb79565876daf11c904fa544c18956c)
@@ -12,115 +12,15 @@
 #include <unistd.h>
 
-#include <iup.h>
-#include <iupcontrols.h>
 
 #include "device.h"
-#include "poller.h"
-
-/* UI */
-int menu_open(Ihandle* that)
-{
-	IupPopup(IupFileDlg(), IUP_CENTER, IUP_CENTER);
-	return IUP_DEFAULT;
-}
-
-int menu_exit(Ihandle* that)
-{
-	return IUP_CLOSE;
-}
-
-
-Ihandle* motoron;
-void GUI_open(int* argc, char*** argv)
-{
-	IupOpen(argc, argv);
-//	IupControlsOpen();
-
-	IupSetFunction("OPEN", menu_open);
-	IupSetFunction("EXIT", menu_exit);
-
-	Ihandle* menu = IupMenu(
-		IupSubmenu("File",
-			IupMenu(
-				IupItem("Open", "OPEN"),		
-				IupItem("Exit", "EXIT"),
-				NULL
-			)
-		),
-		NULL
-	);
-
-	// CONTROL
-	motoron = IupProgressBar();
-	IupSetAttribute(motoron, "RASTERSIZE", "16x16");
-
-	// EXPLORE
-	Ihandle* platformlist = IupList(NULL);
-	IupSetAttribute(platformlist, "EXPAND", "HORIZONTAL");
-	IupSetAttribute(platformlist, "DROPDOWN", "YES");
-	IupSetAttribute(platformlist, "1", "MO5");
-	IupSetAttribute(platformlist, "VALUE", "1");
-
-	Ihandle* blocklist = IupTree();
-	IupSetAttribute(blocklist, "EXPAND", "VERTICAL");
-
-	Ihandle* tabs = IupTabs(
-		IupVbox(
-			IupHbox(
-				IupLabel("Motor"),
-				motoron,
-				NULL
-			),
-			IupHbox(
-				IupToggle("play",NULL),
-				IupToggle("REC",NULL),
-				NULL
-			),
-			NULL
-		),
-		IupVbox(
-			IupHbox(
-				IupLabel("Format:"),
-				platformlist,
-				NULL
-			),
-			IupHbox(
-				blocklist,
-				IupVbox(
-//					IupMatrix(NULL),
-					IupLabel("Checksum:"),
-					NULL
-				),
-				NULL
-			)
-		),
-		NULL
-	);
-
-	IupSetAttribute(tabs,"TABTITLE0", "Control");
-	IupSetAttribute(tabs,"TABTITLE1", "Explore");
-
-	Ihandle* dialog = IupDialog(tabs);
-	IupSetAttribute(dialog, "TITLE", "CrO2 tape emulator");
-	IupSetAttributeHandle(dialog, "MENU", menu);
-	IupShow(dialog);
-
-	// Run the timer
-	startPolling();
-
-	IupMainLoop();
-
-	IupClose();
-}
-
+#include "k5.h"
+#include "gui.h"
 
 int main(int argc, char **argv)
 {
-	//usb_dev_handle      *handle = NULL;
-	unsigned char       buffer[275];
 	int                 nBytes = 0;
 
     if(argc < 2){
-		GUI_open(&argc, &argv);
+		Gui gui(&argc, &argv);
 		exit(0);
     }
@@ -130,48 +30,25 @@
 
 		if(strcmp(argv[1], "get") == 0){
+			unsigned char       buffer[275];
 			memset(buffer, 0, 275);
 			nBytes = dev.read(buffer, sizeof(buffer));
 		}else if(strcmp(argv[1], "put") == 0){
 
-			// wait for motor on
-			while (dev.getStatus() & 8)
-				usleep(1000000);
+			// load file
+			K5 file(argv[2]);
 
-			// load file
-			FILE* fptr = fopen(argv[2], "rb");
-			int blockid;
-			uint8_t blktype, blksize;
-			sscanf(argv[3], "%d", &blockid);
+			for (int k = 0; k < file.getBlockCount(); k++)
+			{
+				// wait for motor on
+				while (dev.getStatus() & 8)
+					Sleep(1000);
 
-			// fast-forward to requested block
-			do
-			{
-				do
-				{
-					fread(&blktype, 1, 1, fptr);
-					if (feof(fptr))
-					{
-						fprintf(stderr, "end of file.\n");
-						fclose(fptr);
-						exit(0);
-					}
-				}
-				while(blktype != 0x5A); // skip sync header
+				K5::Block block = file.getBlock(k);
 
-				fread(&blktype, 1, 1, fptr);
-				fread(&blksize, 1, 1, fptr);
-				blksize -= 2;
-				fread(buffer, 1, blksize + 1, fptr);
-				if (blktype == 0)
-				{
-					// new file
-					printf("%.11s\n",buffer);
-				}
+				nBytes = dev.write(block.data, block.length - 1, block.type);
+
+				// TODO wait for correct time (read status from usb OR compute from size+type)
+				Sleep(1400);
 			}
-			while (blockid --);
-
-			fclose(fptr);
-
-			nBytes = dev.write(buffer, blksize, blktype);
 		}else{
 			// TODO print usage
@@ -186,4 +63,6 @@
 		std::cerr << error << std::endl;
 	}
+
+	exit(0);
 }
 
Index: elec/CrO2/software/gui.cpp
===================================================================
--- elec/CrO2/software/gui.cpp	(revision c7b42180dbb79565876daf11c904fa544c18956c)
+++ elec/CrO2/software/gui.cpp	(revision c7b42180dbb79565876daf11c904fa544c18956c)
@@ -0,0 +1,200 @@
+/* CrO2 datassette emulator
+ * Copyright 2012, Adrien Destugues <pulkomandy@pulkomandy.ath.cx>
+ *
+ * Distributed under the terms of the MIT licence.
+ */
+
+#include "gui.h"
+
+#include "device.h"
+#include "k5.h"
+
+#include <stdint.h>
+#include <iup.h>
+#include <iupcontrols.h>
+
+///////////////////////////////////////////////////////////////////////////////
+// Super awesome extended powerful ultimate deluxe C++ IUP callback system ;)
+
+typedef int (Gui::*Callee)();
+
+class Callback
+{
+	public:
+		static int call(Ihandle* that);
+		static int destroy(Ihandle* that);
+		static void create(Ihandle* handle, const char* name, Gui* self, Callee what);
+
+	private:
+		Callback(Gui* self, Callee what);
+		Callback(); // do not use
+
+		Gui* self;
+		Callee what;
+};
+
+void Callback::create(Ihandle* handle,const char* name, Gui* self, Callee what)
+{
+	Callback* cb = new Callback(self, what);
+	IupSetAttribute(handle, "LCALLBACK", (char*)cb);
+	IupSetCallback(handle, name, Callback::call);
+	IupSetCallback(handle, "LDESTROY_CB", Callback::destroy);
+}
+
+Callback::Callback(Gui* self, Callee what)
+{
+	this->self = self;
+	this->what = what;
+}
+
+int Callback::call(Ihandle* that)
+{
+	Callback* call = (Callback*)IupGetAttribute(that, "LCALLBACK");
+	return ((call->self)->*(call->what))();
+}
+
+int Callback::destroy(Ihandle* that)
+{
+	Callback* call = (Callback*)IupGetAttribute(that, "LCALLBACK");
+	delete call;
+	return IUP_DEFAULT;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+	// Start status poller "thread"
+	int pollStatus(Ihandle* ih)
+	{
+		Ihandle* motoron = (Ihandle*)IupGetAttribute(ih, "target");
+
+		uint8_t status = Device::getDevice().getStatus();
+		if (status & 8)
+			IupSetAttribute(motoron, "VALUE", "0"); // motor OFF
+		else
+			IupSetAttribute(motoron, "VALUE", "1"); // motor ON
+		return IUP_DEFAULT;
+	}
+
+void startPolling(Ihandle* target) {
+	Ihandle* timer = IupTimer();
+
+	IupSetAttribute(timer, "target", (const char*)target);
+
+	IupSetAttribute(timer, "TIME", "300");
+	IupSetCallback(timer, "ACTION_CB", pollStatus);
+	IupSetAttribute(timer, "RUN", "YES");
+}
+
+/* UI */
+
+int menu_exit(Ihandle* that)
+{
+	return IUP_CLOSE;
+}
+
+
+
+Gui::Gui(int* argc, char*** argv)
+{
+	file = NULL;
+
+	IupOpen(argc, argv);
+//	IupControlsOpen();
+
+	IupSetFunction("EXIT", menu_exit);
+
+	Ihandle* menu_open = IupItem("Open", NULL);
+	Callback::create(menu_open, "ACTION", this, &Gui::menu_open);
+
+	Ihandle* menu = IupMenu(
+		IupSubmenu("File",
+			IupMenu(
+				menu_open,		
+				IupItem("Exit", "EXIT"),
+				NULL
+			)
+		),
+		NULL
+	);
+
+
+	// CONTROL
+	Ihandle* motoron = IupProgressBar();
+	IupSetAttribute(motoron, "RASTERSIZE", "16x16");
+
+	// EXPLORE
+	Ihandle* platformlist = IupList(NULL);
+	IupSetAttribute(platformlist, "EXPAND", "HORIZONTAL");
+	IupSetAttribute(platformlist, "DROPDOWN", "YES");
+	IupSetAttribute(platformlist, "1", "MO5");
+	IupSetAttribute(platformlist, "VALUE", "1");
+
+	Ihandle* blocklist = IupTree();
+	IupSetAttribute(blocklist, "EXPAND", "VERTICAL");
+
+	Ihandle* tabs = IupTabs(
+		IupVbox(
+			IupHbox(
+				IupLabel("Motor"),
+				motoron,
+				NULL
+			),
+			IupHbox(
+				IupToggle("play",NULL),
+				IupToggle("REC",NULL),
+				NULL
+			),
+			NULL
+		),
+		IupVbox(
+			IupHbox(
+				IupLabel("Format:"),
+				platformlist,
+				NULL
+			),
+			IupHbox(
+				blocklist,
+				IupVbox(
+//					IupMatrix(NULL),
+					IupLabel("Checksum:"),
+					NULL
+				),
+				NULL
+			)
+		),
+		NULL
+	);
+
+	IupSetAttribute(tabs,"TABTITLE0", "Control");
+	IupSetAttribute(tabs,"TABTITLE1", "Explore");
+
+	Ihandle* dialog = IupDialog(tabs);
+	IupSetAttribute(dialog, "TITLE", "CrO2 tape emulator");
+	IupSetAttributeHandle(dialog, "MENU", menu);
+	IupShow(dialog);
+
+	// Run the timer
+	startPolling(motoron);
+
+	IupMainLoop();
+
+	IupClose();
+}
+
+
+Gui::~Gui()
+{
+	delete file;
+}
+
+int Gui::menu_open()
+{
+	char name[65536];
+	name[0] = 0;
+	if (IupGetFile(name) == 0)
+	{
+		// Load file
+		file = new K5(name);
+	}
+	return IUP_DEFAULT;
+}
Index: elec/CrO2/software/gui.h
===================================================================
--- elec/CrO2/software/gui.h	(revision c7b42180dbb79565876daf11c904fa544c18956c)
+++ elec/CrO2/software/gui.h	(revision c7b42180dbb79565876daf11c904fa544c18956c)
@@ -0,0 +1,20 @@
+/* CrO2 datassette emulator
+ * Copyright 2012, Adrien Destugues <pulkomandy@pulkomandy.ath.cx>
+ *
+ * Distributed under the terms of the MIT licence.
+ */
+
+#include <iup.h>
+
+class K5;
+
+class Gui {
+	public:
+		Gui(int* argc, char*** argv);
+		~Gui();
+
+	private:
+		int menu_open();
+
+		K5* file;
+};
Index: elec/CrO2/software/k5.cpp
===================================================================
--- elec/CrO2/software/k5.cpp	(revision c7b42180dbb79565876daf11c904fa544c18956c)
+++ elec/CrO2/software/k5.cpp	(revision c7b42180dbb79565876daf11c904fa544c18956c)
@@ -0,0 +1,80 @@
+/* CrO2 datassette emulator
+ * Copyright 2012, Adrien Destugues <pulkomandy@pulkomandy.ath.cx>
+ *
+ * Distributed under the terms of the MIT licence.
+ */
+
+#include "k5.h"
+
+#include <fstream>
+#include <string.h>
+
+K5::Block::Block(int length,uint8_t type)
+{
+	this->length = length;
+	data = new uint8_t[length];
+	this->type = type;
+}
+
+K5::Block::Block(const Block& other)
+{
+	length = other.length;
+	type = other.type;
+	data = new uint8_t[length];
+	memcpy(data, other.data, length);
+}
+
+K5::Block::~Block()
+{
+	delete[] data;
+}
+
+
+K5::K5(const char* name)
+{
+	std::ifstream stream(name, std::ifstream::in | std::ifstream::binary);
+
+	if (!stream.is_open()) throw "Could not open file.";
+	if (!stream.good()) throw "File error.";
+
+	uint8_t byte;
+
+	do {
+		do {
+			byte = stream.get();
+			if (stream.eof())
+				return;
+		} while (byte != 0x5A); // Skip pilot tone and sync bytes
+		// TODO make sure this is a standard block ?
+
+		uint8_t blktype = stream.get();
+		int blksize = stream.get(); // includes checksum
+		if (blksize == 0) blksize = 256;
+		blksize --;
+		Block block(blksize, blktype);
+		stream.read((char*)block.data, block.length);
+
+		blocks.push_back(block);
+/*
+		if (block.type == 0)
+		{
+			printf("FILE: %.11s\n",block.data);
+		}
+		
+		printf("blk type %d size %d (%d)\n", block.type, block.length, (int)stream.tellg());
+*/
+	} while (stream.good());
+
+
+	stream.close();
+}
+
+int K5::getBlockCount()
+{
+	return blocks.size();
+}
+
+K5::Block K5::getBlock(int id)
+{
+	return blocks[id];
+}
Index: elec/CrO2/software/k5.h
===================================================================
--- elec/CrO2/software/k5.h	(revision c7b42180dbb79565876daf11c904fa544c18956c)
+++ elec/CrO2/software/k5.h	(revision c7b42180dbb79565876daf11c904fa544c18956c)
@@ -0,0 +1,30 @@
+/* CrO2 datassette emulator
+ * Copyright 2012, Adrien Destugues <pulkomandy@pulkomandy.ath.cx>
+ *
+ * Distributed under the terms of the MIT licence.
+ */
+
+#include <stdint.h>
+#include <vector>
+
+class K5 {
+	public:
+		K5(const char* filename);
+
+		class Block {
+			public:
+				Block(int length, uint8_t type);
+				Block(const Block& other);
+				~Block();
+
+				int length;
+				uint8_t* data;
+				uint8_t type;
+		};
+
+		int getBlockCount();
+		Block getBlock(int number);
+
+	private:
+		std::vector<Block> blocks;
+};
Index: ec/CrO2/software/poller.cpp
===================================================================
--- elec/CrO2/software/poller.cpp	(revision 32c7682511a44bdb2ad4bdfc6948421a4892c4d5)
+++ 	(revision )
@@ -1,24 +1,0 @@
-#include "poller.h"
-
-#include "device.h"
-
-#include <stdint.h>
-
-
-	// Start status poller "thread"
-	int pollStatus(Ihandle* ih)
-	{
-		uint8_t status = Device::getDevice().getStatus();
-		if (status & 8)
-			IupSetAttribute(motoron, "VALUE", "0"); // motor OFF
-		else
-			IupSetAttribute(motoron, "VALUE", "1"); // motor ON
-		return IUP_DEFAULT;
-	}
-
-void startPolling() {
-	Ihandle* timer = IupTimer();
-	IupSetAttribute(timer, "TIME", "300");
-	IupSetCallback(timer, "ACTION_CB", pollStatus);
-	IupSetAttribute(timer, "RUN", "YES");
-}
Index: ec/CrO2/software/poller.h
===================================================================
--- elec/CrO2/software/poller.h	(revision 32c7682511a44bdb2ad4bdfc6948421a4892c4d5)
+++ 	(revision )
@@ -1,5 +1,0 @@
-#include <iup.h>
-
-void startPolling();
-
-extern Ihandle* motoron;
