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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 401 - (hide annotations) (download)
Fri Feb 25 17:31:09 2011 UTC (10 years, 1 month ago) by william
File size: 15004 byte(s)
Auto Commited Import of: pcsx2-0.9.7-DEBUG (upstream: v0.9.7.4358 local: v0.9.7.313-latest) in ./trunk
1 william 31 /* 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 william 273 #include "IsoFileFormats.h"
20 william 31
21     #include <errno.h>
22    
23 william 273 static const uint BlockDumpHeaderSize = 16;
24 william 31
25 william 273 bool isoFile::detect()
26 william 31 {
27     u8 buf[2456];
28     u8* pbuf;
29    
30 william 273 ReadBlock(buf, 16);
31 william 31
32 william 273 pbuf = buf + 24;
33 william 31
34     if (strncmp((char*)(pbuf+1), "CD001", 5)) return false; // Not ISO 9660 compliant
35    
36     if (*(u16*)(pbuf+166) == 2048)
37 william 273 m_type = ISOTYPE_CD;
38 william 31 else
39 william 273 m_type = ISOTYPE_DVD;
40 william 31
41     return true; // We can deal with this.
42     }
43    
44 william 273 void isoFile::_ReadDtable()
45 william 31 {
46 william 273 _IsoPart& headpart( m_parts[0] );
47 william 31
48 william 273 wxFileOffset flen = headpart.handle->GetLength();
49     static const wxFileOffset datalen = flen - BlockDumpHeaderSize;
50     pxAssert( (datalen % (m_blocksize + 4)) == 0);
51 william 31
52 william 273 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 william 31 {
59 william 273 headpart.Read(m_dtable[i]);
60     headpart.Seek(m_blocksize, wxFromCurrent);
61 william 31 }
62     }
63    
64 william 273 bool isoFile::tryIsoType(u32 _size, s32 _offset, s32 _blockofs)
65 william 31 {
66 william 273 m_blocksize = _size;
67     m_offset = _offset;
68     m_blockofs = _blockofs;
69 william 31
70 william 273 return detect();
71 william 31 }
72    
73     // based on florin's CDVDbin detection code :)
74 william 273 // 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 william 31 {
80     char buf[32];
81    
82 william 273 m_type = ISOTYPE_ILLEGAL;
83 william 31
84 william 273 _IsoPart& headpart( m_parts[0] );
85 william 31
86 william 273 headpart.Seek( 0 );
87     headpart.Read( buf, 4 );
88 william 31
89     if (strncmp(buf, "BDV2", 4) == 0)
90     {
91 william 273 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 william 31 }
103    
104 william 273 // 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 william 31
108 william 273 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 william 31 return true;
133     }
134    
135 william 273 // Generates format header information for blockdumps.
136     void isoFile::WriteFormat(int _blockofs, uint _blocksize, uint _blocks)
137 william 31 {
138 william 273 m_blocksize = _blocksize;
139     m_blocks = _blocks;
140     m_blockofs = _blockofs;
141 william 31
142 william 273 Console.WriteLn("blockoffset = %d", m_blockofs);
143     Console.WriteLn("blocksize = %u", m_blocksize);
144     Console.WriteLn("blocks = %u", m_blocks);
145 william 31
146 william 273 if (m_flags & ISOFLAGS_BLOCKDUMP_V2)
147 william 31 {
148 william 273 outWrite("BDV2", 4);
149     outWrite(m_blocksize);
150     outWrite(m_blocks);
151     outWrite(m_blockofs);
152 william 31 }
153 william 273 }
154 william 31
155 william 273 void isoFile::_ReadBlockD(u8* dst, uint lsn)
156     {
157     _IsoPart& headpart( m_parts[0] );
158 william 31
159 william 273 // Console.WriteLn("_isoReadBlockD %u, blocksize=%u, blockofs=%u\n", lsn, iso->blocksize, iso->blockofs);
160 william 31
161 william 273 memset(dst, 0, m_blockofs);
162     for (int i = 0; i < m_dtablesize; ++i)
163 william 31 {
164 william 273 if (m_dtable[i] != lsn) continue;
165 william 31
166 william 273 // 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 william 31
169 william 273 #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 william 31
178 william 273 m_parts[0].Read( dst + m_blockofs, m_blocksize );
179     return;
180     }
181 william 31
182 william 273 Console.WriteLn("Block %u not found in dump", lsn);
183     }
184 william 31
185 william 273 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 william 31
190 william 273 uint i;
191     for (i = 0; i < m_numparts-1; ++i)
192 william 31 {
193 william 273 // 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 william 31 }
197    
198 william 273 wxFileOffset ofs = (wxFileOffset)(lsn - m_parts[i].slsn) * m_blocksize + m_offset;
199 william 62
200 william 273 // Console.WriteLn("_isoReadBlock %u, blocksize=%u, blockofs=%u\n", lsn, iso->blocksize, iso->blockofs);
201 william 31
202 william 273 memset(dst, 0, m_blockofs);
203     m_parts[i].Seek(ofs);
204     m_parts[i].Read(dst + m_blockofs, m_blocksize);
205 william 31 }
206    
207 william 273 void isoFile::ReadBlock(u8* dst, uint lsn)
208 william 31 {
209 william 273 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 william 31
214 william 273 pxAssertDev(false, msg);
215     Console.Error(msg);
216 william 31
217 william 273 // [TODO] : Throw exception?
218     // Typically an error like this is bad; indicating an invalid dump or corrupted
219     // iso file.
220 william 31
221 william 273 return;
222     }
223 william 31
224 william 273 if (m_flags == ISOFLAGS_BLOCKDUMP_V2)
225     _ReadBlockD(dst, lsn);
226     else
227     _ReadBlock(dst, lsn);
228    
229     if (m_type == ISOTYPE_CD)
230 william 31 {
231 william 273 lsn_to_msf(dst + 12, lsn);
232     dst[15] = 2;
233 william 31 }
234 william 273 }
235 william 31
236 william 273 void isoFile::_WriteBlock(const u8* src, uint lsn)
237     {
238     wxFileOffset ofs = (wxFileOffset)lsn * m_blocksize + m_offset;
239 william 31
240 william 273 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 william 280 for (int i=0; i<m_dtablesize; ++i)
248 william 31 {
249 william 273 if (m_dtable[i] == lsn) return;
250 william 31 }
251    
252 william 273 m_dtable[m_dtablesize++] = lsn;
253     outWrite<u32>( lsn );
254     outWrite( src + m_blockofs, m_blocksize );
255     }
256 william 31
257 william 273 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 william 31 }
264    
265 william 273 // --------------------------------------------------------------------------------------
266     // IsoFile (implementations) : Init / Open / Create
267     // --------------------------------------------------------------------------------------
268    
269     isoFile::isoFile()
270 william 31 {
271 william 273 _init();
272     }
273 william 31
274 william 273 isoFile::~isoFile() throw()
275     {
276     Close();
277     }
278 william 31
279 william 273 void isoFile::_init()
280     {
281     m_type = ISOTYPE_ILLEGAL;
282     m_flags = 0;
283 william 31
284 william 273 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 william 31 }
292    
293 william 273 // 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 william 31 {
297 william 273 wxFileName part1 = filename;
298     part1.SetExt( ext.Lower() );
299 william 31
300 william 273 if (part1.FileExists()) return true;
301     if (!wxFileName::IsCaseSensitive()) return false;
302 william 31
303 william 273 part1.SetExt( ext.Upper() );
304     return part1.FileExists();
305     }
306 william 31
307 william 273 void pxStream_OpenCheck( const wxStreamBase& stream, const wxString& fname, const wxString& mode )
308     {
309     if (stream.IsOk()) return;
310 william 31
311 william 273 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 william 31 }
315    
316 william 273 // 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 william 31 {
323 william 273 wxFileName nameparts( m_filename );
324     wxString curext( nameparts.GetExt() );
325     wxChar prefixch = wxTolower(curext[0]);
326 william 31
327 william 273 // 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 william 31
333 william 273 uint i = 0;
334    
335     if ((curext.Length() == 3) && (curext[1] == L'0') && (curext[2] == L'0'))
336 william 31 {
337 william 273 // First file is an OO, so skip 0 in the loop below:
338     i = 1;
339     }
340 william 31
341 william 273 FastFormatUnicode extbuf;
342 william 31
343 william 273 extbuf.Write( L"%c%02u", prefixch, i );
344     nameparts.SetExt( extbuf );
345     if (!pxFileExists_WithExt(nameparts, extbuf)) return;
346 william 31
347 william 273 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 william 31 }
369    
370 william 273 //Console.WriteLn( Color_Blue, "isoFile: multi-part ISO loaded (%u parts found)", m_numparts );
371 william 31 }
372    
373 william 273 // 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 william 31 {
382 william 273 pxAssertMsg( !m_parts[0].handle, "Warning! isoFile::Test is about to clobber whatever existing iso bound to this isoFile object!" );
383 william 31
384 william 273 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 william 283 throw Exception::BadStream()
415     .SetUserMsg(L"Unrecognized ISO image file format")
416     .SetDiagMsg(_("ISO mounting failed: PCSX2 is unable to identify the ISO image type."));
417 william 273
418     if (!(m_flags & ISOFLAGS_BLOCKDUMP_V2))
419 william 31 {
420 william 273 m_blocks = m_parts[0].CalculateBlocks( 0, m_blocksize );
421     FindParts();
422     if (m_numparts > 1)
423 william 31 {
424 william 273 Console.WriteLn( Color_Blue, "isoFile: multi-part ISO detected. %u parts found." );
425 william 31 }
426     }
427    
428 william 273 const char* isotypename = NULL;
429     switch(m_type)
430     {
431     case ISOTYPE_CD: isotypename = "CD"; break;
432     case ISOTYPE_DVD: isotypename = "DVD"; break;
433     case ISOTYPE_AUDIO: isotypename = "Audio CD"; break;
434 william 31
435 william 273 case ISOTYPE_DVDDL:
436     isotypename = "DVD9 (dual-layer)";
437     break;
438 william 31
439 william 273 case ISOTYPE_ILLEGAL:
440     default:
441     isotypename = "illegal media";
442     break;
443     }
444 william 31
445 william 273 Console.WriteLn(Color_StrongBlue, L"isoFile open ok: %s", m_filename.c_str());
446 william 31
447 william 273 ConsoleIndentScope indent;
448     Console.WriteLn("Image type = %s", isotypename);
449     Console.WriteLn("Fileparts = %u", m_numparts);
450     DevCon.WriteLn ("blocks = %u", m_blocks);
451     DevCon.WriteLn ("offset = %d", m_offset);
452     DevCon.WriteLn ("blocksize = %u", m_blocksize);
453     DevCon.WriteLn ("blockoffset = %d", m_blockofs);
454     }
455 william 31
456 william 273 void isoFile::Create(const wxString& filename, int flags)
457     {
458     Close();
459     m_filename = filename;
460    
461     m_flags = flags;
462     m_offset = 0;
463     m_blockofs = 24;
464     m_blocksize = 2048;
465    
466     m_outstream = new wxFileOutputStream( m_filename );
467     pxStream_OpenCheck( *m_outstream, m_filename, L"writing" );
468    
469     Console.WriteLn("isoFile create ok: %s ", m_filename.c_str());
470 william 31 }
471    
472 william 273 void isoFile::Close()
473 william 31 {
474 william 273 for (uint i=0; i<MaxSplits; ++i)
475     m_parts[i].handle.Delete();
476 william 31
477 william 273 m_dtable.Delete();
478 william 31
479 william 273 _init();
480     }
481 william 31
482 william 273 bool isoFile::IsOpened() const
483     {
484     return m_parts[0].handle && m_parts[0].handle->IsOk();
485     }
486 william 31
487 william 273 void isoFile::outWrite( const void* src, size_t size )
488     {
489     m_outstream->Write(src, size);
490     if(m_outstream->GetLastError() == wxSTREAM_WRITE_ERROR)
491 william 31 {
492 william 273 int err = errno;
493     if (!err)
494     throw Exception::BadStream(m_filename).SetDiagMsg(pxsFmt(L"An error occurred while writing %u bytes to file", size));
495    
496     ScopedExcept ex(Exception::FromErrno(m_filename, err));
497     ex->SetDiagMsg( pxsFmt(L"An error occurred while writing %u bytes to file: %s", size, ex->DiagMsg().c_str()) );
498     ex->Rethrow();
499 william 31 }
500     }
501    
502 william 273 // --------------------------------------------------------------------------------------
503     // _IsoPart
504     // --------------------------------------------------------------------------------------
505 william 31
506 william 273 _IsoPart::~_IsoPart() throw()
507 william 31 {
508     }
509    
510 william 273 void _IsoPart::Read( void* dest, size_t size )
511 william 31 {
512 william 273 handle->Read(dest, size);
513     if (handle->GetLastError() == wxSTREAM_READ_ERROR)
514     {
515     int err = errno;
516     if (!err)
517     throw Exception::BadStream(filename).SetDiagMsg(L"Cannot read from file (bad file handle?)");
518 william 31
519 william 273 ScopedExcept ex(Exception::FromErrno(filename, err));
520     ex->SetDiagMsg( L"cannot read from file: " + ex->DiagMsg() );
521     ex->Rethrow();
522     }
523 william 31
524 william 273 // IMPORTANT! The underlying file/source Eof() stuff is not really reliable, so we
525     // must always use the explicit check against the number of bytes read to determine
526     // end-of-stream conditions.
527 william 31
528 william 401 // Throwing exceptions here ends emulation.
529     // We should let the game decide what to do with missing data though.
530 william 273 if ((size_t)handle->LastRead() < size)
531 william 401 Console.Warning( "ISO read problem (Bad game rip?)" );
532     //throw Exception::EndOfStream( filename );
533 william 31 }
534    
535 william 273 void _IsoPart::Seek(wxFileOffset pos, wxSeekMode mode)
536 william 31 {
537 william 273 handle->SeekI(pos, mode);
538 william 31 }
539    
540 william 273 void _IsoPart::SeekEnd(wxFileOffset pos)
541 william 31 {
542 william 273 handle->SeekI(pos, wxFromEnd);
543 william 31 }
544    
545 william 273 wxFileOffset _IsoPart::Tell() const
546     {
547     return handle->TellI();
548     }
549    
550     // returns the number of blocks contained in this part of the iso image.
551     uint _IsoPart::CalculateBlocks( uint startBlock, uint blocksize )
552     {
553     wxFileOffset partsize = handle->GetLength();
554    
555     slsn = startBlock;
556     uint numBlocks = partsize / blocksize;
557     elsn = startBlock + numBlocks - 1;
558     return numBlocks;
559     }

  ViewVC Help
Powered by ViewVC 1.1.22