- fixed a gross bug in SetCurrentDirectoryA (where changing to a new
  non-existent directory would leave the new drive enabled upon aborting
  (fixes CuteFTP install and should fix MANY apps)
  This has probably been caused by myself. *brownpaperbag*
- added GetVolumeInformation serial number bug-for-bug compatibility
- corrected Unicode labels (better CD-ROM handling in general)
- moved CD-ROM label detection code to misc/cdrom.c
- possible to use loopback mounted ISO9660 files
- init some uninitialised drive fields
- remove some unnecessary header includes

diff --git a/files/drive.c b/files/drive.c
index e8ab1a0..8f5f010 100644
--- a/files/drive.c
+++ b/files/drive.c
@@ -239,6 +239,7 @@
         strcpy( DOSDrives[2].label_conf, "Drive C    " );
         DOSDrives[2].serial_conf   = 12345678;
         DOSDrives[2].type     = TYPE_HD;
+        DOSDrives[2].device   = NULL;
         DOSDrives[2].flags    = 0;
         DRIVE_CurDrive = 2;
     }
@@ -456,9 +457,7 @@
 	    offs = 0;
 	    break;
 	case TYPE_CDROM:
-	/* FIXME: Maybe we should search for the first data track on the CD,
-		  not just assume that it is the first track */
-	    offs = (off_t)2048*(16+0);
+	    offs = CDROM_Data_FindBestVoldesc(fd);
 	    break;
 		default:
 		    offs = 0;
@@ -473,7 +472,7 @@
 	case TYPE_FLOPPY:
 	case TYPE_HD:
 	    if ((buff[0x26]!=0x29) ||  /* Check for FAT present */
-                /* FIXME: do really all Fat have their name beginning with
+                /* FIXME: do really all FAT have their name beginning with
                    "FAT" ? (At least FAT12, FAT16 and FAT32 have :) */
 	    	memcmp( buff+0x36,"FAT",3))
             {
@@ -544,41 +543,20 @@
     if (!DRIVE_IsValid( drive )) return NULL;
     if (DRIVE_GetType(drive) == TYPE_CDROM)
     {
-	WINE_CDAUDIO wcda;
-
-	if (!(CDROM_Open(&wcda, drive)))
-	{
-	    int media = CDROM_GetMediaType(&wcda);
-
-	    if (media == CDS_AUDIO)
-	    {
-		strcpy(DOSDrives[drive].label_read, "Audio CD   ");
-		read = 1;
-	    }
-	    else
-	    if (media == CDS_NO_INFO)
-	    {
-		strcpy(DOSDrives[drive].label_read, "           ");
-		read = 1;
-	    }
-
-	    CDROM_Close(&wcda);
-}
+	read = CDROM_GetLabel(drive, DOSDrives[drive].label_read); 
     }
-    if ((!read) && (DOSDrives[drive].flags & DRIVE_READ_VOL_INFO))
+    else
+    if (DOSDrives[drive].flags & DRIVE_READ_VOL_INFO)
     {
 	if (DRIVE_ReadSuperblock(drive,(char *) buff))
 	    ERR("Invalid or unreadable superblock on %s (%c:).\n",
 		DOSDrives[drive].device, (char)(drive+'A'));
 	else {
-	    if (DOSDrives[drive].type == TYPE_CDROM)
-		offs = 40;
-	    else
 	    if (DOSDrives[drive].type == TYPE_FLOPPY ||
 		DOSDrives[drive].type == TYPE_HD)
 		offs = 0x2b;
 
-	    /* FIXME: ISO9660 uses 32-bytes long label. Should we do also? */
+	    /* FIXME: ISO9660 uses a 32 bytes long label. Should we do also? */
 	    if (offs != -1) memcpy(DOSDrives[drive].label_read,buff+offs,11);
 	    DOSDrives[drive].label_read[11]='\0';
 	    read = 1;
@@ -683,7 +661,7 @@
 
     strcpy( buffer, "A:" );
     buffer[0] += drive;
-    TRACE("(%c:,%s)\n", buffer[0], path );
+    TRACE("(%s,%s)\n", buffer, path );
     lstrcpynA( buffer + 2, path, sizeof(buffer) - 2 );
 
     if (!DOSFS_GetFullName( buffer, TRUE, &full_name )) return 0;
@@ -769,8 +747,8 @@
 
     if ( new->root )
     {
-        TRACE("Can\'t map drive %c to drive %c - drive %c already exists\n",
-              'A' + existing_drive, 'A' + new_drive, 'A' + new_drive );
+        TRACE("Can't map drive %c: to already existing drive %c:\n",
+              'A' + existing_drive, 'A' + new_drive );
 	/* it is already mapped there, so return success */
 	if (!strcmp(old->root,new->root))
 	    return 1;
@@ -780,14 +758,16 @@
     new->root = HEAP_strdupA( GetProcessHeap(), 0, old->root );
     new->dos_cwd = HEAP_strdupA( GetProcessHeap(), 0, old->dos_cwd );
     new->unix_cwd = HEAP_strdupA( GetProcessHeap(), 0, old->unix_cwd );
+    new->device = HEAP_strdupA( GetProcessHeap(), 0, old->device );
     memcpy ( new->label_conf, old->label_conf, 12 );
+    memcpy ( new->label_read, old->label_read, 12 );
     new->serial_conf = old->serial_conf;
     new->type = old->type;
     new->flags = old->flags;
     new->dev = old->dev;
     new->ino = old->ino;
 
-    TRACE("Drive %c is now equal to drive %c\n",
+    TRACE("Drive %c: is now equal to drive %c:\n",
           'A' + new_drive, 'A' + existing_drive );
 
     return 1;
@@ -825,15 +805,15 @@
     else
     {
         memset(dataptr, 0, nr_sect * 512);
-		if (fake_success)
+	if (fake_success)
         {
-			if (begin == 0 && nr_sect > 1) *(dataptr + 512) = 0xf8;
-			if (begin == 1) *dataptr = 0xf8;
-		}
-		else
-		return 0;
+	    if (begin == 0 && nr_sect > 1) *(dataptr + 512) = 0xf8;
+	    if (begin == 1) *dataptr = 0xf8;
+	}
+	else
+	    return 0;
     }
-	return 1;
+    return 1;
 }
 
 
@@ -844,7 +824,7 @@
  */
 int DRIVE_RawWrite(BYTE drive, DWORD begin, DWORD nr_sect, BYTE *dataptr, BOOL fake_success)
 {
-	int fd;
+    int fd;
 
     if ((fd = DRIVE_OpenDevice( drive, O_RDONLY )) != -1)
     {
@@ -854,10 +834,10 @@
         close( fd );
     }
     else
-	if (!(fake_success))
-		return 0;
+    if (!(fake_success))
+	return 0;
 
-	return 1;
+    return 1;
 }
 
 
@@ -1093,13 +1073,13 @@
     if (total)
     {
         total->s.HighPart = size.s.HighPart;
-        total->s.LowPart = size.s.LowPart ;
+        total->s.LowPart = size.s.LowPart;
     }
 
     if (totalfree)
     {
         totalfree->s.HighPart = available.s.HighPart;
-        totalfree->s.LowPart = available.s.LowPart ;
+        totalfree->s.LowPart = available.s.LowPart;
     }
 
     if (avail)
@@ -1124,7 +1104,7 @@
         /* Quick hack, should eventually be fixed to work 100% with
            Windows2000 (see comment above). */
         avail->s.HighPart = available.s.HighPart;
-        avail->s.LowPart = available.s.LowPart ;
+        avail->s.LowPart = available.s.LowPart;
     }
 
     return TRUE;
@@ -1176,7 +1156,7 @@
 /***********************************************************************
  *           GetDriveTypeA   (KERNEL32.208)
  *
- * Returns the type of the disk drive specified.  If root is NULL the
+ * Returns the type of the disk drive specified. If root is NULL the
  * root of the current directory is used.
  *
  * RETURNS
@@ -1184,15 +1164,15 @@
  *  Type of drive (from Win32 SDK):
  *
  *   DRIVE_UNKNOWN     unable to find out anything about the drive
- *   DRIVE_NO_ROOT_DIR nonexistand root dir
+ *   DRIVE_NO_ROOT_DIR nonexistent root dir
  *   DRIVE_REMOVABLE   the disk can be removed from the machine
  *   DRIVE_FIXED       the disk can not be removed from the machine
  *   DRIVE_REMOTE      network disk
  *   DRIVE_CDROM       CDROM drive
- *   DRIVE_RAMDISK     virtual disk in ram
+ *   DRIVE_RAMDISK     virtual disk in RAM
  *
- *   DRIVE_DOESNOTEXIST    XXX Not valid return value
- *   DRIVE_CANNOTDETERMINE XXX Not valid return value
+ *   DRIVE_DOESNOTEXIST    FIXME Not valid return value
+ *   DRIVE_CANNOTDETERMINE FIXME Not valid return value
  *   
  * BUGS
  *
@@ -1297,7 +1277,7 @@
  */
 BOOL WINAPI SetCurrentDirectoryA( LPCSTR dir )
 {
-    int olddrive, drive = DRIVE_GetCurrentDrive();
+    int drive, olddrive = DRIVE_GetCurrentDrive();
 
     if (!dir) {
     	ERR_(file)("(NULL)!\n");
@@ -1305,13 +1285,14 @@
     }
     if (dir[0] && (dir[1]==':'))
     {
-        drive = tolower( *dir ) - 'a';
+        drive = toupper( *dir ) - 'A';
         dir += 2;
     }
+    else
+	drive = olddrive;
 
     /* WARNING: we need to set the drive before the dir, as DRIVE_Chdir
        sets pTask->curdir only if pTask->curdrive is drive */
-    olddrive = drive; /* in case DRIVE_Chdir fails */
     if (!(DRIVE_SetCurrentDrive( drive )))
 	return FALSE;
     /* FIXME: what about empty strings? Add a \\ ? */
@@ -1356,10 +1337,10 @@
                 *p++ = '\0';
             }
         *p = '\0';
-       return count * 4;
+        return count * 4;
     }
     else
-      return (count * 4) + 1;/* account for terminating null */
+        return (count * 4) + 1; /* account for terminating null */
     /* The API tells about these different return values */
 }
 
diff --git a/include/cdrom.h b/include/cdrom.h
index 0de4797..cde7d22 100644
--- a/include/cdrom.h
+++ b/include/cdrom.h
@@ -67,9 +67,11 @@
 UINT16 	CDROM_Audio_GetNumberOfTracks(WINE_CDAUDIO* wcda);
 BOOL 	CDROM_Audio_GetTracksInfo(WINE_CDAUDIO* wcda);
 BOOL	CDROM_Audio_GetCDStatus(WINE_CDAUDIO* wcda);
+WORD	CDROM_Data_FindBestVoldesc(int fd);
 DWORD	CDROM_Audio_GetSerial(WINE_CDAUDIO* wcda);
 DWORD	CDROM_Data_GetSerial(WINE_CDAUDIO* wcda);
 DWORD	CDROM_GetSerial(int drive);
+DWORD	CDROM_GetLabel(int drive, char *label);
 
 #define CDFRAMES_PERSEC 		75
 #define SECONDS_PERMIN	 		60
diff --git a/misc/cdrom.c b/misc/cdrom.c
index 2f2da7d..14a2ffe 100644
--- a/misc/cdrom.c
+++ b/misc/cdrom.c
@@ -16,6 +16,7 @@
 #include "cdrom.h"
 #include "drive.h"
 #include "debugtools.h"
+#include "winbase.h"
 
 DEFAULT_DEBUG_CHANNEL(cdrom);
 
@@ -205,7 +206,7 @@
 	    entry.cdte_track = CDROM_LEADOUT;
 #else
 #define LEADOUT 0xaa
-	entry.starting_track = LEADOUT; /* XXX */
+	entry.starting_track = LEADOUT; /* FIXME */
 #endif
 	else
 #ifdef linux
@@ -553,7 +554,7 @@
 #endif
 }
 
-unsigned int get_offs_best_voldesc(int fd)
+WORD CDROM_Data_FindBestVoldesc(int fd)
 {
     BYTE cur_vd_type, max_vd_type = 0;
     unsigned int offs, best_offs = 0;
@@ -562,7 +563,7 @@
     {
         lseek(fd, offs, SEEK_SET);
         read(fd, &cur_vd_type, 1);
-        if (cur_vd_type == 0xff)
+        if (cur_vd_type == 0xff) /* voldesc set terminator */
             break;
         if (cur_vd_type > max_vd_type)
         {
@@ -602,27 +603,41 @@
  */
 DWORD CDROM_Data_GetSerial(WINE_CDAUDIO* wcda)
 {
-    unsigned int offs = get_offs_best_voldesc(wcda->unixdev);
+    WORD offs = CDROM_Data_FindBestVoldesc(wcda->unixdev);
     union {
 	unsigned long val;
 	unsigned char p[4];
     } serial;
-
+    BYTE b0 = 0, b1 = 1, b2 = 2, b3 = 3;
+    
     serial.val = 0;
     if (offs)
     {
 	BYTE buf[2048];
+	OSVERSIONINFOA ovi;
 	int i;
 
 	lseek(wcda->unixdev,offs,SEEK_SET);
 	read(wcda->unixdev,buf,2048);
+	/*
+	 * OK, another braindead one... argh. Just believe it.
+	 * Me$$ysoft chose to reverse the serial number in NT4/W2K.
+	 * It's true and nobody will ever be able to change it.
+	 */
+	ovi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
+	GetVersionExA(&ovi);
+	if ((ovi.dwPlatformId == VER_PLATFORM_WIN32_NT)
+	&&  (ovi.dwMajorVersion >= 4))
+	{
+	    b0 = 3; b1 = 2; b2 = 1; b3 = 0;
+	}
 	for(i=0; i<2048; i+=4)
 	{
 	    /* DON'T optimize this into DWORD !! (breaks overflow) */
-	    serial.p[0] += buf[i+0];
-	    serial.p[1] += buf[i+1];
-	    serial.p[2] += buf[i+2];
-	    serial.p[3] += buf[i+3];
+	    serial.p[b0] += buf[i+b0];
+	    serial.p[b1] += buf[i+b1];
+	    serial.p[b2] += buf[i+b2];
+	    serial.p[b3] += buf[i+b3];
 	}
     }
     return serial.val;
@@ -655,9 +670,13 @@
 	    serial = CDROM_Audio_GetSerial(&wcda);
 	}
 	else
-	if (media > CDS_AUDIO)
+	if ((media > CDS_AUDIO)
+	||  (media == -1) /* ioctl() error: ISO9660 image file given ? */
+	   )
 	    /* hopefully a data CD */
 	    serial = CDROM_Data_GetSerial(&wcda);
+	else
+	    WARN("Strange CD type (%d) or empty ?\n", media);
 
 	p = (media == CDS_AUDIO) ? "Audio " :
 	    (media > CDS_AUDIO) ? "Data " : "";
@@ -671,3 +690,96 @@
     return serial;
 }
 
+static const char empty_label[] = "           ";
+
+/**************************************************************************
+ *				CDROM_Data_GetLabel		[internal]
+ */
+DWORD CDROM_Data_GetLabel(WINE_CDAUDIO* wcda, char *label)
+{
+#define LABEL_LEN	32+1
+    WORD offs = CDROM_Data_FindBestVoldesc(wcda->unixdev);
+    WCHAR label_read[LABEL_LEN]; /* Unicode possible, too */
+    DWORD unicode_id = 0;
+
+    if (offs)
+    {
+	if ((lseek(wcda->unixdev, offs+0x58, SEEK_SET) == offs+0x58)
+	&&  (read(wcda->unixdev, &unicode_id, 3) == 3))
+	{
+	    int ver = (unicode_id & 0xff0000) >> 16;
+
+	    if ((lseek(wcda->unixdev, offs+0x28, SEEK_SET) != offs+0x28)
+	    ||  (read(wcda->unixdev, &label_read, LABEL_LEN) != LABEL_LEN))
+		goto failure;
+
+	    if ((LOWORD(unicode_id) == 0x2f25) /* Unicode ID */
+	    &&  ((ver == 0x40) || (ver == 0x43) || (ver == 0x45)))
+	    { /* yippee, unicode */
+		int i;
+		WORD ch;
+		for (i=0; i<LABEL_LEN;i++)
+		{ /* Motorola -> Intel Unicode conversion :-\ */
+		     ch = label_read[i];
+		     label_read[i] = (ch << 8) | (ch >> 8);
+		}
+		lstrcpynWtoA(label, label_read, 11);
+	    }
+	    else
+	    {
+		strncpy(label, (LPSTR)label_read, 11);
+		label[11] = '\0';
+	    }
+	    return 0;
+	}
+    }
+failure:
+    ERR("error reading label !\n");
+    strcpy(label, empty_label);
+    return 0;
+}
+
+/**************************************************************************
+ *				CDROM_GetLabel			[internal]
+ */
+DWORD CDROM_GetLabel(int drive, char *label)
+{
+    WINE_CDAUDIO wcda;
+    DWORD res = 1;
+
+    if (!(CDROM_Open(&wcda, drive)))
+    {
+	int media = CDROM_GetMediaType(&wcda);
+	LPSTR p;
+
+	if (media == CDS_AUDIO)
+	{
+	    strcpy(label, "Audio CD   ");
+	}
+	else
+	if (media == CDS_NO_INFO)
+	{
+	    strcpy(label, empty_label);
+	}
+	else
+	if ((media > CDS_AUDIO)
+	||  (media == -1) /* ioctl() error: ISO9660 image file given ? */
+	   )
+	    /* hopefully a data CD */
+	    CDROM_Data_GetLabel(&wcda, label);
+	else
+	{
+	    WARN("Strange CD type (%d) or empty ?\n", media);
+	    strcpy(label, empty_label);
+	    res = 0;
+	}
+
+	p = (media == CDS_AUDIO) ? "Audio " :
+	    (media > CDS_AUDIO) ? "Data " : "";
+	TRACE("%sCD label is '%s'.\n",
+	    p, label);
+	CDROM_Close(&wcda);
+    }
+    return res;
+}
+
diff --git a/msdos/int2f.c b/msdos/int2f.c
index e52149f..acca51b 100644
--- a/msdos/int2f.c
+++ b/msdos/int2f.c
@@ -9,19 +9,11 @@
 
 #include "config.h"
 
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
 #include "wine/winbase16.h"
-#include "ldt.h"
 #include "drive.h"
-#include "msdos.h"
 #include "miscemu.h"
 #include "module.h"
 #include "task.h"
-#include "dosexe.h"
-#include "heap.h"
 /* #define DEBUG_INT */
 #include "debugtools.h"
 #include "cdrom.h"
@@ -582,7 +574,7 @@
 			MSCDEX_StoreMSF(wcda.dwCurFrame, io_stru + 2);
 			break;
 		    default:
-			ERR("CDRom-Driver: Unsupported addressing mode !!\n");
+			ERR("CD-ROM driver: unsupported addressing mode !!\n");
 			Error = 0x0c;
 		    }	
 		    TRACE(" ----> HEAD LOCATION <%ld>\n", PTR_AT(io_stru, 2, DWORD));