mirror of https://github.com/bsnes-emu/bsnes.git
351 lines
7.2 KiB
C++
Executable File
351 lines
7.2 KiB
C++
Executable File
// unrar_core 3.8.5. http://www.slack.net/~ant/
|
|
|
|
#include "unrar.h"
|
|
|
|
#include "rar.hpp"
|
|
#include <assert.h>
|
|
|
|
// This source code is a heavily modified version based on the unrar package.
|
|
// It may not be used to develop a RAR (WinRAR) compatible archiver.
|
|
// See unrar/license.txt for copyright and licensing.
|
|
|
|
// Same as printf when debugging, otherwise 0
|
|
#ifndef debug_printf
|
|
#define debug_printf 1 ? (void)0 : (void)
|
|
#endif
|
|
|
|
// If expr != unrar_ok, returns its value
|
|
#define RETURN_ERR( expr ) \
|
|
do {\
|
|
unrar_err_t err_;\
|
|
if ( (err_ = (expr)) != unrar_ok )\
|
|
return err_;\
|
|
} while ( 0 )
|
|
|
|
|
|
// Receives errors reported from deep within library.
|
|
// MUST be macro.
|
|
#define NONLOCAL_ERROR( p ) \
|
|
setjmp( p->Arc.jmp_env )
|
|
|
|
void Rar_Error_Handler::ReportError( unrar_err_t err )
|
|
{
|
|
if ( err )
|
|
longjmp( jmp_env, err );
|
|
}
|
|
|
|
void Rar_Error_Handler::MemoryError()
|
|
{
|
|
ReportError( unrar_err_memory );
|
|
}
|
|
|
|
|
|
//// Internal
|
|
|
|
unrar_t::unrar_t() :
|
|
Buffer( &Arc )
|
|
{
|
|
Arc.user_read = NULL;
|
|
Arc.user_write = NULL;
|
|
Arc.Tell_ = 0;
|
|
Arc.write_error = unrar_ok;
|
|
data_ = NULL;
|
|
own_data_ = NULL;
|
|
close_file = NULL;
|
|
FileCount = 0;
|
|
Unp = NULL;
|
|
|
|
unrar_init();
|
|
}
|
|
|
|
unrar_t::~unrar_t()
|
|
{
|
|
if ( Arc.write_error ) { }
|
|
|
|
if ( close_file )
|
|
close_file( Arc.user_read_data );
|
|
|
|
delete Unp;
|
|
|
|
free( own_data_ );
|
|
}
|
|
|
|
// True if current file is compressed in way that affects solid extraction state
|
|
static inline bool solid_file( const unrar_t* p )
|
|
{
|
|
return p->Arc.Solid &&
|
|
p->Arc.NewLhd.Method != 0x30 &&
|
|
p->Arc.NewLhd.FullPackSize != 0;
|
|
}
|
|
|
|
static void update_solid_pos( unrar_t* p )
|
|
{
|
|
if ( p->solid_pos == p->Arc.CurBlockPos )
|
|
p->solid_pos = p->Arc.NextBlockPos;
|
|
}
|
|
|
|
static unrar_err_t extract_( unrar_t* p, unrar_write_func user_write, void* user_data )
|
|
{
|
|
assert( !p->done );
|
|
assert( !solid_file( p ) || p->solid_pos == p->Arc.CurBlockPos );
|
|
|
|
if ( p->Arc.write_error ) { }
|
|
p->Arc.write_error = unrar_ok;
|
|
p->Arc.user_write = user_write;
|
|
p->Arc.user_write_data = user_data;
|
|
RETURN_ERR( p->ExtractCurrentFile( user_write == NULL ) );
|
|
p->Arc.user_write = NULL;
|
|
RETURN_ERR( p->Arc.write_error );
|
|
|
|
update_solid_pos( p );
|
|
|
|
return unrar_ok;
|
|
}
|
|
|
|
static unrar_err_t skip_solid( unrar_t* p )
|
|
{
|
|
if ( !solid_file( p ) )
|
|
{
|
|
update_solid_pos( p );
|
|
return unrar_ok;
|
|
}
|
|
|
|
return extract_( p, NULL, NULL );
|
|
}
|
|
|
|
static inline bool IsLink(uint Attr)
|
|
{
|
|
return((Attr & 0xF000)==0xA000);
|
|
}
|
|
|
|
static unrar_err_t next_( unrar_t* p, bool skipping_solid )
|
|
{
|
|
if ( p->done )
|
|
return unrar_err_arc_eof;
|
|
|
|
free( p->own_data_ );
|
|
p->own_data_ = NULL;
|
|
p->data_ = NULL;
|
|
|
|
for (;;)
|
|
{
|
|
p->Arc.SeekToNext();
|
|
unrar_err_t const err = p->Arc.ReadHeader();
|
|
if ( err != unrar_err_arc_eof )
|
|
RETURN_ERR( err );
|
|
//else
|
|
// debug_printf( "unrar: Didn't end with ENDARC_HEAD\n" ); // rar -en causes this
|
|
|
|
HEADER_TYPE const type = (HEADER_TYPE) p->Arc.GetHeaderType();
|
|
|
|
if ( err != unrar_ok || type == ENDARC_HEAD )
|
|
{
|
|
p->done = true;
|
|
break;
|
|
}
|
|
|
|
if ( type != FILE_HEAD )
|
|
{
|
|
// Skip non-files
|
|
if ( type != NEWSUB_HEAD && type != PROTECT_HEAD && type != SIGN_HEAD && type != SUB_HEAD )
|
|
debug_printf( "unrar: Skipping unknown block type: %X\n", (unsigned) type );
|
|
|
|
update_solid_pos( p );
|
|
}
|
|
else
|
|
{
|
|
// Update even for non-solid files, in case it's not extracted
|
|
if ( !solid_file( p ) )
|
|
update_solid_pos( p );
|
|
|
|
if ( p->Arc.IsArcLabel() )
|
|
{
|
|
// Ignore labels
|
|
}
|
|
else if ( IsLink( p->Arc.NewLhd.FileAttr ) )
|
|
{
|
|
// Ignore links
|
|
|
|
p->update_first_file_pos();
|
|
p->FileCount++; // Links are treated as files
|
|
}
|
|
else if ( p->Arc.IsArcDir() )
|
|
{
|
|
// Ignore directories
|
|
}
|
|
else
|
|
{
|
|
p->info.size = p->Arc.NewLhd.UnpSize;
|
|
p->info.name = p->Arc.NewLhd.FileName;
|
|
p->info.name_w = p->Arc.NewLhd.FileNameW;
|
|
p->info.is_unicode = (p->Arc.NewLhd.Flags & LHD_UNICODE) != 0;
|
|
p->info.dos_date = p->Arc.NewLhd.mtime.time;
|
|
p->info.crc = p->Arc.NewLhd.FileCRC;
|
|
p->info.is_crc32 = !p->Arc.OldFormat;
|
|
|
|
// Stop for files
|
|
break;
|
|
}
|
|
|
|
// Original code assumed that non-file items were never solid compressed
|
|
check( !solid_file( p ) );
|
|
|
|
// Skip non-file solid-compressed items (original code assumed there were none)
|
|
if ( skipping_solid )
|
|
RETURN_ERR( skip_solid( p ) );
|
|
}
|
|
}
|
|
|
|
return unrar_ok;
|
|
}
|
|
|
|
static unrar_err_t open_( unrar_t* p, unrar_read_func read, void* user_data )
|
|
{
|
|
p->Arc.user_read = read;
|
|
p->Arc.user_read_data = user_data;
|
|
|
|
RETURN_ERR( p->Arc.IsArchive() );
|
|
|
|
p->begin_pos = p->Arc.NextBlockPos;
|
|
p->solid_pos = p->Arc.NextBlockPos;
|
|
p->first_file_pos = INT_MAX;
|
|
p->done = false;
|
|
|
|
return unrar_ok;
|
|
}
|
|
|
|
|
|
//// Interface
|
|
|
|
// Needed when user read throws exception
|
|
struct unrar_ptr {
|
|
unrar_t* p;
|
|
unrar_ptr() { p = NULL; }
|
|
~unrar_ptr() { delete p; }
|
|
};
|
|
|
|
unrar_err_t unrar_open_custom( unrar_t** impl_out, unrar_read_func read, void* user_data )
|
|
{
|
|
*impl_out = NULL;
|
|
|
|
unrar_ptr ptr;
|
|
ptr.p = new unrar_t;
|
|
if ( !ptr.p )
|
|
return unrar_err_memory;
|
|
|
|
RETURN_ERR( NONLOCAL_ERROR( ptr.p ) );
|
|
RETURN_ERR( open_( ptr.p, read, user_data ) );
|
|
RETURN_ERR( next_( ptr.p, false ) );
|
|
|
|
*impl_out = ptr.p;
|
|
ptr.p = NULL;
|
|
|
|
//delete ptr.p; // done automatically at end of function
|
|
|
|
return unrar_ok;
|
|
}
|
|
|
|
void unrar_close( unrar_t* ar )
|
|
{
|
|
delete ar;
|
|
}
|
|
|
|
unrar_bool unrar_done( const unrar_t* p )
|
|
{
|
|
return p->done;
|
|
}
|
|
|
|
unrar_err_t unrar_next( unrar_t* p )
|
|
{
|
|
assert( !unrar_done( p ) );
|
|
|
|
RETURN_ERR( NONLOCAL_ERROR( p ) );
|
|
return next_( p, false );
|
|
}
|
|
|
|
const unrar_info_t* unrar_info( unrar_t const* p )
|
|
{
|
|
assert( !unrar_done( p ) );
|
|
|
|
return &p->info;
|
|
}
|
|
|
|
unrar_pos_t unrar_tell( const unrar_t* p )
|
|
{
|
|
return p->Arc.CurBlockPos;
|
|
}
|
|
|
|
unrar_err_t unrar_seek( unrar_t* p, unrar_pos_t n )
|
|
{
|
|
p->Arc.NextBlockPos = n;
|
|
p->done = false;
|
|
p->FileCount = (n <= p->first_file_pos ? 0 : 1);
|
|
|
|
return unrar_next( p );
|
|
}
|
|
|
|
unrar_err_t unrar_rewind( unrar_t* p )
|
|
{
|
|
return unrar_seek( p, p->begin_pos );
|
|
}
|
|
|
|
unrar_err_t unrar_try_extract( const unrar_t* p )
|
|
{
|
|
assert( !unrar_done( p ) );
|
|
|
|
return ((unrar_t*) p)->ExtractCurrentFile( true, true );
|
|
}
|
|
|
|
static unrar_err_t reopen( unrar_t* p )
|
|
{
|
|
// Save and restore archive reader
|
|
unrar_read_func read = p->Arc.user_read;
|
|
void* user_data = p->Arc.user_read_data;
|
|
|
|
void (*close_file)( void* ) = p->close_file;
|
|
p->close_file = NULL;
|
|
|
|
p->~unrar_t();
|
|
new (p) unrar_t;
|
|
|
|
p->close_file = close_file;
|
|
|
|
return open_( p, read, user_data );
|
|
}
|
|
|
|
unrar_err_t unrar_extract_custom( unrar_t* p, unrar_write_func user_write, void* user_data )
|
|
{
|
|
assert( !unrar_done( p ) );
|
|
|
|
RETURN_ERR( NONLOCAL_ERROR( p ) );
|
|
|
|
if ( solid_file( p ) )
|
|
{
|
|
unrar_pos_t pos = p->Arc.CurBlockPos;
|
|
if ( p->solid_pos != pos )
|
|
{
|
|
// Next file to solid extract isn't current one
|
|
|
|
if ( p->solid_pos > pos )
|
|
RETURN_ERR( reopen( p ) );
|
|
else
|
|
p->Arc.NextBlockPos = p->solid_pos;
|
|
|
|
RETURN_ERR( next_( p, true ) );
|
|
|
|
// Keep extracting until solid position is at desired file
|
|
while ( !p->done && p->solid_pos < pos )
|
|
{
|
|
RETURN_ERR( skip_solid( p ) );
|
|
RETURN_ERR( next_( p, true ) );
|
|
}
|
|
|
|
// Be sure we're at right file
|
|
if ( p->solid_pos != pos || p->Arc.CurBlockPos != pos )
|
|
return unrar_err_corrupt;
|
|
}
|
|
}
|
|
|
|
return extract_( p, user_write, user_data );
|
|
}
|