Compare commits

...

673 Commits

Author SHA1 Message Date
Liam 05b66877d1 image_info: adjust rescale thresholds and refactor constant use 2023-06-08 17:46:40 -04:00
bunnei 9c6fc44a59 Merge pull request #10650 from qurious-pixel/android_tv
Android TV banner
2023-06-07 16:32:25 -07:00
qurious-pixel 45f80f2b06 remove version code declaration 2023-06-07 13:27:51 -07:00
liamwhite 86cbd867d2 Merge pull request #10655 from Morph1984/msvc-cxx20
CMakeLists: Force C++20 on MSVC due to conflicts with C++23 modules
2023-06-07 14:04:25 -04:00
liamwhite 5c79a07a36 Merge pull request #10635 from mrcmunir/l4t-tx1-nvidia
Make VK_EXT_robustness2 optional
2023-06-07 14:04:14 -04:00
liamwhite cfb76d8f3e Merge pull request #10476 from ameerj/gl-memory-maps
OpenGL: Make use of persistent buffer maps in buffer cache
2023-06-07 14:03:57 -04:00
liamwhite 6907d30258 Merge pull request #10583 from ameerj/ill-logic
AccelerateDMA: Fix incorrect check in Buffer<->Texture copies
2023-06-07 14:03:40 -04:00
liamwhite 219bd90152 Merge pull request #10591 from keve1227/localized-game-icons
Localize game icons
2023-06-07 14:03:28 -04:00
Morph f62f43c0da CMakeLists: Force C++20 on MSVC due to conflicts with C++23 modules
The latest version of MSVC STL brings C++23 standard library modules, which conflict with precompiled headers.
Disabling with /experimental:module- has no effect, so force C++20 in the meantime while we wait for module support in other compilers.
2023-06-06 20:20:09 -04:00
Morph 238e46ec93 Merge pull request #10651 from Morph1984/a
github/gitmodules: Misc fixes
2023-06-06 17:15:41 -04:00
Morph 08ba16e105 gitmodules: Fix libadrenotools submodule 2023-06-06 15:12:12 -04:00
Morph 099f3f639e github: Remove release workflow 2023-06-06 15:12:12 -04:00
Morph 4c9769d7ea Merge pull request #10649 from german77/version
android: Set version code
2023-06-06 15:11:54 -04:00
Live session user 9611a9e220 Android TV banner 2023-06-06 11:32:25 -07:00
Narr the Reg 4d61319307 android: Set version code 2023-06-06 12:14:38 -06:00
Carlos Estrague / Mrc_munir b854981917 Updated to lexicographical order suggestions 2023-06-06 19:33:52 +02:00
Narr the Reg d967ab8e79 Merge pull request #10643 from 8bitDream/gradle-config
android: Improve Gradle build configuration
2023-06-06 11:19:20 -06:00
Narr the Reg 7d5cc33feb Merge pull request #10641 from 8bitDream/ci-android
android: Fix ci builds with Java 17
2023-06-06 11:17:46 -06:00
Abandoned Cart 0968e315f4 android: Improve Gradle build configuration 2023-06-06 12:46:21 -04:00
Abandoned Cart 5ff28ffc6d android: Fix ci builds with Java 17 2023-06-06 07:06:34 -04:00
bunnei 069d7e6be4 android: audio_core: sink_stream: Remove unnecessary check. 2023-06-05 21:47:36 -07:00
bunnei cb95d7fe1b Merge pull request #10508 from yuzu-emu/lime
Project Lime - yuzu Android Port
2023-06-05 21:43:43 -07:00
Carlos Estrague / Mrc_munir 19d05bd4d7 Make VK_EXT_robustness2 optional
For some reason nvidia implemented Vulkan 1.2 supported without support for VK_EXT_robustness2 in tegra X1/X2 .

Fix vulkan work in TX1/TX2  L4T drivers .
2023-06-06 06:32:47 +02:00
bunnei 036996429e Merge pull request #10633 from t895/variable-surface-ratio
android: Use a custom view for changing emulation aspect ratio
2023-06-05 20:27:58 -07:00
bunnei dc2a0b2e50 Merge pull request #10578 from PabloG02/lime-firmware&logs
Add UI to import firmware and share logs
2023-06-05 17:41:19 -07:00
bunnei e1078ec0f4 android: HomeSettingsFragment: Use string resource for "Share log". 2023-06-05 17:40:43 -07:00
Charles Lombardo c8b91b3a89 android: Use a custom view for changing emulation aspect ratio
Credit to the Skyline team for the FixedRatioSurfaceView.
2023-06-05 20:24:36 -04:00
bunnei db7b106f1d Merge pull request #10611 from liamwhite/audio-deadlock
audio_renderer: resolve adsp thread deadlock shutdown
2023-06-05 17:15:19 -07:00
bunnei 71766f3269 Merge pull request #10618 from t895/licenses
android: Add licenses page
2023-06-05 17:14:15 -07:00
PabloG02 409ff26f02 Address feedback 2023-06-06 00:07:54 +02:00
Charles Lombardo cba5865afe android: Create licenses page 2023-06-05 14:34:23 -04:00
bunnei 2f7658bd75 Merge pull request #10613 from t895/settings-changes
android: String and settings organization changes
2023-06-04 19:17:42 -07:00
bunnei 78319435e6 Merge pull request #10622 from t895/load-settings
android: Load settings at the start of each activity
2023-06-04 19:14:38 -07:00
Charles Lombardo 5e58af0616 android: Move settings to debug submenu 2023-06-04 19:53:27 -04:00
Charles Lombardo 0078f97227 android: Load settings at the start of each activity 2023-06-04 19:37:10 -04:00
bunnei e6fce1cbbd Merge pull request #10594 from liamwhite/double-patch
fsp-srv: avoid patching romfs multiple times
2023-06-04 13:24:47 -07:00
PabloG02 3733187c14 Attempt to move the unzip coroutine to a ViewModel 2023-06-04 20:52:12 +02:00
PabloG02 72597b8ffe android: update strings 2023-06-04 20:52:12 +02:00
PabloG02 8713c442e9 android: add option to share log 2023-06-04 20:52:12 +02:00
PabloG02 5435f0be5e android: add option to install firmware 2023-06-04 20:52:12 +02:00
PabloG02 19674ec78d android: move unzip function to FileUtil and use SecurityException 2023-06-04 20:50:00 +02:00
Charles Lombardo 5de8c5b5c7 android: Several string changes 2023-06-04 13:30:56 -04:00
Liam e96a3a1713 audio_renderer: resolve adsp thread deadlock shutdown 2023-06-04 13:00:10 -04:00
bunnei 125a0e5a07 Merge pull request #10588 from liamwhite/vfs-cached
vfs: add vfs_cached for romfs build
2023-06-03 23:23:45 -07:00
Charles Lombardo e804f24519 Merge pull request #10605 from 8bitDream/kotlin
android: Resolve a couple Gradle warnings
2023-06-04 02:22:34 -04:00
bunnei c5782150f0 Merge pull request #10550 from kkoniuszy/linux-map-buildup-fix
host_memory: merge adjacent placeholder mappings on Linux
2023-06-03 23:22:24 -07:00
Abandoned Cart 00a391ce10 android: Resolve a couple Gradle warnings 2023-06-04 02:06:38 -04:00
bunnei 42074dc6c7 Merge pull request #10595 from 8bitDream/deprecated
android: Replace deprecated and Java code
2023-06-03 22:08:30 -07:00
Abandoned Cart cfa8bec5b9 android: Add support for split foldable view 2023-06-03 22:57:28 -04:00
Kevin Sundqvist Norlén a2cfe3749a Fix typo
Co-authored-by: liamwhite <liamwhite@users.noreply.github.com>
2023-06-03 21:31:44 +02:00
Abandoned Cart b394a6b937 android: Replace deprecated and Java code 2023-06-03 15:16:25 -04:00
Liam a75bc759fe fsp-srv: avoid patching romfs multiple times 2023-06-03 14:27:08 -04:00
Keve1227 a0f235f4fd Update Chinese NX language names
... as per the TLoZ: TotK icon files. Would this conflict with older games?
2023-06-03 17:23:14 +02:00
Keve1227 27313fe576 Issue a reload if the system language changed 2023-06-03 17:17:03 +02:00
Keve1227 d0aa63069f Pick game icon based on the configured system language 2023-06-03 17:13:24 +02:00
Minionguyjpro 58c54aecb6 Update README.md (Add Android at builds description) (#10586) 2023-06-03 16:29:26 +02:00
Liam 6e23c84669 romfs: use vfs_cached for romfs output 2023-06-03 08:56:59 -04:00
Liam 790f91fcc5 vfs: add vfs_cached for romfs build 2023-06-03 08:50:54 -04:00
bunnei 296ccb698d android: cmake: Use cmake_dependent_option as appropriate. 2023-06-03 00:14:33 -07:00
Charles Lombardo a789046127 android: Fix crash on importing invalid save 2023-06-03 00:06:08 -07:00
bunnei db6737f2ba android: vk_presentation_manager: Fix unusued needs_recreation. 2023-06-03 00:06:08 -07:00
Charles Lombardo f94eb320ff android: Rename "Input Overlay" to "Overlay Options" 2023-06-03 00:06:08 -07:00
Charles Lombardo c927a30d09 android: Adjust import/export saves dialog 2023-06-03 00:06:08 -07:00
Charles Lombardo 20abd49a21 android: Warning dialogs for key errors 2023-06-03 00:06:08 -07:00
bunnei df70fdc95b android: vk_turbo_mode: Remove unnecessary device recreation.
- Fixes a rare crash.
2023-06-03 00:06:08 -07:00
bunnei 4ac9778652 android: EmulationFragment: Remove unnecessary surface destroy on pause. 2023-06-03 00:06:08 -07:00
bunnei 098e2c4077 android: renderer_vulkan: Fix crash with surface recreation. 2023-06-03 00:06:07 -07:00
bunnei 057117f009 android: Fix presentation layout on foldable and tablet devices. 2023-06-03 00:06:07 -07:00
Charles Lombardo ca4b07a2d7 android: Enable overlay scale/opacity dialog 2023-06-03 00:06:07 -07:00
PabloG02 1957b7e6cc Add image to card_game.xml to preview in the Layout Editor 2023-06-03 00:06:07 -07:00
PabloG02 a7e0a0d5b1 Save the position of buttons as a percentage 2023-06-03 00:06:07 -07:00
Charles Lombardo 8e8627a258 android: Don't crash the app when selecting a zip that causes a SecurityException 2023-06-03 00:06:07 -07:00
bunnei eb4ab9bc58 input_common: Fix virtual amiibos 2023-06-03 00:06:06 -07:00
bunnei 40e938376b android: audio_core: Avoid shutdown hang. 2023-06-03 00:06:06 -07:00
bunnei 9ca8687b5f android: ForegroundService: Handle null intent. 2023-06-03 00:06:06 -07:00
bunnei 17ae85e724 android: ImportExportSavesFragment: Cleanup strings. 2023-06-03 00:06:06 -07:00
bunnei b325ad16bc Update src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/ImportExportSavesFragment.kt 2023-06-03 00:06:06 -07:00
PabloG02 4010764279 Remove ?. 2023-06-03 00:06:06 -07:00
PabloG02 9650c1d02d Check if folder exists before letting the user import/export saves 2023-06-03 00:06:06 -07:00
PabloG02 33d36ded28 Add save import/export in UI 2023-06-03 00:06:06 -07:00
bunnei b2570d2546 android: Use ext-android-bin for external binaries. 2023-06-03 00:06:06 -07:00
Charles Lombardo ad9c2356a8 android: Fix FPS text getting cut off by rounded display corners 2023-06-03 00:06:05 -07:00
Charles Lombardo fdd200d33f android: Prevent deleting the settings file while a game is running 2023-06-03 00:06:05 -07:00
Charles Lombardo 4e87a01db6 android: Fix link text color for base theme dialog 2023-06-03 00:06:05 -07:00
bunnei ee8caac82f android: Various fixes for CI. 2023-06-03 00:06:05 -07:00
bunnei 17b5ed9baf android: externals: Update libadrenotools, use useLegacyPackaging. 2023-06-03 00:06:05 -07:00
Charles Lombardo 0f9c5b8d6a android: Re-enable service notification 2023-06-03 00:06:05 -07:00
Charles Lombardo 09747ca2d3 android: Ensure keys are loaded before populating games list 2023-06-03 00:06:05 -07:00
Charles Lombardo 897b748895 android: Use dialog fragment for the reset settings dialog 2023-06-03 00:06:05 -07:00
Charles Lombardo 9c1d42342d android: Upgrade AGP to 8.0.2 2023-06-03 00:06:04 -07:00
Charles Lombardo 7de86266fd android: Show notification permission page during setup 2023-06-03 00:06:04 -07:00
Charles Lombardo 419daf770d android: DIsable FPS counter by default 2023-06-03 00:06:04 -07:00
Charles Lombardo bfb4e3bcaa android: Improve searches with one character
The Jaccard algorithm is great for searches with 2 or more characters but nothing is returned for searches with one character. To get around this, just search with JaroWinkler for single character searches.
2023-06-03 00:06:04 -07:00
Charles Lombardo 0cbae33790 android: Stop building x86 packages in APKs
This was really only meant for building the app to run in an emulator. If this is necessary, just add manually.
2023-06-03 00:06:04 -07:00
Charles Lombardo d49eb7faad android: Add FPS toggle 2023-06-03 00:06:04 -07:00
liushuyu be6159842a CI: use the verify pipeline to do releases 2023-06-03 00:06:04 -07:00
Charles Lombardo 8426e97f45 android: Clean up app build.gradle
Removes the conflicting declaration of "version" and changes to versionCode that did nothing.
2023-06-03 00:06:04 -07:00
bunnei cf9f4f67dd video_core: vk_rasterizer: Decrease draw dispatch count for Android. 2023-06-03 00:06:04 -07:00
bunnei b4725332a2 android: config: Expose VSync as a configurable setting. 2023-06-03 00:06:04 -07:00
bunnei f0ed20c8a2 android: GPU: Enable async presentation, increase frames in flight. 2023-06-03 00:06:03 -07:00
Charles Lombardo 116e2b5f02 android: Enable onBackInvokedCallback
For now this enables the ability to see the new Android 13 back gesture animations but later we can create custom animations that follow the back gesture.
2023-06-03 00:06:03 -07:00
Charles Lombardo 7812de4ade android: Remove deprecated use of onBackPressed() 2023-06-03 00:06:03 -07:00
Charles Lombardo ffba83d568 android: Add option for touch overlay haptics
Disabled by default
2023-06-03 00:06:03 -07:00
Charles Lombardo 5dbf842a46 android: Improve missing game handling
Previously the app would crash if you selected a game that no longer existed. Now we show an error message and reload the games list to remove any invalid games from the list.
2023-06-03 00:06:03 -07:00
Charles Lombardo c11c5b2eb7 android: Clean up dependencies
Additionally updates material and androidx core libraries
2023-06-03 00:06:03 -07:00
Charles Lombardo a10b1c8ef5 android: Delete java code style file 2023-06-03 00:06:03 -07:00
Charles Lombardo e42c966110 android: Settings UI tweaks
New spacing and fonts for list items
2023-06-03 00:06:03 -07:00
Charles Lombardo 412c95e0b0 android: Simplify setup in search and games fragments 2023-06-03 00:06:03 -07:00
Charles Lombardo aa8a48e94c android: Use collapsing toolbar layout in settings 2023-06-03 00:06:03 -07:00
Charles Lombardo b31ab11842 android: Remove unnecessary JvmStatic/JvmField annotations 2023-06-03 00:06:02 -07:00
Charles Lombardo 8d3288b6ff android: Fix navigation rail animation in rtl layout 2023-06-03 00:06:02 -07:00
Charles Lombardo c930b2bad2 android: Use cutout insets on setup fragment 2023-06-03 00:06:02 -07:00
Charles Lombardo ee57aa83a4 android: Button to reset all settings 2023-06-03 00:06:02 -07:00
Charles Lombardo 0f06e73a7c android: Use proguard file in relWithDebInfo 2023-06-03 00:06:02 -07:00
Charles Lombardo a0a0703f30 android: Fix background color within inset areas 2023-06-03 00:06:02 -07:00
Charles Lombardo 070d250858 android: Shortcut to settings activity on reselection 2023-06-03 00:06:01 -07:00
Charles Lombardo c31a37c828 android: Expose custom RTC setting 2023-06-03 00:06:01 -07:00
Charles Lombardo f7934bdaf7 android: Reset setting on long press 2023-06-03 00:06:01 -07:00
Charles Lombardo 2289f7ad15 android: Fix issues with ea/main icons and version codes
Now all yuzu icon variants are taken care of and now we have a build variant that uses the versioning we need for the play store.
2023-06-03 00:06:01 -07:00
Charles Lombardo 5213701e18 android: Move theme options out of advanced settings 2023-06-03 00:06:01 -07:00
Charles Lombardo bafd4d344f android: Check if cached games are valid
Fixes bug when you close yuzu, delete games, and reopen to an instant crash.
2023-06-03 00:06:01 -07:00
german77 aa957df0dc android: Invert rotation to match phone orientation 2023-06-03 00:06:01 -07:00
bunnei 5d43594a70 android: vulkan_device: Skip BGR565 emulation on S8gen2. 2023-06-03 00:06:01 -07:00
bunnei 1a424ea6c6 android: config: Use default anisotropic filtering. 2023-06-03 00:06:01 -07:00
Charles Lombardo 41c20f8460 android: Remove top padding from in game menu items 2023-06-03 00:06:00 -07:00
Charles Lombardo ec81c6cf44 android: Use different icons for mainline/ea 2023-06-03 00:06:00 -07:00
Charles Lombardo f69bc78dc5 android: Add early access upgrade fragment
We now have a second build flavor that will determine whether the "Get Early Access" button appears.
2023-06-03 00:06:00 -07:00
bunnei 8b8123b733 android: vulkan_device: Only compile OverrideBcnFormats when used. 2023-06-03 00:06:00 -07:00
Liam 6b2e89a865 android: remove spurious warnings about BCn formats when patched with adrenotools 2023-06-03 00:06:00 -07:00
bunnei befd477279 android: video_core: Disable some problematic things on GPU Normal. 2023-06-03 00:06:00 -07:00
bunnei 6b093224c1 android: settings: Use mailbox vsync by default. 2023-06-03 00:06:00 -07:00
bunnei 8dc7fe0c96 android: video_core: Disable problematic compute shaders.
- Fixes #104.
2023-06-03 00:06:00 -07:00
Charles Lombardo 1c9dae7cac android: Update progard to fix settings crash
R8 full mode was removing important classes from Wini that would cause a crash on saving settings. This keeps the relevant classes and suppresses warnings about irrelevant ones.
2023-06-03 00:05:59 -07:00
bunnei 117bc2ae6c android: vulkan: Recreate surface after suspension & adapt to async. presentation. 2023-06-03 00:05:59 -07:00
Charles Lombardo 65dc35a1a5 android: Game data cache 2023-06-03 00:05:59 -07:00
Charles Lombardo b0bef6173a android: Update to Kotlin 1.8.21 2023-06-03 00:05:59 -07:00
Charles Lombardo c16e663f70 android: Disable jetifier
We no longer depend on any legacy libraries that required this flag
2023-06-03 00:05:59 -07:00
Charles Lombardo d8bacdfc14 android: Update dependencies 2023-06-03 00:05:59 -07:00
Charles Lombardo 56d3711e34 android: Migrate to AGP 8.0.1 2023-06-03 00:05:59 -07:00
Charles Lombardo 48065c7a0e android: Enable non-transitive R classes
New default going forward for new android projects. Best to follow the new standard.
2023-06-03 00:05:59 -07:00
bunnei 570c4a2c1b android: config: Enable asynchronous presentation by default on Android. 2023-06-03 00:05:59 -07:00
bunnei ca4bf3844e video_core: Enable support_descriptor_aliasing on Turnip, disable storage atomic otherwise. 2023-06-03 00:05:58 -07:00
german77 e5bdb7011b android: fix deadzone calculation 2023-06-03 00:05:58 -07:00
Charles Lombardo 13b29c3f49 android: Fix background color when starting emulation 2023-06-03 00:05:58 -07:00
Charles Lombardo 34b7d58157 android: Persistent scrollbars on home settings fragment 2023-06-03 00:05:58 -07:00
Charles Lombardo f461465a92 android: Use short build hash 2023-06-03 00:05:58 -07:00
Charles Lombardo 86e395595a android: Use navigation bar shade view 2023-06-03 00:05:58 -07:00
Charles Lombardo 21e8a8277a android: About fragment 2023-06-03 00:05:58 -07:00
Charles Lombardo 09e7b14d0e android: Use x-axis animation for navigation rail 2023-06-03 00:05:58 -07:00
Charles Lombardo 03541703fa android: Sort games alphabetically by default 2023-06-03 00:05:58 -07:00
Charles Lombardo 9f433e281a android: New icons for navigation bar 2023-06-03 00:05:57 -07:00
Charles Lombardo efc054e47f android: New icons for home settings fragment 2023-06-03 00:05:57 -07:00
Charles Lombardo 9f6f21946c android: Add navigation rail 2023-06-03 00:05:57 -07:00
Charles Lombardo 6df030998a android: Search Fragment 2023-06-03 00:05:57 -07:00
Charles Lombardo 3281dc597e android: Fix potential zip traversal exploit 2023-06-03 00:05:57 -07:00
german77 d1fb7ea58b android: Add dedicated show overlay checkbox 2023-06-03 00:05:57 -07:00
Charles Lombardo 912bf6a0c6 android: Add user directory shortcut 2023-06-03 00:05:57 -07:00
german77 265b9139e0 android: Fix inline keyboard input 2023-06-03 00:05:57 -07:00
Charles Lombardo 7131432037 android: Fix grammatical mistake in video core error message 2023-06-03 00:05:56 -07:00
Charles Lombardo 4bb82e4860 android: Adjust wording on GPU driver install button 2023-06-03 00:05:56 -07:00
Narr the Reg c46a1da5dc android: Add deadzone to stick input 2023-06-03 00:05:56 -07:00
german77 166bff88b8 android: Move motion listener to emulation activity 2023-06-03 00:05:56 -07:00
Narr the Reg 5c1310dc5d core: hid: Finish linking motion from virtual controllers 2023-06-03 00:05:56 -07:00
Charles Lombardo d015b0db93 android: Change wording for "Add Games" button (#100)
Co-authored-by: bunnei <bunneidev@gmail.com>
2023-06-03 00:05:56 -07:00
Charles Lombardo 0d16805445 android: Scroll shortcut for games list
If you reselect the "Games" menu item in the bottom navigation menu, the list smoothly scrolls to the top.
2023-06-03 00:05:56 -07:00
Charles Lombardo 274b2be24f android: Setup screen hotfix
Added help button link for add games warning and a check for whether a task was completed on a given screen.
2023-06-03 00:05:56 -07:00
Charles Lombardo 792ce5cb2f android: Swap Default and Install buttons for GPU driver installation dialog 2023-06-03 00:05:56 -07:00
Charles Lombardo 72247a2324 android: Add warnings to setup screens 2023-06-03 00:05:56 -07:00
Charles Lombardo 71667e4d6d android: Allow search bar to scroll offscreen 2023-06-03 00:05:55 -07:00
Charles Lombardo 3b9791d887 android: Update app icon
Small icon updates from Flam
2023-06-03 00:05:55 -07:00
Charles Lombardo c070a588b9 android: Change organization of the settings tab in the home screen 2023-06-03 00:05:55 -07:00
Charles Lombardo c29f14fa0f android: Properly pop setup fragment from the back stack 2023-06-03 00:05:55 -07:00
Charles Lombardo f41939b66e android: Vertically scalable setup pages
Previously the setup pages would remain at a fixed height but now the icon and two text boxes will give up space as a device gets shorter. This eliminates the need for a scrolling view further problems with padding.
2023-06-03 00:05:55 -07:00
Charles Lombardo 48c506682d android: Fix setup rotation bug
If you rotated the device at the "Add Games" screen the buttons would disappear until you trigged them from the beginning page swap. Now button state is saved across recreation.
2023-06-03 00:05:55 -07:00
Charles Lombardo 61e0042633 android: Temporarily switch for a fixed version code for testing 2023-06-03 00:05:55 -07:00
Charles Lombardo 986f858e6f android: Fix alignment of SwipeRefreshLayout 2023-06-03 00:05:55 -07:00
Charles Lombardo 388dc0757f android: Shape/spacing adjustments to game card
Ripple effect now reaches into rounded corners, icon size changed, company text removed, title font adjusted, and spacing around the card was adjusted as well. Text also doesn't get cut off anymore and instead scrolls indefinitely on one line.
2023-06-03 00:05:55 -07:00
Charles Lombardo f984775f12 android: Manual tweaks for dialog colors
Small fix for Flam
2023-06-03 00:05:54 -07:00
Charles Lombardo f2cadc4ce1 android: Fix black backgrounds bug
Start using a specific night mode check because black backgrounds could apply incorrectly when using the light app mode, dark system mode, and black backgrounds. Launching the settings activity will show light mode colors/navigation bars but with black backgrounds.
2023-06-03 00:05:54 -07:00
Charles Lombardo 033adb9723 android: Use navigation bar shade view for settings activity 2023-06-03 00:05:54 -07:00
Charles Lombardo cfa3d73f2f android: Disable editing themes during emulation 2023-06-03 00:05:54 -07:00
Charles Lombardo 62c47011a0 android: Prevent situation where binding is called on a null view 2023-06-03 00:05:54 -07:00
Charles Lombardo d7178ed16e android: Add black backgrounds toggle 2023-06-03 00:05:54 -07:00
Charles Lombardo d9684a2010 android: Add theme mode picker 2023-06-03 00:05:54 -07:00
Charles Lombardo f0ba58f5aa android: Add theme picker 2023-06-03 00:05:54 -07:00
Charles Lombardo 5b26ac9293 android: Prevent potential abstract settings crash 2023-06-03 00:05:54 -07:00
Charles Lombardo b6f57da70a android: Fix cast for abstract settings 2023-06-03 00:05:54 -07:00
Charles Lombardo 7531641ecc android: Create xml for Material You theme 2023-06-03 00:05:54 -07:00
Charles Lombardo 89593d70bf android: Remove check for API 29 in themes 2023-06-03 00:05:53 -07:00
Charles Lombardo 8cef21a0b0 android: Adjustments to home option card
Several spacing/color adjustments provided by Flam
2023-06-03 00:05:53 -07:00
Charles Lombardo a82f8fe1c1 android: Use different colors for logo in options menu
Reverting to the official logo colors
2023-06-03 00:05:53 -07:00
Charles Lombardo 9673974f73 android: New default theme colors 2023-06-03 00:05:53 -07:00
Charles Lombardo 6549348656 android: Use libnx default icon
Credit to jaames for the original icon
2023-06-03 00:05:53 -07:00
Liam 8b4c5a9939 android: enable LTO 2023-06-03 00:05:53 -07:00
Charles Lombardo 83dae1739c android: Show error if invalid keys file is selected
There aren't MIME types specific enough for filtering out files that aren't amiibo or production keys. So here we just check for the extensions "bin" or "keys" where appropriate and stop the process if incorrect. Previously you could select any document and it could cause the app to hang.
2023-06-03 00:05:53 -07:00
Charles Lombardo d8c102444c android: Fix first time setup scrolling bug
If you quickly scrolled from the second page to the first and then back, the next/back buttons would disappear.
2023-06-03 00:05:53 -07:00
Charles Lombardo 45d81dc6bd android: Fix A button preference key 2023-06-03 00:05:53 -07:00
Charles Lombardo 59525ddbeb android: First time setup screen 2023-06-03 00:05:52 -07:00
Charles Lombardo 766655fa41 android: Prevent editing unsafe settings at runtime
There currently isn't a visual "disabled" cue for any of the view holders that aren't the switch setting. This will be improved in the future.
2023-06-03 00:05:52 -07:00
Charles Lombardo c609847e49 android: Abstract settings
Previously we could only add settings that would change our ini file. Now we can create abstract settings in our presenter to alter things like shared preferences for theme support!
2023-06-03 00:05:52 -07:00
german77 6dfe4240ac android: Implement gamepad input 2023-06-03 00:05:52 -07:00
Charles Lombardo f5c48f92f2 android: Bump minimum version to Android 11 2023-06-03 00:05:52 -07:00
Charles Lombardo 9c9d9131b5 android: Decouple status bar shade from navigation bar visibility 2023-06-03 00:05:52 -07:00
Charles Lombardo d57ae50f17 android: Enable code minification 2023-06-03 00:05:52 -07:00
Charles Lombardo 921e6dddcc android: Switch from a colored status bar to a custom view
Allows for smoother transitions with the search bar
2023-06-03 00:05:52 -07:00
Charles Lombardo 3f2b832371 android: Adjustments to card_game
Removed a currently unused text view and moved to material text views.
2023-06-03 00:05:51 -07:00
Charles Lombardo 233ae9ab69 android: MainActivity overhaul
This moves several parts of the main activity into fragments that manage themselves to react to changes. UI changes like the appearance of a new search view or when the games list changes now gets updated via multiple view models. This also starts a conversion to the androidx navigation component which furthers the goals mentioned previously with more fragment responsibility. This will eventually allow us to use one activity with interchanging fragments and multiple view models that are stored within that central activity.

fdas
2023-06-03 00:05:51 -07:00
Charles Lombardo 859c40f00e android: Enforce Vulkan 1.1 support as minimum 2023-06-03 00:05:51 -07:00
Charles Lombardo e5487243d3 android: Update gradle version to 8.1 2023-06-03 00:05:51 -07:00
Charles Lombardo b1ebc1c395 android: Update app dependencies 2023-06-03 00:05:51 -07:00
Charles Lombardo ab87f74998 android: Convert gradle scripts to Kotlin DSL 2023-06-03 00:05:51 -07:00
bunnei 12c9e18b55 android: vulkan: Disable vertex_input_dynamic_state on Qualcomm. 2023-06-03 00:05:51 -07:00
bunnei d2e55558df android: settings: Add scaling filter & anti-aliasing options. (#66) 2023-06-03 00:05:50 -07:00
bunnei 4006468f73 android: video_core: Add support for disk shader cache. (#64) 2023-06-03 00:05:50 -07:00
bunnei 6d2e7de2e0 android: vulkan_debug_callback: Ignore many innocuous errors. 2023-06-03 00:05:50 -07:00
bunnei 30bf5d5b07 android: config: Change docked mode and GPU accuracy to favor performance on Android. 2023-06-03 00:05:50 -07:00
german77 dc52152a81 service: account: Save user profile folder on first user creation 2023-06-03 00:05:50 -07:00
german77 f9974f7ef3 android: Initialize account manager 2023-06-03 00:05:50 -07:00
german77 5aec62930c android: Remove unsafe null check 2023-06-03 00:05:50 -07:00
Charles Lombardo 78f02ab9a6 android: Scale input overlay independently of system display scale 2023-06-03 00:05:50 -07:00
Charles Lombardo aeaddf407b android: Use apply instead of commit for shared preferences
Previously we were operating on the assumption that apply'd settings wouldn't be visible immediately. This isn't true and settings will be accessible via memory before being stored to disk. This reduces any potential stutters caused by saving to shared preferences.
2023-06-03 00:05:50 -07:00
Charles Lombardo fcbf08ca98 android: Add DPad slide toggle 2023-06-03 00:05:50 -07:00
Charles Lombardo bebc822334 android: Add relative stick center toggle 2023-06-03 00:05:49 -07:00
Charles Lombardo 63819af214 android: Make hash and branch accessible from BuildConfig 2023-06-03 00:05:49 -07:00
Charles Lombardo f5055ca930 android: Backup shared preferences where applicable 2023-06-03 00:05:49 -07:00
Charles Lombardo 940dbdcff2 android: Enable retaining app data after uninstall 2023-06-03 00:05:49 -07:00
Charles Lombardo 1cc8011355 android: Remove unused doFrame function 2023-06-03 00:05:49 -07:00
Charles Lombardo 4d9011a8f0 android: Convert NativeLibrary to Kotlin 2023-06-03 00:05:49 -07:00
Charles Lombardo a827486391 android: Remove LocalBroadcastManager
This causes a couple of minor changes to directory initialization. We don't have a lengthy initialization step so we could spend less time creating state receivers and just run initialization on the main thread. We also don't have a situation where external storage will be a concern so checks are removed in favor of a binary check to see if initialization is ready.

This additionally removes the unused DoFrame callback.
2023-06-03 00:05:49 -07:00
Charles Lombardo 9d7a60346f android: Remove game database
The content provider + database solution was excessive and is now replaced with the simple file checks from before but turned into an array list held within a viewmodel.
2023-06-03 00:05:49 -07:00
Charles Lombardo 4816d5158e android: Adjust game icon loading 2023-06-03 00:05:49 -07:00
Charles Lombardo 730c63df7e android: Remove unused dimensions files 2023-06-03 00:05:48 -07:00
Charles Lombardo 83b1459af5 android: Slightly reduce game card size 2023-06-03 00:05:48 -07:00
Charles Lombardo ac417a4ffa android: Only show company text view if it has content 2023-06-03 00:05:48 -07:00
Charles Lombardo c0aa5392b9 android: Fix check for ok text in software keyboard 2023-06-03 00:05:48 -07:00
Narr the Reg ca4be4283d android: Implement amiibo reading from nfc tag 2023-06-03 00:05:48 -07:00
bunnei b2aeb50229 android: vulkan_device: Disable VK_EXT_custom_border_color on Adreno.
- Causes crashes on sampler creation with Super Mario Odyssey.
2023-06-03 00:05:48 -07:00
Charles Lombardo 1ea84854bd android: Add toggle controls option to input overlay 2023-06-03 00:05:48 -07:00
Charles Lombardo 8b99a1e49b android: Do not update FPS text on null view 2023-06-03 00:05:48 -07:00
Charles Lombardo d30103b69f android: Convert keyboard applet to kotlin and refactor 2023-06-03 00:05:48 -07:00
bunnei d5ebfc8e21 android: Implement basic software keyboard applet. 2023-06-03 00:05:47 -07:00
bunnei 58ede89c60 android: config: Disable shader cache by default on Android. 2023-06-03 00:05:47 -07:00
german77 a862c33fc4 android: Fix fps counter not showing up 2023-06-03 00:05:47 -07:00
Charles Lombardo ec048361af android: Prevent showing games on an invalid view 2023-06-03 00:05:47 -07:00
Charles Lombardo b0a434b99f android: Re-implement overlay editing 2023-06-03 00:05:47 -07:00
Charles Lombardo 5807cf1b4d android: Fix popup menu going out of bounds 2023-06-03 00:05:47 -07:00
Charles Lombardo 3878c6ced1 android: Use autofit grid for games fragment 2023-06-03 00:05:47 -07:00
Charles Lombardo 9cb7e7072d android: Prevent updating empty game list text on invalid view 2023-06-03 00:05:47 -07:00
Charles Lombardo 295ffd4d47 android: Persist settings across configuration changes
Mostly things get refactored here to remove previous assumptions made about how the activity/fragment lifecycles would operate. The important change for persistence is removing the assumption that the user will be at the first settings fragment on recreation when deciding whether or not to reload settings. Now we check a flag in Settings to know if we loaded the settings within this lifecycle.
2023-06-03 00:05:47 -07:00
Charles Lombardo aaefe8a0e0 android: Store settings object in viewmodel 2023-06-03 00:05:47 -07:00
Charles Lombardo 06e58cf088 android: Remove configChanges exceptions 2023-06-03 00:05:46 -07:00
Charles Lombardo 35e9a99452 Android: Enable resizeable activities 2023-06-03 00:05:46 -07:00
Charles Lombardo 89b9285627 android: Fix emulation fragment comments 2023-06-03 00:05:46 -07:00
Charles Lombardo 273e81bb94 android: Use modal navigation drawer as in game menu 2023-06-03 00:05:46 -07:00
Charles Lombardo 1f3b41366c android: Make Game class parcelable 2023-06-03 00:05:46 -07:00
Charles Lombardo c53e927368 android: Add kotlin parcelize plugin 2023-06-03 00:05:46 -07:00
Charles Lombardo e2a7143a3d android: Remove deprecated use of onActivityResult 2023-06-03 00:05:46 -07:00
Charles Lombardo d9e7e71a8e android: Fix RTL layouts 2023-06-03 00:05:46 -07:00
Charles Lombardo 3c9aa8d230 android: Use ellipsis character 2023-06-03 00:05:46 -07:00
Charles Lombardo 16c7afbd46 android: Move all array strings to main strings file 2023-06-03 00:05:46 -07:00
Charles Lombardo 03d9247527 android: Remove unused strings 2023-06-03 00:05:45 -07:00
Charles Lombardo bf584d85ad android: Remove unused colors 2023-06-03 00:05:45 -07:00
Charles Lombardo 9a842deba5 android: Remove citra date time picker 2023-06-03 00:05:45 -07:00
Charles Lombardo 87211c8aec android: Remove unused premium header layout 2023-06-03 00:05:45 -07:00
Charles Lombardo 1c93ac8e03 android: Remove unused fragment animations 2023-06-03 00:05:45 -07:00
Charles Lombardo 27ec749bdf android: Remove unused string arrays 2023-06-03 00:05:45 -07:00
Charles Lombardo dafa6dff07 android: Remove unused integer xmls 2023-06-03 00:05:45 -07:00
Charles Lombardo 74653f1e80 android: Refactor ic_launcher.xml to drawables 2023-06-03 00:05:45 -07:00
Charles Lombardo 8a34e58ad6 android: Suppress lint in InsetsHelper 2023-06-03 00:05:45 -07:00
Charles Lombardo 1b40a3df19 android: Add data extraction rules 2023-06-03 00:05:44 -07:00
Charles Lombardo 93d6a1fc9c android: Remove requestLegacyExternalStorage attribute 2023-06-03 00:05:44 -07:00
Charles Lombardo c803d9e5c7 android: Remove unused permissions 2023-06-03 00:05:44 -07:00
Charles Lombardo d3c3b69755 android: Inset input overlay based on system cutouts 2023-06-03 00:05:44 -07:00
Narr the Reg 3f35b34515 Use yuzu as category instead of citra 2023-06-03 00:05:44 -07:00
Charles Lombardo 1634391bff android: Stop updating fps counter when emulation stops 2023-06-03 00:05:44 -07:00
Charles Lombardo a49a24b079 android: Move driver installation off of main thread
Additionally creates an indeterminate loading dialog during installation
2023-06-03 00:05:44 -07:00
Charles Lombardo 72bef4fa95 android: Fix crash when decodeGameIcon creates a null Bitmap 2023-06-03 00:05:44 -07:00
Charles Lombardo 72679c7bae android: Use view binding 2023-06-03 00:05:44 -07:00
Charles Lombardo e49e6cac7e android: Enable view binding 2023-06-03 00:05:44 -07:00
Charles Lombardo 4de3abdd5a android: Refactor CheckBoxSetting to SwitchSetting 2023-06-03 00:05:44 -07:00
bunnei b5b4e50c32 android: EmulationActivity: Fix variable shadowing in fragment creation. 2023-06-03 00:05:43 -07:00
bunnei f45a0b94d6 android: res: fragment_emulation: Ensure FPS counter is shown. 2023-06-03 00:05:43 -07:00
Liam b19754c73f common: link libandroid on android 2023-06-03 00:05:43 -07:00
Liam ee10cdad35 cmake: download architecture-specific ffmpeg for android 2023-06-03 00:05:43 -07:00
Liam 616cf70a80 build: only enable adrenotools on arm64 2023-06-03 00:05:43 -07:00
Charles Lombardo 515f3deea1 android: Use Skyline's document provider 2023-06-03 00:05:43 -07:00
Charles Lombardo 55e4c2d87b android: Use androidx splash screen 2023-06-03 00:05:43 -07:00
Charles Lombardo 3fcc6b1104 android: Replace Picasso with Coil 2023-06-03 00:05:43 -07:00
Charles Lombardo 37cc94526b android: New swipe to refresh color scheme 2023-06-03 00:05:43 -07:00
Charles Lombardo 352559b83d android: New settings fragment animations 2023-06-03 00:05:43 -07:00
Charles Lombardo f40059e4ba android: Use edge to edge 2023-06-03 00:05:43 -07:00
Charles Lombardo 5840d60724 android: Use Material 3 components 2023-06-03 00:05:42 -07:00
Charles Lombardo 18f4ef436d android: Modernize theme system 2023-06-03 00:05:42 -07:00
Charles Lombardo 09780c76aa android: Use vector icons 2023-06-03 00:05:42 -07:00
Charles Lombardo 527229c8b3 android: Use adaptive icon 2023-06-03 00:05:42 -07:00
bunnei c385b2b07b android: settings: Dynamically evaluate valueAsString
Co-Authored-By: bunnei <bunneidev@gmail.com>
2023-06-03 00:05:42 -07:00
Charles Lombardo 2d934720f5 android: Add license identifier 2023-06-03 00:05:42 -07:00
Charles Lombardo 95af1b2a23 android: Convert YuzuApplication to Kotlin 2023-06-03 00:05:42 -07:00
Charles Lombardo 1a4de8d213 android: Convert Action1 to Kotlin 2023-06-03 00:05:42 -07:00
Charles Lombardo 7a0d7bb3f3 android: Convert GameViewHolder to Kotlin 2023-06-03 00:05:41 -07:00
Charles Lombardo 8a4eb062e8 android: Remove ThemeUtil 2023-06-03 00:05:41 -07:00
Charles Lombardo b8eb8bd2b5 android: Convert StartupHandler to Kotlin 2023-06-03 00:05:41 -07:00
Charles Lombardo de1dff557d android: Convert Log to Kotlin 2023-06-03 00:05:41 -07:00
Charles Lombardo a05c6deb66 android: Convert GpuDriverMetadata to Kotlin 2023-06-03 00:05:41 -07:00
Charles Lombardo d2f9cf133d android: Convert GpuDriverHelper to Kotlin 2023-06-03 00:05:41 -07:00
Charles Lombardo bf0c383024 android: Convert GameIconRequestHandler to Kotlin 2023-06-03 00:05:41 -07:00
Charles Lombardo 5d5233ec32 android: Convert ForegroundService to Kotlin 2023-06-03 00:05:41 -07:00
Charles Lombardo 7b54c2b2e2 android: Convert FileUtil to Kotlin 2023-06-03 00:05:41 -07:00
Charles Lombardo 7fb7f3e83f android: Convert FileBrowserHelper to Kotlin 2023-06-03 00:05:41 -07:00
Charles Lombardo c9d2d74f1f android: Convert EmulationMenuSettings to Kotlin 2023-06-03 00:05:40 -07:00
Charles Lombardo 2444df2bf4 android: Convert DocumentsTree to Kotlin 2023-06-03 00:05:40 -07:00
Charles Lombardo 711aedeaae android: Convert DirectoryStateReceiver to Kotlin 2023-06-03 00:05:40 -07:00
Charles Lombardo a8994a57d6 android: Convert DirectoryInitialization to Kotlin 2023-06-03 00:05:40 -07:00
Charles Lombardo b9f1f70688 android: Convert ControllerMappingHelper to Kotlin 2023-06-03 00:05:40 -07:00
Charles Lombardo c02a27ebd2 android: Convert BiMap to Kotlin 2023-06-03 00:05:40 -07:00
Charles Lombardo 8710c6e14c android: Convert AddDirectoryHelper to Kotlin 2023-06-03 00:05:40 -07:00
Charles Lombardo e83de8eefb android: Convert PlatformGamesView to Kotlin 2023-06-03 00:05:40 -07:00
Charles Lombardo fcce7b898f android: Convert PlatformGamesPresenter to Kotlin 2023-06-03 00:05:40 -07:00
Charles Lombardo 0b2350ad5b android: Convert PlatformGamesFragment to Kotlin 2023-06-03 00:05:40 -07:00
Charles Lombardo 67c2e89d2c android: Convert MainView to Kotlin 2023-06-03 00:05:39 -07:00
Charles Lombardo 0f742b3464 android: Convert MainPresenter to Kotlin 2023-06-03 00:05:39 -07:00
Charles Lombardo 42b3e72e96 android: Convert InputOverlayDrawableJoystick to Kotlin 2023-06-03 00:05:39 -07:00
Charles Lombardo 5c8372a566 android: Convert MainActivity to Kotlin 2023-06-03 00:05:39 -07:00
Charles Lombardo f4508b255f android: Remove ExampleInstrumentedTest 2023-06-03 00:05:39 -07:00
Charles Lombardo 482d3e0b5f android: Remove TwoPaneOnBackPressedCallback
Leftover UI code for dolphin's cheat system. Removing for now.
2023-06-03 00:05:39 -07:00
Charles Lombardo d85678a80f android: Convert InputOverlayDrawableDpad to Kotlin 2023-06-03 00:05:39 -07:00
Charles Lombardo 0177e908e9 android: Convert InputOverlayDrawableButton to Kotlin 2023-06-03 00:05:39 -07:00
Charles Lombardo a1c57de466 android: Convert InputOverlay to Kotlin 2023-06-03 00:05:39 -07:00
Charles Lombardo 096cdc57bb android: Remove DividerItemDecoration
Removed in favor of material components version
2023-06-03 00:05:39 -07:00
Charles Lombardo 24ade95a13 android: Inherit from Material 3 themes
Partially breaks the UI for now but is necessary to use new material components.
2023-06-03 00:05:38 -07:00
Charles Lombardo b5819594ba android: Convert MinimalDocumentFile to Kotlin 2023-06-03 00:05:38 -07:00
Charles Lombardo 9e8ab499dc android: Convert GameProvider to Kotlin 2023-06-03 00:05:38 -07:00
Charles Lombardo 4ce86a526c android: Convert GameDatabase to Kotlin 2023-06-03 00:05:38 -07:00
Charles Lombardo bbe5dee9f8 android: Convert Game to Kotlin 2023-06-03 00:05:38 -07:00
Charles Lombardo 66079923ae android: Convert EmulationFragment to Kotlin 2023-06-03 00:05:38 -07:00
Charles Lombardo 0e4256651a android: Convert SettingsFile to Kotlin 2023-06-03 00:05:38 -07:00
Charles Lombardo a29c615f8d android: Convert SettingsFrameLayout to Kotlin 2023-06-03 00:05:38 -07:00
Charles Lombardo c39bf17f83 android: Convert SettingsFragmentView to Kotlin 2023-06-03 00:05:38 -07:00
Charles Lombardo 14d156701f android: Convert SettingsFragmentPresenter to Kotlin 2023-06-03 00:05:38 -07:00
Charles Lombardo 1fc66f1b30 android: Convert SettingsFragment to Kotlin 2023-06-03 00:05:37 -07:00
Charles Lombardo 469f0ec019 android: Convert SettingsActivityView to Kotlin 2023-06-03 00:05:37 -07:00
Charles Lombardo 34ce4877bd android: Convert SettingsActivityPresenter to Kotlin 2023-06-03 00:05:37 -07:00
Charles Lombardo b10e13c090 android: Convert SettingsActivity to Kotlin 2023-06-03 00:05:37 -07:00
Charles Lombardo ed83650ee4 android: Convert SubmenuViewHolder to Kotlin 2023-06-03 00:05:37 -07:00
Charles Lombardo 6044d924f7 android: Convert SliderViewHolder to Kotlin 2023-06-03 00:05:37 -07:00
Charles Lombardo fd1801aec4 android: Convert SingleChoiceViewHolder to Kotlin 2023-06-03 00:05:37 -07:00
Charles Lombardo c42eb92557 android: Convert SettingViewHolder to Kotlin 2023-06-03 00:05:37 -07:00
Charles Lombardo d472f41580 android: Convert HeaderViewHolder to Kotlin 2023-06-03 00:05:37 -07:00
Charles Lombardo e02e33826b android: Convert DateTimeViewHolder to Kotlin 2023-06-03 00:05:37 -07:00
Charles Lombardo 3a5b9ecba2 android: Convert CheckBoxSettingViewHolder to Kotlin 2023-06-03 00:05:36 -07:00
Charles Lombardo f0a9fcf100 android: Convert StringSetting to Kotlin 2023-06-03 00:05:36 -07:00
Charles Lombardo 412ec72d26 android: Convert SettingSection to Kotlin 2023-06-03 00:05:36 -07:00
Charles Lombardo 6f80f9d5b0 android: Convert Setting to Kotlin 2023-06-03 00:05:36 -07:00
Charles Lombardo 6e7fdcb484 android: Convert IntSetting to Kotlin 2023-06-03 00:05:36 -07:00
Charles Lombardo 2439fc8374 android: Convert FloatSetting to Kotlin 2023-06-03 00:05:36 -07:00
Charles Lombardo 88b9d484e8 android: Convert BooleanSetting to Kotlin 2023-06-03 00:05:36 -07:00
Charles Lombardo b98aaf1635 android: Convert SubmenuSetting to Kotlin 2023-06-03 00:05:36 -07:00
Charles Lombardo 91884976a1 android: Convert StringSingleChoiceSetting to Kotlin 2023-06-03 00:05:36 -07:00
Charles Lombardo 89eed93ce0 android: Convert SliderSetting to Kotlin 2023-06-03 00:05:36 -07:00
Charles Lombardo a0e91e3a93 android: Convert SingleChoiceSetting to Kotlin 2023-06-03 00:05:35 -07:00
Charles Lombardo 22b44be0b2 android: Convert SettingsItem to Kotlin 2023-06-03 00:05:35 -07:00
Charles Lombardo 537c16d4cf android: Convert HeaderSetting to Kotlin 2023-06-03 00:05:35 -07:00
Charles Lombardo 21841b6520 android: Convert DateTimeSetting to Kotlin 2023-06-03 00:05:35 -07:00
Charles Lombardo 4d9cfc6798 android: Convert CheckBoxSetting to Kotlin 2023-06-03 00:05:35 -07:00
Charles Lombardo 0d044e9f2f android: Convert GameAdapter to Kotlin 2023-06-03 00:05:35 -07:00
Charles Lombardo 87f4c3f105 android: Convert SettingsAdapter to Kotlin
Update SettingsAdapter.kt
2023-06-03 00:05:35 -07:00
Charles Lombardo 39a65f8446 android: Convert EmulationActivity to Kotlin 2023-06-03 00:05:35 -07:00
Charles Lombardo 7cd72a7c6d android: Use material slider in settings dialog 2023-06-03 00:05:35 -07:00
Charles Lombardo fa38c7be4f android: Convert Settings to Kotlin 2023-06-03 00:05:35 -07:00
Charles Lombardo 753a0c6b5d android: Use androidx preferences 2023-06-03 00:05:34 -07:00
bunnei 2dfbfadf82 android: frontend: Add unique error strings for Vulkan initialization errors. 2023-06-03 00:05:34 -07:00
german77 bde568c3c5 android: Use the center of the object and reduce draw calls 2023-06-03 00:05:34 -07:00
german77 7dd02363a3 android: Replace old buttons with vectors 2023-06-03 00:05:34 -07:00
Charles Lombardo 834d53fbbf android: Enable Kotlin support 2023-06-03 00:05:34 -07:00
Charles Lombardo 96ea063b45 android: Upgrade java version to 11 2023-06-03 00:05:33 -07:00
Charles Lombardo 9191ae23ec android: Upgrade dependencies 2023-06-03 00:05:33 -07:00
Charles Lombardo 82dca7e586 android: Upgrade to AGP 7.4.2 2023-06-03 00:05:33 -07:00
Charles Lombardo fc785972f9 android: Replace lintOptions with lint 2023-06-03 00:05:33 -07:00
Charles Lombardo 9507e99165 android: Move namespace to app module build.gradle 2023-06-03 00:05:33 -07:00
Charles Lombardo fc0c5fa86f android: bump compile/target sdk to 33 2023-06-03 00:05:33 -07:00
Charles Lombardo 9762646112 android: Upgrade gradle to 8.0.1 2023-06-03 00:05:33 -07:00
liushuyu e26bd1421e video_core: fix clang-format errors 2023-06-03 00:05:33 -07:00
liushuyu 19eec22b38 CMake: fix pkg-config behavior when building for Android 2023-06-03 00:05:33 -07:00
liushuyu 1d0329a065 CI: add Android build systems 2023-06-03 00:05:33 -07:00
bunnei 26ee6844c2 android: build.gradle: Cleanup build types. 2023-06-03 00:05:32 -07:00
bunnei 4769d716fc android: frontend: settings: Add graphics debugging. 2023-06-03 00:05:32 -07:00
bunnei 0276197744 android: jni: Ensure system is only initialized once.
- Fixes likelihood that fastmem allocation succeeds.
2023-06-03 00:05:32 -07:00
bunnei ff2f370946 video_core: vulkan_device: Correct error message for unsuitable driver. 2023-06-03 00:05:32 -07:00
bunnei dcbf0c43c0 android: frontend: Cleanup framerate counter. 2023-06-03 00:05:32 -07:00
bunnei 8248d69093 android: vulkan: Implement adrenotools turbo mode. 2023-06-03 00:05:32 -07:00
bunnei 21320d80d9 android: vulkan_device: Disable VK_EXT_extended_dynamic_state2 on Qualcomm.
- Newer drivers report this as supported, but it is broken.
2023-06-03 00:05:32 -07:00
bunnei b0f8aef057 android: frontend: Add support for GPU driver selection. 2023-06-03 00:05:32 -07:00
bunnei 4c38220a64 android: native: Add support for custom Vulkan driver loading. 2023-06-03 00:05:31 -07:00
bunnei ae099d583c core: frontend: Refactor GraphicsContext to its own module. 2023-06-03 00:05:31 -07:00
bunnei 32cf6beee3 common: dynamic_library: Add ctor for existing handle. 2023-06-03 00:05:31 -07:00
bunnei e9f35d3260 android: EmulationFragment: Always reset overlay.
- Ensures correct placement until we have better overlay configuration.
2023-06-03 00:05:31 -07:00
Billy Laws b4a12b889e Avoid using VectorExtractDynamic for subgroup mask on Adreno GPUs
This crashes their shader compiler for some reason.
2023-06-03 00:05:31 -07:00
Billy Laws 158a1896ec Implement scaled vertex buffer format emulation
These formats are unsupported by mobile GPUs so they need to be emulated in shaders instead.
2023-06-03 00:05:31 -07:00
Billy Laws 206f1304d6 Disable push descriptors on adreno drivers
Regular descriptors are around 1.5x faster to update.
2023-06-03 00:05:31 -07:00
Billy Laws 26bdecbf45 Disable VK_EXT_extended_dynamic_state on mali 2023-06-03 00:05:31 -07:00
Billy Laws a3c261d940 Disable multithreaded pipeline compilation on Qualcomm drivers
This causes crashes during compilation on several 6xx and 5xx driver versions.
2023-06-03 00:05:31 -07:00
Narr the Reg f1bb2f3685 android: Add motion sensor 2023-06-03 00:05:30 -07:00
Narr the Reg 92fb7cc4e4 android: Hook jni input properly 2023-06-03 00:05:30 -07:00
Narr the Reg 5b80dee181 android: cleanup touch update loop 2023-06-03 00:05:30 -07:00
Narr the Reg 3be891ea6f android: Clean joystick overlay 2023-06-03 00:05:30 -07:00
Narr the Reg 639a1f885c android: Clean dpad overlay 2023-06-03 00:05:30 -07:00
Narr the Reg 1ab269775d android: Clean button overlay 2023-06-03 00:05:30 -07:00
Narr the Reg 43e43021a3 android: Add all buttons to screen controller 2023-06-03 00:05:30 -07:00
Narr the Reg 58531ecf4f android: Apply clang format 2023-06-03 00:05:30 -07:00
bunnei 0e52d11ede android: frontend: Implement game grid view. (#9) 2023-06-03 00:05:30 -07:00
german77 5ed8d46340 android: Replace notification icon with yuzu 2023-06-03 00:05:30 -07:00
bunnei 7a89c2fe3a android: strings: Refresh key dumping URL. 2023-06-03 00:05:29 -07:00
bunnei ddf10cdb18 android: frontend: Modify ROM load messaging for invalid keys. 2023-06-03 00:05:29 -07:00
bunnei 93cf8c3090 android: frontend: Integrate key installation for SAF. 2023-06-03 00:05:29 -07:00
bunnei 63a98e3e1c android: jni: Add function to reload keys. 2023-06-03 00:05:29 -07:00
bunnei 93bad47edb core: crypto: key_manager: Add methods to reload & validate keys. 2023-06-03 00:05:29 -07:00
bunnei f33776af67 android: EmulationActivity: Temporarily disable running notification. 2023-06-03 00:05:29 -07:00
bunnei ef605f7d8f android: Implement SAF support & migrate to SDK 31. (#4) 2023-06-03 00:05:29 -07:00
bunnei 39ab81a098 android: Harden emulation shutdown when loader fails. 2023-06-03 00:05:29 -07:00
bunnei e12e1efa40 android: SettingsFragmentPresenter: Fix default renderer backend. 2023-06-03 00:05:29 -07:00
bunnei d6a41b3290 android: jni: native: Add lock around HaltEmulation, tighten run loop. 2023-06-03 00:05:29 -07:00
bunnei d553fd4c3a android: jni: native: Refactor locking for is_running. 2023-06-03 00:05:28 -07:00
bunnei 104ff475d2 android: jni: native: Remove unnecessary atomic for is_running. 2023-06-03 00:05:28 -07:00
bunnei 9ba67eab4f android: jni: native: Tighten up emulation start/stop signaling. 2023-06-03 00:05:28 -07:00
bunnei 1e94d16dad android: jni: native: Consolidate emulation state into EmulationSession singleton.
- Fixes state management issues across multiple boots.
- Fixes crashes related to unsafe access of perf stats.
2023-06-03 00:05:28 -07:00
bunnei 6cc21a56d9 android: Frontend: Fix rendering aspect ratio & add a setting for it. 2023-06-03 00:05:28 -07:00
bunnei 4f903d8d35 android: Integrate settings frontend with yuzu & remove unused code. 2023-06-03 00:05:28 -07:00
Liam f7a3f1ddf4 externals: add adrenotools for bcenabler 2023-06-03 00:05:28 -07:00
Liam 7cdeaa90af device_memory: Use smaller virtual reservation size for compatibility with 39-bit paging 2023-06-03 00:05:28 -07:00
bunnei 2972a3ccc7 video_core: vulkan_device: Device initialization for Adreno. 2023-06-03 00:05:28 -07:00
bunnei 91350524c2 video_core: vk_pipeline_cache: Disable support_descriptor_aliasing on Android. 2023-06-03 00:05:28 -07:00
bunnei 6ed62a9f10 video_core: vk_swapchain: Fix image format for Android. 2023-06-03 00:05:28 -07:00
bunnei 5e198d1421 android: Minimize frontend & convert to yuzu. 2023-06-03 00:05:27 -07:00
bunnei 18527a8c42 video_core: vk_blit_screen: Rotate viewport for Android landscape. 2023-06-03 00:05:27 -07:00
bunnei cdbab60bbb common: error: Fix for Android. 2023-06-03 00:05:27 -07:00
bunnei f8b87e6fab common: fs: Implement for Android. 2023-06-03 00:05:27 -07:00
bunnei 99296a1510 common: logging: Implement Android logcat backend. 2023-06-03 00:05:26 -07:00
bunnei afdee9abea common: host_memory: Implement for Android. 2023-06-03 00:05:26 -07:00
bunnei e6d5dbb58e android: Minimal JNI for yuzu. 2023-06-03 00:05:26 -07:00
bunnei bb2cbbfba3 android: Add Citra frontend. 2023-06-03 00:05:26 -07:00
bunnei 851b1008a8 cmake: Integrate bundled FFmpeg for Android. 2023-06-03 00:05:26 -07:00
bunnei 5de8ee7bba cmake: Integrate submoduled LLVM & fixes for Android. 2023-06-03 00:05:26 -07:00
bunnei e931bb8c44 Merge pull request #10575 from bm01/issue-template
issue_template: Add link to website, Getting Log Files
2023-06-02 23:58:04 -07:00
ameerj 1fc47361a1 texture_cache: Fix incorrect logic for AccelerateDMA 2023-06-02 18:07:52 -04:00
Baptiste Marie 206fe987e1 issue_template: Add link to website, Getting Log Files 2023-06-02 17:21:46 +02:00
kkoniuszy 584e8b5c52 host_memory: merge adjacent placeholder mappings on Linux
Track the private anonymous placeholder mappings created by Unmap() and
wherever possible, replace existing placeholders with larger ones
instead of creating many small ones.

This helps with the buildup of mappings in /proc/YUZU_PID/maps after a
longer gaming session, improving stability without having to increase
vm.max_map_count to a ridiculous value. The amount of placeholder
mappings will no longer outgrow the amount of actual memfd mappings in
cases of high memory fragmentation.
2023-06-01 22:57:27 +02:00
liamwhite 9d7131bc82 Merge pull request #10541 from zhaobot/tx-update-20230601030142
Update translations (2023-06-01)
2023-06-01 10:53:29 -04:00
liamwhite d89b98d8e8 Merge pull request #10091 from Kelebek1/bc_bugggggg
Fix buffer overlap checking skipping a page for stream score right expand
2023-06-01 09:06:07 -04:00
liamwhite 00b6bef65a Merge pull request #10530 from Kelebek1/syncpt_oob
Fix incorrect syncpt id bounds check and potential out of bounds lookup
2023-06-01 09:05:42 -04:00
liamwhite 988f486061 Merge pull request #10474 from GPUCode/you-left-me-waiting
Remove timeline semaphore wait
2023-06-01 09:05:30 -04:00
liamwhite 5ab5987e74 Merge pull request #10352 from grimkor/add-context-menu-status-bar-settings
add context menu for status bar settings
2023-06-01 09:05:23 -04:00
liamwhite 1a4f3b37f1 Merge pull request #10482 from german77/gamelist
yuzu: Disable game list while game is running
2023-06-01 09:05:18 -04:00
liamwhite e4cde17e5b Merge pull request #10518 from lat9nq/ffmpeg-zombie-var
CopyFFmpegDeps: Update variable name
2023-06-01 09:05:05 -04:00
The yuzu Community 96a9032d6d Update translations (2023-06-01) 2023-06-01 03:01:52 +00:00
Kelebek1 6839341f17 Fix incorrect id check and potential out of bounds lookup 2023-05-31 19:10:00 +01:00
lat9nq 9a1c64264d CopyFFmpegDeps: Update variable name
FFmpeg_DLL_DIR does not exist anywhere else in the repository.
Evidently, the variable name was antiquated at some point, but it
continued to work here as a zombie.

Update the name and avoid copy issues.
2023-05-31 03:59:46 +00:00
bunnei 05e38ee149 Merge pull request #10506 from Kelebek1/bc_channel_fix
Skip BufferCache tickframe with no channel state set
2023-05-30 14:42:12 -07:00
Kelebek1 661375a222 Skip BufferCache tickframe with no channel state set 2023-05-30 21:57:13 +01:00
Narr the Reg 810d19b6be Merge pull request #10504 from 12101111/rename-pagesize
input_common: rename PAGE_SIZE to avoid conflict
2023-05-30 06:13:01 -06:00
12101111 f78f82e08d input_common: rename PAGE_SIZE to avoid conflict
See also: https://github.com/yuzu-emu/yuzu/issues/8779
2023-05-30 16:59:06 +08:00
german77 e5be1835ab yuzu: Disable game list while game is running 2023-05-29 10:00:34 -06:00
liamwhite 53fd08b360 Merge pull request #10484 from Morph1984/fmt10
externals: Update to fmt 10 and add format_as formatter for BitField
2023-05-28 16:33:42 -04:00
Morph 124dd86820 CMakeLists: Rollback minimum to fmt 9
The mingw fmt package https://aur.archlinux.org/packages/mingw-w64-fmt has not been updated yet.
2023-05-28 15:20:35 -04:00
Morph 9950a388d2 externals: Update to fmt 10 and add format_as formatter for BitField
Implicit conversions are now disallowed in fmt 10. Use format_as to convert to the underlying type.
2023-05-28 15:05:55 -04:00
liamwhite b26018e2e6 Merge pull request #10475 from ameerj/microprofile-workaround
microprofile: Avoid crashing due to OOB stackPos
2023-05-28 13:18:54 -04:00
liamwhite 381caf4c00 Merge pull request #10483 from ameerj/gl-cpu-astc
gl_texture_cache: Fix ASTC CPU decoding with compression disabled
2023-05-28 13:18:31 -04:00
liamwhite 379d4b5e6a Merge pull request #10280 from danilaml/cmake-bin-dir
Use TARGET_FILE_DIR generator expression
2023-05-28 13:18:06 -04:00
liamwhite bf3f450211 Merge pull request #10283 from danilaml/support-interlaced-videos
Add support for deinterlaced video playback
2023-05-28 13:17:58 -04:00
liamwhite 09743fa681 Merge pull request #10376 from abouvier/cmake-default
cmake: apply defaults to all externals
2023-05-28 13:17:50 -04:00
liamwhite 93c17ee4da Merge pull request #10463 from liamwhite/this-is-why-we-need-g
vfs_concat: fix time complexity of read
2023-05-28 13:17:42 -04:00
liamwhite 18595738fd Merge pull request #10464 from liamwhite/clear-cache
qt: add menu item to remove cache storage
2023-05-28 13:17:33 -04:00
liamwhite e994388b43 Merge pull request #10469 from Kelebek1/bc_state
Move buffer bindings to per-channel state
2023-05-28 13:17:26 -04:00
liamwhite 88ccc420b8 Merge pull request #10471 from Kelebek1/test2
Wait indefinitely when audio buffer queue is too big
2023-05-28 13:17:12 -04:00
ameerj ea2e155b0b gl_texture_cache: Fix ASTC CPU decoding with compression disabled
gl_format was incorrectly being overwritten when compression was disabled
2023-05-28 13:14:51 -04:00
ameerj cb0a410907 gl_staging_buffers: Optimization to reduce fence waiting 2023-05-28 00:38:47 -04:00
ameerj 642c14f0c7 OpenGL: Make use of persistent buffer maps in buffer cache downloads
Persistent buffer maps were already used by the texture cache, this extends their usage for the buffer cache.

In my testing, using the memory maps for uploads was slower than the existing "ImmediateUpload" path, so the memory map usage is limited to downloads for the time being.
2023-05-28 00:38:46 -04:00
ameerj fee91096ca microprofile: Avoid crashing due to OOB stack pos 2023-05-27 22:24:22 -04:00
GPUCode 4a292efbff renderer_vulkan: Remove timeline semaphore wait 2023-05-28 02:39:44 +03:00
Kelebek1 9c2b211f12 Audren wait as suggested by ByLaws 2023-05-27 17:38:07 +01:00
Kelebek1 b0bea13ed8 Move buffer bindings to per-channel state 2023-05-27 17:04:18 +01:00
Liam fcd48eb239 qt: add menu item to remove cache storage 2023-05-26 23:29:44 -04:00
Matías Locatti d6db422098 Merge pull request #10414 from liamwhite/anv-push-descriptor
vulkan_device: Enable VK_KHR_push_descriptor on newer ANV
2023-05-26 17:36:37 -03:00
Matías Locatti 919b54848b Merge pull request #10418 from liamwhite/blink-and-youll-miss-it
texture_cache: process aliases and overlaps in the correct order
2023-05-26 17:36:09 -03:00
Matías Locatti eff0cc3591 Merge pull request #10459 from liamwhite/shf
shader_recompiler: fix copy-paste error
2023-05-26 17:35:13 -03:00
Liam 0596a4afb1 vfs_concat: fix time complexity of read 2023-05-26 16:07:38 -04:00
Kelebek1 7ce181edcf Fix buffer overlap checking skipping a page for stream score right expand 2023-05-26 10:35:46 +01:00
Liam 13d25063a1 shader_recompiler: fix copy-paste error 2023-05-26 00:36:12 -04:00
bunnei 83b502c08c Merge pull request #10221 from Kelebek1/partial_dsp_revert
Add a 5ms tiemout to the DSP processing wait
2023-05-25 21:34:50 -07:00
bunnei ffa1fba7d6 Merge pull request #10396 from german77/amiibo_write
input_common: Implement amiibo writing
2023-05-25 14:07:16 -07:00
liamwhite a596c6e438 Merge pull request #10454 from 521337/fix-u-option
Don't exit when using "-u" option in yuzu-cmd
2023-05-25 14:12:51 -04:00
Fernando S 3c3830953a Merge pull request #10452 from liamwhite/ibgc
video_core: don't garbage collect during configuration
2023-05-25 20:09:08 +02:00
Ariel Cabello 7d5df4f0ba Don't exit when using "-u" option in yuzu-cmd 2023-05-25 20:07:52 +02:00
liamwhite 385636dbb1 Merge pull request #10450 from 521337/add-u-option
Add short "-u" option for yuzu_cmd.
2023-05-25 12:24:36 -04:00
Liam 904dc1a567 video_core: don't garbage collect during configuration 2023-05-25 12:03:12 -04:00
Ariel Cabello d33bdc97d0 Add short "-u" option for yuzu_cmd.
The -u short option was documented but not implemented in yuzu_cmd.
The same long option --user worked before.
2023-05-25 16:05:22 +02:00
bunnei e264ab4ad0 Merge pull request #10415 from german77/amiibo-no-key
service: nfc: Remove encryption key requirement
2023-05-24 22:14:55 -07:00
bunnei 73a0ea0738 Merge pull request #10435 from FernandoS27/gotta-clean-mess-ups
Texture cache: revert wrong acceleration assumption
2023-05-24 21:00:53 -07:00
bunnei 593236f211 Merge pull request #10433 from FernandoS27/theres-a-lime-coming-around
Texture Cache Util: Fix block depth adjustment on slices.
2023-05-24 20:59:21 -07:00
Fernando Sahmkow be3a7f4096 Texture cache: revert wrong acceleration assumption 2023-05-24 10:52:02 +02:00
Fernando Sahmkow 01c4568786 Texture Cache Util: Fix block depth adjustment on slices. 2023-05-24 10:06:58 +02:00
Fernando Sahmkow 72c1ee1bf9 texture_cache: process aliases and overlaps in the correct order 2023-05-24 09:53:42 +02:00
Alexandre Bouvier 9c3c7ec009 cmake: apply defaults to all externals 2023-05-24 04:57:04 +02:00
Fernando S 72a14a6817 Merge pull request #10422 from liamwhite/gc
video_core: tune garbage collection aggressiveness
2023-05-24 03:58:49 +02:00
Fernando S 4ee4fdf78c Merge pull request #10417 from liamwhite/check-state
k_memory_block_manager: remove auditing calls
2023-05-24 03:56:58 +02:00
Fernando S 76f6388969 Merge pull request #10398 from liamwhite/bcn
video_core: add ASTC recompression
2023-05-24 03:55:45 +02:00
grimkor b3ebfd2481 add context menu for filter and anti-aliasing status buttons 2023-05-24 00:21:04 +00:00
Liam 3b1172c10f video_core: tune garbage collection aggressiveness 2023-05-23 12:55:14 -04:00
Liam 415c78b87c textures: add BC1 and BC3 compressors and recompression setting 2023-05-23 12:54:40 -04:00
liamwhite a5d4c3e5ad Merge pull request #10388 from GPUCode/fence-wait
vk_master_semaphore: Move fence wait on separate thread
2023-05-23 09:42:56 -04:00
liamwhite 58be9b12f4 Merge pull request #10402 from liamwhite/uh
renderer_vulkan: barrier attachment feedback loops
2023-05-23 09:42:49 -04:00
liamwhite 09ed5ec665 Merge pull request #10411 from scorpion81/gc-steamdeck-fix-attempt
Limit device access memory for integrated devices to 4 GB
2023-05-23 09:42:39 -04:00
Liam 5e3b3c6643 k_memory_block_manager: remove auditing calls 2023-05-23 09:15:16 -04:00
Narr the Reg f63586c5f5 service: nfc: Remove encryption key requirement 2023-05-22 18:42:09 -06:00
Liam 8bba9f7dea vulkan_device: Enable VK_KHR_push_descriptor on newer ANV 2023-05-22 19:53:20 -04:00
Liam 8758932031 renderer_vulkan: barrier attachment feedback loops 2023-05-22 18:10:16 -04:00
Narr the Reg e8a025b4f8 Merge pull request #10386 from german77/high
input_common: Map motion with relative values not absolute ones
2023-05-22 13:13:50 -06:00
scorpion81 f8e7b44d28 Limit the device access memory to 4 GB
Hardly limiting the device access memory to 4 GB for integrated vulkan devices here. This works for the Steam Deck in order not to go above 4 GB VRAM usage any more (above this value the likelihood to crash when the RAM exceeds 12 GB as well raises).

But there will be perhaps a detection mechanism necessary for detecting the real memory limit for integrated vulkan devices. Those likely might have small limits anyway, but what about integrated GPUs on machines with > 16 GB RAM, aka larger amounts ?
2023-05-22 16:48:55 +02:00
Narr the Reg fdb2002f77 input_common: Implement amiibo writting 2023-05-21 21:09:20 -06:00
Danila Malyutin 7701a00a02 Add support for deinterlaced videos playback
This is a follow up to #10254 to improve the playback of cut scenes in Layton's Mystery Journey.
It uses ffmpeg's yadif filter for deinterlacing.
2023-05-22 01:43:44 +04:00
liamwhite f82efe9f65 Merge pull request #10392 from danilaml/update-cubeb-again
externals: update cubeb (again)
2023-05-21 00:24:12 -04:00
Danila Malyutin e5c2ec223a externals: update cubeb 2023-05-21 03:02:26 +04:00
GPUCode 4e491ab59b vk_master_semaphore: Move fence wait on separate thread 2023-05-20 19:23:53 +03:00
german77 dbcdb3523b input_common: Map motion with relative values not absolute ones 2023-05-19 21:19:29 -06:00
bunnei 1e398e6c36 Merge pull request #10344 from german77/pro-amiibo
input_common: Fix pro controller amiibo support
2023-05-18 18:10:38 -07:00
Danila Malyutin 55d740fffa externals: update cubeb (#10362) 2023-05-19 00:24:45 +02:00
Mai f6c5507873 Merge pull request #10377 from liamwhite/constexpr
renderer_vulkan: remove wrong constexpr
2023-05-18 18:04:07 -04:00
Liam de7c92d7c4 renderer_vulkan: remove wrong constexpr 2023-05-18 18:01:01 -04:00
Kelebek1 d75bcdd077 Smooth out the DSP callback by adding a 5ms wait time limit 2023-05-18 13:03:32 +01:00
Matías Locatti f35c14fb73 Merge pull request #10365 from lat9nq/anv_push_descriptor
vulkan_device: Disable VK_KHR_push_descriptor on ANV
2023-05-18 01:38:24 -03:00
lat9nq c1a8a508bc vulkan_device: Disable VK_KHR_push_descriptor on ANV
Mesa commit ff91c5ca42bc80aa411cb3fd8f550aa6fdd16bdc breaks
VK_KHR_push_descriptor usage on ANV drivers 22.3.0, so disable it
and allow games to boot.
2023-05-17 22:19:57 -04:00
bunnei 9d4a2de72b Merge pull request #10262 from liamwhite/depth-clamp
vulkan_common: disable depth clamp dynamic state for older radv
2023-05-17 12:19:03 -07:00
Narr the Reg 5693434b8a input_common: Fix pro controller amiibo support 2023-05-16 18:37:22 -06:00
liamwhite 47c5c37bed Merge pull request #10217 from Kelebek1/clear_value
Use the rendertarget format of the correct RT rather than the first valid
2023-05-16 10:06:30 -04:00
liamwhite a540d248f3 Merge pull request #10107 from grimkor/allow-fully-customised-hotkeys
Allow fully customised controller hotkeys
2023-05-16 10:06:15 -04:00
liamwhite bbb6b58aa4 Merge pull request #10181 from lat9nq/intel-compute-toggle
configure_graphics: Add option to enable compute pipelines for Intel proprietary
2023-05-15 12:05:24 -04:00
liamwhite 736a31e4ff Merge pull request #10234 from Kelebek1/clouds_depth
Fix Tears of the Kingdom flickering clouds and depths geometry.
2023-05-15 12:03:31 -04:00
liamwhite 6060685609 Merge pull request #10249 from FernandoS27/sorry-i-am-late
Buffer Cache: Clear sync code.
2023-05-15 12:03:25 -04:00
liamwhite 5b85925469 Merge pull request #10254 from danilaml/fix-h264-decode
Fix missing pic_order_present_flag in h264 header
2023-05-15 12:03:14 -04:00
liamwhite 033aa264cf Merge pull request #10265 from german77/amiibo-lag
input_common: Make amiibo scanning less demanding
2023-05-15 12:03:08 -04:00
liamwhite 9087fe10e9 Merge pull request #10294 from german77/vibration_span
service: hid: Use span instead of vector reference
2023-05-15 12:03:00 -04:00
german77 6e54615b16 service: hid: Use span instead of vector reference 2023-05-14 18:07:04 -06:00
Fernando Sahmkow c4bfbc6d25 Buffer Cache: Clear sync code. 2023-05-15 01:50:21 +02:00
liamwhite e9069dfe76 Merge pull request #10288 from liamwhite/vram-limits
vulkan_device: reserve extra memory to prevent swaps
2023-05-14 17:02:15 -04:00
Liam 2be751100b vulkan_device: reserve extra memory to prevent swaps 2023-05-14 16:49:59 -04:00
german77 ac531aa15f input_common: Make amiibo scanning less demanding 2023-05-14 09:04:37 -06:00
bunnei 29c7176f55 Merge pull request #10286 from liamwhite/compatible-bits
vulkan_common: fix incompatible property flags
2023-05-14 01:23:29 -07:00
Liam 122435e080 vulkan_common: fix incompatible property flags 2023-05-14 01:13:11 -04:00
Danila Malyutin 7325fb054d Fixup upload.ps1 for GHA
No extra folders are created with ninja generator after previous CMake fixes.
2023-05-14 01:23:07 +03:00
Danila Malyutin c9c5d140b8 Use TARGET_FILE_DIR generator expression
Use $<TARGET_FILE_DIR:...> where appropriate instead of trying to guess where the binary will end up.
2023-05-13 23:58:17 +04:00
Liam 856838f7ce vulkan_common: disable depth clamp dynamic state for older radv 2023-05-13 00:37:17 -04:00
Fernando S 9c739f1506 Merge pull request #10244 from liamwhite/lower-upper
time: implement ContinuousAdjustmentTimePoint
2023-05-13 03:51:05 +02:00
Fernando S 075d73f076 Merge pull request #10243 from Kelebek1/red_dot
Correctly track render target index in the framebuffer for image aspects
2023-05-13 03:50:31 +02:00
bunnei 021e503cc8 Merge pull request #10237 from liamwhite/cache-storage
fs: stub cache storage
2023-05-12 16:42:17 -07:00
bunnei 1805de0301 Merge pull request #10236 from liamwhite/thats-not-an-ibinder
nvnflinger: fix Parcel serialization
2023-05-12 16:07:35 -07:00
Danila Malyutin 9367769fe7 Fix missing pic_order_present_flag in h264 header
Fixes #9635
2023-05-12 22:30:59 +04:00
Liam a22c5a3880 time: implement ContinuousAdjustmentTimePoint 2023-05-11 21:05:27 -04:00
Kelebek1 cd0ded7771 Correctly track RT indexes for image aspect lookup during clears 2023-05-12 01:40:21 +01:00
Liam 351079a4ba fs: adjust future save path 2023-05-11 17:30:30 -04:00
Liam 62bcb99ba8 am: stub CreateCacheStorage 2023-05-11 17:26:02 -04:00
Liam 13e4ceb990 fs: stub cache storage and fix params alignment 2023-05-11 17:23:28 -04:00
Liam bb94beed15 nvnflinger: fix Parcel serialization 2023-05-11 17:09:19 -04:00
Liam 6e10a0c130 nvnflinger: fix producer slot fence init 2023-05-11 17:08:14 -04:00
Kelebek1 e42b4a16b6 Fix Tears of the Kingdom flickering clouds and depths. 2023-05-11 19:25:24 +01:00
liamwhite 182221b9ff Merge pull request #10132 from Kelebek1/fermi_blit2
Allow Fermi blit accelerate to work without images in cache
2023-05-11 10:45:59 -04:00
liamwhite 2643ea80df Merge pull request #10216 from Kelebek1/buffer_cache_region_checks
Swap order of checking/setting region modifications in the buffer_cache
2023-05-11 10:45:47 -04:00
liamwhite f94186d3c3 Merge pull request #10222 from liamwhite/q
renderer_vulkan: separate guest and host compute descriptor queues
2023-05-11 10:45:36 -04:00
Kelebek1 bf08bc3c0f Allow Fermi blit accelerate to add src/dst to the cache if they don't exist already. Use ScratchBuffers in the software blit path. 2023-05-11 06:42:38 +01:00
Fernando S 871e7cacf6 Merge pull request #10224 from yuzu-emu/readme-update
Update README.md to remove Skyline license exception.
2023-05-11 05:51:32 +02:00
bunnei 2fe922aae5 Update README.md to remove Skyline license exception. 2023-05-10 15:52:30 -07:00
grimkor ceb65c259a Allow fully customisable controller hotkeys 2023-05-10 19:28:11 +01:00
Liam 67fd1df762 renderer_vulkan: separate guest and host compute descriptor queues 2023-05-10 13:46:48 -04:00
liamwhite b7f60e9123 Merge pull request #10207 from german77/amiibo_cheater
service: nfp: Allow to load with a different amiibo id
2023-05-10 10:25:48 -04:00
Narr the Reg 3ec027400e Merge pull request #10119 from marius851000/improved_non_hd_feeback
Attempt at improving HD Rumble emulation
2023-05-09 22:59:32 -06:00
Narr the Reg 42e1db4b0e service: nfc: Seed all random values 2023-05-09 17:54:07 -06:00
german77 1968cc7b10 service: nfp: Allow to load with a different amiibo id 2023-05-09 17:51:59 -06:00
Kelebek1 a386003b64 Use the rendertarget format of the correct RT rather than the first valid 2023-05-09 22:13:15 +01:00
Kelebek1 b3691fc33c Swap order of checking/setting region modifications in the buffer_cache 2023-05-09 20:21:08 +01:00
liamwhite 7944f271dc Merge pull request #10183 from liamwhite/mods
vfs_vector: avoid n^2 lookup in layeredfs building
2023-05-09 09:47:36 -04:00
liamwhite 5890b96ce5 Merge pull request #10203 from german77/calibration
core: hid: Allow to calibrate gyro sensor
2023-05-09 09:47:29 -04:00
liamwhite 1f14b58315 Merge pull request #10206 from FernandoS27/astc-3d
Texture Cache: Fix 3D ASTC textures
2023-05-09 09:47:22 -04:00
liamwhite 3f048770d3 Merge pull request #10208 from german77/amiibo_joycon
input_common: Fix nfc detection for joycons
2023-05-09 09:47:14 -04:00
german77 cb1487d774 input_common: Fix nfc detection for joycons 2023-05-09 00:32:53 -06:00
Fernando Sahmkow 8a214e5530 Texture Cache: Fix ASTC textures 2023-05-09 02:42:10 +02:00
liamwhite 15ec8d3e44 Merge pull request #10205 from jbeich/freebsd
qt_common: unbreak build on BSDs
2023-05-08 17:29:33 -04:00
Jan Beich a4362765a6 qt_common: consistently ifdef QPlatform after cbd79df233
src/yuzu/qt_common.cpp:45:33: error: member access into incomplete type 'QPlatformNativeInterface'
    wsi.display_connection = pni->nativeResourceForWindow("display", window);
                                ^
/usr/include/qt6/QtGui/qguiapplication.h:20:7: note: forward declaration of 'QPlatformNativeInterface'
class QPlatformNativeInterface;
      ^
src/yuzu/qt_common.cpp:47:42: error: member access into incomplete type 'QPlatformNativeInterface'
        wsi.render_surface = window ? pni->nativeResourceForWindow("surface", window) : nullptr;
                                         ^
/usr/include/qt6/QtGui/qguiapplication.h:20:7: note: forward declaration of 'QPlatformNativeInterface'
class QPlatformNativeInterface;
      ^
2023-05-08 20:47:16 +00:00
Narr the Reg e1838f51a3 yuzu: Make 3d cube with joycon shape 2023-05-08 12:06:39 -06:00
Narr the Reg 97bd6d6418 core: hid: Allow to calibrate gyro sensor 2023-05-08 12:06:38 -06:00
lat9nq 40f3e2fbf1 configure_graphics_advanced: Hide input compute toggle a little later
SetColoredTristate causes the setting to become visible as it calls
`show()` on it.
2023-05-08 12:32:56 -04:00
bunnei b70a205a96 Merge pull request #10075 from Kelebek1/silence_nifm_spam
Silence network spam
2023-05-07 17:45:32 -07:00
bunnei 3d8eca92f9 Merge pull request #10197 from liamwhite/resume-token
bootmanager: remove stop_token header
2023-05-07 17:43:15 -07:00
bunnei d6d60f7104 Merge pull request #10194 from bunnei/update-dynarmic-3
externals: Update dynarmic to include latest patch.
2023-05-07 17:13:32 -07:00
Liam bdb7c11d8e bootmanager: remove stop_token header 2023-05-07 19:20:09 -04:00
liamwhite 8f605b542c Merge pull request #10195 from german77/mutex
core: hid: Update motion on a better place
2023-05-07 19:06:10 -04:00
liamwhite 2688fb1aa2 Merge pull request #10155 from FernandoS27/reactive-flushing-new
Y.F.C. bring back Reactive Flushing
2023-05-07 19:05:56 -04:00
Liam d100de27ee vfs_layered: avoid n^2 lookup in layeredfs building 2023-05-07 19:03:41 -04:00
german77 cf023aa8ec core: hid: Update motion on a better place 2023-05-07 17:01:57 -06:00
bunnei 7eb17f3aa5 externals: Update dynarmic to include latest patch. 2023-05-07 15:06:58 -07:00
Fernando Sahmkow 8014dd8259 Texture cache: Only force flush the dma downloads 2023-05-07 23:46:12 +02:00
Fernando Sahmkow 2df19ef0fd Buffer Cache: disable reactive flushing in it. 2023-05-07 23:46:12 +02:00
Fernando Sahmkow 016c6feb49 Texture cache: reverse inmediate flush changes 2023-05-07 23:46:12 +02:00
Fernando Sahmkow 36c302fa32 Buffer cache: always use async buffer downloads and fix regression. 2023-05-07 23:46:12 +02:00
Fernando Sahmkow 6f90dff293 Address feedback, add CR notice, etc 2023-05-07 23:46:12 +02:00
Fernando Sahmkow ab0c0a469c Query cache: stop updating pages as it's not affected by cpu writes 2023-05-07 23:46:12 +02:00
Fernando Sahmkow 92da86290c Settings: add option to enable / disable reactive flushing 2023-05-07 23:46:12 +02:00
Fernando Sahmkow 0f4f18265f Texture cache: sync the first flush. 2023-05-07 23:46:12 +02:00
Fernando Sahmkow c6cac2ffaa GPU: Add Reactive flushing 2023-05-07 23:46:12 +02:00
Liam 5792a72c29 vfs_vector: avoid n^2 lookup in layeredfs building 2023-05-07 16:50:35 -04:00
bunnei e58090c9c7 Merge pull request #10097 from german77/nfp_full
service: nfc: Merge device interfaces and create the device manager
2023-05-07 12:18:09 -07:00
bunnei ed98afdebc Merge pull request #10192 from bunnei/update-dynarmic-2
externals: Update dynarmic to include latest patch.
2023-05-07 12:17:40 -07:00
bunnei e46074a2e3 externals: Update dynarmic to include latest patch. 2023-05-07 12:04:42 -07:00
liamwhite 0c5bdc7241 Merge pull request #10081 from Kelebek1/copy_overlap_tick
Sort overlap_ids by modification tick before copy
2023-05-07 14:09:10 -04:00
liamwhite cb49c3b64c Merge pull request #10172 from Kelebek1/debug_validation_names
Log object names with debug renderer, add a GPU address to ImageViews
2023-05-07 14:09:03 -04:00
liamwhite 35862b21d6 Merge pull request #10189 from Qigo42/master
yuzu/applets/qt_profile_select: connect double-click to accept()
2023-05-07 14:08:57 -04:00
QGJ 4366a21eae yuzu/applets/qt_profile_select: connect double-click to accept()
In the profile selection window:

Allow the user to start the game by double-clicking a profile to avoid having to additionally click the OK button. This avoids an unnecessary "step" to the start of the game...
2023-05-07 18:05:42 +02:00
Fernando S 1166ca3a57 Merge pull request #10187 from Kelebek1/address_space
Fix address space allocator slow path to avoid OOB
2023-05-07 17:06:36 +02:00
Kelebek1 6fed48b3a4 Fix address space allocator slow path to avoid OOB 2023-05-07 15:11:16 +01:00
bunnei f36b5dfd18 Merge pull request #10180 from german77/debug
input_common: Revert debugging changes
2023-05-06 22:26:53 -07:00
german77 91695a453b input_common: Revert debugging changes 2023-05-06 23:06:44 -06:00
lat9nq 55c77dd25b yuzu-qt/config: Add option to disable compute on Intel
This option is only visible if an Intel GPU using the proprietary
driver is found during Vulkan device enumeration.

configure_graphics: More directly get driver id

Vulkan::Device does quite a bit more than we need just to see the
driver ID here.
2023-05-07 01:06:22 -04:00
lat9nq 6ed6e6e18e vk_pipeline_cache: Use setting to disable intel compute 2023-05-07 01:06:22 -04:00
lat9nq 725aacb4bc settings: Add enable compute pipelines
For the Intel proprietary driver's deficiencies.

settings: Restore compute option global state
2023-05-07 01:06:20 -04:00
bunnei 3547248ec2 Merge pull request #10125 from lat9nq/vsync-select
configuration: Expose separate swap present modes
2023-05-06 21:55:39 -07:00
bunnei 82188b2f79 Merge pull request #10174 from german77/motriod
input_common: Add experimental motion to button
2023-05-06 21:40:20 -07:00
bunnei 5467d39bc3 Merge pull request #10171 from german77/powera
input_common: Add property to invert an axis button
2023-05-06 21:39:44 -07:00
liamwhite 27ff00acb1 Merge pull request #10162 from lat9nq/sdl-remove-old
yuzu-sdl,audio_core: Remove antiquated warning ignore
2023-05-06 23:10:09 -04:00
liamwhite f1b770a8f0 Merge pull request #10165 from merryhime/update-dynarmic
externals: Update dynarmic to 6.4.7
2023-05-06 23:10:02 -04:00
liamwhite 9c9b4616c3 Merge pull request #10167 from german77/motion_preview
yuzu: Add motion preview to controller input
2023-05-06 23:09:55 -04:00
liamwhite e7b6389e44 Merge pull request #10178 from ronikirla/2-hour-crash
Fix read access violation after 2 hours of gameplay in PMD: RTDX
2023-05-06 23:09:36 -04:00
Roni Kirla a13fd5f7cc Fix read access violation 2023-05-07 00:13:33 +03:00
german77 9b771bcb8f input_common: Add experimental motion to button 2023-05-05 22:49:56 -06:00
Narr the Reg 94151097b9 service: nfc: Merge device interfaces and create the device manager 2023-05-05 22:02:59 -06:00
Kelebek1 ca6bf06ef7 Log object names with debug renderer, add a GPU address to ImageViews 2023-05-06 04:48:32 +01:00
Narr the Reg f017335fef input_common: Add property to invert an axis button 2023-05-05 17:18:35 -06:00
Narr the Reg f764223f93 input_common: Add property to invert an axis button 2023-05-05 17:14:20 -06:00
Narr the Reg 46e835f2d6 yuzu: Add motion preview to controller input 2023-05-05 13:53:38 -06:00
bunnei bb2e407772 Merge pull request #10159 from german77/home_screenshot
core: hid: Fix state of capture and home buttons
2023-05-05 12:02:15 -07:00
marius david 0a6bd8b236 Improve emulation of HD Rumble 2023-05-05 19:30:40 +02:00
Merry 432d754d7d externals: Update dynarmic to 6.4.7 2023-05-05 16:30:18 +01:00
lat9nq 740c349820 yuzu-sdl,audio_core: Remove antiquated warning ignore
Issue was fixed a long time ago, both by SDL2 and in yuzu by including
SDL2 as a system library.
2023-05-05 03:48:28 -04:00
german77 8df3aed2f1 core: hid: Fix state of capture and home buttons 2023-05-04 22:36:59 -06:00
liamwhite 16939b1a6e Merge pull request #10128 from Kelebek1/audren_terminate
Wait for the terminate event before destroying a system instance
2023-05-04 14:44:09 -04:00
liamwhite 60d54d911e Merge pull request #10145 from Kelebek1/code_size
Fix shader code resize to use word size rather than byte size
2023-05-04 14:44:02 -04:00
liamwhite e2b81ae5fe Merge pull request #10156 from v1993/looks-decent-to-me
Remove LGTM config
2023-05-04 14:43:55 -04:00
Valeri b095a0242d Remove LGTM config
LGTM.com is no longer available since it was superseded by CodeQL.
2023-05-04 15:36:47 +03:00
Fernando S 2506594c50 Merge pull request #10153 from FernandoS27/a-quickie-fixie
Memory manager: Fix possible softlock
2023-05-04 03:56:53 +02:00
Fernando S 7d5683c63c Merge pull request #10154 from liamwhite/optimistic
settings: remove pessimistic flushing
2023-05-04 01:55:51 +02:00
bunnei 055ee84024 Merge pull request #10142 from FernandoS27/missing-astc
GPU: implement missing ASTC
2023-05-03 16:49:27 -07:00
Liam ae59ffc56c settings: remove pessimistic flushing 2023-05-03 18:52:42 -04:00
lat9nq a71498d163 qt_common: Remove yuzu prefix 2023-05-03 18:11:53 -04:00
Fernando Sahmkow 87a9be8dec GPU: implement missing ASTC 2023-05-03 11:33:28 -04:00
lat9nq 35e7f36a39 configure_graphics: No there isn't a hyphen in VSync
Most sources seem to suggest VSync and not V-Sync
2023-05-02 21:52:43 -04:00
lat9nq d82cad3fb3 configure_input_player: Add missing include
Cleaning up includes in bootmanager and configure_graphics has exposed a
missing include here.
2023-05-02 21:52:43 -04:00
lat9nq cd2981ee12 configure_graphics: Clean up includes [IWYU] 2023-05-02 21:52:43 -04:00
lat9nq 0c0f5b7ccc bootmanager: Clean up includes [IWYU]
bootmanager: Remove system-specific headers

IWYU can be too complete I suppose.
2023-05-02 21:52:43 -04:00
lat9nq a546ecbb12 configure_graphics: Actively find present modes
When Vulkan devices are enumerated, this also determines the available
present modes for each device, maps them to a vector, and gives
those options to the user.
OpenGL options are limited to On/Off.

Required creating a VkSurfaceKHR during device enumeration, which
may or may not be desireable. For the sake of a less confusing UI.

Also fixes a bug where if a graphics device disappears on the host, we
don't try and select the non-existant devices.

configure_graphics: Remove vsync runtime lock for Vulkan

configure_graphics: Recommend Mailbox present mode

configure_graphics: Fix type-limits warning

configure_graphics: Clean up includes

configure_graphics: Add tooltip
2023-05-02 21:52:43 -04:00
lat9nq 952b271092 vk_swapchain: Use certain modes for unlocked
Uses mailbox, then immediate for unlocked framerate depending on
support for either. Also adds support for FIFO_RELAXED.

This function now assumes vsync_mode was originially configured to a value
that the driver supports.

vk_swapchain: ChooseSwapPresentMode determines updates

Simplifies swapchain a bit and allows us to change the present mode
during guest runtime.

vk_swapchain: Fix MSVC error

vk_swapchain: Enforce available present modes

Some frontends don't check the value of vsync_mode before comitting it.
Just as well, since a driver update or misconfiguration could problems
in the swap chain.

vk_swapchain: Silence warnings

Silences GCC warnings implicit-fallthrough and shadow, which apparently
are not enabled on clang.
2023-05-02 21:52:43 -04:00
lat9nq a090a380be bootmanager: Remove inaccurate switch
Those vulkan settings do not correspond 1:1 to the swap intervals that
they set for OpenGL, so remove it.

bootmanager: Add missing include

I didn't add this log why did it break
2023-05-02 21:51:30 -04:00
lat9nq cbd79df233 qt_common: Move window info function out of bootmanager
Function is useful outside of bootmanager, so put it in a common place.

qt_common: Add missing include

qt_common: Add some newlines

qt_common: Add trailing newline

qt_common: Add trainline newline
2023-05-02 21:51:30 -04:00
lat9nq c6c11c1553 vulkan_surface: Pass only window info for surface creation
We don't need the whole EmuWindow when creating a surface,
and it creates onerous requirements outside of typical usage for
creating a surface elsewhere.
2023-05-02 21:51:30 -04:00
lat9nq 2528cf7c54 settings: Enable FIFO relaxed
Not entirely sure if we need this, but there's also no reason not to
support it.

settings: Give VSyncMode values
2023-05-02 21:51:30 -04:00
lat9nq 6b973c5986 configure_graphics: Fix another typo 2023-05-02 21:51:29 -04:00
lat9nq 40f062f749 telemetry_session: Make translate function static
Addresses review feedback

Co-authored-by: Lioncash <mathew1800@gmail.com>
2023-05-02 21:51:29 -04:00
lat9nq 29a56496bf bootmanager: Return value in impossible case
The setting is ranged, so this return statement is unreachable.
But GCC can't tell I guess.
2023-05-02 21:51:29 -04:00
lat9nq 41a103c0fc configure_graphics: Fix typo 2023-05-02 21:51:29 -04:00
lat9nq 66ed1c1872 default_ini: Update V-Sync description
default_ini: Update vsync text

default_ini: Add tooltip from configure_graphics
2023-05-02 21:51:29 -04:00
lat9nq 6f0929df82 configuration: Expose separate swap present modes
Previously, yuzu would try and guess which vsync mode to use given
different scenarios, but apparently we didn't always get it right. This
exposes the separate modes in a drop-down the user can select.

If a mode isn't available in Vulkan, it defaults to FIFO.
2023-05-02 21:51:29 -04:00
Kelebek1 f902cc2a2b Fix code resize to use word size rather than byte size 2023-05-02 23:52:21 +01:00
Kelebek1 2feb40f14d Wait for the terminate event before destroying a system instance 2023-05-01 00:27:12 +01:00
german77 5e16fe4579 core: service: Add FunctionInfoTyped to allow expanding existing interfaces 2023-04-25 23:36:24 -06:00
Narr the Reg 00d76fc5f5 service: nfc: Create mifare interface 2023-04-23 23:28:09 -06:00
Narr the Reg a3fa64fcc4 service: nfc: Create interface 2023-04-23 23:28:09 -06:00
Kelebek1 4da4ecb1ff Silence nifm spam 2023-04-22 14:29:58 +01:00
Kelebek1 3346de365a Sort overlap_ids by modification tick before copy 2023-04-22 14:02:10 +01:00
576 changed files with 47378 additions and 20083 deletions
+15
View File
@@ -0,0 +1,15 @@
#!/bin/bash -ex
# SPDX-FileCopyrightText: 2023 yuzu Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later
export NDK_CCACHE="$(which ccache)"
ccache -s
BUILD_FLAVOR=mainline
cd src/android
chmod +x ./gradlew
./gradlew "assemble${BUILD_FLAVOR}Release" "bundle${BUILD_FLAVOR}Release"
ccache -s
+27
View File
@@ -0,0 +1,27 @@
#!/bin/bash -ex
# SPDX-FileCopyrightText: 2023 yuzu Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later
. ./.ci/scripts/common/pre-upload.sh
REV_NAME="yuzu-${GITDATE}-${GITREV}"
BUILD_FLAVOR=mainline
cp src/android/app/build/outputs/apk/"${BUILD_FLAVOR}/release/app-${BUILD_FLAVOR}-release.apk" \
"artifacts/${REV_NAME}.apk"
cp src/android/app/build/outputs/bundle/"${BUILD_FLAVOR}Release"/"app-${BUILD_FLAVOR}-release.aab" \
"artifacts/${REV_NAME}.aab"
if [ -n "${ANDROID_KEYSTORE_B64}" ]
then
echo "Signing apk..."
base64 --decode <<< "${ANDROID_KEYSTORE_B64}" > ks.jks
apksigner sign --ks ks.jks \
--ks-key-alias "${ANDROID_KEY_ALIAS}" \
--ks-pass env:ANDROID_KEYSTORE_PASS "artifacts/${REV_NAME}.apk"
else
echo "No keystore specified, not signing the APK files."
fi
+5 -1
View File
@@ -26,7 +26,11 @@ $env:BUILD_ZIP = $MSVC_BUILD_ZIP
$env:BUILD_SYMBOLS = $MSVC_BUILD_PDB
$env:BUILD_UPDATE = $MSVC_SEVENZIP
$BUILD_DIR = ".\build\bin\Release"
if (Test-Path -Path ".\build\bin\Release") {
$BUILD_DIR = ".\build\bin\Release"
} else {
$BUILD_DIR = ".\build\bin\"
}
# Cleanup unneeded data in submodules
git submodule foreach git clean -fxd
+1 -1
View File
@@ -43,7 +43,7 @@ body:
id: log
attributes:
label: Log File
description: A log file will help our developers to better diagnose and fix the issue.
description: A log file will help our developers to better diagnose and fix the issue. Instructions can be found [here](https://yuzu-emu.org/help/reference/log-files).
validations:
required: true
- type: textarea
+43
View File
@@ -122,3 +122,46 @@ jobs:
with:
name: ${{ env.INDIVIDUAL_EXE }}
path: ${{ env.INDIVIDUAL_EXE }}
android:
runs-on: ubuntu-latest
needs: format
steps:
- uses: actions/checkout@v3
with:
submodules: recursive
- name: set up JDK 17
uses: actions/setup-java@v3
with:
java-version: '17'
distribution: 'adopt'
- name: Set up cache
uses: actions/cache@v3
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
~/.ccache
key: ${{ runner.os }}-android-${{ github.sha }}
restore-keys: |
${{ runner.os }}-android-
- name: Query tag name
uses: olegtarasov/get-tag@v2.1.2
id: tagName
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install -y ccache apksigner glslang-dev glslang-tools
git -C ./externals/vcpkg/ fetch --all --unshallow
- name: Build
run: ./.ci/scripts/android/build.sh
- name: Copy and sign artifacts
env:
ANDROID_KEYSTORE_B64: ${{ secrets.ANDROID_KEYSTORE_B64 }}
ANDROID_KEY_ALIAS: ${{ secrets.ANDROID_KEY_ALIAS }}
ANDROID_KEYSTORE_PASS: ${{ secrets.ANDROID_KEYSTORE_PASS }}
run: ./.ci/scripts/android/upload.sh
- name: Upload
uses: actions/upload-artifact@v3
with:
name: android
path: artifacts/
+3
View File
@@ -49,3 +49,6 @@
[submodule "cpp-jwt"]
path = externals/cpp-jwt
url = https://github.com/arun11299/cpp-jwt.git
[submodule "libadrenotools"]
path = externals/libadrenotools
url = https://github.com/bylaws/libadrenotools
-13
View File
@@ -1,13 +0,0 @@
# SPDX-FileCopyrightText: 2020 yuzu Emulator Project
# SPDX-License-Identifier: GPL-2.0-or-later
path_classifiers:
library: "externals"
extraction:
cpp:
prepare:
packages:
- "libsdl2-dev"
- "qtmultimedia5-dev"
- "libtbb-dev"
- "libjack-jackd2-dev"
+12
View File
@@ -135,3 +135,15 @@ License: GPL-3.0-or-later
Files: .github/ISSUE_TEMPLATE/*
Copyright: 2022 yuzu Emulator Project
License: GPL-2.0-or-later
Files: src/android/app/src/ea/res/*
Copyright: 2023 yuzu Emulator Project
License: GPL-3.0-or-later
Files: src/android/app/src/main/res/*
Copyright: 2023 yuzu Emulator Project
License: GPL-3.0-or-later
Files: src/android/gradle/wrapper/*
Copyright: 2023 yuzu Emulator Project
License: GPL-3.0-or-later
+66 -4
View File
@@ -11,6 +11,7 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/externals/cmake-modul
include(DownloadExternals)
include(CMakeDependentOption)
include(CTest)
include(FetchContent)
# Set bundled sdl2/qt as dependent options.
# OFF by default, but if ENABLE_SDL2 and MSVC are true then ON
@@ -19,7 +20,7 @@ CMAKE_DEPENDENT_OPTION(YUZU_USE_BUNDLED_SDL2 "Download bundled SDL2 binaries" ON
# On Linux system SDL2 is likely to be lacking HIDAPI support which have drawbacks but is needed for SDL motion
CMAKE_DEPENDENT_OPTION(YUZU_USE_EXTERNAL_SDL2 "Compile external SDL2" ON "ENABLE_SDL2;NOT MSVC" OFF)
option(ENABLE_LIBUSB "Enable the use of LibUSB" ON)
cmake_dependent_option(ENABLE_LIBUSB "Enable the use of LibUSB" ON "NOT ANDROID" OFF)
option(ENABLE_OPENGL "Enable OpenGL" ON)
mark_as_advanced(FORCE ENABLE_OPENGL)
@@ -48,7 +49,7 @@ option(YUZU_TESTS "Compile tests" "${BUILD_TESTING}")
option(YUZU_USE_PRECOMPILED_HEADERS "Use precompiled headers" ON)
option(YUZU_ROOM "Compile LDN room server" ON)
cmake_dependent_option(YUZU_ROOM "Compile LDN room server" ON "NOT ANDROID" OFF)
CMAKE_DEPENDENT_OPTION(YUZU_CRASH_DUMPS "Compile Windows crash dump (Minidump) support" OFF "WIN32" OFF)
@@ -60,7 +61,67 @@ option(YUZU_ENABLE_LTO "Enable link-time optimization" OFF)
CMAKE_DEPENDENT_OPTION(YUZU_USE_FASTER_LD "Check if a faster linker is available" ON "NOT WIN32" OFF)
# On Android, fetch and compile libcxx before doing anything else
if (ANDROID)
set(CMAKE_SKIP_INSTALL_RULES ON)
set(LLVM_VERSION "15.0.6")
# Note: even though libcxx and libcxxabi have separate releases on the project page,
# the separated releases cannot be compiled. Only in-tree builds work. Therefore we
# must fetch the source release for the entire llvm tree.
FetchContent_Declare(llvm
URL "https://github.com/llvm/llvm-project/releases/download/llvmorg-${LLVM_VERSION}/llvm-project-${LLVM_VERSION}.src.tar.xz"
URL_HASH SHA256=9d53ad04dc60cb7b30e810faf64c5ab8157dadef46c8766f67f286238256ff92
TLS_VERIFY TRUE
)
FetchContent_MakeAvailable(llvm)
# libcxx has support for most of the range library, but it's gated behind a flag:
add_compile_definitions(_LIBCPP_ENABLE_EXPERIMENTAL)
# Disable standard header inclusion
set(ANDROID_STL "none")
# libcxxabi
set(LIBCXXABI_INCLUDE_TESTS OFF)
set(LIBCXXABI_ENABLE_SHARED FALSE)
set(LIBCXXABI_ENABLE_STATIC TRUE)
set(LIBCXXABI_LIBCXX_INCLUDES "${LIBCXX_TARGET_INCLUDE_DIRECTORY}" CACHE STRING "" FORCE)
add_subdirectory("${llvm_SOURCE_DIR}/libcxxabi" "${llvm_BINARY_DIR}/libcxxabi")
link_libraries(cxxabi_static)
# libcxx
set(LIBCXX_ABI_NAMESPACE "__ndk1" CACHE STRING "" FORCE)
set(LIBCXX_CXX_ABI "libcxxabi")
set(LIBCXX_INCLUDE_TESTS OFF)
set(LIBCXX_INCLUDE_BENCHMARKS OFF)
set(LIBCXX_INCLUDE_DOCS OFF)
set(LIBCXX_ENABLE_SHARED FALSE)
set(LIBCXX_ENABLE_STATIC TRUE)
set(LIBCXX_ENABLE_ASSERTIONS FALSE)
add_subdirectory("${llvm_SOURCE_DIR}/libcxx" "${llvm_BINARY_DIR}/libcxx")
set_target_properties(cxx-headers PROPERTIES INTERFACE_COMPILE_OPTIONS "-isystem${CMAKE_BINARY_DIR}/${LIBCXX_INSTALL_INCLUDE_DIR}")
link_libraries(cxx_static cxx-headers)
endif()
if (YUZU_USE_BUNDLED_VCPKG)
if (ANDROID)
set(ENV{ANDROID_NDK_HOME} "${ANDROID_NDK}")
list(APPEND VCPKG_MANIFEST_FEATURES "android")
if (CMAKE_ANDROID_ARCH_ABI STREQUAL "arm64-v8a")
set(VCPKG_TARGET_TRIPLET "arm64-android")
# this is to avoid CMake using the host pkg-config to find the host
# libraries when building for Android targets
set(PKG_CONFIG_EXECUTABLE "aarch64-none-linux-android-pkg-config" CACHE FILEPATH "" FORCE)
elseif (CMAKE_ANDROID_ARCH_ABI STREQUAL "x86_64")
set(VCPKG_TARGET_TRIPLET "x64-android")
set(PKG_CONFIG_EXECUTABLE "x86_64-none-linux-android-pkg-config" CACHE FILEPATH "" FORCE)
else()
message(FATAL_ERROR "Unsupported Android architecture ${CMAKE_ANDROID_ARCH_ABI}")
endif()
endif()
if (YUZU_TESTS)
list(APPEND VCPKG_MANIFEST_FEATURES "yuzu-tests")
endif()
@@ -194,7 +255,7 @@ endif()
# boost asio's concept usage doesn't play nicely with some compilers yet.
add_definitions(-DBOOST_ASIO_DISABLE_CONCEPTS)
if (MSVC)
add_compile_options($<$<COMPILE_LANGUAGE:CXX>:/std:c++latest>)
add_compile_options($<$<COMPILE_LANGUAGE:CXX>:/std:c++20>)
# boost still makes use of deprecated result_of.
add_definitions(-D_HAS_DEPRECATED_RESULT_OF)
@@ -453,10 +514,11 @@ endif()
# List of all FFmpeg components required
set(FFmpeg_COMPONENTS
avcodec
avfilter
avutil
swscale)
if (UNIX AND NOT APPLE)
if (UNIX AND NOT APPLE AND NOT ANDROID)
find_package(PkgConfig REQUIRED)
pkg_check_modules(LIBVA libva)
endif()
+2 -2
View File
@@ -3,8 +3,8 @@
function(copy_yuzu_FFmpeg_deps target_dir)
include(WindowsCopyFiles)
set(DLL_DEST "${CMAKE_BINARY_DIR}/bin/$<CONFIG>/")
set(DLL_DEST "$<TARGET_FILE_DIR:${target_dir}>/")
file(READ "${FFmpeg_PATH}/requirements.txt" FFmpeg_REQUIRED_DLLS)
string(STRIP "${FFmpeg_REQUIRED_DLLS}" FFmpeg_REQUIRED_DLLS)
windows_copy_files(${target_dir} ${FFmpeg_DLL_DIR} ${DLL_DEST} ${FFmpeg_REQUIRED_DLLS})
windows_copy_files(${target_dir} ${FFmpeg_LIBRARY_DIR} ${DLL_DEST} ${FFmpeg_REQUIRED_DLLS})
endfunction(copy_yuzu_FFmpeg_deps)
+1 -1
View File
@@ -4,7 +4,7 @@
function(copy_yuzu_Qt5_deps target_dir)
include(WindowsCopyFiles)
if (MSVC)
set(DLL_DEST "${CMAKE_BINARY_DIR}/bin/$<CONFIG>/")
set(DLL_DEST "$<TARGET_FILE_DIR:${target_dir}>/")
set(Qt5_DLL_DIR "${Qt5_DIR}/../../../bin")
else()
set(DLL_DEST "${CMAKE_BINARY_DIR}/bin/")
+1 -1
View File
@@ -3,6 +3,6 @@
function(copy_yuzu_SDL_deps target_dir)
include(WindowsCopyFiles)
set(DLL_DEST "${CMAKE_BINARY_DIR}/bin/$<CONFIG>/")
set(DLL_DEST "$<TARGET_FILE_DIR:${target_dir}>/")
windows_copy_files(${target_dir} ${SDL2_DLL_DIR} ${DLL_DEST} SDL2.dll)
endfunction(copy_yuzu_SDL_deps)
+5 -1
View File
@@ -7,6 +7,7 @@
# prefix_var: name of a variable which will be set with the path to the extracted contents
function(download_bundled_external remote_path lib_name prefix_var)
set(package_base_url "https://github.com/yuzu-emu/")
set(package_repo "no_platform")
set(package_extension "no_platform")
if (WIN32)
@@ -15,10 +16,13 @@ if (WIN32)
elseif (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
set(package_repo "ext-linux-bin/raw/main/")
set(package_extension ".tar.xz")
elseif (ANDROID)
set(package_repo "ext-android-bin/raw/main/")
set(package_extension ".tar.xz")
else()
message(FATAL_ERROR "No package available for this platform")
endif()
set(package_url "https://github.com/yuzu-emu/${package_repo}")
set(package_url "${package_base_url}${package_repo}")
set(prefix "${CMAKE_BINARY_DIR}/externals/${lib_name}")
if (NOT EXISTS "${prefix}")
+373
View File
@@ -0,0 +1,373 @@
Mozilla Public License Version 2.0
==================================
1. Definitions
--------------
1.1. "Contributor"
means each individual or legal entity that creates, contributes to
the creation of, or owns Covered Software.
1.2. "Contributor Version"
means the combination of the Contributions of others (if any) used
by a Contributor and that particular Contributor's Contribution.
1.3. "Contribution"
means Covered Software of a particular Contributor.
1.4. "Covered Software"
means Source Code Form to which the initial Contributor has attached
the notice in Exhibit A, the Executable Form of such Source Code
Form, and Modifications of such Source Code Form, in each case
including portions thereof.
1.5. "Incompatible With Secondary Licenses"
means
(a) that the initial Contributor has attached the notice described
in Exhibit B to the Covered Software; or
(b) that the Covered Software was made available under the terms of
version 1.1 or earlier of the License, but not also under the
terms of a Secondary License.
1.6. "Executable Form"
means any form of the work other than Source Code Form.
1.7. "Larger Work"
means a work that combines Covered Software with other material, in
a separate file or files, that is not Covered Software.
1.8. "License"
means this document.
1.9. "Licensable"
means having the right to grant, to the maximum extent possible,
whether at the time of the initial grant or subsequently, any and
all of the rights conveyed by this License.
1.10. "Modifications"
means any of the following:
(a) any file in Source Code Form that results from an addition to,
deletion from, or modification of the contents of Covered
Software; or
(b) any new file in Source Code Form that contains any Covered
Software.
1.11. "Patent Claims" of a Contributor
means any patent claim(s), including without limitation, method,
process, and apparatus claims, in any patent Licensable by such
Contributor that would be infringed, but for the grant of the
License, by the making, using, selling, offering for sale, having
made, import, or transfer of either its Contributions or its
Contributor Version.
1.12. "Secondary License"
means either the GNU General Public License, Version 2.0, the GNU
Lesser General Public License, Version 2.1, the GNU Affero General
Public License, Version 3.0, or any later versions of those
licenses.
1.13. "Source Code Form"
means the form of the work preferred for making modifications.
1.14. "You" (or "Your")
means an individual or a legal entity exercising rights under this
License. For legal entities, "You" includes any entity that
controls, is controlled by, or is under common control with You. For
purposes of this definition, "control" means (a) the power, direct
or indirect, to cause the direction or management of such entity,
whether by contract or otherwise, or (b) ownership of more than
fifty percent (50%) of the outstanding shares or beneficial
ownership of such entity.
2. License Grants and Conditions
--------------------------------
2.1. Grants
Each Contributor hereby grants You a world-wide, royalty-free,
non-exclusive license:
(a) under intellectual property rights (other than patent or trademark)
Licensable by such Contributor to use, reproduce, make available,
modify, display, perform, distribute, and otherwise exploit its
Contributions, either on an unmodified basis, with Modifications, or
as part of a Larger Work; and
(b) under Patent Claims of such Contributor to make, use, sell, offer
for sale, have made, import, and otherwise transfer either its
Contributions or its Contributor Version.
2.2. Effective Date
The licenses granted in Section 2.1 with respect to any Contribution
become effective for each Contribution on the date the Contributor first
distributes such Contribution.
2.3. Limitations on Grant Scope
The licenses granted in this Section 2 are the only rights granted under
this License. No additional rights or licenses will be implied from the
distribution or licensing of Covered Software under this License.
Notwithstanding Section 2.1(b) above, no patent license is granted by a
Contributor:
(a) for any code that a Contributor has removed from Covered Software;
or
(b) for infringements caused by: (i) Your and any other third party's
modifications of Covered Software, or (ii) the combination of its
Contributions with other software (except as part of its Contributor
Version); or
(c) under Patent Claims infringed by Covered Software in the absence of
its Contributions.
This License does not grant any rights in the trademarks, service marks,
or logos of any Contributor (except as may be necessary to comply with
the notice requirements in Section 3.4).
2.4. Subsequent Licenses
No Contributor makes additional grants as a result of Your choice to
distribute the Covered Software under a subsequent version of this
License (see Section 10.2) or under the terms of a Secondary License (if
permitted under the terms of Section 3.3).
2.5. Representation
Each Contributor represents that the Contributor believes its
Contributions are its original creation(s) or it has sufficient rights
to grant the rights to its Contributions conveyed by this License.
2.6. Fair Use
This License is not intended to limit any rights You have under
applicable copyright doctrines of fair use, fair dealing, or other
equivalents.
2.7. Conditions
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
in Section 2.1.
3. Responsibilities
-------------------
3.1. Distribution of Source Form
All distribution of Covered Software in Source Code Form, including any
Modifications that You create or to which You contribute, must be under
the terms of this License. You must inform recipients that the Source
Code Form of the Covered Software is governed by the terms of this
License, and how they can obtain a copy of this License. You may not
attempt to alter or restrict the recipients' rights in the Source Code
Form.
3.2. Distribution of Executable Form
If You distribute Covered Software in Executable Form then:
(a) such Covered Software must also be made available in Source Code
Form, as described in Section 3.1, and You must inform recipients of
the Executable Form how they can obtain a copy of such Source Code
Form by reasonable means in a timely manner, at a charge no more
than the cost of distribution to the recipient; and
(b) You may distribute such Executable Form under the terms of this
License, or sublicense it under different terms, provided that the
license for the Executable Form does not attempt to limit or alter
the recipients' rights in the Source Code Form under this License.
3.3. Distribution of a Larger Work
You may create and distribute a Larger Work under terms of Your choice,
provided that You also comply with the requirements of this License for
the Covered Software. If the Larger Work is a combination of Covered
Software with a work governed by one or more Secondary Licenses, and the
Covered Software is not Incompatible With Secondary Licenses, this
License permits You to additionally distribute such Covered Software
under the terms of such Secondary License(s), so that the recipient of
the Larger Work may, at their option, further distribute the Covered
Software under the terms of either this License or such Secondary
License(s).
3.4. Notices
You may not remove or alter the substance of any license notices
(including copyright notices, patent notices, disclaimers of warranty,
or limitations of liability) contained within the Source Code Form of
the Covered Software, except that You may alter any license notices to
the extent required to remedy known factual inaccuracies.
3.5. Application of Additional Terms
You may choose to offer, and to charge a fee for, warranty, support,
indemnity or liability obligations to one or more recipients of Covered
Software. However, You may do so only on Your own behalf, and not on
behalf of any Contributor. You must make it absolutely clear that any
such warranty, support, indemnity, or liability obligation is offered by
You alone, and You hereby agree to indemnify every Contributor for any
liability incurred by such Contributor as a result of warranty, support,
indemnity or liability terms You offer. You may include additional
disclaimers of warranty and limitations of liability specific to any
jurisdiction.
4. Inability to Comply Due to Statute or Regulation
---------------------------------------------------
If it is impossible for You to comply with any of the terms of this
License with respect to some or all of the Covered Software due to
statute, judicial order, or regulation then You must: (a) comply with
the terms of this License to the maximum extent possible; and (b)
describe the limitations and the code they affect. Such description must
be placed in a text file included with all distributions of the Covered
Software under this License. Except to the extent prohibited by statute
or regulation, such description must be sufficiently detailed for a
recipient of ordinary skill to be able to understand it.
5. Termination
--------------
5.1. The rights granted under this License will terminate automatically
if You fail to comply with any of its terms. However, if You become
compliant, then the rights granted under this License from a particular
Contributor are reinstated (a) provisionally, unless and until such
Contributor explicitly and finally terminates Your grants, and (b) on an
ongoing basis, if such Contributor fails to notify You of the
non-compliance by some reasonable means prior to 60 days after You have
come back into compliance. Moreover, Your grants from a particular
Contributor are reinstated on an ongoing basis if such Contributor
notifies You of the non-compliance by some reasonable means, this is the
first time You have received notice of non-compliance with this License
from such Contributor, and You become compliant prior to 30 days after
Your receipt of the notice.
5.2. If You initiate litigation against any entity by asserting a patent
infringement claim (excluding declaratory judgment actions,
counter-claims, and cross-claims) alleging that a Contributor Version
directly or indirectly infringes any patent, then the rights granted to
You by any and all Contributors for the Covered Software under Section
2.1 of this License shall terminate.
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
end user license agreements (excluding distributors and resellers) which
have been validly granted by You or Your distributors under this License
prior to termination shall survive termination.
************************************************************************
* *
* 6. Disclaimer of Warranty *
* ------------------------- *
* *
* Covered Software is provided under this License on an "as is" *
* basis, without warranty of any kind, either expressed, implied, or *
* statutory, including, without limitation, warranties that the *
* Covered Software is free of defects, merchantable, fit for a *
* particular purpose or non-infringing. The entire risk as to the *
* quality and performance of the Covered Software is with You. *
* Should any Covered Software prove defective in any respect, You *
* (not any Contributor) assume the cost of any necessary servicing, *
* repair, or correction. This disclaimer of warranty constitutes an *
* essential part of this License. No use of any Covered Software is *
* authorized under this License except under this disclaimer. *
* *
************************************************************************
************************************************************************
* *
* 7. Limitation of Liability *
* -------------------------- *
* *
* Under no circumstances and under no legal theory, whether tort *
* (including negligence), contract, or otherwise, shall any *
* Contributor, or anyone who distributes Covered Software as *
* permitted above, be liable to You for any direct, indirect, *
* special, incidental, or consequential damages of any character *
* including, without limitation, damages for lost profits, loss of *
* goodwill, work stoppage, computer failure or malfunction, or any *
* and all other commercial damages or losses, even if such party *
* shall have been informed of the possibility of such damages. This *
* limitation of liability shall not apply to liability for death or *
* personal injury resulting from such party's negligence to the *
* extent applicable law prohibits such limitation. Some *
* jurisdictions do not allow the exclusion or limitation of *
* incidental or consequential damages, so this exclusion and *
* limitation may not apply to You. *
* *
************************************************************************
8. Litigation
-------------
Any litigation relating to this License may be brought only in the
courts of a jurisdiction where the defendant maintains its principal
place of business and such litigation shall be governed by laws of that
jurisdiction, without reference to its conflict-of-law provisions.
Nothing in this Section shall prevent a party's ability to bring
cross-claims or counter-claims.
9. Miscellaneous
----------------
This License represents the complete agreement concerning the subject
matter hereof. If any provision of this License is held to be
unenforceable, such provision shall be reformed only to the extent
necessary to make it enforceable. Any law or regulation which provides
that the language of a contract shall be construed against the drafter
shall not be used to construe this License against a Contributor.
10. Versions of the License
---------------------------
10.1. New Versions
Mozilla Foundation is the license steward. Except as provided in Section
10.3, no one other than the license steward has the right to modify or
publish new versions of this License. Each version will be given a
distinguishing version number.
10.2. Effect of New Versions
You may distribute the Covered Software under the terms of the version
of the License under which You originally received the Covered Software,
or under the terms of any subsequent version published by the license
steward.
10.3. Modified Versions
If you create software not governed by this License, and you want to
create a new license for such software, you may create and use a
modified version of this License if you rename the license and remove
any references to the name of the license steward (except to note that
such modified license differs from this License).
10.4. Distributing Source Code Form that is Incompatible With Secondary
Licenses
If You choose to distribute Source Code Form that is Incompatible With
Secondary Licenses under the terms of this version of the License, the
notice described in Exhibit B of this License must be attached.
Exhibit A - Source Code Form License Notice
-------------------------------------------
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.
If it is not possible or desirable to put the notice in a particular
file, then You may include the notice in a location (such as a LICENSE
file in a relevant directory) where a recipient would be likely to look
for such a notice.
You may add additional accurate notices of copyright ownership.
Exhibit B - "Incompatible With Secondary Licenses" Notice
---------------------------------------------------------
This Source Code Form is "Incompatible With Secondary Licenses", as
defined by the Mozilla Public License, v. 2.0.
+1 -3
View File
@@ -13,7 +13,7 @@ SPDX-License-Identifier: GPL-2.0-or-later
<h4 align="center"><b>yuzu</b> is the world's most popular, open-source, Nintendo Switch emulator — started by the creators of <a href="https://citra-emu.org" target="_blank">Citra</a>.
<br>
It is written in C++ with portability in mind, and we actively maintain builds for Windows and Linux.
It is written in C++ with portability in mind, and we actively maintain builds for Windows, Linux and Android.
</h4>
<p align="center">
@@ -83,5 +83,3 @@ If you wish to support us a different way, please join our [Discord](https://dis
## License
yuzu is licensed under the GPLv3 (or any later version). Refer to the [LICENSE.txt](https://github.com/yuzu-emu/yuzu/blob/master/LICENSE.txt) file.
The [Skyline-Emulator Team](https://github.com/skyline-emu/skyline) may choose to use the code from these contributors under the GPL-3.0-or-later OR MPL-2.0: [FernandoS27](https://github.com/FernandoS27), [lioncash](https://github.com/lioncash), [bunnei](https://github.com/bunnei), [ReinUsesLisp](https://github.com/ReinUsesLisp), [Morph1984](https://github.com/Morph1984), [ogniK5377](https://github.com/ogniK5377), [german77](https://github.com/german77), [ameerj](https://github.com/ameerj), [Kelebek1](https://github.com/Kelebek1) and [lat9nq](https://github.com/lat9nq)
+691 -592
View File
File diff suppressed because it is too large Load Diff
+691 -592
View File
File diff suppressed because it is too large Load Diff
+691 -592
View File
File diff suppressed because it is too large Load Diff
+748 -648
View File
File diff suppressed because it is too large Load Diff
+728 -621
View File
File diff suppressed because it is too large Load Diff
+698 -595
View File
File diff suppressed because it is too large Load Diff
+687 -584
View File
File diff suppressed because it is too large Load Diff
+692 -593
View File
File diff suppressed because it is too large Load Diff
+683 -584
View File
File diff suppressed because it is too large Load Diff
+715 -616
View File
File diff suppressed because it is too large Load Diff
+716 -616
View File
File diff suppressed because it is too large Load Diff
+1213 -1073
View File
File diff suppressed because it is too large Load Diff
+689 -586
View File
File diff suppressed because it is too large Load Diff
+691 -593
View File
File diff suppressed because it is too large Load Diff
+683 -584
View File
File diff suppressed because it is too large Load Diff
+683 -584
View File
File diff suppressed because it is too large Load Diff
+688 -585
View File
File diff suppressed because it is too large Load Diff
+691 -592
View File
File diff suppressed because it is too large Load Diff
+894 -785
View File
File diff suppressed because it is too large Load Diff
+813 -673
View File
File diff suppressed because it is too large Load Diff
+1197 -872
View File
File diff suppressed because it is too large Load Diff
+1197 -872
View File
File diff suppressed because it is too large Load Diff
+688 -585
View File
File diff suppressed because it is too large Load Diff
+698 -595
View File
File diff suppressed because it is too large Load Diff
+31 -17
View File
@@ -8,15 +8,21 @@ set(CMAKE_POLICY_DEFAULT_CMP0077 NEW)
# Disable tests in all externals supporting the standard option name
set(BUILD_TESTING OFF)
# Build only static externals
set(BUILD_SHARED_LIBS OFF)
# Skip install rules for all externals
set_directory_properties(PROPERTIES EXCLUDE_FROM_ALL ON)
# xbyak
if ((ARCHITECTURE_x86 OR ARCHITECTURE_x86_64) AND NOT TARGET xbyak::xbyak)
add_subdirectory(xbyak EXCLUDE_FROM_ALL)
add_subdirectory(xbyak)
endif()
# Dynarmic
if ((ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64) AND NOT TARGET dynarmic::dynarmic)
set(DYNARMIC_IGNORE_ASSERTS ON)
add_subdirectory(dynarmic EXCLUDE_FROM_ALL)
add_subdirectory(dynarmic)
add_library(dynarmic::dynarmic ALIAS dynarmic)
endif()
@@ -34,7 +40,7 @@ if (NOT TARGET inih::INIReader)
endif()
# mbedtls
add_subdirectory(mbedtls EXCLUDE_FROM_ALL)
add_subdirectory(mbedtls)
target_include_directories(mbedtls PUBLIC ./mbedtls/include)
# MicroProfile
@@ -48,7 +54,7 @@ endif()
# libusb
if (ENABLE_LIBUSB AND NOT TARGET libusb::usb)
add_subdirectory(libusb EXCLUDE_FROM_ALL)
add_subdirectory(libusb)
endif()
# SDL2
@@ -67,18 +73,16 @@ if (YUZU_USE_EXTERNAL_SDL2)
set(HIDAPI ON)
endif()
set(SDL_STATIC ON)
set(SDL_SHARED OFF)
if (APPLE)
set(SDL_FILE ON)
endif()
add_subdirectory(SDL EXCLUDE_FROM_ALL)
add_subdirectory(SDL)
endif()
# ENet
if (NOT TARGET enet::enet)
add_subdirectory(enet EXCLUDE_FROM_ALL)
add_subdirectory(enet)
target_include_directories(enet INTERFACE ./enet/include)
add_library(enet::enet ALIAS enet)
endif()
@@ -86,24 +90,26 @@ endif()
# Cubeb
if (ENABLE_CUBEB AND NOT TARGET cubeb::cubeb)
set(BUILD_TESTS OFF)
add_subdirectory(cubeb EXCLUDE_FROM_ALL)
set(BUILD_TOOLS OFF)
add_subdirectory(cubeb)
add_library(cubeb::cubeb ALIAS cubeb)
endif()
# DiscordRPC
if (USE_DISCORD_PRESENCE AND NOT TARGET DiscordRPC::discord-rpc)
add_subdirectory(discord-rpc EXCLUDE_FROM_ALL)
set(BUILD_EXAMPLES OFF)
add_subdirectory(discord-rpc)
target_include_directories(discord-rpc INTERFACE ./discord-rpc/include)
add_library(DiscordRPC::discord-rpc ALIAS discord-rpc)
endif()
# Sirit
add_subdirectory(sirit EXCLUDE_FROM_ALL)
add_subdirectory(sirit)
# httplib
if (ENABLE_WEB_SERVICE AND NOT TARGET httplib::httplib)
set(HTTPLIB_REQUIRE_OPENSSL ON)
add_subdirectory(cpp-httplib EXCLUDE_FROM_ALL)
add_subdirectory(cpp-httplib)
endif()
# cpp-jwt
@@ -111,12 +117,12 @@ if (ENABLE_WEB_SERVICE AND NOT TARGET cpp-jwt::cpp-jwt)
set(CPP_JWT_BUILD_EXAMPLES OFF)
set(CPP_JWT_BUILD_TESTS OFF)
set(CPP_JWT_USE_VENDORED_NLOHMANN_JSON OFF)
add_subdirectory(cpp-jwt EXCLUDE_FROM_ALL)
add_subdirectory(cpp-jwt)
endif()
# Opus
if (NOT TARGET Opus::opus)
add_subdirectory(opus EXCLUDE_FROM_ALL)
add_subdirectory(opus)
endif()
# FFMpeg
@@ -130,12 +136,20 @@ endif()
# Vulkan-Headers
if (YUZU_USE_EXTERNAL_VULKAN_HEADERS)
add_subdirectory(Vulkan-Headers EXCLUDE_FROM_ALL)
add_subdirectory(Vulkan-Headers)
endif()
if (NOT TARGET LLVM::Demangle)
add_library(demangle STATIC)
add_library(demangle demangle/ItaniumDemangle.cpp)
target_include_directories(demangle PUBLIC ./demangle)
target_sources(demangle PRIVATE demangle/ItaniumDemangle.cpp)
add_library(LLVM::Demangle ALIAS demangle)
endif()
add_library(stb stb/stb_dxt.cpp)
target_include_directories(stb PUBLIC ./stb)
if (ANDROID)
if (ARCHITECTURE_arm64)
add_subdirectory(libadrenotools)
endif()
endif()
+65 -10
View File
@@ -1,7 +1,7 @@
# SPDX-FileCopyrightText: 2021 yuzu Emulator Project
# SPDX-License-Identifier: GPL-2.0-or-later
if (NOT WIN32)
if (NOT WIN32 AND NOT ANDROID)
# Build FFmpeg from externals
message(STATUS "Using FFmpeg from externals")
@@ -44,10 +44,12 @@ if (NOT WIN32)
endforeach()
find_package(PkgConfig REQUIRED)
pkg_check_modules(LIBVA libva)
pkg_check_modules(CUDA cuda)
pkg_check_modules(FFNVCODEC ffnvcodec)
pkg_check_modules(VDPAU vdpau)
if (NOT ANDROID)
pkg_check_modules(LIBVA libva)
pkg_check_modules(CUDA cuda)
pkg_check_modules(FFNVCODEC ffnvcodec)
pkg_check_modules(VDPAU vdpau)
endif()
set(FFmpeg_HWACCEL_LIBRARIES)
set(FFmpeg_HWACCEL_FLAGS)
@@ -121,6 +123,26 @@ if (NOT WIN32)
list(APPEND FFmpeg_HWACCEL_FLAGS --disable-vdpau)
endif()
find_program(BASH_PROGRAM bash REQUIRED)
set(FFmpeg_CROSS_COMPILE_FLAGS "")
if (ANDROID)
string(TOLOWER "${CMAKE_HOST_SYSTEM_NAME}" FFmpeg_HOST_SYSTEM_NAME)
set(TOOLCHAIN "${ANDROID_NDK}/toolchains/llvm/prebuilt/${FFmpeg_HOST_SYSTEM_NAME}-${CMAKE_HOST_SYSTEM_PROCESSOR}")
set(SYSROOT "${TOOLCHAIN}/sysroot")
set(FFmpeg_CPU "armv8-a")
list(APPEND FFmpeg_CROSS_COMPILE_FLAGS
--arch=arm64
#--cpu=${FFmpeg_CPU}
--enable-cross-compile
--cross-prefix=${TOOLCHAIN}/bin/aarch64-linux-android-
--sysroot=${SYSROOT}
--target-os=android
--extra-ldflags="--ld-path=${TOOLCHAIN}/bin/ld.lld"
--extra-ldflags="-nostdlib"
)
endif()
# `configure` parameters builds only exactly what yuzu needs from FFmpeg
# `--disable-vdpau` is needed to avoid linking issues
set(FFmpeg_CC ${CMAKE_C_COMPILER_LAUNCHER} ${CMAKE_C_COMPILER})
@@ -129,9 +151,8 @@ if (NOT WIN32)
OUTPUT
${FFmpeg_MAKEFILE}
COMMAND
/bin/bash ${FFmpeg_PREFIX}/configure
${BASH_PROGRAM} ${FFmpeg_PREFIX}/configure
--disable-avdevice
--disable-avfilter
--disable-avformat
--disable-doc
--disable-everything
@@ -143,15 +164,18 @@ if (NOT WIN32)
--enable-decoder=h264
--enable-decoder=vp8
--enable-decoder=vp9
--enable-filter=yadif
--cc="${FFmpeg_CC}"
--cxx="${FFmpeg_CXX}"
${FFmpeg_HWACCEL_FLAGS}
${FFmpeg_CROSS_COMPILE_FLAGS}
WORKING_DIRECTORY
${FFmpeg_BUILD_DIR}
)
unset(FFmpeg_CC)
unset(FFmpeg_CXX)
unset(FFmpeg_HWACCEL_FLAGS)
unset(FFmpeg_CROSS_COMPILE_FLAGS)
# Workaround for Ubuntu 18.04's older version of make not being able to call make as a child
# with context of the jobserver. Also helps ninja users.
@@ -197,19 +221,50 @@ if (NOT WIN32)
else()
message(FATAL_ERROR "FFmpeg not found")
endif()
else(WIN32)
elseif(ANDROID)
# Use yuzu FFmpeg binaries
set(FFmpeg_EXT_NAME "ffmpeg-4.4")
if (ARCHITECTURE_arm64)
set(FFmpeg_EXT_NAME "ffmpeg-android-v5.1.LTS-aarch64")
elseif (ARCHITECTURE_x86_64)
set(FFmpeg_EXT_NAME "ffmpeg-android-v5.1.LTS-x86_64")
else()
message(FATAL_ERROR "Unsupported architecture for Android FFmpeg")
endif()
set(FFmpeg_PATH "${CMAKE_BINARY_DIR}/externals/${FFmpeg_EXT_NAME}")
download_bundled_external("ffmpeg/" ${FFmpeg_EXT_NAME} "")
set(FFmpeg_FOUND YES)
set(FFmpeg_INCLUDE_DIR "${FFmpeg_PATH}/include" CACHE PATH "Path to FFmpeg headers" FORCE)
set(FFmpeg_LIBRARY_DIR "${FFmpeg_PATH}/lib" CACHE PATH "Path to FFmpeg library directory" FORCE)
set(FFmpeg_LDFLAGS "" CACHE STRING "FFmpeg linker flags" FORCE)
set(FFmpeg_LIBRARIES
${FFmpeg_LIBRARY_DIR}/libavcodec.so
${FFmpeg_LIBRARY_DIR}/libavdevice.so
${FFmpeg_LIBRARY_DIR}/libavfilter.so
${FFmpeg_LIBRARY_DIR}/libavformat.so
${FFmpeg_LIBRARY_DIR}/libavutil.so
${FFmpeg_LIBRARY_DIR}/libswresample.so
${FFmpeg_LIBRARY_DIR}/libswscale.so
${FFmpeg_LIBRARY_DIR}/libvpx.a
${FFmpeg_LIBRARY_DIR}/libx264.a
CACHE PATH "Paths to FFmpeg libraries" FORCE)
# exported variables
set(FFmpeg_PATH "${FFmpeg_PATH}" PARENT_SCOPE)
set(FFmpeg_LDFLAGS "${FFmpeg_LDFLAGS}" PARENT_SCOPE)
set(FFmpeg_LIBRARIES "${FFmpeg_LIBRARIES}" PARENT_SCOPE)
set(FFmpeg_INCLUDE_DIR "${FFmpeg_INCLUDE_DIR}" PARENT_SCOPE)
elseif(WIN32)
# Use yuzu FFmpeg binaries
set(FFmpeg_EXT_NAME "ffmpeg-5.1.3")
set(FFmpeg_PATH "${CMAKE_BINARY_DIR}/externals/${FFmpeg_EXT_NAME}")
download_bundled_external("ffmpeg/" ${FFmpeg_EXT_NAME} "")
set(FFmpeg_FOUND YES)
set(FFmpeg_INCLUDE_DIR "${FFmpeg_PATH}/include" CACHE PATH "Path to FFmpeg headers" FORCE)
set(FFmpeg_LIBRARY_DIR "${FFmpeg_PATH}/bin" CACHE PATH "Path to FFmpeg library directory" FORCE)
set(FFmpeg_LDFLAGS "" CACHE STRING "FFmpeg linker flags" FORCE)
set(FFmpeg_DLL_DIR "${FFmpeg_PATH}/bin" CACHE PATH "Path to FFmpeg dll's" FORCE)
set(FFmpeg_LIBRARIES
${FFmpeg_LIBRARY_DIR}/swscale.lib
${FFmpeg_LIBRARY_DIR}/avcodec.lib
${FFmpeg_LIBRARY_DIR}/avfilter.lib
${FFmpeg_LIBRARY_DIR}/avutil.lib
CACHE PATH "Paths to FFmpeg libraries" FORCE)
# exported variables
+1 -1
View File
@@ -1,7 +1,7 @@
# SPDX-FileCopyrightText: 2015 Yuri Kunde Schlesner <yuriks@yuriks.net>
# SPDX-License-Identifier: GPL-2.0-or-later
add_library(glad STATIC
add_library(glad
src/glad.c
include/KHR/khrplatform.h
include/glad/glad.h
Vendored Submodule
+1
+1 -1
View File
@@ -122,7 +122,7 @@ else() # MINGW OR (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
add_compile_options(/utf-8)
endif()
add_library(usb STATIC EXCLUDE_FROM_ALL
add_library(usb
libusb/libusb/core.c
libusb/libusb/core.c
libusb/libusb/descriptor.c
+7 -1
View File
@@ -1697,7 +1697,13 @@ void MicroProfileFlip()
{
int nTimer = MicroProfileLogTimerIndex(LE);
uint8_t nGroup = pTimerToGroup[nTimer];
MP_ASSERT(nStackPos < MICROPROFILE_STACK_MAX);
// To avoid crashing due to OOB memory accesses/asserts
// simply skip this iteration
// MP_ASSERT(nStackPos < MICROPROFILE_STACK_MAX);
if (nStackPos >= MICROPROFILE_STACK_MAX) {
break;
}
MP_ASSERT(nGroup < MICROPROFILE_MAX_GROUPS);
pGroupStackPos[nGroup]++;
pStack[nStackPos++] = k;
+1 -1
View File
@@ -23,7 +23,7 @@ else()
endif()
endif()
add_library(opus STATIC
add_library(opus
# CELT sources
opus/celt/bands.c
opus/celt/celt.c
+765
View File
@@ -0,0 +1,765 @@
// SPDX-FileCopyrightText: fabian "ryg" giesen
// SPDX-License-Identifier: MIT
// stb_dxt.h - v1.12 - DXT1/DXT5 compressor
#include <stb_dxt.h>
#include <stdlib.h>
#include <string.h>
#if !defined(STBD_FABS)
#include <math.h>
#endif
#ifndef STBD_FABS
#define STBD_FABS(x) fabs(x)
#endif
static const unsigned char stb__OMatch5[256][2] = {
{0, 0}, {0, 0}, {0, 1}, {0, 1}, {1, 0}, {1, 0}, {1, 0}, {1, 1}, {1, 1},
{1, 1}, {1, 2}, {0, 4}, {2, 1}, {2, 1}, {2, 1}, {2, 2}, {2, 2}, {2, 2},
{2, 3}, {1, 5}, {3, 2}, {3, 2}, {4, 0}, {3, 3}, {3, 3}, {3, 3}, {3, 4},
{3, 4}, {3, 4}, {3, 5}, {4, 3}, {4, 3}, {5, 2}, {4, 4}, {4, 4}, {4, 5},
{4, 5}, {5, 4}, {5, 4}, {5, 4}, {6, 3}, {5, 5}, {5, 5}, {5, 6}, {4, 8},
{6, 5}, {6, 5}, {6, 5}, {6, 6}, {6, 6}, {6, 6}, {6, 7}, {5, 9}, {7, 6},
{7, 6}, {8, 4}, {7, 7}, {7, 7}, {7, 7}, {7, 8}, {7, 8}, {7, 8}, {7, 9},
{8, 7}, {8, 7}, {9, 6}, {8, 8}, {8, 8}, {8, 9}, {8, 9}, {9, 8}, {9, 8},
{9, 8}, {10, 7}, {9, 9}, {9, 9}, {9, 10}, {8, 12}, {10, 9}, {10, 9}, {10, 9},
{10, 10}, {10, 10}, {10, 10}, {10, 11}, {9, 13}, {11, 10}, {11, 10}, {12, 8}, {11, 11},
{11, 11}, {11, 11}, {11, 12}, {11, 12}, {11, 12}, {11, 13}, {12, 11}, {12, 11}, {13, 10},
{12, 12}, {12, 12}, {12, 13}, {12, 13}, {13, 12}, {13, 12}, {13, 12}, {14, 11}, {13, 13},
{13, 13}, {13, 14}, {12, 16}, {14, 13}, {14, 13}, {14, 13}, {14, 14}, {14, 14}, {14, 14},
{14, 15}, {13, 17}, {15, 14}, {15, 14}, {16, 12}, {15, 15}, {15, 15}, {15, 15}, {15, 16},
{15, 16}, {15, 16}, {15, 17}, {16, 15}, {16, 15}, {17, 14}, {16, 16}, {16, 16}, {16, 17},
{16, 17}, {17, 16}, {17, 16}, {17, 16}, {18, 15}, {17, 17}, {17, 17}, {17, 18}, {16, 20},
{18, 17}, {18, 17}, {18, 17}, {18, 18}, {18, 18}, {18, 18}, {18, 19}, {17, 21}, {19, 18},
{19, 18}, {20, 16}, {19, 19}, {19, 19}, {19, 19}, {19, 20}, {19, 20}, {19, 20}, {19, 21},
{20, 19}, {20, 19}, {21, 18}, {20, 20}, {20, 20}, {20, 21}, {20, 21}, {21, 20}, {21, 20},
{21, 20}, {22, 19}, {21, 21}, {21, 21}, {21, 22}, {20, 24}, {22, 21}, {22, 21}, {22, 21},
{22, 22}, {22, 22}, {22, 22}, {22, 23}, {21, 25}, {23, 22}, {23, 22}, {24, 20}, {23, 23},
{23, 23}, {23, 23}, {23, 24}, {23, 24}, {23, 24}, {23, 25}, {24, 23}, {24, 23}, {25, 22},
{24, 24}, {24, 24}, {24, 25}, {24, 25}, {25, 24}, {25, 24}, {25, 24}, {26, 23}, {25, 25},
{25, 25}, {25, 26}, {24, 28}, {26, 25}, {26, 25}, {26, 25}, {26, 26}, {26, 26}, {26, 26},
{26, 27}, {25, 29}, {27, 26}, {27, 26}, {28, 24}, {27, 27}, {27, 27}, {27, 27}, {27, 28},
{27, 28}, {27, 28}, {27, 29}, {28, 27}, {28, 27}, {29, 26}, {28, 28}, {28, 28}, {28, 29},
{28, 29}, {29, 28}, {29, 28}, {29, 28}, {30, 27}, {29, 29}, {29, 29}, {29, 30}, {29, 30},
{30, 29}, {30, 29}, {30, 29}, {30, 30}, {30, 30}, {30, 30}, {30, 31}, {30, 31}, {31, 30},
{31, 30}, {31, 30}, {31, 31}, {31, 31},
};
static const unsigned char stb__OMatch6[256][2] = {
{0, 0}, {0, 1}, {1, 0}, {1, 1}, {1, 1}, {1, 2}, {2, 1}, {2, 2}, {2, 2},
{2, 3}, {3, 2}, {3, 3}, {3, 3}, {3, 4}, {4, 3}, {4, 4}, {4, 4}, {4, 5},
{5, 4}, {5, 5}, {5, 5}, {5, 6}, {6, 5}, {6, 6}, {6, 6}, {6, 7}, {7, 6},
{7, 7}, {7, 7}, {7, 8}, {8, 7}, {8, 8}, {8, 8}, {8, 9}, {9, 8}, {9, 9},
{9, 9}, {9, 10}, {10, 9}, {10, 10}, {10, 10}, {10, 11}, {11, 10}, {8, 16}, {11, 11},
{11, 12}, {12, 11}, {9, 17}, {12, 12}, {12, 13}, {13, 12}, {11, 16}, {13, 13}, {13, 14},
{14, 13}, {12, 17}, {14, 14}, {14, 15}, {15, 14}, {14, 16}, {15, 15}, {15, 16}, {16, 14},
{16, 15}, {17, 14}, {16, 16}, {16, 17}, {17, 16}, {18, 15}, {17, 17}, {17, 18}, {18, 17},
{20, 14}, {18, 18}, {18, 19}, {19, 18}, {21, 15}, {19, 19}, {19, 20}, {20, 19}, {20, 20},
{20, 20}, {20, 21}, {21, 20}, {21, 21}, {21, 21}, {21, 22}, {22, 21}, {22, 22}, {22, 22},
{22, 23}, {23, 22}, {23, 23}, {23, 23}, {23, 24}, {24, 23}, {24, 24}, {24, 24}, {24, 25},
{25, 24}, {25, 25}, {25, 25}, {25, 26}, {26, 25}, {26, 26}, {26, 26}, {26, 27}, {27, 26},
{24, 32}, {27, 27}, {27, 28}, {28, 27}, {25, 33}, {28, 28}, {28, 29}, {29, 28}, {27, 32},
{29, 29}, {29, 30}, {30, 29}, {28, 33}, {30, 30}, {30, 31}, {31, 30}, {30, 32}, {31, 31},
{31, 32}, {32, 30}, {32, 31}, {33, 30}, {32, 32}, {32, 33}, {33, 32}, {34, 31}, {33, 33},
{33, 34}, {34, 33}, {36, 30}, {34, 34}, {34, 35}, {35, 34}, {37, 31}, {35, 35}, {35, 36},
{36, 35}, {36, 36}, {36, 36}, {36, 37}, {37, 36}, {37, 37}, {37, 37}, {37, 38}, {38, 37},
{38, 38}, {38, 38}, {38, 39}, {39, 38}, {39, 39}, {39, 39}, {39, 40}, {40, 39}, {40, 40},
{40, 40}, {40, 41}, {41, 40}, {41, 41}, {41, 41}, {41, 42}, {42, 41}, {42, 42}, {42, 42},
{42, 43}, {43, 42}, {40, 48}, {43, 43}, {43, 44}, {44, 43}, {41, 49}, {44, 44}, {44, 45},
{45, 44}, {43, 48}, {45, 45}, {45, 46}, {46, 45}, {44, 49}, {46, 46}, {46, 47}, {47, 46},
{46, 48}, {47, 47}, {47, 48}, {48, 46}, {48, 47}, {49, 46}, {48, 48}, {48, 49}, {49, 48},
{50, 47}, {49, 49}, {49, 50}, {50, 49}, {52, 46}, {50, 50}, {50, 51}, {51, 50}, {53, 47},
{51, 51}, {51, 52}, {52, 51}, {52, 52}, {52, 52}, {52, 53}, {53, 52}, {53, 53}, {53, 53},
{53, 54}, {54, 53}, {54, 54}, {54, 54}, {54, 55}, {55, 54}, {55, 55}, {55, 55}, {55, 56},
{56, 55}, {56, 56}, {56, 56}, {56, 57}, {57, 56}, {57, 57}, {57, 57}, {57, 58}, {58, 57},
{58, 58}, {58, 58}, {58, 59}, {59, 58}, {59, 59}, {59, 59}, {59, 60}, {60, 59}, {60, 60},
{60, 60}, {60, 61}, {61, 60}, {61, 61}, {61, 61}, {61, 62}, {62, 61}, {62, 62}, {62, 62},
{62, 63}, {63, 62}, {63, 63}, {63, 63},
};
static int stb__Mul8Bit(int a, int b) {
int t = a * b + 128;
return (t + (t >> 8)) >> 8;
}
static void stb__From16Bit(unsigned char* out, unsigned short v) {
int rv = (v & 0xf800) >> 11;
int gv = (v & 0x07e0) >> 5;
int bv = (v & 0x001f) >> 0;
// expand to 8 bits via bit replication
out[0] = static_cast<unsigned char>((rv * 33) >> 2);
out[1] = static_cast<unsigned char>((gv * 65) >> 4);
out[2] = static_cast<unsigned char>((bv * 33) >> 2);
out[3] = 0;
}
static unsigned short stb__As16Bit(int r, int g, int b) {
return (unsigned short)((stb__Mul8Bit(r, 31) << 11) + (stb__Mul8Bit(g, 63) << 5) +
stb__Mul8Bit(b, 31));
}
// linear interpolation at 1/3 point between a and b, using desired rounding
// type
static int stb__Lerp13(int a, int b) {
#ifdef STB_DXT_USE_ROUNDING_BIAS
// with rounding bias
return a + stb__Mul8Bit(b - a, 0x55);
#else
// without rounding bias
// replace "/ 3" by "* 0xaaab) >> 17" if your compiler sucks or you really
// need every ounce of speed.
return (2 * a + b) / 3;
#endif
}
// linear interpolation at 1/2 point between a and b
static int stb__Lerp12(int a, int b) {
return (a + b) / 2;
}
// lerp RGB color
static void stb__Lerp13RGB(unsigned char* out, unsigned char* p1, unsigned char* p2) {
out[0] = (unsigned char)stb__Lerp13(p1[0], p2[0]);
out[1] = (unsigned char)stb__Lerp13(p1[1], p2[1]);
out[2] = (unsigned char)stb__Lerp13(p1[2], p2[2]);
}
static void stb__Lerp12RGB(unsigned char* out, unsigned char* p1, unsigned char* p2) {
out[0] = (unsigned char)stb__Lerp12(p1[0], p2[0]);
out[1] = (unsigned char)stb__Lerp12(p1[1], p2[1]);
out[2] = (unsigned char)stb__Lerp12(p1[2], p2[2]);
}
/****************************************************************************/
static void stb__Eval4Colors(unsigned char* color, unsigned short c0, unsigned short c1) {
stb__From16Bit(color + 0, c0);
stb__From16Bit(color + 4, c1);
stb__Lerp13RGB(color + 8, color + 0, color + 4);
stb__Lerp13RGB(color + 12, color + 4, color + 0);
}
static void stb__Eval3Colors(unsigned char* color, unsigned short c0, unsigned short c1) {
stb__From16Bit(color + 0, c0);
stb__From16Bit(color + 4, c1);
stb__Lerp12RGB(color + 8, color + 0, color + 4);
}
// The color matching function
static unsigned int stb__MatchColorsBlock(unsigned char* block, unsigned char* color) {
unsigned int mask = 0;
int dirr = color[0 * 4 + 0] - color[1 * 4 + 0];
int dirg = color[0 * 4 + 1] - color[1 * 4 + 1];
int dirb = color[0 * 4 + 2] - color[1 * 4 + 2];
int dots[16];
int stops[4];
int i;
int c0Point, halfPoint, c3Point;
for (i = 0; i < 16; i++)
dots[i] = block[i * 4 + 0] * dirr + block[i * 4 + 1] * dirg + block[i * 4 + 2] * dirb;
for (i = 0; i < 4; i++)
stops[i] = color[i * 4 + 0] * dirr + color[i * 4 + 1] * dirg + color[i * 4 + 2] * dirb;
// think of the colors as arranged on a line; project point onto that line,
// then choose next color out of available ones. we compute the crossover
// points for "best color in top half"/"best in bottom half" and then the same
// inside that subinterval.
//
// relying on this 1d approximation isn't always optimal in terms of euclidean
// distance, but it's very close and a lot faster.
// http://cbloomrants.blogspot.com/2008/12/12-08-08-dxtc-summary.html
c0Point = (stops[1] + stops[3]);
halfPoint = (stops[3] + stops[2]);
c3Point = (stops[2] + stops[0]);
for (i = 15; i >= 0; i--) {
int dot = dots[i] * 2;
mask <<= 2;
if (dot < halfPoint)
mask |= (dot < c0Point) ? 1 : 3;
else
mask |= (dot < c3Point) ? 2 : 0;
}
return mask;
}
static unsigned int stb__MatchColorsAlphaBlock(unsigned char* block, unsigned char* color) {
unsigned int mask = 0;
int dirr = color[0 * 4 + 0] - color[1 * 4 + 0];
int dirg = color[0 * 4 + 1] - color[1 * 4 + 1];
int dirb = color[0 * 4 + 2] - color[1 * 4 + 2];
int dots[16];
int stops[3];
int i;
int c0Point, c2Point;
for (i = 0; i < 16; i++)
dots[i] = block[i * 4 + 0] * dirr + block[i * 4 + 1] * dirg + block[i * 4 + 2] * dirb;
for (i = 0; i < 3; i++)
stops[i] = color[i * 4 + 0] * dirr + color[i * 4 + 1] * dirg + color[i * 4 + 2] * dirb;
c0Point = (stops[1] + stops[2]);
c2Point = (stops[2] + stops[0]);
for (i = 15; i >= 0; i--) {
int dot = dots[i] * 2;
mask <<= 2;
if (block[i * 4 + 3] == 0)
mask |= 3;
else if (dot < c2Point)
mask |= (dot < c0Point) ? 0 : 2;
else
mask |= (dot < c0Point) ? 1 : 0;
}
return mask;
}
static void stb__ReorderColors(unsigned short* pmax16, unsigned short* pmin16) {
if (*pmin16 < *pmax16) {
unsigned short t = *pmin16;
*pmin16 = *pmax16;
*pmax16 = t;
}
}
static void stb__FinalizeColors(unsigned short* pmax16, unsigned short* pmin16,
unsigned int* pmask) {
if (*pmax16 < *pmin16) {
unsigned short t = *pmin16;
*pmin16 = *pmax16;
*pmax16 = t;
*pmask ^= 0x55555555;
}
}
// The color optimization function. (Clever code, part 1)
static void stb__OptimizeColorsBlock(unsigned char* block, unsigned short* pmax16,
unsigned short* pmin16) {
int mind, maxd;
unsigned char *minp, *maxp;
double magn;
int v_r, v_g, v_b;
static const int nIterPower = 4;
float covf[6], vfr, vfg, vfb;
// determine color distribution
int cov[6];
int mu[3], min[3], max[3];
int ch, i, iter;
for (ch = 0; ch < 3; ch++) {
const unsigned char* bp = ((const unsigned char*)block) + ch;
int muv, minv, maxv;
muv = minv = maxv = bp[0];
for (i = 4; i < 64; i += 4) {
muv += bp[i];
if (bp[i] < minv)
minv = bp[i];
else if (bp[i] > maxv)
maxv = bp[i];
}
mu[ch] = (muv + 8) >> 4;
min[ch] = minv;
max[ch] = maxv;
}
// determine covariance matrix
for (i = 0; i < 6; i++)
cov[i] = 0;
for (i = 0; i < 16; i++) {
int r = block[i * 4 + 0] - mu[0];
int g = block[i * 4 + 1] - mu[1];
int b = block[i * 4 + 2] - mu[2];
cov[0] += r * r;
cov[1] += r * g;
cov[2] += r * b;
cov[3] += g * g;
cov[4] += g * b;
cov[5] += b * b;
}
// convert covariance matrix to float, find principal axis via power iter
for (i = 0; i < 6; i++)
covf[i] = static_cast<float>(cov[i]) / 255.0f;
vfr = (float)(max[0] - min[0]);
vfg = (float)(max[1] - min[1]);
vfb = (float)(max[2] - min[2]);
for (iter = 0; iter < nIterPower; iter++) {
float r = vfr * covf[0] + vfg * covf[1] + vfb * covf[2];
float g = vfr * covf[1] + vfg * covf[3] + vfb * covf[4];
float b = vfr * covf[2] + vfg * covf[4] + vfb * covf[5];
vfr = r;
vfg = g;
vfb = b;
}
magn = STBD_FABS(vfr);
if (STBD_FABS(vfg) > magn)
magn = STBD_FABS(vfg);
if (STBD_FABS(vfb) > magn)
magn = STBD_FABS(vfb);
if (magn < 4.0f) { // too small, default to luminance
v_r = 299; // JPEG YCbCr luma coefs, scaled by 1000.
v_g = 587;
v_b = 114;
} else {
magn = 512.0 / magn;
v_r = (int)(vfr * magn);
v_g = (int)(vfg * magn);
v_b = (int)(vfb * magn);
}
minp = maxp = block;
mind = maxd = block[0] * v_r + block[1] * v_g + block[2] * v_b;
// Pick colors at extreme points
for (i = 1; i < 16; i++) {
int dot = block[i * 4 + 0] * v_r + block[i * 4 + 1] * v_g + block[i * 4 + 2] * v_b;
if (dot < mind) {
mind = dot;
minp = block + i * 4;
}
if (dot > maxd) {
maxd = dot;
maxp = block + i * 4;
}
}
*pmax16 = stb__As16Bit(maxp[0], maxp[1], maxp[2]);
*pmin16 = stb__As16Bit(minp[0], minp[1], minp[2]);
stb__ReorderColors(pmax16, pmin16);
}
static void stb__OptimizeColorsAlphaBlock(unsigned char* block, unsigned short* pmax16,
unsigned short* pmin16) {
int mind, maxd;
unsigned char *minp, *maxp;
double magn;
int v_r, v_g, v_b;
static const int nIterPower = 4;
float covf[6], vfr, vfg, vfb;
// determine color distribution
int cov[6];
int mu[3], min[3], max[3];
int ch, i, iter;
for (ch = 0; ch < 3; ch++) {
const unsigned char* bp = ((const unsigned char*)block) + ch;
int muv = 0, minv = 256, maxv = -1;
int num = 0;
for (i = 0; i < 64; i += 4) {
if (bp[3 - ch] == 0) {
continue;
}
muv += bp[i];
if (bp[i] < minv)
minv = bp[i];
else if (bp[i] > maxv)
maxv = bp[i];
num++;
}
mu[ch] = num > 0 ? (muv + 8) / num : 0;
min[ch] = minv;
max[ch] = maxv;
}
// determine covariance matrix
for (i = 0; i < 6; i++)
cov[i] = 0;
for (i = 0; i < 16; i++) {
if (block[i * 4 + 3] == 0) {
continue;
}
int r = block[i * 4 + 0] - mu[0];
int g = block[i * 4 + 1] - mu[1];
int b = block[i * 4 + 2] - mu[2];
cov[0] += r * r;
cov[1] += r * g;
cov[2] += r * b;
cov[3] += g * g;
cov[4] += g * b;
cov[5] += b * b;
}
// convert covariance matrix to float, find principal axis via power iter
for (i = 0; i < 6; i++)
covf[i] = static_cast<float>(cov[i]) / 255.0f;
vfr = (float)(max[0] - min[0]);
vfg = (float)(max[1] - min[1]);
vfb = (float)(max[2] - min[2]);
for (iter = 0; iter < nIterPower; iter++) {
float r = vfr * covf[0] + vfg * covf[1] + vfb * covf[2];
float g = vfr * covf[1] + vfg * covf[3] + vfb * covf[4];
float b = vfr * covf[2] + vfg * covf[4] + vfb * covf[5];
vfr = r;
vfg = g;
vfb = b;
}
magn = STBD_FABS(vfr);
if (STBD_FABS(vfg) > magn)
magn = STBD_FABS(vfg);
if (STBD_FABS(vfb) > magn)
magn = STBD_FABS(vfb);
if (magn < 4.0f) { // too small, default to luminance
v_r = 299; // JPEG YCbCr luma coefs, scaled by 1000.
v_g = 587;
v_b = 114;
} else {
magn = 512.0 / magn;
v_r = (int)(vfr * magn);
v_g = (int)(vfg * magn);
v_b = (int)(vfb * magn);
}
minp = maxp = NULL;
mind = 0x7fffffff;
maxd = -0x80000000;
// Pick colors at extreme points
for (i = 0; i < 16; i++) {
if (block[i * 4 + 3] == 0) {
continue;
}
int dot = block[i * 4 + 0] * v_r + block[i * 4 + 1] * v_g + block[i * 4 + 2] * v_b;
if (dot < mind) {
mind = dot;
minp = block + i * 4;
}
if (dot > maxd) {
maxd = dot;
maxp = block + i * 4;
}
}
if (!maxp) {
// all alpha, no color
*pmin16 = 0xffff;
*pmax16 = 0;
} else {
// endpoint colors found
*pmax16 = stb__As16Bit(maxp[0], maxp[1], maxp[2]);
*pmin16 = stb__As16Bit(minp[0], minp[1], minp[2]);
if (*pmax16 == *pmin16) {
// modify the endpoints to indicate presence of an alpha block
if (*pmax16 > 0) {
(*pmax16)--;
} else {
(*pmin16)++;
}
}
stb__ReorderColors(pmax16, pmin16);
}
}
static const float stb__midpoints5[32] = {
0.015686f, 0.047059f, 0.078431f, 0.111765f, 0.145098f, 0.176471f, 0.207843f, 0.241176f,
0.274510f, 0.305882f, 0.337255f, 0.370588f, 0.403922f, 0.435294f, 0.466667f, 0.5f,
0.533333f, 0.564706f, 0.596078f, 0.629412f, 0.662745f, 0.694118f, 0.725490f, 0.758824f,
0.792157f, 0.823529f, 0.854902f, 0.888235f, 0.921569f, 0.952941f, 0.984314f, 1.0f};
static const float stb__midpoints6[64] = {
0.007843f, 0.023529f, 0.039216f, 0.054902f, 0.070588f, 0.086275f, 0.101961f, 0.117647f,
0.133333f, 0.149020f, 0.164706f, 0.180392f, 0.196078f, 0.211765f, 0.227451f, 0.245098f,
0.262745f, 0.278431f, 0.294118f, 0.309804f, 0.325490f, 0.341176f, 0.356863f, 0.372549f,
0.388235f, 0.403922f, 0.419608f, 0.435294f, 0.450980f, 0.466667f, 0.482353f, 0.500000f,
0.517647f, 0.533333f, 0.549020f, 0.564706f, 0.580392f, 0.596078f, 0.611765f, 0.627451f,
0.643137f, 0.658824f, 0.674510f, 0.690196f, 0.705882f, 0.721569f, 0.737255f, 0.754902f,
0.772549f, 0.788235f, 0.803922f, 0.819608f, 0.835294f, 0.850980f, 0.866667f, 0.882353f,
0.898039f, 0.913725f, 0.929412f, 0.945098f, 0.960784f, 0.976471f, 0.992157f, 1.0f};
static unsigned short stb__Quantize5(float x) {
unsigned short q;
x = x < 0 ? 0 : x > 1 ? 1 : x; // saturate
q = (unsigned short)(x * 31);
q += (x > stb__midpoints5[q]);
return q;
}
static unsigned short stb__Quantize6(float x) {
unsigned short q;
x = x < 0 ? 0 : x > 1 ? 1 : x; // saturate
q = (unsigned short)(x * 63);
q += (x > stb__midpoints6[q]);
return q;
}
// The refinement function. (Clever code, part 2)
// Tries to optimize colors to suit block contents better.
// (By solving a least squares system via normal equations+Cramer's rule)
static int stb__RefineBlock(unsigned char* block, unsigned short* pmax16, unsigned short* pmin16,
unsigned int mask) {
static const int w1Tab[4] = {3, 0, 2, 1};
static const int prods[4] = {0x090000, 0x000900, 0x040102, 0x010402};
// ^some magic to save a lot of multiplies in the accumulating loop...
// (precomputed products of weights for least squares system, accumulated
// inside one 32-bit register)
float f;
unsigned short oldMin, oldMax, min16, max16;
int i, akku = 0, xx, xy, yy;
int At1_r, At1_g, At1_b;
int At2_r, At2_g, At2_b;
unsigned int cm = mask;
oldMin = *pmin16;
oldMax = *pmax16;
if ((mask ^ (mask << 2)) < 4) // all pixels have the same index?
{
// yes, linear system would be singular; solve using optimal
// single-color match on average color
int r = 8, g = 8, b = 8;
for (i = 0; i < 16; ++i) {
r += block[i * 4 + 0];
g += block[i * 4 + 1];
b += block[i * 4 + 2];
}
r >>= 4;
g >>= 4;
b >>= 4;
max16 = static_cast<unsigned short>((stb__OMatch5[r][0] << 11) | (stb__OMatch6[g][0] << 5) |
stb__OMatch5[b][0]);
min16 = static_cast<unsigned short>((stb__OMatch5[r][1] << 11) | (stb__OMatch6[g][1] << 5) |
stb__OMatch5[b][1]);
} else {
At1_r = At1_g = At1_b = 0;
At2_r = At2_g = At2_b = 0;
for (i = 0; i < 16; ++i, cm >>= 2) {
int step = cm & 3;
int w1 = w1Tab[step];
int r = block[i * 4 + 0];
int g = block[i * 4 + 1];
int b = block[i * 4 + 2];
akku += prods[step];
At1_r += w1 * r;
At1_g += w1 * g;
At1_b += w1 * b;
At2_r += r;
At2_g += g;
At2_b += b;
}
At2_r = 3 * At2_r - At1_r;
At2_g = 3 * At2_g - At1_g;
At2_b = 3 * At2_b - At1_b;
// extract solutions and decide solvability
xx = akku >> 16;
yy = (akku >> 8) & 0xff;
xy = (akku >> 0) & 0xff;
f = 3.0f / 255.0f / static_cast<float>(xx * yy - xy * xy);
max16 = static_cast<unsigned short>(
stb__Quantize5(static_cast<float>(At1_r * yy - At2_r * xy) * f) << 11);
max16 |= static_cast<unsigned short>(
stb__Quantize6(static_cast<float>(At1_g * yy - At2_g * xy) * f) << 5);
max16 |= static_cast<unsigned short>(
stb__Quantize5(static_cast<float>(At1_b * yy - At2_b * xy) * f) << 0);
min16 = static_cast<unsigned short>(
stb__Quantize5(static_cast<float>(At2_r * xx - At1_r * xy) * f) << 11);
min16 |= static_cast<unsigned short>(
stb__Quantize6(static_cast<float>(At2_g * xx - At1_g * xy) * f) << 5);
min16 |= static_cast<unsigned short>(
stb__Quantize5(static_cast<float>(At2_b * xx - At1_b * xy) * f) << 0);
}
*pmin16 = min16;
*pmax16 = max16;
stb__ReorderColors(pmax16, pmin16);
return oldMin != min16 || oldMax != max16;
}
// Color block compression
static void stb__CompressColorBlock(unsigned char* dest, unsigned char* block, int alpha,
int mode) {
unsigned int mask;
int i;
int refinecount;
unsigned short max16, min16;
unsigned char color[4 * 4];
refinecount = (mode & STB_DXT_HIGHQUAL) ? 2 : 1;
// check if block is constant
for (i = 1; i < 16; i++)
if (((unsigned int*)block)[i] != ((unsigned int*)block)[0])
break;
if (i == 16 && block[3] == 0 && alpha) { // constant alpha
mask = 0xffffffff;
max16 = 0;
min16 = 0xffff;
} else if (i == 16) { // constant color
int r = block[0], g = block[1], b = block[2];
mask = 0xaaaaaaaa;
max16 = static_cast<unsigned short>((stb__OMatch5[r][0] << 11) | (stb__OMatch6[g][0] << 5) |
stb__OMatch5[b][0]);
min16 = static_cast<unsigned short>((stb__OMatch5[r][1] << 11) | (stb__OMatch6[g][1] << 5) |
stb__OMatch5[b][1]);
} else if (alpha) {
stb__OptimizeColorsAlphaBlock(block, &max16, &min16);
stb__Eval3Colors(color, max16, min16);
mask = stb__MatchColorsAlphaBlock(block, color);
} else {
// first step: PCA+map along principal axis
stb__OptimizeColorsBlock(block, &max16, &min16);
if (max16 != min16) {
stb__Eval4Colors(color, max16, min16);
mask = stb__MatchColorsBlock(block, color);
} else
mask = 0;
// third step: refine (multiple times if requested)
for (i = 0; i < refinecount; i++) {
unsigned int lastmask = mask;
if (stb__RefineBlock(block, &max16, &min16, mask)) {
if (max16 != min16) {
stb__Eval4Colors(color, max16, min16);
mask = stb__MatchColorsBlock(block, color);
} else {
mask = 0;
break;
}
}
if (mask == lastmask)
break;
}
}
// write the color block
if (!alpha)
stb__FinalizeColors(&max16, &min16, &mask);
dest[0] = (unsigned char)(max16);
dest[1] = (unsigned char)(max16 >> 8);
dest[2] = (unsigned char)(min16);
dest[3] = (unsigned char)(min16 >> 8);
dest[4] = (unsigned char)(mask);
dest[5] = (unsigned char)(mask >> 8);
dest[6] = (unsigned char)(mask >> 16);
dest[7] = (unsigned char)(mask >> 24);
}
// Alpha block compression (this is easy for a change)
static void stb__CompressAlphaBlock(unsigned char* dest, unsigned char* src, int stride) {
int i, dist, bias, dist4, dist2, bits, mask;
// find min/max color
int mn, mx;
mn = mx = src[0];
for (i = 1; i < 16; i++) {
if (src[i * stride] < mn)
mn = src[i * stride];
else if (src[i * stride] > mx)
mx = src[i * stride];
}
// encode them
dest[0] = (unsigned char)mx;
dest[1] = (unsigned char)mn;
dest += 2;
// determine bias and emit color indices
// given the choice of mx/mn, these indices are optimal:
// http://fgiesen.wordpress.com/2009/12/15/dxt5-alpha-block-index-determination/
dist = mx - mn;
dist4 = dist * 4;
dist2 = dist * 2;
bias = (dist < 8) ? (dist - 1) : (dist / 2 + 2);
bias -= mn * 7;
bits = 0, mask = 0;
for (i = 0; i < 16; i++) {
int a = src[i * stride] * 7 + bias;
int ind, t;
// select index. this is a "linear scale" lerp factor between 0 (val=min)
// and 7 (val=max).
t = (a >= dist4) ? -1 : 0;
ind = t & 4;
a -= dist4 & t;
t = (a >= dist2) ? -1 : 0;
ind += t & 2;
a -= dist2 & t;
ind += (a >= dist);
// turn linear scale into DXT index (0/1 are extremal pts)
ind = -ind & 7;
ind ^= (2 > ind);
// write index
mask |= ind << bits;
if ((bits += 3) >= 8) {
*dest++ = (unsigned char)mask;
mask >>= 8;
bits -= 8;
}
}
}
void stb_compress_bc1_block(unsigned char* dest, const unsigned char* src, int alpha, int mode) {
stb__CompressColorBlock(dest, (unsigned char*)src, alpha, mode);
}
void stb_compress_bc3_block(unsigned char* dest, const unsigned char* src, int mode) {
unsigned char data[16][4];
int i;
stb__CompressAlphaBlock(dest, (unsigned char*)src + 3, 4);
dest += 8;
// make a new copy of the data in which alpha is opaque,
// because code uses a fast test for color constancy
memcpy(data, src, 4 * 16);
for (i = 0; i < 16; ++i)
data[i][3] = 255;
src = &data[0][0];
stb__CompressColorBlock(dest, (unsigned char*)src, 0, mode);
}
+36
View File
@@ -0,0 +1,36 @@
// SPDX-FileCopyrightText: fabian "ryg" giesen
// SPDX-License-Identifier: MIT
// stb_dxt.h - v1.12 - DXT1/DXT5 compressor
#ifndef STB_INCLUDE_STB_DXT_H
#define STB_INCLUDE_STB_DXT_H
#ifdef __cplusplus
extern "C" {
#endif
#ifdef STB_DXT_STATIC
#define STBDDEF static
#else
#define STBDDEF extern
#endif
// compression mode (bitflags)
#define STB_DXT_NORMAL 0
#define STB_DXT_DITHER 1 // use dithering. was always dubious, now deprecated. does nothing!
#define STB_DXT_HIGHQUAL \
2 // high quality mode, does two refinement steps instead of 1. ~30-40% slower.
STBDDEF void stb_compress_bc1_block(unsigned char* dest,
const unsigned char* src_rgba_four_bytes_per_pixel, int alpha,
int mode);
STBDDEF void stb_compress_bc3_block(unsigned char* dest, const unsigned char* src, int mode);
#define STB_COMPRESS_DXT_BLOCK
#ifdef __cplusplus
}
#endif
#endif // STB_INCLUDE_STB_DXT_H
+9 -2
View File
@@ -43,7 +43,7 @@ if (MSVC)
/Zo
/permissive-
/EHsc
/std:c++latest
/std:c++20
/utf-8
/volatile:iso
/Zc:externConstexpr
@@ -51,8 +51,10 @@ if (MSVC)
/Zc:throwingNew
/GT
# Modules
/experimental:module- # Disable module support explicitly due to conflicts with precompiled headers
# External headers diagnostics
/experimental:external # Enables the external headers options. This option isn't required in Visual Studio 2019 version 16.10 and later
/external:anglebrackets # Treats all headers included by #include <header>, where the header file is enclosed in angle brackets (< >), as external headers
/external:W0 # Sets the default warning level to 0 for external headers, effectively turning off warnings for external headers
@@ -195,3 +197,8 @@ endif()
if (ENABLE_WEB_SERVICE)
add_subdirectory(web_service)
endif()
if (ANDROID)
add_subdirectory(android/app/src/main/jni)
target_include_directories(yuzu-android PRIVATE android/app/src/main)
endif()
+65
View File
@@ -0,0 +1,65 @@
# SPDX-FileCopyrightText: 2023 yuzu Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later
# Built application files
*.apk
*.ap_
# Files for the ART/Dalvik VM
*.dex
# Java class files
*.class
# Generated files
bin/
gen/
out/
# Gradle files
.gradle/
build/
# Local configuration file (sdk path, etc)
local.properties
# Proguard folder generated by Eclipse
proguard/
# Log Files
*.log
# Android Studio Navigation editor temp files
.navigation/
# Android Studio captures folder
captures/
# IntelliJ
*.iml
.idea/
# Keystore files
# Uncomment the following line if you do not want to check your keystore files in.
#*.jks
# External native build folder generated in Android Studio 2.2 and later
.externalNativeBuild
# CXX compile cache
app/.cxx
# Google Services (e.g. APIs or Firebase)
google-services.json
# Freeline
freeline.py
freeline/
freeline_project_description.json
# fastlane
fastlane/report.xml
fastlane/Preview.html
fastlane/screenshots
fastlane/test_output
fastlane/readme.md
+249
View File
@@ -0,0 +1,249 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
import android.annotation.SuppressLint
plugins {
id("com.android.application")
id("org.jetbrains.kotlin.android")
id("kotlin-parcelize")
kotlin("plugin.serialization") version "1.8.21"
}
/**
* Use the number of seconds/10 since Jan 1 2016 as the versionCode.
* This lets us upload a new build at most every 10 seconds for the
* next 680 years.
*/
val autoVersion = (((System.currentTimeMillis() / 1000) - 1451606400) / 10).toInt()
@Suppress("UnstableApiUsage")
android {
namespace = "org.yuzu.yuzu_emu"
compileSdkVersion = "android-33"
ndkVersion = "25.2.9519653"
buildFeatures {
viewBinding = true
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = "17"
}
packaging {
// This is necessary for libadrenotools custom driver loading
jniLibs.useLegacyPackaging = true
}
lint {
// This is important as it will run lint but not abort on error
// Lint has some overly obnoxious "errors" that should really be warnings
abortOnError = false
//Uncomment disable lines for test builds...
//disable 'MissingTranslation'bin
//disable 'ExtraTranslation'
}
defaultConfig {
// TODO If this is ever modified, change application_id in strings.xml
applicationId = "org.yuzu.yuzu_emu"
minSdk = 30
targetSdk = 33
versionCode = 1
versionName = getGitVersion()
ndk {
@SuppressLint("ChromeOsAbiSupport")
abiFilters += listOf("arm64-v8a")
}
buildConfigField("String", "GIT_HASH", "\"${getGitHash()}\"")
buildConfigField("String", "BRANCH", "\"${getBranch()}\"")
}
// Define build types, which are orthogonal to product flavors.
buildTypes {
// Signed by release key, allowing for upload to Play Store.
release {
signingConfig = signingConfigs.getByName("debug")
isMinifyEnabled = true
isDebuggable = false
proguardFiles(
getDefaultProguardFile("proguard-android.txt"),
"proguard-rules.pro"
)
}
register("relWithVersionCode") {
signingConfig = signingConfigs.getByName("debug")
isMinifyEnabled = true
isDebuggable = false
proguardFiles(
getDefaultProguardFile("proguard-android.txt"),
"proguard-rules.pro"
)
}
// builds a release build that doesn't need signing
// Attaches 'debug' suffix to version and package name, allowing installation alongside the release build.
register("relWithDebInfo") {
signingConfig = signingConfigs.getByName("debug")
isMinifyEnabled = true
isDebuggable = true
proguardFiles(
getDefaultProguardFile("proguard-android.txt"),
"proguard-rules.pro"
)
versionNameSuffix = "-debug"
isJniDebuggable = true
}
// Signed by debug key disallowing distribution on Play Store.
// Attaches 'debug' suffix to version and package name, allowing installation alongside the release build.
debug {
isDebuggable = true
isJniDebuggable = true
versionNameSuffix = "-debug"
}
}
flavorDimensions.add("version")
productFlavors {
create("mainline") {
dimension = "version"
buildConfigField("Boolean", "PREMIUM", "false")
}
create("ea") {
dimension = "version"
buildConfigField("Boolean", "PREMIUM", "true")
applicationIdSuffix = ".ea"
}
}
externalNativeBuild {
cmake {
version = "3.22.1"
path = file("../../../CMakeLists.txt")
}
}
defaultConfig {
externalNativeBuild {
cmake {
arguments(
"-DENABLE_QT=0", // Don't use QT
"-DENABLE_SDL2=0", // Don't use SDL
"-DENABLE_WEB_SERVICE=0", // Don't use telemetry
"-DBUNDLE_SPEEX=ON",
"-DANDROID_ARM_NEON=true", // cryptopp requires Neon to work
"-DYUZU_USE_BUNDLED_VCPKG=ON",
"-DYUZU_USE_BUNDLED_FFMPEG=ON",
"-DYUZU_ENABLE_LTO=ON"
)
abiFilters("arm64-v8a", "x86_64")
}
}
}
}
dependencies {
implementation("androidx.core:core-ktx:1.10.1")
implementation("androidx.appcompat:appcompat:1.6.1")
implementation("androidx.recyclerview:recyclerview:1.3.0")
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
implementation("androidx.fragment:fragment-ktx:1.5.7")
implementation("androidx.documentfile:documentfile:1.0.1")
implementation("com.google.android.material:material:1.9.0")
implementation("androidx.preference:preference:1.2.0")
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.1")
implementation("io.coil-kt:coil:2.2.2")
implementation("androidx.core:core-splashscreen:1.0.1")
implementation("androidx.window:window:1.0.0")
implementation("org.ini4j:ini4j:0.5.4")
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")
implementation("androidx.navigation:navigation-fragment-ktx:2.5.3")
implementation("androidx.navigation:navigation-ui-ktx:2.5.3")
implementation("info.debatty:java-string-similarity:2.0.0")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.0")
}
fun getGitVersion(): String {
var versionName = "0.0"
try {
versionName = ProcessBuilder("git", "describe", "--always", "--long")
.directory(project.rootDir)
.redirectOutput(ProcessBuilder.Redirect.PIPE)
.redirectError(ProcessBuilder.Redirect.PIPE)
.start().inputStream.bufferedReader().use { it.readText() }
.trim()
.replace(Regex("(-0)?-[^-]+$"), "")
} catch (e: Exception) {
logger.error("Cannot find git, defaulting to dummy version number")
}
if (System.getenv("GITHUB_ACTIONS") != null) {
val gitTag = System.getenv("GIT_TAG_NAME")
versionName = gitTag ?: versionName
}
return versionName
}
fun getGitHash(): String {
try {
val processBuilder = ProcessBuilder("git", "rev-parse", "--short", "HEAD")
processBuilder.directory(project.rootDir)
val process = processBuilder.start()
val inputStream = process.inputStream
val errorStream = process.errorStream
process.waitFor()
return if (process.exitValue() == 0) {
inputStream.bufferedReader()
.use { it.readText().trim() } // return the value of gitHash
} else {
val errorMessage = errorStream.bufferedReader().use { it.readText().trim() }
logger.error("Error running git command: $errorMessage")
"dummy-hash" // return a dummy hash value in case of an error
}
} catch (e: Exception) {
logger.error("$e: Cannot find git, defaulting to dummy build hash")
return "dummy-hash" // return a dummy hash value in case of an error
}
}
fun getBranch(): String {
try {
val processBuilder = ProcessBuilder("git", "rev-parse", "--abbrev-ref", "HEAD")
processBuilder.directory(project.rootDir)
val process = processBuilder.start()
val inputStream = process.inputStream
val errorStream = process.errorStream
process.waitFor()
return if (process.exitValue() == 0) {
inputStream.bufferedReader()
.use { it.readText().trim() } // return the value of gitHash
} else {
val errorMessage = errorStream.bufferedReader().use { it.readText().trim() }
logger.error("Error running git command: $errorMessage")
"dummy-hash" // return a dummy hash value in case of an error
}
} catch (e: Exception) {
logger.error("$e: Cannot find git, defaulting to dummy build hash")
return "dummy-hash" // return a dummy hash value in case of an error
}
}
+24
View File
@@ -0,0 +1,24 @@
# SPDX-FileCopyrightText: 2023 yuzu Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later
# To get usable stack traces
-dontobfuscate
# Prevents crashing when using Wini
-keep class org.ini4j.spi.IniParser
-keep class org.ini4j.spi.IniBuilder
-keep class org.ini4j.spi.IniFormatter
# Suppress warnings for R8
-dontwarn org.bouncycastle.jsse.BCSSLParameters
-dontwarn org.bouncycastle.jsse.BCSSLSocket
-dontwarn org.bouncycastle.jsse.provider.BouncyCastleJsseProvider
-dontwarn org.conscrypt.Conscrypt$Version
-dontwarn org.conscrypt.Conscrypt
-dontwarn org.conscrypt.ConscryptHostnameVerifier
-dontwarn org.openjsse.javax.net.ssl.SSLParameters
-dontwarn org.openjsse.javax.net.ssl.SSLSocket
-dontwarn org.openjsse.net.ssl.OpenJSSE
-dontwarn java.beans.Introspector
-dontwarn java.beans.VetoableChangeListener
-dontwarn java.beans.VetoableChangeSupport
@@ -0,0 +1,22 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="200dp"
android:height="200dp"
android:viewportWidth="500"
android:viewportHeight="500">
<path
android:fillColor="#C6C6C6"
android:fillType="nonZero"
android:pathData="M262.66,175.11L262.66,375.05C318.54,375.05 363.85,330.29 363.85,275.08C363.85,219.87 318.54,175.11 262.66,175.11M282.43,197.01C318.67,206 344.09,238.19 344.09,275.11C344.09,312.03 318.67,344.22 282.43,353.2L282.43,197.01"
android:strokeWidth="1.46"
android:strokeColor="#00000000"
android:strokeLineCap="butt"
android:strokeLineJoin="miter" />
<path
android:fillColor="#FFDC00"
android:fillType="nonZero"
android:pathData="M237.31,125.11C181.43,125.11 136.12,169.87 136.12,225.08C136.12,280.29 181.43,325.05 237.31,325.05ZM217.57,147.01L217.57,303.2C189.11,296.16 166.67,274.54 158.84,246.6C151.01,218.65 159,188.71 179.75,168.21C190.16,157.86 203.24,150.53 217.57,147.01"
android:strokeWidth="1.46"
android:strokeColor="#00000000"
android:strokeLineCap="butt"
android:strokeLineJoin="miter" />
</vector>
@@ -0,0 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="155.3dp"
android:height="172.55dp"
android:viewportWidth="155.3"
android:viewportHeight="172.55">
<path
android:fillColor="#C6C6C6"
android:pathData="M86.28,34.51v138a69,69 0,0 0,0 -138M99.76,49.63a55.57,55.57 0,0 1,0 107.8V49.63" />
<path
android:fillColor="#FFDC00"
android:pathData="M69,0a69,69 0,0 0,0 138ZM55.54,15.12v107.8A55.55,55.55 0,0 1,29.75 29.75,55.1 55.1,0 0,1 55.54,15.12" />
</vector>
@@ -0,0 +1,24 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="340.97dp"
android:height="389.85dp"
android:viewportWidth="340.97"
android:viewportHeight="389.85">
<path
android:fillColor="?attr/colorOnSurface"
android:pathData="M341,268.68v73c0,14.5 -2.24,25.24 -6.83,32.82 -5.92,10.15 -16.21,15.32 -30.54,15.32S279,384.61 273,374.27c-4.56,-7.64 -6.8,-18.42 -6.8,-32.92V268.68a4.52,4.52 0,0 1,4.51 -4.51H273a4.5,4.5 0,0 1,4.5 4.51v72.5c0,33.53 14.88,37.4 26.07,37.4 12.14,0 26.08,-4.17 26.08,-36.71V268.68a4.52,4.52 0,0 1,4.52 -4.51h2.27A4.5,4.5 0,0 1,341 268.68Z" />
<path
android:fillColor="?attr/colorOnSurface"
android:pathData="M246.49,389.85H178.6c-2.35,0 -4.72,-1.88 -4.72,-6.08a8.28,8.28 0,0 1,1.33 -4.48l60.33,-104.47H186a4.51,4.51 0,0 1,-4.51 -4.51v-1.58a4.51,4.51 0,0 1,4.48 -4.51h0.8c58.69,-0.11 59.12,0 59.67,0.07a5.19,5.19 0,0 1,4 5.8,8.69 8.69,0 0,1 -1.33,3.76l-60.6,104.77h58a4.51,4.51 0,0 1,4.51 4.51v2.21A4.51,4.51 0,0 1,246.49 389.85Z" />
<path
android:fillColor="?attr/colorOnSurface"
android:pathData="M73.6,268.68v82.06c0,26 -11.8,38.44 -37.12,39.09h-0.12a4.51,4.51 0,0 1,-4.51 -4.51V383a4.51,4.51 0,0 1,4.48 -4.5c18.49,-0.15 26,-8.23 26,-27.9v-2.37A32.34,32.34 0,0 1,59 351.46c-6.39,5.5 -14.5,8.29 -24.07,8.29C12.09,359.75 0,347.34 0,323.86V268.68a4.52,4.52 0,0 1,4.51 -4.51H6.73a4.52,4.52 0,0 1,4.5 4.51v55c0,7.6 1.82,14.22 5,18.18 3.57,4.56 9.17,6.49 18.75,6.49 10.13,0 17.32,-3.76 22,-11.5 3.61,-5.92 5.43,-13.66 5.43,-23V268.68a4.52,4.52 0,0 1,4.51 -4.51h2.22A4.52,4.52 0,0 1,73.6 268.68Z" />
<path
android:fillColor="?attr/colorOnSurface"
android:pathData="M163.27,268.68v73c0,14.5 -2.24,25.24 -6.84,32.82 -5.92,10.15 -16.2,15.32 -30.53,15.32s-24.62,-5.23 -30.58,-15.57c-4.56,-7.64 -6.79,-18.42 -6.79,-32.92V268.68A4.51,4.51 0,0 1,93 264.17h2.28a4.51,4.51 0,0 1,4.51 4.51v72.5c0,33.53 14.88,37.4 26.07,37.4 12.14,0 26.08,-4.17 26.08,-36.71V268.68a4.51,4.51 0,0 1,4.51 -4.51h2.27A4.51,4.51 0,0 1,163.27 268.68Z" />
<path
android:fillColor="#C6C6C6"
android:pathData="M181.2,42.83V214.17a85.67,85.67 0,0 0,0 -171.34M197.93,61.6a69,69 0,0 1,0 133.8V61.6" />
<path
android:fillColor="#FFDC00"
android:pathData="M159.78,0a85.67,85.67 0,1 0,0 171.33ZM143.05,18.77v133.8A69,69 0,0 1,111 36.92a68.47,68.47 0,0 1,32 -18.15" />
</vector>
@@ -0,0 +1,85 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
SPDX-FileCopyrightText: 2023 yuzu Emulator Project
SPDX-License-Identifier: GPL-3.0-or-later
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-feature android:name="android.hardware.touchscreen" android:required="false" />
<uses-feature android:name="android.hardware.gamepad" android:required="false" />
<uses-feature android:name="android.software.leanback" android:required="false" />
<uses-feature android:name="android.hardware.vulkan.version" android:version="0x401000" android:required="true" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.NFC" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<application
android:name="org.yuzu.yuzu_emu.YuzuApplication"
android:label="@string/app_name"
android:icon="@drawable/ic_launcher"
android:allowBackup="true"
android:hasFragileUserData="true"
android:supportsRtl="true"
android:isGame="true"
android:banner="@drawable/tv_banner"
android:extractNativeLibs="true"
android:fullBackupContent="@xml/data_extraction_rules"
android:dataExtractionRules="@xml/data_extraction_rules_api_31"
android:enableOnBackInvokedCallback="true">
<activity
android:name="org.yuzu.yuzu_emu.ui.main.MainActivity"
android:exported="true"
android:theme="@style/Theme.Yuzu.Splash.Main">
<!-- This intentfilter marks this Activity as the one that gets launched from Home screen. -->
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<category android:name="android.intent.category.LEANBACK_LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name="org.yuzu.yuzu_emu.features.settings.ui.SettingsActivity"
android:theme="@style/Theme.Yuzu.Main"
android:label="@string/preferences_settings"/>
<activity
android:name="org.yuzu.yuzu_emu.activities.EmulationActivity"
android:theme="@style/Theme.Yuzu.Main"
android:launchMode="singleTop"
android:screenOrientation="userLandscape"
android:exported="true">
<intent-filter>
<action android:name="android.nfc.action.TECH_DISCOVERED" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="application/octet-stream" />
</intent-filter>
<meta-data
android:name="android.nfc.action.TECH_DISCOVERED"
android:resource="@xml/nfc_tech_filter" />
</activity>
<service android:name="org.yuzu.yuzu_emu.utils.ForegroundService"/>
<provider
android:name=".features.DocumentProvider"
android:authorities="${applicationId}.user"
android:grantUriPermissions="true"
android:exported="true"
android:permission="android.permission.MANAGE_DOCUMENTS">
<intent-filter>
<action android:name="android.content.action.DOCUMENTS_PROVIDER" />
</intent-filter>
</provider>
</application>
</manifest>
@@ -0,0 +1,508 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu
import android.app.Dialog
import android.content.DialogInterface
import android.os.Bundle
import android.text.Html
import android.text.method.LinkMovementMethod
import android.view.Surface
import android.view.View
import android.widget.TextView
import androidx.annotation.Keep
import androidx.fragment.app.DialogFragment
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import org.yuzu.yuzu_emu.YuzuApplication.Companion.appContext
import org.yuzu.yuzu_emu.activities.EmulationActivity
import org.yuzu.yuzu_emu.utils.DocumentsTree.Companion.isNativePath
import org.yuzu.yuzu_emu.utils.FileUtil.getFileSize
import org.yuzu.yuzu_emu.utils.FileUtil.openContentUri
import org.yuzu.yuzu_emu.utils.Log.error
import org.yuzu.yuzu_emu.utils.Log.verbose
import org.yuzu.yuzu_emu.utils.Log.warning
import org.yuzu.yuzu_emu.utils.SerializableHelper.serializable
import java.lang.ref.WeakReference
/**
* Class which contains methods that interact
* with the native side of the Yuzu code.
*/
object NativeLibrary {
/**
* Default controller id for each device
*/
const val Player1Device = 0
const val Player2Device = 1
const val Player3Device = 2
const val Player4Device = 3
const val Player5Device = 4
const val Player6Device = 5
const val Player7Device = 6
const val Player8Device = 7
const val ConsoleDevice = 8
/**
* Controller type for each device
*/
const val ProController = 3
const val Handheld = 4
const val JoyconDual = 5
const val JoyconLeft = 6
const val JoyconRight = 7
const val GameCube = 8
const val Pokeball = 9
const val NES = 10
const val SNES = 11
const val N64 = 12
const val SegaGenesis = 13
@JvmField
var sEmulationActivity = WeakReference<EmulationActivity?>(null)
init {
try {
System.loadLibrary("yuzu-android")
} catch (ex: UnsatisfiedLinkError) {
error("[NativeLibrary] $ex")
}
}
@Keep
@JvmStatic
fun openContentUri(path: String?, openmode: String?): Int {
return if (isNativePath(path!!)) {
YuzuApplication.documentsTree!!.openContentUri(path, openmode)
} else openContentUri(appContext, path, openmode)
}
@Keep
@JvmStatic
fun getSize(path: String?): Long {
return if (isNativePath(path!!)) {
YuzuApplication.documentsTree!!.getFileSize(path)
} else getFileSize(appContext, path)
}
/**
* Returns true if pro controller isn't available and handheld is
*/
external fun isHandheldOnly(): Boolean
/**
* Changes controller type for a specific device.
*
* @param Device The input descriptor of the gamepad.
* @param Type The NpadStyleIndex of the gamepad.
*/
external fun setDeviceType(Device: Int, Type: Int): Boolean
/**
* Handles event when a gamepad is connected.
*
* @param Device The input descriptor of the gamepad.
*/
external fun onGamePadConnectEvent(Device: Int): Boolean
/**
* Handles event when a gamepad is disconnected.
*
* @param Device The input descriptor of the gamepad.
*/
external fun onGamePadDisconnectEvent(Device: Int): Boolean
/**
* Handles button press events for a gamepad.
*
* @param Device The input descriptor of the gamepad.
* @param Button Key code identifying which button was pressed.
* @param Action Mask identifying which action is happening (button pressed down, or button released).
* @return If we handled the button press.
*/
external fun onGamePadButtonEvent(Device: Int, Button: Int, Action: Int): Boolean
/**
* Handles joystick movement events.
*
* @param Device The device ID of the gamepad.
* @param Axis The axis ID
* @param x_axis The value of the x-axis represented by the given ID.
* @param y_axis The value of the y-axis represented by the given ID.
*/
external fun onGamePadJoystickEvent(
Device: Int,
Axis: Int,
x_axis: Float,
y_axis: Float
): Boolean
/**
* Handles motion events.
*
* @param delta_timestamp The finger id corresponding to this event
* @param gyro_x,gyro_y,gyro_z The value of the accelerometer sensor.
* @param accel_x,accel_y,accel_z The value of the y-axis
*/
external fun onGamePadMotionEvent(
Device: Int,
delta_timestamp: Long,
gyro_x: Float,
gyro_y: Float,
gyro_z: Float,
accel_x: Float,
accel_y: Float,
accel_z: Float
): Boolean
/**
* Signals and load a nfc tag
*
* @param data Byte array containing all the data from a nfc tag
*/
external fun onReadNfcTag(data: ByteArray?): Boolean
/**
* Removes current loaded nfc tag
*/
external fun onRemoveNfcTag(): Boolean
/**
* Handles touch press events.
*
* @param finger_id The finger id corresponding to this event
* @param x_axis The value of the x-axis.
* @param y_axis The value of the y-axis.
*/
external fun onTouchPressed(finger_id: Int, x_axis: Float, y_axis: Float)
/**
* Handles touch movement.
*
* @param x_axis The value of the instantaneous x-axis.
* @param y_axis The value of the instantaneous y-axis.
*/
external fun onTouchMoved(finger_id: Int, x_axis: Float, y_axis: Float)
/**
* Handles touch release events.
*
* @param finger_id The finger id corresponding to this event
*/
external fun onTouchReleased(finger_id: Int)
external fun reloadSettings()
external fun getUserSetting(gameID: String?, Section: String?, Key: String?): String?
external fun setUserSetting(gameID: String?, Section: String?, Key: String?, Value: String?)
external fun initGameIni(gameID: String?)
/**
* Gets the embedded icon within the given ROM.
*
* @param filename the file path to the ROM.
* @return a byte array containing the JPEG data for the icon.
*/
external fun getIcon(filename: String): ByteArray
/**
* Gets the embedded title of the given ISO/ROM.
*
* @param filename The file path to the ISO/ROM.
* @return the embedded title of the ISO/ROM.
*/
external fun getTitle(filename: String): String
external fun getDescription(filename: String): String
external fun getGameId(filename: String): String
external fun getRegions(filename: String): String
external fun getCompany(filename: String): String
external fun setAppDirectory(directory: String)
external fun initializeGpuDriver(
hookLibDir: String?,
customDriverDir: String?,
customDriverName: String?,
fileRedirectDir: String?
)
external fun reloadKeys(): Boolean
external fun initializeEmulation()
external fun defaultCPUCore(): Int
/**
* Begins emulation.
*/
external fun run(path: String?)
/**
* Begins emulation from the specified savestate.
*/
external fun run(path: String?, savestatePath: String?, deleteSavestate: Boolean)
// Surface Handling
external fun surfaceChanged(surf: Surface?)
external fun surfaceDestroyed()
/**
* Unpauses emulation from a paused state.
*/
external fun unPauseEmulation()
/**
* Pauses emulation.
*/
external fun pauseEmulation()
/**
* Stops emulation.
*/
external fun stopEmulation()
/**
* Resets the in-memory ROM metadata cache.
*/
external fun resetRomMetadata()
/**
* Returns true if emulation is running (or is paused).
*/
external fun isRunning(): Boolean
/**
* Returns the performance stats for the current game
*/
external fun getPerfStats(): DoubleArray
/**
* Notifies the core emulation that the orientation has changed.
*/
external fun notifyOrientationChange(layout_option: Int, rotation: Int)
enum class CoreError {
ErrorSystemFiles,
ErrorSavestate,
ErrorUnknown
}
private var coreErrorAlertResult = false
private val coreErrorAlertLock = Object()
class CoreErrorDialogFragment : DialogFragment() {
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val title = requireArguments().serializable<String>("title")
val message = requireArguments().serializable<String>("message")
return MaterialAlertDialogBuilder(requireActivity())
.setTitle(title)
.setMessage(message)
.setPositiveButton(R.string.continue_button, null)
.setNegativeButton(R.string.abort_button) { _: DialogInterface?, _: Int ->
coreErrorAlertResult = false
synchronized(coreErrorAlertLock) { coreErrorAlertLock.notify() }
}
.create()
}
override fun onDismiss(dialog: DialogInterface) {
coreErrorAlertResult = true
synchronized(coreErrorAlertLock) { coreErrorAlertLock.notify() }
}
companion object {
fun newInstance(title: String?, message: String?): CoreErrorDialogFragment {
val frag = CoreErrorDialogFragment()
val args = Bundle()
args.putString("title", title)
args.putString("message", message)
frag.arguments = args
return frag
}
}
}
private fun onCoreErrorImpl(title: String, message: String) {
val emulationActivity = sEmulationActivity.get()
if (emulationActivity == null) {
error("[NativeLibrary] EmulationActivity not present")
return
}
val fragment = CoreErrorDialogFragment.newInstance(title, message)
fragment.show(emulationActivity.supportFragmentManager, "coreError")
}
/**
* Handles a core error.
*
* @return true: continue; false: abort
*/
fun onCoreError(error: CoreError?, details: String): Boolean {
val emulationActivity = sEmulationActivity.get()
if (emulationActivity == null) {
error("[NativeLibrary] EmulationActivity not present")
return false
}
val title: String
val message: String
when (error) {
CoreError.ErrorSystemFiles -> {
title = emulationActivity.getString(R.string.system_archive_not_found)
message = emulationActivity.getString(
R.string.system_archive_not_found_message,
details.ifEmpty { emulationActivity.getString(R.string.system_archive_general) }
)
}
CoreError.ErrorSavestate -> {
title = emulationActivity.getString(R.string.save_load_error)
message = details
}
CoreError.ErrorUnknown -> {
title = emulationActivity.getString(R.string.fatal_error)
message = emulationActivity.getString(R.string.fatal_error_message)
}
else -> {
return true
}
}
// Show the AlertDialog on the main thread.
emulationActivity.runOnUiThread(Runnable { onCoreErrorImpl(title, message) })
// Wait for the lock to notify that it is complete.
synchronized(coreErrorAlertLock) { coreErrorAlertLock.wait() }
return coreErrorAlertResult
}
@Keep
@JvmStatic
fun exitEmulationActivity(resultCode: Int) {
val Success = 0
val ErrorNotInitialized = 1
val ErrorGetLoader = 2
val ErrorSystemFiles = 3
val ErrorSharedFont = 4
val ErrorVideoCore = 5
val ErrorUnknown = 6
val ErrorLoader = 7
val captionId: Int
var descriptionId: Int
when (resultCode) {
ErrorVideoCore -> {
captionId = R.string.loader_error_video_core
descriptionId = R.string.loader_error_video_core_description
}
else -> {
captionId = R.string.loader_error_encrypted
descriptionId = R.string.loader_error_encrypted_roms_description
if (!reloadKeys()) {
descriptionId = R.string.loader_error_encrypted_keys_description
}
}
}
val emulationActivity = sEmulationActivity.get()
if (emulationActivity == null) {
warning("[NativeLibrary] EmulationActivity is null, can't exit.")
return
}
val builder = MaterialAlertDialogBuilder(emulationActivity)
.setTitle(captionId)
.setMessage(
Html.fromHtml(
emulationActivity.getString(descriptionId),
Html.FROM_HTML_MODE_LEGACY
)
)
.setPositiveButton(android.R.string.ok) { _: DialogInterface?, _: Int -> emulationActivity.finish() }
.setOnDismissListener { emulationActivity.finish() }
emulationActivity.runOnUiThread {
val alert = builder.create()
alert.show()
(alert.findViewById<View>(android.R.id.message) as TextView).movementMethod =
LinkMovementMethod.getInstance()
}
}
fun setEmulationActivity(emulationActivity: EmulationActivity?) {
verbose("[NativeLibrary] Registering EmulationActivity.")
sEmulationActivity = WeakReference(emulationActivity)
}
fun clearEmulationActivity() {
verbose("[NativeLibrary] Unregistering EmulationActivity.")
sEmulationActivity.clear()
}
/**
* Logs the Yuzu version, Android version and, CPU.
*/
external fun logDeviceInfo()
/**
* Submits inline keyboard text. Called on input for buttons that result text.
* @param text Text to submit to the inline software keyboard implementation.
*/
external fun submitInlineKeyboardText(text: String?)
/**
* Submits inline keyboard input. Used to indicate keys pressed that are not text.
* @param key_code Android Key Code associated with the keyboard input.
*/
external fun submitInlineKeyboardInput(key_code: Int)
/**
* Button type for use in onTouchEvent
*/
object ButtonType {
const val BUTTON_A = 0
const val BUTTON_B = 1
const val BUTTON_X = 2
const val BUTTON_Y = 3
const val STICK_L = 4
const val STICK_R = 5
const val TRIGGER_L = 6
const val TRIGGER_R = 7
const val TRIGGER_ZL = 8
const val TRIGGER_ZR = 9
const val BUTTON_PLUS = 10
const val BUTTON_MINUS = 11
const val DPAD_LEFT = 12
const val DPAD_UP = 13
const val DPAD_RIGHT = 14
const val DPAD_DOWN = 15
const val BUTTON_SL = 16
const val BUTTON_SR = 17
const val BUTTON_HOME = 18
const val BUTTON_CAPTURE = 19
}
/**
* Stick type for use in onTouchEvent
*/
object StickType {
const val STICK_L = 0
const val STICK_R = 1
}
/**
* Button states
*/
object ButtonState {
const val RELEASED = 0
const val PRESSED = 1
}
}
@@ -0,0 +1,61 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu
import android.app.Application
import android.app.NotificationChannel
import android.app.NotificationManager
import android.content.Context
import org.yuzu.yuzu_emu.utils.DirectoryInitialization
import org.yuzu.yuzu_emu.utils.DocumentsTree
import org.yuzu.yuzu_emu.utils.GpuDriverHelper
import java.io.File
fun Context.getPublicFilesDir() : File = getExternalFilesDir(null) ?: filesDir
class YuzuApplication : Application() {
private fun createNotificationChannels() {
val emulationChannel = NotificationChannel(
getString(R.string.emulation_notification_channel_id),
getString(R.string.emulation_notification_channel_name),
NotificationManager.IMPORTANCE_LOW
)
emulationChannel.description = getString(R.string.emulation_notification_channel_description)
emulationChannel.setSound(null, null)
emulationChannel.vibrationPattern = null
val noticeChannel = NotificationChannel(
getString(R.string.notice_notification_channel_id),
getString(R.string.notice_notification_channel_name),
NotificationManager.IMPORTANCE_HIGH
)
noticeChannel.description = getString(R.string.notice_notification_channel_description)
noticeChannel.setSound(null, null)
// Register the channel with the system; you can't change the importance
// or other notification behaviors after this
val notificationManager = getSystemService(NotificationManager::class.java)
notificationManager.createNotificationChannel(emulationChannel)
notificationManager.createNotificationChannel(noticeChannel)
}
override fun onCreate() {
super.onCreate()
application = this
documentsTree = DocumentsTree()
DirectoryInitialization.start(applicationContext)
GpuDriverHelper.initializeDriverParameters(applicationContext)
NativeLibrary.logDeviceInfo()
createNotificationChannels();
}
companion object {
var documentsTree: DocumentsTree? = null
lateinit var application: YuzuApplication
val appContext: Context
get() = application.applicationContext
}
}
@@ -0,0 +1,333 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.activities
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.content.res.Configuration
import android.graphics.Rect
import android.hardware.Sensor
import android.hardware.SensorEvent
import android.hardware.SensorEventListener
import android.hardware.SensorManager
import android.hardware.display.DisplayManager
import android.os.Bundle
import android.view.Display
import android.view.InputDevice
import android.view.KeyEvent
import android.view.MotionEvent
import android.view.Surface
import android.view.View
import android.view.inputmethod.InputMethodManager
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.getSystemService
import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.WindowInsetsControllerCompat
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import androidx.window.layout.WindowInfoTracker
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.features.settings.model.SettingsViewModel
import org.yuzu.yuzu_emu.fragments.EmulationFragment
import org.yuzu.yuzu_emu.model.Game
import org.yuzu.yuzu_emu.utils.ControllerMappingHelper
import org.yuzu.yuzu_emu.utils.EmulationMenuSettings
import org.yuzu.yuzu_emu.utils.ForegroundService
import org.yuzu.yuzu_emu.utils.InputHandler
import org.yuzu.yuzu_emu.utils.NfcReader
import org.yuzu.yuzu_emu.utils.SerializableHelper.parcelable
import org.yuzu.yuzu_emu.utils.ThemeHelper
import kotlin.math.roundToInt
class EmulationActivity : AppCompatActivity(), SensorEventListener {
private var controllerMappingHelper: ControllerMappingHelper? = null
var isActivityRecreated = false
private var emulationFragment: EmulationFragment? = null
private lateinit var nfcReader: NfcReader
private lateinit var inputHandler: InputHandler
private val gyro = FloatArray(3)
private val accel = FloatArray(3)
private var motionTimestamp: Long = 0
private var flipMotionOrientation: Boolean = false
private lateinit var game: Game
private val settingsViewModel: SettingsViewModel by viewModels()
override fun onDestroy() {
stopForegroundService(this)
super.onDestroy()
}
override fun onCreate(savedInstanceState: Bundle?) {
ThemeHelper.setTheme(this)
settingsViewModel.settings.loadSettings()
super.onCreate(savedInstanceState)
if (savedInstanceState == null) {
// Get params we were passed
game = intent.parcelable(EXTRA_SELECTED_GAME)!!
isActivityRecreated = false
} else {
isActivityRecreated = true
restoreState(savedInstanceState)
}
controllerMappingHelper = ControllerMappingHelper()
// Set these options now so that the SurfaceView the game renders into is the right size.
enableFullscreenImmersive()
setContentView(R.layout.activity_emulation)
window.decorView.setBackgroundColor(getColor(android.R.color.black))
// Find or create the EmulationFragment
emulationFragment =
supportFragmentManager.findFragmentById(R.id.frame_emulation_fragment) as EmulationFragment?
if (emulationFragment == null) {
emulationFragment = EmulationFragment.newInstance(game)
supportFragmentManager.beginTransaction()
.add(R.id.frame_emulation_fragment, emulationFragment!!)
.commit()
}
title = game.title
nfcReader = NfcReader(this)
nfcReader.initialize()
inputHandler = InputHandler()
inputHandler.initialize()
lifecycleScope.launch(Dispatchers.Main) {
lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
WindowInfoTracker.getOrCreate(this@EmulationActivity)
.windowLayoutInfo(this@EmulationActivity)
.collect { emulationFragment?.updateCurrentLayout(this@EmulationActivity, it) }
}
}
// Start a foreground service to prevent the app from getting killed in the background
val startIntent = Intent(this, ForegroundService::class.java)
startForegroundService(startIntent)
}
override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
if (event.action == KeyEvent.ACTION_DOWN) {
if (keyCode == KeyEvent.KEYCODE_ENTER) {
// Special case, we do not support multiline input, dismiss the keyboard.
val overlayView: View =
this.findViewById(R.id.surface_input_overlay)
val im =
overlayView.context.getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager
im.hideSoftInputFromWindow(overlayView.windowToken, 0)
} else {
val textChar = event.unicodeChar
if (textChar == 0) {
// No text, button input.
NativeLibrary.submitInlineKeyboardInput(keyCode)
} else {
// Text submitted.
NativeLibrary.submitInlineKeyboardText(textChar.toChar().toString())
}
}
}
return super.onKeyDown(keyCode, event)
}
override fun onResume() {
super.onResume()
nfcReader.startScanning()
startMotionSensorListener()
NativeLibrary.notifyOrientationChange(
EmulationMenuSettings.landscapeScreenLayout,
getAdjustedRotation()
)
}
override fun onPause() {
super.onPause()
nfcReader.stopScanning()
stopMotionSensorListener()
}
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
setIntent(intent)
nfcReader.onNewIntent(intent)
}
override fun onSaveInstanceState(outState: Bundle) {
outState.putParcelable(EXTRA_SELECTED_GAME, game)
super.onSaveInstanceState(outState)
}
override fun dispatchKeyEvent(event: KeyEvent): Boolean {
if (event.source and InputDevice.SOURCE_JOYSTICK != InputDevice.SOURCE_JOYSTICK &&
event.source and InputDevice.SOURCE_GAMEPAD != InputDevice.SOURCE_GAMEPAD
) {
return super.dispatchKeyEvent(event)
}
return inputHandler.dispatchKeyEvent(event)
}
override fun dispatchGenericMotionEvent(event: MotionEvent): Boolean {
if (event.source and InputDevice.SOURCE_JOYSTICK != InputDevice.SOURCE_JOYSTICK &&
event.source and InputDevice.SOURCE_GAMEPAD != InputDevice.SOURCE_GAMEPAD
) {
return super.dispatchGenericMotionEvent(event)
}
// Don't attempt to do anything if we are disconnecting a device.
if (event.actionMasked == MotionEvent.ACTION_CANCEL) {
return true
}
return inputHandler.dispatchGenericMotionEvent(event)
}
override fun onSensorChanged(event: SensorEvent) {
val rotation = this.display?.rotation
if (rotation == Surface.ROTATION_90) {
flipMotionOrientation = true
}
if (rotation == Surface.ROTATION_270) {
flipMotionOrientation = false
}
if (event.sensor.type == Sensor.TYPE_ACCELEROMETER) {
if (flipMotionOrientation) {
accel[0] = event.values[1] / SensorManager.GRAVITY_EARTH
accel[1] = -event.values[0] / SensorManager.GRAVITY_EARTH
} else {
accel[0] = -event.values[1] / SensorManager.GRAVITY_EARTH
accel[1] = event.values[0] / SensorManager.GRAVITY_EARTH
}
accel[2] = -event.values[2] / SensorManager.GRAVITY_EARTH
}
if (event.sensor.type == Sensor.TYPE_GYROSCOPE) {
// Investigate why sensor value is off by 6x
if (flipMotionOrientation) {
gyro[0] = -event.values[1] / 6.0f
gyro[1] = event.values[0] / 6.0f
} else {
gyro[0] = event.values[1] / 6.0f
gyro[1] = -event.values[0] / 6.0f
}
gyro[2] = event.values[2] / 6.0f
}
// Only update state on accelerometer data
if (event.sensor.type != Sensor.TYPE_ACCELEROMETER) {
return
}
val deltaTimestamp = (event.timestamp - motionTimestamp) / 1000
motionTimestamp = event.timestamp
NativeLibrary.onGamePadMotionEvent(
NativeLibrary.Player1Device,
deltaTimestamp,
gyro[0],
gyro[1],
gyro[2],
accel[0],
accel[1],
accel[2]
)
NativeLibrary.onGamePadMotionEvent(
NativeLibrary.ConsoleDevice,
deltaTimestamp,
gyro[0],
gyro[1],
gyro[2],
accel[0],
accel[1],
accel[2]
)
}
override fun onAccuracyChanged(sensor: Sensor, i: Int) {}
private fun getAdjustedRotation():Int {
val rotation = getSystemService<DisplayManager>()!!.getDisplay(Display.DEFAULT_DISPLAY).rotation
val config: Configuration = resources.configuration
if ((config.screenLayout and Configuration.SCREENLAYOUT_LONG_YES) != 0 ||
(config.screenLayout and Configuration.SCREENLAYOUT_LONG_NO) == 0) {
return rotation
}
when (rotation) {
Surface.ROTATION_0 -> return Surface.ROTATION_90
Surface.ROTATION_90 -> return Surface.ROTATION_0
Surface.ROTATION_180 -> return Surface.ROTATION_270
Surface.ROTATION_270 -> return Surface.ROTATION_180
}
return rotation
}
private fun restoreState(savedInstanceState: Bundle) {
game = savedInstanceState.parcelable(EXTRA_SELECTED_GAME)!!
}
private fun enableFullscreenImmersive() {
WindowCompat.setDecorFitsSystemWindows(window, false)
WindowInsetsControllerCompat(window, window.decorView).let { controller ->
controller.hide(WindowInsetsCompat.Type.systemBars())
controller.systemBarsBehavior =
WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
}
}
private fun startMotionSensorListener() {
val sensorManager = this.getSystemService(Context.SENSOR_SERVICE) as SensorManager
val gyroSensor = sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE)
val accelSensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)
sensorManager.registerListener(this, gyroSensor, SensorManager.SENSOR_DELAY_GAME)
sensorManager.registerListener(this, accelSensor, SensorManager.SENSOR_DELAY_GAME)
}
private fun stopMotionSensorListener() {
val sensorManager = this.getSystemService(Context.SENSOR_SERVICE) as SensorManager
val gyroSensor = sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE)
val accelSensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)
sensorManager.unregisterListener(this, gyroSensor)
sensorManager.unregisterListener(this, accelSensor)
}
companion object {
const val EXTRA_SELECTED_GAME = "SelectedGame"
fun launch(activity: AppCompatActivity, game: Game) {
val launcher = Intent(activity, EmulationActivity::class.java)
launcher.putExtra(EXTRA_SELECTED_GAME, game)
activity.startActivity(launcher)
}
fun stopForegroundService(activity: Activity) {
val startIntent = Intent(activity, ForegroundService::class.java)
startIntent.action = ForegroundService.ACTION_STOP
activity.startForegroundService(startIntent)
}
private fun areCoordinatesOutside(view: View?, x: Float, y: Float): Boolean {
if (view == null) {
return true
}
val viewBounds = Rect()
view.getGlobalVisibleRect(viewBounds)
return !viewBounds.contains(x.roundToInt(), y.roundToInt())
}
}
}
@@ -0,0 +1,134 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.adapters
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.net.Uri
import android.text.TextUtils
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.documentfile.provider.DocumentFile
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import androidx.preference.PreferenceManager
import androidx.recyclerview.widget.AsyncDifferConfig
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import coil.load
import kotlinx.coroutines.launch
import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.databinding.CardGameBinding
import org.yuzu.yuzu_emu.activities.EmulationActivity
import org.yuzu.yuzu_emu.model.Game
import org.yuzu.yuzu_emu.adapters.GameAdapter.GameViewHolder
import org.yuzu.yuzu_emu.model.GamesViewModel
class GameAdapter(private val activity: AppCompatActivity) :
ListAdapter<Game, GameViewHolder>(AsyncDifferConfig.Builder(DiffCallback()).build()),
View.OnClickListener {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): GameViewHolder {
// Create a new view.
val binding = CardGameBinding.inflate(LayoutInflater.from(parent.context), parent, false)
binding.cardGame.setOnClickListener(this)
// Use that view to create a ViewHolder.
return GameViewHolder(binding)
}
override fun onBindViewHolder(holder: GameViewHolder, position: Int) {
holder.bind(currentList[position])
}
override fun getItemCount(): Int = currentList.size
/**
* Launches the game that was clicked on.
*
* @param view The card representing the game the user wants to play.
*/
override fun onClick(view: View) {
val holder = view.tag as GameViewHolder
val gameExists = DocumentFile.fromSingleUri(YuzuApplication.appContext, Uri.parse(holder.game.path))?.exists() == true
if (!gameExists) {
Toast.makeText(
YuzuApplication.appContext,
R.string.loader_error_file_not_found,
Toast.LENGTH_LONG
).show()
ViewModelProvider(activity)[GamesViewModel::class.java].reloadGames(true)
return
}
val preferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
preferences.edit()
.putLong(
holder.game.keyLastPlayedTime,
System.currentTimeMillis()
)
.apply()
EmulationActivity.launch(activity, holder.game)
}
inner class GameViewHolder(val binding: CardGameBinding) :
RecyclerView.ViewHolder(binding.root) {
lateinit var game: Game
init {
binding.cardGame.tag = this
}
fun bind(game: Game) {
this.game = game
binding.imageGameScreen.scaleType = ImageView.ScaleType.CENTER_CROP
activity.lifecycleScope.launch {
val bitmap = decodeGameIcon(game.path)
binding.imageGameScreen.load(bitmap) {
error(R.drawable.default_icon)
}
}
binding.textGameTitle.text = game.title.replace("[\\t\\n\\r]+".toRegex(), " ")
binding.textGameTitle.postDelayed(
{
binding.textGameTitle.ellipsize = TextUtils.TruncateAt.MARQUEE
binding.textGameTitle.isSelected = true
},
3000
)
}
}
private class DiffCallback : DiffUtil.ItemCallback<Game>() {
override fun areItemsTheSame(oldItem: Game, newItem: Game): Boolean {
return oldItem.gameId == newItem.gameId
}
override fun areContentsTheSame(oldItem: Game, newItem: Game): Boolean {
return oldItem == newItem
}
}
private fun decodeGameIcon(uri: String): Bitmap? {
val data = NativeLibrary.getIcon(uri)
return BitmapFactory.decodeByteArray(
data,
0,
data.size,
BitmapFactory.Options()
)
}
}
@@ -0,0 +1,69 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.adapters
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import androidx.core.content.res.ResourcesCompat
import androidx.recyclerview.widget.RecyclerView
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.databinding.CardHomeOptionBinding
import org.yuzu.yuzu_emu.model.HomeSetting
class HomeSettingAdapter(private val activity: AppCompatActivity, var options: List<HomeSetting>) :
RecyclerView.Adapter<HomeSettingAdapter.HomeOptionViewHolder>(),
View.OnClickListener {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): HomeOptionViewHolder {
val binding =
CardHomeOptionBinding.inflate(LayoutInflater.from(parent.context), parent, false)
binding.root.setOnClickListener(this)
return HomeOptionViewHolder(binding)
}
override fun getItemCount(): Int {
return options.size
}
override fun onBindViewHolder(holder: HomeOptionViewHolder, position: Int) {
holder.bind(options[position])
}
override fun onClick(view: View) {
val holder = view.tag as HomeOptionViewHolder
holder.option.onClick.invoke()
}
inner class HomeOptionViewHolder(val binding: CardHomeOptionBinding) :
RecyclerView.ViewHolder(binding.root) {
lateinit var option: HomeSetting
init {
itemView.tag = this
}
fun bind(option: HomeSetting) {
this.option = option
binding.optionTitle.text = activity.resources.getString(option.titleId)
binding.optionDescription.text = activity.resources.getString(option.descriptionId)
binding.optionIcon.setImageDrawable(
ResourcesCompat.getDrawable(
activity.resources,
option.iconId,
activity.theme
)
)
when (option.titleId) {
R.string.get_early_access -> binding.optionLayout.background =
ContextCompat.getDrawable(
binding.optionCard.context,
R.drawable.premium_background
)
}
}
}
}
@@ -0,0 +1,54 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.adapters
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.RecyclerView.ViewHolder
import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding
import org.yuzu.yuzu_emu.fragments.LicenseBottomSheetDialogFragment
import org.yuzu.yuzu_emu.model.License
class LicenseAdapter(private val activity: AppCompatActivity, var licenses: List<License>) :
RecyclerView.Adapter<LicenseAdapter.LicenseViewHolder>(),
View.OnClickListener {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): LicenseViewHolder {
val binding =
ListItemSettingBinding.inflate(LayoutInflater.from(parent.context), parent, false)
binding.root.setOnClickListener(this)
return LicenseViewHolder(binding)
}
override fun getItemCount(): Int = licenses.size
override fun onBindViewHolder(holder: LicenseViewHolder, position: Int) {
holder.bind(licenses[position])
}
override fun onClick(view: View) {
val license = (view.tag as LicenseViewHolder).license
LicenseBottomSheetDialogFragment.newInstance(license)
.show(activity.supportFragmentManager, LicenseBottomSheetDialogFragment.TAG)
}
inner class LicenseViewHolder(val binding: ListItemSettingBinding) : ViewHolder(binding.root) {
lateinit var license: License
init {
itemView.tag = this
}
fun bind(license: License) {
this.license = license
val context = YuzuApplication.appContext
binding.textSettingName.text = context.getString(license.titleId)
binding.textSettingDescription.text = context.getString(license.descriptionId)
}
}
}
@@ -0,0 +1,70 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.adapters
import android.text.Html
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.res.ResourcesCompat
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.button.MaterialButton
import org.yuzu.yuzu_emu.databinding.PageSetupBinding
import org.yuzu.yuzu_emu.model.SetupPage
class SetupAdapter(val activity: AppCompatActivity, val pages: List<SetupPage>) :
RecyclerView.Adapter<SetupAdapter.SetupPageViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SetupPageViewHolder {
val binding = PageSetupBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return SetupPageViewHolder(binding)
}
override fun getItemCount(): Int = pages.size
override fun onBindViewHolder(holder: SetupPageViewHolder, position: Int) =
holder.bind(pages[position])
inner class SetupPageViewHolder(val binding: PageSetupBinding) :
RecyclerView.ViewHolder(binding.root) {
lateinit var page: SetupPage
init {
itemView.tag = this
}
fun bind(page: SetupPage) {
this.page = page
binding.icon.setImageDrawable(
ResourcesCompat.getDrawable(
activity.resources,
page.iconId,
activity.theme
)
)
binding.textTitle.text = activity.resources.getString(page.titleId)
binding.textDescription.text =
Html.fromHtml(activity.resources.getString(page.descriptionId), 0)
binding.buttonAction.apply {
text = activity.resources.getString(page.buttonTextId)
if (page.buttonIconId != 0) {
icon = ResourcesCompat.getDrawable(
activity.resources,
page.buttonIconId,
activity.theme
)
}
iconGravity =
if (page.leftAlignedIcon) {
MaterialButton.ICON_GRAVITY_START
} else {
MaterialButton.ICON_GRAVITY_END
}
setOnClickListener {
page.buttonAction.invoke()
}
}
}
}
}
@@ -0,0 +1,121 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.applets.keyboard
import android.content.Context
import android.os.Handler
import android.os.Looper
import android.view.KeyEvent
import android.view.View
import android.view.WindowInsets
import android.view.inputmethod.InputMethodManager
import androidx.annotation.Keep
import androidx.core.view.ViewCompat
import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.applets.keyboard.ui.KeyboardDialogFragment
import java.io.Serializable
@Keep
object SoftwareKeyboard {
lateinit var data: KeyboardData
val dataLock = Object()
private fun executeNormalImpl(config: KeyboardConfig) {
val emulationActivity = NativeLibrary.sEmulationActivity.get()
data = KeyboardData(SwkbdResult.Cancel.ordinal, "")
val fragment = KeyboardDialogFragment.newInstance(config)
fragment.show(emulationActivity!!.supportFragmentManager, KeyboardDialogFragment.TAG)
}
private fun executeInlineImpl(config: KeyboardConfig) {
val emulationActivity = NativeLibrary.sEmulationActivity.get()
val overlayView = emulationActivity!!.findViewById<View>(R.id.surface_input_overlay)
val im =
overlayView.context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
im.showSoftInput(overlayView, InputMethodManager.SHOW_FORCED)
// There isn't a good way to know that the IMM is dismissed, so poll every 500ms to submit inline keyboard result.
val handler = Handler(Looper.myLooper()!!)
val delayMs = 500
handler.postDelayed(object : Runnable {
override fun run() {
val insets = ViewCompat.getRootWindowInsets(overlayView)
val isKeyboardVisible = insets!!.isVisible(WindowInsets.Type.ime())
if (isKeyboardVisible) {
handler.postDelayed(this, delayMs.toLong())
return
}
// No longer visible, submit the result.
NativeLibrary.submitInlineKeyboardInput(KeyEvent.KEYCODE_ENTER)
}
}, delayMs.toLong())
}
@JvmStatic
fun executeNormal(config: KeyboardConfig): KeyboardData {
NativeLibrary.sEmulationActivity.get()!!.runOnUiThread { executeNormalImpl(config) }
synchronized(dataLock) {
dataLock.wait()
}
return data
}
@JvmStatic
fun executeInline(config: KeyboardConfig) {
NativeLibrary.sEmulationActivity.get()!!.runOnUiThread { executeInlineImpl(config) }
}
// Corresponds to Service::AM::Applets::SwkbdType
enum class SwkbdType {
Normal,
NumberPad,
Qwerty,
Unknown3,
Latin,
SimplifiedChinese,
TraditionalChinese,
Korean
}
// Corresponds to Service::AM::Applets::SwkbdPasswordMode
enum class SwkbdPasswordMode {
Disabled,
Enabled
}
// Corresponds to Service::AM::Applets::SwkbdResult
enum class SwkbdResult {
Ok,
Cancel
}
@Keep
data class KeyboardConfig(
var ok_text: String? = null,
var header_text: String? = null,
var sub_text: String? = null,
var guide_text: String? = null,
var initial_text: String? = null,
var left_optional_symbol_key: Short = 0,
var right_optional_symbol_key: Short = 0,
var max_text_length: Int = 0,
var min_text_length: Int = 0,
var initial_cursor_position: Int = 0,
var type: Int = 0,
var password_mode: Int = 0,
var text_draw_type: Int = 0,
var key_disable_flags: Int = 0,
var use_blur_background: Boolean = false,
var enable_backspace_button: Boolean = false,
var enable_return_button: Boolean = false,
var disable_cancel_button: Boolean = false
) : Serializable
// Corresponds to Frontend::KeyboardData
@Keep
data class KeyboardData(var result: Int, var text: String)
}
@@ -0,0 +1,100 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.applets.keyboard.ui
import android.app.Dialog
import android.content.DialogInterface
import android.os.Bundle
import android.text.InputFilter
import android.text.InputType
import androidx.fragment.app.DialogFragment
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.applets.keyboard.SoftwareKeyboard
import org.yuzu.yuzu_emu.applets.keyboard.SoftwareKeyboard.KeyboardConfig
import org.yuzu.yuzu_emu.databinding.DialogEditTextBinding
import org.yuzu.yuzu_emu.utils.SerializableHelper.serializable
class KeyboardDialogFragment : DialogFragment() {
private lateinit var binding: DialogEditTextBinding
private lateinit var config: KeyboardConfig
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
binding = DialogEditTextBinding.inflate(layoutInflater)
config = requireArguments().serializable(CONFIG)!!
// Set up the input
binding.editText.hint = config.initial_text
binding.editText.isSingleLine = !config.enable_return_button
binding.editText.filters =
arrayOf<InputFilter>(InputFilter.LengthFilter(config.max_text_length))
// Handle input type
var inputType: Int
when (config.type) {
SoftwareKeyboard.SwkbdType.Normal.ordinal,
SoftwareKeyboard.SwkbdType.Qwerty.ordinal,
SoftwareKeyboard.SwkbdType.Unknown3.ordinal,
SoftwareKeyboard.SwkbdType.Latin.ordinal,
SoftwareKeyboard.SwkbdType.SimplifiedChinese.ordinal,
SoftwareKeyboard.SwkbdType.TraditionalChinese.ordinal,
SoftwareKeyboard.SwkbdType.Korean.ordinal -> {
inputType = InputType.TYPE_CLASS_TEXT
if (config.password_mode == SoftwareKeyboard.SwkbdPasswordMode.Enabled.ordinal) {
inputType = inputType or InputType.TYPE_TEXT_VARIATION_PASSWORD
}
}
SoftwareKeyboard.SwkbdType.NumberPad.ordinal -> {
inputType = InputType.TYPE_CLASS_NUMBER
if (config.password_mode == SoftwareKeyboard.SwkbdPasswordMode.Enabled.ordinal) {
inputType = inputType or InputType.TYPE_NUMBER_VARIATION_PASSWORD
}
}
else -> {
inputType = InputType.TYPE_CLASS_TEXT
if (config.password_mode == SoftwareKeyboard.SwkbdPasswordMode.Enabled.ordinal) {
inputType = inputType or InputType.TYPE_TEXT_VARIATION_PASSWORD
}
}
}
binding.editText.inputType = inputType
val headerText =
config.header_text!!.ifEmpty { resources.getString(R.string.software_keyboard) }
val okText =
config.ok_text!!.ifEmpty { resources.getString(R.string.submit) }
return MaterialAlertDialogBuilder(requireContext())
.setTitle(headerText)
.setView(binding.root)
.setPositiveButton(okText) { _, _ ->
SoftwareKeyboard.data.result = SoftwareKeyboard.SwkbdResult.Ok.ordinal
SoftwareKeyboard.data.text = binding.editText.text.toString()
}
.setNegativeButton(resources.getString(android.R.string.cancel)) { _, _ ->
SoftwareKeyboard.data.result = SoftwareKeyboard.SwkbdResult.Cancel.ordinal
}
.create()
}
override fun onDismiss(dialog: DialogInterface) {
super.onDismiss(dialog)
synchronized(SoftwareKeyboard.dataLock) {
SoftwareKeyboard.dataLock.notifyAll()
}
}
companion object {
const val TAG = "KeyboardDialogFragment"
const val CONFIG = "keyboard_config"
fun newInstance(config: KeyboardConfig?): KeyboardDialogFragment {
val frag = KeyboardDialogFragment()
val args = Bundle()
args.putSerializable(CONFIG, config)
frag.arguments = args
return frag
}
}
}
@@ -0,0 +1,48 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.disk_shader_cache
import androidx.annotation.Keep
import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.disk_shader_cache.ui.ShaderProgressDialogFragment
@Keep
object DiskShaderCacheProgress {
val finishLock = Object()
private lateinit var fragment: ShaderProgressDialogFragment
private fun prepareDialog() {
val emulationActivity = NativeLibrary.sEmulationActivity.get()!!
emulationActivity.runOnUiThread {
fragment = ShaderProgressDialogFragment.newInstance(
emulationActivity.getString(R.string.loading),
emulationActivity.getString(R.string.preparing_shaders)
)
fragment.show(emulationActivity.supportFragmentManager, ShaderProgressDialogFragment.TAG)
}
synchronized(finishLock) { finishLock.wait() }
}
@JvmStatic
fun loadProgress(stage: Int, progress: Int, max: Int) {
val emulationActivity = NativeLibrary.sEmulationActivity.get()
?: error("[DiskShaderCacheProgress] EmulationActivity not present")
when (LoadCallbackStage.values()[stage]) {
LoadCallbackStage.Prepare -> prepareDialog()
LoadCallbackStage.Build -> fragment.onUpdateProgress(
emulationActivity.getString(R.string.building_shaders),
progress,
max
)
LoadCallbackStage.Complete -> fragment.dismiss()
}
}
// Equivalent to VideoCore::LoadCallbackStage
enum class LoadCallbackStage {
Prepare, Build, Complete
}
}
@@ -0,0 +1,31 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.disk_shader_cache
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
class ShaderProgressViewModel : ViewModel() {
private val _progress = MutableLiveData(0)
val progress: LiveData<Int> get() = _progress
private val _max = MutableLiveData(0)
val max: LiveData<Int> get() = _max
private val _message = MutableLiveData("")
val message: LiveData<String> get() = _message
fun setProgress(progress: Int) {
_progress.postValue(progress)
}
fun setMax(max: Int) {
_max.postValue(max)
}
fun setMessage(msg: String) {
_message.postValue(msg)
}
}
@@ -0,0 +1,101 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.disk_shader_cache.ui
import android.app.Dialog
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.DialogFragment
import androidx.lifecycle.ViewModelProvider
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import org.yuzu.yuzu_emu.databinding.DialogProgressBarBinding
import org.yuzu.yuzu_emu.disk_shader_cache.DiskShaderCacheProgress
import org.yuzu.yuzu_emu.disk_shader_cache.ShaderProgressViewModel
class ShaderProgressDialogFragment : DialogFragment() {
private var _binding: DialogProgressBarBinding? = null
private val binding get() = _binding!!
private lateinit var alertDialog: AlertDialog
private lateinit var shaderProgressViewModel: ShaderProgressViewModel
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
_binding = DialogProgressBarBinding.inflate(layoutInflater)
shaderProgressViewModel =
ViewModelProvider(requireActivity())[ShaderProgressViewModel::class.java]
val title = requireArguments().getString(TITLE)
val message = requireArguments().getString(MESSAGE)
isCancelable = false
alertDialog = MaterialAlertDialogBuilder(requireActivity())
.setView(binding.root)
.setTitle(title)
.setMessage(message)
.create()
return alertDialog
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
shaderProgressViewModel.progress.observe(viewLifecycleOwner) { progress ->
binding.progressBar.progress = progress
setUpdateText()
}
shaderProgressViewModel.max.observe(viewLifecycleOwner) { max ->
binding.progressBar.max = max
setUpdateText()
}
shaderProgressViewModel.message.observe(viewLifecycleOwner) { msg ->
alertDialog.setMessage(msg)
}
synchronized(DiskShaderCacheProgress.finishLock) { DiskShaderCacheProgress.finishLock.notifyAll() }
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
fun onUpdateProgress(msg: String, progress: Int, max: Int) {
shaderProgressViewModel.setProgress(progress)
shaderProgressViewModel.setMax(max)
shaderProgressViewModel.setMessage(msg)
}
private fun setUpdateText() {
binding.progressText.text = String.format(
"%d/%d",
shaderProgressViewModel.progress.value,
shaderProgressViewModel.max.value
)
}
companion object {
const val TAG = "ProgressDialogFragment"
const val TITLE = "title"
const val MESSAGE = "message"
fun newInstance(title: String, message: String): ShaderProgressDialogFragment {
val frag = ShaderProgressDialogFragment()
val args = Bundle()
args.putString(TITLE, title)
args.putString(MESSAGE, message)
frag.arguments = args
return frag
}
}
}
@@ -0,0 +1,302 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2023 Skyline Team and Contributors (https://github.com/skyline-emu/)
package org.yuzu.yuzu_emu.features
import android.database.Cursor
import android.database.MatrixCursor
import android.os.CancellationSignal
import android.os.ParcelFileDescriptor
import android.provider.DocumentsContract
import android.provider.DocumentsProvider
import android.webkit.MimeTypeMap
import org.yuzu.yuzu_emu.BuildConfig
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.getPublicFilesDir
import java.io.*
class DocumentProvider : DocumentsProvider() {
private val baseDirectory: File
get() = File(YuzuApplication.application.getPublicFilesDir().canonicalPath)
companion object {
private val DEFAULT_ROOT_PROJECTION: Array<String> = arrayOf(
DocumentsContract.Root.COLUMN_ROOT_ID,
DocumentsContract.Root.COLUMN_MIME_TYPES,
DocumentsContract.Root.COLUMN_FLAGS,
DocumentsContract.Root.COLUMN_ICON,
DocumentsContract.Root.COLUMN_TITLE,
DocumentsContract.Root.COLUMN_SUMMARY,
DocumentsContract.Root.COLUMN_DOCUMENT_ID,
DocumentsContract.Root.COLUMN_AVAILABLE_BYTES
)
private val DEFAULT_DOCUMENT_PROJECTION: Array<String> = arrayOf(
DocumentsContract.Document.COLUMN_DOCUMENT_ID,
DocumentsContract.Document.COLUMN_MIME_TYPE,
DocumentsContract.Document.COLUMN_DISPLAY_NAME,
DocumentsContract.Document.COLUMN_LAST_MODIFIED,
DocumentsContract.Document.COLUMN_FLAGS,
DocumentsContract.Document.COLUMN_SIZE
)
const val AUTHORITY : String = BuildConfig.APPLICATION_ID + ".user"
const val ROOT_ID: String = "root"
}
override fun onCreate(): Boolean {
return true
}
/**
* @return The [File] that corresponds to the document ID supplied by [getDocumentId]
*/
private fun getFile(documentId: String): File {
if (documentId.startsWith(ROOT_ID)) {
val file = baseDirectory.resolve(documentId.drop(ROOT_ID.length + 1))
if (!file.exists()) throw FileNotFoundException("${file.absolutePath} ($documentId) not found")
return file
} else {
throw FileNotFoundException("'$documentId' is not in any known root")
}
}
/**
* @return A unique ID for the provided [File]
*/
private fun getDocumentId(file: File): String {
return "$ROOT_ID/${file.toRelativeString(baseDirectory)}"
}
override fun queryRoots(projection: Array<out String>?): Cursor {
val cursor = MatrixCursor(projection ?: DEFAULT_ROOT_PROJECTION)
cursor.newRow().apply {
add(DocumentsContract.Root.COLUMN_ROOT_ID, ROOT_ID)
add(DocumentsContract.Root.COLUMN_SUMMARY, null)
add(
DocumentsContract.Root.COLUMN_FLAGS,
DocumentsContract.Root.FLAG_SUPPORTS_CREATE or DocumentsContract.Root.FLAG_SUPPORTS_IS_CHILD
)
add(DocumentsContract.Root.COLUMN_TITLE, context!!.getString(R.string.app_name))
add(DocumentsContract.Root.COLUMN_DOCUMENT_ID, getDocumentId(baseDirectory))
add(DocumentsContract.Root.COLUMN_MIME_TYPES, "*/*")
add(DocumentsContract.Root.COLUMN_AVAILABLE_BYTES, baseDirectory.freeSpace)
add(DocumentsContract.Root.COLUMN_ICON, R.drawable.ic_yuzu)
}
return cursor
}
override fun queryDocument(documentId: String?, projection: Array<out String>?): Cursor {
val cursor = MatrixCursor(projection ?: DEFAULT_DOCUMENT_PROJECTION)
return includeFile(cursor, documentId, null)
}
override fun isChildDocument(parentDocumentId: String?, documentId: String?): Boolean {
return documentId?.startsWith(parentDocumentId!!) ?: false
}
/**
* @return A new [File] with a unique name based off the supplied [name], not conflicting with any existing file
*/
private fun File.resolveWithoutConflict(name: String): File {
var file = resolve(name)
if (file.exists()) {
var noConflictId =
1 // Makes sure two files don't have the same name by adding a number to the end
val extension = name.substringAfterLast('.')
val baseName = name.substringBeforeLast('.')
while (file.exists())
file = resolve("$baseName (${noConflictId++}).$extension")
}
return file
}
override fun createDocument(
parentDocumentId: String?,
mimeType: String?,
displayName: String
): String {
val parentFile = getFile(parentDocumentId!!)
val newFile = parentFile.resolveWithoutConflict(displayName)
try {
if (DocumentsContract.Document.MIME_TYPE_DIR == mimeType) {
if (!newFile.mkdir())
throw IOException("Failed to create directory")
} else {
if (!newFile.createNewFile())
throw IOException("Failed to create file")
}
} catch (e: IOException) {
throw FileNotFoundException("Couldn't create document '${newFile.path}': ${e.message}")
}
return getDocumentId(newFile)
}
override fun deleteDocument(documentId: String?) {
val file = getFile(documentId!!)
if (!file.delete())
throw FileNotFoundException("Couldn't delete document with ID '$documentId'")
}
override fun removeDocument(documentId: String, parentDocumentId: String?) {
val parent = getFile(parentDocumentId!!)
val file = getFile(documentId)
if (parent == file || file.parentFile == null || file.parentFile!! == parent) {
if (!file.delete())
throw FileNotFoundException("Couldn't delete document with ID '$documentId'")
} else {
throw FileNotFoundException("Couldn't delete document with ID '$documentId'")
}
}
override fun renameDocument(documentId: String?, displayName: String?): String {
if (displayName == null)
throw FileNotFoundException("Couldn't rename document '$documentId' as the new name is null")
val sourceFile = getFile(documentId!!)
val sourceParentFile = sourceFile.parentFile
?: throw FileNotFoundException("Couldn't rename document '$documentId' as it has no parent")
val destFile = sourceParentFile.resolve(displayName)
try {
if (!sourceFile.renameTo(destFile))
throw FileNotFoundException("Couldn't rename document from '${sourceFile.name}' to '${destFile.name}'")
} catch (e: Exception) {
throw FileNotFoundException("Couldn't rename document from '${sourceFile.name}' to '${destFile.name}': ${e.message}")
}
return getDocumentId(destFile)
}
private fun copyDocument(
sourceDocumentId: String, sourceParentDocumentId: String,
targetParentDocumentId: String?
): String {
if (!isChildDocument(sourceParentDocumentId, sourceDocumentId))
throw FileNotFoundException("Couldn't copy document '$sourceDocumentId' as its parent is not '$sourceParentDocumentId'")
return copyDocument(sourceDocumentId, targetParentDocumentId)
}
override fun copyDocument(sourceDocumentId: String, targetParentDocumentId: String?): String {
val parent = getFile(targetParentDocumentId!!)
val oldFile = getFile(sourceDocumentId)
val newFile = parent.resolveWithoutConflict(oldFile.name)
try {
if (!(newFile.createNewFile() && newFile.setWritable(true) && newFile.setReadable(true)))
throw IOException("Couldn't create new file")
FileInputStream(oldFile).use { inStream ->
FileOutputStream(newFile).use { outStream ->
inStream.copyTo(outStream)
}
}
} catch (e: IOException) {
throw FileNotFoundException("Couldn't copy document '$sourceDocumentId': ${e.message}")
}
return getDocumentId(newFile)
}
override fun moveDocument(
sourceDocumentId: String, sourceParentDocumentId: String?,
targetParentDocumentId: String?
): String {
try {
val newDocumentId = copyDocument(
sourceDocumentId, sourceParentDocumentId!!,
targetParentDocumentId
)
removeDocument(sourceDocumentId, sourceParentDocumentId)
return newDocumentId
} catch (e: FileNotFoundException) {
throw FileNotFoundException("Couldn't move document '$sourceDocumentId'")
}
}
private fun includeFile(cursor: MatrixCursor, documentId: String?, file: File?): MatrixCursor {
val localDocumentId = documentId ?: file?.let { getDocumentId(it) }
val localFile = file ?: getFile(documentId!!)
var flags = 0
if (localFile.isDirectory && localFile.canWrite()) {
flags = DocumentsContract.Document.FLAG_DIR_SUPPORTS_CREATE
} else if (localFile.canWrite()) {
flags = DocumentsContract.Document.FLAG_SUPPORTS_WRITE
flags = flags or DocumentsContract.Document.FLAG_SUPPORTS_DELETE
flags = flags or DocumentsContract.Document.FLAG_SUPPORTS_REMOVE
flags = flags or DocumentsContract.Document.FLAG_SUPPORTS_MOVE
flags = flags or DocumentsContract.Document.FLAG_SUPPORTS_COPY
flags = flags or DocumentsContract.Document.FLAG_SUPPORTS_RENAME
}
cursor.newRow().apply {
add(DocumentsContract.Document.COLUMN_DOCUMENT_ID, localDocumentId)
add(
DocumentsContract.Document.COLUMN_DISPLAY_NAME,
if (localFile == baseDirectory) context!!.getString(R.string.app_name) else localFile.name
)
add(DocumentsContract.Document.COLUMN_SIZE, localFile.length())
add(DocumentsContract.Document.COLUMN_MIME_TYPE, getTypeForFile(localFile))
add(DocumentsContract.Document.COLUMN_LAST_MODIFIED, localFile.lastModified())
add(DocumentsContract.Document.COLUMN_FLAGS, flags)
if (localFile == baseDirectory)
add(DocumentsContract.Root.COLUMN_ICON, R.drawable.ic_yuzu)
}
return cursor
}
private fun getTypeForFile(file: File): Any {
return if (file.isDirectory)
DocumentsContract.Document.MIME_TYPE_DIR
else
getTypeForName(file.name)
}
private fun getTypeForName(name: String): Any {
val lastDot = name.lastIndexOf('.')
if (lastDot >= 0) {
val extension = name.substring(lastDot + 1)
val mime = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension)
if (mime != null)
return mime
}
return "application/octect-stream"
}
override fun queryChildDocuments(
parentDocumentId: String?,
projection: Array<out String>?,
sortOrder: String?
): Cursor {
var cursor = MatrixCursor(projection ?: DEFAULT_DOCUMENT_PROJECTION)
val parent = getFile(parentDocumentId!!)
for (file in parent.listFiles()!!)
cursor = includeFile(cursor, null, file)
return cursor
}
override fun openDocument(
documentId: String?,
mode: String?,
signal: CancellationSignal?
): ParcelFileDescriptor {
val file = documentId?.let { getFile(it) }
val accessMode = ParcelFileDescriptor.parseMode(mode)
return ParcelFileDescriptor.open(file, accessMode)
}
}
@@ -0,0 +1,8 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.features.settings.model
interface AbstractBooleanSetting : AbstractSetting {
var boolean: Boolean
}
@@ -0,0 +1,8 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.features.settings.model
interface AbstractFloatSetting : AbstractSetting {
var float: Float
}
@@ -0,0 +1,8 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.features.settings.model
interface AbstractIntSetting : AbstractSetting {
var int: Int
}
@@ -0,0 +1,12 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.features.settings.model
interface AbstractSetting {
val key: String?
val section: String?
val isRuntimeEditable: Boolean
val valueAsString: String
val defaultValue: Any
}
@@ -0,0 +1,8 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.features.settings.model
interface AbstractStringSetting : AbstractSetting {
var string: String
}
@@ -0,0 +1,38 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.features.settings.model
enum class BooleanSetting(
override val key: String,
override val section: String,
override val defaultValue: Boolean
) : AbstractBooleanSetting {
USE_CUSTOM_RTC("custom_rtc_enabled", Settings.SECTION_SYSTEM, false);
override var boolean: Boolean = defaultValue
override val valueAsString: String
get() = boolean.toString()
override val isRuntimeEditable: Boolean
get() {
for (setting in NOT_RUNTIME_EDITABLE) {
if (setting == this) {
return false
}
}
return true
}
companion object {
private val NOT_RUNTIME_EDITABLE = listOf(
USE_CUSTOM_RTC
)
fun from(key: String): BooleanSetting? =
BooleanSetting.values().firstOrNull { it.key == key }
fun clear() = BooleanSetting.values().forEach { it.boolean = it.defaultValue }
}
}
@@ -0,0 +1,36 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.features.settings.model
enum class FloatSetting(
override val key: String,
override val section: String,
override val defaultValue: Float
) : AbstractFloatSetting {
// No float settings currently exist
EMPTY_SETTING("", "", 0f);
override var float: Float = defaultValue
override val valueAsString: String
get() = float.toString()
override val isRuntimeEditable: Boolean
get() {
for (setting in NOT_RUNTIME_EDITABLE) {
if (setting == this) {
return false
}
}
return true
}
companion object {
private val NOT_RUNTIME_EDITABLE = emptyList<FloatSetting>()
fun from(key: String): FloatSetting? = FloatSetting.values().firstOrNull { it.key == key }
fun clear() = FloatSetting.values().forEach { it.float = it.defaultValue }
}
}
@@ -0,0 +1,131 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.features.settings.model
enum class IntSetting(
override val key: String,
override val section: String,
override val defaultValue: Int
) : AbstractIntSetting {
RENDERER_USE_SPEED_LIMIT(
"use_speed_limit",
Settings.SECTION_RENDERER,
1
),
USE_DOCKED_MODE(
"use_docked_mode",
Settings.SECTION_SYSTEM,
0
),
RENDERER_USE_DISK_SHADER_CACHE(
"use_disk_shader_cache",
Settings.SECTION_RENDERER,
1
),
RENDERER_FORCE_MAX_CLOCK(
"force_max_clock",
Settings.SECTION_RENDERER,
1
),
RENDERER_ASYNCHRONOUS_SHADERS(
"use_asynchronous_shaders",
Settings.SECTION_RENDERER,
0
),
RENDERER_DEBUG(
"debug",
Settings.SECTION_RENDERER,
0
),
RENDERER_SPEED_LIMIT(
"speed_limit",
Settings.SECTION_RENDERER,
100
),
CPU_ACCURACY(
"cpu_accuracy",
Settings.SECTION_CPU,
0
),
REGION_INDEX(
"region_index",
Settings.SECTION_SYSTEM,
-1
),
LANGUAGE_INDEX(
"language_index",
Settings.SECTION_SYSTEM,
1
),
RENDERER_BACKEND(
"backend",
Settings.SECTION_RENDERER,
1
),
RENDERER_ACCURACY(
"gpu_accuracy",
Settings.SECTION_RENDERER,
0
),
RENDERER_RESOLUTION(
"resolution_setup",
Settings.SECTION_RENDERER,
2
),
RENDERER_VSYNC(
"use_vsync",
Settings.SECTION_RENDERER,
0
),
RENDERER_SCALING_FILTER(
"scaling_filter",
Settings.SECTION_RENDERER,
1
),
RENDERER_ANTI_ALIASING(
"anti_aliasing",
Settings.SECTION_RENDERER,
0
),
RENDERER_ASPECT_RATIO(
"aspect_ratio",
Settings.SECTION_RENDERER,
0
),
AUDIO_VOLUME(
"volume",
Settings.SECTION_AUDIO,
100
);
override var int: Int = defaultValue
override val valueAsString: String
get() = int.toString()
override val isRuntimeEditable: Boolean
get() {
for (setting in NOT_RUNTIME_EDITABLE) {
if (setting == this) {
return false
}
}
return true
}
companion object {
private val NOT_RUNTIME_EDITABLE = listOf(
RENDERER_USE_DISK_SHADER_CACHE,
RENDERER_ASYNCHRONOUS_SHADERS,
RENDERER_DEBUG,
RENDERER_BACKEND,
RENDERER_RESOLUTION,
RENDERER_VSYNC
)
fun from(key: String): IntSetting? = IntSetting.values().firstOrNull { it.key == key }
fun clear() = IntSetting.values().forEach { it.int = it.defaultValue }
}
}
@@ -0,0 +1,37 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.features.settings.model
/**
* A semantically-related group of Settings objects. These Settings are
* internally stored as a HashMap.
*/
class SettingSection(val name: String) {
val settings = HashMap<String, AbstractSetting>()
/**
* Convenience method; inserts a value directly into the backing HashMap.
*
* @param setting The Setting to be inserted.
*/
fun putSetting(setting: AbstractSetting) {
settings[setting.key!!] = setting
}
/**
* Convenience method; gets a value directly from the backing HashMap.
*
* @param key Used to retrieve the Setting.
* @return A Setting object (you should probably cast this before using)
*/
fun getSetting(key: String): AbstractSetting? {
return settings[key]
}
fun mergeSection(settingSection: SettingSection) {
for (setting in settingSection.settings.values) {
putSetting(setting)
}
}
}
@@ -0,0 +1,158 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.features.settings.model
import android.text.TextUtils
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.features.settings.ui.SettingsActivityView
import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
import java.util.*
class Settings {
private var gameId: String? = null
var isLoaded = false
/**
* A HashMap<String></String>, SettingSection> that constructs a new SettingSection instead of returning null
* when getting a key not already in the map
*/
class SettingsSectionMap : HashMap<String, SettingSection?>() {
override operator fun get(key: String): SettingSection? {
if (!super.containsKey(key)) {
val section = SettingSection(key)
super.put(key, section)
return section
}
return super.get(key)
}
}
var sections: HashMap<String, SettingSection?> = SettingsSectionMap()
fun getSection(sectionName: String): SettingSection? {
return sections[sectionName]
}
val isEmpty: Boolean
get() = sections.isEmpty()
fun loadSettings(view: SettingsActivityView? = null) {
sections = SettingsSectionMap()
loadYuzuSettings(view)
if (!TextUtils.isEmpty(gameId)) {
loadCustomGameSettings(gameId!!, view)
}
isLoaded = true
}
private fun loadYuzuSettings(view: SettingsActivityView?) {
for ((fileName) in configFileSectionsMap) {
sections.putAll(SettingsFile.readFile(fileName, view))
}
}
private fun loadCustomGameSettings(gameId: String, view: SettingsActivityView?) {
// Custom game settings
mergeSections(SettingsFile.readCustomGameSettings(gameId, view))
}
private fun mergeSections(updatedSections: HashMap<String, SettingSection?>) {
for ((key, updatedSection) in updatedSections) {
if (sections.containsKey(key)) {
val originalSection = sections[key]
originalSection!!.mergeSection(updatedSection!!)
} else {
sections[key] = updatedSection
}
}
}
fun loadSettings(gameId: String, view: SettingsActivityView) {
this.gameId = gameId
loadSettings(view)
}
fun saveSettings(view: SettingsActivityView) {
if (TextUtils.isEmpty(gameId)) {
view.showToastMessage(
YuzuApplication.appContext.getString(R.string.ini_saved),
false
)
for ((fileName, sectionNames) in configFileSectionsMap) {
val iniSections = TreeMap<String, SettingSection>()
for (section in sectionNames) {
iniSections[section] = sections[section]!!
}
SettingsFile.saveFile(fileName, iniSections, view)
}
} else {
// Custom game settings
view.showToastMessage(
YuzuApplication.appContext.getString(R.string.gameid_saved, gameId),
false
)
SettingsFile.saveCustomGameSettings(gameId, sections)
}
}
companion object {
const val SECTION_GENERAL = "General"
const val SECTION_SYSTEM = "System"
const val SECTION_RENDERER = "Renderer"
const val SECTION_AUDIO = "Audio"
const val SECTION_CPU = "Cpu"
const val SECTION_THEME = "Theme"
const val SECTION_DEBUG = "Debug"
const val PREF_OVERLAY_INIT = "OverlayInit"
const val PREF_CONTROL_SCALE = "controlScale"
const val PREF_CONTROL_OPACITY = "controlOpacity"
const val PREF_TOUCH_ENABLED = "isTouchEnabled"
const val PREF_BUTTON_TOGGLE_0 = "buttonToggle0"
const val PREF_BUTTON_TOGGLE_1 = "buttonToggle1"
const val PREF_BUTTON_TOGGLE_2 = "buttonToggle2"
const val PREF_BUTTON_TOGGLE_3 = "buttonToggle3"
const val PREF_BUTTON_TOGGLE_4 = "buttonToggle4"
const val PREF_BUTTON_TOGGLE_5 = "buttonToggle5"
const val PREF_BUTTON_TOGGLE_6 = "buttonToggle6"
const val PREF_BUTTON_TOGGLE_7 = "buttonToggle7"
const val PREF_BUTTON_TOGGLE_8 = "buttonToggle8"
const val PREF_BUTTON_TOGGLE_9 = "buttonToggle9"
const val PREF_BUTTON_TOGGLE_10 = "buttonToggle10"
const val PREF_BUTTON_TOGGLE_11 = "buttonToggle11"
const val PREF_BUTTON_TOGGLE_12 = "buttonToggle12"
const val PREF_BUTTON_TOGGLE_13 = "buttonToggle13"
const val PREF_BUTTON_TOGGLE_14 = "buttonToggle14"
const val PREF_MENU_SETTINGS_JOYSTICK_REL_CENTER = "EmulationMenuSettings_JoystickRelCenter"
const val PREF_MENU_SETTINGS_DPAD_SLIDE = "EmulationMenuSettings_DpadSlideEnable"
const val PREF_MENU_SETTINGS_HAPTICS = "EmulationMenuSettings_Haptics"
const val PREF_MENU_SETTINGS_LANDSCAPE = "EmulationMenuSettings_LandscapeScreenLayout"
const val PREF_MENU_SETTINGS_SHOW_FPS = "EmulationMenuSettings_ShowFps"
const val PREF_MENU_SETTINGS_SHOW_OVERLAY = "EmulationMenuSettings_ShowOverlay"
const val PREF_FIRST_APP_LAUNCH = "FirstApplicationLaunch"
const val PREF_THEME = "Theme"
const val PREF_THEME_MODE = "ThemeMode"
const val PREF_BLACK_BACKGROUNDS = "BlackBackgrounds"
private val configFileSectionsMap: MutableMap<String, List<String>> = HashMap()
init {
configFileSectionsMap[SettingsFile.FILE_NAME_CONFIG] =
listOf(
SECTION_GENERAL,
SECTION_SYSTEM,
SECTION_RENDERER,
SECTION_AUDIO,
SECTION_CPU
)
}
}
}
@@ -0,0 +1,10 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.features.settings.model
import androidx.lifecycle.ViewModel
class SettingsViewModel : ViewModel() {
val settings = Settings()
}
@@ -0,0 +1,37 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.features.settings.model
enum class StringSetting(
override val key: String,
override val section: String,
override val defaultValue: String
) : AbstractStringSetting {
CUSTOM_RTC("custom_rtc", Settings.SECTION_SYSTEM, "0");
override var string: String = defaultValue
override val valueAsString: String
get() = string
override val isRuntimeEditable: Boolean
get() {
for (setting in NOT_RUNTIME_EDITABLE) {
if (setting == this) {
return false
}
}
return true
}
companion object {
private val NOT_RUNTIME_EDITABLE = listOf(
CUSTOM_RTC
)
fun from(key: String): StringSetting? = StringSetting.values().firstOrNull { it.key == key }
fun clear() = StringSetting.values().forEach { it.string = it.defaultValue }
}
}
@@ -0,0 +1,31 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.features.settings.model.view
import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting
import org.yuzu.yuzu_emu.features.settings.model.AbstractStringSetting
class DateTimeSetting(
setting: AbstractSetting?,
titleId: Int,
descriptionId: Int,
val key: String? = null,
private val defaultValue: String? = null
) : SettingsItem(setting, titleId, descriptionId) {
override val type = TYPE_DATETIME_SETTING
val value: String
get() = if (setting != null) {
val setting = setting as AbstractStringSetting
setting.string
} else {
defaultValue!!
}
fun setSelectedValue(datetime: String): AbstractStringSetting {
val stringSetting = setting as AbstractStringSetting
stringSetting.string = datetime
return stringSetting
}
}
@@ -0,0 +1,14 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.features.settings.model.view
import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting
class HeaderSetting(
setting: AbstractSetting?,
titleId: Int,
descriptionId: Int
) : SettingsItem(setting, titleId, descriptionId) {
override val type = TYPE_HEADER
}
@@ -0,0 +1,13 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.features.settings.model.view
class RunnableSetting(
titleId: Int,
descriptionId: Int,
val isRuntimeRunnable: Boolean,
val runnable: () -> Unit
) : SettingsItem(null, titleId, descriptionId) {
override val type = TYPE_RUNNABLE
}
@@ -0,0 +1,39 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.features.settings.model.view
import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting
/**
* ViewModel abstraction for an Item in the RecyclerView powering SettingsFragments.
* Each one corresponds to a [AbstractSetting] object, so this class's subclasses
* should vaguely correspond to those subclasses. There are a few with multiple analogues
* and a few with none (Headers, for example, do not correspond to anything in the ini
* file.)
*/
abstract class SettingsItem(
var setting: AbstractSetting?,
val nameId: Int,
val descriptionId: Int
) {
abstract val type: Int
val isEditable: Boolean
get() {
if (!NativeLibrary.isRunning()) return true
return setting?.isRuntimeEditable ?: false
}
companion object {
const val TYPE_HEADER = 0
const val TYPE_SWITCH = 1
const val TYPE_SINGLE_CHOICE = 2
const val TYPE_SLIDER = 3
const val TYPE_SUBMENU = 4
const val TYPE_STRING_SINGLE_CHOICE = 5
const val TYPE_DATETIME_SETTING = 6
const val TYPE_RUNNABLE = 7
}
}
@@ -0,0 +1,40 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.features.settings.model.view
import org.yuzu.yuzu_emu.features.settings.model.AbstractIntSetting
import org.yuzu.yuzu_emu.features.settings.model.IntSetting
class SingleChoiceSetting(
setting: AbstractIntSetting?,
titleId: Int,
descriptionId: Int,
val choicesId: Int,
val valuesId: Int,
val key: String? = null,
val defaultValue: Int? = null
) : SettingsItem(setting, titleId, descriptionId) {
override val type = TYPE_SINGLE_CHOICE
val selectedValue: Int
get() = if (setting != null) {
val setting = setting as AbstractIntSetting
setting.int
} else {
defaultValue!!
}
/**
* Write a value to the backing int. If that int was previously null,
* initializes a new one and returns it, so it can be added to the Hashmap.
*
* @param selection New value of the int.
* @return the existing setting with the new value applied.
*/
fun setSelectedValue(selection: Int): AbstractIntSetting {
val intSetting = setting as AbstractIntSetting
intSetting.int = selection
return intSetting
}
}
@@ -0,0 +1,64 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.features.settings.model.view
import org.yuzu.yuzu_emu.features.settings.model.AbstractFloatSetting
import org.yuzu.yuzu_emu.features.settings.model.AbstractIntSetting
import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting
import org.yuzu.yuzu_emu.features.settings.model.FloatSetting
import org.yuzu.yuzu_emu.features.settings.model.IntSetting
import org.yuzu.yuzu_emu.utils.Log
import kotlin.math.roundToInt
class SliderSetting(
setting: AbstractSetting?,
titleId: Int,
descriptionId: Int,
val min: Int,
val max: Int,
val units: String,
val key: String? = null,
val defaultValue: Int? = null,
) : SettingsItem(setting, titleId, descriptionId) {
override val type = TYPE_SLIDER
val selectedValue: Int
get() {
val setting = setting ?: return defaultValue!!
return when (setting) {
is AbstractIntSetting -> setting.int
is AbstractFloatSetting -> setting.float.roundToInt()
else -> {
Log.error("[SliderSetting] Error casting setting type.")
-1
}
}
}
/**
* Write a value to the backing int. If that int was previously null,
* initializes a new one and returns it, so it can be added to the Hashmap.
*
* @param selection New value of the int.
* @return the existing setting with the new value applied.
*/
fun setSelectedValue(selection: Int): AbstractIntSetting {
val intSetting = setting as AbstractIntSetting
intSetting.int = selection
return intSetting
}
/**
* Write a value to the backing float. If that float was previously null,
* initializes a new one and returns it, so it can be added to the Hashmap.
*
* @param selection New value of the float.
* @return the existing setting with the new value applied.
*/
fun setSelectedValue(selection: Float): AbstractFloatSetting {
val floatSetting = setting as AbstractFloatSetting
floatSetting.float = selection
return floatSetting
}
}
@@ -0,0 +1,58 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.features.settings.model.view
import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting
import org.yuzu.yuzu_emu.features.settings.model.AbstractStringSetting
import org.yuzu.yuzu_emu.features.settings.model.StringSetting
class StringSingleChoiceSetting(
val key: String? = null,
setting: AbstractSetting?,
titleId: Int,
descriptionId: Int,
val choicesId: Array<String>,
private val valuesId: Array<String>?,
private val defaultValue: String? = null
) : SettingsItem(setting, titleId, descriptionId) {
override val type = TYPE_STRING_SINGLE_CHOICE
fun getValueAt(index: Int): String? {
if (valuesId == null) return null
return if (index >= 0 && index < valuesId.size) {
valuesId[index]
} else ""
}
val selectedValue: String
get() = if (setting != null) {
val setting = setting as AbstractStringSetting
setting.string
} else {
defaultValue!!
}
val selectValueIndex: Int
get() {
val selectedValue = selectedValue
for (i in valuesId!!.indices) {
if (valuesId[i] == selectedValue) {
return i
}
}
return -1
}
/**
* Write a value to the backing int. If that int was previously null,
* initializes a new one and returns it, so it can be added to the Hashmap.
*
* @param selection New value of the int.
* @return the existing setting with the new value applied.
*/
fun setSelectedValue(selection: String): AbstractStringSetting {
val stringSetting = setting as AbstractStringSetting
stringSetting.string = selection
return stringSetting
}
}
@@ -0,0 +1,14 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.features.settings.model.view
import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting
class SubmenuSetting(
titleId: Int,
descriptionId: Int,
val menuKey: String
) : SettingsItem(null, titleId, descriptionId) {
override val type = TYPE_SUBMENU
}
@@ -0,0 +1,62 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.features.settings.model.view
import org.yuzu.yuzu_emu.features.settings.model.AbstractBooleanSetting
import org.yuzu.yuzu_emu.features.settings.model.AbstractIntSetting
import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting
class SwitchSetting(
setting: AbstractSetting,
titleId: Int,
descriptionId: Int,
val key: String? = null,
val defaultValue: Any? = null
) : SettingsItem(setting, titleId, descriptionId) {
override val type = TYPE_SWITCH
val isChecked: Boolean
get() {
if (setting == null) {
return defaultValue as Boolean
}
// Try integer setting
try {
val setting = setting as AbstractIntSetting
return setting.int == 1
} catch (_: ClassCastException) {
}
// Try boolean setting
try {
val setting = setting as AbstractBooleanSetting
return setting.boolean
} catch (_: ClassCastException) {
}
return defaultValue as Boolean
}
/**
* Write a value to the backing boolean. If that boolean was previously null,
* initializes a new one and returns it, so it can be added to the Hashmap.
*
* @param checked Pretty self explanatory.
* @return the existing setting with the new value applied.
*/
fun setChecked(checked: Boolean): AbstractSetting {
// Try integer setting
try {
val setting = setting as AbstractIntSetting
setting.int = if (checked) 1 else 0
return setting
} catch (_: ClassCastException) {
}
// Try boolean setting
val setting = setting as AbstractBooleanSetting
setting.boolean = checked
return setting
}
}
@@ -0,0 +1,243 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.features.settings.ui
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.view.Menu
import android.view.View
import android.widget.Toast
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat
import android.view.ViewGroup.MarginLayoutParams
import androidx.activity.OnBackPressedCallback
import androidx.core.view.updatePadding
import com.google.android.material.color.MaterialColors
import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.databinding.ActivitySettingsBinding
import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
import org.yuzu.yuzu_emu.features.settings.model.FloatSetting
import org.yuzu.yuzu_emu.features.settings.model.IntSetting
import org.yuzu.yuzu_emu.features.settings.model.Settings
import org.yuzu.yuzu_emu.features.settings.model.SettingsViewModel
import org.yuzu.yuzu_emu.features.settings.model.StringSetting
import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
import org.yuzu.yuzu_emu.utils.*
import java.io.IOException
class SettingsActivity : AppCompatActivity(), SettingsActivityView {
private val presenter = SettingsActivityPresenter(this)
private lateinit var binding: ActivitySettingsBinding
private val settingsViewModel: SettingsViewModel by viewModels()
override val settings: Settings get() = settingsViewModel.settings
override fun onCreate(savedInstanceState: Bundle?) {
ThemeHelper.setTheme(this)
super.onCreate(savedInstanceState)
binding = ActivitySettingsBinding.inflate(layoutInflater)
setContentView(binding.root)
WindowCompat.setDecorFitsSystemWindows(window, false)
val launcher = intent
val gameID = launcher.getStringExtra(ARG_GAME_ID)
val menuTag = launcher.getStringExtra(ARG_MENU_TAG)
presenter.onCreate(savedInstanceState, menuTag!!, gameID!!)
// Show "Back" button in the action bar for navigation
setSupportActionBar(binding.toolbarSettings)
supportActionBar!!.setDisplayHomeAsUpEnabled(true)
if (InsetsHelper.getSystemGestureType(applicationContext) != InsetsHelper.GESTURE_NAVIGATION) {
binding.navigationBarShade.setBackgroundColor(
ThemeHelper.getColorWithOpacity(
MaterialColors.getColor(
binding.navigationBarShade,
com.google.android.material.R.attr.colorSurface
),
ThemeHelper.SYSTEM_BAR_ALPHA
)
)
}
onBackPressedDispatcher.addCallback(
this,
object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() = navigateBack()
})
setInsets()
}
override fun onSupportNavigateUp(): Boolean {
navigateBack()
return true
}
private fun navigateBack() {
if (supportFragmentManager.backStackEntryCount > 0) {
supportFragmentManager.popBackStack()
} else {
finish()
}
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
val inflater = menuInflater
inflater.inflate(R.menu.menu_settings, menu)
return true
}
override fun onSaveInstanceState(outState: Bundle) {
// Critical: If super method is not called, rotations will be busted.
super.onSaveInstanceState(outState)
presenter.saveState(outState)
}
override fun onStart() {
super.onStart()
presenter.onStart()
}
/**
* If this is called, the user has left the settings screen (potentially through the
* home button) and will expect their changes to be persisted. So we kick off an
* IntentService which will do so on a background thread.
*/
override fun onStop() {
super.onStop()
presenter.onStop(isFinishing)
}
override fun showSettingsFragment(menuTag: String, addToStack: Boolean, gameId: String) {
if (!addToStack && settingsFragment != null) {
return
}
val transaction = supportFragmentManager.beginTransaction()
if (addToStack) {
if (areSystemAnimationsEnabled()) {
transaction.setCustomAnimations(
R.anim.anim_settings_fragment_in,
R.anim.anim_settings_fragment_out,
0,
R.anim.anim_pop_settings_fragment_out
)
}
transaction.addToBackStack(null)
}
transaction.replace(
R.id.frame_content,
SettingsFragment.newInstance(menuTag, gameId),
FRAGMENT_TAG
)
transaction.commit()
}
private fun areSystemAnimationsEnabled(): Boolean {
val duration = android.provider.Settings.Global.getFloat(
contentResolver,
android.provider.Settings.Global.ANIMATOR_DURATION_SCALE, 1f
)
val transition = android.provider.Settings.Global.getFloat(
contentResolver,
android.provider.Settings.Global.TRANSITION_ANIMATION_SCALE, 1f
)
return duration != 0f && transition != 0f
}
override fun onSettingsFileLoaded() {
val fragment: SettingsFragmentView? = settingsFragment
fragment?.loadSettingsList()
}
override fun onSettingsFileNotFound() {
val fragment: SettingsFragmentView? = settingsFragment
fragment?.loadSettingsList()
}
override fun showToastMessage(message: String, is_long: Boolean) {
Toast.makeText(
this,
message,
if (is_long) Toast.LENGTH_LONG else Toast.LENGTH_SHORT
).show()
}
override fun onSettingChanged() {
presenter.onSettingChanged()
}
fun onSettingsReset() {
// Prevents saving to a non-existent settings file
presenter.onSettingsReset()
// Reset the static memory representation of each setting
BooleanSetting.clear()
FloatSetting.clear()
IntSetting.clear()
StringSetting.clear()
// Delete settings file because the user may have changed values that do not exist in the UI
val settingsFile = SettingsFile.getSettingsFile(SettingsFile.FILE_NAME_CONFIG)
if (!settingsFile.delete()) {
throw IOException("Failed to delete $settingsFile")
}
showToastMessage(getString(R.string.settings_reset), true)
finish()
}
fun setToolbarTitle(title: String) {
binding.toolbarSettingsLayout.title = title
}
private val settingsFragment: SettingsFragment?
get() = supportFragmentManager.findFragmentByTag(FRAGMENT_TAG) as SettingsFragment?
private fun setInsets() {
ViewCompat.setOnApplyWindowInsetsListener(binding.frameContent) { view: View, windowInsets: WindowInsetsCompat ->
val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout())
view.updatePadding(
left = barInsets.left + cutoutInsets.left,
right = barInsets.right + cutoutInsets.right
)
val mlpAppBar = binding.appbarSettings.layoutParams as MarginLayoutParams
mlpAppBar.leftMargin = barInsets.left + cutoutInsets.left
mlpAppBar.rightMargin = barInsets.right + cutoutInsets.right
binding.appbarSettings.layoutParams = mlpAppBar
val mlpShade = binding.navigationBarShade.layoutParams as MarginLayoutParams
mlpShade.height = barInsets.bottom
binding.navigationBarShade.layoutParams = mlpShade
windowInsets
}
}
companion object {
private const val ARG_MENU_TAG = "menu_tag"
private const val ARG_GAME_ID = "game_id"
private const val FRAGMENT_TAG = "settings"
fun launch(context: Context, menuTag: String?, gameId: String?) {
val settings = Intent(context, SettingsActivity::class.java)
settings.putExtra(ARG_MENU_TAG, menuTag)
settings.putExtra(ARG_GAME_ID, gameId)
context.startActivity(settings)
}
}
}
@@ -0,0 +1,84 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.features.settings.ui
import android.content.Context
import android.os.Bundle
import android.text.TextUtils
import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.features.settings.model.Settings
import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
import org.yuzu.yuzu_emu.utils.DirectoryInitialization
import org.yuzu.yuzu_emu.utils.Log
import java.io.File
class SettingsActivityPresenter(private val activityView: SettingsActivityView) {
val settings: Settings get() = activityView.settings
private var shouldSave = false
private lateinit var menuTag: String
private lateinit var gameId: String
fun onCreate(savedInstanceState: Bundle?, menuTag: String, gameId: String) {
this.menuTag = menuTag
this.gameId = gameId
if (savedInstanceState != null) {
shouldSave = savedInstanceState.getBoolean(KEY_SHOULD_SAVE)
}
}
fun onStart() {
prepareDirectoriesIfNeeded()
}
private fun loadSettingsUI() {
if (!settings.isLoaded) {
if (!TextUtils.isEmpty(gameId)) {
settings.loadSettings(gameId, activityView)
} else {
settings.loadSettings(activityView)
}
}
activityView.showSettingsFragment(menuTag, false, gameId)
activityView.onSettingsFileLoaded()
}
private fun prepareDirectoriesIfNeeded() {
val configFile =
File(DirectoryInitialization.userDirectory + "/config/" + SettingsFile.FILE_NAME_CONFIG + ".ini")
if (!configFile.exists()) {
Log.error(DirectoryInitialization.userDirectory + "/config/" + SettingsFile.FILE_NAME_CONFIG + ".ini")
Log.error("yuzu config file could not be found!")
}
if (!DirectoryInitialization.areDirectoriesReady) {
DirectoryInitialization.start(activityView as Context)
}
loadSettingsUI()
}
fun onStop(finishing: Boolean) {
if (finishing && shouldSave) {
Log.debug("[SettingsActivity] Settings activity stopping. Saving settings to INI...")
settings.saveSettings(activityView)
}
NativeLibrary.reloadSettings()
}
fun onSettingChanged() {
shouldSave = true
}
fun onSettingsReset() {
shouldSave = false
}
fun saveState(outState: Bundle) {
outState.putBoolean(KEY_SHOULD_SAVE, shouldSave)
}
companion object {
private const val KEY_SHOULD_SAVE = "should_save"
}
}
@@ -0,0 +1,57 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.features.settings.ui
import org.yuzu.yuzu_emu.features.settings.model.Settings
/**
* Abstraction for the Activity that manages SettingsFragments.
*/
interface SettingsActivityView {
/**
* Show a new SettingsFragment.
*
* @param menuTag Identifier for the settings group that should be displayed.
* @param addToStack Whether or not this fragment should replace a previous one.
*/
fun showSettingsFragment(menuTag: String, addToStack: Boolean, gameId: String)
/**
* Called by a contained Fragment to get access to the Setting HashMap
* loaded from disk, so that each Fragment doesn't need to perform its own
* read operation.
*
* @return A HashMap of Settings.
*/
val settings: Settings
/**
* Called when a load operation completes.
*/
fun onSettingsFileLoaded()
/**
* Called when a load operation fails.
*/
fun onSettingsFileNotFound()
/**
* Display a popup text message on screen.
*
* @param message The contents of the onscreen message.
* @param is_long Whether this should be a long Toast or short one.
*/
fun showToastMessage(message: String, is_long: Boolean)
/**
* End the activity.
*/
fun finish()
/**
* Called by a containing Fragment to tell the Activity that a setting was changed;
* unless this has been called, the Activity will not save to disk.
*/
fun onSettingChanged()
}
@@ -0,0 +1,340 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.features.settings.ui
import android.content.Context
import android.content.DialogInterface
import android.icu.util.Calendar
import android.icu.util.TimeZone
import android.text.format.DateFormat
import android.view.LayoutInflater
import android.view.ViewGroup
import android.widget.TextView
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.setFragmentResultListener
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.datepicker.MaterialDatePicker
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.slider.Slider
import com.google.android.material.timepicker.MaterialTimePicker
import com.google.android.material.timepicker.TimeFormat
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.databinding.DialogSliderBinding
import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding
import org.yuzu.yuzu_emu.databinding.ListItemSettingSwitchBinding
import org.yuzu.yuzu_emu.databinding.ListItemSettingsHeaderBinding
import org.yuzu.yuzu_emu.features.settings.model.AbstractBooleanSetting
import org.yuzu.yuzu_emu.features.settings.model.AbstractFloatSetting
import org.yuzu.yuzu_emu.features.settings.model.AbstractIntSetting
import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting
import org.yuzu.yuzu_emu.features.settings.model.AbstractStringSetting
import org.yuzu.yuzu_emu.features.settings.model.FloatSetting
import org.yuzu.yuzu_emu.features.settings.model.view.*
import org.yuzu.yuzu_emu.features.settings.ui.viewholder.*
class SettingsAdapter(
private val fragmentView: SettingsFragmentView,
private val context: Context
) : RecyclerView.Adapter<SettingViewHolder?>(), DialogInterface.OnClickListener {
private var settings: ArrayList<SettingsItem>? = null
private var clickedItem: SettingsItem? = null
private var clickedPosition: Int
private var dialog: AlertDialog? = null
private var sliderProgress = 0
private var textSliderValue: TextView? = null
private var defaultCancelListener =
DialogInterface.OnClickListener { _: DialogInterface?, _: Int -> closeDialog() }
init {
clickedPosition = -1
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SettingViewHolder {
val inflater = LayoutInflater.from(parent.context)
return when (viewType) {
SettingsItem.TYPE_HEADER -> {
HeaderViewHolder(ListItemSettingsHeaderBinding.inflate(inflater), this)
}
SettingsItem.TYPE_SWITCH -> {
SwitchSettingViewHolder(ListItemSettingSwitchBinding.inflate(inflater), this)
}
SettingsItem.TYPE_SINGLE_CHOICE, SettingsItem.TYPE_STRING_SINGLE_CHOICE -> {
SingleChoiceViewHolder(ListItemSettingBinding.inflate(inflater), this)
}
SettingsItem.TYPE_SLIDER -> {
SliderViewHolder(ListItemSettingBinding.inflate(inflater), this)
}
SettingsItem.TYPE_SUBMENU -> {
SubmenuViewHolder(ListItemSettingBinding.inflate(inflater), this)
}
SettingsItem.TYPE_DATETIME_SETTING -> {
DateTimeViewHolder(ListItemSettingBinding.inflate(inflater), this)
}
SettingsItem.TYPE_RUNNABLE -> {
RunnableViewHolder(ListItemSettingBinding.inflate(inflater), this)
}
else -> {
// TODO: Create an error view since we can't return null now
HeaderViewHolder(ListItemSettingsHeaderBinding.inflate(inflater), this)
}
}
}
override fun onBindViewHolder(holder: SettingViewHolder, position: Int) {
holder.bind(getItem(position))
}
private fun getItem(position: Int): SettingsItem {
return settings!![position]
}
override fun getItemCount(): Int {
return if (settings != null) {
settings!!.size
} else {
0
}
}
override fun getItemViewType(position: Int): Int {
return getItem(position).type
}
fun setSettingsList(settings: ArrayList<SettingsItem>?) {
this.settings = settings
notifyDataSetChanged()
}
fun onBooleanClick(item: SwitchSetting, position: Int, checked: Boolean) {
val setting = item.setChecked(checked)
fragmentView.putSetting(setting)
fragmentView.onSettingChanged()
}
private fun onSingleChoiceClick(item: SingleChoiceSetting) {
clickedItem = item
val value = getSelectionForSingleChoiceValue(item)
dialog = MaterialAlertDialogBuilder(context)
.setTitle(item.nameId)
.setSingleChoiceItems(item.choicesId, value, this)
.show()
}
fun onSingleChoiceClick(item: SingleChoiceSetting, position: Int) {
clickedPosition = position
onSingleChoiceClick(item)
}
private fun onStringSingleChoiceClick(item: StringSingleChoiceSetting) {
clickedItem = item
dialog = MaterialAlertDialogBuilder(context)
.setTitle(item.nameId)
.setSingleChoiceItems(item.choicesId, item.selectValueIndex, this)
.show()
}
fun onStringSingleChoiceClick(item: StringSingleChoiceSetting, position: Int) {
clickedPosition = position
onStringSingleChoiceClick(item)
}
fun onDateTimeClick(item: DateTimeSetting, position: Int) {
clickedItem = item
clickedPosition = position
val storedTime = java.lang.Long.decode(item.value) * 1000
// Helper to extract hour and minute from epoch time
val calendar: Calendar = Calendar.getInstance()
calendar.timeInMillis = storedTime
calendar.timeZone = TimeZone.getTimeZone("UTC")
var timeFormat: Int = TimeFormat.CLOCK_12H
if (DateFormat.is24HourFormat(fragmentView.activityView as AppCompatActivity)) {
timeFormat = TimeFormat.CLOCK_24H
}
val datePicker: MaterialDatePicker<Long> = MaterialDatePicker.Builder.datePicker()
.setSelection(storedTime)
.setTitleText(R.string.select_rtc_date)
.build()
val timePicker: MaterialTimePicker = MaterialTimePicker.Builder()
.setTimeFormat(timeFormat)
.setHour(calendar.get(Calendar.HOUR_OF_DAY))
.setMinute(calendar.get(Calendar.MINUTE))
.setTitleText(R.string.select_rtc_time)
.build()
datePicker.addOnPositiveButtonClickListener {
timePicker.show(
(fragmentView.activityView as AppCompatActivity).supportFragmentManager,
"TimePicker"
)
}
timePicker.addOnPositiveButtonClickListener {
var epochTime: Long = datePicker.selection!! / 1000
epochTime += timePicker.hour.toLong() * 60 * 60
epochTime += timePicker.minute.toLong() * 60
val rtcString = epochTime.toString()
if (item.value != rtcString) {
fragmentView.onSettingChanged()
}
notifyItemChanged(clickedPosition)
val setting = item.setSelectedValue(rtcString)
fragmentView.putSetting(setting)
clickedItem = null
}
datePicker.show(
(fragmentView.activityView as AppCompatActivity).supportFragmentManager,
"DatePicker"
)
}
fun onSliderClick(item: SliderSetting, position: Int) {
clickedItem = item
clickedPosition = position
sliderProgress = item.selectedValue
val inflater = LayoutInflater.from(context)
val sliderBinding = DialogSliderBinding.inflate(inflater)
textSliderValue = sliderBinding.textValue
textSliderValue!!.text = sliderProgress.toString()
sliderBinding.textUnits.text = item.units
sliderBinding.slider.apply {
valueFrom = item.min.toFloat()
valueTo = item.max.toFloat()
value = sliderProgress.toFloat()
addOnChangeListener { _: Slider, value: Float, _: Boolean ->
sliderProgress = value.toInt()
textSliderValue!!.text = sliderProgress.toString()
}
}
dialog = MaterialAlertDialogBuilder(context)
.setTitle(item.nameId)
.setView(sliderBinding.root)
.setPositiveButton(android.R.string.ok, this)
.setNegativeButton(android.R.string.cancel, defaultCancelListener)
.setNeutralButton(R.string.slider_default) { dialog: DialogInterface, which: Int ->
sliderBinding.slider.value = item.defaultValue!!.toFloat()
onClick(dialog, which)
}
.show()
}
fun onSubmenuClick(item: SubmenuSetting) {
fragmentView.loadSubMenu(item.menuKey)
}
override fun onClick(dialog: DialogInterface, which: Int) {
when (clickedItem) {
is SingleChoiceSetting -> {
val scSetting = clickedItem as SingleChoiceSetting
val value = getValueForSingleChoiceSelection(scSetting, which)
if (scSetting.selectedValue != value) {
fragmentView.onSettingChanged()
}
// Get the backing Setting, which may be null (if for example it was missing from the file)
val setting = scSetting.setSelectedValue(value)
fragmentView.putSetting(setting)
closeDialog()
}
is StringSingleChoiceSetting -> {
val scSetting = clickedItem as StringSingleChoiceSetting
val value = scSetting.getValueAt(which)
if (scSetting.selectedValue != value) fragmentView.onSettingChanged()
val setting = scSetting.setSelectedValue(value!!)
fragmentView.putSetting(setting)
closeDialog()
}
is SliderSetting -> {
val sliderSetting = clickedItem as SliderSetting
if (sliderSetting.selectedValue != sliderProgress) {
fragmentView.onSettingChanged()
}
if (sliderSetting.setting is FloatSetting) {
val value = sliderProgress.toFloat()
val setting = sliderSetting.setSelectedValue(value)
fragmentView.putSetting(setting)
} else {
val setting = sliderSetting.setSelectedValue(sliderProgress)
fragmentView.putSetting(setting)
}
closeDialog()
}
}
clickedItem = null
sliderProgress = -1
}
fun onLongClick(setting: AbstractSetting, position: Int): Boolean {
MaterialAlertDialogBuilder(context)
.setMessage(R.string.reset_setting_confirmation)
.setPositiveButton(android.R.string.ok) { dialog: DialogInterface, which: Int ->
when (setting) {
is AbstractBooleanSetting -> setting.boolean = setting.defaultValue as Boolean
is AbstractFloatSetting -> setting.float = setting.defaultValue as Float
is AbstractIntSetting -> setting.int = setting.defaultValue as Int
is AbstractStringSetting -> setting.string = setting.defaultValue as String
}
notifyItemChanged(position)
fragmentView.onSettingChanged()
}
.setNegativeButton(android.R.string.cancel, null)
.show()
return true
}
fun closeDialog() {
if (dialog != null) {
if (clickedPosition != -1) {
notifyItemChanged(clickedPosition)
clickedPosition = -1
}
dialog!!.dismiss()
dialog = null
}
}
private fun getValueForSingleChoiceSelection(item: SingleChoiceSetting, which: Int): Int {
val valuesId = item.valuesId
return if (valuesId > 0) {
val valuesArray = context.resources.getIntArray(valuesId)
valuesArray[which]
} else {
which
}
}
private fun getSelectionForSingleChoiceValue(item: SingleChoiceSetting): Int {
val value = item.selectedValue
val valuesId = item.valuesId
if (valuesId > 0) {
val valuesArray = context.resources.getIntArray(valuesId)
for (index in valuesArray.indices) {
val current = valuesArray[index]
if (current == value) {
return index
}
}
} else {
return value
}
return -1
}
}
@@ -0,0 +1,122 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.features.settings.ui
import android.content.Context
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.updatePadding
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.LinearLayoutManager
import com.google.android.material.divider.MaterialDividerItemDecoration
import org.yuzu.yuzu_emu.databinding.FragmentSettingsBinding
import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting
import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
class SettingsFragment : Fragment(), SettingsFragmentView {
override var activityView: SettingsActivityView? = null
private val fragmentPresenter = SettingsFragmentPresenter(this)
private var settingsAdapter: SettingsAdapter? = null
private var _binding: FragmentSettingsBinding? = null
private val binding get() = _binding!!
override fun onAttach(context: Context) {
super.onAttach(context)
activityView = requireActivity() as SettingsActivityView
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val menuTag = requireArguments().getString(ARGUMENT_MENU_TAG)
val gameId = requireArguments().getString(ARGUMENT_GAME_ID)
fragmentPresenter.onCreate(menuTag!!, gameId!!)
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentSettingsBinding.inflate(layoutInflater)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
settingsAdapter = SettingsAdapter(this, requireActivity())
val dividerDecoration = MaterialDividerItemDecoration(requireContext(), LinearLayoutManager.VERTICAL)
dividerDecoration.isLastItemDecorated = false
binding.listSettings.apply {
adapter = settingsAdapter
layoutManager = LinearLayoutManager(activity)
addItemDecoration(dividerDecoration)
}
fragmentPresenter.onViewCreated()
setInsets()
}
override fun onDetach() {
super.onDetach()
activityView = null
if (settingsAdapter != null) {
settingsAdapter!!.closeDialog()
}
}
override fun showSettingsList(settingsList: ArrayList<SettingsItem>) {
settingsAdapter!!.setSettingsList(settingsList)
}
override fun loadSettingsList() {
fragmentPresenter.loadSettingsList()
}
override fun loadSubMenu(menuKey: String) {
activityView!!.showSettingsFragment(
menuKey,
true,
requireArguments().getString(ARGUMENT_GAME_ID)!!
)
}
override fun showToastMessage(message: String?, is_long: Boolean) {
activityView!!.showToastMessage(message!!, is_long)
}
override fun putSetting(setting: AbstractSetting) {
fragmentPresenter.putSetting(setting)
}
override fun onSettingChanged() {
activityView!!.onSettingChanged()
}
private fun setInsets() {
ViewCompat.setOnApplyWindowInsetsListener(binding.listSettings) { view: View, windowInsets: WindowInsetsCompat ->
val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
view.updatePadding(bottom = insets.bottom)
windowInsets
}
}
companion object {
private const val ARGUMENT_MENU_TAG = "menu_tag"
private const val ARGUMENT_GAME_ID = "game_id"
fun newInstance(menuTag: String?, gameId: String?): Fragment {
val fragment = SettingsFragment()
val arguments = Bundle()
arguments.putString(ARGUMENT_MENU_TAG, menuTag)
arguments.putString(ARGUMENT_GAME_ID, gameId)
fragment.arguments = arguments
return fragment
}
}
}
@@ -0,0 +1,465 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.features.settings.ui
import android.content.SharedPreferences
import android.os.Build
import android.text.TextUtils
import androidx.preference.PreferenceManager
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.features.settings.model.AbstractBooleanSetting
import org.yuzu.yuzu_emu.features.settings.model.AbstractIntSetting
import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting
import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
import org.yuzu.yuzu_emu.features.settings.model.IntSetting
import org.yuzu.yuzu_emu.features.settings.model.Settings
import org.yuzu.yuzu_emu.features.settings.model.StringSetting
import org.yuzu.yuzu_emu.features.settings.model.view.*
import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
import org.yuzu.yuzu_emu.fragments.ResetSettingsDialogFragment
import org.yuzu.yuzu_emu.utils.ThemeHelper
class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView) {
private var menuTag: String? = null
private lateinit var gameId: String
private var settingsList: ArrayList<SettingsItem>? = null
private val settingsActivity get() = fragmentView.activityView as SettingsActivity
private val settings get() = fragmentView.activityView!!.settings
private lateinit var preferences: SharedPreferences
fun onCreate(menuTag: String, gameId: String) {
this.gameId = gameId
this.menuTag = menuTag
}
fun onViewCreated() {
preferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
loadSettingsList()
}
fun putSetting(setting: AbstractSetting) {
if (setting.section == null) {
return
}
val section = settings.getSection(setting.section!!)!!
if (section.getSetting(setting.key!!) == null) {
section.putSetting(setting)
}
}
fun loadSettingsList() {
if (!TextUtils.isEmpty(gameId)) {
settingsActivity.setToolbarTitle("Game Settings: $gameId")
}
val sl = ArrayList<SettingsItem>()
if (menuTag == null) {
return
}
when (menuTag) {
SettingsFile.FILE_NAME_CONFIG -> addConfigSettings(sl)
Settings.SECTION_GENERAL -> addGeneralSettings(sl)
Settings.SECTION_SYSTEM -> addSystemSettings(sl)
Settings.SECTION_RENDERER -> addGraphicsSettings(sl)
Settings.SECTION_AUDIO -> addAudioSettings(sl)
Settings.SECTION_THEME -> addThemeSettings(sl)
Settings.SECTION_DEBUG -> addDebugSettings(sl)
else -> {
fragmentView.showToastMessage("Unimplemented menu", false)
return
}
}
settingsList = sl
fragmentView.showSettingsList(settingsList!!)
}
private fun addConfigSettings(sl: ArrayList<SettingsItem>) {
settingsActivity.setToolbarTitle(settingsActivity.getString(R.string.advanced_settings))
sl.apply {
add(
SubmenuSetting(
R.string.preferences_general,
0,
Settings.SECTION_GENERAL
)
)
add(
SubmenuSetting(
R.string.preferences_system,
0,
Settings.SECTION_SYSTEM
)
)
add(
SubmenuSetting(
R.string.preferences_graphics,
0,
Settings.SECTION_RENDERER
)
)
add(
SubmenuSetting(
R.string.preferences_audio,
0,
Settings.SECTION_AUDIO
)
)
add(
SubmenuSetting(
R.string.preferences_debug,
0,
Settings.SECTION_DEBUG
)
)
add(
RunnableSetting(
R.string.reset_to_default,
0,
false
) {
ResetSettingsDialogFragment().show(
settingsActivity.supportFragmentManager,
ResetSettingsDialogFragment.TAG
)
}
)
}
}
private fun addGeneralSettings(sl: ArrayList<SettingsItem>) {
settingsActivity.setToolbarTitle(settingsActivity.getString(R.string.preferences_general))
sl.apply {
add(
SwitchSetting(
IntSetting.RENDERER_USE_SPEED_LIMIT,
R.string.frame_limit_enable,
R.string.frame_limit_enable_description,
IntSetting.RENDERER_USE_SPEED_LIMIT.key,
IntSetting.RENDERER_USE_SPEED_LIMIT.defaultValue
)
)
add(
SliderSetting(
IntSetting.RENDERER_SPEED_LIMIT,
R.string.frame_limit_slider,
R.string.frame_limit_slider_description,
1,
200,
"%",
IntSetting.RENDERER_SPEED_LIMIT.key,
IntSetting.RENDERER_SPEED_LIMIT.defaultValue
)
)
add(
SingleChoiceSetting(
IntSetting.CPU_ACCURACY,
R.string.cpu_accuracy,
0,
R.array.cpuAccuracyNames,
R.array.cpuAccuracyValues,
IntSetting.CPU_ACCURACY.key,
IntSetting.CPU_ACCURACY.defaultValue
)
)
}
}
private fun addSystemSettings(sl: ArrayList<SettingsItem>) {
settingsActivity.setToolbarTitle(settingsActivity.getString(R.string.preferences_system))
sl.apply {
add(
SwitchSetting(
IntSetting.USE_DOCKED_MODE,
R.string.use_docked_mode,
R.string.use_docked_mode_description,
IntSetting.USE_DOCKED_MODE.key,
IntSetting.USE_DOCKED_MODE.defaultValue
)
)
add(
SingleChoiceSetting(
IntSetting.REGION_INDEX,
R.string.emulated_region,
0,
R.array.regionNames,
R.array.regionValues,
IntSetting.REGION_INDEX.key,
IntSetting.REGION_INDEX.defaultValue
)
)
add(
SingleChoiceSetting(
IntSetting.LANGUAGE_INDEX,
R.string.emulated_language,
0,
R.array.languageNames,
R.array.languageValues,
IntSetting.LANGUAGE_INDEX.key,
IntSetting.LANGUAGE_INDEX.defaultValue
)
)
add(
SwitchSetting(
BooleanSetting.USE_CUSTOM_RTC,
R.string.use_custom_rtc,
R.string.use_custom_rtc_description,
BooleanSetting.USE_CUSTOM_RTC.key,
BooleanSetting.USE_CUSTOM_RTC.defaultValue
)
)
add(
DateTimeSetting(
StringSetting.CUSTOM_RTC,
R.string.set_custom_rtc,
0,
StringSetting.CUSTOM_RTC.key,
StringSetting.CUSTOM_RTC.defaultValue
)
)
}
}
private fun addGraphicsSettings(sl: ArrayList<SettingsItem>) {
settingsActivity.setToolbarTitle(settingsActivity.getString(R.string.preferences_graphics))
sl.apply {
add(
SingleChoiceSetting(
IntSetting.RENDERER_ACCURACY,
R.string.renderer_accuracy,
0,
R.array.rendererAccuracyNames,
R.array.rendererAccuracyValues,
IntSetting.RENDERER_ACCURACY.key,
IntSetting.RENDERER_ACCURACY.defaultValue
)
)
add(
SingleChoiceSetting(
IntSetting.RENDERER_RESOLUTION,
R.string.renderer_resolution,
0,
R.array.rendererResolutionNames,
R.array.rendererResolutionValues,
IntSetting.RENDERER_RESOLUTION.key,
IntSetting.RENDERER_RESOLUTION.defaultValue
)
)
add(
SingleChoiceSetting(
IntSetting.RENDERER_VSYNC,
R.string.renderer_vsync,
0,
R.array.rendererVSyncNames,
R.array.rendererVSyncValues,
IntSetting.RENDERER_VSYNC.key,
IntSetting.RENDERER_VSYNC.defaultValue
)
)
add(
SingleChoiceSetting(
IntSetting.RENDERER_SCALING_FILTER,
R.string.renderer_scaling_filter,
0,
R.array.rendererScalingFilterNames,
R.array.rendererScalingFilterValues,
IntSetting.RENDERER_SCALING_FILTER.key,
IntSetting.RENDERER_SCALING_FILTER.defaultValue
)
)
add(
SingleChoiceSetting(
IntSetting.RENDERER_ANTI_ALIASING,
R.string.renderer_anti_aliasing,
0,
R.array.rendererAntiAliasingNames,
R.array.rendererAntiAliasingValues,
IntSetting.RENDERER_ANTI_ALIASING.key,
IntSetting.RENDERER_ANTI_ALIASING.defaultValue
)
)
add(
SingleChoiceSetting(
IntSetting.RENDERER_ASPECT_RATIO,
R.string.renderer_aspect_ratio,
0,
R.array.rendererAspectRatioNames,
R.array.rendererAspectRatioValues,
IntSetting.RENDERER_ASPECT_RATIO.key,
IntSetting.RENDERER_ASPECT_RATIO.defaultValue
)
)
add(
SwitchSetting(
IntSetting.RENDERER_USE_DISK_SHADER_CACHE,
R.string.use_disk_shader_cache,
R.string.use_disk_shader_cache_description,
IntSetting.RENDERER_USE_DISK_SHADER_CACHE.key,
IntSetting.RENDERER_USE_DISK_SHADER_CACHE.defaultValue
)
)
add(
SwitchSetting(
IntSetting.RENDERER_FORCE_MAX_CLOCK,
R.string.renderer_force_max_clock,
R.string.renderer_force_max_clock_description,
IntSetting.RENDERER_FORCE_MAX_CLOCK.key,
IntSetting.RENDERER_FORCE_MAX_CLOCK.defaultValue
)
)
add(
SwitchSetting(
IntSetting.RENDERER_ASYNCHRONOUS_SHADERS,
R.string.renderer_asynchronous_shaders,
R.string.renderer_asynchronous_shaders_description,
IntSetting.RENDERER_ASYNCHRONOUS_SHADERS.key,
IntSetting.RENDERER_ASYNCHRONOUS_SHADERS.defaultValue
)
)
}
}
private fun addAudioSettings(sl: ArrayList<SettingsItem>) {
settingsActivity.setToolbarTitle(settingsActivity.getString(R.string.preferences_audio))
sl.add(
SliderSetting(
IntSetting.AUDIO_VOLUME,
R.string.audio_volume,
R.string.audio_volume_description,
0,
100,
"%",
IntSetting.AUDIO_VOLUME.key,
IntSetting.AUDIO_VOLUME.defaultValue
)
)
}
private fun addThemeSettings(sl: ArrayList<SettingsItem>) {
settingsActivity.setToolbarTitle(settingsActivity.getString(R.string.preferences_theme))
sl.apply {
val theme: AbstractIntSetting = object : AbstractIntSetting {
override var int: Int
get() = preferences.getInt(Settings.PREF_THEME, 0)
set(value) {
preferences.edit()
.putInt(Settings.PREF_THEME, value)
.apply()
settingsActivity.recreate()
}
override val key: String? = null
override val section: String? = null
override val isRuntimeEditable: Boolean = false
override val valueAsString: String
get() = preferences.getInt(Settings.PREF_THEME, 0).toString()
override val defaultValue: Any = 0
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
add(
SingleChoiceSetting(
theme,
R.string.change_app_theme,
0,
R.array.themeEntriesA12,
R.array.themeValuesA12
)
)
} else {
add(
SingleChoiceSetting(
theme,
R.string.change_app_theme,
0,
R.array.themeEntries,
R.array.themeValues
)
)
}
val themeMode: AbstractIntSetting = object : AbstractIntSetting {
override var int: Int
get() = preferences.getInt(Settings.PREF_THEME_MODE, -1)
set(value) {
preferences.edit()
.putInt(Settings.PREF_THEME_MODE, value)
.apply()
ThemeHelper.setThemeMode(settingsActivity)
}
override val key: String? = null
override val section: String? = null
override val isRuntimeEditable: Boolean = false
override val valueAsString: String
get() = preferences.getInt(Settings.PREF_THEME_MODE, -1).toString()
override val defaultValue: Any = -1
}
add(
SingleChoiceSetting(
themeMode,
R.string.change_theme_mode,
0,
R.array.themeModeEntries,
R.array.themeModeValues
)
)
val blackBackgrounds: AbstractBooleanSetting = object : AbstractBooleanSetting {
override var boolean: Boolean
get() = preferences.getBoolean(Settings.PREF_BLACK_BACKGROUNDS, false)
set(value) {
preferences.edit()
.putBoolean(Settings.PREF_BLACK_BACKGROUNDS, value)
.apply()
settingsActivity.recreate()
}
override val key: String? = null
override val section: String? = null
override val isRuntimeEditable: Boolean = false
override val valueAsString: String
get() = preferences.getBoolean(Settings.PREF_BLACK_BACKGROUNDS, false)
.toString()
override val defaultValue: Any = false
}
add(
SwitchSetting(
blackBackgrounds,
R.string.use_black_backgrounds,
R.string.use_black_backgrounds_description
)
)
}
}
private fun addDebugSettings(sl: ArrayList<SettingsItem>) {
settingsActivity.setToolbarTitle(settingsActivity.getString(R.string.preferences_debug))
sl.apply {
add(
SingleChoiceSetting(
IntSetting.RENDERER_BACKEND,
R.string.renderer_api,
0,
R.array.rendererApiNames,
R.array.rendererApiValues,
IntSetting.RENDERER_BACKEND.key,
IntSetting.RENDERER_BACKEND.defaultValue
)
)
add(
SwitchSetting(
IntSetting.RENDERER_DEBUG,
R.string.renderer_debug,
R.string.renderer_debug_description,
IntSetting.RENDERER_DEBUG.key,
IntSetting.RENDERER_DEBUG.defaultValue
)
)
}
}
}
@@ -0,0 +1,58 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.features.settings.ui
import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting
import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
/**
* Abstraction for a screen showing a list of settings. Instances of
* this type of view will each display a layer of the setting hierarchy.
*/
interface SettingsFragmentView {
/**
* Pass an ArrayList to the View so that it can be displayed on screen.
*
* @param settingsList The result of converting the HashMap to an ArrayList
*/
fun showSettingsList(settingsList: ArrayList<SettingsItem>)
/**
* Instructs the Fragment to load the settings screen.
*/
fun loadSettingsList()
/**
* @return The Fragment's containing activity.
*/
val activityView: SettingsActivityView?
/**
* Tell the Fragment to tell the containing Activity to show a new
* Fragment containing a submenu of settings.
*
* @param menuKey Identifier for the settings group that should be shown.
*/
fun loadSubMenu(menuKey: String)
/**
* Tell the Fragment to tell the containing activity to display a toast message.
*
* @param message Text to be shown in the Toast
* @param is_long Whether this should be a long Toast or short one.
*/
fun showToastMessage(message: String?, is_long: Boolean)
/**
* Have the fragment add a setting to the HashMap.
*
* @param setting The (possibly previously missing) new setting.
*/
fun putSetting(setting: AbstractSetting)
/**
* Have the fragment tell the containing Activity that a setting was modified.
*/
fun onSettingChanged()
}

Some files were not shown because too many files have changed in this diff Show More