mirror of https://github.com/PCSX2/pcsx2.git
366 lines
8.6 KiB
C++
366 lines
8.6 KiB
C++
/* PCSX2 - PS2 Emulator for PCs
|
|
* Copyright (C) 2002-2010 PCSX2 Dev Team
|
|
*
|
|
* 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 "PrecompiledHeader.h"
|
|
#include "IopCommon.h"
|
|
#include "IsoFileFormats.h"
|
|
|
|
#include <errno.h>
|
|
|
|
static const char* nameFromType(int type)
|
|
{
|
|
switch(type)
|
|
{
|
|
case ISOTYPE_CD: return "CD";
|
|
case ISOTYPE_DVD: return "DVD";
|
|
case ISOTYPE_AUDIO: return "Audio CD";
|
|
case ISOTYPE_DVDDL: return "DVD9 (dual-layer)";
|
|
case ISOTYPE_ILLEGAL: return "Illegal media";
|
|
default: return "Unknown or corrupt";
|
|
}
|
|
}
|
|
|
|
int InputIsoFile::ReadSync(u8* dst, uint lsn)
|
|
{
|
|
if (lsn > m_blocks)
|
|
{
|
|
FastFormatUnicode msg;
|
|
msg.Write("isoFile error: Block index is past the end of file! (%u > %u).", lsn, m_blocks);
|
|
|
|
pxAssertDev(false, msg);
|
|
Console.Error(msg.c_str());
|
|
return -1;
|
|
}
|
|
|
|
return m_reader->ReadSync(dst+m_blockofs, lsn, 1);
|
|
}
|
|
|
|
void InputIsoFile::BeginRead2(uint lsn)
|
|
{
|
|
if (lsn > m_blocks)
|
|
{
|
|
FastFormatUnicode msg;
|
|
msg.Write("isoFile error: Block index is past the end of file! (%u > %u).", lsn, m_blocks);
|
|
|
|
pxAssertDev(false, msg);
|
|
Console.Error(msg.c_str());
|
|
|
|
// [TODO] : Throw exception?
|
|
// Typically an error like this is bad; indicating an invalid dump or corrupted
|
|
// iso file.
|
|
|
|
m_current_lsn = -1;
|
|
return;
|
|
}
|
|
|
|
m_current_lsn = lsn;
|
|
|
|
if(lsn >= m_read_lsn && lsn < (m_read_lsn+m_read_count))
|
|
{
|
|
// Already buffered
|
|
return;
|
|
}
|
|
|
|
m_read_lsn = lsn;
|
|
m_read_count = 1;
|
|
|
|
if(ReadUnit > 1)
|
|
{
|
|
//m_read_lsn = lsn - (lsn % ReadUnit);
|
|
|
|
m_read_count = std::min(ReadUnit, m_blocks - m_read_lsn);
|
|
}
|
|
|
|
m_reader->BeginRead(m_readbuffer, m_read_lsn, m_read_count);
|
|
m_read_inprogress = true;
|
|
}
|
|
|
|
int InputIsoFile::FinishRead3(u8* dst, uint mode)
|
|
{
|
|
int _offset = 0;
|
|
int length = 0;
|
|
int ret = 0;
|
|
|
|
if(m_current_lsn < 0)
|
|
return -1;
|
|
|
|
if(m_read_inprogress)
|
|
{
|
|
ret = m_reader->FinishRead();
|
|
m_read_inprogress = false;
|
|
|
|
if(ret < 0)
|
|
return ret;
|
|
}
|
|
|
|
switch (mode)
|
|
{
|
|
case CDVD_MODE_2352:
|
|
_offset = 0;
|
|
length = 2352;
|
|
break;
|
|
case CDVD_MODE_2340:
|
|
_offset = 12;
|
|
length = 2340;
|
|
break;
|
|
case CDVD_MODE_2328:
|
|
_offset = 24;
|
|
length = 2328;
|
|
break;
|
|
case CDVD_MODE_2048:
|
|
_offset = 24;
|
|
length = 2048;
|
|
break;
|
|
}
|
|
|
|
int end1 = m_blockofs + m_blocksize;
|
|
int end2 = _offset + length;
|
|
int end = std::min(end1, end2);
|
|
|
|
int diff = m_blockofs - _offset;
|
|
int ndiff = 0;
|
|
if(diff > 0)
|
|
{
|
|
memset(dst, 0, diff);
|
|
_offset = m_blockofs;
|
|
}
|
|
else
|
|
{
|
|
ndiff = -diff;
|
|
diff = 0;
|
|
}
|
|
|
|
length = end - _offset;
|
|
|
|
uint read_offset = (m_current_lsn - m_read_lsn) * m_blocksize;
|
|
memcpy(dst + diff, m_readbuffer + ndiff + read_offset, length);
|
|
|
|
if (m_type == ISOTYPE_CD && diff >= 12)
|
|
{
|
|
lsn_to_msf(dst + diff - 12, m_current_lsn);
|
|
dst[diff - 9] = 2;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
InputIsoFile::InputIsoFile()
|
|
{
|
|
_init();
|
|
}
|
|
|
|
InputIsoFile::~InputIsoFile() throw()
|
|
{
|
|
Close();
|
|
}
|
|
|
|
void InputIsoFile::_init()
|
|
{
|
|
m_type = ISOTYPE_ILLEGAL;
|
|
m_flags = 0;
|
|
|
|
m_offset = 0;
|
|
m_blockofs = 0;
|
|
m_blocksize = 0;
|
|
m_blocks = 0;
|
|
|
|
m_read_inprogress = false;
|
|
m_read_count = 0;
|
|
ReadUnit = 0;
|
|
m_current_lsn = -1;
|
|
m_read_lsn = -1;
|
|
m_reader = NULL;
|
|
}
|
|
|
|
// Tests the specified filename to see if it is a supported ISO type. This function typically
|
|
// executes faster than IsoFile::Open since it does not do the following:
|
|
// * check for multi-part ISOs. I tests for header info in the main/root ISO only.
|
|
// * load blockdump indexes.
|
|
//
|
|
// Note that this is a member method, and that it will clobber any existing ISO state.
|
|
// (assertions are generated in debug mode if the object state is not already closed).
|
|
bool InputIsoFile::Test( const wxString& srcfile )
|
|
{
|
|
Close();
|
|
m_filename = srcfile;
|
|
|
|
return Open(srcfile, true);
|
|
}
|
|
|
|
bool InputIsoFile::Open( const wxString& srcfile, bool testOnly )
|
|
{
|
|
Close();
|
|
m_filename = srcfile;
|
|
m_reader = NULL;
|
|
|
|
bool isBlockdump = false;
|
|
bool isCompressed = false;
|
|
|
|
// First try using a compressed reader. If it works, go with it.
|
|
m_reader = CompressedFileReader::GetNewReader(m_filename);
|
|
isCompressed = m_reader != NULL;
|
|
|
|
// If it wasn't compressed, let's open it has a FlatFileReader.
|
|
if (!isCompressed)
|
|
{
|
|
// Allow write sharing of the iso based on the ini settings.
|
|
// Mostly useful for romhacking, where the disc is frequently
|
|
// changed and the emulator would block modifications
|
|
m_reader = new FlatFileReader(EmuConfig.CdvdShareWrite);
|
|
}
|
|
|
|
m_reader->Open(m_filename);
|
|
|
|
// It might actually be a blockdump file.
|
|
// Check that before continuing with the FlatFileReader.
|
|
isBlockdump = BlockdumpFileReader::DetectBlockdump(m_reader);
|
|
if (isBlockdump)
|
|
{
|
|
delete m_reader;
|
|
|
|
BlockdumpFileReader *bdr = new BlockdumpFileReader();
|
|
bdr->Open(m_filename);
|
|
|
|
m_blockofs = bdr->GetBlockOffset();
|
|
m_blocksize = bdr->GetBlockSize();
|
|
|
|
m_reader = bdr;
|
|
|
|
ReadUnit = 1;
|
|
}
|
|
|
|
bool detected = Detect();
|
|
|
|
if(testOnly)
|
|
return detected;
|
|
|
|
if (!detected)
|
|
throw Exception::BadStream()
|
|
.SetUserMsg(_("Unrecognized ISO image file format"))
|
|
.SetDiagMsg(L"ISO mounting failed: PCSX2 is unable to identify the ISO image type.");
|
|
|
|
if(!isBlockdump && !isCompressed)
|
|
{
|
|
ReadUnit = MaxReadUnit;
|
|
|
|
m_reader->SetDataOffset(m_offset);
|
|
m_reader->SetBlockSize(m_blocksize);
|
|
|
|
// Returns the original reader if single-part or a Multipart reader otherwise
|
|
AsyncFileReader* m_reader_old = m_reader;
|
|
m_reader = MultipartFileReader::DetectMultipart(m_reader);
|
|
if (m_reader != m_reader_old) // Not the same object the old one need to be deleted
|
|
delete m_reader_old;
|
|
}
|
|
|
|
m_blocks = m_reader->GetBlockCount();
|
|
|
|
Console.WriteLn(Color_StrongBlue, L"isoFile open ok: %s", WX_STR(m_filename));
|
|
|
|
ConsoleIndentScope indent;
|
|
|
|
Console.WriteLn("Image type = %s", nameFromType(m_type));
|
|
//Console.WriteLn("Fileparts = %u", m_numparts); // Pointless print, it's 1 unless it says otherwise above
|
|
DevCon.WriteLn ("blocks = %u", m_blocks);
|
|
DevCon.WriteLn ("offset = %d", m_offset);
|
|
DevCon.WriteLn ("blocksize = %u", m_blocksize);
|
|
DevCon.WriteLn ("blockoffset = %d", m_blockofs);
|
|
|
|
return true;
|
|
}
|
|
|
|
void InputIsoFile::Close()
|
|
{
|
|
delete m_reader;
|
|
m_reader = NULL;
|
|
|
|
_init();
|
|
}
|
|
|
|
bool InputIsoFile::IsOpened() const
|
|
{
|
|
return m_reader != NULL;
|
|
}
|
|
|
|
bool InputIsoFile::tryIsoType(u32 _size, s32 _offset, s32 _blockofs)
|
|
{
|
|
static u8 buf[2456];
|
|
|
|
m_blocksize = _size;
|
|
m_offset = _offset;
|
|
m_blockofs = _blockofs;
|
|
|
|
m_reader->SetDataOffset(_offset);
|
|
m_reader->SetBlockSize(_size);
|
|
|
|
if(ReadSync(buf, 16) < 0)
|
|
return false;
|
|
|
|
if (strncmp((char*)(buf+25), "CD001", 5)) // Not ISO 9660 compliant
|
|
return false;
|
|
|
|
m_type = (*(u16*)(buf+190) == 2048) ? ISOTYPE_CD : ISOTYPE_DVD;
|
|
|
|
return true; // We can deal with this.
|
|
}
|
|
|
|
// based on florin's CDVDbin detection code :)
|
|
// Parameter:
|
|
//
|
|
//
|
|
// Returns true if the image is valid/known/supported, or false if not (type == ISOTYPE_ILLEGAL).
|
|
bool InputIsoFile::Detect( bool readType )
|
|
{
|
|
m_type = ISOTYPE_ILLEGAL;
|
|
|
|
AsyncFileReader* headpart = m_reader;
|
|
|
|
// First sanity check: no sane CD image has less than 16 sectors, since that's what
|
|
// we need simply to contain a TOC. So if the file size is not large enough to
|
|
// accommodate that, it is NOT a CD image --->
|
|
|
|
int sectors = headpart->GetBlockCount();
|
|
|
|
if (sectors < 17)
|
|
return false;
|
|
|
|
m_blocks = 17;
|
|
|
|
if (tryIsoType(2048, 0, 24)) return true; // ISO 2048
|
|
if (tryIsoType(2336, 0, 16)) return true; // RAW 2336
|
|
if (tryIsoType(2352, 0, 0)) return true; // RAW 2352
|
|
if (tryIsoType(2448, 0, 0)) return true; // RAWQ 2448
|
|
|
|
if (tryIsoType(2048, 150 * 2048, 24)) return true; // NERO ISO 2048
|
|
if (tryIsoType(2352, 150 * 2048, 0)) return true; // NERO RAW 2352
|
|
if (tryIsoType(2448, 150 * 2048, 0)) return true; // NERO RAWQ 2448
|
|
|
|
if (tryIsoType(2048, -8, 24)) return true; // ISO 2048
|
|
if (tryIsoType(2352, -8, 0)) return true; // RAW 2352
|
|
if (tryIsoType(2448, -8, 0)) return true; // RAWQ 2448
|
|
|
|
m_offset = 0;
|
|
m_blocksize = CD_FRAMESIZE_RAW;
|
|
m_blockofs = 0;
|
|
m_type = ISOTYPE_AUDIO;
|
|
|
|
m_reader->SetDataOffset(m_offset);
|
|
m_reader->SetBlockSize(m_blocksize);
|
|
|
|
//BUG: This also detects a memory-card-file as a valid Audio-CD ISO... -avih
|
|
return true;
|
|
}
|