1 |
/* PCSX2 - PS2 Emulator for PCs |
2 |
* Copyright (C) 2002-2010 PCSX2 Dev Team |
3 |
* |
4 |
* PCSX2 is free software: you can redistribute it and/or modify it under the terms |
5 |
* of the GNU Lesser General Public License as published by the Free Software Found- |
6 |
* ation, either version 3 of the License, or (at your option) any later version. |
7 |
* |
8 |
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; |
9 |
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR |
10 |
* PURPOSE. See the GNU General Public License for more details. |
11 |
* |
12 |
* You should have received a copy of the GNU General Public License along with PCSX2. |
13 |
* If not, see <http://www.gnu.org/licenses/>. |
14 |
*/ |
15 |
|
16 |
|
17 |
#include "PrecompiledHeader.h" |
18 |
#include "IopCommon.h" |
19 |
|
20 |
#define ENABLE_TIMESTAMPS |
21 |
|
22 |
#ifdef _WIN32 |
23 |
# include <wx/msw/wrapwin.h> |
24 |
#endif |
25 |
|
26 |
#include <ctype.h> |
27 |
#include <time.h> |
28 |
#include <wx/datetime.h> |
29 |
#include <exception> |
30 |
|
31 |
#include "IsoFS/IsoFS.h" |
32 |
#include "IsoFS/IsoFSCDVD.h" |
33 |
#include "CDVDisoReader.h" |
34 |
#include "Utilities/ScopedPtr.h" |
35 |
|
36 |
const wxChar* CDVD_SourceLabels[] = |
37 |
{ |
38 |
L"Iso", |
39 |
L"Plugin", |
40 |
L"NoDisc", |
41 |
NULL |
42 |
}; |
43 |
|
44 |
// ---------------------------------------------------------------------------- |
45 |
// diskTypeCached |
46 |
// Internal disc type cache, to reduce the overhead of disc type checks, which are |
47 |
// performed quite liberally by many games (perhaps intended to keep the PS2 DVD |
48 |
// from spinning down due to idle activity?). |
49 |
// Cache is set to -1 for init and when the disc is removed/changed, which invokes |
50 |
// a new DiskTypeCheck. All subsequent checks use the non-negative value here. |
51 |
// |
52 |
static int diskTypeCached = -1; |
53 |
|
54 |
// used to bridge the gap between the old getBuffer api and the new getBuffer2 api. |
55 |
int lastReadSize; |
56 |
int lastLSN; // needed for block dumping |
57 |
|
58 |
// Records last read block length for block dumping |
59 |
//static int plsn = 0; |
60 |
static isoFile blockDumpFile; |
61 |
|
62 |
// Assertion check for CDVD != NULL (in devel and debug builds), because its handier than |
63 |
// relying on DEP exceptions -- and a little more reliable too. |
64 |
static void CheckNullCDVD() |
65 |
{ |
66 |
pxAssertDev( CDVD != NULL, "Invalid CDVD object state (null pointer exception)" ); |
67 |
} |
68 |
|
69 |
////////////////////////////////////////////////////////////////////////////////////////// |
70 |
// Disk Type detection stuff (from cdvdGigaherz) |
71 |
// |
72 |
static int CheckDiskTypeFS(int baseType) |
73 |
{ |
74 |
IsoFSCDVD isofs; |
75 |
IsoDirectory rootdir(isofs); |
76 |
try { |
77 |
IsoFile file( rootdir, L"SYSTEM.CNF;1"); |
78 |
|
79 |
int size = file.getLength(); |
80 |
|
81 |
ScopedArray<char> buffer((int)file.getLength()+1); |
82 |
file.read((u8*)(buffer.GetPtr()),size); |
83 |
buffer[size]='\0'; |
84 |
|
85 |
char* pos = strstr(buffer.GetPtr(), "BOOT2"); |
86 |
if (pos == NULL) |
87 |
{ |
88 |
pos = strstr(buffer.GetPtr(), "BOOT"); |
89 |
if (pos == NULL) return CDVD_TYPE_ILLEGAL; |
90 |
return CDVD_TYPE_PSCD; |
91 |
} |
92 |
|
93 |
return (baseType==CDVD_TYPE_DETCTCD) ? CDVD_TYPE_PS2CD : CDVD_TYPE_PS2DVD; |
94 |
} |
95 |
catch( Exception::FileNotFound& ) |
96 |
{ |
97 |
} |
98 |
|
99 |
try { |
100 |
IsoFile file( rootdir, L"PSX.EXE;1"); |
101 |
return CDVD_TYPE_PSCD; |
102 |
} |
103 |
catch( Exception::FileNotFound& ) |
104 |
{ |
105 |
} |
106 |
|
107 |
try { |
108 |
IsoFile file( rootdir, L"VIDEO_TS/VIDEO_TS.IFO;1"); |
109 |
return CDVD_TYPE_DVDV; |
110 |
} |
111 |
catch( Exception::FileNotFound& ) |
112 |
{ |
113 |
} |
114 |
|
115 |
return CDVD_TYPE_ILLEGAL; // << Only for discs which aren't ps2 at all. |
116 |
} |
117 |
|
118 |
static int FindDiskType(int mType) |
119 |
{ |
120 |
int dataTracks = 0; |
121 |
int audioTracks = 0; |
122 |
int iCDType = mType; |
123 |
cdvdTN tn; |
124 |
|
125 |
CDVD->getTN(&tn); |
126 |
|
127 |
if (tn.strack != tn.etrack) // multitrack == CD. |
128 |
{ |
129 |
iCDType = CDVD_TYPE_DETCTCD; |
130 |
} |
131 |
else if (mType < 0) |
132 |
{ |
133 |
static u8 bleh[CD_FRAMESIZE_RAW]; |
134 |
cdvdTD td; |
135 |
|
136 |
CDVD->getTD(0,&td); |
137 |
if (td.lsn > 452849) |
138 |
{ |
139 |
iCDType = CDVD_TYPE_DETCTDVDS; |
140 |
} |
141 |
else |
142 |
{ |
143 |
if (DoCDVDreadSector(bleh, 16, CDVD_MODE_2048) == 0) |
144 |
{ |
145 |
//const cdVolDesc& volDesc = (cdVolDesc&)bleh; |
146 |
//if(volDesc.rootToc.tocSize == 2048) |
147 |
if(*(u16*)(bleh+166) == 2048) |
148 |
iCDType = CDVD_TYPE_DETCTCD; |
149 |
else |
150 |
iCDType = CDVD_TYPE_DETCTDVDS; |
151 |
} |
152 |
} |
153 |
} |
154 |
|
155 |
if (iCDType == CDVD_TYPE_DETCTDVDS) |
156 |
{ |
157 |
s32 dlt = 0; |
158 |
u32 l1s = 0; |
159 |
|
160 |
if(CDVD->getDualInfo(&dlt,&l1s)==0) |
161 |
{ |
162 |
if (dlt > 0) iCDType = CDVD_TYPE_DETCTDVDD; |
163 |
} |
164 |
} |
165 |
|
166 |
switch(iCDType) |
167 |
{ |
168 |
case CDVD_TYPE_DETCTCD: |
169 |
Console.WriteLn(" * CDVD Disk Open: CD, %d tracks (%d to %d):", tn.etrack-tn.strack+1,tn.strack,tn.etrack); |
170 |
break; |
171 |
|
172 |
case CDVD_TYPE_DETCTDVDS: |
173 |
Console.WriteLn(" * CDVD Disk Open: DVD, Single layer or unknown:"); |
174 |
break; |
175 |
|
176 |
case CDVD_TYPE_DETCTDVDD: |
177 |
Console.WriteLn(" * CDVD Disk Open: DVD, Double layer:"); |
178 |
break; |
179 |
} |
180 |
|
181 |
audioTracks = dataTracks = 0; |
182 |
for(int i = tn.strack; i <= tn.etrack; i++) |
183 |
{ |
184 |
cdvdTD td,td2; |
185 |
|
186 |
CDVD->getTD(i,&td); |
187 |
|
188 |
if (tn.etrack > i) |
189 |
CDVD->getTD(i+1,&td2); |
190 |
else |
191 |
CDVD->getTD(0,&td2); |
192 |
|
193 |
int tlength = td2.lsn - td.lsn; |
194 |
|
195 |
if (td.type == CDVD_AUDIO_TRACK) |
196 |
{ |
197 |
audioTracks++; |
198 |
Console.WriteLn(" * * Track %d: Audio (%d sectors)", i,tlength); |
199 |
} |
200 |
else |
201 |
{ |
202 |
dataTracks++; |
203 |
Console.WriteLn(" * * Track %d: Data (Mode %d) (%d sectors)", i,((td.type==CDVD_MODE1_TRACK)?1:2),tlength); |
204 |
} |
205 |
} |
206 |
|
207 |
if (dataTracks > 0) |
208 |
{ |
209 |
iCDType=CheckDiskTypeFS(iCDType); |
210 |
} |
211 |
|
212 |
if (audioTracks > 0) |
213 |
{ |
214 |
switch (iCDType) |
215 |
{ |
216 |
case CDVD_TYPE_PS2CD: |
217 |
iCDType=CDVD_TYPE_PS2CDDA; |
218 |
break; |
219 |
case CDVD_TYPE_PSCD: |
220 |
iCDType=CDVD_TYPE_PSCDDA; |
221 |
break; |
222 |
default: |
223 |
iCDType=CDVD_TYPE_CDDA; |
224 |
break; |
225 |
} |
226 |
} |
227 |
|
228 |
return iCDType; |
229 |
} |
230 |
|
231 |
static void DetectDiskType() |
232 |
{ |
233 |
if (CDVD->getTrayStatus() == CDVD_TRAY_OPEN) |
234 |
{ |
235 |
diskTypeCached = CDVD_TYPE_NODISC; |
236 |
return; |
237 |
} |
238 |
|
239 |
int baseMediaType = CDVD->getDiskType(); |
240 |
int mType = -1; |
241 |
|
242 |
// Paranoid mode: do not trust the plugin's detection system to work correctly. |
243 |
// (.. and there's no reason plugins should be doing their own detection anyway). |
244 |
|
245 |
switch(baseMediaType) |
246 |
{ |
247 |
#if 0 |
248 |
case CDVD_TYPE_CDDA: |
249 |
case CDVD_TYPE_PSCD: |
250 |
case CDVD_TYPE_PS2CD: |
251 |
case CDVD_TYPE_PSCDDA: |
252 |
case CDVD_TYPE_PS2CDDA: |
253 |
mType = CDVD_TYPE_DETCTCD; |
254 |
break; |
255 |
|
256 |
case CDVD_TYPE_DVDV: |
257 |
case CDVD_TYPE_PS2DVD: |
258 |
mType = CDVD_TYPE_DETCTDVDS; |
259 |
break; |
260 |
|
261 |
case CDVD_TYPE_DETCTDVDS: |
262 |
case CDVD_TYPE_DETCTDVDD: |
263 |
case CDVD_TYPE_DETCTCD: |
264 |
mType = baseMediaType; |
265 |
break; |
266 |
#endif |
267 |
|
268 |
case CDVD_TYPE_NODISC: |
269 |
diskTypeCached = CDVD_TYPE_NODISC; |
270 |
return; |
271 |
} |
272 |
|
273 |
diskTypeCached = FindDiskType(mType); |
274 |
} |
275 |
|
276 |
static wxString m_SourceFilename[3]; |
277 |
static CDVD_SourceType m_CurrentSourceType = CDVDsrc_NoDisc; |
278 |
|
279 |
void CDVDsys_SetFile( CDVD_SourceType srctype, const wxString& newfile ) |
280 |
{ |
281 |
m_SourceFilename[srctype] = newfile; |
282 |
} |
283 |
|
284 |
const wxString& CDVDsys_GetFile( CDVD_SourceType srctype ) |
285 |
{ |
286 |
return m_SourceFilename[srctype]; |
287 |
} |
288 |
|
289 |
CDVD_SourceType CDVDsys_GetSourceType() |
290 |
{ |
291 |
return m_CurrentSourceType; |
292 |
} |
293 |
|
294 |
void CDVDsys_ChangeSource( CDVD_SourceType type ) |
295 |
{ |
296 |
GetCorePlugins().Close( PluginId_CDVD ); |
297 |
|
298 |
static bool firstRun = true; |
299 |
if (!firstRun) cdvdCtrlTrayOpen(); |
300 |
firstRun = false; |
301 |
|
302 |
switch( m_CurrentSourceType = type ) |
303 |
{ |
304 |
case CDVDsrc_Iso: |
305 |
CDVD = &CDVDapi_Iso; |
306 |
break; |
307 |
|
308 |
case CDVDsrc_NoDisc: |
309 |
CDVD = &CDVDapi_NoDisc; |
310 |
break; |
311 |
|
312 |
case CDVDsrc_Plugin: |
313 |
CDVD = &CDVDapi_Plugin; |
314 |
break; |
315 |
|
316 |
jNO_DEFAULT; |
317 |
} |
318 |
} |
319 |
|
320 |
bool DoCDVDopen() |
321 |
{ |
322 |
CheckNullCDVD(); |
323 |
|
324 |
// the new disk callback is set on Init also, but just in case the plugin clears it for |
325 |
// some reason on close, we re-send here: |
326 |
CDVD->newDiskCB( cdvdNewDiskCB ); |
327 |
|
328 |
// Win32 Fail: the old CDVD api expects MBCS on Win32 platforms, but generating a MBCS |
329 |
// from unicode is problematic since we need to know the codepage of the text being |
330 |
// converted (which isn't really practical knowledge). A 'best guess' would be the |
331 |
// default codepage of the user's Windows install, but even that will fail and return |
332 |
// question marks if the filename is another language. |
333 |
// Likely Fix: Force new versions of CDVD plugins to expect UTF8 instead. |
334 |
|
335 |
int ret = CDVD->open( !m_SourceFilename[m_CurrentSourceType].IsEmpty() ? |
336 |
m_SourceFilename[m_CurrentSourceType].ToUTF8() : (char*)NULL |
337 |
); |
338 |
|
339 |
if( ret == -1 ) return false; // error! (handled by caller) |
340 |
if( ret == 1 ) throw Exception::CancelEvent(L"User canceled the CDVD plugin's open dialog."); |
341 |
|
342 |
int cdtype = DoCDVDdetectDiskType(); |
343 |
|
344 |
if (!EmuConfig.CdvdDumpBlocks || (cdtype == CDVD_TYPE_NODISC)) |
345 |
{ |
346 |
blockDumpFile.Close(); |
347 |
return true; |
348 |
} |
349 |
|
350 |
// TODO: Add a blockdumps configurable folder, and use that instead of CWD(). |
351 |
|
352 |
// TODO: "Untitled" should use pnach/slus name resolution, slus if no patch, |
353 |
// and finally an "Untitled-[ElfCRC]" if no slus. |
354 |
|
355 |
wxString somepick( Path::GetFilenameWithoutExt( m_SourceFilename[m_CurrentSourceType] ) ); |
356 |
if( somepick.IsEmpty() ) |
357 |
somepick = L"Untitled"; |
358 |
|
359 |
wxString temp( Path::Combine( wxGetCwd(), somepick ) ); |
360 |
|
361 |
#ifdef ENABLE_TIMESTAMPS |
362 |
wxDateTime curtime( wxDateTime::GetTimeNow() ); |
363 |
|
364 |
temp += pxsFmt( L" (%04d-%02d-%02d %02d-%02d-%02d)", |
365 |
curtime.GetYear(), curtime.GetMonth(), curtime.GetDay(), |
366 |
curtime.GetHour(), curtime.GetMinute(), curtime.GetSecond() |
367 |
); |
368 |
#endif |
369 |
temp += L".dump"; |
370 |
|
371 |
cdvdTD td; |
372 |
CDVD->getTD(0, &td); |
373 |
|
374 |
blockDumpFile.Create(temp, ISOFLAGS_BLOCKDUMP_V3); |
375 |
|
376 |
if( blockDumpFile.IsOpened() ) |
377 |
{ |
378 |
int blockofs = 0; |
379 |
uint blocksize = CD_FRAMESIZE_RAW; |
380 |
uint blocks = td.lsn; |
381 |
|
382 |
// hack: Because of limitations of the current cdvd design, we can't query the blocksize |
383 |
// of the underlying media. So lets make a best guess: |
384 |
|
385 |
switch(cdtype) |
386 |
{ |
387 |
case CDVD_TYPE_PS2DVD: |
388 |
case CDVD_TYPE_DVDV: |
389 |
case CDVD_TYPE_DETCTDVDS: |
390 |
case CDVD_TYPE_DETCTDVDD: |
391 |
blocksize = 2048; |
392 |
break; |
393 |
} |
394 |
blockDumpFile.WriteFormat(blockofs, blocksize, blocks); |
395 |
} |
396 |
|
397 |
return true; |
398 |
} |
399 |
|
400 |
void DoCDVDclose() |
401 |
{ |
402 |
CheckNullCDVD(); |
403 |
blockDumpFile.Close(); |
404 |
|
405 |
if( CDVD->close != NULL ) |
406 |
CDVD->close(); |
407 |
|
408 |
DoCDVDresetDiskTypeCache(); |
409 |
} |
410 |
|
411 |
s32 DoCDVDreadSector(u8* buffer, u32 lsn, int mode) |
412 |
{ |
413 |
CheckNullCDVD(); |
414 |
int ret = CDVD->readSector(buffer,lsn,mode); |
415 |
|
416 |
if (ret == 0 && blockDumpFile.IsOpened()) |
417 |
{ |
418 |
blockDumpFile.WriteBlock(buffer, lsn); |
419 |
} |
420 |
|
421 |
return ret; |
422 |
} |
423 |
|
424 |
s32 DoCDVDreadTrack(u32 lsn, int mode) |
425 |
{ |
426 |
CheckNullCDVD(); |
427 |
|
428 |
// TEMP: until all the plugins use the new CDVDgetBuffer style |
429 |
switch (mode) |
430 |
{ |
431 |
case CDVD_MODE_2352: |
432 |
lastReadSize = 2352; |
433 |
break; |
434 |
case CDVD_MODE_2340: |
435 |
lastReadSize = 2340; |
436 |
break; |
437 |
case CDVD_MODE_2328: |
438 |
lastReadSize = 2328; |
439 |
break; |
440 |
case CDVD_MODE_2048: |
441 |
lastReadSize = 2048; |
442 |
break; |
443 |
} |
444 |
|
445 |
//DevCon.Warning("CDVD readTrack(lsn=%d,mode=%d)",params lsn, lastReadSize); |
446 |
lastLSN = lsn; |
447 |
return CDVD->readTrack(lsn,mode); |
448 |
} |
449 |
|
450 |
s32 DoCDVDgetBuffer(u8* buffer) |
451 |
{ |
452 |
CheckNullCDVD(); |
453 |
int ret = CDVD->getBuffer2(buffer); |
454 |
|
455 |
if (ret == 0 && blockDumpFile.IsOpened()) |
456 |
{ |
457 |
blockDumpFile.WriteBlock(buffer, lastLSN); |
458 |
} |
459 |
|
460 |
return ret; |
461 |
} |
462 |
|
463 |
s32 DoCDVDdetectDiskType() |
464 |
{ |
465 |
CheckNullCDVD(); |
466 |
if(diskTypeCached < 0) DetectDiskType(); |
467 |
return diskTypeCached; |
468 |
} |
469 |
|
470 |
void DoCDVDresetDiskTypeCache() |
471 |
{ |
472 |
diskTypeCached = -1; |
473 |
} |
474 |
|
475 |
//////////////////////////////////////////////////////// |
476 |
// |
477 |
// CDVD null interface for Run BIOS menu |
478 |
|
479 |
|
480 |
|
481 |
s32 CALLBACK NODISCopen(const char* pTitle) |
482 |
{ |
483 |
return 0; |
484 |
} |
485 |
|
486 |
void CALLBACK NODISCclose() |
487 |
{ |
488 |
} |
489 |
|
490 |
s32 CALLBACK NODISCreadTrack(u32 lsn, int mode) |
491 |
{ |
492 |
return -1; |
493 |
} |
494 |
|
495 |
// return can be NULL (for async modes) |
496 |
u8* CALLBACK NODISCgetBuffer() |
497 |
{ |
498 |
return NULL; |
499 |
} |
500 |
|
501 |
s32 CALLBACK NODISCreadSubQ(u32 lsn, cdvdSubQ* subq) |
502 |
{ |
503 |
return -1; |
504 |
} |
505 |
|
506 |
s32 CALLBACK NODISCgetTN(cdvdTN *Buffer) |
507 |
{ |
508 |
return -1; |
509 |
} |
510 |
|
511 |
s32 CALLBACK NODISCgetTD(u8 Track, cdvdTD *Buffer) |
512 |
{ |
513 |
return -1; |
514 |
} |
515 |
|
516 |
s32 CALLBACK NODISCgetTOC(void* toc) |
517 |
{ |
518 |
return -1; |
519 |
} |
520 |
|
521 |
s32 CALLBACK NODISCgetDiskType() |
522 |
{ |
523 |
return CDVD_TYPE_NODISC; |
524 |
} |
525 |
|
526 |
s32 CALLBACK NODISCgetTrayStatus() |
527 |
{ |
528 |
return CDVD_TRAY_CLOSE; |
529 |
} |
530 |
|
531 |
s32 CALLBACK NODISCdummyS32() |
532 |
{ |
533 |
return 0; |
534 |
} |
535 |
|
536 |
void CALLBACK NODISCnewDiskCB(void (* /* callback */)()) |
537 |
{ |
538 |
} |
539 |
|
540 |
s32 CALLBACK NODISCreadSector(u8* tempbuffer, u32 lsn, int mode) |
541 |
{ |
542 |
return -1; |
543 |
} |
544 |
|
545 |
s32 CALLBACK NODISCgetBuffer2(u8* buffer) |
546 |
{ |
547 |
return -1; |
548 |
} |
549 |
|
550 |
s32 CALLBACK NODISCgetDualInfo(s32* dualType, u32* _layer1start) |
551 |
{ |
552 |
return -1; |
553 |
} |
554 |
|
555 |
CDVD_API CDVDapi_NoDisc = |
556 |
{ |
557 |
NODISCclose, |
558 |
NODISCopen, |
559 |
NODISCreadTrack, |
560 |
NODISCgetBuffer, |
561 |
NODISCreadSubQ, |
562 |
NODISCgetTN, |
563 |
NODISCgetTD, |
564 |
NODISCgetTOC, |
565 |
NODISCgetDiskType, |
566 |
NODISCgetTrayStatus, |
567 |
NODISCdummyS32, |
568 |
NODISCdummyS32, |
569 |
|
570 |
NODISCnewDiskCB, |
571 |
|
572 |
NODISCreadSector, |
573 |
NODISCgetBuffer2, |
574 |
NODISCgetDualInfo, |
575 |
}; |