2014-04-06 13:11:19 +00:00
|
|
|
/* 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/>.
|
|
|
|
*/
|
|
|
|
|
2010-04-24 21:37:39 +00:00
|
|
|
#include "CDVD.h"
|
|
|
|
|
2016-09-19 17:24:54 +00:00
|
|
|
const s32 prefetch_max_blocks = 16;
|
|
|
|
s32 prefetch_mode = 0;
|
|
|
|
s32 prefetch_last_lba = 0;
|
|
|
|
s32 prefetch_last_mode = 0;
|
|
|
|
s32 prefetch_left = 0;
|
2010-04-24 21:37:39 +00:00
|
|
|
|
2016-08-08 21:18:36 +00:00
|
|
|
HANDLE hNotify = nullptr;
|
|
|
|
HANDLE hThread = nullptr;
|
|
|
|
HANDLE hRequestComplete = nullptr;
|
2010-04-24 21:37:39 +00:00
|
|
|
|
|
|
|
CRITICAL_SECTION CacheMutex;
|
|
|
|
|
2016-09-19 17:24:54 +00:00
|
|
|
DWORD pidThread = 0;
|
2010-04-24 21:37:39 +00:00
|
|
|
|
2010-04-25 00:31:27 +00:00
|
|
|
typedef struct
|
2010-04-24 21:37:39 +00:00
|
|
|
{
|
2016-09-19 17:24:54 +00:00
|
|
|
int lsn;
|
|
|
|
int mode;
|
2016-10-16 19:39:56 +00:00
|
|
|
char data[2352 * 16]; //we will read in blocks of 16 sectors
|
2010-04-24 21:37:39 +00:00
|
|
|
} SectorInfo;
|
|
|
|
|
2014-05-26 12:28:56 +00:00
|
|
|
//bits: 12 would use 1<<12 entries, or 4096*16 sectors ~ 128MB
|
|
|
|
#define CACHE_SIZE 12
|
2010-04-24 21:37:39 +00:00
|
|
|
|
2016-09-19 17:24:54 +00:00
|
|
|
const s32 CacheSize = (1 << CACHE_SIZE);
|
2010-04-24 21:37:39 +00:00
|
|
|
SectorInfo Cache[CacheSize];
|
|
|
|
|
2016-09-19 17:24:54 +00:00
|
|
|
bool threadRequestPending;
|
|
|
|
SectorInfo threadRequestInfo;
|
2010-04-24 21:37:39 +00:00
|
|
|
|
2016-09-19 17:24:54 +00:00
|
|
|
u32 cdvdSectorHash(int lsn, int mode)
|
2010-04-24 21:37:39 +00:00
|
|
|
{
|
2016-09-19 17:24:54 +00:00
|
|
|
u32 t = 0;
|
2010-04-24 21:37:39 +00:00
|
|
|
|
2016-09-19 17:24:54 +00:00
|
|
|
int i = 32;
|
|
|
|
int m = CacheSize - 1;
|
2010-04-24 21:37:39 +00:00
|
|
|
|
2016-09-19 17:24:54 +00:00
|
|
|
while (i >= 0) {
|
|
|
|
t ^= lsn & m;
|
|
|
|
lsn >>= CACHE_SIZE;
|
|
|
|
i -= CACHE_SIZE;
|
|
|
|
}
|
2010-04-24 21:37:39 +00:00
|
|
|
|
2016-09-19 17:24:54 +00:00
|
|
|
return (t ^ mode) & m;
|
2010-04-24 21:37:39 +00:00
|
|
|
}
|
|
|
|
|
2016-09-19 17:24:54 +00:00
|
|
|
void cdvdCacheUpdate(int lsn, int mode, char *data)
|
2010-04-24 21:37:39 +00:00
|
|
|
{
|
2016-09-19 17:24:54 +00:00
|
|
|
EnterCriticalSection(&CacheMutex);
|
|
|
|
u32 entry = cdvdSectorHash(lsn, mode);
|
2010-04-24 21:37:39 +00:00
|
|
|
|
2016-09-19 17:24:54 +00:00
|
|
|
memcpy(Cache[entry].data, data, 2352 * 16);
|
|
|
|
Cache[entry].lsn = lsn;
|
|
|
|
Cache[entry].mode = mode;
|
|
|
|
LeaveCriticalSection(&CacheMutex);
|
2010-04-24 21:37:39 +00:00
|
|
|
}
|
|
|
|
|
2016-09-19 17:24:54 +00:00
|
|
|
bool cdvdCacheFetch(int lsn, int mode, char *data)
|
2010-04-24 21:37:39 +00:00
|
|
|
{
|
2016-09-19 17:24:54 +00:00
|
|
|
EnterCriticalSection(&CacheMutex);
|
|
|
|
u32 entry = cdvdSectorHash(lsn, mode);
|
|
|
|
|
|
|
|
if ((Cache[entry].lsn == lsn) &&
|
|
|
|
(Cache[entry].mode == mode)) {
|
|
|
|
memcpy(data, Cache[entry].data, 2352 * 16);
|
|
|
|
LeaveCriticalSection(&CacheMutex);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
//printf("NOT IN CACHE\n");
|
|
|
|
LeaveCriticalSection(&CacheMutex);
|
|
|
|
return false;
|
2010-04-24 21:37:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void cdvdCacheReset()
|
|
|
|
{
|
2016-09-19 17:24:54 +00:00
|
|
|
EnterCriticalSection(&CacheMutex);
|
|
|
|
for (int i = 0; i < CacheSize; i++) {
|
|
|
|
Cache[i].lsn = -1;
|
|
|
|
Cache[i].mode = -1;
|
|
|
|
}
|
|
|
|
LeaveCriticalSection(&CacheMutex);
|
2010-04-24 21:37:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void cdvdCallNewDiscCB()
|
|
|
|
{
|
2016-09-19 17:24:54 +00:00
|
|
|
weAreInNewDiskCB = true;
|
|
|
|
newDiscCB();
|
|
|
|
weAreInNewDiskCB = false;
|
2010-04-24 21:37:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool cdvdUpdateDiscStatus()
|
|
|
|
{
|
2016-10-22 23:53:41 +00:00
|
|
|
bool ready = src->DiscReady();
|
2016-09-19 17:24:54 +00:00
|
|
|
|
2016-10-22 23:53:41 +00:00
|
|
|
if (!ready) {
|
2016-09-19 17:24:54 +00:00
|
|
|
if (!disc_has_changed) {
|
|
|
|
disc_has_changed = true;
|
|
|
|
curDiskType = CDVD_TYPE_NODISC;
|
|
|
|
curTrayStatus = CDVD_TRAY_OPEN;
|
|
|
|
cdvdCallNewDiscCB();
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (disc_has_changed) {
|
|
|
|
curDiskType = CDVD_TYPE_NODISC;
|
|
|
|
curTrayStatus = CDVD_TRAY_CLOSE;
|
|
|
|
|
|
|
|
disc_has_changed = false;
|
|
|
|
cdvdRefreshData();
|
|
|
|
cdvdCallNewDiscCB();
|
|
|
|
}
|
|
|
|
}
|
2016-10-22 23:53:41 +00:00
|
|
|
return !ready;
|
2010-04-24 21:37:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
DWORD CALLBACK cdvdThread(PVOID param)
|
|
|
|
{
|
2016-09-19 17:24:54 +00:00
|
|
|
printf(" * CDVD: IO thread started...\n");
|
|
|
|
|
|
|
|
while (cdvd_is_open) {
|
|
|
|
if (cdvdUpdateDiscStatus()) {
|
|
|
|
// Need to sleep some to avoid an aggressive spin that sucks the cpu dry.
|
|
|
|
Sleep(10);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (prefetch_left)
|
|
|
|
WaitForSingleObject(hNotify, 1);
|
|
|
|
else
|
|
|
|
WaitForSingleObject(hNotify, 250);
|
|
|
|
|
|
|
|
// check again to make sure we're not done here...
|
|
|
|
if (!cdvd_is_open)
|
|
|
|
break;
|
|
|
|
|
|
|
|
static SectorInfo info;
|
|
|
|
|
|
|
|
bool handlingRequest = false;
|
|
|
|
|
|
|
|
if (threadRequestPending) {
|
|
|
|
info = threadRequestInfo;
|
|
|
|
handlingRequest = true;
|
|
|
|
} else {
|
|
|
|
info.lsn = prefetch_last_lba;
|
|
|
|
info.mode = prefetch_last_mode;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (threadRequestPending || prefetch_left) {
|
|
|
|
s32 count = 16;
|
2016-10-17 19:15:57 +00:00
|
|
|
s32 left = src->GetSectorCount() - info.lsn;
|
2016-09-19 17:24:54 +00:00
|
|
|
|
|
|
|
if (left < count)
|
|
|
|
count = left;
|
|
|
|
|
2016-10-17 22:42:13 +00:00
|
|
|
for (int tries = 0; tries < 4; ++tries) {
|
|
|
|
if (info.mode == CDVD_MODE_2048) {
|
|
|
|
if (src->ReadSectors2048(info.lsn, count, info.data))
|
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
if (src->ReadSectors2352(info.lsn, count, info.data))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2016-09-19 17:24:54 +00:00
|
|
|
|
|
|
|
cdvdCacheUpdate(info.lsn, info.mode, info.data);
|
|
|
|
|
|
|
|
if (handlingRequest) {
|
|
|
|
threadRequestInfo = info;
|
|
|
|
|
|
|
|
handlingRequest = false;
|
|
|
|
threadRequestPending = false;
|
|
|
|
PulseEvent(hRequestComplete);
|
|
|
|
|
|
|
|
prefetch_last_lba = info.lsn;
|
|
|
|
prefetch_last_mode = info.mode;
|
|
|
|
|
|
|
|
prefetch_left = prefetch_max_blocks;
|
|
|
|
} else {
|
|
|
|
prefetch_last_lba += 16;
|
|
|
|
prefetch_left--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
printf(" * CDVD: IO thread finished.\n");
|
|
|
|
return 0;
|
2010-04-24 21:37:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
s32 cdvdStartThread()
|
|
|
|
{
|
2016-09-19 17:24:54 +00:00
|
|
|
InitializeCriticalSection(&CacheMutex);
|
2010-04-24 21:37:39 +00:00
|
|
|
|
2016-09-19 17:24:54 +00:00
|
|
|
hNotify = CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
|
|
if (hNotify == nullptr)
|
|
|
|
return -1;
|
2010-04-24 21:37:39 +00:00
|
|
|
|
2016-09-19 17:24:54 +00:00
|
|
|
hRequestComplete = CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
|
|
if (hRequestComplete == nullptr)
|
|
|
|
return -1;
|
2010-04-24 21:37:39 +00:00
|
|
|
|
2016-09-19 17:24:54 +00:00
|
|
|
cdvd_is_open = true;
|
|
|
|
hThread = CreateThread(NULL, 0, cdvdThread, NULL, 0, &pidThread);
|
2010-04-24 21:37:39 +00:00
|
|
|
|
2016-09-19 17:24:54 +00:00
|
|
|
if (hThread == nullptr)
|
|
|
|
return -1;
|
2010-04-24 21:37:39 +00:00
|
|
|
|
2016-09-19 17:24:54 +00:00
|
|
|
SetThreadPriority(hThread, THREAD_PRIORITY_NORMAL);
|
2010-04-24 21:37:39 +00:00
|
|
|
|
2016-09-19 17:24:54 +00:00
|
|
|
cdvdCacheReset();
|
2010-04-24 21:37:39 +00:00
|
|
|
|
2016-09-19 17:24:54 +00:00
|
|
|
return 0;
|
2010-04-24 21:37:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void cdvdStopThread()
|
|
|
|
{
|
2016-09-19 17:24:54 +00:00
|
|
|
cdvd_is_open = false;
|
|
|
|
PulseEvent(hNotify);
|
|
|
|
if (WaitForSingleObject(hThread, 4000) == WAIT_TIMEOUT) {
|
|
|
|
TerminateThread(hThread, 0);
|
|
|
|
}
|
|
|
|
CloseHandle(hThread);
|
|
|
|
CloseHandle(hNotify);
|
|
|
|
CloseHandle(hRequestComplete);
|
|
|
|
|
|
|
|
DeleteCriticalSection(&CacheMutex);
|
2010-04-24 21:37:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
s32 cdvdRequestSector(u32 sector, s32 mode)
|
|
|
|
{
|
2016-10-17 19:15:57 +00:00
|
|
|
if (sector >= src->GetSectorCount())
|
2016-09-19 17:24:54 +00:00
|
|
|
return -1;
|
2010-04-24 21:37:39 +00:00
|
|
|
|
2016-10-16 19:39:56 +00:00
|
|
|
sector &= ~15; //align to 16-sector block
|
2010-04-24 21:37:39 +00:00
|
|
|
|
2016-09-19 17:24:54 +00:00
|
|
|
threadRequestInfo.lsn = sector;
|
|
|
|
threadRequestInfo.mode = mode;
|
|
|
|
threadRequestPending = false;
|
|
|
|
if (cdvdCacheFetch(sector, mode, threadRequestInfo.data)) {
|
|
|
|
return 0;
|
|
|
|
}
|
2010-04-24 21:37:39 +00:00
|
|
|
|
2016-09-19 17:24:54 +00:00
|
|
|
threadRequestPending = true;
|
|
|
|
ResetEvent(hRequestComplete);
|
|
|
|
PulseEvent(hNotify);
|
2010-04-24 21:37:39 +00:00
|
|
|
|
2016-09-19 17:24:54 +00:00
|
|
|
return 0;
|
2010-04-24 21:37:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
s32 cdvdRequestComplete()
|
|
|
|
{
|
2016-09-19 17:24:54 +00:00
|
|
|
return !threadRequestPending;
|
2010-04-24 21:37:39 +00:00
|
|
|
}
|
|
|
|
|
2016-09-19 17:24:54 +00:00
|
|
|
s8 *cdvdGetSector(s32 sector, s32 mode)
|
2010-04-24 21:37:39 +00:00
|
|
|
{
|
2016-09-19 17:24:54 +00:00
|
|
|
while (threadRequestPending) {
|
|
|
|
WaitForSingleObject(hRequestComplete, 10);
|
|
|
|
}
|
|
|
|
|
|
|
|
s32 offset;
|
|
|
|
|
|
|
|
if (mode == CDVD_MODE_2048) {
|
|
|
|
offset = 2048 * (sector - threadRequestInfo.lsn);
|
|
|
|
return threadRequestInfo.data + offset;
|
|
|
|
}
|
|
|
|
|
|
|
|
offset = 2352 * (sector - threadRequestInfo.lsn);
|
|
|
|
s8 *data = threadRequestInfo.data + offset;
|
|
|
|
|
|
|
|
switch (mode) {
|
|
|
|
case CDVD_MODE_2328:
|
|
|
|
return data + 24;
|
|
|
|
case CDVD_MODE_2340:
|
|
|
|
return data + 12;
|
|
|
|
}
|
|
|
|
return data;
|
2010-04-24 21:37:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
s32 cdvdDirectReadSector(s32 first, s32 mode, char *buffer)
|
|
|
|
{
|
2016-09-19 17:24:54 +00:00
|
|
|
static char data[16 * 2352];
|
2010-04-24 21:37:39 +00:00
|
|
|
|
2016-10-17 19:15:57 +00:00
|
|
|
if ((u32)first >= src->GetSectorCount())
|
2016-09-19 17:24:54 +00:00
|
|
|
return -1;
|
2010-04-24 21:37:39 +00:00
|
|
|
|
2016-10-16 19:39:56 +00:00
|
|
|
s32 sector = first & (~15); //align to 16-sector block
|
2010-04-24 21:37:39 +00:00
|
|
|
|
2016-09-19 17:24:54 +00:00
|
|
|
EnterCriticalSection(&CacheMutex);
|
|
|
|
if (!cdvdCacheFetch(sector, mode, data)) {
|
|
|
|
s32 count = 16;
|
2016-10-17 19:15:57 +00:00
|
|
|
s32 left = src->GetSectorCount() - sector;
|
2010-04-24 21:37:39 +00:00
|
|
|
|
2016-09-19 17:24:54 +00:00
|
|
|
if (left < count)
|
|
|
|
count = left;
|
2010-04-24 21:37:39 +00:00
|
|
|
|
2016-10-17 22:42:13 +00:00
|
|
|
for (int tries = 0; tries < 4; ++tries) {
|
|
|
|
if (mode == CDVD_MODE_2048) {
|
|
|
|
if (src->ReadSectors2048(sector, count, data))
|
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
if (src->ReadSectors2352(sector, count, data))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2010-04-24 21:37:39 +00:00
|
|
|
|
2016-09-19 17:24:54 +00:00
|
|
|
cdvdCacheUpdate(sector, mode, data);
|
|
|
|
}
|
|
|
|
LeaveCriticalSection(&CacheMutex);
|
2010-04-24 21:37:39 +00:00
|
|
|
|
2016-09-19 17:24:54 +00:00
|
|
|
s32 offset;
|
2010-04-24 21:37:39 +00:00
|
|
|
|
2016-09-19 17:24:54 +00:00
|
|
|
if (mode == CDVD_MODE_2048) {
|
|
|
|
offset = 2048 * (first - sector);
|
|
|
|
memcpy(buffer, data + offset, 2048);
|
|
|
|
return 0;
|
|
|
|
}
|
2010-04-25 00:31:27 +00:00
|
|
|
|
2016-09-19 17:24:54 +00:00
|
|
|
offset = 2352 * (first - sector);
|
|
|
|
s8 *bfr = data + offset;
|
2010-04-24 21:37:39 +00:00
|
|
|
|
2016-09-19 17:24:54 +00:00
|
|
|
switch (mode) {
|
|
|
|
case CDVD_MODE_2328:
|
|
|
|
memcpy(buffer, bfr + 24, 2328);
|
|
|
|
return 0;
|
|
|
|
case CDVD_MODE_2340:
|
|
|
|
memcpy(buffer, bfr + 12, 2340);
|
|
|
|
return 0;
|
|
|
|
default:
|
|
|
|
memcpy(buffer, bfr + 12, 2352);
|
|
|
|
return 0;
|
|
|
|
}
|
2010-04-24 21:37:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
s32 cdvdGetMediaType()
|
|
|
|
{
|
2016-09-19 17:24:54 +00:00
|
|
|
return src->GetMediaType();
|
2010-04-24 21:37:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
s32 cdvdRefreshData()
|
|
|
|
{
|
2016-09-19 17:24:54 +00:00
|
|
|
char *diskTypeName = "Unknown";
|
|
|
|
|
|
|
|
//read TOC from device
|
|
|
|
cdvdParseTOC();
|
|
|
|
|
|
|
|
if ((etrack == 0) || (strack > etrack)) {
|
|
|
|
curDiskType = CDVD_TYPE_NODISC;
|
|
|
|
} else {
|
|
|
|
s32 mt = cdvdGetMediaType();
|
|
|
|
|
|
|
|
if (mt < 0)
|
|
|
|
curDiskType = CDVD_TYPE_DETCTCD;
|
|
|
|
else if (mt == 0)
|
|
|
|
curDiskType = CDVD_TYPE_DETCTDVDS;
|
|
|
|
else
|
|
|
|
curDiskType = CDVD_TYPE_DETCTDVDD;
|
|
|
|
}
|
|
|
|
|
|
|
|
curTrayStatus = CDVD_TRAY_CLOSE;
|
|
|
|
|
|
|
|
switch (curDiskType) {
|
|
|
|
case CDVD_TYPE_DETCTDVDD:
|
|
|
|
diskTypeName = "Double-Layer DVD";
|
|
|
|
break;
|
|
|
|
case CDVD_TYPE_DETCTDVDS:
|
|
|
|
diskTypeName = "Single-Layer DVD";
|
|
|
|
break;
|
|
|
|
case CDVD_TYPE_DETCTCD:
|
|
|
|
diskTypeName = "CD-ROM";
|
|
|
|
break;
|
|
|
|
case CDVD_TYPE_NODISC:
|
|
|
|
diskTypeName = "No Disc";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
printf(" * CDVD: Disk Type: %s\n", diskTypeName);
|
|
|
|
|
|
|
|
cdvdCacheReset();
|
|
|
|
|
|
|
|
return 0;
|
2010-04-24 21:37:39 +00:00
|
|
|
}
|