daedalus/Source/RomFile/RomFileCompressed.cpp

289 lines
7.6 KiB
C++

/*
Copyright (C) 2001,2006 StrmnNrmn
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program 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 this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "Base/Types.h"
#include "RomFile/RomFileCompressed.h"
#ifdef DAEDALUS_COMPRESSED_ROM_SUPPORT
#include <vector>
#include "Utility/MathUtil.h"
#include "Debug/DBGConsole.h"
#include "Base/Macros.h"
#include "Utility/Stream.h"
//*****************************************************************************
//
//*****************************************************************************
ROMFileCompressed::ROMFileCompressed( const std::filesystem::path filename )
: ROMFile( filename )
, mZipFile( NULL )
, mFoundRom( false )
, mRomSize( 0 )
{
}
//*****************************************************************************
//
//*****************************************************************************
ROMFileCompressed::~ROMFileCompressed()
{
if(mZipFile != NULL)
{
unzClose( mZipFile );
}
}
//*****************************************************************************
//
//*****************************************************************************
bool ROMFileCompressed::Open( COutputStream & messages )
{
#ifdef DAEDALUS_ENABLE_ASSERTS
DAEDALUS_ASSERT( mZipFile == NULL, "Opening the zipfile twice?" );
#endif
mFoundRom = false;
mZipFile = unzOpen(mFilename.c_str());
if (mZipFile == NULL)
{
messages << "Couldn't open " << mFilename.c_str();
return false;
}
s32 err;
unz_file_info file_info;
std::vector<char> rom_filename(256);
err = unzGoToFirstFile(mZipFile);
if (err != UNZ_OK)
{
messages << "error " << err << "with zipfile in unzGoToFirstFile";
}
else
{
do
{
err = unzGetCurrentFileInfo(mZipFile, &file_info,
rom_filename.data(), rom_filename.size(),
NULL, 0,
NULL, 0);
if (err != UNZ_OK)
{
messages << "error " << err << "with zipfile in unzGetCurrentFileInfo";
break;
}
// If filesize is > 1MB, assume it's a rom
if (file_info.uncompressed_size > 1*1024*1024)
{
// Open and try to find 4 byte header (0x80371240)
err = unzOpenCurrentFile(mZipFile);
if (err == UNZ_OK)
{
u32 magic;
const u32 u32size( sizeof( u32 ) );
u32 bytes_read( unzReadCurrentFile( mZipFile, &magic, u32size ) );
if (bytes_read == u32size)
{
// Check the format:
//DBGConsole_Msg(0, "%s %08x", rom_filename, dwVal);
// Could be byteswapped - check all variations
if (magic == 0x80371240 ||
magic == 0x40123780 ||
magic == 0x12408037)
{
unzCloseCurrentFile(mZipFile);
mRomSize = file_info.uncompressed_size;
mFoundRom = true;
#ifdef DAEDALUS_DEBUG_CONSOLE
if (!SetHeaderMagic( magic ))
{
DBGConsole_Msg(0, "Bad header magic for [C%s]", rom_filename.data());
}
#endif
break;
}
}
unzCloseCurrentFile(mZipFile);
}
}
} while (unzGoToNextFile(mZipFile) == UNZ_OK);
}
//
// Ensure that we've opened the file and are read to start reading
//
if( mFoundRom )
{
err = unzOpenCurrentFile(mZipFile);
if(err != UNZ_OK)
{
mFoundRom = false;
}
}
return mFoundRom;
}
//*****************************************************************************
//
//*****************************************************************************
bool ROMFileCompressed::LoadRawData( u32 bytes_to_read, u8 *p_bytes, COutputStream & messages )
{
#ifdef DAEDALUS_ENABLE_ASSERTS
DAEDALUS_ASSERT( mZipFile != NULL, "No open zipfile?" );
DAEDALUS_ASSERT( mFoundRom, "Why are we loading data when no rom was found?" );
#endif
if (p_bytes == NULL)
{
return false;
}
//
// It is assumed that the file is already openened and ready for reading here
//
s32 bytes_read( unzReadCurrentFile( mZipFile, p_bytes, bytes_to_read ) );
if( u32( bytes_read ) != bytes_to_read )
{
#ifdef DAEDALUS_DEBUG_CONSOLE
if( bytes_read < 0 )
{
messages << "error (" << bytes_read << ") with zipfile in unzReadCurrentFile";
}
else if( bytes_read == 0 )
{
// EOF?
}
else if( bytes_read < s32( bytes_to_read ) )
{
// Not enough bytes read
messages << "Unable to read sufficent bytes from zip.\nRead " << bytes_read << ", wanted " << bytes_to_read;
}
#endif
unzCloseCurrentFile(mZipFile);
return false;
}
//
// Close the file here to determine crc errors. No need to reopen presently
//
int err;
err = unzCloseCurrentFile(mZipFile);
if( err == UNZ_CRCERROR )
{
messages << "CRC Error in ZipFile";
unzCloseCurrentFile(mZipFile);
return false;
}
// Apply the bytesswapping before returning the buffer
CorrectSwap( p_bytes, bytes_to_read );
return true;
}
//*****************************************************************************
// Utility function to seek to the specified location in the zipfile
// Uses the specified buffer as a scratch pad to temporarilly decompress data.
//*****************************************************************************
bool ROMFileCompressed::Seek( u32 offset, u8 * p_scratch_block, u32 block_size )
{
#ifdef DAEDALUS_ENABLE_ASSERTS
DAEDALUS_ASSERT( mZipFile != NULL, "No open zipfile?" );
#endif
int err;
u32 current_offset( unztell( mZipFile ) );
#ifdef DAEDALUS_ENABLE_ASSERTS
DAEDALUS_ASSERT( (current_offset % block_size) == 0, "Performance: Trying to seek and current offset isn't a multiple of the block size" );
DAEDALUS_ASSERT( (offset % block_size) == 0, "Performance: Trying to seek to an address which isn't the block size" );
#endif
if(current_offset > offset)
{
#ifdef DAEDALUS_DEBUG_CONSOLE
DBGConsole_Msg( 0, "[CRomCache - seeking from %08x to %08x in zip", current_offset, offset );
#endif
// Annoyingly, have to close and reopen the zip file
unzCloseCurrentFile( mZipFile ); // Ignore errors here
err = unzOpenCurrentFile( mZipFile );
if(err != UNZ_OK)
{
// Why would this happen?
return false;
}
current_offset = 0;
}
while( current_offset < offset )
{
u32 bytes_remaining( offset - current_offset );
u32 bytes_to_read( std::min( bytes_remaining, block_size ) );
u32 bytes_read( unzReadCurrentFile( mZipFile, p_scratch_block, bytes_to_read ) );
if(bytes_read != bytes_to_read)
{
return false;
}
current_offset += bytes_to_read;
}
#ifdef DAEDALUS_ENABLE_ASSERTS
DAEDALUS_ASSERT( u32( unztell( mZipFile ) ) == offset, "Failed to seek to the specified offset" );
#endif
return true;
}
//*****************************************************************************
//
//*****************************************************************************
bool ROMFileCompressed::ReadChunk( u32 offset, u8 * p_dst, u32 length )
{
#ifdef DAEDALUS_ENABLE_ASSERTS
DAEDALUS_ASSERT( mZipFile != NULL, "No open zipfile?" );
DAEDALUS_ASSERT( mFoundRom, "Why are we loading data when no rom was found?" );
#endif
if( !Seek( offset, p_dst, length ) )
{
return false;
}
if( u32( unzReadCurrentFile( mZipFile, p_dst, length ) ) != length)
{
return false;
}
// Apply the bytesswapping before returning the buffer
CorrectSwap( p_dst, length );
return true;
}
#endif // DAEDALUS_COMPRESSED_ROM_SUPPORT