mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-08-15 05:15:14 +00:00
Compare commits
40 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7ff5b69c23 | |||
| c05d999225 | |||
| 71d3dd67d3 | |||
| 9b2fba1275 | |||
| 19eab4d7aa | |||
| 50a6f331cf | |||
| 9c13c71da8 | |||
| 7d53849cd3 | |||
| fd0c5655c4 | |||
| 88f0e7862a | |||
| d99a8c65cc | |||
| 876884e783 | |||
| 028050cf04 | |||
| 1f787ffc39 | |||
| 148dc7b480 | |||
| 612a203ab2 | |||
| 5e927199c5 | |||
| ac99ea96da | |||
| d1b7824443 | |||
| 34fa39eae8 | |||
| 9ace6742d7 | |||
| 79f29abcba | |||
| 3ce5463d2d | |||
| cae70c30fa | |||
| bcceced96d | |||
| 82e374f66c | |||
| 6e76014824 | |||
| bb71ace365 | |||
| 21f9db1c27 | |||
| b4a485e244 | |||
| 81a344f3db | |||
| c0fbb2526d | |||
| c3afd2fabd | |||
| ee2891c55e | |||
| dd91b41a78 | |||
| e9f4541069 | |||
| cf7086de7c | |||
| 8e14f07a69 | |||
| 0b179517b3 | |||
| 7a8176f63f |
@@ -1,10 +1,9 @@
|
||||
name: tx-pull
|
||||
|
||||
on:
|
||||
# monday, wednesday, saturday at 2pm
|
||||
# tuesday, saturday at 2pm
|
||||
schedule:
|
||||
cron:
|
||||
- '0 14 * * 1,3,6'
|
||||
- cron: '0 14 * * 2,6'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
@@ -59,4 +58,3 @@ jobs:
|
||||
-H 'Authorization: Bearer ${{ secrets.CI_FJ_TOKEN }}' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d "@data.json" --fail
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
name: update-deps
|
||||
|
||||
on:
|
||||
# saturday at noon
|
||||
schedule:
|
||||
- cron: '0 12 * * 6'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
tx-update:
|
||||
runs-on: source
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Update deps
|
||||
run: |
|
||||
git config --local user.name "Eden CI"
|
||||
git config --local user.email "ci@eden-emu.dev"
|
||||
git config --local user.signingkey "D57652791BB25D2A"
|
||||
git config --local push.autoSetupRemote true
|
||||
|
||||
git remote set-url origin ci:eden-emu/eden.git
|
||||
|
||||
DATE=$(date +"%b %d")
|
||||
echo "DATE=$DATE" >> "$GITHUB_ENV"
|
||||
|
||||
git switch -c update-deps-$DATE
|
||||
tools/cpmutil.sh package update -ac
|
||||
git push
|
||||
|
||||
- name: Create PR
|
||||
run: |
|
||||
TITLE="[externals] Dependency update for $DATE"
|
||||
BODY="$(git show -s --format='%b')"
|
||||
BASE=master
|
||||
HEAD=update-deps-$DATE
|
||||
|
||||
cat << EOF > data.json
|
||||
{
|
||||
"base": "$BASE",
|
||||
"body": "$BODY",
|
||||
"head": "$HEAD",
|
||||
"title": "$TITLE"
|
||||
}
|
||||
EOF
|
||||
|
||||
curl -X 'POST' \
|
||||
'https://git.eden-emu.dev/api/v1/repos/eden-emu/eden/pulls' \
|
||||
-H 'accept: application/json' \
|
||||
-H 'Authorization: Bearer ${{ secrets.CI_FJ_TOKEN }}' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d "@data.json" --fail
|
||||
@@ -37,10 +37,10 @@ set(GIT_DESC ${BUILD_VERSION})
|
||||
|
||||
# Auto-updater metadata! Must somewhat mirror GitHub API endpoint
|
||||
if (NIGHTLY_BUILD)
|
||||
set(BUILD_AUTO_UPDATE_WEBSITE "https://github.com")
|
||||
set(BUILD_AUTO_UPDATE_API "api.github.com")
|
||||
set(BUILD_AUTO_UPDATE_API_PATH "/repos/")
|
||||
set(BUILD_AUTO_UPDATE_REPO "Eden-CI/Nightly")
|
||||
set(BUILD_AUTO_UPDATE_WEBSITE "https://git.eden-emu.dev")
|
||||
set(BUILD_AUTO_UPDATE_API "git.eden-emu.dev")
|
||||
set(BUILD_AUTO_UPDATE_API_PATH "/api/v1/repos/")
|
||||
set(BUILD_AUTO_UPDATE_REPO "eden-ci/nightly")
|
||||
set(REPO_NAME "Eden Nightly")
|
||||
else()
|
||||
set(BUILD_AUTO_UPDATE_WEBSITE "https://git.eden-emu.dev")
|
||||
|
||||
+89
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 19 KiB |
+1
@@ -0,0 +1 @@
|
||||
#43fcfcff
|
||||
Vendored
+403
-413
File diff suppressed because it is too large
Load Diff
Vendored
+393
-403
File diff suppressed because it is too large
Load Diff
Vendored
+393
-403
File diff suppressed because it is too large
Load Diff
Vendored
+393
-403
File diff suppressed because it is too large
Load Diff
Vendored
+393
-403
File diff suppressed because it is too large
Load Diff
Vendored
+393
-403
File diff suppressed because it is too large
Load Diff
Vendored
+467
-477
File diff suppressed because it is too large
Load Diff
Vendored
+393
-403
File diff suppressed because it is too large
Load Diff
Vendored
+393
-403
File diff suppressed because it is too large
Load Diff
Vendored
+393
-403
File diff suppressed because it is too large
Load Diff
Vendored
+393
-403
File diff suppressed because it is too large
Load Diff
Vendored
+553
-547
File diff suppressed because it is too large
Load Diff
Vendored
+393
-403
File diff suppressed because it is too large
Load Diff
Vendored
+393
-403
File diff suppressed because it is too large
Load Diff
Vendored
+398
-408
File diff suppressed because it is too large
Load Diff
Vendored
+393
-403
File diff suppressed because it is too large
Load Diff
Vendored
+393
-403
File diff suppressed because it is too large
Load Diff
Vendored
+393
-403
File diff suppressed because it is too large
Load Diff
Vendored
+393
-403
File diff suppressed because it is too large
Load Diff
Vendored
+393
-403
File diff suppressed because it is too large
Load Diff
Vendored
+393
-403
File diff suppressed because it is too large
Load Diff
Vendored
+398
-408
File diff suppressed because it is too large
Load Diff
Vendored
+395
-405
File diff suppressed because it is too large
Load Diff
Vendored
+393
-403
File diff suppressed because it is too large
Load Diff
Vendored
+393
-403
File diff suppressed because it is too large
Load Diff
Vendored
+397
-407
File diff suppressed because it is too large
Load Diff
Vendored
+393
-403
File diff suppressed because it is too large
Load Diff
+1
-1
@@ -334,7 +334,7 @@ pacman -Syuu --needed --noconfirm $packages
|
||||
<summary>HaikuOS</summary>
|
||||
|
||||
```sh
|
||||
pkgman install git cmake patch libfmt_devel nlohmann_json lz4_devel opus_devel boost1.89_devel vulkan_devel qt6_base_devel qt6_declarative_devel libsdl2_devel ffmpeg7_devel libx11_devel enet_devel catch2_devel quazip1_qt5_devel qt6_5compat_devel glslang qt6_devel qt6_charts_devel
|
||||
pkgman install git cmake patch libfmt_devel nlohmann_json lz4_devel opus_devel boost1.90_devel vulkan_devel qt6_base_devel qt6_declarative_devel libsdl2_devel ffmpeg7_devel libx11_devel enet_devel catch2_devel quazip1_qt5_devel qt6_5compat_devel glslang qt6_devel qt6_charts_devel
|
||||
```
|
||||
|
||||
[Caveats](./Caveats.md#haikuos).
|
||||
|
||||
@@ -16,6 +16,7 @@ This contains documentation created by developers. This contains build instructi
|
||||
- **[Dynarmic](./dynarmic)**
|
||||
- **[Cross compilation](./CrossCompile.md)**
|
||||
- **[Driver Bugs](./DriverBugs.md)**
|
||||
- **[Building Older Commits](./build/OlderCommits.md)**
|
||||
|
||||
## Policies
|
||||
|
||||
|
||||
Vendored
+40
@@ -0,0 +1,40 @@
|
||||
# Building Older Commits
|
||||
|
||||
Bisecting and debugging older versions of Eden can be difficult, as many of our submodules have been deleted or removed. However, work has been done to make this process as simple as possible for users.
|
||||
|
||||
## Script
|
||||
|
||||
Copy the following script and store it in `fix.sh`:
|
||||
|
||||
```sh
|
||||
#!/bin/sh -e
|
||||
|
||||
git -C externals/discord-rpc checkout 0d8b2d6a37c6e47d62b37caa14708bf747c883bb
|
||||
git add externals/discord-rpc
|
||||
|
||||
git -C externals/dynarmic checkout 05b7ba50588d1004e23ef91f1bda8be234be68f4
|
||||
git add externals/dynarmic
|
||||
|
||||
git -C externals/mbedtls checkout ce4f81f4a926a0e0dcadd0128e016baba416e8ea
|
||||
git add externals/mbedtls
|
||||
|
||||
git -C externals/oboe checkout e4f06f2143eb0173bf4a2bd15aae5e8cc3179405
|
||||
git add externals/oboe
|
||||
|
||||
git -C externals/sirit checkout b870b062998244231a4f08004d3b25151732c5c5
|
||||
git add externals/sirit
|
||||
```
|
||||
|
||||
Then, run `chmod +x fix.sh`
|
||||
|
||||
## Submodules
|
||||
|
||||
To check out submodules successfully, use this order of operations:
|
||||
|
||||
```sh
|
||||
git submodule update --init --recursive --depth 1 --jobs 8 --progress
|
||||
./fix.sh
|
||||
git submodule update --init --recursive --depth 1 --jobs 8 --progress
|
||||
```
|
||||
|
||||
And you should be good to go! If you check out a different commit that changes submodule commits, run the above command list again.
|
||||
+35
-15
@@ -56,7 +56,7 @@ Use this when you need to connect to a multiplayer room for LDN functionality in
|
||||
- Multiplayer Options Configured in Eden Settings
|
||||
- Network Access
|
||||
|
||||
## Steps
|
||||
### Steps
|
||||
There are 2 primary methods that you can use to connect to an existing room, depending on how the room is hosted.
|
||||
|
||||
- Joining a Public Lobby
|
||||
@@ -70,7 +70,7 @@ There are 2 primary methods that you can use to connect to an existing room, dep
|
||||
|
||||
</aside>
|
||||
|
||||
### Joining a Public Lobby
|
||||
## Joining a Public Lobby
|
||||
1. Open Eden and navigate to *Multiplayer → Browse Public Game Lobby*.
|
||||
2. The **Public Room Browser** will now open and display a list of publicly accessible rooms. Find one you want to connect to and double click it.
|
||||
|
||||
@@ -90,7 +90,7 @@ If the hoster has not made the lobby public, or you don't want to find it in the
|
||||
|
||||
---
|
||||
|
||||
# Hosting a Multiplayer Room
|
||||
## Hosting a Multiplayer Room
|
||||
Use this guide for when you want to host a multiplayer lobby to play with others in Eden. In order to have someone access the room from outside your local network, see the *Access Your Multiplayer Room Externally* section for next steps.
|
||||
|
||||
**Click [Here](https://evilperson1337.notion.site/Hosting-a-Multiplayer-Room-2c357c2edaf6819481dbe8a99926cea2) for a version of this guide with images & visual elements.**
|
||||
@@ -100,7 +100,7 @@ Use this guide for when you want to host a multiplayer lobby to play with others
|
||||
- Network Access
|
||||
- Ability to allow programs through the firewall on your device.
|
||||
|
||||
## Steps
|
||||
### Steps
|
||||
1. Open Eden and navigate to *Emulation → Multiplayer → Create Room.*
|
||||
2. Fill out the following information in the popup dialog box.
|
||||
|
||||
@@ -120,7 +120,7 @@ Use this guide for when you want to host a multiplayer lobby to play with others
|
||||
|
||||
---
|
||||
|
||||
# Access Your Multiplayer Room Externally
|
||||
## Access Your Multiplayer Room Externally
|
||||
Quite often the person with whom you want to play is located off of your internal network (LAN). If you want to host a room and play with them you will need to get your devices to communicate with each other. This guide will go over your options on how to do this so that you can play together.
|
||||
|
||||
**Click [Here](https://evilperson1337.notion.site/Access-Your-Multiplayer-Room-Externally-2c357c2edaf681c0ab2ce2ee624d809d) for a version of this guide with images & visual elements.**
|
||||
@@ -129,9 +129,9 @@ Quite often the person with whom you want to play is located off of your interna
|
||||
- Eden set up and Functioning
|
||||
- Network Access
|
||||
|
||||
## Options
|
||||
### Options
|
||||
|
||||
### Port Forwarding
|
||||
#### Port Forwarding
|
||||
|
||||
- **Difficulty Level**: High
|
||||
|
||||
@@ -148,8 +148,9 @@ The process works by creating a static mapping—often called a “port-forward
|
||||
|
||||
For our purposes we would pick the port we want to expose (*e.g. 24872*) and we would access our router's configuration and create a port-forward rule to send the traffic from an external connection to your local machine over our specified port (*24872)*. The exact way to do so, varies greatly by router manufacturer - and sometimes require contacting your ISP to do so depending on your agreement. You can look up your router on [*portforward.com*](https://portforward.com/router.htm) which may have instructions on how to do so for your specific equipment. If it is not there, you will have to use Google/ChatGPT to determine the steps for your equipment.
|
||||
|
||||
Remember you can't have one port open for multiple devices at the same time - you must only host from one device (or do more convoluted networking which we will not cover here).
|
||||
|
||||
### Use a Tunnelling Service
|
||||
#### Use a Tunnelling Service
|
||||
- **Difficulty Level**: Easy
|
||||
|
||||
<aside>
|
||||
@@ -167,7 +168,7 @@ For our purposes we would spawn the listener for the port that way chose when ho
|
||||
- [*Playit.GG*](https://playit.gg/)
|
||||
|
||||
|
||||
### Use a VPN Service
|
||||
#### Use a VPN Service
|
||||
|
||||
- **Difficulty**: Easy
|
||||
|
||||
@@ -189,7 +190,7 @@ The VPN solution is a good compromise between the tunnelling solution and port f
|
||||
|
||||
---
|
||||
|
||||
# Finding the Server Information for a Multiplayer Room
|
||||
## Finding the Server Information for a Multiplayer Room
|
||||
Use this guide when you need to determine the connection information for the Public Multiplayer Lobby you are connected to.
|
||||
|
||||
**Click [Here](https://evilperson1337.notion.site/Finding-the-Server-Information-for-a-Multiplayer-Room-2c557c2edaf6809e94e8ed3429b9eb26) for a version of this guide with images & visual elements.**
|
||||
@@ -198,7 +199,7 @@ Use this guide when you need to determine the connection information for the Pub
|
||||
- Eden set up and configured
|
||||
- Internet Access
|
||||
|
||||
## Steps
|
||||
### Steps
|
||||
|
||||
### Method 1: Grabbing the Address from the Log File
|
||||
1. Open Eden and Connect to the room you want to identify.
|
||||
@@ -222,7 +223,7 @@ Use this guide when you need to determine the connection information for the Pub
|
||||
2. Open the terminal supported by your operating system.
|
||||
3. Run one of the following commands, replacing *<Name>* with the name of the server from step 1.
|
||||
|
||||
### PowerShell Command [Windows Users]
|
||||
#### PowerShell Command [Windows Users]
|
||||
|
||||
```powershell
|
||||
# Calls the API to get the address and port information
|
||||
@@ -235,7 +236,7 @@ Use this guide when you need to determine the connection information for the Pub
|
||||
#}
|
||||
```
|
||||
|
||||
### CURL Command [MacOS/Linux Users] **Requires jq*
|
||||
#### CURL Command [MacOS/Linux Users] **Requires jq*
|
||||
|
||||
```bash
|
||||
# Calls the API to get the address and port information
|
||||
@@ -252,7 +253,7 @@ Use this guide when you need to determine the connection information for the Pub
|
||||
|
||||
---
|
||||
|
||||
# Multiplayer for Local Co-Op Games
|
||||
## Multiplayer for Local Co-Op Games
|
||||
Use this guide when you want to play with a friend on a different system for games that only support local co-op.
|
||||
|
||||
**Click [Here](https://evilperson1337.notion.site/Multiplayer-for-Local-Co-Op-Games-2c657c2edaf680c59975ec6b52022a2d) for a version of this guide with images & visual elements.**
|
||||
@@ -271,7 +272,7 @@ In either situation at its core, we are emulating an input device on the host ma
|
||||
- Parsec is free to use for personal, non-commercial use. For instructions on how to set up an account and install the client you should refer to the Parsec documentation on it's site.
|
||||
- Parsec client installed on your machine and remote (friend's) machine
|
||||
|
||||
## Steps
|
||||
### Steps
|
||||
|
||||
<aside>
|
||||
|
||||
@@ -294,3 +295,22 @@ This guide will assume you are the one hosting the game and go over things *Pars
|
||||
10. Set up the remote player's controller.
|
||||
11. Hit **OK** to apply the changes.
|
||||
12. Launch the game you want to play and enter the co-op mode. How this works depends on the game, so you will have to look in the menus or online to find out.
|
||||
|
||||
## Metaserver troubleshooting
|
||||
|
||||
If you can't connect to the metaserver, it's likely your ISP is blocking the requests.
|
||||
|
||||
### Linux and Steamdeck
|
||||
|
||||
Most Linux systems and Steamdeck should allow to modify the base `/etc/hosts` file, this should fix the DNS lookup issue; hence add the following to said file:
|
||||
```
|
||||
28.165.181.135 api.ynet-fun.xyz api.ynet-fun.xyz
|
||||
```
|
||||
|
||||
### Zapret
|
||||
|
||||
In `lists/list-general.txt` add the following:
|
||||
```
|
||||
api.ynet-fun.xyz
|
||||
ynet-fun.xyz
|
||||
```
|
||||
|
||||
Vendored
+6
-3
@@ -62,6 +62,12 @@ endif()
|
||||
# unordered_dense
|
||||
AddJsonPackage(unordered-dense)
|
||||
|
||||
# httplib
|
||||
if (IOS)
|
||||
set(HTTPLIB_USE_BROTLI_IF_AVAILABLE OFF)
|
||||
endif()
|
||||
AddJsonPackage(httplib)
|
||||
|
||||
if (YUZU_STATIC_ROOM)
|
||||
return()
|
||||
endif()
|
||||
@@ -227,9 +233,6 @@ if (VulkanMemoryAllocator_ADDED)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# httplib
|
||||
AddJsonPackage(httplib)
|
||||
|
||||
# cpp-jwt
|
||||
if (ENABLE_WEB_SERVICE OR ENABLE_UPDATE_CHECKER)
|
||||
AddJsonPackage(cpp-jwt)
|
||||
|
||||
Vendored
+3
-1
@@ -30,13 +30,15 @@
|
||||
"tag": "v%VERSION%",
|
||||
"hash": "5efa8140aadffe105dcf39935b732476e95755f6c7473ada3d0b64df2bc02c557633ae3948a25b45e1cf67e89a3ff6329fb30362e4ac033b9a1d1e453aa2eded",
|
||||
"git_version": "0.37.0",
|
||||
"version": "0.18.7",
|
||||
"find_args": "MODULE GLOBAL",
|
||||
"patches": [
|
||||
"0001-mingw.patch",
|
||||
"0002-fix-zstd.patch"
|
||||
],
|
||||
"options": [
|
||||
"HTTPLIB_REQUIRE_OPENSSL ON"
|
||||
"HTTPLIB_REQUIRE_OPENSSL ON",
|
||||
"HTTPLIB_DISABLE_MACOSX_AUTOMATIC_ROOT_CERTIFICATES ON"
|
||||
]
|
||||
},
|
||||
"cpp-jwt": {
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@
|
||||
include_directories(.)
|
||||
|
||||
# Dynarmic
|
||||
if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64 AND NOT YUZU_STATIC_ROOM)
|
||||
if ((ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64 OR ARCHITECTURE_riscv64) AND NOT YUZU_STATIC_ROOM)
|
||||
add_subdirectory(dynarmic)
|
||||
add_library(dynarmic::dynarmic ALIAS dynarmic)
|
||||
endif()
|
||||
|
||||
@@ -25,6 +25,11 @@ import android.hardware.SensorEventListener
|
||||
import android.hardware.SensorManager
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import androidx.navigation.NavOptions
|
||||
import org.yuzu.yuzu_emu.fragments.EmulationFragment
|
||||
import org.yuzu.yuzu_emu.utils.CustomSettingsHandler
|
||||
import android.util.Rational
|
||||
import android.view.InputDevice
|
||||
import android.view.KeyEvent
|
||||
@@ -87,6 +92,28 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener, InputManager
|
||||
private val emulationViewModel: EmulationViewModel by viewModels()
|
||||
|
||||
private var foregroundService: Intent? = null
|
||||
private val mainHandler = Handler(Looper.getMainLooper())
|
||||
private var pendingRomSwapIntent: Intent? = null
|
||||
private var isWaitingForRomSwapStop = false
|
||||
private var romSwapNativeStopped = false
|
||||
private var romSwapThreadStopped = false
|
||||
private var romSwapGeneration = 0
|
||||
private var hasEmulationSession = processHasEmulationSession
|
||||
private val romSwapStopTimeoutRunnable = Runnable { onRomSwapStopTimeout() }
|
||||
|
||||
private fun onRomSwapStopTimeout() {
|
||||
if (!isWaitingForRomSwapStop) {
|
||||
return
|
||||
}
|
||||
Log.warning("[EmulationActivity] ROM swap stop timed out; retrying native stop and continuing to wait")
|
||||
NativeLibrary.stopEmulation()
|
||||
scheduleRomSwapStopTimeout()
|
||||
}
|
||||
|
||||
private fun scheduleRomSwapStopTimeout() {
|
||||
mainHandler.removeCallbacks(romSwapStopTimeoutRunnable)
|
||||
mainHandler.postDelayed(romSwapStopTimeoutRunnable, ROM_SWAP_STOP_TIMEOUT_MS)
|
||||
}
|
||||
|
||||
override fun attachBaseContext(base: Context) {
|
||||
super.attachBaseContext(YuzuApplication.applyLanguage(base))
|
||||
@@ -128,9 +155,29 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener, InputManager
|
||||
binding = ActivityEmulationBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
|
||||
val launchIntent = Intent(intent)
|
||||
val shouldDeferLaunchForSwap = hasEmulationSession && isSwapIntent(launchIntent)
|
||||
if (shouldDeferLaunchForSwap) {
|
||||
Log.info("[EmulationActivity] onCreate detected existing session; deferring new game setup for swap")
|
||||
emulationViewModel.setIsEmulationStopping(true)
|
||||
emulationViewModel.setEmulationStopped(false)
|
||||
}
|
||||
|
||||
val navHostFragment =
|
||||
supportFragmentManager.findFragmentById(R.id.fragment_container) as NavHostFragment
|
||||
navHostFragment.navController.setGraph(R.navigation.emulation_navigation, intent.extras)
|
||||
val initialArgs = if (shouldDeferLaunchForSwap) {
|
||||
Bundle(intent.extras ?: Bundle()).apply {
|
||||
processSessionGame?.let { putParcelable("game", it) }
|
||||
}
|
||||
} else {
|
||||
intent.extras
|
||||
}
|
||||
navHostFragment.navController.setGraph(R.navigation.emulation_navigation, initialArgs)
|
||||
if (shouldDeferLaunchForSwap) {
|
||||
mainHandler.post {
|
||||
handleSwapIntent(launchIntent)
|
||||
}
|
||||
}
|
||||
|
||||
isActivityRecreated = savedInstanceState != null
|
||||
|
||||
@@ -210,6 +257,7 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener, InputManager
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
mainHandler.removeCallbacks(romSwapStopTimeoutRunnable)
|
||||
super.onDestroy()
|
||||
inputManager.unregisterInputDeviceListener(this)
|
||||
stopForegroundService(this)
|
||||
@@ -228,17 +276,123 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener, InputManager
|
||||
|
||||
override fun onNewIntent(intent: Intent) {
|
||||
super.onNewIntent(intent)
|
||||
setIntent(intent)
|
||||
|
||||
// Reset navigation graph with new intent data to recreate EmulationFragment
|
||||
val navHostFragment =
|
||||
supportFragmentManager.findFragmentById(R.id.fragment_container) as NavHostFragment
|
||||
navHostFragment.navController.setGraph(R.navigation.emulation_navigation, intent.extras)
|
||||
|
||||
handleSwapIntent(intent)
|
||||
nfcReader.onNewIntent(intent)
|
||||
InputHandler.updateControllerData()
|
||||
}
|
||||
|
||||
private fun isSwapIntent(intent: Intent): Boolean {
|
||||
return when {
|
||||
intent.getBooleanExtra(EXTRA_OVERLAY_GAMELESS_EDIT_MODE, false) -> false
|
||||
intent.action == CustomSettingsHandler.CUSTOM_CONFIG_ACTION -> true
|
||||
intent.data != null -> true
|
||||
else -> {
|
||||
val extras = intent.extras
|
||||
extras != null &&
|
||||
BundleCompat.getParcelable(extras, EXTRA_SELECTED_GAME, Game::class.java) != null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleSwapIntent(intent: Intent) {
|
||||
if (!isSwapIntent(intent)) {
|
||||
return
|
||||
}
|
||||
|
||||
pendingRomSwapIntent = Intent(intent)
|
||||
|
||||
if (!isWaitingForRomSwapStop) {
|
||||
Log.info("[EmulationActivity] Begin ROM swap: data=${intent.data}")
|
||||
isWaitingForRomSwapStop = true
|
||||
romSwapNativeStopped = false
|
||||
romSwapThreadStopped = false
|
||||
romSwapGeneration += 1
|
||||
val thisSwapGeneration = romSwapGeneration
|
||||
emulationViewModel.setIsEmulationStopping(true)
|
||||
emulationViewModel.setEmulationStopped(false)
|
||||
val navHostFragment =
|
||||
supportFragmentManager.findFragmentById(R.id.fragment_container) as? NavHostFragment
|
||||
val childFragmentManager = navHostFragment?.childFragmentManager
|
||||
val stoppingFragmentForSwap =
|
||||
(childFragmentManager?.primaryNavigationFragment as? EmulationFragment) ?:
|
||||
childFragmentManager
|
||||
?.fragments
|
||||
?.asReversed()
|
||||
?.firstOrNull {
|
||||
it is EmulationFragment &&
|
||||
it.isAdded &&
|
||||
it.view != null &&
|
||||
!it.isRemoving
|
||||
} as? EmulationFragment
|
||||
|
||||
val hasSessionForSwap = hasEmulationSession || stoppingFragmentForSwap != null
|
||||
|
||||
if (!hasSessionForSwap) {
|
||||
romSwapNativeStopped = true
|
||||
romSwapThreadStopped = true
|
||||
} else {
|
||||
if (stoppingFragmentForSwap != null) {
|
||||
stoppingFragmentForSwap.stopForRomSwap()
|
||||
stoppingFragmentForSwap.notifyWhenEmulationThreadStops {
|
||||
if (!isWaitingForRomSwapStop || romSwapGeneration != thisSwapGeneration) {
|
||||
return@notifyWhenEmulationThreadStops
|
||||
}
|
||||
romSwapThreadStopped = true
|
||||
Log.info("[EmulationActivity] ROM swap thread stop acknowledged")
|
||||
launchPendingRomSwap(force = false)
|
||||
}
|
||||
} else {
|
||||
Log.warning("[EmulationActivity] ROM swap stop target fragment not found; requesting native stop")
|
||||
romSwapThreadStopped = true
|
||||
NativeLibrary.stopEmulation()
|
||||
}
|
||||
|
||||
scheduleRomSwapStopTimeout()
|
||||
}
|
||||
}
|
||||
|
||||
launchPendingRomSwap(force = false)
|
||||
}
|
||||
|
||||
private fun launchPendingRomSwap(force: Boolean) {
|
||||
if (!isWaitingForRomSwapStop) {
|
||||
return
|
||||
}
|
||||
if (!force && (!romSwapNativeStopped || !romSwapThreadStopped)) {
|
||||
return
|
||||
}
|
||||
val swapIntent = pendingRomSwapIntent ?: return
|
||||
Log.info("[EmulationActivity] Launching pending ROM swap: data=${swapIntent.data}")
|
||||
pendingRomSwapIntent = null
|
||||
isWaitingForRomSwapStop = false
|
||||
romSwapNativeStopped = false
|
||||
romSwapThreadStopped = false
|
||||
mainHandler.removeCallbacks(romSwapStopTimeoutRunnable)
|
||||
applyGameLaunchIntent(swapIntent)
|
||||
}
|
||||
|
||||
private fun applyGameLaunchIntent(intent: Intent) {
|
||||
hasEmulationSession = true
|
||||
processHasEmulationSession = true
|
||||
emulationViewModel.setIsEmulationStopping(false)
|
||||
emulationViewModel.setEmulationStopped(false)
|
||||
setIntent(Intent(intent))
|
||||
val navHostFragment =
|
||||
supportFragmentManager.findFragmentById(R.id.fragment_container) as NavHostFragment
|
||||
val navController = navHostFragment.navController
|
||||
val startArgs = intent.extras?.let { Bundle(it) } ?: Bundle()
|
||||
val navOptions = NavOptions.Builder()
|
||||
.setPopUpTo(R.id.emulationFragment, true)
|
||||
.build()
|
||||
|
||||
runCatching {
|
||||
navController.navigate(R.id.emulationFragment, startArgs, navOptions)
|
||||
}.onFailure {
|
||||
Log.warning("[EmulationActivity] ROM swap navigate fallback to setGraph: ${it.message}")
|
||||
navController.setGraph(R.navigation.emulation_navigation, startArgs)
|
||||
}
|
||||
}
|
||||
|
||||
override fun dispatchKeyEvent(event: KeyEvent): Boolean {
|
||||
|
||||
if (event.keyCode == KeyEvent.KEYCODE_VOLUME_UP ||
|
||||
@@ -608,19 +762,48 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener, InputManager
|
||||
}
|
||||
|
||||
fun onEmulationStarted() {
|
||||
if (Looper.myLooper() != Looper.getMainLooper()) {
|
||||
mainHandler.post { onEmulationStarted() }
|
||||
return
|
||||
}
|
||||
hasEmulationSession = true
|
||||
processHasEmulationSession = true
|
||||
emulationViewModel.setEmulationStarted(true)
|
||||
emulationViewModel.setIsEmulationStopping(false)
|
||||
emulationViewModel.setEmulationStopped(false)
|
||||
NativeLibrary.playTimeManagerStart()
|
||||
|
||||
}
|
||||
|
||||
fun onEmulationStopped(status: Int) {
|
||||
if (status == 0 && emulationViewModel.programChanged.value == -1) {
|
||||
if (Looper.myLooper() != Looper.getMainLooper()) {
|
||||
mainHandler.post { onEmulationStopped(status) }
|
||||
return
|
||||
}
|
||||
hasEmulationSession = false
|
||||
processHasEmulationSession = false
|
||||
if (isWaitingForRomSwapStop) {
|
||||
romSwapNativeStopped = true
|
||||
Log.info("[EmulationActivity] ROM swap native stop acknowledged")
|
||||
launchPendingRomSwap(force = false)
|
||||
} else if (status == 0 && emulationViewModel.programChanged.value == -1) {
|
||||
processSessionGame = null
|
||||
finish()
|
||||
} else if (!isWaitingForRomSwapStop) {
|
||||
processSessionGame = null
|
||||
}
|
||||
emulationViewModel.setEmulationStopped(true)
|
||||
}
|
||||
|
||||
fun updateSessionGame(game: Game?) {
|
||||
processSessionGame = game
|
||||
}
|
||||
|
||||
fun onProgramChanged(programIndex: Int) {
|
||||
if (Looper.myLooper() != Looper.getMainLooper()) {
|
||||
mainHandler.post { onProgramChanged(programIndex) }
|
||||
return
|
||||
}
|
||||
emulationViewModel.setProgramChanged(programIndex)
|
||||
}
|
||||
|
||||
@@ -644,6 +827,11 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener, InputManager
|
||||
companion object {
|
||||
const val EXTRA_SELECTED_GAME = "SelectedGame"
|
||||
const val EXTRA_OVERLAY_GAMELESS_EDIT_MODE = "overlayGamelessEditMode"
|
||||
private const val ROM_SWAP_STOP_TIMEOUT_MS = 5000L
|
||||
@Volatile
|
||||
private var processHasEmulationSession = false
|
||||
@Volatile
|
||||
private var processSessionGame: Game? = null
|
||||
|
||||
fun stopForegroundService(activity: Activity) {
|
||||
val startIntent = Intent(activity, ForegroundService::class.java)
|
||||
|
||||
@@ -50,6 +50,7 @@ import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.navigation.findNavController
|
||||
import androidx.navigation.fragment.NavHostFragment
|
||||
import androidx.navigation.fragment.navArgs
|
||||
import androidx.window.layout.FoldingFeature
|
||||
import androidx.window.layout.WindowInfoTracker
|
||||
@@ -135,6 +136,8 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
||||
|
||||
private var intentGame: Game? = null
|
||||
private var isCustomSettingsIntent = false
|
||||
private var isStoppingForRomSwap = false
|
||||
private var deferGameSetupUntilStopCompletes = false
|
||||
|
||||
private var perfStatsRunnable: Runnable? = null
|
||||
private var socRunnable: Runnable? = null
|
||||
@@ -238,6 +241,14 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
||||
}
|
||||
}
|
||||
|
||||
if (emulationViewModel.isEmulationStopping.value) {
|
||||
deferGameSetupUntilStopCompletes = true
|
||||
if (game == null) {
|
||||
game = args.game ?: intentGame
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
finishGameSetup()
|
||||
}
|
||||
|
||||
@@ -260,6 +271,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
||||
}
|
||||
|
||||
game = gameToUse
|
||||
emulationActivity?.updateSessionGame(gameToUse)
|
||||
} catch (e: Exception) {
|
||||
Log.error("[EmulationFragment] Error during game setup: ${e.message}")
|
||||
Toast.makeText(
|
||||
@@ -334,7 +346,8 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
||||
}
|
||||
|
||||
emulationState = EmulationState(game!!.path) {
|
||||
return@EmulationState driverViewModel.isInteractionAllowed.value
|
||||
return@EmulationState driverViewModel.isInteractionAllowed.value &&
|
||||
!isStoppingForRomSwap
|
||||
}
|
||||
}
|
||||
|
||||
@@ -890,8 +903,12 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
||||
}
|
||||
)
|
||||
|
||||
GameIconUtils.loadGameIcon(game!!, binding.loadingImage)
|
||||
binding.loadingTitle.text = game!!.title
|
||||
game?.let {
|
||||
GameIconUtils.loadGameIcon(it, binding.loadingImage)
|
||||
binding.loadingTitle.text = it.title
|
||||
} ?: run {
|
||||
binding.loadingTitle.text = ""
|
||||
}
|
||||
binding.loadingTitle.isSelected = true
|
||||
binding.loadingText.isSelected = true
|
||||
|
||||
@@ -959,6 +976,12 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
||||
ViewUtils.showView(binding.loadingIndicator)
|
||||
ViewUtils.hideView(binding.inputContainer)
|
||||
ViewUtils.hideView(binding.showStatsOverlayText)
|
||||
} else if (deferGameSetupUntilStopCompletes) {
|
||||
if (!isAdded) {
|
||||
return@collect
|
||||
}
|
||||
deferGameSetupUntilStopCompletes = false
|
||||
finishGameSetup()
|
||||
}
|
||||
}
|
||||
emulationViewModel.drawerOpen.collect(viewLifecycleOwner) {
|
||||
@@ -995,26 +1018,24 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
||||
}
|
||||
|
||||
driverViewModel.isInteractionAllowed.collect(viewLifecycleOwner) {
|
||||
if (it && !NativeLibrary.isRunning() && !NativeLibrary.isPaused()) {
|
||||
startEmulation()
|
||||
if (it &&
|
||||
!isStoppingForRomSwap &&
|
||||
!NativeLibrary.isRunning() &&
|
||||
!NativeLibrary.isPaused()
|
||||
) {
|
||||
if (!DirectoryInitialization.areDirectoriesReady) {
|
||||
DirectoryInitialization.start()
|
||||
}
|
||||
|
||||
updateScreenLayout()
|
||||
|
||||
emulationState.run(emulationActivity!!.isActivityRecreated)
|
||||
}
|
||||
}
|
||||
|
||||
driverViewModel.onLaunchGame()
|
||||
}
|
||||
|
||||
private fun startEmulation(programIndex: Int = 0) {
|
||||
if (!NativeLibrary.isRunning() && !NativeLibrary.isPaused()) {
|
||||
if (!DirectoryInitialization.areDirectoriesReady) {
|
||||
DirectoryInitialization.start()
|
||||
}
|
||||
|
||||
updateScreenLayout()
|
||||
|
||||
emulationState.run(emulationActivity!!.isActivityRecreated, programIndex)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onConfigurationChanged(newConfig: Configuration) {
|
||||
super.onConfigurationChanged(newConfig)
|
||||
val b = _binding ?: return
|
||||
@@ -1375,6 +1396,9 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
||||
super.onDestroyView()
|
||||
amiiboLoadJob?.cancel()
|
||||
amiiboLoadJob = null
|
||||
perfStatsRunnable?.let { perfStatsUpdateHandler.removeCallbacks(it) }
|
||||
socRunnable?.let { socUpdateHandler.removeCallbacks(it) }
|
||||
handler.removeCallbacksAndMessages(null)
|
||||
clearPausedFrame()
|
||||
_binding?.surfaceInputOverlay?.touchEventListener = null
|
||||
_binding = null
|
||||
@@ -1382,7 +1406,9 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
||||
}
|
||||
|
||||
override fun onDetach() {
|
||||
NativeLibrary.clearEmulationActivity()
|
||||
if (!hasNewerEmulationFragment()) {
|
||||
NativeLibrary.clearEmulationActivity()
|
||||
}
|
||||
super.onDetach()
|
||||
}
|
||||
|
||||
@@ -1840,10 +1866,74 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
||||
}
|
||||
|
||||
override fun surfaceDestroyed(holder: SurfaceHolder) {
|
||||
emulationState.clearSurface()
|
||||
if (this::emulationState.isInitialized && !hasNewerEmulationFragment()) {
|
||||
emulationState.clearSurface()
|
||||
}
|
||||
emulationStarted = false
|
||||
}
|
||||
|
||||
private fun hasNewerEmulationFragment(): Boolean {
|
||||
val activity = emulationActivity ?: return false
|
||||
return try {
|
||||
val navHostFragment =
|
||||
activity.supportFragmentManager.findFragmentById(R.id.fragment_container) as? NavHostFragment
|
||||
?: return false
|
||||
val currentFragment = navHostFragment.childFragmentManager.fragments
|
||||
.filterIsInstance<EmulationFragment>()
|
||||
.firstOrNull()
|
||||
currentFragment != null && currentFragment !== this
|
||||
} catch (_: Exception) {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
// xbzk: called from EmulationActivity when a new game is loaded while this fragment is still active,
|
||||
// to wait for the emulation thread to stop before allowing the ROM swap to proceed
|
||||
fun notifyWhenEmulationThreadStops(onStopped: () -> Unit) {
|
||||
if (!this::emulationState.isInitialized) {
|
||||
onStopped()
|
||||
return
|
||||
}
|
||||
val emuThread = runCatching { emulationState.emulationThread }.getOrNull()
|
||||
if (emuThread == null || !emuThread.isAlive) {
|
||||
onStopped()
|
||||
return
|
||||
}
|
||||
Thread({
|
||||
runCatching { emuThread.join() }
|
||||
Handler(Looper.getMainLooper()).post {
|
||||
onStopped()
|
||||
}
|
||||
}, "RomSwapWait").start()
|
||||
}
|
||||
|
||||
// xbzk: called from EmulationActivity when a new game is loaded while this
|
||||
// fragment is still active, to stop the current emulation before swapping the ROM
|
||||
fun stopForRomSwap() {
|
||||
if (isStoppingForRomSwap) {
|
||||
return
|
||||
}
|
||||
isStoppingForRomSwap = true
|
||||
clearPausedFrame()
|
||||
emulationViewModel.setIsEmulationStopping(true)
|
||||
_binding?.let {
|
||||
binding.loadingText.setText(R.string.shutting_down)
|
||||
ViewUtils.showView(binding.loadingIndicator)
|
||||
ViewUtils.hideView(binding.inputContainer)
|
||||
ViewUtils.hideView(binding.showStatsOverlayText)
|
||||
}
|
||||
if (this::emulationState.isInitialized) {
|
||||
emulationState.stop()
|
||||
if (NativeLibrary.isRunning() || NativeLibrary.isPaused()) {
|
||||
Log.warning("[EmulationFragment] ROM swap stop fallback: forcing native stop request.")
|
||||
NativeLibrary.stopEmulation()
|
||||
}
|
||||
} else {
|
||||
NativeLibrary.stopEmulation()
|
||||
}
|
||||
NativeConfig.reloadGlobalConfig()
|
||||
}
|
||||
|
||||
private fun showOverlayOptions() {
|
||||
val anchor = binding.inGameMenu.findViewById<View>(R.id.menu_overlay_controls)
|
||||
val popup = PopupMenu(requireContext(), anchor)
|
||||
@@ -2134,6 +2224,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
||||
state = State.STOPPED
|
||||
} else {
|
||||
Log.warning("[EmulationFragment] Stop called while already stopped.")
|
||||
NativeLibrary.stopEmulation()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@ import org.yuzu.yuzu_emu.databinding.FragmentGamePropertiesBinding
|
||||
import org.yuzu.yuzu_emu.features.DocumentProvider
|
||||
import org.yuzu.yuzu_emu.features.settings.model.Settings
|
||||
import org.yuzu.yuzu_emu.features.settings.ui.SettingsSubscreen
|
||||
import org.yuzu.yuzu_emu.model.AddonViewModel
|
||||
import org.yuzu.yuzu_emu.model.DriverViewModel
|
||||
import org.yuzu.yuzu_emu.model.GameProperty
|
||||
import org.yuzu.yuzu_emu.model.GamesViewModel
|
||||
@@ -46,6 +47,7 @@ import org.yuzu.yuzu_emu.model.SubmenuProperty
|
||||
import org.yuzu.yuzu_emu.model.TaskState
|
||||
import org.yuzu.yuzu_emu.utils.DirectoryInitialization
|
||||
import org.yuzu.yuzu_emu.utils.FileUtil
|
||||
import org.yuzu.yuzu_emu.utils.GameHelper
|
||||
import org.yuzu.yuzu_emu.utils.GameIconUtils
|
||||
import org.yuzu.yuzu_emu.utils.GpuDriverHelper
|
||||
import org.yuzu.yuzu_emu.utils.MemoryUtil
|
||||
@@ -61,6 +63,7 @@ class GamePropertiesFragment : Fragment() {
|
||||
|
||||
private val homeViewModel: HomeViewModel by activityViewModels()
|
||||
private val gamesViewModel: GamesViewModel by activityViewModels()
|
||||
private val addonViewModel: AddonViewModel by activityViewModels()
|
||||
private val driverViewModel: DriverViewModel by activityViewModels()
|
||||
|
||||
private val args by navArgs<GamePropertiesFragmentArgs>()
|
||||
@@ -118,6 +121,20 @@ class GamePropertiesFragment : Fragment() {
|
||||
.show(childFragmentManager, LaunchGameDialogFragment.TAG)
|
||||
}
|
||||
|
||||
if (GameHelper.cachedGameList.isEmpty()) {
|
||||
binding.buttonStart.isEnabled = false
|
||||
viewLifecycleOwner.lifecycleScope.launch {
|
||||
withContext(Dispatchers.IO) {
|
||||
GameHelper.restoreContentForGame(args.game)
|
||||
}
|
||||
if (_binding == null) {
|
||||
return@launch
|
||||
}
|
||||
addonViewModel.onAddonsViewStarted(args.game)
|
||||
binding.buttonStart.isEnabled = true
|
||||
}
|
||||
}
|
||||
|
||||
reloadList()
|
||||
|
||||
homeViewModel.openImportSaves.collect(
|
||||
|
||||
@@ -100,42 +100,45 @@ class GamesViewModel : ViewModel() {
|
||||
|
||||
viewModelScope.launch {
|
||||
withContext(Dispatchers.IO) {
|
||||
if (firstStartup) {
|
||||
// Retrieve list of cached games
|
||||
val storedGames =
|
||||
PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
|
||||
.getStringSet(GameHelper.KEY_GAMES, emptySet())
|
||||
if (storedGames!!.isNotEmpty()) {
|
||||
val deserializedGames = mutableSetOf<Game>()
|
||||
storedGames.forEach {
|
||||
val game: Game
|
||||
try {
|
||||
game = Json.decodeFromString(it)
|
||||
} catch (e: Exception) {
|
||||
// We don't care about any errors related to parsing the game cache
|
||||
return@forEach
|
||||
}
|
||||
try {
|
||||
if (firstStartup) {
|
||||
// Retrieve list of cached games
|
||||
val storedGames =
|
||||
PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
|
||||
.getStringSet(GameHelper.KEY_GAMES, emptySet())
|
||||
if (storedGames!!.isNotEmpty()) {
|
||||
val deserializedGames = mutableSetOf<Game>()
|
||||
storedGames.forEach {
|
||||
val game: Game
|
||||
try {
|
||||
game = Json.decodeFromString(it)
|
||||
} catch (e: Exception) {
|
||||
// We don't care about any errors related to parsing the game cache
|
||||
return@forEach
|
||||
}
|
||||
|
||||
val gameExists =
|
||||
DocumentFile.fromSingleUri(
|
||||
YuzuApplication.appContext,
|
||||
Uri.parse(game.path)
|
||||
)?.exists()
|
||||
if (gameExists == true) {
|
||||
deserializedGames.add(game)
|
||||
val gameExists =
|
||||
DocumentFile.fromSingleUri(
|
||||
YuzuApplication.appContext,
|
||||
Uri.parse(game.path)
|
||||
)?.exists()
|
||||
if (gameExists == true) {
|
||||
deserializedGames.add(game)
|
||||
}
|
||||
}
|
||||
setGames(deserializedGames.toList())
|
||||
}
|
||||
setGames(deserializedGames.toList())
|
||||
}
|
||||
}
|
||||
|
||||
setGames(GameHelper.getGames())
|
||||
reloading.set(false)
|
||||
_isReloading.value = false
|
||||
_shouldScrollAfterReload.value = true
|
||||
setGames(GameHelper.getGames())
|
||||
_shouldScrollAfterReload.value = true
|
||||
|
||||
if (directoriesChanged) {
|
||||
setShouldSwapData(true)
|
||||
if (directoriesChanged) {
|
||||
setShouldSwapData(true)
|
||||
}
|
||||
} finally {
|
||||
reloading.set(false)
|
||||
_isReloading.value = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,8 +23,8 @@ object DirectoryInitialization {
|
||||
fun start() {
|
||||
if (!areDirectoriesReady) {
|
||||
initializeInternalStorage()
|
||||
NativeLibrary.initializeSystem(false)
|
||||
NativeConfig.initializeGlobalConfig()
|
||||
NativeLibrary.initializeSystem(false)
|
||||
NativeLibrary.reloadProfiles()
|
||||
migrateSettings()
|
||||
areDirectoriesReady = true
|
||||
|
||||
@@ -8,9 +8,11 @@ package org.yuzu.yuzu_emu.utils
|
||||
|
||||
import android.content.SharedPreferences
|
||||
import android.net.Uri
|
||||
import android.provider.DocumentsContract
|
||||
import androidx.preference.PreferenceManager
|
||||
import kotlinx.serialization.encodeToString
|
||||
import kotlinx.serialization.json.Json
|
||||
import java.io.File
|
||||
import org.yuzu.yuzu_emu.NativeLibrary
|
||||
import org.yuzu.yuzu_emu.YuzuApplication
|
||||
import org.yuzu.yuzu_emu.model.Game
|
||||
@@ -49,29 +51,8 @@ object GameHelper {
|
||||
// Remove previous filesystem provider information so we can get up to date version info
|
||||
NativeLibrary.clearFilesystemProvider()
|
||||
|
||||
// Scan External Content directories and register all NSP/XCI files
|
||||
val externalContentDirs = NativeConfig.getExternalContentDirs()
|
||||
val uniqueExternalContentDirs = linkedSetOf<String>()
|
||||
externalContentDirs.forEach { externalDir ->
|
||||
if (externalDir.isNotEmpty()) {
|
||||
uniqueExternalContentDirs.add(externalDir)
|
||||
}
|
||||
}
|
||||
|
||||
val mountedContainerUris = mutableSetOf<String>()
|
||||
for (externalDir in uniqueExternalContentDirs) {
|
||||
if (externalDir.isNotEmpty()) {
|
||||
val externalDirUri = externalDir.toUri()
|
||||
if (FileUtil.isTreeUriValid(externalDirUri)) {
|
||||
scanContentContainersRecursive(FileUtil.listFiles(externalDirUri), 3) {
|
||||
val containerUri = it.uri.toString()
|
||||
if (mountedContainerUris.add(containerUri)) {
|
||||
NativeLibrary.addFileToFilesystemProvider(containerUri)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
mountExternalContentDirectories(mountedContainerUris)
|
||||
|
||||
val badDirs = mutableListOf<Int>()
|
||||
gameDirs.forEachIndexed { index: Int, gameDir: GameDir ->
|
||||
@@ -115,6 +96,15 @@ object GameHelper {
|
||||
return games.toList()
|
||||
}
|
||||
|
||||
fun restoreContentForGame(game: Game) {
|
||||
NativeLibrary.reloadKeys()
|
||||
|
||||
val mountedContainerUris = mutableSetOf<String>()
|
||||
mountExternalContentDirectories(mountedContainerUris)
|
||||
mountGameFolderContent(Uri.parse(game.path), mountedContainerUris)
|
||||
NativeLibrary.addFileToFilesystemProvider(game.path)
|
||||
}
|
||||
|
||||
// File extensions considered as external content, buuut should
|
||||
// be done better imo.
|
||||
private val externalContentExtensions = setOf("nsp", "xci")
|
||||
@@ -181,6 +171,71 @@ object GameHelper {
|
||||
}
|
||||
}
|
||||
|
||||
private fun mountExternalContentDirectories(mountedContainerUris: MutableSet<String>) {
|
||||
val uniqueExternalContentDirs = linkedSetOf<String>()
|
||||
NativeConfig.getExternalContentDirs().forEach { externalDir ->
|
||||
if (externalDir.isNotEmpty()) {
|
||||
uniqueExternalContentDirs.add(externalDir)
|
||||
}
|
||||
}
|
||||
|
||||
for (externalDir in uniqueExternalContentDirs) {
|
||||
val externalDirUri = externalDir.toUri()
|
||||
if (FileUtil.isTreeUriValid(externalDirUri)) {
|
||||
scanContentContainersRecursive(FileUtil.listFiles(externalDirUri), 3) {
|
||||
val containerUri = it.uri.toString()
|
||||
if (mountedContainerUris.add(containerUri)) {
|
||||
NativeLibrary.addFileToFilesystemProvider(containerUri)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun mountGameFolderContent(gameUri: Uri, mountedContainerUris: MutableSet<String>) {
|
||||
if (gameUri.scheme == "content") {
|
||||
val parentUri = getParentDocumentUri(gameUri) ?: return
|
||||
scanContentContainersRecursive(FileUtil.listFiles(parentUri), 1) {
|
||||
val containerUri = it.uri.toString()
|
||||
if (mountedContainerUris.add(containerUri)) {
|
||||
NativeLibrary.addGameFolderFileToFilesystemProvider(containerUri)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
val gameFile = File(gameUri.path ?: gameUri.toString())
|
||||
val parentDir = gameFile.parentFile ?: return
|
||||
parentDir.listFiles()?.forEach { sibling ->
|
||||
if (!sibling.isFile) {
|
||||
return@forEach
|
||||
}
|
||||
|
||||
val extension = sibling.extension.lowercase()
|
||||
if (externalContentExtensions.contains(extension)) {
|
||||
val containerUri = Uri.fromFile(sibling).toString()
|
||||
if (mountedContainerUris.add(containerUri)) {
|
||||
NativeLibrary.addGameFolderFileToFilesystemProvider(containerUri)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getParentDocumentUri(uri: Uri): Uri? {
|
||||
return try {
|
||||
val documentId = DocumentsContract.getDocumentId(uri)
|
||||
val separatorIndex = documentId.lastIndexOf('/')
|
||||
if (separatorIndex == -1) {
|
||||
null
|
||||
} else {
|
||||
val parentDocumentId = documentId.substring(0, separatorIndex)
|
||||
DocumentsContract.buildDocumentUriUsingTree(uri, parentDocumentId)
|
||||
}
|
||||
} catch (_: Exception) {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
fun getGame(
|
||||
uri: Uri,
|
||||
addedToLibrary: Boolean,
|
||||
|
||||
@@ -1066,6 +1066,8 @@
|
||||
<string name="theme_material_you">ألوان ديناميكية</string>
|
||||
<string name="app_settings">إعدادات التطبيق</string>
|
||||
<string name="theme_and_color">السمة واللون</string>
|
||||
<string name="fullscreen_mode">وضع ملء الشاشة</string>
|
||||
<string name="fullscreen_mode_description">إخفاء أشرطة نظام Android عبر شاشات التطبيق. اسحب من أحد الحواف لإظهارها مؤقتًا.</string>
|
||||
|
||||
<!-- Theme Modes -->
|
||||
<string name="change_theme_mode">تغيير وضع السمة</string>
|
||||
@@ -1096,7 +1098,7 @@
|
||||
<string name="blue">أزرق</string>
|
||||
<string name="cyan">سماوي</string>
|
||||
<string name="red">أحمر</string>
|
||||
<string name="green">أخضر (افتراضي)</string>
|
||||
<string name="green">أخضر</string>
|
||||
<string name="yellow">أصفر</string>
|
||||
<string name="orange">برتقالي</string>
|
||||
<string name="pink">وردي</string>
|
||||
|
||||
@@ -1004,7 +1004,6 @@ Wirklich fortfahren?</string>
|
||||
<string name="theme_material_you">Material You</string>
|
||||
<string name="app_settings">App-Einstellungen</string>
|
||||
<string name="theme_and_color">Theme und Farben</string>
|
||||
|
||||
<!-- Theme Modes -->
|
||||
<string name="change_theme_mode">Design</string>
|
||||
<string name="theme_mode_follow_system">System folgen</string>
|
||||
|
||||
@@ -302,7 +302,7 @@
|
||||
<string name="manage_save_data">Administrar datos de guardado</string>
|
||||
<string name="manage_save_data_description">Guardar los datos encontrados. Por favor, seleccione una opción de abajo.</string>
|
||||
<string name="import_save_warning">Importar datos de guardado</string>
|
||||
<string name="import_save_warning_description">Ésto sobreescribirá todos los datos de guardado existentes con el archivo proporcionado. ¿Está seguro de querer continuar?</string>
|
||||
<string name="import_save_warning_description">Esto sobreescribirá todos los datos de guardado existentes con el archivo proporcionado. ¿Está seguro de querer continuar\?</string>
|
||||
<string name="save_files_importing">Importando archivos de guardado...</string>
|
||||
<string name="save_files_exporting">Exportando archivos de guardado...</string>
|
||||
<string name="save_file_imported_success">Importado correctamente</string>
|
||||
@@ -333,8 +333,8 @@
|
||||
<string name="install_game_content_help_link">https://yuzu-mirror.github.io/help/quickstart/#dumping-installed-updates</string>
|
||||
<string name="custom_driver_not_supported">Controladores personalizados no soportados</string>
|
||||
<string name="custom_driver_not_supported_description">La carga de controladores personalizados no está disponible para este dispositivo.\n¡Compruebe más adelante si se añadió el soporte a esta opción!</string>
|
||||
<string name="manage_yuzu_data">Administrar datos de Eden</string>
|
||||
<string name="manage_yuzu_data_description">Importa/exporta el firmware, las claves, los datos de usuario, ¡y más!</string>
|
||||
<string name="manage_yuzu_data">Gestionar datos de Eden</string>
|
||||
<string name="manage_yuzu_data_description">Importar/exportar el firmware, las claves, los datos de usuario, ¡y más!</string>
|
||||
<string name="game_folders">Carpetas de juegos</string>
|
||||
<string name="deep_scan">Escaneo recursivo </string>
|
||||
<string name="add_game_folder">Añadir carpeta con juegos</string>
|
||||
@@ -350,8 +350,8 @@
|
||||
<item quantity="many">%d archivos de guardado importados con éxito.</item>
|
||||
<item quantity="other">%d archivos de guardado importados con éxito.</item>
|
||||
</plurals>
|
||||
<string name="no_save_data_found">No hay archivos de guardado</string>
|
||||
<string name="verify_installed_content">Verificar contenidos instalados</string>
|
||||
<string name="no_save_data_found">No se encontraron datos de guardado</string>
|
||||
<string name="verify_installed_content">Verificar contenido instalado</string>
|
||||
<string name="verify_installed_content_description">Comprueba todo el contenido instalado por si hubiese alguno corrupto</string>
|
||||
|
||||
<string name="keys_missing">Faltan las claves de encriptación</string>
|
||||
@@ -768,7 +768,7 @@
|
||||
<string name="developer">Desarrollador</string>
|
||||
<string name="version">Versión</string>
|
||||
<string name="copy_details">Copiar detalles</string>
|
||||
<string name="add_ons">Extras/Add-ons</string>
|
||||
<string name="add_ons">Complementos</string>
|
||||
<string name="add_ons_description">Activa/desactiva mods, actualizaciones y contenidos descargables</string>
|
||||
<string name="playtime">Tiempo jugado:</string>
|
||||
<string name="reset_playtime">Borrar tiempo de juego</string>
|
||||
@@ -791,28 +791,28 @@
|
||||
<string name="cleared_shaders_successfully">Sombreadores limpiados con éxito</string>
|
||||
<string name="driver_shader_wipe_dialog_title">Sombreadores limpiados</string>
|
||||
<string name="driver_shader_wipe_dialog_message">Eden ha limpiado automáticamente todos los sombreadores guardados para mantener la canalización de Vulkan válida. Esto es esencial al cambiar o actualizar los controladores de la GPU para prevenir errores y fallos gráficos. Es posible que experimente algunas interrupciones durante la reconstrucción de los sombreadores.</string>
|
||||
<string name="addons_game">Addons: %1$s</string>
|
||||
<string name="addons_game">Complementos: %1$s</string>
|
||||
<string name="save_data">Datos de guardado</string>
|
||||
<string name="save_data_description">Controla los datos de guardado de este juego</string>
|
||||
<string name="save_data_description">Gestiona los datos de guardado de este juego</string>
|
||||
<string name="delete_save_data">Borrar datos de guardado</string>
|
||||
<string name="delete_save_data_description">Elimina todos los datos de guardado de este juego</string>
|
||||
<string name="delete_save_data_warning_description">Ésto elimina de manera permanente todos los datos de guardado de este juego. ¿Seguro que quieres continuar?</string>
|
||||
<string name="delete_save_data_description">Elimina todos los datos de guardado especificos de este juego</string>
|
||||
<string name="delete_save_data_warning_description">Esto eliminará de manera permanente todos los datos de guardado de este juego. ¿Está seguro de que quiere continuar\?</string>
|
||||
<string name="save_data_deleted_successfully">Datos de guardado eliminados con éxito</string>
|
||||
<string name="select_content_type">Tipo de contenido</string>
|
||||
<string name="updates_and_dlc">Actualizaciones y contenido descargable</string>
|
||||
<string name="mods_and_cheats">Mods y trucos</string>
|
||||
<string name="addon_notice">Aviso importante de addons</string>
|
||||
<string name="addon_notice">Aviso importante de complementos</string>
|
||||
<!-- \"cheats/" "romfs/" and \"exefs/ should not be translated -->
|
||||
<string name="addon_notice_description">Para instalar mods y trucos, debe seleccionar una carpeta que contenga los directorios cheats/, romfs/, o exefs/ . ¡No podemos confirmar si éstos serán compatibles con su juego, así que tenga cuidado!</string>
|
||||
<string name="invalid_directory">Directorio no válido</string>
|
||||
<!-- \"cheats/" "romfs/" and \"exefs/ should not be translated -->
|
||||
<string name="invalid_directory_description">Por favor, asegúrese de que el directorio que ha selecionado incluye las carpetas cheats/, romfs/, o exefs/ e inténtelo de nuevo.</string>
|
||||
<string name="addon_installed_successfully">Addon instalado con éxito</string>
|
||||
<string name="addon_installed_successfully">Complemento instalado con éxito</string>
|
||||
<string name="verifying_content">Verificando contenido...</string>
|
||||
<string name="content_install_notice">Aviso importante de contenido</string>
|
||||
<string name="content_install_notice_description">El contenido seleccionado no es de este juego.\n¿Instalar aun que\?</string>
|
||||
<string name="confirm_uninstall">Confirmar desinstalación</string>
|
||||
<string name="confirm_uninstall_description">¿De verdad quieres desinstalar este addon?</string>
|
||||
<string name="confirm_uninstall_description">¿Está seguro de que quiere desinstalar este complemento\?</string>
|
||||
<string name="verify_integrity">Verificar integridad</string>
|
||||
<string name="verifying">Verificando...</string>
|
||||
<string name="verify_success">¡La verificación de integridad ha sido un éxito!</string>
|
||||
@@ -828,14 +828,14 @@
|
||||
|
||||
<!-- ROM loading errors -->
|
||||
<string name="loader_error_encrypted">Su ROM está encriptada</string>
|
||||
<string name="loader_error_encrypted_roms_description"><![CDATA[Siga las guías para volver a volcar sus <a href=\"https://yuzu-mirror.github.io/help/quickstart/#dumping-physical-titles-game-cards\">tarjetas de juego</a> o <a href=\"https://yuzu-mirror.github.io/help/quickstart/#dumping-digital-titles-eshop\">títulos digitales</a>.]]></string>
|
||||
<string name="loader_error_encrypted_keys_description"><![CDATA[Por favor, compruebe que su archivo <a href=\"https://yuzu-mirror.github.io/help/quickstart/#dumping-prodkeys-and-titlekeys\">prod.keys</a> está instalado, para que los juegos sean descifrados.]]></string>
|
||||
<string name="loader_error_encrypted_roms_description"><![CDATA[Siga las guías para volver a volcar sus <a href=\"https://yuzu-mirror.github.io/help/quickstart/#dumping-physical-titles-game-cards\">tarjetas de juego</a> o <a href=\"https://yuzu-mirror.github.io/help/quickstart/#dumping-digital-titles-eshop\">títulos instalados</a>.]]></string>
|
||||
<string name="loader_error_encrypted_keys_description"><![CDATA[Por favor, compruebe que su archivo <a href=\"https://yuzu-mirror.github.io/help/quickstart/#dumping-prodkeys-and-titlekeys\">prod.keys</a> está instalado, para que los juegos sean desencriptados.]]></string>
|
||||
<string name="loader_error_video_core">Ocurrió un error al inicializar el núcleo de video, posiblemente debido a una incompatibilidad con el driver seleccionado</string>
|
||||
<string name="loader_error_video_core_description">Esto suele deberse a un controlador de la GPU incompatible. La instalación de un controlador de la GPU personalizado puede resolver este problema.</string>
|
||||
<string name="loader_error_file_not_found">Archivo ROM no existe</string>
|
||||
|
||||
<string name="loader_requires_firmware">El juego requiere firmware</string>
|
||||
<string name="loader_requires_firmware_description"><![CDATA[El juego que intentas iniciar requiere firmware para arrancar o pasar el menú de inicio. Por favor <a href=\"https://yuzu-mirror.github.io/help/quickstart\"> vuelca e instala el firmware</a>, o pulsa \"Aceptar\" para continuar de todos modos.]]></string>
|
||||
<string name="loader_requires_firmware_description"><![CDATA[El juego que intenta iniciar requiere el firmware para arrancar o pasar el menú de inicio. Por favor <a href=\"https://yuzu-mirror.github.io/help/quickstart\"> vuelque e instale el firmware</a>, o pulse \"Aceptar\" para continuar de todos modos.]]></string>
|
||||
|
||||
<!-- Intent Launch strings -->
|
||||
<string name="searching_for_game">Buscando juego...</string>
|
||||
@@ -1060,6 +1060,8 @@
|
||||
<string name="theme_material_you">Material You</string>
|
||||
<string name="app_settings">Configuración de la aplicación</string>
|
||||
<string name="theme_and_color">Tema y color</string>
|
||||
<string name="fullscreen_mode">Modo pantalla completa</string>
|
||||
<string name="fullscreen_mode_description">Ocultar las barras del sistema de Android. Desliza la pantalla desde una esquina para mostrarlas temporalmente.</string>
|
||||
|
||||
<!-- Theme Modes -->
|
||||
<string name="change_theme_mode">Cambiar modo del tema</string>
|
||||
@@ -1090,7 +1092,7 @@
|
||||
<string name="blue">Azul</string>
|
||||
<string name="cyan">Cian</string>
|
||||
<string name="red">Rojo</string>
|
||||
<string name="green">Verde (Predeterminado)</string>
|
||||
<string name="green">Verde</string>
|
||||
<string name="yellow">Amarillo</string>
|
||||
<string name="orange">Naranja</string>
|
||||
<string name="pink">Rosa</string>
|
||||
@@ -1119,7 +1121,7 @@
|
||||
<string name="airplane_mode">Modo avión</string>
|
||||
<string name="airplane_mode_description">Pasa el modo avión al sistema operativo Switch</string>
|
||||
|
||||
<string name="enable_overlay">Habilitar el Applet de superposición</string>
|
||||
<string name="enable_overlay">Habilitar el applet de superposición</string>
|
||||
<string name="enable_overlay_description">Activa el applet integrado superpuesto de Horizon. Mantenga pulsado el botón de inicio durante 1 segundo para que aparezca.</string>
|
||||
|
||||
<!-- Profile Management -->
|
||||
|
||||
@@ -275,8 +275,11 @@
|
||||
<string name="cpu_info">Informations sur le processeur central</string>
|
||||
<string name="gpu_information">Informations sur le processeur graphique</string>
|
||||
<string name="vulkan_driver_version">Version du pilote Vulkan</string>
|
||||
<string name="error_getting_emulator_info">Impossible d’obtenir les infos de l’émulateur</string>
|
||||
<string name="memory_info">Informations sur la mémoire</string>
|
||||
<string name="total_memory">Capacité totale de mémoire</string>
|
||||
<string name="soc">SOC:</string>
|
||||
|
||||
<string name="warning_help">Aide</string>
|
||||
<string name="warning">Avertissement</string>
|
||||
<string name="warning_skip">Sauter</string>
|
||||
@@ -314,6 +317,9 @@
|
||||
<string name="share_log">Partager les logs de débogage</string>
|
||||
<string name="share_log_description">Partagez le fichier de log de Eden pour déboguer les problèmes.</string>
|
||||
<string name="share_log_missing">Aucun fichier de log trouvé</string>
|
||||
<string name="share_gpu_log">Partager les logs du GPU</string>
|
||||
<string name="share_gpu_log_description">Partage les logs du GPU générés par Eden afin de déboguer les problèmes graphiques.</string>
|
||||
<string name="share_gpu_log_missing">Aucun fichier de log du GPU trouvé,</string>
|
||||
<string name="install_game_content">Installer le contenu du jeu</string>
|
||||
<string name="install_game_content_description">Installer une mise à jour ou un DLC</string>
|
||||
<string name="installing_game_content">Installation du contenu en cours...</string>
|
||||
@@ -369,7 +375,9 @@
|
||||
<string name="qlaunch_description">Lancer des applications depuis l\'écran d\'accueil du système</string>
|
||||
<string name="applets">Lanceur d\'applets</string>
|
||||
<string name="applets_description">Lancer des applets système en utilisant le firmware installé</string>
|
||||
<string name="applets_error_firmware">Aucun firmware installé</string>
|
||||
<string name="applets_error_applet">Applet non disponible</string>
|
||||
<string name="applets_error_description"><![CDATA[Veuillez vérifier que votre fichier<a href=\"https://yuzu-mirror.github.io/help/quickstart/#dumping-prodkeys-and-titlekeys\">prod.keys</a> et le<a href=\"https://yuzu-mirror.github.io/help/quickstart/#dumping-system-firmware\">firmware</a>sont installés et réessayez.]]></string>
|
||||
<string name="album_applet">Album</string>
|
||||
<string name="album_applet_description">Afficher les images stockées dans le dossier de captures d\'écran de l\'utilisateur avec le visualiseur de photos système.</string>
|
||||
<string name="mii_edit_applet">Éditeur Mii</string>
|
||||
@@ -387,6 +395,7 @@
|
||||
<string name="copied_to_clipboard">Copié dans le presse-papier</string>
|
||||
<string name="about_app_description">Un émulateur Switch open source</string>
|
||||
<string name="contributors">Contributeurs</string>
|
||||
<string name="contributors_description">Les gens qui ont rendu possible Eden pour Android</string>
|
||||
<string name="licenses_description">Des projets qui rendent possible Eden pour Android</string>
|
||||
<string name="build">Build</string>
|
||||
<string name="user_data">Données utilisateur</string>
|
||||
@@ -403,6 +412,8 @@
|
||||
<string name="frame_limit_enable_description">Limiter la vitesse d\'émulation à un pourcentage spécifié de la vitesse normale</string>
|
||||
<string name="frame_limit_slider">Limiter le pourcentage de vitesse</string>
|
||||
<string name="frame_limit_slider_description">Spécifier le pourcentage pour limiter la vitesse d\'émulation. 100% correspond à la vitesse normale. Des valeurs plus élevées ou plus basses augmenteront ou diminueront la limite de vitesse.</string>
|
||||
<string name="turbo_speed_limit">Vitesse turbo</string>
|
||||
<string name="slow_speed_limit">Vitesse lente</string>
|
||||
<string name="cpu_backend">Backend du CPU</string>
|
||||
<string name="cpu_accuracy">Précision du CPU</string>
|
||||
<string name="value_with_units">%1$s%2$s</string>
|
||||
@@ -419,6 +430,9 @@
|
||||
<string name="use_custom_rtc_description">Vous permet de définir une horloge en temps réel personnalisée distincte de l\'heure actuelle de votre système.</string>
|
||||
<string name="set_custom_rtc">Définir l\'horloge RTC personnalisée</string>
|
||||
|
||||
<!-- CPU -->
|
||||
<string name="fast_cpu_time">Overclock du CPU</string>
|
||||
<string name="fast_cpu_time_description">Force le CPU émulé à fonctionner à une fréquence plus élevée, en contournant certaines limitations de FPS. Utilisez Boost (1700 MHz) pour atteindre la fréquence native maximale de la Nintendo Switch, ou Fast (2000 MHz) pour doubler la fréquence. C’est un “hack”. Certains chipsets moins puissants peuvent voir leurs performances réduites et les jeux peuvent se comporter différemment.</string>
|
||||
<string name="custom_cpu_ticks">Ticks CPU personnalisés</string>
|
||||
<string name="custom_cpu_ticks_description">Définissez une valeur personnalisée de ticks CPU. Des valeurs plus élevées peuvent améliorer les performances, mais peuvent aussi provoquer des gels du jeu. Une plage de 77 à 21000 est recommandée.</string>
|
||||
<string name="cpu_ticks">Ticks</string>
|
||||
@@ -897,7 +911,6 @@
|
||||
<string name="theme_material_you">Material You</string>
|
||||
<string name="app_settings">Paramètres de l’App</string>
|
||||
<string name="theme_and_color">Thème et Couleur</string>
|
||||
|
||||
<!-- Theme Modes -->
|
||||
<string name="change_theme_mode">Changer le mode de thème</string>
|
||||
<string name="theme_mode_follow_system">Automatique</string>
|
||||
|
||||
@@ -844,7 +844,6 @@
|
||||
<string name="theme_material_you">Material You</string>
|
||||
<string name="app_settings">Impostazioni app</string>
|
||||
<string name="theme_and_color">Tema e colore</string>
|
||||
|
||||
<!-- Theme Modes -->
|
||||
<string name="change_theme_mode">Cambia la modalità del tema</string>
|
||||
<string name="theme_mode_follow_system">Segue il Sistema</string>
|
||||
|
||||
@@ -960,7 +960,6 @@
|
||||
<string name="theme_material_you">Material You</string>
|
||||
<string name="app_settings">Ustawienia aplikacji</string>
|
||||
<string name="theme_and_color">Motyw i kolor</string>
|
||||
|
||||
<!-- Theme Modes -->
|
||||
<string name="change_theme_mode">Zmień tryb motywu</string>
|
||||
<string name="theme_mode_follow_system">Podążaj za systemowym</string>
|
||||
|
||||
@@ -905,7 +905,6 @@
|
||||
<string name="theme_material_you">Material You</string>
|
||||
<string name="app_settings">Configurações do App</string>
|
||||
<string name="theme_and_color">Tema e Cores</string>
|
||||
|
||||
<!-- Theme Modes -->
|
||||
<string name="change_theme_mode">Alterar o tema</string>
|
||||
<string name="theme_mode_follow_system">Igual ao Sistema</string>
|
||||
|
||||
@@ -352,7 +352,7 @@
|
||||
<item quantity="many">Импортировано %d сохранений</item>
|
||||
<item quantity="other">Импортировано %d сохранений</item>
|
||||
</plurals>
|
||||
<string name="no_save_data_found">Не найдены сохраненмия</string>
|
||||
<string name="no_save_data_found">Файлы сохранений не найдены</string>
|
||||
<string name="verify_installed_content">Подтвердить установленный контент</string>
|
||||
<string name="verify_installed_content_description">Проверяет весь установленный контент на наличие повреждений</string>
|
||||
|
||||
@@ -361,7 +361,7 @@
|
||||
<string name="keys_missing_help">https://yuzu-mirror.github.io/help/quickstart/#dumping-decryption-keys</string>
|
||||
|
||||
<string name="uninstall_firmware">Удалить прошивку</string>
|
||||
<string name="uninstall_firmware_description">Удаление прошивки сотрет ее с устройства и может повлиять на совместимость игр.</string>
|
||||
<string name="uninstall_firmware_description">Удаление прошивки сотрёт её с устройства и может повлиять на совместимость игр.</string>
|
||||
<string name="firmware_uninstalling">Удаление прошивки...</string>
|
||||
<string name="firmware_uninstalled_success">Прошивка успешно удалена</string>
|
||||
|
||||
@@ -397,6 +397,7 @@
|
||||
<string name="copied_to_clipboard">Скопировано в буфер обмена</string>
|
||||
<string name="about_app_description">Эмулятор Switch с открытым исходным кодом</string>
|
||||
<string name="contributors">Контрибьюторы</string>
|
||||
<string name="contributors_description">Люди, благодаря которым появился Eden для Android</string>
|
||||
<string name="licenses_description">Проекты, которые сделали Eden для Android возможным</string>
|
||||
<string name="build">Сборка</string>
|
||||
<string name="user_data">Данные пользователя</string>
|
||||
@@ -481,7 +482,7 @@
|
||||
<string name="sync_memory_operations_description">Обеспечивает согласованность данных между вычислительными операциями и операциями с памятью. Эта опция должна исправлять проблемы в некоторых играх, но может снижать производительность в некоторых случаях. Наиболее сильно это затрагивает игры на Unreal Engine 4.</string>
|
||||
<string name="use_disk_shader_cache">Кэш шейдеров на диске</string>
|
||||
<string name="use_disk_shader_cache_description">Уменьшение зависаний за счет хранения и загрузки сгенерированных шейдеров.</string>
|
||||
<string name="renderer_force_max_clock">Принудительно заставить максимальную тактовую частоту (только для Adreno)</string>
|
||||
<string name="renderer_force_max_clock">Принудительная максимальная тактовая частота (только для Adreno)</string>
|
||||
<string name="renderer_force_max_clock_description">Заставляет ГПУ работать на максимально возможных тактовых частотах (тепловые ограничения все равно будут применяться).</string>
|
||||
<string name="renderer_reactive_flushing">Реактивная очистка</string>
|
||||
<string name="renderer_reactive_flushing_description">Повышение точности рендеринга в некоторых играх за счет снижения производительности.</string>
|
||||
@@ -1061,6 +1062,8 @@
|
||||
<string name="theme_material_you">Material You</string>
|
||||
<string name="app_settings">Настройки приложения</string>
|
||||
<string name="theme_and_color">Тема и цвет</string>
|
||||
<string name="fullscreen_mode">Полноэкранный режим</string>
|
||||
<string name="fullscreen_mode_description">Скрывать системные панели Android на всех экранах приложения. Проведите от края экрана, чтобы временно отобразить их</string>
|
||||
|
||||
<!-- Theme Modes -->
|
||||
<string name="change_theme_mode">Сменить режим темы</string>
|
||||
@@ -1090,7 +1093,7 @@
|
||||
<string name="blue">Синий</string>
|
||||
<string name="cyan">Циановый</string>
|
||||
<string name="red">Красный</string>
|
||||
<string name="green">Зелёный (По умолчанию)</string>
|
||||
<string name="green">Зелёный</string>
|
||||
<string name="yellow">Жёлтый</string>
|
||||
<string name="orange">Оранжевый</string>
|
||||
<string name="pink">Розовый</string>
|
||||
@@ -1116,8 +1119,8 @@
|
||||
|
||||
<string name="swkbd_applet">Программная клавиатура</string>
|
||||
|
||||
<string name="airplane_mode">Режим полета</string>
|
||||
<string name="airplane_mode_description">Передает режим полета в ОС Switch</string>
|
||||
<string name="airplane_mode">Режим полёта</string>
|
||||
<string name="airplane_mode_description">Передаёт режим полёта в ОС Switch</string>
|
||||
|
||||
<string name="enable_overlay">Включить апплет-оверлей</string>
|
||||
<string name="enable_overlay_description">Активирует встроенный в Horizon оверлейный апплет. Для его отображения нажмите и удерживайте кнопку «HOME» в течение одной секунды.</string>
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
<!-- Spinbox strings -->
|
||||
<string name="increment">Збільшення</string>
|
||||
<string name="decrement">Зменшення</string>
|
||||
<string name="value_too_low">Значення повинно бути щонайменше %1$d</string>
|
||||
<string name="value_too_low">Значення повинно бути не менше %1$d</string>
|
||||
<string name="value_too_high">Значення повинно бути не більше %1$d</string>
|
||||
<string name="invalid_value">Неправильне значення</string>
|
||||
|
||||
@@ -1062,6 +1062,8 @@
|
||||
<string name="theme_material_you">Material You</string>
|
||||
<string name="app_settings">Налаштування застосунку</string>
|
||||
<string name="theme_and_color">Тема і колір</string>
|
||||
<string name="fullscreen_mode">Повноекранний режим</string>
|
||||
<string name="fullscreen_mode_description">Приховати системні панелі Android у застосунку. Проведіть пальцем від краю екрана, щоб показати їх на короткий час.</string>
|
||||
|
||||
<!-- Theme Modes -->
|
||||
<string name="change_theme_mode">Змінити режим теми</string>
|
||||
@@ -1092,7 +1094,7 @@
|
||||
<string name="blue">Синій</string>
|
||||
<string name="cyan">Блакитний</string>
|
||||
<string name="red">Червоний</string>
|
||||
<string name="green">Зелений (стандартний)</string>
|
||||
<string name="green">Зелений</string>
|
||||
<string name="yellow">Жовтий</string>
|
||||
<string name="orange">Помаранчевий</string>
|
||||
<string name="pink">Рожевий</string>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
|
||||
|
||||
<string name="app_disclaimer">此软件可以运行Nintendo Switch游戏机的游戏,但本身并不自带任何游戏或密钥。<br /><br />开始之前,请先选择<![CDATA[<b>密钥文件(prod.keys)</b>]]>。<br /><br /><![CDATA[<a href=\"https://yuzu-mirror.github.io/help/quickstart\">详情</a>]]></string>
|
||||
<string name="notice_notification_channel_name">通知及错误提醒</string>
|
||||
<string name="app_disclaimer">本软件可以运行Nintendo Switch游戏主机的游戏,但自身并不包含任何游戏或密钥。<br /><br />请于开始之前选定位于你的设备存储空间中的<![CDATA[<b>prod.keys</b>]]>。<br /><br /><![CDATA[<a href=\"https://yuzu-mirror.github.io/help/quickstart\">了解更多</a>]]></string>
|
||||
<string name="notice_notification_channel_name">错误与注意事项</string>
|
||||
<string name="notice_notification_channel_description">当发生错误时显示通知。</string>
|
||||
<string name="notification_permission_not_granted">未授予通知权限!</string>
|
||||
<string name="app_notification_channel_description">Eden模拟器通知</string>
|
||||
@@ -12,36 +12,36 @@
|
||||
<!-- Spinbox strings -->
|
||||
<string name="increment">增加</string>
|
||||
<string name="decrement">减少</string>
|
||||
<string name="value_too_low">值必须最小为 %1$d</string>
|
||||
<string name="value_too_high">值最大必须为 %1$d</string>
|
||||
<string name="value_too_low">此值至少应为 %1$d</string>
|
||||
<string name="value_too_high">此值至多应为 %1$d</string>
|
||||
<string name="invalid_value">无效的值</string>
|
||||
|
||||
<string name="using_per_game_config">使用针对特定游戏的配置</string>
|
||||
<string name="using_per_game_config">使用各别游戏配置</string>
|
||||
|
||||
<!-- Input Overlay -->
|
||||
<string name="show_input_overlay">虚拟按键输入</string>
|
||||
<string name="show_input_overlay_description">在模拟过程中显示触摸控制覆盖</string>
|
||||
<string name="overlay_snap_to_grid">截图到网格</string>
|
||||
<string name="overlay_snap_to_grid_description">编辑时将叠加控件吸附到网格</string>
|
||||
<string name="overlay_grid_size">网格大小</string>
|
||||
<string name="overlay_grid_size_description">网格单元的大小(以像素为单位)</string>
|
||||
<string name="input_overlay_behavior">平衡</string>
|
||||
<string name="overlay_auto_hide">自动隐藏虚拟按键</string>
|
||||
<string name="overlay_auto_hide_description">在指定的时间间隔后自动隐藏触摸控制覆盖层。</string>
|
||||
<string name="enable_input_overlay_auto_hide">开启自动隐藏虚拟按键</string>
|
||||
<string name="hide_overlay_on_controller_input">使用控制器时自动隐藏虚拟输入</string>
|
||||
<string name="hide_overlay_on_controller_input_description">在使用控制器自动隐藏虚拟输入,当控制器断开会自动再现。</string>
|
||||
<string name="invert_confirm_back_controller_buttons">反转 确认/返回 手柄按钮</string>
|
||||
<string name="invert_confirm_back_controller_buttons_description">在使用应用界面时,将安卓确认和返回按钮的作切换到 Switch 和 Xbox 的样式。</string>
|
||||
<string name="show_input_overlay">显示输入叠加层</string>
|
||||
<string name="show_input_overlay_description">在游戏模拟期间显示触控叠加层</string>
|
||||
<string name="overlay_snap_to_grid">对齐到网格</string>
|
||||
<string name="overlay_snap_to_grid_description">在编辑时将叠加层控件对齐到网格</string>
|
||||
<string name="overlay_grid_size">网格尺寸</string>
|
||||
<string name="overlay_grid_size_description">网格单元像素单位尺寸</string>
|
||||
<string name="input_overlay_behavior">行为</string>
|
||||
<string name="overlay_auto_hide">触控叠加层自动隐藏</string>
|
||||
<string name="overlay_auto_hide_description">在指定时间内未进行任何操作后,自动隐藏触控叠加层。</string>
|
||||
<string name="enable_input_overlay_auto_hide">启用触控叠加层自动隐藏</string>
|
||||
<string name="hide_overlay_on_controller_input">使用控制器时隐藏触控叠加层</string>
|
||||
<string name="hide_overlay_on_controller_input_description">在使用实体控制器时自动隐藏触控叠加层,而当控制器断开时触控叠加层则会重新显现。</string>
|
||||
<string name="invert_confirm_back_controller_buttons">切换“确认/返回”控制器按钮功能</string>
|
||||
<string name="invert_confirm_back_controller_buttons_description">在与本应用的界面交互时,交换 Android 的“确认”与“返回”按钮的处理方式,以匹配 Switch 和 Xbox 的风格。</string>
|
||||
|
||||
<string name="input_overlay_options">虚拟按键输入</string>
|
||||
<string name="input_overlay_options_description">配置虚拟按键</string>
|
||||
<string name="edit_overlay_layout">编辑虚拟按键</string>
|
||||
<string name="edit_overlay_layout_description">调整屏幕控件的的位置和大小</string>
|
||||
<string name="input_overlay_options">输入叠加层</string>
|
||||
<string name="input_overlay_options_description">配置屏上控件</string>
|
||||
<string name="edit_overlay_layout">编辑触控叠加层布局</string>
|
||||
<string name="edit_overlay_layout_description">调整屏上控件的的位置和大小</string>
|
||||
|
||||
|
||||
<!-- Stats Overlay settings -->
|
||||
<string name="process_ram">进程内存: %1$d MB</string>
|
||||
<string name="process_ram">进程内存占用: %1$d MB</string>
|
||||
<string name="shaders_prefix">正在编译</string>
|
||||
<string name="shaders_suffix">着色器</string>
|
||||
<string name="charging">(充电中)</string>
|
||||
@@ -51,31 +51,31 @@
|
||||
<string name="stats_overlay_customization">自定义</string>
|
||||
<string name="stats_overlay_items">显示项</string>
|
||||
<string name="stats_overlay_options">悬浮窗</string>
|
||||
<string name="enable_stats_overlay_">启用性能统计悬浮窗</string>
|
||||
<string name="stats_overlay_options_description">配置悬浮窗中显示的性能信息</string>
|
||||
<string name="enable_stats_overlay_">启用性能统计叠加层</string>
|
||||
<string name="stats_overlay_options_description">配置性能统计叠加层中显示的信息</string>
|
||||
<string name="show_fps">显示帧率</string>
|
||||
<string name="show_fps_description">显示当前帧率(FPS)</string>
|
||||
<string name="show_fps_description">显示当前帧率</string>
|
||||
<string name="show_frametime">显示帧时间</string>
|
||||
<string name="show_app_ram_usage">显示应用内存占用</string>
|
||||
<string name="show_app_ram_usage_description">显示模拟器正在使用的内存量</string>
|
||||
<string name="show_system_ram_usage">显示系统内存占用</string>
|
||||
<string name="show_system_ram_usage_description">显示系统使用的内存量</string>
|
||||
<string name="show_app_ram_usage">显示应用内存使用情况</string>
|
||||
<string name="show_app_ram_usage_description">显示模拟器内存用量</string>
|
||||
<string name="show_system_ram_usage">显示系统内存使用情况</string>
|
||||
<string name="show_system_ram_usage_description">显示系统内存用量</string>
|
||||
<string name="show_bat_temperature">显示电池温度</string>
|
||||
<string name="bat_temperature_unit">电池温度单位</string>
|
||||
<string name="show_power_info">显示电池信息</string>
|
||||
<string name="show_power_info_description">显示当前功耗和电池剩余容量</string>
|
||||
<string name="show_shaders_building">显示着色器构建</string>
|
||||
<string name="show_shaders_building_description">显示当前正在构建的着色器数量</string>
|
||||
<string name="overlay_position">悬浮窗位置</string>
|
||||
<string name="overlay_position_description">选择悬浮窗在屏幕上的显示位置</string>
|
||||
<string name="show_shaders_building">显示着色器编译信息</string>
|
||||
<string name="show_shaders_building_description">显示当前正在编译的着色器数量</string>
|
||||
<string name="overlay_position">叠加层位置</string>
|
||||
<string name="overlay_position_description">选择叠加层在屏幕上显示的位置</string>
|
||||
<string name="overlay_position_top_left">左上</string>
|
||||
<string name="overlay_position_top_right">右上</string>
|
||||
<string name="overlay_position_bottom_left">左下</string>
|
||||
<string name="overlay_position_bottom_right">右下</string>
|
||||
<string name="overlay_position_center_top">顶部居中</string>
|
||||
<string name="overlay_position_center_bottom">底部居中</string>
|
||||
<string name="perf_overlay_background">悬浮窗背景</string>
|
||||
<string name="perf_overlay_background_description">为悬浮窗添加背景以提高可读性</string>
|
||||
<string name="perf_overlay_background">叠加层背景</string>
|
||||
<string name="perf_overlay_background_description">为叠加层添加背景以提高可读性</string>
|
||||
|
||||
<!-- Device Overlay settings -->
|
||||
<string name="show_soc_overlay">显示设备信息叠加层</string>
|
||||
@@ -391,6 +391,7 @@
|
||||
<string name="copied_to_clipboard">已复制到剪贴板</string>
|
||||
<string name="about_app_description">一款开源的 Switch 模拟器</string>
|
||||
<string name="contributors">贡献者</string>
|
||||
<string name="contributors_description">Eden Android 版的诞生离不开这些人的支持</string>
|
||||
<string name="licenses_description">Android 版 Eden 离不开这些项目的支持</string>
|
||||
<string name="build">构建版本</string>
|
||||
<string name="user_data">用户数据</string>
|
||||
@@ -628,7 +629,7 @@
|
||||
<string name="toggle_axis">切换轴</string>
|
||||
<string name="connected">已连接</string>
|
||||
<string name="use_system_vibrator">使用系统马达</string>
|
||||
<string name="input_overlay">虚拟按键输入</string>
|
||||
<string name="input_overlay">输入叠加层</string>
|
||||
<string name="vibration">震动</string>
|
||||
<string name="vibration_strength">震动强度</string>
|
||||
<string name="profile">用户配置</string>
|
||||
@@ -876,19 +877,19 @@
|
||||
<string name="emulation_show_overlay">显示控制器</string>
|
||||
<string name="emulation_hide_overlay">隐藏控制器</string>
|
||||
<string name="emulation_toggle_all">全部切换</string>
|
||||
<string name="emulation_control_adjust">调整虚拟按键</string>
|
||||
<string name="emulation_control_adjust">调整触控叠加层</string>
|
||||
<string name="emulation_control_scale">缩放</string>
|
||||
<string name="emulation_control_opacity">不透明度</string>
|
||||
<string name="emulation_touch_overlay_reset">重置虚拟按键</string>
|
||||
<string name="emulation_touch_overlay_edit">编辑虚拟按键</string>
|
||||
<string name="emulation_touch_overlay_reset">重置触控叠加层</string>
|
||||
<string name="emulation_touch_overlay_edit">编辑触控叠加层</string>
|
||||
<string name="emulation_snap_to_grid">截图到网格</string>
|
||||
<string name="emulation_pause">暂停模拟</string>
|
||||
<string name="emulation_unpause">继续模拟</string>
|
||||
<string name="emulation_input_overlay">虚拟按键选项</string>
|
||||
<string name="emulation_input_overlay">触控叠加层选项</string>
|
||||
<string name="load_amiibo">加载 Amiibo</string>
|
||||
<string name="touchscreen">触摸屏</string>
|
||||
<string name="lock_drawer">锁定侧边菜单</string>
|
||||
<string name="unlock_drawer">解锁侧边菜单</string>
|
||||
<string name="lock_drawer">锁定侧边栏</string>
|
||||
<string name="unlock_drawer">解锁侧边栏</string>
|
||||
<string name="reset">重置</string>
|
||||
|
||||
<!-- Amiibo -->
|
||||
@@ -902,7 +903,7 @@
|
||||
|
||||
|
||||
<!-- Software keyboard -->
|
||||
<string name="software_keyboard">软件键盘</string>
|
||||
<string name="software_keyboard">软键盘</string>
|
||||
|
||||
<!-- Errors and warnings -->
|
||||
<string name="abort_button">中止</string>
|
||||
@@ -913,7 +914,7 @@
|
||||
<string name="save_load_error">保存/载入发生错误</string>
|
||||
<string name="fatal_error">致命错误</string>
|
||||
<string name="fatal_error_message">发生致命错误。请查阅日志获取详细信息。\n继续模拟可能会导致崩溃。</string>
|
||||
<string name="device_memory_inadequate">设备RAM:%1$s\n建议:%2$s</string>
|
||||
<string name="device_memory_inadequate">设备内存:%1$s\n建议:%2$s</string>
|
||||
<string name="memory_formatted">%1$s%2$s</string>
|
||||
<string name="no_game_present">当前没有可启动的游戏!</string>
|
||||
|
||||
@@ -924,7 +925,7 @@
|
||||
<string name="region_australia">澳大利亚</string>
|
||||
<string name="region_china">中国</string>
|
||||
<string name="region_korea">韩国</string>
|
||||
<string name="region_taiwan">台湾地区</string>
|
||||
<string name="region_taiwan">台湾</string>
|
||||
|
||||
<!-- Memory Layouts -->
|
||||
<string name="memory_4gb">4GB (推荐)</string>
|
||||
@@ -941,21 +942,21 @@
|
||||
<string name="fast_gpu_high">高 (512)</string>
|
||||
|
||||
<!-- GPU swizzle texture size -->
|
||||
<string name="gpu_texturesizeswizzle_verysmall">很小 (16 MB)</string>
|
||||
<string name="gpu_texturesizeswizzle_verysmall">极小 (16 MB)</string>
|
||||
<string name="gpu_texturesizeswizzle_small">较小 (32 MB)</string>
|
||||
<string name="gpu_texturesizeswizzle_normal">正常 (128 MB)</string>
|
||||
<string name="gpu_texturesizeswizzle_large">较大 (256 MB)</string>
|
||||
<string name="gpu_texturesizeswizzle_verylarge">很大 (512 MB)</string>
|
||||
<string name="gpu_texturesizeswizzle_verylarge">极大 (512 MB)</string>
|
||||
|
||||
<!-- GPU swizzle streams -->
|
||||
<string name="gpu_swizzle_verylow">很低 (4 MB)</string>
|
||||
<string name="gpu_swizzle_verylow">极低 (4 MB)</string>
|
||||
<string name="gpu_swizzle_low">低 (8 MB)</string>
|
||||
<string name="gpu_swizzle_normal">正常 (16 MB)</string>
|
||||
<string name="gpu_swizzle_medium">中 (32 MB)</string>
|
||||
<string name="gpu_swizzle_high">高 (64 MB)</string>
|
||||
|
||||
<!-- GPU swizzle chunks -->
|
||||
<string name="gpu_swizzlechunk_verylow">很低 (32)</string>
|
||||
<string name="gpu_swizzlechunk_verylow">极低 (32)</string>
|
||||
<string name="gpu_swizzlechunk_low">低 (64)</string>
|
||||
<string name="gpu_swizzlechunk_normal">正常 (128)</string>
|
||||
<string name="gpu_swizzlechunk_medium">中 (256)</string>
|
||||
@@ -1053,8 +1054,10 @@
|
||||
<string name="change_app_theme">切换主题</string>
|
||||
<string name="theme_default">系统默认</string>
|
||||
<string name="theme_material_you">Material You</string>
|
||||
<string name="app_settings">App 设置</string>
|
||||
<string name="app_settings">应用设置</string>
|
||||
<string name="theme_and_color">主题与颜色</string>
|
||||
<string name="fullscreen_mode">全屏模式</string>
|
||||
<string name="fullscreen_mode_description">于应用中隐藏 Android 系统栏,若要短暂显示只需从屏幕边缘向内滑动即可。</string>
|
||||
|
||||
<!-- Theme Modes -->
|
||||
<string name="change_theme_mode">更改主题模式</string>
|
||||
@@ -1075,16 +1078,17 @@
|
||||
<string name="enable_qlaunch_button_description">显示启动 QLaunch 的按钮</string>
|
||||
|
||||
<!-- App Language -->
|
||||
<string name="app_language">App 语言</string>
|
||||
<string name="app_language_description">更改 app 界面的语言</string>
|
||||
<string name="app_language">应用使用的语言</string>
|
||||
<string name="app_language_description">更改应用界面语言</string>
|
||||
<string name="app_language_system">跟随系统</string>
|
||||
<!-- Static Themes -->
|
||||
<string name="static_theme_color">主题颜色</string>
|
||||
<string name="eden_theme">Eden</string>
|
||||
<string name="violet">紫色(默认)</string>
|
||||
<string name="violet">紫色</string>
|
||||
<string name="blue">蓝色</string>
|
||||
<string name="cyan">青色</string>
|
||||
<string name="red">红色</string>
|
||||
<string name="green">绿色</string>
|
||||
<string name="yellow">黄色</string>
|
||||
<string name="orange">橙色</string>
|
||||
<string name="pink">粉色</string>
|
||||
@@ -1108,13 +1112,13 @@
|
||||
<string name="applet_hle">自定义前端</string>
|
||||
<string name="applet_lle">真实小程序</string>
|
||||
|
||||
<string name="swkbd_applet">软件键盘</string>
|
||||
<string name="swkbd_applet">软键盘</string>
|
||||
|
||||
<string name="airplane_mode">飞行模式</string>
|
||||
<string name="airplane_mode_description">将飞行模式传递给Switch操作系统</string>
|
||||
|
||||
<string name="enable_overlay">开启覆盖层小程序</string>
|
||||
<string name="enable_overlay_description">开启 Horizon 内置的覆盖层小程序。请按住 home 键 1 秒来显示它。</string>
|
||||
<string name="enable_overlay">启用叠加层小程序</string>
|
||||
<string name="enable_overlay_description">开启 Horizon 内建的叠加层小程序,要显示它请按压 home 键 1 秒</string>
|
||||
|
||||
<!-- Profile Management -->
|
||||
<string name="profile_manager">用户配置管理</string>
|
||||
@@ -1147,7 +1151,7 @@
|
||||
|
||||
<!-- Licenses screen strings -->
|
||||
<string name="licenses">许可证</string>
|
||||
<string name="license_fidelityfx_fsr_description">来自 AMD 的高品质画质升级技术</string>
|
||||
<string name="license_fidelityfx_fsr_description">AMD 的高品质画面增强技术</string>
|
||||
<string name="external_content">外部内容</string>
|
||||
<string name="add_folders">添加文件夹</string>
|
||||
|
||||
|
||||
@@ -908,7 +908,6 @@
|
||||
<string name="theme_material_you">質感設計3</string>
|
||||
<string name="app_settings">Eden介面設定</string>
|
||||
<string name="theme_and_color">主題和顏色</string>
|
||||
|
||||
<!-- Theme Modes -->
|
||||
<string name="change_theme_mode">變更主題模式</string>
|
||||
<string name="theme_mode_follow_system">跟隨系統</string>
|
||||
|
||||
@@ -134,6 +134,8 @@ add_library(
|
||||
typed_address.h
|
||||
uint128.h
|
||||
unique_function.h
|
||||
random.cpp
|
||||
random.h
|
||||
uuid.cpp
|
||||
uuid.h
|
||||
vector_math.h
|
||||
@@ -144,7 +146,8 @@ add_library(
|
||||
zstd_compression.cpp
|
||||
zstd_compression.h
|
||||
fs/ryujinx_compat.h fs/ryujinx_compat.cpp
|
||||
fs/symlink.h fs/symlink.cpp)
|
||||
fs/symlink.h fs/symlink.cpp
|
||||
httplib.h)
|
||||
|
||||
if(WIN32)
|
||||
target_sources(common PRIVATE windows/timer_resolution.cpp
|
||||
@@ -242,7 +245,7 @@ else()
|
||||
target_link_libraries(common PUBLIC Boost::headers)
|
||||
endif()
|
||||
|
||||
target_link_libraries(common PUBLIC Boost::filesystem Boost::context)
|
||||
target_link_libraries(common PUBLIC Boost::filesystem Boost::context httplib::httplib)
|
||||
|
||||
if (lz4_ADDED)
|
||||
target_include_directories(common PRIVATE ${lz4_SOURCE_DIR}/lib)
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#define CPPHTTPLIB_DISABLE_MACOSX_AUTOMATIC_ROOT_CERTIFICATES 1
|
||||
#define CPPHTTPLIB_OPENSSL_SUPPORT 1
|
||||
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic push
|
||||
#ifndef __clang__
|
||||
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
|
||||
#endif
|
||||
#endif
|
||||
#include <httplib.h>
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
@@ -0,0 +1,22 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include <optional>
|
||||
#include <random>
|
||||
#include "common/random.h"
|
||||
|
||||
namespace Common::Random {
|
||||
[[nodiscard]] static std::random_device& GetGlobalRandomDevice() noexcept {
|
||||
static std::random_device g_random_device{};
|
||||
return g_random_device;
|
||||
}
|
||||
[[nodiscard]] u32 Random32(u32 seed) noexcept {
|
||||
return GetGlobalRandomDevice()();
|
||||
}
|
||||
[[nodiscard]] u64 Random64(u64 seed) noexcept {
|
||||
return GetGlobalRandomDevice()();
|
||||
}
|
||||
[[nodiscard]] std::mt19937 GetMT19937() noexcept {
|
||||
return std::mt19937(GetGlobalRandomDevice()());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <random>
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Common::Random {
|
||||
[[nodiscard]] u32 Random32(u32 seed) noexcept;
|
||||
[[nodiscard]] u64 Random64(u64 seed) noexcept;
|
||||
[[nodiscard]] std::mt19937 GetMT19937() noexcept;
|
||||
}
|
||||
@@ -287,8 +287,6 @@ struct Values {
|
||||
true,
|
||||
&use_custom_cpu_ticks};
|
||||
|
||||
SwitchableSetting<bool> vtable_bouncing{linkage, true, "vtable_bouncing", Category::Cpu};
|
||||
|
||||
Setting<bool> cpuopt_page_tables{linkage, true, "cpuopt_page_tables", Category::CpuDebug};
|
||||
Setting<bool> cpuopt_block_linking{linkage, true, "cpuopt_block_linking", Category::CpuDebug};
|
||||
Setting<bool> cpuopt_return_stack_buffer{linkage, true, "cpuopt_return_stack_buffer",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
@@ -218,12 +218,6 @@ public:
|
||||
return t0;
|
||||
}
|
||||
|
||||
u64 GenerateRandomU64() {
|
||||
const u32 lo = this->GenerateRandomU32();
|
||||
const u32 hi = this->GenerateRandomU32();
|
||||
return (u64{hi} << 32) | u64{lo};
|
||||
}
|
||||
|
||||
float GenerateRandomF32() {
|
||||
// Floats have 24 bits of mantissa.
|
||||
constexpr u32 MantissaBits = 24;
|
||||
|
||||
+5
-6
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -10,6 +13,7 @@
|
||||
#include "common/assert.h"
|
||||
#include "common/tiny_mt.h"
|
||||
#include "common/uuid.h"
|
||||
#include "common/random.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
@@ -175,21 +179,16 @@ u128 UUID::AsU128() const {
|
||||
}
|
||||
|
||||
UUID UUID::MakeRandom() {
|
||||
std::random_device device;
|
||||
|
||||
return MakeRandomWithSeed(device());
|
||||
return MakeRandomWithSeed(Common::Random::Random32(0));
|
||||
}
|
||||
|
||||
UUID UUID::MakeRandomWithSeed(u32 seed) {
|
||||
// Create and initialize our RNG.
|
||||
TinyMT rng;
|
||||
rng.Initialize(seed);
|
||||
|
||||
UUID uuid;
|
||||
|
||||
// Populate the UUID with random bytes.
|
||||
rng.GenerateRandomBytes(uuid.uuid.data(), sizeof(UUID));
|
||||
|
||||
return uuid;
|
||||
}
|
||||
|
||||
|
||||
@@ -1246,7 +1246,7 @@ if (HAS_NCE)
|
||||
target_link_libraries(core PRIVATE merry::oaknut)
|
||||
endif()
|
||||
|
||||
if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64)
|
||||
if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64 OR ARCHITECTURE_riscv64)
|
||||
target_sources(core PRIVATE
|
||||
arm/dynarmic/arm_dynarmic.h
|
||||
arm/dynarmic/arm_dynarmic_64.cpp
|
||||
@@ -1269,7 +1269,6 @@ endif()
|
||||
target_sources(core PRIVATE hle/service/ssl/ssl_backend_openssl.cpp)
|
||||
|
||||
target_link_libraries(core PRIVATE OpenSSL::SSL OpenSSL::Crypto)
|
||||
target_compile_definitions(core PRIVATE CPPHTTPLIB_OPENSSL_SUPPORT)
|
||||
|
||||
# TODO
|
||||
|
||||
|
||||
@@ -59,14 +59,10 @@ CallbackOrAccessOneWord DynarmicCP15::CompileSendOneWord(bool two, unsigned opc1
|
||||
#if defined(_MSC_VER) && defined(ARCHITECTURE_x86_64)
|
||||
_mm_mfence();
|
||||
_mm_lfence();
|
||||
#elif defined(ARCHITECTURE_x86_64)
|
||||
asm volatile("mfence\n\tlfence\n\t" : : : "memory");
|
||||
#elif defined(_MSC_VER) && defined(ARCHITECTURE_arm64)
|
||||
_Memory_barrier();
|
||||
#elif defined(ARCHITECTURE_arm64)
|
||||
asm volatile("dsb sy\n\t" : : : "memory");
|
||||
#else
|
||||
#error Unsupported architecture
|
||||
__sync_synchronize();
|
||||
#endif
|
||||
return 0;
|
||||
},
|
||||
@@ -78,14 +74,10 @@ CallbackOrAccessOneWord DynarmicCP15::CompileSendOneWord(bool two, unsigned opc1
|
||||
[](void*, std::uint32_t, std::uint32_t) -> std::uint64_t {
|
||||
#if defined(_MSC_VER) && defined(ARCHITECTURE_x86_64)
|
||||
_mm_mfence();
|
||||
#elif defined(ARCHITECTURE_x86_64)
|
||||
asm volatile("mfence\n\t" : : : "memory");
|
||||
#elif defined(_MSC_VER) && defined(ARCHITECTURE_arm64)
|
||||
_Memory_barrier();
|
||||
#elif defined(ARCHITECTURE_arm64)
|
||||
asm volatile("dmb sy\n\t" : : : "memory");
|
||||
#else
|
||||
#error Unsupported architecture
|
||||
__sync_synchronize();
|
||||
#endif
|
||||
return 0;
|
||||
},
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "common/fs/path_util.h"
|
||||
#include "common/hex_util.h"
|
||||
#include "common/logging.h"
|
||||
#include "common/random.h"
|
||||
#include "common/string_util.h"
|
||||
#include "core/crypto/key_manager.h"
|
||||
#include "core/file_sys/card_image.h"
|
||||
@@ -490,17 +491,13 @@ std::vector<NcaID> PlaceholderCache::List() const {
|
||||
}
|
||||
|
||||
NcaID PlaceholderCache::Generate() {
|
||||
std::random_device device;
|
||||
std::mt19937 gen(device());
|
||||
auto gen = Common::Random::GetMT19937();
|
||||
std::uniform_int_distribution<u64> distribution(1, (std::numeric_limits<u64>::max)());
|
||||
|
||||
NcaID out{};
|
||||
|
||||
const auto v1 = distribution(gen);
|
||||
const auto v2 = distribution(gen);
|
||||
std::memcpy(out.data(), &v1, sizeof(u64));
|
||||
std::memcpy(out.data() + sizeof(u64), &v2, sizeof(u64));
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <random>
|
||||
|
||||
#include "common/literals.h"
|
||||
#include "common/random.h"
|
||||
#include "common/settings.h"
|
||||
|
||||
#include "core/hle/kernel/board/nintendo/nx/k_system_control.h"
|
||||
@@ -201,15 +202,8 @@ u64 GenerateUniformRange(u64 min, u64 max, F f) {
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
u64 KSystemControl::GenerateRandomU64() {
|
||||
std::random_device device;
|
||||
std::mt19937 gen(device());
|
||||
std::uniform_int_distribution<u64> distribution(1, (std::numeric_limits<u64>::max)());
|
||||
return distribution(gen);
|
||||
}
|
||||
|
||||
u64 KSystemControl::GenerateRandomRange(u64 min, u64 max) {
|
||||
return GenerateUniformRange(min, max, GenerateRandomU64);
|
||||
return GenerateUniformRange(min, max, Common::Random::GetMT19937());
|
||||
}
|
||||
|
||||
size_t KSystemControl::CalculateRequiredSecureMemorySize(size_t size, u32 pool) {
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -33,7 +36,6 @@ public:
|
||||
|
||||
// Randomness.
|
||||
static u64 GenerateRandomRange(u64 min, u64 max);
|
||||
static u64 GenerateRandomU64();
|
||||
|
||||
// Secure Memory.
|
||||
static size_t CalculateRequiredSecureMemorySize(size_t size, u32 pool);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "common/bit_util.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/tiny_mt.h"
|
||||
#include "common/random.h"
|
||||
#include "core/hle/kernel/k_system_control.h"
|
||||
|
||||
namespace Kernel {
|
||||
@@ -23,7 +24,7 @@ public:
|
||||
class RandomBitGenerator {
|
||||
public:
|
||||
RandomBitGenerator() {
|
||||
m_rng.Initialize(static_cast<u32>(KSystemControl::GenerateRandomU64()));
|
||||
m_rng.Initialize(u32(Common::Random::Random64(0)));
|
||||
}
|
||||
|
||||
u64 SelectRandomBit(u64 bitmap) {
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "common/fiber.h"
|
||||
#include "common/logging.h"
|
||||
#include "common/settings.h"
|
||||
#include "common/random.h"
|
||||
#include "core/core.h"
|
||||
#include "core/cpu_manager.h"
|
||||
#include "core/hardware_properties.h"
|
||||
@@ -45,8 +46,7 @@ namespace {
|
||||
|
||||
constexpr inline s32 TerminatingThreadPriority = Kernel::Svc::SystemThreadPriorityHighest - 1;
|
||||
|
||||
static void ResetThreadContext32(Kernel::Svc::ThreadContext& ctx, u64 stack_top, u64 entry_point,
|
||||
u64 arg) {
|
||||
static void ResetThreadContext32(Kernel::Svc::ThreadContext& ctx, u64 stack_top, u64 entry_point, u64 arg) {
|
||||
ctx = {};
|
||||
ctx.r[0] = arg;
|
||||
ctx.r[15] = entry_point;
|
||||
@@ -55,11 +55,10 @@ static void ResetThreadContext32(Kernel::Svc::ThreadContext& ctx, u64 stack_top,
|
||||
ctx.fpsr = 0;
|
||||
}
|
||||
|
||||
static void ResetThreadContext64(Kernel::Svc::ThreadContext& ctx, u64 stack_top, u64 entry_point,
|
||||
u64 arg) {
|
||||
static void ResetThreadContext64(Kernel::Svc::ThreadContext& ctx, u64 stack_top, u64 entry_point, u64 arg) {
|
||||
ctx = {};
|
||||
ctx.r[0] = arg;
|
||||
ctx.r[18] = Kernel::KSystemControl::GenerateRandomU64() | 1;
|
||||
ctx.r[18] = Common::Random::Random64(0) | 1;
|
||||
ctx.pc = entry_point;
|
||||
ctx.sp = stack_top;
|
||||
ctx.fpcr = 0;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
@@ -103,26 +103,12 @@ void PhysicalCore::RunThread(Kernel::KThread* thread) {
|
||||
const bool data_abort = True(hr & Core::HaltReason::DataAbort);
|
||||
const bool interrupt = True(hr & Core::HaltReason::BreakLoop);
|
||||
|
||||
bool may_abort = true; // Ignore aborting virtual functions (for debugging)
|
||||
if (prefetch_abort && ::Settings::values.vtable_bouncing) {
|
||||
auto& lock = m_kernel.GlobalSchedulerContext().SchedulerLock();
|
||||
lock.Lock();
|
||||
Kernel::Svc::ThreadContext ctx;
|
||||
interface->GetContext(ctx);
|
||||
LOG_WARNING(Core_ARM, "vtable bouncing {:016X}", ctx.lr);
|
||||
ctx.pc = ctx.lr;
|
||||
ctx.r[0] = 0;
|
||||
interface->SetContext(ctx);
|
||||
lock.Unlock();
|
||||
may_abort = false;
|
||||
}
|
||||
|
||||
// Since scheduling may occur here, we cannot use any cached
|
||||
// state after returning from calls we make.
|
||||
|
||||
// Notify the debugger and go to sleep if a breakpoint was hit,
|
||||
// or if the thread is unable to continue for any reason.
|
||||
if (breakpoint || (prefetch_abort && may_abort)) {
|
||||
if (breakpoint || prefetch_abort) {
|
||||
if (breakpoint) {
|
||||
interface->RewindBreakpointInstruction();
|
||||
}
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
|
||||
#include "core/core.h"
|
||||
#include "core/hle/kernel/k_thread.h"
|
||||
#include "core/hle/kernel/svc.h"
|
||||
@@ -8,14 +15,62 @@
|
||||
|
||||
namespace Kernel::Svc {
|
||||
|
||||
constexpr auto MAX_MSG_TIME = std::chrono::milliseconds(250);
|
||||
const auto MAX_MSG_SIZE = 0x1000;
|
||||
|
||||
static std::string msg_buffer;
|
||||
static std::mutex msg_mutex;
|
||||
static std::condition_variable msg_cv;
|
||||
static std::chrono::steady_clock::time_point last_msg_time;
|
||||
static bool worker_running = true;
|
||||
static std::unique_ptr<std::thread> flush_thread;
|
||||
static std::once_flag start_flag;
|
||||
|
||||
static void FlushDbgLoop() {
|
||||
while (true) {
|
||||
std::unique_lock lock(msg_mutex);
|
||||
|
||||
msg_cv.wait(lock, [] { return !msg_buffer.empty() || !worker_running; });
|
||||
if (!worker_running && msg_buffer.empty()) break;
|
||||
|
||||
auto timeout = last_msg_time + MAX_MSG_TIME;
|
||||
bool woke_early = msg_cv.wait_until(lock, timeout, [] {
|
||||
return msg_buffer.size() >= MAX_MSG_SIZE || !worker_running;
|
||||
});
|
||||
|
||||
if (!woke_early || msg_buffer.size() >= MAX_MSG_SIZE || !worker_running) {
|
||||
if (!msg_buffer.empty()) {
|
||||
// Remove trailing newline as LOG_INFO adds that anyways
|
||||
if (msg_buffer.back() == '\n')
|
||||
msg_buffer.pop_back();
|
||||
|
||||
LOG_INFO(Debug_Emulated, "\n{}", msg_buffer);
|
||||
msg_buffer.clear();
|
||||
}
|
||||
if (!worker_running) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Used to output a message on a debug hardware unit - does nothing on a retail unit
|
||||
Result OutputDebugString(Core::System& system, u64 address, u64 len) {
|
||||
R_SUCCEED_IF(len == 0);
|
||||
|
||||
std::string str(len, '\0');
|
||||
GetCurrentMemory(system.Kernel()).ReadBlock(address, str.data(), str.size());
|
||||
LOG_INFO(Debug_Emulated, "{}", str);
|
||||
// Only start the thread the very first time this function is called
|
||||
std::call_once(start_flag, [] {
|
||||
flush_thread = std::make_unique<std::thread>(FlushDbgLoop);
|
||||
});
|
||||
|
||||
{
|
||||
std::lock_guard lock(msg_mutex);
|
||||
const auto old_size = msg_buffer.size();
|
||||
msg_buffer.resize(old_size + len);
|
||||
GetCurrentMemory(system.Kernel()).ReadBlock(address, msg_buffer.data() + old_size, len);
|
||||
|
||||
last_msg_time = std::chrono::steady_clock::now();
|
||||
}
|
||||
|
||||
msg_cv.notify_one();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
@@ -27,4 +82,16 @@ Result OutputDebugString64From32(Core::System& system, uint32_t debug_str, uint3
|
||||
R_RETURN(OutputDebugString(system, debug_str, len));
|
||||
}
|
||||
|
||||
struct BufferAutoFlush {
|
||||
~BufferAutoFlush() {
|
||||
{
|
||||
std::lock_guard lock(msg_mutex);
|
||||
worker_running = false;
|
||||
}
|
||||
msg_cv.notify_all();
|
||||
if (flush_thread && flush_thread->joinable()) flush_thread->join();
|
||||
}
|
||||
};
|
||||
static BufferAutoFlush auto_flusher;
|
||||
|
||||
} // namespace Kernel::Svc
|
||||
|
||||
@@ -15,9 +15,7 @@
|
||||
#include <fmt/format.h>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
#include <httplib.h>
|
||||
#endif
|
||||
#include "common/httplib.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <cstring>
|
||||
@@ -37,7 +35,8 @@
|
||||
namespace Service::News {
|
||||
namespace {
|
||||
|
||||
constexpr const char* GitHubAPI_EdenReleases = "/repos/eden-emulator/Releases/releases";
|
||||
// TODO(crueter): COMPILE DEFINITION
|
||||
constexpr const char* GitHubAPI_EdenReleases = "/api/v1/repos/eden-emu/eden/releases";
|
||||
|
||||
// Cached logo data
|
||||
std::vector<u8> default_logo_small;
|
||||
@@ -103,8 +102,6 @@ std::vector<u8> TryLoadFromDisk(const std::filesystem::path& path) {
|
||||
|
||||
std::vector<u8> DownloadImage(const std::string& url_path, const std::filesystem::path& cache_path) {
|
||||
LOG_INFO(Service_BCAT, "Downloading image: https://eden-emu.dev{}", url_path);
|
||||
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
try {
|
||||
httplib::Client cli("https://eden-emu.dev");
|
||||
cli.set_follow_location(true);
|
||||
@@ -128,8 +125,6 @@ std::vector<u8> DownloadImage(const std::string& url_path, const std::filesystem
|
||||
} catch (...) {
|
||||
LOG_WARNING(Service_BCAT, "Failed to download: {}", url_path);
|
||||
}
|
||||
#endif
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -232,30 +227,63 @@ void WriteCachedJson(std::string_view json) {
|
||||
}
|
||||
|
||||
std::optional<std::string> DownloadReleasesJson() {
|
||||
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
try {
|
||||
httplib::SSLClient cli{"api.github.com", 443};
|
||||
cli.set_connection_timeout(10);
|
||||
cli.set_read_timeout(10);
|
||||
|
||||
httplib::Headers headers{
|
||||
{"User-Agent", "Eden"},
|
||||
{"Accept", "application/vnd.github+json"},
|
||||
};
|
||||
|
||||
// TODO(crueter): automate this in some way...
|
||||
#ifdef YUZU_BUNDLED_OPENSSL
|
||||
cli.load_ca_cert_store(kCert, sizeof(kCert));
|
||||
const auto url = "https://git.eden-emu.dev";
|
||||
#else
|
||||
const auto url = "git.eden-emu.dev";
|
||||
#endif
|
||||
|
||||
if (auto res = cli.Get(GitHubAPI_EdenReleases, headers); res && res->status < 400) {
|
||||
return res->body;
|
||||
// TODO(crueter): This is duplicated between frontend and here.
|
||||
constexpr auto path = GitHubAPI_EdenReleases;
|
||||
|
||||
constexpr std::size_t timeout_seconds = 15;
|
||||
|
||||
std::unique_ptr<httplib::Client> client = std::make_unique<httplib::Client>(url);
|
||||
client->set_connection_timeout(timeout_seconds);
|
||||
client->set_read_timeout(timeout_seconds);
|
||||
client->set_write_timeout(timeout_seconds);
|
||||
|
||||
#ifdef YUZU_BUNDLED_OPENSSL
|
||||
client->load_ca_cert_store(kCert, sizeof(kCert));
|
||||
#endif
|
||||
|
||||
if (client == nullptr) {
|
||||
LOG_ERROR(Service_BCAT, "Invalid URL {}{}", url, path);
|
||||
return {};
|
||||
}
|
||||
|
||||
httplib::Request request{
|
||||
.method = "GET",
|
||||
.path = path,
|
||||
};
|
||||
|
||||
client->set_follow_location(true);
|
||||
httplib::Result result = client->send(request);
|
||||
|
||||
if (!result) {
|
||||
LOG_ERROR(Service_BCAT, "GET to {}{} returned null", url, path);
|
||||
return {};
|
||||
} else if (result->status < 400) {
|
||||
return result->body;
|
||||
}
|
||||
|
||||
if (result->status >= 400) {
|
||||
LOG_ERROR(Service_BCAT,
|
||||
"GET to {}{} returned error status code: {}",
|
||||
url,
|
||||
path,
|
||||
result->status);
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!result->headers.contains("content-type")) {
|
||||
LOG_ERROR(Service_BCAT, "GET to {}{} returned no content", url, path);
|
||||
return {};
|
||||
}
|
||||
} catch (...) {
|
||||
LOG_WARNING(Service_BCAT, " failed to download releases");
|
||||
}
|
||||
#endif
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
@@ -341,7 +369,7 @@ std::string FormatBody(const nlohmann::json& release, std::string_view title) {
|
||||
body.pop_back();
|
||||
}
|
||||
|
||||
body += "\n\n... View more on GitHub";
|
||||
body += "\n\n... View more on Forgejo";
|
||||
}
|
||||
|
||||
return body;
|
||||
@@ -498,7 +526,7 @@ std::vector<u8> BuildMsgpack(std::string_view title, std::string_view body,
|
||||
w.WriteString("");
|
||||
|
||||
w.WriteKey("allow_domains");
|
||||
w.WriteString("^https?://github.com(/|$)");
|
||||
w.WriteString("^https?://git.eden-emu.dev(/|$)");
|
||||
|
||||
// More link
|
||||
w.WriteKey("more");
|
||||
@@ -508,7 +536,7 @@ std::vector<u8> BuildMsgpack(std::string_view title, std::string_view body,
|
||||
w.WriteKey("url");
|
||||
w.WriteString(html_url);
|
||||
w.WriteKey("text");
|
||||
w.WriteString("Open GitHub");
|
||||
w.WriteString("Open Forgejo");
|
||||
|
||||
// Body
|
||||
w.WriteKey("body");
|
||||
@@ -545,7 +573,7 @@ void EnsureBuiltinNewsLoaded() {
|
||||
if (const auto fresh = DownloadReleasesJson()) {
|
||||
WriteCachedJson(*fresh);
|
||||
ImportReleases(*fresh);
|
||||
LOG_DEBUG(Service_BCAT, "news: {} entries updated from GitHub", NewsStorage::Instance().ListAll().size());
|
||||
LOG_DEBUG(Service_BCAT, "news: {} entries updated from Forgejo", NewsStorage::Instance().ListAll().size());
|
||||
}
|
||||
}).detach();
|
||||
});
|
||||
|
||||
@@ -438,20 +438,20 @@ void WriteOutArgument(bool is_domain, CallArguments& args, u8* raw_data, HLERequ
|
||||
|
||||
template <bool Domain, typename T, typename... A>
|
||||
void CmifReplyWrapImpl(HLERequestContext& ctx, T& t, Result (T::*f)(A...)) {
|
||||
const auto mgr = ctx.GetManager().get();
|
||||
// Verify domain state.
|
||||
if constexpr (!Domain) {
|
||||
const auto _mgr = ctx.GetManager();
|
||||
const bool _is_domain = _mgr ? _mgr->IsDomain() : false;
|
||||
ASSERT_MSG(!_is_domain,
|
||||
"Non-domain reply used on domain session\n"
|
||||
"Service={} (TIPC={} CmdType={} Cmd=0x{:08X}\n"
|
||||
"HasDomainHeader={} DomainHandlers={}\nDesc={}",
|
||||
t.GetServiceName(), ctx.IsTipc(),
|
||||
static_cast<u32>(ctx.GetCommandType()), static_cast<u32>(ctx.GetCommand()),
|
||||
ctx.HasDomainMessageHeader(), _mgr ? static_cast<u32>(_mgr->DomainHandlerCount()) : 0u,
|
||||
ctx.Description());
|
||||
const bool is_domain = mgr ? mgr->IsDomain() : false;
|
||||
ASSERT_MSG(!is_domain,
|
||||
"Non-domain reply used on domain session\n"
|
||||
"Service={} (TIPC={} CmdType={} Cmd=0x{:08X}\n"
|
||||
"HasDomainHeader={} DomainHandlers={}\nDesc={}",
|
||||
t.GetServiceName(), ctx.IsTipc(),
|
||||
u32(ctx.GetCommandType()), u32(ctx.GetCommand()),
|
||||
ctx.HasDomainMessageHeader(), mgr ? u32(mgr->DomainHandlerCount()) : 0u,
|
||||
ctx.Description());
|
||||
}
|
||||
const bool is_domain = Domain ? ctx.GetManager()->IsDomain() : false;
|
||||
const bool is_domain = Domain ? mgr->IsDomain() : false;
|
||||
|
||||
static_assert(ConstIfReference<A...>(), "Arguments taken by reference must be const");
|
||||
using MethodArguments = std::tuple<std::remove_cvref_t<A>...>;
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: 2016 Citra Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -78,32 +81,29 @@ public:
|
||||
memset(cmdbuf, 0, sizeof(u32) * IPC::COMMAND_BUFFER_LENGTH);
|
||||
|
||||
IPC::CommandHeader header{};
|
||||
auto const mgr = ctx.GetManager().get();
|
||||
|
||||
// The entire size of the raw data section in u32 units, including the 16 bytes of mandatory
|
||||
// padding.
|
||||
u32 raw_data_size = ctx.write_size =
|
||||
ctx.IsTipc() ? normal_params_size - 1 : normal_params_size;
|
||||
u32 raw_data_size = ctx.write_size = ctx.IsTipc() ? normal_params_size - 1 : normal_params_size;
|
||||
u32 num_handles_to_move{};
|
||||
u32 num_domain_objects{};
|
||||
const bool always_move_handles{
|
||||
(static_cast<u32>(flags) & static_cast<u32>(Flags::AlwaysMoveHandles)) != 0};
|
||||
if (!ctx.GetManager()->IsDomain() || always_move_handles) {
|
||||
const bool always_move_handles = (u32(flags) & u32(Flags::AlwaysMoveHandles)) != 0;
|
||||
if (!mgr->IsDomain() || always_move_handles) {
|
||||
num_handles_to_move = num_objects_to_move;
|
||||
} else {
|
||||
num_domain_objects = num_objects_to_move;
|
||||
}
|
||||
|
||||
if (ctx.GetManager()->IsDomain()) {
|
||||
raw_data_size +=
|
||||
static_cast<u32>(sizeof(DomainMessageHeader) / sizeof(u32) + num_domain_objects);
|
||||
if (mgr->IsDomain()) {
|
||||
raw_data_size += u32(sizeof(DomainMessageHeader) / sizeof(u32) + num_domain_objects);
|
||||
ctx.write_size += num_domain_objects;
|
||||
}
|
||||
|
||||
if (ctx.IsTipc()) {
|
||||
header.type.Assign(ctx.GetCommandType());
|
||||
} else {
|
||||
raw_data_size += static_cast<u32>(sizeof(IPC::DataPayloadHeader) / sizeof(u32) + 4 +
|
||||
normal_params_size);
|
||||
raw_data_size += u32(sizeof(IPC::DataPayloadHeader) / sizeof(u32) + 4 + normal_params_size);
|
||||
}
|
||||
|
||||
header.data_size.Assign(raw_data_size);
|
||||
@@ -126,7 +126,7 @@ public:
|
||||
if (!ctx.IsTipc()) {
|
||||
AlignWithPadding();
|
||||
|
||||
if (ctx.GetManager()->IsDomain() && ctx.HasDomainMessageHeader()) {
|
||||
if (mgr->IsDomain() && ctx.HasDomainMessageHeader()) {
|
||||
IPC::DomainMessageHeader domain_header{};
|
||||
domain_header.num_objects = num_domain_objects;
|
||||
PushRaw(domain_header);
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -7,6 +10,7 @@
|
||||
#include <span>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/random.h"
|
||||
#include "common/swap.h"
|
||||
#include "common/uuid.h"
|
||||
#include "core/hle/service/mii/mii_types.h"
|
||||
@@ -65,11 +69,9 @@ public:
|
||||
|
||||
template <typename T>
|
||||
static T GetRandomValue(T min, T max) {
|
||||
std::random_device device;
|
||||
std::mt19937 gen(device());
|
||||
std::uniform_int_distribution<u64> distribution(static_cast<u64>(min),
|
||||
static_cast<u64>(max));
|
||||
return static_cast<T>(distribution(gen));
|
||||
std::uniform_int_distribution<u64> distribution{u64(min), u64(max)};
|
||||
auto gen = Common::Random::GetMT19937();
|
||||
return T(distribution(gen));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include <cstring>
|
||||
#include "common/logging.h"
|
||||
#include "common/random.h"
|
||||
#include "common/settings.h"
|
||||
#include "core/core.h"
|
||||
#include "core/file_sys/content_archive.h"
|
||||
@@ -229,7 +230,7 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect
|
||||
// TODO: this is bad form of ASLR, it sucks
|
||||
size_t aslr_offset = ((::Settings::values.rng_seed_enabled.GetValue()
|
||||
? ::Settings::values.rng_seed.GetValue()
|
||||
: std::rand()) * 0x734287f27) & 0xfff000;
|
||||
: Common::Random::Random64(0)) * 0x734287f27) & 0xfff000;
|
||||
|
||||
// Setup the process code layout
|
||||
if (process.LoadFromMetadata(metadata, code_size, fastmem_base, aslr_offset, is_hbl).IsError()) {
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include <cstring>
|
||||
#include "common/settings.h"
|
||||
#include "common/random.h"
|
||||
#include "core/file_sys/kernel_executable.h"
|
||||
#include "core/file_sys/program_metadata.h"
|
||||
#include "core/hle/kernel/code_set.h"
|
||||
@@ -90,7 +91,7 @@ AppLoader::LoadResult AppLoader_KIP::Load(Kernel::KProcess& process,
|
||||
// TODO: this is bad form of ASLR, it sucks
|
||||
size_t aslr_offset = ((::Settings::values.rng_seed_enabled.GetValue()
|
||||
? ::Settings::values.rng_seed.GetValue()
|
||||
: std::rand()) * 0x734287f27) & 0xfff000;
|
||||
: Common::Random::Random64(0)) * 0x734287f27) & 0xfff000;
|
||||
|
||||
// Setup the process code layout
|
||||
if (process.LoadFromMetadata(FileSys::ProgramMetadata::GetDefault(), codeset.memory.size(), 0, aslr_offset, false).IsError()) {
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging.h"
|
||||
#include "common/settings.h"
|
||||
#include "common/random.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/core.h"
|
||||
#include "core/file_sys/control_metadata.h"
|
||||
@@ -243,7 +244,7 @@ static bool LoadNroImpl(Core::System& system, Kernel::KProcess& process,
|
||||
// TODO: this is bad form of ASLR, it sucks
|
||||
size_t aslr_offset = ((::Settings::values.rng_seed_enabled.GetValue()
|
||||
? ::Settings::values.rng_seed.GetValue()
|
||||
: std::rand()) * 0x734287f27) & 0xfff000;
|
||||
: Common::Random::Random64(0)) * 0x734287f27) & 0xfff000;
|
||||
|
||||
// Setup the process code layout
|
||||
if (process
|
||||
|
||||
+2
-12
@@ -45,11 +45,7 @@ static inline bool AddressSpaceContains(const Common::PageTable& table, const Co
|
||||
// from outside classes. This also allows modification to the internals of the memory
|
||||
// subsystem without needing to rebuild all files that make use of the memory interface.
|
||||
struct Memory::Impl {
|
||||
explicit Impl(Core::System& system_) : system{system_} {
|
||||
// Initialize thread count based on available cores for parallel memory operations
|
||||
const unsigned int hw_concurrency = std::thread::hardware_concurrency();
|
||||
thread_count = (std::max)(2u, (std::min)(hw_concurrency, 8u)); // Limit to 8 threads max
|
||||
}
|
||||
explicit Impl(Core::System& system_) : system{system_} {}
|
||||
|
||||
void SetCurrentPageTable(Kernel::KProcess& process) {
|
||||
current_page_table = &process.GetPageTable().GetImpl();
|
||||
@@ -856,13 +852,7 @@ struct Memory::Impl {
|
||||
Tegra::MaxwellDeviceMemoryManager* gpu_device_memory{};
|
||||
Common::PageTable* current_page_table = nullptr;
|
||||
|
||||
// Number of threads to use for parallel memory operations
|
||||
unsigned int thread_count = 2;
|
||||
|
||||
// Minimum size in bytes for which parallel processing is beneficial
|
||||
//size_t PARALLEL_THRESHOLD = (L3 CACHE * NUM PHYSICAL CORES); // 64 KB
|
||||
std::array<VideoCore::RasterizerDownloadArea, Core::Hardware::NUM_CPU_CORES>
|
||||
rasterizer_read_areas{};
|
||||
std::array<VideoCore::RasterizerDownloadArea, Core::Hardware::NUM_CPU_CORES> rasterizer_read_areas{};
|
||||
std::array<GPUDirtyState, Core::Hardware::NUM_CPU_CORES> rasterizer_write_areas{};
|
||||
std::array<Common::ScratchBuffer<u32>, Core::Hardware::NUM_CPU_CORES> scratch_buffers{};
|
||||
std::span<Core::GPUDirtyMemoryManager> gpu_dirty_managers;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
@@ -51,10 +51,12 @@ void RenderdocAPI::ToggleCapture() {
|
||||
if (!rdoc_api) [[unlikely]] {
|
||||
return;
|
||||
}
|
||||
|
||||
auto* api = static_cast<RENDERDOC_API_1_6_0*>(rdoc_api);
|
||||
if (!is_capturing) {
|
||||
rdoc_api->StartFrameCapture(NULL, NULL);
|
||||
api->StartFrameCapture(NULL, NULL);
|
||||
} else {
|
||||
rdoc_api->EndFrameCapture(NULL, NULL);
|
||||
api->EndFrameCapture(NULL, NULL);
|
||||
}
|
||||
is_capturing = !is_capturing;
|
||||
}
|
||||
|
||||
@@ -6,8 +6,6 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
struct RENDERDOC_API_1_7_0;
|
||||
|
||||
namespace Tools {
|
||||
|
||||
class RenderdocAPI {
|
||||
@@ -18,7 +16,7 @@ public:
|
||||
void ToggleCapture();
|
||||
|
||||
private:
|
||||
RENDERDOC_API_1_7_0* rdoc_api{};
|
||||
void* rdoc_api{};
|
||||
bool is_capturing{false};
|
||||
};
|
||||
|
||||
|
||||
@@ -126,7 +126,7 @@ if ("arm64" IN_LIST ARCHITECTURE OR DYNARMIC_TESTS)
|
||||
find_package(oaknut 2.0.1 CONFIG)
|
||||
endif()
|
||||
|
||||
if ("riscv" IN_LIST ARCHITECTURE)
|
||||
if ("riscv64" IN_LIST ARCHITECTURE)
|
||||
find_package(biscuit 0.9.1 REQUIRED)
|
||||
endif()
|
||||
|
||||
|
||||
@@ -13,10 +13,7 @@ add_library(dynarmic STATIC
|
||||
backend/block_range_information.h
|
||||
backend/exception_handler.h
|
||||
common/always_false.h
|
||||
common/assert.cpp
|
||||
common/assert.h
|
||||
common/cast_util.h
|
||||
common/common_types.h
|
||||
common/crypto/aes.cpp
|
||||
common/crypto/aes.h
|
||||
common/crypto/crc32.cpp
|
||||
@@ -258,7 +255,7 @@ if ("arm64" IN_LIST ARCHITECTURE)
|
||||
)
|
||||
endif()
|
||||
|
||||
if ("riscv" IN_LIST ARCHITECTURE)
|
||||
if ("riscv64" IN_LIST ARCHITECTURE)
|
||||
target_link_libraries(dynarmic PRIVATE biscuit::biscuit)
|
||||
|
||||
target_sources(dynarmic PRIVATE
|
||||
@@ -281,6 +278,7 @@ if ("riscv" IN_LIST ARCHITECTURE)
|
||||
backend/riscv64/emit_riscv64_vector.cpp
|
||||
backend/riscv64/emit_riscv64.cpp
|
||||
backend/riscv64/emit_riscv64.h
|
||||
backend/riscv64/exclusive_monitor.cpp
|
||||
backend/riscv64/reg_alloc.cpp
|
||||
backend/riscv64/reg_alloc.h
|
||||
backend/riscv64/stack_layout.h
|
||||
@@ -289,9 +287,12 @@ if ("riscv" IN_LIST ARCHITECTURE)
|
||||
backend/riscv64/a32_address_space.h
|
||||
backend/riscv64/a32_core.h
|
||||
backend/riscv64/a32_interface.cpp
|
||||
backend/riscv64/a64_interface.cpp
|
||||
backend/riscv64/code_block.h
|
||||
|
||||
common/spin_lock_riscv64.cpp
|
||||
)
|
||||
message(FATAL_ERROR "TODO: Unimplemented frontend for this host architecture")
|
||||
message(WARNING "TODO: Incomplete frontend for this host architecture")
|
||||
endif()
|
||||
|
||||
if (WIN32)
|
||||
@@ -359,7 +360,7 @@ set_target_properties(dynarmic PROPERTIES
|
||||
target_compile_options(dynarmic PRIVATE ${DYNARMIC_CXX_FLAGS})
|
||||
|
||||
target_link_libraries(dynarmic PRIVATE unordered_dense::unordered_dense)
|
||||
target_link_libraries(dynarmic PUBLIC fmt::fmt)
|
||||
target_link_libraries(dynarmic PUBLIC fmt::fmt common)
|
||||
|
||||
if (BOOST_NO_HEADERS)
|
||||
target_link_libraries(dynarmic PRIVATE Boost::variant Boost::icl Boost::pool)
|
||||
|
||||
@@ -227,7 +227,7 @@ void A32AddressSpace::EmitPrelude() {
|
||||
|
||||
if (conf.HasOptimization(OptimizationFlag::ReturnStackBuffer)) {
|
||||
code.LDR(Xscratch0, l_return_to_dispatcher);
|
||||
for (size_t i = 0; i < RSBCount; i++) {
|
||||
for (std::size_t i = 0; i < RSBCount; i++) {
|
||||
code.STR(Xscratch0, SP, offsetof(StackLayout, rsb) + offsetof(RSBEntry, code_ptr) + i * sizeof(RSBEntry));
|
||||
}
|
||||
}
|
||||
@@ -266,7 +266,7 @@ void A32AddressSpace::EmitPrelude() {
|
||||
|
||||
if (conf.HasOptimization(OptimizationFlag::ReturnStackBuffer)) {
|
||||
code.LDR(Xscratch0, l_return_to_dispatcher);
|
||||
for (size_t i = 0; i < RSBCount; i++) {
|
||||
for (std::size_t i = 0; i < RSBCount; i++) {
|
||||
code.STR(Xscratch0, SP, offsetof(StackLayout, rsb) + offsetof(RSBEntry, code_ptr) + i * sizeof(RSBEntry));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,8 +10,8 @@
|
||||
#include <mutex>
|
||||
|
||||
#include <boost/icl/interval_set.hpp>
|
||||
#include "dynarmic/common/assert.h"
|
||||
#include "dynarmic/common/common_types.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
#include "dynarmic/backend/arm64/a32_address_space.h"
|
||||
#include "dynarmic/backend/arm64/a32_core.h"
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
#include "dynarmic/backend/arm64/a32_jitstate.h"
|
||||
|
||||
#include "dynarmic/mcl/bit.hpp"
|
||||
#include "dynarmic/common/common_types.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Dynarmic::Backend::Arm64 {
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
/* This file is part of the dynarmic project.
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
#include <array>
|
||||
|
||||
#include "dynarmic/common/common_types.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
#include "dynarmic/frontend/A32/a32_location_descriptor.h"
|
||||
#include "dynarmic/ir/location_descriptor.h"
|
||||
|
||||
@@ -403,7 +403,7 @@ void A64AddressSpace::EmitPrelude() {
|
||||
|
||||
if (conf.HasOptimization(OptimizationFlag::ReturnStackBuffer)) {
|
||||
code.LDR(Xscratch0, l_return_to_dispatcher);
|
||||
for (size_t i = 0; i < RSBCount; i++) {
|
||||
for (std::size_t i = 0; i < RSBCount; i++) {
|
||||
code.STR(Xscratch0, SP, offsetof(StackLayout, rsb) + offsetof(RSBEntry, code_ptr) + i * sizeof(RSBEntry));
|
||||
}
|
||||
}
|
||||
@@ -441,7 +441,7 @@ void A64AddressSpace::EmitPrelude() {
|
||||
|
||||
if (conf.HasOptimization(OptimizationFlag::ReturnStackBuffer)) {
|
||||
code.LDR(Xscratch0, l_return_to_dispatcher);
|
||||
for (size_t i = 0; i < RSBCount; i++) {
|
||||
for (std::size_t i = 0; i < RSBCount; i++) {
|
||||
code.STR(Xscratch0, SP, offsetof(StackLayout, rsb) + offsetof(RSBEntry, code_ptr) + i * sizeof(RSBEntry));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,8 +10,8 @@
|
||||
#include <mutex>
|
||||
|
||||
#include <boost/icl/interval_set.hpp>
|
||||
#include "dynarmic/common/assert.h"
|
||||
#include "dynarmic/common/common_types.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
#include "dynarmic/backend/arm64/a64_address_space.h"
|
||||
#include "dynarmic/backend/arm64/a64_core.h"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
/* This file is part of the dynarmic project.
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
#include <array>
|
||||
|
||||
#include "dynarmic/common/common_types.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
#include "dynarmic/frontend/A64/a64_location_descriptor.h"
|
||||
|
||||
|
||||
@@ -11,22 +11,22 @@
|
||||
#include <vector>
|
||||
|
||||
#include "dynarmic/mcl/bit.hpp"
|
||||
#include "dynarmic/common/common_types.h"
|
||||
#include "common/common_types.h"
|
||||
#include <oaknut/oaknut.hpp>
|
||||
|
||||
namespace Dynarmic::Backend::Arm64 {
|
||||
|
||||
using namespace oaknut::util;
|
||||
|
||||
static constexpr size_t gpr_size = 8;
|
||||
static constexpr size_t fpr_size = 16;
|
||||
static constexpr std::size_t gpr_size = 8;
|
||||
static constexpr std::size_t fpr_size = 16;
|
||||
|
||||
struct FrameInfo {
|
||||
std::vector<int> gprs;
|
||||
std::vector<int> fprs;
|
||||
size_t frame_size;
|
||||
size_t gprs_size;
|
||||
size_t fprs_size;
|
||||
std::size_t frame_size;
|
||||
std::size_t gprs_size;
|
||||
std::size_t fprs_size;
|
||||
};
|
||||
|
||||
static std::vector<int> ListToIndexes(u32 list) {
|
||||
@@ -39,15 +39,15 @@ static std::vector<int> ListToIndexes(u32 list) {
|
||||
return indexes;
|
||||
}
|
||||
|
||||
static FrameInfo CalculateFrameInfo(RegisterList rl, size_t frame_size) {
|
||||
static FrameInfo CalculateFrameInfo(RegisterList rl, std::size_t frame_size) {
|
||||
const auto gprs = ListToIndexes(static_cast<u32>(rl));
|
||||
const auto fprs = ListToIndexes(static_cast<u32>(rl >> 32));
|
||||
|
||||
const size_t num_gprs = gprs.size();
|
||||
const size_t num_fprs = fprs.size();
|
||||
const std::size_t num_gprs = gprs.size();
|
||||
const std::size_t num_fprs = fprs.size();
|
||||
|
||||
const size_t gprs_size = (num_gprs + 1) / 2 * 16;
|
||||
const size_t fprs_size = num_fprs * 16;
|
||||
const std::size_t gprs_size = (num_gprs + 1) / 2 * 16;
|
||||
const std::size_t fprs_size = num_fprs * 16;
|
||||
|
||||
return {
|
||||
gprs,
|
||||
@@ -60,16 +60,16 @@ static FrameInfo CalculateFrameInfo(RegisterList rl, size_t frame_size) {
|
||||
|
||||
#define DO_IT(TYPE, REG_TYPE, PAIR_OP, SINGLE_OP, OFFSET) \
|
||||
if (frame_info.TYPE##s.size() > 0) { \
|
||||
for (size_t i = 0; i < frame_info.TYPE##s.size() - 1; i += 2) { \
|
||||
for (std::size_t i = 0; i < frame_info.TYPE##s.size() - 1; i += 2) { \
|
||||
code.PAIR_OP(oaknut::REG_TYPE{frame_info.TYPE##s[i]}, oaknut::REG_TYPE{frame_info.TYPE##s[i + 1]}, SP, (OFFSET) + i * TYPE##_size); \
|
||||
} \
|
||||
if (frame_info.TYPE##s.size() % 2 == 1) { \
|
||||
const size_t i = frame_info.TYPE##s.size() - 1; \
|
||||
const std::size_t i = frame_info.TYPE##s.size() - 1; \
|
||||
code.SINGLE_OP(oaknut::REG_TYPE{frame_info.TYPE##s[i]}, SP, (OFFSET) + i * TYPE##_size); \
|
||||
} \
|
||||
}
|
||||
|
||||
void ABI_PushRegisters(oaknut::CodeGenerator& code, RegisterList rl, size_t frame_size) {
|
||||
void ABI_PushRegisters(oaknut::CodeGenerator& code, RegisterList rl, std::size_t frame_size) {
|
||||
const FrameInfo frame_info = CalculateFrameInfo(rl, frame_size);
|
||||
|
||||
code.SUB(SP, SP, frame_info.gprs_size + frame_info.fprs_size);
|
||||
@@ -80,7 +80,7 @@ void ABI_PushRegisters(oaknut::CodeGenerator& code, RegisterList rl, size_t fram
|
||||
code.SUB(SP, SP, frame_info.frame_size);
|
||||
}
|
||||
|
||||
void ABI_PopRegisters(oaknut::CodeGenerator& code, RegisterList rl, size_t frame_size) {
|
||||
void ABI_PopRegisters(oaknut::CodeGenerator& code, RegisterList rl, std::size_t frame_size) {
|
||||
const FrameInfo frame_info = CalculateFrameInfo(rl, frame_size);
|
||||
|
||||
code.ADD(SP, SP, frame_info.frame_size);
|
||||
|
||||
@@ -9,14 +9,11 @@
|
||||
#pragma once
|
||||
|
||||
#include <initializer_list>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
|
||||
#include "dynarmic/common/common_types.h"
|
||||
#include "dynarmic/common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/assert.h"
|
||||
#include <oaknut/oaknut.hpp>
|
||||
|
||||
#include "dynarmic/common/always_false.h"
|
||||
|
||||
namespace Dynarmic::Backend::Arm64 {
|
||||
|
||||
@@ -29,7 +26,7 @@ constexpr oaknut::XReg Xpagetable{24};
|
||||
constexpr oaknut::XReg Xscratch0{16}, Xscratch1{17}, Xscratch2{30};
|
||||
constexpr oaknut::WReg Wscratch0{16}, Wscratch1{17}, Wscratch2{30};
|
||||
|
||||
template<size_t bitsize>
|
||||
template<std::size_t bitsize>
|
||||
constexpr auto Rscratch0() {
|
||||
if constexpr (bitsize == 32) {
|
||||
return Wscratch0;
|
||||
@@ -40,7 +37,7 @@ constexpr auto Rscratch0() {
|
||||
}
|
||||
}
|
||||
|
||||
template<size_t bitsize>
|
||||
template<std::size_t bitsize>
|
||||
constexpr auto Rscratch1() {
|
||||
if constexpr (bitsize == 32) {
|
||||
return Wscratch1;
|
||||
@@ -70,7 +67,7 @@ constexpr RegisterList ToRegList(oaknut::Reg reg) {
|
||||
constexpr RegisterList ABI_CALLEE_SAVE = 0x0000ff00'7ff80000;
|
||||
constexpr RegisterList ABI_CALLER_SAVE = 0xffffffff'4000ffff;
|
||||
|
||||
void ABI_PushRegisters(oaknut::CodeGenerator& code, RegisterList rl, size_t stack_space);
|
||||
void ABI_PopRegisters(oaknut::CodeGenerator& code, RegisterList rl, size_t stack_space);
|
||||
void ABI_PushRegisters(oaknut::CodeGenerator& code, RegisterList rl, std::size_t stack_space);
|
||||
void ABI_PopRegisters(oaknut::CodeGenerator& code, RegisterList rl, std::size_t stack_space);
|
||||
|
||||
} // namespace Dynarmic::Backend::Arm64
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user