Added cross-platform libarchive interface to allow Qt GUI to use 7zip ROM archives. Code is setup to allow for libarchive to be an optional dependency and will fallback to regular minizip if unavailable.

This commit is contained in:
harry 2023-04-24 22:49:30 -04:00
parent 084b2b9ccc
commit 45b0ea6f8d
3 changed files with 324 additions and 150 deletions

View File

@ -110,6 +110,13 @@ else(WIN32)
add_definitions( -D_SYSTEM_MINIZIP ${MINIZIP_CFLAGS} )
endif()
pkg_check_modules( LIBARCHIVE libarchive)
if ( ${LIBARCHIVE_FOUND} )
message( STATUS "Using System Libarchive Library ${LIBARCHIVE_VERSION}" )
add_definitions( -D_USE_LIBARCHIVE ${LIBARCHIVE_CFLAGS} )
endif()
pkg_check_modules( X264 x264)
if ( ${X264_FOUND} )
@ -643,7 +650,7 @@ target_link_libraries( ${APP_NAME} ${ASAN_LDFLAGS}
${${Qt}OpenGLWidgets_LIBRARIES}
${OPENGL_LDFLAGS}
${SDL2_LDFLAGS}
${MINIZIP_LDFLAGS} ${ZLIB_LIBRARIES}
${MINIZIP_LDFLAGS} ${ZLIB_LIBRARIES} ${LIBARCHIVE_LDFLAGS}
${LUA_LDFLAGS} ${X264_LDFLAGS} ${X265_LDFLAGS} ${LIBAV_LDFLAGS}
${SYS_LIBS}
)

View File

@ -2388,7 +2388,7 @@ void consoleWin_t::openROMFile(void)
QDir d;
const QStringList filters(
{ "All Useable files (*.nes *.NES *.nsf *.NSF *.fds *.FDS *.unf *.UNF *.unif *.UNIF *.zip *.ZIP)",
{ "All Useable files (*.nes *.NES *.nsf *.NSF *.fds *.FDS *.unf *.UNF *.unif *.UNIF *.zip *.ZIP, *.7z *.7zip)",
"NES files (*.nes *.NES)",
"NSF files (*.nsf *.NSF)",
"UNF files (*.unf *.UNF *.unif *.UNIF)",

View File

@ -1483,20 +1483,19 @@ int fceuWrapperUpdate( void )
return 0;
}
ArchiveScanRecord FCEUD_ScanArchive(std::string fname)
static int minizip_ScanArchive( const char *filepath, ArchiveScanRecord &rec)
{
int idx=0, ret;
unzFile zf;
unz_file_info fi;
char filename[512];
ArchiveScanRecord rec;
zf = unzOpen( fname.c_str() );
zf = unzOpen( filepath );
if ( zf == NULL )
{
//printf("Error: Failed to open Zip File: '%s'\n", fname.c_str() );
return rec;
return -1;
}
rec.type = 0;
@ -1526,18 +1525,313 @@ ArchiveScanRecord FCEUD_ScanArchive(std::string fname)
unzClose( zf );
return 0;
}
#ifdef _USE_LIBARCHIVE
#include <archive.h>
#include <archive_entry.h>
static int libarchive_ScanArchive( const char *filepath, ArchiveScanRecord &rec)
{
int r, idx=0;
struct archive *a;
struct archive_entry *entry;
a = archive_read_new();
if (a == nullptr)
{
return -1;
}
// Initialize decoders
r = archive_read_support_filter_all(a);
if (r)
{
archive_read_free(a);
return -1;
}
// Initialize formats
r = archive_read_support_format_all(a);
if (r)
{
archive_read_free(a);
return -1;
}
r = archive_read_open_filename(a, filepath, 10240);
if (r)
{
archive_read_free(a);
return -1;
}
rec.type = 1;
while (1)
{
r = archive_read_next_header(a, &entry);
if (r == ARCHIVE_EOF)
{
break;
}
else if (r != ARCHIVE_OK)
{
printf("archive_read_next_header() %s\n", archive_error_string(a));
break;
}
const char *filename = archive_entry_pathname(entry);
FCEUARCHIVEFILEINFO_ITEM item;
item.name.assign( filename );
item.size = archive_entry_size(entry);
item.index = idx; idx++;
rec.files.push_back( item );
}
rec.numFilesInArchive = idx;
archive_read_free(a);
return 0;
}
#endif
ArchiveScanRecord FCEUD_ScanArchive(std::string fname)
{
int ret = -1;
ArchiveScanRecord rec;
#ifdef _USE_LIBARCHIVE
ret = libarchive_ScanArchive( fname.c_str(), rec );
#endif
if (ret == -1)
{
minizip_ScanArchive( fname.c_str(), rec );
}
return rec;
}
FCEUFILE* FCEUD_OpenArchive(ArchiveScanRecord& asr, std::string& fname, std::string* innerFilename, int* userCancel)
static FCEUFILE* minizip_OpenArchive(ArchiveScanRecord& asr, std::string &fname, std::string *searchFile, int innerIndex )
{
int ret;
FCEUFILE* fp = 0;
int ret, idx=0;
FCEUFILE* fp = nullptr;
void *tmpMem = nullptr;
unzFile zf;
unz_file_info fi;
char filename[512];
char foundFile = 0;
void *tmpMem = NULL;
bool foundFile = false;
zf = unzOpen( fname.c_str() );
if ( zf == NULL )
{
//printf("Error: Failed to open Zip File: '%s'\n", fname.c_str() );
return fp;
}
//printf("Searching for %s in %s \n", searchFile.c_str(), fname.c_str() );
ret = unzGoToFirstFile( zf );
//printf("unzGoToFirstFile: %i \n", ret );
while ( ret == 0 )
{
unzGetCurrentFileInfo( zf, &fi, filename, sizeof(filename), NULL, 0, NULL, 0 );
//printf("Filename: %u '%s' \n", fi.uncompressed_size, filename );
if (searchFile)
{
if ( strcmp( searchFile->c_str(), filename ) == 0 )
{
//printf("Found Filename: %u '%s' \n", fi.uncompressed_size, filename );
foundFile = true; break;
}
}
else if ((innerIndex != -1) && (idx == innerIndex))
{
foundFile = true; break;
}
ret = unzGoToNextFile( zf );
//printf("unzGoToNextFile: %i \n", ret );
idx++;
}
if ( !foundFile )
{
unzClose( zf );
return fp;
}
tmpMem = ::malloc( fi.uncompressed_size );
if ( tmpMem == NULL )
{
unzClose( zf );
return fp;
}
//printf("Loading via minizip\n");
EMUFILE_MEMORY* ms = new EMUFILE_MEMORY(fi.uncompressed_size);
unzOpenCurrentFile( zf );
unzReadCurrentFile( zf, tmpMem, fi.uncompressed_size );
unzCloseCurrentFile( zf );
ms->fwrite( tmpMem, fi.uncompressed_size );
free( tmpMem );
//if we extracted the file correctly
fp = new FCEUFILE();
fp->archiveFilename = fname;
fp->filename = filename;
fp->fullFilename = fp->archiveFilename + "|" + fp->filename;
fp->archiveIndex = idx;
fp->mode = FCEUFILE::READ;
fp->size = fi.uncompressed_size;
fp->stream = ms;
fp->archiveCount = (int)asr.numFilesInArchive;
ms->fseek(0,SEEK_SET); //rewind so that the rom analyzer sees a freshly opened file
unzClose( zf );
return fp;
}
#ifdef _USE_LIBARCHIVE
static FCEUFILE* libarchive_OpenArchive( ArchiveScanRecord& asr, std::string& fname, std::string *searchFile, int innerIndex)
{
int r, idx=0;
struct archive *a;
struct archive_entry *entry;
const char *filename = nullptr;
bool foundFile = false;
int fileSize = 0;
FCEUFILE* fp = nullptr;
a = archive_read_new();
if (a == nullptr)
{
archive_read_free(a);
return nullptr;
}
// Initialize decoders
r = archive_read_support_filter_all(a);
if (r)
{
archive_read_free(a);
return nullptr;
}
// Initialize formats
r = archive_read_support_format_all(a);
if (r)
{
archive_read_free(a);
return nullptr;
}
r = archive_read_open_filename(a, fname.c_str(), 10240);
if (r)
{
archive_read_free(a);
return nullptr;
}
while (1)
{
r = archive_read_next_header(a, &entry);
if (r == ARCHIVE_EOF)
{
break;
}
else if (r != ARCHIVE_OK)
{
printf("archive_read_next_header() %s\n", archive_error_string(a));
break;
}
filename = archive_entry_pathname(entry);
fileSize = archive_entry_size(entry);
if (searchFile)
{
if (strcmp( filename, searchFile->c_str() ) == 0)
{
foundFile = true; break;
}
}
else if ((innerIndex != -1) && (idx == innerIndex))
{
foundFile = true; break;
}
idx++;
}
if (foundFile && (fileSize > 0))
{
const void *buff;
size_t size, totalSize = 0;
#if ARCHIVE_VERSION_NUMBER >= 3000000
int64_t offset;
#else
off_t offset;
#endif
//printf("Loading via libarchive\n");
EMUFILE_MEMORY* ms = new EMUFILE_MEMORY(fileSize);
while (1)
{
r = archive_read_data_block(a, &buff, &size, &offset);
if (r == ARCHIVE_EOF)
{
break;
}
if (r != ARCHIVE_OK)
{
break;
}
//printf("Read: %p Size:%zu Offset:%llu\n", buff, size, (long long int)offset);
ms->fwrite( buff, size );
totalSize += size;
}
//if we extracted the file correctly
fp = new FCEUFILE();
fp->archiveFilename = fname;
fp->filename = filename;
fp->fullFilename = fp->archiveFilename + "|" + fp->filename;
fp->archiveIndex = idx;
fp->mode = FCEUFILE::READ;
fp->size = totalSize;
fp->stream = ms;
fp->archiveCount = (int)asr.numFilesInArchive;
ms->fseek(0,SEEK_SET); //rewind so that the rom analyzer sees a freshly opened file
}
archive_read_free(a);
return fp;
}
#endif
FCEUFILE* FCEUD_OpenArchive(ArchiveScanRecord& asr, std::string& fname, std::string* innerFilename, int* userCancel)
{
FCEUFILE* fp = nullptr;
std::string searchFile;
if ( innerFilename != NULL )
@ -1587,75 +1881,14 @@ FCEUFILE* FCEUD_OpenArchive(ArchiveScanRecord& asr, std::string& fname, std::str
}
}
zf = unzOpen( fname.c_str() );
#ifdef _USE_LIBARCHIVE
fp = libarchive_OpenArchive(asr, fname, &searchFile, -1 );
#endif
if ( zf == NULL )
if (fp == nullptr)
{
//printf("Error: Failed to open Zip File: '%s'\n", fname.c_str() );
return fp;
fp = minizip_OpenArchive(asr, fname, &searchFile, -1 );
}
//printf("Searching for %s in %s \n", searchFile.c_str(), fname.c_str() );
ret = unzGoToFirstFile( zf );
//printf("unzGoToFirstFile: %i \n", ret );
while ( ret == 0 )
{
unzGetCurrentFileInfo( zf, &fi, filename, sizeof(filename), NULL, 0, NULL, 0 );
//printf("Filename: %u '%s' \n", fi.uncompressed_size, filename );
if ( strcmp( searchFile.c_str(), filename ) == 0 )
{
//printf("Found Filename: %u '%s' \n", fi.uncompressed_size, filename );
foundFile = 1; break;
}
ret = unzGoToNextFile( zf );
//printf("unzGoToNextFile: %i \n", ret );
}
if ( !foundFile )
{
unzClose( zf );
return fp;
}
tmpMem = ::malloc( fi.uncompressed_size );
if ( tmpMem == NULL )
{
unzClose( zf );
return fp;
}
EMUFILE_MEMORY* ms = new EMUFILE_MEMORY(fi.uncompressed_size);
unzOpenCurrentFile( zf );
unzReadCurrentFile( zf, tmpMem, fi.uncompressed_size );
unzCloseCurrentFile( zf );
ms->fwrite( tmpMem, fi.uncompressed_size );
free( tmpMem );
//if we extracted the file correctly
fp = new FCEUFILE();
fp->archiveFilename = fname;
fp->filename = searchFile;
fp->fullFilename = fp->archiveFilename + "|" + fp->filename;
fp->archiveIndex = ret;
fp->mode = FCEUFILE::READ;
fp->size = fi.uncompressed_size;
fp->stream = ms;
fp->archiveCount = (int)asr.numFilesInArchive;
ms->fseek(0,SEEK_SET); //rewind so that the rom analyzer sees a freshly opened file
unzClose( zf );
return fp;
}
@ -1668,82 +1901,16 @@ FCEUFILE* FCEUD_OpenArchive(ArchiveScanRecord& asr, std::string& fname, std::str
FCEUFILE* FCEUD_OpenArchiveIndex(ArchiveScanRecord& asr, std::string &fname, int innerIndex, int* userCancel)
{
int ret, idx=0;
FCEUFILE* fp = 0;
unzFile zf;
unz_file_info fi;
char filename[512];
char foundFile = 0;
void *tmpMem = NULL;
FCEUFILE* fp = nullptr;
zf = unzOpen( fname.c_str() );
if ( zf == NULL )
#ifdef _USE_LIBARCHIVE
fp = libarchive_OpenArchive( asr, fname, nullptr, innerIndex );
#endif
if (fp == nullptr)
{
//printf("Error: Failed to open Zip File: '%s'\n", fname.c_str() );
return fp;
fp = minizip_OpenArchive(asr, fname, nullptr, innerIndex);
}
ret = unzGoToFirstFile( zf );
//printf("unzGoToFirstFile: %i \n", ret );
while ( ret == 0 )
{
unzGetCurrentFileInfo( zf, &fi, filename, sizeof(filename), NULL, 0, NULL, 0 );
//printf("Filename: %u '%s' \n", fi.uncompressed_size, filename );
if ( idx == innerIndex )
{
//printf("Found Filename: %u '%s' \n", fi.uncompressed_size, filename );
foundFile = 1; break;
}
idx++;
ret = unzGoToNextFile( zf );
//printf("unzGoToNextFile: %i \n", ret );
}
if ( !foundFile )
{
unzClose( zf );
return fp;
}
tmpMem = ::malloc( fi.uncompressed_size );
if ( tmpMem == NULL )
{
unzClose( zf );
return fp;
}
EMUFILE_MEMORY* ms = new EMUFILE_MEMORY(fi.uncompressed_size);
unzOpenCurrentFile( zf );
unzReadCurrentFile( zf, tmpMem, fi.uncompressed_size );
unzCloseCurrentFile( zf );
ms->fwrite( tmpMem, fi.uncompressed_size );
free( tmpMem );
//if we extracted the file correctly
fp = new FCEUFILE();
fp->archiveFilename = fname;
fp->filename = filename;
fp->fullFilename = fp->archiveFilename + "|" + fp->filename;
fp->archiveIndex = ret;
fp->mode = FCEUFILE::READ;
fp->size = fi.uncompressed_size;
fp->stream = ms;
fp->archiveCount = (int)asr.numFilesInArchive;
ms->fseek(0,SEEK_SET); //rewind so that the rom analyzer sees a freshly opened file
unzClose( zf );
return fp;
}