From 45b0ea6f8daddd2d4940e3024940f97c6e79c372 Mon Sep 17 00:00:00 2001 From: harry Date: Mon, 24 Apr 2023 22:49:30 -0400 Subject: [PATCH] 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. --- src/CMakeLists.txt | 9 +- src/drivers/Qt/ConsoleWindow.cpp | 2 +- src/drivers/Qt/fceuWrapper.cpp | 463 +++++++++++++++++++++---------- 3 files changed, 324 insertions(+), 150 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3dde1ae0..73608e9d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -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} ) diff --git a/src/drivers/Qt/ConsoleWindow.cpp b/src/drivers/Qt/ConsoleWindow.cpp index 7e2e3da4..20a91243 100644 --- a/src/drivers/Qt/ConsoleWindow.cpp +++ b/src/drivers/Qt/ConsoleWindow.cpp @@ -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)", diff --git a/src/drivers/Qt/fceuWrapper.cpp b/src/drivers/Qt/fceuWrapper.cpp index d44900e8..9954987a 100644 --- a/src/drivers/Qt/fceuWrapper.cpp +++ b/src/drivers/Qt/fceuWrapper.cpp @@ -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 +#include + +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; }