pcsx2/plugins/cdvdGigaherz/src/CDVD.cpp

611 lines
13 KiB
C++

/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2014 David Quintana [gigaherz]
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 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 PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include "CDVD.h"
#include "resource.h"
#include "Shlwapi.h"
#include "svnrev.h"
#include <assert.h>
void (*newDiscCB)();
HANDLE hThread_keepAlive = INVALID_HANDLE_VALUE;
HANDLE hNotify_keepAlive = INVALID_HANDLE_VALUE;
DWORD pidThreadKeepAlive = 0;
#define STRFY(x) #x
#define TOSTR(x) STRFY(x)
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// State Information //
int strack;
int etrack;
track tracks[100];
int curDiskType;
int curTrayStatus;
int csector;
int cmode;
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Plugin Interface //
char *LibName = "cdvdGigaherz "
#ifdef PCSX2_DEBUG
" Debug "
#endif
"(" TOSTR(SVN_REV)
#if SVN_MODS
"/modded"
#endif
")";
const unsigned char version = PS2E_CDVD_VERSION;
const unsigned char revision = 0;
const unsigned char build = 9;
HINSTANCE hinst;
BOOL WINAPI DllMain(
HINSTANCE hinstDLL, // handle to DLL module
DWORD fdwReason, // reason for calling function
LPVOID lpvReserved // reserved
)
{
if(fdwReason==DLL_PROCESS_ATTACH) {
hinst=hinstDLL;
}
return TRUE;
}
char* CALLBACK PS2EgetLibName() {
return LibName;
}
u32 CALLBACK PS2EgetLibType() {
return PS2E_LT_CDVD;
}
u32 CALLBACK PS2EgetLibVersion2(u32 type) {
return (version << 16) | (revision << 8) | build;
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Utility Functions //
void SysMessage(char *fmt, ...) {
va_list list;
char tmp[512];
va_start(list,fmt);
vsprintf(tmp,fmt,list);
va_end(list);
MessageBox(0, tmp, "cdvdGigaherz Msg", 0);
}
u8 __inline dec_to_bcd(u8 dec)
{
return ((dec/10)<<4)|(dec%10);
}
void __inline lsn_to_msf(u8* minute, u8* second, u8* frame, u32 lsn)
{
*frame = dec_to_bcd(lsn%75);
lsn/=75;
*second= dec_to_bcd(lsn%60);
lsn/=60;
*minute= dec_to_bcd(lsn%100);
}
void __inline lba_to_msf(s32 lba, u8* m, u8* s, u8* f) {
lba += 150;
*m = (u8)(lba / (60*75));
*s = (u8)((lba / 75) % 60);
*f = (u8)(lba % 75);
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// CDVD processing functions //
char csrc[20];
bool cdvd_is_open = false;
bool cdvdKeepAlive_is_open = false;
bool disc_has_changed = false;
bool weAreInNewDiskCB = false;
Source *src;
char bfr[2352];
char throwaway[2352];
extern s32 prefetch_last_lba;
extern s32 prefetch_last_mode;
///////////////////////////////////////////////////////////////////////////////
// keepAliveThread throws a read event regularly to prevent drive spin down //
DWORD CALLBACK keepAliveThread(PVOID param)
{
printf(" * CDVD: KeepAlive thread started...\n");
while (cdvdKeepAlive_is_open)
{
// Sleep 30 seconds with thread abort check
if (WaitForSingleObject(hNotify_keepAlive, 30000) != WAIT_TIMEOUT) break;
if (!cdvdKeepAlive_is_open) {
break;
}
//printf(" * keepAliveThread: polling drive.\n");
//if (prefetch_last_mode == CDVD_MODE_2048)
src->ReadSectors2048(prefetch_last_lba, 1, throwaway);
//else
// src->ReadSectors2352(prefetch_last_lba, 1, throwaway);
}
printf(" * CDVD: KeepAlive thread finished.\n");
return 0;
}
s32 StartKeepAliveThread()
{
hNotify_keepAlive = CreateEvent(NULL, FALSE, FALSE, NULL);
if (hNotify_keepAlive == INVALID_HANDLE_VALUE)
return -1;
cdvdKeepAlive_is_open = true;
hThread_keepAlive = CreateThread(NULL, 0, keepAliveThread, NULL, 0, &pidThreadKeepAlive);
if (hThread_keepAlive == INVALID_HANDLE_VALUE) {
cdvdKeepAlive_is_open = false;
return -1;
}
SetThreadPriority(hThread_keepAlive, THREAD_PRIORITY_NORMAL);
return 0;
}
void StopKeepAliveThread()
{
cdvdKeepAlive_is_open = false;
PulseEvent(hNotify_keepAlive);
if (WaitForSingleObject(hThread_keepAlive, 5000) == WAIT_TIMEOUT)
{
TerminateThread(hThread_keepAlive, 0);
}
CloseHandle(hThread_keepAlive);
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// CDVD Pluin Interface //
void CALLBACK CDVDsetSettingsDir(const char* dir)
{
CfgSetSettingsDir(dir);
}
s32 CALLBACK CDVDinit()
{
return 0;
}
s32 CALLBACK CDVDopen(const char* pTitleFilename)
{
ReadSettings();
if(source_drive=='-')
{
// MSDN : Trailing backslash is required to ensure consistent behavior across
// various versions of Windows and storage types.
char temp[]="A:\\";
for(char d='A';d<='Z';d++)
{
temp[0]=d;
if(GetDriveType(temp)==DRIVE_CDROM)
{
source_drive=d;
break;
}
}
}
if(source_drive=='@')
{
curDiskType=CDVD_TYPE_NODISC;
return 0;
}
if(source_drive=='$')
{
printf(" * CDVD: Opening image '%s'...\n",source_file);
//open device file
src= TryLoaders(source_file);
}
else
{
sprintf(csrc,"\\\\.\\%c:",source_drive);
printf(" * CDVD: Opening drive '%s'...\n",csrc);
//open device file
src=new IOCtlSrc(csrc);
}
if(!src->IsOK())
{
printf(" * CDVD: Error opening source.\n");
return -1;
}
//setup threading manager
cdvdStartThread();
StartKeepAliveThread();
return cdvdRefreshData();
}
void CALLBACK CDVDclose()
{
StopKeepAliveThread();
cdvdStopThread();
//close device
delete src;
src=NULL;
}
void CALLBACK CDVDshutdown()
{
//nothing to do here
}
s32 CALLBACK CDVDgetDualInfo(s32* dualType, u32* _layer1start)
{
switch(src->GetMediaType())
{
case 1:
*dualType = 1;
*_layer1start = src->GetLayerBreakAddress();
return 1;
case 2:
*dualType = 2;
*_layer1start = src->GetLayerBreakAddress();
return 1;
case 0:
*dualType = 0;
*_layer1start = 0;
return 1;
}
return 0;
}
int lastReadInNewDiskCB=0;
char directReadSectorBuffer[2448];
s32 CALLBACK CDVDreadSector(u8* buffer, s32 lsn, int mode)
{
return cdvdDirectReadSector(lsn,mode,(char*)buffer);
}
s32 CALLBACK CDVDreadTrack(u32 lsn, int mode)
{
csector=lsn;
cmode=mode;
if(weAreInNewDiskCB)
{
int ret = cdvdDirectReadSector(lsn,mode,directReadSectorBuffer);
if(ret==0) lastReadInNewDiskCB=1;
return ret;
}
if(lsn>tracks[0].length) // track 0 is total disc.
{
return -1;
}
return cdvdRequestSector(lsn,mode);
}
// return can be NULL (for async modes)
u8* CALLBACK CDVDgetBuffer()
{
if(lastReadInNewDiskCB)
{
lastReadInNewDiskCB=0;
return (u8*)directReadSectorBuffer;
}
u8 *s = (u8*)cdvdGetSector(csector,cmode);
return s;
}
// return can be NULL (for async modes)
int CALLBACK CDVDgetBuffer2(u8* dest)
{
int csize = 2352;
switch(cmode)
{
case CDVD_MODE_2048: csize = 2048; break;
case CDVD_MODE_2328: csize = 2328; break;
case CDVD_MODE_2340: csize = 2340; break;
}
if(lastReadInNewDiskCB)
{
lastReadInNewDiskCB=0;
memcpy(dest, directReadSectorBuffer, csize);
return 0;
}
memcpy(dest, cdvdGetSector(csector,cmode), csize);
return 0;
}
s32 CALLBACK CDVDreadSubQ(u32 lsn, cdvdSubQ* subq)
{
int i;
// the formatted subq command returns: control/adr, track, index, trk min, trk sec, trk frm, 0x00, abs min, abs sec, abs frm
if(lsn>tracks[0].length) // track 0 is total disc.
return -1;
memset(subq,0,sizeof(cdvdSubQ));
lsn_to_msf(&subq->discM,&subq->discS,&subq->discF,lsn+150);
i=strack;
while(i<=etrack)
{
if(lsn<=tracks[i].length)
break;
lsn-=tracks[i].length;
i++;
}
if(i>etrack)
i=etrack;
lsn_to_msf(&subq->trackM,&subq->trackS,&subq->trackF,lsn);
subq->mode=1;
subq->ctrl=tracks[i].type;
subq->trackNum=i;
subq->trackIndex=1;
return 0;
}
s32 CALLBACK CDVDgetTN(cdvdTN *Buffer)
{
Buffer->strack=strack;
Buffer->etrack=etrack;
return 0;
}
s32 CALLBACK CDVDgetTD(u8 Track, cdvdTD *Buffer)
{
if(Track==0)
{
Buffer->lsn = tracks[0].length;
Buffer->type= 0;
return 0;
}
if(Track<strack) return -1;
if(Track>etrack) return -1;
Buffer->lsn = tracks[Track].start_lba;
Buffer->type= tracks[Track].type;
return 0;
}
u32 layer1start=-1;
s32 CALLBACK CDVDgetTOC(u8* tocBuff)
{
//return src->ReadTOC((char*)toc,2048);
//that didn't work too well...
if(curDiskType==CDVD_TYPE_NODISC)
return -1;
if((curDiskType == CDVD_TYPE_DVDV) ||
(curDiskType == CDVD_TYPE_PS2DVD))
{
memset(tocBuff, 0, 2048);
s32 mt=src->GetMediaType();
if(mt<0)
return -1;
if(mt==0) //single layer
{
// fake it
tocBuff[ 0] = 0x04;
tocBuff[ 1] = 0x02;
tocBuff[ 2] = 0xF2;
tocBuff[ 3] = 0x00;
tocBuff[ 4] = 0x86;
tocBuff[ 5] = 0x72;
tocBuff[16] = 0x00; // first sector for layer 0
tocBuff[17] = 0x03;
tocBuff[18] = 0x00;
tocBuff[19] = 0x00;
}
else if(mt==1) //PTP
{
layer1start = src->GetLayerBreakAddress() + 0x30000;
// dual sided
tocBuff[ 0] = 0x24;
tocBuff[ 1] = 0x02;
tocBuff[ 2] = 0xF2;
tocBuff[ 3] = 0x00;
tocBuff[ 4] = 0x41;
tocBuff[ 5] = 0x95;
tocBuff[14] = 0x61; // PTP
tocBuff[16] = 0x00;
tocBuff[17] = 0x03;
tocBuff[18] = 0x00;
tocBuff[19] = 0x00;
tocBuff[20] = (layer1start>>24);
tocBuff[21] = (layer1start>>16)&0xff;
tocBuff[22] = (layer1start>> 8)&0xff;
tocBuff[23] = (layer1start>> 0)&0xff;
}
else //OTP
{
layer1start = src->GetLayerBreakAddress() + 0x30000;
// dual sided
tocBuff[ 0] = 0x24;
tocBuff[ 1] = 0x02;
tocBuff[ 2] = 0xF2;
tocBuff[ 3] = 0x00;
tocBuff[ 4] = 0x41;
tocBuff[ 5] = 0x95;
tocBuff[14] = 0x71; // OTP
tocBuff[16] = 0x00;
tocBuff[17] = 0x03;
tocBuff[18] = 0x00;
tocBuff[19] = 0x00;
tocBuff[24] = (layer1start>>24);
tocBuff[25] = (layer1start>>16)&0xff;
tocBuff[26] = (layer1start>> 8)&0xff;
tocBuff[27] = (layer1start>> 0)&0xff;
}
}
else if(curDiskType == CDVD_TYPE_CDDA ||
curDiskType == CDVD_TYPE_PS2CDDA ||
curDiskType == CDVD_TYPE_PS2CD ||
curDiskType == CDVD_TYPE_PSCDDA ||
curDiskType == CDVD_TYPE_PSCD)
{
// cd toc
// (could be replaced by 1 command that reads the full toc)
u8 min, sec, frm,i;
s32 err;
cdvdTN diskInfo;
cdvdTD trackInfo;
memset(tocBuff, 0, 1024);
if (CDVDgetTN(&diskInfo) == -1) { diskInfo.etrack = 0;diskInfo.strack = 1; }
if (CDVDgetTD(0, &trackInfo) == -1) trackInfo.lsn = 0;
tocBuff[0] = 0x41;
tocBuff[1] = 0x00;
#define itob(n) ((((n)/10)<<4)+((n)%10))
//Number of FirstTrack
tocBuff[2] = 0xA0;
tocBuff[7] = itob(diskInfo.strack);
//Number of LastTrack
tocBuff[12] = 0xA1;
tocBuff[17] = itob(diskInfo.etrack);
//DiskLength
lba_to_msf(trackInfo.lsn, &min, &sec, &frm);
tocBuff[22] = 0xA2;
tocBuff[27] = itob(min);
tocBuff[28] = itob(sec);
tocBuff[29] = itob(frm);
fprintf(stderr,"Track 0: %d mins %d secs %d frames\n",min,sec,frm);
for (i=diskInfo.strack; i<=diskInfo.etrack; i++)
{
err = CDVDgetTD(i, &trackInfo);
lba_to_msf(trackInfo.lsn, &min, &sec, &frm);
tocBuff[i*10+30] = trackInfo.type;
tocBuff[i*10+32] = err == -1 ? 0 : itob(i); //number
tocBuff[i*10+37] = itob(min);
tocBuff[i*10+38] = itob(sec);
tocBuff[i*10+39] = itob(frm);
fprintf(stderr,"Track %d: %d mins %d secs %d frames\n",i,min,sec,frm);
}
}
else
return -1;
return 0;
}
s32 CALLBACK CDVDgetDiskType()
{
return curDiskType;
}
s32 CALLBACK CDVDgetTrayStatus()
{
return curTrayStatus;
}
s32 CALLBACK CDVDctrlTrayOpen()
{
curTrayStatus=CDVD_TRAY_OPEN;
return 0;
}
s32 CALLBACK CDVDctrlTrayClose()
{
curTrayStatus=CDVD_TRAY_CLOSE;
return 0;
}
void CALLBACK CDVDnewDiskCB(void (*callback)())
{
newDiscCB=callback;
}
void configure();
void CALLBACK CDVDconfigure()
{
configure();
}
void CALLBACK CDVDabout() {
SysMessage("%s %d.%d", LibName, revision, build);
}
s32 CALLBACK CDVDtest() {
return 0;
}