/*
 *  Copyright (C) 2002-2007  The DOSBox Team
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

/* $Id: cdrom_ioctl_win32.cpp,v 1.14 2007/01/08 19:45:39 qbix79 Exp $ */

#if defined (WIN32)

// *****************************************************************
// Windows IOCTL functions (not suitable for 95/98/Me)
// *****************************************************************

#include <windows.h>
#include <io.h>

#if defined (_MSC_VER)
#include <ntddcdrm.h>			// Ioctl stuff
#include <winioctl.h>			// Ioctl stuff
#else 
#include "ddk/ntddcdrm.h"		// Ioctl stuff
#endif

#include "cdrom.h"

CDROM_Interface_Ioctl::CDROM_Interface_Ioctl()
{
	pathname[0] = 0;
	hIOCTL = INVALID_HANDLE_VALUE;
	memset(&oldLeadOut,0,sizeof(oldLeadOut));
};

CDROM_Interface_Ioctl::~CDROM_Interface_Ioctl()
{
	StopAudio();
	Close();
};

bool CDROM_Interface_Ioctl::GetUPC(unsigned char& attr, char* upc)
{
	// FIXME : To Do
	return true;
}

bool CDROM_Interface_Ioctl::GetAudioTracks(int& stTrack, int& endTrack, TMSF& leadOut) 
{
//	Open();
	CDROM_TOC toc;
	DWORD	byteCount;
	BOOL	bStat = DeviceIoControl(hIOCTL,IOCTL_CDROM_READ_TOC, NULL, 0, 
									&toc, sizeof(toc), &byteCount,NULL);
//	Close();
	if (!bStat) return false;

	stTrack		= toc.FirstTrack;
	endTrack	= toc.LastTrack;
	leadOut.min = toc.TrackData[endTrack].Address[1];
	leadOut.sec	= toc.TrackData[endTrack].Address[2];
	leadOut.fr	= toc.TrackData[endTrack].Address[3];
	return true;
};

bool CDROM_Interface_Ioctl::GetAudioTrackInfo(int track, TMSF& start, unsigned char& attr)
{
//	Open();
	CDROM_TOC toc;
	DWORD	byteCount;
	BOOL	bStat = DeviceIoControl(hIOCTL,IOCTL_CDROM_READ_TOC, NULL, 0, 
									&toc, sizeof(toc), &byteCount,NULL);
//	Close();
	if (!bStat) return false;
	
	attr		= (toc.TrackData[track-1].Control << 4) & 0xEF;
	start.min	= toc.TrackData[track-1].Address[1];
	start.sec	= toc.TrackData[track-1].Address[2];
	start.fr	= toc.TrackData[track-1].Address[3];
	return true;
};	

bool CDROM_Interface_Ioctl::GetAudioSub(unsigned char& attr, unsigned char& track, unsigned char& index, TMSF& relPos, TMSF& absPos)
{
//	Open();

	CDROM_SUB_Q_DATA_FORMAT insub;
	SUB_Q_CHANNEL_DATA sub;
	DWORD	byteCount;

	insub.Format = IOCTL_CDROM_CURRENT_POSITION;

	BOOL	bStat = DeviceIoControl(hIOCTL,IOCTL_CDROM_READ_Q_CHANNEL, &insub, sizeof(insub), 
									&sub, sizeof(sub), &byteCount,NULL);
//	Close();
	if (!bStat) return false;

	attr		= (sub.CurrentPosition.Control << 4) & 0xEF;
	track		= sub.CurrentPosition.TrackNumber;
	index		= sub.CurrentPosition.IndexNumber;
	relPos.min	= sub.CurrentPosition.TrackRelativeAddress[1];
	relPos.sec	= sub.CurrentPosition.TrackRelativeAddress[2];
	relPos.fr	= sub.CurrentPosition.TrackRelativeAddress[3];
	absPos.min	= sub.CurrentPosition.AbsoluteAddress[1];
	absPos.sec	= sub.CurrentPosition.AbsoluteAddress[2];
	absPos.fr	= sub.CurrentPosition.AbsoluteAddress[3];
	
	return true;
};

bool CDROM_Interface_Ioctl::GetAudioStatus(bool& playing, bool& pause)
{
//	Open();

	CDROM_SUB_Q_DATA_FORMAT insub;
	SUB_Q_CHANNEL_DATA sub;
	DWORD byteCount;

	insub.Format = IOCTL_CDROM_CURRENT_POSITION;

	BOOL	bStat = DeviceIoControl(hIOCTL,IOCTL_CDROM_READ_Q_CHANNEL, &insub, sizeof(insub), 
									&sub, sizeof(sub), &byteCount,NULL);
//	Close();
	if (!bStat) return false;

	playing = (sub.CurrentPosition.Header.AudioStatus == AUDIO_STATUS_IN_PROGRESS);
	pause	= (sub.CurrentPosition.Header.AudioStatus == AUDIO_STATUS_PAUSED);

	return true;
};

bool CDROM_Interface_Ioctl::GetMediaTrayStatus(bool& mediaPresent, bool& mediaChanged, bool& trayOpen)
{
	// Seems not possible to get this values using ioctl...
	int		track1,track2;
	TMSF	leadOut;
	// If we can read, there's a media
	mediaPresent = GetAudioTracks(track1, track2, leadOut),
	trayOpen	 = !mediaPresent;
	mediaChanged = (oldLeadOut.min!=leadOut.min) || (oldLeadOut.sec!=leadOut.sec) || (oldLeadOut.fr!=leadOut.fr);
	if (mediaChanged) {
		// Open new media
		Close(); Open();
	};
	// Save old values
	oldLeadOut.min = leadOut.min;
	oldLeadOut.sec = leadOut.sec;
	oldLeadOut.fr  = leadOut.fr;
	// always success
	return true;
};

bool CDROM_Interface_Ioctl::PlayAudioSector	(unsigned long start,unsigned long len)
{
//	Open();
	CDROM_PLAY_AUDIO_MSF audio;
	DWORD	byteCount;
	// Start
	unsigned long addr	= start + 150;
	audio.StartingF = (UCHAR)(addr%75); addr/=75;
	audio.StartingS = (UCHAR)(addr%60); 
	audio.StartingM = (UCHAR)(addr/60);
	// End
	addr			= start + len + 150;
	audio.EndingF	= (UCHAR)(addr%75); addr/=75;
	audio.EndingS	= (UCHAR)(addr%60); 
	audio.EndingM	= (UCHAR)(addr/60);

	BOOL	bStat = DeviceIoControl(hIOCTL,IOCTL_CDROM_PLAY_AUDIO_MSF, &audio, sizeof(audio), 
									NULL, 0, &byteCount,NULL);
//	Close();
	return bStat>0;
};

bool CDROM_Interface_Ioctl::PauseAudio(bool resume)
{
//	Open();
	BOOL bStat; 
	DWORD byteCount;
	if (resume) bStat = DeviceIoControl(hIOCTL,IOCTL_CDROM_RESUME_AUDIO, NULL, 0, 
										NULL, 0, &byteCount,NULL);	
	else		bStat = DeviceIoControl(hIOCTL,IOCTL_CDROM_PAUSE_AUDIO, NULL, 0, 
										NULL, 0, &byteCount,NULL);
//	Close();
	return bStat>0;
};

bool CDROM_Interface_Ioctl::StopAudio(void)
{
//	Open();
	BOOL bStat; 
	DWORD byteCount;
	bStat = DeviceIoControl(hIOCTL,IOCTL_CDROM_STOP_AUDIO, NULL, 0, 
							NULL, 0, &byteCount,NULL);	
//	Close();
	return bStat>0;
};

bool CDROM_Interface_Ioctl::LoadUnloadMedia(bool unload)
{
//	Open();
	BOOL bStat; 
	DWORD byteCount;
	if (unload) bStat = DeviceIoControl(hIOCTL,IOCTL_STORAGE_EJECT_MEDIA, NULL, 0, 
										NULL, 0, &byteCount,NULL);	
	else		bStat = DeviceIoControl(hIOCTL,IOCTL_STORAGE_LOAD_MEDIA, NULL, 0, 
										NULL, 0, &byteCount,NULL);	
//	Close();
	return bStat>0;
};

bool CDROM_Interface_Ioctl::ReadSectors(PhysPt buffer, bool raw, unsigned long sector, unsigned long num)
{
	BOOL  bStat;
	DWORD byteCount = 0;

//	Open();

	Bitu	buflen	= raw ? num*RAW_SECTOR_SIZE : num*COOKED_SECTOR_SIZE;
	Bit8u*	bufdata = new Bit8u[buflen];

	if (!raw) {
		// Cooked
		int	  success = 0;
		DWORD newPos  = SetFilePointer(hIOCTL, sector*COOKED_SECTOR_SIZE, 0, FILE_BEGIN);
		if (newPos != 0xFFFFFFFF) success = ReadFile(hIOCTL, bufdata, buflen, &byteCount, NULL);
		bStat = (success!=0);
	} else {
		// Raw
		RAW_READ_INFO in;
		in.DiskOffset.LowPart	= sector;
		in.DiskOffset.HighPart	= 0;
		in.SectorCount			= num;
		in.TrackMode			= CDDA;		
		bStat = DeviceIoControl(hIOCTL,IOCTL_CDROM_RAW_READ, &in, sizeof(in), 
								bufdata, buflen, &byteCount,NULL);
	}
//	Close();

	MEM_BlockWrite(buffer,bufdata,buflen);
	delete[] bufdata;

	return (byteCount==buflen) && (bStat>0);
}

bool CDROM_Interface_Ioctl::SetDevice(char* path, int forceCD)
{
	if (GetDriveType(path)==DRIVE_CDROM) {
		char letter [3] = { 0, ':', 0 };
		letter[0] = path[0];
		strcpy(pathname,"\\\\.\\");
		strcat(pathname,letter);
		if (Open()) {
//			Close();
			return true;
		};
	}
	return false;
}

bool CDROM_Interface_Ioctl::Open(void)
{
	hIOCTL	= CreateFile(pathname,			// drive to open
						GENERIC_READ,		// read access
						FILE_SHARE_READ |	// share mode
						FILE_SHARE_WRITE, 
						NULL,				// default security attributes
						OPEN_EXISTING,		// disposition
						0,					// file attributes
						NULL);				// do not copy file attributes
	return (hIOCTL!=INVALID_HANDLE_VALUE);
};

void CDROM_Interface_Ioctl::Close(void)
{
	CloseHandle(hIOCTL);
};

#endif
