mirror of https://github.com/bsnes-emu/bsnes.git
258 lines
5.7 KiB
C++
258 lines
5.7 KiB
C++
|
// File_Extractor 1.0.0. http://www.slack.net/~ant/
|
||
|
|
||
|
#include "Zlib_Inflater.h"
|
||
|
|
||
|
/* Copyright (C) 2006-2009 Shay Green. This module 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 Foundation; either
|
||
|
version 2.1 of the License, or (at your option) any later version. This
|
||
|
module 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 Lesser General Public License for more
|
||
|
details. You should have received a copy of the GNU Lesser General Public
|
||
|
License along with this module; if not, write to the Free Software Foundation,
|
||
|
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
|
||
|
|
||
|
#include "blargg_source.h"
|
||
|
|
||
|
int const block_size = 4096;
|
||
|
|
||
|
static const char* get_zlib_err( int code )
|
||
|
{
|
||
|
assert( code != Z_OK );
|
||
|
switch ( code )
|
||
|
{
|
||
|
case Z_MEM_ERROR: return blargg_err_memory;
|
||
|
case Z_DATA_ERROR: return blargg_err_file_corrupt;
|
||
|
// TODO: handle more error codes
|
||
|
}
|
||
|
|
||
|
const char* str = zError( code );
|
||
|
if ( !str )
|
||
|
str = BLARGG_ERR( BLARGG_ERR_GENERIC, "problem unzipping data" );
|
||
|
|
||
|
return str;
|
||
|
}
|
||
|
|
||
|
void Zlib_Inflater::end()
|
||
|
{
|
||
|
if ( deflated_ )
|
||
|
{
|
||
|
deflated_ = false;
|
||
|
if ( inflateEnd( &zbuf ) )
|
||
|
check( false );
|
||
|
}
|
||
|
buf.clear();
|
||
|
|
||
|
static z_stream const empty = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
|
||
|
memcpy( &zbuf, &empty, sizeof zbuf );
|
||
|
}
|
||
|
|
||
|
Zlib_Inflater::Zlib_Inflater()
|
||
|
{
|
||
|
deflated_ = false;
|
||
|
end(); // initialize things
|
||
|
}
|
||
|
|
||
|
Zlib_Inflater::~Zlib_Inflater()
|
||
|
{
|
||
|
end();
|
||
|
}
|
||
|
|
||
|
blargg_err_t Zlib_Inflater::fill_buf( int count )
|
||
|
{
|
||
|
byte* out = buf.end() - count;
|
||
|
RETURN_ERR( callback( user_data, out, &count ) );
|
||
|
zbuf.avail_in = count;
|
||
|
zbuf.next_in = out;
|
||
|
return blargg_ok;
|
||
|
}
|
||
|
|
||
|
blargg_err_t Zlib_Inflater::begin( callback_t new_callback, void* new_user_data,
|
||
|
int new_buf_size, int initial_read )
|
||
|
{
|
||
|
callback = new_callback;
|
||
|
user_data = new_user_data;
|
||
|
|
||
|
end();
|
||
|
|
||
|
// TODO: decide whether using different size on alloc failure is a good idea
|
||
|
//RETURN_ERR( buf.resize( new_buf_size ? new_buf_size : 4 * block_size ) );
|
||
|
if ( new_buf_size && buf.resize( new_buf_size ) )
|
||
|
{
|
||
|
ACK_FAILURE();
|
||
|
new_buf_size = 0;
|
||
|
}
|
||
|
|
||
|
if ( !new_buf_size )
|
||
|
{
|
||
|
RETURN_ERR( buf.resize( 4 * block_size ) );
|
||
|
initial_read = 0;
|
||
|
}
|
||
|
|
||
|
// Fill buffer with some data, less than normal buffer size since caller might
|
||
|
// just be examining beginning of file.
|
||
|
return fill_buf( initial_read ? initial_read : block_size );
|
||
|
}
|
||
|
|
||
|
blargg_err_t Zlib_Inflater::set_mode( mode_t mode, int data_offset )
|
||
|
{
|
||
|
zbuf.next_in += data_offset;
|
||
|
zbuf.avail_in -= data_offset;
|
||
|
|
||
|
if ( mode == mode_auto )
|
||
|
{
|
||
|
// examine buffer for gzip header
|
||
|
mode = mode_copy;
|
||
|
unsigned const min_gzip_size = 2 + 8 + 8;
|
||
|
if ( zbuf.avail_in >= min_gzip_size &&
|
||
|
zbuf.next_in [0] == 0x1F && zbuf.next_in [1] == 0x8B )
|
||
|
mode = mode_ungz;
|
||
|
}
|
||
|
|
||
|
if ( mode != mode_copy )
|
||
|
{
|
||
|
int wb = MAX_WBITS + 16; // have zlib handle gzip header
|
||
|
if ( mode == mode_raw_deflate )
|
||
|
wb = -MAX_WBITS;
|
||
|
|
||
|
int zerr = inflateInit2( &zbuf, wb );
|
||
|
if ( zerr )
|
||
|
{
|
||
|
zbuf.next_in = NULL;
|
||
|
return get_zlib_err( zerr );
|
||
|
}
|
||
|
|
||
|
deflated_ = true;
|
||
|
}
|
||
|
return blargg_ok;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
// Reads/inflates entire stream. All input must be in buffer, and count must be total
|
||
|
// of all output.
|
||
|
blargg_err_t read_all( void* out, int count );
|
||
|
|
||
|
|
||
|
// zlib automatically applies this optimization (uses inflateFast)
|
||
|
// TODO: remove
|
||
|
blargg_err_t Zlib_Inflater::read_all( void* out, int count )
|
||
|
{
|
||
|
if ( deflated_ )
|
||
|
{
|
||
|
zbuf.next_out = (Bytef*) out;
|
||
|
zbuf.avail_out = count;
|
||
|
|
||
|
int err = inflate( &zbuf, Z_FINISH );
|
||
|
|
||
|
if ( zbuf.avail_out || err != Z_STREAM_END )
|
||
|
return blargg_err_file_corrupt;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if ( zbuf.avail_in < count )
|
||
|
return blargg_err_file_corrupt;
|
||
|
|
||
|
memcpy( out, zbuf.next_in, count );
|
||
|
|
||
|
zbuf.next_in += count;
|
||
|
zbuf.avail_in -= count;
|
||
|
}
|
||
|
|
||
|
return blargg_ok;
|
||
|
}
|
||
|
*/
|
||
|
|
||
|
blargg_err_t Zlib_Inflater::read( void* out, int* count_io )
|
||
|
{
|
||
|
int remain = *count_io;
|
||
|
if ( remain && zbuf.next_in )
|
||
|
{
|
||
|
if ( deflated_ )
|
||
|
{
|
||
|
zbuf.next_out = (Bytef*) out;
|
||
|
zbuf.avail_out = remain;
|
||
|
|
||
|
while ( 1 )
|
||
|
{
|
||
|
uInt old_avail_in = zbuf.avail_in;
|
||
|
int err = inflate( &zbuf, Z_NO_FLUSH );
|
||
|
if ( err == Z_STREAM_END )
|
||
|
{
|
||
|
remain = zbuf.avail_out;
|
||
|
end();
|
||
|
break; // no more data to inflate
|
||
|
}
|
||
|
|
||
|
if ( err && (err != Z_BUF_ERROR || old_avail_in) )
|
||
|
return get_zlib_err( err );
|
||
|
|
||
|
if ( !zbuf.avail_out )
|
||
|
{
|
||
|
remain = 0;
|
||
|
break; // requested number of bytes inflated
|
||
|
}
|
||
|
|
||
|
if ( zbuf.avail_in )
|
||
|
{
|
||
|
// inflate() should never leave input if there's still space for output
|
||
|
check( false );
|
||
|
return blargg_err_file_corrupt;
|
||
|
}
|
||
|
|
||
|
RETURN_ERR( fill_buf( buf.size() ) );
|
||
|
if ( !zbuf.avail_in )
|
||
|
return blargg_err_file_corrupt; // stream didn't end but there's no more data
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
while ( 1 )
|
||
|
{
|
||
|
// copy buffered data
|
||
|
if ( zbuf.avail_in )
|
||
|
{
|
||
|
long count = zbuf.avail_in;
|
||
|
if ( count > remain )
|
||
|
count = remain;
|
||
|
memcpy( out, zbuf.next_in, count );
|
||
|
zbuf.total_out += count;
|
||
|
out = (char*) out + count;
|
||
|
remain -= count;
|
||
|
zbuf.next_in += count;
|
||
|
zbuf.avail_in -= count;
|
||
|
}
|
||
|
|
||
|
if ( !zbuf.avail_in && zbuf.next_in < buf.end() )
|
||
|
{
|
||
|
end();
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
// read large request directly
|
||
|
if ( remain + zbuf.total_out % block_size >= buf.size() )
|
||
|
{
|
||
|
int count = remain;
|
||
|
RETURN_ERR( callback( user_data, out, &count ) );
|
||
|
zbuf.total_out += count;
|
||
|
out = (char*) out + count;
|
||
|
remain -= count;
|
||
|
|
||
|
if ( remain )
|
||
|
{
|
||
|
end();
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( !remain )
|
||
|
break;
|
||
|
|
||
|
RETURN_ERR( fill_buf( buf.size() - zbuf.total_out % block_size ) );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
*count_io -= remain;
|
||
|
return blargg_ok;
|
||
|
}
|