bsnes/snesreader/fex/Zip7_Extractor.cpp

253 lines
5.6 KiB
C++
Executable File

// File_Extractor 1.0.0. http://www.slack.net/~ant/
#include "Zip7_Extractor.h"
#include "7z_C/7zExtract.h"
#include "7z_C/7zAlloc.h"
#include "7z_C/7zCrc.h"
/* Copyright (C) 2005-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"
static ISzAlloc zip7_alloc = { SzAlloc, SzFree };
static ISzAlloc zip7_alloc_temp = { SzAllocTemp, SzFreeTemp };
struct Zip7_Extractor_Impl :
ISeekInStream
{
CLookToRead look;
CSzArEx db;
// SzExtract state
UInt32 block_index;
Byte* buf;
size_t buf_size;
File_Reader* in;
const char* in_err;
};
extern "C"
{
// 7-zip callbacks pass an ISeekInStream* for data, so we must cast it
// back to ISeekInStream* FIRST, then cast to our Impl structure
static SRes zip7_read_( void* vstream, void* out, size_t* size )
{
assert( out && size );
ISeekInStream* stream = STATIC_CAST(ISeekInStream*,vstream);
Zip7_Extractor_Impl* impl = STATIC_CAST(Zip7_Extractor_Impl*,stream);
long lsize = *size;
blargg_err_t err = impl->in->read_avail( out, &lsize );
if ( err )
{
*size = 0;
impl->in_err = err;
return SZ_ERROR_READ;
}
*size = lsize;
return SZ_OK;
}
static SRes zip7_seek_( void* vstream, Int64* pos, ESzSeek mode )
{
ISeekInStream* stream = STATIC_CAST(ISeekInStream*,vstream);
Zip7_Extractor_Impl* impl = STATIC_CAST(Zip7_Extractor_Impl*,stream);
assert( mode != SZ_SEEK_CUR ); // never used
if ( mode == SZ_SEEK_END )
{
assert( *pos == 0 ); // only used to find file length
*pos = impl->in->size();
return SZ_OK;
}
assert( mode == SZ_SEEK_SET );
blargg_err_t err = impl->in->seek( *pos );
if ( err )
{
// don't set in_err in this case, since it might be benign
if ( err == blargg_err_file_eof )
return SZ_ERROR_INPUT_EOF;
impl->in_err = err;
return SZ_ERROR_READ;
}
return SZ_OK;
}
}
blargg_err_t Zip7_Extractor::zip7_err( int err )
{
// TODO: ignore in_err in some cases? unsure about which error to use
blargg_err_t in_err = impl->in_err;
impl->in_err = NULL;
if ( in_err )
{
check( err != SZ_OK );
return in_err;
}
switch ( err )
{
case SZ_OK: return blargg_ok;
case SZ_ERROR_MEM: return blargg_err_memory;
case SZ_ERROR_READ: return blargg_err_file_io;
case SZ_ERROR_CRC:
case SZ_ERROR_DATA:
case SZ_ERROR_INPUT_EOF:
case SZ_ERROR_ARCHIVE: return blargg_err_file_corrupt;
case SZ_ERROR_UNSUPPORTED: return blargg_err_file_feature;
case SZ_ERROR_NO_ARCHIVE: return blargg_err_file_type;
}
return blargg_err_generic;
}
static blargg_err_t init_7z()
{
static bool inited;
if ( !inited )
{
inited = true;
CrcGenerateTable();
}
return blargg_ok;
}
static File_Extractor* new_7z()
{
return BLARGG_NEW Zip7_Extractor;
}
fex_type_t_ const fex_7z_type [1] = {{
".7z",
&new_7z,
"7-zip archive",
&init_7z
}};
Zip7_Extractor::Zip7_Extractor() :
File_Extractor( fex_7z_type )
{
impl = NULL;
}
Zip7_Extractor::~Zip7_Extractor()
{
close();
}
blargg_err_t Zip7_Extractor::open_v()
{
RETURN_ERR( init_7z() );
if ( !impl )
{
impl = (Zip7_Extractor_Impl*) malloc( sizeof *impl );
CHECK_ALLOC( impl );
}
impl->in = &arc();
impl->block_index = (UInt32) -1;
impl->buf = NULL;
impl->buf_size = 0;
LookToRead_CreateVTable( &impl->look, false );
impl->ISeekInStream::Read = zip7_read_;
impl->ISeekInStream::Seek = zip7_seek_;
impl->look.realStream = impl;
LookToRead_Init( &impl->look );
SzArEx_Init( &impl->db );
impl->in_err = NULL;
RETURN_ERR( zip7_err( SzArEx_Open( &impl->db, &impl->look.s,
&zip7_alloc, &zip7_alloc_temp ) ) );
return seek_arc_v( 0 );
}
void Zip7_Extractor::close_v()
{
if ( impl )
{
if ( impl->in )
{
impl->in = NULL;
SzArEx_Free( &impl->db, &zip7_alloc );
}
IAlloc_Free( &zip7_alloc, impl->buf );
free( impl );
impl = NULL;
}
}
blargg_err_t Zip7_Extractor::next_v()
{
while ( ++index < (int) impl->db.db.NumFiles )
{
CSzFileItem const& item = impl->db.db.Files [index];
if ( !item.IsDir )
{
// TODO: Support date.
// NTFS representation, stored as 64-bit value.
// Divide by 10000000 (ten million) to get seconds
//item.MTime.Low + (.High << 32)
// How to convert to DOS style?
set_name( item.Name );
set_info( item.Size, 0, (item.FileCRCDefined ? item.FileCRC : 0) );
break;
}
}
return blargg_ok;
}
blargg_err_t Zip7_Extractor::rewind_v()
{
return seek_arc_v( 0 );
}
fex_pos_t Zip7_Extractor::tell_arc_v() const
{
return index;
}
blargg_err_t Zip7_Extractor::seek_arc_v( fex_pos_t pos )
{
assert( 0 <= pos && pos <= (int) impl->db.db.NumFiles );
index = pos - 1;
return next_v();
}
blargg_err_t Zip7_Extractor::data_v( void const** out )
{
impl->in_err = NULL;
size_t offset = 0;
size_t count = 0;
RETURN_ERR( zip7_err( SzAr_Extract( &impl->db, &impl->look.s, index,
&impl->block_index, &impl->buf, &impl->buf_size,
&offset, &count, &zip7_alloc, &zip7_alloc_temp ) ) );
assert( count == (size_t) size() );
*out = impl->buf + offset;
return blargg_ok;
}