diff --git a/.ci/linux/build.sh b/.ci/linux/build.sh index aa15333ac2..093b30a7ea 100755 --- a/.ci/linux/build.sh +++ b/.ci/linux/build.sh @@ -36,15 +36,16 @@ case "$1" in ARCH=armv9 ARCH_FLAGS="-march=armv9-a -mtune=generic -w" ;; + *) + echo "Invalid target $1 specified, must be one of amd64, steamdeck, allyx, rog-ally, legacy, aarch64, armv9" + exit 1 + ;; esac export ARCH_FLAGS="$ARCH_FLAGS -O3" -NPROC="$2" if [ -z "$NPROC" ]; then NPROC="$(nproc)" -else - shift fi if [ "$1" != "" ]; then shift; fi @@ -72,11 +73,15 @@ else MULTIMEDIA=ON fi +if [ -z "$BUILD_TYPE" ]; then + export BUILD_TYPE="Release" +fi + export EXTRA_CMAKE_FLAGS=("${EXTRA_CMAKE_FLAGS[@]}" $@) mkdir -p build && cd build cmake .. -G Ninja \ - -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_BUILD_TYPE="$BUILD_TYPE" \ -DENABLE_QT_TRANSLATION=ON \ -DUSE_DISCORD_PRESENCE=ON \ -DCMAKE_CXX_FLAGS="$ARCH_FLAGS" \ diff --git a/.ci/windows/build.sh b/.ci/windows/build.sh index 667fd316fa..d0c697655a 100644 --- a/.ci/windows/build.sh +++ b/.ci/windows/build.sh @@ -17,6 +17,10 @@ else export EXTRA_CMAKE_FLAGS=("${EXTRA_CMAKE_FLAGS[@]}" -DYUZU_USE_BUNDLED_QT=OFF) fi +if [ -z "$BUILD_TYPE" ]; then + export BUILD_TYPE="Release" +fi + if [ "$WINDEPLOYQT" == "" ]; then echo "You must supply the WINDEPLOYQT environment variable." exit 1 @@ -38,7 +42,7 @@ export EXTRA_CMAKE_FLAGS=("${EXTRA_CMAKE_FLAGS[@]}" $@) mkdir -p build && cd build cmake .. -G Ninja \ - -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_BUILD_TYPE="$BUILD_TYPE" \ -DENABLE_QT_TRANSLATION=ON \ -DUSE_DISCORD_PRESENCE=ON \ -DYUZU_USE_BUNDLED_SDL2=OFF \ diff --git a/.gitignore b/.gitignore index 9aaf549512..4417d3f132 100644 --- a/.gitignore +++ b/.gitignore @@ -52,4 +52,3 @@ Thumbs.db eden-windows-msvc artifacts *.AppImage* -*.patch diff --git a/.patch/quazip/0001-strict.patch b/.patch/quazip/0001-strict.patch new file mode 100644 index 0000000000..8283497230 --- /dev/null +++ b/.patch/quazip/0001-strict.patch @@ -0,0 +1,80 @@ +diff --git a/quazip/quazipdir.cpp b/quazip/quazipdir.cpp +index d43f1c1..eb24bf1 100644 +--- a/quazip/quazipdir.cpp ++++ b/quazip/quazipdir.cpp +@@ -293,8 +293,8 @@ bool QuaZipDirComparator::operator()(const QuaZipFileInfo64 &info1, + } + + template +-bool QuaZipDirPrivate::entryInfoList(QStringList nameFilters, +- QDir::Filters filter, QDir::SortFlags sort, TFileInfoList &result) const ++bool QuaZipDirPrivate::entryInfoList(QStringList _nameFilters, ++ QDir::Filters _filter, QDir::SortFlags sort, TFileInfoList &result) const + { + QString basePath = simplePath(); + if (!basePath.isEmpty()) +@@ -305,12 +305,12 @@ bool QuaZipDirPrivate::entryInfoList(QStringList nameFilters, + if (!zip->goToFirstFile()) { + return zip->getZipError() == UNZ_OK; + } +- QDir::Filters fltr = filter; ++ QDir::Filters fltr = _filter; + if (fltr == QDir::NoFilter) + fltr = this->filter; + if (fltr == QDir::NoFilter) + fltr = QDir::AllEntries; +- QStringList nmfltr = nameFilters; ++ QStringList nmfltr = _nameFilters; + if (nmfltr.isEmpty()) + nmfltr = this->nameFilters; + QSet dirsFound; +diff --git a/quazip/quazipfile.cpp b/quazip/quazipfile.cpp +index 4a5f2f9..f7865f5 100644 +--- a/quazip/quazipfile.cpp ++++ b/quazip/quazipfile.cpp +@@ -241,14 +241,14 @@ void QuaZipFile::setFileName(const QString& fileName, QuaZip::CaseSensitivity cs + p->caseSensitivity=cs; + } + +-void QuaZipFilePrivate::setZipError(int zipError) const ++void QuaZipFilePrivate::setZipError(int _zipError) const + { + QuaZipFilePrivate *fakeThis = const_cast(this); // non-const +- fakeThis->zipError=zipError; +- if(zipError==UNZ_OK) ++ fakeThis->zipError = _zipError; ++ if(_zipError == UNZ_OK) + q->setErrorString(QString()); + else +- q->setErrorString(QuaZipFile::tr("ZIP/UNZIP API error %1").arg(zipError)); ++ q->setErrorString(QuaZipFile::tr("ZIP/UNZIP API error %1").arg(_zipError)); + } + + bool QuaZipFile::open(OpenMode mode) +diff --git a/quazip/unzip.c b/quazip/unzip.c +index a39365d..ee7b487 100644 +--- a/quazip/unzip.c ++++ b/quazip/unzip.c +@@ -1054,7 +1054,7 @@ local int unz64local_GetCurrentFileInfoInternal (unzFile file, + /* ZIP64 extra fields */ + if (headerId == 0x0001) + { +- uLong uL; ++ uLong _uL; + + if(file_info.uncompressed_size == (ZPOS64_T)0xFFFFFFFFu) + { +@@ -1078,7 +1078,7 @@ local int unz64local_GetCurrentFileInfoInternal (unzFile file, + if(file_info.disk_num_start == 0xFFFFFFFFu) + { + /* Disk Start Number */ +- if (unz64local_getLong(&s->z_filefunc, s->filestream,&uL) != UNZ_OK) ++ if (unz64local_getLong(&s->z_filefunc, s->filestream, &_uL) != UNZ_OK) + err=UNZ_ERRNO; + } + +@@ -2151,3 +2151,4 @@ int ZEXPORT unzClearFlags(unzFile file, unsigned flags) + s->flags &= ~flags; + return UNZ_OK; + } ++ diff --git a/.patch/quazip/0002-oldstyle.patch b/.patch/quazip/0002-oldstyle.patch new file mode 100644 index 0000000000..2694128f04 --- /dev/null +++ b/.patch/quazip/0002-oldstyle.patch @@ -0,0 +1,26 @@ +diff --git a/quazip/minizip_crypt.h b/quazip/minizip_crypt.h +index 2e833f7..ea9d277 100644 +--- a/quazip/minizip_crypt.h ++++ b/quazip/minizip_crypt.h +@@ -90,13 +90,14 @@ static void init_keys(const char* passwd,unsigned long* pkeys,const z_crc_t FAR + # define ZCR_SEED2 3141592654UL /* use PI as default pattern */ + # endif + +-static int crypthead(passwd, buf, bufSize, pkeys, pcrc_32_tab, crcForCrypting) +- const char *passwd; /* password string */ +- unsigned char *buf; /* where to write header */ +- int bufSize; +- unsigned long* pkeys; +- const z_crc_t FAR * pcrc_32_tab; +- unsigned long crcForCrypting; ++static int crypthead( ++ const char *passwd, /* password string */ ++ unsigned char *buf, /* where to write header */ ++ int bufSize, ++ unsigned long* pkeys, ++ const z_crc_t FAR * pcrc_32_tab, ++ unsigned long crcForCrypting ++) + { + int n; /* index in random header */ + int t; /* temporary */ diff --git a/.patch/quazip/0003-predecls.patch b/.patch/quazip/0003-predecls.patch new file mode 100644 index 0000000000..ec3414c82a --- /dev/null +++ b/.patch/quazip/0003-predecls.patch @@ -0,0 +1,19 @@ +diff --git a/quazip/zip.c b/quazip/zip.c +index 7788b88..f4e21aa 100644 +--- a/quazip/zip.c ++++ b/quazip/zip.c +@@ -645,6 +645,14 @@ local ZPOS64_T zip64local_SearchCentralDir64(const zlib_filefunc64_32_def* pzlib + return relativeOffset; + } + ++// compilers hate this ONE SIMPLE TRICK! ++static int LoadCentralDirectoryRecord(zip64_internal* pziinit); ++static int Write_LocalFileHeader(zip64_internal* zi, const char* filename, uInt size_extrafield_local, const void* extrafield_local, uLong version_to_extract); ++static int Write_Zip64EndOfCentralDirectoryLocator(zip64_internal* zi, ZPOS64_T zip64eocd_pos_inzip); ++static int Write_Zip64EndOfCentralDirectoryRecord(zip64_internal* zi, uLong size_centraldir, ZPOS64_T centraldir_pos_inzip); ++static int Write_EndOfCentralDirectoryRecord(zip64_internal* zi, uLong size_centraldir, ZPOS64_T centraldir_pos_inzip); ++static int Write_GlobalComment(zip64_internal* zi, const char* global_comment); ++ + int LoadCentralDirectoryRecord(zip64_internal* pziinit) + { + int err=ZIP_OK; diff --git a/.patch/quazip/0004-qt6-only.patch b/.patch/quazip/0004-qt6-only.patch new file mode 100644 index 0000000000..8906df2472 --- /dev/null +++ b/.patch/quazip/0004-qt6-only.patch @@ -0,0 +1,400 @@ +"Debloats" QuaZip by removing some unneeded stuff (Qt <6, bzip2, emscripten...) + +This is completely optional. + +diff --git a/CMakeLists.txt b/CMakeLists.txt +index b376fb2..4aac4ec 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -3,64 +3,16 @@ cmake_minimum_required(VERSION 3.15...3.18) + + project(QuaZip VERSION 1.5) + +-include(cmake/clone-repo.cmake) +- + set(QUAZIP_LIB_VERSION ${QuaZip_VERSION}) + set(QUAZIP_LIB_SOVERSION 1.5.0) + +-if(EMSCRIPTEN) +- #option(ZLIB_INCLUDE "Path to include dir" "") +- #option(ZLIB_LIBRARY "Path to library dir" "") +- option(BUILD_SHARED_LIBS "" OFF) +- option(QUAZIP_INSTALL "" OFF) +- option(QUAZIP_USE_QT_ZLIB "" OFF) +- option(QUAZIP_ENABLE_TESTS "Build QuaZip tests" OFF) +-else() +- option(BUILD_SHARED_LIBS "" ON) +- option(QUAZIP_INSTALL "" ON) +- option(QUAZIP_USE_QT_ZLIB "" OFF) +- option(QUAZIP_ENABLE_TESTS "Build QuaZip tests" OFF) +-endif() ++option(BUILD_SHARED_LIBS "" ON) ++option(QUAZIP_INSTALL "" ON) ++option(QUAZIP_ENABLE_TESTS "Build QuaZip tests" OFF) + + OPTION(ZLIB_CONST "Sets ZLIB_CONST preprocessor definition" OFF) + +-# Make BZIP2 optional +-option(QUAZIP_BZIP2 "Enables BZIP2 compression" ON) +-option(QUAZIP_BZIP2_STDIO "Output BZIP2 errors to stdio" ON) +- +-option(QUAZIP_FETCH_LIBS "Enables fetching third-party libraries if not found" ${WIN32}) +-option(QUAZIP_FORCE_FETCH_LIBS "Enables fetching third-party libraries always" OFF) +- +-if (QUAZIP_USE_QT_ZLIB AND BUILD_SHARED_LIBS) +- message(FATAL_ERROR "Using BUILD_SHARED_LIBS=ON together with QUAZIP_USE_QT_ZLIB=ON is not supported." ) +-endif() +- +-# Set the default value of `${QUAZIP_QT_MAJOR_VERSION}`. +-# We search quietly for Qt6, Qt5 and Qt4 in that order. +-# Qt6 and Qt5 provide config files for CMake. +-# Qt4 relies on `FindQt4.cmake`. +-find_package( +- QT NAMES Qt6 Qt5 +- QUIET COMPONENTS Core +-) +-if (NOT QT_FOUND) +- find_package(Qt4 QUIET COMPONENTS QtCore) +- if (Qt4_FOUND) +- set(QT_VERSION_MAJOR 4) +- else() +- # If neither 6, 5 nor 4 are found, we default to 5. +- # The setup will fail further down. +- set(QT_VERSION_MAJOR 5) +- endif() +-endif() +- +-set(QUAZIP_QT_MAJOR_VERSION ${QT_VERSION_MAJOR} CACHE STRING "Qt version to use (4, 5 or 6), defaults to ${QT_VERSION_MAJOR}") +- +-if (QUAZIP_QT_MAJOR_VERSION EQUAL 6) +- set(CMAKE_CXX_STANDARD 17) +-else() +- set(CMAKE_CXX_STANDARD 14) +-endif() ++set(CMAKE_CXX_STANDARD 17) + + if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE RELEASE) +@@ -77,92 +29,17 @@ set(QUAZIP_LIB_TARGET_NAME QuaZip) + set(QUAZIP_DIR_NAME QuaZip-Qt${QUAZIP_QT_MAJOR_VERSION}-${QUAZIP_LIB_VERSION}) + set(QUAZIP_PACKAGE_NAME QuaZip-Qt${QUAZIP_QT_MAJOR_VERSION}) + +-message(STATUS "QUAZIP_QT_MAJOR_VERSION set to ${QUAZIP_QT_MAJOR_VERSION}") +-message(STATUS "CMAKE_CXX_STANDARD set to ${CMAKE_CXX_STANDARD}") +- +-if(QUAZIP_QT_MAJOR_VERSION EQUAL 6) +- find_package(Qt6 REQUIRED COMPONENTS Core Core5Compat +- OPTIONAL_COMPONENTS Network Test) +- message(STATUS "Found Qt version ${Qt6_VERSION} at ${Qt6_DIR}") +- set(QUAZIP_QT_ZLIB_COMPONENT BundledZLIB) +- set(QUAZIP_QT_ZLIB_HEADER_COMPONENT ZlibPrivate) +- set(QUAZIP_LIB_LIBRARIES Qt6::Core Qt6::Core5Compat) +- set(QUAZIP_TEST_QT_LIBRARIES Qt6::Core Qt6::Core5Compat Qt6::Network Qt6::Test) +- set(QUAZIP_PKGCONFIG_REQUIRES "zlib, Qt6Core") +-elseif(QUAZIP_QT_MAJOR_VERSION EQUAL 5) +- find_package(Qt5 REQUIRED COMPONENTS Core +- OPTIONAL_COMPONENTS Network Test) +- message(STATUS "Found Qt version ${Qt5_VERSION} at ${Qt5_DIR}") +- set(QUAZIP_QT_ZLIB_COMPONENT Zlib) +- set(QUAZIP_LIB_LIBRARIES Qt5::Core) +- set(QUAZIP_TEST_QT_LIBRARIES Qt5::Core Qt5::Network Qt5::Test) +- set(QUAZIP_PKGCONFIG_REQUIRES "zlib, Qt5Core") +-elseif(QUAZIP_QT_MAJOR_VERSION EQUAL 4) +- find_package(Qt4 4.5.0 REQUIRED COMPONENTS QtCore +- OPTIONAL_COMPONENTS QtNetwork QtTest) +- set(QUAZIP_QT_ZLIB_COMPONENT Zlib) +- set(QUAZIP_LIB_LIBRARIES Qt4::QtCore) +- set(QUAZIP_TEST_QT_LIBRARIES Qt4::QtCore Qt4::QtNetwork Qt4::QtTest) +- set(QUAZIP_PKGCONFIG_REQUIRES "zlib, QtCore") +-else() +- message(FATAL_ERROR "Qt version ${QUAZIP_QT_MAJOR_VERSION} is not supported") +-endif() +- +-message(STATUS "Using Qt version ${QUAZIP_QT_MAJOR_VERSION}") +- +-set(QUAZIP_QT_ZLIB_USED OFF) +-if(QUAZIP_USE_QT_ZLIB) +- find_package(Qt${QUAZIP_QT_MAJOR_VERSION} OPTIONAL_COMPONENTS ${QUAZIP_QT_ZLIB_COMPONENT}) +- set(QUAZIP_QT_ZLIB_COMPONENT_FOUND Qt${QUAZIP_QT_MAJOR_VERSION}${QUAZIP_QT_ZLIB_COMPONENT}_FOUND) +- if (DEFINED QUAZIP_QT_ZLIB_HEADER_COMPONENT) +- find_package(Qt${QUAZIP_QT_MAJOR_VERSION} OPTIONAL_COMPONENTS ${QUAZIP_QT_ZLIB_HEADER_COMPONENT}) +- set(QUAZIP_QT_ZLIB_HEADER_COMPONENT_FOUND Qt${QUAZIP_QT_MAJOR_VERSION}${QUAZIP_QT_ZLIB_HEADER_COMPONENT}_FOUND) +- else() +- set(QUAZIP_QT_ZLIB_HEADER_COMPONENT_FOUND ON) +- endif() +- if(QUAZIP_QT_ZLIB_COMPONENT_FOUND AND QUAZIP_QT_ZLIB_HEADER_COMPONENT_FOUND) +- message(STATUS "Qt component ${QUAZIP_QT_ZLIB_COMPONENT} found") +- set(QUAZIP_LIB_LIBRARIES ${QUAZIP_LIB_LIBRARIES} Qt${QUAZIP_QT_MAJOR_VERSION}::${QUAZIP_QT_ZLIB_COMPONENT}) +- if(DEFINED QUAZIP_QT_ZLIB_HEADER_COMPONENT) +- message(STATUS "Qt component ${QUAZIP_QT_ZLIB_HEADER_COMPONENT} found") +- set(QUAZIP_LIB_LIBRARIES ${QUAZIP_LIB_LIBRARIES} Qt${QUAZIP_QT_MAJOR_VERSION}::${QUAZIP_QT_ZLIB_HEADER_COMPONENT}) +- endif() +- set(QUAZIP_QT_ZLIB_USED ON) +- else() +- message(FATAL_ERROR "QUAZIP_USE_QT_ZLIB was set but bundled zlib was not found. Terminating to prevent accidental linking to system libraries.") +- endif() +-endif() +- +-if(QUAZIP_QT_ZLIB_USED AND QUAZIP_QT_ZLIB_COMPONENT STREQUAL BundledZLIB) +- # Qt's new BundledZLIB uses z-prefix in zlib +- add_compile_definitions(Z_PREFIX) +-endif() +- +-if(NOT QUAZIP_QT_ZLIB_USED) +- +- if(EMSCRIPTEN) +- if(NOT DEFINED ZLIB_LIBRARY) +- message(WARNING "ZLIB_LIBRARY is not set") +- endif() ++find_package(Qt6 REQUIRED COMPONENTS Core Core5Compat ++ OPTIONAL_COMPONENTS Network Test) ++message(STATUS "Found Qt version ${Qt6_VERSION} at ${Qt6_DIR}") ++set(QUAZIP_QT_ZLIB_COMPONENT BundledZLIB) ++set(QUAZIP_QT_ZLIB_HEADER_COMPONENT ZlibPrivate) ++set(QUAZIP_LIB_LIBRARIES Qt6::Core Qt6::Core5Compat) ++set(QUAZIP_TEST_QT_LIBRARIES Qt6::Core Qt6::Core5Compat Qt6::Network Qt6::Test) ++set(QUAZIP_PKGCONFIG_REQUIRES "zlib, Qt6Core") + +- if(NOT DEFINED ZLIB_INCLUDE) +- message(WARNING "ZLIB_INCLUDE is not set") +- else() +- include_directories(${ZLIB_INCLUDE}) +- endif() +- +- if(NOT DEFINED ZCONF_INCLUDE) +- message(WARNING "ZCONF_INCLUDE is not set") +- else() +- include_directories(${ZCONF_INCLUDE}) +- endif() +- +- set(QUAZIP_LIB_LIBRARIES ${QUAZIP_LIB_LIBRARIES} ${ZLIB_LIBRARY}) +- else() +- find_package(ZLIB REQUIRED) +- set(QUAZIP_LIB_LIBRARIES ${QUAZIP_LIB_LIBRARIES} ZLIB::ZLIB) +- endif() +-endif() ++find_package(ZLIB REQUIRED) ++set(QUAZIP_LIB_LIBRARIES ${QUAZIP_LIB_LIBRARIES} ZLIB::ZLIB) + + if (ZLIB_CONST) + add_compile_definitions(ZLIB_CONST) +@@ -173,65 +50,4 @@ set(QUAZIP_INC) + set(QUAZIP_LIB) + set(QUAZIP_LBD) + +-if(QUAZIP_BZIP2) +- # Check if bzip2 is present +- set(QUAZIP_BZIP2 ON) +- +- if(NOT QUAZIP_FORCE_FETCH_LIBS) +- find_package(BZip2 QUIET) +- endif() +- +- if(BZIP2_FOUND AND NOT QUAZIP_FORCE_FETCH_LIBS) +- message(STATUS "Using BZIP2 ${BZIP2_VERSION_STRING}") +- +- list(APPEND QUAZIP_INC ${BZIP2_INCLUDE_DIRS}) +- list(APPEND QUAZIP_LIB ${BZIP2_LIBRARIES}) +- list(APPEND QUAZIP_LBD ${BZIP2_LIBRARY_DIRS}) +- +- set(PC_PRIVATE_LIBS "${PC_PRIVATE_LIBS} -lbzip2") +- elseif(QUAZIP_FETCH_LIBS) +- clone_repo(bzip2 https://sourceware.org/git/bzip2.git) +- +- # BZip2 repository does not support cmake so we have to create +- # the bzip2 library ourselves +- set(BZIP2_SRC +- ${BZIP2_SOURCE_DIR}/blocksort.c +- ${BZIP2_SOURCE_DIR}/bzlib.c +- ${BZIP2_SOURCE_DIR}/compress.c +- ${BZIP2_SOURCE_DIR}/crctable.c +- ${BZIP2_SOURCE_DIR}/decompress.c +- ${BZIP2_SOURCE_DIR}/huffman.c +- ${BZIP2_SOURCE_DIR}/randtable.c) +- +- set(BZIP2_HDR +- ${BZIP2_SOURCE_DIR}/bzlib.h +- ${BZIP2_SOURCE_DIR}/bzlib_private.h) +- +- add_library(bzip2 STATIC ${BZIP2_SRC} ${BZIP2_HDR}) +- +- if(NOT QUAZIP_BZIP2_STDIO) +- target_compile_definitions(bzip2 PRIVATE -DBZ_NO_STDIO) +- endif() +- +- list(APPEND QUAZIP_DEP bzip2) +- list(APPEND QUAZIP_LIB bzip2) +- list(APPEND QUAZIP_INC ${BZIP2_SOURCE_DIR}) +- else() +- message(STATUS "BZip2 library not found") +- +- set(QUAZIP_BZIP2 OFF) +- endif() +- +- if(QUAZIP_BZIP2) +- find_package(BZip2) +- add_compile_definitions(HAVE_BZIP2) +- endif() +-endif() +- + add_subdirectory(quazip) +- +-if(QUAZIP_ENABLE_TESTS) +- message(STATUS "Building QuaZip tests") +- enable_testing() +- add_subdirectory(qztest) +-endif() +diff --git a/quazip/CMakeLists.txt b/quazip/CMakeLists.txt +index 6cfdf4e..66bc4cb 100644 +--- a/quazip/CMakeLists.txt ++++ b/quazip/CMakeLists.txt +@@ -46,10 +46,6 @@ set(QUAZIP_INCLUDE_PATH ${QUAZIP_DIR_NAME}/quazip) + set(QUAZIP_INSTALL_CONFIGDIR ${CMAKE_INSTALL_LIBDIR}/cmake) + set(QUAZIP_PKGCONFIG_NAME quazip${QuaZip_VERSION_MAJOR}-qt${QUAZIP_QT_MAJOR_VERSION}) + +-if(EMSCRIPTEN) +- set(BUILD_SHARED_LIBS OFF) +-endif() +- + add_library(${QUAZIP_LIB_TARGET_NAME} ${QUAZIP_SOURCES}) + add_library(QuaZip::QuaZip ALIAS ${QUAZIP_LIB_TARGET_NAME}) + +diff --git a/quazip/quazip_qt_compat.h b/quazip/quazip_qt_compat.h +index 0dde011..41f9dd1 100644 +--- a/quazip/quazip_qt_compat.h ++++ b/quazip/quazip_qt_compat.h +@@ -14,16 +14,11 @@ + + // Legacy encodings are still everywhere, but the Qt team decided we + // don't need them anymore and moved them out of Core in Qt 6. +-#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) +-# include +-#else +-# include +-#endif ++#include + + // QSaveFile terribly breaks the is-a idiom (Liskov substitution principle): + // QSaveFile is-a QIODevice, but it makes close() private and aborts + // if you call it through the base class. Hence this ugly hack: +-#if (QT_VERSION >= 0x050100) + #include + inline bool quazip_close(QIODevice *device) { + QSaveFile *file = qobject_cast(device); +@@ -34,74 +29,35 @@ inline bool quazip_close(QIODevice *device) { + device->close(); + return true; + } +-#else +-inline bool quazip_close(QIODevice *device) { +- device->close(); +- return true; +-} +-#endif + +-// this is yet another stupid move and deprecation +-#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) + using Qt::SkipEmptyParts; +-#else +-#include +-const auto SkipEmptyParts = QString::SplitBehavior::SkipEmptyParts; +-#endif + + // and yet another... (why didn't they just make qSort delegate to std::sort?) + #include +-#if (QT_VERSION >= QT_VERSION_CHECK(5, 2, 0)) + #include + template + inline void quazip_sort(T begin, T end, C comparator) { + std::sort(begin, end, comparator); + } +-#else +-#include +-template +-inline void quazip_sort(T begin, T end, C comparator) { +- qSort(begin, end, comparator); +-} +-#endif + + // this is a stupid rename... + #include + #include +-#if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)) + inline QDateTime quazip_ctime(const QFileInfo &fi) { + return fi.birthTime(); + } +-#else +-inline QDateTime quazip_ctime(const QFileInfo &fi) { +- return fi.created(); +-} +-#endif + + // this is just a slightly better alternative + #include +-#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) + inline bool quazip_is_symlink(const QFileInfo &fi) { + return fi.isSymbolicLink(); + } +-#else +-inline bool quazip_is_symlink(const QFileInfo &fi) { +- // also detects *.lnk on Windows, but better than nothing +- return fi.isSymLink(); +-} +-#endif + + // I'm not even sure what this one is, but nevertheless + #include +-#if (QT_VERSION >= QT_VERSION_CHECK(5, 13, 0)) + inline QString quazip_symlink_target(const QFileInfo &fi) { + return fi.symLinkTarget(); + } +-#else +-inline QString quazip_symlink_target(const QFileInfo &fi) { +- return fi.readLink(); // What's the difference? I've no idea. +-} +-#endif + + // deprecation + #if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0) +@@ -125,40 +81,19 @@ inline QDateTime quazip_since_epoch_ntfs() { + + // this is not a deprecation but an improvement, for a change + #include +-#if (QT_VERSION >= 0x040700) + inline quint64 quazip_ntfs_ticks(const QDateTime &time, int fineTicks) { + QDateTime base = quazip_since_epoch_ntfs(); + return base.msecsTo(time) * 10000 + fineTicks; + } +-#else +-inline quint64 quazip_ntfs_ticks(const QDateTime &time, int fineTicks) { +- QDateTime base = quazip_since_epoch_ntfs(); +- QDateTime utc = time.toUTC(); +- return (static_cast(base.date().daysTo(utc.date())) +- * Q_INT64_C(86400000) +- + static_cast(base.time().msecsTo(utc.time()))) +- * Q_INT64_C(10000) + fineTicks; +-} +-#endif + + // yet another improvement... + #include +-#if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) // Yay! Finally a way to get time as qint64! + inline qint64 quazip_to_time64_t(const QDateTime &time) { + return time.toSecsSinceEpoch(); + } +-#else +-inline qint64 quazip_to_time64_t(const QDateTime &time) { +- return static_cast(time.toTime_t()); // 32 bits only, but better than nothing +-} +-#endif + + #include +-// and another stupid move +-#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) + const auto quazip_endl = Qt::endl; +-#else +-const auto quazip_endl = endl; +-#endif + + #endif // QUAZIP_QT_COMPAT_H ++ diff --git a/CMakeLists.txt b/CMakeLists.txt index 1b8f60089d..124a5ac80a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -60,7 +60,7 @@ endif() if (${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD") option(YUZU_USE_EXTERNAL_VULKAN_HEADERS "Use Vulkan-Headers from externals" OFF) else() - option(YUZU_USE_EXTERNAL_VULKAN_HEADERS "Use Vulkan-Headers from externals" ON) + option(YUZU_USE_EXTERNAL_VULKAN_HEADERS "Use Vulkan-Headers from externals" ON) endif() if (${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD") @@ -79,6 +79,8 @@ option(YUZU_USE_QT_MULTIMEDIA "Use QtMultimedia for Camera" OFF) option(YUZU_USE_QT_WEB_ENGINE "Use QtWebEngine for web applet implementation" OFF) +set(YUZU_QT_MIRROR "" CACHE STRING "What mirror to use for downloading the bundled Qt libraries") + option(ENABLE_CUBEB "Enables the cubeb audio backend" ON) option(USE_DISCORD_PRESENCE "Enables Discord Rich Presence" OFF) @@ -465,12 +467,10 @@ if (ENABLE_QT) list(APPEND CMAKE_PREFIX_PATH "${Qt6_DIR}") endif() - # QT6 Multimedia pulls in unneeded audio systems (ALSA, Pulseaudio) for FreeBSD - # ALSA is the default sound system on Linux, but FreeBSD uses OSS which works well enough - if (${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD") - find_package(Qt6 REQUIRED COMPONENTS Widgets Concurrent) - else() - find_package(Qt6 REQUIRED COMPONENTS Widgets Multimedia Concurrent) + find_package(Qt6 REQUIRED COMPONENTS Widgets Concurrent) + + if (YUZU_USE_QT_MULTIMEDIA) + find_package(Qt6 REQUIRED COMPONENTS Multimedia) endif() if (UNIX AND NOT APPLE) diff --git a/CMakeModules/CPM.cmake b/CMakeModules/CPM.cmake new file mode 100644 index 0000000000..84748734ce --- /dev/null +++ b/CMakeModules/CPM.cmake @@ -0,0 +1,24 @@ +# SPDX-License-Identifier: MIT +# +# SPDX-FileCopyrightText: Copyright (c) 2019-2023 Lars Melchior and contributors + +set(CPM_DOWNLOAD_VERSION 0.42.0) +set(CPM_HASH_SUM "2020b4fc42dba44817983e06342e682ecfc3d2f484a581f11cc5731fbe4dce8a") + +if(CPM_SOURCE_CACHE) + set(CPM_DOWNLOAD_LOCATION "${CPM_SOURCE_CACHE}/cpm/CPM_${CPM_DOWNLOAD_VERSION}.cmake") +elseif(DEFINED ENV{CPM_SOURCE_CACHE}) + set(CPM_DOWNLOAD_LOCATION "$ENV{CPM_SOURCE_CACHE}/cpm/CPM_${CPM_DOWNLOAD_VERSION}.cmake") +else() + set(CPM_DOWNLOAD_LOCATION "${CMAKE_BINARY_DIR}/cmake/CPM_${CPM_DOWNLOAD_VERSION}.cmake") +endif() + +# Expand relative path. This is important if the provided path contains a tilde (~) +get_filename_component(CPM_DOWNLOAD_LOCATION ${CPM_DOWNLOAD_LOCATION} ABSOLUTE) + +file(DOWNLOAD + https://github.com/cpm-cmake/CPM.cmake/releases/download/v${CPM_DOWNLOAD_VERSION}/CPM.cmake + ${CPM_DOWNLOAD_LOCATION} EXPECTED_HASH SHA256=${CPM_HASH_SUM} +) + +include(${CPM_DOWNLOAD_LOCATION}) diff --git a/CMakeModules/DownloadExternals.cmake b/CMakeModules/DownloadExternals.cmake index a82d1d72a3..3e9bc26d69 100644 --- a/CMakeModules/DownloadExternals.cmake +++ b/CMakeModules/DownloadExternals.cmake @@ -94,7 +94,7 @@ function(determine_qt_parameters target host_out type_out arch_out arch_path_out else() set(host "linux") set(type "desktop") - set(arch "gcc_64") + set(arch "linux_gcc_64") set(arch_path "linux") endif() @@ -133,12 +133,26 @@ function(download_qt_configuration prefix_out target host type arch arch_path ba set(install_args ${install_args} install-tool --outputdir ${base_path} ${host} desktop ${target}) else() set(prefix "${base_path}/${target}/${arch_path}") - set(install_args ${install_args} install-qt --outputdir ${base_path} ${host} ${type} ${target} ${arch} -m qt3d qt5compat qtactiveqt qtcharts qtconnectivity qtdatavis3d qtgraphs qtgrpc qthttpserver qtimageformats qtlanguageserver qtlocation qtlottie qtmultimedia qtnetworkauth qtpdf qtpositioning qtquick3d qtquick3dphysics qtquickeffectmaker qtquicktimeline qtremoteobjects qtscxml qtsensors qtserialbus qtserialport qtshadertools qtspeech qtvirtualkeyboard qtwebchannel qtwebengine qtwebsockets qtwebview) + set(install_args ${install_args} install-qt --outputdir ${base_path} ${host} ${type} ${target} ${arch} -m qt_base) + + if (YUZU_USE_QT_MULTIMEDIA) + set(install_args ${install_args} qtmultimedia) + endif() + + if (YUZU_USE_QT_WEB_ENGINE) + set(install_args ${install_args} qtpositioning qtwebchannel qtwebengine) + endif() + + if (NOT ${YUZU_QT_MIRROR} STREQUAL "") + message(STATUS "Using Qt mirror ${YUZU_QT_MIRROR}") + set(install_args ${install_args} -b ${YUZU_QT_MIRROR}) + endif() endif() + message(STATUS "Install Args ${install_args}") if (NOT EXISTS "${prefix}") message(STATUS "Downloading Qt binaries for ${target}:${host}:${type}:${arch}:${arch_path}") - set(AQT_PREBUILD_BASE_URL "https://github.com/miurahr/aqtinstall/releases/download/v3.2.1") + set(AQT_PREBUILD_BASE_URL "https://github.com/miurahr/aqtinstall/releases/download/v3.3.0") if (WIN32) set(aqt_path "${base_path}/aqt.exe") if (NOT EXISTS "${aqt_path}") diff --git a/externals/dynarmic/src/dynarmic/interface/A32/config.h b/externals/dynarmic/src/dynarmic/interface/A32/config.h index 6e75e9b99c..11fe2236a2 100644 --- a/externals/dynarmic/src/dynarmic/interface/A32/config.h +++ b/externals/dynarmic/src/dynarmic/interface/A32/config.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + /* This file is part of the dynarmic project. * Copyright (c) 2016 MerryMage * SPDX-License-Identifier: 0BSD diff --git a/externals/sirit/CMakeLists.txt b/externals/sirit/CMakeLists.txt index d98a8b5ba5..782ce8f660 100644 --- a/externals/sirit/CMakeLists.txt +++ b/externals/sirit/CMakeLists.txt @@ -1,6 +1,6 @@ # This file has been adapted from dynarmic -cmake_minimum_required(VERSION 3.8) +cmake_minimum_required(VERSION 3.12) project(sirit CXX) # Determine if we're built as a subproject (using add_subdirectory) diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/dialogs/NetPlayDialog.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/dialogs/NetPlayDialog.kt index ede2cfafa4..cd3e9a4474 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/dialogs/NetPlayDialog.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/dialogs/NetPlayDialog.kt @@ -34,6 +34,7 @@ import org.yuzu.yuzu_emu.databinding.ItemBanListBinding import org.yuzu.yuzu_emu.databinding.ItemButtonNetplayBinding import org.yuzu.yuzu_emu.databinding.ItemTextNetplayBinding import org.yuzu.yuzu_emu.features.settings.model.StringSetting +import org.yuzu.yuzu_emu.network.NetDataValidators import org.yuzu.yuzu_emu.network.NetPlayManager import org.yuzu.yuzu_emu.utils.CompatUtils import org.yuzu.yuzu_emu.utils.GameHelper @@ -102,8 +103,16 @@ class NetPlayDialog(context: Context) : BottomSheetDialog(context) { dismiss() } btnLobbyBrowser.setOnClickListener { - LobbyBrowser(context).show() - dismiss() + if (!NetDataValidators.username()) { + Toast.makeText( + context, + R.string.multiplayer_nickname_invalid, + Toast.LENGTH_LONG + ).show() + } else { + LobbyBrowser(context).show() + dismiss() + } } } } @@ -368,7 +377,7 @@ class NetPlayDialog(context: Context) : BottomSheetDialog(context) { ) ) { override fun validate(s: String): Boolean { - return s.length in 3..20 + return NetDataValidators.roomName(s) } } @@ -378,7 +387,7 @@ class NetPlayDialog(context: Context) : BottomSheetDialog(context) { context.getString(R.string.multiplayer_required) ) { override fun validate(s: String): Boolean { - return s.isNotEmpty() + return NetDataValidators.notEmpty(s) } } @@ -388,12 +397,7 @@ class NetPlayDialog(context: Context) : BottomSheetDialog(context) { context.getString(R.string.multiplayer_token_required) ) { override fun validate(s: String): Boolean { - if (s != context.getString(R.string.multiplayer_public_visibility)) { - return true; - } - - val token = StringSetting.WEB_TOKEN.getString() - return token.matches(Regex("[a-z]{48}")) + return NetDataValidators.roomVisibility(s, context) } } @@ -403,12 +407,7 @@ class NetPlayDialog(context: Context) : BottomSheetDialog(context) { context.getString(R.string.multiplayer_ip_error) ) { override fun validate(s: String): Boolean { - return try { - InetAddress.getByName(s) - s.length >= 7 - } catch (_: Exception) { - false - } + return NetDataValidators.ipAddress(s) } } @@ -418,7 +417,7 @@ class NetPlayDialog(context: Context) : BottomSheetDialog(context) { context.getString(R.string.multiplayer_username_error) ) { override fun validate(s: String): Boolean { - return s.length in 4..20 + return NetDataValidators.username(s) } } @@ -428,7 +427,7 @@ class NetPlayDialog(context: Context) : BottomSheetDialog(context) { context.getString(R.string.multiplayer_port_error) ) { override fun validate(s: String): Boolean { - return s.toIntOrNull() in 1..65535 + return NetDataValidators.port(s) } } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt index ab35a9180c..a269cab254 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt @@ -20,6 +20,7 @@ import org.yuzu.yuzu_emu.features.settings.model.IntSetting import org.yuzu.yuzu_emu.features.settings.model.LongSetting import org.yuzu.yuzu_emu.features.settings.model.ShortSetting import org.yuzu.yuzu_emu.features.settings.model.StringSetting +import org.yuzu.yuzu_emu.network.NetDataValidators import org.yuzu.yuzu_emu.utils.GpuDriverHelper import org.yuzu.yuzu_emu.utils.NativeConfig @@ -300,9 +301,7 @@ abstract class SettingsItem( val chars = "abcdefghijklmnopqrstuvwxyz" (1..48).map { chars.random() }.joinToString("") }, - validator = { s -> - s?.matches(Regex("[a-z]{48}")) == true - }, + validator = NetDataValidators::token, errorId = R.string.multiplayer_token_error ) ) @@ -312,9 +311,7 @@ abstract class SettingsItem( StringSetting.WEB_USERNAME, titleId = R.string.web_username, descriptionId = R.string.web_username_description, - validator = { s -> - s?.length in 4..20 - }, + validator = NetDataValidators::username, errorId = R.string.multiplayer_username_error ) ) diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsDialogFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsDialogFragment.kt index e50ebe50f4..aa17d05e34 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsDialogFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsDialogFragment.kt @@ -154,8 +154,6 @@ class SettingsDialogFragment : DialogFragment(), DialogInterface.OnClickListener stringInputBinding.generate.setOnClickListener { stringInputBinding.editText.setText(onGenerate()) } - } else { - stringInputBinding.generate.isVisible = false } val validator = item.validator @@ -179,8 +177,9 @@ class SettingsDialogFragment : DialogFragment(), DialogInterface.OnClickListener } override fun afterTextChanged(s: Editable?) { - stringInputBinding.editText.error = - if (validator(s.toString())) null else requireContext().getString(item.errorId) + val isValid = validator(s.toString()) + stringInputBinding.editTextLayout.isErrorEnabled = !isValid + stringInputBinding.editTextLayout.error = if (isValid) null else requireContext().getString(item.errorId) } } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/network/NetDataValidators.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/network/NetDataValidators.kt new file mode 100644 index 0000000000..b3edf35d8e --- /dev/null +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/network/NetDataValidators.kt @@ -0,0 +1,56 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +package org.yuzu.yuzu_emu.network + +import android.content.Context +import org.yuzu.yuzu_emu.R +import org.yuzu.yuzu_emu.features.settings.model.StringSetting +import java.net.InetAddress + +object NetDataValidators { + fun roomName(s: String): Boolean { + return s.length in 3..20 + } + + fun notEmpty(s: String): Boolean { + return s.isNotEmpty() + } + + fun token(s: String?): Boolean { + return s?.matches(Regex("[a-z]{48}")) == true + } + + fun token(): Boolean { + return token(StringSetting.WEB_TOKEN.getString()) + } + + fun roomVisibility(s: String, context: Context): Boolean { + if (s != context.getString(R.string.multiplayer_public_visibility)) { + return true; + } + + return token() + } + + fun ipAddress(s: String): Boolean { + return try { + InetAddress.getByName(s) + s.length >= 7 + } catch (_: Exception) { + false + } + } + + fun username(s: String?): Boolean { + return s?.matches(Regex("^[ a-zA-Z0-9._-]{4,20}$")) == true + } + + fun username(): Boolean { + return username(StringSetting.WEB_USERNAME.getString()) + } + + fun port(s: String): Boolean { + return s.toIntOrNull() in 1..65535 + } +} \ No newline at end of file diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GpuDriverHelper.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GpuDriverHelper.kt index 81943e9235..99f7fd81fe 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GpuDriverHelper.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GpuDriverHelper.kt @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -177,6 +180,10 @@ object GpuDriverHelper { * @return A non-null [GpuDriverMetadata] instance that may have null members */ fun getMetadataFromZip(driver: File): GpuDriverMetadata { + if (!driver.exists()) { + return GpuDriverMetadata() + } + try { ZipFile(driver).use { zf -> val entries = zf.entries() diff --git a/src/android/app/src/main/res/drawable/ic_icon_bg.png b/src/android/app/src/main/res/drawable/ic_icon_bg.png new file mode 100644 index 0000000000..30b29a32d8 Binary files /dev/null and b/src/android/app/src/main/res/drawable/ic_icon_bg.png differ diff --git a/src/android/app/src/main/res/drawable/ic_icon_bg.xml b/src/android/app/src/main/res/drawable/ic_icon_bg.xml deleted file mode 100644 index df62dde92e..0000000000 --- a/src/android/app/src/main/res/drawable/ic_icon_bg.xml +++ /dev/null @@ -1,751 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/android/app/src/main/res/drawable/ic_icon_bg_orig.png b/src/android/app/src/main/res/drawable/ic_icon_bg_orig.png new file mode 100644 index 0000000000..9ebc6ce17c Binary files /dev/null and b/src/android/app/src/main/res/drawable/ic_icon_bg_orig.png differ diff --git a/src/android/app/src/main/res/layout/dialog_edit_text.xml b/src/android/app/src/main/res/layout/dialog_edit_text.xml index 3612f46eae..bc7864a395 100644 --- a/src/android/app/src/main/res/layout/dialog_edit_text.xml +++ b/src/android/app/src/main/res/layout/dialog_edit_text.xml @@ -27,6 +27,7 @@ android:layout_height="wrap_content" android:layout_marginTop="15dp" android:text="@string/generate" + android:visibility="gone" app:layout_constraintEnd_toEndOf="@+id/edit_text_layout" app:layout_constraintTop_toBottomOf="@+id/edit_text_layout" /> diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml index e611e66c1f..3814105ea0 100644 --- a/src/android/app/src/main/res/values/strings.xml +++ b/src/android/app/src/main/res/values/strings.xml @@ -148,18 +148,18 @@ Failed to join room Name is too short Invalid address - Invalid port! + Invalid port Exit Room Network error Lost connection - Name collision + Username already taken MAC Address collision Console ID collision Wrong version Wrong password Could not connect Room is full - Host banned + You are banned from this room Permission denied No such user Already in room @@ -221,7 +221,8 @@ Required Web Token required, go to Advanced Settings -> System -> Network Invalid IP format - Must be between 4–20 characters + Must be between 4–20 characters, and contain alphanumeric characters, periods, dashes, underscores, and spaces only + Username invalid, ensure it is set properly in System -> Network Must be 48 characters, and lowercase a-z only Must be between 1 and 65535 Cancel @@ -467,7 +468,7 @@ Web Token Web token used for creating public lobbies. It is a 48-character string containing only lowercase a-z. Web Username - Username to be shown in multiplayer lobbies. It must be 4–20 characters. + Username to be shown in multiplayer lobbies. It must be 4–20 characters, containing only alphanumeric characters, dashes, periods, underscores, and spaces. Network diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp index 23aa3d79d4..afbf178349 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp index 1c81ee66f1..99a80644ad 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/video_core/host1x/ffmpeg/ffmpeg.cpp b/src/video_core/host1x/ffmpeg/ffmpeg.cpp index 9b718f2591..3170f53884 100644 --- a/src/video_core/host1x/ffmpeg/ffmpeg.cpp +++ b/src/video_core/host1x/ffmpeg/ffmpeg.cpp @@ -23,31 +23,48 @@ namespace FFmpeg { namespace { -constexpr AVPixelFormat PreferredGpuFormat = AV_PIX_FMT_NV12; -constexpr AVPixelFormat PreferredCpuFormat = AV_PIX_FMT_YUV420P; constexpr std::array PreferredGpuDecoders = { +#if defined (_WIN32) AV_HWDEVICE_TYPE_CUDA, -#ifdef _WIN32 AV_HWDEVICE_TYPE_D3D11VA, AV_HWDEVICE_TYPE_DXVA2, -#elif defined(__unix__) - AV_HWDEVICE_TYPE_VAAPI, +#elif defined(__FreeBSD__) AV_HWDEVICE_TYPE_VDPAU, +#elif defined(__unix__) + AV_HWDEVICE_TYPE_CUDA, + AV_HWDEVICE_TYPE_VAAPI, + AV_HWDEVICE_TYPE_VDPAU, #endif AV_HWDEVICE_TYPE_VULKAN, }; AVPixelFormat GetGpuFormat(AVCodecContext* codec_context, const AVPixelFormat* pix_fmts) { - for (const AVPixelFormat* p = pix_fmts; *p != AV_PIX_FMT_NONE; ++p) { - if (*p == codec_context->pix_fmt) { - return codec_context->pix_fmt; - } - } + // Check if there is a pixel format supported by the GPU decoder. + const auto desc = av_pix_fmt_desc_get(codec_context->pix_fmt); + if (desc && desc->flags & AV_PIX_FMT_FLAG_HWACCEL) { + for (const AVPixelFormat* p = pix_fmts; *p != AV_PIX_FMT_NONE; ++p) { + if (*p == codec_context->pix_fmt) { + return codec_context->pix_fmt; + } + } + } - LOG_INFO(HW_GPU, "Could not find compatible GPU AV format, falling back to CPU"); + // Another check to confirm if there is a pixel format supported by specific GPU decoders. + for (int i = 0;; i++) { + const AVCodecHWConfig* config = avcodec_get_hw_config(codec_context->codec, i); + if (!config) { + break; + } + + if ((config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX) && (config->device_type == AV_HWDEVICE_TYPE_CUDA || config->device_type == AV_HWDEVICE_TYPE_VAAPI)) { + return config->pix_fmt; + } + } + + // Fallback to CPU decoder. + LOG_INFO(HW_GPU, "Could not find compatible GPU pixel format, falling back to CPU"); av_buffer_unref(&codec_context->hw_device_ctx); - codec_context->pix_fmt = PreferredCpuFormat; return codec_context->pix_fmt; } @@ -57,7 +74,7 @@ std::string AVError(int errnum) { return errbuf; } -} // namespace +} Packet::Packet(std::span data) { m_packet = av_packet_alloc(); @@ -80,16 +97,16 @@ Frame::~Frame() { Decoder::Decoder(Tegra::Host1x::NvdecCommon::VideoCodec codec) { const AVCodecID av_codec = [&] { switch (codec) { - case Tegra::Host1x::NvdecCommon::VideoCodec::H264: - return AV_CODEC_ID_H264; - case Tegra::Host1x::NvdecCommon::VideoCodec::VP8: - return AV_CODEC_ID_VP8; - case Tegra::Host1x::NvdecCommon::VideoCodec::VP9: - return AV_CODEC_ID_VP9; - default: - UNIMPLEMENTED_MSG("Unknown codec {}", codec); - return AV_CODEC_ID_NONE; - } + case Tegra::Host1x::NvdecCommon::VideoCodec::H264: + return AV_CODEC_ID_H264; + case Tegra::Host1x::NvdecCommon::VideoCodec::VP8: + return AV_CODEC_ID_VP8; + case Tegra::Host1x::NvdecCommon::VideoCodec::VP9: + return AV_CODEC_ID_VP9; + default: + UNIMPLEMENTED_MSG("Unknown codec {}", codec); + return AV_CODEC_ID_NONE; + } }(); m_codec = avcodec_find_decoder(av_codec); @@ -102,6 +119,7 @@ bool Decoder::SupportsDecodingOnDevice(AVPixelFormat* out_pix_fmt, AVHWDeviceTyp LOG_DEBUG(HW_GPU, "{} decoder does not support device type {}", m_codec->name, av_hwdevice_get_type_name(type)); break; } + if (config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX && config->device_type == type) { LOG_INFO(HW_GPU, "Using {} GPU decoder", av_hwdevice_get_type_name(type)); *out_pix_fmt = config->pix_fmt; @@ -214,19 +232,17 @@ bool DecoderContext::OpenContext(const Decoder& decoder) { } bool DecoderContext::SendPacket(const Packet& packet) { - m_temp_frame = std::make_shared(); - - if (const int ret = avcodec_send_packet(m_codec_context, packet.GetPacket()); ret < 0) { + if (const int ret = avcodec_send_packet(m_codec_context, packet.GetPacket()); ret < 0 && ret != AVERROR_EOF) { LOG_ERROR(HW_GPU, "avcodec_send_packet error: {}", AVError(ret)); return false; } - + return true; } std::shared_ptr DecoderContext::ReceiveFrame() { auto ReceiveImpl = [&](AVFrame* frame) -> bool { - if (const int ret = avcodec_receive_frame(m_codec_context, frame); ret < 0) { + if (const int ret = avcodec_receive_frame(m_codec_context, frame); ret < 0 && ret != AVERROR_EOF) { LOG_ERROR(HW_GPU, "avcodec_receive_frame error: {}", AVError(ret)); return false; } @@ -238,14 +254,15 @@ std::shared_ptr DecoderContext::ReceiveFrame() { return {}; } - const auto desc = av_pix_fmt_desc_get(intermediate_frame->GetPixelFormat()); - if (m_codec_context->hw_device_ctx && (desc && desc->flags & AV_PIX_FMT_FLAG_HWACCEL)) { - m_temp_frame->SetFormat(PreferredGpuFormat); + m_temp_frame = std::make_shared(); + if (m_codec_context->hw_device_ctx) { + m_temp_frame->SetFormat(AV_PIX_FMT_NV12); if (int ret = av_hwframe_transfer_data(m_temp_frame->GetFrame(), intermediate_frame->GetFrame(), 0); ret < 0) { LOG_ERROR(HW_GPU, "av_hwframe_transfer_data error: {}", AVError(ret)); return {}; } } else { + m_temp_frame->SetFormat(AV_PIX_FMT_YUV420P); m_temp_frame = std::move(intermediate_frame); } @@ -288,4 +305,4 @@ std::shared_ptr DecodeApi::ReceiveFrame() { return m_decoder_context->ReceiveFrame(); } -} // namespace FFmpeg +} diff --git a/src/video_core/host1x/ffmpeg/ffmpeg.h b/src/video_core/host1x/ffmpeg/ffmpeg.h index 6200fa691d..0864fd0cbe 100644 --- a/src/video_core/host1x/ffmpeg/ffmpeg.h +++ b/src/video_core/host1x/ffmpeg/ffmpeg.h @@ -26,7 +26,6 @@ extern "C" { #include #include -// Works quite fine, and omits the hacky ffmpeg building for now... #if defined(__FreeBSD__) #include #else diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index 0dc6f562b8..34f2ba455a 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt @@ -462,8 +462,8 @@ if (WIN32 AND NOT YUZU_USE_BUNDLED_QT AND QT_VERSION VERSION_GREATER_EQUAL 6) endif() if (YUZU_USE_BUNDLED_QT) - include(CopyYuzuQt6Deps) - copy_yuzu_Qt6_deps(yuzu) + include(CopyYuzuQt6Deps) + copy_yuzu_Qt6_deps(yuzu) endif() if (ENABLE_SDL2) @@ -494,4 +494,8 @@ if (YUZU_ROOM) target_link_libraries(yuzu PRIVATE yuzu-room) endif() +# Extra deps +add_subdirectory(externals) +target_link_libraries(yuzu PRIVATE QuaZip::QuaZip) + create_target_directory_groups(yuzu) diff --git a/src/yuzu/about_dialog.cpp b/src/yuzu/about_dialog.cpp index 6ef3072894..5b6e32149d 100644 --- a/src/yuzu/about_dialog.cpp +++ b/src/yuzu/about_dialog.cpp @@ -16,9 +16,9 @@ AboutDialog::AboutDialog(QWidget* parent) std::string yuzu_build; if (Common::g_is_dev_build) { - yuzu_build = fmt::format("eden Nightly | {}-{}", description, build_id); + yuzu_build = fmt::format("Eden Nightly | {}-{}", description, build_id); } else { - yuzu_build = fmt::format("eden | {}", description); + yuzu_build = fmt::format("Eden | {}", description); } const auto override_build = fmt::format(fmt::runtime( diff --git a/src/yuzu/externals/CMakeLists.txt b/src/yuzu/externals/CMakeLists.txt new file mode 100644 index 0000000000..d606e27108 --- /dev/null +++ b/src/yuzu/externals/CMakeLists.txt @@ -0,0 +1,17 @@ +# Disable tests in all externals supporting the standard option name +set(BUILD_TESTING OFF) + +# Build only static externals +set(BUILD_SHARED_LIBS OFF) + +# QuaZip +include(CPM) +set(CPM_SOURCE_CACHE ${CMAKE_SOURCE_DIR}/.cache/cpm) +set(CPM_USE_LOCAL_PACKAGES ON) + +CPMAddPackage( + NAME QuaZip-Qt6 + VERSION 1.3 + GIT_REPOSITORY "https://github.com/crueter/quazip-qt6.git" + GIT_TAG v1.5-qt6 +) diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 081f08cf52..8e3082a58e 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -12,6 +12,8 @@ #include "core/tools/renderdoc.h" #include "frontend_common/firmware_manager.h" +#include + #ifdef __APPLE__ #include // for chdir #endif @@ -1683,7 +1685,8 @@ void GMainWindow::ConnectMenuEvents() { connect_menu(ui->action_Discord, &GMainWindow::OnOpenDiscord); connect_menu(ui->action_Verify_installed_contents, &GMainWindow::OnVerifyInstalledContents); - connect_menu(ui->action_Install_Firmware, &GMainWindow::OnInstallFirmware); + connect_menu(ui->action_Firmware_From_Folder, &GMainWindow::OnInstallFirmware); + connect_menu(ui->action_Firmware_From_ZIP, &GMainWindow::OnInstallFirmwareFromZIP); connect_menu(ui->action_Install_Keys, &GMainWindow::OnInstallDecryptionKeys); connect_menu(ui->action_About, &GMainWindow::OnAbout); } @@ -1714,7 +1717,8 @@ void GMainWindow::UpdateMenuState() { action->setEnabled(emulation_running); } - ui->action_Install_Firmware->setEnabled(!emulation_running); + ui->action_Firmware_From_Folder->setEnabled(!emulation_running); + ui->action_Firmware_From_ZIP->setEnabled(!emulation_running); ui->action_Install_Keys->setEnabled(!emulation_running); for (QAction* action : applet_actions) { @@ -4239,26 +4243,8 @@ void GMainWindow::OnVerifyInstalledContents() { } } -void GMainWindow::OnInstallFirmware() { - // Don't do this while emulation is running, that'd probably be a bad idea. - if (emu_thread != nullptr && emu_thread->IsRunning()) { - return; - } - - // Check for installed keys, error out, suggest restart? - if (!ContentManager::AreKeysPresent()) { - QMessageBox::information( - this, tr("Keys not installed"), - tr("Install decryption keys and restart eden before attempting to install firmware.")); - return; - } - - const QString firmware_source_location = QFileDialog::getExistingDirectory( - this, tr("Select Dumped Firmware Source Location"), {}, QFileDialog::ShowDirsOnly); - if (firmware_source_location.isEmpty()) { - return; - } - +void GMainWindow::InstallFirmware(const QString &location, bool recursive) +{ QProgressDialog progress(tr("Installing Firmware..."), tr("Cancel"), 0, 100, this); progress.setWindowModality(Qt::WindowModal); progress.setMinimumDuration(100); @@ -4272,11 +4258,11 @@ void GMainWindow::OnInstallFirmware() { return progress.wasCanceled(); }; - LOG_INFO(Frontend, "Installing firmware from {}", firmware_source_location.toStdString()); + LOG_INFO(Frontend, "Installing firmware from {}", location.toStdString()); // Check for a reasonable number of .nca files (don't hardcode them, just see if there's some in // there.) - std::filesystem::path firmware_source_path = firmware_source_location.toStdString(); + std::filesystem::path firmware_source_path = location.toStdString(); if (!Common::FS::IsDir(firmware_source_path)) { progress.close(); return; @@ -4294,7 +4280,12 @@ void GMainWindow::OnInstallFirmware() { QtProgressCallback(100, 10); - Common::FS::IterateDirEntries(firmware_source_path, callback, Common::FS::DirEntryFilter::File); + if (recursive) { + Common::FS::IterateDirEntriesRecursively(firmware_source_path, callback, Common::FS::DirEntryFilter::File); + } else { + Common::FS::IterateDirEntries(firmware_source_path, callback, Common::FS::DirEntryFilter::File); + } + if (out.size() <= 0) { progress.close(); QMessageBox::warning(this, tr("Firmware install failed"), @@ -4377,6 +4368,93 @@ void GMainWindow::OnInstallFirmware() { OnCheckFirmware(); } +void GMainWindow::OnInstallFirmware() { + // Don't do this while emulation is running, that'd probably be a bad idea. + if (emu_thread != nullptr && emu_thread->IsRunning()) { + return; + } + + // Check for installed keys, error out, suggest restart? + if (!ContentManager::AreKeysPresent()) { + QMessageBox::information( + this, tr("Keys not installed"), + tr("Install decryption keys and restart Eden before attempting to install firmware.")); + return; + } + + const QString firmware_source_location = QFileDialog::getExistingDirectory( + this, tr("Select Dumped Firmware Source Location"), {}, QFileDialog::ShowDirsOnly); + if (firmware_source_location.isEmpty()) { + return; + } + + InstallFirmware(firmware_source_location); +} + +void GMainWindow::OnInstallFirmwareFromZIP() +{ + // Don't do this while emulation is running, that'd probably be a bad idea. + if (emu_thread != nullptr && emu_thread->IsRunning()) { + return; + } + + // Check for installed keys, error out, suggest restart? + if (!ContentManager::AreKeysPresent()) { + QMessageBox::information( + this, tr("Keys not installed"), + tr("Install decryption keys and restart Eden before attempting to install firmware.")); + return; + } + + const QString firmware_zip_location = QFileDialog::getOpenFileName( + this, tr("Select Dumped Firmware ZIP"), {}, tr("Zipped Archives (*.zip)")); + if (firmware_zip_location.isEmpty()) { + return; + } + + namespace fs = std::filesystem; + fs::path tmp{std::filesystem::temp_directory_path()}; + + if (!std::filesystem::create_directories(tmp / "eden" / "firmware")) { + goto unzipFailed; + } + + { + tmp /= "eden"; + tmp /= "firmware"; + + QString qCacheDir = QString::fromStdString(tmp.string()); + + QFile zip(firmware_zip_location); + + QStringList result = JlCompress::extractDir(&zip, qCacheDir); + if (result.isEmpty()) { + goto unzipFailed; + } + + // In this case, it has to be done recursively, since sometimes people + // will pack it into a subdirectory after dumping + InstallFirmware(qCacheDir, true); + + std::error_code ec; + std::filesystem::remove_all(tmp, ec); + + if (ec) { + QMessageBox::warning(this, tr("Firmware cleanup failed"), + tr("Failed to clean up extracted firmware cache.\n" + "Check write permissions in the system temp directory and try again.\nOS reported error: %1") + .arg(QString::fromStdString(ec.message()))); + } + + return; + } +unzipFailed: + QMessageBox::critical(this, tr("Firmware unzip failed"), + tr("Check write permissions in the system temp directory and try again.")); + return; + +} + void GMainWindow::OnInstallDecryptionKeys() { // Don't do this while emulation is running. if (emu_thread != nullptr && emu_thread->IsRunning()) { @@ -4824,9 +4902,9 @@ void GMainWindow::UpdateWindowTitle(std::string_view title_name, std::string_vie std::string yuzu_title; if (Common::g_is_dev_build) { - yuzu_title = fmt::format("eden Nightly | {}-{}", description, build_id); + yuzu_title = fmt::format("Eden Nightly | {}-{}", description, build_id); } else { - yuzu_title = fmt::format("eden | {}", description); + yuzu_title = fmt::format("Eden | {}", description); } const auto override_title = diff --git a/src/yuzu/main.h b/src/yuzu/main.h index 1f2582098a..7e7c00ec0b 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -394,6 +394,7 @@ private slots: void OnOpenLogFolder(); void OnVerifyInstalledContents(); void OnInstallFirmware(); + void OnInstallFirmwareFromZIP(); void OnInstallDecryptionKeys(); void OnAbout(); void OnToggleFilterBar(); @@ -614,6 +615,8 @@ private: std::string arguments, const bool needs_title); + void InstallFirmware(const QString& location, bool recursive = false); + protected: void dropEvent(QDropEvent* event) override; void dragEnterEvent(QDragEnterEvent* event) override; diff --git a/src/yuzu/main.ui b/src/yuzu/main.ui index e35b7afa5a..970f8d2901 100644 --- a/src/yuzu/main.ui +++ b/src/yuzu/main.ui @@ -182,8 +182,15 @@ + + + Install Firmware + + + + - + @@ -484,11 +491,6 @@ Open &Controller Menu - - - Install Firmware - - Install Decryption Keys @@ -545,6 +547,16 @@ &Log Folder + + + + + From Folder + + + + + From ZIP diff --git a/tools/update-cpm.sh b/tools/update-cpm.sh new file mode 100755 index 0000000000..30e400209d --- /dev/null +++ b/tools/update-cpm.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +wget -O CMakeModules/CPM.cmake https://github.com/cpm-cmake/CPM.cmake/releases/latest/download/get_cpm.cmake