mirror of
https://github.com/0ldsk00l/nestopia.git
synced 2025-04-02 10:31:51 -04:00
1136 lines
22 KiB
C++
1136 lines
22 KiB
C++
////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Nestopia - NES/Famicom emulator written in C++
|
|
//
|
|
// Copyright (C) 2003-2008 Martin Freij
|
|
//
|
|
// This file is part of Nestopia.
|
|
//
|
|
// Nestopia 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.
|
|
//
|
|
// Nestopia 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 Nestopia; if not, write to the Free Software
|
|
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
#include <new>
|
|
#include "NstApplicationInstance.hpp"
|
|
#include "NstWindowUser.hpp"
|
|
#include "NstSystemDll.hpp"
|
|
#include "NstObjectPod.hpp"
|
|
#include "NstIoFile.hpp"
|
|
#include "NstIoLog.hpp"
|
|
#include "NstIoArchive.hpp"
|
|
#include <initguid.h>
|
|
#include <ObjBase.h>
|
|
#include <OleAuto.h>
|
|
#include "../unrar/unrar.h"
|
|
#include "../7zip/IArchive.h"
|
|
|
|
#if NST_MSVC
|
|
#pragma comment(lib,"zlibstat")
|
|
#endif
|
|
|
|
DEFINE_GUID(CLSID_CFormat7z,0x23170F69,0x40C1,0x278A,0x10,0x00,0x00,0x01,0x10,0x07,0x00,0x00);
|
|
|
|
#define ZLIB_WINAPI
|
|
#define ZCALLBACK WINAPIV
|
|
#include "../zlib/unzip.h"
|
|
|
|
namespace Nestopia
|
|
{
|
|
namespace Io
|
|
{
|
|
class Archive::Codec
|
|
{
|
|
public:
|
|
|
|
enum Exception
|
|
{
|
|
ERR_DLL,
|
|
ERR_CREATE,
|
|
ERR_OPEN
|
|
};
|
|
|
|
virtual ~Codec() {}
|
|
|
|
virtual bool Build(Items&) = 0;
|
|
virtual uint Extract(uint,void*,uint) = 0;
|
|
};
|
|
|
|
class Archive::UnZip : public Codec
|
|
{
|
|
struct Stream
|
|
{
|
|
const uchar* const buffer;
|
|
uint pos;
|
|
const uint size;
|
|
|
|
explicit Stream(const void* b=NULL,uint s=0)
|
|
: buffer(static_cast<const uchar*>(b)), pos(0), size(s) {}
|
|
};
|
|
|
|
Stream stream;
|
|
void* const handle;
|
|
|
|
bool Build(Items& files)
|
|
{
|
|
uint numFiles;
|
|
|
|
{
|
|
unz_global_info_s info;
|
|
|
|
if
|
|
(
|
|
::unzGetGlobalInfo( handle, &info ) != UNZ_OK ||
|
|
info.number_entry == 0 ||
|
|
::unzGoToFirstFile( handle ) != UNZ_OK
|
|
)
|
|
return false;
|
|
|
|
numFiles = info.number_entry;
|
|
}
|
|
|
|
files.reserve( numFiles );
|
|
|
|
char path[MAX_PATH+1];
|
|
unz_file_info info;
|
|
uint i=0;
|
|
|
|
do
|
|
{
|
|
if (::unzGetCurrentFileInfo( handle, &info, path, MAX_PATH, NULL, 0, NULL, 0 ) == UNZ_OK && info.uncompressed_size && *path)
|
|
{
|
|
wchar_t unipath[MAX_PATH+1];
|
|
::MultiByteToWideChar( CP_OEMCP, 0, path, -1, unipath, MAX_PATH );
|
|
files.push_back( Item(this,unipath,info.uncompressed_size,i) );
|
|
}
|
|
}
|
|
while (++i < numFiles && ::unzGoToNextFile( handle ) == UNZ_OK);
|
|
|
|
return !files.empty();
|
|
}
|
|
|
|
uint Extract(uint index,void* buffer,uint size)
|
|
{
|
|
if (::unzGoToFirstFile( handle ) == UNZ_OK)
|
|
{
|
|
for (uint i=0; i < index; ++i)
|
|
{
|
|
if (::unzGoToNextFile( handle ) != UNZ_OK)
|
|
return false;
|
|
}
|
|
|
|
if (::unzOpenCurrentFile( handle ) == UNZ_OK)
|
|
{
|
|
const int extracted = ::unzReadCurrentFile( handle, buffer, size );
|
|
::unzCloseCurrentFile( handle );
|
|
|
|
if (extracted == int(size))
|
|
return size;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static void* ZCALLBACK OnOpen(void* opaque,cstring,int mode)
|
|
{
|
|
if (mode & ZLIB_FILEFUNC_MODE_WRITE)
|
|
return NULL;
|
|
else
|
|
return opaque;
|
|
}
|
|
|
|
static ulong ZCALLBACK OnWrite(voidpf,voidpf,const void*,ulong)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int ZCALLBACK OnClose(voidpf,voidpf stream)
|
|
{
|
|
return !stream;
|
|
}
|
|
|
|
static int ZCALLBACK OnTest(voidpf,voidpf stream)
|
|
{
|
|
return !stream;
|
|
}
|
|
|
|
static ulong ZCALLBACK OnStreamRead(voidpf,voidpf object,void* data,ulong length)
|
|
{
|
|
if (object && data)
|
|
{
|
|
Stream& stream = *static_cast<Stream*>( object );
|
|
|
|
if (length > stream.size - stream.pos)
|
|
length = stream.size - stream.pos;
|
|
|
|
std::memcpy( data, stream.buffer + stream.pos, length );
|
|
stream.pos += length;
|
|
|
|
return length;
|
|
}
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static long ZCALLBACK OnStreamTell(voidpf,voidpf object)
|
|
{
|
|
if (const Stream* const stream = static_cast<const Stream*>( object ))
|
|
return stream->pos;
|
|
else
|
|
return -1;
|
|
}
|
|
|
|
static long ZCALLBACK OnStreamSeek(voidpf,voidpf object,ulong distance,int origin)
|
|
{
|
|
if (Stream* const stream = static_cast<Stream*>( object ))
|
|
{
|
|
switch (origin)
|
|
{
|
|
case ZLIB_FILEFUNC_SEEK_SET: stream->pos = distance; break;
|
|
case ZLIB_FILEFUNC_SEEK_CUR: stream->pos += distance; break;
|
|
case ZLIB_FILEFUNC_SEEK_END: stream->pos = stream->size + distance; break;
|
|
default: return -1L;
|
|
}
|
|
|
|
if (stream->pos <= stream->size)
|
|
return 0L;
|
|
|
|
stream->pos = stream->size;
|
|
}
|
|
|
|
return -1L;
|
|
}
|
|
|
|
static ulong ZCALLBACK OnFileRead(voidpf,voidpf file,void* data,ulong size)
|
|
{
|
|
if (file && data)
|
|
{
|
|
try
|
|
{
|
|
return static_cast<const File*>(file)->ReadSome( data, size );
|
|
}
|
|
catch (...)
|
|
{
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static long ZCALLBACK OnFileTell(voidpf,voidpf file)
|
|
{
|
|
if (file)
|
|
{
|
|
try
|
|
{
|
|
return long (static_cast<const File*>(file)->Position());
|
|
}
|
|
catch (...)
|
|
{
|
|
}
|
|
}
|
|
|
|
return -1L;
|
|
}
|
|
|
|
static long ZCALLBACK OnFileSeek(voidpf,voidpf file,ulong distance,int origin)
|
|
{
|
|
NST_COMPILE_ASSERT
|
|
(
|
|
ZLIB_FILEFUNC_SEEK_SET == File::BEGIN &&
|
|
ZLIB_FILEFUNC_SEEK_CUR == File::CURRENT &&
|
|
ZLIB_FILEFUNC_SEEK_END == File::END
|
|
);
|
|
|
|
if (file && origin < 3)
|
|
{
|
|
try
|
|
{
|
|
static_cast<const File*>(file)->Seek( static_cast<File::Offset>(origin), distance );
|
|
return 0L;
|
|
}
|
|
catch (...)
|
|
{
|
|
}
|
|
}
|
|
|
|
return -1L;
|
|
}
|
|
|
|
static void* Create(const File& file)
|
|
{
|
|
zlib_filefunc_def_s def;
|
|
|
|
def.zopen_file = &OnOpen;
|
|
def.zread_file = &OnFileRead;
|
|
def.zwrite_file = &OnWrite;
|
|
def.ztell_file = &OnFileTell;
|
|
def.zseek_file = &OnFileSeek;
|
|
def.zclose_file = &OnClose;
|
|
def.zerror_file = &OnTest;
|
|
def.opaque = const_cast<void*>(static_cast<const void*>(&file));
|
|
|
|
void* handle = ::unzOpen2( NULL, &def );
|
|
|
|
if (handle == NULL)
|
|
throw ERR_OPEN;
|
|
|
|
return handle;
|
|
}
|
|
|
|
static void* Create(Stream& stream)
|
|
{
|
|
zlib_filefunc_def_s def;
|
|
|
|
def.zopen_file = &OnOpen;
|
|
def.zread_file = &OnStreamRead;
|
|
def.zwrite_file = &OnWrite;
|
|
def.ztell_file = &OnStreamTell;
|
|
def.zseek_file = &OnStreamSeek;
|
|
def.zclose_file = &OnClose;
|
|
def.zerror_file = &OnTest;
|
|
def.opaque = &stream;
|
|
|
|
void* handle = ::unzOpen2( NULL, &def );
|
|
|
|
if (handle == NULL)
|
|
throw ERR_OPEN;
|
|
|
|
return handle;
|
|
}
|
|
|
|
public:
|
|
|
|
UnZip(const File& file)
|
|
: handle(Create(file)) {}
|
|
|
|
UnZip(const void* buffer,uint size)
|
|
: stream(buffer,size), handle(Create(stream)) {}
|
|
|
|
~UnZip()
|
|
{
|
|
::unzClose( handle );
|
|
}
|
|
};
|
|
|
|
class Archive::UnRar : public Codec
|
|
{
|
|
public:
|
|
|
|
explicit UnRar(const Path&);
|
|
|
|
private:
|
|
|
|
class Dll : System::Dll
|
|
{
|
|
typedef HANDLE (PASCAL *OpenArchiveExFunc)(RAROpenArchiveDataEx*);
|
|
typedef int (PASCAL *CloseArchiveFunc)(HANDLE);
|
|
typedef int (PASCAL *ReadHeaderFunc)(HANDLE,RARHeaderData*);
|
|
typedef int (PASCAL *ReadHeaderExFunc)(HANDLE,RARHeaderDataEx*);
|
|
typedef int (PASCAL *ProcessFileFunc)(HANDLE,int,char*,char*);
|
|
typedef int (PASCAL *ProcessFileWFunc)(HANDLE,int,wchar_t*,wchar_t*);
|
|
typedef void (PASCAL *SetProcessDataProcFunc)(HANDLE,PROCESSDATAPROC);
|
|
|
|
public:
|
|
|
|
OpenArchiveExFunc OpenArchiveEx;
|
|
CloseArchiveFunc CloseArchive;
|
|
ReadHeaderFunc ReadHeader;
|
|
ReadHeaderExFunc ReadHeaderEx;
|
|
ProcessFileFunc ProcessFile;
|
|
ProcessFileWFunc ProcessFileW;
|
|
SetProcessDataProcFunc SetProcessDataProc;
|
|
|
|
bool Load()
|
|
{
|
|
if (*this)
|
|
{
|
|
return true;
|
|
}
|
|
else if (System::Dll::Load( L"unrar.dll" ))
|
|
{
|
|
if
|
|
(
|
|
NULL != (OpenArchiveEx = Fetch< OpenArchiveExFunc >( "RAROpenArchiveEx" )) &&
|
|
NULL != (CloseArchive = Fetch< CloseArchiveFunc >( "RARCloseArchive" )) &&
|
|
NULL != (ReadHeader = Fetch< ReadHeaderFunc >( "RARReadHeader" )) &&
|
|
NULL != (ReadHeaderEx = Fetch< ReadHeaderExFunc >( "RARReadHeaderEx" )) &&
|
|
NULL != (ProcessFile = Fetch< ProcessFileFunc >( "RARProcessFile" )) &&
|
|
NULL != (ProcessFileW = Fetch< ProcessFileWFunc >( "RARProcessFileW" )) &&
|
|
NULL != (SetProcessDataProc = Fetch< SetProcessDataProcFunc >( "RARSetProcessDataProc" ))
|
|
)
|
|
return true;
|
|
|
|
Unload();
|
|
}
|
|
|
|
return false;
|
|
}
|
|
};
|
|
|
|
static Dll dll;
|
|
|
|
void* Open(uint);
|
|
uint Extract(uint,void*,uint);
|
|
bool Build(Items&);
|
|
|
|
Path path;
|
|
};
|
|
|
|
Archive::UnRar::Dll Archive::UnRar::dll;
|
|
|
|
Archive::UnRar::UnRar(const Path& string)
|
|
: path(string)
|
|
{
|
|
if (!dll.Load())
|
|
throw ERR_DLL;
|
|
}
|
|
|
|
void* Archive::UnRar::Open(uint mode)
|
|
{
|
|
Object::Pod<RAROpenArchiveDataEx> data;
|
|
|
|
data.ArcName = NULL;
|
|
data.ArcNameW = path.Ptr();
|
|
data.OpenMode = mode;
|
|
data.CmtBuf = NULL;
|
|
|
|
return dll.OpenArchiveEx( &data );
|
|
}
|
|
|
|
bool Archive::UnRar::Build(Items& files)
|
|
{
|
|
if (void* const handle = Open( RAR_OM_LIST ))
|
|
{
|
|
try
|
|
{
|
|
Object::Pod<RARHeaderDataEx> header;
|
|
|
|
for (uint i=0; dll.ReadHeaderEx( handle, &header ) == 0; ++i)
|
|
{
|
|
if (header.UnpSize && *header.FileName)
|
|
files.push_back( Item(this,header.FileNameW,header.UnpSize,i) );
|
|
|
|
if (dll.ProcessFile( handle, RAR_SKIP, NULL, NULL ))
|
|
break;
|
|
}
|
|
}
|
|
catch (...)
|
|
{
|
|
files.clear();
|
|
}
|
|
|
|
dll.CloseArchive( handle );
|
|
|
|
return !files.empty();
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
uint Archive::UnRar::Extract(uint index,void* buffer,uint size)
|
|
{
|
|
static struct
|
|
{
|
|
uchar* buffer;
|
|
uint pos;
|
|
uint size;
|
|
} stream;
|
|
|
|
struct Callback
|
|
{
|
|
static int PASCAL OnRead(uchar* input,int size)
|
|
{
|
|
if (input && size > 0 && size <= stream.size)
|
|
{
|
|
std::memcpy( stream.buffer + stream.pos, input, size );
|
|
stream.pos += size;
|
|
return 1;
|
|
}
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
};
|
|
};
|
|
|
|
uint extracted = 0;
|
|
|
|
if (void* const handle = Open( RAR_OM_EXTRACT ))
|
|
{
|
|
stream.buffer = static_cast<uchar*>(buffer);
|
|
stream.pos = 0;
|
|
stream.size = size;
|
|
|
|
dll.SetProcessDataProc( handle, Callback::OnRead );
|
|
|
|
try
|
|
{
|
|
Object::Pod<RARHeaderData> header;
|
|
header.CmtBuf = NULL;
|
|
|
|
for (uint i=0; ; ++i)
|
|
{
|
|
if (dll.ReadHeader( handle, &header ))
|
|
break;
|
|
|
|
if (i < index)
|
|
{
|
|
if (dll.ProcessFile( handle, RAR_SKIP, NULL, NULL ))
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
Path path( Application::Instance::GetTmpPath() );
|
|
|
|
if (dll.ProcessFileW( handle, RAR_EXTRACT, NULL, path.Ptr() ) == 0)
|
|
{
|
|
if (stream.pos == stream.size)
|
|
extracted = size;
|
|
|
|
if (!File::Delete( path.Ptr() ))
|
|
Log() << "Archive: warning, couldn't delete temporary RAR file: " << path << '!';
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
catch (...)
|
|
{
|
|
}
|
|
|
|
dll.CloseArchive( handle );
|
|
}
|
|
|
|
return extracted;
|
|
}
|
|
|
|
class Archive::Un7zip : public Codec
|
|
{
|
|
public:
|
|
|
|
explicit Un7zip(const File&);
|
|
Un7zip(const void*,uint);
|
|
|
|
private:
|
|
|
|
~Un7zip();
|
|
|
|
IInArchive& archive;
|
|
IInStream* const stream;
|
|
|
|
static IInArchive* Create();
|
|
|
|
class InStream : public IInStream, private IStreamGetSize
|
|
{
|
|
ulong refCount;
|
|
|
|
HRESULT STDMETHODCALLTYPE QueryInterface(REFGUID,void**)
|
|
{
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
HRESULT STDMETHODCALLTYPE GetSize(UInt64* save)
|
|
{
|
|
if (save)
|
|
{
|
|
*save = size;
|
|
return S_OK;
|
|
}
|
|
else
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
}
|
|
|
|
ulong STDMETHODCALLTYPE AddRef()
|
|
{
|
|
return ++refCount;
|
|
}
|
|
|
|
ulong STDMETHODCALLTYPE Release()
|
|
{
|
|
return --refCount;
|
|
}
|
|
|
|
protected:
|
|
|
|
const uint size;
|
|
|
|
public:
|
|
|
|
explicit InStream(uint s)
|
|
: refCount(0), size(s) {}
|
|
};
|
|
|
|
class InFileStream : public InStream
|
|
{
|
|
const File& file;
|
|
|
|
HRESULT STDMETHODCALLTYPE Read(void* data,UInt32 length,UInt32* save)
|
|
{
|
|
if (data != NULL || length == 0)
|
|
{
|
|
try
|
|
{
|
|
length = file.ReadSome( data, length );
|
|
}
|
|
catch (File::Exception)
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
|
|
if (save)
|
|
*save = length;
|
|
|
|
return S_OK;
|
|
}
|
|
else
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
}
|
|
|
|
HRESULT STDMETHODCALLTYPE Seek(Int64 offset,UInt32 origin,UInt64* pos)
|
|
{
|
|
NST_COMPILE_ASSERT( File::BEGIN == 0 && File::CURRENT == 1 && File::END == 2 );
|
|
|
|
if (origin < 3)
|
|
{
|
|
try
|
|
{
|
|
origin = file.Seek( static_cast<File::Offset>(origin), int(offset) );
|
|
}
|
|
catch (File::Exception)
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
|
|
if (pos)
|
|
*pos = origin;
|
|
|
|
return S_OK;
|
|
}
|
|
else
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
}
|
|
|
|
public:
|
|
|
|
explicit InFileStream(const File& f)
|
|
: InStream(f.Size()), file(f) {}
|
|
};
|
|
|
|
class InMemStream : public InStream
|
|
{
|
|
const uchar* const buffer;
|
|
uint pos;
|
|
|
|
HRESULT STDMETHODCALLTYPE Read(void* data,UInt32 length,UInt32* save)
|
|
{
|
|
if (data != NULL || length == 0)
|
|
{
|
|
if (length > size - pos)
|
|
length = size - pos;
|
|
|
|
std::memcpy( data, buffer + pos, length );
|
|
pos += length;
|
|
|
|
if (save)
|
|
*save = length;
|
|
|
|
return S_OK;
|
|
}
|
|
else
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
}
|
|
|
|
HRESULT STDMETHODCALLTYPE Seek(Int64 offset,UInt32 origin,UInt64* save)
|
|
{
|
|
if (origin < 3)
|
|
{
|
|
pos = (origin == 0 ? 0 : origin == 1 ? pos : size) + int(offset);
|
|
|
|
if (pos > size)
|
|
pos = size;
|
|
|
|
if (save)
|
|
*save = pos;
|
|
|
|
return S_OK;
|
|
}
|
|
else
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
}
|
|
|
|
public:
|
|
|
|
InMemStream(const void* b,uint s)
|
|
: InStream(s), buffer(static_cast<const uchar*>(b)), pos(0) {}
|
|
};
|
|
|
|
class OutStream : public IArchiveExtractCallback
|
|
{
|
|
class SeqStream : public ISequentialOutStream
|
|
{
|
|
uchar* const output;
|
|
uint pos;
|
|
const uint size;
|
|
ulong refCount;
|
|
|
|
HRESULT STDMETHODCALLTYPE QueryInterface(REFGUID,void**)
|
|
{
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
HRESULT STDMETHODCALLTYPE Write(const void* data,UInt32 length,UInt32* save)
|
|
{
|
|
if (data != NULL || size == 0)
|
|
{
|
|
NST_VERIFY( length <= size - pos );
|
|
|
|
if (length > size - pos)
|
|
length = size - pos;
|
|
|
|
std::memcpy( output + pos, data, length );
|
|
pos += length;
|
|
|
|
if (save)
|
|
*save = length;
|
|
|
|
return S_OK;
|
|
}
|
|
else
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
}
|
|
|
|
ulong STDMETHODCALLTYPE AddRef()
|
|
{
|
|
return ++refCount;
|
|
}
|
|
|
|
ulong STDMETHODCALLTYPE Release()
|
|
{
|
|
return --refCount;
|
|
}
|
|
|
|
public:
|
|
|
|
SeqStream(void* d,uint s)
|
|
: output(static_cast<uchar*>(d)), pos(0), size(s), refCount(0) {}
|
|
|
|
uint Size() const
|
|
{
|
|
return pos;
|
|
}
|
|
};
|
|
|
|
SeqStream seqStream;
|
|
const uint index;
|
|
ulong refCount;
|
|
|
|
HRESULT STDMETHODCALLTYPE QueryInterface(REFGUID,void**)
|
|
{
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
HRESULT STDMETHODCALLTYPE PrepareOperation(Int32)
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT STDMETHODCALLTYPE SetTotal(UInt64)
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT STDMETHODCALLTYPE SetCompleted(const UInt64*)
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT STDMETHODCALLTYPE SetOperationResult(Int32)
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT STDMETHODCALLTYPE GetStream(UInt32 id,ISequentialOutStream** ptr,Int32 mode)
|
|
{
|
|
switch (mode)
|
|
{
|
|
case NArchive::NExtract::NAskMode::kExtract:
|
|
case NArchive::NExtract::NAskMode::kTest:
|
|
|
|
if (id != index || ptr == NULL)
|
|
return S_FALSE;
|
|
else
|
|
*ptr = &seqStream;
|
|
|
|
case NArchive::NExtract::NAskMode::kSkip:
|
|
return S_OK;
|
|
|
|
default:
|
|
return E_INVALIDARG;
|
|
}
|
|
}
|
|
|
|
ulong STDMETHODCALLTYPE AddRef()
|
|
{
|
|
return ++refCount;
|
|
}
|
|
|
|
ulong STDMETHODCALLTYPE Release()
|
|
{
|
|
return --refCount;
|
|
}
|
|
|
|
public:
|
|
|
|
OutStream(uint index,void* data,uint size)
|
|
: seqStream(data,size), index(index), refCount(0) {}
|
|
|
|
uint Size() const
|
|
{
|
|
return seqStream.Size();
|
|
}
|
|
};
|
|
|
|
void Open();
|
|
void Close();
|
|
bool Build(Items&);
|
|
uint Extract(uint,void*,uint);
|
|
};
|
|
|
|
IInArchive* Archive::Un7zip::Create()
|
|
{
|
|
static System::Dll dll;
|
|
|
|
if (!dll && !dll.Load( L"7zxa.dll" ))
|
|
throw ERR_DLL;
|
|
|
|
typedef UINT32 (WINAPI *CreateObjectFunc)(const GUID*,const GUID*,void**);
|
|
CreateObjectFunc const CreateObject = dll.Fetch<CreateObjectFunc>("CreateObject");
|
|
|
|
void* object;
|
|
|
|
if (CreateObject == NULL || FAILED(CreateObject( &CLSID_CFormat7z, &IID_IInArchive, &object )))
|
|
throw ERR_CREATE;
|
|
|
|
return static_cast<IInArchive*>(object);
|
|
}
|
|
|
|
Archive::Un7zip::Un7zip(const void* data,uint size)
|
|
: archive(*Create()), stream(new (std::nothrow) InMemStream(data,size))
|
|
{
|
|
Open();
|
|
}
|
|
|
|
Archive::Un7zip::Un7zip(const File& file)
|
|
: archive(*Create()), stream(new (std::nothrow) InFileStream(file))
|
|
{
|
|
Open();
|
|
}
|
|
|
|
Archive::Un7zip::~Un7zip()
|
|
{
|
|
Close();
|
|
}
|
|
|
|
void Archive::Un7zip::Open()
|
|
{
|
|
if (stream)
|
|
{
|
|
try
|
|
{
|
|
if (SUCCEEDED(archive.Open( stream, NULL, NULL )))
|
|
return;
|
|
}
|
|
catch (...)
|
|
{
|
|
}
|
|
|
|
delete stream;
|
|
}
|
|
|
|
archive.Release();
|
|
|
|
throw ERR_OPEN;
|
|
}
|
|
|
|
void Archive::Un7zip::Close()
|
|
{
|
|
archive.Close();
|
|
delete stream;
|
|
archive.Release();
|
|
}
|
|
|
|
bool Archive::Un7zip::Build(Items& files)
|
|
{
|
|
UInt32 numFiles;
|
|
|
|
if (SUCCEEDED(archive.GetNumberOfItems( &numFiles )) && numFiles)
|
|
{
|
|
files.reserve( numFiles );
|
|
|
|
Path path;
|
|
PROPVARIANT prop;
|
|
|
|
for (uint i=0; i < numFiles; ++i)
|
|
{
|
|
prop.vt = VT_EMPTY;
|
|
|
|
if (FAILED(archive.GetProperty( i, kpidSize, &prop )) || prop.vt != VT_UI8 || !prop.uhVal.LowPart || prop.uhVal.HighPart)
|
|
continue;
|
|
|
|
const uint size = prop.uhVal.LowPart;
|
|
|
|
prop.vt = VT_EMPTY;
|
|
|
|
if (FAILED(archive.GetProperty( i, kpidPath, &prop )) || prop.vt != VT_BSTR || prop.bstrVal == NULL)
|
|
continue;
|
|
|
|
path = prop.bstrVal;
|
|
|
|
::VariantClear( reinterpret_cast<VARIANTARG*>(&prop) );
|
|
|
|
if (path.Length())
|
|
files.push_back( Item(this,path.Ptr(),size,i) );
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
uint Archive::Un7zip::Extract(uint index,void* data,uint size)
|
|
{
|
|
NST_ASSERT( data );
|
|
|
|
OutStream outStream( index, data, size );
|
|
const UInt32 indices[1] = {index};
|
|
|
|
if (SUCCEEDED(archive.Extract( indices, sizeof(array(indices)), 0, &outStream )) && outStream.Size() == size)
|
|
return size;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
Archive::Archive()
|
|
: codec(NULL)
|
|
{
|
|
}
|
|
|
|
Archive::Archive(const File& file)
|
|
: codec(NULL)
|
|
{
|
|
Open( &file, NULL, 0 );
|
|
}
|
|
|
|
Archive::Archive(const void* raw,uint size)
|
|
: codec(NULL)
|
|
{
|
|
Open( NULL, raw, size );
|
|
}
|
|
|
|
Archive::~Archive()
|
|
{
|
|
Close();
|
|
}
|
|
|
|
bool Archive::Open(const File& file)
|
|
{
|
|
Close();
|
|
return Open( &file, NULL, 0 );
|
|
}
|
|
|
|
bool Archive::Open(const void* raw,uint size)
|
|
{
|
|
Close();
|
|
return Open( NULL, raw, size );
|
|
}
|
|
|
|
bool Archive::Open(const File* const file,const void* const raw,uint size)
|
|
{
|
|
try
|
|
{
|
|
if (raw)
|
|
{
|
|
if (size >= 4)
|
|
{
|
|
switch (FourCC<>::T(static_cast<const uchar*>(raw)))
|
|
{
|
|
case FILE_ID_ZIP:
|
|
|
|
codec = new UnZip( raw, size );
|
|
break;
|
|
|
|
case FILE_ID_7Z:
|
|
|
|
codec = new Un7zip( raw, size );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else switch (file->Peek32())
|
|
{
|
|
case FILE_ID_ZIP:
|
|
|
|
codec = new UnZip( *file );
|
|
break;
|
|
|
|
case FILE_ID_7Z:
|
|
|
|
codec = new Un7zip( *file );
|
|
break;
|
|
|
|
case FILE_ID_RAR:
|
|
|
|
codec = new UnRar( file->GetName() );
|
|
break;
|
|
}
|
|
|
|
return codec ? codec->Build( files ) : false;
|
|
}
|
|
catch (...)
|
|
{
|
|
}
|
|
|
|
Close();
|
|
|
|
return false;
|
|
}
|
|
|
|
void Archive::Close()
|
|
{
|
|
if (codec)
|
|
{
|
|
delete codec;
|
|
codec = NULL;
|
|
}
|
|
|
|
files.clear();
|
|
}
|
|
|
|
inline Archive::Item::Item
|
|
(
|
|
Codec* const c,
|
|
wcstring const n,
|
|
const uint s,
|
|
const uint i
|
|
)
|
|
:
|
|
codec ( c ),
|
|
name ( n ),
|
|
size ( s ),
|
|
index ( i )
|
|
{}
|
|
|
|
uint Archive::Item::Uncompress(void* const data) const
|
|
{
|
|
NST_ASSERT( data && codec && size );
|
|
return codec->Extract( index, data, size );
|
|
}
|
|
|
|
uint Archive::Find(const GenericString name) const
|
|
{
|
|
for (uint i=0, n=files.size(); i < n; ++i)
|
|
{
|
|
if (files[i].GetName() == name)
|
|
return i+1;
|
|
}
|
|
|
|
return NO_FILES;
|
|
}
|
|
|
|
uint Archive::UserSelect() const
|
|
{
|
|
if (files.empty())
|
|
{
|
|
return NO_FILES;
|
|
}
|
|
else if (files.size() == 1)
|
|
{
|
|
return FIRST_FILE;
|
|
}
|
|
else
|
|
{
|
|
std::vector<wcstring> names( files.size() );
|
|
|
|
for (uint i=0, n=names.size(); i < n; ++i)
|
|
names[i] = files[i].GetName().Ptr();
|
|
|
|
return Window::User::Choose( IDS_CHOOSE_FILE, IDS_TEXT_ABORT, &names.front(), names.size() );
|
|
}
|
|
}
|
|
|
|
uint Archive::UserSelect(const GenericString* const filter,const uint count) const
|
|
{
|
|
if (filter && count)
|
|
{
|
|
std::vector<wcstring> names;
|
|
std::vector<uint> indices;
|
|
|
|
for (Items::const_iterator it(files.begin()), end(files.end()); it != end; ++it)
|
|
{
|
|
const GenericString extension( it->GetName().Extension() );
|
|
|
|
for (uint i=0; i < count; ++i)
|
|
{
|
|
if (filter[i] == extension)
|
|
{
|
|
indices.push_back( it - files.begin() );
|
|
names.push_back( it->GetName().Ptr() );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (names.empty())
|
|
{
|
|
return NO_FILES;
|
|
}
|
|
else if (names.size() == 1)
|
|
{
|
|
return indices.front() + 1;
|
|
}
|
|
else
|
|
{
|
|
return Window::User::Choose
|
|
(
|
|
IDS_CHOOSE_FILE,
|
|
IDS_TEXT_ABORT,
|
|
&names.front(),
|
|
names.size(),
|
|
&indices.front()
|
|
);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return UserSelect();
|
|
}
|
|
}
|
|
}
|
|
}
|