diff --git a/.ci/linux.sh b/.ci/linux.sh index c7a1ef4cd..e10804dcc 100755 --- a/.ci/linux.sh +++ b/.ci/linux.sh @@ -17,7 +17,7 @@ else fi if [ "$GITHUB_REF_TYPE" == "tag" ]; then - export EXTRA_CMAKE_FLAGS=("${EXTRA_CMAKE_FLAGS[@]}" -DENABLE_QT_UPDATE_CHECKER=ON) + export EXTRA_CMAKE_FLAGS=("${EXTRA_CMAKE_FLAGS[@]}" -DENABLE_UPDATE_CHECKER=ON) fi mkdir build && cd build diff --git a/.ci/macos.sh b/.ci/macos.sh index b6fb54b1e..27f4a0e7b 100755 --- a/.ci/macos.sh +++ b/.ci/macos.sh @@ -1,7 +1,7 @@ #!/bin/bash -ex if [ "$GITHUB_REF_TYPE" == "tag" ]; then - export EXTRA_CMAKE_FLAGS=(-DENABLE_QT_UPDATE_CHECKER=ON) + export EXTRA_CMAKE_FLAGS=(-DENABLE_UPDATE_CHECKER=ON) fi mkdir -p build/$BUILD_ARCH && cd build/$BUILD_ARCH diff --git a/.ci/windows.sh b/.ci/windows.sh index d94fd6435..f6144861c 100644 --- a/.ci/windows.sh +++ b/.ci/windows.sh @@ -3,7 +3,7 @@ mkdir build && cd build if [ "$GITHUB_REF_TYPE" == "tag" ]; then - export EXTRA_CMAKE_FLAGS=(-DENABLE_QT_UPDATE_CHECKER=ON) + export EXTRA_CMAKE_FLAGS=(-DENABLE_UPDATE_CHECKER=ON) fi cmake .. -G Ninja \ diff --git a/CMakeLists.txt b/CMakeLists.txt index 54eb90e90..77870ea90 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -110,7 +110,7 @@ option(USE_SYSTEM_SDL2 "Use the system SDL2 lib (instead of the bundled one)" OF # Set bundled qt as dependent options. option(ENABLE_QT "Enable the Qt frontend" ON) option(ENABLE_QT_TRANSLATION "Enable translations for the Qt frontend" OFF) -option(ENABLE_QT_UPDATE_CHECKER "Enable built-in update checker for the Qt frontend" OFF) +option(ENABLE_UPDATE_CHECKER "Enable built-in update checker for the Qt / Android frontend" OFF) CMAKE_DEPENDENT_OPTION(ENABLE_TESTS "Enable generating tests executable" ON "NOT IOS" OFF) CMAKE_DEPENDENT_OPTION(ENABLE_ROOM "Enable dedicated room functionality" ON "NOT ANDROID AND NOT IOS" OFF) diff --git a/CMakeModules/GenerateSettingKeys.cmake b/CMakeModules/GenerateSettingKeys.cmake index 90110cd33..74e134f7b 100644 --- a/CMakeModules/GenerateSettingKeys.cmake +++ b/CMakeModules/GenerateSettingKeys.cmake @@ -143,6 +143,8 @@ foreach(KEY IN ITEMS "web_api_url" "citra_username" "citra_token" + "check_for_update_on_start" + "update_check_channel" ) set(SETTING_KEY_LIST "${SETTING_KEY_LIST}\n\"${KEY}\",") set(SETTING_KEY_DEFINITIONS "${SETTING_KEY_DEFINITIONS}\nDEFINE_KEY(${KEY})") diff --git a/src/android/app/build.gradle.kts b/src/android/app/build.gradle.kts index 5e2dc1960..75c064b21 100644 --- a/src/android/app/build.gradle.kts +++ b/src/android/app/build.gradle.kts @@ -169,6 +169,11 @@ android { isDefault = true dimension = "version" versionNameSuffix = "-vanilla" + externalNativeBuild { + cmake { + arguments += "-DENABLE_UPDATE_CHECKER=ON" + } + } } register("googlePlay") { dimension = "version" diff --git a/src/android/app/src/main/java/org/citra/citra_emu/CitraApplication.kt b/src/android/app/src/main/java/org/citra/citra_emu/CitraApplication.kt index c4db63928..1076f5dd8 100644 --- a/src/android/app/src/main/java/org/citra/citra_emu/CitraApplication.kt +++ b/src/android/app/src/main/java/org/citra/citra_emu/CitraApplication.kt @@ -16,6 +16,11 @@ import org.citra.citra_emu.utils.GpuDriverHelper import org.citra.citra_emu.utils.PermissionsHandler import org.citra.citra_emu.utils.Log import org.citra.citra_emu.utils.MemoryUtil +import java.io.File +import java.io.FileOutputStream +import java.security.KeyStore +import javax.net.ssl.TrustManagerFactory +import javax.net.ssl.X509TrustManager class CitraApplication : Application() { private fun createNotificationChannel() { @@ -59,6 +64,46 @@ class CitraApplication : Application() { logDeviceInfo() createNotificationChannel() NativeLibrary.playTimeManagerInit() + + if (NativeLibrary.isUpdateCheckerEnabled()) { + initializeCACertificates() + } + } + + // needed for update checking + private fun initializeCACertificates() { + try { + val factory = TrustManagerFactory.getInstance( + TrustManagerFactory.getDefaultAlgorithm() + ) + factory.init(null as KeyStore?) + + val trustManager = factory.trustManagers[0] as X509TrustManager + + val certFile = File(filesDir, "cacert.pem") + + if (!certFile.exists()) { + FileOutputStream(certFile).use { out -> + trustManager.acceptedIssuers.forEach { cert -> + out.write("-----BEGIN CERTIFICATE-----\n".toByteArray()) + + val encoded = android.util.Base64.encodeToString( + cert.encoded, + android.util.Base64.NO_WRAP // 👈 important + ) + + out.write(encoded.toByteArray()) + out.write("\n-----END CERTIFICATE-----\n".toByteArray()) + } + } + } + + NativeLibrary.setCACertificatePath(certFile.absolutePath) + + Log.info("[SSL] CA certs ready: ${certFile.absolutePath}") + } catch (e: Exception) { + Log.error("[SSL] Failed to init CA certs: ${e.message}") + } } fun logDeviceInfo() { diff --git a/src/android/app/src/main/java/org/citra/citra_emu/NativeLibrary.kt b/src/android/app/src/main/java/org/citra/citra_emu/NativeLibrary.kt index 9d2015baa..267b3f9b9 100644 --- a/src/android/app/src/main/java/org/citra/citra_emu/NativeLibrary.kt +++ b/src/android/app/src/main/java/org/citra/citra_emu/NativeLibrary.kt @@ -527,6 +527,13 @@ object NativeLibrary { sEmulationActivity.clear() } + // Update checker + external fun getUpdateTag(): String + external fun isUpdateCheckerEnabled(): Boolean + external fun getUpdateUrl(): String + // Sets the path to CA certificates for SSL/TLS verification. + external fun setCACertificatePath(path: String) + private val cameraPermissionLock = Object() private var cameraPermissionGranted = false const val REQUEST_CODE_NATIVE_CAMERA = 800 diff --git a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/SettingKeys.kt b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/SettingKeys.kt index 1d6e0dcee..d04ec37a7 100644 --- a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/SettingKeys.kt +++ b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/SettingKeys.kt @@ -138,4 +138,7 @@ object SettingKeys { external fun android_hide_images(): String external fun screen_orientation(): String external fun performance_overlay_position(): String + external fun check_for_update_on_start(): String + external fun update_check_channel(): String + } \ No newline at end of file diff --git a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/BooleanSetting.kt b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/BooleanSetting.kt index e12d87544..0d3ed1c1f 100644 --- a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/BooleanSetting.kt +++ b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/BooleanSetting.kt @@ -56,7 +56,8 @@ enum class BooleanSetting( COMPRESS_INSTALLED_CIA_CONTENT(SettingKeys.compress_cia_installs(), Settings.SECTION_STORAGE, false), ANDROID_HIDE_IMAGES(SettingKeys.android_hide_images(), Settings.SECTION_MISC, false), APPLY_REGION_FREE_PATCH(SettingKeys.apply_region_free_patch(), Settings.SECTION_SYSTEM, true), - USE_INTEGER_SCALING(SettingKeys.use_integer_scaling(), Settings.SECTION_RENDERER, false); + USE_INTEGER_SCALING(SettingKeys.use_integer_scaling(), Settings.SECTION_RENDERER, true), + CHECK_FOR_UPDATES(SettingKeys.check_for_update_on_start(), Settings.SECTION_THEME, true); override var boolean: Boolean = defaultValue diff --git a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/IntSetting.kt b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/IntSetting.kt index 2c8cbf2a1..5046d4d2d 100644 --- a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/IntSetting.kt +++ b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/IntSetting.kt @@ -56,6 +56,7 @@ enum class IntSetting( TURBO_LIMIT(SettingKeys.turbo_limit(), Settings.SECTION_CORE, 200), PERFORMANCE_OVERLAY_POSITION(SettingKeys.performance_overlay_position(), Settings.SECTION_LAYOUT, 0), RENDER_3D_WHICH_DISPLAY(SettingKeys.render_3d_which_display(),Settings.SECTION_RENDERER,0), + UPDATE_CHECK_CHANNEL(SettingKeys.update_check_channel(), Settings.SECTION_THEME, 0), ASPECT_RATIO(SettingKeys.aspect_ratio(), Settings.SECTION_LAYOUT, 0); override var int: Int = defaultValue diff --git a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/Settings.kt b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/Settings.kt index 547a53594..272416b93 100644 --- a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/Settings.kt +++ b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/Settings.kt @@ -246,7 +246,8 @@ class Settings { SECTION_STORAGE, SECTION_UTILITY, SECTION_AUDIO, - SECTION_DEBUG + SECTION_DEBUG, + SECTION_THEME ) } } diff --git a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsFragmentPresenter.kt b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsFragmentPresenter.kt index 25e56517f..c7352b7bf 100644 --- a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsFragmentPresenter.kt +++ b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsFragmentPresenter.kt @@ -16,6 +16,7 @@ import androidx.preference.PreferenceManager import com.google.android.material.dialog.MaterialAlertDialogBuilder import kotlinx.serialization.builtins.IntArraySerializer import org.citra.citra_emu.CitraApplication +import org.citra.citra_emu.NativeLibrary import org.citra.citra_emu.R import org.citra.citra_emu.display.ScreenLayout import org.citra.citra_emu.display.StereoMode @@ -1849,7 +1850,7 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView) } private fun addThemeSettings(sl: ArrayList) { - settingsActivity.setToolbarTitle(settingsActivity.getString(R.string.preferences_theme)) + settingsActivity.setToolbarTitle(settingsActivity.getString(R.string.app_settings)) sl.apply { val theme: AbstractBooleanSetting = object : AbstractBooleanSetting { override var boolean: Boolean @@ -1868,6 +1869,37 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView) override val defaultValue = false } + if (NativeLibrary.isUpdateCheckerEnabled()) { + add( + HeaderSetting( + R.string.app_settings, + ) + ) + add( + SwitchSetting( + BooleanSetting.CHECK_FOR_UPDATES, + R.string.check_for_updates_on_start, + R.string.check_for_updates_on_start_description, + ) + ) + add( + SingleChoiceSetting( + IntSetting.UPDATE_CHECK_CHANNEL, + R.string.update_check_channel, + 0, + R.array.updateCheckChannelNames, + R.array.updateCheckChannelValues, + IntSetting.UPDATE_CHECK_CHANNEL.key, + IntSetting.UPDATE_CHECK_CHANNEL.defaultValue + ) + ) + } + + add( + HeaderSetting( + R.string.set_up_theme_settings, + ) + ) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { add( SwitchSetting( diff --git a/src/android/app/src/main/java/org/citra/citra_emu/fragments/HomeSettingsFragment.kt b/src/android/app/src/main/java/org/citra/citra_emu/fragments/HomeSettingsFragment.kt index ceecc7572..a35838e2f 100644 --- a/src/android/app/src/main/java/org/citra/citra_emu/fragments/HomeSettingsFragment.kt +++ b/src/android/app/src/main/java/org/citra/citra_emu/fragments/HomeSettingsFragment.kt @@ -170,8 +170,8 @@ class HomeSettingsFragment : Fragment() { details = homeViewModel.gamesDir ), HomeSetting( - R.string.preferences_theme, - R.string.theme_and_color_description, + R.string.app_settings, + R.string.app_settings_description, R.drawable.ic_palette, { SettingsActivity.launch(requireContext(), Settings.SECTION_THEME, "") } ), diff --git a/src/android/app/src/main/java/org/citra/citra_emu/ui/main/MainActivity.kt b/src/android/app/src/main/java/org/citra/citra_emu/ui/main/MainActivity.kt index 91df60632..8bb253616 100644 --- a/src/android/app/src/main/java/org/citra/citra_emu/ui/main/MainActivity.kt +++ b/src/android/app/src/main/java/org/citra/citra_emu/ui/main/MainActivity.kt @@ -21,6 +21,7 @@ import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.viewModels import androidx.appcompat.app.AppCompatActivity import androidx.core.content.ContextCompat +import androidx.core.net.toUri import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen import androidx.core.view.ViewCompat import androidx.core.view.WindowCompat @@ -38,6 +39,7 @@ import androidx.work.OneTimeWorkRequest import androidx.work.OutOfQuotaPolicy import androidx.work.WorkManager import com.google.android.material.color.MaterialColors +import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.navigation.NavigationBarView import kotlin.time.Duration.Companion.milliseconds import kotlin.time.TimeSource @@ -47,6 +49,7 @@ import org.citra.citra_emu.NativeLibrary import org.citra.citra_emu.R import org.citra.citra_emu.contracts.OpenFileResultContract import org.citra.citra_emu.databinding.ActivityMainBinding +import org.citra.citra_emu.features.settings.model.BooleanSetting import org.citra.citra_emu.features.settings.model.Settings import org.citra.citra_emu.features.settings.model.SettingsViewModel import org.citra.citra_emu.features.settings.ui.SettingsActivity @@ -189,6 +192,13 @@ class MainActivity : AppCompatActivity(), ThemeProvider { } } + val firstTimeSetup = PreferenceManager.getDefaultSharedPreferences(applicationContext) + .getBoolean(Settings.PREF_FIRST_APP_LAUNCH, true) + + if (!firstTimeSetup && NativeLibrary.isUpdateCheckerEnabled() && BooleanSetting.CHECK_FOR_UPDATES.boolean) { + checkForUpdates() + } + setInsets() } @@ -270,6 +280,37 @@ class MainActivity : AppCompatActivity(), ThemeProvider { } } + private fun checkForUpdates() { + Thread { + val latestVersion = NativeLibrary.getUpdateTag() + if (!latestVersion.isEmpty()) { + runOnUiThread { + showUpdateDialog(latestVersion) + } + } + }.start() + } + + private fun showUpdateDialog(version: String) { + MaterialAlertDialogBuilder(this) + .setTitle(R.string.update_available) + .setMessage(getString(R.string.update_available_description, version)) + .setPositiveButton(android.R.string.ok) { _, _ -> + val url = NativeLibrary.getUpdateUrl() + val intent = Intent(Intent.ACTION_VIEW, url.toUri()) + startActivity(intent) + } + .setNeutralButton(android.R.string.cancel) { dialog, _ -> + dialog.dismiss() + } + .setNegativeButton(R.string.dont_show_again) { dialog, _ -> + BooleanSetting.CHECK_FOR_UPDATES.boolean = false + settingsViewModel.settings.saveSetting(BooleanSetting.CHECK_FOR_UPDATES, SettingsFile.FILE_NAME_CONFIG) + dialog.dismiss() + } + .show() + } + fun finishSetup(navController: NavController) { navController.navigate(R.id.action_firstTimeSetupFragment_to_gamesFragment) (binding.navigationView as NavigationBarView).setupWithNavController(navController) diff --git a/src/android/app/src/main/jni/config.cpp b/src/android/app/src/main/jni/config.cpp index e0fa260c5..5e37dd232 100644 --- a/src/android/app/src/main/jni/config.cpp +++ b/src/android/app/src/main/jni/config.cpp @@ -300,6 +300,10 @@ void Config::ReadValues() { ReadSetting("Miscellaneous", Settings::values.log_filter); ReadSetting("Miscellaneous", Settings::values.log_regex_filter); + // App settings / Theme Settings + ReadSetting("Theme", Settings::values.update_check_channel); + ReadSetting("Theme", Settings::values.check_for_update_on_start); + // Apply the log_filter setting as the logger has already been initialized // and doesn't pick up the filter on its own. Common::Log::Filter filter; diff --git a/src/android/app/src/main/jni/default_ini.h b/src/android/app/src/main/jni/default_ini.h index 5e2eab1d1..02be9ca79 100644 --- a/src/android/app/src/main/jni/default_ini.h +++ b/src/android/app/src/main/jni/default_ini.h @@ -519,6 +519,14 @@ static const char* android_config_default_file_content = (BOOST_HANA_STRING(R"( # 0 (default): No, 1: Yes )") DECLARE_KEY(android_hide_images) BOOST_HANA_STRING(R"( + +[Theme] +# Update check channel for Android. 0: Stable (Default), 1: Pre-release +)") DECLARE_KEY(update_check_channel) BOOST_HANA_STRING(R"( + +# Whether to check for updates on startup. 0: No, 1 (default): Yes +)") DECLARE_KEY(check_for_update_on_start) BOOST_HANA_STRING(R"( + [Debugging] # Record frame time data, can be found in the log directory. Boolean value )") DECLARE_KEY(record_frame_times) BOOST_HANA_STRING(R"( diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp index f4a30610c..9155de1a2 100644 --- a/src/android/app/src/main/jni/native.cpp +++ b/src/android/app/src/main/jni/native.cpp @@ -52,6 +52,10 @@ #include "jni/camera/still_image_camera.h" #include "jni/config.h" +#ifdef ENABLE_UPDATE_CHECKER +#include "common/update_checker.h" +#endif + #ifdef ENABLE_OPENGL #include "jni/emu_window/emu_window_gl.h" #endif @@ -646,6 +650,79 @@ jstring Java_org_citra_citra_1emu_NativeLibrary_getRecommendedExtension( return env->NewStringUTF(j_should_compress ? compressed_ext.c_str() : uncompressed_ext.c_str()); } +JNIEXPORT jboolean JNICALL Java_org_citra_citra_1emu_NativeLibrary_isUpdateCheckerEnabled( + JNIEnv* env, + jobject obj) { +#ifdef ENABLE_UPDATE_CHECKER + return JNI_TRUE; +#else + return JNI_FALSE; +#endif +} + +#ifdef ENABLE_UPDATE_CHECKER +JNIEXPORT void JNICALL Java_org_citra_citra_1emu_NativeLibrary_setCACertificatePath( + JNIEnv* env, + jobject obj, + jstring path) { + const char* path_str = env->GetStringUTFChars(path, nullptr); + UpdateChecker::SetCACertPath(path_str); +} + +bool IsPrereleaseBuild() { + return ((strstr(Common::g_build_fullname, "alpha") != nullptr) || + (strstr(Common::g_build_fullname, "beta") != nullptr) || + (strstr(Common::g_build_fullname, "rc") != nullptr)); +} + +static bool ShouldCheckForPrereleaseUpdates() { + const bool update_channel = Settings::values.update_check_channel.GetValue(); + const bool using_prerelease_channel = + (update_channel == Settings::UpdateCheckChannels::PRERELEASE); + return (IsPrereleaseBuild() || using_prerelease_channel); +} + +static int GetMajorVersion(const std::string& version) { + size_t dot = version.find('.'); + try { + return std::stoi(version.substr(0, dot)); + } catch (...) { + return 0; + } +} + +JNIEXPORT jstring JNICALL Java_org_citra_citra_1emu_NativeLibrary_getUpdateTag( + JNIEnv* env, + jobject obj) { + const std::optional latest_release_tag = + UpdateChecker::GetLatestRelease(ShouldCheckForPrereleaseUpdates()); + + if (latest_release_tag && latest_release_tag.value() != Common::g_build_fullname) { + const int latest_major_version = GetMajorVersion(latest_release_tag.value()); + const int current_major_version = GetMajorVersion(Common::g_build_fullname); + if (current_major_version <= latest_major_version) { + return env->NewStringUTF(latest_release_tag->c_str()); + } + } + + return env->NewStringUTF(""); +} + +JNIEXPORT jstring JNICALL Java_org_citra_citra_1emu_NativeLibrary_getUpdateUrl( + JNIEnv* env, + jobject obj) { + std::string update_page_url; + if (ShouldCheckForPrereleaseUpdates()) { + update_page_url = "https://github.com/azahar-emu/azahar/releases"; + } else { + update_page_url = "https://azahar-emu.org/pages/download/"; + } + + return env->NewStringUTF(update_page_url.c_str()); +} + +#endif + void Java_org_citra_citra_1emu_NativeLibrary_setUserDirectory(JNIEnv* env, [[maybe_unused]] jobject obj, jstring j_directory) { diff --git a/src/android/app/src/main/res/values/arrays.xml b/src/android/app/src/main/res/values/arrays.xml index 2a08cd546..beb74aa8a 100644 --- a/src/android/app/src/main/res/values/arrays.xml +++ b/src/android/app/src/main/res/values/arrays.xml @@ -55,6 +55,15 @@ 3 + + @string/update_check_channel_stable + @string/update_check_channel_pre_release + + + 0 + 1 + + @string/small_screen_position_top_right @string/small_screen_position_middle_right @@ -646,4 +655,6 @@ 9 + Stable + Pre-Release diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml index 750677fd5..3780d78dc 100644 --- a/src/android/app/src/main/res/values/strings.xml +++ b/src/android/app/src/main/res/values/strings.xml @@ -34,7 +34,7 @@ Build version, credits, and more Application directory selected Changes the files that Azahar uses to load applications - Modify the look of the app + Modify the look and behavior of the app Install CIA @@ -431,6 +431,7 @@ Audio Debug Theme and Color + App Settings Layout @@ -948,4 +949,13 @@ Decompression failed. Already installed applications cannot be compressed or decompressed. + + App Settings + Check For Updates + Checks for updates once on app start. + Update Channel + A new version is available: %1$s\n\nWould you like to download it? + Update found! + Don\'t Show Again + diff --git a/src/citra_qt/CMakeLists.txt b/src/citra_qt/CMakeLists.txt index 20971828a..fa065bc4f 100644 --- a/src/citra_qt/CMakeLists.txt +++ b/src/citra_qt/CMakeLists.txt @@ -209,12 +209,6 @@ file(GLOB COMPAT_LIST file(GLOB_RECURSE ICONS ${PROJECT_SOURCE_DIR}/dist/icons/*) file(GLOB_RECURSE THEMES ${PROJECT_SOURCE_DIR}/dist/qt_themes/*) -if (ENABLE_QT_UPDATE_CHECKER) - target_link_libraries(citra_qt PRIVATE httplib json-headers) - target_sources(citra_qt PRIVATE update_checker.cpp) - target_compile_definitions(citra_qt PUBLIC ENABLE_QT_UPDATE_CHECKER) -endif() - if (ENABLE_QT_TRANSLATION) set(CITRA_QT_LANGUAGES "${PROJECT_SOURCE_DIR}/dist/languages" CACHE PATH "Path to the translation bundle for the Qt frontend") option(GENERATE_QT_TRANSLATION "Generate en.ts as the translation source file" OFF) diff --git a/src/citra_qt/citra_qt.cpp b/src/citra_qt/citra_qt.cpp index 9bb0b2e34..dd5e54b5b 100644 --- a/src/citra_qt/citra_qt.cpp +++ b/src/citra_qt/citra_qt.cpp @@ -21,6 +21,7 @@ #include #include #include + #ifdef __APPLE__ #include // for chdir #endif @@ -74,8 +75,8 @@ #include "citra_qt/qt_swizzle.h" #include "citra_qt/uisettings.h" #include "common/play_time_manager.h" -#ifdef ENABLE_QT_UPDATE_CHECKER -#include "citra_qt/update_checker.h" +#ifdef ENABLE_UPDATE_CHECKER +#include "common/update_checker.h" #endif #include "citra_qt/util/clickable_label.h" #include "citra_qt/util/graphics_device_info.h" @@ -180,11 +181,11 @@ bool IsPrereleaseBuild() { (strstr(Common::g_build_fullname, "rc") != NULL)); } -#ifdef ENABLE_QT_UPDATE_CHECKER +#ifdef ENABLE_UPDATE_CHECKER static bool ShouldCheckForPrereleaseUpdates() { - const bool update_channel = UISettings::values.update_check_channel.GetValue(); + const bool update_channel = Settings::values.update_check_channel.GetValue(); const bool using_prerelease_channel = - (update_channel == UISettings::UpdateCheckChannels::PRERELEASE); + (update_channel == Settings::UpdateCheckChannels::PRERELEASE); return (IsPrereleaseBuild() || using_prerelease_channel); } @@ -438,8 +439,8 @@ GMainWindow::GMainWindow(Core::System& system_) } #endif -#ifdef ENABLE_QT_UPDATE_CHECKER - if (UISettings::values.check_for_update_on_start) { +#ifdef ENABLE_UPDATE_CHECKER + if (Settings::values.check_for_update_on_start) { update_future = QtConcurrent::run([]() -> QString { const std::optional latest_release_tag = UpdateChecker::GetLatestRelease(ShouldCheckForPrereleaseUpdates()); @@ -4080,7 +4081,7 @@ void GMainWindow::OnMoviePlaybackCompleted() { QMessageBox::information(this, tr("Playback Completed"), tr("Movie playback completed.")); } -#ifdef ENABLE_QT_UPDATE_CHECKER +#ifdef ENABLE_UPDATE_CHECKER void GMainWindow::OnEmulatorUpdateAvailable() { QString version_string = update_future.result(); if (version_string.isEmpty()) diff --git a/src/citra_qt/citra_qt.h b/src/citra_qt/citra_qt.h index cdf9eaff6..2549798f1 100644 --- a/src/citra_qt/citra_qt.h +++ b/src/citra_qt/citra_qt.h @@ -10,7 +10,7 @@ #ifdef __unix__ #include #endif -#ifdef ENABLE_QT_UPDATE_CHECKER +#ifdef ENABLE_UPDATE_CHECKER #include #include #endif @@ -307,7 +307,7 @@ private slots: void OnDecreaseVolume(); void OnIncreaseVolume(); void OnMute(); -#ifdef ENABLE_QT_UPDATE_CHECKER +#ifdef ENABLE_UPDATE_CHECKER void OnEmulatorUpdateAvailable(); #endif void OnSwitchDiskResources(VideoCore::LoadCallbackStage stage, std::size_t value, @@ -444,7 +444,7 @@ private: std::shared_ptr qt_cameras; -#ifdef ENABLE_QT_UPDATE_CHECKER +#ifdef ENABLE_UPDATE_CHECKER // Prompt shown when update check succeeds QFuture update_future; QFutureWatcher update_watcher; diff --git a/src/citra_qt/configuration/config.cpp b/src/citra_qt/configuration/config.cpp index a5aa9896b..6a49b0263 100644 --- a/src/citra_qt/configuration/config.cpp +++ b/src/citra_qt/configuration/config.cpp @@ -577,9 +577,9 @@ void QtConfig::ReadMiscellaneousValues() { #ifdef __unix__ ReadBasicSetting(Settings::values.enable_gamemode); #endif -#ifdef ENABLE_QT_UPDATE_CHECKER - ReadBasicSetting(UISettings::values.check_for_update_on_start); - ReadBasicSetting(UISettings::values.update_check_channel); +#ifdef ENABLE_UPDATE_CHECKER + ReadBasicSetting(Settings::values.check_for_update_on_start); + ReadBasicSetting(Settings::values.update_check_channel); #endif qt_config->endGroup(); @@ -1160,9 +1160,9 @@ void QtConfig::SaveMiscellaneousValues() { #ifdef __unix__ WriteBasicSetting(Settings::values.enable_gamemode); #endif -#ifdef ENABLE_QT_UPDATE_CHECKER - WriteBasicSetting(UISettings::values.check_for_update_on_start); - WriteBasicSetting(UISettings::values.update_check_channel); +#ifdef ENABLE_UPDATE_CHECKER + WriteBasicSetting(Settings::values.check_for_update_on_start); + WriteBasicSetting(Settings::values.update_check_channel); #endif qt_config->endGroup(); } diff --git a/src/citra_qt/configuration/configure_general.cpp b/src/citra_qt/configuration/configure_general.cpp index ca548b21b..9df674ccf 100644 --- a/src/citra_qt/configuration/configure_general.cpp +++ b/src/citra_qt/configuration/configure_general.cpp @@ -43,7 +43,7 @@ ConfigureGeneral::ConfigureGeneral(QWidget* parent) #ifndef __unix__ ui->toggle_gamemode->setVisible(false); #endif -#ifndef ENABLE_QT_UPDATE_CHECKER +#ifndef ENABLE_UPDATE_CHECKER ui->updates_group->setVisible(false); #endif @@ -90,11 +90,11 @@ void ConfigureGeneral::SetConfiguration() { ui->toggle_background_mute->setChecked( UISettings::values.mute_when_in_background.GetValue()); ui->toggle_hide_mouse->setChecked(UISettings::values.hide_mouse.GetValue()); -#ifdef ENABLE_QT_UPDATE_CHECKER +#ifdef ENABLE_UPDATE_CHECKER ui->toggle_update_checker->setChecked( - UISettings::values.check_for_update_on_start.GetValue()); + Settings::values.check_for_update_on_start.GetValue()); ui->update_channel_combobox->setCurrentIndex( - UISettings::values.update_check_channel.GetValue()); + Settings::values.update_check_channel.GetValue()); #endif #ifdef __unix__ ui->toggle_gamemode->setChecked(Settings::values.enable_gamemode.GetValue()); @@ -182,9 +182,9 @@ void ConfigureGeneral::ApplyConfiguration() { UISettings::values.pause_when_in_background = ui->toggle_background_pause->isChecked(); UISettings::values.mute_when_in_background = ui->toggle_background_mute->isChecked(); UISettings::values.hide_mouse = ui->toggle_hide_mouse->isChecked(); -#ifdef ENABLE_QT_UPDATE_CHECKER - UISettings::values.check_for_update_on_start = ui->toggle_update_checker->isChecked(); - UISettings::values.update_check_channel = ui->update_channel_combobox->currentIndex(); +#ifdef ENABLE_UPDATE_CHECKER + Settings::values.check_for_update_on_start = ui->toggle_update_checker->isChecked(); + Settings::values.update_check_channel = ui->update_channel_combobox->currentIndex(); #endif #ifdef __unix__ Settings::values.enable_gamemode = ui->toggle_gamemode->isChecked(); diff --git a/src/citra_qt/uisettings.h b/src/citra_qt/uisettings.h index ef87b3fe3..3a0d2925d 100644 --- a/src/citra_qt/uisettings.h +++ b/src/citra_qt/uisettings.h @@ -59,12 +59,6 @@ enum class GameListText : s32 { ListEnd, ///< Keep this at the end of the enum. }; -class UpdateCheckChannels { -public: - static constexpr int STABLE = 0; - static constexpr int PRERELEASE = 1; -}; - struct Values { QByteArray geometry; QByteArray state; @@ -89,11 +83,6 @@ struct Values { Settings::Setting pause_when_in_background{false, "pauseWhenInBackground"}; Settings::Setting mute_when_in_background{false, "muteWhenInBackground"}; Settings::Setting hide_mouse{false, "hideInactiveMouse"}; -#ifdef ENABLE_QT_UPDATE_CHECKER - Settings::Setting check_for_update_on_start{true, "check_for_update_on_start"}; - Settings::Setting update_check_channel{UpdateCheckChannels::STABLE, - "update_check_channel"}; -#endif Settings::Setting inserted_cartridge{"", "inserted_cartridge"}; diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 4b6e74c49..6d1f37c7e 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -176,4 +176,10 @@ if (SSE42_COMPILE_OPTION) target_compile_options(citra_common PRIVATE ${SSE42_COMPILE_OPTION}) endif() -target_link_libraries(citra_common PUBLIC xxHash::xxhash) \ No newline at end of file +target_link_libraries(citra_common PUBLIC xxHash::xxhash) + +if (ENABLE_UPDATE_CHECKER) + target_link_libraries(citra_common PRIVATE httplib json-headers) + target_sources(citra_common PRIVATE update_checker.cpp) + target_compile_definitions(citra_common PUBLIC ENABLE_UPDATE_CHECKER) +endif() diff --git a/src/common/settings.h b/src/common/settings.h index 90922bcff..6398ab4a9 100644 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -19,6 +19,13 @@ namespace Settings { +class UpdateCheckChannels { +public: + static constexpr int STABLE = 0; + static constexpr int PRERELEASE = 1; +}; + + enum class GraphicsAPI { Software = 0, OpenGL = 1, @@ -642,6 +649,11 @@ struct Values { // Miscellaneous Setting log_filter{"*:Info", Keys::log_filter}; Setting log_regex_filter{"", Keys::log_regex_filter}; +#ifdef ENABLE_UPDATE_CHECKER + Settings::Setting check_for_update_on_start{true, Keys::check_for_update_on_start}; + Settings::Setting update_check_channel{UpdateCheckChannels::STABLE, + Keys::update_check_channel}; +#endif // Video Dumping std::string output_format; diff --git a/src/citra_qt/update_checker.cpp b/src/common/update_checker.cpp similarity index 94% rename from src/citra_qt/update_checker.cpp rename to src/common/update_checker.cpp index 3686775e4..3557c193d 100644 --- a/src/citra_qt/update_checker.cpp +++ b/src/common/update_checker.cpp @@ -7,9 +7,14 @@ #include #include #include -#include "common/logging/log.h" +#include "logging/log.h" #include "update_checker.h" +std::string g_ca_cert_path; +void UpdateChecker::SetCACertPath(std::string path) { + g_ca_cert_path = std::move(path); +} + std::optional GetResponse(std::string url, std::string path) { constexpr std::size_t timeout_seconds = 15; @@ -18,6 +23,8 @@ std::optional GetResponse(std::string url, std::string path) { client->set_read_timeout(timeout_seconds); client->set_write_timeout(timeout_seconds); + client->set_ca_cert_path(g_ca_cert_path.c_str()); + if (client == nullptr) { LOG_ERROR(Frontend, "Invalid URL {}{}", url, path); return {}; @@ -50,7 +57,9 @@ std::optional GetResponse(std::string url, std::string path) { } std::optional UpdateChecker::GetLatestRelease(bool include_prereleases) { + constexpr auto update_check_url = "http://api.github.com"; + std::string update_check_path = "/repos/azahar-emu/azahar"; try { if (include_prereleases) { // This can return either a prerelease or a stable release, diff --git a/src/citra_qt/update_checker.h b/src/common/update_checker.h similarity index 88% rename from src/citra_qt/update_checker.h rename to src/common/update_checker.h index 843b5a1cb..7d79566e2 100644 --- a/src/citra_qt/update_checker.h +++ b/src/common/update_checker.h @@ -9,4 +9,5 @@ namespace UpdateChecker { std::optional GetLatestRelease(bool); +void SetCACertPath(std::string path); }