/[pcsx2_0.9.7]/trunk/pcsx2/CDVD/IsoFileFormats.cpp
ViewVC logotype

Contents of /trunk/pcsx2/CDVD/IsoFileFormats.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 280 - (show annotations) (download)
Thu Dec 23 12:02:12 2010 UTC (9 years, 2 months ago) by william
File size: 14734 byte(s)
re-commit (had local access denied errors when committing)
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 #include "IsoFileFormats.h"
20
21 #include <errno.h>
22
23 static const uint BlockDumpHeaderSize = 16;
24
25 bool isoFile::detect()
26 {
27 u8 buf[2456];
28 u8* pbuf;
29
30 ReadBlock(buf, 16);
31
32 pbuf = buf + 24;
33
34 if (strncmp((char*)(pbuf+1), "CD001", 5)) return false; // Not ISO 9660 compliant
35
36 if (*(u16*)(pbuf+166) == 2048)
37 m_type = ISOTYPE_CD;
38 else
39 m_type = ISOTYPE_DVD;
40
41 return true; // We can deal with this.
42 }
43
44 void isoFile::_ReadDtable()
45 {
46 _IsoPart& headpart( m_parts[0] );
47
48 wxFileOffset flen = headpart.handle->GetLength();
49 static const wxFileOffset datalen = flen - BlockDumpHeaderSize;
50 pxAssert( (datalen % (m_blocksize + 4)) == 0);
51
52 m_dtablesize = datalen / (m_blocksize + 4);
53 m_dtable = new u32[m_dtablesize];
54
55 headpart.Seek(BlockDumpHeaderSize);
56
57 for (int i=0; i < m_dtablesize; ++i)
58 {
59 headpart.Read(m_dtable[i]);
60 headpart.Seek(m_blocksize, wxFromCurrent);
61 }
62 }
63
64 bool isoFile::tryIsoType(u32 _size, s32 _offset, s32 _blockofs)
65 {
66 m_blocksize = _size;
67 m_offset = _offset;
68 m_blockofs = _blockofs;
69
70 return detect();
71 }
72
73 // based on florin's CDVDbin detection code :)
74 // Parameter:
75 //
76 //
77 // Returns true if the image is valid/known/supported, or false if not (type == ISOTYPE_ILLEGAL).
78 bool isoFile::Detect( bool readType )
79 {
80 char buf[32];
81
82 m_type = ISOTYPE_ILLEGAL;
83
84 _IsoPart& headpart( m_parts[0] );
85
86 headpart.Seek( 0 );
87 headpart.Read( buf, 4 );
88
89 if (strncmp(buf, "BDV2", 4) == 0)
90 {
91 m_flags = ISOFLAGS_BLOCKDUMP_V2;
92 headpart.Read(m_blocksize);
93 headpart.Read(m_blocks);
94 headpart.Read(m_blockofs);
95
96 if (readType)
97 {
98 _ReadDtable();
99 return detect();
100 }
101 return true;
102 }
103
104 // First sanity check: no sane CD image has less than 16 sectors, since that's what
105 // we need simply to contain a TOC. So if the file size is not large enough to
106 // accommodate that, it is NOT a CD image --->
107
108 wxFileOffset size = headpart.handle->GetLength();
109
110 if (size < (2048 * 16)) return false;
111
112 m_blocks = 16;
113
114 if (tryIsoType(2048, 0, 24)) return true; // ISO 2048
115 if (tryIsoType(2336, 0, 16)) return true; // RAW 2336
116 if (tryIsoType(2352, 0, 0)) return true; // RAW 2352
117 if (tryIsoType(2448, 0, 0)) return true; // RAWQ 2448
118
119 if (tryIsoType(2048, 150 * 2048, 24)) return true; // NERO ISO 2048
120 if (tryIsoType(2352, 150 * 2048, 0)) return true; // NERO RAW 2352
121 if (tryIsoType(2448, 150 * 2048, 0)) return true; // NERO RAWQ 2448
122
123 if (tryIsoType(2048, -8, 24)) return true; // ISO 2048
124 if (tryIsoType(2352, -8, 0)) return true; // RAW 2352
125 if (tryIsoType(2448, -8, 0)) return true; // RAWQ 2448
126
127 m_offset = 0;
128 m_blocksize = CD_FRAMESIZE_RAW;
129 m_blockofs = 0;
130 m_type = ISOTYPE_AUDIO;
131
132 return true;
133 }
134
135 // Generates format header information for blockdumps.
136 void isoFile::WriteFormat(int _blockofs, uint _blocksize, uint _blocks)
137 {
138 m_blocksize = _blocksize;
139 m_blocks = _blocks;
140 m_blockofs = _blockofs;
141
142 Console.WriteLn("blockoffset = %d", m_blockofs);
143 Console.WriteLn("blocksize = %u", m_blocksize);
144 Console.WriteLn("blocks = %u", m_blocks);
145
146 if (m_flags & ISOFLAGS_BLOCKDUMP_V2)
147 {
148 outWrite("BDV2", 4);
149 outWrite(m_blocksize);
150 outWrite(m_blocks);
151 outWrite(m_blockofs);
152 }
153 }
154
155 void isoFile::_ReadBlockD(u8* dst, uint lsn)
156 {
157 _IsoPart& headpart( m_parts[0] );
158
159 // Console.WriteLn("_isoReadBlockD %u, blocksize=%u, blockofs=%u\n", lsn, iso->blocksize, iso->blockofs);
160
161 memset(dst, 0, m_blockofs);
162 for (int i = 0; i < m_dtablesize; ++i)
163 {
164 if (m_dtable[i] != lsn) continue;
165
166 // We store the LSN (u32) along with each block inside of blockdumps, so the
167 // seek position ends up being based on (m_blocksize + 4) instead of just m_blocksize.
168
169 #ifdef PCSX2_DEBUG
170 u32 check_lsn;
171 headpart.Seek( BlockDumpHeaderSize + (i * (m_blocksize + 4)) );
172 m_parts[0].Read( check_lsn );
173 pxAssert( check_lsn == lsn );
174 #else
175 headpart.Seek( BlockDumpHeaderSize + (i * (m_blocksize + 4)) + 4 );
176 #endif
177
178 m_parts[0].Read( dst + m_blockofs, m_blocksize );
179 return;
180 }
181
182 Console.WriteLn("Block %u not found in dump", lsn);
183 }
184
185 void isoFile::_ReadBlock(u8* dst, uint lsn)
186 {
187 pxAssumeMsg(lsn <= m_blocks, "Invalid lsn passed into isoFile::_ReadBlock.");
188 pxAssumeMsg(m_numparts, "Invalid isoFile object state; an iso file needs at least one part!");
189
190 uint i;
191 for (i = 0; i < m_numparts-1; ++i)
192 {
193 // lsn indexes should always go in order; use an assertion just to be sure:
194 pxAssume(lsn >= m_parts[i].slsn);
195 if (lsn <= m_parts[i].elsn) break;
196 }
197
198 wxFileOffset ofs = (wxFileOffset)(lsn - m_parts[i].slsn) * m_blocksize + m_offset;
199
200 // Console.WriteLn("_isoReadBlock %u, blocksize=%u, blockofs=%u\n", lsn, iso->blocksize, iso->blockofs);
201
202 memset(dst, 0, m_blockofs);
203 m_parts[i].Seek(ofs);
204 m_parts[i].Read(dst + m_blockofs, m_blocksize);
205 }
206
207 void isoFile::ReadBlock(u8* dst, uint lsn)
208 {
209 if (lsn > m_blocks)
210 {
211 FastFormatUnicode msg;
212 msg.Write("isoFile error: Block index is past the end of file! (%u > %u).", lsn, m_blocks);
213
214 pxAssertDev(false, msg);
215 Console.Error(msg);
216
217 // [TODO] : Throw exception?
218 // Typically an error like this is bad; indicating an invalid dump or corrupted
219 // iso file.
220
221 return;
222 }
223
224 if (m_flags == ISOFLAGS_BLOCKDUMP_V2)
225 _ReadBlockD(dst, lsn);
226 else
227 _ReadBlock(dst, lsn);
228
229 if (m_type == ISOTYPE_CD)
230 {
231 lsn_to_msf(dst + 12, lsn);
232 dst[15] = 2;
233 }
234 }
235
236 void isoFile::_WriteBlock(const u8* src, uint lsn)
237 {
238 wxFileOffset ofs = (wxFileOffset)lsn * m_blocksize + m_offset;
239
240 m_outstream->SeekO( ofs );
241 outWrite( src + m_blockofs, m_blocksize );
242 }
243
244 void isoFile::_WriteBlockD(const u8* src, uint lsn)
245 {
246 // Find and ignore blocks that have already been dumped:
247 for (int i=0; i<m_dtablesize; ++i)
248 {
249 if (m_dtable[i] == lsn) return;
250 }
251
252 m_dtable[m_dtablesize++] = lsn;
253 outWrite<u32>( lsn );
254 outWrite( src + m_blockofs, m_blocksize );
255 }
256
257 void isoFile::WriteBlock(const u8* src, uint lsn)
258 {
259 if (m_flags == ISOFLAGS_BLOCKDUMP_V2)
260 _WriteBlockD(src, lsn);
261 else
262 _WriteBlock(src, lsn);
263 }
264
265 // --------------------------------------------------------------------------------------
266 // IsoFile (implementations) : Init / Open / Create
267 // --------------------------------------------------------------------------------------
268
269 isoFile::isoFile()
270 {
271 _init();
272 }
273
274 isoFile::~isoFile() throw()
275 {
276 Close();
277 }
278
279 void isoFile::_init()
280 {
281 m_type = ISOTYPE_ILLEGAL;
282 m_flags = 0;
283
284 m_offset = 0;
285 m_blockofs = 0;
286 m_blocksize = 0;
287 m_blocks = 0;
288
289 m_dtable = 0;
290 m_dtablesize = 0;
291 }
292
293 // Tests for a filename extension in both upper and lower case, if the filesystem happens
294 // to be case-sensitive.
295 bool pxFileExists_WithExt( const wxFileName& filename, const wxString& ext )
296 {
297 wxFileName part1 = filename;
298 part1.SetExt( ext.Lower() );
299
300 if (part1.FileExists()) return true;
301 if (!wxFileName::IsCaseSensitive()) return false;
302
303 part1.SetExt( ext.Upper() );
304 return part1.FileExists();
305 }
306
307 void pxStream_OpenCheck( const wxStreamBase& stream, const wxString& fname, const wxString& mode )
308 {
309 if (stream.IsOk()) return;
310
311 ScopedExcept ex(Exception::FromErrno(fname, errno));
312 ex->SetDiagMsg( pxsFmt(L"Unable to open the file for %s: %s", mode.c_str(), ex->DiagMsg().c_str()) );
313 ex->Rethrow();
314 }
315
316 // multi-part ISO support is provided for FAT32 compatibility; so that large 4GB+ isos
317 // can be split into multiple smaller files.
318 //
319 // Returns TRUE if multiple parts for the ISO are found. Returns FALSE if only one
320 // part is found.
321 void isoFile::FindParts()
322 {
323 wxFileName nameparts( m_filename );
324 wxString curext( nameparts.GetExt() );
325 wxChar prefixch = wxTolower(curext[0]);
326
327 // Multi-part rules!
328 // * The first part can either be the proper extension (ISO, MDF, etc) or the numerical
329 // extension (I00, I01, M00, M01, etc).
330 // * Numerical extensions MUST begin at 00 (I00 etc), regardless of if the first part
331 // is proper or numerical.
332
333 uint i = 0;
334
335 if ((curext.Length() == 3) && (curext[1] == L'0') && (curext[2] == L'0'))
336 {
337 // First file is an OO, so skip 0 in the loop below:
338 i = 1;
339 }
340
341 FastFormatUnicode extbuf;
342
343 extbuf.Write( L"%c%02u", prefixch, i );
344 nameparts.SetExt( extbuf );
345 if (!pxFileExists_WithExt(nameparts, extbuf)) return;
346
347 DevCon.WriteLn( Color_Blue, "isoFile: multi-part %s detected...", curext.Upper().c_str() );
348 ConsoleIndentScope indent;
349
350 for (; i < MaxSplits; ++i)
351 {
352 extbuf.Clear();
353 extbuf.Write( L"%c%02u", prefixch, i );
354 if (!pxFileExists_WithExt(nameparts, extbuf)) break;
355
356 _IsoPart& thispart( m_parts[m_numparts] );
357
358 thispart.handle = new wxFileInputStream( nameparts.GetFullPath() );
359 pxStream_OpenCheck( *thispart.handle, nameparts.GetFullPath(), L"reading" );
360
361 m_blocks += thispart.CalculateBlocks( m_blocks, m_blocksize );
362
363 DevCon.WriteLn( Color_Blue, L"\tblocks %u - %u in: %s",
364 thispart.slsn, thispart.elsn,
365 nameparts.GetFullPath().c_str()
366 );
367 ++m_numparts;
368 }
369
370 //Console.WriteLn( Color_Blue, "isoFile: multi-part ISO loaded (%u parts found)", m_numparts );
371 }
372
373 // Tests the specified filename to see if it is a supported ISO type. This function typically
374 // executes faster than isoFile::Open since it does not do the following:
375 // * check for multi-part ISOs. I tests for header info in the main/root ISO only.
376 // * load blockdump indexes.
377 //
378 // Note that this is a member method, and that it will clobber any existing ISO state.
379 // (assertions are generated in debug mode if the object state is not already closed).
380 bool isoFile::Test( const wxString& srcfile )
381 {
382 pxAssertMsg( !m_parts[0].handle, "Warning! isoFile::Test is about to clobber whatever existing iso bound to this isoFile object!" );
383
384 Close();
385 m_filename = srcfile;
386
387 m_parts[0].handle = new wxFileInputStream( m_filename );
388 pxStream_OpenCheck( *m_parts[0].handle, m_filename, L"reading" );
389
390 m_numparts = 1;
391 m_parts[0].slsn = 0;
392
393 // elsn is unknown at this time, but is also unused when m_numparts == 1.
394 // (and if numparts is incremented, elsn will get assigned accordingly)
395
396 return Detect( false );
397 }
398
399 void isoFile::Open( const wxString& srcfile )
400 {
401 Close();
402 m_filename = srcfile;
403
404 m_parts[0].handle = new wxFileInputStream( m_filename );
405 pxStream_OpenCheck( *m_parts[0].handle, m_filename, L"reading" );
406
407 m_numparts = 1;
408 m_parts[0].slsn = 0;
409
410 // elsn is unknown at this time, but is also unused when m_numparts == 1.
411 // (and if numparts is incremented, elsn will get assigned accordingly)
412
413 if (!Detect())
414 throw Exception::BadStream().SetUserMsg(wxLt("Unrecognized ISO file format."));
415
416 if (!(m_flags & ISOFLAGS_BLOCKDUMP_V2))
417 {
418 m_blocks = m_parts[0].CalculateBlocks( 0, m_blocksize );
419 FindParts();
420 if (m_numparts > 1)
421 {
422 Console.WriteLn( Color_Blue, "isoFile: multi-part ISO detected. %u parts found." );
423 }
424 }
425
426 const char* isotypename = NULL;
427 switch(m_type)
428 {
429 case ISOTYPE_CD: isotypename = "CD"; break;
430 case ISOTYPE_DVD: isotypename = "DVD"; break;
431 case ISOTYPE_AUDIO: isotypename = "Audio CD"; break;
432
433 case ISOTYPE_DVDDL:
434 isotypename = "DVD9 (dual-layer)";
435 break;
436
437 case ISOTYPE_ILLEGAL:
438 default:
439 isotypename = "illegal media";
440 break;
441 }
442
443 Console.WriteLn(Color_StrongBlue, L"isoFile open ok: %s", m_filename.c_str());
444
445 ConsoleIndentScope indent;
446 Console.WriteLn("Image type = %s", isotypename);
447 Console.WriteLn("Fileparts = %u", m_numparts);
448 DevCon.WriteLn ("blocks = %u", m_blocks);
449 DevCon.WriteLn ("offset = %d", m_offset);
450 DevCon.WriteLn ("blocksize = %u", m_blocksize);
451 DevCon.WriteLn ("blockoffset = %d", m_blockofs);
452 }
453
454 void isoFile::Create(const wxString& filename, int flags)
455 {
456 Close();
457 m_filename = filename;
458
459 m_flags = flags;
460 m_offset = 0;
461 m_blockofs = 24;
462 m_blocksize = 2048;
463
464 m_outstream = new wxFileOutputStream( m_filename );
465 pxStream_OpenCheck( *m_outstream, m_filename, L"writing" );
466
467 Console.WriteLn("isoFile create ok: %s ", m_filename.c_str());
468 }
469
470 void isoFile::Close()
471 {
472 for (uint i=0; i<MaxSplits; ++i)
473 m_parts[i].handle.Delete();
474
475 m_dtable.Delete();
476
477 _init();
478 }
479
480 bool isoFile::IsOpened() const
481 {
482 return m_parts[0].handle && m_parts[0].handle->IsOk();
483 }
484
485 void isoFile::outWrite( const void* src, size_t size )
486 {
487 m_outstream->Write(src, size);
488 if(m_outstream->GetLastError() == wxSTREAM_WRITE_ERROR)
489 {
490 int err = errno;
491 if (!err)
492 throw Exception::BadStream(m_filename).SetDiagMsg(pxsFmt(L"An error occurred while writing %u bytes to file", size));
493
494 ScopedExcept ex(Exception::FromErrno(m_filename, err));
495 ex->SetDiagMsg( pxsFmt(L"An error occurred while writing %u bytes to file: %s", size, ex->DiagMsg().c_str()) );
496 ex->Rethrow();
497 }
498 }
499
500 // --------------------------------------------------------------------------------------
501 // _IsoPart
502 // --------------------------------------------------------------------------------------
503
504 _IsoPart::~_IsoPart() throw()
505 {
506 }
507
508 void _IsoPart::Read( void* dest, size_t size )
509 {
510 handle->Read(dest, size);
511 if (handle->GetLastError() == wxSTREAM_READ_ERROR)
512 {
513 int err = errno;
514 if (!err)
515 throw Exception::BadStream(filename).SetDiagMsg(L"Cannot read from file (bad file handle?)");
516
517 ScopedExcept ex(Exception::FromErrno(filename, err));
518 ex->SetDiagMsg( L"cannot read from file: " + ex->DiagMsg() );
519 ex->Rethrow();
520 }
521
522 // IMPORTANT! The underlying file/source Eof() stuff is not really reliable, so we
523 // must always use the explicit check against the number of bytes read to determine
524 // end-of-stream conditions.
525
526 if ((size_t)handle->LastRead() < size)
527 throw Exception::EndOfStream( filename );
528 }
529
530 void _IsoPart::Seek(wxFileOffset pos, wxSeekMode mode)
531 {
532 handle->SeekI(pos, mode);
533 }
534
535 void _IsoPart::SeekEnd(wxFileOffset pos)
536 {
537 handle->SeekI(pos, wxFromEnd);
538 }
539
540 wxFileOffset _IsoPart::Tell() const
541 {
542 return handle->TellI();
543 }
544
545 // returns the number of blocks contained in this part of the iso image.
546 uint _IsoPart::CalculateBlocks( uint startBlock, uint blocksize )
547 {
548 wxFileOffset partsize = handle->GetLength();
549
550 slsn = startBlock;
551 uint numBlocks = partsize / blocksize;
552 elsn = startBlock + numBlocks - 1;
553 return numBlocks;
554 }

  ViewVC Help
Powered by ViewVC 1.1.22