#define DLMI_MAGIC 0x494D4C44
#define DLMI_VERSION 0x00010000
#define DLMI_MAJOR(x) ((x>>16)&0xffff)
#define DLMI_MINOR(x) (x&0xffff)
#define DLMI_MAJOR_VERSION DLMI_MAJOR(DLMI_VERSION)
#define DLMI_MINOR_VERSION DLMI_MINOR(DLMI_VERSION)

typedef struct DLMI_s {
	u32 Magic;
	u32 Version;
	char Name[24];

	char DeviceID[8][4];

	u32 Features;
	u32 RamBase;
	u32 RamSize;
	u32 FlashBase;
	u32 FlashSize;
	u32 SRAMBase;
	u32 SRAMSize;
	u32 Reserved;

	u32 (*UnlockMedia)(void);
	u32 (*UnlockRAMRW)(void);
	u32 (*UnlockRAMRO)(void);

	u32 (*FlashWrite)(u32 Offset, u16* Source, u32 Size);
	u32 (*FlashRead)(u32 Offset, u16* Dest, u32 Size);

	u32 (*UnlockSRAM)(u16 Page);

	u32 (*Rumble)(u32 shake);
	u32 (*Motion)(void);	// Not ready yet.

	u32 (*UnlockBoot)(void);
	void (*SerialWrite)(u8 Data);
	u8 (*SerialRead)(void);
} DLMI_t;

static inline DLMI_t *DLMI_Load(char *filename) {
	FILE *fd;
	struct stat st;
	DLMI_t *driver;

	fd = fopen(filename, "r");
	if(!fd) return NULL;

	if(fstat(fd, &st)) {
		fclose(fd);
		return NULL;
	}

	driver = (DLMI_t *) malloc(st.st_size);
	if(!driver) {
		fclose(fd);
		return NULL;
	}

	fread(driver, st.st_size, 1, fd);
	fclose(fd);

	if(driver->Magic != DLMI_MAGIC) {
		free(driver);
		return NULL;
	}

#if 0
	if(DLMI_MAJOR(driver->Version) < DLMI_MAJOR_VERSION) {
		free(driver);
		return NULL;
	}
#endif

	driver->UnlockMedia	+= (u32)driver;
	driver->UnlockRAMRW	+= (u32)driver;
	driver->UnlockRAMRO	+= (u32)driver;

	driver->FlashWrite	+= (u32)driver;
	driver->FlashRead	+= (u32)driver;

	driver->UnlockSRAM	+= (u32)driver;

	driver->UnlockBoot	+= (u32)driver;
	driver->SerialWrite	+= (u32)driver;
	driver->SerialRead	+= (u32)driver;

	return driver;
}

