diff --git a/.gitignore b/.gitignore index 9e11b5d75..37591363a 100644 --- a/.gitignore +++ b/.gitignore @@ -33,3 +33,7 @@ Thumbs.db # Python files *.pyc + +# Flatpak generated files +.flatpak-builder/ +repo/ diff --git a/.travis.yml b/.travis.yml index 54b7a9091..402c9538e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -61,6 +61,18 @@ matrix: script: "./.travis/linux-mingw/build.sh" after_success: "./.travis/linux-mingw/upload.sh" cache: ccache + - if: repo =~ ^.*\/(citra-canary|citra-nightly)$ AND tag IS present + git: + depth: false + os: linux + env: NAME="flatpak build" + sudo: required + dist: trusty + services: docker + cache: ccache + install: "./.travis/linux-flatpak/deps.sh" + script: "./.travis/linux-flatpak/build.sh" + after_script: "./.travis/linux-flatpak/finish.sh" deploy: provider: releases diff --git a/.travis/linux-flatpak/build.sh b/.travis/linux-flatpak/build.sh new file mode 100755 index 000000000..91fba01aa --- /dev/null +++ b/.travis/linux-flatpak/build.sh @@ -0,0 +1,4 @@ +#!/bin/bash -ex +mkdir -p "$HOME/.ccache" +# Configure docker and call the script that generates application data and build scripts +docker run --env-file .travis/common/travis-ci.env --env-file .travis/linux-flatpak/travis-ci-flatpak.env -v $(pwd):/citra -v "$HOME/.ccache":/root/.ccache -v "$HOME/.ssh":/root/.ssh --privileged citraemu/build-environments:linux-flatpak /bin/bash -ex /citra/.travis/linux-flatpak/generate-data.sh diff --git a/.travis/linux-flatpak/deps.sh b/.travis/linux-flatpak/deps.sh new file mode 100755 index 000000000..b8fd4974c --- /dev/null +++ b/.travis/linux-flatpak/deps.sh @@ -0,0 +1,4 @@ +#!/bin/sh -ex + +# Download the docker image that contains flatpak build dependencies +docker pull citraemu/build-environments:linux-flatpak diff --git a/.travis/linux-flatpak/docker.sh b/.travis/linux-flatpak/docker.sh new file mode 100755 index 000000000..31e2bb777 --- /dev/null +++ b/.travis/linux-flatpak/docker.sh @@ -0,0 +1,35 @@ +#!/bin/bash -ex + +# Converts "citra-emu/citra-nightly" to "citra-nightly" +REPO_NAME=$(echo $TRAVIS_REPO_SLUG | cut -d'/' -f 2) +CITRA_SRC_DIR="/citra" +BUILD_DIR="$CITRA_SRC_DIR/build" +REPO_DIR="$CITRA_SRC_DIR/repo" +STATE_DIR="$CITRA_SRC_DIR/.flatpak-builder" +KEYS_ARCHIVE="/tmp/keys.tar" +SSH_DIR="/upload" +SSH_KEY="/tmp/ssh.key" +GPG_KEY="/tmp/gpg.key" + +# Extract keys +openssl aes-256-cbc -K $FLATPAK_ENC_K -iv $FLATPAK_ENC_IV -in "$CITRA_SRC_DIR/keys.tar.enc" -out "$KEYS_ARCHIVE" -d +tar -C /tmp -xvf $KEYS_ARCHIVE + +# Configure SSH keys +eval "$(ssh-agent -s)" +chmod -R 600 "$HOME/.ssh" +chown -R root "$HOME/.ssh" +chmod 600 "$SSH_KEY" +ssh-add "$SSH_KEY" +echo "[$FLATPAK_SSH_HOSTNAME]:$FLATPAK_SSH_PORT,[$(dig +short $FLATPAK_SSH_HOSTNAME)]:$FLATPAK_SSH_PORT $FLATPAK_SSH_PUBLIC_KEY" > ~/.ssh/known_hosts + +# Configure GPG keys +gpg2 --import "$GPG_KEY" + +# Mount our flatpak repository +mkdir -p "$REPO_DIR" +sshfs "$FLATPAK_SSH_USER@$FLATPAK_SSH_HOSTNAME:$SSH_DIR" "$REPO_DIR" -C -p "$FLATPAK_SSH_PORT" -o IdentityFile="$SSH_KEY" + +# Build the citra flatpak +flatpak-builder -v --jobs=4 --ccache --force-clean --state-dir="$STATE_DIR" --gpg-sign="$FLATPAK_GPG_PUBLIC_KEY" --repo="$REPO_DIR" "$BUILD_DIR" "/tmp/org.citra.$REPO_NAME.json" +flatpak build-update-repo "$REPO_DIR" -v --generate-static-deltas --gpg-sign="$FLATPAK_GPG_PUBLIC_KEY" diff --git a/.travis/linux-flatpak/finish.sh b/.travis/linux-flatpak/finish.sh new file mode 100755 index 000000000..c9f0c0731 --- /dev/null +++ b/.travis/linux-flatpak/finish.sh @@ -0,0 +1,9 @@ +#!/bin/bash -ex + +CITRA_SRC_DIR="/citra" +REPO_DIR="$CITRA_SRC_DIR/repo" + +# When the script finishes, unmount the repository and delete sensitive files, +# regardless of whether the build passes or fails +umount "$REPO_DIR" +rm -rf "$REPO_DIR" "/tmp/*" diff --git a/.travis/linux-flatpak/generate-data.sh b/.travis/linux-flatpak/generate-data.sh new file mode 100644 index 000000000..d8ca90aab --- /dev/null +++ b/.travis/linux-flatpak/generate-data.sh @@ -0,0 +1,142 @@ +#!/bin/bash -ex +# This script generates the appdata.xml and org.citra.$REPO_NAME.json files +# needed to define application metadata and build citra depending on what version +# of citra we're building (nightly or canary) + +# Converts "citra-emu/citra-nightly" to "citra-nightly" +REPO_NAME=$(echo $TRAVIS_REPO_SLUG | cut -d'/' -f 2) +# Converts "citra-nightly" to "Citra Nightly" +REPO_NAME_FRIENDLY=$(echo $REPO_NAME | sed -e 's/-/ /g' -e 's/\b\(.\)/\u\1/g') + +# Generate the correct appdata.xml for the version of Citra we're building +cat > /tmp/appdata.xml < + + org.citra.$REPO_NAME.desktop + $REPO_NAME_FRIENDLY + Nintendo 3DS emulator + CC0-1.0 + GPL-2.0 + +

Citra is an experimental open-source Nintendo 3DS emulator/debugger written in C++. It is written with portability in mind, with builds actively maintained for Windows, Linux and macOS.

+

Citra emulates a subset of 3DS hardware and therefore is useful for running/debugging homebrew applications, and it is also able to run many commercial games! Some of these do not run at a playable state, but we are working every day to advance the project forward. (Playable here means compatibility of at least "Okay" on our game compatibility list.)

+
+ https://citra-emu.org/ + https://citra-emu.org/donate/ + https://github.com/citra-emu/citra/issues + https://citra-emu.org/wiki/faq/ + https://citra-emu.org/wiki/home/ + https://raw.githubusercontent.com/citra-emu/citra-web/master/site/static/images/screenshots/01-Super%20Mario%203D%20Land.jpg + https://raw.githubusercontent.com/citra-emu/citra-web/master/site/static/images/screenshots/02-Mario%20Kart%207.jpg + https://raw.githubusercontent.com/citra-emu/citra-web/master/site/static/images/screenshots/28-The%20Legend%20of%20Zelda%20Ocarina%20of%20Time%203D.jpg + https://raw.githubusercontent.com/citra-emu/citra-web/master/site/static/images/screenshots/35-Pok%C3%A9mon%20ORAS.png + + Games + Emulator + +
+EOF + +# Generate the citra flatpak manifest, appending certain variables depending on +# whether we're building nightly or canary. +cat > /tmp/org.citra.$REPO_NAME.json <> /app/share/applications/citra.desktop", + "install -Dm644 ../dist/citra.svg /app/share/icons/hicolor/scalable/apps/citra.svg", + "install -Dm644 ../dist/icon.png /app/share/icons/hicolor/512x512/apps/citra.png", + "mv /app/share/mime/packages/citra.xml /app/share/mime/packages/org.citra.$REPO_NAME.xml", + "sed 's/citra/org.citra.citra-nightly/g' -i /app/share/mime/packages/org.citra.$REPO_NAME.xml", + "install -m644 --target-directory=/app/lib /usr/lib/sdk/gcc7/lib/libstdc++.so*" + ], + "sources": [ + { + "type": "git", + "url": "https://github.com/citra-emu/$REPO_NAME.git", + "branch": "$TRAVIS_BRANCH", + "disable-shallow-clone": true + }, + { + "type": "file", + "path": "/tmp/appdata.xml" + } + ] + } + ] +} +EOF + +# Call the script to build citra +/bin/bash -ex /citra/.travis/linux-flatpak/docker.sh diff --git a/.travis/linux-flatpak/travis-ci-flatpak.env b/.travis/linux-flatpak/travis-ci-flatpak.env new file mode 100644 index 000000000..0823d91c8 --- /dev/null +++ b/.travis/linux-flatpak/travis-ci-flatpak.env @@ -0,0 +1,9 @@ +# Flatpak specific environment variables +FLATPAK_ENC_IV +FLATPAK_ENC_K +FLATPAK_GPG_PUBLIC_KEY +FLATPAK_SSH_HOSTNAME +FLATPAK_SSH_LOCATION +FLATPAK_SSH_PORT +FLATPAK_SSH_PUBLIC_KEY +FLATPAK_SSH_USER diff --git a/CMakeLists.txt b/CMakeLists.txt index b91e996f9..094904cb6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,21 +24,21 @@ option(USE_DISCORD_PRESENCE "Enables Discord Rich Presence" OFF) option(ENABLE_SCRIPTING "Enables scripting support" OFF) -if(NOT EXISTS ${CMAKE_SOURCE_DIR}/.git/hooks/pre-commit) +if(NOT EXISTS ${PROJECT_SOURCE_DIR}/.git/hooks/pre-commit) message(STATUS "Copying pre-commit hook") file(COPY hooks/pre-commit - DESTINATION ${CMAKE_SOURCE_DIR}/.git/hooks) + DESTINATION ${PROJECT_SOURCE_DIR}/.git/hooks) endif() # Sanity check : Check that all submodules are present # ======================================================================= function(check_submodules_present) - file(READ "${CMAKE_SOURCE_DIR}/.gitmodules" gitmodules) + file(READ "${PROJECT_SOURCE_DIR}/.gitmodules" gitmodules) string(REGEX MATCHALL "path *= *[^ \t\r\n]*" gitmodules ${gitmodules}) foreach(module ${gitmodules}) string(REGEX REPLACE "path *= *" "" module ${module}) - if (NOT EXISTS "${CMAKE_SOURCE_DIR}/${module}/.git") + if (NOT EXISTS "${PROJECT_SOURCE_DIR}/${module}/.git") message(SEND_ERROR "Git submodule ${module} not found." "Please run: git submodule update --init --recursive") endif() @@ -46,17 +46,17 @@ function(check_submodules_present) endfunction() check_submodules_present() -configure_file(${CMAKE_SOURCE_DIR}/dist/compatibility_list/compatibility_list.qrc - ${CMAKE_BINARY_DIR}/dist/compatibility_list/compatibility_list.qrc +configure_file(${PROJECT_SOURCE_DIR}/dist/compatibility_list/compatibility_list.qrc + ${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.qrc COPYONLY) -if (ENABLE_COMPATIBILITY_LIST_DOWNLOAD AND NOT EXISTS ${CMAKE_BINARY_DIR}/dist/compatibility_list/compatibility_list.json) +if (ENABLE_COMPATIBILITY_LIST_DOWNLOAD AND NOT EXISTS ${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.json) message(STATUS "Downloading compatibility list for citra...") file(DOWNLOAD https://api.citra-emu.org/gamedb/ - "${CMAKE_BINARY_DIR}/dist/compatibility_list/compatibility_list.json" SHOW_PROGRESS) + "${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.json" SHOW_PROGRESS) endif() -if (NOT EXISTS ${CMAKE_BINARY_DIR}/dist/compatibility_list/compatibility_list.json) - file(WRITE ${CMAKE_BINARY_DIR}/dist/compatibility_list/compatibility_list.json "") +if (NOT EXISTS ${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.json) + file(WRITE ${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.json "") endif() # Detect current compilation architecture and create standard definitions @@ -107,7 +107,7 @@ set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) # set up output paths for executable binaries -set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin) if (NOT MSVC) @@ -170,7 +170,7 @@ endif() # On modern Unixes, this is typically already the case. The lone exception is # glibc, which may default to 32 bits. glibc allows this to be configured # by setting _FILE_OFFSET_BITS. -if(CMAKE_SYSTEM_NAME STREQUAL "Linux") +if(CMAKE_SYSTEM_NAME STREQUAL "Linux" OR MINGW) add_definitions(-D_FILE_OFFSET_BITS=64) endif() @@ -179,9 +179,6 @@ add_definitions(-DSINGLETHREADED) set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS $<$:_DEBUG> $<$>:NDEBUG>) -math(EXPR EMU_ARCH_BITS ${CMAKE_SIZEOF_VOID_P}*8) -add_definitions(-DEMU_ARCH_BITS=${EMU_ARCH_BITS}) - # System imported libraries # ====================== @@ -189,7 +186,7 @@ find_package(Boost 1.66.0 QUIET) if (NOT Boost_FOUND) message(STATUS "Boost 1.66.0 or newer not found, falling back to externals") - set(BOOST_ROOT "${CMAKE_SOURCE_DIR}/externals/boost") + set(BOOST_ROOT "${PROJECT_SOURCE_DIR}/externals/boost") set(Boost_NO_SYSTEM_PATHS OFF) find_package(Boost QUIET REQUIRED) endif() @@ -290,12 +287,12 @@ set(CLANG_FORMAT_POSTFIX "-6.0") find_program(CLANG_FORMAT NAMES clang-format${CLANG_FORMAT_POSTFIX} clang-format - PATHS ${CMAKE_BINARY_DIR}/externals) + PATHS ${PROJECT_BINARY_DIR}/externals) # if find_program doesn't find it, try to download from externals if (NOT CLANG_FORMAT) if (WIN32) message(STATUS "Clang format not found! Downloading...") - set(CLANG_FORMAT "${CMAKE_BINARY_DIR}/externals/clang-format${CLANG_FORMAT_POSTFIX}.exe") + set(CLANG_FORMAT "${PROJECT_BINARY_DIR}/externals/clang-format${CLANG_FORMAT_POSTFIX}.exe") file(DOWNLOAD https://github.com/yuzu-emu/ext-windows-bin/raw/master/clang-format${CLANG_FORMAT_POSTFIX}.exe "${CLANG_FORMAT}" SHOW_PROGRESS @@ -311,7 +308,7 @@ if (NOT CLANG_FORMAT) endif() if (CLANG_FORMAT) - set(SRCS ${CMAKE_SOURCE_DIR}/src) + set(SRCS ${PROJECT_SOURCE_DIR}/src) set(CCOMMENT "Running clang format against all the .h and .cpp files in src/") if (WIN32) add_custom_target(clang-format @@ -392,22 +389,22 @@ endif() # http://standards.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html # http://standards.freedesktop.org/shared-mime-info-spec/shared-mime-info-spec-latest.html if(ENABLE_QT AND UNIX AND NOT APPLE) - install(FILES "${CMAKE_SOURCE_DIR}/dist/citra.desktop" + install(FILES "${PROJECT_SOURCE_DIR}/dist/citra.desktop" DESTINATION "${CMAKE_INSTALL_PREFIX}/share/applications") - install(FILES "${CMAKE_SOURCE_DIR}/dist/citra.svg" + install(FILES "${PROJECT_SOURCE_DIR}/dist/citra.svg" DESTINATION "${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/scalable/apps") - install(FILES "${CMAKE_SOURCE_DIR}/dist/citra.xml" + install(FILES "${PROJECT_SOURCE_DIR}/dist/citra.xml" DESTINATION "${CMAKE_INSTALL_PREFIX}/share/mime/packages") endif() if(UNIX) if(ENABLE_SDL2) - install(FILES "${CMAKE_SOURCE_DIR}/dist/citra.6" + install(FILES "${PROJECT_SOURCE_DIR}/dist/citra.6" DESTINATION "${CMAKE_INSTALL_PREFIX}/share/man/man6") endif() if (ENABLE_QT) - install(FILES "${CMAKE_SOURCE_DIR}/dist/citra-qt.6" + install(FILES "${PROJECT_SOURCE_DIR}/dist/citra-qt.6" DESTINATION "${CMAKE_INSTALL_PREFIX}/share/man/man6") endif() endif() diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f965ea7d2..3e98984c4 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,137 +1 @@ -# Reporting Issues - -**The issue tracker is not a support forum.** Unless you can provide precise *technical information* regarding an issue, you *should not post in it*. If you need support, first read the [FAQ](https://github.com/citra-emu/citra/wiki/FAQ) and then either visit our IRC or [Discord](https://citra-emu.org/discord/) channel, [our forum](https://community.citra-emu.org) or ask in a general emulation forum such as [/r/emulation](https://www.reddit.com/r/emulation/). If you post support questions, generic messages to the developers or vague reports without technical details, they will be closed and locked. - -If you believe you have a valid issue report, please post text or a screenshot from the log (the console window that opens alongside Citra) and build version (hex string visible in the titlebar and zip filename), as well as your hardware and software information if applicable. - -# Contributing - -Citra is a brand new project, so we have a great opportunity to keep things clean and well organized early on. As such, coding style is very important when making commits. We run clang-format on our CI to check the code. Please use it to format your code when contributing. However, it doesn't cover all the rules below. Some of them aren't very strict rules since we want to be flexible and we understand that under certain circumstances some of them can be counterproductive. Just try to follow as many of them as possible. - -# Using clang format (version 6.0) -When generating the native build script for your toolset, cmake will try to find the correct version of clang format (or will download it on windows). Before running cmake, please install clang format version 6.0 for your platform as follows: - -* Windows: do nothing; cmake will download a pre built binary for MSVC and MINGW. MSVC users can additionally install a clang format Visual Studio extension to add features like format on save. -* OSX: run `brew install clang-format`. -* Linux: use your package manager to get an appropriate binary. - -If clang format is found, then cmake will add a custom build target that can be run at any time to run clang format against *all* source files and update the formatting in them. This should be used before making a pull request so that the reviewers can spend more time reviewing the code instead of having to worry about minor style violations. On MSVC, you can run clang format by building the clang-format project in the solution. On OSX, you can either use the Makefile target `make clang-format` or by building the clang-format target in XCode. For Makefile builds, you can use the clang-format target with `make clang-format` - -### General Rules -* A lot of code was taken from other projects (e.g. Dolphin, PPSSPP, Gekko, SkyEye). In general, when editing other people's code, follow the style of the module you're in (or better yet, fix the style if it drastically differs from our guide). -* Line width is typically 100 characters. Please do not use 80-characters. -* Don't ever introduce new external dependencies into Core -* Don't use any platform specific code in Core -* Use namespaces often -* Avoid the use of C-style casts and instead prefer C++-style `static_cast` and `reinterpret_cast`. Try to avoid using `dynamic_cast`. Never use `const_cast`. - -### Naming Rules -* Functions: `PascalCase` -* Variables: `lower_case_underscored`. Prefix with `g_` if global. -* Classes: `PascalCase` -* Files and Directories: `lower_case_underscored` -* Namespaces: `PascalCase`, `_` may also be used for clarity (e.g. `ARM_InitCore`) - -### Indentation/Whitespace Style -Follow the indentation/whitespace style shown below. Do not use tabs, use 4-spaces instead. - -### Comments -* For regular comments, use C++ style (`//`) comments, even for multi-line ones. -* For doc-comments (Doxygen comments), use `/// ` if it's a single line, else use the `/**` `*/` style featured in the example. Start the text on the second line, not the first containing `/**`. -* For items that are both defined and declared in two separate files, put the doc-comment only next to the associated declaration. (In a header file, usually.) Otherwise, put it next to the implementation. Never duplicate doc-comments in both places. - -```cpp -// Includes should be sorted lexicographically -// STD includes first -#include -#include - -// then, library includes -#include - -// finally, citra includes -#include "common/math_util.h" -#include "common/vector_math.h" - -// each major module is separated -#include "video_core/pica.h" -#include "video_core/video_core.h" - -namespace Example { - -// Namespace contents are not indented - -// Declare globals at the top -int g_foo{}; // {} can be used to initialize types as 0, false, or nullptr -char* g_some_pointer{}; // Pointer * and reference & stick to the type name, and make sure to initialize as nullptr! - -/// A colorful enum. -enum SomeEnum { - ColorRed, ///< The color of fire. - ColorGreen, ///< The color of grass. - ColorBlue, ///< Not actually the color of water. -}; - -/** - * Very important struct that does a lot of stuff. - * Note that the asterisks are indented by one space to align to the first line. - */ -struct Position { - int x{}, y{}; // Always intitialize member variables! -}; - -// Use "typename" rather than "class" here -template -void FooBar() { - const std::string some_string{ "prefer uniform initialization" }; - - int some_array[]{ - 5, - 25, - 7, - 42, - }; - - if (note == the_space_after_the_if) { - CallAfunction(); - } else { - // Use a space after the // when commenting - } - - // Place a single space after the for loop semicolons, prefer pre-increment - for (int i{}; i != 25; ++i) { - // This is how we write loops - } - - DoStuff(this, function, call, takes, up, multiple, - lines, like, this); - - if (this || condition_takes_up_multiple && - lines && like && this || everything || - alright || then) { - - // Leave a blank space before the if block body if the condition was continued across - // several lines. - } - - switch (var) { - // No indentation for case label - case 1: { - int case_var{ var + 3 }; - DoSomething(case_var); - break; - } - case 3: - DoSomething(var); - return; - - default: - // Yes, even break for the last case - break; - } - - std::vector you_can_declare, a_few, variables, like_this; -} - -} -``` +**The Contributor's Guide has moved to [the wiki](https://github.com/citra-emu/citra/wiki/Contributing).** \ No newline at end of file diff --git a/dist/languages/da_DK.ts b/dist/languages/da_DK.ts index ccd2e9d4a..5a35a8301 100644 --- a/dist/languages/da_DK.ts +++ b/dist/languages/da_DK.ts @@ -70,45 +70,50 @@ p, li { white-space: pre-wrap; } BreakPointModel - + Pica command loaded Pica-kommando indlæst - + Pica command processed Pica-kommando behandlet - + Incoming primitive batch Kommende primitivt parti - + Finished primitive batch Færdiggjort primitivt parti - + Vertex shader invocation Aktivering af vertex-shader - + Incoming display transfer Kommende displayoverførsel - + GSP command processed GSP-kommando behandlet - + Buffers swapped Buffers byttet + + + Unknown debug context event + + CalibrationConfigurationDialog @@ -171,13 +176,13 @@ p, li { white-space: pre-wrap; } Spil - - + + Block Player - + When you block a player, you will no longer receive chat messages from them.<br><br>Are you sure you would like to block %1? @@ -198,17 +203,17 @@ p, li { white-space: pre-wrap; } ClientRoomWindow - + Connected Forbundet - + Disconnected Afbrudt - + %1 (%2/%3 members) - connected %1 (%2/%3 medlemmer) - forbundet @@ -301,6 +306,26 @@ p, li { white-space: pre-wrap; } Thank you for your submission! Tak for din indsendelse! + + + Submitting + + + + + Communication error + + + + + An error occured while sending the Testcase + + + + + Next + + ConfigureAudio @@ -340,9 +365,9 @@ p, li { white-space: pre-wrap; } - - - %1 % + + %1% + Volume percentage (e.g. 50%) @@ -606,50 +631,78 @@ p, li { white-space: pre-wrap; } ConfigureDialog - + Citra Configuration Konfiguration af Citra - + + + General Generelt - + + + System System - + + + Input Input - + + + Graphics Grafik - + + + Audio Lyd - + + + Camera Kamera - + + + Debug Fejlfinding - + + + Web Web + + + + + UI + + + + + Controls + + ConfigureGeneral @@ -669,64 +722,54 @@ p, li { white-space: pre-wrap; } Bekræft afslutning når emulatoren kører - - Interface language - Sprog i grænsefladen - - - + Updates Opdateringer - + Check for updates on start Tjek efter opdateringer ved opstart - + Silently auto update after closing Opdater automatisk i baggrunden efter lukning - + Emulation Emulering - + Region: Region: - + Auto-select Vælg automatisk - - Theme - Tema: - - - - Theme: - Tema: - - - + Hotkeys Genvejstaster - - <System> - <System> + + Reset All Settings + - - English - Engelsk + + Citra + + + + + Are you sure you want to <b>reset your settings</b> and close Citra? + @@ -1026,7 +1069,7 @@ p, li { white-space: pre-wrap; } - + Set Analog Stick Indstil analogstik @@ -1067,11 +1110,44 @@ p, li { white-space: pre-wrap; } + Clear All + + + + Restore Defaults Gendan standarder - + + + Clear + + + + + + [not set] + + + + + + Restore Default + + + + + Information + + + + + After pressing OK, first move your joystick horizontally, and then vertically. + + + + [press key] [tryk på tast] @@ -1440,17 +1516,27 @@ p, li { white-space: pre-wrap; } - + + yyyy-MM-ddTHH:mm:ss + + + + + Play Coins: + + + + Console ID: Konsol-id: - + Regenerate Generer nyt - + System settings are available only when game is not running. Systemindstillinger kan kun ændres når et spil ikke kører. @@ -2120,22 +2206,120 @@ p, li { white-space: pre-wrap; } - - + + Console ID: 0x%1 Konsol-id: 0x%1 - + This will replace your current virtual 3DS with a new one. Your current virtual 3DS will not be recoverable. This might have unexpected effects in games. This might fail, if you use an outdated config savegame. Continue? Dette erstatter din nuværende virtuelle 3DS med en ny. Din nuværende virtuelle 3DS vil ikke kunne genskabes. Dette kan have uventede effekter i spil. Dette fejler måske hvis du bruger et forældet config-lager. Fortsæt? - + Warning Advarsel + + ConfigureUi + + + Form + + + + + General + + + + + Interface language: + + + + + Theme: + + + + + Game List + + + + + Icon Size: + + + + + + None + + + + + Small (24x24) + + + + + Large (48x48) + + + + + Row 1 Text: + + + + + + File Name + + + + + + Full Path + + + + + + Title Name + + + + + + Title ID + + + + + Row 2 Text: + + + + + Hide Titles without Icon + + + + + <System> + + + + + English + + + ConfigureWeb @@ -2155,7 +2339,7 @@ p, li { white-space: pre-wrap; } - + Verify Bekræft @@ -2215,48 +2399,48 @@ p, li { white-space: pre-wrap; } - + <a href='https://citra-emu.org/entry/telemetry-and-why-thats-a-good-thing/'><span style="text-decoration: underline; color:#039be5;">Learn more</span></a> <a href='https://citra-emu.org/entry/telemetry-and-why-thats-a-good-thing/'><span style="text-decoration: underline; color:#039be5;">Lær mere</span></a> - - <a href='https://services.citra-emu.org/'><span style="text-decoration: underline; color:#039be5;">Sign up</span></a> - <a href='https://services.citra-emu.org/'><span style="text-decoration: underline; color:#039be5;">Opret konto</span></a> + + <a href='https://profile.citra-emu.org/'><span style="text-decoration: underline; color:#039be5;">Sign up</span></a> + - + <a href='https://citra-emu.org/wiki/citra-web-service/'><span style="text-decoration: underline; color:#039be5;">What is my token?</span></a> <a href='https://citra-emu.org/wiki/citra-web-service/'><span style="text-decoration: underline; color:#039be5;">Hvad er min token?</span></a> - - + + Telemetry ID: 0x%1 Telemetri-id: 0x%1 - + Username and token not verified Brugernavn og token er ikke bekræftet - + Username and token were not verified. The changes to your username and/or token have not been saved. Dit brugernavn og token blev ikke bekræftet. Ændringerne til dit brugernavn eller token er bliver blevet gemt. - + Verifying Bekræfter - + Verification failed Bekræftelse mislykkedes - + Verification failed. Check that you have entered your username and token correctly, and that your internet connection is working. Bekræftelse mislykkedes. Kontroller, at du har indtastet dit brugernavn og token korrekt og at din internetforbindelse virker. @@ -2317,12 +2501,12 @@ p, li { white-space: pre-wrap; } DirectConnectWindow - + Connecting Forbinder - + Connect Forbind @@ -2330,413 +2514,445 @@ p, li { white-space: pre-wrap; } GMainWindow - + <a href='https://citra-emu.org/entry/telemetry-and-why-thats-a-good-thing/'>Anonymous data is collected</a> to help improve Citra. <br/><br/>Would you like to share your usage data with us? - + Telemetry - - + + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a 3DS. Nuværende emuleringshastighed. Værdier højere eller lavere end 100% indikerer at emuleringen kører hurtigere eller langsommere end en 3DS. - - + + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. Hvor mange billeder pr sekund spillet vises med. Dette vil variere fra spil til spil og scene til scene. - - + + Time taken to emulate a 3DS frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. Tid det tog at emulere en 3DS-skærmbillede, hastighedsbegrænsning og v-sync er tille talt med. For emulering med fuld hastighed skal dette højest være 16,67ms. - + Clear Recent Files - + F9 F9 - + F10 F10 - + CTRL+F Ctrl+F - + Update Available - + An update is available. Would you like to install it now? - + No Update Found - + No update is found. - - + + OpenGL 3.3 Unsupported + + + + + Your GPU may not support OpenGL 3.3, or you do not have the latest graphics driver. + + + + + Invalid ROM Format - - + + Your ROM format is not supported.<br/>Please follow the guides to redump your <a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>game cartridges</a> or <a href='https://citra-emu.org/wiki/dumping-installed-titles/'>installed titles</a>. - + ROM Corrupted - + Your ROM is corrupted. <br/>Please follow the guides to redump your <a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>game cartridges</a> or <a href='https://citra-emu.org/wiki/dumping-installed-titles/'>installed titles</a>. - + ROM Encrypted - + Your ROM is encrypted. <br/>Please follow the guides to redump your <a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>game cartridges</a> or <a href='https://citra-emu.org/wiki/dumping-installed-titles/'>installed titles</a>. - - + + Video Core Error - + An error has occured. Please <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>see the log</a> for more details. Ensure that you have the latest graphics drivers for your GPU. - + You are running default Windows drivers for your GPU. You need to install the proper drivers for your graphics card from the manufacturer's website. - + Error while loading ROM! Kunne ikke indlæse ROM! - + An unknown error occured. Please see the log for more details. En ukendt fejl opstod. Kig i logfilen for flere detaljer. - + Start Start - + Error Opening %1 Folder Fejl ved åbning af %1-mappen - - + + Folder does not exist! Mappen findes ikke! - + Error Opening %1 - + Select Directory Vælg mappe - - 3DS Executable - 3DS-programfil + + 3DS Executable (%1);;All Files (*.*) + %1 is an identifier for the 3DS executable file extensions. + - - - All Files (*.*) - Alle filer (*.*) - - - + Load File Indlæs fil - + Load Files Indlæs filer - + 3DS Installation File (*.CIA*) 3DS-installationsfil (*.CIA) - + + All Files (*.*) + Alle filer (*.*) + + + %1 has been installed successfully. %1 blev succesfuldt installeret. - + Unable to open File Kunne ikke åbne filen - + Could not open %1 Kunne ikke åbne %1 - + Installation aborted Installation afbrudt - + The installation of %1 was aborted. Please see the log for more details Installationen af %1 blev afbrudt. Se logfilen for flere detaljer. - + Invalid File Ugyldig fil - + %1 is not a valid CIA %1 er ikke en gyldig CIA - + Encrypted File Krypteret fil - + %1 must be decrypted before being used with Citra. A real 3DS is required. %1 skal dekrypteres før brug i Citra. En rigtig 3DS skal bruges. - + File not found Filen blev ikke fundet - + File "%1" not found Filen "%1" blev ikke fundet - - - + + + Continue Fortsæt - + Missing Citra Account Manglende Citra-konto - + You must link your Citra account to submit test cases.<br/>Go to Emulation &gt; Configure... &gt; Web to do so. - - - + + Amiibo File (%1);; All Files (*.*) + + + + + Load Amiibo + + + + + + + Record Movie - - + + To keep consistency with the RNG, it is recommended to record the movie from game start.<br>Are you sure you still want to record movies now? + + + + + Citra TAS Movie (*.ctm) - + Recording will start once you boot a game. - + The movie file you are trying to load was created on a different revision of Citra.<br/>Citra has had some changes during the time, and the playback may desync or not work as expected.<br/><br/>Are you sure you still want to load the movie file? - + The movie file you are trying to load was recorded with a different game.<br/>The playback may not work as expected, and it may cause unexpected results.<br/><br/>Are you sure you still want to load the movie file? - - + + The movie file you are trying to load is invalid.<br/>Either the file is corrupted, or Citra has had made some major changes to the Movie module.<br/>Please choose a different movie file and try again. - + Revision Dismatch - + Game Dismatch - - + + Invalid Movie File - + + Play Movie - + + To keep consistency with the RNG, it is recommended to play the movie from game start.<br>Are you sure you still want to play movies now? + + + + Game Not Found - + The movie you are trying to play is from a game that is not in the game list. If you own the game, please add the game folder to the game list and try to play the movie again. - + Movie recording cancelled. - + Movie Saved - + The movie is successfully saved. - + Speed: %1% / %2% Hastighed: %1%/%2% - + Speed: %1% Hastighed: %1% - + Game: %1 FPS Spil: %1FPS - + Frame: %1 ms Billede: %1ms - + %1 is missing. Please <a href='https://citra-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-3ds-console/'>dump your system archives</a>.<br/>Continuing emulation may result in crashes and bugs. - + System Archive Not Found Systemarkiver blev ikke fundet - + Fatal Error Alvorlig fejl - + A fatal error occured. <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>Check the log</a> for details.<br/>Continuing emulation may result in crashes and bugs. - + Abort - - + + Citra Citra - + Would you like to exit now? - + The game is still running. Would you like to stop emulation? - + Playback Completed - + Movie playback completed. - + Citra %1 - + Citra %1| %2 @@ -2799,37 +3015,67 @@ p, li { white-space: pre-wrap; } GameList - + + Name + + + + + Compatibility + + + + + Region + + + + + File type + + + + + Size + + + + Open Save Data Location Åbn mappe til spildata - + + Open Extra Data Location + + + + Open Application Location Åbn programmappe - + Open Update Data Location Åbn mappe til opdateringsdata - + Navigate to GameDB entry Naviger til GameDB-side - + Scan Subfolders - + Remove Game Directory - + Open Directory Location @@ -2837,77 +3083,77 @@ p, li { white-space: pre-wrap; } GameListItemCompat - + Perfect - + Game functions flawless with no audio or graphical glitches, all tested functionality works as intended without any workarounds needed. - + Great - + Game functions with minor graphical or audio glitches and is playable from start to finish. May require some workarounds. - + Okay - + Game functions with major graphical or audio glitches, but game is playable from start to finish with workarounds. - + Bad - + Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches even with workarounds. - + Intro/Menu - + Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start Screen. - + Won't Boot - + The game crashes when attempting to startup. - + Not Tested - + The game has not yet been tested. @@ -2915,7 +3161,7 @@ Screen. GameListPlaceholder - + Double-click to add a new folder to the game list @@ -2923,27 +3169,27 @@ Screen. GameListSearchField - + of - + result - + results - + Filter: - + Enter pattern to filter @@ -2951,23 +3197,23 @@ Screen. GraphicsBreakPointsWidget - + Pica Breakpoints Pica-breakpoints - - + + Emulation running Emulering kører - + Resume Fortsæt - + Emulation halted at breakpoint Emuleringen stoppet ved breakpoint @@ -3384,42 +3630,42 @@ Screen. Genopfrisk lobby - + Password Required to Join Adgangskode krævet for deltagelse - + Password: Adgangskode: - + Room Name Rumnavn - + Preferred Game Foretrukket spil - + Host Vært - + Players Spillere - + Refreshing Genopfrisker - + Refresh List Genopfrisk liste @@ -3442,220 +3688,245 @@ Screen. Seneste filer - + + Amiibo + + + + &Emulation &Emulering - + &View &Vis - + Debugging Fejlfinding - + Screen Layout Skærmlayout - + Movie - + Multiplayer Multiplayer - + &Help &Hjælp - + Load File... Indlæs fil… - + Install CIA... Installer CIA… - + Load Symbol Map... Indlæs symbolliste… - + E&xit A&fslut - + &Start &Start - + &Pause &Pause - + &Stop &Stop - + FAQ FAQ - + About Citra Om Citra - + Single Window Mode Tilstand med enkelt vindue - + Configure... Konfigurer… - + Display Dock Widget Headers Vis titler på dokbare widgets - + Show Filter Bar Vis filterlinje - + Show Status Bar Vis statuslinje - + Select Game Directory... Vælg spilmappe… - + Selects a folder to display in the game list Vælger en mappe, der bliver vist i spillisten - + Create Pica Surface Viewer Opret Pica-overfladeviser - + Record Movie - + Play Movie - + Stop Recording / Playback - + + Enable Frame Advancing + + + + + Advance Frame + + + + Browse Public Game Lobby Gennemse offentlig spillobby - + Create Room Opret rum - + Leave Room Forlad rum - + Direct Connect to Room Forbind direkte til rum - + Show Current Room Vis nuværende rum - + Fullscreen Fuld skærm - + Modify Citra Install Modificer Citra-installation - + Opens the maintenance tool to modify your Citra installation Åben vedligeholdelsesværktøjet for at modificere din installation af Citra - + Default Standard - + Single Screen Enkelt skærm - + Large Screen Stor skærm - + Side by Side Side om side - + Swap Screens Byt om på skærme - + Check for Updates Tjek efter opdateringer - + Report Compatibility Rapporter kompatibilitet - + Restart + + + Load... + + + + + Remove + + MicroProfileDialog @@ -3681,23 +3952,23 @@ Screen. - + Connected Forbundet - + Not Connected Ikke forbundet - + Error Fejl - + Failed to announce the room to the public lobby. In order to host a room publicly, you must have a valid Citra account configured in Emulation -> Configure -> Web. If you do not want to publish a room in the public lobby, then select Unlisted instead. Debug Message: Kunne ikke annoncere om rummet i den offentlige lobby. For at køre et offentligt rum skal du have en gyldig Citra-konto sat op i Emulering -> Konfiguration -> Web. Hvis du ikke har lyst til at at offentliggøre et rum i lobbyen, vælg i stedet "ikke listet". @@ -3816,106 +4087,106 @@ Fejlfindingsbesked: %1 spiller %2 - - + + Invalid region Ugyldig region - + Japan Japan - + North America Nordamerika - + Europe Europa - + Australia Australien - + China Kina - + Korea Korea - + Taiwan Taiwan - + Region free Regionsfri - + Invalid Region Ugyldig region - + Shift Shift - + Ctrl Ctrl - + Alt Alt - - + + [not set] [ikke sat] - + Hat %1 %2 - + Axis %1%2 - + Button %1 - - + + [unknown] [ukendt] - + [unused] [ubrugt] - - + + Axis %1 diff --git a/dist/languages/de.ts b/dist/languages/de.ts index 925cffc0c..b7de72426 100644 --- a/dist/languages/de.ts +++ b/dist/languages/de.ts @@ -70,52 +70,57 @@ p, li { white-space: pre-wrap; } BreakPointModel - + Pica command loaded Pica-Befehl geladen - + Pica command processed Pica-Befehl verarbeitet - + Incoming primitive batch Eingehender primitiver Stapel - + Finished primitive batch Fertiger primitiver Stapel - + Vertex shader invocation Vertex Shader Aufruf - + Incoming display transfer Eingehende Anzeigeübertragung - + GSP command processed GSP-Befehl verarbeitet - + Buffers swapped Puffer getauscht + + + Unknown debug context event + + CalibrationConfigurationDialog Communicating with the server... - Kommuniziere mit Server... + Kommuniziere mit dem Server... @@ -130,12 +135,12 @@ p, li { white-space: pre-wrap; } Now touch the bottom right corner <br>of your touchpad. - Jetzt berühre die untere rechte Ecke <br>deines Touchpads. + Berühre jetzt die untere rechte Ecke <br>deines Touchpads. Configuration completed! - Konfiguration erfolgreich! + Konfiguration abgeschlossen! @@ -171,13 +176,13 @@ p, li { white-space: pre-wrap; } Spiel - - + + Block Player Spieler blockieren - + When you block a player, you will no longer receive chat messages from them.<br><br>Are you sure you would like to block %1? Wenn du einen Spieler blockierst, kannst du keine Chatnachrichten mehr von dem Spieler empfangen.<br><br> Bist du sicher, dass du %1 blockieren möchtest? @@ -198,17 +203,17 @@ p, li { white-space: pre-wrap; } ClientRoomWindow - + Connected Verbunden - + Disconnected Nicht verbunden - + %1 (%2/%3 members) - connected %1 (%2/%3 Nutzer) - verbunden @@ -301,6 +306,26 @@ p, li { white-space: pre-wrap; } Thank you for your submission! Vielen Dank für deinen Beitrag! + + + Submitting + + + + + Communication error + + + + + An error occured while sending the Testcase + + + + + Next + + ConfigureAudio @@ -340,10 +365,10 @@ p, li { white-space: pre-wrap; } 0 % - - - %1 % - %1 % + + %1% + Volume percentage (e.g. 50%) + %1% @@ -600,56 +625,84 @@ p, li { white-space: pre-wrap; } Enable CPU JIT - CPU JIT einschalten + CPU JIT aktivieren ConfigureDialog - + Citra Configuration Citra-Konfiguration - + + + General Allgemein - + + + System System - + + + Input Eingabe - + + + Graphics Grafik - + + + Audio Audio - + + + Camera Kamera - + + + Debug Debug - + + + Web Web + + + + + UI + Nutzeroberfläche + + + + Controls + Steuerung + ConfigureGeneral @@ -669,64 +722,54 @@ p, li { white-space: pre-wrap; } Verlassen bestätigen, wenn Emulation läuft - - Interface language - Benutzeroberflächensprache - - - + Updates Updates - + Check for updates on start Beim Start auf Updates überprüfen - + Silently auto update after closing Nach dem Beenden im Hintergrund automatisch updaten - + Emulation Emulation - + Region: Region: - + Auto-select Automatisch auswählen - - Theme - Design - - - - Theme: - Design: - - - + Hotkeys Tastenkürzel - - <System> - <System> + + Reset All Settings + Alle Einstellungen zurücksetzen - - English - Englisch + + Citra + Citra + + + + Are you sure you want to <b>reset your settings</b> and close Citra? + Bist du dir sicher, dass du <b>die Einstellungen zurücksetzen</b> und Citra beenden möchtest? @@ -925,7 +968,7 @@ p, li { white-space: pre-wrap; } Hardware Shader support is broken on macOS, and will cause graphical issues like showing a black screen.<br><br>The option is only there for test/development purposes. If you experience graphical issues with Hardware Shader, please turn it off. - Hardware Shader support funktioniert nicht unter macOS und wird grafische Fehler, wie zum Beispiel das Anzeigen eines schwarzen Bildschirms hervorrufen. <br><br>Die Option ist nur für Test- und Entwicklungszwecke vorhanden. Bitte schalte es aus, wenn du grafische Fehler mit Hardware Shadern feststellst. + Hardware Shader Support funktioniert unter MacOS nicht wie vorhergesehen und verursacht Grafikfehler, wie zum Beispiel das Anzeigen eines schwarzen Bildschirms. <br><br>Die Option ist nur für Test- und Entwicklungszwecke vorhanden. Bitte deaktiviere die Option, wenn du Grafikfehler feststellst. @@ -1026,7 +1069,7 @@ p, li { white-space: pre-wrap; } - + Set Analog Stick Analog-Stick festlegen @@ -1063,15 +1106,48 @@ p, li { white-space: pre-wrap; } Motion / Touch... - + Bewegungssteuerung / Toucheingaben... + Clear All + Alle zurücksetzen + + + Restore Defaults Standardwerte wiederherstellen - + + + Clear + Zurücksetzen + + + + + [not set] + [Nicht konfiguriert] + + + + + Restore Default + Standard wiederherstellen + + + + Information + Information + + + + After pressing OK, first move your joystick horizontally, and then vertically. + Drücke OK und bewege den Joystick anschließend zuerst in horizontale, dann in vertikale Richtung. + + + [press key] [Taste drücken] @@ -1081,165 +1157,165 @@ p, li { white-space: pre-wrap; } Configure Motion / Touch - + Bewegungssteuerung / Toucheingaben konfigurieren Motion - + Bewegungssteuerung Motion Provider: - + Quelle Bewegungsdaten: Sensitivity: - + Empfindlichkeit: Touch - + Touch Touch Provider: - + Quelle Toucheingaben: Calibration: - + Kalibrierung: (100, 50) - (1800, 850) - + (100, 50) - (1800, 850) Configure - + Konfigurieren CemuhookUDP Config - + CemuhookUDP Konfiguration You may use any Cemuhook compatible UDP input source to provide motion and touch input. - + Du kannst jede mit Cemuhook kompatible UDP Quelle verwenden, um Touch- und Bewegungseingaben bereitzustellen. Server: - + Server: Port: - + Port: Pad: - + Pad: Pad 1 - + Pad 1 Pad 2 - + Pad 2 Pad 3 - + Pad 3 Pad 4 - + Pad 4 Learn More - + Mehr erfahren Test - + Test Mouse (Right Click) - + Maus (Rechtsklick) CemuhookUDP - + CemuhookUDP Emulator Window - + Emulator Fenster <a href='https://citra-emu.org/wiki/using-a-controller-or-android-phone-for-motion-or-touch-input'><span style="text-decoration: underline; color:#039be5;">Learn More</span></a> - + <a href='https://citra-emu.org/wiki/using-a-controller-or-android-phone-for-motion-or-touch-input'><span style="text-decoration: underline; color:#039be5;">Mehr erfahren</span></a> Testing - + Test läuft Configuring - + Wird konfiguriert Test Successful - + Test erfolgreich Successfully received data from the server. - + Daten wurden erfolgreich vom Server empfangen. Test Failed - + Test gescheitert Could not receive valid data from the server.<br>Please verify that the server is set up correctly and the address and port are correct. - + Keine gültigen Daten vom Server empfangen. Bitte versichere dich, dass der Server korrekt eingerichtet ist und die Serveradresse und der Port richtig sind. Citra - + Citra UDP Test or calibration configuration is in progress.<br>Please wait for them to finish. - + UDP Test oder Kalibrierung werden gerade ausgeführt.<br>Bitte warte, bis sie abgeschlossen sind. @@ -1437,20 +1513,30 @@ p, li { white-space: pre-wrap; } Startup time - + Zum Start benötigte Zeit - + + yyyy-MM-ddTHH:mm:ss + yyyy-MM-ddTHH:mm:ss + + + + Play Coins: + Spielemünzen + + + Console ID: Konsolen-ID: - + Regenerate Wiederherstellen - + System settings are available only when game is not running. Systemeinstellungen sind nur verfügbar wenn kein Spiel läuft. @@ -2120,22 +2206,120 @@ p, li { white-space: pre-wrap; } Bermudas - - + + Console ID: 0x%1 Konsolen-ID: 0x%1 - + This will replace your current virtual 3DS with a new one. Your current virtual 3DS will not be recoverable. This might have unexpected effects in games. This might fail, if you use an outdated config savegame. Continue? Dies wird deinen jetziger virtueller 3DS wird mit einem neuen ersetzt. Dein jetziger virtueller 3DS kann nicht wiederhergestellt werden. Es könnte unerwartete Effekte in Spielen haben. Es könnte fehlschlagen, wenn du einen veralteten Konfigurationspeicherstand benutzt. Fortfahren? - + Warning Warnung + + ConfigureUi + + + Form + + + + + General + Allgemein + + + + Interface language: + Oberflächensprache + + + + Theme: + Thema: + + + + Game List + Spieleliste + + + + Icon Size: + Icongröße + + + + + None + + + + + Small (24x24) + Klein (24x24) + + + + Large (48x48) + Groß (48x48) + + + + Row 1 Text: + Text Zeile 1: + + + + + File Name + Dateiname + + + + + Full Path + Vollständiger Pfad + + + + + Title Name + Titelname + + + + + Title ID + Titel ID + + + + Row 2 Text: + Text Zeile 2: + + + + Hide Titles without Icon + Titel ohne Icon verbergen + + + + <System> + <System> + + + + English + Englisch + + ConfigureWeb @@ -2155,7 +2339,7 @@ p, li { white-space: pre-wrap; } - + Verify Verifizieren @@ -2207,56 +2391,56 @@ p, li { white-space: pre-wrap; } Discord Presence - + Discord Presence Show Current Game in your Discord Status - + Aktuelles Spiel in deinem Discordstatus anzeigen - + <a href='https://citra-emu.org/entry/telemetry-and-why-thats-a-good-thing/'><span style="text-decoration: underline; color:#039be5;">Learn more</span></a> <a href='https://citra-emu.org/entry/telemetry-and-why-thats-a-good-thing/'><span style="text-decoration: underline; color:#039be5;">Mehr erfahren</span></a> - - <a href='https://services.citra-emu.org/'><span style="text-decoration: underline; color:#039be5;">Sign up</span></a> - <a href='https://services.citra-emu.org/'><span style="text-decoration: underline; color:#039be5;">Registrieren</span></a> + + <a href='https://profile.citra-emu.org/'><span style="text-decoration: underline; color:#039be5;">Sign up</span></a> + <a href='https://profile.citra-emu.org/'><span style="text-decoration: underline; color:#039be5;">Anmelden</span></a> - + <a href='https://citra-emu.org/wiki/citra-web-service/'><span style="text-decoration: underline; color:#039be5;">What is my token?</span></a> <a href='https://citra-emu.org/wiki/citra-web-service/'><span style="text-decoration: underline; color:#039be5;">Was ist mein Token?</span></a> - - + + Telemetry ID: 0x%1 Telemetrie-ID: 0x%1 - + Username and token not verified Benutzername und Token nicht verifiziert - + Username and token were not verified. The changes to your username and/or token have not been saved. Nutzername und Token wurden nicht verifiziert. Die Änderungen an deinem Nutzernamen und/oder Token wurden nicht gespeichert. - + Verifying Verifiziere - + Verification failed Verifizierung fehlgeschlagen - + Verification failed. Check that you have entered your username and token correctly, and that your internet connection is working. Verifizierung fehlgeschlagen. Überprüfe, dass du deinen Nutzernamen und Token korrekt eingegeben hast und deine Internetverbindung funktioniert. @@ -2317,12 +2501,12 @@ p, li { white-space: pre-wrap; } DirectConnectWindow - + Connecting Verbinden - + Connect Verbinden @@ -2330,413 +2514,445 @@ p, li { white-space: pre-wrap; } GMainWindow - + <a href='https://citra-emu.org/entry/telemetry-and-why-thats-a-good-thing/'>Anonymous data is collected</a> to help improve Citra. <br/><br/>Would you like to share your usage data with us? - + Möchtest du <a href='https://citra-emu.org/entry/telemetry-and-why-thats-a-good-thing/'>anonyme Nutzungsdaten</a> mit dem Citra-Team teilen und so helfen, Citra weiter zu verbessern? - + Telemetry Telemetrie - - + + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a 3DS. Derzeitige Emulationsgeschwindigkeit. Werte höher oder niedriger als 100% zeigen, dass die Emulation schneller oder langsamer läuft als auf einem 3DS. - - + + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. Die Anzahl der Bilder pro Sekunde die das Spiel gerade darstellt. Dies wird von Spiel zu Spiel und von Szene zu Szene variieren. - - + + Time taken to emulate a 3DS frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. Die benötigte Zeit um ein 3DS-Einzelbild zu emulieren (V-Sync oder Bildratenbegrenzung nicht mitgezählt). Für eine Emulation in Echtzeit sollte dies maximal 16,67 ms betragen. - + Clear Recent Files - + Kürzlich verwendete Dateien zurücksetzten - + F9 F9 - + F10 F10 - + CTRL+F STRG+F - + Update Available Update verfügbar - + An update is available. Would you like to install it now? Ein Update ist verfügbar. Möchtest du es jetzt installieren? - + No Update Found - Kein Update + Kein Update gefunden - + No update is found. Kein Update gefunden. - - + + OpenGL 3.3 Unsupported + OpenGL 3.3 wird nicht unterstützt + + + + Your GPU may not support OpenGL 3.3, or you do not have the latest graphics driver. + Dein Grafiktreiber unterstützt OpenGL 3.3 möglicherweise nicht, oder der Grafiktreiber ist nicht aktuell. + + + + Invalid ROM Format Ungültiges ROM-Format - - + + Your ROM format is not supported.<br/>Please follow the guides to redump your <a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>game cartridges</a> or <a href='https://citra-emu.org/wiki/dumping-installed-titles/'>installed titles</a>. - Dieses ROM-Format wird nicht unterstützt.<br/>Bitte befolge die Guides um deine <a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>Karten</a> oder <a href='https://citra-emu.org/wiki/dumping-installed-titles/'>installierte Titel</a>neu zu dumpen. + Dieses ROM-Format wird nicht unterstützt.<br/>Bitte befolge die Guides, um deine <a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>Spielekarten</a> oder <a href='https://citra-emu.org/wiki/dumping-installed-titles/'>installierten Titel</a>erneut zu dumpen. - + ROM Corrupted - Korrupte ROM + ROM beschädigt - + Your ROM is corrupted. <br/>Please follow the guides to redump your <a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>game cartridges</a> or <a href='https://citra-emu.org/wiki/dumping-installed-titles/'>installed titles</a>. - Deine ROM ist korrupt. <br/>Bitte befolge die Guides um deine <a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>Karten</a> oder <a href='https://citra-emu.org/wiki/dumping-installed-titles/'>installierte Titel</a>neu zu dumpen. + Dieses ROM ist korrupt. <br/>Bitte befolge die Guides, um deine <a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>Karten</a> oder <a href='https://citra-emu.org/wiki/dumping-installed-titles/'>installierten Titel</a> erneut zu dumpen. - + ROM Encrypted ROM verschlüsselt - + Your ROM is encrypted. <br/>Please follow the guides to redump your <a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>game cartridges</a> or <a href='https://citra-emu.org/wiki/dumping-installed-titles/'>installed titles</a>. - Deine ROM ist verschlüsselt. <br/>Bitte befolge die Guides um deine <a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>Karten</a> oder <a href='https://citra-emu.org/wiki/dumping-installed-titles/'>installierte Titel</a>neu zu dumpen. + Deine ROM ist verschlüsselt. <br/>Bitte befolge die Guides, um deine <a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>Spielekarten</a> oder <a href='https://citra-emu.org/wiki/dumping-installed-titles/'>installierten Titel</a>erneut zu dumpen. - - + + Video Core Error Video Kern Fehler - + An error has occured. Please <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>see the log</a> for more details. Ensure that you have the latest graphics drivers for your GPU. - Ein Fehler ist aufgetreten. Bitte <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>schau in den Log</a> für mehr Details. Stelle sicher, dass du die neuesten Grafiktreiber für deine GPU hast. + Ein Fehler ist aufgetreten. Weitere Details findest du in der <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>Logdatei</a>. Stelle sicher, dass du den aktuellsten Grafiktreiber für deine GPU installiert hast. - + You are running default Windows drivers for your GPU. You need to install the proper drivers for your graphics card from the manufacturer's website. Du benutzt die vorinstallierten Windows Treiber für deine GPU. Du musst die richtigen Treiber für deine Grafikkarte von der Herstellerseite laden und installieren. - + Error while loading ROM! Fehler beim Laden des ROMs! - + An unknown error occured. Please see the log for more details. Ein unbekannter Fehler ist aufgetreten. Bitte schau für mehr Details in den Log. - + Start Start - + Error Opening %1 Folder Fehler beim Öffnen des Ordners %1 - - + + Folder does not exist! Ordner existiert nicht! - + Error Opening %1 Fehler beim Öffnen von %1 - + Select Directory Ordner auswählen - - 3DS Executable - 3DS-Executable + + 3DS Executable (%1);;All Files (*.*) + %1 is an identifier for the 3DS executable file extensions. + 3DS Programmdatei (%1);;Alle Dateien (*.*) - - - All Files (*.*) - Alle Dateien (*.*) - - - + Load File Datei laden - + Load Files Dateien laden - + 3DS Installation File (*.CIA*) 3DS Installationsdatei (*.CIA*) - + + All Files (*.*) + Alle Dateien (*.*) + + + %1 has been installed successfully. %1 wurde erfolgreich installiert. - + Unable to open File Datei konnte nicht geöffnet werden - + Could not open %1 Konnte %1 nicht öffnen - + Installation aborted Installation abgebrochen - + The installation of %1 was aborted. Please see the log for more details Die Installation von %1 wurde abgebrochen. Weitere Details dazu im Log. - + Invalid File Ungültige Datei - + %1 is not a valid CIA %1 ist kein gültiges CIA - + Encrypted File Verschlüsselte Datei - + %1 must be decrypted before being used with Citra. A real 3DS is required. %1 muss entschlüsselt werden, bevor es mit Citra verwendet werden kann. Hierzu ist ein 3DS ist notwendig. - + File not found Datei nicht gefunden - + File "%1" not found Datei "%1" nicht gefunden - - - + + + Continue Fortsetzen - + Missing Citra Account Fehlender Citra-Account - + You must link your Citra account to submit test cases.<br/>Go to Emulation &gt; Configure... &gt; Web to do so. Du musst deinen deinen Citra-Account verknüpfen, um Tests hochzuladen.<br/>Gehe zu Emulation &gt; Configure... &gt; Web um das zu tun. - - - + + Amiibo File (%1);; All Files (*.*) + Amiibo Datei (%1);; Alle Dateien (*.*) + + + + Load Amiibo + Amiibo laden + + + + + + Record Movie Aufnahme - - + + To keep consistency with the RNG, it is recommended to record the movie from game start.<br>Are you sure you still want to record movies now? + Um unterschiedliche Ergebnisse bei zufallsbasierten Ereignissen zu vermeiden, sollte eine Aufnahme zusammen mit dem dem Spiel gestartet werden. <br>Möchtest du die Aufnahme dennoch jetzt starten? + + + + Citra TAS Movie (*.ctm) Citra TAS Movie (*.ctm) - + Recording will start once you boot a game. - Die Aufnahme startet sobald du ein Spiel startest. + Die Aufnahme startet, sobald du ein Spiel startest. - + The movie file you are trying to load was created on a different revision of Citra.<br/>Citra has had some changes during the time, and the playback may desync or not work as expected.<br/><br/>Are you sure you still want to load the movie file? - Die gewünschte Movie Datei wurde mit einer anderen Version von Citra erstellt.<br/>Citra hatte seitdem einige Änderungen, und das Playback könnte nicht synchron oder fehlerhaft sein.<br/><br/>Soll die Datei trotzdem geladen werden? + Die gewünschte Aufnahme wurde mit einer anderen Version von Citra erstellt.<br/>Citra hat seitdem einige Änderungen durchlaufen und die Aufnahme könnte asynchron oder fehlerhaft sein.<br/><br/>Soll sie trotzdem geladen werden? - + The movie file you are trying to load was recorded with a different game.<br/>The playback may not work as expected, and it may cause unexpected results.<br/><br/>Are you sure you still want to load the movie file? - Die gewünschte Movie Datei wurde mit einem anderen Spiel aufgenommen.<br/>Die Wiedergabe könnte unerwartete Fehler beinhalten.<br/><br/>Soll die Datei trotzdem geladen werden? + Die gewünschte Aufnahme wurde mit einem anderen Spiel aufgenommen.<br/>Die Wiedergabe könnte unerwartete Fehler verursachen.<br/><br/>Soll sie dennoch geladen werden? - - + + The movie file you are trying to load is invalid.<br/>Either the file is corrupted, or Citra has had made some major changes to the Movie module.<br/>Please choose a different movie file and try again. - Die gewünschte Movie Datei ist fehlerhaft.<br/>Entweder ist die Datei korrupt oder Citra hatte große Änderungen im Movie-Modul.<br/><br/>Bitte wähle eine andere Movie-Datei und versuche es noch einmal. + Die gewünschte Aufnahme ist ungültig.<br/>Die Datei ist entweder beschädigt, oder es wurden große Veränderungen an Citra's Aufnahme-Modul vorgenommen.<br/><br/>Bitte wähle eine andere Aufnahme und versuche es noch einmal. - + Revision Dismatch - + Revision stimmt nicht überein - + Game Dismatch - + Spiel stimmt nicht überein - - + + Invalid Movie File - Fehlerhafte Movie-Datei + Ungültige Aufnahme - + + Play Movie - Movie abspielen + Aufnahme abspielen - + + To keep consistency with the RNG, it is recommended to play the movie from game start.<br>Are you sure you still want to play movies now? + Um unterschiedliche Ergebnisse bei zufallsbasierten Ereignissen während der Wiedergabe einer Aufnahme zu vermeiden, sollte die Wiedergabe immer zusammen mit dem Spiel gestartet werden. <br>Möchtest du die Aufnahme dennoch jetzt wiedergeben? + + + Game Not Found Spiel nicht gefunden - + The movie you are trying to play is from a game that is not in the game list. If you own the game, please add the game folder to the game list and try to play the movie again. - Das gewünschte Movie ist von einem Spiel, das nicht in der Liste ist. Falls du das Spiel besitzt, bitte füge es zum Spiele-Ordner hinzu und versuche nochmal, es abzuspielen. + Die gewünschte Aufnahme ist von einem Spiel, das nicht in der Liste ist. Wenn du das Spiel besitzt, füge den entsprechenden Ordner zur Spieleliste hinzu und versuche es erneut. - + Movie recording cancelled. Aufnahme abgebrochen. - + Movie Saved - Movie gespeichert + Aufnahme gespeichert - + The movie is successfully saved. - Das Movie wurde erfolgreich gespeichert. + Die Aufnahme wurde erfolgreich gespeichert. - + Speed: %1% / %2% Geschwindigkeit: %1% / %2% - + Speed: %1% Geschwindigkeit: %1% - + Game: %1 FPS Spiel: %1 FPS - + Frame: %1 ms Einzelbild: %1 ms - + %1 is missing. Please <a href='https://citra-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-3ds-console/'>dump your system archives</a>.<br/>Continuing emulation may result in crashes and bugs. - + %1 Fehlt. <a href='https://citra-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-3ds-console/'>Bitte dumpe deine Systemarchive</a>. <br/>Das Fortsetzen der Emulation könnte zu einem Absturz oder Bugs führen. - + System Archive Not Found Systemarchiv nicht gefunden - + Fatal Error Fataler Fehler - + A fatal error occured. <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>Check the log</a> for details.<br/>Continuing emulation may result in crashes and bugs. - + Ein schwerwiegender Fehler ist aufgetreten. Weitere Details findest du in der <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>Logdatei</a>.<br/>Das fortsetzten der Emulation könnte zu einem Absturz oder Bugs führen. - + Abort Abbrechen - - + + Citra Citra - + Would you like to exit now? - + Möchtest du die Anwendung jetzt verlassen? - + The game is still running. Would you like to stop emulation? - + Das Spiel läuft noch. Willst du die Emulation stoppen? - + Playback Completed - + Wiedergabe abgeschlossen - + Movie playback completed. - + Wiedergabe der Aufnahme abgeschlossen. - + Citra %1 Citra %1 - + Citra %1| %2 Citra %1| %2 @@ -2799,37 +3015,67 @@ p, li { white-space: pre-wrap; } GameList - + + Name + Name + + + + Compatibility + Kompatibilität + + + + Region + Region + + + + File type + Dateityp + + + + Size + Größe + + + Open Save Data Location Ort der Speicherdaten öffnen - + + Open Extra Data Location + + + + Open Application Location Ort der Applikationsdaten öffnen - + Open Update Data Location Ort der Updatedaten öffnen - + Navigate to GameDB entry Navigiere zum GameDB Eintrag - + Scan Subfolders Unterordner scannen - + Remove Game Directory Spieleverzeichnis entfernen - + Open Directory Location Verzeichnispfad öffnen @@ -2837,77 +3083,79 @@ p, li { white-space: pre-wrap; } GameListItemCompat - + Perfect Perfekt - + Game functions flawless with no audio or graphical glitches, all tested functionality works as intended without any workarounds needed. - Das Spiel funktioniert fehlerfrei ohne Audio- oder Grafikfehler und sämtliche getestete Funktionalität funktioniert wie vorhergesehen ohne Workarounds. - - - - Great - - - - - Game functions with minor graphical or audio glitches and is playable from start to finish. May require some -workarounds. - - - - - Okay - - - - - Game functions with major graphical or audio glitches, but game is playable from start to finish with -workarounds. - + Das Spiel funktioniert fehlerfrei, ohne Audio- oder Grafikfehler, und sämtliche getestete Funktionalität funktioniert wie vorhergesehen ohne Workarounds. + Great + Super + + + + Game functions with minor graphical or audio glitches and is playable from start to finish. May require some +workarounds. + Das Spiel funktioniert mit kleineren Audio- oder Grafikfehlern und lässt sich bis zum Ende durchspielen. +Eventuell sind einige Workarounds notwendig. + + + + Okay + Okay + + + + Game functions with major graphical or audio glitches, but game is playable from start to finish with +workarounds. + Das Spiel funktioniert mit größern Audio- oder Grafikfehlern, +lässt sich aber mit Workarounds bis zum Ende durchspielen. + + + Bad Schlecht - + Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches even with workarounds. - Das Spiel funktioniert, aber mit größeren Audio- oder Grafikfehlern. Fortschritt ist in manchen Gebieten durch Glitches unmöglich, sogar mit Workarounds. + Das Spiel funktioniert, aber mit größeren Audio- oder Grafikfehlern. Spielfortschritt ist aufgrund von Glitches zum Teil unmöglich, auch nicht mit Workarounds. - + Intro/Menu Intro/Menü - + Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start Screen. - Das Spiel ist wegen schwerwiegenden Audio- oder Grafikfehlern komplett unspielbar. Kein Fortschritt nach dem Startbildschirm möglich. + Das Spiel ist wegen schwerwiegenden Audio- oder Grafikfehlern unspielbar. Das Spiel lässt sich lediglich starten. - + Won't Boot Startet nicht - + The game crashes when attempting to startup. - Das Spiel crasht beim Versuch es zu starten. + Das Spiel stürzt beim Versuch es zu starten ab. - + Not Tested Nicht getestet - + The game has not yet been tested. Das Spiel wurde noch nicht getestet. @@ -2915,7 +3163,7 @@ Screen. GameListPlaceholder - + Double-click to add a new folder to the game list Doppelklicken, um einen neuen Ordner zur Spieleliste hinzuzufügen @@ -2923,27 +3171,27 @@ Screen. GameListSearchField - + of von - + result - resultat + Ergebnis - + results Ergebnisse - + Filter: Filter: - + Enter pattern to filter Gib Wörter zum Filtern ein @@ -2951,23 +3199,23 @@ Screen. GraphicsBreakPointsWidget - + Pica Breakpoints Pica-Haltepunkt - - + + Emulation running Emulation läuft - + Resume Fortsetzen - + Emulation halted at breakpoint Emulation angehalten am Haltepunkt @@ -3343,7 +3591,7 @@ Falls nein, werden die aufgenommmen Daten verworfen. Toggle LLE Service Modules - + LLE Servicemodule aktivieren / deaktivieren @@ -3385,42 +3633,42 @@ Falls nein, werden die aufgenommmen Daten verworfen. Lobby aktualisieren - + Password Required to Join Passwort zum Beitreten benötigt - + Password: Passwort: - + Room Name Raumname - + Preferred Game Bevorzugtes Spiel - + Host Host - + Players Spieler - + Refreshing Neu Laden - + Refresh List Liste aktualisieren @@ -3443,220 +3691,245 @@ Falls nein, werden die aufgenommmen Daten verworfen. Kürzliche Dateien - + + Amiibo + Amiibo + + + &Emulation &Emulation - + &View &Anzeige - + Debugging Debugging - + Screen Layout Bildschirmanordnung - + Movie - + Aufnahme - + Multiplayer Mehrspieler - + &Help &Hilfe - + Load File... Datei laden... - + Install CIA... CIA installieren... - + Load Symbol Map... Symbolkarte laden... - + E&xit V&erlassen - + &Start &Start - + &Pause &Pause - + &Stop &Stop - + FAQ FAQ - + About Citra Über Citra - + Single Window Mode Einzelfenstermodus - + Configure... Konfigurieren... - + Display Dock Widget Headers Dock Widget Header anzeigen - + Show Filter Bar Filterleiste anzeigen - + Show Status Bar Statusleiste anzeigen - + Select Game Directory... Spieleverzeichnis auswählen... - + Selects a folder to display in the game list Wählt einen Ordner aus, der in der Spieleliste angezeigt werden soll. - + Create Pica Surface Viewer Pica-Oberflächenansicht - + Record Movie - + Aufnahme starten - + Play Movie - + Aufnahme wiedergeben - + Stop Recording / Playback + Aufnahme anhalten + + + + Enable Frame Advancing - + + Advance Frame + + + + Browse Public Game Lobby Durchsuche Lobby für Öffentliche Spiele - + Create Room Raum erstellen - + Leave Room Raum verlassen - + Direct Connect to Room Direkt zum Raum Verbinden - + Show Current Room Zeige derzeitigen Raum an - + Fullscreen Vollbild - + Modify Citra Install Citra-Installation modifizieren - + Opens the maintenance tool to modify your Citra installation Öffnet das Wartungstool, um deine Citra-Installation zu modifizieren - + Default Standard - + Single Screen Einzelner Bildschirm - + Large Screen Großer Bildschirm - + Side by Side Seite an Seite - + Swap Screens Bildschirme tauschen - + Check for Updates Auf Updates prüfen - + Report Compatibility Kompatibilität melden - + Restart Neustart + + + Load... + Laden... + + + + Remove + + MicroProfileDialog @@ -3682,23 +3955,23 @@ Falls nein, werden die aufgenommmen Daten verworfen. - + Connected Verbunden - + Not Connected Nicht verbunden - + Error Fehler - + Failed to announce the room to the public lobby. In order to host a room publicly, you must have a valid Citra account configured in Emulation -> Configure -> Web. If you do not want to publish a room in the public lobby, then select Unlisted instead. Debug Message: Konnte den Raum nicht an der öffentlichen Lobby anmelden. Um einen Raum öffentlich zu betreiben muss ein gültiger Citra Account unter Emulation -> Konfiguration -> Web angegeben sein. Wenn der Raum nicht veröffentlicht werden soll, bitte "nicht-gelistet" auswählen. @@ -3817,108 +4090,108 @@ Debug Meldung: %1 spielt %2 - - + + Invalid region Ungültiges Gebiet - + Japan Japan - + North America Nord Amerika - + Europe Europa - + Australia Australien - + China China - + Korea Korea - + Taiwan Taiwan - + Region free Regionsfrei - + Invalid Region Ungültiges Gebiet - + Shift Umschalttaste - + Ctrl Strg - + Alt Alt - - + + [not set] [nicht gesetzt] - + Hat %1 %2 - + Hat %1 %2 - + Axis %1%2 - + Achse %1%2 - + Button %1 - + Taste %1 - - + + [unknown] [unbekannt] - + [unused] [ungenutzt] - - + + Axis %1 - + Achse %1 @@ -3946,7 +4219,7 @@ Debug Meldung: Text length is not correct (should be %1 characters) - Textlänge ist nicht korrekt (Soll %1 Buchstaben sein) + Textlänge ist nicht korrekt (Soll %1 Buchstabe(n) sein) diff --git a/dist/languages/es_ES.ts b/dist/languages/es_ES.ts index 074c66559..bcf2c6ea5 100644 --- a/dist/languages/es_ES.ts +++ b/dist/languages/es_ES.ts @@ -70,45 +70,50 @@ p, li { white-space: pre-wrap; } BreakPointModel - + Pica command loaded Comando de Pica cargado - + Pica command processed Comando de Pica procesado - + Incoming primitive batch Iniciando lote primitivo - + Finished primitive batch Lote primitivo terminado - + Vertex shader invocation Invocación del Sombreado de vértices - + Incoming display transfer Iniciando transferencia de pantalla - + GSP command processed Comando de GSP procesado - + Buffers swapped Buffers intercambiados + + + Unknown debug context event + Evento de depuración desconocido + CalibrationConfigurationDialog @@ -171,13 +176,13 @@ p, li { white-space: pre-wrap; } Juego - - + + Block Player Bloquear jugador - + When you block a player, you will no longer receive chat messages from them.<br><br>Are you sure you would like to block %1? Cuando bloquees a un jugador, ya no recibirás más mensajes de chat de éste.<br><br>¿Estás seguro de que quieres bloquear a %1? @@ -198,17 +203,17 @@ p, li { white-space: pre-wrap; } ClientRoomWindow - + Connected Conectado - + Disconnected Desconectado - + %1 (%2/%3 members) - connected %1 (%2/%3 miembros) - conectado @@ -301,6 +306,26 @@ p, li { white-space: pre-wrap; } Thank you for your submission! ¡Gracias por su colaboración! + + + Submitting + Enviando + + + + Communication error + Error de comunicación + + + + An error occured while sending the Testcase + Ha ocurrido un error mientras se enviaba la prueba. + + + + Next + Siguiente + ConfigureAudio @@ -340,10 +365,10 @@ p, li { white-space: pre-wrap; } 0 % - - - %1 % - %1 % + + %1% + Volume percentage (e.g. 50%) + %1% @@ -606,50 +631,78 @@ p, li { white-space: pre-wrap; } ConfigureDialog - + Citra Configuration Configuración de Citra - + + + General General - + + + System Sistema - + + + Input Controles - + + + Graphics Gráficos - + + + Audio Audio - + + + Camera Cámara - + + + Debug Depuración - + + + Web Web + + + + + UI + UI + + + + Controls + Controles + ConfigureGeneral @@ -669,64 +722,54 @@ p, li { white-space: pre-wrap; } Confirmar salida durante la emulación - - Interface language - Idioma de la interfaz - - - + Updates Actualizaciones - + Check for updates on start Buscar actualizaciones al iniciar - + Silently auto update after closing Actualizar automáticamente de forma silenciosa después de cerrar - + Emulation Emulación - + Region: Región: - + Auto-select Auto-elegir - - Theme - Tema - - - - Theme: - Tema: - - - + Hotkeys Teclas de atajo: - - <System> - <System> + + Reset All Settings + Reiniciar Toda la Configuración - - English - Inglés + + Citra + Citra + + + + Are you sure you want to <b>reset your settings</b> and close Citra? + ¿Estás seguro de que quieres <b>restablecer tu configuración</b> y cerrar Citra? @@ -1026,7 +1069,7 @@ p, li { white-space: pre-wrap; } - + Set Analog Stick Configurar Palanca Analógica @@ -1067,11 +1110,44 @@ p, li { white-space: pre-wrap; } + Clear All + Reiniciar todo + + + Restore Defaults Restablecer - + + + Clear + Reiniciar + + + + + [not set] + [no establecido] + + + + + Restore Default + Restablecer + + + + Information + Información + + + + After pressing OK, first move your joystick horizontally, and then vertically. + Después de pulsar Aceptar, mueve tu joystick horizontalmente, y luego verticalmente. + + + [press key] [pulsa un botón] @@ -1440,17 +1516,27 @@ p, li { white-space: pre-wrap; } Tiempo del Inicio - + + yyyy-MM-ddTHH:mm:ss + aaaa-mm-ddTHH:mm:ss + + + + Play Coins: + Monedas de Juego: + + + Console ID: ID de Consola: - + Regenerate Regenerar - + System settings are available only when game is not running. La Configuración de la Consola sólo está disponible cuando no se está emulando ningún juego. @@ -2120,22 +2206,120 @@ p, li { white-space: pre-wrap; } Bermudas - - + + Console ID: 0x%1 ID de Consola: 0x%1 - + This will replace your current virtual 3DS with a new one. Your current virtual 3DS will not be recoverable. This might have unexpected effects in games. This might fail, if you use an outdated config savegame. Continue? - Esto reemplazará tu 3DS virtual con una nueva. Tu 3DS virtual actual será irrecuperable. Esto puede tener efectos inesperados en ciertos juegos. Si usas un archivo de configuración anticuado, esto puede fallar. ¿Desea continuar? + Esto reemplazará tu 3DS virtual por una nueva. Tu 3DS virtual actual será irrecuperable. Esto puede tener efectos inesperados en determinados juegos. Si usas un archivo de configuración obsoleto, esto podría fallar. ¿Desea continuar? - + Warning Advertencia + + ConfigureUi + + + Form + Formulario + + + + General + General + + + + Interface language: + Idioma de la Interfaz + + + + Theme: + Tema: + + + + Game List + Lista de Juegos + + + + Icon Size: + Tamaño de Icono: + + + + + None + Ninguno + + + + Small (24x24) + Pequeño (24x24) + + + + Large (48x48) + Grande (48x48) + + + + Row 1 Text: + Texto de Fila 1: + + + + + File Name + Nombre de Archivo + + + + + Full Path + Ruta Completa + + + + + Title Name + Nombre del Título + + + + + Title ID + ID del Título + + + + Row 2 Text: + Texto de Fila 2: + + + + Hide Titles without Icon + Ocultar Títulos sin Icono + + + + <System> + <System> + + + + English + Inglés (English) + + ConfigureWeb @@ -2155,7 +2339,7 @@ p, li { white-space: pre-wrap; } - + Verify Verificar @@ -2215,48 +2399,48 @@ p, li { white-space: pre-wrap; } Mostrar Juego Actual en el Estado de Discord - + <a href='https://citra-emu.org/entry/telemetry-and-why-thats-a-good-thing/'><span style="text-decoration: underline; color:#039be5;">Learn more</span></a> <a href='https://citra-emu.org/entry/telemetry-and-why-thats-a-good-thing/'><span style="text-decoration: underline; color:#039be5;">Más información</span></a> - - <a href='https://services.citra-emu.org/'><span style="text-decoration: underline; color:#039be5;">Sign up</span></a> - <a href='https://services.citra-emu.org/'><span style="text-decoration: underline; color:#039be5;">Regístrate</span></a> + + <a href='https://profile.citra-emu.org/'><span style="text-decoration: underline; color:#039be5;">Sign up</span></a> + <a href='https://profile.citra-emu.org/'><span style="text-decoration: underline; color:#039be5;">Regístrate</span></a> - + <a href='https://citra-emu.org/wiki/citra-web-service/'><span style="text-decoration: underline; color:#039be5;">What is my token?</span></a> <a href='https://citra-emu.org/wiki/citra-web-service/'><span style="text-decoration: underline; color:#039be5;">¿Cuál es mi token?</span></a> - - + + Telemetry ID: 0x%1 ID de Telemetría: 0x%1 - + Username and token not verified Nombre de usuario y token no verificados - + Username and token were not verified. The changes to your username and/or token have not been saved. El nombre de usuario y el token no han sido verificados. Los cambios de tu nombre de usuario y/o del token no han sido guardados. - + Verifying Verificando - + Verification failed La verificación falló - + Verification failed. Check that you have entered your username and token correctly, and that your internet connection is working. La verificación falló. Comprueba que has introducido tu nombre de usuario y tu token correctamente, y que tu conexión internet funcione correctamente. @@ -2317,12 +2501,12 @@ p, li { white-space: pre-wrap; } DirectConnectWindow - + Connecting Conectando - + Connect Conectar @@ -2330,413 +2514,445 @@ p, li { white-space: pre-wrap; } GMainWindow - + <a href='https://citra-emu.org/entry/telemetry-and-why-thats-a-good-thing/'>Anonymous data is collected</a> to help improve Citra. <br/><br/>Would you like to share your usage data with us? <a href='https://citra-emu.org/entry/telemetry-and-why-thats-a-good-thing/'>Se recogen datos anónimos</a> para ayudar a mejorar Citra. <br/><br/>¿Quieres compartir tus datos de uso con nosotros? - + Telemetry Telemetría - - + + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a 3DS. La velocidad de emulación actual. Valores mayores o menores de 100% indican que la velocidad de emulación funciona más rápida o lentamente que en una 3DS. - - + + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. Los fotogramas por segundo que está mostrando el juego. Variarán de juego en juego y de escena a escena. - - + + Time taken to emulate a 3DS frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. El tiempo que lleva emular un fotograma de 3DS, sin tener en cuenta el limitador de fotogramas, ni la sincronización vertical. Para una emulación óptima, este valor no debe superar los 16.67 ms. - + Clear Recent Files Limpiar Archivos Recientes - + F9 F9 - + F10 F10 - + CTRL+F CTRL+F - + Update Available Actualización Disponible - + An update is available. Would you like to install it now? Hay una actualización disponible. ¿Quieres instalarla ahora? - + No Update Found Actualización No Encontrada - + No update is found. No se han encontrado actualizaciones. - - + + OpenGL 3.3 Unsupported + Tu gráfica no soporta OpenGL 3.3 + + + + Your GPU may not support OpenGL 3.3, or you do not have the latest graphics driver. + Tu tarjeta gráfica no soporta OpenGL 3.3, o no tienes los últimos controladores gráficos para tu tarjeta. + + + + Invalid ROM Format Formato de ROM no válido - - + + Your ROM format is not supported.<br/>Please follow the guides to redump your <a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>game cartridges</a> or <a href='https://citra-emu.org/wiki/dumping-installed-titles/'>installed titles</a>. Tu formato de ROM no es válido.<br/>Por favor, sigue las instrucciones para volver a volcar tus <a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>cartuchos de juego</a> y/o <a href='https://citra-emu.org/wiki/dumping-installed-titles/'>títulos instalados</a>. - + ROM Corrupted ROM Corrupto - + Your ROM is corrupted. <br/>Please follow the guides to redump your <a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>game cartridges</a> or <a href='https://citra-emu.org/wiki/dumping-installed-titles/'>installed titles</a>. Tu ROM está corrupto. <br/>Por favor, sigue las instrucciones para volver a volcar tus<a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>cartuchos de juego</a> y/o <a href='https://citra-emu.org/wiki/dumping-installed-titles/'>títulos instalados</a>. - + ROM Encrypted ROM Encriptado - + Your ROM is encrypted. <br/>Please follow the guides to redump your <a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>game cartridges</a> or <a href='https://citra-emu.org/wiki/dumping-installed-titles/'>installed titles</a>. Tu ROM está encriptado. <br/>Por favor, sigue las instrucciones para volver a volcar tus<a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>cartuchos de juego</a> y/o <a href='https://citra-emu.org/wiki/dumping-installed-titles/'>títulos instalados</a>. - - + + Video Core Error Error en el Núcleo de Vídeo - + An error has occured. Please <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>see the log</a> for more details. Ensure that you have the latest graphics drivers for your GPU. Ha ocurrido un error. Por favor,<a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>mira el log</a> para más detalles. Asegúrese de tener los últimos drivers de tu tarjeta gráfica instalados. - + You are running default Windows drivers for your GPU. You need to install the proper drivers for your graphics card from the manufacturer's website. Estás usando los controladores por defecto de Windows para tu GPU. Necesitas instalar los controladores de tu tarjeta gráfica de la página del fabricante. - + Error while loading ROM! ¡Error al cargar la ROM! - + An unknown error occured. Please see the log for more details. Un error desconocido ha ocurrido. Por favor, mira el log para más detalles. - + Start Iniciar - + Error Opening %1 Folder Error al abrir la carpeta %1 - - + + Folder does not exist! ¡La carpeta no existe! - + Error Opening %1 Error al abrir %1 - + Select Directory Seleccionar directorio - - 3DS Executable - Ejecutable de 3DS + + 3DS Executable (%1);;All Files (*.*) + %1 is an identifier for the 3DS executable file extensions. + Ejecutable 3DS(%1);;Todos los archivos(*.*) - - - All Files (*.*) - Todos los archivos (*.*) - - - + Load File Cargar Archivo - + Load Files Cargar archivos - + 3DS Installation File (*.CIA*) Archivo de Instalación de 3DS (*.CIA*) - + + All Files (*.*) + Todos los archivos (*.*) + + + %1 has been installed successfully. %1 ha sido instalado con éxito. - + Unable to open File No se pudo abrir el Archivo - + Could not open %1 No se pudo abrir %1 - + Installation aborted Instalación interrumpida - + The installation of %1 was aborted. Please see the log for more details La instalación de %1 ha sido cancelada. Por favor, consulte los registros para más información. - + Invalid File Archivo no válido - + %1 is not a valid CIA %1 no es un archivo CIA válido - + Encrypted File Archivo Encriptado - + %1 must be decrypted before being used with Citra. A real 3DS is required. %1 debe ser desencriptado antes de ser usado en Citra. Se requiere de una 3DS real. - + File not found Archivo no encontrado - + File "%1" not found Archivo "%1" no encontrado - - - + + + Continue Continuar - + Missing Citra Account Falta la cuenta de Citra - + You must link your Citra account to submit test cases.<br/>Go to Emulation &gt; Configure... &gt; Web to do so. Debes vincular tu cuenta de Citra para enviar casos de pruebas.<br/>Ve a Emulación &gt; Configurar... &gt; Web para hacerlo. - - - + + Amiibo File (%1);; All Files (*.*) + Archivo de Amiibo(%1);; Todos los archivos (*.*) + + + + Load Amiibo + Cargar Amiibo + + + + + + Record Movie Grabar Película - - + + To keep consistency with the RNG, it is recommended to record the movie from game start.<br>Are you sure you still want to record movies now? + Para mantener la consistencia con el RNG, se recomienda grabar la película desde el inicio del juego.<br>¿Estás seguro de que quieres grabar una película ahora? + + + + Citra TAS Movie (*.ctm) Citra TAS Movie (*.ctm) - + Recording will start once you boot a game. La grabación comenzará una vez que inicies un juego. - + The movie file you are trying to load was created on a different revision of Citra.<br/>Citra has had some changes during the time, and the playback may desync or not work as expected.<br/><br/>Are you sure you still want to load the movie file? La película que estás intentando cargar se creó en una revisión distinta de Citra.<br/>Citra ha tenido cambios durante ese tiempo, y la reproducción puede desincronizarse o no funcionar de la forma esperada.<br/><br/>¿Quiere seguir cargando el archivo de la película? - + The movie file you are trying to load was recorded with a different game.<br/>The playback may not work as expected, and it may cause unexpected results.<br/><br/>Are you sure you still want to load the movie file? La película que está intentando cargar fue grabada con otro juego.<br/>La reproducción puede no funcionar bien, y puede derivar en resultados inesperados.<br/><br/>¿Quiere seguir reproduciendo la película? - - + + The movie file you are trying to load is invalid.<br/>Either the file is corrupted, or Citra has had made some major changes to the Movie module.<br/>Please choose a different movie file and try again. La película que estás intentando cargar no es válida.<br/>O el archivo está corrupto, o Citra ha hecho muchos cambios al módulo Película.<br/> Por favor, elija una película distinta e inténtalo de nuevo. - + Revision Dismatch No coincide la revisión - + Game Dismatch No coincide el juego - - + + Invalid Movie File Película No Válida - + + Play Movie Reproducir Película - + + To keep consistency with the RNG, it is recommended to play the movie from game start.<br>Are you sure you still want to play movies now? + Para mantener la constancia con el RNG, se recomienda reproducir la película desde el inicio del juego.<br>¿Estás seguro de que quieres reproducir una película ahora? + + + Game Not Found Juego No Encontrado - + The movie you are trying to play is from a game that is not in the game list. If you own the game, please add the game folder to the game list and try to play the movie again. La película que intenta reproducir es de un juego que no está en su lista de juegos. Si tienes el juego, por favor, añade el juego a su lista de juegos e intente reproducir la película de nuevo. - + Movie recording cancelled. Grabación de película cancelada. - + Movie Saved Película Guardada - + The movie is successfully saved. Película guardada con éxito. - + Speed: %1% / %2% Velocidad: %1% / %2% - + Speed: %1% Velocidad: %1% - + Game: %1 FPS Juego: %1 FPS - + Frame: %1 ms Frame: %1 ms - + %1 is missing. Please <a href='https://citra-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-3ds-console/'>dump your system archives</a>.<br/>Continuing emulation may result in crashes and bugs. Falta %1. Por favor, <a href='https://citra-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-3ds-console/'>vuelca tus archivos de sistema</a>.<br/>Continuar la emulación puede resultar en cuelgues y errores. - + System Archive Not Found Archivo de Sistema no encontrado - + Fatal Error Error Fatal - + A fatal error occured. <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>Check the log</a> for details.<br/>Continuing emulation may result in crashes and bugs. Ha ocurrido un error fatal. <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>Mira el log</a> para más detalles.<br/>Continuar la emulación puede resultar en cuelgues y errores. - + Abort Abortar - - + + Citra Citra - + Would you like to exit now? ¿Quiere salir ahora? - + The game is still running. Would you like to stop emulation? El juego sigue ejecutándose. ¿Quiere parar la emulación? - + Playback Completed Reproducción Completada - + Movie playback completed. Reproducción de película completada. - + Citra %1 Citra %1 - + Citra %1| %2 Citra %1| %2 @@ -2799,37 +3015,67 @@ p, li { white-space: pre-wrap; } GameList - + + Name + Nombre + + + + Compatibility + Compatibilidad + + + + Region + Región + + + + File type + Tipo de Archivo + + + + Size + Tamaño + + + Open Save Data Location Abrir ubicación de los archivos de guardado - + + Open Extra Data Location + Abrir ubicación de los Datos Adicionales + + + Open Application Location Abrir ubicación de la aplicación - + Open Update Data Location Abrir ubicación de los archivos de actualización - + Navigate to GameDB entry Ir a la base de datos de los juegos - + Scan Subfolders Escanear subdirectorios - + Remove Game Directory Eliminar directorio de juegos - + Open Directory Location Abrir ubicación del directorio @@ -2837,82 +3083,82 @@ p, li { white-space: pre-wrap; } GameListItemCompat - + Perfect Perfecto - + Game functions flawless with no audio or graphical glitches, all tested functionality works as intended without any workarounds needed. El juego funciona a la perfección, sin problemas de sonido o gráficos, todas las funcionalidades probadas funcionan según lo previsto sin la necesidad de soluciones temporales. - + Great Excelente - + Game functions with minor graphical or audio glitches and is playable from start to finish. May require some workarounds. El juego funciona con pequeños problemas gráficos o de sonido y es jugable de principio a fin. Podría requerir de soluciones temporales. - + Okay Bien - + Game functions with major graphical or audio glitches, but game is playable from start to finish with workarounds. El juego funciona con importantes problemas gráficos o de sonido, pero el juego es jugable de principio a fin con soluciones temporales. - + Bad Mal - + Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches even with workarounds. El juego funciona, pero con notables problemas gráficos o de sonido. Es imposible avanzar en zonas específicas debido a fallos incluso con soluciones temporales. - + Intro/Menu Intro/Menú - + Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start Screen. No es posible jugar al juego debido a importantes problemas gráficos o de sonido. Es imposible avanzar más allá de la pantalla de inicio. - + Won't Boot No inicia - + The game crashes when attempting to startup. El juego se bloquea al intentar iniciarse. - + Not Tested Sin probar - + The game has not yet been tested. El juego todavía no ha sido testeado. @@ -2920,7 +3166,7 @@ más allá de la pantalla de inicio. GameListPlaceholder - + Double-click to add a new folder to the game list Haz doble click para añadir una nueva carpeta a la lista de juegos @@ -2928,27 +3174,27 @@ más allá de la pantalla de inicio. GameListSearchField - + of de - + result resultado - + results resultados - + Filter: Filtro: - + Enter pattern to filter Introduzca un patrón para filtrar @@ -2956,23 +3202,23 @@ más allá de la pantalla de inicio. GraphicsBreakPointsWidget - + Pica Breakpoints Pica Breakpoints - - + + Emulation running Ejecutando emulación - + Resume Reanudar - + Emulation halted at breakpoint Emulación parada en un breakpoint @@ -3389,42 +3635,42 @@ más allá de la pantalla de inicio. Actualizar Lobby - + Password Required to Join Contraseña Necesaria para Unirse - + Password: Contraseña: - + Room Name Nombre de Sala - + Preferred Game Juego Preferente - + Host Host - + Players Jugadores - + Refreshing Actualizando - + Refresh List Actualizar Lista @@ -3447,220 +3693,245 @@ más allá de la pantalla de inicio. Archivos Recientes - + + Amiibo + Amiibo + + + &Emulation &Emulación - + &View &Ver - + Debugging Depuración - + Screen Layout Estilo de pantalla - + Movie Película - + Multiplayer Multijugador - + &Help &Ayuda - + Load File... Cargar Archivo... - + Install CIA... Instalar CIA... - + Load Symbol Map... Cargar Mapa de Símbolos... - + E&xit S&alir - + &Start &Iniciar - + &Pause &Pausar - + &Stop &Parar - + FAQ FAQ - + About Citra Acerca de Citra - + Single Window Mode Modo Ventana Única - + Configure... Configurar... - + Display Dock Widget Headers Mostrar Títulos de Widgets del Dock - + Show Filter Bar Mostrar Barra de Filtro - + Show Status Bar Mostrar Barra de Estado - + Select Game Directory... Seleccionar Directorio de Juego... - + Selects a folder to display in the game list Selecciona una carpeta para mostrar en la lista de juegos - + Create Pica Surface Viewer Crear Observador de Superficie de Pica - + Record Movie Grabar Película - + Play Movie Reproducir Película - + Stop Recording / Playback Dejar de Grabar / Reproducir - + + Enable Frame Advancing + Activar Avance de Fotogramas + + + + Advance Frame + Avanzar Fotograma + + + Browse Public Game Lobby Buscar Salas Públicas - + Create Room Crear Sala - + Leave Room Abandonar Sala - + Direct Connect to Room Conectar Directamente a Sala - + Show Current Room Mostrar Sala Actual - + Fullscreen Pantalla Completa - + Modify Citra Install Modificar Instalación de Citra - + Opens the maintenance tool to modify your Citra installation Abre la herramienta de mantenimiento para modificar tu traducción de Citra - + Default Por defecto - + Single Screen Pantalla única - + Large Screen Pantalla amplia - + Side by Side Conjunta - + Swap Screens Intercambiar pantallas - + Check for Updates Buscar Actualizaciones - + Report Compatibility Informar de compatibilidad - + Restart Reiniciar + + + Load... + Cargar... + + + + Remove + Quitar + MicroProfileDialog @@ -3686,23 +3957,23 @@ más allá de la pantalla de inicio. - + Connected Conectado - + Not Connected No conectado - + Error Error - + Failed to announce the room to the public lobby. In order to host a room publicly, you must have a valid Citra account configured in Emulation -> Configure -> Web. If you do not want to publish a room in the public lobby, then select Unlisted instead. Debug Message: No se ha podido anunciar la sala en el lobby público. Para ser el anfitrión de una sala pública, debes tener una cuenta de Citra válida configurada en Emulación -> Configurar -> Web. Si no deseas publicar una sala en el lobby público, entonces seleccione Privada. @@ -3821,106 +4092,106 @@ Mensaje de Depuración: %1 está jugando a %2 - - + + Invalid region Región no válida - + Japan Japón - + North America Norteamérica - + Europe Europa - + Australia Australia - + China China - + Korea Corea - + Taiwan Taiwán - + Region free Region free - + Invalid Region Región no válida - + Shift Shift - + Ctrl Ctrl - + Alt Alt - - + + [not set] [no establecido] - + Hat %1 %2 Rotación %1 %2 - + Axis %1%2 Axis %1%2 - + Button %1 Botón %1 - - + + [unknown] [desconocido] - + [unused] [sin usar] - - + + Axis %1 Axis %1 diff --git a/dist/languages/fr.ts b/dist/languages/fr.ts index d902a6b93..ee478d782 100644 --- a/dist/languages/fr.ts +++ b/dist/languages/fr.ts @@ -70,45 +70,50 @@ p, li { white-space: pre-wrap; } BreakPointModel - + Pica command loaded Pica command loaded - + Pica command processed Pica command processed - + Incoming primitive batch Incoming primitive batch - + Finished primitive batch Finished primitive batch - + Vertex shader invocation Vertex shader invocation - + Incoming display transfer Incoming display transfer - + GSP command processed GSP command processed - + Buffers swapped Buffers swapped + + + Unknown debug context event + Événement contextuel de débogage inconnu + CalibrationConfigurationDialog @@ -130,12 +135,12 @@ p, li { white-space: pre-wrap; } Now touch the bottom right corner <br>of your touchpad. - Maintenant touchez le coin inférieur droite <br> de votre pavé tactile. + Maintenant touchez le coin inférieur droit <br> de votre pavé tactile. Configuration completed! - Configuration terminée! + Configuration terminée ! @@ -171,13 +176,13 @@ p, li { white-space: pre-wrap; } Jeu - - + + Block Player Bloquer un joueur - + When you block a player, you will no longer receive chat messages from them.<br><br>Are you sure you would like to block %1? Lorsque vous bloquez un joueur, vous ne recevrez plus de message de sa part.<br><br>Êtes-vous sûr de vouloir bloquer %1? @@ -198,17 +203,17 @@ p, li { white-space: pre-wrap; } ClientRoomWindow - + Connected Connecté - + Disconnected Déconnecté - + %1 (%2/%3 members) - connected %1 (%2/%3 membres) - connecté @@ -301,6 +306,26 @@ p, li { white-space: pre-wrap; } Thank you for your submission! Merci pour votre contribution! + + + Submitting + Transmission + + + + Communication error + Erreur de communication + + + + An error occured while sending the Testcase + Une erreur s'est produite pendant l'envoi du cas test. + + + + Next + Suivant + ConfigureAudio @@ -312,7 +337,7 @@ p, li { white-space: pre-wrap; } Output Engine: - Moteur de sortie: + Moteur de sortie : @@ -327,12 +352,12 @@ p, li { white-space: pre-wrap; } Audio Device: - Périphérique Audio: + Périphérique Audio : Volume: - Volume: + Volume : @@ -340,10 +365,10 @@ p, li { white-space: pre-wrap; } 0 % - - - %1 % - %1 % + + %1% + Volume percentage (e.g. 50%) + %1% @@ -367,7 +392,7 @@ p, li { white-space: pre-wrap; } Camera to configure: - Caméra à configurer: + Caméra à configurer : @@ -388,7 +413,7 @@ p, li { white-space: pre-wrap; } Camera mode: - Mode de la caméra: + Mode de la caméra : @@ -409,7 +434,7 @@ p, li { white-space: pre-wrap; } Camera position: - Position de la caméra: + Position de la caméra : @@ -455,7 +480,7 @@ p, li { white-space: pre-wrap; } File: - Fichier: + Fichier : @@ -471,7 +496,7 @@ p, li { white-space: pre-wrap; } Camera: - Caméra: + Caméra : @@ -487,7 +512,7 @@ p, li { white-space: pre-wrap; } Flip: - Basculement: + Basculement : @@ -527,7 +552,7 @@ p, li { white-space: pre-wrap; } Resolution: 512*384 - Définition: 512*384 + Résolution : 512*384 @@ -537,7 +562,7 @@ p, li { white-space: pre-wrap; } Resolution: - Définition: + Définition : @@ -606,50 +631,78 @@ p, li { white-space: pre-wrap; } ConfigureDialog - + Citra Configuration Configuration de Citra - + + + General Général - + + + System Système - + + + Input Contrôles - + + + Graphics Graphiques - + + + Audio Son - + + + Camera - Caméra: + Caméra : - + + + Debug Débug - + + + Web Web + + + + + UI + Interface + + + + Controls + Contrôles + ConfigureGeneral @@ -669,64 +722,54 @@ p, li { white-space: pre-wrap; } Confirmer la fermeture du programme pendant l'exécution de l'émulation - - Interface language - Langage de l'interface - - - + Updates Mises à Jour - + Check for updates on start Vérifier les mises à jour au démarrage - + Silently auto update after closing Mise à jour automatique après la fermeture - + Emulation Émulation - + Region: - Région: + Région : - + Auto-select Sélection Automatique - - Theme - Thème - - - - Theme: - Thème: - - - + Hotkeys Raccourcis - - <System> - <System> + + Reset All Settings + Réinitialiser tous les paramètres - - English - Anglais + + Citra + Citra + + + + Are you sure you want to <b>reset your settings</b> and close Citra? + Êtes-vous sûr de vouloir <b> réinitialiser vos paramètres </b> et fermer Citra? @@ -915,7 +958,7 @@ p, li { white-space: pre-wrap; } Background Color: - Couleur d'arrière-plan: + Couleur d'arrière-plan : @@ -943,22 +986,22 @@ p, li { white-space: pre-wrap; } A: - A + A : B: - B + B : X: - X + X : Y: - Y + Y : @@ -970,28 +1013,28 @@ p, li { white-space: pre-wrap; } Up: - Haut + Haut : Down: - Bas: + Bas : Left: - Gauche + Gauche : Right: - Droit + Droite : @@ -1001,22 +1044,22 @@ p, li { white-space: pre-wrap; } L: - L + L : R: - R + R : ZL: - ZL + ZL : ZR: - ZR + ZR : @@ -1026,7 +1069,7 @@ p, li { white-space: pre-wrap; } - + Set Analog Stick Configurer le stick analogique @@ -1058,7 +1101,7 @@ p, li { white-space: pre-wrap; } Circle Mod: - Circle Mod: + Circle Mod : @@ -1067,11 +1110,44 @@ p, li { white-space: pre-wrap; } + Clear All + Tout effacer + + + Restore Defaults Restaurer les paramètres par défaut - + + + Clear + Effacer + + + + + [not set] + [non définie] + + + + + Restore Default + Réinitialiser + + + + Information + Information + + + + After pressing OK, first move your joystick horizontally, and then vertically. + Après avoir appuyé sur OK, déplacez votre joystick horizontalement, puis verticalement. + + + [press key] [appuyer sur une touche] @@ -1096,7 +1172,7 @@ p, li { white-space: pre-wrap; } Sensitivity: - Sensibilité: + Sensibilité : @@ -1106,12 +1182,12 @@ p, li { white-space: pre-wrap; } Touch Provider: - Gestion du tactile: + Gestion du tactile : Calibration: - Calibration: + Calibration : @@ -1440,17 +1516,27 @@ p, li { white-space: pre-wrap; } Heure au démarrage - + + yyyy-MM-ddTHH:mm:ss + aaaa-MM-jjTHH:mm:ss + + + + Play Coins: + Pièces de jeu: + + + Console ID: Console ID: - + Regenerate Regénérer - + System settings are available only when game is not running. Les paramètres systèmes ne sont disponibles que lorsque l'émulation n'est pas en cours. @@ -2120,22 +2206,120 @@ p, li { white-space: pre-wrap; } Bermudes - - + + Console ID: 0x%1 Console ID: 0x%1 - + This will replace your current virtual 3DS with a new one. Your current virtual 3DS will not be recoverable. This might have unexpected effects in games. This might fail, if you use an outdated config savegame. Continue? Ceci va remplacer votre 3DS virtuelle actuelle avec une nouvelle. Votre 3DS virtuelle actuelle ne sera plus récupérable. Cela pourrait avoir des effets inattendus pour certains jeux. Cela pourrait échouer, si vous utilisez une ancienne sauvegarde de jeu. Continuer? - + Warning Avertissement + + ConfigureUi + + + Form + Formulaire + + + + General + Générale + + + + Interface language: + Langue de l'interface: + + + + Theme: + Thème: + + + + Game List + Liste de jeux + + + + Icon Size: + Talle de l'icône: + + + + + None + Aucun + + + + Small (24x24) + Petite (24x24) + + + + Large (48x48) + Grande (48x48) + + + + Row 1 Text: + Texte de la ligne 1: + + + + + File Name + Nom du fichier + + + + + Full Path + Chemin complet + + + + + Title Name + Nom du titre + + + + + Title ID + ID du titre + + + + Row 2 Text: + Texte de la ligne 2: + + + + Hide Titles without Icon + Masquer les titres sans icône + + + + <System> + <System> + + + + English + Anglais + + ConfigureWeb @@ -2155,7 +2339,7 @@ p, li { white-space: pre-wrap; } - + Verify Vérifier @@ -2215,48 +2399,48 @@ p, li { white-space: pre-wrap; } Afficher votre jeu en cours dans votre statut Discord - + <a href='https://citra-emu.org/entry/telemetry-and-why-thats-a-good-thing/'><span style="text-decoration: underline; color:#039be5;">Learn more</span></a> <a href='https://citra-emu.org/entry/telemetry-and-why-thats-a-good-thing/'><span style="text-decoration: underline; color:#039be5;">En savoir plus</span></a> - - <a href='https://services.citra-emu.org/'><span style="text-decoration: underline; color:#039be5;">Sign up</span></a> - <a href='https://services.citra-emu.org/'><span style="text-decoration: underline; color:#039be5;">S'inscrire</span></a> + + <a href='https://profile.citra-emu.org/'><span style="text-decoration: underline; color:#039be5;">Sign up</span></a> + <a href='https://profile.citra-emu.org/'><span style="text-decoration: underline; color:#039be5;">S'inscrire</span></a> - + <a href='https://citra-emu.org/wiki/citra-web-service/'><span style="text-decoration: underline; color:#039be5;">What is my token?</span></a> <a href='https://citra-emu.org/wiki/citra-web-service/'><span style="text-decoration: underline; color:#039be5;">Quel est mon jeton?</span></a> - - + + Telemetry ID: 0x%1 ID de télémétrie: 0x%1 - + Username and token not verified Nom d'utilisateur et token non vérifiés - + Username and token were not verified. The changes to your username and/or token have not been saved. Le nom d'utilisateur et le token n'ont pas été vérifiés. Les modifications apportées à votre nom d'utilisateur et / ou token n'ont pas été enregistrées. - + Verifying Vérification en cours - + Verification failed Échec de la vérification - + Verification failed. Check that you have entered your username and token correctly, and that your internet connection is working. Échec de la vérification. Vérifiez que vous avez correctement entré votre nom d'utilisateur et votre token, et que votre connexion Internet fonctionne. @@ -2317,12 +2501,12 @@ p, li { white-space: pre-wrap; } DirectConnectWindow - + Connecting Connexion en cours - + Connect Connecter @@ -2330,413 +2514,445 @@ p, li { white-space: pre-wrap; } GMainWindow - + <a href='https://citra-emu.org/entry/telemetry-and-why-thats-a-good-thing/'>Anonymous data is collected</a> to help improve Citra. <br/><br/>Would you like to share your usage data with us? <a href='https://citra-emu.org/entry/telemetry-and-why-thats-a-good-thing/'>Des données anonymes sont collectées</a> afin d'aider à l'amélioration de Citra. <br/><br/>Voulez-vous nous communiquer vos données? - + Telemetry Télémétrie - - + + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a 3DS. Vitesse actuelle d'émulation. Les valeurs supérieures ou inférieures à 100% indiquent que l'émulation est plus rapide ou plus lente qu'une 3DS. - - + + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. Nombre d'images par seconde le jeu affiche actuellement. Cela varie d'un jeu à l'autre et d'une scène à l'autre. - - + + Time taken to emulate a 3DS frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. Temps nécessaire pour émuler une trame 3DS, sans compter la limitation de trame ou la synchronisation verticale V-Sync. Pour une émulation à pleine vitesse, cela ne devrait pas dépasser 16,67 ms. - + Clear Recent Files Effacer les fichiers récents - + F9 F9 - + F10 F10 - + CTRL+F CTRL+F - + Update Available Mise à jour disponible - + An update is available. Would you like to install it now? Une mise à jour est disponible. Voulez-vous l'installer maintenant? - + No Update Found Aucune mise à jour trouvée - + No update is found. Aucune mise à jour n'a été trouvée. - - + + OpenGL 3.3 Unsupported + OpenGL 3.3 n'est pas supporté + + + + Your GPU may not support OpenGL 3.3, or you do not have the latest graphics driver. + Votre carte graphique ne supporte pas OpenGL 3.3, ou vous n'avez pas le dernier pilote disponible. + + + + Invalid ROM Format Format de ROM non valide - - + + Your ROM format is not supported.<br/>Please follow the guides to redump your <a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>game cartridges</a> or <a href='https://citra-emu.org/wiki/dumping-installed-titles/'>installed titles</a>. Le format de votre ROM n'est pas supporté.<br/>Veuillez suivre les guides afin de dumper (copier) vos <a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>cartouches de jeu</a> ou <a href='https://citra-emu.org/wiki/dumping-installed-titles/'>les titres installés</a> - + ROM Corrupted ROM corrompue - + Your ROM is corrupted. <br/>Please follow the guides to redump your <a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>game cartridges</a> or <a href='https://citra-emu.org/wiki/dumping-installed-titles/'>installed titles</a>. Votre ROM est corrompue. <br/>Veuilez suivre les guides afin de dumper vos<a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>cartouches de jeu</a> ou <a href='https://citra-emu.org/wiki/dumping-installed-titles/'>les titres installés</a>. - + ROM Encrypted ROM chiffrée - + Your ROM is encrypted. <br/>Please follow the guides to redump your <a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>game cartridges</a> or <a href='https://citra-emu.org/wiki/dumping-installed-titles/'>installed titles</a>. Votre ROM est chiffrée. <br/>Veuillez suivre les guides afin de redumper vos <a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>cartouches de jeu</a> ou <a href='https://citra-emu.org/wiki/dumping-installed-titles/'>titres installés</a>. - - + + Video Core Error Erreur du moteur graphique - + An error has occured. Please <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>see the log</a> for more details. Ensure that you have the latest graphics drivers for your GPU. Une erreur s'est produite. Veuillez <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>consulter les journaux</a>pour plus de détails. Faîtes en sorte d'avoir les derniers pilotes graphiques pour votre carte. - + You are running default Windows drivers for your GPU. You need to install the proper drivers for your graphics card from the manufacturer's website. Votre carte graphique utilise les pilotes Windows par défaut. Vous devez installer les pilotes adaptés à votre carte à partir du site du fabricant. - + Error while loading ROM! Erreur lors du chargement de la ROM! - + An unknown error occured. Please see the log for more details. Une erreur inconnue est survenue. Veuillez consulter le journal pour plus de détails. - + Start Démarrer - + Error Opening %1 Folder Erreur lors de l'ouverture du dossier %1 - - + + Folder does not exist! Le répertoire n'existe pas! - + Error Opening %1 Erreur lors de l'ouverture de %1 - + Select Directory Sélectionner un répertoire - - 3DS Executable - Fichier exécutable 3DS + + 3DS Executable (%1);;All Files (*.*) + %1 is an identifier for the 3DS executable file extensions. + Exécutable 3DS (%1);;Tous les fichiers (*.*) - - - All Files (*.*) - Tous les fichiers (*.*) - - - + Load File Charger un fichier - + Load Files Charger les fichiers - + 3DS Installation File (*.CIA*) Fichier d'installation 3DS (*.CIA*) - + + All Files (*.*) + Tous les fichiers (*.*) + + + %1 has been installed successfully. %1 a été installé avec succès. - + Unable to open File Impossible d'ouvrir le fichier - + Could not open %1 Impossible d'ouvrir %1 - + Installation aborted Installation annulée - + The installation of %1 was aborted. Please see the log for more details L'installation de %1 a été interrompue. Veuillez consulter le fichier log pour plus de détails. - + Invalid File Fichier invalide - + %1 is not a valid CIA %1 n'est pas un CIA valide - + Encrypted File Fichier encrypté - + %1 must be decrypted before being used with Citra. A real 3DS is required. %1 doit être décrypté avant de fonctionner avec Citra. Une véritable 3DS est requise. - + File not found Fichier non trouvé - + File "%1" not found Le fichier "%1" n'a pas été trouvé - - - + + + Continue Continuer - + Missing Citra Account Compte Citra absent - + You must link your Citra account to submit test cases.<br/>Go to Emulation &gt; Configure... &gt; Web to do so. Vous devez rattacher votre compte Citra pour soumettre des cas test.<br/>Allez sur Emulation &gt; Configurer... &gt; Web pour procéder. - - - + + Amiibo File (%1);; All Files (*.*) + Fichier Amiibo (%1);; Tous les fichiers (*.*) + + + + Load Amiibo + Charger un Amiibo + + + + + + Record Movie Enregistrer une vidéo - - + + To keep consistency with the RNG, it is recommended to record the movie from game start.<br>Are you sure you still want to record movies now? + Pour être cohérent avec le RNG, nous recommandons d'enregistrer votre fichier vidéo depuis le début du jeu<br>Etes-vous sûr de vouloir enregistrer la vidéo maintenant? + + + + Citra TAS Movie (*.ctm) Citra TAS Movie (*.ctm) - + Recording will start once you boot a game. L'enregistrement démarrera au lancement d'un jeu. - + The movie file you are trying to load was created on a different revision of Citra.<br/>Citra has had some changes during the time, and the playback may desync or not work as expected.<br/><br/>Are you sure you still want to load the movie file? Le fichier vidéo que vous essayez de charger a été crée sur une version différente de Citra.<br/>Citra a été modifié entre-temps, et la lecture peut être désynchronisée ou ne pas fonctionner comme prévu.<br/><br/>Etes-vous sûr de vouloir lancer le fichier vidéo? - + The movie file you are trying to load was recorded with a different game.<br/>The playback may not work as expected, and it may cause unexpected results.<br/><br/>Are you sure you still want to load the movie file? Le fichier vidéo que vous essayez de charger a été enregistré avec un jeu différent.<br/>La lecture peut ne pas fonctionner comme prévu, et provoquer des résultats inattendus.<br/><br/>Etes-vous sûr de vouloir lancer le fichier vidéo? - - + + The movie file you are trying to load is invalid.<br/>Either the file is corrupted, or Citra has had made some major changes to the Movie module.<br/>Please choose a different movie file and try again. Le fichier vidéo que vous essayez de charger n'est pas valide.<br/>Le fichier est corrompu, ou le module Vidéo de Citra a connu de profonds changements.<br/>Veuillez choisir un fichier vidéo différent puis réessayez. - + Revision Dismatch Incohérence de la version - + Game Dismatch Incohérence du jeu - - + + Invalid Movie File Fichier Vidéo invalide - + + Play Movie Jouer une vidéo - + + To keep consistency with the RNG, it is recommended to play the movie from game start.<br>Are you sure you still want to play movies now? + Pour être cohérent avec le RNG, nous recommandons de lire votre fichier vidéo depuis le début du jeu.<br>Etes-vous sûr de vouloir lire la vidéo maintenant? + + + Game Not Found Jeu non trouvé - + The movie you are trying to play is from a game that is not in the game list. If you own the game, please add the game folder to the game list and try to play the movie again. La vidéo que vous essayez de lire vient d'un jeu absent de la liste de jeux. Si vous possédez le jeu, veuillez ajouter le dossier du jeu à la liste de jeux et réessayez la lecture. - + Movie recording cancelled. Enregistrement de la vidéo annulé. - + Movie Saved Vidéo enregistrée - + The movie is successfully saved. La vidéo a été enregistrée avec succès. - + Speed: %1% / %2% Vitesse: %1% / %2% - + Speed: %1% Vitesse: %1% - + Game: %1 FPS Jeux: %1 FPS - + Frame: %1 ms Trame: %1 ms - + %1 is missing. Please <a href='https://citra-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-3ds-console/'>dump your system archives</a>.<br/>Continuing emulation may result in crashes and bugs. %1 est manquant. Merci de <a href='https://citra-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-3ds-console/'>dumper vos archives système</a>.<br/>Continuer l'émulation peut entrainer des plantages et des bugs. - + System Archive Not Found Archive système non trouvé - + Fatal Error Erreur fatale - + A fatal error occured. <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>Check the log</a> for details.<br/>Continuing emulation may result in crashes and bugs. Une erreur fatale s'est produite. <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>Consultez les journaux</a>pour plus de détails.<br/>Continuer l'émulation peut entrainer des plantages et des bugs. - + Abort Abandonner - - + + Citra Citra - + Would you like to exit now? Voulez-vous quitter maintenant? - + The game is still running. Would you like to stop emulation? Le jeu est en cours de fonctionnement. Voulez-vous arrêter l'émulation? - + Playback Completed Lecture terminée - + Movie playback completed. Lecture de la vidéo terminée. - + Citra %1 Citra %1 - + Citra %1| %2 Citra %1| %2 @@ -2799,37 +3015,67 @@ p, li { white-space: pre-wrap; } GameList - + + Name + Nom + + + + Compatibility + Compatibilité + + + + Region + Région + + + + File type + Type de fichier + + + + Size + Taille + + + Open Save Data Location Ouvrir l'emplacement des données de sauvegarde - + + Open Extra Data Location + Ouvrir un emplacement de données supplémentaire + + + Open Application Location Ouvrir l'emplacement de l'application - + Open Update Data Location Ouvrir l'emplacement des données de mise à jour - + Navigate to GameDB entry Naviguer jusqu'à l'entrée de la GameDB - + Scan Subfolders Scanner les sous-dossiers - + Remove Game Directory Supprimer ce répertoire de jeu - + Open Directory Location Ouvrir l'emplacement de ce répertoire @@ -2837,77 +3083,77 @@ p, li { white-space: pre-wrap; } GameListItemCompat - + Perfect Parfait - + Game functions flawless with no audio or graphical glitches, all tested functionality works as intended without any workarounds needed. Le jeu fonctionne parfaitement sans bug audio ni graphique, toutes les fonctionnalités testées marchent comme prévu sans besoin de correctif. - + Great Bien - + Game functions with minor graphical or audio glitches and is playable from start to finish. May require some workarounds. Le jeu fonctionne avec des bugs audio ou graphiques mineurs et est jouable du début à la fin. Peut nécessiter certains ajustements. - + Okay Ok - + Game functions with major graphical or audio glitches, but game is playable from start to finish with workarounds. Le jeu fonctionne avec des bugs audio ou graphiques majeurs, mais il est jouable du début à la fin avec des ajustements. - + Bad Mauvais - + Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches even with workarounds. Le jeu fonctionne, mais avec des bugs audio ou graphiques majeurs. Impossible de se rendre dans certaines zones à cause des bugs même avec des ajustements. - + Intro/Menu Intro/Menu - + Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start Screen. Le jeu est totalement injouable à cause de bugs graphiques ou audio majeurs. Impossible de dépasser l'écran titre. - + Won't Boot Ne se lance pas - + The game crashes when attempting to startup. Le jeu plante au démarrage. - + Not Tested Non testé - + The game has not yet been tested. Le jeu n'a pas encore été testé. @@ -2915,7 +3161,7 @@ Screen. GameListPlaceholder - + Double-click to add a new folder to the game list Faites un double-clic pour ajouter un nouveau dossier à la liste de jeux @@ -2923,27 +3169,27 @@ Screen. GameListSearchField - + of sur - + result résultat - + results résultats - + Filter: Filtre: - + Enter pattern to filter Entrer le motif de filtrage @@ -2951,23 +3197,23 @@ Screen. GraphicsBreakPointsWidget - + Pica Breakpoints Points d'arrêt PICA - - + + Emulation running Émulation en cours - + Resume Reprendre - + Emulation halted at breakpoint L'émulation s'est arrêtée au point d'arrêt @@ -3384,42 +3630,42 @@ Screen. Rafraîchir le hall - + Password Required to Join Mot de passe nécessaire pour devenir membre - + Password: Mot de passe: - + Room Name Nom du salon - + Preferred Game Jeu préféré - + Host Hôte - + Players Joueurs - + Refreshing Rafraîchissement - + Refresh List Rafraîchir la liste @@ -3442,220 +3688,245 @@ Screen. Fichiers récents - + + Amiibo + Amiibo + + + &Emulation &Émulation - + &View &Voir - + Debugging Débogguer - + Screen Layout Disposition de l'écran - + Movie Vidéo - + Multiplayer Multijoueurs - + &Help &Aide - + Load File... Charger un fichier... - + Install CIA... Installer un fichier CIA... - + Load Symbol Map... Charger Configuration des Boutons... - + E&xit A&rrêter - + &Start &Commencer - + &Pause &Pause - + &Stop &Stop - + FAQ FAQ - + About Citra À propos - + Single Window Mode Mode fenêtre unique - + Configure... Configurer... - + Display Dock Widget Headers Display Dock Widget Headers - + Show Filter Bar Montrer la barre des Filtres - + Show Status Bar Montrer la barre de Statut - + Select Game Directory... Sélectionnez un dossier... - + Selects a folder to display in the game list Sélectionnez un dossier pour apparaître dans la liste - + Create Pica Surface Viewer Créer une surface Pica - + Record Movie Enregistrer une vidéo - + Play Movie Jouer une vidéo - + Stop Recording / Playback Arrêter l'enregistrement / la lecture - + + Enable Frame Advancing + Activer l'avancement de trame + + + + Advance Frame + Avancer la trame + + + Browse Public Game Lobby Naviguer dans le hall de jeux publics - + Create Room Créer un salon - + Leave Room Quitter le salon - + Direct Connect to Room Connexion directe à un salon - + Show Current Room Afficher le salon actuel - + Fullscreen Plein écran - + Modify Citra Install Modifier l'installation de Citra - + Opens the maintenance tool to modify your Citra installation Lancez l'outil de maintenance pour modifier l'installation de Citra - + Default Par défaut - + Single Screen Un seul écran - + Large Screen Écran large - + Side by Side Côte à côte - + Swap Screens Permuter les écrans - + Check for Updates Rechercher les mises à jour - + Report Compatibility Faire un rapport de compatibilité - + Restart Redémarrer + + + Load... + Charger... + + + + Remove + Supprimer + MicroProfileDialog @@ -3681,23 +3952,23 @@ Screen. - + Connected Connecté - + Not Connected Non connecté - + Error Erreur - + Failed to announce the room to the public lobby. In order to host a room publicly, you must have a valid Citra account configured in Emulation -> Configure -> Web. If you do not want to publish a room in the public lobby, then select Unlisted instead. Debug Message: L'introduction du salon dans le hall public a échoué. Pour héberger un salon public, vous devez configurer un compte Citra valide dans Emulation -> Configurer... -> Web. Si vous ne souhaitez pas publier un salon dans le hall public, choisissez Non Listé à la place. @@ -3816,106 +4087,106 @@ Message de debug: %1 joue à %2 - - + + Invalid region Région Invalide - + Japan Japon - + North America Amérique du Nord - + Europe Europe - + Australia Australie - + China Chine - + Korea Corée - + Taiwan Taiwan - + Region free Dézoné - + Invalid Region Région Invalide - + Shift Maj - + Ctrl Ctrl - + Alt Alt - - + + [not set] [non défini] - + Hat %1 %2 - + Hat %1 %2 - + Axis %1%2 Axe %1%2 - + Button %1 Bouton %1 - - + + [unknown] [inconnu] - + [unused] [inutilisé] - - + + Axis %1 Axe %1 diff --git a/dist/languages/hu_HU.ts b/dist/languages/hu_HU.ts index 072e6d768..3b2153aba 100644 --- a/dist/languages/hu_HU.ts +++ b/dist/languages/hu_HU.ts @@ -70,45 +70,50 @@ p, li { white-space: pre-wrap; } BreakPointModel - + Pica command loaded Pica parancs betöltve - + Pica command processed Pica parancs feldolgozva - + Incoming primitive batch Bejövő primitív tétel - + Finished primitive batch Befejezett primitív tétel - + Vertex shader invocation Vertex Shader invokáció - + Incoming display transfer Bejövő megjelenítés átvitel - + GSP command processed GSP parancs feldolgozva - + Buffers swapped Pufferek cserélve + + + Unknown debug context event + + CalibrationConfigurationDialog @@ -171,13 +176,13 @@ p, li { white-space: pre-wrap; } Játék - - + + Block Player - + When you block a player, you will no longer receive chat messages from them.<br><br>Are you sure you would like to block %1? @@ -198,17 +203,17 @@ p, li { white-space: pre-wrap; } ClientRoomWindow - + Connected Kapcsolódva - + Disconnected Lecsatlakozva - + %1 (%2/%3 members) - connected %1 (%2/%3 members) - kapcsolva @@ -301,6 +306,26 @@ p, li { white-space: pre-wrap; } Thank you for your submission! Köszönjük a beküldést! + + + Submitting + + + + + Communication error + + + + + An error occured while sending the Testcase + + + + + Next + + ConfigureAudio @@ -340,9 +365,9 @@ p, li { white-space: pre-wrap; } - - - %1 % + + %1% + Volume percentage (e.g. 50%) @@ -606,50 +631,78 @@ p, li { white-space: pre-wrap; } ConfigureDialog - + Citra Configuration Citra Konfiguráció - + + + General Általános - + + + System Rendszer - + + + Input Bevitel - + + + Graphics Grafika - + + + Audio Hang - + + + Camera Kamera - + + + Debug Debug - + + + Web Web + + + + + UI + + + + + Controls + + ConfigureGeneral @@ -669,64 +722,54 @@ p, li { white-space: pre-wrap; } Kilépés megerősítése amikor az emuláció fut - - Interface language - Kezelőfelület nyelve - - - + Updates Frissítések - + Check for updates on start Frissítések keresése induláskor - + Silently auto update after closing Auto frissítés a háttérben bezárás után - + Emulation Emuláció - + Region: Régió: - + Auto-select Autó-kiválasztás - - Theme - Téma - - - - Theme: - Téma: - - - + Hotkeys Gyorsbillentyűk: - - <System> - <System> + + Reset All Settings + - - English - English + + Citra + + + + + Are you sure you want to <b>reset your settings</b> and close Citra? + @@ -1026,7 +1069,7 @@ p, li { white-space: pre-wrap; } - + Set Analog Stick Analóg Pad beállítása @@ -1067,11 +1110,44 @@ p, li { white-space: pre-wrap; } + Clear All + + + + Restore Defaults Visszaállítás Alapértelmezettre - + + + Clear + + + + + + [not set] + + + + + + Restore Default + + + + + Information + + + + + After pressing OK, first move your joystick horizontally, and then vertically. + + + + [press key] [nyomj meg egy gombot] @@ -1440,17 +1516,27 @@ p, li { white-space: pre-wrap; } - + + yyyy-MM-ddTHH:mm:ss + + + + + Play Coins: + + + + Console ID: Konzol ID: - + Regenerate Regenerálás - + System settings are available only when game is not running. A rendszer-beállítások csak akkor érhetőek el, amikor a játék nem fut. @@ -2120,22 +2206,120 @@ p, li { white-space: pre-wrap; } - - + + Console ID: 0x%1 Konzol ID: 0x%1 - + This will replace your current virtual 3DS with a new one. Your current virtual 3DS will not be recoverable. This might have unexpected effects in games. This might fail, if you use an outdated config savegame. Continue? Ez ki fogja cserélni a jelenlegi virtuális 3DS-edet egy újra. A jelenlegi virtuális 3DS-edetet ne lehet majd később visszaállítani. Ez lehet, hogy váratlan hatást okoz a játékokban. Ez lehet hogy nem fog sikerülni, ha egy elavult konfigurációs mentésfájlt használsz. Folytatod? - + Warning Figyelem + + ConfigureUi + + + Form + + + + + General + + + + + Interface language: + + + + + Theme: + + + + + Game List + + + + + Icon Size: + + + + + + None + + + + + Small (24x24) + + + + + Large (48x48) + + + + + Row 1 Text: + + + + + + File Name + + + + + + Full Path + + + + + + Title Name + + + + + + Title ID + + + + + Row 2 Text: + + + + + Hide Titles without Icon + + + + + <System> + + + + + English + + + ConfigureWeb @@ -2155,7 +2339,7 @@ p, li { white-space: pre-wrap; } - + Verify Ellenőrzés @@ -2215,48 +2399,48 @@ p, li { white-space: pre-wrap; } - + <a href='https://citra-emu.org/entry/telemetry-and-why-thats-a-good-thing/'><span style="text-decoration: underline; color:#039be5;">Learn more</span></a> <a href='https://citra-emu.org/entry/telemetry-and-why-thats-a-good-thing/'><span style="text-decoration: underline; color:#039be5;">Tudj meg többet</span></a> - - <a href='https://services.citra-emu.org/'><span style="text-decoration: underline; color:#039be5;">Sign up</span></a> - <a href='https://services.citra-emu.org/'><span style="text-decoration: underline; color:#039be5;">Regisztráció</span></a> + + <a href='https://profile.citra-emu.org/'><span style="text-decoration: underline; color:#039be5;">Sign up</span></a> + - + <a href='https://citra-emu.org/wiki/citra-web-service/'><span style="text-decoration: underline; color:#039be5;">What is my token?</span></a> <a href='https://citra-emu.org/wiki/citra-web-service/'><span style="text-decoration: underline; color:#039be5;">Mi a tokenem?</span></a> - - + + Telemetry ID: 0x%1 Telemetria ID: 0x%1 - + Username and token not verified Felhasználónév és token nincs leellenőrizve - + Username and token were not verified. The changes to your username and/or token have not been saved. A felhasználónév és token nem lett leellenőrizve. A felhasználóneveddel és/vagy a tokeneddel történt változások nem lettek elmentve. - + Verifying Ellenőrzés - + Verification failed Ellenőrzés sikertelen - + Verification failed. Check that you have entered your username and token correctly, and that your internet connection is working. Ellenőrzés sikertelen. Ellenőrizd le hogy jól írtad-e a felhasználóneved és tokenedet, és hogy működik-e az internetkapcsolatod. @@ -2317,12 +2501,12 @@ p, li { white-space: pre-wrap; } DirectConnectWindow - + Connecting Kapcsolódás - + Connect Kapcsolás @@ -2330,413 +2514,445 @@ p, li { white-space: pre-wrap; } GMainWindow - + <a href='https://citra-emu.org/entry/telemetry-and-why-thats-a-good-thing/'>Anonymous data is collected</a> to help improve Citra. <br/><br/>Would you like to share your usage data with us? - + Telemetry - - + + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a 3DS. Jelenlegi emulációs sebesség. A 100%-nál nagyobb vagy kisebb értékek azt mutatják, hogy az emuláció egy 3DS-nél gyorsabban vagy lassabban fut. - - + + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. Mennyi képkocka/másodpercet jelez a játék jelenleg. Ez játékról játékra és helyszínről helyszínre változik. - - + + Time taken to emulate a 3DS frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. Mennyi idő szükséges egy 3DS képkocka emulálásához, képkocka-limit vagy V-Syncet leszámítva. Teljes sebességű emulációnál ez maximum 16.67 ms-nek kéne lennie. - + Clear Recent Files - + F9 F9 - + F10 F10 - + CTRL+F CTRL+F - + Update Available - + An update is available. Would you like to install it now? - + No Update Found - + No update is found. - - + + OpenGL 3.3 Unsupported + + + + + Your GPU may not support OpenGL 3.3, or you do not have the latest graphics driver. + + + + + Invalid ROM Format - - + + Your ROM format is not supported.<br/>Please follow the guides to redump your <a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>game cartridges</a> or <a href='https://citra-emu.org/wiki/dumping-installed-titles/'>installed titles</a>. - + ROM Corrupted - + Your ROM is corrupted. <br/>Please follow the guides to redump your <a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>game cartridges</a> or <a href='https://citra-emu.org/wiki/dumping-installed-titles/'>installed titles</a>. - + ROM Encrypted - + Your ROM is encrypted. <br/>Please follow the guides to redump your <a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>game cartridges</a> or <a href='https://citra-emu.org/wiki/dumping-installed-titles/'>installed titles</a>. - - + + Video Core Error - + An error has occured. Please <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>see the log</a> for more details. Ensure that you have the latest graphics drivers for your GPU. - + You are running default Windows drivers for your GPU. You need to install the proper drivers for your graphics card from the manufacturer's website. - + Error while loading ROM! Hiba a ROM betöltése közben! - + An unknown error occured. Please see the log for more details. Egy ismeretlen hiba történt. Kérjük olvasd el a naplót több részletért. - + Start Indít - + Error Opening %1 Folder Hiba %1 Mappa Megnyitásában - - + + Folder does not exist! A mappa nem létezik! - + Error Opening %1 Hiba Indulás %1 - + Select Directory Könyvtár Kiválasztása - - 3DS Executable - 3DS Végrehajtható + + 3DS Executable (%1);;All Files (*.*) + %1 is an identifier for the 3DS executable file extensions. + - - - All Files (*.*) - Minden fájl (*.*) - - - + Load File Fájl Betöltése - + Load Files Fájlok Betöltése - + 3DS Installation File (*.CIA*) 3DS Telepítési Fájl (*.CIA*) - + + All Files (*.*) + Minden fájl (*.*) + + + %1 has been installed successfully. %1 sikeresen fel lett telepítve. - + Unable to open File A fájl megnyitása sikertelen - + Could not open %1 Nem lehet megnyitni: %1 - + Installation aborted Telepítés megszakítva - + The installation of %1 was aborted. Please see the log for more details %1 telepítése meg lett szakítva. Kérjük olvasd el a naplót több részletért. - + Invalid File Ismeretlen Fájl - + %1 is not a valid CIA %1 nem érvényes CIA - + Encrypted File Titkosított Fájl - + %1 must be decrypted before being used with Citra. A real 3DS is required. %1 vissza kell legyen fejtve mielőtt a Citrával lehetne használni. Egy igazi 3DS-re is szükség van. - + File not found A fájl nem található - + File "%1" not found Fájl "%1" nem található - - - + + + Continue Folytatás - + Missing Citra Account Hiányzó Citra Fiók - + You must link your Citra account to submit test cases.<br/>Go to Emulation &gt; Configure... &gt; Web to do so. - - - + + Amiibo File (%1);; All Files (*.*) + + + + + Load Amiibo + + + + + + + Record Movie - - + + To keep consistency with the RNG, it is recommended to record the movie from game start.<br>Are you sure you still want to record movies now? + + + + + Citra TAS Movie (*.ctm) - + Recording will start once you boot a game. - + The movie file you are trying to load was created on a different revision of Citra.<br/>Citra has had some changes during the time, and the playback may desync or not work as expected.<br/><br/>Are you sure you still want to load the movie file? - + The movie file you are trying to load was recorded with a different game.<br/>The playback may not work as expected, and it may cause unexpected results.<br/><br/>Are you sure you still want to load the movie file? - - + + The movie file you are trying to load is invalid.<br/>Either the file is corrupted, or Citra has had made some major changes to the Movie module.<br/>Please choose a different movie file and try again. - + Revision Dismatch - + Game Dismatch - - + + Invalid Movie File - + + Play Movie - + + To keep consistency with the RNG, it is recommended to play the movie from game start.<br>Are you sure you still want to play movies now? + + + + Game Not Found - + The movie you are trying to play is from a game that is not in the game list. If you own the game, please add the game folder to the game list and try to play the movie again. - + Movie recording cancelled. - + Movie Saved - + The movie is successfully saved. - + Speed: %1% / %2% Sebesség: %1% / %2% - + Speed: %1% Sebesség: %1% - + Game: %1 FPS Játék: %1 FPS - + Frame: %1 ms Képkocka: %1 ms - + %1 is missing. Please <a href='https://citra-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-3ds-console/'>dump your system archives</a>.<br/>Continuing emulation may result in crashes and bugs. - + System Archive Not Found Rendszerarchívum Nem Található - + Fatal Error Kritikus Hiba - + A fatal error occured. <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>Check the log</a> for details.<br/>Continuing emulation may result in crashes and bugs. - + Abort - - + + Citra Citra - + Would you like to exit now? - + The game is still running. Would you like to stop emulation? - + Playback Completed - + Movie playback completed. - + Citra %1 Citra %1 - + Citra %1| %2 Citra %1| %2 @@ -2799,37 +3015,67 @@ p, li { white-space: pre-wrap; } GameList - + + Name + + + + + Compatibility + + + + + Region + + + + + File type + + + + + Size + + + + Open Save Data Location Mentésadat Helyének Megnyitása - + + Open Extra Data Location + + + + Open Application Location Alkalmazás Helyének Megnyitása - + Open Update Data Location Frissítésadat Helyének Megnyitása - + Navigate to GameDB entry GameDB helyéhez menni - + Scan Subfolders - + Remove Game Directory - + Open Directory Location @@ -2837,77 +3083,77 @@ p, li { white-space: pre-wrap; } GameListItemCompat - + Perfect - + Game functions flawless with no audio or graphical glitches, all tested functionality works as intended without any workarounds needed. - + Great - + Game functions with minor graphical or audio glitches and is playable from start to finish. May require some workarounds. - + Okay - + Game functions with major graphical or audio glitches, but game is playable from start to finish with workarounds. - + Bad - + Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches even with workarounds. - + Intro/Menu - + Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start Screen. - + Won't Boot - + The game crashes when attempting to startup. - + Not Tested - + The game has not yet been tested. @@ -2915,7 +3161,7 @@ Screen. GameListPlaceholder - + Double-click to add a new folder to the game list @@ -2923,27 +3169,27 @@ Screen. GameListSearchField - + of - + result - + results - + Filter: - + Enter pattern to filter @@ -2951,23 +3197,23 @@ Screen. GraphicsBreakPointsWidget - + Pica Breakpoints Pica Töréspontok - - + + Emulation running Emuláció fut - + Resume Folytatás - + Emulation halted at breakpoint Emuláció a törésponton leállt @@ -3384,42 +3630,42 @@ Screen. Lobby Frissítése - + Password Required to Join A Csatlakozáshoz Szükséges Jelszó - + Password: Jelszó: - + Room Name Szoba Neve - + Preferred Game Preferált Játék - + Host Gazda - + Players Játékosok - + Refreshing Frissítés - + Refresh List Lista Frissítése @@ -3442,220 +3688,245 @@ Screen. Legutóbbi Fájlok - + + Amiibo + + + + &Emulation &Emuláció - + &View &Nézet - + Debugging Hibakeresés - + Screen Layout Képernyő Elrendezése - + Movie - + Multiplayer Többjátékos - + &Help &Segítség - + Load File... Fájl Betöltése... - + Install CIA... CIA Telepítése... - + Load Symbol Map... Szimbólumtérkép Betöltése... - + E&xit &Kilépés - + &Start &Indítás - + &Pause &Szünet - + &Stop &Megállítás - + FAQ Gyakori Kérdések - + About Citra A Citráról - + Single Window Mode Egyablakos Mód - + Configure... Konfiguráció... - + Display Dock Widget Headers Dokk Modul Fejlécek Megjelenítése - + Show Filter Bar Filtersáv Megjelenítése - + Show Status Bar Állapotsáv Megjelenítése - + Select Game Directory... Játékkönyvtár Kiválasztása... - + Selects a folder to display in the game list Egy mappát választ ki, amely megjelenik a játéklistában - + Create Pica Surface Viewer Pica Felülnézegető Létrehozása - + Record Movie - + Play Movie - + Stop Recording / Playback - + + Enable Frame Advancing + + + + + Advance Frame + + + + Browse Public Game Lobby Nyilvános Játék Lobby Böngészése - + Create Room Szoba Létrehozása - + Leave Room Szoba Elhagyása - + Direct Connect to Room Közvetlen Kapcsolódás Szobához - + Show Current Room Jelenlegi Szoba Mutatása - + Fullscreen Teljes Képernyő - + Modify Citra Install Citra Telepítés Módosítása - + Opens the maintenance tool to modify your Citra installation Megnyitja a karbantartási eszközt, amivel a Citra telepítést módosíthatod - + Default Alapértelmezett - + Single Screen Egy Képernyő - + Large Screen Nagy Képernyő - + Side by Side Egymás Mellett - + Swap Screens Képernyők Cseréje - + Check for Updates Frissítések Keresése - + Report Compatibility Kompatibilitás Jelentése - + Restart + + + Load... + + + + + Remove + + MicroProfileDialog @@ -3681,23 +3952,23 @@ Screen. - + Connected Kapcsolódva - + Not Connected Nincs Kapcsolódva - + Error Hiba - + Failed to announce the room to the public lobby. In order to host a room publicly, you must have a valid Citra account configured in Emulation -> Configure -> Web. If you do not want to publish a room in the public lobby, then select Unlisted instead. Debug Message: A szoba bejelentése a nyilvános lobbyba sikertelen. Ahhoz, hogy tudj egy szobát hosztolni nyilvánosan, szükséged lesz egy hiteles Citra fiókra, az Emuláció -> Konfigurálás -> Web helyen. Ha nem szeretnél fiókot hosztolni a nyilvános lobbyban, akkor válaszd a Nem listázott menüpontot. @@ -3816,106 +4087,106 @@ Debug Üzenet: %1 játszik ezzel: %2 - - + + Invalid region Érvénytelen régió - + Japan Japán - + North America Észak-Amerika - + Europe Európa - + Australia Ausztrália - + China Kína - + Korea Korea - + Taiwan Tajvan - + Region free Régiómentes - + Invalid Region Érvénytelen Régió - + Shift Shift - + Ctrl Ctrl - + Alt Alt - - + + [not set] [nincs megadva] - + Hat %1 %2 - + Axis %1%2 - + Button %1 - - + + [unknown] [ismeretlen] - + [unused] [nem használt] - - + + Axis %1 diff --git a/dist/languages/id.ts b/dist/languages/id.ts index 1af80cfcc..5f5fae12f 100644 --- a/dist/languages/id.ts +++ b/dist/languages/id.ts @@ -54,7 +54,7 @@ p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Ubuntu'; font-size:11pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MS Shell Dlg 2'; font-size:12pt;">Citra adalah emulator 3DS gratis dan open source dengan lisensi GPLv2.0 atau versi setelahnya.</span></p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'MS Shell Dlg 2'; font-size:8pt;"><br /></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MS Shell Dlg 2'; font-size:12pt;">Software ini seharusnya tidak digunakan untuk memainkan permainan yang didapatkan secara illegal.</span></p></body></html> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MS Shell Dlg 2'; font-size:12pt;">Software ini seharusnya tidak digunakan untuk memainkan permainan games yang didapatkan secara illegal.</span></p></body></html> @@ -70,77 +70,82 @@ p, li { white-space: pre-wrap; } BreakPointModel - + Pica command loaded Perintah Pica dimuat - + Pica command processed Perintah Pica terproses - + Incoming primitive batch Batch primitif masuk - + Finished primitive batch Batch primitif selesai - + Vertex shader invocation - Memanggil shader Vertex + Permintaan Shader Vertex - + Incoming display transfer Transfer tampilan masuk - + GSP command processed Perintah GSP terproses - + Buffers swapped Buffer ditukar + + + Unknown debug context event + + CalibrationConfigurationDialog Communicating with the server... - + Berkomunikasi dengan server... Cancel - + Batalkan Touch the top left corner <br>of your touchpad. - + Sentuh bagian pojok kiri atas <br>dari touchpad anda. Now touch the bottom right corner <br>of your touchpad. - + Sekarang sentuh bagian pojok kanan bawah<br>dari touchpad anda. Configuration completed! - + Pengaturan Telah Selesai OK - + OK @@ -148,7 +153,7 @@ p, li { white-space: pre-wrap; } Room Window - Jendela Ruang + Jendela Grup @@ -171,15 +176,15 @@ p, li { white-space: pre-wrap; } Permainan - - + + Block Player Blokir Pemain - + When you block a player, you will no longer receive chat messages from them.<br><br>Are you sure you would like to block %1? - Ketika Anda memblokir seorang pemain, Anda tidak lagi akan menerima pesan dari mereka. <br><br>Apakah Anda yakin ingin memblokir pemain %1? + Ketika Anda memblokir seorang pemain, Anda tidak dapat lagi menerima pesan chat dari mereka. <br><br>Apakah Anda yakin ingin memblokir pemain %1? @@ -187,28 +192,28 @@ p, li { white-space: pre-wrap; } Room Window - Jendela Ruang + Jendela Grup Leave Room - Tinggalkan Ruangan + Tinggalkan Grup ClientRoomWindow - + Connected Terhubung - + Disconnected Terputus - + %1 (%2/%3 members) - connected %1 (%2/%3 anggota) - terhubung @@ -229,7 +234,7 @@ p, li { white-space: pre-wrap; } <html><head/><body><p><span style=" font-size:10pt;">Should you choose to submit a test case to the </span><a href="https://citra-emu.org/game/"><span style=" font-size:10pt; text-decoration: underline; color:#0000ff;">Citra Compatibility List</span></a><span style=" font-size:10pt;">, The following information will be collected and displayed on the site:</span></p><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Hardware Information (CPU / GPU / Operating System)</li><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Which version of Citra you are running</li><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">The connected Citra account</li></ul></body></html> - <html><head/><body><p><span style=" font-size:10pt;">Jika kamu memilih untuk memberikan test case ke </span><a href="https://citra-emu.org/game/"><span style=" font-size:10pt; text-decoration: underline; color:#0000ff;">Daftar Kompatibilitas Citra</span></a><span style=" font-size:10pt;">, Informasi berikut akan dikumpulkan dan ditampilkan pada situs: </span></p><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Informasi perangkat keras (CPU / GPU / Sistem Operasi) </li><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Versi Citra yang Anda jalankan </li><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Akun Citra yang terhubung </li></ul></body></html> + <html><head/><body><p><span style=" font-size:10pt;">Jika kamu memilih untuk memberikan test case ke </span><a href="https://citra-emu.org/game/"><span style=" font-size:10pt; text-decoration: underline; color:#0000ff;">Daftar Kompatibilitas Citra</span></a><span style=" font-size:10pt;">, Informasi berikut ini akan dikumpulkan dan ditampilkan pada situs: </span></p><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Informasi perangkat keras (CPU / GPU / Sistem Operasi) </li><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Versi Citra apa saja yang Anda jalankan </li><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Akun Citra yang terhubung </li></ul></body></html> @@ -239,7 +244,7 @@ p, li { white-space: pre-wrap; } <html><head/><body><p>Game functions flawlessly with no audio or graphical glitches.</p></body></html> - <html><head/><body><p> Permainan berfungsi dengan mulus tanpa kecacatan audiovisual </p></body></html> + <html><head/><body><p> Permainan berfungsi dengan lancar tanpa kecacatan audio atau gambar </p></body></html> @@ -249,17 +254,17 @@ p, li { white-space: pre-wrap; } <html><head/><body><p>Game functions with minor graphical or audio glitches and is playable from start to finish. May require some workarounds.</p></body></html> - <html><head/><body><p>Permainan berfungsi dengan sedikit kecacatan audiovisual dan dapat dimainkan dari awal hingga akhir. Mungkin membutuhkan beberapa solusi tambahan. </p></body></html> + <html><head/><body><p>Permainan berfungsi dengan sedikit kecacatan audio atau gambar dan dapat dimainkan dari awal hingga akhir. Mungkin membutuhkan beberapa perbaikan tambahan. </p></body></html> Okay - Baik + Oke <html><head/><body><p>Game functions with major graphical or audio glitches, but game is playable from start to finish with workarounds.</p></body></html> - <html><head/><body><p>Permainan berfungsi dengan banyak kecacatan audiovisual, tapi permainan dapat dimainkan dari awal hingga akhir dengan solusi tambahan. </p></body></html> + <html><head/><body><p>Permainan berfungsi dengan banyak kecacatan audi atau gambar, tapi permainan dapat dimainkan dari awal hingga akhir dengan perbaikan tambahan. </p></body></html> @@ -269,7 +274,7 @@ p, li { white-space: pre-wrap; } <html><head/><body><p>Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches even with workarounds.</p></body></html> - <html><head/><body><p>Permainan berfungsi, tapi dengan banyak kecacatan audiovisual. Tidak dapat berjalan di beberapa daerah karena cacat bahkan dengan solusi tambahan. </p></body></html> + <html><head/><body><p>Permainan berfungsi, tapi dengan banyak kecacatan besar audio dan gambar. Tidak dapat berjalan di beberapa daerah karena cacat bahkan dengan perbaikan tambahan. </p></body></html> @@ -279,28 +284,48 @@ p, li { white-space: pre-wrap; } <html><head/><body><p>Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start Screen.</p></body></html> - <html><head/><body><p>Permainan benar-benar tidak bisa dimainkan karena banyak kecacatan audiovisual. Tidak dapat melewati Layar Mulai.</p></body></html> + <html><head/><body><p>Permainan secara total tidak bisa dimainkan karena banyak kecacatan besar audio dan gambar. Tidak dapat melewati Layar Mulai.</p></body></html> Won't Boot - Tidak mau mulai + Tidak Mau Memulai <html><head/><body><p>The game crashes when attempting to startup.</p></body></html> - <html><head/><body><p>Permainan berhenti ketika mencoba untuk dimulai. </p></body></html> + <html><head/><body><p>Permainan ini tiba-tiba berhenti ketika mencoba untuk dimulai. </p></body></html> <html><head/><body><p>Independent of speed or performance, how well does this game play from start to finish on this version of Citra?</p></body></html> - <html><head/><body><p>Terlepas dari kelajuan atau performa, bagaimana permainan ini berjalan dari awal hingga akhir di versi Citra ini?</p></body></html> + <html><head/><body><p>Terlepas dari kelajuan atau performa, seberapa baik permainan ini berjalan dari awal hingga akhir di versi Citra ini?</p></body></html> Thank you for your submission! Terima kasih atas masukannya! + + + Submitting + + + + + Communication error + + + + + An error occured while sending the Testcase + + + + + Next + + ConfigureAudio @@ -312,17 +337,17 @@ p, li { white-space: pre-wrap; } Output Engine: - Output Engine: + Mesin Pengeluaran: This post-processing effect adjusts audio speed to match emulation speed and helps prevent audio stutter. This however increases audio latency. - Pasca-pemrosesan ini dilakukan untuk menyesuaikan kelajuan audio agar sesuai dengan kelajuan emulasi dan mencegah terjadinya gagap audio. Namun proses ini meningkatkan latensi audio. + Efek pasca-pemrosesan ini dilakukan untuk menyesuaikan kecepatan audio agar sesuai dengan kecepatan emulasi dan mencegah terjadinya audio stutter. Namun proses ini meningkatkan latensi audio. Enable audio stretching - Aktifkan peregangan audio + Aktifkan audio stretching @@ -340,10 +365,10 @@ p, li { white-space: pre-wrap; } 0 % - - - %1 % - %1 % + + %1% + Volume percentage (e.g. 50%) + %1% @@ -362,7 +387,7 @@ p, li { white-space: pre-wrap; } Select the camera to configure - Pilih kamera untuk diatur + Pilih kamera untuk di atur @@ -404,7 +429,7 @@ p, li { white-space: pre-wrap; } Select the position of camera to configure - Pilih posisi kamera untuk diatur + Pilih posisi kamera untuk di atur @@ -430,7 +455,7 @@ p, li { white-space: pre-wrap; } Select where the image of the emulated camera comes from. It may be an image or a real camera. - Pilih darimana gambar dari kamera yang diemulasi datang. Bisa dari gambar atau kamera asli. + Pilih di mana gambar dari kamera yang di emulasi datang berasal. Bisa dari gambar atau kamera asli. @@ -455,7 +480,7 @@ p, li { white-space: pre-wrap; } File: - Berkas: + File: @@ -492,7 +517,7 @@ p, li { white-space: pre-wrap; } None - Tidak ada + Tidak Ada @@ -512,17 +537,17 @@ p, li { white-space: pre-wrap; } Select an image file every time before the camera is loaded - Pilih berkas gambar setiap kali sebelum kamera dimuat + Pilih file gambar setiap kali sebelum kamera di muat Prompt before load - Tampilkan sebelum dimuat + Tampilkan sebelum di muat Preview - Pratinjau + Peninjauan @@ -532,7 +557,7 @@ p, li { white-space: pre-wrap; } Click to preview - Klik untuk pratinjau + Klik untuk meninjau @@ -542,12 +567,12 @@ p, li { white-space: pre-wrap; } Supported image files (%1) - Berkas gambar yang disupport (%1) + File gambar yang di dukung (%1) Open File - Buka Berkas + Buka File @@ -585,7 +610,7 @@ p, li { white-space: pre-wrap; } Show Log Console (Windows Only) - Tampilkan Log Konsol (Hanya Jendela) + Tampilkan Log Konsol (Hanya Jendela Windows) @@ -595,61 +620,89 @@ p, li { white-space: pre-wrap; } Miscellaneous - Lain-lain + Tambahan Lain-lain Enable CPU JIT - Aktifkan JIT CPU + Aktifkan CPU JIT ConfigureDialog - + Citra Configuration Konfigurasi Citra - + + + General Umum - + + + System Sistem - + + + Input - Input + Masukan - + + + Graphics Grafis - + + + Audio Audio - + + + Camera Kamera - + + + Debug Debug - + + + Web Web + + + + + UI + + + + + Controls + Kontrol + ConfigureGeneral @@ -669,64 +722,54 @@ p, li { white-space: pre-wrap; } Konfirmasi keluar saat emulasi berjalan - - Interface language - Bahasa Tampilan - - - + Updates Pembaruan - + Check for updates on start Periksa pembaruan ketika memulai - + Silently auto update after closing Otomatis memperbarui setelah menutup - + Emulation Emulasi - + Region: - Region: + Wilayah: - + Auto-select - Otomatis + Otomatis Pilih - - Theme - Tema - - - - Theme: - Tema: - - - + Hotkeys - Hotkey + Hotkeys - - <System> - <System> + + Reset All Settings + - - English - Inggris + + Citra + + + + + Are you sure you want to <b>reset your settings</b> and close Citra? + @@ -765,7 +808,7 @@ p, li { white-space: pre-wrap; } <html><head/><body><p>Use OpenGL to accelerate rendering.</p><p>Disable to debug graphics-related problem.</p></body></html> - <html><head/><body><p>Gunakan OpenGL untuk mengakselerasi proses render.</p><p>Nonaktifkan untuk men-debug isu terkait grafis</p></body></html> + <html><head/><body><p>Gunakan OpenGL untuk mempercepat proses render.</p><p>Nonaktifkan untuk men-debug isu terkait grafis</p></body></html> @@ -775,12 +818,12 @@ p, li { white-space: pre-wrap; } Internal Resolution - Resolusi Internal + Resolusi Bawaan Auto (Window Size) - Otomatis (Ukuran Jendela) + Otomatis (Ukuran Jendela Window) @@ -835,27 +878,27 @@ p, li { white-space: pre-wrap; } <html><head/><body><p>Use OpenGL to accelerate shader emulation.</p><p>Requires a relatively powerful GPU for better performance.</p></body></html> - <html><head/><body><p>Gunakan OpenGL untuk mengakselerasi emulasi shader</p><p>Membutuhkan GPU yang relatif kuat untuk performa yang lebih baik.</p></body></html> + <html><head/><body><p>Gunakan OpenGL untuk mempercepat emulasi shader</p><p>Membutuhkan GPU yang relatif kuat untuk performa yang lebih baik.</p></body></html> Enable Hardware Shader - Gunakan Shader Hardware + Gunakan Hardware Shader <html><head/><body><p>Correctly handle all edge cases in multiplication operation in shaders. </p><p>Some games requires this to be enabled for the hardware shader to render properly.</p><p>However this would reduce performance in most games.</p></body></html> - <html><head/><body><p>Menangani segala kasus tepian dalam operasi perkalian didalam shader secara tepat.</p><p>Beberapa permainan membutuhkan fitur ini untuk merender secara benar.</p><p>Namun fitur ini akan mengurangi performa dalam banyak permainan.</p></body></html> + <html><head/><body><p>Menangani semua kasus tepian dalam operasi penggandaan dalam shader secara tepat.</p><p>Beberapa game membutuhkan fitur ini di aktifkan untuk merender hardware shader secara benar.</p><p>Namun fitur ini akan mengurangi performa dalam kebanyakan game.</p></body></html> Accurate Multiplication - Perkalian Akurat + Penggandaan Akurat <html><head/><body><p>Force to fall back to software shader emulation when geometry shaders are used. </p><p>Some games require this to be enabled for the hardware shader to render properly.</p><p>However this might reduce performance in some games</p></body></html> - <html><head/><body><p>Paksa kembali ke emulasi shader software ketika shader geometri digunakan. </p><p>Beberapa permainan membutuhkan ini untuk digunakan agar shader hardware bekerja dengan baik.</p><p>Namun ini dapat mengakibatkan berkurangnya performa dalam beberapa permainan</p></body></html> + <html><head/><body><p>Paksa kembalikan kepada emulasi shader software ketika shader geometri digunakan. </p><p>Beberapa permainan membutuhkan filtur ini untuk di aktifkan agar hardware shader bekerja dengan baik.</p><p>Namun ini dapat mengakibatkan berkurangnya performa dalam beberapa game</p></body></html> @@ -865,12 +908,12 @@ p, li { white-space: pre-wrap; } <html><head/><body><p>Use the JIT engine instead of the interpreter for software shader emulation. </p><p>Enable this for better performance.</p></body></html> - <html><head/><body><p>Gunakan JIT sebagai ganti interperter untuk emulasi shader software.</p><p>Gunakan ini untuk performa yang lebih baik.</p></body></html> + <html><head/><body><p>Gunakan JIT Engine sebagai ganti interperter untuk emulasi shader software.</p><p>Gunakan ini untuk performa yang lebih baik.</p></body></html> Enable Shader JIT - Gunakan Shader JIT + Aktifkan Shader JIT @@ -880,7 +923,7 @@ p, li { white-space: pre-wrap; } Enable Stereoscopic 3D - Gunakan Stereoscopic 3D + Aktifkan Stereoskopik 3D @@ -905,7 +948,7 @@ p, li { white-space: pre-wrap; } Side by Side - Bersebelahan + Saling Bersebelahan @@ -920,12 +963,12 @@ p, li { white-space: pre-wrap; } Hardware Shader Warning - Peringatan Shader Perangkat Keras + Peringatan Hardware Shader Hardware Shader support is broken on macOS, and will cause graphical issues like showing a black screen.<br><br>The option is only there for test/development purposes. If you experience graphical issues with Hardware Shader, please turn it off. - Shader Perangkat Keras tidak berfungsi di macOS, dan akan menyebabkan berbagai masalah pada tampilan seperti layar hitam.<br><br>Pilihan tersebut ada hanya untuk tujuan pengetesan/pengembangan. Apabila Anda mengalami masalah pada tampilan ketika Shader Perangkat Keras menyala, tolong matikan fitur tersebut. + Dukungan Hardware Shader tidak berfungsi di macOS, dan akan menyebabkan berbagai masalah pada tampilan seperti layar berwarna hitam.<br><br>Pilihan tersebut ada hanya untuk tujuan pengujian/pengembangan. Jika Anda mengalami masalah pada tampilan dengan Hardware Shader, tolong matikan fitur tersebut. @@ -938,7 +981,7 @@ p, li { white-space: pre-wrap; } Face Buttons - Face Buttons + Tombol Face Buttons @@ -996,7 +1039,7 @@ p, li { white-space: pre-wrap; } Shoulder Buttons - Shoulder Buttons + Tombol Shoulder Buttons @@ -1021,14 +1064,14 @@ p, li { white-space: pre-wrap; } Circle Pad - Circle Pad + Tombol Circle Pad - + Set Analog Stick - Set Analog Stick + Tentukan Analog Stick @@ -1043,12 +1086,12 @@ p, li { white-space: pre-wrap; } Start: - Start: + Mulai: Select: - Select: + Pilih: @@ -1058,20 +1101,53 @@ p, li { white-space: pre-wrap; } Circle Mod: - Circle Mod: + Tombol Circle Mod: Motion / Touch... - + Motion/Touch... - Restore Defaults - Kembali ke Default + Clear All + Bersihkan Semua - + + Restore Defaults + Kembalikan ke kondisi Default + + + + + Clear + Bersihkan + + + + + [not set] + [belum di tentukan] + + + + + Restore Default + Kembalikan Ke Kondisi Default + + + + Information + Informasi + + + + After pressing OK, first move your joystick horizontally, and then vertically. + Setelah menekan tombol OK, pindahkan joystick anda secara horizontal kemudian secara vertikal. + + + [press key] [tekan tombol] @@ -1081,165 +1157,165 @@ p, li { white-space: pre-wrap; } Configure Motion / Touch - + Atur Motion/Touch Motion - + Gerakan Motion Provider: - + Penyedian Gerakan: Sensitivity: - + Sensitivitas: Touch - + Sentuhan Touch Provider: - + Penyedia Sentuhan: Calibration: - + Penyesuaian (100, 50) - (1800, 850) - + (100,50)-(1800,850) Configure - + Pengaturan CemuhookUDP Config - + Pengaturan CemuhookUDP You may use any Cemuhook compatible UDP input source to provide motion and touch input. - + Kamu dapat menggunakan Cemuhook apa saja yang cocok dengan sumber input UDP untuk menyediakan input motion dan touch. Server: - + Server Port: - + Port: Pad: - + Pad: Pad 1 - + Pad 1 Pad 2 - + Pad 2 Pad 3 - + Pad 3 Pad 4 - + Pad 4 Learn More - + Pelajari Lebih Banyak Test - + Tes Mouse (Right Click) - + Mouse (Klik Kanan) CemuhookUDP - + CemuhookUDP Emulator Window - + Jendela Window Emulator <a href='https://citra-emu.org/wiki/using-a-controller-or-android-phone-for-motion-or-touch-input'><span style="text-decoration: underline; color:#039be5;">Learn More</span></a> - + <a href='https://citra-emu.org/wiki/using-a-controller-or-android-phone-for-motion-or-touch-input'><span style="text-decoration: underline; color:#039be5;">Pelajari Lebih Banyak</span></a> Testing - + Pengujian Configuring - + Mengatur Test Successful - + Pengujian Berhasil Successfully received data from the server. - + Berhasil Menerima Data Dari Server. Test Failed - + Pengujian Gagal Could not receive valid data from the server.<br>Please verify that the server is set up correctly and the address and port are correct. - + Tidak bisa menerima data valid dari server <br>Tolong pastikan bahwa server telah di atur dengan benar dan alamat serta port-nya sudah benar. Citra - + Citra UDP Test or calibration configuration is in progress.<br>Please wait for them to finish. - + Pengaturan pengujian atau penyesuaian UDP sedang dalam proses.<br>Tolong tunggu sampai selesai. @@ -1257,7 +1333,7 @@ p, li { white-space: pre-wrap; } Username - Username + Nama User @@ -1332,7 +1408,7 @@ p, li { white-space: pre-wrap; } Note: this can be overridden when region setting is auto-select - Catatan: ini akan diganti bila setting region adalah otomatis + Catatan: ini bisa di lewati bila pengaturan region-nya otomatis di pilih @@ -1347,7 +1423,7 @@ p, li { white-space: pre-wrap; } French (français) - Perancis (français) + Prancis (français) @@ -1367,7 +1443,7 @@ p, li { white-space: pre-wrap; } Simplified Chinese (简体中文) - Cina Disederhanakan (简体中文) + Cina Yang Di Sederhanakan (简体中文) @@ -1422,37 +1498,47 @@ p, li { white-space: pre-wrap; } Clock - + Jam System Clock - + Jam Sistem Fixed Time - + Waktu Tetap Startup time + Waktu Mulai + + + + yyyy-MM-ddTHH:mm:ss - + + Play Coins: + Koin Permainan: + + + Console ID: ID Konsol: - + Regenerate - Perbarui + Perbaharui - + System settings are available only when game is not running. - Pengaturan sistem hanya tersedia jika game sedang tidak berjalan. + Pengaturan sistem hanya tersedia ketika game sedang tidak berjalan. @@ -1482,7 +1568,7 @@ p, li { white-space: pre-wrap; } Bahamas - Bahamas + Bahama @@ -1492,7 +1578,7 @@ p, li { white-space: pre-wrap; } Belize - Belise + Belize @@ -1502,12 +1588,12 @@ p, li { white-space: pre-wrap; } Brazil - Brasil + Brazil British Virgin Islands - Kepulauan Virgin Britania Raya + Kepulauan Virginia Britania Raya @@ -1522,7 +1608,7 @@ p, li { white-space: pre-wrap; } Chile - Chile + Chili @@ -1562,12 +1648,12 @@ p, li { white-space: pre-wrap; } Grenada - Grenada + Granada Guadeloupe - Guadeloupe + Guadelope @@ -1647,7 +1733,7 @@ p, li { white-space: pre-wrap; } Saint Vincent and the Grenadines - Saint Vincent dan Grenadines + Saint Vincent dan Granadines @@ -1677,7 +1763,7 @@ p, li { white-space: pre-wrap; } US Virgin Islands - Kepulauan Virgin Amerika Serikat + Kepulauan Virginia Amerika Serikat @@ -1807,7 +1893,7 @@ p, li { white-space: pre-wrap; } Luxembourg - Luksembourg + Luksemburg @@ -1842,7 +1928,7 @@ p, li { white-space: pre-wrap; } New Zealand - New Zealand + Selandia Baru @@ -1852,7 +1938,7 @@ p, li { white-space: pre-wrap; } Poland - Poland + Polandia @@ -1862,7 +1948,7 @@ p, li { white-space: pre-wrap; } Romania - Rumania + Romania @@ -1877,7 +1963,7 @@ p, li { white-space: pre-wrap; } Slovakia - Slovakia + Slowakia @@ -1917,7 +2003,7 @@ p, li { white-space: pre-wrap; } United Kingdom - Britania Raya + Inggris @@ -1977,7 +2063,7 @@ p, li { white-space: pre-wrap; } Andorra - Andorra + Andora @@ -2102,7 +2188,7 @@ p, li { white-space: pre-wrap; } Jordan - Jordan + Yordania @@ -2120,22 +2206,120 @@ p, li { white-space: pre-wrap; } Bermuda - - + + Console ID: 0x%1 ID Konsol: 0x%1 - + This will replace your current virtual 3DS with a new one. Your current virtual 3DS will not be recoverable. This might have unexpected effects in games. This might fail, if you use an outdated config savegame. Continue? - Ini akan menggantikan 3DS virtual saat ini dengan yang baru. 3DS virtual Anda saat ini tidak dapat dikembalikan. Mungkin akan memberikan efek tak terduga dalam permainan. Kemungkinan ini akan gagal jika Anda menggunakan config savegame lama. Lanjutkan? + Ini akan menggantikan 3DS virtual anda saat ini dengan yang baru. 3DS virtual Anda saat ini tidak dapat dikembalikan. Hal ini mungkin akan memberikan efek tak terduga dalam game. Kemungkinan hal ini akan gagal jika Anda menggunakan pengaturan save game lama. Lanjutkan? - + Warning Peringatan + + ConfigureUi + + + Form + + + + + General + + + + + Interface language: + + + + + Theme: + + + + + Game List + + + + + Icon Size: + + + + + + None + + + + + Small (24x24) + + + + + Large (48x48) + + + + + Row 1 Text: + + + + + + File Name + + + + + + Full Path + + + + + + Title Name + + + + + + Title ID + + + + + Row 2 Text: + + + + + Hide Titles without Icon + + + + + <System> + + + + + English + + + ConfigureWeb @@ -2151,11 +2335,11 @@ p, li { white-space: pre-wrap; } By providing your username and token, you agree to allow Citra to collect additional usage data, which may include user identifying information. - Dengan memberikan username dan token, Anda menyetujui untuk memperbolehkan Citra mengumpulkan data penggunaan tambahan, yang mungkin berisi informasi identifikasi pengguna. + Dengan memberikan nama user dan token, Anda menyetujui Citra untuk mengumpulkan data penggunaan tambahan, mungkin termaksud informasi identifikasi pengguna. - + Verify Verifikasi @@ -2172,7 +2356,7 @@ p, li { white-space: pre-wrap; } Username: - Username: + Nama User: @@ -2192,7 +2376,7 @@ p, li { white-space: pre-wrap; } Learn more - Pelajari lebih lanjut + Pelajari lebih Banyak @@ -2212,53 +2396,53 @@ p, li { white-space: pre-wrap; } Show Current Game in your Discord Status - Tampilkan Permainan Saat Ini ke Status Discord + Tampilkan Game Saat Ini ke Status Discord Anda - + <a href='https://citra-emu.org/entry/telemetry-and-why-thats-a-good-thing/'><span style="text-decoration: underline; color:#039be5;">Learn more</span></a> - <a href='https://citra-emu.org/entry/telemetry-and-why-thats-a-good-thing/'><span style="text-decoration: underline; color:#039be5;">Pelajari lebih lanjut</span></a> + <a href='https://citra-emu.org/entry/telemetry-and-why-thats-a-good-thing/'><span style="text-decoration: underline; color:#039be5;">Pelajari lebih banyak</span></a> - - <a href='https://services.citra-emu.org/'><span style="text-decoration: underline; color:#039be5;">Sign up</span></a> - <a href='https://services.citra-emu.org/'><span style="text-decoration: underline; color:#039be5;">Daftar</span></a> + + <a href='https://profile.citra-emu.org/'><span style="text-decoration: underline; color:#039be5;">Sign up</span></a> + - + <a href='https://citra-emu.org/wiki/citra-web-service/'><span style="text-decoration: underline; color:#039be5;">What is my token?</span></a> <a href='https://citra-emu.org/wiki/citra-web-service/'><span style="text-decoration: underline; color:#039be5;">Apa token saya ?</span></a> - - + + Telemetry ID: 0x%1 ID Telemetri: 0x%1 - - - Username and token not verified - Nama pengguna dan token tidak terverifikasi - - Username and token were not verified. The changes to your username and/or token have not been saved. - Username dan token tidak terverifikasi. Perubahan pada username dan/atau token tidak disimpan. + Username and token not verified + Nama user dan token tidak terverifikasi - + + Username and token were not verified. The changes to your username and/or token have not been saved. + Nama user dan token tidak terverifikasi. Perubahan pada nama user dan/atau token tidak disimpan. + + + Verifying Memverifikasi - + Verification failed Verifikasi gagal - + Verification failed. Check that you have entered your username and token correctly, and that your internet connection is working. - Verifikasi gagal. Pastikan username dan token yang Anda masukkan benar dan koneksi internet Anda bekerja. + Verifikasi gagal. Pastikan nama user dan token yang Anda masukkan benar serta koneksi internet Anda bekerja. @@ -2281,7 +2465,7 @@ p, li { white-space: pre-wrap; } <html><head/><body><p>IPv4 address of the host</p></body></html> - <html><head/><body><p>Alamat IPv4 yang digunakan oleh host</p></body></html> + <html><head/><body><p>Alamat IPv4 dari host</p></body></html> @@ -2291,7 +2475,7 @@ p, li { white-space: pre-wrap; } <html><head/><body><p>Port number the host is listening on</p></body></html> - <html><head/><body><p>Nomor Port yang digunakan oleh host</p></body></html> + <html><head/><body><p>Nomor Port yang digunakan oleh host sedang di dengarkan</p></body></html> @@ -2301,7 +2485,7 @@ p, li { white-space: pre-wrap; } Nickname - Nickname + Nama Panggilan @@ -2317,12 +2501,12 @@ p, li { white-space: pre-wrap; } DirectConnectWindow - + Connecting Menghubungkan - + Connect Hubungkan @@ -2330,413 +2514,445 @@ p, li { white-space: pre-wrap; } GMainWindow - + <a href='https://citra-emu.org/entry/telemetry-and-why-thats-a-good-thing/'>Anonymous data is collected</a> to help improve Citra. <br/><br/>Would you like to share your usage data with us? - <a href='https://citra-emu.org/entry/telemetry-and-why-thats-a-good-thing/'>Data anonim dikumpulkan</a>untuk membantu menjadikan Citra lebih baik. <br/><br/>Apakah Anda ingin membagikan penggunaan data Anda dengan kami? + <a href='https://citra-emu.org/entry/telemetry-and-why-thats-a-good-thing/'>Data anonim dikumpulkan</a>untuk membantu Citra menjadi lebih baik. <br/><br/>Apakah Anda ingin membagikan penggunaan data Anda dengan kami? - + Telemetry Telemetri - - + + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a 3DS. - Kecepatan emulasi saat ini. Nilai yang lebih tinggi atau lebih rendah dari 100% menunjukan emulasi yang lebih cepat atau lebih lambat dari 3DS. + Kecepatan emulasi saat ini. Nilai yang lebih tinggi atau lebih rendah dari 100% menunjukan emulasi berjalan lebih cepat atau lebih lambat dari 3DS. - - + + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. - Seberapa banyak frame per detik yang ditampilkan oleh permainan. Tiap permainan dan adegan akan bervariasi. + Beberapa banyak frame per detik yang ditampilkan oleh permainan saat ini. Hal ini akan bervariasi dari tiap game dan adegan per-adegan. - - + + Time taken to emulate a 3DS frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. - Waktu yang dibutuhkan untuk meniru frame 3DS, framelimiting atau v-sync tidak termasuk. setidaknya 16.67 ms untuk emulasi kecepatan penuh. + Waktu yang dibutuhkan untuk mengemulasi frame 3DS, tidak menghitung pembatasan frame atau v-sync. setidaknya emulasi yang tergolong kecepatan penuh harus berada setidaknya pada 16.67 ms. - + Clear Recent Files - Bersihkan Berkas-berkas Terbaru + Bersihkan Berkas File Terbaru - + F9 F9 - + F10 F10 - + CTRL+F CTRL+F - + Update Available Pembaharuan Tersedia - + An update is available. Would you like to install it now? Sebuah pembaharuan tersedia. Apakah Anda ingin memasangnya sekarang? - + No Update Found - Tidak Ada Pembaharuan Tersedia - - - - No update is found. - Tidak Ada Pembaharuan Tersedia. + Tidak Ada Pembaharuan di Temukan - + No update is found. + Tidak Ada Pembaharuan Yang di Temukan. + + + + OpenGL 3.3 Unsupported + + + + + Your GPU may not support OpenGL 3.3, or you do not have the latest graphics driver. + + + + + Invalid ROM Format - Format ROM Salah + Format ROM Invalid - - + + Your ROM format is not supported.<br/>Please follow the guides to redump your <a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>game cartridges</a> or <a href='https://citra-emu.org/wiki/dumping-installed-titles/'>installed titles</a>. - Format ROM Anda tidak didukung.<br/>Mohon ikuti petunjuk untuk membuat ulang<a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>kartrid permainan</a> atau <a href='https://citra-emu.org/wiki/dumping-installed-titles/'>judul terpasang</a>anda. + Format ROM Anda tidak didukung.<br/>Mohon ikuti petunjuk untuk redump<a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>kartrid permainan</a> atau <a href='https://citra-emu.org/wiki/dumping-installed-titles/'>judul terpasang</a>anda. - + ROM Corrupted - ROM Bermasalah + ROM Mengalami Kerusakan - + Your ROM is corrupted. <br/>Please follow the guides to redump your <a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>game cartridges</a> or <a href='https://citra-emu.org/wiki/dumping-installed-titles/'>installed titles</a>. - ROM anda bermasalah. <br/>Mohon ikuti petunjuk untuk membuat ulang <a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>kartrid permainan</a> atau <a href='https://citra-emu.org/wiki/dumping-installed-titles/'>judul terpasang</a>anda. + ROM anda mengalami kerusakan. <br/>Mohon ikuti petunjuk untuk redump <a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>kartrid game</a> atau <a href='https://citra-emu.org/wiki/dumping-installed-titles/'>judul terpasang</a>anda. - + ROM Encrypted ROM Dienkripsi - + Your ROM is encrypted. <br/>Please follow the guides to redump your <a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>game cartridges</a> or <a href='https://citra-emu.org/wiki/dumping-installed-titles/'>installed titles</a>. ROM anda dienkripsi. <br/>Mohon ikuti petunjuk untuk membuat ulang <a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>kartrid permainan</a> atau <a href='https://citra-emu.org/wiki/dumping-installed-titles/'>judul terpasang</a>anda. - - + + Video Core Error Video Core Bermasalah - + An error has occured. Please <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>see the log</a> for more details. Ensure that you have the latest graphics drivers for your GPU. Sebuah masalah telah terjadi. Mohon<a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>lihat catatan</a> untuk informasi lebih lanjut. Pastikan anda telah memiliki driver terbaru untuk perangkat GPU anda. - + You are running default Windows drivers for your GPU. You need to install the proper drivers for your graphics card from the manufacturer's website. Anda menggunakan driver bawaan Windows untuk GPU anda. Anda perlu menginstall driver terbaru dari website produsen GPU anda. - + Error while loading ROM! Error saat memuat ROM! - + An unknown error occured. Please see the log for more details. Terjadi error yang tidak diketahui. Lihat log untuk detail lebih lanjut. - + Start Mulai - + Error Opening %1 Folder Kesalahan Dalam Membuka Folder %1 - - + + Folder does not exist! Folder tidak ada! - + Error Opening %1 Kesalahan Dalam Membuka %1 - + Select Directory Pilih Direktori - - 3DS Executable - 3DS Executable + + 3DS Executable (%1);;All Files (*.*) + %1 is an identifier for the 3DS executable file extensions. + - - - All Files (*.*) - Semua File (*.*) - - - + Load File Muat File - + Load Files Muat berkas - + 3DS Installation File (*.CIA*) 3DS Installation File (*.CIA*) - + + All Files (*.*) + Semua File (*.*) + + + %1 has been installed successfully. %1 telah terinstall. - + Unable to open File Tidak dapat membuka File - + Could not open %1 Tidak dapat membuka %1 - + Installation aborted Instalasi dibatalkan - + The installation of %1 was aborted. Please see the log for more details Instalasi %1 dibatalkan. Silahkan lihat file log untuk info lebih lanjut. - + Invalid File File yang tidak valid - + %1 is not a valid CIA %1 bukan CIA yang valid - + Encrypted File File ter-Encrypt - + %1 must be decrypted before being used with Citra. A real 3DS is required. %1 harus di dekripsi sebelum digunakan dengan Citra. 3DS asli dibutuhkan. - + File not found File tidak ditemukan - + File "%1" not found File "%1" tidak ditemukan - - - + + + Continue Lanjut - + Missing Citra Account Akun Citra Tidak Ada - + You must link your Citra account to submit test cases.<br/>Go to Emulation &gt; Configure... &gt; Web to do so. Anda harus menautkan akun Citra anda untuk mengajukan skenario pengetesan.<br/>Pergi Ke Emulasi &gt; Konfigurasi... &gt; Alamat Website untuk melakukannya. - - - - Record Movie + + Amiibo File (%1);; All Files (*.*) - - - Citra TAS Movie (*.ctm) - - - - - Recording will start once you boot a game. - - - - - The movie file you are trying to load was created on a different revision of Citra.<br/>Citra has had some changes during the time, and the playback may desync or not work as expected.<br/><br/>Are you sure you still want to load the movie file? - - - - - The movie file you are trying to load was recorded with a different game.<br/>The playback may not work as expected, and it may cause unexpected results.<br/><br/>Are you sure you still want to load the movie file? - - - - - - The movie file you are trying to load is invalid.<br/>Either the file is corrupted, or Citra has had made some major changes to the Movie module.<br/>Please choose a different movie file and try again. - - - - - Revision Dismatch - - - - - Game Dismatch - - - - - - Invalid Movie File - - - - - Play Movie - - - - - Game Not Found - - - - - The movie you are trying to play is from a game that is not in the game list. If you own the game, please add the game folder to the game list and try to play the movie again. - - - - - Movie recording cancelled. + + Load Amiibo + + - Movie Saved - + + Record Movie + Rekam Video + + + + To keep consistency with the RNG, it is recommended to record the movie from game start.<br>Are you sure you still want to record movies now? + Untuk menjaga kekonsistenan dengan RNG,di sarankan untuk merekam video dari permulaan game.<br>Apa anda yakin masih ingin merekam video-nya sekarang? + + + + + Citra TAS Movie (*.ctm) + Citra TAS Video (*.ctm) - The movie is successfully saved. - + Recording will start once you boot a game. + Proses perekaman akan segera di mulai begitu kamu memulai game. + + + + The movie file you are trying to load was created on a different revision of Citra.<br/>Citra has had some changes during the time, and the playback may desync or not work as expected.<br/><br/>Are you sure you still want to load the movie file? + File video yang anda coba untuk muat telah di buat dalam perubahan berbeda dari Citra.<br/>Citra telah punya beberapa perubahan selama waktu tersebut dan proses pemutaran kembali mungin tidak sinkron atau tidak bekerja seperti yang di harapkan.<br/><br/>Pa anda masih yakin untuk memuat file video-nya? + The movie file you are trying to load was recorded with a different game.<br/>The playback may not work as expected, and it may cause unexpected results.<br/><br/>Are you sure you still want to load the movie file? + File video yang anda coba muat telah di rekam dengan game berbeda.<br/>Proses pemutaran kembali mungkin tidak akan bekerja seperti yang di harapkan dan mungkin menyebabkan hasil yang tidak dapat di duga.<br/><br/>Apa anda masih yakin ingin memuat file video-nya? + + + + + The movie file you are trying to load is invalid.<br/>Either the file is corrupted, or Citra has had made some major changes to the Movie module.<br/>Please choose a different movie file and try again. + File video yang anda coba untuk muat tidak valid.<br/>Bisa jadi di karenakan file-nya rusak atau Citra telah membuat beberapa perubahan besar kepada Modul Video.<br/>Tolong pilih file video yang berbeda dan silahkan coba kembali. + + + + Revision Dismatch + Perubahan Tidak Pas + + + + Game Dismatch + Game Tidak Pas + + + + + Invalid Movie File + File Video Tidak Valid + + + + + Play Movie + Putar Video + + + + To keep consistency with the RNG, it is recommended to play the movie from game start.<br>Are you sure you still want to play movies now? + Untuk menjaga kekonsistenan dengan RNG,di sarankan untuk memutar video dari awal game.<br>Apa anda masih yakin untuk memainkan video-nya sekarang? + + + + Game Not Found + Game Tidak Di Temukan + + + + The movie you are trying to play is from a game that is not in the game list. If you own the game, please add the game folder to the game list and try to play the movie again. + Video yang anda coba untuk putar dari game yang tidak ada dalam daftar list game.Jika anda memiliki game-nya,tolong tambahkan game folder-,nya ke daftar list game dan coba mainkan video-nya lagi, + + + + Movie recording cancelled. + Perekaman Video Di Batalkan. + + + + Movie Saved + Video Di Simpan + + + + The movie is successfully saved. + Video telah berhasil di simpan. + + + Speed: %1% / %2% Kelajuan: %1% / %2% - + Speed: %1% Kecepatan: %1% - + Game: %1 FPS Game: %1 FPS - + Frame: %1 ms Frame: %1 ms - + %1 is missing. Please <a href='https://citra-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-3ds-console/'>dump your system archives</a>.<br/>Continuing emulation may result in crashes and bugs. %1 tidak tersedia. Mohon <a href='https://citra-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-3ds-console/'>buat arsip sistem anda</a>.<br/>Melanjutkan emulasi dapat menyebabkan kegagalan dan masalah-masalah lainnya. - + System Archive Not Found Arsip Sistem Tidak Ditemukan - + Fatal Error Fatal Error - + A fatal error occured. <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>Check the log</a> for details.<br/>Continuing emulation may result in crashes and bugs. Sebuah masalah serius telah terjadi. <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>Periksa log</a> untuk informasi lebih lanjut.<br/>Melanjutkan emulasi dapat menyebabkan kegagalan dan masalah-masalah lainnya. - + Abort Batalkan - - + + Citra Citra - + Would you like to exit now? Apakah anda ingin keluar sekarang? - + The game is still running. Would you like to stop emulation? Permainan sedang berjalan. Apakah anda ingin menghentikan emulasi? - + Playback Completed - + Pemutaran Kembali Telah Selesai - + Movie playback completed. - + Pemutaran kembali video telah selesai. - + Citra %1 Citra %1 - + Citra %1| %2 Citra %1| %2 @@ -2799,37 +3015,67 @@ p, li { white-space: pre-wrap; } GameList - + + Name + + + + + Compatibility + + + + + Region + + + + + File type + + + + + Size + + + + Open Save Data Location Buka Lokasi Save Data - + + Open Extra Data Location + Buka Lokasi Data Tambahan + + + Open Application Location Buka Lokasi Aplikasi - + Open Update Data Location Buka Lokasi Data Pembaruan - + Navigate to GameDB entry Arahkan ke entri GameDB - + Scan Subfolders Pindai Subfolder - + Remove Game Directory Hapus Lokasi Permainan - + Open Directory Location Buka Lokasi Penyimpanan @@ -2837,85 +3083,90 @@ p, li { white-space: pre-wrap; } GameListItemCompat - + Perfect - + Sempurna - + Game functions flawless with no audio or graphical glitches, all tested functionality works as intended without any workarounds needed. - + Fungsi Game-nya akan sempurna tanpa kerusakan audio atau gambar,Semua fungsionalitas yang telah di uji bekerja sebagaimana yang telah di tujukan tanpa +ada lagi perbaikan yang di butuhkan. - + Great - + Hebat - + Game functions with minor graphical or audio glitches and is playable from start to finish. May require some workarounds. - + Fungsi Game dengan kerusakan gambar atau audio yang kecil dan dapat di mainkan dari awal sampai selesai.Mungkin membutuhkan beberapa +perbaikan. - + Okay - + Setuju - + Game functions with major graphical or audio glitches, but game is playable from start to finish with workarounds. - + Fungsi Game dengan kerusakan gambar atau audio yang besar,tetapi game-nya dapat di mainkan dari awal sampai akhir dengan berbagai macam +perbaikan. - + Bad - + Buruk - + Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches even with workarounds. - + Fungsi Game,tetapi dengan kerusakan gambar atau audio yang besar.Tidak bisa berlanjut memasuki ke dalam area tertentu do karenakan kerusakannya tersebut +bahkan dengan berbagai macam perbaikan - + Intro/Menu - + Intro/Menu - + Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start Screen. - + Game secara total tidak dapat di mainkan di karenakan kerusakan gambar atau audio yang besar.Tidak bisa berlanjut hingga melewati Layar +Mulai. - + Won't Boot - + Tidak Mau Memulai - + The game crashes when attempting to startup. - + Game ini mengalami kegagalan ketika ingin memulai. - + Not Tested - + Belum Di Uji - + The game has not yet been tested. - + Game ini masih belum di uji. GameListPlaceholder - + Double-click to add a new folder to the game list Klik 2 kali untuk menambahkan folder baru pada daftar permainan @@ -2923,27 +3174,27 @@ Screen. GameListSearchField - + of dari - + result hasil - + results hasil - + Filter: Saringan: - + Enter pattern to filter Masukkan pola untuk menyaring @@ -2951,23 +3202,23 @@ Screen. GraphicsBreakPointsWidget - + Pica Breakpoints Pica Breakpoints - - + + Emulation running Emulasi berjalan - + Resume Jalankan - + Emulation halted at breakpoint Emulasi berhenti di breakpoint @@ -3384,42 +3635,42 @@ Screen. Segarkan Lobi - + Password Required to Join Kata Sandi Dibutuhkan untuk Bergabung - + Password: Kata Sandi: - + Room Name Nama Ruangan - + Preferred Game Preferensi Permainan - + Host Host - + Players Pemain - + Refreshing Menyegarkan - + Refresh List Segarkan Daftar @@ -3442,220 +3693,245 @@ Screen. File Terkini - + + Amiibo + + + + &Emulation &Emulasi - + &View &Tampilan - + Debugging Debugging - + Screen Layout Tata Letak Layar - + Movie - + Video - + Multiplayer Multiplayer - + &Help Ba&ntuan - + Load File... Muat File... - + Install CIA... Pasang CIA... - + Load Symbol Map... Muat Symbol Map... - + E&xit &Keluar - + &Start &Mulai - + &Pause &Jeda - + &Stop &Berhenti - + FAQ FAQ - + About Citra Tentang Citra - + Single Window Mode Mode Satu Jendela - + Configure... Konfigurasi... - + Display Dock Widget Headers Tampilkan Dock Widget Headers - + Show Filter Bar Tampilkan Filter Bar - + Show Status Bar Tampilkan Status Bar - + Select Game Directory... Pilih Direktori Permainan... - + Selects a folder to display in the game list Pilih folder untuk ditampilkan pada daftar permainan - + Create Pica Surface Viewer Buat Penampil Permukaan Pica - + Record Movie - + Rekam Video - + Play Movie - + Putar Video - + Stop Recording / Playback - + Berhenti Merekam/Putar Kembali - + + Enable Frame Advancing + Aktifkan Pemercepat Frame + + + + Advance Frame + Pemercepat Frame + + + Browse Public Game Lobby Jelajahi Lobi Permainan Publik - + Create Room Buat Ruangan - + Leave Room Tinggalkan Ruangan - + Direct Connect to Room Koneksi Langsung ke Ruangan - + Show Current Room Tunjukkan Ruangan Saat Ini - + Fullscreen Layar penuh - + Modify Citra Install Modifikasi Pemasangan Citra - + Opens the maintenance tool to modify your Citra installation Buka alat perbaikan untuk memodifikasi pemasangan Citra Anda - + Default Default - + Single Screen Layar Tunggal - + Large Screen Layar Besar - + Side by Side Bersebelahan - + Swap Screens Layar Swap - + Check for Updates Periksa Pembaruan - + Report Compatibility Laporkan Kompatibilitas - + Restart Mulai ulang + + + Load... + + + + + Remove + + MicroProfileDialog @@ -3681,23 +3957,23 @@ Screen. - + Connected Terhubung - + Not Connected Tidak Terhubung - + Error Error - + Failed to announce the room to the public lobby. In order to host a room publicly, you must have a valid Citra account configured in Emulation -> Configure -> Web. If you do not want to publish a room in the public lobby, then select Unlisted instead. Debug Message: Gagal menyiarkan ruangan ke lobi publik. Untuk menghost sebuah ruangan secara publik,, anda harus memiliki akun Citra yang valid dan terkonfigurasi di Emulasi-> Konfigurasi -> Web. Jika Anda tidak ingin menyiarkan ruangan ke lobi publik silahkan pilih Tidak Terdaftar. @@ -3816,108 +4092,108 @@ Pesan Debug: %1 Sedang Memainkan %2 - - + + Invalid region Region Tidak Valid - + Japan Jepang - + North America Amerika Utara - + Europe Eropa - + Australia Australia - + China China - + Korea Korea - + Taiwan Taiwan - + Region free Bebas Region - + Invalid Region Region Tidak Valid - + Shift Shift - + Ctrl Ctrl - + Alt Alt - - + + [not set] [tidak diatur] - + Hat %1 %2 - + Hat %1%2 - + Axis %1%2 - + Axis %1%2 - + Button %1 - + Tombol %1 - - + + [unknown] [tidak diketahui] - + [unused] [tidak terpakai] - - + + Axis %1 - + Axis %1 diff --git a/dist/languages/it.ts b/dist/languages/it.ts index 7ad040793..b1a2d2adb 100644 --- a/dist/languages/it.ts +++ b/dist/languages/it.ts @@ -70,67 +70,72 @@ p, li { white-space: pre-wrap; } BreakPointModel - + Pica command loaded Comando Pica caricato - + Pica command processed Comando Pica eseguito - + Incoming primitive batch Batch primitivo in arrivo - + Finished primitive batch Batch primitivo terminato - + Vertex shader invocation Invocazione vertex shader - + Incoming display transfer Trasferimento display in arrivo - + GSP command processed Comando GSP eseguito - + Buffers swapped Buffer scambiati + + + Unknown debug context event + + CalibrationConfigurationDialog Communicating with the server... - + Comunicando con il server... Cancel - + Annulla Touch the top left corner <br>of your touchpad. - + Tocca l'angolo in alto a sinistra del tuo touchpad. Now touch the bottom right corner <br>of your touchpad. - + Adesso tocca l'angolo in basso a destra del tuo touchpad. @@ -171,13 +176,13 @@ p, li { white-space: pre-wrap; } Gioco - - + + Block Player Blocca Giocatore - + When you block a player, you will no longer receive chat messages from them.<br><br>Are you sure you would like to block %1? Quando blocchi un giocatore, non riceverai più messaggi da quel giocatore.<br><br>Sei sicuro di voler bloccare %1? @@ -198,17 +203,17 @@ p, li { white-space: pre-wrap; } ClientRoomWindow - + Connected Connesso - + Disconnected Disconnesso - + %1 (%2/%3 members) - connected %1 (%2/%3 membri) - connesso @@ -301,6 +306,26 @@ p, li { white-space: pre-wrap; } Thank you for your submission! Grazie per la segnalazione! + + + Submitting + + + + + Communication error + + + + + An error occured while sending the Testcase + + + + + Next + + ConfigureAudio @@ -340,10 +365,10 @@ p, li { white-space: pre-wrap; } 0 % - - - %1 % - %1 % + + %1% + Volume percentage (e.g. 50%) + %1% @@ -483,17 +508,17 @@ Seleziona da dove l'immagine della fotocamera emulata viene.Potrebbe essere Select the image flip to apply - + Seleziona il capovolgimento dell'immagine da applicare Flip: - + Rotazione: None - + Nessuna @@ -508,7 +533,7 @@ Seleziona da dove l'immagine della fotocamera emulata viene.Potrebbe essere Reverse - + Inversa @@ -607,50 +632,78 @@ Seleziona da dove l'immagine della fotocamera emulata viene.Potrebbe essere ConfigureDialog - + Citra Configuration Configurazione di Citra - + + + General Generale - + + + System Sistema - + + + Input Input - + + + Graphics Grafica - + + + Audio Audio - + + + Camera Camera - + + + Debug Debug - + + + Web Web + + + + + UI + + + + + Controls + Controlli + ConfigureGeneral @@ -670,64 +723,54 @@ Seleziona da dove l'immagine della fotocamera emulata viene.Potrebbe essere Conferma uscita con emulazione in corso - - Interface language - Lingua dell'Interfaccia - - - + Updates Aggiornamenti - + Check for updates on start Controlla aggiornamenti all'avvio - + Silently auto update after closing Aggiorna automaticamente dopo la chiusura - + Emulation Emulazione - + Region: Regione: - + Auto-select Selezione automatica - - Theme - Tema - - - - Theme: - Tema: - - - + Hotkeys Hotkey: - - <System> - <System> + + Reset All Settings + - - English - English + + Citra + + + + + Are you sure you want to <b>reset your settings</b> and close Citra? + @@ -766,7 +809,7 @@ Seleziona da dove l'immagine della fotocamera emulata viene.Potrebbe essere <html><head/><body><p>Use OpenGL to accelerate rendering.</p><p>Disable to debug graphics-related problem.</p></body></html> - <html><head/><body><p>Usa OpenGL per accellerare il rendering </p><p>Disabilitalo per debuggare problemi grafici + <html><head/><body><p>Usa OpenGL per accellerare il rendering. </p><p>Disabilitalo per il debug di problemi grafici @@ -836,7 +879,7 @@ Seleziona da dove l'immagine della fotocamera emulata viene.Potrebbe essere <html><head/><body><p>Use OpenGL to accelerate shader emulation.</p><p>Requires a relatively powerful GPU for better performance.</p></body></html> - + <html><head/><body><p>Attiva OpenGL per accelerare l'emulazione degli shader.</p><p>Necessita di una GPU relativamente potente per prestazioni migliori.</p></body></html> @@ -846,7 +889,7 @@ Seleziona da dove l'immagine della fotocamera emulata viene.Potrebbe essere <html><head/><body><p>Correctly handle all edge cases in multiplication operation in shaders. </p><p>Some games requires this to be enabled for the hardware shader to render properly.</p><p>However this would reduce performance in most games.</p></body></html> - + <html><head/><body><p>Gestisce tutti i casi limite nelle operazioni di moltiplicazione degli shaders.</p><p> Alcuni giochi ne necessitano l'attivazione per gli shader hardware così da essere visualizzati nel modo corretto.</p><p>Tuttavia riduce le prestazioni in gran parte dei giochi.</p></body></html> @@ -856,7 +899,7 @@ Seleziona da dove l'immagine della fotocamera emulata viene.Potrebbe essere <html><head/><body><p>Force to fall back to software shader emulation when geometry shaders are used. </p><p>Some games require this to be enabled for the hardware shader to render properly.</p><p>However this might reduce performance in some games</p></body></html> - + <html><head/><body><p>Forza l'utilizzo dell'emulazione degli shader via software quando vengono utilizzati dei geometry shaders. </p><p>Alcuni giochi ne necessitano l'attivazione per gli shader hardware così da essere visualizzati nel modo corretto.</p><p>Tuttavia riduce le prestazioni in gran parte dei giochi.</p></body></html> @@ -866,7 +909,7 @@ Seleziona da dove l'immagine della fotocamera emulata viene.Potrebbe essere <html><head/><body><p>Use the JIT engine instead of the interpreter for software shader emulation. </p><p>Enable this for better performance.</p></body></html> - + <html><head/><body><p>Utilizza il JIT engine invece dell'interprete per l'emulazione degli shader via software. </p><p>Abilita questa opzione per prestazioni migliori.</p></body></html> @@ -926,7 +969,7 @@ Seleziona da dove l'immagine della fotocamera emulata viene.Potrebbe essere Hardware Shader support is broken on macOS, and will cause graphical issues like showing a black screen.<br><br>The option is only there for test/development purposes. If you experience graphical issues with Hardware Shader, please turn it off. - + Il supporto degli Shader Hardware non funziona su macOS, e provoca problemi grafici come mostrare una schermata nera.<br><br>L'opzione è presente solo per finalità di test/sviluppo. Se si verifica un problema grafico con lo Shader Hardware, si prega di disattivarlo. @@ -1027,7 +1070,7 @@ Seleziona da dove l'immagine della fotocamera emulata viene.Potrebbe essere - + Set Analog Stick Imposta Stick Analogico @@ -1064,15 +1107,48 @@ Seleziona da dove l'immagine della fotocamera emulata viene.Potrebbe essere Motion / Touch... - + Movimenti / Touch... + Clear All + Rimuovi Tutto + + + Restore Defaults Reimposta Default - + + + Clear + Rimuovi + + + + + [not set] + [non impostato] + + + + + Restore Default + Reimposta valori predefiniti + + + + Information + Informazioni + + + + After pressing OK, first move your joystick horizontally, and then vertically. + Dopo aver premuto OK, muovi il tuo joystick orizzontalmente, e poi verticalmente. + + + [press key] [premi pulsante] @@ -1082,32 +1158,32 @@ Seleziona da dove l'immagine della fotocamera emulata viene.Potrebbe essere Configure Motion / Touch - + Configura Movimenti / Schermo Touch Motion - + Movimenti Motion Provider: - + Provider dei Movimenti: Sensitivity: - + Sensibilità: Touch - + Touch Touch Provider: - + Provider del Touch: @@ -1117,110 +1193,110 @@ Seleziona da dove l'immagine della fotocamera emulata viene.Potrebbe essere (100, 50) - (1800, 850) - + (100, 50) - (1800, 850) Configure - + Configura CemuhookUDP Config - + Configurazione CemuhookUDP You may use any Cemuhook compatible UDP input source to provide motion and touch input. - + Puoi utilizzare qualsiasi input UDP compatibile con Cemuhook per fornire input di movimento e di tocco. Server: - + Server: Port: - + Porta: Pad: - + Pad: Pad 1 - + Pad 1 Pad 2 - + Pad 2 Pad 3 - + Pad 3 Pad 4 - + Pad 4 Learn More - + Per Saperne di Più Test - + Test Mouse (Right Click) - + Mouse (Tasto Destro) CemuhookUDP - + CemuhookUDP Emulator Window - + Finestra di Emulazione <a href='https://citra-emu.org/wiki/using-a-controller-or-android-phone-for-motion-or-touch-input'><span style="text-decoration: underline; color:#039be5;">Learn More</span></a> - + <a href='https://citra-emu.org/wiki/using-a-controller-or-android-phone-for-motion-or-touch-input'><span style="text-decoration: underline; color:#039be5;">Per saperne di più</span></a> Testing - + Prova Configuring - + Configurazione Test Successful - + Prova Riuscita Successfully received data from the server. - + Dati ricevuti con successo dal server. @@ -1230,7 +1306,7 @@ Seleziona da dove l'immagine della fotocamera emulata viene.Potrebbe essere Could not receive valid data from the server.<br>Please verify that the server is set up correctly and the address and port are correct. - + Impossibile ricevere informazioni valide dal server.<br>Si prega di verificare la corretta configurazione del server insieme alla porta e all'indirizzo forniti. @@ -1240,7 +1316,7 @@ Seleziona da dove l'immagine della fotocamera emulata viene.Potrebbe essere UDP Test or calibration configuration is in progress.<br>Please wait for them to finish. - + Test UDP o configurazione della calibrazione in corso.<br>Si prega di attendere fino al loro termine. @@ -1423,35 +1499,45 @@ Seleziona da dove l'immagine della fotocamera emulata viene.Potrebbe essere Clock - + Orario System Clock - + Orario di Sistema Fixed Time - + Orario Fisso Startup time + Tempo di Avvio + + + + yyyy-MM-ddTHH:mm:ss - + + Play Coins: + Monete di gioco: + + + Console ID: ID Console: - + Regenerate Rigenera - + System settings are available only when game is not running. Le impostazioni del sistema sono disponibili solo quando non è in esecuzione nessun gioco. @@ -1463,42 +1549,42 @@ Seleziona da dove l'immagine della fotocamera emulata viene.Potrebbe essere Anguilla - + Anguilla Antigua and Barbuda - + Antigua e Barbuda Argentina - + Argentina Aruba - + Aruba Bahamas - + Bahamas Barbados - + Barbados Belize - + Belize Bolivia - + Bolivia @@ -1508,7 +1594,7 @@ Seleziona da dove l'immagine della fotocamera emulata viene.Potrebbe essere British Virgin Islands - + Isole Vergini britanniche @@ -1518,87 +1604,87 @@ Seleziona da dove l'immagine della fotocamera emulata viene.Potrebbe essere Cayman Islands - + Isole Cayman Chile - + Cile Colombia - + Colombia Costa Rica - + Costa Rica Dominica - + Dominica Dominican Republic - + Repubblica Dominicana Ecuador - + Ecuador El Salvador - + El Salvador French Guiana - + Guyana francese Grenada - + Grenada Guadeloupe - + Guadalupa Guatemala - + Guatemala Guyana - + Guyana Haiti - + Haiti Honduras - + Honduras Jamaica - + Giamaica Martinique - + Martinica @@ -1608,62 +1694,62 @@ Seleziona da dove l'immagine della fotocamera emulata viene.Potrebbe essere Montserrat - + Montserrat Netherlands Antilles - + Antille Olandesi Nicaragua - + Nicaragua Panama - + Panama Paraguay - + Paraguay Peru - + Perù Saint Kitts and Nevis - + Saint Kitts and Nevis Saint Lucia - + Saint Lucia Saint Vincent and the Grenadines - + Saint Vincent e Grenadines Suriname - + Suriname Trinidad and Tobago - + Trinidad e Tobago Turks and Caicos Islands - + Turks e Caicos @@ -1673,17 +1759,17 @@ Seleziona da dove l'immagine della fotocamera emulata viene.Potrebbe essere Uruguay - + Uruguay US Virgin Islands - + Isole Vergini Americane Venezuela - + Venezuela @@ -2018,92 +2104,92 @@ Seleziona da dove l'immagine della fotocamera emulata viene.Potrebbe essere Hong Kong - + Hong Kong Macau - + Macao Indonesia - + Indonesia Singapore - + Singapore Thailand - + Tailandia Philippines - + Filippine Malaysia - + Malesia China - + Cina United Arab Emirates - + Emirati Arabi Uniti India - + India Egypt - + Egitto Oman - + Oman Qatar - + Qatar Kuwait - + Kuwait Saudi Arabia - + Arabia Saudita Syria - + Siria Bahrain - + Bahrein Jordan - + Jordan @@ -2121,22 +2207,120 @@ Seleziona da dove l'immagine della fotocamera emulata viene.Potrebbe essere Bermuda - - + + Console ID: 0x%1 ID Console: 0x%1 - + This will replace your current virtual 3DS with a new one. Your current virtual 3DS will not be recoverable. This might have unexpected effects in games. This might fail, if you use an outdated config savegame. Continue? Questo rimpiazzerà il tuo 3DS virtuale corrente con uno nuovo. Il tuo 3DS virtuale corrente non sarà ripristinabile. Questo potrebbe causare degli effetti inaspettati sui giochi. Potrebbe inoltre fallire, se usi una configurazione di salvataggio datata. Desideri continuare? - + Warning Attenzione + + ConfigureUi + + + Form + + + + + General + + + + + Interface language: + + + + + Theme: + + + + + Game List + + + + + Icon Size: + + + + + + None + + + + + Small (24x24) + + + + + Large (48x48) + + + + + Row 1 Text: + + + + + + File Name + + + + + + Full Path + + + + + + Title Name + + + + + + Title ID + + + + + Row 2 Text: + + + + + Hide Titles without Icon + + + + + <System> + + + + + English + + + ConfigureWeb @@ -2156,7 +2340,7 @@ Seleziona da dove l'immagine della fotocamera emulata viene.Potrebbe essere - + Verify Verifica @@ -2213,51 +2397,51 @@ Seleziona da dove l'immagine della fotocamera emulata viene.Potrebbe essere Show Current Game in your Discord Status - + Mostra il Gioco Attuale nel tuo Stato di Discord - + <a href='https://citra-emu.org/entry/telemetry-and-why-thats-a-good-thing/'><span style="text-decoration: underline; color:#039be5;">Learn more</span></a> <a href='https://citra-emu.org/entry/telemetry-and-why-thats-a-good-thing/'><span style="text-decoration: underline; color:#039be5;">Per saperne di più</span></a> - - <a href='https://services.citra-emu.org/'><span style="text-decoration: underline; color:#039be5;">Sign up</span></a> - <a href='https://services.citra-emu.org/'><span style="text-decoration: underline; color:#039be5;">Registrati</span></a> + + <a href='https://profile.citra-emu.org/'><span style="text-decoration: underline; color:#039be5;">Sign up</span></a> + - + <a href='https://citra-emu.org/wiki/citra-web-service/'><span style="text-decoration: underline; color:#039be5;">What is my token?</span></a> <a href='https://citra-emu.org/wiki/citra-web-service/'><span style="text-decoration: underline; color:#039be5;">Qual è il mio token?</span></a> - - + + Telemetry ID: 0x%1 ID Telemetria: 0x%1 - + Username and token not verified Nome utente e token non verificati - + Username and token were not verified. The changes to your username and/or token have not been saved. Nome utente e token non verificati. I cambiamenti al tuo nome utente e/o token non sono stati salvati. - + Verifying Verifica in corso - + Verification failed Verifica fallita - + Verification failed. Check that you have entered your username and token correctly, and that your internet connection is working. Verifica fallita. Controlla di aver inserito correttamente il tuo username e token, e che la tua connessione ad internet sia funzionante. @@ -2318,12 +2502,12 @@ Seleziona da dove l'immagine della fotocamera emulata viene.Potrebbe essere DirectConnectWindow - + Connecting Connessione in corso - + Connect Connetti @@ -2331,413 +2515,445 @@ Seleziona da dove l'immagine della fotocamera emulata viene.Potrebbe essere GMainWindow - + <a href='https://citra-emu.org/entry/telemetry-and-why-thats-a-good-thing/'>Anonymous data is collected</a> to help improve Citra. <br/><br/>Would you like to share your usage data with us? - + <a href='https://citra-emu.org/entry/telemetry-and-why-thats-a-good-thing/'>Vengono raccolti dati anonimi </a> per aiutare lo sviluppo di Citra. <br/><br/>Vuoi condividere i tuoi dati di utilizzo con noi? - + Telemetry Telemetria - - + + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a 3DS. Velocità di emulazione corrente. Valori più alti o più bassi di 100% indicano che l'emulazione sta funzionando più velocemente o lentamente di un 3DS. - - + + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. Quanti frame al secondo il gioco visualizza attualmente. Questo varia da gioco a gioco e da situazione a situazione. - - + + Time taken to emulate a 3DS frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. Tempo necessario per emulare un frame del 3DS, senza contare il limite al framerate o il v-sync. Per un'emulazione a pieno regime questa dovrebbe essere al più 16.67 ms. - + Clear Recent Files Elimina File Recenti - + F9 F9 - + F10 F10 - + CTRL+F CTRL+F - + Update Available Aggiornamento Disponibile - + An update is available. Would you like to install it now? È disponibile un aggiornamento. Desideri installarlo ora? - + No Update Found Nessun Aggiornamento Disponibile - + No update is found. Non ci sono aggiornamenti. - - + + OpenGL 3.3 Unsupported + + + + + Your GPU may not support OpenGL 3.3, or you do not have the latest graphics driver. + + + + + Invalid ROM Format Formato ROM non valido - - + + Your ROM format is not supported.<br/>Please follow the guides to redump your <a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>game cartridges</a> or <a href='https://citra-emu.org/wiki/dumping-installed-titles/'>installed titles</a>. - + Il formato della ROM non è supportato.<br/>Si prega di seguire le guide per eseguire il redump delle<a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>cartuccie di gioco</a> o dei <a href='https://citra-emu.org/wiki/dumping-installed-titles/'>titoli installati</a>. - + ROM Corrupted ROM Corrotta - + Your ROM is corrupted. <br/>Please follow the guides to redump your <a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>game cartridges</a> or <a href='https://citra-emu.org/wiki/dumping-installed-titles/'>installed titles</a>. La tua ROM è corrotta. <br/>Per favore segui le linee guida per riestrarre i tuoi <a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>giochi su cartuccia</a> o <a href='https://citra-emu.org/wiki/dumping-installed-titles/'>titoli installati</a>. - + ROM Encrypted ROM Criptata - + Your ROM is encrypted. <br/>Please follow the guides to redump your <a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>game cartridges</a> or <a href='https://citra-emu.org/wiki/dumping-installed-titles/'>installed titles</a>. - + La ROM è criptata.<br/>Si prega di seguire le guide per eseguire il redump delle <a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>cartuccie di gioco</a> o dei <a href='https://citra-emu.org/wiki/dumping-installed-titles/'>titoli instalati</a>. - - + + Video Core Error Errore Core Video - + An error has occured. Please <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>see the log</a> for more details. Ensure that you have the latest graphics drivers for your GPU. - + Si è verificato un errore. Per favore <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>guarda il log</a> per maggiori dettagli. Assicurati di avere i più recenti driver grafici per la tua GPU. - + You are running default Windows drivers for your GPU. You need to install the proper drivers for your graphics card from the manufacturer's website. Stai usando i driver di default di Windows per la tua GPU. Devi installare i driver appositi per la tua scheda grafica dal sito del produttore. - + Error while loading ROM! Errore nel caricamento della ROM! - + An unknown error occured. Please see the log for more details. Errore sconosciuto. Visualizza il log per maggiori dettagli. - + Start Avvia - + Error Opening %1 Folder Errore nell'Apertura della Cartella %1 - - + + Folder does not exist! La cartella non esiste! - + Error Opening %1 Errore nell'Apertura di %1 - + Select Directory Seleziona cartella - - 3DS Executable - Eseguibile 3DS + + 3DS Executable (%1);;All Files (*.*) + %1 is an identifier for the 3DS executable file extensions. + - - - All Files (*.*) - Tutti i file (*.*) - - - + Load File Carica file - + Load Files Carica File - + 3DS Installation File (*.CIA*) File di installazione 3DS (*.CIA*) - + + All Files (*.*) + Tutti i file (*.*) + + + %1 has been installed successfully. %1 è stato installato con successo. - + Unable to open File Impossibile aprire il File - + Could not open %1 Impossibile aprire %1 - + Installation aborted Installazione annullata - + The installation of %1 was aborted. Please see the log for more details L'installazione di %1 è stata annullata. Visualizza il log per maggiori dettagli. - + Invalid File File non valido - + %1 is not a valid CIA %1 non è un CIA valido - + Encrypted File File Criptato - + %1 must be decrypted before being used with Citra. A real 3DS is required. %1 deve essere decriptato prima di poter essere usato con Citra. E' necessario un 3DS fisico. - + File not found File non trovato - + File "%1" not found File "%1" non trovato - - - + + + Continue Continua - + Missing Citra Account Account di Citra Mancante - + You must link your Citra account to submit test cases.<br/>Go to Emulation &gt; Configure... &gt; Web to do so. + Devi collegare il tuo account Citra per inviare dei casi d'esempio. Vai su Emulazione &gt; Configura... &gt; Web per farlo. + + + + Amiibo File (%1);; All Files (*.*) - - - + + Load Amiibo + + + + + + + Record Movie + Registra Filmato + + + + To keep consistency with the RNG, it is recommended to record the movie from game start.<br>Are you sure you still want to record movies now? - - + + Citra TAS Movie (*.ctm) - + Citra TAS Movie (*.ctm) - + Recording will start once you boot a game. - + La registrazione comincerà quando avvierai un gioco. - + The movie file you are trying to load was created on a different revision of Citra.<br/>Citra has had some changes during the time, and the playback may desync or not work as expected.<br/><br/>Are you sure you still want to load the movie file? - + The movie file you are trying to load was recorded with a different game.<br/>The playback may not work as expected, and it may cause unexpected results.<br/><br/>Are you sure you still want to load the movie file? - - + + The movie file you are trying to load is invalid.<br/>Either the file is corrupted, or Citra has had made some major changes to the Movie module.<br/>Please choose a different movie file and try again. - + Revision Dismatch - + Game Dismatch - - + + Invalid Movie File - + File del Filmato Non Valido - + + Play Movie + Riproduci Filmato + + + + To keep consistency with the RNG, it is recommended to play the movie from game start.<br>Are you sure you still want to play movies now? - + Game Not Found - + Gioco Non Trovato - + The movie you are trying to play is from a game that is not in the game list. If you own the game, please add the game folder to the game list and try to play the movie again. - + Movie recording cancelled. - + Registrazione del filmato cancellata. - + Movie Saved - + Filmato Salvato - + The movie is successfully saved. - + Il filmato è stato salvato con successo. - + Speed: %1% / %2% Velocità: %1% / %2% - + Speed: %1% Velocità: %1% - + Game: %1 FPS Gioco: %1 FPS - + Frame: %1 ms Frame: %1 ms - + %1 is missing. Please <a href='https://citra-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-3ds-console/'>dump your system archives</a>.<br/>Continuing emulation may result in crashes and bugs. - + System Archive Not Found Archivio di Sistema Non Trovato - + Fatal Error Errore fatale - + A fatal error occured. <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>Check the log</a> for details.<br/>Continuing emulation may result in crashes and bugs. - + Abort - - + + Citra Citra - + Would you like to exit now? - + Desideri uscire ora? - + The game is still running. Would you like to stop emulation? - + Playback Completed - + Riproduzione Completata - + Movie playback completed. - + Riproduzione del filmato completata - + Citra %1 Citra %1 - + Citra %1| %2 Citra %1| %2 @@ -2800,37 +3016,67 @@ Seleziona da dove l'immagine della fotocamera emulata viene.Potrebbe essere GameList - + + Name + + + + + Compatibility + + + + + Region + + + + + File type + + + + + Size + + + + Open Save Data Location Apri Cartella dei Dati di Salvataggio - + + Open Extra Data Location + + + + Open Application Location Apri Percorso Applicazione - + Open Update Data Location Apri Percorso Dati degli Aggiornamenti - + Navigate to GameDB entry Vai alla voce di GameDB - + Scan Subfolders Controlla le sottocartelle - + Remove Game Directory Rimuovi Cartella - + Open Directory Location Apri Cartella @@ -2838,77 +3084,77 @@ Seleziona da dove l'immagine della fotocamera emulata viene.Potrebbe essere GameListItemCompat - + Perfect - + Perfetto - + Game functions flawless with no audio or graphical glitches, all tested functionality works as intended without any workarounds needed. - + Great - + Ottimo - + Game functions with minor graphical or audio glitches and is playable from start to finish. May require some workarounds. - + Okay - + Buono - + Game functions with major graphical or audio glitches, but game is playable from start to finish with workarounds. - + Bad - + Scadente - + Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches even with workarounds. - + Intro/Menu - + Intro/Menu - + Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start Screen. - + Won't Boot - + Non Avviabile - + The game crashes when attempting to startup. - + Not Tested - + Non Testato - + The game has not yet been tested. @@ -2916,35 +3162,35 @@ Screen. GameListPlaceholder - + Double-click to add a new folder to the game list - + Fai doppio-click per aggiungere una nuova cartella alla lista giochi GameListSearchField - + of di - + result risultato - + results risultati - + Filter: Filtro: - + Enter pattern to filter @@ -2952,23 +3198,23 @@ Screen. GraphicsBreakPointsWidget - + Pica Breakpoints Breakpoint Pica - - + + Emulation running Emulazione in corso - + Resume Riprendi - + Emulation halted at breakpoint Emulazione arrestata al breakpoint @@ -3351,7 +3597,7 @@ Screen. Public Room Browser - + Navigatore Stanze Pubbliche @@ -3385,42 +3631,42 @@ Screen. Aggiorna Lobby - + Password Required to Join Password Richiesta per Entrare - + Password: Password: - + Room Name Nome Stanza - + Preferred Game Gioco Preferito - + Host Host - + Players Giocatori - + Refreshing Aggiornamento in corso - + Refresh List Aggiorna Lista @@ -3443,220 +3689,245 @@ Screen. File recenti - + + Amiibo + + + + &Emulation &Emulazione - + &View &Visualizza - + Debugging Debugging - + Screen Layout Disposizione schermi - + Movie - + Filmato - + Multiplayer Multigiocatore - + &Help &Aiuto - + Load File... Carica file... - + Install CIA... Installa CIA... - + Load Symbol Map... Carica mappa simboli... - + E&xit &Esci - + &Start &Avvia - + &Pause &Pausa - + &Stop &Stop - + FAQ FAQ - + About Citra Riguardo Citra - + Single Window Mode Modalità finestra singola - + Configure... Configura - + Display Dock Widget Headers Visualizza le intestazioni del dock dei widget - + Show Filter Bar Mosta barra filtri - + Show Status Bar Mostra barra stato - + Select Game Directory... Seleziona cartella dei giochi... - + Selects a folder to display in the game list Seleziona cartella da mostrare nella lista dei giochi - + Create Pica Surface Viewer Crea visualizzatore superficie Pica - + Record Movie - + Registra Filmato - + Play Movie - + Riproduci Filmato - + Stop Recording / Playback + Ferma Registrazione / Riproduzione + + + + Enable Frame Advancing - + + Advance Frame + + + + Browse Public Game Lobby Sfoglia Lobby di Gioco Pubbliche - + Create Room Crea Stanza - + Leave Room Esci dalla Stanza - + Direct Connect to Room Collegamento Diretto alla Stanza - + Show Current Room Mostra Stanza Attuale - + Fullscreen Schermo intero - + Modify Citra Install Modifica Installazione di Citra - + Opens the maintenance tool to modify your Citra installation Accedi allo strumento di manutenzione per modificare la tua Installazione di Citra - + Default Default - + Single Screen Schermo Singolo - + Large Screen Schermo Grande - + Side by Side Affiancati - + Swap Screens Scambia Schermi - + Check for Updates Controlla Aggiornamenti - + Report Compatibility Segnala Compatibilità - + Restart Riavvia + + + Load... + + + + + Remove + + MicroProfileDialog @@ -3682,23 +3953,23 @@ Screen. - + Connected Connesso - + Not Connected Non Connesso - + Error Errore - + Failed to announce the room to the public lobby. In order to host a room publicly, you must have a valid Citra account configured in Emulation -> Configure -> Web. If you do not want to publish a room in the public lobby, then select Unlisted instead. Debug Message: @@ -3816,108 +4087,108 @@ Debug Message: %1 sta giocando a %2 - - + + Invalid region Regione non valida - + Japan Giappone - + North America Nord America - + Europe Europa - + Australia Australia - + China Cina - + Korea Corea - + Taiwan Taiwan - + Region free Region free - + Invalid Region Regione non valida - + Shift Shift - + Ctrl Ctrl - + Alt Alt - - + + [not set] [non impostato] - + Hat %1 %2 - + Hat %1 %2 - + Axis %1%2 - + Asse %1%2 - + Button %1 - + Pulsante %1 - - + + [unknown] [sconosciuto] - + [unused] [inutilizzato] - - + + Axis %1 - + Asse %1 diff --git a/dist/languages/ja_JP.ts b/dist/languages/ja_JP.ts index ed372cba1..3f3c974a8 100644 --- a/dist/languages/ja_JP.ts +++ b/dist/languages/ja_JP.ts @@ -70,45 +70,50 @@ p, li { white-space: pre-wrap; } BreakPointModel - + Pica command loaded Pica command loaded - + Pica command processed Pica command processed - + Incoming primitive batch Incoming primitive batch - + Finished primitive batch Finished primitive batch - + Vertex shader invocation Vertex shader invocation - + Incoming display transfer Incoming display transfer - + GSP command processed GSP command processed - + Buffers swapped Buffers swapped + + + Unknown debug context event + + CalibrationConfigurationDialog @@ -171,13 +176,13 @@ p, li { white-space: pre-wrap; } ゲーム - - + + Block Player - + When you block a player, you will no longer receive chat messages from them.<br><br>Are you sure you would like to block %1? @@ -198,17 +203,17 @@ p, li { white-space: pre-wrap; } ClientRoomWindow - + Connected 接続 - + Disconnected 切断 - + %1 (%2/%3 members) - connected %1 (%2/%3 人) - 接続中 @@ -301,6 +306,26 @@ p, li { white-space: pre-wrap; } Thank you for your submission! ご報告いただき、ありがとうございました! + + + Submitting + + + + + Communication error + + + + + An error occured while sending the Testcase + + + + + Next + + ConfigureAudio @@ -340,9 +365,9 @@ p, li { white-space: pre-wrap; } - - - %1 % + + %1% + Volume percentage (e.g. 50%) @@ -606,50 +631,78 @@ p, li { white-space: pre-wrap; } ConfigureDialog - + Citra Configuration 設定 - + + + General 全般 - + + + System システム - + + + Input 入力 - + + + Graphics グラフィック - + + + Audio サウンド - + + + Camera カメラ - + + + Debug デバッグ - + + + Web Web + + + + + UI + + + + + Controls + + ConfigureGeneral @@ -669,64 +722,54 @@ p, li { white-space: pre-wrap; } エミュレーション停止時に確認 - - Interface language - UIの言語 - - - + Updates 自動更新の設定 - + Check for updates on start Citra起動時に確認 - + Silently auto update after closing Citra終了後に自動更新する - + Emulation エミュレーションに関する設定 - + Region: リージョン(地域)の選択 - + Auto-select 自動 - - Theme - テーマの設定 - - - - Theme: - テーマ選択 - - - + Hotkeys ホットキー - - <System> - <System> + + Reset All Settings + - - English - 英語 + + Citra + + + + + Are you sure you want to <b>reset your settings</b> and close Citra? + @@ -1026,7 +1069,7 @@ p, li { white-space: pre-wrap; } - + Set Analog Stick アナログスティックを設定する @@ -1067,11 +1110,44 @@ p, li { white-space: pre-wrap; } + Clear All + + + + Restore Defaults ボタン設定の初期化 - + + + Clear + + + + + + [not set] + + + + + + Restore Default + + + + + Information + + + + + After pressing OK, first move your joystick horizontally, and then vertically. + + + + [press key] [キーを入力...] @@ -1440,17 +1516,27 @@ p, li { white-space: pre-wrap; } - + + yyyy-MM-ddTHH:mm:ss + + + + + Play Coins: + + + + Console ID: コンソールID: - + Regenerate コンソールの再作成 - + System settings are available only when game is not running. ゲーム実行中にシステム設定を変更することはできません @@ -2120,13 +2206,13 @@ p, li { white-space: pre-wrap; } - - + + Console ID: 0x%1 コンソール ID: 0x%1 - + This will replace your current virtual 3DS with a new one. Your current virtual 3DS will not be recoverable. This might have unexpected effects in games. This might fail, if you use an outdated config savegame. Continue? 仮想3DSコンソールを新しく作り直します。現在使用中の仮想3DSコンソールを復元することはできず、ゲームに予期しない影響を与えることもあります。 この処理は古い設定のセーブデータが残っていると失敗する可能性があります @@ -2134,11 +2220,109 @@ p, li { white-space: pre-wrap; } それでも続行しますか? - + Warning 警告 + + ConfigureUi + + + Form + + + + + General + + + + + Interface language: + + + + + Theme: + + + + + Game List + + + + + Icon Size: + + + + + + None + + + + + Small (24x24) + + + + + Large (48x48) + + + + + Row 1 Text: + + + + + + File Name + + + + + + Full Path + + + + + + Title Name + + + + + + Title ID + + + + + Row 2 Text: + + + + + Hide Titles without Icon + + + + + <System> + + + + + English + + + ConfigureWeb @@ -2158,7 +2342,7 @@ p, li { white-space: pre-wrap; } - + Verify ユーザー名とトークンの確認 @@ -2218,49 +2402,49 @@ p, li { white-space: pre-wrap; } - + <a href='https://citra-emu.org/entry/telemetry-and-why-thats-a-good-thing/'><span style="text-decoration: underline; color:#039be5;">Learn more</span></a> <a href='https://citra-emu.org/entry/telemetry-and-why-thats-a-good-thing/'><span style="text-decoration: underline; color:#039be5;">もっと詳しく</span></a> - - <a href='https://services.citra-emu.org/'><span style="text-decoration: underline; color:#039be5;">Sign up</span></a> - <a href='https://services.citra-emu.org/'><span style="text-decoration: underline; color:#039be5;">ユーザー登録</span></a> + + <a href='https://profile.citra-emu.org/'><span style="text-decoration: underline; color:#039be5;">Sign up</span></a> + - + <a href='https://citra-emu.org/wiki/citra-web-service/'><span style="text-decoration: underline; color:#039be5;">What is my token?</span></a> <a href='https://citra-emu.org/wiki/citra-web-service/'><span style="text-decoration: underline; color:#039be5;">トークンの取得</span></a> - - + + Telemetry ID: 0x%1 テレメトリー ID: 0x%1 - + Username and token not verified ユーザー名とトークンが確認できませんでした - + Username and token were not verified. The changes to your username and/or token have not been saved. ユーザー名とトークンが確認できませんでした 変更は保存されません。 - + Verifying 確認中 - + Verification failed 確認に失敗 - + Verification failed. Check that you have entered your username and token correctly, and that your internet connection is working. 確認に失敗。ユーザー名とトークンが正しく入力されているか、ネット接続が正常に機能しているかの確認をしてください @@ -2321,12 +2505,12 @@ p, li { white-space: pre-wrap; } DirectConnectWindow - + Connecting 接続中... - + Connect 接続 @@ -2334,413 +2518,445 @@ p, li { white-space: pre-wrap; } GMainWindow - + <a href='https://citra-emu.org/entry/telemetry-and-why-thats-a-good-thing/'>Anonymous data is collected</a> to help improve Citra. <br/><br/>Would you like to share your usage data with us? - + Telemetry - - + + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a 3DS. 現在のエミュレーション速度です。100%以外の値は、エミュレーションが3DS実機より高速または低速で行われていることを表します - - + + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. 1秒当たりの表示フレーム数です。ゲームタイトルや場面によって変化することがあります - - + + Time taken to emulate a 3DS frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. 3DSフレームをエミュレートするのにかかった時間です。フレームリミットや垂直同期の有効時にはカウントしません。フルスピードで動作させるには16.67ms以下に保つ必要があります - + Clear Recent Files - + F9 F9 - + F10 F10 - + CTRL+F CTRL+F - + Update Available - + An update is available. Would you like to install it now? - + No Update Found - + No update is found. - - + + OpenGL 3.3 Unsupported + + + + + Your GPU may not support OpenGL 3.3, or you do not have the latest graphics driver. + + + + + Invalid ROM Format - - + + Your ROM format is not supported.<br/>Please follow the guides to redump your <a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>game cartridges</a> or <a href='https://citra-emu.org/wiki/dumping-installed-titles/'>installed titles</a>. - + ROM Corrupted - + Your ROM is corrupted. <br/>Please follow the guides to redump your <a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>game cartridges</a> or <a href='https://citra-emu.org/wiki/dumping-installed-titles/'>installed titles</a>. - + ROM Encrypted - + Your ROM is encrypted. <br/>Please follow the guides to redump your <a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>game cartridges</a> or <a href='https://citra-emu.org/wiki/dumping-installed-titles/'>installed titles</a>. - - + + Video Core Error - + An error has occured. Please <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>see the log</a> for more details. Ensure that you have the latest graphics drivers for your GPU. - + You are running default Windows drivers for your GPU. You need to install the proper drivers for your graphics card from the manufacturer's website. - + Error while loading ROM! ROM読み込みエラー - + An unknown error occured. Please see the log for more details. 不明なエラーが発生しました。詳細はログを確認してください - + Start 開始 - + Error Opening %1 Folder %1 を開く際のエラー - - + + Folder does not exist! フォルダが見つかりません! - + Error Opening %1 - + Select Directory 3DSのROMがあるフォルダを選択 - - 3DS Executable - 3DS 実行可能ファイル + + 3DS Executable (%1);;All Files (*.*) + %1 is an identifier for the 3DS executable file extensions. + - - - All Files (*.*) - すべてのファイル (*.*) - - - + Load File ゲームファイルの読み込み - + Load Files ファイルの読み込み - + 3DS Installation File (*.CIA*) 3DS インストールファイル(.CIA *) - + + All Files (*.*) + すべてのファイル (*.*) + + + %1 has been installed successfully. %1 が正常にインストールされました - + Unable to open File ファイルを開けません - + Could not open %1 %1 を開くことができませんでした - + Installation aborted インストール中止 - + The installation of %1 was aborted. Please see the log for more details %1 のインストールは中断されました。詳細はログを確認してください - + Invalid File 無効なファイル - + %1 is not a valid CIA %1 は有効なCIAではありません - + Encrypted File 暗号化されたファイル - + %1 must be decrypted before being used with Citra. A real 3DS is required. %1 は、Citraで使用する前に復号化する必要があります。3DS実機が必要です - + File not found ファイルが見つかりません - + File "%1" not found ファイル "%1" が見つかりませんでした - - - + + + Continue 続行 - + Missing Citra Account Citraアカウントがありません - + You must link your Citra account to submit test cases.<br/>Go to Emulation &gt; Configure... &gt; Web to do so. - - - + + Amiibo File (%1);; All Files (*.*) + + + + + Load Amiibo + + + + + + + Record Movie - - + + To keep consistency with the RNG, it is recommended to record the movie from game start.<br>Are you sure you still want to record movies now? + + + + + Citra TAS Movie (*.ctm) - + Recording will start once you boot a game. - + The movie file you are trying to load was created on a different revision of Citra.<br/>Citra has had some changes during the time, and the playback may desync or not work as expected.<br/><br/>Are you sure you still want to load the movie file? - + The movie file you are trying to load was recorded with a different game.<br/>The playback may not work as expected, and it may cause unexpected results.<br/><br/>Are you sure you still want to load the movie file? - - + + The movie file you are trying to load is invalid.<br/>Either the file is corrupted, or Citra has had made some major changes to the Movie module.<br/>Please choose a different movie file and try again. - + Revision Dismatch - + Game Dismatch - - + + Invalid Movie File - + + Play Movie - + + To keep consistency with the RNG, it is recommended to play the movie from game start.<br>Are you sure you still want to play movies now? + + + + Game Not Found - + The movie you are trying to play is from a game that is not in the game list. If you own the game, please add the game folder to the game list and try to play the movie again. - + Movie recording cancelled. - + Movie Saved - + The movie is successfully saved. - + Speed: %1% / %2% スピード:%1% / %2% - + Speed: %1% スピード:%1% - + Game: %1 FPS ゲーム:%1 FPS - + Frame: %1 ms フレーム:%1 ms - + %1 is missing. Please <a href='https://citra-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-3ds-console/'>dump your system archives</a>.<br/>Continuing emulation may result in crashes and bugs. - + System Archive Not Found システムアーカイブが見つかりません - + Fatal Error 致命的エラー - + A fatal error occured. <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>Check the log</a> for details.<br/>Continuing emulation may result in crashes and bugs. - + Abort - - + + Citra Citra - + Would you like to exit now? - + The game is still running. Would you like to stop emulation? - + Playback Completed - + Movie playback completed. - + Citra %1 - + Citra %1| %2 @@ -2803,37 +3019,67 @@ p, li { white-space: pre-wrap; } GameList - + + Name + + + + + Compatibility + + + + + Region + + + + + File type + + + + + Size + + + + Open Save Data Location セーブデータの保存先を開く - + + Open Extra Data Location + + + + Open Application Location アプリケーションの保存先を開く - + Open Update Data Location アップデータの保存先を開く - + Navigate to GameDB entry 公式ゲームDBで動作状況を確認 - + Scan Subfolders - + Remove Game Directory - + Open Directory Location @@ -2841,77 +3087,77 @@ p, li { white-space: pre-wrap; } GameListItemCompat - + Perfect - + Game functions flawless with no audio or graphical glitches, all tested functionality works as intended without any workarounds needed. - + Great - + Game functions with minor graphical or audio glitches and is playable from start to finish. May require some workarounds. - + Okay - + Game functions with major graphical or audio glitches, but game is playable from start to finish with workarounds. - + Bad - + Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches even with workarounds. - + Intro/Menu - + Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start Screen. - + Won't Boot - + The game crashes when attempting to startup. - + Not Tested - + The game has not yet been tested. @@ -2919,7 +3165,7 @@ Screen. GameListPlaceholder - + Double-click to add a new folder to the game list @@ -2927,27 +3173,27 @@ Screen. GameListSearchField - + of - + result - + results - + Filter: - + Enter pattern to filter @@ -2955,23 +3201,23 @@ Screen. GraphicsBreakPointsWidget - + Pica Breakpoints Pica Breakpoints - - + + Emulation running Emulation running - + Resume Resume - + Emulation halted at breakpoint Emulation halted at breakpoint @@ -3388,42 +3634,42 @@ Screen. ロビーを更新 - + Password Required to Join パスワードが必要です - + Password: パスワードを入力 - + Room Name 部屋名 - + Preferred Game プレイ希望ゲーム - + Host ホスト - + Players プレイヤー - + Refreshing 更新 - + Refresh List リスト更新 @@ -3446,220 +3692,245 @@ Screen. 最近使ったファイル - + + Amiibo + + + + &Emulation エミュレーション(&E) - + &View 表示(&V) - + Debugging デバッグ - + Screen Layout 画面レイアウト - + Movie - + Multiplayer マルチプレイヤー - + &Help ヘルプ(&H) - + Load File... ファイルを開く... - + Install CIA... CIAのインストール... - + Load Symbol Map... シンボルマップを開く... - + E&xit 終了(&x) - + &Start 開始(&S) - + &Pause 一時停止(&P) - + &Stop 停止(&S) - + FAQ よくある質問 - + About Citra Citraについて - + Single Window Mode シングルウィンドウモード - + Configure... 設定 - + Display Dock Widget Headers Dock Widget ヘッダを表示 - + Show Filter Bar フィルタバーを表示 - + Show Status Bar ステータスバーを表示 - + Select Game Directory... ゲームフォルダの選択 - + Selects a folder to display in the game list ゲームリストに表示するフォルダを選択します - + Create Pica Surface Viewer Create Pica Surface Viewer - + Record Movie - + Play Movie - + Stop Recording / Playback - + + Enable Frame Advancing + + + + + Advance Frame + + + + Browse Public Game Lobby Publicな部屋一覧をブラウズ - + Create Room 新しく部屋を作成 - + Leave Room 退室 - + Direct Connect to Room 指定ルームへ直接接続 - + Show Current Room 現在のルームを表示 - + Fullscreen フルスクリーンで表示 - + Modify Citra Install Citraのインストール形態を変更 - + Opens the maintenance tool to modify your Citra installation Citraのインストール形態を変更するためのメンテナンスツールを開きます - + Default デフォルト - + Single Screen Single Screen - + Large Screen Large Screen - + Side by Side Side by Side - + Swap Screens 画面取替 - + Check for Updates アップデートの確認 - + Report Compatibility レポートの互換性 - + Restart + + + Load... + + + + + Remove + + MicroProfileDialog @@ -3685,23 +3956,23 @@ Screen. - + Connected 接続済 - + Not Connected 未接続 - + Error エラー - + Failed to announce the room to the public lobby. In order to host a room publicly, you must have a valid Citra account configured in Emulation -> Configure -> Web. If you do not want to publish a room in the public lobby, then select Unlisted instead. Debug Message: このルームをPublicな部屋一覧に登録できませんでした。登録するには エミュレーション -> 設定 -> Web で Citraアカウントを設定してください。 Publicな部屋にしたくない場合は「部屋一覧に表示させない(Unlisted)」を選択してください @@ -3820,106 +4091,106 @@ Debug Message: %1 は %2 をプレイ中 - - + + Invalid region 無効なリージョン - + Japan 日本 - + North America 北米 - + Europe ヨーロッパ - + Australia オーストラリア - + China 中国 - + Korea 韓国 - + Taiwan 台湾 - + Region free リージョンフリー - + Invalid Region 無効なリージョン - + Shift Shift - + Ctrl Ctrl - + Alt Alt - - + + [not set] [未設定] - + Hat %1 %2 - + Axis %1%2 - + Button %1 - - + + [unknown] [不明] - + [unused] [未使用] - - + + Axis %1 diff --git a/dist/languages/ko_KR.ts b/dist/languages/ko_KR.ts index 65611786e..6906d4b32 100644 --- a/dist/languages/ko_KR.ts +++ b/dist/languages/ko_KR.ts @@ -70,45 +70,50 @@ p, li { white-space: pre-wrap; } BreakPointModel - + Pica command loaded Pica command loaded - + Pica command processed Pica command processed - + Incoming primitive batch Incoming primitive batch - + Finished primitive batch Finished primitive batch - + Vertex shader invocation Vertex shader invocation - + Incoming display transfer Incoming display transfer - + GSP command processed GSP command processed - + Buffers swapped Buffers swapped + + + Unknown debug context event + 알 수없는 디버그 컨텍스트 이벤트 + CalibrationConfigurationDialog @@ -171,15 +176,15 @@ p, li { white-space: pre-wrap; } 게임 - - + + Block Player 플레이어 차단 - + When you block a player, you will no longer receive chat messages from them.<br><br>Are you sure you would like to block %1? - 플레이어를 차단하면 더 이상 플레이어로부터 채팅 메시지를 받을수 없습니다. <br><br>%을 차단 하시겠습니까? + 플레이어를 차단하면 더 이상 플레이어로부터 채팅 메시지를 받을수 없습니다. <br><br>%1을 차단 하시겠습니까? @@ -198,17 +203,17 @@ p, li { white-space: pre-wrap; } ClientRoomWindow - + Connected 연결됨 - + Disconnected 연결되지 않음 - + %1 (%2/%3 members) - connected %1 (%2/%3 멤버) - 연결됨 @@ -229,7 +234,7 @@ p, li { white-space: pre-wrap; } <html><head/><body><p><span style=" font-size:10pt;">Should you choose to submit a test case to the </span><a href="https://citra-emu.org/game/"><span style=" font-size:10pt; text-decoration: underline; color:#0000ff;">Citra Compatibility List</span></a><span style=" font-size:10pt;">, The following information will be collected and displayed on the site:</span></p><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Hardware Information (CPU / GPU / Operating System)</li><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Which version of Citra you are running</li><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">The connected Citra account</li></ul></body></html> - <html><head/><body><p><span style=" font-size:10pt;"><a href="https://citra-emu.org/game/"><span style=" font-size:10pt; text-decoration: underline; color:#0000ff;">Citra 호환성 리스트에</span></a> 제출할 테스트 케이스를 선택해야 합니다</span><span style=" font-size:10pt;">, 다음 정보가 수집되어 사이트에 표시됩니다:</span></p><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">하드웨어 정보 (CPU/GPU/운영 체제)</li><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">당신이 실행중인 Citra의 버전</li><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">연결된 Citra 계정</li></ul></body></html> + <html><head/><body><p><span style=" font-size:10pt;"><a href="https://citra-emu.org/game/"><span style=" font-size:10pt; text-decoration: underline; color:#0000ff;">Citra 호환성 리스트</span></a>에 제출할 테스트 케이스를 선택해야 합니다</span><span style=" font-size:10pt;">. 다음 정보가 수집되어 사이트에 표시됩니다:</span></p><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">하드웨어 정보 (CPU / GPU / 운영 체제)</li><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">당신이 실행중인 Citra의 버전</li><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">연결된 Citra 계정</li></ul></body></html> @@ -301,6 +306,26 @@ p, li { white-space: pre-wrap; } Thank you for your submission! 제출해주셔서 감사합니다! + + + Submitting + + + + + Communication error + + + + + An error occured while sending the Testcase + + + + + Next + + ConfigureAudio @@ -340,10 +365,10 @@ p, li { white-space: pre-wrap; } 0 % - - - %1 % - %1 % + + %1% + Volume percentage (e.g. 50%) + %1% @@ -430,7 +455,7 @@ p, li { white-space: pre-wrap; } Select where the image of the emulated camera comes from. It may be an image or a real camera. - 에뮬레이션 된 카메라의 이미지를 가져올 위치를 선택하십시오. 이미지 또는 실제 카메라 일 수 있습니다. + 에뮬레이션되는 카메라의 이미지를 가져올 위치를 선택하십시오. 이미지 또는 실제 카메라일 수 있습니다. @@ -606,50 +631,78 @@ p, li { white-space: pre-wrap; } ConfigureDialog - + Citra Configuration Citra 설정 - + + + General 일반 - + + + System 시스템 - + + + Input 입력 - + + + Graphics 그래픽 - + + + Audio 오디오 - + + + Camera 카메라 - + + + Debug 디버그 - + + + Web + + + + + UI + UI + + + + Controls + 컨트롤 + ConfigureGeneral @@ -669,64 +722,54 @@ p, li { white-space: pre-wrap; } 에뮬레이션이 실행되는 동안 종료확인 - - Interface language - 인터페이스 언어 - - - + Updates 업데이트 - + Check for updates on start 시작시 업데이트 확인 - + Silently auto update after closing 종료후 자동으로 업데이트 - + Emulation 에뮬레이션 - + Region: 지역: - + Auto-select 자동 - - Theme - 테마 - - - - Theme: - 테마: - - - + Hotkeys 단축키 - - <System> - <시스템> + + Reset All Settings + - - English - English + + Citra + + + + + Are you sure you want to <b>reset your settings</b> and close Citra? + @@ -1026,7 +1069,7 @@ p, li { white-space: pre-wrap; } - + Set Analog Stick 아날로그 스틱 설정 @@ -1067,11 +1110,44 @@ p, li { white-space: pre-wrap; } + Clear All + 모두 지우기 + + + Restore Defaults 기본값으로 재설정 - + + + Clear + 지우기 + + + + + [not set] + [설정 안함] + + + + + Restore Default + 기본값으로 재설정 + + + + Information + 정보 + + + + After pressing OK, first move your joystick horizontally, and then vertically. + OK을 누른 후 먼저 조이스틱을 수평으로 이동한 다음 수직으로 이동하세요. + + + [press key] [키 입력] @@ -1440,17 +1516,27 @@ p, li { white-space: pre-wrap; } 시작 시간 - + + yyyy-MM-ddTHH:mm:ss + yyyy-MM-ddTHH:mm:ss + + + + Play Coins: + 플레이 코인: + + + Console ID: 콘솔 ID: - + Regenerate 재생성 - + System settings are available only when game is not running. 시스템 설정은 게임이 실행되고 있지 않을 때만 사용할 수 있습니다. @@ -2077,7 +2163,7 @@ p, li { white-space: pre-wrap; } Qatar - Qatar + 카타르 @@ -2087,7 +2173,7 @@ p, li { white-space: pre-wrap; } Saudi Arabia - Saudi Arabia + 사우디아라비아 @@ -2120,22 +2206,120 @@ p, li { white-space: pre-wrap; } 버뮤다 - - + + Console ID: 0x%1 콘솔 ID: 0x%1 - + This will replace your current virtual 3DS with a new one. Your current virtual 3DS will not be recoverable. This might have unexpected effects in games. This might fail, if you use an outdated config savegame. Continue? - 현재 사용하는 가상 3DS를 새로운 시스템으로 교체합니다. 현재 사용하는 가상 3DS는 복구 할 수 없습니다. 이러한 변경은 게임에 예기치 않은 영향을 미칠 수 있습니다. 이 작업은 오래된 Config Savegame를 사용하는 경우 실패할 수 있습니다. 계속하시겠습니까? + 현재 사용하는 가상 3DS를 새로운 시스템으로 교체합니다. 현재 사용하는 가상 3DS는 복구 할 수 없습니다. 이러한 변경은 게임에 예기치 않은 영향을 미칠 수 있습니다. 이 작업은 오래된 Config Savegame을 사용하는 경우 실패할 수 있습니다. 계속하시겠습니까? - + Warning 경고 + + ConfigureUi + + + Form + Form + + + + General + 일반 + + + + Interface language: + 인터페이스 언어: + + + + Theme: + 테마: + + + + Game List + 게임 리스트 + + + + Icon Size: + 아이콘 사이즈: + + + + + None + 없음 + + + + Small (24x24) + 작은 (24x24) + + + + Large (48x48) + 큰 (48x48) + + + + Row 1 Text: + 첫째행 텍스트: + + + + + File Name + 파일 이름 + + + + + Full Path + 전체 경로 + + + + + Title Name + 타이틀 이름 + + + + + Title ID + 타이틀 ID + + + + Row 2 Text: + 둘째행 텍스트: + + + + Hide Titles without Icon + 아이콘이 없는 타이틀 숨기기 + + + + <System> + <시스템> + + + + English + English + + ConfigureWeb @@ -2155,7 +2339,7 @@ p, li { white-space: pre-wrap; } - + Verify 인증 @@ -2215,48 +2399,48 @@ p, li { white-space: pre-wrap; } 디스코드에 실행중인 게임 보이기 - + <a href='https://citra-emu.org/entry/telemetry-and-why-thats-a-good-thing/'><span style="text-decoration: underline; color:#039be5;">Learn more</span></a> <a href='https://citra-emu.org/entry/telemetry-and-why-thats-a-good-thing/'><span style="text-decoration: underline; color:#039be5;">자세히 알아보기</span></a> - - <a href='https://services.citra-emu.org/'><span style="text-decoration: underline; color:#039be5;">Sign up</span></a> - <a href='https://services.citra-emu.org/'><span style="text-decoration: underline; color:#039be5;">가입</span></a> + + <a href='https://profile.citra-emu.org/'><span style="text-decoration: underline; color:#039be5;">Sign up</span></a> + <a href='https://profile.citra-emu.org/'><span style="text-decoration: underline; color:#039be5;">가입</span></a> - + <a href='https://citra-emu.org/wiki/citra-web-service/'><span style="text-decoration: underline; color:#039be5;">What is my token?</span></a> <a href='https://citra-emu.org/wiki/citra-web-service/'><span style="text-decoration: underline; color:#039be5;">나의 토큰이 무엇인가요?</span></a> - - + + Telemetry ID: 0x%1 Telemetry ID: 0x%1 - + Username and token not verified 사용자 이름과 토큰이 확인되지 않았습니다 - + Username and token were not verified. The changes to your username and/or token have not been saved. 사용자 이름 및 토큰을 확인하지 못했습니다. 사용자 이름 및 토큰에 대한 변경 내용이 저장되지 않습니다. - + Verifying 확인중 - + Verification failed 인증 실패 - + Verification failed. Check that you have entered your username and token correctly, and that your internet connection is working. 인증에 실패했습니다. 사용자 이름과 토큰을 올바르게 입력했으며 인터넷 연결이 작동하는지 확인하십시오. @@ -2317,12 +2501,12 @@ p, li { white-space: pre-wrap; } DirectConnectWindow - + Connecting 연결중 - + Connect 연결 @@ -2330,413 +2514,445 @@ p, li { white-space: pre-wrap; } GMainWindow - + <a href='https://citra-emu.org/entry/telemetry-and-why-thats-a-good-thing/'>Anonymous data is collected</a> to help improve Citra. <br/><br/>Would you like to share your usage data with us? Citra를 개선하기위해 <a href='https://citra-emu.org/entry/telemetry-and-why-thats-a-good-thing/'>익명데이터가 수집됩니다</a>. <br/><br/>사용 데이터를 공유하시겠습니까? - + Telemetry 텔레메트리 - - + + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a 3DS. 현재 에뮬레이션 속도. 100%보다 높거나 낮은 값은 에뮬레이션이 3DS보다 빠르거나 느린 것을 나타냅니다. - - + + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. 게임이 현재 표시하고있는 초당 프레임 수입니다. 이것은 게임마다 다르며 장면마다 다릅니다. - - + + Time taken to emulate a 3DS frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. 3DS 프레임을 에뮬레이션 하는 데 걸린 시간, 프레임제한 또는 v-동기화를 카운트하지 않음. 최대 속도 에뮬레이션의 경우, 이는 최대 16.67 ms여야 합니다. - + Clear Recent Files 최근 파일 삭제 - + F9 F9 - + F10 F10 - + CTRL+F CTRL+F - + Update Available 업데이트가 사용가능합니다 - + An update is available. Would you like to install it now? 업데이트가 존재합니다. 지금 설치할까요? - + No Update Found 업데이트를 찾을 수 없음 - + No update is found. 업데이트를 찾을 수 없습니다. - - + + OpenGL 3.3 Unsupported + OpenGL 3.3이 지원되지않음 + + + + Your GPU may not support OpenGL 3.3, or you do not have the latest graphics driver. + GPU가 OpenGL 3.3을 지원하지 않거나 최신 그래픽 드라이버를 가지고 있지 않을 수 있습니다. + + + + Invalid ROM Format 올바르지 않은 롬 포맷 - - + + Your ROM format is not supported.<br/>Please follow the guides to redump your <a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>game cartridges</a> or <a href='https://citra-emu.org/wiki/dumping-installed-titles/'>installed titles</a>. 지원되지 않는 롬 포맷입니다. <br/><a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>게임 카트리지</a>나 <a href='https://citra-emu.org/wiki/dumping-installed-titles/'>설치된 타이틀</a>을 덤프하기위해 가이드를 따르세요 - + ROM Corrupted 롬이 손상되었습니다 - + Your ROM is corrupted. <br/>Please follow the guides to redump your <a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>game cartridges</a> or <a href='https://citra-emu.org/wiki/dumping-installed-titles/'>installed titles</a>. 롬이 손상되었습니다. <br/><a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>게임 카트리지</a>나 <a href='https://citra-emu.org/wiki/dumping-installed-titles/'>설치된 타이틀</a>을 덤프하기위해 가이드를 따르세요 - + ROM Encrypted - 롬이 암호화되어 있습니다 + 롬이 암호화되어 있음 - + Your ROM is encrypted. <br/>Please follow the guides to redump your <a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>game cartridges</a> or <a href='https://citra-emu.org/wiki/dumping-installed-titles/'>installed titles</a>. 롬이 암호화 되어있습니다. <br/><a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>게임 카트리지</a>나 <a href='https://citra-emu.org/wiki/dumping-installed-titles/'>설치된 타이틀</a>을 덤프하기위해 가이드를 따르세요 - - + + Video Core Error 비디오 코어 에러 - + An error has occured. Please <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>see the log</a> for more details. Ensure that you have the latest graphics drivers for your GPU. 치명적인 오류가 발생했습니다. 자세한 내용은 <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>로그를 확인하십시오</a>. GPU용 최신 그래픽 드라이버가 있는지 확인하십시오. - + You are running default Windows drivers for your GPU. You need to install the proper drivers for your graphics card from the manufacturer's website. GPU의 기본 Windows 드라이버를 실행하고 있습니다. 제조업체의 웹 사이트에서 그래픽 카드에 맞는 드라이버를 설치해야합니다. - + Error while loading ROM! ROM을 로드하는 중 오류가 발생했습니다! - + An unknown error occured. Please see the log for more details. 알 수 없는 오류가 발생했습니다. 자세한 내용은 로그를 참조하십시오. - + Start 시작 - + Error Opening %1 Folder %1 폴더 열기 오류 - - + + Folder does not exist! 폴더가 존재하지 않습니다! - + Error Opening %1 %1 열기 오류 - + Select Directory 디렉토리 선택 - - 3DS Executable - 3DS 실행파일 + + 3DS Executable (%1);;All Files (*.*) + %1 is an identifier for the 3DS executable file extensions. + 3DS 실행파일 (%1);;모든파일 (*.*) - - - All Files (*.*) - 모든파일 (*.*) - - - + Load File 파일 열기 - + Load Files 파일 열기 - + 3DS Installation File (*.CIA*) 3DS 설치파일 (*.CIA*) - + + All Files (*.*) + 모든파일 (*.*) + + + %1 has been installed successfully. %1가 성공적으로 설치되었습니다. - + Unable to open File 파일을 열 수 없음 - + Could not open %1 %1를 열수 없음 - + Installation aborted 설치가 중단됨 - + The installation of %1 was aborted. Please see the log for more details %1의 설치가 중단되었습니다. 자세한 내용은 로그를 참조하십시오. - + Invalid File 올바르지 않은 파일 - + %1 is not a valid CIA %1은 올바른 CIA가 아닙니다 - + Encrypted File 암호화된 파일 - + %1 must be decrypted before being used with Citra. A real 3DS is required. %1 은 Citra에서 사용되기전에 디크립트되어야 합니다. 실제 3DS가 필요합니다. - + File not found 파일을 찾을 수 없음 - + File "%1" not found 파일 "%1"을 찾을 수 없음 - - - + + + Continue 계속 - + Missing Citra Account Citra 계정 없음 - + You must link your Citra account to submit test cases.<br/>Go to Emulation &gt; Configure... &gt; Web to do so. 테스트를 제출하기 위해서는 Citra계정을 연결해야합니다.<br/>연결하려면 Emulation &gt; Configure... &gt; Web 으로 가세요. - - - + + Amiibo File (%1);; All Files (*.*) + Amiibo 파일 (%1);; 모든파일 (*.*) + + + + Load Amiibo + Amiibo 불러오기 + + + + + + Record Movie 무비 녹화 - - + + To keep consistency with the RNG, it is recommended to record the movie from game start.<br>Are you sure you still want to record movies now? + RNG와의 일관성을 유지하려면 게임 시작시 동영상을 녹화하는 것이 좋습니다. 지금 무비를 녹화하시겠습니까? + + + + Citra TAS Movie (*.ctm) Citra TAS Movie (*.ctm) - + Recording will start once you boot a game. 게임을 부팅할때 레코딩이 시작됩니다. - + The movie file you are trying to load was created on a different revision of Citra.<br/>Citra has had some changes during the time, and the playback may desync or not work as expected.<br/><br/>Are you sure you still want to load the movie file? 재생하려는 무비파일은 Citra의 다른 리비전에서 만들어 졌습니다. <br/>그동안 Citra에는 많은 변경이 있었고 재생된 내용은 싱크가 맞지않거나 예상과 다르게 작동할 수 있습니다. <br/><br/>정말 무비파일을 여시겠습니까? - + The movie file you are trying to load was recorded with a different game.<br/>The playback may not work as expected, and it may cause unexpected results.<br/><br/>Are you sure you still want to load the movie file? 재생하려는 무비파일은 다른게임으로 부터 녹화되었습니다. <br/>재생은 원하는 결과를 나타내지않거나, 예기치않은 결과를 만들 수 있습니다.<br/><br/> 정말 무비파일을 여시겠습니까? - - + + The movie file you are trying to load is invalid.<br/>Either the file is corrupted, or Citra has had made some major changes to the Movie module.<br/>Please choose a different movie file and try again. 재생하려고 하는 무비파일은 올바르지않습니다. <br/>파일이 손상되었거나 Citra의 무비 모듈에 메이저한 변경이 있었습니다. <br/>다른 무비파일이 선택해서 다시 시도하십시오. - + Revision Dismatch 리비전 불일치 - + Game Dismatch 게임 불일치 - - + + Invalid Movie File 올바르지 않은 무비 파일 - + + Play Movie 무비 재생 - + + To keep consistency with the RNG, it is recommended to play the movie from game start.<br>Are you sure you still want to play movies now? + RNG와의 일관성을 유지하려면 게임 시작시 동영상을 재생하는 것이 좋습니다.<br>지금 무비를 재생하시겠습니까? + + + Game Not Found 게임을 찾을 수 없음 - + The movie you are trying to play is from a game that is not in the game list. If you own the game, please add the game folder to the game list and try to play the movie again. 재생하려는 무비의 게임이 게임리스트에 존재하지 않습니다. 만약 게임을 가지고 있으면 게임 폴더를 게임 리스트에 추가하고 다시 무비를 플레이하세요 - + Movie recording cancelled. 무비 레코딩이 취소되었습니다. - + Movie Saved 무비 저장됨 - + The movie is successfully saved. 무비가 성공적으로 저장되었습니다. - + Speed: %1% / %2% 속도: %1% / %2% - + Speed: %1% 속도: %1% - + Game: %1 FPS 게임: %1 FPS - + Frame: %1 ms 프레임: %1 ms - + %1 is missing. Please <a href='https://citra-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-3ds-console/'>dump your system archives</a>.<br/>Continuing emulation may result in crashes and bugs. %1이 없습니다. <a href='https://citra-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-3ds-console/'>시스템 아카이브를 덤프하십시오</a>.<br/>에뮬레이션을 계속하면 충돌 및 버그가 발생할 수 있습니다. - + System Archive Not Found System Archive를 찾을수 없음 - + Fatal Error 치명적인 에러 - + A fatal error occured. <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>Check the log</a> for details.<br/>Continuing emulation may result in crashes and bugs. 치명적인 오류가 발생했습니다. 자세한 내용은 <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>로그를 확인하십시오</a>. <br/>에뮬레이션을 계속하면 충돌과 버그가 발생할 수 있습니다. - + Abort 중단 - - + + Citra Citra - + Would you like to exit now? 지금 종료하시겠습니까? - + The game is still running. Would you like to stop emulation? 게임이 아직 작동중입니다. 에뮬레이션을 정지할까요? - + Playback Completed 재생 완료 - + Movie playback completed. 무비 재생 완료 - + Citra %1 Citra %1 - + Citra %1| %2 Citra %1| %2 @@ -2799,37 +3015,67 @@ p, li { white-space: pre-wrap; } GameList - + + Name + 이름 + + + + Compatibility + 호환성 + + + + Region + 지역 + + + + File type + 파일 타입 + + + + Size + 크기 + + + Open Save Data Location 세이브 데이터 위치 열기 - + + Open Extra Data Location + Extra Date 위치 열기 + + + Open Application Location 어플리케이션 위치 열기 - + Open Update Data Location 업데이트 데이터 위치 열기 - + Navigate to GameDB entry GameDB 엔트리로 이동 - + Scan Subfolders 서브 디렉토리 스캔 - + Remove Game Directory 게임 디렉토리 삭제 - + Open Directory Location 디렉토리 위치 열기 @@ -2837,81 +3083,81 @@ p, li { white-space: pre-wrap; } GameListItemCompat - + Perfect 완벽함 - + Game functions flawless with no audio or graphical glitches, all tested functionality works as intended without any workarounds needed. 오디오 또는 그래픽 글리치가 없이 게임이 완벽하게 작동하며, 테스트된 모든 기능이 특별한 해결방안 없이 의도대로 작동합니다. - + Great 좋음 - + Game functions with minor graphical or audio glitches and is playable from start to finish. May require some workarounds. 게임은 사소한 그래픽문제나 오디오 글리치와 함께 처음부터 끝까지 진행가능합니다. 몇가지 해결방안이 필요할 수 있습니다. - + Okay 양호 - + Game functions with major graphical or audio glitches, but game is playable from start to finish with workarounds. 게임은 주요 그래픽문제나 오디오 글리치와 함께 처음부터 끝까지 진행가능합니다. 몇가지 해결방안이 필요할 수 있습니다. - + Bad 나쁨 - + Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches even with workarounds. 게임은 작동하지만 주요 그래픽이나 오디오 글리치가 있습니다. 특별한 해결방안에도 불구하고 특정한 지역에서 글리치로 인해 진행이 불가능합니다 - + Intro/Menu 인트로/메뉴 - + Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start Screen. 주요 그래픽 또는 오디오 결함으로 인해 게임을 완전히 재생할 수 없습니다. 시작화면을 지날 수 없습니다 - + Won't Boot 실행불가 - + The game crashes when attempting to startup. 시작할 때 게임이 충돌합니다. - + Not Tested 테스트되지 않음 - + The game has not yet been tested. 이 게임은 아직 테스트되지 않았습니다. @@ -2919,7 +3165,7 @@ Screen. GameListPlaceholder - + Double-click to add a new folder to the game list 더블 클릭하여 게임 목록에 새 폴더 추가 @@ -2927,27 +3173,27 @@ Screen. GameListSearchField - + of - + result 결과 - + results 결과 - + Filter: 필터: - + Enter pattern to filter 검색 필터 입력 @@ -2955,23 +3201,23 @@ Screen. GraphicsBreakPointsWidget - + Pica Breakpoints Pica Breakpoints - - + + Emulation running 에뮬레이션 실행중 - + Resume Resume - + Emulation halted at breakpoint 에뮬레이션이 브레이크포인트에서 정지했습니다 @@ -3388,42 +3634,42 @@ Screen. 로비 새로고침 - + Password Required to Join 참가시 비밀번호 필요 - + Password: 비밀번호: - + Room Name 방 이름 - + Preferred Game 선호되는 게임 - + Host 호스트 - + Players 플레이어 - + Refreshing 새로고침중 - + Refresh List 리스트 새로고침 @@ -3446,220 +3692,245 @@ Screen. 최근 파일 - + + Amiibo + 아미보 + + + &Emulation 에뮬레이션(&E) - + &View 보기(&V) - + Debugging 디버깅 - + Screen Layout 스크린 레이아웃 - + Movie 무비 - + Multiplayer 멀티플레이어 - + &Help 도움말(&H) - + Load File... 파일 열기... - + Install CIA... CIA 설치... - + Load Symbol Map... Symbol Map 열기... - + E&xit 종료(&X) - + &Start 시작(&S) - + &Pause 일시중지(&P) - + &Stop 정지(&S) - + FAQ FAQ - + About Citra Citra에 대하여 - + Single Window Mode 싱글 윈도우 모드 - + Configure... 설정... - + Display Dock Widget Headers Dock 위젯 보이기 - + Show Filter Bar 필터바 보이기 - + Show Status Bar 스테이스바 보이기 - + Select Game Directory... 게임 디렉토리 선택... - + Selects a folder to display in the game list 게임 리스트를 보여줄 디렉토리 선택 - + Create Pica Surface Viewer Pica Surface Viewer 생성 - + Record Movie 무비 녹화 - + Play Movie 무비 재생 - + Stop Recording / Playback 레코딩 / 재생 정지 - + + Enable Frame Advancing + 프레임 전진 사용 + + + + Advance Frame + 프레임 전진 + + + Browse Public Game Lobby 공개 방 찾아보기 - + Create Room 방 만들기 - + Leave Room 방 나가기 - + Direct Connect to Room 방에 직접 연결 - + Show Current Room 현재 방 보이기 - + Fullscreen 전체화면 - + Modify Citra Install Citra 설치 변경 - + Opens the maintenance tool to modify your Citra installation Citra 설치 변경을 위한 유지보수 도구 열기 - + Default 기본 - + Single Screen 단일화면 - + Large Screen 큰 화면 - + Side by Side 좌우화면 (Side by Side) - + Swap Screens 스크린 바꾸기 - + Check for Updates 업데이트 체크 - + Report Compatibility 호환성 보고하기 - + Restart 재시작 + + + Load... + 열기... + + + + Remove + 제거 + MicroProfileDialog @@ -3685,23 +3956,23 @@ Screen. - + Connected 연결됨 - + Not Connected 연결되지 않음 - + Error 오류 - + Failed to announce the room to the public lobby. In order to host a room publicly, you must have a valid Citra account configured in Emulation -> Configure -> Web. If you do not want to publish a room in the public lobby, then select Unlisted instead. Debug Message: 공개로비에 방을 알리는데 실패했습니다. 방을 공개적으로 호스트하기 위해서는 에뮬레이션 -> 설정 -> 웹 에서 유효한 Citra 계정을 설정해야합니다. @@ -3820,108 +4091,108 @@ Debug Message: %1은 %2를 플레이중입니다 - - + + Invalid region 올바르지 않은 지역 - + Japan 일본 - + North America 북미 - + Europe 유럽 - + Australia 호주 - + China 중국 - + Korea 한국 - + Taiwan 대만 - + Region free 지역해제 - + Invalid Region 올바르지 않은 지역 - + Shift Shift - + Ctrl Ctrl - + Alt Alt - - + + [not set] [설정 안함] - + Hat %1 %2 - + 방향키 %1 %2 - + Axis %1%2 - + Axis %1%2 - + Button %1 - + 버튼 %1 - - + + [unknown] [알수없는] - + [unused] [사용되지않음] - - + + Axis %1 - + Axis %1 diff --git a/dist/languages/lt_LT.ts b/dist/languages/lt_LT.ts index ef6ee7491..5ce48fd00 100644 --- a/dist/languages/lt_LT.ts +++ b/dist/languages/lt_LT.ts @@ -70,45 +70,50 @@ p, li { white-space: pre-wrap; } BreakPointModel - + Pica command loaded „Pica“ komanda įkrauta - + Pica command processed „Pica“ komanda apdorota - + Incoming primitive batch Įeinanti primityvi partija - + Finished primitive batch Primityvi partija užbaigta - + Vertex shader invocation Vertekso šešėliuoklės iškvietimas - + Incoming display transfer Įeinantis vaizdo perdavimas - + GSP command processed GSP komanda apdorota - + Buffers swapped Buferiai apkeisti + + + Unknown debug context event + Nežinomas derinimo konteksto įvykis + CalibrationConfigurationDialog @@ -171,13 +176,13 @@ p, li { white-space: pre-wrap; } Žaidimas - - + + Block Player Užblokuoti žaidėją - + When you block a player, you will no longer receive chat messages from them.<br><br>Are you sure you would like to block %1? Kai jūs užblokuosite žaidėją, jūs nebegausite naujų pranešimų nuo jo. <br><br>Ar tikrai norite užblokuoti %1? @@ -198,17 +203,17 @@ p, li { white-space: pre-wrap; } ClientRoomWindow - + Connected Prisijungta - + Disconnected Atsijungta - + %1 (%2/%3 members) - connected %1 (%2/%3 vartotojai) - prisijungta @@ -301,6 +306,26 @@ p, li { white-space: pre-wrap; } Thank you for your submission! Ačiū už jūsų pateikimą! + + + Submitting + Pateikiama + + + + Communication error + Komunikacijos klaida + + + + An error occured while sending the Testcase + Klaida siunčiant suderinamumo pateiktį + + + + Next + Kitas + ConfigureAudio @@ -340,10 +365,10 @@ p, li { white-space: pre-wrap; } 0 % - - - %1 % - %1 % + + %1% + Volume percentage (e.g. 50%) + %1% @@ -606,50 +631,78 @@ p, li { white-space: pre-wrap; } ConfigureDialog - + Citra Configuration „Citra“ konfigūracija - + + + General Pagrindinis - + + + System Sistema - + + + Input Įvestis - + + + Graphics Grafika - + + + Audio Garsas - + + + Camera Kamera - + + + Debug Derinimas - + + + Web Tinklo tarnyba + + + + + UI + NS (naudotojo sąsaja) + + + + Controls + Valdymas + ConfigureGeneral @@ -669,64 +722,54 @@ p, li { white-space: pre-wrap; } Patvirtinti išėjimą veikiant emuliacijai - - Interface language - Sąsajos kalba - - - + Updates Atnaujinimai - + Check for updates on start Paieškoti atnaujinimų paleidžiant programą - + Silently auto update after closing Tyliai atsinaujinti išjungus programą - + Emulation Emuliacija - + Region: Regionas: - + Auto-select Automatiškai pasirinkti - - Theme - Tema - - - - Theme: - Tema: - - - + Hotkeys Spartieji klavišai - - <System> - <System> + + Reset All Settings + Atstatyti visus nustatymus - - English - Anglų + + Citra + „Citra“ + + + + Are you sure you want to <b>reset your settings</b> and close Citra? + Ar tikrai norite <b>atstatyti jūsų nustatymus</b>ir uždaryti „Citra“? @@ -1026,7 +1069,7 @@ p, li { white-space: pre-wrap; } - + Set Analog Stick Nustatyti analoginį valdiklį @@ -1067,11 +1110,44 @@ p, li { white-space: pre-wrap; } + Clear All + Išvalyti viską + + + Restore Defaults Atkurti numatytuosius - + + + Clear + Išvalyti + + + + + [not set] + [nenustatyta] + + + + + Restore Default + Atkurti numatytuosius + + + + Information + Informacija + + + + After pressing OK, first move your joystick horizontally, and then vertically. + Kai paspausite OK, pajudinkite savo valdiklio lazdelę horizontaliai, ir paskui vertikaliai. + + + [press key] [paspauskite klavišą] @@ -1440,17 +1516,27 @@ p, li { white-space: pre-wrap; } Užkrovimo laikas - + + yyyy-MM-ddTHH:mm:ss + metai-MĖNESIAI-dienaTVALANDA:minutė:sekundė + + + + Play Coins: + Žaidimų pinigų kiekis: + + + Console ID: Konsolės ID: - + Regenerate Regeneruoti - + System settings are available only when game is not running. Sistemos nustatymai yra prieinami, tik kai žaidimai nepaleisti. @@ -2120,22 +2206,120 @@ p, li { white-space: pre-wrap; } Bermudai - - + + Console ID: 0x%1 Konsolės ID: 0x%1 - + This will replace your current virtual 3DS with a new one. Your current virtual 3DS will not be recoverable. This might have unexpected effects in games. This might fail, if you use an outdated config savegame. Continue? Tai pakeis jūsų dabartinį virtualų 3DS nauju. Jūsų dabartinis virtualus 3DS bus nebeatkuriamas. Tai gali padaryti netikėtų pokyčių žaidimuose. Tęsti? - + Warning Įspėjimas + + ConfigureUi + + + Form + Forma + + + + General + Pagrindinis + + + + Interface language: + Sąsajos kalba: + + + + Theme: + Tema: + + + + Game List + Žaidimų sąrašo nustatymai + + + + Icon Size: + Žaidimo paveikslėlio dydis: + + + + + None + Nėra + + + + Small (24x24) + Mažas (24x24) + + + + Large (48x48) + Didelis (48x48) + + + + Row 1 Text: + 1 eilutės tekstas: + + + + + File Name + Failo pavadinimas + + + + + Full Path + Pilnas adresas + + + + + Title Name + Programos pavadinimas + + + + + Title ID + Programos ID + + + + Row 2 Text: + 2 eilutės tekstas: + + + + Hide Titles without Icon + Paslėpti programas be paveikslėlio + + + + <System> + <System> + + + + English + Anglų k. + + ConfigureWeb @@ -2155,7 +2339,7 @@ p, li { white-space: pre-wrap; } - + Verify Patikrinti @@ -2215,48 +2399,48 @@ p, li { white-space: pre-wrap; } Rodyti jūsų žaidžiamą žaidimą Discord'e - + <a href='https://citra-emu.org/entry/telemetry-and-why-thats-a-good-thing/'><span style="text-decoration: underline; color:#039be5;">Learn more</span></a> <a href='https://citra-emu.org/entry/telemetry-and-why-thats-a-good-thing/'><span style="text-decoration: underline; color:#039be5;">Sužinoti daugiau</span></a> - - <a href='https://services.citra-emu.org/'><span style="text-decoration: underline; color:#039be5;">Sign up</span></a> - <a href='https://services.citra-emu.org/'><span style="text-decoration: underline; color:#039be5;">Užsiregistruoti</span></a> + + <a href='https://profile.citra-emu.org/'><span style="text-decoration: underline; color:#039be5;">Sign up</span></a> + <a href='https://profile.citra-emu.org/'><span style="text-decoration: underline; color:#039be5;">Užsiregistruoti</span></a> - + <a href='https://citra-emu.org/wiki/citra-web-service/'><span style="text-decoration: underline; color:#039be5;">What is my token?</span></a> <a href='https://citra-emu.org/wiki/citra-web-service/'><span style="text-decoration: underline; color:#039be5;">Koks yra mano simbolinis ID?</span></a> - - + + Telemetry ID: 0x%1 Telemetrijos ID: 0x%1 - + Username and token not verified Vartotojo vardas ir simbolinis ID nėra patikrinti - + Username and token were not verified. The changes to your username and/or token have not been saved. Vartotojo vardas ir simbolinis ID nebuvo patikrinti. Pakeitimai nebuvo išsaugoti. - + Verifying Tikrinama - + Verification failed Tikrinimas nepavyko - + Verification failed. Check that you have entered your username and token correctly, and that your internet connection is working. Tikrinimas nepavyko. Įsitikinkite, ar įvedėte duomenis teisingai ir kad jūsų interneto ryšys veikia. @@ -2317,12 +2501,12 @@ p, li { white-space: pre-wrap; } DirectConnectWindow - + Connecting Jungiamasi - + Connect Prisijungti @@ -2330,413 +2514,445 @@ p, li { white-space: pre-wrap; } GMainWindow - + <a href='https://citra-emu.org/entry/telemetry-and-why-thats-a-good-thing/'>Anonymous data is collected</a> to help improve Citra. <br/><br/>Would you like to share your usage data with us? <a href='https://citra-emu.org/entry/telemetry-and-why-thats-a-good-thing/'>Anoniminiai duomenys yra renkami </a> kad padėtumėte Citra komandai. <br/><br/>Ar norite pasidalinti savo duomenimis su mumis? - + Telemetry Telemetrija - - + + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a 3DS. Dabartinės emuliacijos greitis. Reikšmės žemiau ar aukščiau 100% parodo, kad emuliacija veikia greičiau ar lėčiau negu 3DS. - - + + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. Kiek FPS žaidimas šiuo metu atvaizduoja. Tai gali keistis nuo žaidimo ir scenos. - - + + Time taken to emulate a 3DS frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. Laikas, kuris buvo sunaudotas atvaizduoti 1 3DS kadrą, neskaičiuojant FPS ribojimo ar V-Sync. Pilno greičio emuliacijai reikalinga daugiausia 16.67 ms reikšmė. - + Clear Recent Files Pravalyti neseniai įkrautus failus - + F9 F9 - + F10 F10 - + CTRL+F Ctrl+F - + Update Available Pasiekiamas atnaujinimas - + An update is available. Would you like to install it now? Atnaujinimas yra pasiekiamas. Ar dabar norite jį įdiegti? - + No Update Found Atnaujinimų nerasta - + No update is found. Atnaujinimų nerasta - - + + OpenGL 3.3 Unsupported + OpenGL 3.3 nepalaikomas + + + + Your GPU may not support OpenGL 3.3, or you do not have the latest graphics driver. + Jūsų vaizdo plokštė nepalaiko OpenGL 3.3, arba neturite naujausių vaizdo tvarkyklių. + + + + Invalid ROM Format Klaidingas ROM formatas - - + + Your ROM format is not supported.<br/>Please follow the guides to redump your <a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>game cartridges</a> or <a href='https://citra-emu.org/wiki/dumping-installed-titles/'>installed titles</a>. Jūsų ROM formatas yra nepalaikomas.<br/>Prašome pasižiūrėti į mūsų gidus kaip iškopijuoti jūsų <a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>žaidimų plokšteles</a>arba <a href='https://citra-emu.org/wiki/dumping-installed-titles/'>suinstaliuotus žaidimus</a>. - + ROM Corrupted Pažeistas ROM failas - + Your ROM is corrupted. <br/>Please follow the guides to redump your <a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>game cartridges</a> or <a href='https://citra-emu.org/wiki/dumping-installed-titles/'>installed titles</a>. Jūsų ROM failas yra pažeistas.<br/>Prašome pasižiūrėti į mūsų gidus kaip iškopijuoti jūsų<a href='https://citra-emu.org/wiki/dumping-game-cartridges/'> žaidimų plokšteles</a>arba<a href='https://citra-emu.org/wiki/dumping-installed-titles/'> suinstaliuotus žaidimus</a>. - + ROM Encrypted Užšifruotas ROM failas - + Your ROM is encrypted. <br/>Please follow the guides to redump your <a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>game cartridges</a> or <a href='https://citra-emu.org/wiki/dumping-installed-titles/'>installed titles</a>. Jūsų ROM failas yra užšifruotas. <br/>Prašome pasižiūrėti į mūsų gidus kaip iškopijuoti jūsų <a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>žaidimų plokšteles</a>arba <a href='https://citra-emu.org/wiki/dumping-installed-titles/'>suinstaliuotus žaidimus</a>. - - + + Video Core Error Vaizdo atkūrimo klaida - + An error has occured. Please <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>see the log</a> for more details. Ensure that you have the latest graphics drivers for your GPU. Įvyko klaida. Prašome <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>pasižiūrėti į žurnalą</a>dėl daugiau informacijos. Įsitinkite, ar turite naujausias vaizdo tvarkykles jūsų vaizdo plokštei. - + You are running default Windows drivers for your GPU. You need to install the proper drivers for your graphics card from the manufacturer's website. Jūs turite numatytasias Windows tvarkykles jūsų vaizdo plokštei. Jums reikia įdiegti tikrasias tvarkykles jūsų vaizdo plokštei iš gamintojo internetinio puslapio. - + Error while loading ROM! Klaida įkraunant atvaizdą! - + An unknown error occured. Please see the log for more details. Įvyko nežinoma klaida. Pasižiūrėkite į žurnalą dėl daugiau informacijos. - + Start Pradėti - + Error Opening %1 Folder Klaida atidarant %1 aplanką - - + + Folder does not exist! Aplankas neegzistuoja! - + Error Opening %1 Klaida atidarant %1 - + Select Directory Pasirinkti katalogą - - 3DS Executable - 3DS taik. programa + + 3DS Executable (%1);;All Files (*.*) + %1 is an identifier for the 3DS executable file extensions. + 3DS programa (%1);;Visi failai (*.*) - - - All Files (*.*) - Visi failai (*.*) - - - + Load File Įkrauti failą - + Load Files Įkrauti failus - + 3DS Installation File (*.CIA*) 3DS instaliacijos failas (*.cia*) - + + All Files (*.*) + Visi failai (*.*) + + + %1 has been installed successfully. %1 buvo įdiegtas sėkmingai. - + Unable to open File Negalima atverti failo - + Could not open %1 Nepavyko atverti %1 - + Installation aborted Instaliacija nutraukta - + The installation of %1 was aborted. Please see the log for more details Failo %1 instaliacija buvo nutraukta. Pasižiūrėkite į žurnalą dėl daugiau informacijos - + Invalid File Klaidingas failas - + %1 is not a valid CIA %1 nėra tinkamas CIA - + Encrypted File Šifruotas failas - + %1 must be decrypted before being used with Citra. A real 3DS is required. %1 turi būti iššifruotas prieš naudojant jį su „Citra“. Tikra 3DS konsolė yra būtina. - + File not found Failas nerastas - + File "%1" not found Failas "%1" nerastas - - - + + + Continue Tęsti - + Missing Citra Account Nėra „Citra“ paskyros - + You must link your Citra account to submit test cases.<br/>Go to Emulation &gt; Configure... &gt; Web to do so. Jūs turite prijungti jūsų Citra vartotoją prieš pateikiant suderinamumą. <br/>Eikite į Emuliacija &gt; Konfigūruoti... &gt; Tinklo tarnyba. - - - + + Amiibo File (%1);; All Files (*.*) + „Amiibo“ failas (%1);; Visi failai (*.*) + + + + Load Amiibo + Įkrauti „Amiibo“ + + + + + + Record Movie Įrašyti įvesčių vaizdo įrašą - - + + To keep consistency with the RNG, it is recommended to record the movie from game start.<br>Are you sure you still want to record movies now? + Laikantis įvairių numerių generatoriaus taisyklių, rekomenduojama pradėti įrašinėti įvestis nuo žaidimo paleisties.<br>Ar tikrai norite pradėti įrašinėti įvestis dabar? + + + + Citra TAS Movie (*.ctm) Citra TAS vaizdo įrašas (*.ctm) - + Recording will start once you boot a game. Įrašymas prasidės kai įkrausite žaidimą. - + The movie file you are trying to load was created on a different revision of Citra.<br/>Citra has had some changes during the time, and the playback may desync or not work as expected.<br/><br/>Are you sure you still want to load the movie file? - + Šis įvesčių įrašas buvo sukurtas senesnėje „Citra“ versijoje.<br/>„Citra“ turėjo pakeitimų, ir įrašas gali atitrūkinėti arba neveikti.<br/><br/>Ar tikrai norite įkrauti šį įrašą? - + The movie file you are trying to load was recorded with a different game.<br/>The playback may not work as expected, and it may cause unexpected results.<br/><br/>Are you sure you still want to load the movie file? - + Šis įvesčių įrašas buvo sukurtas kitame žaidime.<br/>Įrašas gali veikti netikėtai.<br/><br/>Ar tikrai norite įkrauti šį įrašą? - - + + The movie file you are trying to load is invalid.<br/>Either the file is corrupted, or Citra has had made some major changes to the Movie module.<br/>Please choose a different movie file and try again. - + Šis įvesčių įrašas yra klaidingas.<br/>Failas yra pažeistas arba „Citra“ turėjo didelių pakitimų įrašų modulyje. <br/>Prašome pasirinkti kitą įrašo failą ir pabandyti iš naujo. - + Revision Dismatch Revizijų nesutapimas - + Game Dismatch - + Žaidimų nesutapimas - - + + Invalid Movie File Klaidingas filmo failas - + + Play Movie Paleisti filmą - + + To keep consistency with the RNG, it is recommended to play the movie from game start.<br>Are you sure you still want to play movies now? + Laikantis įvairių numerių generatoriaus taisyklių, rekomenduojama paleisti įrašą nuo žaidimo paleisties.<br>Ar tikrai norite paleisti įvestį dabar? + + + Game Not Found Žaidimas nerastas - + The movie you are trying to play is from a game that is not in the game list. If you own the game, please add the game folder to the game list and try to play the movie again. - + Įrašas, kurį bandote paleisti, nėra iš žaidimų, kuriuos turite savo žaidimų sąraše. Prašome įtraukti savo žaidimų aplanką į sąrašą ir pabandyti iš naujo. - + Movie recording cancelled. - + Įrašo įrašymas nutrauktas. - + Movie Saved - + Įrašas išsaugotas - + The movie is successfully saved. Filmas sėkmingai išsaugotas. - + Speed: %1% / %2% Greitis: %1% / %2% - + Speed: %1% Greitis: %1% - + Game: %1 FPS Žaidimas: %1 FPS - + Frame: %1 ms Kadras: %1 ms - + %1 is missing. Please <a href='https://citra-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-3ds-console/'>dump your system archives</a>.<br/>Continuing emulation may result in crashes and bugs. - + Trūksta %1. Prašome <a href='https://citra-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-3ds-console/'>iškopijuoti sisteminius archyvus</a>. <br/>Jeigu tęsite emuliaciją, gali įvykti netikėtų išsijungimų ir klaidų. - + System Archive Not Found Sisteminis archyvas nerastas - + Fatal Error Nepataisoma klaida - + A fatal error occured. <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>Check the log</a> for details.<br/>Continuing emulation may result in crashes and bugs. Įvyko nepataisoma klaida. Prašome <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>pasižiūrėti į žurnalą</a>dėl daugiau informacijos. Jeigu tęsite emuliaciją, gali įvykti netikėtų išsijungimų ir riktų. - + Abort - + Nutraukti - - + + Citra „Citra“ - + Would you like to exit now? - + Ar norite išeiti? - + The game is still running. Would you like to stop emulation? - + Žaidimas vis dar veikia. Ar norite sustabdyti emuliaciją? - + Playback Completed - + Atkūrimas užbaigtas - + Movie playback completed. - + Įrašo atkūrimas užbaigtas. - + Citra %1 Citra %1 - + Citra %1| %2 Citra %1| %2 @@ -2799,37 +3015,67 @@ p, li { white-space: pre-wrap; } GameList - + + Name + Pavadinimas + + + + Compatibility + Suderinamumas + + + + Region + Regionas + + + + File type + Failo tipas + + + + Size + Dydis + + + Open Save Data Location Atidaryti išsaugojimo duomenų vietą - + + Open Extra Data Location + Atidaryti papildomų duomenų vietą + + + Open Application Location Atidaryti programos vietą - + Open Update Data Location Atidaryti atnaujinimo duomenų vietą - + Navigate to GameDB entry Eiti į suderinamumo puslapį - + Scan Subfolders Ieškoti poaplankius - + Remove Game Directory Pašalinti žaidimo katalogą - + Open Directory Location Atidaryti katalogo vietą @@ -2837,85 +3083,85 @@ p, li { white-space: pre-wrap; } GameListItemCompat - + Perfect - + Tobulas - + Game functions flawless with no audio or graphical glitches, all tested functionality works as intended without any workarounds needed. - + Žaidimas veikia nuostabiai be jokių garso ar vaizdo trikdžių, visos išbandytos funkcijos veikia kaip numatyta be problemų apėjimų. - + Great - + Puikus - + Game functions with minor graphical or audio glitches and is playable from start to finish. May require some workarounds. - + Žaidimas veikia su smulkiais garso arba vaizdo trikdžiais ir gali būti pereitas nuo pradžios iki galo. Gali reikalauti keleto problemų apėjimų. - + Okay - + Geras - + Game functions with major graphical or audio glitches, but game is playable from start to finish with workarounds. - + Žaidimas veikia su dideliais garso arba vaizdo trikdžiais, bet žaidimas gali būti pereitas nuo pradžios iki galo su problemų apėjimais. - + Bad - + Blogas - + Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches even with workarounds. - + Žaidimas veikia, bet su dideliais garso arba vaizdo trikdžiais. Neįmanoma pereiti keleto vietų net su problemų apėjimais. - + Intro/Menu - + Rodo tik pradžios ekraną - + Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start Screen. - + Žaidimas yra visiškai negalimas žaisti dėl didelių garso ar vaizdo trikdžių. Neįmanoma pratęsti žaidimo toliau negu pradžios ekranas. - + Won't Boot - + Nepasileidžia - + The game crashes when attempting to startup. - + Žaidimas netikėtai išsijungia pasileidžiant. - + Not Tested - + Netestuota - + The game has not yet been tested. - + Žaidimas dar nebuvo išbandytas. GameListPlaceholder - + Double-click to add a new folder to the game list Paspauskite dukart kad pridėtumėte naują katalogą žaidimų sąraše @@ -2923,27 +3169,27 @@ Screen. GameListSearchField - + of - + result rezultatų - + results rezultatai - + Filter: Filtras: - + Enter pattern to filter Įveskite raktinius žodžius filtravimui @@ -2951,23 +3197,23 @@ Screen. GraphicsBreakPointsWidget - + Pica Breakpoints „Pica“ nutrūkimo taškai - - + + Emulation running Emuliacija veikia - + Resume Pratęsti - + Emulation halted at breakpoint Emuliacija sustabdyta nutrūkimo taške @@ -3342,7 +3588,7 @@ Screen. Toggle LLE Service Modules - + Įjungti žemo lygio emuliacijos sisteminius modulius @@ -3384,42 +3630,42 @@ Screen. Atnaujinti sąrašą - + Password Required to Join Reikalingas slaptažodis - + Password: Slaptažodis: - + Room Name Serverio pavadinimas - + Preferred Game Pageidautinas žaidimas - + Host Vartotojas - + Players Žaidėjų skaičius - + Refreshing Atnaujinama - + Refresh List Atnaujinti sąrašą @@ -3442,219 +3688,244 @@ Screen. Neseniai įkrauti failai - + + Amiibo + „Amiibo“ + + + &Emulation &Emuliacija - + &View &Žiūrėti - + Debugging Derinimas - + Screen Layout Ekranų išdėstymas - + Movie Įvesčių įrašai - + Multiplayer Kelių žaidėjų režimas - + &Help &Pagalba - + Load File... Įkrauti failą... - + Install CIA... Diegti CIA... - + Load Symbol Map... Įkrauti simbolių žemėlapį... - + E&xit Į&šeiti - + &Start &Pradėti - + &Pause &Pauzė - + &Stop &Sustabdyti - + FAQ DUK - + About Citra Apie „Citra“ - + Single Window Mode Vieno lango režimas - + Configure... Konfigūruoti... - + Display Dock Widget Headers Rodyti ikonėles apačioje - + Show Filter Bar Rodyti paieškos juostą - + Show Status Bar Rodyti būsenos juostą - + Select Game Directory... Pasirinkti žaidimo katalogą... - + Selects a folder to display in the game list Pasirenka aplanką, kuris rodomas žaidimų saraše - + Create Pica Surface Viewer Sukurti „Pica“ pagrindo žiūryklę - + Record Movie Įrašyti įvesčių vaizdo įrašą - + Play Movie Paleisti įvesčių vaizdo įrašą - + Stop Recording / Playback - + Sustabdyti įrašymą / atkūrimą - + + Enable Frame Advancing + Įjungti kadro perėjimą + + + + Advance Frame + Pereiti į kitą kadrą + + + Browse Public Game Lobby Peržiūrėti viešus žaidimų serverius - + Create Room Sukurti serverį - + Leave Room Palikti serverį - + Direct Connect to Room Tiesioginis prisijungimas prie serverio - + Show Current Room Rodyti dabartinį serverį - + Fullscreen Per visą ekraną - + Modify Citra Install Modifikuoti „Citra“ instaliaciją - + Opens the maintenance tool to modify your Citra installation Atidaro priežiūros įrankį modifikuoti jūsų „Citra“ instaliaciją - + Default Numatytasis - + Single Screen Vienas ekranas - + Large Screen Didelis ekranas - + Side by Side Vienas prie kito šonu - + Swap Screens Apkeisti ekranus - + Check for Updates Tikrinti, ar yra naujinimų - + Report Compatibility Pranešti suderinamumą - + Restart - + Persirauti + + + + Load... + Įkrauti... + + + + Remove + Pašalinti @@ -3681,23 +3952,23 @@ Screen. - + Connected Prisijungta - + Not Connected Neprisijungta - + Error Klaida - + Failed to announce the room to the public lobby. In order to host a room publicly, you must have a valid Citra account configured in Emulation -> Configure -> Web. If you do not want to publish a room in the public lobby, then select Unlisted instead. Debug Message: Nepavyko paskelbti serverio viešai. Norint paskelbti viešai jus privalote turėti tinkamą „Citra“ paskyrą, sukonfigūruotą Emuliacija -> Konfigūruoti -> Tinklo tarnyba. Jeigu nenorite kurti serverio viešai, pasirinkite Nematomas. @@ -3816,108 +4087,108 @@ Derinimo pranešimas: %1 žaidžia %2 - - + + Invalid region Klaidingas regionas - + Japan Japonija - + North America Šiaurės Amerika - + Europe Europa - + Australia Australija - + China Kinija - + Korea Korėja - + Taiwan Taivanas - + Region free Be regiono - + Invalid Region Klaidingas regionas - + Shift Shift - + Ctrl Ctrl - + Alt Alt - - + + [not set] [nenustatytas] - + Hat %1 %2 - + Pusė %1 %2 - + Axis %1%2 - + Ašis %1%2 - + Button %1 - + Mygtukas %1 - - + + [unknown] [nežinomas] - + [unused] [nenaudojamas] - - + + Axis %1 - + Ašis %1 diff --git a/dist/languages/nb.ts b/dist/languages/nb.ts index 4091a8785..2a48ef264 100644 --- a/dist/languages/nb.ts +++ b/dist/languages/nb.ts @@ -70,77 +70,82 @@ p, li { white-space: pre-wrap; } BreakPointModel - + Pica command loaded Pica kommando lastet - + Pica command processed Pica kommandoen behandlet - + Incoming primitive batch Innkommende primitiv batch - + Finished primitive batch Ferdigstilt primitiv batch - + Vertex shader invocation Vertex shader påkalling - + Incoming display transfer Innkommende skjermoverføring - + GSP command processed GSP kommando behandlet - + Buffers swapped Buffere byttet + + + Unknown debug context event + + CalibrationConfigurationDialog Communicating with the server... - + Kommuniserer med serveren... Cancel - + Avbryt Touch the top left corner <br>of your touchpad. - + Trykk på øverste venstre hjørne<br>på styreflaten din. Now touch the bottom right corner <br>of your touchpad. - + Nå berør nederste høyre hjørne <br>på styreflaten. Configuration completed! - + Konfigurasjon fullført! OK - + OK @@ -171,13 +176,13 @@ p, li { white-space: pre-wrap; } Spill - - + + Block Player Blokker Spiller - + When you block a player, you will no longer receive chat messages from them.<br><br>Are you sure you would like to block %1? Når du blokkerer en spiller, vil du ikke lenger motta chatmeldinger fra dem.<br><br>Er du sikker på at du vil blokkere %1? @@ -198,17 +203,17 @@ p, li { white-space: pre-wrap; } ClientRoomWindow - + Connected Tilkoblet - + Disconnected Frakoblet - + %1 (%2/%3 members) - connected %1 (%2/%3 medlemmer) - tilkoblet @@ -301,6 +306,26 @@ p, li { white-space: pre-wrap; } Thank you for your submission! Takk for ditt bidrag! + + + Submitting + + + + + Communication error + + + + + An error occured while sending the Testcase + + + + + Next + + ConfigureAudio @@ -340,10 +365,10 @@ p, li { white-space: pre-wrap; } 0 % - - - %1 % - %1 % + + %1% + Volume percentage (e.g. 50%) + @@ -606,50 +631,78 @@ p, li { white-space: pre-wrap; } ConfigureDialog - + Citra Configuration Citra Konfigurasjon - + + + General Generelt - + + + System System - + + + Input Inngang - + + + Graphics Grafikk - + + + Audio Lyd - + + + Camera Kamera - + + + Debug Debug - + + + Web Web + + + + + UI + + + + + Controls + + ConfigureGeneral @@ -669,64 +722,54 @@ p, li { white-space: pre-wrap; } Bekreft avslutting mens emuleringen kjører - - Interface language - Grensesnittspråk - - - + Updates Oppdateringer - + Check for updates on start Se etter oppdateringer ved start - + Silently auto update after closing Stille automatisk oppdatering etter avslutning - + Emulation Emulering - + Region: Region - + Auto-select Auto-Velg - - Theme - Tema - - - - Theme: - Tema - - - + Hotkeys Hurtigtaster - - <System> - <System> + + Reset All Settings + - - English - Engelsk + + Citra + + + + + Are you sure you want to <b>reset your settings</b> and close Citra? + @@ -1026,7 +1069,7 @@ p, li { white-space: pre-wrap; } - + Set Analog Stick Still Analog Stick @@ -1063,15 +1106,48 @@ p, li { white-space: pre-wrap; } Motion / Touch... - + Bevegelse / Berøring... + Clear All + + + + Restore Defaults Gjenopprett Standardinnstillinger - + + + Clear + + + + + + [not set] + + + + + + Restore Default + + + + + Information + + + + + After pressing OK, first move your joystick horizontally, and then vertically. + + + + [press key] [trykk på tast] @@ -1081,165 +1157,165 @@ p, li { white-space: pre-wrap; } Configure Motion / Touch - + Konfigurer Bevegelse / Berøring Motion - + Bevegelse Motion Provider: - + Bevegelses leverandør: Sensitivity: - + Sensitivitet: Touch - + Berøring Touch Provider: - + Berørings Leverandør: Calibration: - + Kalibrering: (100, 50) - (1800, 850) - + (100, 50) - (1800, 850) Configure - + Konfigurer CemuhookUDP Config - + CemuhookUDP Konfigurasjon You may use any Cemuhook compatible UDP input source to provide motion and touch input. - + Du kan bruke en hvilken som helst Cemuhook kompatibel UDP inngangskilde for å gi bevegelse og berørings inngang. Server: - + Server: Port: - + Port: Pad: - + Pad: Pad 1 - + Pad 1 Pad 2 - + Pad 2 Pad 3 - + Pad 3 Pad 4 - + Pad 4 Learn More - + Lær Mer Test - + Test Mouse (Right Click) - + Mus (Høyre Klikk) CemuhookUDP - + CemuhookUDP Emulator Window - + Emulator Vindu <a href='https://citra-emu.org/wiki/using-a-controller-or-android-phone-for-motion-or-touch-input'><span style="text-decoration: underline; color:#039be5;">Learn More</span></a> - + <a href='https://citra-emu.org/wiki/using-a-controller-or-android-phone-for-motion-or-touch-input'><span style="text-decoration: underline; color:#039be5;">Lær Mer</span></a> Testing - + Testing Configuring - + Konfigurerer Test Successful - + Test Vellykket Successfully received data from the server. - + Mottatt data fra serveren vellykket. Test Failed - + Test Mislyktes Could not receive valid data from the server.<br>Please verify that the server is set up correctly and the address and port are correct. - + Kunne ikke motta gyldige data fra serveren.<br>Kontroller at serveren er riktig konfigurert, og adressen og porten er riktige. Citra - + Citra UDP Test or calibration configuration is in progress.<br>Please wait for them to finish. - + UDP Test eller kalibreringskonfigurasjon pågår.<br>Vennligst vent på at de skal fullføre. @@ -1422,35 +1498,45 @@ p, li { white-space: pre-wrap; } Clock - + Klokke System Clock - + System Klokke Fixed Time - + Bestemt Tid Startup time + Oppstartstid + + + + yyyy-MM-ddTHH:mm:ss - + + Play Coins: + + + + Console ID: Konsoll ID: - + Regenerate Regenerere - + System settings are available only when game is not running. System innstillingene er ikke tilgjengelig når spillet kjører. @@ -2120,22 +2206,120 @@ p, li { white-space: pre-wrap; } Bermuda - - + + Console ID: 0x%1 Konsoll ID: 0x%1 - + This will replace your current virtual 3DS with a new one. Your current virtual 3DS will not be recoverable. This might have unexpected effects in games. This might fail, if you use an outdated config savegame. Continue? Dette erstatter din nåværende virtuelle 3DS med en ny. Din nåværende virtuelle 3DS kan ikke gjenopprettes. Dette kan ha uventede effekter i spill. Dette kan misslykkes, hvis du bruker et utdatert konfigurasjon lagret spill. Fortsette? - + Warning Advarsel + + ConfigureUi + + + Form + + + + + General + + + + + Interface language: + + + + + Theme: + + + + + Game List + + + + + Icon Size: + + + + + + None + + + + + Small (24x24) + + + + + Large (48x48) + + + + + Row 1 Text: + + + + + + File Name + + + + + + Full Path + + + + + + Title Name + + + + + + Title ID + + + + + Row 2 Text: + + + + + Hide Titles without Icon + + + + + <System> + + + + + English + + + ConfigureWeb @@ -2155,7 +2339,7 @@ p, li { white-space: pre-wrap; } - + Verify Verifisere @@ -2207,56 +2391,56 @@ p, li { white-space: pre-wrap; } Discord Presence - + Discord tilstedeværelse Show Current Game in your Discord Status - + Vis Gjeldende Spill i Discord Statusen din. - + <a href='https://citra-emu.org/entry/telemetry-and-why-thats-a-good-thing/'><span style="text-decoration: underline; color:#039be5;">Learn more</span></a> <a href='https://citra-emu.org/entry/telemetry-and-why-thats-a-good-thing/'><span style="text-decoration: underline; color:#039be5;">Lær mer</span></a> - - <a href='https://services.citra-emu.org/'><span style="text-decoration: underline; color:#039be5;">Sign up</span></a> - <a href='https://services.citra-emu.org/'><span style="text-decoration: underline; color:#039be5;">Registrer deg</span></a> + + <a href='https://profile.citra-emu.org/'><span style="text-decoration: underline; color:#039be5;">Sign up</span></a> + - + <a href='https://citra-emu.org/wiki/citra-web-service/'><span style="text-decoration: underline; color:#039be5;">What is my token?</span></a> <a href='https://citra-emu.org/wiki/citra-web-service/'><span style="text-decoration: underline; color:#039be5;">Hva er min nøkkel?</span></a> - - + + Telemetry ID: 0x%1 Telemetri ID: 0x%1 - + Username and token not verified Brukernavn og nøkkel ikke bekreftet - + Username and token were not verified. The changes to your username and/or token have not been saved. Brukernavn og nøkkel ble ikke bekreftet. Endringene i brukernavnet ditt og/eller nøkkel ble ikke lagret. - + Verifying Verifiserer - + Verification failed Verifikasjon misslyktes - + Verification failed. Check that you have entered your username and token correctly, and that your internet connection is working. Verifikasjon mislyktes. Kontroller at du har angitt brukernavnet ditt og nøkkelen riktig, og at Internettilkoblingen din fungerer. @@ -2317,12 +2501,12 @@ p, li { white-space: pre-wrap; } DirectConnectWindow - + Connecting Kobler til - + Connect Koble til @@ -2330,413 +2514,445 @@ p, li { white-space: pre-wrap; } GMainWindow - + <a href='https://citra-emu.org/entry/telemetry-and-why-thats-a-good-thing/'>Anonymous data is collected</a> to help improve Citra. <br/><br/>Would you like to share your usage data with us? - + <a href='https://citra-emu.org/entry/telemetry-and-why-thats-a-good-thing/'>Anonym data samles inn</a> for å forbedre Citra. <br/><br/>Vil du dele dine brukerdata med oss? - + Telemetry - + Telemetri - - + + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a 3DS. Nåværende emuleringhastighet. Verdier høyere eller lavere enn 100% indikerer at emuleringen kjører raskere eller langsommere enn en 3DS. - - + + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. Hvor mange bilder per sekund spillet vises for øyeblikket. Dette vil variere fra spill til spill og scene til scene. - - + + Time taken to emulate a 3DS frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. Tid tatt for å emulere et 3DS bilde, gjelder ikke bildebegrensning eller V-Sync. For raskest emulering bør dette være høyst 16,67 ms. - + Clear Recent Files Tøm nylige filer - + F9 F9 - + F10 F10 - + CTRL+F CTRL+F - + Update Available - + Oppdatering Tilgjengelig - + An update is available. Would you like to install it now? - - - - - No Update Found - - - - - No update is found. - + En oppdatering er tilgjengelig. Ønsker du å installere den nå? - + No Update Found + Ingen Oppdatering Funnet + + + + No update is found. + Ingen oppdatering ble funnet. + + + + OpenGL 3.3 Unsupported + + + + + Your GPU may not support OpenGL 3.3, or you do not have the latest graphics driver. + + + + + Invalid ROM Format - + Ugyldig ROM Format - - + + Your ROM format is not supported.<br/>Please follow the guides to redump your <a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>game cartridges</a> or <a href='https://citra-emu.org/wiki/dumping-installed-titles/'>installed titles</a>. - + ROM-formatet støttes ikke.<br/>Følg veiledningene for å redumpe dine<a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>spillpatroner</a> eller <a href='https://citra-emu.org/wiki/dumping-installed-titles/'>installerte titler</a>. - + ROM Corrupted - + ROM Korrupt - + Your ROM is corrupted. <br/>Please follow the guides to redump your <a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>game cartridges</a> or <a href='https://citra-emu.org/wiki/dumping-installed-titles/'>installed titles</a>. - + ROM er ødelagt.<br/>Vennligst følg veiledningene for å dumpe din<a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>Spillpatronen</a> eller <a href='https://citra-emu.org/wiki/dumping-installed-titles/'>installerte titler</a>. - + ROM Encrypted - + ROM Kryptert - + Your ROM is encrypted. <br/>Please follow the guides to redump your <a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>game cartridges</a> or <a href='https://citra-emu.org/wiki/dumping-installed-titles/'>installed titles</a>. - + Din ROM er kryptert. <br/>Vennligst følg veiledningene får å dumpe <a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>spillpatroner</a> eller <a href='https://citra-emu.org/wiki/dumping-installed-titles/'>installerte titler</a>. - - + + Video Core Error - + Video Kjerne Feil - + An error has occured. Please <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>see the log</a> for more details. Ensure that you have the latest graphics drivers for your GPU. - + En feil har oppstått. Vennligst <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>se loggen</a> for flere detaljer. Kontroller at du har de nyeste grafikkdriverne for GPU. - + You are running default Windows drivers for your GPU. You need to install the proper drivers for your graphics card from the manufacturer's website. - + Du kjører standard Windows-drivere for din GPU. Du må installere de riktige driverne for grafikkortet ditt fra produsentens nettsted. - + Error while loading ROM! Feil ved lasting av ROM! - + An unknown error occured. Please see the log for more details. En ukjent feil oppstod. Vennligst se loggen for mer informasjon. - + Start Start - + Error Opening %1 Folder Feil ved Åpning av %1 Mappe - - + + Folder does not exist! Mappen eksistere ikke! - + Error Opening %1 Feil ved åpning av %1 - + Select Directory Velg Plassering - - 3DS Executable - 3DS Kjørbar + + 3DS Executable (%1);;All Files (*.*) + %1 is an identifier for the 3DS executable file extensions. + - - - All Files (*.*) - Alle Filer (*.*) - - - + Load File Last Fil - + Load Files Last Filer - + 3DS Installation File (*.CIA*) 3DS Installasjons Fil (*.CIA*) - + + All Files (*.*) + Alle Filer (*.*) + + + %1 has been installed successfully. %1 Ble installert vellykket. - + Unable to open File Kan ikke åpne Fil - + Could not open %1 Kunne ikke åpne %1 - + Installation aborted Installasjon avbrutt - + The installation of %1 was aborted. Please see the log for more details Installeringen av %1 ble avbrutt. Vennligst se logg for detaljer - + Invalid File Ugyldig Fil - + %1 is not a valid CIA %1 er ikke en gyldig CIA - + Encrypted File Kryptert Fil - + %1 must be decrypted before being used with Citra. A real 3DS is required. %1 må bli dekryptert før den kan bli brukt av Citra. En ekte 3DS er nødvendig. - + File not found Fil ikke funnet - + File "%1" not found Fil "%1" ble ikke funnet - - - + + + Continue Fortsett - + Missing Citra Account Mangler Citra Bruker - + You must link your Citra account to submit test cases.<br/>Go to Emulation &gt; Configure... &gt; Web to do so. + Du må koble din Citra-konto til å sende inn testtilfeller.<br/>Gå til Emulering &gt; Konfigurasjon... &gt; Web får å gjøre det. + + + + Amiibo File (%1);; All Files (*.*) - - - - Record Movie - - - - - - Citra TAS Movie (*.ctm) - - - - - Recording will start once you boot a game. - - - - - The movie file you are trying to load was created on a different revision of Citra.<br/>Citra has had some changes during the time, and the playback may desync or not work as expected.<br/><br/>Are you sure you still want to load the movie file? - - - - - The movie file you are trying to load was recorded with a different game.<br/>The playback may not work as expected, and it may cause unexpected results.<br/><br/>Are you sure you still want to load the movie file? - - - - - - The movie file you are trying to load is invalid.<br/>Either the file is corrupted, or Citra has had made some major changes to the Movie module.<br/>Please choose a different movie file and try again. - - - - - Revision Dismatch - - - - - Game Dismatch - - - - - - Invalid Movie File - - - - - Play Movie - - - - - Game Not Found - - - - - The movie you are trying to play is from a game that is not in the game list. If you own the game, please add the game folder to the game list and try to play the movie again. - - - - - Movie recording cancelled. + + Load Amiibo + + - Movie Saved + + Record Movie + Ta Opp Video + + + + To keep consistency with the RNG, it is recommended to record the movie from game start.<br>Are you sure you still want to record movies now? + + + + Citra TAS Movie (*.ctm) + Citra TAS Film (*.ctm) + - The movie is successfully saved. - + Recording will start once you boot a game. + Opptak starter når du starter et spill. + + + + The movie file you are trying to load was created on a different revision of Citra.<br/>Citra has had some changes during the time, and the playback may desync or not work as expected.<br/><br/>Are you sure you still want to load the movie file? + Filmen filen du prøver å laste ble opprettet på en annen revisjon av Citra.<br/>Citra har hatt noen endringer i løpet av tiden, og avspillingen kan de-synkroniseres eller ikke fungere som forventet.<br/><br/>Er du sikker på at du fortsatt vil laste video filen? + The movie file you are trying to load was recorded with a different game.<br/>The playback may not work as expected, and it may cause unexpected results.<br/><br/>Are you sure you still want to load the movie file? + video filen du prøver å laste ble tatt opp med et annet spill.<br/>Avspillingen fungerer kanskje ikke som forventet, og det kan føre til uventede resultater.<br/><br/>Er du sikker på at du fortsatt vil laste filmfilen? + + + + + The movie file you are trying to load is invalid.<br/>Either the file is corrupted, or Citra has had made some major changes to the Movie module.<br/>Please choose a different movie file and try again. + Filmfilen du prøver å laste er ugyldig.<br/>Enten er filen skadet, eller så har Citra har gjort noen store endringer i filmmodulen.<br/>Vennligst velg en annen filmfil og prøv igjen. + + + + Revision Dismatch + Revisjon Avvik + + + + Game Dismatch + Spill Avvik + + + + + Invalid Movie File + Ugyldig Video Fil + + + + + Play Movie + Spill Film + + + + To keep consistency with the RNG, it is recommended to play the movie from game start.<br>Are you sure you still want to play movies now? + + + + + Game Not Found + Spill Ikke Funnet + + + + The movie you are trying to play is from a game that is not in the game list. If you own the game, please add the game folder to the game list and try to play the movie again. + Filmen du prøver å spille er fra et spill som ikke er i spillelisten. Hvis du eier spillet, må du legge til spillemappen til spillelisten og prøve å spille av filmen igjen. + + + + Movie recording cancelled. + Filmopptak avbrutt. + + + + Movie Saved + Film Lagret + + + + The movie is successfully saved. + Filmen ble lagret vellykket. + + + Speed: %1% / %2% Fart: %1% / %2% - + Speed: %1% Fart: %1% - + Game: %1 FPS Spill: %1 FPS - + Frame: %1 ms Bilde: %1 ms - + %1 is missing. Please <a href='https://citra-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-3ds-console/'>dump your system archives</a>.<br/>Continuing emulation may result in crashes and bugs. - + %1 mangler. Vennligst <a href='https://citra-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-3ds-console/'>dump systemarkivene dine</a>.<br/>Fortsatt emulering kan føre til krasjer og feil. - + System Archive Not Found System Arkiv ikke funnet - + Fatal Error Fatal Feil - + A fatal error occured. <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>Check the log</a> for details.<br/>Continuing emulation may result in crashes and bugs. - + En fatal feil har oppstått. <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>Se loggen</a> for detaljer.<br/>Fortsatt emulering kan føre til krasjer og feil. - + Abort - + Avbryt - - + + Citra Citra - + Would you like to exit now? - + Vil du avslutte nå? - + The game is still running. Would you like to stop emulation? - + Spillet kjører fortsatt. Vil du stoppe emulering? - + Playback Completed - + Avspilling Fullført - + Movie playback completed. - + Filmavspilling fullført. - + Citra %1 Citra %1 - + Citra %1| %2 Citra %1| %2 @@ -2799,37 +3015,67 @@ p, li { white-space: pre-wrap; } GameList - + + Name + + + + + Compatibility + + + + + Region + + + + + File type + + + + + Size + + + + Open Save Data Location Åpne Lagringsdata Plassering - + + Open Extra Data Location + + + + Open Application Location Åpne applikasjons plassering - + Open Update Data Location Åpne Oppdateringdata Plassering - + Navigate to GameDB entry Naviger til GameDB oppføring - + Scan Subfolders Skann Undermapper - + Remove Game Directory Slett Spill Mappe - + Open Directory Location Fjern Mappe Plassering @@ -2837,85 +3083,90 @@ p, li { white-space: pre-wrap; } GameListItemCompat - + Perfect - + Perfekt - + Game functions flawless with no audio or graphical glitches, all tested functionality works as intended without any workarounds needed. - + Spillet fungerer feilfritt uten lyd- eller grafiske feil, alle testet funksjonalitet fungerer som tiltenkt uten +behov for midlertidige løsninger. - + Great - + Bra - + Game functions with minor graphical or audio glitches and is playable from start to finish. May require some workarounds. - + Spillet Fungerer med minimale grafiske- eller lyd feil og kan spilles fra start til slutt. Kan kreve noe +midlertidige løsninger. - + Okay - + Ok - + Game functions with major graphical or audio glitches, but game is playable from start to finish with workarounds. - + Spillet fungerer med omfattende grafiske- eller lyd feil, men er spillbart fra start til slutt med +midlertidige løsninger. - + Bad - + Dårlig - + Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches even with workarounds. - + Spillet fungerer, men med omfattende grafiske- eller lyd feil. Umulig med fremgang i spesifikke områder på grunn av feil +selv med midlertidige løsninger. - + Intro/Menu - + Intro/Meny - + Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start Screen. - + Spillet er helt uspillbart på grunn av store grafiske- eller lyd feil. Kan ikke gå videre forbi Start +Skjermen. - + Won't Boot - + Vil ikke starte opp - + The game crashes when attempting to startup. - + Spillet krasjer når du prøver å starte opp. - + Not Tested - + Ikke Testet - + The game has not yet been tested. - + Spillet har ikke blitt testet ennå. GameListPlaceholder - + Double-click to add a new folder to the game list Dobbelttrykk for å legge til en ny mappe til spilllisten @@ -2923,27 +3174,27 @@ Screen. GameListSearchField - + of av - + result Resultat - + results Resultater - + Filter: Filter: - + Enter pattern to filter Skriv inn mønster for å filtrere @@ -2951,23 +3202,23 @@ Screen. GraphicsBreakPointsWidget - + Pica Breakpoints Pica Bruddpunkt - - + + Emulation running Emulering kjører - + Resume Fortsett - + Emulation halted at breakpoint Emuleringen stoppet ved bruddpunktet @@ -3384,42 +3635,42 @@ Screen. Oppdater Lobby - + Password Required to Join Passord kreves for å bli med - + Password: Passord: - + Room Name Rom Navn - + Preferred Game Foretrukket Spill - + Host Vert - + Players Spillere - + Refreshing Oppdaterer - + Refresh List Oppdaterer Liste @@ -3442,220 +3693,245 @@ Screen. Nylige Filer - + + Amiibo + + + + &Emulation &Emulering - + &View &Vis - + Debugging Feilsøking - + Screen Layout Skjerm Oppsett - + Movie - + Video - + Multiplayer Flerspiller - + &Help &Hjelp - + Load File... Last inn fil... - + Install CIA... Installer CIA... - + Load Symbol Map... Last inn symbolkart... - + E&xit Avslutt - + &Start &Start - + &Pause &Pause - + &Stop &Stopp - + FAQ FAQ - + About Citra Om Citra - + Single Window Mode Enkelt vindu modus - + Configure... Konfigurer... - + Display Dock Widget Headers Vis Dock Widget Headere - + Show Filter Bar Vis filter linje - + Show Status Bar Vis status linje - + Select Game Directory... Velg spill plassering... - + Selects a folder to display in the game list Velg en mappe til å vise spilllisten - + Create Pica Surface Viewer Lag Pica overflate visning - + Record Movie - + Ta Opp Video - + Play Movie - + Spill Video - + Stop Recording / Playback + Stopp Innspilling / Avspilling + + + + Enable Frame Advancing - + + Advance Frame + + + + Browse Public Game Lobby Bla gjennom Offentlig Spill Lobby - + Create Room Opprett Rom - + Leave Room Forlat Rom - + Direct Connect to Room Koble Direkte til Rom - + Show Current Room Vis Nåværende Rom - + Fullscreen Fullskjerm - + Modify Citra Install Modifiser Citra Installasjon - + Opens the maintenance tool to modify your Citra installation Åpner vedlikeholdvertøyet for din Citra Installasjon - + Default Standard - + Single Screen Enkel Skjerm - + Large Screen Stor Skjerm - + Side by Side Side ved Side - + Swap Screens Bytt Skjerm - + Check for Updates Sjekk for oppdateringer - + Report Compatibility Rapporter Kompatibilitet - + Restart Omstart + + + Load... + + + + + Remove + + MicroProfileDialog @@ -3681,23 +3957,23 @@ Screen. - + Connected Tilkoblet - + Not Connected Ikke Tilkoblet - + Error Feil - + Failed to announce the room to the public lobby. In order to host a room publicly, you must have a valid Citra account configured in Emulation -> Configure -> Web. If you do not want to publish a room in the public lobby, then select Unlisted instead. Debug Message: Kunne ikke kunngjøre rommet til den offentlige lobbyen. For å være vert for et rom offentlig, må du ha en gyldig Citra-konto konfigurert i Emulering -> Konfigurering -> Web. Hvis du ikke vil publisere et rom i den offentlige lobbyen, velger du Unotert i stedet. @@ -3816,108 +4092,108 @@ Feilsøkingsmelding: %1 spiller %2 - - + + Invalid region Ugyldig region - + Japan Japan - + North America Nord Amerika - + Europe Europa - + Australia Australia - + China Kina - + Korea Korea - + Taiwan Taiwan - + Region free Sonefri - + Invalid Region Ugyldig Region - + Shift Shift - + Ctrl Ctrl - + Alt Alt - - + + [not set] [ikke bestemt] - + Hat %1 %2 - + Hat %1 %2 - + Axis %1%2 - + Akse %1%2 - + Button %1 - + Knapp %1 - - + + [unknown] [ukjent] - + [unused] [ubrukt] - - + + Axis %1 - + Akse %1 diff --git a/dist/languages/nl.ts b/dist/languages/nl.ts index a6e2b5660..289792550 100644 --- a/dist/languages/nl.ts +++ b/dist/languages/nl.ts @@ -70,45 +70,83 @@ p, li { white-space: pre-wrap; } BreakPointModel - + Pica command loaded Pica opdracht geladen - + Pica command processed Pica opdracht verwerkt - + Incoming primitive batch Inkomende primitieve batch - + Finished primitive batch Klaar met primitieve batch - + Vertex shader invocation Vertex shader-aanroep - + Incoming display transfer Inkomende schermoverdracht - + GSP command processed GSP opdracht verwerkt - + Buffers swapped Buffers verwisseld + + + Unknown debug context event + + + + + CalibrationConfigurationDialog + + + Communicating with the server... + + + + + Cancel + + + + + Touch the top left corner <br>of your touchpad. + + + + + Now touch the bottom right corner <br>of your touchpad. + + + + + Configuration completed! + + + + + OK + + ChatRoom @@ -128,15 +166,26 @@ p, li { white-space: pre-wrap; } Stuur Bericht - + Name - + Game + + + + Block Player + + + + + When you block a player, you will no longer receive chat messages from them.<br><br>Are you sure you would like to block %1? + + ClientRoom @@ -154,17 +203,17 @@ p, li { white-space: pre-wrap; } ClientRoomWindow - + Connected - + Disconnected - + %1 (%2/%3 members) - connected @@ -257,6 +306,26 @@ p, li { white-space: pre-wrap; } Thank you for your submission! + + + Submitting + + + + + Communication error + + + + + An error occured while sending the Testcase + + + + + Next + + ConfigureAudio @@ -296,9 +365,9 @@ p, li { white-space: pre-wrap; } - - - %1 % + + %1% + Volume percentage (e.g. 50%) @@ -519,88 +588,121 @@ p, li { white-space: pre-wrap; } GDB - - The GDB Stub only works correctly when the CPU JIT is off. - De GDB Stub werkt alleen correct als de CPU JIT is uitgeschakeld. - - - + Enable GDB Stub GBD Stub inschakelen - + Port: Poort: - + Logging - + Global Log Filter - + Show Log Console (Windows Only) - + Open Log Location + + + Miscellaneous + + + + + Enable CPU JIT + + ConfigureDialog - + Citra Configuration Citra configuratie - + + + General Algemeen - + + + System Systeem - + + + Input Invoer - + + + Graphics Beeldkwaliteit - + + + Audio Geluid - + + + Camera - + + + Debug Fouten opsporen - + + + Web Website + + + + + UI + + + + + Controls + + ConfigureGeneral @@ -620,74 +722,54 @@ p, li { white-space: pre-wrap; } Bevestig afsluiten terwijl emulatie bezig is - - Interface language - Interfacetaal - - - + Updates Updates - + Check for updates on start Zoeken naar updates bij opstarten - + Silently auto update after closing Auto update na sluiten in de achtergrond - - Performance - Prestaties - - - - Enable CPU JIT - CPU JIT inschakelen - - - + Emulation Emulatie - + Region: Regio: - + Auto-select Automatisch selecteren - - Theme - Thema - - - - Theme: - Thema: - - - + Hotkeys Sneltoetsen - - <System> - <System> + + Reset All Settings + - - English - Engels + + Citra + + + + + Are you sure you want to <b>reset your settings</b> and close Citra? + @@ -874,12 +956,17 @@ p, li { white-space: pre-wrap; } Scherm Verwisselen - - Hardware Shader Warning + + Background Color: + Hardware Shader Warning + + + + Hardware Shader support is broken on macOS, and will cause graphical issues like showing a black screen.<br><br>The option is only there for test/development purposes. If you experience graphical issues with Hardware Shader, please turn it off. @@ -982,7 +1069,7 @@ p, li { white-space: pre-wrap; } - + Set Analog Stick Analog Stick instellen @@ -1017,16 +1104,220 @@ p, li { white-space: pre-wrap; } Circle Mod: - + + Motion / Touch... + + + + + Clear All + + + + Restore Defaults Standaardwaarde herstellen - + + + Clear + + + + + + [not set] + + + + + + Restore Default + + + + + Information + + + + + After pressing OK, first move your joystick horizontally, and then vertically. + + + + [press key] [Druk op een toets] + + ConfigureMotionTouch + + + Configure Motion / Touch + + + + + Motion + + + + + Motion Provider: + + + + + Sensitivity: + + + + + Touch + + + + + Touch Provider: + + + + + Calibration: + + + + + (100, 50) - (1800, 850) + + + + + + Configure + + + + + CemuhookUDP Config + + + + + You may use any Cemuhook compatible UDP input source to provide motion and touch input. + + + + + Server: + + + + + Port: + + + + + Pad: + + + + + Pad 1 + + + + + Pad 2 + + + + + Pad 3 + + + + + Pad 4 + + + + + Learn More + + + + + + Test + + + + + Mouse (Right Click) + + + + + + CemuhookUDP + + + + + Emulator Window + + + + + <a href='https://citra-emu.org/wiki/using-a-controller-or-android-phone-for-motion-or-touch-input'><span style="text-decoration: underline; color:#039be5;">Learn More</span></a> + + + + + Testing + + + + + Configuring + + + + + Test Successful + + + + + Successfully received data from the server. + + + + + Test Failed + + + + + Could not receive valid data from the server.<br>Please verify that the server is set up correctly and the address and port are correct. + + + + + Citra + + + + + UDP Test or calibration configuration is in progress.<br>Please wait for them to finish. + + + ConfigureSystem @@ -1201,36 +1492,834 @@ p, li { white-space: pre-wrap; } + Country + + + + + Clock + + + + + System Clock + + + + + Fixed Time + + + + + Startup time + + + + + yyyy-MM-ddTHH:mm:ss + + + + + Play Coins: + + + + Console ID: Console ID: - + Regenerate Herstellen - + System settings are available only when game is not running. Systeeminstellingen zijn alleen beschikbaar wanneer er geen spel actief is. - + + Japan + + + + + Anguilla + + + + + Antigua and Barbuda + + + + + Argentina + + + + + Aruba + + + + + Bahamas + + + + + Barbados + + + + + Belize + + + + + Bolivia + + + + + Brazil + + + + + British Virgin Islands + + + + + Canada + + + + + Cayman Islands + + + + + Chile + + + + + Colombia + + + + + Costa Rica + + + + + Dominica + + + + + Dominican Republic + + + + + Ecuador + + + + + El Salvador + + + + + French Guiana + + + + + Grenada + + + + + Guadeloupe + + + + + Guatemala + + + + + Guyana + + + + + Haiti + + + + + Honduras + + + + + Jamaica + + + + + Martinique + + + + + Mexico + + + + + Montserrat + + + + + Netherlands Antilles + + + + + Nicaragua + + + + + Panama + + + + + Paraguay + + + + + Peru + + + + + Saint Kitts and Nevis + + + + + Saint Lucia + + + + + Saint Vincent and the Grenadines + + + + + Suriname + + + + + Trinidad and Tobago + + + + + Turks and Caicos Islands + + + + + United States + + + + + Uruguay + + + + + US Virgin Islands + + + + + Venezuela + + + + + Albania + + + + + Australia + + + + + Austria + + + + + Belgium + + + + + Bosnia and Herzegovina + + + + + Botswana + + + + + Bulgaria + + + + + Croatia + + + + + Cyprus + + + + + Czech Republic + + + + + Denmark + + + + + Estonia + + + + + Finland + + + + + France + + + + + Germany + + + + + Greece + + + + + Hungary + + + + + Iceland + + + + + Ireland + + + + + Italy + + + + + Latvia + + + + + Lesotho + + + + + Liechtenstein + + + + + Lithuania + + + + + Luxembourg + + + + + Macedonia + + + + + Malta + + + + + Montenegro + + + + + Mozambique + + + + + Namibia + + + + + Netherlands + + + + + New Zealand + + + + + Norway + + + + + Poland + + + + + Portugal + + + + + Romania + + + + + Russia + + + + + Serbia + + + + + Slovakia + + + + + Slovenia + + + + + South Africa + + + + + Spain + + + + + Swaziland + + + + + Sweden + + + + + Switzerland + + + + + Turkey + + + + + United Kingdom + + + + + Zambia + + + + + Zimbabwe + + + + + Azerbaijan + + + + + Mauritania + + + + + Mali + + + + + Niger + + + + + Chad + + + + + Sudan + + + + + Eritrea + + + + + Djibouti + + + + + Somalia + + + + + Andorra + + + + + Gibraltar + + + + + Guernsey + + + + + Isle of Man + + + + + Jersey + + + + + Monaco + + + + + Taiwan + + + + + South Korea + + + + Hong Kong + + + + + Macau + + + + + Indonesia + + + + + Singapore + + + + + Thailand + + + + + Philippines + + + + + Malaysia + + + + + China + + + + + United Arab Emirates + + + + + India + + + + + Egypt + + + + + Oman + + + + + Qatar + + + + + Kuwait + + + + + Saudi Arabia + + + + + Syria + + + + + Bahrain + + + + + Jordan + + + + + San Marino + + + + + Vatican City + + + + + Bermuda + + + + + Console ID: 0x%1 Console ID: 0x%1 - + This will replace your current virtual 3DS with a new one. Your current virtual 3DS will not be recoverable. This might have unexpected effects in games. This might fail, if you use an outdated config savegame. Continue? Dit zal je huidige virtuele 3DS vervangen met een nieuwe. Je huidige virtuele 3DS is niet herstelbaar. Dit kan onverwachte effecten hebben in spellen. Dit kan mislukken, als je een verouderd configuratie opslagbestand gebruikt. Doorgaan? - + Warning Waarschuwing + + ConfigureUi + + + Form + + + + + General + + + + + Interface language: + + + + + Theme: + + + + + Game List + + + + + Icon Size: + + + + + + None + + + + + Small (24x24) + + + + + Large (48x48) + + + + + Row 1 Text: + + + + + + File Name + + + + + + Full Path + + + + + + Title Name + + + + + + Title ID + + + + + Row 2 Text: + + + + + Hide Titles without Icon + + + + + <System> + + + + + English + + + ConfigureWeb @@ -1250,7 +2339,7 @@ p, li { white-space: pre-wrap; } - + Verify Verifiëren @@ -1300,48 +2389,58 @@ p, li { white-space: pre-wrap; } Herstellen - + + Discord Presence + + + + + Show Current Game in your Discord Status + + + + <a href='https://citra-emu.org/entry/telemetry-and-why-thats-a-good-thing/'><span style="text-decoration: underline; color:#039be5;">Learn more</span></a> - - <a href='https://services.citra-emu.org/'><span style="text-decoration: underline; color:#039be5;">Sign up</span></a> + + <a href='https://profile.citra-emu.org/'><span style="text-decoration: underline; color:#039be5;">Sign up</span></a> - + <a href='https://citra-emu.org/wiki/citra-web-service/'><span style="text-decoration: underline; color:#039be5;">What is my token?</span></a> - - + + Telemetry ID: 0x%1 Telemetrie ID: 0x%1 - + Username and token not verified - + Username and token were not verified. The changes to your username and/or token have not been saved. Gebruikersnaam en token zijn niet geverifieerd. De veranderingen aan je gebruikersnaam en/of token zijn niet opgeslagen. - + Verifying Verifiëren - + Verification failed Verificatie mislukt - + Verification failed. Check that you have entered your username and token correctly, and that your internet connection is working. Verificatie mislukt. Kijk of je je gebruikersnaam en token goed hebt ingevuld, en dat je internetverbinding werkt. @@ -1402,12 +2501,12 @@ p, li { white-space: pre-wrap; } DirectConnectWindow - + Connecting - + Connect @@ -1415,310 +2514,445 @@ p, li { white-space: pre-wrap; } GMainWindow - - To help improve Citra, the Citra Team collects anonymous usage data. No private or personally identifying information is collected. This data helps us to understand how people use Citra and prioritize our efforts. Furthermore, it helps us to more easily identify emulation bugs and performance issues. This data includes:<ul><li>Information about the version of Citra you are using</li><li>Performance data about the games you play</li><li>Your configuration settings</li><li>Information about your computer hardware</li><li>Emulation errors and crash information</li></ul>By default, this feature is enabled. To disable this feature, click 'Emulation' from the menu and then select 'Configure...'. Then, on the 'Web' tab, uncheck 'Share anonymous usage data with the Citra team'. <br/><br/>By using this software, you agree to the above terms.<br/><br/><a href='https://citra-emu.org/entry/telemetry-and-why-thats-a-good-thing/'>Learn more</a> - Om Citra te verbeteren, zal het Citra team anonieme gebruiksdata verzamelen. Geen privé of persoonlijk identificerende informatie zal worden verzameld. Deze data zal ons helpen begrijpen hoe de mensen Citra gebruiken en onze inspanningen te prioriteren. Daarnaast zal het ons helpen makkelijker emulatiebugs en prestatieproblemen te identificeren. Onder deze data valt:<ul><li>Informatie over de versie van Citra die je gebruikt</li><li>Prestatiedata over de spellen die je speelt</li><li>Je configuratieinstellingen</li><li>Informatie over je computerhardware</li><li>Emulatie errors en crashinformatie</li></ul>Standaard is deze optie ingeschakeld. Om deze optie uit te schakelen, klik op 'Emulatie' vanuit het menu en kies 'Configuratie...'. Dan in de 'Web' tab, deselecteer 'Deel anonieme gebruiksdata met het Citra team'. <br/><br/>Door deze software te gebruiker, ga je akkoord met de bovenstaande voorwaardes.<br/><br/><a href='https://citra-emu.org/entry/telemetry-and-why-thats-a-good-thing/'>Leer meer</a> + + <a href='https://citra-emu.org/entry/telemetry-and-why-thats-a-good-thing/'>Anonymous data is collected</a> to help improve Citra. <br/><br/>Would you like to share your usage data with us? + - + + Telemetry + + + + + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a 3DS. Huidige emulatiesnelheid. Waardes hoger of lager dan 100% geven aan dat de emulatie sneller of langzamer gaat dan een 3DS. - + + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. Hoeveel frames per seconde het spel nu momenteel laat zien. Dit kan variëren van spel tot spel en scene tot scene. - + + Time taken to emulate a 3DS frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. Tijd verstrekt om één 3DS frame te emuleren, zonder framelimitatie of V-Sync te tellen. Voor volledige snelheid emulatie zal dit maximaal 16.67 ms moeten zijn. - + + Clear Recent Files + + + + F9 - + F10 - + CTRL+F CTRL+F - - Update available! - Update beschikbaar! + + Update Available + - - An update for Citra is available. Do you wish to install it now?<br /><br />This <b>will</b> terminate emulation, if it is running. - Een update voor Citra is beschikbaar. Wil je die nu installeren?<br /><br />Dit <b>zal</b> de huidige emulatie stoppen, als deze bezig is. + + An update is available. Would you like to install it now? + - - No update found - Geen update gevonden + + No Update Found + - - No update has been found for Citra. - Geen update voor Citra is gevonden. + + No update is found. + - - Error while initializing OpenGL 3.3 Core! - Fout bij het initialiseren van OpenGL 3.3 Core! + + OpenGL 3.3 Unsupported + - + Your GPU may not support OpenGL 3.3, or you do not have the latest graphics driver. - Je GPU ondersteunt OpenGL 3.3 misschien niet, of je hebt niet de laatste grafische stuurprogramma's. + - - - - - + + + Invalid ROM Format + + + + + + Your ROM format is not supported.<br/>Please follow the guides to redump your <a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>game cartridges</a> or <a href='https://citra-emu.org/wiki/dumping-installed-titles/'>installed titles</a>. + + + + + ROM Corrupted + + + + + Your ROM is corrupted. <br/>Please follow the guides to redump your <a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>game cartridges</a> or <a href='https://citra-emu.org/wiki/dumping-installed-titles/'>installed titles</a>. + + + + + ROM Encrypted + + + + + Your ROM is encrypted. <br/>Please follow the guides to redump your <a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>game cartridges</a> or <a href='https://citra-emu.org/wiki/dumping-installed-titles/'>installed titles</a>. + + + + + + Video Core Error + + + + + An error has occured. Please <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>see the log</a> for more details. Ensure that you have the latest graphics drivers for your GPU. + + + + + You are running default Windows drivers for your GPU. You need to install the proper drivers for your graphics card from the manufacturer's website. + + + + Error while loading ROM! Fout tijdens het laden van de ROM! - - - The ROM format is not supported. - De ROM-formaat is niet ondersteund. - - - - Could not determine the system mode. - Kon de systeemmodus niet vaststellen. - - - - The game that you are trying to load must be decrypted before being used with Citra. A real 3DS is required.<br/><br/>For more information on dumping and decrypting games, please see the following wiki pages: <ul><li><a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>Dumping Game Cartridges</a></li><li><a href='https://citra-emu.org/wiki/dumping-installed-titles/'>Dumping Installed Titles</a></li></ul> - Het spel dat je probeert te laden moet eerst gedecodeerd worden voordat je het kan gebruiken met Citra. Een echte 3DS is vereist.<br/><br/>Voor meer informatie over dumping en decoderen van spellen, zie de volgende wiki-pagina's: <ul><li><a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>Dumping Game Cartridges</a></li><li><a href='https://citra-emu.org/wiki/dumping-installed-titles/'>Dumping Installed Titles</a></li></ul> - - - - An error occured in the video core. - Een fout in de video core is opgetreden. - - - - Citra has encountered an error while running the video core, please see the log for more details.For more information on accessing the log, please see the following page: <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>How to Upload the Log File</a>.Ensure that you have the latest graphics drivers for your GPU. - Citra heeft een fout aangetroffen tijdens het uitvoeren van de video core, raadpleeg het logboek voor meer informatie. Raadpleeg de volgende pagina voor meer informatie over het openen van het logboek: <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>How to Upload the Log File</a>. Zorg dat je beschikt over de nieuwste grafische stuurprogramma's voor uw GPU. - - - + An unknown error occured. Please see the log for more details. Er is een onbekende fout opgetreden. Zie het logboek voor meer details. - + Start Start - + Error Opening %1 Folder - - + + Folder does not exist! Map bestaat niet! - + Error Opening %1 - + Select Directory Selecteer Folder - - 3DS Executable - 3DS Executable + + 3DS Executable (%1);;All Files (*.*) + %1 is an identifier for the 3DS executable file extensions. + - - - All Files (*.*) - Alle bestanden (*.*) - - - + Load File Laad bestand - + Load Files - + 3DS Installation File (*.CIA*) 3DS Installatie bestand (*.CIA*) - + + All Files (*.*) + Alle bestanden (*.*) + + + %1 has been installed successfully. - + Unable to open File Kan bestand niet openen - + Could not open %1 - + Installation aborted Installatie onderbroken - + The installation of %1 was aborted. Please see the log for more details - + Invalid File Ongeldig bestand - + %1 is not a valid CIA - + Encrypted File Versleuterd bestand - + %1 must be decrypted before being used with Citra. A real 3DS is required. - + File not found Bestand niet gevonden - + File "%1" not found Bestand "%1" niet gevonden - + + + Continue Doorgaan - + Missing Citra Account - - In order to submit a game compatibility test case, you must link your Citra account.<br><br/>To link your Citra account, go to Emulation &gt; Configuration &gt; Web. + + You must link your Citra account to submit test cases.<br/>Go to Emulation &gt; Configure... &gt; Web to do so. - + + Amiibo File (%1);; All Files (*.*) + + + + + Load Amiibo + + + + + + + + Record Movie + + + + + To keep consistency with the RNG, it is recommended to record the movie from game start.<br>Are you sure you still want to record movies now? + + + + + + Citra TAS Movie (*.ctm) + + + + + Recording will start once you boot a game. + + + + + The movie file you are trying to load was created on a different revision of Citra.<br/>Citra has had some changes during the time, and the playback may desync or not work as expected.<br/><br/>Are you sure you still want to load the movie file? + + + + + The movie file you are trying to load was recorded with a different game.<br/>The playback may not work as expected, and it may cause unexpected results.<br/><br/>Are you sure you still want to load the movie file? + + + + + + The movie file you are trying to load is invalid.<br/>Either the file is corrupted, or Citra has had made some major changes to the Movie module.<br/>Please choose a different movie file and try again. + + + + + Revision Dismatch + + + + + Game Dismatch + + + + + + Invalid Movie File + + + + + + Play Movie + + + + + To keep consistency with the RNG, it is recommended to play the movie from game start.<br>Are you sure you still want to play movies now? + + + + + Game Not Found + + + + + The movie you are trying to play is from a game that is not in the game list. If you own the game, please add the game folder to the game list and try to play the movie again. + + + + + Movie recording cancelled. + + + + + Movie Saved + + + + + The movie is successfully saved. + + + + Speed: %1% / %2% - + Speed: %1% Snelheid: %1% - + Game: %1 FPS Spel: %1 FPS - + Frame: %1 ms Frame: %1 ms - - The game you are trying to load requires additional files from your 3DS to be dumped before playing.<br/><br/>For more information on dumping these files, please see the following wiki page: <a href='https://citra-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-3ds-console/'>Dumping System Archives and the Shared Fonts from a 3DS Console</a>.<br/><br/>Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs. - Het spel dat je probeert te laden vereist dat er extra bestanden van uw 3DS worden gedumpt voordat je kan spelen.<br/><br/>Voor meer informatie over dumping van deze bestanden, kijk dan naar de volgende wiki pagina: <a href='https://citra-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-3ds-console/'>Dumping System Archives and the Shared Fonts from a 3DS Console</a>.<br/><br/>Wil je afsluiten en terug gaan naar de lijst met spellen? Doorgaan met de emulatie kan crashes, beschadigde save data of andere bugs veroorzaken. + + %1 is missing. Please <a href='https://citra-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-3ds-console/'>dump your system archives</a>.<br/>Continuing emulation may result in crashes and bugs. + - - : %1. - : %1. - - - + System Archive Not Found Systeem archief niet gevonden - - Citra was unable to locate the 3DS shared fonts. - Citra was kon de locatie van de 3DS gedeelde fonts niet vinden. - - - - Shared Fonts Not Found - Gedeelde Fonts Niet Gevonden - - - + Fatal Error Fatale Fout - - Citra has encountered a fatal error, please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>How to Upload the Log File</a>.<br/><br/>Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs. - Citra heeft een fatale fout aangetroffen, raadpleeg het logboek voor meer informatie. Raadpleeg de volgende pagina voor meer informatie over het openen van het logboek: <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>How to Upload the Log File</a>.<br/><br/>Wil je afsluiten en terug gaan naar de lijst met spellen? Doorgaan met de emulatie kan crashes, beschadigde save data of andere bugs veroorzaken. + + A fatal error occured. <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>Check the log</a> for details.<br/>Continuing emulation may result in crashes and bugs. + - - + + Abort + + + + + Citra Citra - - Are you sure you want to close Citra? - Weet je zeker dat je Citra wilt afsluiten? + + Would you like to exit now? + - - Are you sure you want to stop the emulation? Any unsaved progress will be lost. - Weet je zeker dat je de emulatie wilt stoppen? Alle niet opgeslagen voortgang zal verloren gaan. + + The game is still running. Would you like to stop emulation? + - + + Playback Completed + + + + + Movie playback completed. + + + + Citra %1 - + Citra %1| %2 @@ -1781,146 +3015,205 @@ p, li { white-space: pre-wrap; } GameList - + + Name + + + + + Compatibility + + + + + Region + + + + + File type + + + + + Size + + + + + Open Save Data Location + Open opslag data locatie + + + + Open Extra Data Location + + + + + Open Application Location + + + + + Open Update Data Location + + + + + Navigate to GameDB entry + + + + + Scan Subfolders + + + + + Remove Game Directory + + + + + Open Directory Location + + + + + GameListItemCompat + + Perfect - + Game functions flawless with no audio or graphical glitches, all tested functionality works as intended without any workarounds needed. - + Great - + Game functions with minor graphical or audio glitches and is playable from start to finish. May require some workarounds. - + Okay - + Game functions with major graphical or audio glitches, but game is playable from start to finish with workarounds. - + Bad - + Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches even with workarounds. - + Intro/Menu - + Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start Screen. - + Won't Boot - + The game crashes when attempting to startup. - + Not Tested - + The game has not yet been tested. + + + GameListPlaceholder - - of - van de + + Double-click to add a new folder to the game list + + + + GameListSearchField - - result - Resultaat + + of + - results - Resultaten + result + - + + results + + + + Filter: - + Enter pattern to filter - - - Open Save Data Location - Open opslag data locatie - - - - Open Application Location - - - - - Open Update Data Location - - - - - Navigate to GameDB entry - - GraphicsBreakPointsWidget - + Pica Breakpoints Pica breekpunten - - + + Emulation running Emulatie loopt - + Resume Hervatten - + Emulation halted at breakpoint Emulatie gestopt op breekpunt @@ -1928,122 +3221,122 @@ Screen. GraphicsSurfaceWidget - + Pica Surface Viewer Pica Surface Viewer - + Color Buffer Kleur Buffer - + Depth Buffer Diepte Buffer - + Stencil Buffer Stencil Buffer - + Texture 0 Structuur 0 - + Texture 1 Structuur 1 - + Texture 2 Structuur 2 - + Custom Aangepast - + Unknown Onbekend - + Save Opslaan - + Source: Bron: - + Physical Address: Fysiek Adres: - + Width: Breedte: - + Height: Hoogte: - + Format: Formaat: - + X: X: - + Y: Y: - + Pixel out of bounds Pixel buiten bereik - + (unable to access pixel data) (geen toegang tot pixel data) - + (invalid surface address) (ongeldig surface adres) - + (unknown surface format) (onbekend surface formaat) - + Portable Network Graphic (*.png) Portable Network Graphic (*.png) - + Binary data (*.bin) Binary data (*.bin) - + Save Surface Surface opslaan @@ -2290,6 +3583,14 @@ Screen. + + LLEServiceModulesWidget + + + Toggle LLE Service Modules + + + Lobby @@ -2329,42 +3630,42 @@ Screen. - + Password Required to Join - + Password: - + Room Name - + Preferred Game - + Host - + Players - + Refreshing - + Refresh List @@ -2387,195 +3688,245 @@ Screen. Recente Bestanden - + + Amiibo + + + + &Emulation &Emulatie - + &View &Beeld - + Debugging Foutopsporing - + Screen Layout - + + Movie + + + + Multiplayer - + &Help &Help - + Load File... Laad Bestand... - + Install CIA... CIA Installeren... - + Load Symbol Map... Symbool Map Laden... - + E&xit &Afsluiten - + &Start &Start - + &Pause &Pauzeren - + &Stop &Stop - + FAQ FAQ - + About Citra Over Citra - + Single Window Mode Enkel Scherm Modus - + Configure... Configureren... - + Display Dock Widget Headers Dock Widget Headers Tonen - + Show Filter Bar Filter Bar Tonen - + Show Status Bar Status Bar Tonen - + Select Game Directory... Selecteer Spel Folder... - + Selects a folder to display in the game list Selecteert een folder om te tonen in de spellen lijst - + Create Pica Surface Viewer Pica Surface Viewer Maken - + + Record Movie + + + + + Play Movie + + + + + Stop Recording / Playback + + + + + Enable Frame Advancing + + + + + Advance Frame + + + + Browse Public Game Lobby - + Create Room - + Leave Room - + Direct Connect to Room - + Show Current Room - + Fullscreen Volledig Scherm - + Modify Citra Install Citra Installatie Aanpassen - + Opens the maintenance tool to modify your Citra installation Opent de onderhouds-tool om de Citra installatie aan te passen - + Default - + Single Screen - + Large Screen - + Side by Side - + Swap Screens - + Check for Updates Zoeken naar Updates - + Report Compatibility + + + Restart + + + + + Load... + + + + + Remove + + MicroProfileDialog @@ -2589,31 +3940,35 @@ Screen. MultiplayerState + Current connection status + Not Connected. Click here to find a room! - + + Connected - + + Not Connected - + Error - + Failed to announce the room to the public lobby. In order to host a room publicly, you must have a valid Citra account configured in Emulation -> Configure -> Web. If you do not want to publish a room in the public lobby, then select Unlisted instead. Debug Message: @@ -2731,114 +4086,108 @@ Debug Message: - - + + Invalid region - + Japan - + North America - + Europe - + Australia - + China - + Korea - + Taiwan - + Region free - + Invalid Region - + Shift Shift - + Ctrl Ctrl - + Alt Alt - - + + [not set] [niet ingesteld] - - - Joystick %1 - Joystick %1 + + Hat %1 %2 + - - Hat %1 %2 - Hat %1 %2 + + Axis %1%2 + - - Axis %1%2 - Axis %1%2 + + Button %1 + - - Button %1 - Knop %1 - - - + [unknown] [onbekend] - + [unused] [ongebruikt] - Axis %1 - Axis %1 + Axis %1 + @@ -2930,7 +4279,7 @@ Debug Message: WaitTreeEvent - + reset type = %1 reset type = %1 @@ -2938,12 +4287,12 @@ Debug Message: WaitTreeMutex - + locked %1 times by thread: %1 keer gesloten door thread: - + free vrij @@ -2951,7 +4300,7 @@ Debug Message: WaitTreeMutexList - + holding mutexes mutexes worden vasthouden @@ -2959,12 +4308,12 @@ Debug Message: WaitTreeObjectList - + waiting for all objects wachten op alle objecten - + waiting for one of the following objects wachten op een van de volgende objecten @@ -2972,12 +4321,12 @@ Debug Message: WaitTreeSemaphore - + available count = %1 beschikbare aantal = %1 - + max count = %1 maximale aantal = %1 @@ -2985,102 +4334,102 @@ Debug Message: WaitTreeThread - + running lopend - + ready Gereed - + waiting for address 0x%1 Wachten op adres 0x%1 - + sleeping sluimeren - + waiting for IPC response wachten op IPC antwoord - + waiting for objects wachten op objecten - + waiting for HLE return wachten op HLE teruggave - + dormant slapend - + dead dood - + PC = 0x%1 LR = 0x%2 PC = 0x%1 LR = 0x%2 - + default standaard - + all alle - + AppCore AppCore - + SysCore SysCore - + Unknown processor %1 Onbekende processor %1 - + processor = %1 processor = %1 - + thread id = %1 thread id = %1 - + priority = %1(current) / %2(normal) prioriteit = %1(huidige) / %2(normaal) - + last running ticks = %1 laatste lopende tikken = %1 - + not holding mutex geen mutex wordt vastgehouden @@ -3088,7 +4437,7 @@ Debug Message: WaitTreeThreadList - + waited by thread gepauzeerd door thread @@ -3096,17 +4445,17 @@ Debug Message: WaitTreeTimer - + reset type = %1 reset type = %1 - + initial delay = %1 Initiële vertraging = %1 - + interval delay = %1 interval vertraging = %1 @@ -3114,27 +4463,27 @@ Debug Message: WaitTreeWaitObject - + [%1]%2 %3 [%1]%2 %3 - + waited by no thread gepauzeerd door geen thread - + one shot one shot - + sticky sticky - + pulse pulse @@ -3142,7 +4491,7 @@ Debug Message: WaitTreeWidget - + Wait Tree Wait Tree diff --git a/dist/languages/pl_PL.ts b/dist/languages/pl_PL.ts index 1c8a4ae33..337dff20e 100644 --- a/dist/languages/pl_PL.ts +++ b/dist/languages/pl_PL.ts @@ -70,45 +70,50 @@ p, li { white-space: pre-wrap; } BreakPointModel - + Pica command loaded Polecenie Pica załadowane - + Pica command processed Polecenie Pica wykonane - + Incoming primitive batch Przychodząca prymitywna partia - + Finished primitive batch Zakończona prymitywna partia - + Vertex shader invocation Wywołanie cienieniowania Vertex - + Incoming display transfer Przychodzący transfer ekranu - + GSP command processed Polecenie GSP wykonane - + Buffers swapped Bufory zamienione + + + Unknown debug context event + + CalibrationConfigurationDialog @@ -171,13 +176,13 @@ p, li { white-space: pre-wrap; } Gra - - + + Block Player Zablokuj Gracza - + When you block a player, you will no longer receive chat messages from them.<br><br>Are you sure you would like to block %1? Jeżeli zablokujesz gracza, nie będziesz więcej otrzymywał wiadomości od niego.<br><br>Jesteś pewien, że chcesz zablokować %1? @@ -198,17 +203,17 @@ p, li { white-space: pre-wrap; } ClientRoomWindow - + Connected Połączono - + Disconnected Rozłączono - + %1 (%2/%3 members) - connected %1 (%2/%3 członków) - połączonych @@ -301,6 +306,26 @@ p, li { white-space: pre-wrap; } Thank you for your submission! Dziękujemy za zgłoszenie! + + + Submitting + + + + + Communication error + + + + + An error occured while sending the Testcase + + + + + Next + + ConfigureAudio @@ -340,10 +365,10 @@ p, li { white-space: pre-wrap; } 0 % - - - %1 % - %1 % + + %1% + Volume percentage (e.g. 50%) + %1% @@ -606,50 +631,78 @@ p, li { white-space: pre-wrap; } ConfigureDialog - + Citra Configuration Konfiguracja Citry - + + + General Ogólne - + + + System System - + + + Input Sterowanie - + + + Graphics Obraz - + + + Audio Dźwięk - + + + Camera Kamera - + + + Debug Debug - + + + Web Sieć + + + + + UI + + + + + Controls + Sterowanie + ConfigureGeneral @@ -669,64 +722,54 @@ p, li { white-space: pre-wrap; } Potwierdź wyjście, gdy emulator jest uruchomiony - - Interface language - Język Interfejsu - - - + Updates Aktualizacje - + Check for updates on start Sprawdź dostępność aktualizacji przy uruchomieniu - + Silently auto update after closing Automatycznie aktualizuj w tle po zamknięciu - + Emulation Emulacja - + Region: Region: - + Auto-select Wybór automatyczny - - Theme - Motyw - - - - Theme: - Motyw: - - - + Hotkeys Skróty klawiszowe - - <System> - <System> + + Reset All Settings + - - English - Angielski + + Citra + + + + + Are you sure you want to <b>reset your settings</b> and close Citra? + @@ -1026,7 +1069,7 @@ p, li { white-space: pre-wrap; } - + Set Analog Stick Ustaw Analog @@ -1067,11 +1110,44 @@ p, li { white-space: pre-wrap; } + Clear All + Wyczyść Wszystko + + + Restore Defaults Przywróć Domyślne - + + + Clear + Wyczyść + + + + + [not set] + [nieustawione] + + + + + Restore Default + Przywróć Domyślne + + + + Information + Informacja + + + + After pressing OK, first move your joystick horizontally, and then vertically. + Po naciśnięciu OK, wykonaj ruch analogiem horyzontalnie, a następnie wertykalnie. + + + [press key] [naciśnij przycisk] @@ -1440,17 +1516,27 @@ p, li { white-space: pre-wrap; } Czas Uruchomienia - + + yyyy-MM-ddTHH:mm:ss + + + + + Play Coins: + Monety Gry: + + + Console ID: ID konsoli: - + Regenerate Regeneruj - + System settings are available only when game is not running. Ustawienia systemowe są dostępne tylko wtedy, gdy gra nie jest uruchomiona @@ -2120,22 +2206,120 @@ p, li { white-space: pre-wrap; } Bermudy - - + + Console ID: 0x%1 ID konsoli: 0x%1 - + This will replace your current virtual 3DS with a new one. Your current virtual 3DS will not be recoverable. This might have unexpected effects in games. This might fail, if you use an outdated config savegame. Continue? To zastąpi twojego obecnego wirtualnego 3DS. Odzyskanie twojego obecnego 3DS będzie niemożliwe. To może spowodować niespodziewane efekty w grach. Operacja może się nie powieść jeżeli korzystasz z przestarzałej konfiguracji zapisów gier. Kontynuować? - + Warning Ostrzeżenie + + ConfigureUi + + + Form + + + + + General + + + + + Interface language: + + + + + Theme: + + + + + Game List + + + + + Icon Size: + + + + + + None + + + + + Small (24x24) + + + + + Large (48x48) + + + + + Row 1 Text: + + + + + + File Name + + + + + + Full Path + + + + + + Title Name + + + + + + Title ID + + + + + Row 2 Text: + + + + + Hide Titles without Icon + + + + + <System> + + + + + English + + + ConfigureWeb @@ -2155,7 +2339,7 @@ p, li { white-space: pre-wrap; } - + Verify Zweryfikuj @@ -2215,48 +2399,48 @@ p, li { white-space: pre-wrap; } Pokaż obecnie włączoną grę w statusie na Discrodzie - + <a href='https://citra-emu.org/entry/telemetry-and-why-thats-a-good-thing/'><span style="text-decoration: underline; color:#039be5;">Learn more</span></a> <a href='https://citra-emu.org/entry/telemetry-and-why-thats-a-good-thing/'><span style="text-decoration: underline; color:#039be5;">Dowiedz się więcej</span></a> - - <a href='https://services.citra-emu.org/'><span style="text-decoration: underline; color:#039be5;">Sign up</span></a> - <a href='https://services.citra-emu.org/'><span style="text-decoration: underline; color:#039be5;">Zarejestruj się</span></a> + + <a href='https://profile.citra-emu.org/'><span style="text-decoration: underline; color:#039be5;">Sign up</span></a> + - + <a href='https://citra-emu.org/wiki/citra-web-service/'><span style="text-decoration: underline; color:#039be5;">What is my token?</span></a> <a href='https://citra-emu.org/wiki/citra-web-service/'><span style="text-decoration: underline; color:#039be5;">Czym jest mój token?</span></a> - - + + Telemetry ID: 0x%1 ID Telemetrii: 0x%1 - + Username and token not verified Nazwa Użytkownika i Token nie są zweryfikowane - + Username and token were not verified. The changes to your username and/or token have not been saved. Nazwa Użytkownika i Token nie zostały zweryfikowane. Zmiana Nazwy Użytkownika i/lub Tokenu nie została zapisana - + Verifying Weryfikowanie - + Verification failed Weryfikacja nieudana - + Verification failed. Check that you have entered your username and token correctly, and that your internet connection is working. Weryfikacja nieudana. Sprawdź czy wprowadziłeś swoją nazwę użytkownika i swój token prawidłowo, a także czy twoje połączenie internetowe jest sprawne. @@ -2317,12 +2501,12 @@ p, li { white-space: pre-wrap; } DirectConnectWindow - + Connecting Łączenie - + Connect Połączono @@ -2330,413 +2514,445 @@ p, li { white-space: pre-wrap; } GMainWindow - + <a href='https://citra-emu.org/entry/telemetry-and-why-thats-a-good-thing/'>Anonymous data is collected</a> to help improve Citra. <br/><br/>Would you like to share your usage data with us? <a href='https://citra-emu.org/entry/telemetry-and-why-thats-a-good-thing/'>Anonimowe dane są zbierane</a> w celu wsparcia rozwoju Citry. <br/><br/>Czy chciałbyś się z nami podzielić danymi użytkowania? - + Telemetry Telemetria - - + + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a 3DS. Obecna szybkość emulacji. Wartości większe lub mniejsze niż 100 % oznaczają, że emulacja jest szybsza lub wolniejsza niż 3DS - - + + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. Jak wiele klatek na sekundę gra wyświetla w tej chwili. Ta wartość będzie się różniła między grami, jak również między scenami w grze. - - + + Time taken to emulate a 3DS frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. Czas potrzebny do emulacji klatki 3DS, nie zawiera limitowania klatek oraz v-sync. Dla pełnej prędkości emulacji, wartość nie powinna przekraczać 16.67 ms. - + Clear Recent Files Wyczyść Ostatnio Używane - + F9 F9 - + F10 F10 - + CTRL+F CTRL+F - + Update Available Aktualizacja dostępna - + An update is available. Would you like to install it now? Aktualizacja jest dostępna. Chciałbyś ją teraz zainstalować? - + No Update Found Nie znaleziono aktualizacji - + No update is found. Nie znaleziono aktualizacji - - + + OpenGL 3.3 Unsupported + + + + + Your GPU may not support OpenGL 3.3, or you do not have the latest graphics driver. + + + + + Invalid ROM Format Nieprawidłowy format ROMu - - + + Your ROM format is not supported.<br/>Please follow the guides to redump your <a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>game cartridges</a> or <a href='https://citra-emu.org/wiki/dumping-installed-titles/'>installed titles</a>. Format Twojego ROMu nie jest wspierany.<br/>Wykonaj ROM ponownie korzystając z naszych poradników <a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>gry na kartridżach</a> lub <a href='https://citra-emu.org/wiki/dumping-installed-titles/'>tytuły zainstalowane</a>. - + ROM Corrupted ROM jest uszkodzony - + Your ROM is corrupted. <br/>Please follow the guides to redump your <a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>game cartridges</a> or <a href='https://citra-emu.org/wiki/dumping-installed-titles/'>installed titles</a>. Twój ROM jest uszkodzony.<br/>Wykonaj ROM ponownie korzystając z naszych poradników <a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>gry na kartridżach</a> lub <a href='https://citra-emu.org/wiki/dumping-installed-titles/'>tytuły zainstalowane</a>. - + ROM Encrypted ROM jest zaszyfrowany - + Your ROM is encrypted. <br/>Please follow the guides to redump your <a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>game cartridges</a> or <a href='https://citra-emu.org/wiki/dumping-installed-titles/'>installed titles</a>. Twój ROM jest zaszyfrowany.<br/>Wykonaj ROM ponownie korzystając z naszych poradników <a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>gry na kartridżach</a> lub <a href='https://citra-emu.org/wiki/dumping-installed-titles/'>tytuły zainstalowane</a>. - - + + Video Core Error Błąd Rdzenia Wideo - + An error has occured. Please <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>see the log</a> for more details. Ensure that you have the latest graphics drivers for your GPU. Wystąpił błąd. <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>Spójrz w log</a>, aby dowiedzieć się więcej. Upewnij się, że posiadasz najnowsze sterowniki dla swojej kraty graficznej. - + You are running default Windows drivers for your GPU. You need to install the proper drivers for your graphics card from the manufacturer's website. Korzystasz z domyślnych sterowników systemu Windows dla swojej karty graficznej. Zainstaluj sterowniki dostarczane przez producenta twojej karty graficznej. - + Error while loading ROM! Błąd podczas ładowania ROM'u! - + An unknown error occured. Please see the log for more details. Wystąpił nieznany błąd. Sprawdź logi, aby dowiedzieć się więcej. - + Start Start - + Error Opening %1 Folder Błąd podczas otwierania folderu %1 - - + + Folder does not exist! Folder nie istnieje! - + Error Opening %1 Błąd podczas otwierania %1 - + Select Directory Wybierz Folder - - 3DS Executable - Plik wykonywalny 3DS'a + + 3DS Executable (%1);;All Files (*.*) + %1 is an identifier for the 3DS executable file extensions. + - - - All Files (*.*) - Wszystkie Pliki (*.*) - - - + Load File Załaduj Plik - + Load Files Załaduj Pliki - + 3DS Installation File (*.CIA*) Plik Instalacyjny 3DS'a (*.CIA*) - + + All Files (*.*) + Wszystkie Pliki (*.*) + + + %1 has been installed successfully. %1 został poprawnie zainstalowany. - + Unable to open File Nie można otworzyć Pliku - + Could not open %1 Nie można otworzyć %1 - + Installation aborted Instalacja przerwana - + The installation of %1 was aborted. Please see the log for more details Instalacja %1 została przerwana. Sprawdź logi, aby uzyskać więcej informacji. - + Invalid File Niepoprawny Plik - + %1 is not a valid CIA %1 nie jest prawidłowym plikiem CIA - + Encrypted File Plik Zaszyfrowany - + %1 must be decrypted before being used with Citra. A real 3DS is required. %1 musi zostać zdeszyfrowany przed użyciem w Citra. Prawdziwy 3DS jest wymagany. - + File not found Nie znaleziono pliku - + File "%1" not found Nie znaleziono pliku "%1" - - - + + + Continue Kontynuuj - + Missing Citra Account Brakuje konta Citra - + You must link your Citra account to submit test cases.<br/>Go to Emulation &gt; Configure... &gt; Web to do so. Musisz podłączyć Citrę do swojego konta, aby móc zgłosić przypadek testowy.<br/>Przejdź do Emulacja &gt; Skonfiguruj... &gt; Sieć, aby tego dokonać. - - - + + Amiibo File (%1);; All Files (*.*) + + + + + Load Amiibo + + + + + + + Record Movie Nagraj Film - - + + To keep consistency with the RNG, it is recommended to record the movie from game start.<br>Are you sure you still want to record movies now? + + + + + Citra TAS Movie (*.ctm) Citra TAS Movie (*.ctm) - + Recording will start once you boot a game. Nagrywanie rozpocznie się wraz ze startem gry. - + The movie file you are trying to load was created on a different revision of Citra.<br/>Citra has had some changes during the time, and the playback may desync or not work as expected.<br/><br/>Are you sure you still want to load the movie file? Nagranie, które próbujesz odtworzyć pochodzi z innej wersji Citry. <br/> Citra wprowadziła pewne zmiany w tym czasie, odtwarzanie może być niezsynchronizowane lub może nie działać zgodnie z założeniami. <br/><br/> Jesteś pewien, że chcesz załadować ten plik? - + The movie file you are trying to load was recorded with a different game.<br/>The playback may not work as expected, and it may cause unexpected results.<br/><br/>Are you sure you still want to load the movie file? Nagranie, które próbujesz załadować pochodzi z innej gry. <br/> Odtwarzanie może nie działać zgodnie poprawnie i może wiązać się z nieoczekiwanym rezultatem. <br/><br/> Jesteś pewien, że chcesz załadować ten plik? - - + + The movie file you are trying to load is invalid.<br/>Either the file is corrupted, or Citra has had made some major changes to the Movie module.<br/>Please choose a different movie file and try again. Nagranie, które próbujesz odtworzyć jest nieprawidłowe. <br/> Może być uszkodzone, lub Citra wprowadziła spore zmiany w module Filmów. <br/>Spróbuj ponownie z innym plikiem nagrania. - + Revision Dismatch Niepasująca Rewizja - + Game Dismatch Niepasująca Gra - - + + Invalid Movie File Nieprawidłowy Plik Filmu - + + Play Movie Odtwórz Film - + + To keep consistency with the RNG, it is recommended to play the movie from game start.<br>Are you sure you still want to play movies now? + + + + Game Not Found Nie Znaleziono Gry - + The movie you are trying to play is from a game that is not in the game list. If you own the game, please add the game folder to the game list and try to play the movie again. Nagranie, które próbujesz odtworzyć pochodzi z gry, której nie masz na swojej liście. Jeżeli posiadasz tę grę, dodaj ją do swojej listy i spróbuj odtworzyć nagranie ponownie. - + Movie recording cancelled. Nagrywanie zostało przerwane. - + Movie Saved Zapisano Film - + The movie is successfully saved. Film został poprawnie zapisany. - + Speed: %1% / %2% Prędkość: %1% / %2% - + Speed: %1% Prędkość: %1% - + Game: %1 FPS Gra: %1 FPS - + Frame: %1 ms Klatka: %1 ms - + %1 is missing. Please <a href='https://citra-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-3ds-console/'>dump your system archives</a>.<br/>Continuing emulation may result in crashes and bugs. Brakuje %1. <a href='https://citra-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-3ds-console/'>Zgraj swoje archiva systemowe</a>.<br/>Kontynuowanie emulacji może wiązać się z błędami oraz awariami gry. - + System Archive Not Found Archiwum Systemowe nie zostało odnalezione - + Fatal Error Błąd Krytyczny - + A fatal error occured. <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>Check the log</a> for details.<br/>Continuing emulation may result in crashes and bugs. Wystąpił błąd krytyczny. <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>Spójrz w log</a>, aby dowiedzieć się więcej.<br/>Kontynuowanie emulacji może wiązać się z błędami oraz awariami gry. - + Abort Przerwij - - + + Citra Citra - + Would you like to exit now? Czy chcesz teraz wyjść? - + The game is still running. Would you like to stop emulation? Gra jest nadal uruchomiona. Czy chcesz przerwać emulację? - + Playback Completed Odtwarzanie Zakończone - + Movie playback completed. Odtwarzanie filmu zostało zakończone. - + Citra %1 Citra %1 - + Citra %1| %2 Citra %1| %2 @@ -2799,37 +3015,67 @@ p, li { white-space: pre-wrap; } GameList - + + Name + + + + + Compatibility + + + + + Region + + + + + File type + + + + + Size + + + + Open Save Data Location Otwórz Lokalizację Zapisów Gry - + + Open Extra Data Location + Otwórz Dodatkową Lokalizację Aplikacji + + + Open Application Location Otwórz Lokalizację Aplikacji - + Open Update Data Location Otwórz Lokalizację Aktualizacji - + Navigate to GameDB entry Przejdź do wpisu w bazie gier - + Scan Subfolders Przeszukaj Podkatalogi - + Remove Game Directory Usuń Katalog Gier - + Open Directory Location Otwórz lokalizację katalogu @@ -2837,78 +3083,78 @@ p, li { white-space: pre-wrap; } GameListItemCompat - + Perfect Idealna - + Game functions flawless with no audio or graphical glitches, all tested functionality works as intended without any workarounds needed. Gra działa bez zarzutu, bez błędów graficznych lub dźwiękowych. Nie potrzebuje żadnych obejść ani poprawek. - + Great Świetna - + Game functions with minor graphical or audio glitches and is playable from start to finish. May require some workarounds. Gra działa z pomniejszymi błędami dźwiękowymi lub graficznymi, jest grywalna od początku do końca. Może wymagać kilku obejść/poprawek. - + Okay W porządku - + Game functions with major graphical or audio glitches, but game is playable from start to finish with workarounds. Gra działa z większymi błędami dźwiękowymi lub graficznymi, ale jest grywalna od początku do końca. Może wymagać kilku obejść/poprawek. - + Bad Zła - + Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches even with workarounds. Gra działa z większymi błędami dźwiękowymi lub graficznymi. Niemożliwe jest przejście konkretnych miejsc nawet z obejściami. - + Intro/Menu Intro/Menu - + Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start Screen. Gra jest całkowicie niegrywalna z uwagi na poważne błędy graficzne lub dźwiękowe. Działa jedynie ekran startowy. - + Won't Boot Nie uruchamia się - + The game crashes when attempting to startup. Gra wysypuje się przy próbie uruchomienia - + Not Tested Nieprzetestowana - + The game has not yet been tested. Gra nie została jeszcze przetestowana. @@ -2916,7 +3162,7 @@ Screen. GameListPlaceholder - + Double-click to add a new folder to the game list Dwuklik, aby dodać nowy folder do listy gier @@ -2924,27 +3170,27 @@ Screen. GameListSearchField - + of z - + result wynik - + results wyniki - + Filter: Filtr: - + Enter pattern to filter Wprowadź wzór filtra @@ -2952,23 +3198,23 @@ Screen. GraphicsBreakPointsWidget - + Pica Breakpoints Punkty przerwania Pica - - + + Emulation running Emulacja uruchomiona - + Resume Wznów - + Emulation halted at breakpoint Emulacja zatrzymana w punkcie przerwania @@ -3385,42 +3631,42 @@ Screen. Odśwież Lobby - + Password Required to Join Wymagane Hasło, aby Dołączyć - + Password: Hasło: - + Room Name Nazwa Pokoju - + Preferred Game Preferowana Gra - + Host Gospodarz - + Players Graczy - + Refreshing Odświeżanie - + Refresh List Odśwież Listę @@ -3443,220 +3689,245 @@ Screen. Ostatnio Używane - + + Amiibo + + + + &Emulation &Emulacja - + &View &Widok - + Debugging Debugowanie - + Screen Layout Układ Ekranów: - + Movie Film - + Multiplayer Multiplayer - + &Help &Pomoc - + Load File... Załaduj Plik... - + Install CIA... Instaluj CIA... - + Load Symbol Map... Ładowanie Mapy Symboli... - + E&xit &Wyjście - + &Start &Uruchom - + &Pause &Pauza - + &Stop &Zatrzymaj - + FAQ FAQ - + About Citra O Citra - + Single Window Mode Tryb Pojedynczego Okna - + Configure... Skonfiguruj... - + Display Dock Widget Headers Wyświetl Nagłówki Widgetów. - + Show Filter Bar Pokaż Pasek Filtrowania - + Show Status Bar Pokaż Pasek Statusu - + Select Game Directory... Wybierz Katalog Gier... - + Selects a folder to display in the game list Wybiera katalog, z którego ma załadować listę gier - + Create Pica Surface Viewer Stwórz Podgląd Powierzchni Pica - + Record Movie Nagraj Film - + Play Movie Odtwórz Film - + Stop Recording / Playback Przerwij Nagrywanie / Odtwarzanie - + + Enable Frame Advancing + + + + + Advance Frame + + + + Browse Public Game Lobby Przeglądaj Publiczne Pokoje - + Create Room Stwórz Pokój - + Leave Room Opóść Pokój - + Direct Connect to Room Bezpośrednie Połączenie z Pokojem - + Show Current Room Pokaż Aktualny Pokój - + Fullscreen Pełny Ekran - + Modify Citra Install Zmodyfikuj instalację Citry - + Opens the maintenance tool to modify your Citra installation Otwiera narzędzie do modyfikacji twojej instalacji Citry - + Default Domyślny - + Single Screen Pojedynczy Ekran - + Large Screen Duży Ekran - + Side by Side Obok Siebie - + Swap Screens Zamień Ekrany - + Check for Updates Sprawdź dostępność Aktualizacji - + Report Compatibility Zgłoś Kompatybilność - + Restart Zrestartuj + + + Load... + + + + + Remove + + MicroProfileDialog @@ -3682,23 +3953,23 @@ Screen. - + Connected Połączono - + Not Connected Nie Połączono - + Error Błąd - + Failed to announce the room to the public lobby. In order to host a room publicly, you must have a valid Citra account configured in Emulation -> Configure -> Web. If you do not want to publish a room in the public lobby, then select Unlisted instead. Debug Message: Nie udało się rozgłosić pokoju na publicznej liście. W celu hostowania publicznego pokoju, niezbędne jest skonfigurowanie konta Citra w Emulacja - > Skonfiguruj...-> Sieć. Jeżeli nie chcesz upubliczniać pokoju, wybierz Niewidoczny podczas jego tworzenia. @@ -3817,106 +4088,106 @@ Debug: %1 gra w %2 - - + + Invalid region Nieprawidłowy region - + Japan Japonia - + North America Ameryka Północna - + Europe Europa - + Australia Australia - + China Chiny - + Korea Korea - + Taiwan Tajwan - + Region free Bez ograniczenia regionalnego - + Invalid Region Nieprawidłowy Region - + Shift Shift - + Ctrl Ctrl - + Alt Alt - - + + [not set] [nie ustawiono] - + Hat %1 %2 Kapelusz %1 %2 - + Axis %1%2 Osie %1%2 - + Button %1 Przycisk %1 - - + + [unknown] [nieznany] - + [unused] [nieużywany] - - + + Axis %1 Oś %1 diff --git a/dist/languages/pt_BR.ts b/dist/languages/pt_BR.ts index 64664cfaf..248fc029e 100644 --- a/dist/languages/pt_BR.ts +++ b/dist/languages/pt_BR.ts @@ -64,58 +64,63 @@ p, li { white-space: pre-wrap; } <html><head/><body><p><span style=" font-size:7pt;">&quot;3DS&quot; is a trademark of Nintendo. Citra is not affiliated with Nintendo in any way.</span></p></body></html> - <html><head/><body><p><span style=" font-size:7pt;">&quot;3DS&quot; é uma marca da Nintendo. Citra não é afiliado com a Nintendo de nenhuma forma.</span></p></body></html> + <html><head/><body><p><span style=" font-size:7pt;">&quot;3DS&quot; é uma marca da Nintendo. O Citra não é afiliado com a Nintendo de nenhuma forma.</span></p></body></html> BreakPointModel - + Pica command loaded Comando Pica carregado - + Pica command processed Comando Pica processado - + Incoming primitive batch - Recebendo Primitive batch + Recebendo primitive batch - + Finished primitive batch Primitive batch finalizado - + Vertex shader invocation - Invocação de Vertex Shader + Invocação de vertex shader - + Incoming display transfer - Transferência de display iminente + Recebendo transferência de display - + GSP command processed Comando GSP processado - + Buffers swapped Buffers trocados + + + Unknown debug context event + Evento de contexto de depuração desconhecido + CalibrationConfigurationDialog Communicating with the server... - Conectando ao servidor... + Conectando-se ao servidor... @@ -171,15 +176,15 @@ p, li { white-space: pre-wrap; } Jogo - - + + Block Player Bloquear jogador - + When you block a player, you will no longer receive chat messages from them.<br><br>Are you sure you would like to block %1? - Ao bloquear um jogador, você não irá mais receber mensagens dele no chat.<br><br>Deseja mesmo bloquear %1? + Ao bloquear um jogador, você não receberá mais mensagens dele no chat.<br><br>Deseja mesmo bloquear %1? @@ -198,17 +203,17 @@ p, li { white-space: pre-wrap; } ClientRoomWindow - + Connected Conectado - + Disconnected Desconectado - + %1 (%2/%3 members) - connected %1 (%2/%3 membros) - conectado @@ -218,18 +223,18 @@ p, li { white-space: pre-wrap; } Report Compatibility - Informar compatibilidade + Relatar compatibilidade Report Game Compatibility - Informar compatibilidade de jogo + Relatar compatibilidade de jogo <html><head/><body><p><span style=" font-size:10pt;">Should you choose to submit a test case to the </span><a href="https://citra-emu.org/game/"><span style=" font-size:10pt; text-decoration: underline; color:#0000ff;">Citra Compatibility List</span></a><span style=" font-size:10pt;">, The following information will be collected and displayed on the site:</span></p><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Hardware Information (CPU / GPU / Operating System)</li><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Which version of Citra you are running</li><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">The connected Citra account</li></ul></body></html> - <html><head/><body><p><span style=" font-size:10pt;">Ao enviar um caso de teste à </span><a href="https://citra-emu.org/game/"><span style=" font-size:10pt; text-decoration: underline; color:#0000ff;">Lista de compatibilidade do Citra</span></a><span style=" font-size:10pt;">, as seguintes informações serão coletadas e exibidas no site:</span></p><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Informações de hardware (CPU / GPU / sistema operacional)</li><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Qual versão do Citra você está usando</li><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">A conta do Citra conectada</li></ul></body></html> + <html><head/><body><p><span style=" font-size:10pt;">Ao enviar um caso de teste à </span><a href="https://citra-emu.org/game/"><span style=" font-size:10pt; text-decoration: underline; color:#0000ff;">lista de compatibilidade do Citra</span></a><span style=" font-size:10pt;">, as seguintes informações serão recolhidas e exibidas no site:</span></p><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Informações de hardware (CPU / GPU / sistema operacional)</li><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Qual versão do Citra você está usando</li><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">A conta do Citra conectada</li></ul></body></html> @@ -239,7 +244,7 @@ p, li { white-space: pre-wrap; } <html><head/><body><p>Game functions flawlessly with no audio or graphical glitches.</p></body></html> - <html><head/><body><p>O jogo funciona perfeitamente sem falhas no áudio ou nos gráficos.</p></body></html> + <html><head/><body><p>O jogo funciona impecavelmente, sem falhas no áudio ou nos gráficos.</p></body></html> @@ -259,7 +264,7 @@ p, li { white-space: pre-wrap; } <html><head/><body><p>Game functions with major graphical or audio glitches, but game is playable from start to finish with workarounds.</p></body></html> - <html><head/><body><p>O jogo funciona com grandes falhas gráficas ou de áudio, mas o jogo é jogável do início ao fim com soluções alternativas.</p></body></html> + <html><head/><body><p>O jogo funciona com grandes falhas gráficas ou de áudio, mas é jogável do início ao fim com o uso de soluções alternativas.</p></body></html> @@ -274,7 +279,7 @@ p, li { white-space: pre-wrap; } Intro/Menu - Intro/Menu + Intro/menu @@ -301,6 +306,26 @@ p, li { white-space: pre-wrap; } Thank you for your submission! Agradecemos pelo seu relatório! + + + Submitting + Enviando + + + + Communication error + Erro de comunicação + + + + An error occured while sending the Testcase + Ocorreu um erro ao enviar o caso de testes + + + + Next + Próximo + ConfigureAudio @@ -312,22 +337,22 @@ p, li { white-space: pre-wrap; } Output Engine: - Engine de Saída: + Engine de saída: This post-processing effect adjusts audio speed to match emulation speed and helps prevent audio stutter. This however increases audio latency. - Este efeito de pós-processamento ajusta a velocidade do áudio para acompanhar a velocidade de emulação e ajuda prevenir cortes no áudio. Isso entretanto aumenta a latência do áudio. + Este efeito de pós-processamento ajusta a velocidade do áudio para acompanhar a velocidade de emulação e ajuda a evitar cortes no áudio. No entanto, isto aumenta a latência do áudio. Enable audio stretching - Habilitar alongamento de áudio + Ativar alongamento de áudio Audio Device: - Dispositivo de Áudio: + Dispositivo de áudio: @@ -340,10 +365,10 @@ p, li { white-space: pre-wrap; } 0 % - - - %1 % - %1 % + + %1% + Volume percentage (e.g. 50%) + %1% @@ -367,7 +392,7 @@ p, li { white-space: pre-wrap; } Camera to configure: - Câmera para configurar: + Câmera a configurar: @@ -435,7 +460,7 @@ p, li { white-space: pre-wrap; } Camera Image Source: - Caminho da imagem da câmera: + Fonte da imagem da câmera: @@ -527,7 +552,7 @@ p, li { white-space: pre-wrap; } Resolution: 512*384 - Resolução: 512*384 + Resolução: 512x384 @@ -542,12 +567,12 @@ p, li { white-space: pre-wrap; } Supported image files (%1) - Arquivos de imagem suportados (%1) + Arquivos de imagem compatíveis (%1) Open File - Abrir Arquivo + Abrir arquivo @@ -606,50 +631,78 @@ p, li { white-space: pre-wrap; } ConfigureDialog - + Citra Configuration Configurações do Citra - + + + General Geral - + + + System Sistema - + + + Input Controle - + + + Graphics Gráficos - + + + Audio Áudio - + + + Camera Câmera - + + + Debug - Debug + Depuração - + + + Web Rede + + + + + UI + Interface + + + + Controls + Controles + ConfigureGeneral @@ -666,67 +719,57 @@ p, li { white-space: pre-wrap; } Confirm exit while emulation is running - Confirmar saída quando a emulação estiver rodando + Confirmar saída quando a emulação estiver em execução - - Interface language - Idioma da interface - - - + Updates Atualizações - + Check for updates on start Verificar atualizações ao iniciar - + Silently auto update after closing Atualizar silenciosamente ao fechar - + Emulation Emulação - + Region: Região: - + Auto-select Seleção automática - - Theme - Tema - - - - Theme: - Tema: - - - + Hotkeys Teclas de atalho - - <System> - <Sistema> + + Reset All Settings + Redefinir todos os ajustes - - English - Inglês + + Citra + Citra + + + + Are you sure you want to <b>reset your settings</b> and close Citra? + Deseja mesmo <b>restaurar as suas configurações ao padrão</b> e fechar o Citra? @@ -744,12 +787,12 @@ p, li { white-space: pre-wrap; } Enable V-Sync - Habilitar V-Sync + Ativar V-Sync Limit Speed Percent - Limitar Porcentagem de Velocidade + Limitar porcentagem de velocidade @@ -780,7 +823,7 @@ p, li { white-space: pre-wrap; } Auto (Window Size) - Automática (Tamanho da Janela) + Automática (tamanho da janela) @@ -790,52 +833,52 @@ p, li { white-space: pre-wrap; } 2x Native (800x480) - 2x Nativa (800x480) + 2x nativa (800x480) 3x Native (1200x720) - 3x Nativa (1200x720) + 3x nativa (1200x720) 4x Native (1600x960) - 4x Nativa (1600x960) + 4x nativa (1600x960) 5x Native (2000x1200) - 5x Nativa (2000x1200) + 5x nativa (2000x1200) 6x Native (2400x1440) - 6x Nativa (2400x1440) + 6x nativa (2400x1440) 7x Native (2800x1680) - 7x Nativa (2800x1680) + 7x nativa (2800x1680) 8x Native (3200x1920) - 8x Nativa (3200x1920) + 8x nativa (3200x1920) 9x Native (3600x2160) - 9x Nativa (3600x2160) + 9x nativa (3600x2160) 10x Native (4000x2400) - 10x Nativa (4000x2400) + 10x nativa (4000x2400) <html><head/><body><p>Use OpenGL to accelerate shader emulation.</p><p>Requires a relatively powerful GPU for better performance.</p></body></html> - <html><head/><body><p>Usa o OpenGL para acelerar a emulação de shaders.</p><p>Requer uma GPU relativamente poderosa para um melhor desempenho.</p></body></html> + <html><head/><body><p>Usa o OpenGL para acelerar a emulação de shaders.</p><p>Requer uma GPU relativamente potente para um melhor desempenho.</p></body></html> @@ -855,7 +898,7 @@ p, li { white-space: pre-wrap; } <html><head/><body><p>Force to fall back to software shader emulation when geometry shaders are used. </p><p>Some games require this to be enabled for the hardware shader to render properly.</p><p>However this might reduce performance in some games</p></body></html> - <html><head/><body><p>Força a emulação de shaders voltar a ser processada via software quando shaders de geometria forem usados. </p><p>Alguns jogos precisam que isto esteja ativado para que os shaders sejam adequadamente renderizados via hardware.</p><p>No entanto, isso pode reduzir o desempenho na maioria dos jogos.</p></body></html> + <html><head/><body><p>Força a emulação de shaders voltar a ser processada via software quando shaders de geometria forem usados. </p><p>Alguns jogos precisam que isto esteja ativado para que os shaders sejam adequadamente renderizados via hardware.</p><p>No entanto, isso pode reduzir o desempenho em alguns jogos.</p></body></html> @@ -865,7 +908,7 @@ p, li { white-space: pre-wrap; } <html><head/><body><p>Use the JIT engine instead of the interpreter for software shader emulation. </p><p>Enable this for better performance.</p></body></html> - <html><head/><body><p>Usa o mecanismo JIT (ao invés do interpretador) para emular shaders via software. </p><p>Ative isso para um melhor desempenho.</p></body></html> + <html><head/><body><p>Usa o mecanismo JIT (ao invés do interpretador) para emular shaders via software. </p><p>Ative isto para um melhor desempenho.</p></body></html> @@ -875,7 +918,7 @@ p, li { white-space: pre-wrap; } Layout - Layout + Estilo @@ -885,7 +928,7 @@ p, li { white-space: pre-wrap; } Screen Layout: - Layout de tela: + Estilo de tela: @@ -910,7 +953,7 @@ p, li { white-space: pre-wrap; } Swap Screens - Trocar Telas + Trocar telas @@ -1026,9 +1069,9 @@ p, li { white-space: pre-wrap; } - + Set Analog Stick - Definir Analog Stick + Definir direcional analógico @@ -1067,11 +1110,44 @@ p, li { white-space: pre-wrap; } + Clear All + Limpar tudo + + + Restore Defaults Restaurar padrões - + + + Clear + Limpar + + + + + [not set] + [não definido] + + + + + Restore Default + Restaurar padrão + + + + Information + Informação + + + + After pressing OK, first move your joystick horizontally, and then vertically. + Após pressionar OK, mova seu direcional analógico primeiro horizontalmente e depois verticalmente. + + + [press key] [pressione uma tecla] @@ -1239,7 +1315,7 @@ p, li { white-space: pre-wrap; } UDP Test or calibration configuration is in progress.<br>Please wait for them to finish. - Teste UDP ou configuração de calibração está em progresso no momento.<br>Por favor, aguarde até a conclusão deles. + O teste UDP ou configuração de calibração está em progresso no momento.<br>Aguarde até a conclusão deles. @@ -1252,12 +1328,12 @@ p, li { white-space: pre-wrap; } System Settings - Configurações de Sistema + Configurações de sistema Username - Usuário + Nome de usuário @@ -1367,7 +1443,7 @@ p, li { white-space: pre-wrap; } Simplified Chinese (简体中文) - Chinês Simplificado (简体中文) + Chinês simplificado (简体中文) @@ -1392,7 +1468,7 @@ p, li { white-space: pre-wrap; } Traditional Chinese (正體中文) - Chinês Tradicional (正體中文) + Chinês tradicional (正體中文) @@ -1440,19 +1516,29 @@ p, li { white-space: pre-wrap; } Horário na inicialização - + + yyyy-MM-ddTHH:mm:ss + aaaa-MM-ddTHH:mm:ss + + + + Play Coins: + Moedas de jogo: + + + Console ID: ID do console: - + Regenerate Regerar - + System settings are available only when game is not running. - Configurações de sistema estão disponíveis apenas quando o game não estiver rodando. + As configurações de sistema estão disponíveis apenas quando não houver nenhum jogo em execução. @@ -1612,7 +1698,7 @@ p, li { white-space: pre-wrap; } Netherlands Antilles - Antilhas holandesas + Antilhas Holandesas @@ -1657,7 +1743,7 @@ p, li { white-space: pre-wrap; } Trinidad and Tobago - Trinidad e Tobago + Trindade e Tobago @@ -2120,22 +2206,120 @@ p, li { white-space: pre-wrap; } Bermuda - - + + Console ID: 0x%1 ID do console: 0x%1 - + This will replace your current virtual 3DS with a new one. Your current virtual 3DS will not be recoverable. This might have unexpected effects in games. This might fail, if you use an outdated config savegame. Continue? - Isso vai substituir seu 3DS virtual atual por um novo. Seu 3DS virtual atual não será recuperável. Isso pode causar efeitos inesperados em jogos. Isso pode falhar se você utilizar uma configuração de jogo salvo desatualizada. Continuar? + Isso vai substituir seu 3DS virtual atual por um novo. Seu 3DS virtual atual não será recuperável. Isto pode causar efeitos inesperados em jogos. Isto pode falhar se você utilizar uma configuração de jogo salvo desatualizada. Continuar? - + Warning Alerta + + ConfigureUi + + + Form + Formulário + + + + General + Geral + + + + Interface language: + Idioma da interface: + + + + Theme: + Tema: + + + + Game List + Lista de jogos + + + + Icon Size: + Tamanho do ícone: + + + + + None + Não mostrar + + + + Small (24x24) + Pequeno (24x24) + + + + Large (48x48) + Grande (48x48) + + + + Row 1 Text: + Texto da 1ª linha: + + + + + File Name + Nome do arquivo + + + + + Full Path + Caminho completo + + + + + Title Name + Nome do título + + + + + Title ID + ID do título + + + + Row 2 Text: + Texto da 2ª linha: + + + + Hide Titles without Icon + Ocultar títulos sem ícone + + + + <System> + <Sistema> + + + + English + Inglês (English) + + ConfigureWeb @@ -2151,18 +2335,18 @@ p, li { white-space: pre-wrap; } By providing your username and token, you agree to allow Citra to collect additional usage data, which may include user identifying information. - Ao preencher seu usuário e token, você concorda em permitir ao Citra coletar dados de uso adicionais, que podem incluir informações de identificação de usuário. + Ao informar seu usuário e token, você concorda em permitir ao Citra recolher dados de uso adicionais, que podem incluir informações de identificação de usuário. - + Verify Verificar Sign up - Cadastrar + Cadastrar-se @@ -2172,7 +2356,7 @@ p, li { white-space: pre-wrap; } Username: - Usuário: + Nome de usuário @@ -2197,7 +2381,7 @@ p, li { white-space: pre-wrap; } Telemetry ID: - ID de Telemetria: + ID de telemetria: @@ -2215,48 +2399,48 @@ p, li { white-space: pre-wrap; } Mostrar o jogo atual no seu status do Discord - + <a href='https://citra-emu.org/entry/telemetry-and-why-thats-a-good-thing/'><span style="text-decoration: underline; color:#039be5;">Learn more</span></a> <a href='https://citra-emu.org/entry/telemetry-and-why-thats-a-good-thing/'><span style="text-decoration: underline; color:#039be5;">Saiba mais</span></a> - - <a href='https://services.citra-emu.org/'><span style="text-decoration: underline; color:#039be5;">Sign up</span></a> - <a href='https://services.citra-emu.org/'><span style="text-decoration: underline; color:#039be5;">Cadastrar-se</span></a> + + <a href='https://profile.citra-emu.org/'><span style="text-decoration: underline; color:#039be5;">Sign up</span></a> + <a href='https://profile.citra-emu.org/'><span style="text-decoration: underline; color:#039be5;">Cadastrar-se</span></a> - + <a href='https://citra-emu.org/wiki/citra-web-service/'><span style="text-decoration: underline; color:#039be5;">What is my token?</span></a> <a href='https://citra-emu.org/wiki/citra-web-service/'><span style="text-decoration: underline; color:#039be5;">Qual é o meu token?</span></a> - - + + Telemetry ID: 0x%1 - ID de Telemetria: 0x%1 + ID de telemetria: 0x%1 - + Username and token not verified Nome de usuário e token não verificados - + Username and token were not verified. The changes to your username and/or token have not been saved. - Usuário e token não foram verificados. As mudanças de seu usuário e/ou token não foram salvas. + O nome de usuário e token não foram verificados. As mudanças do seu usuário e/ou token não foram salvas. - + Verifying Verificando - + Verification failed - Verificação falhou + A verificação falhou - + Verification failed. Check that you have entered your username and token correctly, and that your internet connection is working. A verificação falhou. Verifique se você colocou seu usuário e token corretamente, e se sua conexão com a internet está funcionando. @@ -2266,7 +2450,7 @@ p, li { white-space: pre-wrap; } Direct Connect - Conectar-se Diretamente + Conectar-se diretamente @@ -2317,12 +2501,12 @@ p, li { white-space: pre-wrap; } DirectConnectWindow - + Connecting Conectando - + Connect Conectar @@ -2330,414 +2514,446 @@ p, li { white-space: pre-wrap; } GMainWindow - + <a href='https://citra-emu.org/entry/telemetry-and-why-thats-a-good-thing/'>Anonymous data is collected</a> to help improve Citra. <br/><br/>Would you like to share your usage data with us? - <a href='https://citra-emu.org/entry/telemetry-and-why-thats-a-good-thing/'>Dados anônimos são coletados</a> para ajudar a melhorar o Citra. <br/><br/>Gostaria de compartilhar seus dados de uso conosco? + <a href='https://citra-emu.org/entry/telemetry-and-why-thats-a-good-thing/'>Dados anônimos são recolhidos</a> para ajudar a melhorar o Citra. <br/><br/>Gostaria de compartilhar os seus dados de uso conosco? - + Telemetry Telemetria - - + + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a 3DS. - Atual velocidade de emulação. Valores maiores ou menores que 100% indicam que a emulação está rodando mais rápida ou lentamente que um 3DS. + Atual velocidade de emulação. Valores maiores ou menores que 100% indicam que a emulação está rodando mais rápida ou lentamente que em um 3DS. - - + + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. - Quantos quadros por segundo o jogo está atualmente mostrando. Isto vai variar de jogo para jogo e cena para cena. + Quantos quadros por segundo o jogo está exibindo atualmente. Isto vai variar de jogo para jogo e cena para cena. - - + + Time taken to emulate a 3DS frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. - Tempo levado para emular um quadro do 3DS, não contando o limitador de framerate ou o v-sync. Para emulação em velocidade total isso deve ser pelo menos 16.67 ms. + Tempo levado para emular um quadro do 3DS, sem considerar o limitador de taxa de quadros ou o V-sync. Valores menores ou iguais a 16.67 ms indicam que a emulação está em velocidade plena. - + Clear Recent Files Limpar arquivos recentes - + F9 F9 - + F10 F10 - + CTRL+F CTRL+F - + Update Available Atualização disponível - + An update is available. Would you like to install it now? Uma atualização está disponível. Gostaria de instalá-la agora? - + No Update Found Nenhuma atualização encontrada - + No update is found. Nenhuma atualização encontrada - - + + OpenGL 3.3 Unsupported + Sem suporte a OpenGL 3.3 + + + + Your GPU may not support OpenGL 3.3, or you do not have the latest graphics driver. + Ou sua GPU não é compatível com o OpenGL 3.3 ou você não possui o driver de gráficos mais recente. + + + + Invalid ROM Format Formato de ROM inválido - - + + Your ROM format is not supported.<br/>Please follow the guides to redump your <a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>game cartridges</a> or <a href='https://citra-emu.org/wiki/dumping-installed-titles/'>installed titles</a>. O formato da sua ROM é incompatível.<br/>Por favor, siga os guias para extrair jogos de seus <a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>cartuchos de jogo</a> ou <a href='https://citra-emu.org/wiki/dumping-installed-titles/'>títulos instalados</a>. - + ROM Corrupted ROM corrompida - + Your ROM is corrupted. <br/>Please follow the guides to redump your <a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>game cartridges</a> or <a href='https://citra-emu.org/wiki/dumping-installed-titles/'>installed titles</a>. Sua ROM está corrompida.<br/>Por favor, siga os guias para extrair jogos de seus <a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>cartuchos de jogo</a> ou <a href='https://citra-emu.org/wiki/dumping-installed-titles/'>títulos instalados</a>. - + ROM Encrypted ROM encriptada - + Your ROM is encrypted. <br/>Please follow the guides to redump your <a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>game cartridges</a> or <a href='https://citra-emu.org/wiki/dumping-installed-titles/'>installed titles</a>. Sua ROM está encriptada.<br/>Por favor, siga os guias para extrair jogos de seus <a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>cartuchos de jogo</a> ou <a href='https://citra-emu.org/wiki/dumping-installed-titles/'>títulos instalados</a>. - - + + Video Core Error Erro no núcleo de vídeo - + An error has occured. Please <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>see the log</a> for more details. Ensure that you have the latest graphics drivers for your GPU. - Um erro ocorreu. Para mais detalhes, <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>confira o log</a>. Verifique se os drivers mais recentes da sua GPU estão instalados. + Ocorreu um erro. Para mais detalhes, <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>confira o registro</a>. Verifique se os drivers mais recentes da sua GPU estão instalados. - + You are running default Windows drivers for your GPU. You need to install the proper drivers for your graphics card from the manufacturer's website. Você está usando os drivers padrão do Windows para a sua GPU. Você precisa instalar os drivers adequados para a sua placa de vídeo disponíveis no site da fabricante. - + Error while loading ROM! Erro ao carregar ROM! - + An unknown error occured. Please see the log for more details. - Um erro desconhecido ocorreu. Por favor veja o log para mais detalhes. + Ocorreu um erro desconhecido. Verifique o registro para mais detalhes. - + Start Iniciar - + Error Opening %1 Folder Erro ao abrir a pasta %1 - - + + Folder does not exist! - Pasta não existe! + A pasta não existe! - + Error Opening %1 Erro ao abrir %1 - + Select Directory - Selecionar Diretório + Selecionar pasta - - 3DS Executable - Executável 3DS + + 3DS Executable (%1);;All Files (*.*) + %1 is an identifier for the 3DS executable file extensions. + Executável do 3DS (%1);;Todos os arquivos (*.*) - - + + Load File + Carregar arquivo + + + + Load Files + Carregar arquivos + + + + 3DS Installation File (*.CIA*) + Arquivo de instalação 3DS (*.CIA*) + + + All Files (*.*) Todos os arquivos (*.*) - - Load File - Carregar Arquivo - - - - Load Files - Carregar Arquivos - - - - 3DS Installation File (*.CIA*) - Arquivo de Instalação 3DS (*.CIA*) - - - + %1 has been installed successfully. %1 foi instalado com sucesso. - + Unable to open File Não foi possível abrir o arquivo - + Could not open %1 Não foi possível abrir %1 - + Installation aborted Instalação cancelada - + The installation of %1 was aborted. Please see the log for more details - A instalação de %1 foi abortada. Verifique o log para mais detalhes + A instalação de %1 foi interrompida. Verifique o registro para mais detalhes - + Invalid File Arquivo inválido - + %1 is not a valid CIA %1 não é um CIA válido - + Encrypted File - Arquivo criptografado + Arquivo encriptado - + %1 must be decrypted before being used with Citra. A real 3DS is required. - %1 deve ser decriptado antes de ser usado no Citra. + %1 precisa ser decriptado antes de ser usado no Citra. É necessário um 3DS de verdade. - + File not found Arquivo não encontrado - + File "%1" not found Arquivo "%1" não encontrado - - - + + + Continue Continuar - + Missing Citra Account - Conta do Citra Faltando + Conta do Citra faltando - + You must link your Citra account to submit test cases.<br/>Go to Emulation &gt; Configure... &gt; Web to do so. Você precisa entrar com sua conta do Citra para enviar casos de teste.<br/>Para isso, vá para Emulação &gt; Configurar... &gt; Rede. - - - + + Amiibo File (%1);; All Files (*.*) + Arquivo do Amiibo (%1);; Todos os arquivos (*.*) + + + + Load Amiibo + Carregar Amiibo + + + + + + Record Movie Gravar passos - - + + To keep consistency with the RNG, it is recommended to record the movie from game start.<br>Are you sure you still want to record movies now? + Para manter consistência com sistemas de geração de números aleatórios (RNG), recomendamos começar a gravar assim que o jogo iniciar.<br>Você ainda deseja iniciar uma gravação agora? + + + + Citra TAS Movie (*.ctm) Arquivo de gravação TAS do Citra (*.ctm) - + Recording will start once you boot a game. A gravação será iniciada assim que você iniciar um jogo. - + The movie file you are trying to load was created on a different revision of Citra.<br/>Citra has had some changes during the time, and the playback may desync or not work as expected.<br/><br/>Are you sure you still want to load the movie file? A gravação que você está tentando carregar foi criada em uma revisão diferente do Citra.<br/>O Citra sofreu algumas mudanças com o passar do tempo e, por causa disso, a reprodução pode dessincronizar ou não funcionar como o esperado.<br/><br/>Você ainda deseja carregar esta gravação? - + The movie file you are trying to load was recorded with a different game.<br/>The playback may not work as expected, and it may cause unexpected results.<br/><br/>Are you sure you still want to load the movie file? - A gravação que você está tentando carregar foi criada com um jogo diferente.<br/>A reprodução pode não funcionar como o esperado, podendo causar resultados inesperados.<br/><br/>Você ainda deseja carregar esta gravação? + A gravação que você está tentando carregar foi criada em um jogo diferente.<br/>A reprodução pode não funcionar adequadamente, podendo causar resultados inesperados.<br/><br/>Você ainda deseja carregar esta gravação? - - + + The movie file you are trying to load is invalid.<br/>Either the file is corrupted, or Citra has had made some major changes to the Movie module.<br/>Please choose a different movie file and try again. O arquivo de gravação que você está tentando carregar é inválido.<br/>Ou o arquivo está corrompido ou o Citra passou por grandes mudanças em seu módulo de gravações.<br/>Por favor, escolha um arquivo de gravação diferente e tente novamente. - + Revision Dismatch Discrepância de revisões - + Game Dismatch Discrepância de jogos - - + + Invalid Movie File Arquivo de gravação inválido - + + Play Movie Reproduzir gravação - + + To keep consistency with the RNG, it is recommended to play the movie from game start.<br>Are you sure you still want to play movies now? + Para manter consistência com sistemas de geração de números aleatórios (RNG), recomendamos começar a reproduzir a gravação assim que o jogo iniciar.<br>Você ainda deseja reproduzir uma gravação agora? + + + Game Not Found Jogo não encontrado - + The movie you are trying to play is from a game that is not in the game list. If you own the game, please add the game folder to the game list and try to play the movie again. A gravação que você está tentando reproduzir é de um jogo que não está presente na sua lista de jogos. Se você tiver esse jogo, adicione a pasta dele à lista de jogos e tente reproduzir a gravação novamente. - + Movie recording cancelled. Gravação cancelada. - + Movie Saved Gravação salva - + The movie is successfully saved. A gravação foi salva com sucesso. - + Speed: %1% / %2% Velocidade: %1% / %2% - + Speed: %1% Velocidade: %1% - + Game: %1 FPS Jogo: %1 FPS - + Frame: %1 ms Quadro: %1 ms - + %1 is missing. Please <a href='https://citra-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-3ds-console/'>dump your system archives</a>.<br/>Continuing emulation may result in crashes and bugs. %1 está faltando. Por favor, <a href='https://citra-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-3ds-console/'>faça a extração dos arquivos do seu sistema</a>.<br/>Continuar a emulação pode resultar em travamentos e bugs. - + System Archive Not Found - Arquivo de Sistema não Encontrado + Arquivo de sistema não encontrado - + Fatal Error - Erro Fatal + Erro fatal - + A fatal error occured. <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>Check the log</a> for details.<br/>Continuing emulation may result in crashes and bugs. - Um erro fatal ocorreu. Para mais detalhes, <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>confira o log</a>. Continuar a emulação pode resultar em travamentos e bugs. + Ocorreu um erro fatal. Para mais detalhes, <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>confira o registro</a>. Continuar a emulação pode resultar em travamentos e bugs. - + Abort - Abortar + Interromper - - + + Citra Citra - + Would you like to exit now? Deseja sair agora? - + The game is still running. Would you like to stop emulation? - O jogo ainda está rodando. Deseja parar a emulação? + O jogo ainda está em execução. Deseja parar a emulação? - + Playback Completed Reprodução concluída - + Movie playback completed. Reprodução dos passos concluída. - + Citra %1 Citra %1 - + Citra %1| %2 Citra %1| %2 @@ -2747,7 +2963,7 @@ p, li { white-space: pre-wrap; } Command Name - Nome do Comando + Nome do comando @@ -2762,7 +2978,7 @@ p, li { white-space: pre-wrap; } New Value - Novo Valor + Novo valor @@ -2770,23 +2986,23 @@ p, li { white-space: pre-wrap; } Pica Command List - Lista de Comandos Pica + Lista de comandos Pica Start Tracing - Iniciar Tracing + Iniciar rastreamento Copy All - Copiar todos + Copiar tudo Finish Tracing - Finalizar Tracing + Finalizar rastreamento @@ -2794,43 +3010,73 @@ p, li { white-space: pre-wrap; } Graphics Debugger - Debugger Gráfico + Depurador gráfico GameList - + + Name + Nome + + + + Compatibility + Compatibilidade + + + + Region + Região + + + + File type + Tipo de arquivo + + + + Size + Tamanho + + + Open Save Data Location - Abrir Local dos Dados Salvos + Abrir local dos dados salvos - + + Open Extra Data Location + Abrir local dos dados extras + + + Open Application Location - Abrir Local do Aplicativo + Abrir local do aplicativo - + Open Update Data Location - Abrir Local dos Dados de Atualizações + Abrir local dos dados de atualizações - + Navigate to GameDB entry Abrir artigo do jogo no GameDB - + Scan Subfolders Escanear subpastas - + Remove Game Directory Remover pasta de jogos - + Open Directory Location Abrir local da pasta @@ -2838,82 +3084,82 @@ p, li { white-space: pre-wrap; } GameListItemCompat - + Perfect Perfeito - + Game functions flawless with no audio or graphical glitches, all tested functionality works as intended without any workarounds needed. - O jogo roda impecavelmente, sem nenhum problema no áudio ou nos gráficos. Todos os recursos testados + O jogo funciona impecavelmente, sem nenhum problema no áudio ou nos gráficos. Todos os recursos testados do jogo funcionam como deveriam, sem nenhuma solução alternativa necessária. - + Great Ótimo - + Game functions with minor graphical or audio glitches and is playable from start to finish. May require some workarounds. - O jogo roda com pequenos problemas nos gráficos ou no áudio e é jogável do início ao fim. Pode exigir + O jogo funciona com pequenos problemas nos gráficos ou no áudio e é jogável do início ao fim. Pode exigir algumas soluções alternativas. - + Okay Razoável - + Game functions with major graphical or audio glitches, but game is playable from start to finish with workarounds. - O jogo roda com grandes problemas nos gráficos ou no áudio, mas é jogável do início ao fim + O jogo funciona com grandes problemas nos gráficos ou no áudio, mas é jogável do início ao fim com o uso de soluções alternativas. - + Bad Ruim - + Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches even with workarounds. - O jogo roda, só que com problemas significativos nos gráficos ou no áudio. Não é possível progredir em áreas + O jogo funciona, só que com problemas significativos nos gráficos ou no áudio. Não é possível progredir em áreas específicas por conta de tais problemas, mesmo com soluções alternativas. - + Intro/Menu Intro/menu - + Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start Screen. É impossível jogar o jogo devido a grandes problemas nos gráficos ou no áudio. Não é possível passar da tela inicial do jogo. - + Won't Boot Não inicia - + The game crashes when attempting to startup. O jogo trava ou se encerra abruptamente ao se tentar iniciá-lo. - + Not Tested Não testado - + The game has not yet been tested. O jogo ainda não foi testado. @@ -2921,7 +3167,7 @@ tela inicial do jogo. GameListPlaceholder - + Double-click to add a new folder to the game list Clique duas vezes para adicionar uma nova pasta à lista de jogos @@ -2929,27 +3175,27 @@ tela inicial do jogo. GameListSearchField - + of de - + result resultado - + results resultados - + Filter: Filtro: - + Enter pattern to filter Digite o padrão para filtrar @@ -2957,25 +3203,25 @@ tela inicial do jogo. GraphicsBreakPointsWidget - + Pica Breakpoints - Pica Breakpoints + Pontos de interrupção Pica - - + + Emulation running - Emulação rodando + Emulação em execução - + Resume Retomar - + Emulation halted at breakpoint - Emulação interrompida no breakpoint + Emulação interrompida no ponto de interrupção @@ -2983,17 +3229,17 @@ tela inicial do jogo. Pica Surface Viewer - Visualizador de Superfície Pica + Visualizador de superfícies Pica Color Buffer - Buffer de Cor + Buffer de cor Depth Buffer - Buffer de Profundidade + Buffer de profundidade @@ -3073,7 +3319,7 @@ tela inicial do jogo. (unable to access pixel data) - (incapaz de acessar dado de pixel) + (não é possível acessar o dado do pixel) @@ -3098,7 +3344,7 @@ tela inicial do jogo. Save Surface - Salvar Superfície + Salvar superfície @@ -3111,17 +3357,17 @@ tela inicial do jogo. Start Recording - Iniciar Gravação + Iniciar gravação Stop and Save - Parar e Salvar + Parar e salvar Abort Recording - Interromper Gravação + Interromper gravação @@ -3141,7 +3387,7 @@ tela inicial do jogo. A CiTrace is still being recorded. Do you want to save it? If not, all recorded data will be discarded. - Um CiTrace ainda está sendo gravado. Deseja salva-lo ? Se não, todos os dados gravados serão descartados. + Um CiTrace ainda está sendo gravado. Deseja salvá-lo? Caso contrário, todos os dados gravados serão descartados. @@ -3167,17 +3413,17 @@ tela inicial do jogo. Save Shader Dump - Salvar Extração de Shader + Salvar extração de shader Shader Binary (*.shbin) - Shader Binary (*.shbin) + Shader binário (*.shbin) (data only available at vertex shader invocation breakpoints) - (dados apenas disponíveis nos breakpoints de invocação do vertex shader) + (dados apenas disponíveis nos pontos de interrupção de invocação de vertex shaders) @@ -3187,7 +3433,7 @@ tela inicial do jogo. Input Data - Dado de Entrada + Dado de entrada @@ -3197,7 +3443,7 @@ tela inicial do jogo. Cycle Index: - Índice Cíclico: + Índice cíclico: @@ -3238,35 +3484,35 @@ tela inicial do jogo. Address Registers: %1, %2 - Registradores de Endereço: %1, %2 + Registradores de endereço: %1, %2 Compare Result: %1, %2 - Comparar Resultado: %1, %2 + Comparar resultado: %1, %2 Static Condition: %1 - Condição Estática: %1 + Condição estática: %1 Dynamic Conditions: %1, %2 - Condições Dinâmicas: %1, %2 + Condições dinâmicas: %1, %2 Loop Parameters: %1 (repeats), %2 (initializer), %3 (increment), %4 - Parametros de loop: %1 (repetições), %2 (inicializador), %3 (incremento), %4 + Parâmetros de loop: %1 (repetições), %2 (inicializador), %3 (incremento), %4 @@ -3290,27 +3536,27 @@ tela inicial do jogo. Create Room - Criar Sala + Criar sala Room Name - Nome da Sala + Nome da sala Preferred Game - Jogo Preferido + Jogo preferido Max Players - Máximo de Jogadores + Máximo de jogadores Username - Nome de Usuário + Nome de usuário @@ -3335,12 +3581,12 @@ tela inicial do jogo. Unlisted - Não Listada + Não listada Host Room - Hospedar Sala + Hospedar sala @@ -3356,7 +3602,7 @@ tela inicial do jogo. Public Room Browser - Navegador de Salas Públicas + Navegador de salas públicas @@ -3382,52 +3628,52 @@ tela inicial do jogo. Hide Full Rooms - Esconder salas cheias + Ocultar salas cheias Refresh Lobby - Atualizar Lobby + Atualizar lobby - + Password Required to Join É necessária uma senha para entrar - + Password: Senha: - + Room Name - Nome da Sala + Nome da sala - + Preferred Game - Jogo Preferido + Jogo preferido - + Host Host - + Players Jogadores - + Refreshing Atualizando - + Refresh List - Atualizar Lista + Atualizar lista @@ -3448,220 +3694,245 @@ tela inicial do jogo. Arquivos recentes - + + Amiibo + Amiibo + + + &Emulation &Emulação - + &View &Exibir - + Debugging Debugging - + Screen Layout - Layout da tela - - - - Movie - Gravador + Estilo da tela - Multiplayer - Multiplayer + Movie + Gravações - + + Multiplayer + Multijogador + + + &Help A&juda - + Load File... Carregar arquivo... - + Install CIA... Instalar CIA... - + Load Symbol Map... - Carregar Mapa de Símbolos... + Carregar mapa de símbolos... - + E&xit &Sair - + &Start &Iniciar - + &Pause &Pausar - + &Stop P&arar - + FAQ - FAQ + Perguntas frequentes - + About Citra Sobre o Citra - + Single Window Mode - Modo Janela Única + Modo de janela única - + Configure... Configurar... - + Display Dock Widget Headers - Exibir Cabeçalhos do Widget do Dock + Exibir títulos de widgets afixados - + Show Filter Bar Mostrar barra de filtro - + Show Status Bar Mostrar barra de status - + Select Game Directory... - Selecionar diretório de jogos... + Selecionar pasta de jogos... - + Selects a folder to display in the game list Selecionar a pasta para exibir na lista de jogos - + Create Pica Surface Viewer - Visualizador do Criador de Superfície Pica + Criar visualizador de superfícies Pica - + Record Movie Gravar passos - + Play Movie Reproduzir gravação - + Stop Recording / Playback Parar gravação/reprodução - + + Enable Frame Advancing + Ativar avanço de quadros + + + + Advance Frame + Avançar quadro + + + Browse Public Game Lobby - Navegar pelas Salas Públicas + Navegar pelas salas públicas - + Create Room - Criar Sala + Criar sala - + Leave Room - Sair da Sala + Sair da sala - + Direct Connect to Room - Conectar-se diretamente a uma Sala + Conectar-se diretamente a uma sala - + Show Current Room - Mostrar Sala Atual + Mostrar sala atual - + Fullscreen Tela cheia - + Modify Citra Install Modificar instalação do Citra - + Opens the maintenance tool to modify your Citra installation Abre a ferramenta de manutenção para modificar sua instalação do Citra - + Default Padrão - + Single Screen Tela única - + Large Screen Tela grande - + Side by Side Lado a lado - + Swap Screens Trocar telas - + Check for Updates Verificar atualizações - + Report Compatibility - Informar compatibilidade + Relatar compatibilidade - + Restart Reiniciar + + + Load... + Carregar... + + + + Remove + Remover + MicroProfileDialog @@ -3677,7 +3948,7 @@ tela inicial do jogo. Current connection status - Status atual de conexão + Estado atual da conexão @@ -3687,26 +3958,26 @@ tela inicial do jogo. - + Connected Conectado - + Not Connected Não conectado - + Error Erro - + Failed to announce the room to the public lobby. In order to host a room publicly, you must have a valid Citra account configured in Emulation -> Configure -> Web. If you do not want to publish a room in the public lobby, then select Unlisted instead. Debug Message: - Falha ao anunciar a sala no lobby público. Para poder hospedar uma sala publicamente, você deve ter uma conta válida do Citra configurada em Emulação > Configurar... > Rede. Se você não quiser publicar uma sala no lobby público, deixe-a como Não Listada. + Falha ao anunciar a sala no lobby público. Para poder hospedar uma sala publicamente, você precisa ter uma conta válida do Citra configurada em Emulação > Configurar... > Rede. Se você não quiser publicar uma sala no lobby público, deixe-a como não listada. Mensagem para depuração: @@ -3715,12 +3986,12 @@ Mensagem para depuração: Username is not valid. Must be 4 to 20 alphanumeric characters. - Nome de usuário não é válido. Deve conter entre 4 e 20 caracteres alfanuméricos. + Nome de usuário inválido. Precisa ter entre 4 e 20 caracteres alfanuméricos. Room name is not valid. Must be 4 to 20 alphanumeric characters. - Nome da sala não é válido. Deve conter entre 4 e 20 caracteres alfanuméricos. + Nome da sala inválido. Precisa ter entre 4 e 20 caracteres alfanuméricos. @@ -3735,7 +4006,7 @@ Mensagem para depuração: Port must be a number between 0 to 65535. - Porta deve ser um número entre 0 e 65535. + A porta deve ser um número entre 0 e 65535. @@ -3745,7 +4016,7 @@ Mensagem para depuração: Unable to connect to the host. Verify that the connection settings are correct. If you still cannot connect, contact the room host and verify that the host is properly configured with the external port forwarded. - Não foi possível se conectar ao host. Verifique se as configurações de conexão estão corretas. Se você ainda não puder se conectar, entre em contato com o dono da sala e verifique se as configurações da sala e o encaminhamento externo da porta estão corretos. + Não foi possível se conectar ao host. Verifique se as configurações de conexão estão corretas. Se ainda assim você não conseguir se conectar, entre em contato com o dono da sala e verifique se as configurações da sala e o encaminhamento externo da porta estão corretos. @@ -3760,7 +4031,7 @@ Mensagem para depuração: Version mismatch! Please update to the latest version of Citra. If the problem persists, contact the room host and ask them to update the server. - Incompatibilidade de versões! Por favor, atualize seu Citra para a última versão. Se o problema persistir, entre em contato com o dono da sala e peça para que atualize o servidor. + Incompatibilidade de versões! Por favor, atualize seu Citra para a versão mais recente. Se o problema persistir, entre em contato com o dono da sala e peça para que atualize o servidor. @@ -3770,7 +4041,7 @@ Mensagem para depuração: An unknown error occured. If this error continues to occur, please open an issue - Ocorreu um erro desconhecido. Se esse erro persistir, abra uma issue no GitHub + Ocorreu um erro desconhecido. Se esse erro persistir, relate-o no GitHub. @@ -3785,7 +4056,7 @@ Mensagem para depuração: Leave Room - Sair da Sala + Sair da sala @@ -3822,106 +4093,106 @@ Mensagem para depuração: %1 está jogando %2 - - + + Invalid region Região inválida - + Japan Japão - + North America América do Norte - + Europe Europa - + Australia Austrália - + China China - + Korea - Coréia + Coreia - + Taiwan Taiwan - + Region free Livre de região - + Invalid Region Região inválida - + Shift Shift - + Ctrl Ctrl - + Alt Alt - - + + [not set] [não definido] - + Hat %1 %2 Hat %1 %2 - + Axis %1%2 Eixo %1%2 - + Button %1 Botão %1 - - + + [unknown] [desconhecido] - + [unused] - [não usado] + [não utilizado] - - + + Axis %1 Eixo %1 @@ -3943,7 +4214,7 @@ Mensagem para depuração: Open File - Abrir Arquivo + Abrir arquivo @@ -3989,27 +4260,27 @@ Mensagem para depuração: VFP System Registers - Registradores de Sistema VFP + Registradores de sistema VFP Vector Length - Comprimento de Vetor + Comprimento de vetor Vector Stride - Passo de Vetor + Passo de vetor Rounding Mode - Modo de Arredondamento + Modo de arredondamento Vector Iteration Count - Contagem de Iterações de Vetor + Contagem de iterações de vetor @@ -4046,12 +4317,12 @@ Mensagem para depuração: waiting for all objects - esperando por todos os objetos + aguardando todos os objetos waiting for one of the following objects - Esperando por um dos seguintes objetos + aguardando um dos seguintes objetos @@ -4072,7 +4343,7 @@ Mensagem para depuração: running - rodando + em execução @@ -4082,7 +4353,7 @@ Mensagem para depuração: waiting for address 0x%1 - esperando pelo endereço 0x%1 + aguardando endereço 0x%1 @@ -4092,17 +4363,17 @@ Mensagem para depuração: waiting for IPC response - esperando por resposta IPC + aguardando resposta IPC waiting for objects - esperando por objetos + aguardando objetos waiting for HLE return - esperando pelo retorno HLE + aguardando retorno HLE diff --git a/dist/languages/pt_PT.ts b/dist/languages/pt_PT.ts index fd901ad2b..9a2233588 100644 --- a/dist/languages/pt_PT.ts +++ b/dist/languages/pt_PT.ts @@ -64,47 +64,52 @@ p, li { white-space: pre-wrap; } BreakPointModel - + Pica command loaded Comando Pica carregado - + Pica command processed Comando Pica processado - + Incoming primitive batch Batch primitivo a chegar - + Finished primitive batch Batch primitivo terminado - + Vertex shader invocation Invocação do Vertex shader - + Incoming display transfer Transferência de display a chegar - + GSP command processed Comando GSP processado - + Buffers swapped Buffers swapped PS: you can replace "swapped" for "trocados" but between programmers we Portuguese use the English word. + + + Unknown debug context event + + CalibrationConfigurationDialog @@ -167,13 +172,13 @@ PS: you can replace "swapped" for "trocados" but between pro Jogo - - + + Block Player - + When you block a player, you will no longer receive chat messages from them.<br><br>Are you sure you would like to block %1? @@ -194,17 +199,17 @@ PS: you can replace "swapped" for "trocados" but between pro ClientRoomWindow - + Connected Conectado - + Disconnected Desconectado - + %1 (%2/%3 members) - connected %1 (%2/%3 membros) - conectados @@ -297,6 +302,26 @@ PS: you can replace "swapped" for "trocados" but between pro Thank you for your submission! Obrigado pela tua submissão! + + + Submitting + + + + + Communication error + + + + + An error occured while sending the Testcase + + + + + Next + + ConfigureAudio @@ -336,9 +361,9 @@ PS: you can replace "swapped" for "trocados" but between pro - - - %1 % + + %1% + Volume percentage (e.g. 50%) @@ -602,50 +627,78 @@ PS: you can replace "swapped" for "trocados" but between pro ConfigureDialog - + Citra Configuration Configuração do Citra - + + + General Geral - + + + System Sistema - + + + Input Entrada - + + + Graphics Gráficos - + + + Audio Áudio - + + + Camera Câmara - + + + Debug Debug - + + + Web Web + + + + + UI + + + + + Controls + + ConfigureGeneral @@ -665,64 +718,54 @@ PS: you can replace "swapped" for "trocados" but between pro Confirma saída enquanto a emulação está a correr - - Interface language - Línguagem de interface - - - + Updates Atualizações - + Check for updates on start Procurar atualizações no arranque - + Silently auto update after closing Atualizar automaticamente após fechar - + Emulation Emulação - + Region: Região: - + Auto-select Seleção automática - - Theme - Tema - - - - Theme: - Tema: - - - + Hotkeys Hotkeys - - <System> - <System> + + Reset All Settings + - - English - Inglês + + Citra + + + + + Are you sure you want to <b>reset your settings</b> and close Citra? + @@ -1022,7 +1065,7 @@ PS: you can replace "swapped" for "trocados" but between pro - + Set Analog Stick Definir Stick Analógico @@ -1063,11 +1106,44 @@ PS: you can replace "swapped" for "trocados" but between pro + Clear All + + + + Restore Defaults Restaurar Padrão - + + + Clear + + + + + + [not set] + + + + + + Restore Default + + + + + Information + + + + + After pressing OK, first move your joystick horizontally, and then vertically. + + + + [press key] [pressiona a tecla] @@ -1436,17 +1512,27 @@ PS: you can replace "swapped" for "trocados" but between pro - + + yyyy-MM-ddTHH:mm:ss + + + + + Play Coins: + + + + Console ID: ID da Consola: - + Regenerate Regenerar - + System settings are available only when game is not running. As definições do sistema estão apenas disponíveis quando o jogo não está a correr. @@ -2116,22 +2202,120 @@ PS: you can replace "swapped" for "trocados" but between pro - - + + Console ID: 0x%1 ID da Consola: 0x%1 - + This will replace your current virtual 3DS with a new one. Your current virtual 3DS will not be recoverable. This might have unexpected effects in games. This might fail, if you use an outdated config savegame. Continue? Isto irá substituir a tua atual 3DS virtual por uma nova. A tua 3DS virtual atual não será recuperável. Isto poderá ter efeitos não esperados nos jogos. Isto poderá falhar se usar um ficheiro config savegame fora de prazo. Continuar? - + Warning Aviso + + ConfigureUi + + + Form + + + + + General + + + + + Interface language: + + + + + Theme: + + + + + Game List + + + + + Icon Size: + + + + + + None + + + + + Small (24x24) + + + + + Large (48x48) + + + + + Row 1 Text: + + + + + + File Name + + + + + + Full Path + + + + + + Title Name + + + + + + Title ID + + + + + Row 2 Text: + + + + + Hide Titles without Icon + + + + + <System> + + + + + English + + + ConfigureWeb @@ -2151,7 +2335,7 @@ PS: you can replace "swapped" for "trocados" but between pro - + Verify Verificar @@ -2211,48 +2395,48 @@ PS: you can replace "swapped" for "trocados" but between pro - + <a href='https://citra-emu.org/entry/telemetry-and-why-thats-a-good-thing/'><span style="text-decoration: underline; color:#039be5;">Learn more</span></a> <a href='https://citra-emu.org/entry/telemetry-and-why-thats-a-good-thing/'><span style="text-decoration: underline; color:#039be5;">Saber mais</span></a> - - <a href='https://services.citra-emu.org/'><span style="text-decoration: underline; color:#039be5;">Sign up</span></a> - <a href='https://services.citra-emu.org/'><span style="text-decoration: underline; color:#039be5;">Registar</span></a> + + <a href='https://profile.citra-emu.org/'><span style="text-decoration: underline; color:#039be5;">Sign up</span></a> + - + <a href='https://citra-emu.org/wiki/citra-web-service/'><span style="text-decoration: underline; color:#039be5;">What is my token?</span></a> <a href='https://citra-emu.org/wiki/citra-web-service/'><span style="text-decoration: underline; color:#039be5;">Qual é a minha token?</span></a> - - + + Telemetry ID: 0x%1 ID de Telemetria: 0x%1 - + Username and token not verified Nome de Utilizador e token não verificados - + Username and token were not verified. The changes to your username and/or token have not been saved. O Nome de Utilizador e a token não foram verificados. As mudanças ao teu nome de utilizador e/ou token não foram guardadas. - + Verifying A Verificar - + Verification failed Falha na Verificação - + Verification failed. Check that you have entered your username and token correctly, and that your internet connection is working. Falha na Verificação. Verifica se inseriste o teu nome de utilizador e token corretamente, e que a tua ligação à internet está a funcionar. @@ -2313,12 +2497,12 @@ PS: you can replace "swapped" for "trocados" but between pro DirectConnectWindow - + Connecting A Conectar - + Connect Conectar @@ -2326,413 +2510,445 @@ PS: you can replace "swapped" for "trocados" but between pro GMainWindow - + <a href='https://citra-emu.org/entry/telemetry-and-why-thats-a-good-thing/'>Anonymous data is collected</a> to help improve Citra. <br/><br/>Would you like to share your usage data with us? - + Telemetry - - + + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a 3DS. Velocidade de emulação atual. Valores superiores ou inferiores a 100% indicam que a emulação está a correr mais rápida ou mais lenta que uma 3DS. - - + + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. Quantos frames por segundo o jogo está atualmente a mostrar. Isto varia de jogo para jogo e de cena para cena. - - + + Time taken to emulate a 3DS frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. Tempo demorado para emular um frame de 3DS, descontando framelimiting ou v-sync. Para velocidade total de emulação, isto deveria ser, no máximo, 16.67 ms. - + Clear Recent Files - + F9 F9 - + F10 F10 - + CTRL+F CTRL+F - + Update Available - + An update is available. Would you like to install it now? - + No Update Found - + No update is found. - - + + OpenGL 3.3 Unsupported + + + + + Your GPU may not support OpenGL 3.3, or you do not have the latest graphics driver. + + + + + Invalid ROM Format - - + + Your ROM format is not supported.<br/>Please follow the guides to redump your <a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>game cartridges</a> or <a href='https://citra-emu.org/wiki/dumping-installed-titles/'>installed titles</a>. - + ROM Corrupted - + Your ROM is corrupted. <br/>Please follow the guides to redump your <a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>game cartridges</a> or <a href='https://citra-emu.org/wiki/dumping-installed-titles/'>installed titles</a>. - + ROM Encrypted - + Your ROM is encrypted. <br/>Please follow the guides to redump your <a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>game cartridges</a> or <a href='https://citra-emu.org/wiki/dumping-installed-titles/'>installed titles</a>. - - + + Video Core Error - + An error has occured. Please <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>see the log</a> for more details. Ensure that you have the latest graphics drivers for your GPU. - + You are running default Windows drivers for your GPU. You need to install the proper drivers for your graphics card from the manufacturer's website. - + Error while loading ROM! Erro a ler a ROM! - + An unknown error occured. Please see the log for more details. Ocorreu um erro desconhecido. Por favor veja o log para mais detalhes. - + Start Iniciar - + Error Opening %1 Folder Erro ao abrir a pasta %1 - - + + Folder does not exist! A pasta não existe! - + Error Opening %1 Erro ao Abrir %1 - + Select Directory Selecionar o Diretório - - 3DS Executable - Executável 3DS + + 3DS Executable (%1);;All Files (*.*) + %1 is an identifier for the 3DS executable file extensions. + - - - All Files (*.*) - Todos os Ficheiros (*.*) - - - + Load File Carregar Ficheiro - + Load Files Carregar Ficheiros - + 3DS Installation File (*.CIA*) Ficheiro de instalação 3DS (*.CIA) - + + All Files (*.*) + Todos os Ficheiros (*.*) + + + %1 has been installed successfully. %1 foi instalado com sucesso. - + Unable to open File Impossível abrir o Ficheiro - + Could not open %1 Impossível abrir %1 - + Installation aborted Instalação abortada - + The installation of %1 was aborted. Please see the log for more details A instalação de %1 foi abortada. Por favor veja o log para mais detalhes - + Invalid File Ficheiro Inválido - + %1 is not a valid CIA %1 não é um CIA válido - + Encrypted File Ficheiro Encriptado - + %1 must be decrypted before being used with Citra. A real 3DS is required. %1 deve ser desencriptado antes de ser usado com o Citra. Uma 3DS real é necessária. - + File not found Ficheiro não encontrado - + File "%1" not found Ficheiro "%1" não encontrado - - - + + + Continue Continuar - + Missing Citra Account Conta Citra em Falta - + You must link your Citra account to submit test cases.<br/>Go to Emulation &gt; Configure... &gt; Web to do so. - - - + + Amiibo File (%1);; All Files (*.*) + + + + + Load Amiibo + + + + + + + Record Movie - - + + To keep consistency with the RNG, it is recommended to record the movie from game start.<br>Are you sure you still want to record movies now? + + + + + Citra TAS Movie (*.ctm) - + Recording will start once you boot a game. - + The movie file you are trying to load was created on a different revision of Citra.<br/>Citra has had some changes during the time, and the playback may desync or not work as expected.<br/><br/>Are you sure you still want to load the movie file? - + The movie file you are trying to load was recorded with a different game.<br/>The playback may not work as expected, and it may cause unexpected results.<br/><br/>Are you sure you still want to load the movie file? - - + + The movie file you are trying to load is invalid.<br/>Either the file is corrupted, or Citra has had made some major changes to the Movie module.<br/>Please choose a different movie file and try again. - + Revision Dismatch - + Game Dismatch - - + + Invalid Movie File - + + Play Movie - + + To keep consistency with the RNG, it is recommended to play the movie from game start.<br>Are you sure you still want to play movies now? + + + + Game Not Found - + The movie you are trying to play is from a game that is not in the game list. If you own the game, please add the game folder to the game list and try to play the movie again. - + Movie recording cancelled. - + Movie Saved - + The movie is successfully saved. - + Speed: %1% / %2% Velocidade: %1% / %2% - + Speed: %1% Velocidade: %1% - + Game: %1 FPS Jogo: %1 FPS - + Frame: %1 ms Frame: %1 ms - + %1 is missing. Please <a href='https://citra-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-3ds-console/'>dump your system archives</a>.<br/>Continuing emulation may result in crashes and bugs. - + System Archive Not Found Arquivo de Sistema Não Encontrado - + Fatal Error Erro Fatal - + A fatal error occured. <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>Check the log</a> for details.<br/>Continuing emulation may result in crashes and bugs. - + Abort - - + + Citra Citra - + Would you like to exit now? - + The game is still running. Would you like to stop emulation? - + Playback Completed - + Movie playback completed. - + Citra %1 Citra %1 - + Citra %1| %2 Citra %1| %2 @@ -2795,37 +3011,67 @@ PS: you can replace "swapped" for "trocados" but between pro GameList - + + Name + + + + + Compatibility + + + + + Region + + + + + File type + + + + + Size + + + + Open Save Data Location Abrir a Localização de Dados Guardados - + + Open Extra Data Location + + + + Open Application Location Abrir Localização de Aplicações - + Open Update Data Location Abrir Localização de Dados de Atualização - + Navigate to GameDB entry Navegar para a entrada do GameDB - + Scan Subfolders - + Remove Game Directory - + Open Directory Location @@ -2833,77 +3079,77 @@ PS: you can replace "swapped" for "trocados" but between pro GameListItemCompat - + Perfect - + Game functions flawless with no audio or graphical glitches, all tested functionality works as intended without any workarounds needed. - + Great - + Game functions with minor graphical or audio glitches and is playable from start to finish. May require some workarounds. - + Okay - + Game functions with major graphical or audio glitches, but game is playable from start to finish with workarounds. - + Bad - + Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches even with workarounds. - + Intro/Menu - + Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start Screen. - + Won't Boot - + The game crashes when attempting to startup. - + Not Tested - + The game has not yet been tested. @@ -2911,7 +3157,7 @@ Screen. GameListPlaceholder - + Double-click to add a new folder to the game list @@ -2919,27 +3165,27 @@ Screen. GameListSearchField - + of - + result - + results - + Filter: - + Enter pattern to filter @@ -2947,23 +3193,23 @@ Screen. GraphicsBreakPointsWidget - + Pica Breakpoints Pica Breakpoints - - + + Emulation running Emulação a correr - + Resume Retomar - + Emulation halted at breakpoint Emulação parada num breakpoint @@ -3380,42 +3626,42 @@ Screen. Atualizar Lobby - + Password Required to Join Palavra Passe Necessária para se Juntar - + Password: Palavra Passe: - + Room Name Nome da Sala - + Preferred Game Jogo Preferido - + Host Hóspede - + Players Jogadores - + Refreshing A Atualizar - + Refresh List Atualizar a Lista @@ -3438,220 +3684,245 @@ Screen. Ficheiros Recentes - + + Amiibo + + + + &Emulation &Emulação - + &View &Vista - + Debugging Debugging - + Screen Layout Layout de Ecrã - + Movie - + Multiplayer Multi Jogador - + &Help &Ajuda - + Load File... Ler Ficheiro... - + Install CIA... Instalar CIA... - + Load Symbol Map... Ler Mapa de Símbolos... - + E&xit &Sair - + &Start &Iniciar - + &Pause &Pausar - + &Stop &Parar - + FAQ FAQ - + About Citra Sobre o Citra - + Single Window Mode Modo de Janela Única - + Configure... Configurar... - + Display Dock Widget Headers Mostrar Dock Widget Headers - + Show Filter Bar Exibir Barra de Filtro - + Show Status Bar Exibir Barra de Estado - + Select Game Directory... Selecionar o Diretório de Jogos... - + Selects a folder to display in the game list Seleciona uma pasta para mostrar a lista de jogos - + Create Pica Surface Viewer Criar uma Pica Surface Viewer - + Record Movie - + Play Movie - + Stop Recording / Playback - + + Enable Frame Advancing + + + + + Advance Frame + + + + Browse Public Game Lobby Pesquisar o Lobby de Jogos Públicos - + Create Room Criar Sala - + Leave Room Sair da Sala - + Direct Connect to Room Conecção Direta à Sala - + Show Current Room Mostrar a Sala Atual - + Fullscreen Ecrã-Inteiro - + Modify Citra Install Modificar a Instalação do Citra - + Opens the maintenance tool to modify your Citra installation Abre a ferramenta de manutenção para modificar a tua instalação do Citra - + Default Padrão - + Single Screen Ecrã Único - + Large Screen Ecrã Largo - + Side by Side Lado a Lado - + Swap Screens Trocar Ecrãs - + Check for Updates Verificar a existência de Atualizações - + Report Compatibility Reportar Compatibilidade - + Restart + + + Load... + + + + + Remove + + MicroProfileDialog @@ -3677,23 +3948,23 @@ Screen. - + Connected Conectado - + Not Connected Não Conectado - + Error Erro - + Failed to announce the room to the public lobby. In order to host a room publicly, you must have a valid Citra account configured in Emulation -> Configure -> Web. If you do not want to publish a room in the public lobby, then select Unlisted instead. Debug Message: Falhou a anunciar a sala ao lobby público. De forma a hospedar uma sala publicamente, deve possuir uma conta Citra válida configurada em Emulação -> Configurar -> Web. Se não deseja publicar uma sala no lobby público, selecione "Não Listada". @@ -3812,106 +4083,106 @@ Mensagem de Debug: %1 está a jogar %2 - - + + Invalid region Região Inválida - + Japan Japão - + North America América do Norte - + Europe Europa - + Australia Austrália - + China China - + Korea Coreia - + Taiwan Taiwan - + Region free Livre de Região - + Invalid Region Região Inválida - + Shift Shift - + Ctrl Ctrl - + Alt Alt - - + + [not set] [não definido] - + Hat %1 %2 - + Axis %1%2 - + Button %1 - - + + [unknown] [desconhecido] - + [unused] [não utilizado] - - + + Axis %1 diff --git a/dist/languages/ru_RU.ts b/dist/languages/ru_RU.ts index 2965f3322..d7589e7d4 100644 --- a/dist/languages/ru_RU.ts +++ b/dist/languages/ru_RU.ts @@ -70,45 +70,50 @@ p, li { white-space: pre-wrap; } BreakPointModel - + Pica command loaded Команда Pica загружена - + Pica command processed Команда Pica выполнена - + Incoming primitive batch Поступающий пакет примитивов - + Finished primitive batch Загрузка пакета примитивов завершена - + Vertex shader invocation Вызов вершинного шейдера - + Incoming display transfer Передача входящего отображения - + GSP command processed Команда GSP обработана - + Buffers swapped Буферы заменены + + + Unknown debug context event + Неизвестное событие с контекстом отладки + CalibrationConfigurationDialog @@ -171,13 +176,13 @@ p, li { white-space: pre-wrap; } Игра - - + + Block Player Заблокировать игрока - + When you block a player, you will no longer receive chat messages from them.<br><br>Are you sure you would like to block %1? Когда вы заблокируете игрока, то не сможете получать от него сообщения в чате.<br><br>Вы уверены, что хотите заблокировать %1? @@ -198,17 +203,17 @@ p, li { white-space: pre-wrap; } ClientRoomWindow - + Connected Подключился - + Disconnected Отключился - + %1 (%2/%3 members) - connected %1 (%2/%3 участников) - подключен @@ -301,6 +306,26 @@ p, li { white-space: pre-wrap; } Thank you for your submission! Благодарим за предоставленную информацию! + + + Submitting + Отправка + + + + Communication error + Ошибка соединения + + + + An error occured while sending the Testcase + При отправке тестовой информации произошла ошибка + + + + Next + Далее + ConfigureAudio @@ -340,10 +365,10 @@ p, li { white-space: pre-wrap; } 0 % - - - %1 % - %1 % + + %1% + Volume percentage (e.g. 50%) + %1% @@ -606,50 +631,78 @@ p, li { white-space: pre-wrap; } ConfigureDialog - + Citra Configuration Настройки Citra - + + + General Общие - + + + System Система - + + + Input Управление - + + + Graphics Графика - + + + Audio Аудио - + + + Camera Камера - + + + Debug Отладка - + + + Web Веб + + + + + UI + Интерфейс + + + + Controls + Управление + ConfigureGeneral @@ -669,64 +722,54 @@ p, li { white-space: pre-wrap; } Подтверждать выход при запущенной эмуляции - - Interface language - Язык интерфейса - - - + Updates Обновления - + Check for updates on start Проверять обновления при запуске - + Silently auto update after closing Тихое автообновление после закрытия - + Emulation Эмуляция - + Region: Регион: - + Auto-select Автоматически - - Theme - Тема - - - - Theme: - Тема: - - - + Hotkeys Горячие клавиши - - <System> - <System> + + Reset All Settings + Сбросить все настройки - - English - English + + Citra + Citra + + + + Are you sure you want to <b>reset your settings</b> and close Citra? + Вы уверены, что хотите <b>сбросить свои настройки</b> и закрыть Citra? @@ -1026,7 +1069,7 @@ p, li { white-space: pre-wrap; } - + Set Analog Stick Назначить аналоговый стик @@ -1067,11 +1110,44 @@ p, li { white-space: pre-wrap; } + Clear All + Очистить всё + + + Restore Defaults Восстановить по умолчанию - + + + Clear + Очистить + + + + + [not set] + [не назначено] + + + + + Restore Default + По умолчанию + + + + Information + Информация + + + + After pressing OK, first move your joystick horizontally, and then vertically. + После нажатия OK двигайте стик по горизонтали, а затем по вертикали. + + + [press key] [нажмите кнопку] @@ -1440,17 +1516,27 @@ p, li { white-space: pre-wrap; } Время при запуске - + + yyyy-MM-ddTHH:mm:ss + dd-MM-yyyyTHH:mm:ss + + + + Play Coins: + Игровые монеты: + + + Console ID: ID консоли: - + Regenerate Пересоздать - + System settings are available only when game is not running. Настройки системы доступны только когда игра не запущена. @@ -2120,22 +2206,120 @@ p, li { white-space: pre-wrap; } Бермудские острова - - + + Console ID: 0x%1 ID консоли: 0x%1 - + This will replace your current virtual 3DS with a new one. Your current virtual 3DS will not be recoverable. This might have unexpected effects in games. This might fail, if you use an outdated config savegame. Continue? Это заменит вашу нынешнюю виртуальную 3DS новой. Вашу текущую виртуальную 3DS нельзя будет вернуть. Это может иметь неожиданные эффекты в играх. Генерация может не сработать, если вы используете устаревший "Config Savegame". Продолжить? - + Warning Предупреждение + + ConfigureUi + + + Form + Форма + + + + General + Общее + + + + Interface language: + Язык интерфейса: + + + + Theme: + Тема: + + + + Game List + Список игр + + + + Icon Size: + Размер иконок: + + + + + None + Отсутствуют + + + + Small (24x24) + Маленькие (24x24) + + + + Large (48x48) + Большие (48x48) + + + + Row 1 Text: + Текст на 1-й строке: + + + + + File Name + Имя файла + + + + + Full Path + Полный путь + + + + + Title Name + Название + + + + + Title ID + Идентификатор + + + + Row 2 Text: + Текст на 2-й строке: + + + + Hide Titles without Icon + Скрывать элементы без иконок + + + + <System> + <System> + + + + English + Английский + + ConfigureWeb @@ -2155,7 +2339,7 @@ p, li { white-space: pre-wrap; } - + Verify Подтвердить @@ -2215,48 +2399,48 @@ p, li { white-space: pre-wrap; } Показывать текущую игру в статусе Discord - + <a href='https://citra-emu.org/entry/telemetry-and-why-thats-a-good-thing/'><span style="text-decoration: underline; color:#039be5;">Learn more</span></a> <a href='https://citra-emu.org/entry/telemetry-and-why-thats-a-good-thing/'><span style="text-decoration: underline; color:#039be5;">Узнать больше</span></a> - - <a href='https://services.citra-emu.org/'><span style="text-decoration: underline; color:#039be5;">Sign up</span></a> - <a href='https://services.citra-emu.org/'><span style="text-decoration: underline; color:#039be5;">Регистрация</span></a> + + <a href='https://profile.citra-emu.org/'><span style="text-decoration: underline; color:#039be5;">Sign up</span></a> + <a href='https://profile.citra-emu.org/'><span style="text-decoration: underline; color:#039be5;">Зарегистрироваться</span></a> - + <a href='https://citra-emu.org/wiki/citra-web-service/'><span style="text-decoration: underline; color:#039be5;">What is my token?</span></a> <a href='https://citra-emu.org/wiki/citra-web-service/'><span style="text-decoration: underline; color:#039be5;">Что такое токен?</span></a> - - + + Telemetry ID: 0x%1 ID телеметрии: 0x%1 - + Username and token not verified Имя пользователя и токен не верифицированы - + Username and token were not verified. The changes to your username and/or token have not been saved. Имя пользователя и токен не были верифицированы. Изменения в имени пользователя и/или токене не были сохранены. - + Verifying Верификация - + Verification failed Верификация не удалась - + Verification failed. Check that you have entered your username and token correctly, and that your internet connection is working. Ошибка верификации. Убедитесь, что вы правильно ввели свое имя пользователя и токен, и что ваше интернет-соединение работает. @@ -2317,12 +2501,12 @@ p, li { white-space: pre-wrap; } DirectConnectWindow - + Connecting Подключение - + Connect Подключиться @@ -2330,413 +2514,445 @@ p, li { white-space: pre-wrap; } GMainWindow - + <a href='https://citra-emu.org/entry/telemetry-and-why-thats-a-good-thing/'>Anonymous data is collected</a> to help improve Citra. <br/><br/>Would you like to share your usage data with us? Для помощи в улучшении Citra <a href='https://citra-emu.org/entry/telemetry-and-why-thats-a-good-thing/'>собираются анонимные данные</a>. <br/><br/>Вы хотите поделиться с нами своими данными об использовании? - + Telemetry Телеметрия - - + + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a 3DS. Текущая скорость эмуляции. Значения выше или ниже 100% указывают, что эмуляция работает быстрее или медленнее, чем 3DS. - - + + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. Сколько кадров в секунду сейчас отображается в игре. Это будет варьироваться от игры к игре и от сцены к сцене. - - + + Time taken to emulate a 3DS frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. Время, затраченное на эмуляцию кадра 3DS, не считая ограничения кадров или вертикальной синхронизации. Для полноскоростной эмуляции это значение должно быть не более 16,67 мс. - + Clear Recent Files Очистить последние файлы - + F9 F9 - + F10 F10 - + CTRL+F CTRL+F - + Update Available Доступно обновление - + An update is available. Would you like to install it now? Доступно обновление. Вы хотите установить его сейчас? - + No Update Found Обновления не найдены - + No update is found. Обновления не найдены. - - + + OpenGL 3.3 Unsupported + Не поддерживается OpenGL 3.3 + + + + Your GPU may not support OpenGL 3.3, or you do not have the latest graphics driver. + Ваш ГП может не поддерживать OpenGL 3.3, или у вас установлена не последняя версия драйвера видеокарты. + + + + Invalid ROM Format Некорректный формат ROM - - + + Your ROM format is not supported.<br/>Please follow the guides to redump your <a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>game cartridges</a> or <a href='https://citra-emu.org/wiki/dumping-installed-titles/'>installed titles</a>. Формат вашего ROM не поддерживается.<br/>Следуйте инструкциям, чтобы сделать новый дамп ваших <a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>игровых картриджей</a> или <a href='https://citra-emu.org/wiki/dumping-installed-titles/'>установленных продуктов</a>. - + ROM Corrupted ROM повреждён - + Your ROM is corrupted. <br/>Please follow the guides to redump your <a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>game cartridges</a> or <a href='https://citra-emu.org/wiki/dumping-installed-titles/'>installed titles</a>. Ваш ROM повреждён. <br/>Следуйте инструкциям, чтобы сделать новый дамп ваших <a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>игровых картриджей</a> или <a href='https://citra-emu.org/wiki/dumping-installed-titles/'>установленных продуктов</a>. - + ROM Encrypted ROM зашифрован - + Your ROM is encrypted. <br/>Please follow the guides to redump your <a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>game cartridges</a> or <a href='https://citra-emu.org/wiki/dumping-installed-titles/'>installed titles</a>. Ваш ROM зашифрован. <br/>Следуйте инструкциям, чтобы сделать новый дамп ваших <a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>игровых картриджей</a> или <a href='https://citra-emu.org/wiki/dumping-installed-titles/'>установленных продуктов</a>. - - + + Video Core Error Ошибка видеоядра - + An error has occured. Please <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>see the log</a> for more details. Ensure that you have the latest graphics drivers for your GPU. Произошла ошибка. Информацию вы найдёте <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>в логе</a>. Убедитесь, что у вас стоит последняя версия драйвера для вашего ГП. - + You are running default Windows drivers for your GPU. You need to install the proper drivers for your graphics card from the manufacturer's website. Вы используете стандартные драйверы ГП Windows. Вам необходимо установить подходящие драйверы для вашей видеокарты с сайта производителя. - + Error while loading ROM! Ошибка при загрузке ROM! - + An unknown error occured. Please see the log for more details. Произошла неизвестная ошибка. Более подробную информацию см. в Журнале. - + Start Запуск - + Error Opening %1 Folder Ошибка открытия папки %1 - - + + Folder does not exist! Папка не существует! - + Error Opening %1 Ошибка при открытии %1 - + Select Directory Выбрать каталог - - 3DS Executable - Исполняемый файл 3DS + + 3DS Executable (%1);;All Files (*.*) + %1 is an identifier for the 3DS executable file extensions. + Исполняемый файл 3DS (%1);;Все файлы (*.*) - - - All Files (*.*) - Все файлы (*.*) - - - + Load File Загрузить файл - + Load Files Загрузка файлов - + 3DS Installation File (*.CIA*) Установочный файл 3DS (*.CIA*) - + + All Files (*.*) + Все файлы (*.*) + + + %1 has been installed successfully. %1 был успешно установлен. - + Unable to open File Невозможно открыть файл - + Could not open %1 Не удалось открыть %1 - + Installation aborted Установка прервана - + The installation of %1 was aborted. Please see the log for more details Установка %1 была прервана. Более подробную информацию см. в Журнале. - + Invalid File Неправильный файл - + %1 is not a valid CIA %1 не является корректным CIA - + Encrypted File Зашифрованный файл - + %1 must be decrypted before being used with Citra. A real 3DS is required. %1 должен быть расшифрован перед использованием с Citra. Требуется реальная 3DS. - + File not found Файл не найден - + File "%1" not found Файл "%1" не найден - - - + + + Continue Продолжить - + Missing Citra Account Аккаунт Citra не найден - + You must link your Citra account to submit test cases.<br/>Go to Emulation &gt; Configure... &gt; Web to do so. Для отправки тестовой информации вы должны привязать аккаунт Citra.<br/>Это можно сделать в Эмуляция &gt; Настроить... &gt; Веб. - - - + + Amiibo File (%1);; All Files (*.*) + Файл Amiibo (%1);; Все файлы (*.*) + + + + Load Amiibo + Загрузить Amiibo + + + + + + Record Movie Записать ролик - - + + To keep consistency with the RNG, it is recommended to record the movie from game start.<br>Are you sure you still want to record movies now? + Чтобы сохранить согласованность с ГСЧ, рекомендуется записывать ролики с начала игры.<br>Вы уверены, что хотите записать ролики? + + + + Citra TAS Movie (*.ctm) TAS-ролик Citra (*.ctm) - + Recording will start once you boot a game. Запись начнётся после загрузки игры. - + The movie file you are trying to load was created on a different revision of Citra.<br/>Citra has had some changes during the time, and the playback may desync or not work as expected.<br/><br/>Are you sure you still want to load the movie file? Файл ролика, который вы пытаетесь загрузить, создан на другой ревизии Citra.<br/>С тех пор в Citra были произведены изменения, и может возникнуть рассинхрон или другое неожиданное поведение.<br/><br/>Вы уверены, что хотите загрузить данный файл ролика? - + The movie file you are trying to load was recorded with a different game.<br/>The playback may not work as expected, and it may cause unexpected results.<br/><br/>Are you sure you still want to load the movie file? Файл ролика, который вы пытаетесь загрузить, записан с другой игры.<br/>Воспроизведение может привести к неожиданным результатам.<br/><br/>Вы уверены, что хотите загрузить данный файл ролика? - - + + The movie file you are trying to load is invalid.<br/>Either the file is corrupted, or Citra has had made some major changes to the Movie module.<br/>Please choose a different movie file and try again. Файл ролика, который вы пытаетесь загрузить, некорректный.<br/>Или файл повреждён, или в модуле записи роликов Citra произошли большие изменения.<br/>Пожалуйста, выберите другой файл ролика и попробуйте снова. - + Revision Dismatch Несоответствие ревизии - + Game Dismatch Несоответствие игры - - + + Invalid Movie File Некорректный файл ролика - + + Play Movie Воспроизвести ролик - + + To keep consistency with the RNG, it is recommended to play the movie from game start.<br>Are you sure you still want to play movies now? + Чтобы сохранить согласованность с ГСЧ, рекомендуется проигрывать ролики с начала игры.<br>Вы уверены, что хотите проиграть ролики? + + + Game Not Found Игра не найдена - + The movie you are trying to play is from a game that is not in the game list. If you own the game, please add the game folder to the game list and try to play the movie again. Ролик, который вы пытаетесь воспроизвести, из игры, которой нет в вашем списке игр. Если у вас есть эта игра, пожалуйста, добавьте папку с игрой в список игр и попробуйте воспроизвести ролик ещё раз. - + Movie recording cancelled. Запись ролика отменена. - + Movie Saved Ролик сохранён - + The movie is successfully saved. Ролик успешно сохранён. - + Speed: %1% / %2% Скорость: %1% / %2% - + Speed: %1% Скорость: %1% - + Game: %1 FPS Игра: %1 FPS - + Frame: %1 ms Кадр: %1 мс - + %1 is missing. Please <a href='https://citra-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-3ds-console/'>dump your system archives</a>.<br/>Continuing emulation may result in crashes and bugs. %1 отсутствует. Пожалуйста, <a href='https://citra-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-3ds-console/'>создайте дамп ваших системных архивов</a>.<br/>Если продолжить эмуляцию, могут быть падения и ошибки. - + System Archive Not Found Системный архив не найден - + Fatal Error Критическая ошибка - + A fatal error occured. <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>Check the log</a> for details.<br/>Continuing emulation may result in crashes and bugs. Произошла критическая ошибка. Более подробную информацию вы <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>найдёте в логе</a>.<br/>Если продолжить эмуляцию, могут быть падения и ошибки. - + Abort Отмена - - + + Citra Citra - + Would you like to exit now? Вы хотите выйти сейчас? - + The game is still running. Would you like to stop emulation? Игра всё ещё запущена. Вы хотите остановить эмуляцию? - + Playback Completed Воспроизведение завершено. - + Movie playback completed. Воспроизведение ролика завершено. - + Citra %1 Citra %1 - + Citra %1| %2 Citra %1| %2 @@ -2799,37 +3015,67 @@ p, li { white-space: pre-wrap; } GameList - + + Name + Название + + + + Compatibility + Совместимость + + + + Region + Регион + + + + File type + Тип файла + + + + Size + Размер + + + Open Save Data Location Открыть местоположение данных сохранений - + + Open Extra Data Location + Открыть местоположение доп. данных + + + Open Application Location Открыть местоположение приложения - + Open Update Data Location Открыть местоположение обновления - + Navigate to GameDB entry Перейти к записи в GameDB - + Scan Subfolders Сканировать подпапки - + Remove Game Directory Удалить каталог с играми - + Open Directory Location Открыть местоположение каталога @@ -2837,81 +3083,81 @@ p, li { white-space: pre-wrap; } GameListItemCompat - + Perfect Идеально - + Game functions flawless with no audio or graphical glitches, all tested functionality works as intended without any workarounds needed. Игра работает безупречно, без звуковых и визуальных искажений. Все функции работают, как положено, без использования способов обхода. - + Great Хорошо - + Game functions with minor graphical or audio glitches and is playable from start to finish. May require some workarounds. Игра работает с небольшими графическими или звуковыми искажениями и играбельна от начала и до конца. Может требовать способы обхода. - + Okay Сносно - + Game functions with major graphical or audio glitches, but game is playable from start to finish with workarounds. Игра работает с серьёзными графическими или звуковыми искажениями, но играбельна от начала и до конца со способами обхода. - + Bad Плохо - + Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches even with workarounds. Игра работает, но с серьёзными графическими или звуковыми искажениями. Некоторые уровни невозможно пройти из-за глюков, даже со способами обхода. - + Intro/Menu Ролики/меню - + Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start Screen. Невозможно играть из-за серьёзных графических или звуковых искажений. Нельзя пройти дальше начального экрана. - + Won't Boot Не запускается - + The game crashes when attempting to startup. Игра падает во время запуска. - + Not Tested Не проверялась - + The game has not yet been tested. Эта игра ещё не проверялась. @@ -2919,7 +3165,7 @@ Screen. GameListPlaceholder - + Double-click to add a new folder to the game list Щёлкните дважды, чтобы добавить новую папку в список игр @@ -2927,27 +3173,27 @@ Screen. GameListSearchField - + of из - + result результат - + results результатов - + Filter: Фильтр: - + Enter pattern to filter Введите шаблон для фильтрации @@ -2955,23 +3201,23 @@ Screen. GraphicsBreakPointsWidget - + Pica Breakpoints Точки прерывания Pica - - + + Emulation running Эмуляция запущена - + Resume Продолжить - + Emulation halted at breakpoint Эмуляция остановлена ​​в точке прерывания @@ -3388,42 +3634,42 @@ Screen. Обновить лобби - + Password Required to Join Для входа требуется пароль - + Password: Пароль: - + Room Name Название комнаты - + Preferred Game Желаемая игра - + Host Хост - + Players Игроки - + Refreshing Обновление - + Refresh List Обновить список @@ -3446,220 +3692,245 @@ Screen. Последние файлы - + + Amiibo + Amiibo + + + &Emulation &Эмуляция - + &View &Вид - + Debugging Отладка - + Screen Layout Компоновка экрана - + Movie Ролик - + Multiplayer Мультиплеер - + &Help &Помощь - + Load File... Загрузить файл... - + Install CIA... Установить CIA... - + Load Symbol Map... Загрузить карту символов... - + E&xit В&ыход - + &Start &Старт - + &Pause &Пауза - + &Stop &Стоп - + FAQ ЧаВо - + About Citra О Citra - + Single Window Mode Режим одиночного окна - + Configure... Настроить... - + Display Dock Widget Headers Отображать заголовки виджетов дока - + Show Filter Bar Показать панель фильтров - + Show Status Bar Показать строку состояния - + Select Game Directory... Выбрать каталог с играми... - + Selects a folder to display in the game list Выбор папки для отображения в списке игр - + Create Pica Surface Viewer Создать просмотрщик поверхностей Pica - + Record Movie Записать ролик - + Play Movie Воспроизвести ролик - + Stop Recording / Playback Остановить запись/воспроизведение - + + Enable Frame Advancing + Включить перемотку кадров + + + + Advance Frame + Следующий кадр + + + Browse Public Game Lobby Обзор публичных игровых комнат - + Create Room Создать комнату - + Leave Room Покинуть комнату - + Direct Connect to Room Прямое подключение к комнате - + Show Current Room Показать текущую комнату - + Fullscreen Полный экран - + Modify Citra Install Изменить установку Citra - + Opens the maintenance tool to modify your Citra installation Открывает инструмент обслуживания, чтобы изменить установку Citra - + Default По умолчанию - + Single Screen Один экран - + Large Screen Большой экран - + Side by Side Бок о бок - + Swap Screens Поменять местами - + Check for Updates Проверить наличие обновлений - + Report Compatibility Сообщить о совместимости - + Restart Перезапустить + + + Load... + Загрузить... + + + + Remove + Удалить + MicroProfileDialog @@ -3685,23 +3956,23 @@ Screen. - + Connected Подключен - + Not Connected Не подключен - + Error Ошибка - + Failed to announce the room to the public lobby. In order to host a room publicly, you must have a valid Citra account configured in Emulation -> Configure -> Web. If you do not want to publish a room in the public lobby, then select Unlisted instead. Debug Message: Не удалось анонсировать комнату в публичном лобби. Чтобы создать публичную комнату, у вас должен быть указан действующий аккаунт Citra в Эмуляция -> Настроить -> Веб. Если вы не хотите публиковать комнату в публичном лобби, выберите Частная. @@ -3820,106 +4091,106 @@ Debug Message: %1 играет в %2 - - + + Invalid region Некорректный регион - + Japan Япония - + North America Северная Америка - + Europe Европа - + Australia Австралия - + China Китай - + Korea Корея - + Taiwan Тайвань - + Region free Без региона - + Invalid Region Некорректный регион - + Shift Shift - + Ctrl Ctrl - + Alt Alt - - + + [not set] [не задано] - + Hat %1 %2 Миниджойстик %1 %2 - + Axis %1%2 Ось %1%2 - + Button %1 Кнопка %1 - - + + [unknown] [Неизвестно] - + [unused] [не использовано] - - + + Axis %1 Ось %1 diff --git a/dist/languages/tr_TR.ts b/dist/languages/tr_TR.ts index 0f8986085..388246950 100644 --- a/dist/languages/tr_TR.ts +++ b/dist/languages/tr_TR.ts @@ -70,45 +70,50 @@ p, li { white-space: pre-wrap; } BreakPointModel - + Pica command loaded Pica command loaded - + Pica command processed Pica command processed - + Incoming primitive batch Incoming primitive batch - + Finished primitive batch Finished primitive batch - + Vertex shader invocation Vertex shader invocation - + Incoming display transfer Incoming display transfer - + GSP command processed GSP command processed - + Buffers swapped Buffers swapped + + + Unknown debug context event + + CalibrationConfigurationDialog @@ -171,13 +176,13 @@ p, li { white-space: pre-wrap; } Oyun - - + + Block Player Kullanıcıyı Engelle - + When you block a player, you will no longer receive chat messages from them.<br><br>Are you sure you would like to block %1? Bir kullanıcıyı engellediğinizde ondan sohbet mesajları alamazsınız.<br><br>%1 kullanıcısını engellemek istediğinize emin misiniz? @@ -198,17 +203,17 @@ p, li { white-space: pre-wrap; } ClientRoomWindow - + Connected Bağlanıldı - + Disconnected Bağlantı Kesildi - + %1 (%2/%3 members) - connected %1 (%2/%3 kullanıcı) - bağlandı @@ -301,6 +306,26 @@ p, li { white-space: pre-wrap; } Thank you for your submission! Gönderiniz için teşekkürler! + + + Submitting + + + + + Communication error + + + + + An error occured while sending the Testcase + + + + + Next + + ConfigureAudio @@ -340,10 +365,10 @@ p, li { white-space: pre-wrap; } 0 % - - - %1 % - %1 % + + %1% + Volume percentage (e.g. 50%) + @@ -606,50 +631,78 @@ p, li { white-space: pre-wrap; } ConfigureDialog - + Citra Configuration Citra Yapılandırması - + + + General Genel - + + + System Sistem - + + + Input Girdi - + + + Graphics Grafikler - + + + Audio Ses - + + + Camera Kamera - + + + Debug Hata Ayıklama - + + + Web + + + + + UI + + + + + Controls + + ConfigureGeneral @@ -669,64 +722,54 @@ p, li { white-space: pre-wrap; } Emülasyon devam ederken çıkışı onaylayın - - Interface language - Arayüz dili - - - + Updates Güncellemeler - + Check for updates on start Açılışta güncellemeleri kontrol et - + Silently auto update after closing Kapandıktan sonra arkaplanda otomatik güncelle - + Emulation Emülasyon - + Region: Bölge: - + Auto-select Otomatik seç - - Theme - Tema - - - - Theme: - Tema: - - - + Hotkeys Kısayollar - - <System> - <System> + + Reset All Settings + - - English - İngilizce + + Citra + + + + + Are you sure you want to <b>reset your settings</b> and close Citra? + @@ -1026,7 +1069,7 @@ p, li { white-space: pre-wrap; } - + Set Analog Stick Analog Çubuğunu Ayarla @@ -1067,11 +1110,44 @@ p, li { white-space: pre-wrap; } + Clear All + + + + Restore Defaults Varsayılanlara Dön - + + + Clear + + + + + + [not set] + + + + + + Restore Default + + + + + Information + + + + + After pressing OK, first move your joystick horizontally, and then vertically. + + + + [press key] [tuşa bas] @@ -1229,7 +1305,7 @@ p, li { white-space: pre-wrap; } Could not receive valid data from the server.<br>Please verify that the server is set up correctly and the address and port are correct. - Sunucudan geçerli veri alınamıyor.<br>Lütfen sunucunun düzgün kurulduğundan ve adresin ve portun doğru girildiğinden emin olun. + Sunucudan geçerli veri alınamıyor.<br>Lütfen sunucunun düzgün kurulduğundan, adres ve portun doğru girildiğinden emin olun. @@ -1440,17 +1516,27 @@ p, li { white-space: pre-wrap; } - + + yyyy-MM-ddTHH:mm:ss + + + + + Play Coins: + + + + Console ID: Konsol ID: - + Regenerate Yeniden Oluştur - + System settings are available only when game is not running. Sistem ayarlarına sadece oyun çalışmıyorken erişilebilir. @@ -1507,7 +1593,7 @@ p, li { white-space: pre-wrap; } British Virgin Islands - İngiliz Virgin Adaları + İngiliz Virjin Adaları @@ -1677,7 +1763,7 @@ p, li { white-space: pre-wrap; } US Virgin Islands - Amerika Birleşik Devletleri Virgin Adaları + Amerika Birleşik Devletleri Virjin Adaları @@ -2120,22 +2206,120 @@ p, li { white-space: pre-wrap; } Bermuda - - + + Console ID: 0x%1 Konsol ID: 0x%1 - + This will replace your current virtual 3DS with a new one. Your current virtual 3DS will not be recoverable. This might have unexpected effects in games. This might fail, if you use an outdated config savegame. Continue? Bu mevcut sanal 3DS'nizi yenisiyle değiştirecektir. Mevcut sanal 3DS'niz geri alınamayacaktır. Bu oyunlarda beklenmedik etkilere sebep olabilir. Eğer eski yapılandırmalı oyun kayıtı kullanırsanız bu başarısız olabilir. Devam edilsin mi? - + Warning Uyarı + + ConfigureUi + + + Form + + + + + General + + + + + Interface language: + + + + + Theme: + + + + + Game List + + + + + Icon Size: + + + + + + None + + + + + Small (24x24) + + + + + Large (48x48) + + + + + Row 1 Text: + + + + + + File Name + + + + + + Full Path + + + + + + Title Name + + + + + + Title ID + + + + + Row 2 Text: + + + + + Hide Titles without Icon + + + + + <System> + + + + + English + + + ConfigureWeb @@ -2155,7 +2339,7 @@ p, li { white-space: pre-wrap; } - + Verify Doğrula @@ -2215,48 +2399,48 @@ p, li { white-space: pre-wrap; } - + <a href='https://citra-emu.org/entry/telemetry-and-why-thats-a-good-thing/'><span style="text-decoration: underline; color:#039be5;">Learn more</span></a> <a href='https://citra-emu.org/entry/telemetry-and-why-thats-a-good-thing/'><span style="text-decoration: underline; color:#039be5;">Daha fazla bilgi edinin</span></a> - - <a href='https://services.citra-emu.org/'><span style="text-decoration: underline; color:#039be5;">Sign up</span></a> - <a href='https://services.citra-emu.org/'><span style="text-decoration: underline; color:#039be5;">Kaydol </span></a> + + <a href='https://profile.citra-emu.org/'><span style="text-decoration: underline; color:#039be5;">Sign up</span></a> + - + <a href='https://citra-emu.org/wiki/citra-web-service/'><span style="text-decoration: underline; color:#039be5;">What is my token?</span></a> <a href='https://citra-emu.org/wiki/citra-web-service/'><span style="text-decoration: underline; color:#039be5;">Tokenim nedir?</span></a> - - + + Telemetry ID: 0x%1 Telemetri ID: 0x%1 - + Username and token not verified Kullanıcı Adı ve Token doğrulanmadı - + Username and token were not verified. The changes to your username and/or token have not been saved. Kullanıcı adı ve token doğrulanamadı. Kullanıcı adınıza ve/veya tokeninize yapılan değişiklikler kaydedilmedi. - + Verifying Doğrulanıyor - + Verification failed Doğrulama başarısız oldu - + Verification failed. Check that you have entered your username and token correctly, and that your internet connection is working. Doğrulama başarısız oldu. Kullanıcı adınızı ve tokeninizi doğru girdiğinizden ve internet bağlantınızın çalıştığından emin olun. @@ -2317,12 +2501,12 @@ p, li { white-space: pre-wrap; } DirectConnectWindow - + Connecting Bağlanılıyor - + Connect Bağlan @@ -2330,413 +2514,445 @@ p, li { white-space: pre-wrap; } GMainWindow - + <a href='https://citra-emu.org/entry/telemetry-and-why-thats-a-good-thing/'>Anonymous data is collected</a> to help improve Citra. <br/><br/>Would you like to share your usage data with us? - <a href='https://citra-emu.org/entry/telemetry-and-why-thats-a-good-thing/'>Citrayı geliştirmeye yardımcı olmak için</a>Anonim veri toplandı. <br/><br/>Kullanım verinizi bizimle paylaşır mısınız? + <a href='https://citra-emu.org/entry/telemetry-and-why-thats-a-good-thing/'>Citrayı geliştirmeye yardımcı olmak için</a> anonim veri toplandı. <br/><br/>Kullanım verinizi bizimle paylaşmak ister misiniz? - + Telemetry - - + + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a 3DS. Geçerli emülasyon hızı. 100%'den az veya çok olan değerler emülasyonun bir 3DS'den daha yavaş veya daha hızlı çalıştığını gösterir. - - + + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. Oyunun şu anda saniye başına kaç kare gösterdiği. Bu oyundan oyuna ve sahneden sahneye farklılık gösterebilir. - - + + Time taken to emulate a 3DS frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. Bir 3DS karesini emüle etmekte geçen zaman, karelimitleme ve v-sync hariç. Tam hız emülasyon için bu en çok 16,67 ms. olmalı. - + Clear Recent Files Son Dosyaları Temizle - + F9 F9 - + F10 F10 - + CTRL+F CTRL+F - + Update Available Güncelleme Mevcut - + An update is available. Would you like to install it now? Bir güncelleme mevcut. Şimdi yüklemek ister misiniz? - + No Update Found Güncelleme Bulunamadı - + No update is found. Güncelleme bulunamadı. - - + + OpenGL 3.3 Unsupported + + + + + Your GPU may not support OpenGL 3.3, or you do not have the latest graphics driver. + + + + + Invalid ROM Format Geçersiz Dosya Biçimi - - + + Your ROM format is not supported.<br/>Please follow the guides to redump your <a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>game cartridges</a> or <a href='https://citra-emu.org/wiki/dumping-installed-titles/'>installed titles</a>. Oyun dosyanızın biçimi desteklenmiyor. <br/>Lütfen <a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>oyun kartuşlarınızı</a> veya <a href='https://citra-emu.org/wiki/dumping-installed-titles/'>yüklenmiş oyunlarınızı</a> yeniden dump etmek için rehberleri takip ediniz. - + ROM Corrupted Dosya Bozulmuş - + Your ROM is corrupted. <br/>Please follow the guides to redump your <a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>game cartridges</a> or <a href='https://citra-emu.org/wiki/dumping-installed-titles/'>installed titles</a>. Oyun dosyanız bozuk. <br/>Lütfen <a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>oyun kartuşlarınızı</a> veya <a href='https://citra-emu.org/wiki/dumping-installed-titles/'>yüklenmiş oyunlarınızı</a> yeniden dump etmek için rehberleri takip ediniz. - + ROM Encrypted Dosya Şifreli - + Your ROM is encrypted. <br/>Please follow the guides to redump your <a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>game cartridges</a> or <a href='https://citra-emu.org/wiki/dumping-installed-titles/'>installed titles</a>. Oyun dosyanız şifreli.<br/>Lütfen <a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>oyun kartuşlarınızı</a> veya <a href='https://citra-emu.org/wiki/dumping-installed-titles/'>yüklenmiş oyunlarınızı</a> yeniden dump etmek için rehberleri takip ediniz. - - + + Video Core Error - + An error has occured. Please <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>see the log</a> for more details. Ensure that you have the latest graphics drivers for your GPU. - + You are running default Windows drivers for your GPU. You need to install the proper drivers for your graphics card from the manufacturer's website. Windowsun varsayılan GPU sürücülerini kullanıyorsunuz. Üreticinin sitesinden ekran kartınız için geçerli sürücüleri yüklemelisiniz. - + Error while loading ROM! ROM yüklenirken hata oluştu! - + An unknown error occured. Please see the log for more details. Bilinmeyen bir hata meydana geldi. Lütfen daha fazla detay için kütüğe bakınız. - + Start Başlat - + Error Opening %1 Folder %1 Klasörü Açılırken Hata Oluştu - - + + Folder does not exist! Klasör mevcut değil! - + Error Opening %1 %1 Açılırken Hata Oluştu - + Select Directory Dizin Seç - - 3DS Executable - 3DS Çalıştırılabilir Dosyası + + 3DS Executable (%1);;All Files (*.*) + %1 is an identifier for the 3DS executable file extensions. + - - - All Files (*.*) - Tüm Dosyalar (*.*) - - - + Load File Dosya Yükle - + Load Files Dosyaları Yükle - + 3DS Installation File (*.CIA*) 3DS Kurulum Dosyası (*.CIA*) - + + All Files (*.*) + Tüm Dosyalar (*.*) + + + %1 has been installed successfully. %1 başarıyla yüklendi. - + Unable to open File Dosya açılamıyor - + Could not open %1 %1 açılamıyor - + Installation aborted Yükleme iptal edildi - + The installation of %1 was aborted. Please see the log for more details %1'in yüklemesi iptal edildi. Daha fazla detay için lütfen kütüğe bakınız. - + Invalid File Geçersiz Dosya - + %1 is not a valid CIA %1 geçerli bir CIA dosyası değil - + Encrypted File Şifrelenmiş Dosya - + %1 must be decrypted before being used with Citra. A real 3DS is required. %1 Citra ile kullanılmadan önce deşifre edilmelidir. Gerçek bir 3DS gereklidir. - + File not found Dosya bulunamadı - + File "%1" not found "%1" Dosyası bulunamadı - - - + + + Continue Devam - + Missing Citra Account Citra Hesabı Eksik - + You must link your Citra account to submit test cases.<br/>Go to Emulation &gt; Configure... &gt; Web to do so. - - - + + Amiibo File (%1);; All Files (*.*) + + + + + Load Amiibo + + + + + + + Record Movie Klip Kaydet - - + + To keep consistency with the RNG, it is recommended to record the movie from game start.<br>Are you sure you still want to record movies now? + + + + + Citra TAS Movie (*.ctm) Citra TAS Movie (*.ctm) - + Recording will start once you boot a game. Kayıt bir oyun başlattığınız zaman başlayacak. - + The movie file you are trying to load was created on a different revision of Citra.<br/>Citra has had some changes during the time, and the playback may desync or not work as expected.<br/><br/>Are you sure you still want to load the movie file? - + The movie file you are trying to load was recorded with a different game.<br/>The playback may not work as expected, and it may cause unexpected results.<br/><br/>Are you sure you still want to load the movie file? İzlemeye çalıştığınız klip dosyası başka bir oyun ile kaydetilmiş.<br/>Oynatma beklendiği gibi çalışmayabilir ve beklenmedik sonuçlar doğurabilir.<br/><br/>Klip dosyasını hala yüklemek istediğinize emin misiniz? - - + + The movie file you are trying to load is invalid.<br/>Either the file is corrupted, or Citra has had made some major changes to the Movie module.<br/>Please choose a different movie file and try again. İzlemeye çalıştığınız klip dosyası geçersiz.<br/>Dosya bozulmuş veya Citra Klip modülünde büyük değişiklikler yapılmış olabilir.<br/>Lütfen varklı bir klip dosyası seçin ve tekrardan deneyin. - + Revision Dismatch - + Game Dismatch - - + + Invalid Movie File - + + Play Movie - + + To keep consistency with the RNG, it is recommended to play the movie from game start.<br>Are you sure you still want to play movies now? + + + + Game Not Found Oyun Bulunamadı - + The movie you are trying to play is from a game that is not in the game list. If you own the game, please add the game folder to the game list and try to play the movie again. Oynatmaya çalıştığınız klip oyun listenizde olmayan bir oyundan, eğer oyuna sahipseniz lütfen oyun klasörünü listeye ekleyin ve klipi tekrardan oynatmaya çalışın. - + Movie recording cancelled. Klip kaydı iptal edildi. - + Movie Saved Klip Kaydedildi - + The movie is successfully saved. Klip başarıyla kayıt edildi. - + Speed: %1% / %2% Hız: %1% / %2% - + Speed: %1% Hız: %1% - + Game: %1 FPS Oyun: %1 FPS - + Frame: %1 ms Kare: %1 ms - + %1 is missing. Please <a href='https://citra-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-3ds-console/'>dump your system archives</a>.<br/>Continuing emulation may result in crashes and bugs. %1 eksik. Lütfen <a href='https://citra-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-3ds-console/'>sistem arşivinizi dump edin</a>.<br/>Emülasyona devam etmek çökmelerle ve hatalarla sonuçlanabilir. - + System Archive Not Found Sistem Arşivi Bulunamadı - + Fatal Error Önemli Hata - + A fatal error occured. <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>Check the log</a> for details.<br/>Continuing emulation may result in crashes and bugs. - + Abort - - + + Citra Citra - + Would you like to exit now? - + The game is still running. Would you like to stop emulation? Oyun hala çalışıyor. Emülasyonu durdurmak istiyor musunuz? - + Playback Completed Oynatma Tamamlandı - + Movie playback completed. Klip oynatması tamamlandı. - + Citra %1 Citra %1 - + Citra %1| %2 Citra %1| %2 @@ -2799,37 +3015,67 @@ p, li { white-space: pre-wrap; } GameList - + + Name + + + + + Compatibility + + + + + Region + + + + + File type + + + + + Size + + + + Open Save Data Location Kayıt Dosyası Konumunu Aç - + + Open Extra Data Location + + + + Open Application Location Uygulama Konumunu Aç - + Open Update Data Location Güncelleme Dosyası Konumunu Aç - + Navigate to GameDB entry Oyun Veritabanı Girdisine Git - + Scan Subfolders Alt Dizinleri Tara - + Remove Game Directory Oyun Dizinini Kaldır - + Open Directory Location Dizinin Bulunduğu Yeri Aç @@ -2837,12 +3083,12 @@ p, li { white-space: pre-wrap; } GameListItemCompat - + Perfect Mükemmel - + Game functions flawless with no audio or graphical glitches, all tested functionality works as intended without any workarounds needed. Oyun grafik veya ses hataları olmadan sorunsuz çalışıyor, tüm test edilmiş özellikler @@ -2850,12 +3096,12 @@ geçici çözümler gerektirmeden beklendiği gibi çalışıyor. - + Great - + Game functions with minor graphical or audio glitches and is playable from start to finish. May require some workarounds. Oyun küçük grafik veya ses hatalarıyla çalışıyor ve baştan sona kadar oynanabilir. Bazı @@ -2863,12 +3109,12 @@ geçici çözümler gerektirebilir. - + Okay - + Game functions with major graphical or audio glitches, but game is playable from start to finish with workarounds. Oyun büyük grafik veya ses hatalarıyla çalışıyor fakat geçici çözümler ile baştan sona @@ -2876,12 +3122,12 @@ kadar oynanabilir. - + Bad - + Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches even with workarounds. Oyun çalışıyor fakat büyük grafik veya ses hatalarına sahip. Geçici çözümlerle bile @@ -2889,33 +3135,33 @@ hatalardan dolayı bazı alanlar geçilemiyor. - + Intro/Menu - + Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start Screen. - + Won't Boot - + The game crashes when attempting to startup. - + Not Tested - + The game has not yet been tested. @@ -2923,7 +3169,7 @@ Screen. GameListPlaceholder - + Double-click to add a new folder to the game list Oyun listesine yeni bir klasör eklemek için çift-tıklayın @@ -2931,27 +3177,27 @@ Screen. GameListSearchField - + of 'nun - + result sonuç - + results sonuçlar - + Filter: Filtre: - + Enter pattern to filter Filtrelenecek düzeni girin @@ -2959,23 +3205,23 @@ Screen. GraphicsBreakPointsWidget - + Pica Breakpoints Pica Breakpoints - - + + Emulation running Emulation running - + Resume Resume - + Emulation halted at breakpoint Emulation halted at breakpoint @@ -3392,42 +3638,42 @@ Screen. Lobiyi Yenile - + Password Required to Join Katılmak İçin Şifre Gerekiyor - + Password: Şifre: - + Room Name Oda İsmi - + Preferred Game Öncelikli Oyun - + Host Sunucu - + Players Oyuncular - + Refreshing Yenileniyor - + Refresh List Listeyi Yenile @@ -3450,220 +3696,245 @@ Screen. Son Kullanılan Dosyalar - + + Amiibo + + + + &Emulation &Emülasyon - + &View &Görünüm - + Debugging Hata Ayıklama - + Screen Layout Ekran Düzeni - + Movie - + Multiplayer Çok Oyunculu - + &Help &Yardım - + Load File... Dosya Yükle... - + Install CIA... CIA Yükle... - + Load Symbol Map... Load Symbol Map... - + E&xit &Çıkış - + &Start B&aşlat - + &Pause &Duraklat - + &Stop Du&rdur - + FAQ S.S.S - + About Citra Citra Hakkında - + Single Window Mode Tek Pencere Modu - + Configure... Yapılandır... - + Display Dock Widget Headers Dock Widget'ı Başlıklarını Göster - + Show Filter Bar Filtre Çubuğunu Göster - + Show Status Bar Durum Çubuğunu Göster - + Select Game Directory... Oyun Dizinini Seç... - + Selects a folder to display in the game list Oyun listesinde görüntülenecek bir klasör seçer - + Create Pica Surface Viewer Create Pica Surface Viewer - + Record Movie - + Play Movie - + Stop Recording / Playback - + + Enable Frame Advancing + + + + + Advance Frame + + + + Browse Public Game Lobby Herkese Açık Oyun Lobisi Ara - + Create Room Oda Oluştur - + Leave Room Odadan Ayrıl - + Direct Connect to Room Odaya Doğrudan Bağlan - + Show Current Room Mevcut Odayı Göster - + Fullscreen Tam ekran - + Modify Citra Install Citra Kurulumunu Değiştir - + Opens the maintenance tool to modify your Citra installation Citra kurulumunu değiştirmek için bakım aracını açar - + Default Varsayılan - + Single Screen Tek Ekran - + Large Screen Büyük Ekran - + Side by Side Yan Yana - + Swap Screens Ekranları değiştir - + Check for Updates Güncellemeleri Kontrol Et - + Report Compatibility Uyumluluk Bildir - + Restart Yeniden Başlat + + + Load... + + + + + Remove + + MicroProfileDialog @@ -3689,23 +3960,23 @@ Screen. - + Connected Bağlanıldı - + Not Connected Bağlı Değil - + Error Hata - + Failed to announce the room to the public lobby. In order to host a room publicly, you must have a valid Citra account configured in Emulation -> Configure -> Web. If you do not want to publish a room in the public lobby, then select Unlisted instead. Debug Message: Oda herkese açık lobiye yayınlanamadı. Herkese açık bir oda açmak için, Emülasyon -> Yapılandırma -> Ağ içinde geçerli bir Citra hesabı yapılandırılmış olmalı. Eğer herkese açık lobide oda yayınlamak istemiyorsanız, yerine Liste Dışı'nı seçin. @@ -3824,106 +4095,106 @@ Hata Ayıklama Mesajı: %1 %2 oynuyor - - + + Invalid region Geçersiz Bölge - + Japan Japonya - + North America Kuzey Amerika - + Europe Avrupa - + Australia Avustralya - + China Çin - + Korea Kore - + Taiwan Tayvan - + Region free Bölge kilitsiz - + Invalid Region Geçersiz Bölge - + Shift Shift - + Ctrl Ctrl - + Alt Alt - - + + [not set] [ayarlanmadı] - + Hat %1 %2 - + Axis %1%2 - + Button %1 - - + + [unknown] [bilinmiyor] - + [unused] [kullanılmıyor] - - + + Axis %1 diff --git a/dist/languages/vi_VN.ts b/dist/languages/vi_VN.ts index 07195863d..552030ffc 100644 --- a/dist/languages/vi_VN.ts +++ b/dist/languages/vi_VN.ts @@ -70,72 +70,77 @@ p, li { white-space: pre-wrap; } BreakPointModel - + Pica command loaded Đã khởi động lệnh Pica - + Pica command processed Đã xử lý lệnh Pica - + Incoming primitive batch Đã nhận được dữ liệu nguyên thủy mới - + Finished primitive batch Dữ liệu nguyên thủy đã được xử lý - + Vertex shader invocation Gọi lệnh Vertex Shader - + Incoming display transfer Đang chuyển giao hiển thị - + GSP command processed Đã xử lý lệnh GSP - + Buffers swapped Chuyển dữ liệu tạm thời + + + Unknown debug context event + + CalibrationConfigurationDialog Communicating with the server... - + Đang kết nối với máy chủ... Cancel - + Bỏ qua Touch the top left corner <br>of your touchpad. - + Chạm vào góc trên trái <br>bảng cảm biến. Now touch the bottom right corner <br>of your touchpad. - + Giờ chạm vào góc dưới phải <br>bảng cảm biến. Configuration completed! - + Thiết lập hoàn tất! @@ -171,13 +176,13 @@ p, li { white-space: pre-wrap; } Trò chơi - - + + Block Player Chặn người chơi - + When you block a player, you will no longer receive chat messages from them.<br><br>Are you sure you would like to block %1? Khi bạn chặn người chơi khác, bạn sẽ không thể nhận được các tin nhắn từ họ.<br><br>Bạn có chắc muốn chặn người chơi %1? @@ -198,17 +203,17 @@ p, li { white-space: pre-wrap; } ClientRoomWindow - + Connected Đã kết nối - + Disconnected Đã ngắt kết ngối - + %1 (%2/%3 members) - connected %1 (%2/%3 thành viên) - đã kết nối @@ -301,6 +306,26 @@ p, li { white-space: pre-wrap; } Thank you for your submission! Rất cảm ơn bạn đã phản hồi! + + + Submitting + + + + + Communication error + + + + + An error occured while sending the Testcase + + + + + Next + + ConfigureAudio @@ -340,10 +365,10 @@ p, li { white-space: pre-wrap; } 0 % - - - %1 % - %1 % + + %1% + Volume percentage (e.g. 50%) + @@ -606,50 +631,78 @@ p, li { white-space: pre-wrap; } ConfigureDialog - + Citra Configuration Thiết lập Citra - + + + General Chung - + + + System H. thống - + + + Input Phím - + + + Graphics Đồ họa - + + + Audio Âm thanh - + + + Camera Máy ảnh - + + + Debug Gỡ lỗi - + + + Web Trang web + + + + + UI + + + + + Controls + + ConfigureGeneral @@ -669,64 +722,54 @@ p, li { white-space: pre-wrap; } Xác nhận thoát khi đang chạy giả lập - - Interface language - Ngôn ngữ giao diện - - - + Updates Cập nhật - + Check for updates on start Kiểm tra cập nhật khi khởi chạy - + Silently auto update after closing Tự động cập nhật ngầm sau khi tắt - + Emulation Giả lập - + Region: Vùng: - + Auto-select Tự động chọn - - Theme - Giao diện - - - - Theme: - Giao diện: - - - + Hotkeys Phím tắt - - <System> - <System> + + Reset All Settings + - - English - Tiếng Anh + + Citra + + + + + Are you sure you want to <b>reset your settings</b> and close Citra? + @@ -1026,7 +1069,7 @@ p, li { white-space: pre-wrap; } - + Set Analog Stick Thiết lập Con xoay @@ -1063,15 +1106,48 @@ p, li { white-space: pre-wrap; } Motion / Touch... - + Chuyển động / Chạm... + Clear All + + + + Restore Defaults Khôi phục mặc định - + + + Clear + + + + + + [not set] + + + + + + Restore Default + + + + + Information + + + + + After pressing OK, first move your joystick horizontally, and then vertically. + + + + [press key] [nhấn phím] @@ -1081,12 +1157,12 @@ p, li { white-space: pre-wrap; } Configure Motion / Touch - + Thiết lập chuyển động / chạm Motion - + Chuyển Động @@ -1096,12 +1172,12 @@ p, li { white-space: pre-wrap; } Sensitivity: - + Độ nhạy: Touch - + Chạm @@ -1111,7 +1187,7 @@ p, li { white-space: pre-wrap; } Calibration: - + Hiệu chuẩn: @@ -1122,27 +1198,27 @@ p, li { white-space: pre-wrap; } Configure - + Thiết lập CemuhookUDP Config - + Thiết lập Cemuhook UDP You may use any Cemuhook compatible UDP input source to provide motion and touch input. - + Bạn có thể dùng bất cứ bản Cemuhook nào tương thích với đầu vào UDP để giả lập chuyển động và hành vi chạm. Server: - + Máy chủ: Port: - + Cổng: @@ -1172,74 +1248,74 @@ p, li { white-space: pre-wrap; } Learn More - + Tìm hiểu thêm Test - + Kiểm tra Mouse (Right Click) - + Chuột (Nhấp phải) CemuhookUDP - + CemuhookUDP Emulator Window - + Cửa sổ giả lập <a href='https://citra-emu.org/wiki/using-a-controller-or-android-phone-for-motion-or-touch-input'><span style="text-decoration: underline; color:#039be5;">Learn More</span></a> - + <a href='https://citra-emu.org/wiki/using-a-controller-or-android-phone-for-motion-or-touch-input'><span style="text-decoration: underline; color:#039be5;">Tìm hiểu thêm</span></a> Testing - + Đang kiểm tra Configuring - + Thiết lập Test Successful - + Kiểm tra thành công Successfully received data from the server. - + Đã nhận dữ liệu từ máy chủ thành công. Test Failed - + Kiểm thử thất bại Could not receive valid data from the server.<br>Please verify that the server is set up correctly and the address and port are correct. - + Không thể nhận dữ liệu nào từ máy chủ.<br>Vui lòng kiểm tra máy chủ đã được thiết đặt đúng, kiểm tra địa chỉ và cổng kết nối là chính xác. Citra - + Citra UDP Test or calibration configuration is in progress.<br>Please wait for them to finish. - + Kiểm tra UDP hoặc quá trình đang hiệu chuẩn.<br>Vui lòng đợi quá trình này hoàn tất. @@ -1422,35 +1498,45 @@ p, li { white-space: pre-wrap; } Clock - + Đồng hồ System Clock - + Đồng hồ hệ thống Fixed Time - + Giờ cố định Startup time + Giờ bắt đầu + + + + yyyy-MM-ddTHH:mm:ss - + + Play Coins: + + + + Console ID: ID Máy: - + Regenerate Tạo mới - + System settings are available only when game is not running. Chỉ có thể thay đổi thiết lập hệ thống khi đang không chơi game. @@ -2120,22 +2206,120 @@ p, li { white-space: pre-wrap; } Bermuda - - + + Console ID: 0x%1 Tên Máy: 0x%1 - + This will replace your current virtual 3DS with a new one. Your current virtual 3DS will not be recoverable. This might have unexpected effects in games. This might fail, if you use an outdated config savegame. Continue? Điều này có thể khiến máy ảo 3DS của bạn thay thế bởi một chiếc máy mới. Điều này không thể hoàn tác. Một số game có thể bị ảnh hưởng bởi điều này. Và có thể xảy ra lỗi nếu bạn đang dùng thiết lập lưu game cũ. Tiếp tục? - + Warning Cảnh báo + + ConfigureUi + + + Form + + + + + General + + + + + Interface language: + + + + + Theme: + + + + + Game List + + + + + Icon Size: + + + + + + None + + + + + Small (24x24) + + + + + Large (48x48) + + + + + Row 1 Text: + + + + + + File Name + + + + + + Full Path + + + + + + Title Name + + + + + + Title ID + + + + + Row 2 Text: + + + + + Hide Titles without Icon + + + + + <System> + + + + + English + + + ConfigureWeb @@ -2155,7 +2339,7 @@ p, li { white-space: pre-wrap; } - + Verify Xác thực @@ -2212,51 +2396,51 @@ p, li { white-space: pre-wrap; } Show Current Game in your Discord Status - + Hiển thị game đang chơi trên trạng thái Discord - + <a href='https://citra-emu.org/entry/telemetry-and-why-thats-a-good-thing/'><span style="text-decoration: underline; color:#039be5;">Learn more</span></a> <a href='https://citra-emu.org/entry/telemetry-and-why-thats-a-good-thing/'><span style="text-decoration: underline; color:#039be5;">Tìm hiểu thêm</span></a> - - <a href='https://services.citra-emu.org/'><span style="text-decoration: underline; color:#039be5;">Sign up</span></a> - <a href='https://services.citra-emu.org/'><span style="text-decoration: underline; color:#039be5;">Đăng ký</span></a> + + <a href='https://profile.citra-emu.org/'><span style="text-decoration: underline; color:#039be5;">Sign up</span></a> + - + <a href='https://citra-emu.org/wiki/citra-web-service/'><span style="text-decoration: underline; color:#039be5;">What is my token?</span></a> <a href='https://citra-emu.org/wiki/citra-web-service/'><span style="text-decoration: underline; color:#039be5;">Token là gì?</span></a> - - + + Telemetry ID: 0x%1 Mã theo dõi: 0x%1 - + Username and token not verified Tài khoản và token chưa được xác thực - + Username and token were not verified. The changes to your username and/or token have not been saved. Tài khoản và token này chưa được xác thực. Các thay đổi về người dùng này sẽ không được lưu lại. - + Verifying Đang xác thực - + Verification failed Xác thực thất bại - + Verification failed. Check that you have entered your username and token correctly, and that your internet connection is working. Xác thực thất bại. Vui lòng kiểm tra tài khoản và token bạn đã nhập và kết nối đến Internet của máy. @@ -2317,12 +2501,12 @@ p, li { white-space: pre-wrap; } DirectConnectWindow - + Connecting Đang kết nối - + Connect Kết nối @@ -2330,413 +2514,445 @@ p, li { white-space: pre-wrap; } GMainWindow - + <a href='https://citra-emu.org/entry/telemetry-and-why-thats-a-good-thing/'>Anonymous data is collected</a> to help improve Citra. <br/><br/>Would you like to share your usage data with us? <a href='https://citra-emu.org/entry/telemetry-and-why-thats-a-good-thing/'>Dữ liệu ẩn danh được thu thập</a> để giúp cải thiện Citra. <br/><br/>Bạn có muốn chia sẻ dữ liệu của bạn với chúng tôi? - + Telemetry Theo dõi từ xa - - + + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a 3DS. Tốc độ giả lập hiện tại. Giá trị cao hoặc thấp hơn 100% thể hiện giả lập đang chạy nhanh hay chậm hơn một chiếc máy 3DS thực sự. - - + + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. Tốc độ khung hình thực trong game. Nó sẽ thay đổi tùy game và tùy màn chơi. - - + + Time taken to emulate a 3DS frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. Thời gian để giả lập một khung hình của máy 3DS, không gồm giới hạn khung hay v-sync Một giả lập tốt nhất sẽ tiệm cận 16.67 ms. - + Clear Recent Files Xóa danh sách tệp gần đây - + F9 F9 - + F10 F10 - + CTRL+F CTRL+F - + Update Available Cập nhật - + An update is available. Would you like to install it now? Một bản cập nhật mới đã sẵn sàng. Bạn có muốn cài đặt ngay bây giờ không? - + No Update Found Chưa tìm thấy bản cập nhật mới - + No update is found. Chưa tìm thấy bản cập nhật mới - - + + OpenGL 3.3 Unsupported + + + + + Your GPU may not support OpenGL 3.3, or you do not have the latest graphics driver. + + + + + Invalid ROM Format Định dạng ROM không hợp lệ - - + + Your ROM format is not supported.<br/>Please follow the guides to redump your <a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>game cartridges</a> or <a href='https://citra-emu.org/wiki/dumping-installed-titles/'>installed titles</a>. Định dạng ROM của bạn không được hỗ trợ.<br/>Vui lòng làm theo hướng dẫn để trích xuất dữ liệu từ<a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>thẻ game</a> hoặc <a href='https://citra-emu.org/wiki/dumping-installed-titles/'>game eShop</a>. - + ROM Corrupted ROM hỏng - + Your ROM is corrupted. <br/>Please follow the guides to redump your <a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>game cartridges</a> or <a href='https://citra-emu.org/wiki/dumping-installed-titles/'>installed titles</a>. ROM của bạn đã bị hỏng. <br/>Vui lòng thực hiện theo hướng dẫn đển trích xuát dữ liệu từ <a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>thẻ game</a> hoặc <a href='https://citra-emu.org/wiki/dumping-installed-titles/'>game eShop</a>. - + ROM Encrypted ROM được mã hóa - + Your ROM is encrypted. <br/>Please follow the guides to redump your <a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>game cartridges</a> or <a href='https://citra-emu.org/wiki/dumping-installed-titles/'>installed titles</a>. ROM của bạn được mã hóa. <br/>Vui lòng thực hiện theo hướng dẫn để trích xuất dữ liệu từ <a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>thẻ game</a> hoặc <a href='https://citra-emu.org/wiki/dumping-installed-titles/'>game eShop</a>. - - + + Video Core Error Lỗi Video Core - + An error has occured. Please <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>see the log</a> for more details. Ensure that you have the latest graphics drivers for your GPU. Đã có lỗi xảy ra. Vui lòng <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>xem tệp log</a> để biết thêm chi tiết. Hãy đảm bảo rằng bạn đã cài driver mới nhất cho card đồ họa của bạn - + You are running default Windows drivers for your GPU. You need to install the proper drivers for your graphics card from the manufacturer's website. - + Bạn đang dùng driver mặc định của Windows cho GPU. Bạn cần phải cài đặt bản driver phù hợp cho card đồ họa của mình từ trang chủ của nhà sản xuất. - + Error while loading ROM! Lỗi xuất hiện khi tải ROM! - + An unknown error occured. Please see the log for more details. Một lỗi không rõ đã xảy ra. Vui lòng xem nhật ký để biết thêm chi tiết. - + Start Bắt đầu - + Error Opening %1 Folder Lỗi khi mở thư mục %1 - - + + Folder does not exist! Thư mục này không tồn tại! - + Error Opening %1 Lỗi khi mở %1 - + Select Directory Chọn thư mục - - 3DS Executable - Tệp khởi chạy 3DS + + 3DS Executable (%1);;All Files (*.*) + %1 is an identifier for the 3DS executable file extensions. + - - - All Files (*.*) - Tất cả tệp tin (*.*) - - - + Load File Mở tệp tin - + Load Files Mở các tệp tin - + 3DS Installation File (*.CIA*) Tệp cài đặt 3DS (*.CIA*) - + + All Files (*.*) + Tất cả tệp tin (*.*) + + + %1 has been installed successfully. %1 đã được cài đặt thành công. - + Unable to open File Không thể mở tệp tin - + Could not open %1 Không thể mở %1 - + Installation aborted Việc cài đặt đã bị hoãn - + The installation of %1 was aborted. Please see the log for more details Việc cài đặt %1 đã bị hoãn. Vui lòng xem bản ghi nhật ký để biết thêm chi tiết. - + Invalid File Tệp tin không hợp lệ - + %1 is not a valid CIA %1 không phải là một tệp CIA hợp lệ - + Encrypted File Tệp đã bị mã hóa - + %1 must be decrypted before being used with Citra. A real 3DS is required. %1 cần được giải nén trước khi dùng với Citra. Điều này cần đến một máy 3DS thực sự. - + File not found Không tìm thấy tệp - + File "%1" not found Không tìm thấy tệp tin "%1" - - - + + + Continue Tiếp tục - + Missing Citra Account Mất tài khoản Citra - + You must link your Citra account to submit test cases.<br/>Go to Emulation &gt; Configure... &gt; Web to do so. Bạn phải kết nối với tài khoản Citra của bạn để gửi mẫu thử.<br/>Vào Emulation &gt; Configure... &gt; Web để kết nối. - - - - Record Movie + + Amiibo File (%1);; All Files (*.*) - - + + Load Amiibo + + + + + + + + Record Movie + Quay phim + + + + To keep consistency with the RNG, it is recommended to record the movie from game start.<br>Are you sure you still want to record movies now? + + + + + Citra TAS Movie (*.ctm) - - - Recording will start once you boot a game. - - - - - The movie file you are trying to load was created on a different revision of Citra.<br/>Citra has had some changes during the time, and the playback may desync or not work as expected.<br/><br/>Are you sure you still want to load the movie file? - - - - - The movie file you are trying to load was recorded with a different game.<br/>The playback may not work as expected, and it may cause unexpected results.<br/><br/>Are you sure you still want to load the movie file? - - - - - - The movie file you are trying to load is invalid.<br/>Either the file is corrupted, or Citra has had made some major changes to the Movie module.<br/>Please choose a different movie file and try again. - - - - - Revision Dismatch - - - - - Game Dismatch - - - - - - Invalid Movie File - - - - - Play Movie - - - - - Game Not Found - - - - - The movie you are trying to play is from a game that is not in the game list. If you own the game, please add the game folder to the game list and try to play the movie again. - - - - - Movie recording cancelled. - - - - - Movie Saved - - - The movie is successfully saved. - + Recording will start once you boot a game. + Ghi hình sẽ bắt đầu ngay khi bạn mở một game. + + + + The movie file you are trying to load was created on a different revision of Citra.<br/>Citra has had some changes during the time, and the playback may desync or not work as expected.<br/><br/>Are you sure you still want to load the movie file? + Tệp tin phim bạn đang cố mở đã được tạo ở một phiên bản không tương thích khác của Citra.<br/>Citra có vài sự thay đổi lúc này, và bộ phát có thể bị bất đồng bộ hoặc không hoạt động chính xác.<br/><br/>Bạn có chắc tiếp tục mở tệp tin phim? + The movie file you are trying to load was recorded with a different game.<br/>The playback may not work as expected, and it may cause unexpected results.<br/><br/>Are you sure you still want to load the movie file? + Tệp phim bạn đang cố gắng mở đã được ghi từ một trò chơi khác.<br/>Kết quả phát lại sẽ có thể không chính xác, hoặc gây ra lỗi.<br/><br/>Bạn có chắc muốn tiếp tục mở tệp phim này? + + + + + The movie file you are trying to load is invalid.<br/>Either the file is corrupted, or Citra has had made some major changes to the Movie module.<br/>Please choose a different movie file and try again. + Tệp phim bạn đang cố mở không hợp lệ.<br/>Có thể tệp tin đã bị hỏng, hoặc phiên bản này đã thay đổi lớn về mã hóa tệp phim.<br/>Vui lòng chọn một tệp phim khác xem sao. + + + + Revision Dismatch + Phiên bản không phù hợp + + + + Game Dismatch + Trò chơi không phù hợp + + + + + Invalid Movie File + Tệp tin không hợp lệ + + + + + Play Movie + Phát tệp phim + + + + To keep consistency with the RNG, it is recommended to play the movie from game start.<br>Are you sure you still want to play movies now? + + + + + Game Not Found + Không tìm thấy trò chơi + + + + The movie you are trying to play is from a game that is not in the game list. If you own the game, please add the game folder to the game list and try to play the movie again. + Phim bạn đang định phát dường như từ một trò chơi không nằm trong danh sách game hiện có. Nếu bạn đã mua game này, vui lòng thêm vào danh sách game và thử lại. + + + + Movie recording cancelled. + Ghi hình đã bị hủy. + + + + Movie Saved + Đã lưu phim. + + + + The movie is successfully saved. + Phim đã được lưu lại thành công. + + + Speed: %1% / %2% Tốc độ: %1% / %2% - + Speed: %1% Tốc độ: %1% - + Game: %1 FPS Game: %1 FPS - + Frame: %1 ms Khung: %1 ms - + %1 is missing. Please <a href='https://citra-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-3ds-console/'>dump your system archives</a>.<br/>Continuing emulation may result in crashes and bugs. %1 bị thiếu. Vui lòng <a href='https://citra-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-3ds-console/'>trích xuất các tệp hệ thống từ 3DS</a>.<br/>Nếu chạy tiếp giả lập có thể tự thoát hoặc lỗi. - + System Archive Not Found Không thể tìm thấy mục Lưu trữ hệ thống - + Fatal Error Lỗi nghiêm trọng - + A fatal error occured. <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>Check the log</a> for details.<br/>Continuing emulation may result in crashes and bugs. Lỗi nghiêm trọng đã xảy ra. <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>Kiểm tra tệp log</a> để biết thêm chi tiết<br/>Nếu tiếp tục chạy giả lập có thể tự thoát hoặc phát sinh lỗi. - + Abort Hủy bỏ - - + + Citra Citra - + Would you like to exit now? Bạn có muốn thoát ngay bây giờ không? - + The game is still running. Would you like to stop emulation? Trò chơi vẫn đang chạy. Bạn có muốn dừng không? - + Playback Completed - + Phát lại hoàn tất - + Movie playback completed. - + Phát lại phim hoàn tất. - + Citra %1 Citra %1 - + Citra %1| %2 Citra %1| %2 @@ -2799,37 +3015,67 @@ p, li { white-space: pre-wrap; } GameList - + + Name + + + + + Compatibility + + + + + Region + + + + + File type + + + + + Size + + + + Open Save Data Location Mở thư mục lưu game - + + Open Extra Data Location + + + + Open Application Location Mở thư mục ứng dụng - + Open Update Data Location Mở thư mục dữ liệu cập nhật - + Navigate to GameDB entry Điều hướng đến GameDB - + Scan Subfolders Quét thư mục con - + Remove Game Directory Loại bỏ thư mục chứa game - + Open Directory Location Mở thư mục @@ -2837,77 +3083,77 @@ p, li { white-space: pre-wrap; } GameListItemCompat - + Perfect - + Hoàn mỹ - + Game functions flawless with no audio or graphical glitches, all tested functionality works as intended without any workarounds needed. - + Great - + Game functions with minor graphical or audio glitches and is playable from start to finish. May require some workarounds. - + Okay - + Game functions with major graphical or audio glitches, but game is playable from start to finish with workarounds. - + Bad - + Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches even with workarounds. - + Intro/Menu - + Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start Screen. - + Won't Boot - + The game crashes when attempting to startup. - + Not Tested - + The game has not yet been tested. @@ -2915,7 +3161,7 @@ Screen. GameListPlaceholder - + Double-click to add a new folder to the game list Nhấp đúp chuột để thêm thư mục vào danh sách game @@ -2923,27 +3169,27 @@ Screen. GameListSearchField - + of của - + result kết quả - + results các kết quả - + Filter: Bộ lọc: - + Enter pattern to filter Nhập mẫu ký tự để lọc @@ -2951,23 +3197,23 @@ Screen. GraphicsBreakPointsWidget - + Pica Breakpoints Điểm ngắt - - + + Emulation running Giả lập đang chạy - + Resume Tiếp tục - + Emulation halted at breakpoint Giả lập bị tạm dừng tại điểm ngắt @@ -3384,42 +3630,42 @@ Screen. Làm mới - + Password Required to Join Yêu cầu có mật khẩu để vào - + Password: Mật khẩu phòng: - + Room Name Tên phòng - + Preferred Game Game khuyến nghị - + Host Host - + Players Số người - + Refreshing Đang tải - + Refresh List Làm mới @@ -3442,220 +3688,245 @@ Screen. Tệp tin gần đây - + + Amiibo + + + + &Emulation &Giả lập - + &View &Hiển thị - + Debugging Đang sửa lỗi - + Screen Layout Bố trí màn hình - + Movie - + Phim - + Multiplayer Multiplayer - + &Help &Trợ giúp - + Load File... Mở tệp tin... - + Install CIA... Cài đặt CIA... - + Load Symbol Map... Nạp bảng ký tự... - + E&xit Thoát (&X) - + &Start &Bắt đầu - + &Pause Tạm &dừng - + &Stop Dừ&ng - + FAQ FAQ - + About Citra Về Citra - + Single Window Mode Chế độ đơn cửa sổ - + Configure... Thiết lập... - + Display Dock Widget Headers Hiển thị thanh Dock - + Show Filter Bar Hiển thị thanh tìm kiếm - + Show Status Bar Hiển thị trạng thái - + Select Game Directory... Chọn thư mục chứa game... - + Selects a folder to display in the game list Chọn thư mục để hiển thị danh sách game - + Create Pica Surface Viewer Tạo trình xem mặt bằng Pica - + Record Movie - + Ghi hình - + Play Movie - + Phát tệp phim - + Stop Recording / Playback + Dừng ghi hình/phát lại + + + + Enable Frame Advancing - + + Advance Frame + + + + Browse Public Game Lobby Tìm phòng kết nối - + Create Room Tạo phòng - + Leave Room Rời phòng - + Direct Connect to Room Kết nối trực tiếp đến phòng - + Show Current Room Xem phòng hiện tại - + Fullscreen Toàn màn hình - + Modify Citra Install Thay đổi cài đặt Citra - + Opens the maintenance tool to modify your Citra installation Mở công cụ bảo trì để thay đổi cài đặt của Citra - + Default Mặc định - + Single Screen Đơn màn hình - + Large Screen Màn hình lớn - + Side by Side Nằm kề nhau - + Swap Screens Đổi vị trí màn hình - + Check for Updates Kiểm tra cập nhật - + Report Compatibility Gửi báo cáo tính tương thích - + Restart Khởi động lại + + + Load... + + + + + Remove + + MicroProfileDialog @@ -3681,23 +3952,23 @@ Screen. - + Connected Đã kết nối - + Not Connected Chưa kết nối - + Error Lỗi - + Failed to announce the room to the public lobby. In order to host a room publicly, you must have a valid Citra account configured in Emulation -> Configure -> Web. If you do not want to publish a room in the public lobby, then select Unlisted instead. Debug Message: Tạo phòng công khai thất bại. Để tạo một phòng công khai, bạn cần có tài khoản Citra hợp lệ. Vào Giả lập - Thiết lập - Web để thiết lập tài khoản Citra. Nếu không muốn công khai phòng của mình, vui lòng chọn Ẩn danh. @@ -3816,106 +4087,106 @@ Thông tin debug: %1 đang chơi %2 - - + + Invalid region Vùng không hợp lệ - + Japan Nhật Bản - + North America Bắc Mỹ - + Europe Châu Âu - + Australia Úc - + China Trung Quốc - + Korea Hàn Quốc - + Taiwan Đài Loan - + Region free Khác - + Invalid Region Vùng không hợp lệ - + Shift Shift - + Ctrl Ctrl - + Alt Alt - - + + [not set] [chưa được thiết đặt] - + Hat %1 %2 - + Axis %1%2 - + Button %1 - - + + [unknown] [không rõ] - + [unused] [chưa được dùng] - - + + Axis %1 @@ -4200,7 +4471,7 @@ Thông tin debug: waited by no thread - waited by no thread + không có luồng nào chờ diff --git a/dist/languages/zh_CN.ts b/dist/languages/zh_CN.ts index 4feebacba..be0379d84 100644 --- a/dist/languages/zh_CN.ts +++ b/dist/languages/zh_CN.ts @@ -70,45 +70,50 @@ p, li { white-space: pre-wrap; } BreakPointModel - + Pica command loaded 已加载 Pica 命令 - + Pica command processed 已处理 Pica 命令 - + Incoming primitive batch 正传入的原始批处理任务 - + Finished primitive batch 已完成的原始批处理任务 - + Vertex shader invocation 顶点着色器调用 - + Incoming display transfer 正传入的显示调用 - + GSP command processed 已处理的 GSP 命令 - + Buffers swapped 交换缓冲区 + + + Unknown debug context event + 未知调试上下文的事件 + CalibrationConfigurationDialog @@ -140,7 +145,7 @@ p, li { white-space: pre-wrap; } OK - OK + 确定 @@ -171,13 +176,13 @@ p, li { white-space: pre-wrap; } 游戏 - - + + Block Player 屏蔽玩家 - + When you block a player, you will no longer receive chat messages from them.<br><br>Are you sure you would like to block %1? 屏蔽玩家后,你将不会收到其发送的聊天信息。<br><br>确定要屏蔽 %1 吗? @@ -198,17 +203,17 @@ p, li { white-space: pre-wrap; } ClientRoomWindow - + Connected 已连接 - + Disconnected 已断开连接 - + %1 (%2/%3 members) - connected %1 (%2/%3 人) 已连接 @@ -301,6 +306,26 @@ p, li { white-space: pre-wrap; } Thank you for your submission! 感谢您向我们提交信息! + + + Submitting + 提交中 + + + + Communication error + 网络错误 + + + + An error occured while sending the Testcase + 在提交测试用例时发生了一个错误。 + + + + Next + 下一步 + ConfigureAudio @@ -340,10 +365,10 @@ p, li { white-space: pre-wrap; } 0 % - - - %1 % - %1 % + + %1% + Volume percentage (e.g. 50%) + %1% @@ -606,50 +631,78 @@ p, li { white-space: pre-wrap; } ConfigureDialog - + Citra Configuration Citra 设置 - + + + General 通用 - + + + System 系统 - + + + Input 输入 - + + + Graphics 图形 - + + + Audio 声音 - + + + Camera 摄像头 - + + + Debug 调试 - + + + Web 网络 + + + + + UI + 界面 + + + + Controls + 控制 + ConfigureGeneral @@ -669,64 +722,54 @@ p, li { white-space: pre-wrap; } 在游戏运行时退出需要确认 - - Interface language - 界面语言 - - - + Updates 更新 - + Check for updates on start 启动时检查更新 - + Silently auto update after closing 关闭后静默自动更新 - + Emulation 模拟 - + Region: 地区: - + Auto-select 自动选择 - - Theme - 主题 - - - - Theme: - 主题: - - - + Hotkeys 热键 - - <System> - <系统> + + Reset All Settings + 重置所有设置 - - English - English + + Citra + Citra + + + + Are you sure you want to <b>reset your settings</b> and close Citra? + 你确定要<b>重置所有设置</b>并关闭 Citra 吗? @@ -1026,7 +1069,7 @@ p, li { white-space: pre-wrap; } - + Set Analog Stick 设置摇杆 @@ -1038,7 +1081,7 @@ p, li { white-space: pre-wrap; } Misc. - 杂项. + 杂项 @@ -1067,11 +1110,44 @@ p, li { white-space: pre-wrap; } + Clear All + 全部清除 + + + Restore Defaults 恢复默认设置 - + + + Clear + 清除 + + + + + [not set] + [未设置] + + + + + Restore Default + 恢复默认 + + + + Information + 信息 + + + + After pressing OK, first move your joystick horizontally, and then vertically. + 在按下确定后,首先水平移动你的手柄,然后垂直移动它。 + + + [press key] [请按一个键] @@ -1440,17 +1516,27 @@ p, li { white-space: pre-wrap; } 启动时间 - + + yyyy-MM-ddTHH:mm:ss + yyyy-MM-ddTHH:mm:ss + + + + Play Coins: + 游戏币: + + + Console ID: 设备 ID: - + Regenerate 重置 ID - + System settings are available only when game is not running. 只有当游戏不在运行时,系统设置才可用。 @@ -2120,22 +2206,120 @@ p, li { white-space: pre-wrap; } 百慕大群岛 - - + + Console ID: 0x%1 设备 ID: 0x%1 - + This will replace your current virtual 3DS with a new one. Your current virtual 3DS will not be recoverable. This might have unexpected effects in games. This might fail, if you use an outdated config savegame. Continue? 这将使用一个新的虚拟 3DS 取代你当前的虚拟 3DS。您当前的虚拟 3DS 将无法恢复。在部分游戏中可能会出现意外效果。如果你使用一个过时的配置存档这可能会失败。确定要继续吗? - + Warning 警告 + + ConfigureUi + + + Form + Form + + + + General + 通用 + + + + Interface language: + 界面语言: + + + + Theme: + 主题: + + + + Game List + 游戏列表 + + + + Icon Size: + 图标大小: + + + + + None + + + + + Small (24x24) + 小 (24x24) + + + + Large (48x48) + 大 (48x48) + + + + Row 1 Text: + 第一行: + + + + + File Name + 文件名 + + + + + Full Path + 文件路径 + + + + + Title Name + 游戏名称 + + + + + Title ID + 游戏 ID + + + + Row 2 Text: + 第二行: + + + + Hide Titles without Icon + 隐藏没有图标的游戏 + + + + <System> + <系统> + + + + English + 英语 + + ConfigureWeb @@ -2155,7 +2339,7 @@ p, li { white-space: pre-wrap; } - + Verify 验证 @@ -2215,48 +2399,48 @@ p, li { white-space: pre-wrap; } 在您的 Discord 状态中显示当前运行的游戏 - + <a href='https://citra-emu.org/entry/telemetry-and-why-thats-a-good-thing/'><span style="text-decoration: underline; color:#039be5;">Learn more</span></a> <a href='https://citra-emu.org/entry/telemetry-and-why-thats-a-good-thing/'><span style="text-decoration: underline; color:#039be5;">了解更多</span></a> - - <a href='https://services.citra-emu.org/'><span style="text-decoration: underline; color:#039be5;">Sign up</span></a> - <a href='https://services.citra-emu.org/'><span style="text-decoration: underline; color:#039be5;">注册</span></a> + + <a href='https://profile.citra-emu.org/'><span style="text-decoration: underline; color:#039be5;">Sign up</span></a> + <a href='https://profile.citra-emu.org/'><span style="text-decoration: underline; color:#039be5;">注册</span></a> - + <a href='https://citra-emu.org/wiki/citra-web-service/'><span style="text-decoration: underline; color:#039be5;">What is my token?</span></a> <a href='https://citra-emu.org/wiki/citra-web-service/'><span style="text-decoration: underline; color:#039be5;">什么是令牌?</span></a> - - + + Telemetry ID: 0x%1 数据 ID:0x%1 - + Username and token not verified 用户名和令牌未被验证 - + Username and token were not verified. The changes to your username and/or token have not been saved. 用户名和令牌未被验证。对用户名和/或凭证的更改尚未保存。 - + Verifying 验证中 - + Verification failed 验证失败 - + Verification failed. Check that you have entered your username and token correctly, and that your internet connection is working. 验证失败。检查您输入的用户名和令牌是否正确,并且您的互联网连接是否正常。 @@ -2317,12 +2501,12 @@ p, li { white-space: pre-wrap; } DirectConnectWindow - + Connecting 连接中 - + Connect 连接 @@ -2330,413 +2514,445 @@ p, li { white-space: pre-wrap; } GMainWindow - + <a href='https://citra-emu.org/entry/telemetry-and-why-thats-a-good-thing/'>Anonymous data is collected</a> to help improve Citra. <br/><br/>Would you like to share your usage data with us? <a href='https://citra-emu.org/entry/telemetry-and-why-thats-a-good-thing/'>我们收集匿名数据</a>来帮助改进 Citra 。<br/><br/>您愿意和我们分享你的使用数据吗? - + Telemetry 使用数据共享 - - + + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a 3DS. 当前模拟速度。高于或低于 100% 的值表示模拟正在运行得比实际 3DS 更快或更慢。 - - + + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. 游戏当前运行的帧率。这将因游戏和场景的不同而有所不同。 - - + + Time taken to emulate a 3DS frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. 在不计算速度限制和垂直同步的情况下,模拟一个 3DS 帧的实际时间。若要进行全速模拟,这个数值不应超过 16.67 毫秒。 - + Clear Recent Files 清除最近文件 - + F9 F9 - + F10 F10 - + CTRL+F CTRL+F - + Update Available 更新可用 - + An update is available. Would you like to install it now? 有更新可用。您想现在安装吗? - + No Update Found 没有发现更新 - + No update is found. 没有找到更新。 - - + + OpenGL 3.3 Unsupported + 不支持 OpenGL 3.3 + + + + Your GPU may not support OpenGL 3.3, or you do not have the latest graphics driver. + 您的 GPU 可能不支持 OpenGL 3.3,或者您没有安装最新的图形驱动程序。 + + + + Invalid ROM Format 无效 ROM 格式 - - + + Your ROM format is not supported.<br/>Please follow the guides to redump your <a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>game cartridges</a> or <a href='https://citra-emu.org/wiki/dumping-installed-titles/'>installed titles</a>. 您的 ROM 格式不受支持。<br/>请按照 wiki 文章来重新转储您的<a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>游戏卡带</a>或<a href='https://citra-emu.org/wiki/dumping-installed-titles/'>已安装的游戏</a>。 - + ROM Corrupted ROM 损坏 - + Your ROM is corrupted. <br/>Please follow the guides to redump your <a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>game cartridges</a> or <a href='https://citra-emu.org/wiki/dumping-installed-titles/'>installed titles</a>. 您的 ROM 已损坏。<br/> 请按照 wiki 文章来重新转储您的<a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>游戏卡带</a>或<a href='https://citra-emu.org/wiki/dumping-installed-titles/'>已安装的游戏</a>。 - + ROM Encrypted 加密 ROM - + Your ROM is encrypted. <br/>Please follow the guides to redump your <a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>game cartridges</a> or <a href='https://citra-emu.org/wiki/dumping-installed-titles/'>installed titles</a>. 您的 ROM 是加密的。<br/> 请按照 wiki 文章重新转储您的<a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>游戏卡带</a>或<a href='https://citra-emu.org/wiki/dumping-installed-titles/'>已安装的游戏</a>。 - - + + Video Core Error 视频核心错误 - + An error has occured. Please <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>see the log</a> for more details. Ensure that you have the latest graphics drivers for your GPU. 发生了错误。 请<a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>参阅日志</a>了解更多详情。请确保您已安装最新的 GPU 图形驱动程序。 - + You are running default Windows drivers for your GPU. You need to install the proper drivers for your graphics card from the manufacturer's website. 您正在运行默认的 Windows 驱动程序。您需要从制造商的网站为您的显卡安装正确的驱动程序。 - + Error while loading ROM! 加载 ROM 时出错! - + An unknown error occured. Please see the log for more details. 发生了一个未知的错误。详情请参阅日志。 - + Start 开始 - + Error Opening %1 Folder 无法打开 %1 文件夹 - - + + Folder does not exist! 文件夹不存在! - + Error Opening %1 无法打开 %1 - + Select Directory 选择目录 - - 3DS Executable - 3DS 可执行文件 + + 3DS Executable (%1);;All Files (*.*) + %1 is an identifier for the 3DS executable file extensions. + 3DS 可执行文件 (%1);;所有文件 (*.*) - - - All Files (*.*) - 所有文件 (*.*) - - - + Load File 加载文件 - + Load Files 加载多个文件 - + 3DS Installation File (*.CIA*) 3DS 安装文件 (*.CIA*) - + + All Files (*.*) + 所有文件 (*.*) + + + %1 has been installed successfully. %1 已成功安装。 - + Unable to open File 无法打开文件 - + Could not open %1 无法打开 %1 - + Installation aborted 安装失败 - + The installation of %1 was aborted. Please see the log for more details %1 的安装过程失败。详情请参看日志 - + Invalid File 文件无效 - + %1 is not a valid CIA %1 不是有效的 CIA 文件 - + Encrypted File 文件已加密 - + %1 must be decrypted before being used with Citra. A real 3DS is required. %1 需要解密才能被 Citra 识别。解密过程需要一台实体 3DS 游戏机。 - + File not found 找不到文件 - + File "%1" not found 文件 "%1" 未找到 - - - + + + Continue 继续 - + Missing Citra Account 没有 Citra 帐号 - + You must link your Citra account to submit test cases.<br/>Go to Emulation &gt; Configure... &gt; Web to do so. 您必须设置您的 Citra 帐户来提交测试用例。<br/>请前往模拟 > 设置… > 网络进行设置。 - - - + + Amiibo File (%1);; All Files (*.*) + Amiibo 文件 (%1);;所有文件 (*.*) + + + + Load Amiibo + 加载 Amiibo + + + + + + Record Movie 录制影像 - - + + To keep consistency with the RNG, it is recommended to record the movie from game start.<br>Are you sure you still want to record movies now? + 为保持随机数的一致性,推荐从游戏开始起录制影像。<br>你确定仍然要现在开始录制影像吗? + + + + Citra TAS Movie (*.ctm) Citra TAS 影像 (*.ctm) - + Recording will start once you boot a game. 一旦启动游戏,录制就会开始。 - + The movie file you are trying to load was created on a different revision of Citra.<br/>Citra has had some changes during the time, and the playback may desync or not work as expected.<br/><br/>Are you sure you still want to load the movie file? 您要加载的影像文件是在另一个版本的 Citra 上创建的。<br/>Citra 在此期间有一些更改,您的影像可能无法正常工作。<br/><br/>您确定仍然要加载影像文件么? - + The movie file you are trying to load was recorded with a different game.<br/>The playback may not work as expected, and it may cause unexpected results.<br/><br/>Are you sure you still want to load the movie file? 您要加载的影像文件是使用不同的游戏录制的。<br/>播放可能无法正常工作,并且可能会导致意外结果。<br/><br/>您确定仍然要加载影像文件么? - - + + The movie file you are trying to load is invalid.<br/>Either the file is corrupted, or Citra has had made some major changes to the Movie module.<br/>Please choose a different movie file and try again. 您要加载的影像文件无效。<br/>可能是文件损坏,或 Citra 已对影像模块进行了一些重大更改。<br/>请选择其他影像文件重试。 - + Revision Dismatch 版本不匹配 - + Game Dismatch 游戏不匹配 - - + + Invalid Movie File 影像文件无效 - + + Play Movie 播放影像 - + + To keep consistency with the RNG, it is recommended to play the movie from game start.<br>Are you sure you still want to play movies now? + 为保持随机数的一致性,推荐从游戏开始起播放影像。<br>你确定仍然要现在开始播放影像吗? + + + Game Not Found 游戏未找到 - + The movie you are trying to play is from a game that is not in the game list. If you own the game, please add the game folder to the game list and try to play the movie again. 您尝试播放的影像来自一个不在游戏列表中的游戏。如果您拥有该游戏,请将游戏文件夹添加到游戏列表中,并尝试再次播放该影像。 - + Movie recording cancelled. 影像录制已取消。 - + Movie Saved 影像已保存 - + The movie is successfully saved. 影像已成功保存。 - + Speed: %1% / %2% 速度: %1% / %2% - + Speed: %1% 速度: %1% - + Game: %1 FPS FPS: %1 - + Frame: %1 ms 帧延迟:%1 毫秒 - + %1 is missing. Please <a href='https://citra-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-3ds-console/'>dump your system archives</a>.<br/>Continuing emulation may result in crashes and bugs. 未找到 %1。 请<a href='https://citra-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-3ds-console/'>转储您的系统档案</a>。<br/>继续进行模拟可能会导致崩溃和错误。 - + System Archive Not Found 未找到系统档案 - + Fatal Error 致命错误 - + A fatal error occured. <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>Check the log</a> for details.<br/>Continuing emulation may result in crashes and bugs. 发生了致命错误。请<a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>参阅日志</a>了解详细信息。<br/>继续进行模拟可能会导致崩溃和错误。 - + Abort 中止 - - + + Citra Citra - + Would you like to exit now? 您现在要退出么? - + The game is still running. Would you like to stop emulation? 游戏仍在运行。您想停止模拟吗? - + Playback Completed 播放完成 - + Movie playback completed. 影像播放完成。 - + Citra %1 Citra %1 - + Citra %1| %2 Citra %1| %2 @@ -2799,37 +3015,67 @@ p, li { white-space: pre-wrap; } GameList - + + Name + 名称 + + + + Compatibility + 兼容性 + + + + Region + 地区 + + + + File type + 文件类型 + + + + Size + 大小 + + + Open Save Data Location 打开存档位置 - + + Open Extra Data Location + 打开附加存档数据位置 + + + Open Application Location 打开应用程序位置 - + Open Update Data Location 打开更新数据位置 - + Navigate to GameDB entry 查看兼容性报告 - + Scan Subfolders 扫描子文件夹 - + Remove Game Directory 删除游戏目录 - + Open Directory Location 打开目录位置 @@ -2837,77 +3083,77 @@ p, li { white-space: pre-wrap; } GameListItemCompat - + Perfect 完美 - + Game functions flawless with no audio or graphical glitches, all tested functionality works as intended without any workarounds needed. 游戏功能完美,没有音频或图形问题。所有测试的功能均能工作,不需要任何特殊技巧去完成游戏。 - + Great 良好 - + Game functions with minor graphical or audio glitches and is playable from start to finish. May require some workarounds. 游戏运行时会有非常轻微的图像或音频问题,但是能从头玩到尾。可能需要一些技巧才能完成游戏。 - + Okay 一般 - + Game functions with major graphical or audio glitches, but game is playable from start to finish with workarounds. 游戏运行时会有很多图像或音频错误,但是在使用一些特殊技巧之后能完整地完成游戏。 - + Bad 较差 - + Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches even with workarounds. 游戏能运行,但是会有大量图像或音频错误。即使使用一些技巧仍无法通过游戏的某些区域。 - + Intro/Menu 开场 / 菜单 - + Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start Screen. 游戏完全没法玩,图像或音频有重大错误。通过开场菜单后无法继续。 - + Won't Boot 无法打开 - + The game crashes when attempting to startup. 在启动游戏时直接崩溃了。 - + Not Tested 未测试 - + The game has not yet been tested. 游戏尚未经过测试。 @@ -2915,7 +3161,7 @@ Screen. GameListPlaceholder - + Double-click to add a new folder to the game list 双击添加游戏文件夹 @@ -2923,27 +3169,27 @@ Screen. GameListSearchField - + of - + result 结果 - + results 结果 - + Filter: 搜索: - + Enter pattern to filter 搜索游戏 @@ -2951,23 +3197,23 @@ Screen. GraphicsBreakPointsWidget - + Pica Breakpoints Pica 断点 - - + + Emulation running 模拟正在运行 - + Resume 继续 - + Emulation halted at breakpoint 模拟停止在断点处 @@ -3384,42 +3630,42 @@ Screen. 刷新大厅 - + Password Required to Join 需要密码 - + Password: 密码: - + Room Name 房间名称 - + Preferred Game 首选游戏 - + Host 创建者 - + Players 玩家 - + Refreshing 正在刷新 - + Refresh List 刷新列表 @@ -3442,220 +3688,245 @@ Screen. 最近文件 - + + Amiibo + Amiibo + + + &Emulation 模拟 (&E) - + &View 视图 (&V) - + Debugging 调试 - + Screen Layout 屏幕布局 - + Movie 影像 - + Multiplayer 多人游戏 - + &Help 帮助 (&H) - + Load File... 加载文件… - + Install CIA... 安装 CIA… - + Load Symbol Map... 加载符号表… - + E&xit 退出 (&X) - + &Start 开始 (&S) - + &Pause 暂停 (&P) - + &Stop 停止 (&S) - + FAQ 常见问题 - + About Citra 关于 Citra - + Single Window Mode 单窗口模式 - + Configure... 设置… - + Display Dock Widget Headers 显示停靠小部件的标题 - + Show Filter Bar 显示过滤栏 - + Show Status Bar 显示状态栏 - + Select Game Directory... 选择游戏路径… - + Selects a folder to display in the game list 选择一个文件夹在游戏列表中显示 - + Create Pica Surface Viewer 新建 Pica 表面浏览器 - + Record Movie 录制影像 - + Play Movie 播放影像 - + Stop Recording / Playback 停止录制 / 播放 - + + Enable Frame Advancing + 逐帧播放 + + + + Advance Frame + 播放下一帧 + + + Browse Public Game Lobby 浏览公共游戏大厅 - + Create Room 创建房间 - + Leave Room 离开房间 - + Direct Connect to Room 直接连接到房间 - + Show Current Room 显示当前房间 - + Fullscreen 全屏 - + Modify Citra Install 更改 Citra 安装 - + Opens the maintenance tool to modify your Citra installation 打开维护工具修改 Citra 安装 - + Default 默认 - + Single Screen 单屏 - + Large Screen 大屏 - + Side by Side 横屏 - + Swap Screens 交换上下屏 - + Check for Updates 检查更新 - + Report Compatibility 报告兼容性 - + Restart 重新启动 + + + Load... + 加载... + + + + Remove + 移除 + MicroProfileDialog @@ -3681,23 +3952,23 @@ Screen. - + Connected 已连接 - + Not Connected 未连接 - + Error 错误 - + Failed to announce the room to the public lobby. In order to host a room publicly, you must have a valid Citra account configured in Emulation -> Configure -> Web. If you do not want to publish a room in the public lobby, then select Unlisted instead. Debug Message: 未能创建公开房间。您必须在模拟 - 设置 - 网络中配置有效的 Citra 账户。如果您不希望您的房间在公共游戏大厅中显示,请选择“私有”。 @@ -3816,106 +4087,106 @@ Debug Message: %1 在玩 %2 - - + + Invalid region 无效的地区 - + Japan 日本 - + North America 北美洲 - + Europe 欧洲 - + Australia 澳大利亚 - + China 中国 - + Korea 朝鲜 - + Taiwan 台湾 - + Region free 不锁区 - + Invalid Region 无效的地区 - + Shift Shift - + Ctrl Ctrl - + Alt Alt - - + + [not set] [未设置] - + Hat %1 %2 方向键 %1 %2 - + Axis %1%2 轴 %1%2 - + Button %1 按键 %1 - - + + [unknown] [未知] - + [unused] [未使用] - - + + Axis %1 轴 %1 diff --git a/dist/languages/zh_TW.ts b/dist/languages/zh_TW.ts index 31e1f2660..5a3204e83 100644 --- a/dist/languages/zh_TW.ts +++ b/dist/languages/zh_TW.ts @@ -70,45 +70,50 @@ p, li { white-space: pre-wrap; } BreakPointModel - + Pica command loaded 已讀取 Pica 命令 - + Pica command processed 已處理 Pica 命令 - + Incoming primitive batch 原始批次任務傳入 - + Finished primitive batch 原始批次任務結束 - + Vertex shader invocation 頂點著色器調用 - + Incoming display transfer 畫面轉移傳入 - + GSP command processed 已處理 GSP 命令 - + Buffers swapped 已交換緩衝 + + + Unknown debug context event + + CalibrationConfigurationDialog @@ -171,13 +176,13 @@ p, li { white-space: pre-wrap; } 遊戲 - - + + Block Player 阻擋玩家 - + When you block a player, you will no longer receive chat messages from them.<br><br>Are you sure you would like to block %1? 阻擋玩家後,你將不會收到他傳送的聊天訊息。<br><br>你確定你想要阻擋「%1」嗎? @@ -198,17 +203,17 @@ p, li { white-space: pre-wrap; } ClientRoomWindow - + Connected 已連線 - + Disconnected 已斷線 - + %1 (%2/%3 members) - connected %1(%2/%3 人)- 已連線 @@ -301,6 +306,26 @@ p, li { white-space: pre-wrap; } Thank you for your submission! 感謝您的回報! + + + Submitting + + + + + Communication error + + + + + An error occured while sending the Testcase + + + + + Next + + ConfigureAudio @@ -340,10 +365,10 @@ p, li { white-space: pre-wrap; } 0 % - - - %1 % - %1 % + + %1% + Volume percentage (e.g. 50%) + @@ -606,50 +631,78 @@ p, li { white-space: pre-wrap; } ConfigureDialog - + Citra Configuration Citra 設定 - + + + General 一般 - + + + System 系統 - + + + Input 輸入 - + + + Graphics 圖形 - + + + Audio 音效 - + + + Camera 相機 - + + + Debug 除錯 - + + + Web 網路 + + + + + UI + + + + + Controls + + ConfigureGeneral @@ -669,64 +722,54 @@ p, li { white-space: pre-wrap; } 在遊戲執行中離開時確認 - - Interface language - 介面語言 - - - + Updates 更新 - + Check for updates on start 啟動時檢查更新 - + Silently auto update after closing 關閉後在背景自動更新 - + Emulation 模擬 - + Region: 地區: - + Auto-select 自動選擇 - - Theme - 主題 - - - - Theme: - 面版主題: - - - + Hotkeys 熱鍵 - - <System> - < 系統 > + + Reset All Settings + - - English - English + + Citra + + + + + Are you sure you want to <b>reset your settings</b> and close Citra? + @@ -1026,7 +1069,7 @@ p, li { white-space: pre-wrap; } - + Set Analog Stick 設定類比搖桿 @@ -1067,11 +1110,44 @@ p, li { white-space: pre-wrap; } + Clear All + + + + Restore Defaults 還原預設 - + + + Clear + + + + + + [not set] + + + + + + Restore Default + + + + + Information + + + + + After pressing OK, first move your joystick horizontally, and then vertically. + + + + [press key] [ 請輸入按鍵 ] @@ -1440,17 +1516,27 @@ p, li { white-space: pre-wrap; } - + + yyyy-MM-ddTHH:mm:ss + + + + + Play Coins: + + + + Console ID: 裝置 ID: - + Regenerate 更換 ID - + System settings are available only when game is not running. 遊戲執行時不能修改系統設定。 @@ -2120,22 +2206,120 @@ p, li { white-space: pre-wrap; } - - + + Console ID: 0x%1 裝置 ID:0x%1 - + This will replace your current virtual 3DS with a new one. Your current virtual 3DS will not be recoverable. This might have unexpected effects in games. This might fail, if you use an outdated config savegame. Continue? 更換 ID 等同於更換一台 3DS,產生新 ID 後將無法還原目前 ID,且可能造成部分遊戲出現錯誤。如果您的遊戲存檔使用過期設定,可能造成這個動作失敗。確定繼續嗎? - + Warning 警告 + + ConfigureUi + + + Form + + + + + General + + + + + Interface language: + + + + + Theme: + + + + + Game List + + + + + Icon Size: + + + + + + None + + + + + Small (24x24) + + + + + Large (48x48) + + + + + Row 1 Text: + + + + + + File Name + + + + + + Full Path + + + + + + Title Name + + + + + + Title ID + + + + + Row 2 Text: + + + + + Hide Titles without Icon + + + + + <System> + + + + + English + + + ConfigureWeb @@ -2155,7 +2339,7 @@ p, li { white-space: pre-wrap; } - + Verify 驗證 @@ -2215,48 +2399,48 @@ p, li { white-space: pre-wrap; } - + <a href='https://citra-emu.org/entry/telemetry-and-why-thats-a-good-thing/'><span style="text-decoration: underline; color:#039be5;">Learn more</span></a> <a href='https://citra-emu.org/entry/telemetry-and-why-thats-a-good-thing/'><span style="text-decoration: underline; color:#039be5;">了解更多</span></a> - - <a href='https://services.citra-emu.org/'><span style="text-decoration: underline; color:#039be5;">Sign up</span></a> - <a href='https://services.citra-emu.org/'><span style="text-decoration: underline; color:#039be5;">註冊</span></a> + + <a href='https://profile.citra-emu.org/'><span style="text-decoration: underline; color:#039be5;">Sign up</span></a> + - + <a href='https://citra-emu.org/wiki/citra-web-service/'><span style="text-decoration: underline; color:#039be5;">What is my token?</span></a> <a href='https://citra-emu.org/wiki/citra-web-service/'><span style="text-decoration: underline; color:#039be5;">什麼是 Citra 權杖?</span></a> - - + + Telemetry ID: 0x%1 遙測 ID:0x%1 - + Username and token not verified 未驗證使用者名稱和權杖 - + Username and token were not verified. The changes to your username and/or token have not been saved. 尚未驗證使用者名稱和權杖,您修改的使用者名稱或權杖尚未儲存。 - + Verifying 驗證中 - + Verification failed 驗證失敗 - + Verification failed. Check that you have entered your username and token correctly, and that your internet connection is working. 驗證失敗,請檢查您輸入的使用者名稱和權杖是否正確,且確認網路連線正常。 @@ -2317,12 +2501,12 @@ p, li { white-space: pre-wrap; } DirectConnectWindow - + Connecting 連線中 - + Connect 連線 @@ -2330,415 +2514,447 @@ p, li { white-space: pre-wrap; } GMainWindow - + <a href='https://citra-emu.org/entry/telemetry-and-why-thats-a-good-thing/'>Anonymous data is collected</a> to help improve Citra. <br/><br/>Would you like to share your usage data with us? - + Telemetry - - + + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a 3DS. 目前模擬速度, 「高於/低於」100% 代表模擬速度比 3DS 實機「更快/更慢」。 - - + + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. 遊戲目前的 FPS,不同的遊戲和場景會有不同數值。 - - + + Time taken to emulate a 3DS frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. 不計算影格限制或垂直同步時, 模擬一個 3DS 影格所花的時間。全速模擬時,這個數值最多應為 16.67 毫秒。 - + Clear Recent Files 清除檔案使用紀錄 - + F9 F9 - + F10 F10 - + CTRL+F CTRL+F - + Update Available - + An update is available. Would you like to install it now? - + No Update Found - + No update is found. - - + + OpenGL 3.3 Unsupported + + + + + Your GPU may not support OpenGL 3.3, or you do not have the latest graphics driver. + + + + + Invalid ROM Format - - + + Your ROM format is not supported.<br/>Please follow the guides to redump your <a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>game cartridges</a> or <a href='https://citra-emu.org/wiki/dumping-installed-titles/'>installed titles</a>. - + ROM Corrupted - + Your ROM is corrupted. <br/>Please follow the guides to redump your <a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>game cartridges</a> or <a href='https://citra-emu.org/wiki/dumping-installed-titles/'>installed titles</a>. - + ROM Encrypted - + Your ROM is encrypted. <br/>Please follow the guides to redump your <a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>game cartridges</a> or <a href='https://citra-emu.org/wiki/dumping-installed-titles/'>installed titles</a>. - - + + Video Core Error - + An error has occured. Please <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>see the log</a> for more details. Ensure that you have the latest graphics drivers for your GPU. - + You are running default Windows drivers for your GPU. You need to install the proper drivers for your graphics card from the manufacturer's website. - + Error while loading ROM! 讀取 ROM 時出現錯誤! - + An unknown error occured. Please see the log for more details. 出現未知的錯誤,請參閱日誌了解細節。 - + Start 開始 - + Error Opening %1 Folder 開啟 %1 資料夾時錯誤 - - + + Folder does not exist! 資料夾不存在! - + Error Opening %1 開啟 %1 時錯誤 - + Select Directory 選擇目錄 - - 3DS Executable - 3DS 可執行檔案 + + 3DS Executable (%1);;All Files (*.*) + %1 is an identifier for the 3DS executable file extensions. + - - - All Files (*.*) - 所有檔案 (*.*) - - - + Load File 讀取檔案 - + Load Files 讀取多個檔案 - + 3DS Installation File (*.CIA*) 3DS 安裝檔 (*.CIA) - + + All Files (*.*) + 所有檔案 (*.*) + + + %1 has been installed successfully. 已成功安裝 %1。 - + Unable to open File 無法開啟檔案 - + Could not open %1 無法開啟 %1 - + Installation aborted 安裝中斷 - + The installation of %1 was aborted. Please see the log for more details 安裝 %1 時中斷,請參閱日誌了解細節。 - + Invalid File 無效的檔案 - + %1 is not a valid CIA %1 不是有效的 CIA 檔案 - + Encrypted File 檔案未解密 - + %1 must be decrypted before being used with Citra. A real 3DS is required. %1 需要先解密才能在 Citra 執行,正規的解密方式需要 3DS 實機。 - + File not found 找不到檔案 - + File "%1" not found 找不到「%1」 - - - + + + Continue 繼續 - + Missing Citra Account 找不到 Citra 帳號 - + You must link your Citra account to submit test cases.<br/>Go to Emulation &gt; Configure... &gt; Web to do so. - - - + + Amiibo File (%1);; All Files (*.*) + + + + + Load Amiibo + + + + + + + Record Movie - - + + To keep consistency with the RNG, it is recommended to record the movie from game start.<br>Are you sure you still want to record movies now? + + + + + Citra TAS Movie (*.ctm) - + Recording will start once you boot a game. - + The movie file you are trying to load was created on a different revision of Citra.<br/>Citra has had some changes during the time, and the playback may desync or not work as expected.<br/><br/>Are you sure you still want to load the movie file? - + The movie file you are trying to load was recorded with a different game.<br/>The playback may not work as expected, and it may cause unexpected results.<br/><br/>Are you sure you still want to load the movie file? - - + + The movie file you are trying to load is invalid.<br/>Either the file is corrupted, or Citra has had made some major changes to the Movie module.<br/>Please choose a different movie file and try again. - + Revision Dismatch - + Game Dismatch - - + + Invalid Movie File - + + Play Movie - + + To keep consistency with the RNG, it is recommended to play the movie from game start.<br>Are you sure you still want to play movies now? + + + + Game Not Found - + The movie you are trying to play is from a game that is not in the game list. If you own the game, please add the game folder to the game list and try to play the movie again. - + Movie recording cancelled. - + Movie Saved - + The movie is successfully saved. - + Speed: %1% / %2% 速度:%1% / %2% - + Speed: %1% 速度:%1% - + Game: %1 FPS FPS:%1 - + Frame: %1 ms 影格:%1 ms - + %1 is missing. Please <a href='https://citra-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-3ds-console/'>dump your system archives</a>.<br/>Continuing emulation may result in crashes and bugs. - + System Archive Not Found 找不到系統檔案 - + Fatal Error 嚴重錯誤 - + A fatal error occured. <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>Check the log</a> for details.<br/>Continuing emulation may result in crashes and bugs. - + Abort - - + + Citra Citra - + Would you like to exit now? - + The game is still running. Would you like to stop emulation? - + Playback Completed - + Movie playback completed. - + Citra %1 Citra %1 - + Citra %1| %2 Citra %1| %2 @@ -2801,37 +3017,67 @@ p, li { white-space: pre-wrap; } GameList - + + Name + + + + + Compatibility + + + + + Region + + + + + File type + + + + + Size + + + + Open Save Data Location 開啟存檔位置 - + + Open Extra Data Location + + + + Open Application Location 開啟應用程式位置 - + Open Update Data Location 開啟更新檔位置 - + Navigate to GameDB entry 開啟遊戲相容性網頁 - + Scan Subfolders 掃描子資料夾 - + Remove Game Directory 在列表中移除此路徑 - + Open Directory Location 開啟資料夾位置 @@ -2839,77 +3085,77 @@ p, li { white-space: pre-wrap; } GameListItemCompat - + Perfect - + Game functions flawless with no audio or graphical glitches, all tested functionality works as intended without any workarounds needed. - + Great - + Game functions with minor graphical or audio glitches and is playable from start to finish. May require some workarounds. - + Okay - + Game functions with major graphical or audio glitches, but game is playable from start to finish with workarounds. - + Bad - + Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches even with workarounds. - + Intro/Menu - + Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start Screen. - + Won't Boot - + The game crashes when attempting to startup. - + Not Tested - + The game has not yet been tested. @@ -2917,7 +3163,7 @@ Screen. GameListPlaceholder - + Double-click to add a new folder to the game list 在這裡點兩下新增資料夾到遊戲列表 @@ -2925,27 +3171,27 @@ Screen. GameListSearchField - + of / - + result 項符合 - + results 項符合 - + Filter: 項目篩選 - + Enter pattern to filter 輸入項目關鍵字 @@ -2953,23 +3199,23 @@ Screen. GraphicsBreakPointsWidget - + Pica Breakpoints Pica 斷點 - - + + Emulation running 正在模擬 - + Resume 繼續 - + Emulation halted at breakpoint 模擬停止在斷點 @@ -3386,42 +3632,42 @@ Screen. 重新整理 - + Password Required to Join 需要密碼 - + Password: 密碼: - + Room Name 房間名稱 - + Preferred Game 首選遊戲 - + Host 建立者 - + Players 玩家數 - + Refreshing 正在重新整理 - + Refresh List 重新整理 @@ -3444,220 +3690,245 @@ Screen. 最近開啟的檔案 - + + Amiibo + + + + &Emulation 模擬 (&E) - + &View 檢視 (&V) - + Debugging 除錯 - + Screen Layout 螢幕布局 - + Movie - + Multiplayer 多人連線 (&M) - + &Help 說明 (&H) - + Load File... 讀取檔案… - + Install CIA... 安裝 CIA… - + Load Symbol Map... 讀取符號圖… - + E&xit 離開 (&X) - + &Start 開始 (&S) - + &Pause 暫停 (&P) - + &Stop 停止 (&S) - + FAQ 常見問題 - + About Citra 關於 Citra - + Single Window Mode 統一視窗 - + Configure... 設定… - + Display Dock Widget Headers 顯示小工具的標題 - + Show Filter Bar 顯示項目篩選列 - + Show Status Bar 顯示狀態列 - + Select Game Directory... 選擇遊戲目錄… - + Selects a folder to display in the game list 選擇遊戲資料夾以顯示遊戲列表 - + Create Pica Surface Viewer 建立 Pica 表層檢視器 - + Record Movie - + Play Movie - + Stop Recording / Playback - + + Enable Frame Advancing + + + + + Advance Frame + + + + Browse Public Game Lobby 瀏覽公共房間 - + Create Room 建立房間 - + Leave Room 離開房間 - + Direct Connect to Room 連線到特定房間 - + Show Current Room 顯示目前房間 - + Fullscreen 全螢幕 - + Modify Citra Install 修改 Citra 安裝細節 - + Opens the maintenance tool to modify your Citra installation 開啟管理工具修改 Citra 的安裝細節 - + Default 預設 - + Single Screen 單一畫面 - + Large Screen 大畫面 - + Side by Side 並排 - + Swap Screens 交換上下畫面 - + Check for Updates 檢查更新 - + Report Compatibility 回報遊戲相容性 - + Restart 重新開始 + + + Load... + + + + + Remove + + MicroProfileDialog @@ -3683,23 +3954,23 @@ Screen. - + Connected 已連線 - + Not Connected 未連線 - + Error 錯誤 - + Failed to announce the room to the public lobby. In order to host a room publicly, you must have a valid Citra account configured in Emulation -> Configure -> Web. If you do not want to publish a room in the public lobby, then select Unlisted instead. Debug Message: 無法發布公共房間,您必須先在設定介面的「網路」標籤頁中設定有效的 Citra 帳號。如果您不希望在公共房間列表中顯示您的房間,請在下方選擇「不列出」。 @@ -3818,106 +4089,106 @@ Debug Message: %1 正在玩 %2 - - + + Invalid region 無效的地區 - + Japan 日本 - + North America 北美洲 - + Europe 歐洲 - + Australia 澳洲 - + China 中國 - + Korea 南韓 - + Taiwan 台灣 - + Region free 不鎖區 - + Invalid Region 無效的地區 - + Shift Shift - + Ctrl Ctrl - + Alt Alt - - + + [not set] [ 無設定 ] - + Hat %1 %2 - + Axis %1%2 - + Button %1 - - + + [unknown] [ 未知 ] - + [unused] [ 未使用 ] - - + + Axis %1 diff --git a/dist/scripting/citra.py b/dist/scripting/citra.py index 869f64041..be6068685 100644 --- a/dist/scripting/citra.py +++ b/dist/scripting/citra.py @@ -1,7 +1,6 @@ import zmq import struct import random -import binascii import enum CURRENT_REQUEST_VERSION = 1 diff --git a/keys.tar.enc b/keys.tar.enc new file mode 100644 index 000000000..6a8c1484e Binary files /dev/null and b/keys.tar.enc differ diff --git a/src/audio_core/cubeb_sink.cpp b/src/audio_core/cubeb_sink.cpp index 790629336..29e353fe4 100644 --- a/src/audio_core/cubeb_sink.cpp +++ b/src/audio_core/cubeb_sink.cpp @@ -56,7 +56,8 @@ CubebSink::CubebSink(std::string target_device_name) : impl(std::make_uniquedevid; diff --git a/src/audio_core/hle/hle.cpp b/src/audio_core/hle/hle.cpp index 58604d861..db50c0bff 100644 --- a/src/audio_core/hle/hle.cpp +++ b/src/audio_core/hle/hle.cpp @@ -12,6 +12,7 @@ #include "common/assert.h" #include "common/common_types.h" #include "common/logging/log.h" +#include "core/core.h" #include "core/core_timing.h" using InterruptType = Service::DSP::DSP_DSP::InterruptType; @@ -63,7 +64,7 @@ private: HLE::Mixers mixers; DspHle& parent; - CoreTiming::EventType* tick_event; + Core::TimingEventType* tick_event; std::weak_ptr dsp_dsp; }; @@ -71,15 +72,17 @@ private: DspHle::Impl::Impl(DspHle& parent_) : parent(parent_) { dsp_memory.raw_memory.fill(0); + Core::Timing& timing = Core::System::GetInstance().CoreTiming(); tick_event = - CoreTiming::RegisterEvent("AudioCore::DspHle::tick_event", [this](u64, s64 cycles_late) { + timing.RegisterEvent("AudioCore::DspHle::tick_event", [this](u64, s64 cycles_late) { this->AudioTickCallback(cycles_late); }); - CoreTiming::ScheduleEvent(audio_frame_ticks, tick_event); + timing.ScheduleEvent(audio_frame_ticks, tick_event); } DspHle::Impl::~Impl() { - CoreTiming::UnscheduleEvent(tick_event, 0); + Core::Timing& timing = Core::System::GetInstance().CoreTiming(); + timing.UnscheduleEvent(tick_event, 0); } DspState DspHle::Impl::GetDspState() const { @@ -328,7 +331,8 @@ void DspHle::Impl::AudioTickCallback(s64 cycles_late) { } // Reschedule recurrent event - CoreTiming::ScheduleEvent(audio_frame_ticks - cycles_late, tick_event); + Core::Timing& timing = Core::System::GetInstance().CoreTiming(); + timing.ScheduleEvent(audio_frame_ticks - cycles_late, tick_event); } DspHle::DspHle() : impl(std::make_unique(*this)) {} diff --git a/src/citra_qt/CMakeLists.txt b/src/citra_qt/CMakeLists.txt index ea275f3d2..20fefa87c 100644 --- a/src/citra_qt/CMakeLists.txt +++ b/src/citra_qt/CMakeLists.txt @@ -14,6 +14,8 @@ add_executable(citra-qt applets/swkbd.h bootmanager.cpp bootmanager.h + compatibility_list.cpp + compatibility_list.h camera/camera_util.cpp camera/camera_util.h camera/still_image_camera.cpp @@ -76,6 +78,8 @@ add_executable(citra-qt game_list.cpp game_list.h game_list_p.h + game_list_worker.cpp + game_list_worker.h hotkeys.cpp hotkeys.h main.cpp @@ -136,15 +140,15 @@ set(UIS ) file(GLOB COMPAT_LIST - ${CMAKE_BINARY_DIR}/dist/compatibility_list/compatibility_list.qrc - ${CMAKE_BINARY_DIR}/dist/compatibility_list/compatibility_list.json) -file(GLOB_RECURSE ICONS ${CMAKE_SOURCE_DIR}/dist/icons/*) -file(GLOB_RECURSE THEMES ${CMAKE_SOURCE_DIR}/dist/qt_themes/*) + ${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.qrc + ${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.json) +file(GLOB_RECURSE ICONS ${PROJECT_SOURCE_DIR}/dist/icons/*) +file(GLOB_RECURSE THEMES ${PROJECT_SOURCE_DIR}/dist/qt_themes/*) qt5_wrap_ui(UI_HDRS ${UIS}) if (ENABLE_QT_TRANSLATION) - set(CITRA_QT_LANGUAGES "${CMAKE_SOURCE_DIR}/dist/languages" CACHE PATH "Path to the translation bundle for the Qt frontend") + set(CITRA_QT_LANGUAGES "${PROJECT_SOURCE_DIR}/dist/languages" CACHE PATH "Path to the translation bundle for the Qt frontend") option(GENERATE_QT_TRANSLATION "Generate en.ts as the translation source file" OFF) # Update source TS file if enabled @@ -209,7 +213,7 @@ target_link_libraries(citra-qt PRIVATE Boost::boost glad nihstro-headers Qt5::Op target_link_libraries(citra-qt PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads) if (CITRA_ENABLE_COMPATIBILITY_REPORTING) - add_definitions(-DCITRA_ENABLE_COMPATIBILITY_REPORTING) + target_compile_definitions(citra-qt PRIVATE -DCITRA_ENABLE_COMPATIBILITY_REPORTING) endif() if (USE_DISCORD_PRESENCE) diff --git a/src/citra_qt/bootmanager.cpp b/src/citra_qt/bootmanager.cpp index 9dd2b338e..39115f31e 100644 --- a/src/citra_qt/bootmanager.cpp +++ b/src/citra_qt/bootmanager.cpp @@ -109,9 +109,8 @@ private: GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread) : QWidget(parent), child(nullptr), emu_thread(emu_thread) { - std::string window_title = fmt::format("Citra {} | {}-{}", Common::g_build_name, - Common::g_scm_branch, Common::g_scm_desc); - setWindowTitle(QString::fromStdString(window_title)); + setWindowTitle(QStringLiteral("Citra %1 | %2-%3") + .arg(Common::g_build_name, Common::g_scm_branch, Common::g_scm_desc)); setAttribute(Qt::WA_AcceptTouchEvents); InputCommon::Init(); diff --git a/src/citra_qt/compatdb.cpp b/src/citra_qt/compatdb.cpp index d6712338c..17dd14310 100644 --- a/src/citra_qt/compatdb.cpp +++ b/src/citra_qt/compatdb.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include "citra_qt/compatdb.h" #include "common/telemetry.h" #include "core/core.h" @@ -21,6 +22,8 @@ CompatDB::CompatDB(QWidget* parent) connect(ui->radioButton_IntroMenu, &QRadioButton::clicked, this, &CompatDB::EnableNext); connect(ui->radioButton_WontBoot, &QRadioButton::clicked, this, &CompatDB::EnableNext); connect(button(NextButton), &QPushButton::clicked, this, &CompatDB::Submit); + connect(&testcase_watcher, &QFutureWatcher::finished, this, + &CompatDB::OnTestcaseSubmitted); } CompatDB::~CompatDB() = default; @@ -46,18 +49,38 @@ void CompatDB::Submit() { } break; case CompatDBPage::Final: + back(); LOG_DEBUG(Frontend, "Compatibility Rating: {}", compatibility->checkedId()); Core::Telemetry().AddField(Telemetry::FieldType::UserFeedback, "Compatibility", compatibility->checkedId()); - // older versions of QT don't support the "NoCancelButtonOnLastPage" option, this is a - // workaround + + button(NextButton)->setEnabled(false); + button(NextButton)->setText(tr("Submitting")); button(QWizard::CancelButton)->setVisible(false); + + testcase_watcher.setFuture(QtConcurrent::run( + [this]() { return Core::System::GetInstance().TelemetrySession().SubmitTestcase(); })); break; default: LOG_ERROR(Frontend, "Unexpected page: {}", currentId()); } } +void CompatDB::OnTestcaseSubmitted() { + if (!testcase_watcher.result()) { + QMessageBox::critical(this, tr("Communication error"), + tr("An error occured while sending the Testcase")); + button(NextButton)->setEnabled(true); + button(NextButton)->setText(tr("Next")); + button(QWizard::CancelButton)->setVisible(true); + } else { + next(); + // older versions of QT don't support the "NoCancelButtonOnLastPage" option, this is a + // workaround + button(QWizard::CancelButton)->setVisible(false); + } +} + void CompatDB::EnableNext() { button(NextButton)->setEnabled(true); } diff --git a/src/citra_qt/compatdb.h b/src/citra_qt/compatdb.h index ca0dd11d6..5381f67f7 100644 --- a/src/citra_qt/compatdb.h +++ b/src/citra_qt/compatdb.h @@ -5,6 +5,7 @@ #pragma once #include +#include #include namespace Ui { @@ -19,8 +20,11 @@ public: ~CompatDB(); private: + QFutureWatcher testcase_watcher; + std::unique_ptr ui; void Submit(); + void OnTestcaseSubmitted(); void EnableNext(); }; diff --git a/src/citra_qt/compatibility_list.cpp b/src/citra_qt/compatibility_list.cpp new file mode 100644 index 000000000..92e729dee --- /dev/null +++ b/src/citra_qt/compatibility_list.cpp @@ -0,0 +1,16 @@ +// Copyright 2018 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include +#include +#include "citra_qt/compatibility_list.h" + +CompatibilityList::const_iterator FindMatchingCompatibilityEntry( + const CompatibilityList& compatibility_list, u64 program_id) { + return std::find_if(compatibility_list.begin(), compatibility_list.end(), + [program_id](const auto& element) { + std::string pid = fmt::format("{:016X}", program_id); + return element.first == pid; + }); +} diff --git a/src/citra_qt/compatibility_list.h b/src/citra_qt/compatibility_list.h new file mode 100644 index 000000000..df2dbd60a --- /dev/null +++ b/src/citra_qt/compatibility_list.h @@ -0,0 +1,15 @@ +// Copyright 2018 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include +#include +#include +#include "common/common_types.h" + +using CompatibilityList = std::unordered_map>; + +CompatibilityList::const_iterator FindMatchingCompatibilityEntry( + const CompatibilityList& compatibility_list, u64 program_id); diff --git a/src/citra_qt/configuration/config.cpp b/src/citra_qt/configuration/config.cpp index a7c284471..3004fa936 100644 --- a/src/citra_qt/configuration/config.cpp +++ b/src/citra_qt/configuration/config.cpp @@ -16,11 +16,16 @@ Config::Config() { // TODO: Don't hardcode the path; let the frontend decide where to put the config files. qt_config_loc = FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) + "qt-config.ini"; FileUtil::CreateFullPath(qt_config_loc); - qt_config = new QSettings(QString::fromStdString(qt_config_loc), QSettings::IniFormat); + qt_config = + std::make_unique(QString::fromStdString(qt_config_loc), QSettings::IniFormat); Reload(); } +Config::~Config() { + Save(); +} + const std::array Config::default_buttons = { Qt::Key_A, Qt::Key_S, Qt::Key_Z, Qt::Key_X, Qt::Key_T, Qt::Key_G, Qt::Key_F, Qt::Key_H, Qt::Key_Q, Qt::Key_W, Qt::Key_M, Qt::Key_N, Qt::Key_1, Qt::Key_2, Qt::Key_B, @@ -561,9 +566,3 @@ void Config::Reload() { void Config::Save() { SaveValues(); } - -Config::~Config() { - Save(); - - delete qt_config; -} diff --git a/src/citra_qt/configuration/config.h b/src/citra_qt/configuration/config.h index 502b1a8c0..fdb161c23 100644 --- a/src/citra_qt/configuration/config.h +++ b/src/citra_qt/configuration/config.h @@ -5,6 +5,7 @@ #pragma once #include +#include #include #include #include "core/settings.h" @@ -12,16 +13,6 @@ class QSettings; class Config { - QSettings* qt_config; - std::string qt_config_loc; - - void ReadValues(); - void SaveValues(); - QVariant ReadSetting(const QString& name); - QVariant ReadSetting(const QString& name, const QVariant& default_value); - void WriteSetting(const QString& name, const QVariant& value); - void WriteSetting(const QString& name, const QVariant& value, const QVariant& default_value); - public: Config(); ~Config(); @@ -31,4 +22,15 @@ public: static const std::array default_buttons; static const std::array, Settings::NativeAnalog::NumAnalogs> default_analogs; + +private: + void ReadValues(); + void SaveValues(); + QVariant ReadSetting(const QString& name); + QVariant ReadSetting(const QString& name, const QVariant& default_value); + void WriteSetting(const QString& name, const QVariant& value); + void WriteSetting(const QString& name, const QVariant& value, const QVariant& default_value); + + std::unique_ptr qt_config; + std::string qt_config_loc; }; diff --git a/src/citra_qt/configuration/configure_general.cpp b/src/citra_qt/configuration/configure_general.cpp index 3153ff8c8..cd47b135e 100644 --- a/src/citra_qt/configuration/configure_general.cpp +++ b/src/citra_qt/configuration/configure_general.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include #include "citra_qt/configuration/configure_general.h" #include "citra_qt/ui_settings.h" #include "core/core.h" @@ -15,6 +16,8 @@ ConfigureGeneral::ConfigureGeneral(QWidget* parent) this->setConfiguration(); ui->updateBox->setVisible(UISettings::values.updater_found); + connect(ui->button_reset_defaults, &QPushButton::clicked, this, + &ConfigureGeneral::ResetDefaults); } ConfigureGeneral::~ConfigureGeneral() = default; @@ -33,6 +36,19 @@ void ConfigureGeneral::PopulateHotkeyList(const HotkeyRegistry& registry) { ui->hotkeysDialog->Populate(registry); } +void ConfigureGeneral::ResetDefaults() { + QMessageBox::StandardButton answer = QMessageBox::question( + this, tr("Citra"), + tr("Are you sure you want to reset your settings and close Citra?"), + QMessageBox::Yes | QMessageBox::No, QMessageBox::No); + + if (answer == QMessageBox::No) + return; + + FileUtil::Delete(FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) + "qt-config.ini"); + std::exit(0); +} + void ConfigureGeneral::applyConfiguration() { UISettings::values.confirm_before_closing = ui->toggle_check_exit->isChecked(); diff --git a/src/citra_qt/configuration/configure_general.h b/src/citra_qt/configuration/configure_general.h index 9e67589bf..ff99c9510 100644 --- a/src/citra_qt/configuration/configure_general.h +++ b/src/citra_qt/configuration/configure_general.h @@ -21,6 +21,7 @@ public: ~ConfigureGeneral(); void PopulateHotkeyList(const HotkeyRegistry& registry); + void ResetDefaults(); void applyConfiguration(); void retranslateUi(); diff --git a/src/citra_qt/configuration/configure_general.ui b/src/citra_qt/configuration/configure_general.ui index 103ac54d3..5979dd95b 100644 --- a/src/citra_qt/configuration/configure_general.ui +++ b/src/citra_qt/configuration/configure_general.ui @@ -147,6 +147,13 @@ + + + + Reset All Settings + + + diff --git a/src/citra_qt/configuration/configure_system.cpp b/src/citra_qt/configuration/configure_system.cpp index 475699b86..4239aea76 100644 --- a/src/citra_qt/configuration/configure_system.cpp +++ b/src/citra_qt/configuration/configure_system.cpp @@ -220,12 +220,12 @@ ConfigureSystem::ConfigureSystem(QWidget* parent) : QWidget(parent), ui(new Ui:: ui->setupUi(this); connect(ui->combo_birthmonth, static_cast(&QComboBox::currentIndexChanged), this, - &ConfigureSystem::updateBirthdayComboBox); + &ConfigureSystem::UpdateBirthdayComboBox); connect(ui->combo_init_clock, static_cast(&QComboBox::currentIndexChanged), this, - &ConfigureSystem::updateInitTime); + &ConfigureSystem::UpdateInitTime); connect(ui->button_regenerate_console_id, &QPushButton::clicked, this, - &ConfigureSystem::refreshConsoleID); + &ConfigureSystem::RefreshConsoleID); for (u8 i = 0; i < country_names.size(); i++) { if (country_names.at(i) != "") { ui->combo_country->addItem(tr(country_names.at(i)), i); @@ -270,7 +270,7 @@ void ConfigureSystem::ReadSystemSettings() { // set birthday std::tie(birthmonth, birthday) = cfg->GetBirthday(); ui->combo_birthmonth->setCurrentIndex(birthmonth - 1); - updateBirthdayComboBox( + UpdateBirthdayComboBox( birthmonth - 1); // explicitly update it because the signal from setCurrentIndex is not reliable ui->combo_birthday->setCurrentIndex(birthday - 1); @@ -358,7 +358,7 @@ void ConfigureSystem::applyConfiguration() { Settings::Apply(); } -void ConfigureSystem::updateBirthdayComboBox(int birthmonth_index) { +void ConfigureSystem::UpdateBirthdayComboBox(int birthmonth_index) { if (birthmonth_index < 0 || birthmonth_index >= 12) return; @@ -391,17 +391,17 @@ void ConfigureSystem::ConfigureTime() { this->setConfiguration(); - updateInitTime(ui->combo_init_clock->currentIndex()); + UpdateInitTime(ui->combo_init_clock->currentIndex()); } -void ConfigureSystem::updateInitTime(int init_clock) { +void ConfigureSystem::UpdateInitTime(int init_clock) { const bool is_fixed_time = static_cast(init_clock) == Settings::InitClock::FixedTime; ui->label_init_time->setVisible(is_fixed_time); ui->edit_init_time->setVisible(is_fixed_time); } -void ConfigureSystem::refreshConsoleID() { +void ConfigureSystem::RefreshConsoleID() { QMessageBox::StandardButton reply; QString warning_text = tr("This will replace your current virtual 3DS with a new one. " "Your current virtual 3DS will not be recoverable. " diff --git a/src/citra_qt/configuration/configure_system.h b/src/citra_qt/configuration/configure_system.h index 36c7d2e94..f84457bd6 100644 --- a/src/citra_qt/configuration/configure_system.h +++ b/src/citra_qt/configuration/configure_system.h @@ -23,29 +23,29 @@ class ConfigureSystem : public QWidget { public: explicit ConfigureSystem(QWidget* parent = nullptr); - ~ConfigureSystem(); + ~ConfigureSystem() override; void applyConfiguration(); void setConfiguration(); void retranslateUi(); -public slots: - void updateBirthdayComboBox(int birthmonth_index); - void updateInitTime(int init_clock); - void refreshConsoleID(); - private: void ReadSystemSettings(); void ConfigureTime(); + void UpdateBirthdayComboBox(int birthmonth_index); + void UpdateInitTime(int init_clock); + void RefreshConsoleID(); + std::unique_ptr ui; - bool enabled; + bool enabled = false; std::shared_ptr cfg; std::u16string username; - int birthmonth, birthday; - int language_index; - int sound_index; + int birthmonth = 0; + int birthday = 0; + int language_index = 0; + int sound_index = 0; u8 country_code; u16 play_coin; }; diff --git a/src/citra_qt/configuration/configure_system.ui b/src/citra_qt/configuration/configure_system.ui index 74efa3b27..51ad7c8ca 100644 --- a/src/citra_qt/configuration/configure_system.ui +++ b/src/citra_qt/configuration/configure_system.ui @@ -261,6 +261,9 @@ + + yyyy-MM-ddTHH:mm:ss + diff --git a/src/citra_qt/debugger/graphics/graphics_breakpoints.cpp b/src/citra_qt/debugger/graphics/graphics_breakpoints.cpp index d3d32997c..282b3e8de 100644 --- a/src/citra_qt/debugger/graphics/graphics_breakpoints.cpp +++ b/src/citra_qt/debugger/graphics/graphics_breakpoints.cpp @@ -30,23 +30,8 @@ QVariant BreakPointModel::data(const QModelIndex& index, int role) const { switch (role) { case Qt::DisplayRole: { if (index.column() == 0) { - static const std::map map = { - {Pica::DebugContext::Event::PicaCommandLoaded, tr("Pica command loaded")}, - {Pica::DebugContext::Event::PicaCommandProcessed, tr("Pica command processed")}, - {Pica::DebugContext::Event::IncomingPrimitiveBatch, tr("Incoming primitive batch")}, - {Pica::DebugContext::Event::FinishedPrimitiveBatch, tr("Finished primitive batch")}, - {Pica::DebugContext::Event::VertexShaderInvocation, tr("Vertex shader invocation")}, - {Pica::DebugContext::Event::IncomingDisplayTransfer, - tr("Incoming display transfer")}, - {Pica::DebugContext::Event::GSPCommandProcessed, tr("GSP command processed")}, - {Pica::DebugContext::Event::BufferSwapped, tr("Buffers swapped")}, - }; - - DEBUG_ASSERT(map.size() == - static_cast(Pica::DebugContext::Event::NumEvents)); - return (map.find(event) != map.end()) ? map.at(event) : QString(); + return DebugContextEventToString(event); } - break; } @@ -128,6 +113,30 @@ void BreakPointModel::OnResumed() { active_breakpoint = context->active_breakpoint; } +QString BreakPointModel::DebugContextEventToString(Pica::DebugContext::Event event) { + switch (event) { + case Pica::DebugContext::Event::PicaCommandLoaded: + return tr("Pica command loaded"); + case Pica::DebugContext::Event::PicaCommandProcessed: + return tr("Pica command processed"); + case Pica::DebugContext::Event::IncomingPrimitiveBatch: + return tr("Incoming primitive batch"); + case Pica::DebugContext::Event::FinishedPrimitiveBatch: + return tr("Finished primitive batch"); + case Pica::DebugContext::Event::VertexShaderInvocation: + return tr("Vertex shader invocation"); + case Pica::DebugContext::Event::IncomingDisplayTransfer: + return tr("Incoming display transfer"); + case Pica::DebugContext::Event::GSPCommandProcessed: + return tr("GSP command processed"); + case Pica::DebugContext::Event::BufferSwapped: + return tr("Buffers swapped"); + case Pica::DebugContext::Event::NumEvents: + break; + } + return tr("Unknown debug context event"); +} + GraphicsBreakPointsWidget::GraphicsBreakPointsWidget( std::shared_ptr debug_context, QWidget* parent) : QDockWidget(tr("Pica Breakpoints"), parent), Pica::DebugContext::BreakPointObserver( diff --git a/src/citra_qt/debugger/graphics/graphics_breakpoints_p.h b/src/citra_qt/debugger/graphics/graphics_breakpoints_p.h index e4711dadc..3a53c06a0 100644 --- a/src/citra_qt/debugger/graphics/graphics_breakpoints_p.h +++ b/src/citra_qt/debugger/graphics/graphics_breakpoints_p.h @@ -29,6 +29,8 @@ public: void OnResumed(); private: + static QString DebugContextEventToString(Pica::DebugContext::Event event); + std::weak_ptr context_weak; bool at_breakpoint; Pica::DebugContext::Event active_breakpoint; diff --git a/src/citra_qt/debugger/wait_tree.cpp b/src/citra_qt/debugger/wait_tree.cpp index d006128e5..0758cc792 100644 --- a/src/citra_qt/debugger/wait_tree.cpp +++ b/src/citra_qt/debugger/wait_tree.cpp @@ -51,7 +51,7 @@ std::size_t WaitTreeItem::Row() const { } std::vector> WaitTreeItem::MakeThreadItemList() { - const auto& threads = Kernel::GetThreadList(); + const auto& threads = Core::System::GetInstance().Kernel().GetThreadManager().GetThreadList(); std::vector> item_list; item_list.reserve(threads.size()); for (std::size_t i = 0; i < threads.size(); ++i) { diff --git a/src/citra_qt/game_list.cpp b/src/citra_qt/game_list.cpp index f736c21df..eab2fba15 100644 --- a/src/citra_qt/game_list.cpp +++ b/src/citra_qt/game_list.cpp @@ -21,8 +21,10 @@ #include #include #include +#include "citra_qt/compatibility_list.h" #include "citra_qt/game_list.h" #include "citra_qt/game_list_p.h" +#include "citra_qt/game_list_worker.h" #include "citra_qt/main.h" #include "citra_qt/ui_settings.h" #include "common/common_paths.h" @@ -30,7 +32,6 @@ #include "core/file_sys/archive_extsavedata.h" #include "core/file_sys/archive_source_sd_savedata.h" #include "core/hle/service/fs/archive.h" -#include "core/loader/loader.h" GameListSearchField::KeyReleaseEater::KeyReleaseEater(GameList* gamelist) : gamelist{gamelist} {} @@ -288,11 +289,11 @@ GameList::GameList(GMainWindow* parent) : QWidget{parent} { tree_view->setContextMenuPolicy(Qt::CustomContextMenu); item_model->insertColumns(0, COLUMN_COUNT); - item_model->setHeaderData(COLUMN_NAME, Qt::Horizontal, "Name"); - item_model->setHeaderData(COLUMN_COMPATIBILITY, Qt::Horizontal, "Compatibility"); - item_model->setHeaderData(COLUMN_REGION, Qt::Horizontal, "Region"); - item_model->setHeaderData(COLUMN_FILE_TYPE, Qt::Horizontal, "File type"); - item_model->setHeaderData(COLUMN_SIZE, Qt::Horizontal, "Size"); + item_model->setHeaderData(COLUMN_NAME, Qt::Horizontal, tr("Name")); + item_model->setHeaderData(COLUMN_COMPATIBILITY, Qt::Horizontal, tr("Compatibility")); + item_model->setHeaderData(COLUMN_REGION, Qt::Horizontal, tr("Region")); + item_model->setHeaderData(COLUMN_FILE_TYPE, Qt::Horizontal, tr("File type")); + item_model->setHeaderData(COLUMN_SIZE, Qt::Horizontal, tr("Size")); item_model->setSortRole(GameListItemPath::TitleRole); connect(main_window, &GMainWindow::UpdateThemedIcons, this, &GameList::onUpdateThemedIcons); @@ -648,11 +649,6 @@ void GameList::LoadInterfaceLayout() { const QStringList GameList::supported_file_extensions = {"3ds", "3dsx", "elf", "axf", "cci", "cxi", "app"}; -static bool HasSupportedFileExtension(const std::string& file_name) { - QFileInfo file = QFileInfo(QString::fromStdString(file_name)); - return GameList::supported_file_extensions.contains(file.suffix(), Qt::CaseInsensitive); -} - void GameList::RefreshGameDirectory() { if (!UISettings::values.game_dirs.isEmpty() && current_worker != nullptr) { LOG_INFO(Frontend, "Change detected in the games directory. Reloading game list."); @@ -678,123 +674,6 @@ QString GameList::FindGameByProgramID(QStandardItem* current_item, u64 program_i return ""; } -void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsigned int recursion, - GameListDir* parent_dir) { - const auto callback = [this, recursion, parent_dir](u64* num_entries_out, - const std::string& directory, - const std::string& virtual_name) -> bool { - std::string physical_name = directory + DIR_SEP + virtual_name; - - if (stop_processing) - return false; // Breaks the callback loop. - - bool is_dir = FileUtil::IsDirectory(physical_name); - if (!is_dir && HasSupportedFileExtension(physical_name)) { - std::unique_ptr loader = Loader::GetLoader(physical_name); - if (!loader) - return true; - - u64 program_id = 0; - loader->ReadProgramId(program_id); - - u64 extdata_id = 0; - loader->ReadExtdataId(extdata_id); - - std::vector smdh = [program_id, &loader]() -> std::vector { - std::vector original_smdh; - loader->ReadIcon(original_smdh); - - if (program_id < 0x0004000000000000 || program_id > 0x00040000FFFFFFFF) - return original_smdh; - - std::string update_path = Service::AM::GetTitleContentPath( - Service::FS::MediaType::SDMC, program_id + 0x0000000E00000000); - - if (!FileUtil::Exists(update_path)) - return original_smdh; - - std::unique_ptr update_loader = Loader::GetLoader(update_path); - - if (!update_loader) - return original_smdh; - - std::vector update_smdh; - update_loader->ReadIcon(update_smdh); - return update_smdh; - }(); - - if (!Loader::IsValidSMDH(smdh) && UISettings::values.game_list_hide_no_icon) { - // Skip this invalid entry - return true; - } - - auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id); - - // The game list uses this as compatibility number for untested games - QString compatibility("99"); - if (it != compatibility_list.end()) - compatibility = it->second.first; - - emit EntryReady( - { - new GameListItemPath(QString::fromStdString(physical_name), smdh, program_id, - extdata_id), - new GameListItemCompat(compatibility), - new GameListItemRegion(smdh), - new GameListItem( - QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))), - new GameListItemSize(FileUtil::GetSize(physical_name)), - }, - parent_dir); - - } else if (is_dir && recursion > 0) { - watch_list.append(QString::fromStdString(physical_name)); - AddFstEntriesToGameList(physical_name, recursion - 1, parent_dir); - } - - return true; - }; - - FileUtil::ForeachDirectoryEntry(nullptr, dir_path, callback); -} - -void GameListWorker::run() { - stop_processing = false; - for (UISettings::GameDir& game_dir : game_dirs) { - if (game_dir.path == "INSTALLED") { - QString path = - QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir)) + - "Nintendo " - "3DS/00000000000000000000000000000000/" - "00000000000000000000000000000000/title/00040000"; - watch_list.append(path); - GameListDir* game_list_dir = new GameListDir(game_dir, GameListItemType::InstalledDir); - emit DirEntryReady({game_list_dir}); - AddFstEntriesToGameList(path.toStdString(), 2, game_list_dir); - } else if (game_dir.path == "SYSTEM") { - QString path = - QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir)) + - "00000000000000000000000000000000/title/00040010"; - watch_list.append(path); - GameListDir* game_list_dir = new GameListDir(game_dir, GameListItemType::SystemDir); - emit DirEntryReady({game_list_dir}); - AddFstEntriesToGameList(path.toStdString(), 2, game_list_dir); - } else { - watch_list.append(game_dir.path); - GameListDir* game_list_dir = new GameListDir(game_dir); - emit DirEntryReady({game_list_dir}); - AddFstEntriesToGameList(game_dir.path.toStdString(), game_dir.deep_scan ? 256 : 0, - game_list_dir); - } - }; - emit Finished(watch_list); -} - -void GameListWorker::Cancel() { - this->disconnect(); - stop_processing = true; -} - GameListPlaceholder::GameListPlaceholder(GMainWindow* parent) : QWidget{parent} { this->main_window = parent; diff --git a/src/citra_qt/game_list.h b/src/citra_qt/game_list.h index c355ecc1e..a10d7fe15 100644 --- a/src/citra_qt/game_list.h +++ b/src/citra_qt/game_list.h @@ -4,9 +4,10 @@ #pragma once -#include +#include #include #include +#include "citra_qt/compatibility_list.h" #include "common/common_types.h" #include "ui_settings.h" @@ -70,9 +71,8 @@ signals: void GameChosen(QString game_path); void ShouldCancelWorker(); void OpenFolderRequested(u64 program_id, GameListOpenTarget target); - void NavigateToGamedbEntryRequested( - u64 program_id, - std::unordered_map>& compatibility_list); + void NavigateToGamedbEntryRequested(u64 program_id, + const CompatibilityList& compatibility_list); void OpenDirectory(QString directory); void AddDirectory(); void ShowList(bool show); @@ -103,7 +103,7 @@ private: QStandardItemModel* item_model = nullptr; GameListWorker* current_worker = nullptr; QFileSystemWatcher* watcher = nullptr; - std::unordered_map> compatibility_list; + CompatibilityList compatibility_list; friend class GameListSearchField; }; diff --git a/src/citra_qt/game_list_p.h b/src/citra_qt/game_list_p.h index cc8b92851..b3f473397 100644 --- a/src/citra_qt/game_list_p.h +++ b/src/citra_qt/game_list_p.h @@ -4,7 +4,6 @@ #pragma once -#include #include #include #include @@ -59,17 +58,6 @@ static QPixmap GetDefaultIcon(bool large) { return icon; } -static auto FindMatchingCompatibilityEntry( - const std::unordered_map>& compatibility_list, - u64 program_id) { - return std::find_if( - compatibility_list.begin(), compatibility_list.end(), - [program_id](const std::pair>& element) { - std::string pid = fmt::format("{:016X}", program_id); - return element.first == pid; - }); -} - /** * Gets the short game title from SMDH data. * @param smdh SMDH data @@ -216,7 +204,7 @@ class GameListItemCompat : public GameListItem { public: static const int CompatNumberRole = SortRole; GameListItemCompat() = default; - explicit GameListItemCompat(const QString& compatiblity) { + explicit GameListItemCompat(const QString& compatibility) { setData(type(), TypeRole); struct CompatStatus { @@ -235,13 +223,13 @@ public: {"99", {"#000000", QT_TR_NOOP("Not Tested"), QT_TR_NOOP("The game has not yet been tested.")}}}; // clang-format on - auto iterator = status_data.find(compatiblity); + auto iterator = status_data.find(compatibility); if (iterator == status_data.end()) { - LOG_WARNING(Frontend, "Invalid compatibility number {}", compatiblity.toStdString()); + LOG_WARNING(Frontend, "Invalid compatibility number {}", compatibility.toStdString()); return; } - CompatStatus status = iterator->second; - setData(compatiblity, CompatNumberRole); + const CompatStatus& status = iterator->second; + setData(compatibility, CompatNumberRole); setText(QObject::tr(status.text)); setToolTip(QObject::tr(status.tooltip)); setData(CreateCirclePixmapFromColor(status.color), Qt::DecorationRole); @@ -373,51 +361,6 @@ public: } }; -/** - * Asynchronous worker object for populating the game list. - * Communicates with other threads through Qt's signal/slot system. - */ -class GameListWorker : public QObject, public QRunnable { - Q_OBJECT - -public: - explicit GameListWorker( - QList& game_dirs, - const std::unordered_map>& compatibility_list) - : game_dirs(game_dirs), compatibility_list(compatibility_list) {} - -public slots: - /// Starts the processing of directory tree information. - void run() override; - /// Tells the worker that it should no longer continue processing. Thread-safe. - void Cancel(); - -signals: - /** - * The `EntryReady` signal is emitted once an entry has been prepared and is ready - * to be added to the game list. - * @param entry_items a list with `QStandardItem`s that make up the columns of the new - * entry. - */ - void DirEntryReady(GameListDir* entry_items); - void EntryReady(QList entry_items, GameListDir* parent_dir); - - /** - * After the worker has traversed the game directory looking for entries, this signal is - * emitted with a list of folders that should be watched for changes as well. - */ - void Finished(QStringList watch_list); - -private: - QStringList watch_list; - const std::unordered_map>& compatibility_list; - QList& game_dirs; - std::atomic_bool stop_processing; - - void AddFstEntriesToGameList(const std::string& dir_path, unsigned int recursion, - GameListDir* parent_dir); -}; - class GameList; class QHBoxLayout; class QTreeView; diff --git a/src/citra_qt/game_list_worker.cpp b/src/citra_qt/game_list_worker.cpp new file mode 100644 index 000000000..7ec7c7677 --- /dev/null +++ b/src/citra_qt/game_list_worker.cpp @@ -0,0 +1,150 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include +#include +#include +#include +#include +#include +#include "citra_qt/compatibility_list.h" +#include "citra_qt/game_list.h" +#include "citra_qt/game_list_p.h" +#include "citra_qt/game_list_worker.h" +#include "citra_qt/ui_settings.h" +#include "common/common_paths.h" +#include "common/file_util.h" +#include "core/hle/service/am/am.h" +#include "core/hle/service/fs/archive.h" +#include "core/loader/loader.h" + +namespace { +bool HasSupportedFileExtension(const std::string& file_name) { + const QFileInfo file = QFileInfo(QString::fromStdString(file_name)); + return GameList::supported_file_extensions.contains(file.suffix(), Qt::CaseInsensitive); +} +} // Anonymous namespace + +GameListWorker::GameListWorker(QList& game_dirs, + const CompatibilityList& compatibility_list) + : game_dirs(game_dirs), compatibility_list(compatibility_list) {} + +GameListWorker::~GameListWorker() = default; + +void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsigned int recursion, + GameListDir* parent_dir) { + const auto callback = [this, recursion, parent_dir](u64* num_entries_out, + const std::string& directory, + const std::string& virtual_name) -> bool { + std::string physical_name = directory + DIR_SEP + virtual_name; + + if (stop_processing) + return false; // Breaks the callback loop. + + bool is_dir = FileUtil::IsDirectory(physical_name); + if (!is_dir && HasSupportedFileExtension(physical_name)) { + std::unique_ptr loader = Loader::GetLoader(physical_name); + if (!loader) + return true; + + u64 program_id = 0; + loader->ReadProgramId(program_id); + + u64 extdata_id = 0; + loader->ReadExtdataId(extdata_id); + + std::vector smdh = [program_id, &loader]() -> std::vector { + std::vector original_smdh; + loader->ReadIcon(original_smdh); + + if (program_id < 0x0004000000000000 || program_id > 0x00040000FFFFFFFF) + return original_smdh; + + std::string update_path = Service::AM::GetTitleContentPath( + Service::FS::MediaType::SDMC, program_id + 0x0000000E00000000); + + if (!FileUtil::Exists(update_path)) + return original_smdh; + + std::unique_ptr update_loader = Loader::GetLoader(update_path); + + if (!update_loader) + return original_smdh; + + std::vector update_smdh; + update_loader->ReadIcon(update_smdh); + return update_smdh; + }(); + + if (!Loader::IsValidSMDH(smdh) && UISettings::values.game_list_hide_no_icon) { + // Skip this invalid entry + return true; + } + + auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id); + + // The game list uses this as compatibility number for untested games + QString compatibility("99"); + if (it != compatibility_list.end()) + compatibility = it->second.first; + + emit EntryReady( + { + new GameListItemPath(QString::fromStdString(physical_name), smdh, program_id, + extdata_id), + new GameListItemCompat(compatibility), + new GameListItemRegion(smdh), + new GameListItem( + QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))), + new GameListItemSize(FileUtil::GetSize(physical_name)), + }, + parent_dir); + + } else if (is_dir && recursion > 0) { + watch_list.append(QString::fromStdString(physical_name)); + AddFstEntriesToGameList(physical_name, recursion - 1, parent_dir); + } + + return true; + }; + + FileUtil::ForeachDirectoryEntry(nullptr, dir_path, callback); +} + +void GameListWorker::run() { + stop_processing = false; + for (UISettings::GameDir& game_dir : game_dirs) { + if (game_dir.path == "INSTALLED") { + QString path = + QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir)) + + "Nintendo " + "3DS/00000000000000000000000000000000/" + "00000000000000000000000000000000/title/00040000"; + watch_list.append(path); + GameListDir* game_list_dir = new GameListDir(game_dir, GameListItemType::InstalledDir); + emit DirEntryReady({game_list_dir}); + AddFstEntriesToGameList(path.toStdString(), 2, game_list_dir); + } else if (game_dir.path == "SYSTEM") { + QString path = + QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir)) + + "00000000000000000000000000000000/title/00040010"; + watch_list.append(path); + GameListDir* game_list_dir = new GameListDir(game_dir, GameListItemType::SystemDir); + emit DirEntryReady({game_list_dir}); + AddFstEntriesToGameList(path.toStdString(), 2, game_list_dir); + } else { + watch_list.append(game_dir.path); + GameListDir* game_list_dir = new GameListDir(game_dir); + emit DirEntryReady({game_list_dir}); + AddFstEntriesToGameList(game_dir.path.toStdString(), game_dir.deep_scan ? 256 : 0, + game_list_dir); + } + }; + emit Finished(watch_list); +} + +void GameListWorker::Cancel() { + this->disconnect(); + stop_processing = true; +} diff --git a/src/citra_qt/game_list_worker.h b/src/citra_qt/game_list_worker.h new file mode 100644 index 000000000..a955e6376 --- /dev/null +++ b/src/citra_qt/game_list_worker.h @@ -0,0 +1,62 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "citra_qt/compatibility_list.h" +#include "common/common_types.h" + +class QStandardItem; + +/** + * Asynchronous worker object for populating the game list. + * Communicates with other threads through Qt's signal/slot system. + */ +class GameListWorker : public QObject, public QRunnable { + Q_OBJECT + +public: + GameListWorker(QList& game_dirs, + const CompatibilityList& compatibility_list); + ~GameListWorker() override; + + /// Starts the processing of directory tree information. + void run() override; + + /// Tells the worker that it should no longer continue processing. Thread-safe. + void Cancel(); + +signals: + /** + * The `EntryReady` signal is emitted once an entry has been prepared and is ready + * to be added to the game list. + * @param entry_items a list with `QStandardItem`s that make up the columns of the new entry. + */ + void DirEntryReady(GameListDir* entry_items); + void EntryReady(QList entry_items, GameListDir* parent_dir); + + /** + * After the worker has traversed the game directory looking for entries, this signal is emitted + * with a list of folders that should be watched for changes as well. + */ + void Finished(QStringList watch_list); + +private: + void AddFstEntriesToGameList(const std::string& dir_path, unsigned int recursion, + GameListDir* parent_dir); + + QStringList watch_list; + const CompatibilityList& compatibility_list; + QList& game_dirs; + std::atomic_bool stop_processing; +}; diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp index 669ced7f6..db5c3c2ee 100644 --- a/src/citra_qt/main.cpp +++ b/src/citra_qt/main.cpp @@ -21,6 +21,7 @@ #include "citra_qt/camera/qt_multimedia_camera.h" #include "citra_qt/camera/still_image_camera.h" #include "citra_qt/compatdb.h" +#include "citra_qt/compatibility_list.h" #include "citra_qt/configuration/config.h" #include "citra_qt/configuration/configure_dialog.h" #include "citra_qt/debugger/console.h" @@ -44,6 +45,7 @@ #include "citra_qt/util/clickable_label.h" #include "common/common_paths.h" #include "common/detached_tasks.h" +#include "common/file_util.h" #include "common/logging/backend.h" #include "common/logging/filter.h" #include "common/logging/log.h" @@ -57,6 +59,7 @@ #include "core/frontend/applets/default_applets.h" #include "core/gdbstub/gdbstub.h" #include "core/hle/service/fs/archive.h" +#include "core/hle/service/nfc/nfc.h" #include "core/loader/loader.h" #include "core/movie.h" #include "core/settings.h" @@ -341,8 +344,9 @@ void GMainWindow::InitializeHotkeys() { hotkey_registry.RegisterHotkey("Main Window", "Start Emulation"); hotkey_registry.RegisterHotkey("Main Window", "Continue/Pause", QKeySequence(Qt::Key_F4)); hotkey_registry.RegisterHotkey("Main Window", "Restart", QKeySequence(Qt::Key_F5)); - hotkey_registry.RegisterHotkey("Main Window", "Swap Screens", QKeySequence(tr("F9"))); - hotkey_registry.RegisterHotkey("Main Window", "Toggle Screen Layout", QKeySequence(tr("F10"))); + hotkey_registry.RegisterHotkey("Main Window", "Swap Screens", QKeySequence(Qt::Key_F9)); + hotkey_registry.RegisterHotkey("Main Window", "Toggle Screen Layout", + QKeySequence(Qt::Key_F10)); hotkey_registry.RegisterHotkey("Main Window", "Fullscreen", QKeySequence::FullScreen); hotkey_registry.RegisterHotkey("Main Window", "Exit Fullscreen", QKeySequence(Qt::Key_Escape), Qt::ApplicationShortcut); @@ -356,6 +360,11 @@ void GMainWindow::InitializeHotkeys() { Qt::ApplicationShortcut); hotkey_registry.RegisterHotkey("Main Window", "Advance Frame", QKeySequence(Qt::Key_Backslash), Qt::ApplicationShortcut); + hotkey_registry.RegisterHotkey("Main Window", "Load Amiibo", QKeySequence(Qt::Key_F2), + Qt::ApplicationShortcut); + hotkey_registry.RegisterHotkey("Main Window", "Remove Amiibo", QKeySequence(Qt::Key_F3), + Qt::ApplicationShortcut); + hotkey_registry.LoadHotkeys(); connect(hotkey_registry.GetHotkey("Main Window", "Load File", this), &QShortcut::activated, @@ -417,6 +426,18 @@ void GMainWindow::InitializeHotkeys() { &QShortcut::activated, ui.action_Enable_Frame_Advancing, &QAction::trigger); connect(hotkey_registry.GetHotkey("Main Window", "Advance Frame", this), &QShortcut::activated, ui.action_Advance_Frame, &QAction::trigger); + connect(hotkey_registry.GetHotkey("Main Window", "Load Amiibo", this), &QShortcut::activated, + this, [&] { + if (ui.action_Load_Amiibo->isEnabled()) { + OnLoadAmiibo(); + } + }); + connect(hotkey_registry.GetHotkey("Main Window", "Remove Amiibo", this), &QShortcut::activated, + this, [&] { + if (ui.action_Remove_Amiibo->isEnabled()) { + OnRemoveAmiibo(); + } + }); } void GMainWindow::ShowUpdaterWidgets() { @@ -495,6 +516,8 @@ void GMainWindow::ConnectMenuEvents() { connect(ui.action_Load_File, &QAction::triggered, this, &GMainWindow::OnMenuLoadFile); connect(ui.action_Install_CIA, &QAction::triggered, this, &GMainWindow::OnMenuInstallCIA); connect(ui.action_Exit, &QAction::triggered, this, &QMainWindow::close); + connect(ui.action_Load_Amiibo, &QAction::triggered, this, &GMainWindow::OnLoadAmiibo); + connect(ui.action_Remove_Amiibo, &QAction::triggered, this, &GMainWindow::OnRemoveAmiibo); // Emulation connect(ui.action_Start, &QAction::triggered, this, &GMainWindow::OnStartGame); @@ -667,12 +690,12 @@ bool GMainWindow::LoadROM(const QString& filename) { render_window->InitRenderTarget(); render_window->MakeCurrent(); - const char* below_gl33_title = "OpenGL 3.3 Unsupported"; - const char* below_gl33_message = "Your GPU may not support OpenGL 3.3, or you do not " - "have the latest graphics driver."; + const QString below_gl33_title = tr("OpenGL 3.3 Unsupported"); + const QString below_gl33_message = tr("Your GPU may not support OpenGL 3.3, or you do not " + "have the latest graphics driver."); if (!gladLoadGL()) { - QMessageBox::critical(this, tr(below_gl33_title), tr(below_gl33_message)); + QMessageBox::critical(this, below_gl33_title, below_gl33_message); return false; } @@ -743,7 +766,7 @@ bool GMainWindow::LoadROM(const QString& filename) { break; case Core::System::ResultStatus::ErrorVideoCore_ErrorBelowGL33: - QMessageBox::critical(this, tr(below_gl33_title), tr(below_gl33_message)); + QMessageBox::critical(this, below_gl33_title, below_gl33_message); break; default: @@ -847,6 +870,8 @@ void GMainWindow::ShutdownGame() { ui.action_Pause->setEnabled(false); ui.action_Stop->setEnabled(false); ui.action_Restart->setEnabled(false); + ui.action_Load_Amiibo->setEnabled(false); + ui.action_Remove_Amiibo->setEnabled(false); ui.action_Report_Compatibility->setEnabled(false); ui.action_Enable_Frame_Advancing->setEnabled(false); ui.action_Enable_Frame_Advancing->setChecked(false); @@ -960,14 +985,11 @@ void GMainWindow::OnGameListOpenFolder(u64 data_id, GameListOpenTarget target) { QDesktopServices::openUrl(QUrl::fromLocalFile(qpath)); } -void GMainWindow::OnGameListNavigateToGamedbEntry( - u64 program_id, - std::unordered_map>& compatibility_list) { - +void GMainWindow::OnGameListNavigateToGamedbEntry(u64 program_id, + const CompatibilityList& compatibility_list) { auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id); QString directory; - if (it != compatibility_list.end()) directory = it->second.second; @@ -1137,6 +1159,7 @@ void GMainWindow::OnStartGame() { ui.action_Pause->setEnabled(true); ui.action_Stop->setEnabled(true); ui.action_Restart->setEnabled(true); + ui.action_Load_Amiibo->setEnabled(true); ui.action_Report_Compatibility->setEnabled(true); ui.action_Enable_Frame_Advancing->setEnabled(true); @@ -1291,6 +1314,57 @@ void GMainWindow::OnConfigure() { } } +void GMainWindow::OnLoadAmiibo() { + const QString extensions{"*.bin"}; + const QString file_filter = tr("Amiibo File (%1);; All Files (*.*)").arg(extensions); + const QString filename = QFileDialog::getOpenFileName(this, tr("Load Amiibo"), "", file_filter); + + if (filename.isEmpty()) { + return; + } + + Core::System& system{Core::System::GetInstance()}; + Service::SM::ServiceManager& sm = system.ServiceManager(); + auto nfc = sm.GetService("nfc:u"); + if (nfc == nullptr) { + return; + } + + QFile nfc_file{filename}; + if (!nfc_file.open(QIODevice::ReadOnly)) { + QMessageBox::warning(this, tr("Error opening Amiibo data file"), + tr("Unable to open Amiibo file \"%1\" for reading.").arg(filename)); + return; + } + + Service::NFC::AmiiboData amiibo_data{}; + const u64 read_size = + nfc_file.read(reinterpret_cast(&amiibo_data), sizeof(Service::NFC::AmiiboData)); + if (read_size != sizeof(Service::NFC::AmiiboData)) { + QMessageBox::warning(this, tr("Error reading Amiibo data file"), + tr("Unable to fully read Amiibo data. Expected to read %1 bytes, but " + "was only able to read %2 bytes.") + .arg(sizeof(Service::NFC::AmiiboData)) + .arg(read_size)); + return; + } + + nfc->LoadAmiibo(amiibo_data); + ui.action_Remove_Amiibo->setEnabled(true); +} + +void GMainWindow::OnRemoveAmiibo() { + Core::System& system{Core::System::GetInstance()}; + Service::SM::ServiceManager& sm = system.ServiceManager(); + auto nfc = sm.GetService("nfc:u"); + if (nfc == nullptr) { + return; + } + + nfc->RemoveAmiibo(); + ui.action_Remove_Amiibo->setEnabled(false); +} + void GMainWindow::OnToggleFilterBar() { game_list->setFilterVisible(ui.action_Show_Filter_Bar->isChecked()); if (ui.action_Show_Filter_Bar->isChecked()) { diff --git a/src/citra_qt/main.h b/src/citra_qt/main.h index bbc0714c5..684a9e332 100644 --- a/src/citra_qt/main.h +++ b/src/citra_qt/main.h @@ -9,6 +9,7 @@ #include #include #include +#include "citra_qt/compatibility_list.h" #include "citra_qt/hotkeys.h" #include "common/announce_multiplayer_room.h" #include "core/core.h" @@ -153,9 +154,8 @@ private slots: /// Called whenever a user selects a game in the game list widget. void OnGameListLoadFile(QString game_path); void OnGameListOpenFolder(u64 program_id, GameListOpenTarget target); - void OnGameListNavigateToGamedbEntry( - u64 program_id, - std::unordered_map>& compatibility_list); + void OnGameListNavigateToGamedbEntry(u64 program_id, + const CompatibilityList& compatibility_list); void OnGameListOpenDirectory(QString path); void OnGameListAddDirectory(); void OnGameListShowList(bool show); @@ -166,6 +166,8 @@ private slots: void OnCIAInstallFinished(); void OnMenuRecentFile(); void OnConfigure(); + void OnLoadAmiibo(); + void OnRemoveAmiibo(); void OnToggleFilterBar(); void OnDisplayTitleBars(bool); void ToggleFullscreen(); diff --git a/src/citra_qt/main.ui b/src/citra_qt/main.ui index c2c58300d..77ac7e3ac 100644 --- a/src/citra_qt/main.ui +++ b/src/citra_qt/main.ui @@ -57,11 +57,20 @@ Recent Files + + + Amiibo + + + + + + @@ -73,6 +82,8 @@ + + @@ -139,8 +150,6 @@ - - @@ -415,6 +424,22 @@ Restart + + + false + + + Load... + + + + + false + + + Remove + + diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 7a1c5bc4c..fc196601a 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -65,8 +65,6 @@ add_library(common STATIC logging/text_formatter.cpp logging/text_formatter.h math_util.h - memory_util.cpp - memory_util.h microprofile.cpp microprofile.h microprofileui.h @@ -90,6 +88,7 @@ add_library(common STATIC timer.cpp timer.h vector_math.h + web_result.h ) if(ARCHITECTURE_x86_64) diff --git a/src/common/announce_multiplayer_room.h b/src/common/announce_multiplayer_room.h index 811f78d8c..5f9fc8ec2 100644 --- a/src/common/announce_multiplayer_room.h +++ b/src/common/announce_multiplayer_room.h @@ -9,23 +9,7 @@ #include #include #include "common/common_types.h" - -namespace Common { -struct WebResult { - enum class Code : u32 { - Success, - InvalidURL, - CredentialsMissing, - LibError, - HttpError, - WrongContent, - NoWebservice, - }; - Code result_code; - std::string result_string; - std::string returned_data; -}; -} // namespace Common +#include "common/web_result.h" namespace AnnounceMultiplayerRoom { diff --git a/src/common/common_paths.h b/src/common/common_paths.h index 1db4d7c3d..80aa4b5a0 100644 --- a/src/common/common_paths.h +++ b/src/common/common_paths.h @@ -51,3 +51,4 @@ #define SHARED_FONT "shared_font.bin" #define AES_KEYS "aes_keys.txt" #define BOOTROM9 "boot9.bin" +#define SECRET_SECTOR "sector0x96.bin" diff --git a/src/common/file_util.cpp b/src/common/file_util.cpp index 40ea885b8..b2f9b8d7e 100644 --- a/src/common/file_util.cpp +++ b/src/common/file_util.cpp @@ -14,21 +14,24 @@ #ifdef _WIN32 #include // windows.h needs to be included before other windows headers -#include // for GetSaveFileName -#include // getcwd +#include // getcwd #include #include #include // for SHGetFolderPath #include #include "common/string_util.h" -// 64 bit offsets for windows +#ifdef _MSC_VER +// 64 bit offsets for MSVC #define fseeko _fseeki64 #define ftello _ftelli64 -#define atoll _atoi64 +#define fileno _fileno +#endif + +// 64 bit offsets for MSVC and MinGW. MinGW also needs this for using _wstat64 #define stat _stat64 #define fstat _fstat64 -#define fileno _fileno + #else #ifdef __APPLE__ #include diff --git a/src/common/memory_util.cpp b/src/common/memory_util.cpp deleted file mode 100644 index f85ebca02..000000000 --- a/src/common/memory_util.cpp +++ /dev/null @@ -1,178 +0,0 @@ -// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include -#include "common/logging/log.h" -#include "common/memory_util.h" - -#ifdef _WIN32 -#include -// Windows.h needs to be included before psapi.h -#include -#include "common/common_funcs.h" -#include "common/string_util.h" -#else -#include -#include -#endif - -#if !defined(_WIN32) && defined(ARCHITECTURE_x86_64) && !defined(MAP_32BIT) -#include -#define PAGE_MASK (getpagesize() - 1) -#define round_page(x) ((((unsigned long)(x)) + PAGE_MASK) & ~(PAGE_MASK)) -#endif - -// This is purposely not a full wrapper for virtualalloc/mmap, but it -// provides exactly the primitive operations that Dolphin needs. - -void* AllocateExecutableMemory(std::size_t size, bool low) { -#if defined(_WIN32) - void* ptr = VirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE); -#else - static char* map_hint = nullptr; -#if defined(ARCHITECTURE_x86_64) && !defined(MAP_32BIT) - // This OS has no flag to enforce allocation below the 4 GB boundary, - // but if we hint that we want a low address it is very likely we will - // get one. - // An older version of this code used MAP_FIXED, but that has the side - // effect of discarding already mapped pages that happen to be in the - // requested virtual memory range (such as the emulated RAM, sometimes). - if (low && (!map_hint)) - map_hint = (char*)round_page(512 * 1024 * 1024); /* 0.5 GB rounded up to the next page */ -#endif - void* ptr = mmap(map_hint, size, PROT_READ | PROT_WRITE | PROT_EXEC, - MAP_ANON | MAP_PRIVATE -#if defined(ARCHITECTURE_x86_64) && defined(MAP_32BIT) - | (low ? MAP_32BIT : 0) -#endif - , - -1, 0); -#endif /* defined(_WIN32) */ - -#ifdef _WIN32 - if (ptr == nullptr) { -#else - if (ptr == MAP_FAILED) { - ptr = nullptr; -#endif - LOG_ERROR(Common_Memory, "Failed to allocate executable memory"); - } -#if !defined(_WIN32) && defined(ARCHITECTURE_x86_64) && !defined(MAP_32BIT) - else { - if (low) { - map_hint += size; - map_hint = (char*)round_page(map_hint); /* round up to the next page */ - } - } -#endif - -#if EMU_ARCH_BITS == 64 - if ((u64)ptr >= 0x80000000 && low == true) - LOG_ERROR(Common_Memory, "Executable memory ended up above 2GB!"); -#endif - - return ptr; -} - -void* AllocateMemoryPages(std::size_t size) { -#ifdef _WIN32 - void* ptr = VirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_READWRITE); -#else - void* ptr = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0); - - if (ptr == MAP_FAILED) - ptr = nullptr; -#endif - - if (ptr == nullptr) - LOG_ERROR(Common_Memory, "Failed to allocate raw memory"); - - return ptr; -} - -void* AllocateAlignedMemory(std::size_t size, std::size_t alignment) { -#ifdef _WIN32 - void* ptr = _aligned_malloc(size, alignment); -#else - void* ptr = nullptr; -#ifdef ANDROID - ptr = memalign(alignment, size); -#else - if (posix_memalign(&ptr, alignment, size) != 0) - LOG_ERROR(Common_Memory, "Failed to allocate aligned memory"); -#endif -#endif - - if (ptr == nullptr) - LOG_ERROR(Common_Memory, "Failed to allocate aligned memory"); - - return ptr; -} - -void FreeMemoryPages(void* ptr, std::size_t size) { - if (ptr) { -#ifdef _WIN32 - if (!VirtualFree(ptr, 0, MEM_RELEASE)) - LOG_ERROR(Common_Memory, "FreeMemoryPages failed!\n{}", GetLastErrorMsg()); -#else - munmap(ptr, size); -#endif - } -} - -void FreeAlignedMemory(void* ptr) { - if (ptr) { -#ifdef _WIN32 - _aligned_free(ptr); -#else - free(ptr); -#endif - } -} - -void WriteProtectMemory(void* ptr, std::size_t size, bool allowExecute) { -#ifdef _WIN32 - DWORD oldValue; - if (!VirtualProtect(ptr, size, allowExecute ? PAGE_EXECUTE_READ : PAGE_READONLY, &oldValue)) - LOG_ERROR(Common_Memory, "WriteProtectMemory failed!\n{}", GetLastErrorMsg()); -#else - mprotect(ptr, size, allowExecute ? (PROT_READ | PROT_EXEC) : PROT_READ); -#endif -} - -void UnWriteProtectMemory(void* ptr, std::size_t size, bool allowExecute) { -#ifdef _WIN32 - DWORD oldValue; - if (!VirtualProtect(ptr, size, allowExecute ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE, - &oldValue)) - LOG_ERROR(Common_Memory, "UnWriteProtectMemory failed!\n{}", GetLastErrorMsg()); -#else - mprotect(ptr, size, - allowExecute ? (PROT_READ | PROT_WRITE | PROT_EXEC) : PROT_WRITE | PROT_READ); -#endif -} - -std::string MemUsage() { -#ifdef _WIN32 -#pragma comment(lib, "psapi") - DWORD processID = GetCurrentProcessId(); - HANDLE hProcess; - PROCESS_MEMORY_COUNTERS pmc; - std::string Ret; - - // Print information about the memory usage of the process. - - hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processID); - if (nullptr == hProcess) - return "MemUsage Error"; - - if (GetProcessMemoryInfo(hProcess, &pmc, sizeof(pmc))) - Ret = fmt::format("{} K", Common::ThousandSeparate(pmc.WorkingSetSize / 1024, 7)); - - CloseHandle(hProcess); - return Ret; -#else - return ""; -#endif -} diff --git a/src/common/memory_util.h b/src/common/memory_util.h deleted file mode 100644 index aad071979..000000000 --- a/src/common/memory_util.h +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include -#include - -void* AllocateExecutableMemory(std::size_t size, bool low = true); -void* AllocateMemoryPages(std::size_t size); -void FreeMemoryPages(void* ptr, std::size_t size); -void* AllocateAlignedMemory(std::size_t size, std::size_t alignment); -void FreeAlignedMemory(void* ptr); -void WriteProtectMemory(void* ptr, std::size_t size, bool executable = false); -void UnWriteProtectMemory(void* ptr, std::size_t size, bool allowExecute = false); -std::string MemUsage(); - -inline int GetPageSize() { - return 4096; -} diff --git a/src/common/telemetry.h b/src/common/telemetry.h index 9439edde4..56dc54007 100644 --- a/src/common/telemetry.h +++ b/src/common/telemetry.h @@ -153,6 +153,7 @@ struct VisitorInterface : NonCopyable { /// Completion method, called once all fields have been visited virtual void Complete() = 0; + virtual bool SubmitTestcase() = 0; }; /** @@ -178,6 +179,9 @@ struct NullVisitor : public VisitorInterface { void Visit(const Field& /*field*/) override {} void Complete() override {} + bool SubmitTestcase() override { + return false; + } }; } // namespace Telemetry diff --git a/src/common/web_result.h b/src/common/web_result.h new file mode 100644 index 000000000..286c2b0f4 --- /dev/null +++ b/src/common/web_result.h @@ -0,0 +1,25 @@ +// Copyright 2018 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include +#include "common/common_types.h" + +namespace Common { +struct WebResult { + enum class Code : u32 { + Success, + InvalidURL, + CredentialsMissing, + LibError, + HttpError, + WrongContent, + NoWebservice, + }; + Code result_code; + std::string result_string; + std::string returned_data; +}; +} // namespace Common diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 2b9838a84..2ccdaea27 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -58,6 +58,7 @@ add_library(core STATIC file_sys/disk_archive.h file_sys/errors.h file_sys/file_backend.h + file_sys/delay_generator.cpp file_sys/delay_generator.h file_sys/ivfc_archive.cpp file_sys/ivfc_archive.h @@ -102,8 +103,6 @@ add_library(core STATIC hle/applets/mint.h hle/applets/swkbd.cpp hle/applets/swkbd.h - hle/config_mem.cpp - hle/config_mem.h hle/function_wrappers.h hle/ipc.h hle/ipc_helpers.h @@ -113,6 +112,8 @@ add_library(core STATIC hle/kernel/client_port.h hle/kernel/client_session.cpp hle/kernel/client_session.h + hle/kernel/config_mem.cpp + hle/kernel/config_mem.h hle/kernel/errors.h hle/kernel/event.cpp hle/kernel/event.h @@ -143,6 +144,8 @@ add_library(core STATIC hle/kernel/session.h hle/kernel/shared_memory.cpp hle/kernel/shared_memory.h + hle/kernel/shared_page.cpp + hle/kernel/shared_page.h hle/kernel/svc.cpp hle/kernel/svc.h hle/kernel/thread.cpp @@ -385,8 +388,6 @@ add_library(core STATIC hle/service/ssl_c.h hle/service/y2r_u.cpp hle/service/y2r_u.h - hle/shared_page.cpp - hle/shared_page.h hw/aes/arithmetic128.cpp hw/aes/arithmetic128.h hw/aes/ccm.cpp @@ -446,7 +447,7 @@ target_link_libraries(core PUBLIC common PRIVATE audio_core network video_core) target_link_libraries(core PUBLIC Boost::boost PRIVATE cryptopp fmt open_source_archives) if (ENABLE_WEB_SERVICE) target_compile_definitions(core PRIVATE -DENABLE_WEB_SERVICE) - target_link_libraries(core PRIVATE json-headers web_service) + target_link_libraries(core PRIVATE web_service) endif() if (ARCHITECTURE_x86_64) diff --git a/src/core/arm/dynarmic/arm_dynarmic.cpp b/src/core/arm/dynarmic/arm_dynarmic.cpp index 3cfd2a6d6..e3965f2fe 100644 --- a/src/core/arm/dynarmic/arm_dynarmic.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic.cpp @@ -71,7 +71,8 @@ private: class DynarmicUserCallbacks final : public Dynarmic::A32::UserCallbacks { public: - explicit DynarmicUserCallbacks(ARM_Dynarmic& parent) : parent(parent) {} + explicit DynarmicUserCallbacks(ARM_Dynarmic& parent) + : parent(parent), timing(parent.system.CoreTiming()) {} ~DynarmicUserCallbacks() = default; std::uint8_t MemoryRead8(VAddr vaddr) override { @@ -134,7 +135,8 @@ public: if (GDBStub::IsConnected()) { parent.jit->HaltExecution(); parent.SetPC(pc); - Kernel::Thread* thread = Kernel::GetCurrentThread(); + Kernel::Thread* thread = + Core::System::GetInstance().Kernel().GetThreadManager().GetCurrentThread(); parent.SaveContext(thread->context); GDBStub::Break(); GDBStub::SendTrap(thread, 5); @@ -147,18 +149,19 @@ public: } void AddTicks(std::uint64_t ticks) override { - CoreTiming::AddTicks(ticks); + timing.AddTicks(ticks); } std::uint64_t GetTicksRemaining() override { - s64 ticks = CoreTiming::GetDowncount(); + s64 ticks = timing.GetDowncount(); return static_cast(ticks <= 0 ? 0 : ticks); } ARM_Dynarmic& parent; + Core::Timing& timing; }; -ARM_Dynarmic::ARM_Dynarmic(PrivilegeMode initial_mode) - : cb(std::make_unique(*this)) { +ARM_Dynarmic::ARM_Dynarmic(Core::System& system, PrivilegeMode initial_mode) + : system(system), cb(std::make_unique(*this)) { interpreter_state = std::make_shared(initial_mode); PageTableChanged(); } diff --git a/src/core/arm/dynarmic/arm_dynarmic.h b/src/core/arm/dynarmic/arm_dynarmic.h index 5a1f85e22..0a73c05ac 100644 --- a/src/core/arm/dynarmic/arm_dynarmic.h +++ b/src/core/arm/dynarmic/arm_dynarmic.h @@ -15,11 +15,15 @@ namespace Memory { struct PageTable; } // namespace Memory +namespace Core { +struct System; +} + class DynarmicUserCallbacks; class ARM_Dynarmic final : public ARM_Interface { public: - explicit ARM_Dynarmic(PrivilegeMode initial_mode); + ARM_Dynarmic(Core::System& system, PrivilegeMode initial_mode); ~ARM_Dynarmic(); void Run() override; @@ -50,6 +54,7 @@ public: private: friend class DynarmicUserCallbacks; + Core::System& system; std::unique_ptr cb; std::unique_ptr MakeJit(); diff --git a/src/core/arm/dyncom/arm_dyncom.cpp b/src/core/arm/dyncom/arm_dyncom.cpp index 3dc1aa2f0..d3113632c 100644 --- a/src/core/arm/dyncom/arm_dyncom.cpp +++ b/src/core/arm/dyncom/arm_dyncom.cpp @@ -75,7 +75,7 @@ ARM_DynCom::ARM_DynCom(PrivilegeMode initial_mode) { ARM_DynCom::~ARM_DynCom() {} void ARM_DynCom::Run() { - ExecuteInstructions(std::max(CoreTiming::GetDowncount(), 0)); + ExecuteInstructions(std::max(Core::System::GetInstance().CoreTiming().GetDowncount(), 0)); } void ARM_DynCom::Step() { @@ -146,7 +146,7 @@ void ARM_DynCom::SetCP15Register(CP15Register reg, u32 value) { void ARM_DynCom::ExecuteInstructions(u64 num_instructions) { state->NumInstrsToExecute = num_instructions; unsigned ticks_executed = InterpreterMainLoop(state.get()); - CoreTiming::AddTicks(ticks_executed); + Core::System::GetInstance().CoreTiming().AddTicks(ticks_executed); state->ServeBreak(); } diff --git a/src/core/arm/dyncom/arm_dyncom_interpreter.cpp b/src/core/arm/dyncom/arm_dyncom_interpreter.cpp index cd70b8da3..c6be38873 100644 --- a/src/core/arm/dyncom/arm_dyncom_interpreter.cpp +++ b/src/core/arm/dyncom/arm_dyncom_interpreter.cpp @@ -18,6 +18,7 @@ #include "core/arm/skyeye_common/armstate.h" #include "core/arm/skyeye_common/armsupp.h" #include "core/arm/skyeye_common/vfp/vfp.h" +#include "core/core.h" #include "core/core_timing.h" #include "core/gdbstub/gdbstub.h" #include "core/hle/kernel/svc.h" @@ -3859,7 +3860,7 @@ SUB_INST : { SWI_INST : { if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) { swi_inst* const inst_cream = (swi_inst*)inst_base->component; - CoreTiming::AddTicks(num_instrs); + Core::System::GetInstance().CoreTiming().AddTicks(num_instrs); cpu->NumInstrsToExecute = num_instrs >= cpu->NumInstrsToExecute ? 0 : cpu->NumInstrsToExecute - num_instrs; num_instrs = 0; diff --git a/src/core/arm/skyeye_common/armstate.cpp b/src/core/arm/skyeye_common/armstate.cpp index 88ad7f533..a28f5a87f 100644 --- a/src/core/arm/skyeye_common/armstate.cpp +++ b/src/core/arm/skyeye_common/armstate.cpp @@ -604,7 +604,8 @@ void ARMul_State::ServeBreak() { if (last_bkpt_hit) { Reg[15] = last_bkpt.address; } - Kernel::Thread* thread = Kernel::GetCurrentThread(); + Kernel::Thread* thread = + Core::System::GetInstance().Kernel().GetThreadManager().GetCurrentThread(); Core::CPU().SaveContext(thread->context); if (last_bkpt_hit || GDBStub::GetCpuStepFlag()) { last_bkpt_hit = false; diff --git a/src/core/core.cpp b/src/core/core.cpp index 4f7a5e908..52b27a6ca 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -59,13 +59,13 @@ System::ResultStatus System::RunLoop(bool tight_loop) { // If we don't have a currently active thread then don't execute instructions, // instead advance to the next event and try to yield to the next thread - if (Kernel::GetCurrentThread() == nullptr) { + if (kernel->GetThreadManager().GetCurrentThread() == nullptr) { LOG_TRACE(Core_ARM11, "Idling"); - CoreTiming::Idle(); - CoreTiming::Advance(); + timing->Idle(); + timing->Advance(); PrepareReschedule(); } else { - CoreTiming::Advance(); + timing->Advance(); if (tight_loop) { cpu_core->Run(); } else { @@ -126,7 +126,9 @@ System::ResultStatus System::Load(EmuWindow& emu_window, const std::string& file return init_result; } - const Loader::ResultStatus load_result{app_loader->Load(Kernel::g_current_process)}; + Kernel::SharedPtr process; + const Loader::ResultStatus load_result{app_loader->Load(process)}; + kernel->SetCurrentProcess(process); if (Loader::ResultStatus::Success != load_result) { LOG_CRITICAL(Core, "Failed to load ROM (Error {})!", static_cast(load_result)); System::Shutdown(); @@ -140,7 +142,7 @@ System::ResultStatus System::Load(EmuWindow& emu_window, const std::string& file return ResultStatus::ErrorLoader; } } - Memory::SetCurrentPageTable(&Kernel::g_current_process->vm_manager.page_table); + Memory::SetCurrentPageTable(&kernel->GetCurrentProcess()->vm_manager.page_table); status = ResultStatus::Success; m_emu_window = &emu_window; m_filepath = filepath; @@ -153,7 +155,7 @@ void System::PrepareReschedule() { } PerfStats::Results System::GetAndResetPerfStats() { - return perf_stats.GetAndResetStats(CoreTiming::GetGlobalTimeUs()); + return perf_stats.GetAndResetStats(timing->GetGlobalTimeUs()); } void System::Reschedule() { @@ -162,17 +164,17 @@ void System::Reschedule() { } reschedule_pending = false; - Kernel::Reschedule(); + kernel->GetThreadManager().Reschedule(); } System::ResultStatus System::Init(EmuWindow& emu_window, u32 system_mode) { LOG_DEBUG(HW_Memory, "initialized OK"); - CoreTiming::Init(); + timing = std::make_unique(); if (Settings::values.use_cpu_jit) { #ifdef ARCHITECTURE_x86_64 - cpu_core = std::make_unique(USER32MODE); + cpu_core = std::make_unique(*this, USER32MODE); #else cpu_core = std::make_unique(USER32MODE); LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available"); @@ -191,13 +193,12 @@ System::ResultStatus System::Init(EmuWindow& emu_window, u32 system_mode) { rpc_server = std::make_unique(); #endif - service_manager = std::make_shared(); - shared_page_handler = std::make_shared(); - archive_manager = std::make_unique(); + service_manager = std::make_shared(*this); + archive_manager = std::make_unique(*this); HW::Init(); - Kernel::Init(system_mode); - Service::Init(*this, service_manager); + kernel = std::make_unique(system_mode); + Service::Init(*this); GDBStub::Init(); ResultStatus result = VideoCore::Init(emu_window); @@ -230,6 +231,22 @@ const Service::FS::ArchiveManager& System::ArchiveManager() const { return *archive_manager; } +Kernel::KernelSystem& System::Kernel() { + return *kernel; +} + +const Kernel::KernelSystem& System::Kernel() const { + return *kernel; +} + +Timing& System::CoreTiming() { + return *timing; +} + +const Timing& System::CoreTiming() const { + return *timing; +} + void System::RegisterSoftwareKeyboard(std::shared_ptr swkbd) { registered_swkbd = std::move(swkbd); } @@ -247,8 +264,7 @@ void System::Shutdown() { // Shutdown emulation session GDBStub::Shutdown(); VideoCore::Shutdown(); - Service::Shutdown(); - Kernel::Shutdown(); + kernel.reset(); HW::Shutdown(); telemetry_session.reset(); #ifdef ENABLE_SCRIPTING @@ -257,7 +273,7 @@ void System::Shutdown() { service_manager.reset(); dsp_core.reset(); cpu_core.reset(); - CoreTiming::Shutdown(); + timing.reset(); app_loader.reset(); if (auto room_member = Network::GetRoomMember().lock()) { diff --git a/src/core/core.h b/src/core/core.h index cecd29896..af1291856 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -8,7 +8,6 @@ #include #include "common/common_types.h" #include "core/frontend/applets/swkbd.h" -#include "core/hle/shared_page.h" #include "core/loader/loader.h" #include "core/memory.h" #include "core/perf_stats.h" @@ -36,8 +35,14 @@ class ArchiveManager; } } // namespace Service +namespace Kernel { +class KernelSystem; +} + namespace Core { +class Timing; + class System { public: /** @@ -167,6 +172,18 @@ public: /// Gets a const reference to the archive manager const Service::FS::ArchiveManager& ArchiveManager() const; + /// Gets a reference to the kernel + Kernel::KernelSystem& Kernel(); + + /// Gets a const reference to the kernel + const Kernel::KernelSystem& Kernel() const; + + /// Gets a reference to the timing system + Timing& CoreTiming(); + + /// Gets a const reference to the timing system + const Timing& CoreTiming() const; + PerfStats perf_stats; FrameLimiter frame_limiter; @@ -193,10 +210,6 @@ public: return registered_swkbd; } - std::shared_ptr GetSharedPageHandler() const { - return shared_page_handler; - } - private: /** * Initialize the emulated system. @@ -236,11 +249,14 @@ private: std::unique_ptr rpc_server; #endif - /// Shared Page - std::shared_ptr shared_page_handler; - std::unique_ptr archive_manager; +public: // HACK: this is temporary exposed for tests, + // due to WIP kernel refactor causing desync state in memory + std::unique_ptr kernel; + std::unique_ptr timing; + +private: static System s_instance; ResultStatus status = ResultStatus::Success; diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp index ccee38243..df355ab27 100644 --- a/src/core/core_timing.cpp +++ b/src/core/core_timing.cpp @@ -2,75 +2,25 @@ // Licensed under GPLv2+ // Refer to the license.txt file included. -#include "core/core_timing.h" - #include #include -#include -#include #include -#include -#include #include "common/assert.h" #include "common/logging/log.h" -#include "common/thread.h" -#include "common/threadsafe_queue.h" +#include "core/core_timing.h" -namespace CoreTiming { - -static s64 global_timer; -static s64 slice_length; -static s64 downcount; - -struct EventType { - TimedCallback callback; - const std::string* name; -}; - -struct Event { - s64 time; - u64 fifo_order; - u64 userdata; - const EventType* type; -}; +namespace Core { // Sort by time, unless the times are the same, in which case sort by the order added to the queue -static bool operator>(const Event& left, const Event& right) { - return std::tie(left.time, left.fifo_order) > std::tie(right.time, right.fifo_order); +bool Timing::Event::operator>(const Event& right) const { + return std::tie(time, fifo_order) > std::tie(right.time, right.fifo_order); } -static bool operator<(const Event& left, const Event& right) { - return std::tie(left.time, left.fifo_order) < std::tie(right.time, right.fifo_order); +bool Timing::Event::operator<(const Event& right) const { + return std::tie(time, fifo_order) < std::tie(right.time, right.fifo_order); } -// unordered_map stores each element separately as a linked list node so pointers to elements -// remain stable regardless of rehashes/resizing. -static std::unordered_map event_types; - -// The queue is a min-heap using std::make_heap/push_heap/pop_heap. -// We don't use std::priority_queue because we need to be able to serialize, unserialize and -// erase arbitrary events (RemoveEvent()) regardless of the queue order. These aren't accomodated -// by the standard adaptor class. -static std::vector event_queue; -static u64 event_fifo_id; -// the queue for storing the events from other threads threadsafe until they will be added -// to the event_queue by the emu thread -static Common::MPSCQueue ts_queue; - -static constexpr int MAX_SLICE_LENGTH = 20000; - -static s64 idled_cycles; - -// Are we in a function that has been called from Advance() -// If events are sheduled from a function that gets called from Advance(), -// don't change slice_length and downcount. -static bool is_global_timer_sane; - -static EventType* ev_lost = nullptr; - -static void EmptyTimedCallback(u64 userdata, s64 cyclesLate) {} - -EventType* RegisterEvent(const std::string& name, TimedCallback callback) { +TimingEventType* Timing::RegisterEvent(const std::string& name, TimedCallback callback) { // check for existing type with same name. // we want event type names to remain unique so that we can use them for serialization. ASSERT_MSG(event_types.find(name) == event_types.end(), @@ -78,42 +28,17 @@ EventType* RegisterEvent(const std::string& name, TimedCallback callback) { "during Init to avoid breaking save states.", name); - auto info = event_types.emplace(name, EventType{callback, nullptr}); - EventType* event_type = &info.first->second; + auto info = event_types.emplace(name, TimingEventType{callback, nullptr}); + TimingEventType* event_type = &info.first->second; event_type->name = &info.first->first; return event_type; } -void UnregisterAllEvents() { - ASSERT_MSG(event_queue.empty(), "Cannot unregister events with events pending"); - event_types.clear(); -} - -void Init() { - downcount = MAX_SLICE_LENGTH; - slice_length = MAX_SLICE_LENGTH; - global_timer = 0; - idled_cycles = 0; - - // The time between CoreTiming being intialized and the first call to Advance() is considered - // the slice boundary between slice -1 and slice 0. Dispatcher loops must call Advance() before - // executing the first cycle of each slice to prepare the slice length and downcount for - // that slice. - is_global_timer_sane = true; - - event_fifo_id = 0; - ev_lost = RegisterEvent("_lost_event", &EmptyTimedCallback); -} - -void Shutdown() { +Timing::~Timing() { MoveEvents(); - ClearPendingEvents(); - UnregisterAllEvents(); } -// This should only be called from the CPU thread. If you are calling -// it from any other thread, you are doing something evil -u64 GetTicks() { +u64 Timing::GetTicks() const { u64 ticks = static_cast(global_timer); if (!is_global_timer_sane) { ticks += slice_length - downcount; @@ -121,19 +46,16 @@ u64 GetTicks() { return ticks; } -void AddTicks(u64 ticks) { +void Timing::AddTicks(u64 ticks) { downcount -= ticks; } -u64 GetIdleTicks() { +u64 Timing::GetIdleTicks() const { return static_cast(idled_cycles); } -void ClearPendingEvents() { - event_queue.clear(); -} - -void ScheduleEvent(s64 cycles_into_future, const EventType* event_type, u64 userdata) { +void Timing::ScheduleEvent(s64 cycles_into_future, const TimingEventType* event_type, + u64 userdata) { ASSERT(event_type != nullptr); s64 timeout = GetTicks() + cycles_into_future; @@ -145,11 +67,12 @@ void ScheduleEvent(s64 cycles_into_future, const EventType* event_type, u64 user std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>()); } -void ScheduleEventThreadsafe(s64 cycles_into_future, const EventType* event_type, u64 userdata) { +void Timing::ScheduleEventThreadsafe(s64 cycles_into_future, const TimingEventType* event_type, + u64 userdata) { ts_queue.Push(Event{global_timer + cycles_into_future, 0, userdata, event_type}); } -void UnscheduleEvent(const EventType* event_type, u64 userdata) { +void Timing::UnscheduleEvent(const TimingEventType* event_type, u64 userdata) { auto itr = std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) { return e.type == event_type && e.userdata == userdata; }); @@ -161,7 +84,7 @@ void UnscheduleEvent(const EventType* event_type, u64 userdata) { } } -void RemoveEvent(const EventType* event_type) { +void Timing::RemoveEvent(const TimingEventType* event_type) { auto itr = std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) { return e.type == event_type; }); @@ -172,12 +95,12 @@ void RemoveEvent(const EventType* event_type) { } } -void RemoveNormalAndThreadsafeEvent(const EventType* event_type) { +void Timing::RemoveNormalAndThreadsafeEvent(const TimingEventType* event_type) { MoveEvents(); RemoveEvent(event_type); } -void ForceExceptionCheck(s64 cycles) { +void Timing::ForceExceptionCheck(s64 cycles) { cycles = std::max(0, cycles); if (downcount > cycles) { slice_length -= downcount - cycles; @@ -185,7 +108,7 @@ void ForceExceptionCheck(s64 cycles) { } } -void MoveEvents() { +void Timing::MoveEvents() { for (Event ev; ts_queue.Pop(ev);) { ev.fifo_order = event_fifo_id++; event_queue.emplace_back(std::move(ev)); @@ -193,7 +116,7 @@ void MoveEvents() { } } -void Advance() { +void Timing::Advance() { MoveEvents(); s64 cycles_executed = slice_length - downcount; @@ -220,17 +143,17 @@ void Advance() { downcount = slice_length; } -void Idle() { +void Timing::Idle() { idled_cycles += downcount; downcount = 0; } -std::chrono::microseconds GetGlobalTimeUs() { +std::chrono::microseconds Timing::GetGlobalTimeUs() const { return std::chrono::microseconds{GetTicks() * 1000000 / BASE_CLOCK_RATE_ARM11}; } -s64 GetDowncount() { +s64 Timing::GetDowncount() const { return downcount; } -} // namespace CoreTiming +} // namespace Core diff --git a/src/core/core_timing.h b/src/core/core_timing.h index bfafe4b6b..617c7c6d6 100644 --- a/src/core/core_timing.h +++ b/src/core/core_timing.h @@ -21,8 +21,11 @@ #include #include #include +#include +#include #include "common/common_types.h" #include "common/logging/log.h" +#include "common/threadsafe_queue.h" // The timing we get from the assembly is 268,111,855.956 Hz // It is possible that this number isn't just an integer because the compiler could have @@ -120,73 +123,112 @@ inline u64 cyclesToMs(s64 cycles) { return cycles * 1000 / BASE_CLOCK_RATE_ARM11; } -namespace CoreTiming { - -struct EventType; +namespace Core { using TimedCallback = std::function; -/** - * CoreTiming begins at the boundary of timing slice -1. An initial call to Advance() is - * required to end slice -1 and start slice 0 before the first cycle of code is executed. - */ -void Init(); -void Shutdown(); +struct TimingEventType { + TimedCallback callback; + const std::string* name; +}; -/** - * This should only be called from the emu thread, if you are calling it any other thread, you are - * doing something evil - */ -u64 GetTicks(); -u64 GetIdleTicks(); -void AddTicks(u64 ticks); +class Timing { +public: + ~Timing(); -/** - * Returns the event_type identifier. if name is not unique, it will assert. - */ -EventType* RegisterEvent(const std::string& name, TimedCallback callback); -void UnregisterAllEvents(); + /** + * This should only be called from the emu thread, if you are calling it any other thread, you + * are doing something evil + */ + u64 GetTicks() const; + u64 GetIdleTicks() const; + void AddTicks(u64 ticks); -/** - * After the first Advance, the slice lengths and the downcount will be reduced whenever an event - * is scheduled earlier than the current values. - * Scheduling from a callback will not update the downcount until the Advance() completes. - */ -void ScheduleEvent(s64 cycles_into_future, const EventType* event_type, u64 userdata = 0); + /** + * Returns the event_type identifier. if name is not unique, it will assert. + */ + TimingEventType* RegisterEvent(const std::string& name, TimedCallback callback); -/** - * This is to be called when outside of hle threads, such as the graphics thread, wants to - * schedule things to be executed on the main thread. - * Not that this doesn't change slice_length and thus events scheduled by this might be called - * with a delay of up to MAX_SLICE_LENGTH - */ -void ScheduleEventThreadsafe(s64 cycles_into_future, const EventType* event_type, u64 userdata); + /** + * After the first Advance, the slice lengths and the downcount will be reduced whenever an + * event is scheduled earlier than the current values. Scheduling from a callback will not + * update the downcount until the Advance() completes. + */ + void ScheduleEvent(s64 cycles_into_future, const TimingEventType* event_type, u64 userdata = 0); -void UnscheduleEvent(const EventType* event_type, u64 userdata); + /** + * This is to be called when outside of hle threads, such as the graphics thread, wants to + * schedule things to be executed on the main thread. + * Not that this doesn't change slice_length and thus events scheduled by this might be called + * with a delay of up to MAX_SLICE_LENGTH + */ + void ScheduleEventThreadsafe(s64 cycles_into_future, const TimingEventType* event_type, + u64 userdata); -/// We only permit one event of each type in the queue at a time. -void RemoveEvent(const EventType* event_type); -void RemoveNormalAndThreadsafeEvent(const EventType* event_type); + void UnscheduleEvent(const TimingEventType* event_type, u64 userdata); -/** Advance must be called at the beginning of dispatcher loops, not the end. Advance() ends - * the previous timing slice and begins the next one, you must Advance from the previous - * slice to the current one before executing any cycles. CoreTiming starts in slice -1 so an - * Advance() is required to initialize the slice length before the first cycle of emulated - * instructions is executed. - */ -void Advance(); -void MoveEvents(); + /// We only permit one event of each type in the queue at a time. + void RemoveEvent(const TimingEventType* event_type); + void RemoveNormalAndThreadsafeEvent(const TimingEventType* event_type); -/// Pretend that the main CPU has executed enough cycles to reach the next event. -void Idle(); + /** Advance must be called at the beginning of dispatcher loops, not the end. Advance() ends + * the previous timing slice and begins the next one, you must Advance from the previous + * slice to the current one before executing any cycles. CoreTiming starts in slice -1 so an + * Advance() is required to initialize the slice length before the first cycle of emulated + * instructions is executed. + */ + void Advance(); + void MoveEvents(); -/// Clear all pending events. This should ONLY be done on exit. -void ClearPendingEvents(); + /// Pretend that the main CPU has executed enough cycles to reach the next event. + void Idle(); -void ForceExceptionCheck(s64 cycles); + void ForceExceptionCheck(s64 cycles); -std::chrono::microseconds GetGlobalTimeUs(); + std::chrono::microseconds GetGlobalTimeUs() const; -s64 GetDowncount(); + s64 GetDowncount() const; -} // namespace CoreTiming +private: + struct Event { + s64 time; + u64 fifo_order; + u64 userdata; + const TimingEventType* type; + + bool operator>(const Event& right) const; + bool operator<(const Event& right) const; + }; + + static constexpr int MAX_SLICE_LENGTH = 20000; + + s64 global_timer = 0; + s64 slice_length = MAX_SLICE_LENGTH; + s64 downcount = MAX_SLICE_LENGTH; + + // unordered_map stores each element separately as a linked list node so pointers to + // elements remain stable regardless of rehashes/resizing. + std::unordered_map event_types; + + // The queue is a min-heap using std::make_heap/push_heap/pop_heap. + // We don't use std::priority_queue because we need to be able to serialize, unserialize and + // erase arbitrary events (RemoveEvent()) regardless of the queue order. These aren't + // accomodated by the standard adaptor class. + std::vector event_queue; + u64 event_fifo_id = 0; + // the queue for storing the events from other threads threadsafe until they will be added + // to the event_queue by the emu thread + Common::MPSCQueue ts_queue; + s64 idled_cycles = 0; + + // Are we in a function that has been called from Advance() + // If events are sheduled from a function that gets called from Advance(), + // don't change slice_length and downcount. + // The time between CoreTiming being intialized and the first call to Advance() is considered + // the slice boundary between slice -1 and slice 0. Dispatcher loops must call Advance() before + // executing the first cycle of each slice to prepare the slice length and downcount for + // that slice. + bool is_global_timer_sane = true; +}; + +} // namespace Core diff --git a/src/core/file_sys/archive_savedata.cpp b/src/core/file_sys/archive_savedata.cpp index d551f590f..6e518af8b 100644 --- a/src/core/file_sys/archive_savedata.cpp +++ b/src/core/file_sys/archive_savedata.cpp @@ -3,6 +3,7 @@ // Refer to the license.txt file included. #include +#include "core/core.h" #include "core/file_sys/archive_savedata.h" #include "core/hle/kernel/process.h" @@ -16,16 +17,19 @@ ArchiveFactory_SaveData::ArchiveFactory_SaveData( : sd_savedata_source(std::move(sd_savedata)) {} ResultVal> ArchiveFactory_SaveData::Open(const Path& path) { - return sd_savedata_source->Open(Kernel::g_current_process->codeset->program_id); + return sd_savedata_source->Open( + Core::System::GetInstance().Kernel().GetCurrentProcess()->codeset->program_id); } ResultCode ArchiveFactory_SaveData::Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info) { - return sd_savedata_source->Format(Kernel::g_current_process->codeset->program_id, format_info); + return sd_savedata_source->Format( + Core::System::GetInstance().Kernel().GetCurrentProcess()->codeset->program_id, format_info); } ResultVal ArchiveFactory_SaveData::GetFormatInfo(const Path& path) const { - return sd_savedata_source->GetFormatInfo(Kernel::g_current_process->codeset->program_id); + return sd_savedata_source->GetFormatInfo( + Core::System::GetInstance().Kernel().GetCurrentProcess()->codeset->program_id); } } // namespace FileSys diff --git a/src/core/file_sys/archive_selfncch.cpp b/src/core/file_sys/archive_selfncch.cpp index 14be1f966..dc2c61666 100644 --- a/src/core/file_sys/archive_selfncch.cpp +++ b/src/core/file_sys/archive_selfncch.cpp @@ -7,6 +7,7 @@ #include "common/common_types.h" #include "common/logging/log.h" #include "common/swap.h" +#include "core/core.h" #include "core/file_sys/archive_selfncch.h" #include "core/file_sys/errors.h" #include "core/file_sys/ivfc_archive.h" @@ -279,7 +280,7 @@ void ArchiveFactory_SelfNCCH::Register(Loader::AppLoader& app_loader) { ResultVal> ArchiveFactory_SelfNCCH::Open(const Path& path) { auto archive = std::make_unique( - ncch_data[Kernel::g_current_process->codeset->program_id]); + ncch_data[Core::System::GetInstance().Kernel().GetCurrentProcess()->codeset->program_id]); return MakeResult>(std::move(archive)); } diff --git a/src/core/file_sys/delay_generator.cpp b/src/core/file_sys/delay_generator.cpp new file mode 100644 index 000000000..654550061 --- /dev/null +++ b/src/core/file_sys/delay_generator.cpp @@ -0,0 +1,22 @@ +// Copyright 2018 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include +#include "core/file_sys/delay_generator.h" + +namespace FileSys { + +DelayGenerator::~DelayGenerator() = default; + +u64 DefaultDelayGenerator::GetReadDelayNs(std::size_t length) { + // This is the delay measured for a romfs read. + // For now we will take that as a default + static constexpr u64 slope(94); + static constexpr u64 offset(582778); + static constexpr u64 minimum(663124); + u64 IPCDelayNanoseconds = std::max(static_cast(length) * slope + offset, minimum); + return IPCDelayNanoseconds; +} + +} // namespace FileSys diff --git a/src/core/file_sys/delay_generator.h b/src/core/file_sys/delay_generator.h index c43a5c026..06ef97281 100644 --- a/src/core/file_sys/delay_generator.h +++ b/src/core/file_sys/delay_generator.h @@ -4,10 +4,14 @@ #pragma once +#include +#include "common/common_types.h" + namespace FileSys { class DelayGenerator { public: + virtual ~DelayGenerator(); virtual u64 GetReadDelayNs(std::size_t length) = 0; // TODO (B3N30): Add getter for all other file/directory io operations @@ -15,15 +19,7 @@ public: class DefaultDelayGenerator : public DelayGenerator { public: - u64 GetReadDelayNs(std::size_t length) override { - // This is the delay measured for a romfs read. - // For now we will take that as a default - static constexpr u64 slope(94); - static constexpr u64 offset(582778); - static constexpr u64 minimum(663124); - u64 IPCDelayNanoseconds = std::max(static_cast(length) * slope + offset, minimum); - return IPCDelayNanoseconds; - } + u64 GetReadDelayNs(std::size_t length) override; }; } // namespace FileSys diff --git a/src/core/file_sys/ncch_container.cpp b/src/core/file_sys/ncch_container.cpp index fc84979f0..de767e840 100644 --- a/src/core/file_sys/ncch_container.cpp +++ b/src/core/file_sys/ncch_container.cpp @@ -226,7 +226,9 @@ Loader::ResultStatus NCCHContainer::Load() { std::memcpy(input.data(), key_y_primary.data(), key_y_primary.size()); std::memcpy(input.data() + key_y_primary.size(), seed.data(), seed.size()); CryptoPP::SHA256 sha; - sha.CalculateDigest(key_y_secondary.data(), input.data(), input.size()); + std::array hash; + sha.CalculateDigest(hash.data(), input.data(), input.size()); + std::memcpy(key_y_secondary.data(), hash.data(), key_y_secondary.size()); } } diff --git a/src/core/file_sys/seed_db.cpp b/src/core/file_sys/seed_db.cpp index 56009d293..f5b38fb0e 100644 --- a/src/core/file_sys/seed_db.cpp +++ b/src/core/file_sys/seed_db.cpp @@ -34,7 +34,7 @@ bool SeedDB::Load() { LOG_ERROR(Service_FS, "Failed to read seed database count fully"); return false; } - if (!file.Seek(file.Tell() + SEEDDB_PADDING_BYTES, SEEK_SET)) { + if (!file.Seek(SEEDDB_PADDING_BYTES, SEEK_CUR)) { LOG_ERROR(Service_FS, "Failed to skip seed database padding"); return false; } diff --git a/src/core/gdbstub/gdbstub.cpp b/src/core/gdbstub/gdbstub.cpp index f95b3c66e..1296b71b4 100644 --- a/src/core/gdbstub/gdbstub.cpp +++ b/src/core/gdbstub/gdbstub.cpp @@ -160,7 +160,7 @@ BreakpointMap breakpoints_write; } // Anonymous namespace static Kernel::Thread* FindThreadById(int id) { - const auto& threads = Kernel::GetThreadList(); + const auto& threads = Core::System::GetInstance().Kernel().GetThreadManager().GetThreadList(); for (auto& thread : threads) { if (thread->GetThreadId() == static_cast(id)) { return thread.get(); @@ -535,7 +535,8 @@ static void HandleQuery() { SendReply(target_xml); } else if (strncmp(query, "fThreadInfo", strlen("fThreadInfo")) == 0) { std::string val = "m"; - const auto& threads = Kernel::GetThreadList(); + const auto& threads = + Core::System::GetInstance().Kernel().GetThreadManager().GetThreadList(); for (const auto& thread : threads) { val += fmt::format("{:x},", thread->GetThreadId()); } @@ -547,7 +548,8 @@ static void HandleQuery() { std::string buffer; buffer += "l"; buffer += ""; - const auto& threads = Kernel::GetThreadList(); + const auto& threads = + Core::System::GetInstance().Kernel().GetThreadManager().GetThreadList(); for (const auto& thread : threads) { buffer += fmt::format(R"*()*", thread->GetThreadId(), thread->GetThreadId()); diff --git a/src/core/hle/applets/applet.cpp b/src/core/hle/applets/applet.cpp index 751b85815..f41157d2f 100644 --- a/src/core/hle/applets/applet.cpp +++ b/src/core/hle/applets/applet.cpp @@ -8,6 +8,7 @@ #include #include "common/assert.h" #include "common/common_types.h" +#include "core/core.h" #include "core/core_timing.h" #include "core/hle/applets/applet.h" #include "core/hle/applets/erreula.h" @@ -38,7 +39,7 @@ namespace Applets { static std::unordered_map> applets; /// The CoreTiming event identifier for the Applet update callback. -static CoreTiming::EventType* applet_update_event = nullptr; +static Core::TimingEventType* applet_update_event = nullptr; /// The interval at which the Applet update callback will be called, 16.6ms static const u64 applet_update_interval_us = 16666; @@ -88,8 +89,8 @@ static void AppletUpdateEvent(u64 applet_id, s64 cycles_late) { // If the applet is still running after the last update, reschedule the event if (applet->IsRunning()) { - CoreTiming::ScheduleEvent(usToCycles(applet_update_interval_us) - cycles_late, - applet_update_event, applet_id); + Core::System::GetInstance().CoreTiming().ScheduleEvent( + usToCycles(applet_update_interval_us) - cycles_late, applet_update_event, applet_id); } else { // Otherwise the applet has terminated, in which case we should clean it up applets[id] = nullptr; @@ -101,8 +102,8 @@ ResultCode Applet::Start(const Service::APT::AppletStartupParameter& parameter) if (result.IsError()) return result; // Schedule the update event - CoreTiming::ScheduleEvent(usToCycles(applet_update_interval_us), applet_update_event, - static_cast(id)); + Core::System::GetInstance().CoreTiming().ScheduleEvent( + usToCycles(applet_update_interval_us), applet_update_event, static_cast(id)); return result; } @@ -128,11 +129,12 @@ bool IsLibraryAppletRunning() { void Init() { // Register the applet update callback - applet_update_event = CoreTiming::RegisterEvent("HLE Applet Update Event", AppletUpdateEvent); + applet_update_event = Core::System::GetInstance().CoreTiming().RegisterEvent( + "HLE Applet Update Event", AppletUpdateEvent); } void Shutdown() { - CoreTiming::RemoveEvent(applet_update_event); + Core::System::GetInstance().CoreTiming().RemoveEvent(applet_update_event); } } // namespace Applets } // namespace HLE diff --git a/src/core/hle/applets/erreula.cpp b/src/core/hle/applets/erreula.cpp index a8135a2ae..ef9da4989 100644 --- a/src/core/hle/applets/erreula.cpp +++ b/src/core/hle/applets/erreula.cpp @@ -3,6 +3,7 @@ // Refer to the license.txt file included. #include "common/string_util.h" +#include "core/core.h" #include "core/hle/applets/erreula.h" #include "core/hle/service/apt/apt.h" @@ -27,11 +28,9 @@ ResultCode ErrEula::ReceiveParameter(const Service::APT::MessageParameter& param // TODO: allocated memory never released using Kernel::MemoryPermission; - // Allocate a heap block of the required size for this applet. - heap_memory = std::make_shared>(capture_info.size); // Create a SharedMemory that directly points to this heap block. - framebuffer_memory = Kernel::SharedMemory::CreateForApplet( - heap_memory, 0, capture_info.size, MemoryPermission::ReadWrite, MemoryPermission::ReadWrite, + framebuffer_memory = Core::System::GetInstance().Kernel().CreateSharedMemoryForApplet( + 0, capture_info.size, MemoryPermission::ReadWrite, MemoryPermission::ReadWrite, "ErrEula Memory"); // Send the response message with the newly created SharedMemory diff --git a/src/core/hle/applets/mii_selector.cpp b/src/core/hle/applets/mii_selector.cpp index cfe25b11d..f51b720a0 100644 --- a/src/core/hle/applets/mii_selector.cpp +++ b/src/core/hle/applets/mii_selector.cpp @@ -7,6 +7,7 @@ #include "common/assert.h" #include "common/logging/log.h" #include "common/string_util.h" +#include "core/core.h" #include "core/hle/applets/mii_selector.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/shared_memory.h" @@ -34,11 +35,9 @@ ResultCode MiiSelector::ReceiveParameter(const Service::APT::MessageParameter& p memcpy(&capture_info, parameter.buffer.data(), sizeof(capture_info)); using Kernel::MemoryPermission; - // Allocate a heap block of the required size for this applet. - heap_memory = std::make_shared>(capture_info.size); // Create a SharedMemory that directly points to this heap block. - framebuffer_memory = Kernel::SharedMemory::CreateForApplet( - heap_memory, 0, capture_info.size, MemoryPermission::ReadWrite, MemoryPermission::ReadWrite, + framebuffer_memory = Core::System::GetInstance().Kernel().CreateSharedMemoryForApplet( + 0, capture_info.size, MemoryPermission::ReadWrite, MemoryPermission::ReadWrite, "MiiSelector Memory"); // Send the response message with the newly created SharedMemory diff --git a/src/core/hle/applets/mint.cpp b/src/core/hle/applets/mint.cpp index d71cd7001..1986961bf 100644 --- a/src/core/hle/applets/mint.cpp +++ b/src/core/hle/applets/mint.cpp @@ -3,6 +3,7 @@ // Refer to the license.txt file included. #include "common/string_util.h" +#include "core/core.h" #include "core/hle/applets/mint.h" #include "core/hle/service/apt/apt.h" @@ -27,11 +28,9 @@ ResultCode Mint::ReceiveParameter(const Service::APT::MessageParameter& paramete // TODO: allocated memory never released using Kernel::MemoryPermission; - // Allocate a heap block of the required size for this applet. - heap_memory = std::make_shared>(capture_info.size); // Create a SharedMemory that directly points to this heap block. - framebuffer_memory = Kernel::SharedMemory::CreateForApplet( - heap_memory, 0, capture_info.size, MemoryPermission::ReadWrite, MemoryPermission::ReadWrite, + framebuffer_memory = Core::System::GetInstance().Kernel().CreateSharedMemoryForApplet( + 0, capture_info.size, MemoryPermission::ReadWrite, MemoryPermission::ReadWrite, "Mint Memory"); // Send the response message with the newly created SharedMemory diff --git a/src/core/hle/applets/swkbd.cpp b/src/core/hle/applets/swkbd.cpp index d13050002..750a20849 100644 --- a/src/core/hle/applets/swkbd.cpp +++ b/src/core/hle/applets/swkbd.cpp @@ -8,6 +8,7 @@ #include "common/assert.h" #include "common/logging/log.h" #include "common/string_util.h" +#include "core/core.h" #include "core/hle/applets/swkbd.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/shared_memory.h" @@ -38,11 +39,9 @@ ResultCode SoftwareKeyboard::ReceiveParameter(Service::APT::MessageParameter con memcpy(&capture_info, parameter.buffer.data(), sizeof(capture_info)); using Kernel::MemoryPermission; - // Allocate a heap block of the required size for this applet. - heap_memory = std::make_shared>(capture_info.size); // Create a SharedMemory that directly points to this heap block. - framebuffer_memory = Kernel::SharedMemory::CreateForApplet( - heap_memory, 0, capture_info.size, MemoryPermission::ReadWrite, MemoryPermission::ReadWrite, + framebuffer_memory = Core::System::GetInstance().Kernel().CreateSharedMemoryForApplet( + 0, capture_info.size, MemoryPermission::ReadWrite, MemoryPermission::ReadWrite, "SoftwareKeyboard Memory"); // Send the response message with the newly created SharedMemory diff --git a/src/core/hle/ipc.h b/src/core/hle/ipc.h index e9c7d1b0f..7736f5fca 100644 --- a/src/core/hle/ipc.h +++ b/src/core/hle/ipc.h @@ -9,27 +9,6 @@ #include "core/hle/kernel/thread.h" #include "core/memory.h" -namespace Kernel { - -/// Offset into command buffer of header -static const int kCommandHeaderOffset = 0x80; - -/** - * Returns a pointer to the command buffer in the current thread's TLS - * TODO(Subv): This is not entirely correct, the command buffer should be copied from - * the thread's TLS to an intermediate buffer in kernel memory, and then copied again to - * the service handler process' memory. - * @param offset Optional offset into command buffer - * @param offset Optional offset into command buffer (in bytes) - * @return Pointer to command buffer - */ -inline u32* GetCommandBuffer(const int offset = 0) { - return (u32*)Memory::GetPointer(GetCurrentThread()->GetTLSAddress() + kCommandHeaderOffset + - offset); -} - -} // namespace Kernel - namespace IPC { /// Size of the command buffer area, in 32-bit words. diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp index 1dc005554..d391da208 100644 --- a/src/core/hle/kernel/address_arbiter.cpp +++ b/src/core/hle/kernel/address_arbiter.cpp @@ -7,6 +7,7 @@ #include "common/logging/log.h" #include "core/hle/kernel/address_arbiter.h" #include "core/hle/kernel/errors.h" +#include "core/hle/kernel/kernel.h" #include "core/hle/kernel/thread.h" #include "core/memory.h" @@ -64,11 +65,11 @@ SharedPtr AddressArbiter::ResumeHighestPriorityThread(VAddr address) { return thread; } -AddressArbiter::AddressArbiter() {} +AddressArbiter::AddressArbiter(KernelSystem& kernel) : Object(kernel) {} AddressArbiter::~AddressArbiter() {} -SharedPtr AddressArbiter::Create(std::string name) { - SharedPtr address_arbiter(new AddressArbiter); +SharedPtr KernelSystem::CreateAddressArbiter(std::string name) { + SharedPtr address_arbiter(new AddressArbiter(*this)); address_arbiter->name = std::move(name); diff --git a/src/core/hle/kernel/address_arbiter.h b/src/core/hle/kernel/address_arbiter.h index cf2761819..cdfa64ec2 100644 --- a/src/core/hle/kernel/address_arbiter.h +++ b/src/core/hle/kernel/address_arbiter.h @@ -31,14 +31,6 @@ enum class ArbitrationType : u32 { class AddressArbiter final : public Object { public: - /** - * Creates an address arbiter. - * - * @param name Optional name used for debugging. - * @returns The created AddressArbiter. - */ - static SharedPtr Create(std::string name = "Unknown"); - std::string GetTypeName() const override { return "Arbiter"; } @@ -57,7 +49,7 @@ public: s32 value, u64 nanoseconds); private: - AddressArbiter(); + explicit AddressArbiter(KernelSystem& kernel); ~AddressArbiter() override; /// Puts the thread to wait on the specified arbitration address under this address arbiter. @@ -72,6 +64,8 @@ private: /// Threads waiting for the address arbiter to be signaled. std::vector> waiting_threads; + + friend class KernelSystem; }; } // namespace Kernel diff --git a/src/core/hle/kernel/client_port.cpp b/src/core/hle/kernel/client_port.cpp index e45494db2..02c5d08fd 100644 --- a/src/core/hle/kernel/client_port.cpp +++ b/src/core/hle/kernel/client_port.cpp @@ -13,7 +13,7 @@ namespace Kernel { -ClientPort::ClientPort() = default; +ClientPort::ClientPort(KernelSystem& kernel) : kernel(kernel), Object(kernel) {} ClientPort::~ClientPort() = default; ResultVal> ClientPort::Connect() { @@ -26,7 +26,7 @@ ResultVal> ClientPort::Connect() { active_sessions++; // Create a new session pair, let the created sessions inherit the parent port's HLE handler. - auto sessions = ServerSession::CreateSessionPair(server_port->GetName(), this); + auto sessions = kernel.CreateSessionPair(server_port->GetName(), this); if (server_port->hle_handler) server_port->hle_handler->ClientConnected(std::get>(sessions)); diff --git a/src/core/hle/kernel/client_port.h b/src/core/hle/kernel/client_port.h index 46227347e..149ca77a1 100644 --- a/src/core/hle/kernel/client_port.h +++ b/src/core/hle/kernel/client_port.h @@ -48,13 +48,16 @@ public: void ConnectionClosed(); private: - ClientPort(); + explicit ClientPort(KernelSystem& kernel); ~ClientPort() override; + KernelSystem& kernel; SharedPtr server_port; ///< ServerPort associated with this client port. u32 max_sessions = 0; ///< Maximum number of simultaneous sessions the port can have u32 active_sessions = 0; ///< Number of currently open sessions to this port std::string name; ///< Name of client port (optional) + + friend class KernelSystem; }; } // namespace Kernel diff --git a/src/core/hle/kernel/client_session.cpp b/src/core/hle/kernel/client_session.cpp index 17f4c12d3..ef1b90195 100644 --- a/src/core/hle/kernel/client_session.cpp +++ b/src/core/hle/kernel/client_session.cpp @@ -13,7 +13,7 @@ namespace Kernel { -ClientSession::ClientSession() = default; +ClientSession::ClientSession(KernelSystem& kernel) : Object(kernel) {} ClientSession::~ClientSession() { // This destructor will be called automatically when the last ClientSession handle is closed by // the emulated application. diff --git a/src/core/hle/kernel/client_session.h b/src/core/hle/kernel/client_session.h index 9c6b47927..5d5a2d9cd 100644 --- a/src/core/hle/kernel/client_session.h +++ b/src/core/hle/kernel/client_session.h @@ -12,13 +12,12 @@ namespace Kernel { -class ServerSession; class Session; class Thread; class ClientSession final : public Object { public: - friend class ServerSession; + friend class KernelSystem; std::string GetTypeName() const override { return "ClientSession"; @@ -46,7 +45,7 @@ public: std::shared_ptr parent; private: - ClientSession(); + explicit ClientSession(KernelSystem& kernel); ~ClientSession() override; }; diff --git a/src/core/hle/config_mem.cpp b/src/core/hle/kernel/config_mem.cpp similarity index 87% rename from src/core/hle/config_mem.cpp rename to src/core/hle/kernel/config_mem.cpp index 038af7909..58bef4110 100644 --- a/src/core/hle/config_mem.cpp +++ b/src/core/hle/kernel/config_mem.cpp @@ -3,15 +3,13 @@ // Refer to the license.txt file included. #include -#include "core/hle/config_mem.h" +#include "core/hle/kernel/config_mem.h" //////////////////////////////////////////////////////////////////////////////////////////////////// namespace ConfigMem { -ConfigMemDef config_mem; - -void Init() { +Handler::Handler() { std::memset(&config_mem, 0, sizeof(config_mem)); // Values extracted from firmware 11.2.0-35E @@ -28,4 +26,8 @@ void Init() { config_mem.firm_ctr_sdk_ver = 0x0000F297; } +ConfigMemDef& Handler::GetConfigMem() { + return config_mem; +} + } // namespace ConfigMem diff --git a/src/core/hle/config_mem.h b/src/core/hle/kernel/config_mem.h similarity index 94% rename from src/core/hle/config_mem.h rename to src/core/hle/kernel/config_mem.h index 1840d1760..ecb97c6bd 100644 --- a/src/core/hle/config_mem.h +++ b/src/core/hle/kernel/config_mem.h @@ -49,8 +49,13 @@ struct ConfigMemDef { static_assert(sizeof(ConfigMemDef) == Memory::CONFIG_MEMORY_SIZE, "Config Memory structure size is wrong"); -extern ConfigMemDef config_mem; +class Handler { +public: + Handler(); + ConfigMemDef& GetConfigMem(); -void Init(); +private: + ConfigMemDef config_mem; +}; } // namespace ConfigMem diff --git a/src/core/hle/kernel/errors.h b/src/core/hle/kernel/errors.h index 509ffee58..2e9d63c30 100644 --- a/src/core/hle/kernel/errors.h +++ b/src/core/hle/kernel/errors.h @@ -67,6 +67,10 @@ constexpr ResultCode ERR_MISALIGNED_SIZE(ErrorDescription::MisalignedSize, Error constexpr ResultCode ERR_OUT_OF_MEMORY(ErrorDescription::OutOfMemory, ErrorModule::Kernel, ErrorSummary::OutOfResource, ErrorLevel::Permanent); // 0xD86007F3 +/// Returned when out of heap or linear heap memory when allocating +constexpr ResultCode ERR_OUT_OF_HEAP_MEMORY(ErrorDescription::OutOfMemory, ErrorModule::OS, + ErrorSummary::OutOfResource, + ErrorLevel::Status); // 0xC860180A constexpr ResultCode ERR_NOT_IMPLEMENTED(ErrorDescription::NotImplemented, ErrorModule::OS, ErrorSummary::InvalidArgument, ErrorLevel::Usage); // 0xE0E01BF4 diff --git a/src/core/hle/kernel/event.cpp b/src/core/hle/kernel/event.cpp index 6c93719e3..ce5f3d6ec 100644 --- a/src/core/hle/kernel/event.cpp +++ b/src/core/hle/kernel/event.cpp @@ -7,16 +7,16 @@ #include #include "common/assert.h" #include "core/hle/kernel/event.h" -#include "core/hle/kernel/object.h" +#include "core/hle/kernel/kernel.h" #include "core/hle/kernel/thread.h" namespace Kernel { -Event::Event() {} +Event::Event(KernelSystem& kernel) : WaitObject(kernel) {} Event::~Event() {} -SharedPtr Event::Create(ResetType reset_type, std::string name) { - SharedPtr evt(new Event); +SharedPtr KernelSystem::CreateEvent(ResetType reset_type, std::string name) { + SharedPtr evt(new Event(*this)); evt->signaled = false; evt->reset_type = reset_type; diff --git a/src/core/hle/kernel/event.h b/src/core/hle/kernel/event.h index 53679810e..09c21587d 100644 --- a/src/core/hle/kernel/event.h +++ b/src/core/hle/kernel/event.h @@ -12,13 +12,6 @@ namespace Kernel { class Event final : public WaitObject { public: - /** - * Creates an event - * @param reset_type ResetType describing how to create event - * @param name Optional name of event - */ - static SharedPtr Create(ResetType reset_type, std::string name = "Unknown"); - std::string GetTypeName() const override { return "Event"; } @@ -47,13 +40,15 @@ public: void Clear(); private: - Event(); + explicit Event(KernelSystem& kernel); ~Event() override; ResetType reset_type; ///< Current ResetType bool signaled; ///< Whether the event has already been signaled std::string name; ///< Name of event (optional) + + friend class KernelSystem; }; } // namespace Kernel diff --git a/src/core/hle/kernel/handle_table.cpp b/src/core/hle/kernel/handle_table.cpp index a0805a752..15dcc4583 100644 --- a/src/core/hle/kernel/handle_table.cpp +++ b/src/core/hle/kernel/handle_table.cpp @@ -12,9 +12,7 @@ namespace Kernel { -HandleTable g_handle_table; - -HandleTable::HandleTable() { +HandleTable::HandleTable(KernelSystem& kernel) : kernel(kernel) { next_generation = 1; Clear(); } @@ -74,9 +72,9 @@ bool HandleTable::IsValid(Handle handle) const { SharedPtr HandleTable::GetGeneric(Handle handle) const { if (handle == CurrentThread) { - return GetCurrentThread(); + return kernel.GetThreadManager().GetCurrentThread(); } else if (handle == CurrentProcess) { - return g_current_process; + return kernel.GetCurrentProcess(); } if (!IsValid(handle)) { diff --git a/src/core/hle/kernel/handle_table.h b/src/core/hle/kernel/handle_table.h index 5497dd74a..00cb47a33 100644 --- a/src/core/hle/kernel/handle_table.h +++ b/src/core/hle/kernel/handle_table.h @@ -42,7 +42,7 @@ enum KernelHandle : Handle { */ class HandleTable final : NonCopyable { public: - HandleTable(); + explicit HandleTable(KernelSystem& kernel); /** * Allocates a handle for the given object. @@ -119,8 +119,8 @@ private: /// Head of the free slots linked list. u16 next_free_slot; + + KernelSystem& kernel; }; -extern HandleTable g_handle_table; - } // namespace Kernel diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp index cb63fe5e3..a72d33dd9 100644 --- a/src/core/hle/kernel/hle_ipc.cpp +++ b/src/core/hle/kernel/hle_ipc.cpp @@ -6,6 +6,7 @@ #include #include "common/assert.h" #include "common/common_types.h" +#include "core/core.h" #include "core/hle/kernel/event.h" #include "core/hle/kernel/handle_table.h" #include "core/hle/kernel/hle_ipc.h" @@ -49,13 +50,14 @@ SharedPtr HLERequestContext::SleepClientThread(SharedPtr thread, std::array cmd_buff; Memory::ReadBlock(*process, thread->GetCommandBufferAddress(), cmd_buff.data(), cmd_buff.size() * sizeof(u32)); - context.WriteToOutgoingCommandBuffer(cmd_buff.data(), *process, Kernel::g_handle_table); + context.WriteToOutgoingCommandBuffer(cmd_buff.data(), *process); // Copy the translated command buffer back into the thread's command buffer area. Memory::WriteBlock(*process, thread->GetCommandBufferAddress(), cmd_buff.data(), cmd_buff.size() * sizeof(u32)); }; - auto event = Kernel::Event::Create(Kernel::ResetType::OneShot, "HLE Pause Event: " + reason); + auto event = Core::System::GetInstance().Kernel().CreateEvent(Kernel::ResetType::OneShot, + "HLE Pause Event: " + reason); thread->status = ThreadStatus::WaitHleEvent; thread->wait_objects = {event}; event->AddWaitingThread(thread); @@ -96,8 +98,7 @@ void HLERequestContext::AddStaticBuffer(u8 buffer_id, std::vector data) { } ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(const u32_le* src_cmdbuf, - Process& src_process, - HandleTable& src_table) { + Process& src_process) { IPC::Header header{src_cmdbuf[0]}; std::size_t untranslated_size = 1u + header.normal_params_size; @@ -120,10 +121,10 @@ ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(const u32_le* sr Handle handle = src_cmdbuf[i]; SharedPtr object = nullptr; if (handle != 0) { - object = src_table.GetGeneric(handle); + object = src_process.handle_table.GetGeneric(handle); ASSERT(object != nullptr); // TODO(yuriks): Return error if (descriptor == IPC::DescriptorType::MoveHandle) { - src_table.Close(handle); + src_process.handle_table.Close(handle); } } @@ -161,8 +162,8 @@ ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(const u32_le* sr return RESULT_SUCCESS; } -ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(u32_le* dst_cmdbuf, Process& dst_process, - HandleTable& dst_table) const { +ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(u32_le* dst_cmdbuf, + Process& dst_process) const { IPC::Header header{cmd_buf[0]}; std::size_t untranslated_size = 1u + header.normal_params_size; @@ -187,7 +188,7 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(u32_le* dst_cmdbuf, P Handle handle = 0; if (object != nullptr) { // TODO(yuriks): Figure out the proper error handling for if this fails - handle = dst_table.Create(object).Unwrap(); + handle = dst_process.handle_table.Create(object).Unwrap(); } dst_cmdbuf[i++] = handle; } diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h index 3a2b81b16..4e0c31d98 100644 --- a/src/core/hle/kernel/hle_ipc.h +++ b/src/core/hle/kernel/hle_ipc.h @@ -124,8 +124,7 @@ private: /** * Class containing information about an in-flight IPC request being handled by an HLE service - * implementation. Services should avoid using old global APIs (e.g. Kernel::GetCommandBuffer()) and - * when possible use the APIs in this class to service the request. + * implementation. * * HLE handle protocol * =================== @@ -226,11 +225,9 @@ public: MappedBuffer& GetMappedBuffer(u32 id_from_cmdbuf); /// Populates this context with data from the requesting process/thread. - ResultCode PopulateFromIncomingCommandBuffer(const u32_le* src_cmdbuf, Process& src_process, - HandleTable& src_table); + ResultCode PopulateFromIncomingCommandBuffer(const u32_le* src_cmdbuf, Process& src_process); /// Writes data from this context back to the requesting process/thread. - ResultCode WriteToOutgoingCommandBuffer(u32_le* dst_cmdbuf, Process& dst_process, - HandleTable& dst_table) const; + ResultCode WriteToOutgoingCommandBuffer(u32_le* dst_cmdbuf, Process& dst_process) const; private: std::array cmd_buf; diff --git a/src/core/hle/kernel/ipc.cpp b/src/core/hle/kernel/ipc.cpp index 8db9d241f..779979fc7 100644 --- a/src/core/hle/kernel/ipc.cpp +++ b/src/core/hle/kernel/ipc.cpp @@ -14,6 +14,68 @@ namespace Kernel { +void ScanForAndUnmapBuffer(std::array& dst_cmd_buf, + const std::size_t dst_command_size, std::size_t& target_index, + SharedPtr src_process, SharedPtr dst_process, + const VAddr source_address, const VAddr page_start, const u32 num_pages, + const u32 size, const IPC::MappedBufferPermissions permissions) { + while (target_index < dst_command_size) { + u32 desc = dst_cmd_buf[target_index++]; + + if (IPC::GetDescriptorType(desc) == IPC::DescriptorType::CopyHandle || + IPC::GetDescriptorType(desc) == IPC::DescriptorType::MoveHandle) { + u32 num_handles = IPC::HandleNumberFromDesc(desc); + for (u32 j = 0; j < num_handles; ++j) { + target_index += 1; + } + continue; + } + + if (IPC::GetDescriptorType(desc) == IPC::DescriptorType::CallingPid || + IPC::GetDescriptorType(desc) == IPC::DescriptorType::StaticBuffer) { + target_index += 1; + continue; + } + + if (IPC::GetDescriptorType(desc) == IPC::DescriptorType::MappedBuffer) { + VAddr dest_address = dst_cmd_buf[target_index]; + IPC::MappedBufferDescInfo dest_descInfo{desc}; + u32 dest_size = static_cast(dest_descInfo.size); + IPC::MappedBufferPermissions dest_permissions = dest_descInfo.perms; + + if (dest_size == 0) { + target_index += 1; + continue; + } + + ASSERT(permissions == dest_permissions && size == dest_size); + // Readonly buffers do not need to be copied over to the target + // process again because they were (presumably) not modified. This + // behavior is consistent with the real kernel. + if (permissions != IPC::MappedBufferPermissions::R) { + // Copy the modified buffer back into the target process + Memory::CopyBlock(*src_process, *dst_process, source_address, dest_address, size); + } + + VAddr prev_reserve = page_start - Memory::PAGE_SIZE; + VAddr next_reserve = page_start + num_pages * Memory::PAGE_SIZE; + + auto& prev_vma = src_process->vm_manager.FindVMA(prev_reserve)->second; + auto& next_vma = src_process->vm_manager.FindVMA(next_reserve)->second; + ASSERT(prev_vma.meminfo_state == MemoryState::Reserved && + next_vma.meminfo_state == MemoryState::Reserved); + + // Unmap the buffer and guard pages from the source process + ResultCode result = src_process->vm_manager.UnmapRange( + page_start - Memory::PAGE_SIZE, (num_pages + 2) * Memory::PAGE_SIZE); + ASSERT(result == RESULT_SUCCESS); + + target_index += 1; + break; + } + } +} + ResultCode TranslateCommandBuffer(SharedPtr src_thread, SharedPtr dst_thread, VAddr src_address, VAddr dst_address, bool reply) { @@ -33,6 +95,18 @@ ResultCode TranslateCommandBuffer(SharedPtr src_thread, SharedPtr cmd_buf; Memory::ReadBlock(*src_process, src_address, cmd_buf.data(), command_size * sizeof(u32)); + // Create a copy of the target's command buffer + IPC::Header dst_header; + Memory::ReadBlock(*dst_process, dst_address, &dst_header.raw, sizeof(dst_header.raw)); + + std::size_t dst_untranslated_size = 1u + dst_header.normal_params_size; + std::size_t dst_command_size = dst_untranslated_size + dst_header.translate_params_size; + std::size_t target_index = dst_untranslated_size; + + std::array dst_cmd_buf; + Memory::ReadBlock(*dst_process, dst_address, dst_cmd_buf.data(), + dst_command_size * sizeof(u32)); + std::size_t i = untranslated_size; while (i < command_size) { u32 descriptor = cmd_buf[i]; @@ -60,9 +134,9 @@ ResultCode TranslateCommandBuffer(SharedPtr src_thread, SharedPtrhandle_table.GetGeneric(handle); if (descriptor == IPC::DescriptorType::MoveHandle) { - g_handle_table.Close(handle); + src_process->handle_table.Close(handle); } } @@ -73,7 +147,7 @@ ResultCode TranslateCommandBuffer(SharedPtr src_thread, SharedPtrhandle_table.Create(std::move(object)); cmd_buf[i++] = result.ValueOr(0); } break; @@ -128,76 +202,50 @@ ResultCode TranslateCommandBuffer(SharedPtr src_thread, SharedPtr> Memory::PAGE_BITS; + // Skip when the size is zero and num_pages == 0 + if (size == 0) { + cmd_buf[i++] = 0; + break; + } ASSERT(num_pages >= 1); if (reply) { - // TODO(Subv): Scan the target's command buffer to make sure that there was a - // MappedBuffer descriptor in the original request. The real kernel panics if you - // try to reply with an unsolicited MappedBuffer. + // Scan the target's command buffer for the matching mapped buffer. + // The real kernel panics if you try to reply with an unsolicited MappedBuffer. + ScanForAndUnmapBuffer(dst_cmd_buf, dst_command_size, target_index, src_process, + dst_process, source_address, page_start, num_pages, size, + permissions); - // Unmap the buffers. Readonly buffers do not need to be copied over to the target - // process again because they were (presumably) not modified. This behavior is - // consistent with the real kernel. - if (permissions == IPC::MappedBufferPermissions::R) { - ResultCode result = src_process->vm_manager.UnmapRange( - page_start, num_pages * Memory::PAGE_SIZE); - ASSERT(result == RESULT_SUCCESS); - } - - ASSERT_MSG(permissions == IPC::MappedBufferPermissions::R, - "Unmapping Write MappedBuffers is unimplemented"); i += 1; break; } VAddr target_address = 0; - auto IsPageAligned = [](VAddr address) -> bool { - return (address & Memory::PAGE_MASK) == 0; - }; - - // TODO(Subv): Support more than 1 page and aligned page mappings - ASSERT_MSG( - num_pages == 1 && - (!IsPageAligned(source_address) || !IsPageAligned(source_address + size)), - "MappedBuffers of more than one page or aligned transfers are not implemented"); - // TODO(Subv): Perform permission checks. - // TODO(Subv): Leave a page of Reserved memory before the first page and after the last - // page. + // Reserve a page of memory before the mapped buffer + auto reserve_buffer = std::make_shared>(Memory::PAGE_SIZE); + dst_process->vm_manager.MapMemoryBlockToBase( + Memory::IPC_MAPPING_VADDR, Memory::IPC_MAPPING_SIZE, reserve_buffer, 0, + static_cast(reserve_buffer->size()), Kernel::MemoryState::Reserved); - if (!IsPageAligned(source_address) || - (num_pages == 1 && !IsPageAligned(source_address + size))) { - // If the address of the source buffer is not page-aligned or if the buffer doesn't - // fill an entire page, then we have to allocate a page of memory in the target - // process and copy over the data from the input buffer. This allocated buffer will - // be copied back to the source process and deallocated when the server replies to - // the request via ReplyAndReceive. + auto buffer = std::make_shared>(num_pages * Memory::PAGE_SIZE); + Memory::ReadBlock(*src_process, source_address, buffer->data() + page_offset, size); - auto buffer = std::make_shared>(Memory::PAGE_SIZE); - - // Number of bytes until the next page. - std::size_t difference_to_page = - Common::AlignUp(source_address, Memory::PAGE_SIZE) - source_address; - // If the data fits in one page we can just copy the required size instead of the - // entire page. - std::size_t read_size = - num_pages == 1 ? static_cast(size) : difference_to_page; - - Memory::ReadBlock(*src_process, source_address, buffer->data() + page_offset, - read_size); - - // Map the page into the target process' address space. - target_address = - dst_process->vm_manager - .MapMemoryBlockToBase(Memory::IPC_MAPPING_VADDR, Memory::IPC_MAPPING_SIZE, - buffer, 0, static_cast(buffer->size()), - Kernel::MemoryState::Shared) - .Unwrap(); - } + // Map the page(s) into the target process' address space. + target_address = dst_process->vm_manager + .MapMemoryBlockToBase( + Memory::IPC_MAPPING_VADDR, Memory::IPC_MAPPING_SIZE, buffer, 0, + static_cast(buffer->size()), Kernel::MemoryState::Shared) + .Unwrap(); cmd_buf[i++] = target_address + page_offset; + + // Reserve a page of memory after the mapped buffer + dst_process->vm_manager.MapMemoryBlockToBase( + Memory::IPC_MAPPING_VADDR, Memory::IPC_MAPPING_SIZE, reserve_buffer, 0, + static_cast(reserve_buffer->size()), Kernel::MemoryState::Reserved); break; } default: diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index d494914e3..1c174d2ea 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -2,46 +2,77 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include "core/hle/config_mem.h" +#include "core/hle/kernel/client_port.h" +#include "core/hle/kernel/config_mem.h" #include "core/hle/kernel/handle_table.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/memory.h" #include "core/hle/kernel/process.h" #include "core/hle/kernel/resource_limit.h" +#include "core/hle/kernel/shared_page.h" #include "core/hle/kernel/thread.h" #include "core/hle/kernel/timer.h" -#include "core/hle/shared_page.h" namespace Kernel { -std::atomic Object::next_object_id{0}; - /// Initialize the kernel -void Init(u32 system_mode) { - ConfigMem::Init(); +KernelSystem::KernelSystem(u32 system_mode) { + MemoryInit(system_mode); - Kernel::MemoryInit(system_mode); - - Kernel::ResourceLimitsInit(); - Kernel::ThreadingInit(); - Kernel::TimersInit(); - - Object::next_object_id = 0; - // TODO(Subv): Start the process ids from 10 for now, as lower PIDs are - // reserved for low-level services - Process::next_process_id = 10; + resource_limits = std::make_unique(*this); + thread_manager = std::make_unique(); + timer_manager = std::make_unique(); } /// Shutdown the kernel -void Shutdown() { - g_handle_table.Clear(); // Free all kernel objects +KernelSystem::~KernelSystem() = default; - Kernel::ThreadingShutdown(); - g_current_process = nullptr; +ResourceLimitList& KernelSystem::ResourceLimit() { + return *resource_limits; +} - Kernel::TimersShutdown(); - Kernel::ResourceLimitsShutdown(); - Kernel::MemoryShutdown(); +const ResourceLimitList& KernelSystem::ResourceLimit() const { + return *resource_limits; +} + +u32 KernelSystem::GenerateObjectID() { + return next_object_id++; +} + +SharedPtr KernelSystem::GetCurrentProcess() const { + return current_process; +} + +void KernelSystem::SetCurrentProcess(SharedPtr process) { + current_process = std::move(process); +} + +ThreadManager& KernelSystem::GetThreadManager() { + return *thread_manager; +} + +const ThreadManager& KernelSystem::GetThreadManager() const { + return *thread_manager; +} + +TimerManager& KernelSystem::GetTimerManager() { + return *timer_manager; +} + +const TimerManager& KernelSystem::GetTimerManager() const { + return *timer_manager; +} + +SharedPage::Handler& KernelSystem::GetSharedPageHandler() { + return *shared_page_handler; +} + +const SharedPage::Handler& KernelSystem::GetSharedPageHandler() const { + return *shared_page_handler; +} + +void KernelSystem::AddNamedPort(std::string name, SharedPtr port) { + named_ports.emplace(std::move(name), std::move(port)); } } // namespace Kernel diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index 2bc45d7db..a6db1eb39 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -4,14 +4,250 @@ #pragma once +#include +#include +#include +#include +#include +#include +#include #include "common/common_types.h" +#include "core/hle/kernel/memory.h" +#include "core/hle/result.h" + +namespace ConfigMem { +class Handler; +} + +namespace SharedPage { +class Handler; +} namespace Kernel { -/// Initialize the kernel with the specified system mode. -void Init(u32 system_mode); +class AddressArbiter; +class Event; +class Mutex; +class CodeSet; +class Process; +class Thread; +class Semaphore; +class Timer; +class ClientPort; +class ServerPort; +class ClientSession; +class ServerSession; +class ResourceLimitList; +class SharedMemory; +class ThreadManager; +class TimerManager; +class VMManager; -/// Shutdown the kernel -void Shutdown(); +enum class ResetType { + OneShot, + Sticky, + Pulse, +}; + +/// Permissions for mapped shared memory blocks +enum class MemoryPermission : u32 { + None = 0, + Read = (1u << 0), + Write = (1u << 1), + ReadWrite = (Read | Write), + Execute = (1u << 2), + ReadExecute = (Read | Execute), + WriteExecute = (Write | Execute), + ReadWriteExecute = (Read | Write | Execute), + DontCare = (1u << 28) +}; + +enum class MemoryRegion : u16 { + APPLICATION = 1, + SYSTEM = 2, + BASE = 3, +}; + +template +using SharedPtr = boost::intrusive_ptr; + +class KernelSystem { +public: + explicit KernelSystem(u32 system_mode); + ~KernelSystem(); + + /** + * Creates an address arbiter. + * + * @param name Optional name used for debugging. + * @returns The created AddressArbiter. + */ + SharedPtr CreateAddressArbiter(std::string name = "Unknown"); + + /** + * Creates an event + * @param reset_type ResetType describing how to create event + * @param name Optional name of event + */ + SharedPtr CreateEvent(ResetType reset_type, std::string name = "Unknown"); + + /** + * Creates a mutex. + * @param initial_locked Specifies if the mutex should be locked initially + * @param name Optional name of mutex + * @return Pointer to new Mutex object + */ + SharedPtr CreateMutex(bool initial_locked, std::string name = "Unknown"); + + SharedPtr CreateCodeSet(std::string name, u64 program_id); + + SharedPtr CreateProcess(SharedPtr code_set); + + /** + * Creates and returns a new thread. The new thread is immediately scheduled + * @param name The friendly name desired for the thread + * @param entry_point The address at which the thread should start execution + * @param priority The thread's priority + * @param arg User data to pass to the thread + * @param processor_id The ID(s) of the processors on which the thread is desired to be run + * @param stack_top The address of the thread's stack top + * @param owner_process The parent process for the thread + * @return A shared pointer to the newly created thread + */ + ResultVal> CreateThread(std::string name, VAddr entry_point, u32 priority, + u32 arg, s32 processor_id, VAddr stack_top, + Process& owner_process); + + /** + * Creates a semaphore. + * @param initial_count Number of slots reserved for other threads + * @param max_count Maximum number of slots the semaphore can have + * @param name Optional name of semaphore + * @return The created semaphore + */ + ResultVal> CreateSemaphore(s32 initial_count, s32 max_count, + std::string name = "Unknown"); + + /** + * Creates a timer + * @param reset_type ResetType describing how to create the timer + * @param name Optional name of timer + * @return The created Timer + */ + SharedPtr CreateTimer(ResetType reset_type, std::string name = "Unknown"); + + /** + * Creates a pair of ServerPort and an associated ClientPort. + * + * @param max_sessions Maximum number of sessions to the port + * @param name Optional name of the ports + * @return The created port tuple + */ + std::tuple, SharedPtr> CreatePortPair( + u32 max_sessions, std::string name = "UnknownPort"); + + /** + * Creates a pair of ServerSession and an associated ClientSession. + * @param name Optional name of the ports. + * @param client_port Optional The ClientPort that spawned this session. + * @return The created session tuple + */ + std::tuple, SharedPtr> CreateSessionPair( + const std::string& name = "Unknown", SharedPtr client_port = nullptr); + + ResourceLimitList& ResourceLimit(); + const ResourceLimitList& ResourceLimit() const; + + /** + * Creates a shared memory object. + * @param owner_process Process that created this shared memory object. + * @param size Size of the memory block. Must be page-aligned. + * @param permissions Permission restrictions applied to the process which created the block. + * @param other_permissions Permission restrictions applied to other processes mapping the + * block. + * @param address The address from which to map the Shared Memory. + * @param region If the address is 0, the shared memory will be allocated in this region of the + * linear heap. + * @param name Optional object name, used for debugging purposes. + */ + SharedPtr CreateSharedMemory(Process* owner_process, u32 size, + MemoryPermission permissions, + MemoryPermission other_permissions, + VAddr address = 0, + MemoryRegion region = MemoryRegion::BASE, + std::string name = "Unknown"); + + /** + * Creates a shared memory object from a block of memory managed by an HLE applet. + * @param offset The offset into the heap block that the SharedMemory will map. + * @param size Size of the memory block. Must be page-aligned. + * @param permissions Permission restrictions applied to the process which created the block. + * @param other_permissions Permission restrictions applied to other processes mapping the + * block. + * @param name Optional object name, used for debugging purposes. + */ + SharedPtr CreateSharedMemoryForApplet(u32 offset, u32 size, + MemoryPermission permissions, + MemoryPermission other_permissions, + std::string name = "Unknown Applet"); + + u32 GenerateObjectID(); + + /// Retrieves a process from the current list of processes. + SharedPtr GetProcessById(u32 process_id) const; + + SharedPtr GetCurrentProcess() const; + void SetCurrentProcess(SharedPtr process); + + ThreadManager& GetThreadManager(); + const ThreadManager& GetThreadManager() const; + + TimerManager& GetTimerManager(); + const TimerManager& GetTimerManager() const; + + void MapSharedPages(VMManager& address_space); + + SharedPage::Handler& GetSharedPageHandler(); + const SharedPage::Handler& GetSharedPageHandler() const; + + MemoryRegionInfo* GetMemoryRegion(MemoryRegion region); + + std::array memory_regions; + + /// Adds a port to the named port table + void AddNamedPort(std::string name, SharedPtr port); + + /// Map of named ports managed by the kernel, which can be retrieved using the ConnectToPort + std::unordered_map> named_ports; + +private: + void MemoryInit(u32 mem_type); + + std::unique_ptr resource_limits; + std::atomic next_object_id{0}; + + // Note: keep the member order below in order to perform correct destruction. + // Thread manager is destructed before process list in order to Stop threads and clear thread + // info from their parent processes first. Timer manager is destructed after process list + // because timers are destructed along with process list and they need to clear info from the + // timer manager. + // TODO (wwylele): refactor the cleanup sequence to make this less complicated and sensitive. + + std::unique_ptr timer_manager; + + // TODO(Subv): Start the process ids from 10 for now, as lower PIDs are + // reserved for low-level services + u32 next_process_id = 10; + + // Lists all processes that exist in the current session. + std::vector> process_list; + + SharedPtr current_process; + + std::unique_ptr thread_manager; + + std::unique_ptr config_mem_handler; + std::unique_ptr shared_page_handler; +}; } // namespace Kernel diff --git a/src/core/hle/kernel/memory.cpp b/src/core/hle/kernel/memory.cpp index 3615e0a55..b53c273fe 100644 --- a/src/core/hle/kernel/memory.cpp +++ b/src/core/hle/kernel/memory.cpp @@ -12,8 +12,10 @@ #include "common/common_types.h" #include "common/logging/log.h" #include "core/core.h" -#include "core/hle/config_mem.h" +#include "core/hle/kernel/config_mem.h" #include "core/hle/kernel/memory.h" +#include "core/hle/kernel/process.h" +#include "core/hle/kernel/shared_page.h" #include "core/hle/kernel/vm_manager.h" #include "core/hle/result.h" #include "core/memory.h" @@ -23,8 +25,6 @@ namespace Kernel { -MemoryRegionInfo memory_regions[3]; - /// Size of the APPLICATION, SYSTEM and BASE memory regions (respectively) for each system /// memory configuration type. static const u32 memory_region_sizes[8][3] = { @@ -41,7 +41,7 @@ static const u32 memory_region_sizes[8][3] = { {0x0B200000, 0x02E00000, 0x02000000}, // 7 }; -void MemoryInit(u32 mem_type) { +void KernelSystem::MemoryInit(u32 mem_type) { // TODO(yuriks): On the n3DS, all o3DS configurations (<=5) are forced to 6 instead. ASSERT_MSG(mem_type <= 5, "New 3DS memory configuration aren't supported yet!"); ASSERT(mem_type != 1); @@ -50,13 +50,7 @@ void MemoryInit(u32 mem_type) { // the sizes specified in the memory_region_sizes table. VAddr base = 0; for (int i = 0; i < 3; ++i) { - memory_regions[i].base = base; - memory_regions[i].size = memory_region_sizes[mem_type][i]; - memory_regions[i].used = 0; - memory_regions[i].linear_heap_memory = std::make_shared>(); - // Reserve enough space for this region of FCRAM. - // We do not want this block of memory to be relocated when allocating from it. - memory_regions[i].linear_heap_memory->reserve(memory_regions[i].size); + memory_regions[i].Reset(base, memory_region_sizes[mem_type][i]); base += memory_regions[i].size; } @@ -64,25 +58,19 @@ void MemoryInit(u32 mem_type) { // We must've allocated the entire FCRAM by the end ASSERT(base == Memory::FCRAM_SIZE); - using ConfigMem::config_mem; + config_mem_handler = std::make_unique(); + auto& config_mem = config_mem_handler->GetConfigMem(); config_mem.app_mem_type = mem_type; // app_mem_malloc does not always match the configured size for memory_region[0]: in case the // n3DS type override is in effect it reports the size the game expects, not the real one. config_mem.app_mem_alloc = memory_region_sizes[mem_type][0]; config_mem.sys_mem_alloc = memory_regions[1].size; config_mem.base_mem_alloc = memory_regions[2].size; + + shared_page_handler = std::make_unique(); } -void MemoryShutdown() { - for (auto& region : memory_regions) { - region.base = 0; - region.size = 0; - region.used = 0; - region.linear_heap_memory = nullptr; - } -} - -MemoryRegionInfo* GetMemoryRegion(MemoryRegion region) { +MemoryRegionInfo* KernelSystem::GetMemoryRegion(MemoryRegion region) { switch (region) { case MemoryRegion::APPLICATION: return &memory_regions[0]; @@ -152,23 +140,93 @@ void HandleSpecialMapping(VMManager& address_space, const AddressMapping& mappin mapping.read_only ? VMAPermission::Read : VMAPermission::ReadWrite); } -void MapSharedPages(VMManager& address_space) { - auto cfg_mem_vma = address_space - .MapBackingMemory(Memory::CONFIG_MEMORY_VADDR, - reinterpret_cast(&ConfigMem::config_mem), - Memory::CONFIG_MEMORY_SIZE, MemoryState::Shared) - .Unwrap(); +void KernelSystem::MapSharedPages(VMManager& address_space) { + auto cfg_mem_vma = + address_space + .MapBackingMemory(Memory::CONFIG_MEMORY_VADDR, + reinterpret_cast(&config_mem_handler->GetConfigMem()), + Memory::CONFIG_MEMORY_SIZE, MemoryState::Shared) + .Unwrap(); address_space.Reprotect(cfg_mem_vma, VMAPermission::Read); auto shared_page_vma = address_space - .MapBackingMemory( - Memory::SHARED_PAGE_VADDR, - reinterpret_cast( - &Core::System::GetInstance().GetSharedPageHandler()->GetSharedPage()), - Memory::SHARED_PAGE_SIZE, MemoryState::Shared) + .MapBackingMemory(Memory::SHARED_PAGE_VADDR, + reinterpret_cast(&shared_page_handler->GetSharedPage()), + Memory::SHARED_PAGE_SIZE, MemoryState::Shared) .Unwrap(); address_space.Reprotect(shared_page_vma, VMAPermission::Read); } +void MemoryRegionInfo::Reset(u32 base, u32 size) { + this->base = base; + this->size = size; + used = 0; + free_blocks.clear(); + + // mark the entire region as free + free_blocks.insert(Interval::right_open(base, base + size)); +} + +MemoryRegionInfo::IntervalSet MemoryRegionInfo::HeapAllocate(u32 size) { + IntervalSet result; + u32 rest = size; + + // Try allocating from the higher address + for (auto iter = free_blocks.rbegin(); iter != free_blocks.rend(); ++iter) { + ASSERT(iter->bounds() == boost::icl::interval_bounds::right_open()); + if (iter->upper() - iter->lower() >= rest) { + // Requested size is fulfilled with this block + result += Interval(iter->upper() - rest, iter->upper()); + rest = 0; + break; + } + result += *iter; + rest -= iter->upper() - iter->lower(); + } + + if (rest != 0) { + // There is no enough free space + return {}; + } + + free_blocks -= result; + used += size; + return result; +} + +bool MemoryRegionInfo::LinearAllocate(u32 offset, u32 size) { + Interval interval(offset, offset + size); + if (!boost::icl::contains(free_blocks, interval)) { + // The requested range is already allocated + return false; + } + free_blocks -= interval; + used += size; + return true; +} + +std::optional MemoryRegionInfo::LinearAllocate(u32 size) { + // Find the first sufficient continuous block from the lower address + for (const auto& interval : free_blocks) { + ASSERT(interval.bounds() == boost::icl::interval_bounds::right_open()); + if (interval.upper() - interval.lower() >= size) { + Interval allocated(interval.lower(), interval.lower() + size); + free_blocks -= allocated; + used += size; + return allocated.lower(); + } + } + + // No sufficient block found + return {}; +} + +void MemoryRegionInfo::Free(u32 offset, u32 size) { + Interval interval(offset, offset + size); + ASSERT(!boost::icl::intersects(free_blocks, interval)); // must be allocated blocks + free_blocks += interval; + used -= size; +} + } // namespace Kernel diff --git a/src/core/hle/kernel/memory.h b/src/core/hle/kernel/memory.h index da6bb3563..67512df79 100644 --- a/src/core/hle/kernel/memory.h +++ b/src/core/hle/kernel/memory.h @@ -4,12 +4,13 @@ #pragma once -#include +#include +#include #include "common/common_types.h" -#include "core/hle/kernel/process.h" namespace Kernel { +struct AddressMapping; class VMManager; struct MemoryRegionInfo { @@ -17,15 +18,50 @@ struct MemoryRegionInfo { u32 size; u32 used; - std::shared_ptr> linear_heap_memory; + // The domain of the interval_set are offsets from start of FCRAM + using IntervalSet = boost::icl::interval_set; + using Interval = IntervalSet::interval_type; + + IntervalSet free_blocks; + + /** + * Reset the allocator state + * @param base The base offset the beginning of FCRAM. + * @param size The region size this allocator manages + */ + void Reset(u32 base, u32 size); + + /** + * Allocates memory from the heap. + * @param size The size of memory to allocate. + * @returns The set of blocks that make up the allocation request. Empty set if there is no + * enough space. + */ + IntervalSet HeapAllocate(u32 size); + + /** + * Allocates memory from the linear heap with specific address and size. + * @param offset the address offset to the beginning of FCRAM. + * @param size size of the memory to allocate. + * @returns true if the allocation is successful. false if the requested region is not free. + */ + bool LinearAllocate(u32 offset, u32 size); + + /** + * Allocates memory from the linear heap with only size specified. + * @param size size of the memory to allocate. + * @returns the address offset to the beginning of FCRAM; null if there is no enough space + */ + std::optional LinearAllocate(u32 size); + + /** + * Frees one segment of memory. The memory must have been allocated as heap or linear heap. + * @param offset the region address offset to the beginning of FCRAM. + * @param size the size of the region to free. + */ + void Free(u32 offset, u32 size); }; -void MemoryInit(u32 mem_type); -void MemoryShutdown(); -MemoryRegionInfo* GetMemoryRegion(MemoryRegion region); - void HandleSpecialMapping(VMManager& address_space, const AddressMapping& mapping); -void MapSharedPages(VMManager& address_space); -extern MemoryRegionInfo memory_regions[3]; } // namespace Kernel diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp index 02cbf613c..88d03d8f2 100644 --- a/src/core/hle/kernel/mutex.cpp +++ b/src/core/hle/kernel/mutex.cpp @@ -24,11 +24,11 @@ void ReleaseThreadMutexes(Thread* thread) { thread->held_mutexes.clear(); } -Mutex::Mutex() {} +Mutex::Mutex(KernelSystem& kernel) : WaitObject(kernel) {} Mutex::~Mutex() {} -SharedPtr Mutex::Create(bool initial_locked, std::string name) { - SharedPtr mutex(new Mutex); +SharedPtr KernelSystem::CreateMutex(bool initial_locked, std::string name) { + SharedPtr mutex(new Mutex(*this)); mutex->lock_count = 0; mutex->name = std::move(name); @@ -36,7 +36,7 @@ SharedPtr Mutex::Create(bool initial_locked, std::string name) { // Acquire mutex with current thread if initialized as locked if (initial_locked) - mutex->Acquire(GetCurrentThread()); + mutex->Acquire(thread_manager->GetCurrentThread()); return mutex; } diff --git a/src/core/hle/kernel/mutex.h b/src/core/hle/kernel/mutex.h index 3afb99c59..ec0e3f794 100644 --- a/src/core/hle/kernel/mutex.h +++ b/src/core/hle/kernel/mutex.h @@ -16,14 +16,6 @@ class Thread; class Mutex final : public WaitObject { public: - /** - * Creates a mutex. - * @param initial_locked Specifies if the mutex should be locked initially - * @param name Optional name of mutex - * @return Pointer to new Mutex object - */ - static SharedPtr Create(bool initial_locked, std::string name = "Unknown"); - std::string GetTypeName() const override { return "Mutex"; } @@ -61,8 +53,10 @@ public: ResultCode Release(Thread* thread); private: - Mutex(); + explicit Mutex(KernelSystem& kernel); ~Mutex() override; + + friend class KernelSystem; }; /** diff --git a/src/core/hle/kernel/object.cpp b/src/core/hle/kernel/object.cpp index 48bc80fb2..f9ca68218 100644 --- a/src/core/hle/kernel/object.cpp +++ b/src/core/hle/kernel/object.cpp @@ -3,10 +3,13 @@ // Refer to the license.txt file included. #include "common/assert.h" +#include "core/hle/kernel/kernel.h" #include "core/hle/kernel/object.h" namespace Kernel { +Object::Object(KernelSystem& kernel) : object_id{kernel.GenerateObjectID()} {} + Object::~Object() = default; bool Object::IsWaitable() const { diff --git a/src/core/hle/kernel/object.h b/src/core/hle/kernel/object.h index fbacf9359..f1fd03295 100644 --- a/src/core/hle/kernel/object.h +++ b/src/core/hle/kernel/object.h @@ -6,13 +6,13 @@ #include #include - -#include - #include "common/common_types.h" +#include "core/hle/kernel/kernel.h" namespace Kernel { +class KernelSystem; + using Handle = u32; enum class HandleType : u32 { @@ -37,14 +37,9 @@ enum { DEFAULT_STACK_SIZE = 0x4000, }; -enum class ResetType { - OneShot, - Sticky, - Pulse, -}; - class Object : NonCopyable { public: + explicit Object(KernelSystem& kernel); virtual ~Object(); /// Returns a unique identifier for the object. For debugging purposes only. @@ -66,15 +61,12 @@ public: */ bool IsWaitable() const; -public: - static std::atomic next_object_id; - private: friend void intrusive_ptr_add_ref(Object*); friend void intrusive_ptr_release(Object*); std::atomic ref_count{0}; - std::atomic object_id{next_object_id++}; + std::atomic object_id; }; // Special functions used by boost::instrusive_ptr to do automatic ref-counting @@ -88,9 +80,6 @@ inline void intrusive_ptr_release(Object* object) { } } -template -using SharedPtr = boost::intrusive_ptr; - /** * Attempts to downcast the given Object pointer to a pointer to T. * @return Derived pointer to the object, or `nullptr` if `object` isn't of type T. diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp index 8f3efe5d9..5e699937b 100644 --- a/src/core/hle/kernel/process.cpp +++ b/src/core/hle/kernel/process.cpp @@ -17,11 +17,8 @@ namespace Kernel { -// Lists all processes that exist in the current session. -static std::vector> process_list; - -SharedPtr CodeSet::Create(std::string name, u64 program_id) { - SharedPtr codeset(new CodeSet); +SharedPtr KernelSystem::CreateCodeSet(std::string name, u64 program_id) { + SharedPtr codeset(new CodeSet(*this)); codeset->name = std::move(name); codeset->program_id = program_id; @@ -29,18 +26,17 @@ SharedPtr CodeSet::Create(std::string name, u64 program_id) { return codeset; } -CodeSet::CodeSet() {} +CodeSet::CodeSet(KernelSystem& kernel) : Object(kernel) {} CodeSet::~CodeSet() {} -u32 Process::next_process_id; - -SharedPtr Process::Create(SharedPtr code_set) { - SharedPtr process(new Process); +SharedPtr KernelSystem::CreateProcess(SharedPtr code_set) { + SharedPtr process(new Process(*this)); process->codeset = std::move(code_set); process->flags.raw = 0; process->flags.memory_region.Assign(MemoryRegion::APPLICATION); process->status = ProcessStatus::Created; + process->process_id = ++next_process_id; process_list.push_back(process); return process; @@ -119,17 +115,13 @@ void Process::ParseKernelCaps(const u32* kernel_caps, std::size_t len) { } void Process::Run(s32 main_thread_priority, u32 stack_size) { - memory_region = GetMemoryRegion(flags.memory_region); + memory_region = kernel.GetMemoryRegion(flags.memory_region); auto MapSegment = [&](CodeSet::Segment& segment, VMAPermission permissions, MemoryState memory_state) { - auto vma = vm_manager - .MapMemoryBlock(segment.addr, codeset->memory, segment.offset, segment.size, - memory_state) - .Unwrap(); - vm_manager.Reprotect(vma, permissions); - misc_memory_used += segment.size; - memory_region->used += segment.size; + HeapAllocate(segment.addr, segment.size, permissions, memory_state, true); + Memory::WriteBlock(*this, segment.addr, codeset->memory->data() + segment.offset, + segment.size); }; // Map CodeSet segments @@ -138,16 +130,11 @@ void Process::Run(s32 main_thread_priority, u32 stack_size) { MapSegment(codeset->DataSegment(), VMAPermission::ReadWrite, MemoryState::Private); // Allocate and map stack - vm_manager - .MapMemoryBlock(Memory::HEAP_VADDR_END - stack_size, - std::make_shared>(stack_size, 0), 0, stack_size, - MemoryState::Locked) - .Unwrap(); - misc_memory_used += stack_size; - memory_region->used += stack_size; + HeapAllocate(Memory::HEAP_VADDR_END - stack_size, stack_size, VMAPermission::ReadWrite, + MemoryState::Locked, true); // Map special address mappings - MapSharedPages(vm_manager); + kernel.MapSharedPages(vm_manager); for (const auto& mapping : address_mappings) { HandleSpecialMapping(vm_manager, mapping); } @@ -155,7 +142,7 @@ void Process::Run(s32 main_thread_priority, u32 stack_size) { status = ProcessStatus::Running; vm_manager.LogLayout(Log::Level::Debug); - Kernel::SetupMainThread(codeset->entrypoint, main_thread_priority, this); + Kernel::SetupMainThread(kernel, codeset->entrypoint, main_thread_priority, this); } VAddr Process::GetLinearHeapAreaAddress() const { @@ -172,44 +159,55 @@ VAddr Process::GetLinearHeapLimit() const { return GetLinearHeapBase() + memory_region->size; } -ResultVal Process::HeapAllocate(VAddr target, u32 size, VMAPermission perms) { +ResultVal Process::HeapAllocate(VAddr target, u32 size, VMAPermission perms, + MemoryState memory_state, bool skip_range_check) { + LOG_DEBUG(Kernel, "Allocate heap target={:08X}, size={:08X}", target, size); if (target < Memory::HEAP_VADDR || target + size > Memory::HEAP_VADDR_END || target + size < target) { - return ERR_INVALID_ADDRESS; + if (!skip_range_check) { + LOG_ERROR(Kernel, "Invalid heap address"); + return ERR_INVALID_ADDRESS; + } } - if (heap_memory == nullptr) { - // Initialize heap - heap_memory = std::make_shared>(); - heap_start = heap_end = target; + auto vma = vm_manager.FindVMA(target); + if (vma->second.type != VMAType::Free || vma->second.base + vma->second.size < target + size) { + LOG_ERROR(Kernel, "Trying to allocate already allocated memory"); + return ERR_INVALID_ADDRESS_STATE; } - // If necessary, expand backing vector to cover new heap extents. - if (target < heap_start) { - heap_memory->insert(begin(*heap_memory), heap_start - target, 0); - heap_start = target; - vm_manager.RefreshMemoryBlockMappings(heap_memory.get()); + auto allocated_fcram = memory_region->HeapAllocate(size); + if (allocated_fcram.empty()) { + LOG_ERROR(Kernel, "Not enough space"); + return ERR_OUT_OF_HEAP_MEMORY; } - if (target + size > heap_end) { - heap_memory->insert(end(*heap_memory), (target + size) - heap_end, 0); - heap_end = target + size; - vm_manager.RefreshMemoryBlockMappings(heap_memory.get()); + + // Maps heap block by block + VAddr interval_target = target; + for (const auto& interval : allocated_fcram) { + u32 interval_size = interval.upper() - interval.lower(); + LOG_DEBUG(Kernel, "Allocated FCRAM region lower={:08X}, upper={:08X}", interval.lower(), + interval.upper()); + std::fill(Memory::fcram.begin() + interval.lower(), + Memory::fcram.begin() + interval.upper(), 0); + auto vma = vm_manager.MapBackingMemory( + interval_target, Memory::fcram.data() + interval.lower(), interval_size, memory_state); + ASSERT(vma.Succeeded()); + vm_manager.Reprotect(vma.Unwrap(), perms); + interval_target += interval_size; } - ASSERT(heap_end - heap_start == heap_memory->size()); - CASCADE_RESULT(auto vma, vm_manager.MapMemoryBlock(target, heap_memory, target - heap_start, - size, MemoryState::Private)); - vm_manager.Reprotect(vma, perms); + memory_used += size; + resource_limit->current_commit += size; - heap_used += size; - memory_region->used += size; - - return MakeResult(heap_end - size); + return MakeResult(target); } ResultCode Process::HeapFree(VAddr target, u32 size) { + LOG_DEBUG(Kernel, "Free heap target={:08X}, size={:08X}", target, size); if (target < Memory::HEAP_VADDR || target + size > Memory::HEAP_VADDR_END || target + size < target) { + LOG_ERROR(Kernel, "Invalid heap address"); return ERR_INVALID_ADDRESS; } @@ -217,59 +215,72 @@ ResultCode Process::HeapFree(VAddr target, u32 size) { return RESULT_SUCCESS; } - ResultCode result = vm_manager.UnmapRange(target, size); - if (result.IsError()) - return result; + // Free heaps block by block + CASCADE_RESULT(auto backing_blocks, vm_manager.GetBackingBlocksForRange(target, size)); + for (const auto [backing_memory, block_size] : backing_blocks) { + memory_region->Free(Memory::GetFCRAMOffset(backing_memory), block_size); + } - heap_used -= size; - memory_region->used -= size; + ResultCode result = vm_manager.UnmapRange(target, size); + ASSERT(result.IsSuccess()); + + memory_used -= size; + resource_limit->current_commit -= size; return RESULT_SUCCESS; } ResultVal Process::LinearAllocate(VAddr target, u32 size, VMAPermission perms) { - auto& linheap_memory = memory_region->linear_heap_memory; - - VAddr heap_end = GetLinearHeapBase() + (u32)linheap_memory->size(); - // Games and homebrew only ever seem to pass 0 here (which lets the kernel decide the address), - // but explicit addresses are also accepted and respected. + LOG_DEBUG(Kernel, "Allocate linear heap target={:08X}, size={:08X}", target, size); + u32 physical_offset; if (target == 0) { - target = heap_end; + auto offset = memory_region->LinearAllocate(size); + if (!offset) { + LOG_ERROR(Kernel, "Not enough space"); + return ERR_OUT_OF_HEAP_MEMORY; + } + physical_offset = *offset; + target = physical_offset + GetLinearHeapAreaAddress(); + } else { + if (target < GetLinearHeapBase() || target + size > GetLinearHeapLimit() || + target + size < target) { + LOG_ERROR(Kernel, "Invalid linear heap address"); + return ERR_INVALID_ADDRESS; + } + + // Kernel would crash/return error when target doesn't meet some requirement. + // It seems that target is required to follow immediately after the allocated linear heap, + // or cover the entire hole if there is any. + // Right now we just ignore these checks because they are still unclear. Further more, + // games and homebrew only ever seem to pass target = 0 here (which lets the kernel decide + // the address), so this not important. + + physical_offset = target - GetLinearHeapAreaAddress(); // relative to FCRAM + if (!memory_region->LinearAllocate(physical_offset, size)) { + LOG_ERROR(Kernel, "Trying to allocate already allocated memory"); + return ERR_INVALID_ADDRESS_STATE; + } } - if (target < GetLinearHeapBase() || target + size > GetLinearHeapLimit() || target > heap_end || - target + size < target) { + u8* backing_memory = Memory::fcram.data() + physical_offset; - return ERR_INVALID_ADDRESS; - } + std::fill(backing_memory, backing_memory + size, 0); + auto vma = vm_manager.MapBackingMemory(target, backing_memory, size, MemoryState::Continuous); + ASSERT(vma.Succeeded()); + vm_manager.Reprotect(vma.Unwrap(), perms); - // Expansion of the linear heap is only allowed if you do an allocation immediately at its - // end. It's possible to free gaps in the middle of the heap and then reallocate them later, - // but expansions are only allowed at the end. - if (target == heap_end) { - linheap_memory->insert(linheap_memory->end(), size, 0); - vm_manager.RefreshMemoryBlockMappings(linheap_memory.get()); - } - - // TODO(yuriks): As is, this lets processes map memory allocated by other processes from the - // same region. It is unknown if or how the 3DS kernel checks against this. - std::size_t offset = target - GetLinearHeapBase(); - CASCADE_RESULT(auto vma, vm_manager.MapMemoryBlock(target, linheap_memory, offset, size, - MemoryState::Continuous)); - vm_manager.Reprotect(vma, perms); - - linear_heap_used += size; - memory_region->used += size; + memory_used += size; + resource_limit->current_commit += size; + LOG_DEBUG(Kernel, "Allocated at target={:08X}", target); return MakeResult(target); } ResultCode Process::LinearFree(VAddr target, u32 size) { - auto& linheap_memory = memory_region->linear_heap_memory; - + LOG_DEBUG(Kernel, "Free linear heap target={:08X}, size={:08X}", target, size); if (target < GetLinearHeapBase() || target + size > GetLinearHeapLimit() || target + size < target) { - + LOG_ERROR(Kernel, "Invalid linear heap address"); return ERR_INVALID_ADDRESS; } @@ -277,41 +288,81 @@ ResultCode Process::LinearFree(VAddr target, u32 size) { return RESULT_SUCCESS; } - VAddr heap_end = GetLinearHeapBase() + (u32)linheap_memory->size(); - if (target + size > heap_end) { - return ERR_INVALID_ADDRESS_STATE; - } - ResultCode result = vm_manager.UnmapRange(target, size); - if (result.IsError()) + if (result.IsError()) { + LOG_ERROR(Kernel, "Trying to free already freed memory"); return result; - - linear_heap_used -= size; - memory_region->used -= size; - - if (target + size == heap_end) { - // End of linear heap has been freed, so check what's the last allocated block in it and - // reduce the size. - auto vma = vm_manager.FindVMA(target); - ASSERT(vma != vm_manager.vma_map.end()); - ASSERT(vma->second.type == VMAType::Free); - VAddr new_end = vma->second.base; - if (new_end >= GetLinearHeapBase()) { - linheap_memory->resize(new_end - GetLinearHeapBase()); - } } + memory_used -= size; + resource_limit->current_commit -= size; + + u32 physical_offset = target - GetLinearHeapAreaAddress(); // relative to FCRAM + memory_region->Free(physical_offset, size); + return RESULT_SUCCESS; } -Kernel::Process::Process() {} -Kernel::Process::~Process() {} +ResultCode Process::Map(VAddr target, VAddr source, u32 size, VMAPermission perms) { + LOG_DEBUG(Kernel, "Map memory target={:08X}, source={:08X}, size={:08X}, perms={:08X}", target, + source, size, static_cast(perms)); + if (source < Memory::HEAP_VADDR || source + size > Memory::HEAP_VADDR_END || + source + size < source) { + LOG_ERROR(Kernel, "Invalid source address"); + return ERR_INVALID_ADDRESS; + } -void ClearProcessList() { - process_list.clear(); + // TODO(wwylele): check target address range. Is it also restricted to heap region? + + auto vma = vm_manager.FindVMA(target); + if (vma->second.type != VMAType::Free || vma->second.base + vma->second.size < target + size) { + LOG_ERROR(Kernel, "Trying to map to already allocated memory"); + return ERR_INVALID_ADDRESS_STATE; + } + + // Mark source region as Aliased + CASCADE_CODE(vm_manager.ChangeMemoryState(source, size, MemoryState::Private, + VMAPermission::ReadWrite, MemoryState::Aliased, + VMAPermission::ReadWrite)); + + CASCADE_RESULT(auto backing_blocks, vm_manager.GetBackingBlocksForRange(source, size)); + VAddr interval_target = target; + for (const auto [backing_memory, block_size] : backing_blocks) { + auto target_vma = vm_manager.MapBackingMemory(interval_target, backing_memory, block_size, + MemoryState::Alias); + interval_target += block_size; + } + + return RESULT_SUCCESS; +} +ResultCode Process::Unmap(VAddr target, VAddr source, u32 size, VMAPermission perms) { + LOG_DEBUG(Kernel, "Unmap memory target={:08X}, source={:08X}, size={:08X}, perms={:08X}", + target, source, size, static_cast(perms)); + if (source < Memory::HEAP_VADDR || source + size > Memory::HEAP_VADDR_END || + source + size < source) { + LOG_ERROR(Kernel, "Invalid source address"); + return ERR_INVALID_ADDRESS; + } + + // TODO(wwylele): check target address range. Is it also restricted to heap region? + + // TODO(wwylele): check that the source and the target are actually a pair created by Map + // Should return error 0xD8E007F5 in this case + + CASCADE_CODE(vm_manager.UnmapRange(target, size)); + + // Change back source region state. Note that the permission is reprotected according to param + CASCADE_CODE(vm_manager.ChangeMemoryState(source, size, MemoryState::Aliased, + VMAPermission::None, MemoryState::Private, perms)); + + return RESULT_SUCCESS; } -SharedPtr GetProcessById(u32 process_id) { +Kernel::Process::Process(KernelSystem& kernel) + : Object(kernel), handle_table(kernel), kernel(kernel) {} +Kernel::Process::~Process() {} + +SharedPtr KernelSystem::GetProcessById(u32 process_id) const { auto itr = std::find_if( process_list.begin(), process_list.end(), [&](const SharedPtr& process) { return process->process_id == process_id; }); @@ -321,6 +372,4 @@ SharedPtr GetProcessById(u32 process_id) { return *itr; } - -SharedPtr g_current_process; } // namespace Kernel diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h index 1081be063..6924afe36 100644 --- a/src/core/hle/kernel/process.h +++ b/src/core/hle/kernel/process.h @@ -13,6 +13,7 @@ #include #include "common/bit_field.h" #include "common/common_types.h" +#include "core/hle/kernel/handle_table.h" #include "core/hle/kernel/object.h" #include "core/hle/kernel/vm_manager.h" @@ -26,12 +27,6 @@ struct AddressMapping { bool unk_flag; }; -enum class MemoryRegion : u16 { - APPLICATION = 1, - SYSTEM = 2, - BASE = 3, -}; - union ProcessFlags { u16 raw; @@ -55,15 +50,14 @@ enum class ProcessStatus { Created, Running, Exited }; class ResourceLimit; struct MemoryRegionInfo; -struct CodeSet final : public Object { +class CodeSet final : public Object { +public: struct Segment { std::size_t offset = 0; VAddr addr = 0; u32 size = 0; }; - static SharedPtr Create(std::string name, u64 program_id); - std::string GetTypeName() const override { return "CodeSet"; } @@ -111,14 +105,14 @@ struct CodeSet final : public Object { u64 program_id; private: - CodeSet(); + explicit CodeSet(KernelSystem& kernel); ~CodeSet() override; + + friend class KernelSystem; }; class Process final : public Object { public: - static SharedPtr Create(SharedPtr code_set); - std::string GetTypeName() const override { return "Process"; } @@ -131,7 +125,7 @@ public: return HANDLE_TYPE; } - static u32 next_process_id; + HandleTable handle_table; SharedPtr codeset; /// Resource limit descriptor for this process @@ -153,7 +147,7 @@ public: ProcessStatus status; /// The id of this process - u32 process_id = next_process_id++; + u32 process_id; /** * Parses a list of kernel capability descriptors (as found in the ExHeader) and applies them @@ -171,15 +165,7 @@ public: VMManager vm_manager; - // Memory used to back the allocations in the regular heap. A single vector is used to cover - // the entire virtual address space extents that bound the allocations, including any holes. - // This makes deallocation and reallocation of holes fast and keeps process memory contiguous - // in the emulator address space, allowing Memory::GetPointer to be reasonably safe. - std::shared_ptr> heap_memory; - // The left/right bounds of the address space covered by heap_memory. - VAddr heap_start = 0, heap_end = 0; - - u32 heap_used = 0, linear_heap_used = 0, misc_memory_used = 0; + u32 memory_used = 0; MemoryRegionInfo* memory_region = nullptr; @@ -194,21 +180,22 @@ public: VAddr GetLinearHeapBase() const; VAddr GetLinearHeapLimit() const; - ResultVal HeapAllocate(VAddr target, u32 size, VMAPermission perms); + ResultVal HeapAllocate(VAddr target, u32 size, VMAPermission perms, + MemoryState memory_state = MemoryState::Private, + bool skip_range_check = false); ResultCode HeapFree(VAddr target, u32 size); ResultVal LinearAllocate(VAddr target, u32 size, VMAPermission perms); ResultCode LinearFree(VAddr target, u32 size); + ResultCode Map(VAddr target, VAddr source, u32 size, VMAPermission perms); + ResultCode Unmap(VAddr target, VAddr source, u32 size, VMAPermission perms); + private: - Process(); + explicit Process(Kernel::KernelSystem& kernel); ~Process() override; + + friend class KernelSystem; + KernelSystem& kernel; }; - -void ClearProcessList(); - -/// Retrieves a process from the current list of processes. -SharedPtr GetProcessById(u32 process_id); - -extern SharedPtr g_current_process; } // namespace Kernel diff --git a/src/core/hle/kernel/resource_limit.cpp b/src/core/hle/kernel/resource_limit.cpp index ffe9f5108..3498acb23 100644 --- a/src/core/hle/kernel/resource_limit.cpp +++ b/src/core/hle/kernel/resource_limit.cpp @@ -9,19 +9,17 @@ namespace Kernel { -static SharedPtr resource_limits[4]; - -ResourceLimit::ResourceLimit() {} +ResourceLimit::ResourceLimit(KernelSystem& kernel) : Object(kernel) {} ResourceLimit::~ResourceLimit() {} -SharedPtr ResourceLimit::Create(std::string name) { - SharedPtr resource_limit(new ResourceLimit); +SharedPtr ResourceLimit::Create(KernelSystem& kernel, std::string name) { + SharedPtr resource_limit(new ResourceLimit(kernel)); resource_limit->name = std::move(name); return resource_limit; } -SharedPtr ResourceLimit::GetForCategory(ResourceLimitCategory category) { +SharedPtr ResourceLimitList::GetForCategory(ResourceLimitCategory category) { switch (category) { case ResourceLimitCategory::APPLICATION: case ResourceLimitCategory::SYS_APPLET: @@ -90,10 +88,10 @@ u32 ResourceLimit::GetMaxResourceValue(u32 resource) const { } } -void ResourceLimitsInit() { +ResourceLimitList::ResourceLimitList(KernelSystem& kernel) { // Create the four resource limits that the system uses // Create the APPLICATION resource limit - SharedPtr resource_limit = ResourceLimit::Create("Applications"); + SharedPtr resource_limit = ResourceLimit::Create(kernel, "Applications"); resource_limit->max_priority = 0x18; resource_limit->max_commit = 0x4000000; resource_limit->max_threads = 0x20; @@ -107,7 +105,7 @@ void ResourceLimitsInit() { resource_limits[static_cast(ResourceLimitCategory::APPLICATION)] = resource_limit; // Create the SYS_APPLET resource limit - resource_limit = ResourceLimit::Create("System Applets"); + resource_limit = ResourceLimit::Create(kernel, "System Applets"); resource_limit->max_priority = 0x4; resource_limit->max_commit = 0x5E00000; resource_limit->max_threads = 0x1D; @@ -121,7 +119,7 @@ void ResourceLimitsInit() { resource_limits[static_cast(ResourceLimitCategory::SYS_APPLET)] = resource_limit; // Create the LIB_APPLET resource limit - resource_limit = ResourceLimit::Create("Library Applets"); + resource_limit = ResourceLimit::Create(kernel, "Library Applets"); resource_limit->max_priority = 0x4; resource_limit->max_commit = 0x600000; resource_limit->max_threads = 0xE; @@ -135,7 +133,7 @@ void ResourceLimitsInit() { resource_limits[static_cast(ResourceLimitCategory::LIB_APPLET)] = resource_limit; // Create the OTHER resource limit - resource_limit = ResourceLimit::Create("Others"); + resource_limit = ResourceLimit::Create(kernel, "Others"); resource_limit->max_priority = 0x4; resource_limit->max_commit = 0x2180000; resource_limit->max_threads = 0xE1; @@ -149,6 +147,6 @@ void ResourceLimitsInit() { resource_limits[static_cast(ResourceLimitCategory::OTHER)] = resource_limit; } -void ResourceLimitsShutdown() {} +ResourceLimitList::~ResourceLimitList() = default; } // namespace Kernel diff --git a/src/core/hle/kernel/resource_limit.h b/src/core/hle/kernel/resource_limit.h index 7d6e8611d..3b8b79d0c 100644 --- a/src/core/hle/kernel/resource_limit.h +++ b/src/core/hle/kernel/resource_limit.h @@ -4,6 +4,7 @@ #pragma once +#include #include "common/common_types.h" #include "core/hle/kernel/object.h" @@ -34,14 +35,7 @@ public: /** * Creates a resource limit object. */ - static SharedPtr Create(std::string name = "Unknown"); - - /** - * Retrieves the resource limit associated with the specified resource limit category. - * @param category The resource limit category - * @returns The resource limit associated with the category - */ - static SharedPtr GetForCategory(ResourceLimitCategory category); + static SharedPtr Create(KernelSystem& kernel, std::string name = "Unknown"); std::string GetTypeName() const override { return "ResourceLimit"; @@ -113,14 +107,24 @@ public: s32 current_cpu_time = 0; private: - ResourceLimit(); + explicit ResourceLimit(KernelSystem& kernel); ~ResourceLimit() override; }; -/// Initializes the resource limits -void ResourceLimitsInit(); +class ResourceLimitList { +public: + explicit ResourceLimitList(KernelSystem& kernel); + ~ResourceLimitList(); -// Destroys the resource limits -void ResourceLimitsShutdown(); + /** + * Retrieves the resource limit associated with the specified resource limit category. + * @param category The resource limit category + * @returns The resource limit associated with the category + */ + SharedPtr GetForCategory(ResourceLimitCategory category); + +private: + std::array, 4> resource_limits; +}; } // namespace Kernel diff --git a/src/core/hle/kernel/semaphore.cpp b/src/core/hle/kernel/semaphore.cpp index 4ec1669ba..8193f848d 100644 --- a/src/core/hle/kernel/semaphore.cpp +++ b/src/core/hle/kernel/semaphore.cpp @@ -10,16 +10,16 @@ namespace Kernel { -Semaphore::Semaphore() {} +Semaphore::Semaphore(KernelSystem& kernel) : WaitObject(kernel) {} Semaphore::~Semaphore() {} -ResultVal> Semaphore::Create(s32 initial_count, s32 max_count, - std::string name) { +ResultVal> KernelSystem::CreateSemaphore(s32 initial_count, s32 max_count, + std::string name) { if (initial_count > max_count) return ERR_INVALID_COMBINATION_KERNEL; - SharedPtr semaphore(new Semaphore); + SharedPtr semaphore(new Semaphore(*this)); // When the semaphore is created, some slots are reserved for other threads, // and the rest is reserved for the caller thread diff --git a/src/core/hle/kernel/semaphore.h b/src/core/hle/kernel/semaphore.h index 6019b09a2..7303ee7d1 100644 --- a/src/core/hle/kernel/semaphore.h +++ b/src/core/hle/kernel/semaphore.h @@ -15,16 +15,6 @@ namespace Kernel { class Semaphore final : public WaitObject { public: - /** - * Creates a semaphore. - * @param initial_count Number of slots reserved for other threads - * @param max_count Maximum number of slots the semaphore can have - * @param name Optional name of semaphore - * @return The created semaphore - */ - static ResultVal> Create(s32 initial_count, s32 max_count, - std::string name = "Unknown"); - std::string GetTypeName() const override { return "Semaphore"; } @@ -52,8 +42,10 @@ public: ResultVal Release(s32 release_count); private: - Semaphore(); + explicit Semaphore(KernelSystem& kernel); ~Semaphore() override; + + friend class KernelSystem; }; } // namespace Kernel diff --git a/src/core/hle/kernel/server_port.cpp b/src/core/hle/kernel/server_port.cpp index 7b6211fd8..e17e699a4 100644 --- a/src/core/hle/kernel/server_port.cpp +++ b/src/core/hle/kernel/server_port.cpp @@ -13,7 +13,7 @@ namespace Kernel { -ServerPort::ServerPort() {} +ServerPort::ServerPort(KernelSystem& kernel) : WaitObject(kernel) {} ServerPort::~ServerPort() {} ResultVal> ServerPort::Accept() { @@ -35,11 +35,11 @@ void ServerPort::Acquire(Thread* thread) { ASSERT_MSG(!ShouldWait(thread), "object unavailable!"); } -std::tuple, SharedPtr> ServerPort::CreatePortPair( +std::tuple, SharedPtr> KernelSystem::CreatePortPair( u32 max_sessions, std::string name) { - SharedPtr server_port(new ServerPort); - SharedPtr client_port(new ClientPort); + SharedPtr server_port(new ServerPort(*this)); + SharedPtr client_port(new ClientPort(*this)); server_port->name = name + "_Server"; client_port->name = name + "_Client"; diff --git a/src/core/hle/kernel/server_port.h b/src/core/hle/kernel/server_port.h index 6d458b188..ee09efba1 100644 --- a/src/core/hle/kernel/server_port.h +++ b/src/core/hle/kernel/server_port.h @@ -20,16 +20,6 @@ class SessionRequestHandler; class ServerPort final : public WaitObject { public: - /** - * Creates a pair of ServerPort and an associated ClientPort. - * - * @param max_sessions Maximum number of sessions to the port - * @param name Optional name of the ports - * @return The created port tuple - */ - static std::tuple, SharedPtr> CreatePortPair( - u32 max_sessions, std::string name = "UnknownPort"); - std::string GetTypeName() const override { return "ServerPort"; } @@ -69,8 +59,10 @@ public: void Acquire(Thread* thread) override; private: - ServerPort(); + explicit ServerPort(KernelSystem& kernel); ~ServerPort() override; + + friend class KernelSystem; }; } // namespace Kernel diff --git a/src/core/hle/kernel/server_session.cpp b/src/core/hle/kernel/server_session.cpp index 71f702b9e..83e2ec4fa 100644 --- a/src/core/hle/kernel/server_session.cpp +++ b/src/core/hle/kernel/server_session.cpp @@ -13,7 +13,7 @@ namespace Kernel { -ServerSession::ServerSession() = default; +ServerSession::ServerSession(KernelSystem& kernel) : WaitObject(kernel) {} ServerSession::~ServerSession() { // This destructor will be called automatically when the last ServerSession handle is closed by // the emulated application. @@ -28,8 +28,8 @@ ServerSession::~ServerSession() { parent->server = nullptr; } -ResultVal> ServerSession::Create(std::string name) { - SharedPtr server_session(new ServerSession); +ResultVal> ServerSession::Create(KernelSystem& kernel, std::string name) { + SharedPtr server_session(new ServerSession(kernel)); server_session->name = std::move(name); server_session->parent = nullptr; @@ -100,10 +100,10 @@ ResultCode ServerSession::HandleSyncRequest(SharedPtr thread) { return RESULT_SUCCESS; } -ServerSession::SessionPair ServerSession::CreateSessionPair(const std::string& name, - SharedPtr port) { - auto server_session = ServerSession::Create(name + "_Server").Unwrap(); - SharedPtr client_session(new ClientSession); +std::tuple, SharedPtr> KernelSystem::CreateSessionPair( + const std::string& name, SharedPtr port) { + auto server_session = ServerSession::Create(*this, name + "_Server").Unwrap(); + SharedPtr client_session(new ClientSession(*this)); client_session->name = name + "_Client"; std::shared_ptr parent(new Session); diff --git a/src/core/hle/kernel/server_session.h b/src/core/hle/kernel/server_session.h index c739bae5e..18411e417 100644 --- a/src/core/hle/kernel/server_session.h +++ b/src/core/hle/kernel/server_session.h @@ -48,17 +48,6 @@ public: return HANDLE_TYPE; } - using SessionPair = std::tuple, SharedPtr>; - - /** - * Creates a pair of ServerSession and an associated ClientSession. - * @param name Optional name of the ports. - * @param client_port Optional The ClientPort that spawned this session. - * @return The created session tuple - */ - static SessionPair CreateSessionPair(const std::string& name = "Unknown", - SharedPtr client_port = nullptr); - /** * Sets the HLE handler for the session. This handler will be called to service IPC requests * instead of the regular IPC machinery. (The regular IPC machinery is currently not @@ -95,16 +84,20 @@ public: SharedPtr currently_handling; private: - ServerSession(); + explicit ServerSession(KernelSystem& kernel); ~ServerSession() override; /** * Creates a server session. The server session can have an optional HLE handler, * which will be invoked to handle the IPC requests that this session receives. + * @param kernel The kernel instance to create the server session on * @param name Optional name of the server session. * @return The created server session */ - static ResultVal> Create(std::string name = "Unknown"); + static ResultVal> Create(KernelSystem& kernel, + std::string name = "Unknown"); + + friend class KernelSystem; }; } // namespace Kernel diff --git a/src/core/hle/kernel/shared_memory.cpp b/src/core/hle/kernel/shared_memory.cpp index f5b9dad2c..f32651d49 100644 --- a/src/core/hle/kernel/shared_memory.cpp +++ b/src/core/hle/kernel/shared_memory.cpp @@ -11,14 +11,20 @@ namespace Kernel { -SharedMemory::SharedMemory() {} -SharedMemory::~SharedMemory() {} +SharedMemory::SharedMemory(KernelSystem& kernel) : Object(kernel), kernel(kernel) {} +SharedMemory::~SharedMemory() { + for (const auto& interval : holding_memory) { + kernel.GetMemoryRegion(MemoryRegion::SYSTEM) + ->Free(interval.lower(), interval.upper() - interval.lower()); + } +} -SharedPtr SharedMemory::Create(SharedPtr owner_process, u32 size, - MemoryPermission permissions, - MemoryPermission other_permissions, VAddr address, - MemoryRegion region, std::string name) { - SharedPtr shared_memory(new SharedMemory); +SharedPtr KernelSystem::CreateSharedMemory(Process* owner_process, u32 size, + MemoryPermission permissions, + MemoryPermission other_permissions, + VAddr address, MemoryRegion region, + std::string name) { + SharedPtr shared_memory(new SharedMemory(*this)); shared_memory->owner_process = owner_process; shared_memory->name = std::move(name); @@ -30,64 +36,53 @@ SharedPtr SharedMemory::Create(SharedPtr owner_process, u // We need to allocate a block from the Linear Heap ourselves. // We'll manually allocate some memory from the linear heap in the specified region. MemoryRegionInfo* memory_region = GetMemoryRegion(region); - auto& linheap_memory = memory_region->linear_heap_memory; + auto offset = memory_region->LinearAllocate(size); - ASSERT_MSG(linheap_memory->size() + size <= memory_region->size, - "Not enough space in region to allocate shared memory!"); + ASSERT_MSG(offset, "Not enough space in region to allocate shared memory!"); - shared_memory->backing_block = linheap_memory; - shared_memory->backing_block_offset = linheap_memory->size(); - // Allocate some memory from the end of the linear heap for this region. - linheap_memory->insert(linheap_memory->end(), size, 0); - memory_region->used += size; - - shared_memory->linear_heap_phys_address = - Memory::FCRAM_PADDR + memory_region->base + - static_cast(shared_memory->backing_block_offset); + std::fill(Memory::fcram.data() + *offset, Memory::fcram.data() + *offset + size, 0); + shared_memory->backing_blocks = {{Memory::fcram.data() + *offset, size}}; + shared_memory->holding_memory += MemoryRegionInfo::Interval(*offset, *offset + size); + shared_memory->linear_heap_phys_offset = *offset; // Increase the amount of used linear heap memory for the owner process. if (shared_memory->owner_process != nullptr) { - shared_memory->owner_process->linear_heap_used += size; - } - - // Refresh the address mappings for the current process. - if (Kernel::g_current_process != nullptr) { - Kernel::g_current_process->vm_manager.RefreshMemoryBlockMappings(linheap_memory.get()); + shared_memory->owner_process->memory_used += size; } } else { auto& vm_manager = shared_memory->owner_process->vm_manager; // The memory is already available and mapped in the owner process. - auto vma = vm_manager.FindVMA(address); - ASSERT_MSG(vma != vm_manager.vma_map.end(), "Invalid memory address"); - ASSERT_MSG(vma->second.backing_block, "Backing block doesn't exist for address"); - // The returned VMA might be a bigger one encompassing the desired address. - auto vma_offset = address - vma->first; - ASSERT_MSG(vma_offset + size <= vma->second.size, - "Shared memory exceeds bounds of mapped block"); - - shared_memory->backing_block = vma->second.backing_block; - shared_memory->backing_block_offset = vma->second.offset + vma_offset; + auto backing_blocks = vm_manager.GetBackingBlocksForRange(address, size); + ASSERT_MSG(backing_blocks.Succeeded(), "Trying to share freed memory"); + shared_memory->backing_blocks = std::move(backing_blocks).Unwrap(); } shared_memory->base_address = address; return shared_memory; } -SharedPtr SharedMemory::CreateForApplet(std::shared_ptr> heap_block, - u32 offset, u32 size, - MemoryPermission permissions, - MemoryPermission other_permissions, - std::string name) { - SharedPtr shared_memory(new SharedMemory); +SharedPtr KernelSystem::CreateSharedMemoryForApplet( + u32 offset, u32 size, MemoryPermission permissions, MemoryPermission other_permissions, + std::string name) { + SharedPtr shared_memory(new SharedMemory(*this)); + // Allocate memory in heap + MemoryRegionInfo* memory_region = GetMemoryRegion(MemoryRegion::SYSTEM); + auto backing_blocks = memory_region->HeapAllocate(size); + ASSERT_MSG(!backing_blocks.empty(), "Not enough space in region to allocate shared memory!"); + shared_memory->holding_memory = backing_blocks; shared_memory->owner_process = nullptr; shared_memory->name = std::move(name); shared_memory->size = size; shared_memory->permissions = permissions; shared_memory->other_permissions = other_permissions; - shared_memory->backing_block = heap_block; - shared_memory->backing_block_offset = offset; + for (const auto& interval : backing_blocks) { + shared_memory->backing_blocks.push_back( + {Memory::fcram.data() + interval.lower(), interval.upper() - interval.lower()}); + std::fill(Memory::fcram.data() + interval.lower(), Memory::fcram.data() + interval.upper(), + 0); + } shared_memory->base_address = Memory::HEAP_VADDR + offset; return shared_memory; @@ -147,24 +142,32 @@ ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermi if (base_address == 0 && target_address == 0) { // Calculate the address at which to map the memory block. - auto maybe_vaddr = Memory::PhysicalToVirtualAddress(linear_heap_phys_address); - ASSERT(maybe_vaddr); - target_address = *maybe_vaddr; + // Note: even on new firmware versions, the target address is still in the old linear heap + // region. This exception is made to keep the shared font compatibility. See + // APT:GetSharedFont for detail. + target_address = linear_heap_phys_offset + Memory::LINEAR_HEAP_VADDR; + } + + auto vma = target_process->vm_manager.FindVMA(target_address); + if (vma->second.type != VMAType::Free || + vma->second.base + vma->second.size < target_address + size) { + LOG_ERROR(Kernel, + "cannot map id={}, address=0x{:08X} name={}, mapping to already allocated memory", + GetObjectId(), address, name); + return ERR_INVALID_ADDRESS_STATE; } // Map the memory block into the target process - auto result = target_process->vm_manager.MapMemoryBlock( - target_address, backing_block, backing_block_offset, size, MemoryState::Shared); - if (result.Failed()) { - LOG_ERROR( - Kernel, - "cannot map id={}, target_address=0x{:08X} name={}, error mapping to virtual memory", - GetObjectId(), target_address, name); - return result.Code(); + VAddr interval_target = target_address; + for (const auto& interval : backing_blocks) { + auto vma = target_process->vm_manager.MapBackingMemory( + interval_target, interval.first, interval.second, MemoryState::Shared); + ASSERT(vma.Succeeded()); + target_process->vm_manager.Reprotect(vma.Unwrap(), ConvertPermissions(permissions)); + interval_target += interval.second; } - return target_process->vm_manager.ReprotectRange(target_address, size, - ConvertPermissions(permissions)); + return RESULT_SUCCESS; } ResultCode SharedMemory::Unmap(Process* target_process, VAddr address) { @@ -180,7 +183,10 @@ VMAPermission SharedMemory::ConvertPermissions(MemoryPermission permission) { }; u8* SharedMemory::GetPointer(u32 offset) { - return backing_block->data() + backing_block_offset + offset; + if (backing_blocks.size() != 1) { + LOG_WARNING(Kernel, "Unsafe GetPointer on discontinuous SharedMemory"); + } + return backing_blocks[0].first + offset; } } // namespace Kernel diff --git a/src/core/hle/kernel/shared_memory.h b/src/core/hle/kernel/shared_memory.h index 5ccd81572..a48f607ba 100644 --- a/src/core/hle/kernel/shared_memory.h +++ b/src/core/hle/kernel/shared_memory.h @@ -12,55 +12,8 @@ namespace Kernel { -/// Permissions for mapped shared memory blocks -enum class MemoryPermission : u32 { - None = 0, - Read = (1u << 0), - Write = (1u << 1), - ReadWrite = (Read | Write), - Execute = (1u << 2), - ReadExecute = (Read | Execute), - WriteExecute = (Write | Execute), - ReadWriteExecute = (Read | Write | Execute), - DontCare = (1u << 28) -}; - class SharedMemory final : public Object { public: - /** - * Creates a shared memory object. - * @param owner_process Process that created this shared memory object. - * @param size Size of the memory block. Must be page-aligned. - * @param permissions Permission restrictions applied to the process which created the block. - * @param other_permissions Permission restrictions applied to other processes mapping the - * block. - * @param address The address from which to map the Shared Memory. - * @param region If the address is 0, the shared memory will be allocated in this region of the - * linear heap. - * @param name Optional object name, used for debugging purposes. - */ - static SharedPtr Create(SharedPtr owner_process, u32 size, - MemoryPermission permissions, - MemoryPermission other_permissions, VAddr address = 0, - MemoryRegion region = MemoryRegion::BASE, - std::string name = "Unknown"); - - /** - * Creates a shared memory object from a block of memory managed by an HLE applet. - * @param heap_block Heap block of the HLE applet. - * @param offset The offset into the heap block that the SharedMemory will map. - * @param size Size of the memory block. Must be page-aligned. - * @param permissions Permission restrictions applied to the process which created the block. - * @param other_permissions Permission restrictions applied to other processes mapping the - * block. - * @param name Optional object name, used for debugging purposes. - */ - static SharedPtr CreateForApplet(std::shared_ptr> heap_block, - u32 offset, u32 size, - MemoryPermission permissions, - MemoryPermission other_permissions, - std::string name = "Unknown Applet"); - std::string GetTypeName() const override { return "SharedMemory"; } @@ -105,16 +58,14 @@ public: u8* GetPointer(u32 offset = 0); /// Process that created this shared memory block. - SharedPtr owner_process; + Process* owner_process; /// Address of shared memory block in the owner process if specified. VAddr base_address; - /// Physical address of the shared memory block in the linear heap if no address was specified + /// Offset in FCRAM of the shared memory block in the linear heap if no address was specified /// during creation. - PAddr linear_heap_phys_address; + PAddr linear_heap_phys_offset; /// Backing memory for this shared memory block. - std::shared_ptr> backing_block; - /// Offset into the backing block for this shared memory. - std::size_t backing_block_offset; + std::vector> backing_blocks; /// Size of the memory block. Page-aligned. u32 size; /// Permission restrictions applied to the process which created the block. @@ -124,9 +75,14 @@ public: /// Name of shared memory object. std::string name; + MemoryRegionInfo::IntervalSet holding_memory; + private: - SharedMemory(); + explicit SharedMemory(KernelSystem& kernel); ~SharedMemory() override; + + friend class KernelSystem; + KernelSystem& kernel; }; } // namespace Kernel diff --git a/src/core/hle/shared_page.cpp b/src/core/hle/kernel/shared_page.cpp similarity index 85% rename from src/core/hle/shared_page.cpp rename to src/core/hle/kernel/shared_page.cpp index 962293609..e2af01e0d 100644 --- a/src/core/hle/shared_page.cpp +++ b/src/core/hle/kernel/shared_page.cpp @@ -4,9 +4,10 @@ #include #include +#include "core/core.h" #include "core/core_timing.h" +#include "core/hle/kernel/shared_page.h" #include "core/hle/service/ptm/ptm.h" -#include "core/hle/shared_page.h" #include "core/movie.h" #include "core/settings.h" @@ -53,9 +54,9 @@ Handler::Handler() { init_time = GetInitTime(); using namespace std::placeholders; - update_time_event = CoreTiming::RegisterEvent( + update_time_event = Core::System::GetInstance().CoreTiming().RegisterEvent( "SharedPage::UpdateTimeCallback", std::bind(&Handler::UpdateTimeCallback, this, _1, _2)); - CoreTiming::ScheduleEvent(0, update_time_event); + Core::System::GetInstance().CoreTiming().ScheduleEvent(0, update_time_event); float slidestate = Settings::values.toggle_3d ? (float_le)Settings::values.factor_3d / 100 : 0.0f; @@ -65,8 +66,8 @@ Handler::Handler() { /// Gets system time in 3DS format. The epoch is Jan 1900, and the unit is millisecond. u64 Handler::GetSystemTime() const { std::chrono::milliseconds now = - init_time + - std::chrono::duration_cast(CoreTiming::GetGlobalTimeUs()); + init_time + std::chrono::duration_cast( + Core::System::GetInstance().CoreTiming().GetGlobalTimeUs()); // 3DS system does't allow user to set a time before Jan 1 2000, // so we use it as an auxiliary epoch to calculate the console time. @@ -97,14 +98,15 @@ void Handler::UpdateTimeCallback(u64 userdata, int cycles_late) { shared_page.date_time_counter % 2 ? shared_page.date_time_0 : shared_page.date_time_1; date_time.date_time = GetSystemTime(); - date_time.update_tick = CoreTiming::GetTicks(); + date_time.update_tick = Core::System::GetInstance().CoreTiming().GetTicks(); date_time.tick_to_second_coefficient = BASE_CLOCK_RATE_ARM11; date_time.tick_offset = 0; ++shared_page.date_time_counter; // system time is updated hourly - CoreTiming::ScheduleEvent(msToCycles(60 * 60 * 1000) - cycles_late, update_time_event); + Core::System::GetInstance().CoreTiming().ScheduleEvent(msToCycles(60 * 60 * 1000) - cycles_late, + update_time_event); } void Handler::SetMacAddress(const MacAddress& addr) { diff --git a/src/core/hle/shared_page.h b/src/core/hle/kernel/shared_page.h similarity index 97% rename from src/core/hle/shared_page.h rename to src/core/hle/kernel/shared_page.h index 57f3bc3f2..7b6dc2e06 100644 --- a/src/core/hle/shared_page.h +++ b/src/core/hle/kernel/shared_page.h @@ -21,8 +21,8 @@ //////////////////////////////////////////////////////////////////////////////////////////////////// -namespace CoreTiming { -struct EventType; +namespace Core { +struct TimingEventType; } namespace SharedPage { @@ -96,7 +96,7 @@ public: private: u64 GetSystemTime() const; void UpdateTimeCallback(u64 userdata, int cycles_late); - CoreTiming::EventType* update_time_event; + Core::TimingEventType* update_time_event; std::chrono::seconds init_time; SharedPageDef shared_page; diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index a5889d6c8..16bbddb24 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -83,7 +83,7 @@ static ResultCode ControlMemory(u32* out_addr, u32 operation, u32 addr0, u32 add } VMAPermission vma_permissions = (VMAPermission)permissions; - auto& process = *g_current_process; + auto& process = *Core::System::GetInstance().Kernel().GetCurrentProcess(); switch (operation & MEMOP_OPERATION_MASK) { case MEMOP_FREE: { @@ -114,16 +114,12 @@ static ResultCode ControlMemory(u32* out_addr, u32 operation, u32 addr0, u32 add } case MEMOP_MAP: { - // TODO: This is just a hack to avoid regressions until memory aliasing is implemented - CASCADE_RESULT(*out_addr, process.HeapAllocate(addr0, size, vma_permissions)); + CASCADE_CODE(process.Map(addr0, addr1, size, vma_permissions)); break; } case MEMOP_UNMAP: { - // TODO: This is just a hack to avoid regressions until memory aliasing is implemented - ResultCode result = process.HeapFree(addr0, size); - if (result.IsError()) - return result; + CASCADE_CODE(process.Unmap(addr0, addr1, size, vma_permissions)); break; } @@ -145,19 +141,21 @@ static ResultCode ControlMemory(u32* out_addr, u32 operation, u32 addr0, u32 add } static void ExitProcess() { - LOG_INFO(Kernel_SVC, "Process {} exiting", g_current_process->process_id); + KernelSystem& kernel = Core::System::GetInstance().Kernel(); + SharedPtr current_process = kernel.GetCurrentProcess(); + LOG_INFO(Kernel_SVC, "Process {} exiting", current_process->process_id); - ASSERT_MSG(g_current_process->status == ProcessStatus::Running, "Process has already exited"); + ASSERT_MSG(current_process->status == ProcessStatus::Running, "Process has already exited"); - g_current_process->status = ProcessStatus::Exited; + current_process->status = ProcessStatus::Exited; // Stop all the process threads that are currently waiting for objects. - auto& thread_list = GetThreadList(); + auto& thread_list = kernel.GetThreadManager().GetThreadList(); for (auto& thread : thread_list) { - if (thread->owner_process != g_current_process) + if (thread->owner_process != current_process) continue; - if (thread == GetCurrentThread()) + if (thread == kernel.GetThreadManager().GetCurrentThread()) continue; // TODO(Subv): When are the other running/ready threads terminated? @@ -169,7 +167,7 @@ static void ExitProcess() { } // Kill the current thread - GetCurrentThread()->Stop(); + kernel.GetThreadManager().GetCurrentThread()->Stop(); Core::System::GetInstance().PrepareReschedule(); } @@ -181,7 +179,9 @@ static ResultCode MapMemoryBlock(Handle handle, u32 addr, u32 permissions, u32 o "otherpermission={}", handle, addr, permissions, other_permissions); - SharedPtr shared_memory = g_handle_table.Get(handle); + SharedPtr shared_memory = + Core::System::GetInstance().Kernel().GetCurrentProcess()->handle_table.Get( + handle); if (shared_memory == nullptr) return ERR_INVALID_HANDLE; @@ -195,7 +195,8 @@ static ResultCode MapMemoryBlock(Handle handle, u32 addr, u32 permissions, u32 o case MemoryPermission::WriteExecute: case MemoryPermission::ReadWriteExecute: case MemoryPermission::DontCare: - return shared_memory->Map(g_current_process.get(), addr, permissions_type, + return shared_memory->Map(Core::System::GetInstance().Kernel().GetCurrentProcess().get(), + addr, permissions_type, static_cast(other_permissions)); default: LOG_ERROR(Kernel_SVC, "unknown permissions=0x{:08X}", permissions); @@ -209,11 +210,12 @@ static ResultCode UnmapMemoryBlock(Handle handle, u32 addr) { // TODO(Subv): Return E0A01BF5 if the address is not in the application's heap - SharedPtr shared_memory = g_handle_table.Get(handle); + SharedPtr current_process = Core::System::GetInstance().Kernel().GetCurrentProcess(); + SharedPtr shared_memory = current_process->handle_table.Get(handle); if (shared_memory == nullptr) return ERR_INVALID_HANDLE; - return shared_memory->Unmap(g_current_process.get(), addr); + return shared_memory->Unmap(current_process.get(), addr); } /// Connect to an OS service given the port name, returns the handle to the port to out @@ -229,8 +231,10 @@ static ResultCode ConnectToPort(Handle* out_handle, VAddr port_name_address) { LOG_TRACE(Kernel_SVC, "called port_name={}", port_name); - auto it = Service::g_kernel_named_ports.find(port_name); - if (it == Service::g_kernel_named_ports.end()) { + KernelSystem& kernel = Core::System::GetInstance().Kernel(); + + auto it = kernel.named_ports.find(port_name); + if (it == kernel.named_ports.end()) { LOG_WARNING(Kernel_SVC, "tried to connect to unknown port: {}", port_name); return ERR_NOT_FOUND; } @@ -241,13 +245,15 @@ static ResultCode ConnectToPort(Handle* out_handle, VAddr port_name_address) { CASCADE_RESULT(client_session, client_port->Connect()); // Return the client session - CASCADE_RESULT(*out_handle, g_handle_table.Create(client_session)); + CASCADE_RESULT(*out_handle, kernel.GetCurrentProcess()->handle_table.Create(client_session)); return RESULT_SUCCESS; } /// Makes a blocking IPC call to an OS service. static ResultCode SendSyncRequest(Handle handle) { - SharedPtr session = g_handle_table.Get(handle); + KernelSystem& kernel = Core::System::GetInstance().Kernel(); + SharedPtr session = + kernel.GetCurrentProcess()->handle_table.Get(handle); if (session == nullptr) { return ERR_INVALID_HANDLE; } @@ -256,19 +262,20 @@ static ResultCode SendSyncRequest(Handle handle) { Core::System::GetInstance().PrepareReschedule(); - return session->SendSyncRequest(GetCurrentThread()); + return session->SendSyncRequest(kernel.GetThreadManager().GetCurrentThread()); } /// Close a handle static ResultCode CloseHandle(Handle handle) { LOG_TRACE(Kernel_SVC, "Closing handle 0x{:08X}", handle); - return g_handle_table.Close(handle); + return Core::System::GetInstance().Kernel().GetCurrentProcess()->handle_table.Close(handle); } /// Wait for a handle to synchronize, timeout after the specified nanoseconds static ResultCode WaitSynchronization1(Handle handle, s64 nano_seconds) { - auto object = g_handle_table.Get(handle); - Thread* thread = GetCurrentThread(); + KernelSystem& kernel = Core::System::GetInstance().Kernel(); + auto object = kernel.GetCurrentProcess()->handle_table.Get(handle); + Thread* thread = kernel.GetThreadManager().GetCurrentThread(); if (object == nullptr) return ERR_INVALID_HANDLE; @@ -320,7 +327,8 @@ static ResultCode WaitSynchronization1(Handle handle, s64 nano_seconds) { /// Wait for the given handles to synchronize, timeout after the specified nanoseconds static ResultCode WaitSynchronizationN(s32* out, VAddr handles_address, s32 handle_count, bool wait_all, s64 nano_seconds) { - Thread* thread = GetCurrentThread(); + KernelSystem& kernel = Core::System::GetInstance().Kernel(); + Thread* thread = kernel.GetThreadManager().GetCurrentThread(); if (!Memory::IsValidVirtualAddress(handles_address)) return ERR_INVALID_POINTER; @@ -338,7 +346,7 @@ static ResultCode WaitSynchronizationN(s32* out, VAddr handles_address, s32 hand for (int i = 0; i < handle_count; ++i) { Handle handle = Memory::Read32(handles_address + i * sizeof(Handle)); - auto object = g_handle_table.Get(handle); + auto object = kernel.GetCurrentProcess()->handle_table.Get(handle); if (object == nullptr) return ERR_INVALID_HANDLE; objects[i] = object; @@ -502,9 +510,12 @@ static ResultCode ReplyAndReceive(s32* index, VAddr handles_address, s32 handle_ using ObjectPtr = SharedPtr; std::vector objects(handle_count); + KernelSystem& kernel = Core::System::GetInstance().Kernel(); + SharedPtr current_process = kernel.GetCurrentProcess(); + for (int i = 0; i < handle_count; ++i) { Handle handle = Memory::Read32(handles_address + i * sizeof(Handle)); - auto object = g_handle_table.Get(handle); + auto object = current_process->handle_table.Get(handle); if (object == nullptr) return ERR_INVALID_HANDLE; objects[i] = object; @@ -512,10 +523,11 @@ static ResultCode ReplyAndReceive(s32* index, VAddr handles_address, s32 handle_ // We are also sending a command reply. // Do not send a reply if the command id in the command buffer is 0xFFFF. - u32* cmd_buff = GetCommandBuffer(); - IPC::Header header{cmd_buff[0]}; + Thread* thread = kernel.GetThreadManager().GetCurrentThread(); + u32 cmd_buff_header = Memory::Read32(thread->GetCommandBufferAddress()); + IPC::Header header{cmd_buff_header}; if (reply_target != 0 && header.command_id != 0xFFFF) { - auto session = g_handle_table.Get(reply_target); + auto session = current_process->handle_table.Get(reply_target); if (session == nullptr) return ERR_INVALID_HANDLE; @@ -531,11 +543,11 @@ static ResultCode ReplyAndReceive(s32* index, VAddr handles_address, s32 handle_ return ERR_SESSION_CLOSED_BY_REMOTE; } - VAddr source_address = GetCurrentThread()->GetCommandBufferAddress(); + VAddr source_address = thread->GetCommandBufferAddress(); VAddr target_address = request_thread->GetCommandBufferAddress(); - ResultCode translation_result = TranslateCommandBuffer( - Kernel::GetCurrentThread(), request_thread, source_address, target_address, true); + ResultCode translation_result = + TranslateCommandBuffer(thread, request_thread, source_address, target_address, true); // Note: The real kernel seems to always panic if the Server->Client buffer translation // fails for whatever reason. @@ -555,8 +567,6 @@ static ResultCode ReplyAndReceive(s32* index, VAddr handles_address, s32 handle_ return RESULT_SUCCESS; } - auto thread = GetCurrentThread(); - // Find the first object that is acquirable in the provided list of objects auto itr = std::find_if(objects.begin(), objects.end(), [thread](const ObjectPtr& object) { return !object->ShouldWait(thread); @@ -572,7 +582,7 @@ static ResultCode ReplyAndReceive(s32* index, VAddr handles_address, s32 handle_ return RESULT_SUCCESS; auto server_session = static_cast(object); - return ReceiveIPCRequest(server_session, GetCurrentThread()); + return ReceiveIPCRequest(server_session, thread); } // No objects were ready to be acquired, prepare to suspend the thread. @@ -615,8 +625,10 @@ static ResultCode ReplyAndReceive(s32* index, VAddr handles_address, s32 handle_ /// Create an address arbiter (to allocate access to shared resources) static ResultCode CreateAddressArbiter(Handle* out_handle) { - SharedPtr arbiter = AddressArbiter::Create(); - CASCADE_RESULT(*out_handle, g_handle_table.Create(std::move(arbiter))); + KernelSystem& kernel = Core::System::GetInstance().Kernel(); + SharedPtr arbiter = kernel.CreateAddressArbiter(); + CASCADE_RESULT(*out_handle, + kernel.GetCurrentProcess()->handle_table.Create(std::move(arbiter))); LOG_TRACE(Kernel_SVC, "returned handle=0x{:08X}", *out_handle); return RESULT_SUCCESS; } @@ -627,12 +639,16 @@ static ResultCode ArbitrateAddress(Handle handle, u32 address, u32 type, u32 val LOG_TRACE(Kernel_SVC, "called handle=0x{:08X}, address=0x{:08X}, type=0x{:08X}, value=0x{:08X}", handle, address, type, value); - SharedPtr arbiter = g_handle_table.Get(handle); + KernelSystem& kernel = Core::System::GetInstance().Kernel(); + + SharedPtr arbiter = + kernel.GetCurrentProcess()->handle_table.Get(handle); if (arbiter == nullptr) return ERR_INVALID_HANDLE; - auto res = arbiter->ArbitrateAddress(GetCurrentThread(), static_cast(type), - address, value, nanoseconds); + auto res = + arbiter->ArbitrateAddress(kernel.GetThreadManager().GetCurrentThread(), + static_cast(type), address, value, nanoseconds); // TODO(Subv): Identify in which specific cases this call should cause a reschedule. Core::System::GetInstance().PrepareReschedule(); @@ -675,11 +691,12 @@ static void OutputDebugString(VAddr address, int len) { static ResultCode GetResourceLimit(Handle* resource_limit, Handle process_handle) { LOG_TRACE(Kernel_SVC, "called process=0x{:08X}", process_handle); - SharedPtr process = g_handle_table.Get(process_handle); + SharedPtr current_process = Core::System::GetInstance().Kernel().GetCurrentProcess(); + SharedPtr process = current_process->handle_table.Get(process_handle); if (process == nullptr) return ERR_INVALID_HANDLE; - CASCADE_RESULT(*resource_limit, g_handle_table.Create(process->resource_limit)); + CASCADE_RESULT(*resource_limit, current_process->handle_table.Create(process->resource_limit)); return RESULT_SUCCESS; } @@ -691,7 +708,8 @@ static ResultCode GetResourceLimitCurrentValues(VAddr values, Handle resource_li resource_limit_handle, names, name_count); SharedPtr resource_limit = - g_handle_table.Get(resource_limit_handle); + Core::System::GetInstance().Kernel().GetCurrentProcess()->handle_table.Get( + resource_limit_handle); if (resource_limit == nullptr) return ERR_INVALID_HANDLE; @@ -711,7 +729,8 @@ static ResultCode GetResourceLimitLimitValues(VAddr values, Handle resource_limi resource_limit_handle, names, name_count); SharedPtr resource_limit = - g_handle_table.Get(resource_limit_handle); + Core::System::GetInstance().Kernel().GetCurrentProcess()->handle_table.Get( + resource_limit_handle); if (resource_limit == nullptr) return ERR_INVALID_HANDLE; @@ -733,14 +752,16 @@ static ResultCode CreateThread(Handle* out_handle, u32 priority, u32 entry_point return ERR_OUT_OF_RANGE; } - SharedPtr& resource_limit = g_current_process->resource_limit; + SharedPtr current_process = Core::System::GetInstance().Kernel().GetCurrentProcess(); + + SharedPtr& resource_limit = current_process->resource_limit; if (resource_limit->GetMaxResourceValue(ResourceTypes::PRIORITY) > priority) { return ERR_NOT_AUTHORIZED; } if (processor_id == ThreadProcessorIdDefault) { // Set the target CPU to the one specified in the process' exheader. - processor_id = g_current_process->ideal_processor; + processor_id = current_process->ideal_processor; ASSERT(processor_id != ThreadProcessorIdDefault); } @@ -761,14 +782,14 @@ static ResultCode CreateThread(Handle* out_handle, u32 priority, u32 entry_point break; } - CASCADE_RESULT(SharedPtr thread, - Thread::Create(name, entry_point, priority, arg, processor_id, stack_top, - g_current_process)); + CASCADE_RESULT(SharedPtr thread, Core::System::GetInstance().Kernel().CreateThread( + name, entry_point, priority, arg, processor_id, + stack_top, *current_process)); thread->context->SetFpscr(FPSCR_DEFAULT_NAN | FPSCR_FLUSH_TO_ZERO | FPSCR_ROUND_TOZERO); // 0x03C00000 - CASCADE_RESULT(*out_handle, g_handle_table.Create(std::move(thread))); + CASCADE_RESULT(*out_handle, current_process->handle_table.Create(std::move(thread))); Core::System::GetInstance().PrepareReschedule(); @@ -784,13 +805,14 @@ static ResultCode CreateThread(Handle* out_handle, u32 priority, u32 entry_point static void ExitThread() { LOG_TRACE(Kernel_SVC, "called, pc=0x{:08X}", Core::CPU().GetPC()); - ExitCurrentThread(); + Core::System::GetInstance().Kernel().GetThreadManager().ExitCurrentThread(); Core::System::GetInstance().PrepareReschedule(); } /// Gets the priority for the specified thread static ResultCode GetThreadPriority(u32* priority, Handle handle) { - const SharedPtr thread = g_handle_table.Get(handle); + const SharedPtr thread = + Core::System::GetInstance().Kernel().GetCurrentProcess()->handle_table.Get(handle); if (thread == nullptr) return ERR_INVALID_HANDLE; @@ -804,13 +826,15 @@ static ResultCode SetThreadPriority(Handle handle, u32 priority) { return ERR_OUT_OF_RANGE; } - SharedPtr thread = g_handle_table.Get(handle); + SharedPtr thread = + Core::System::GetInstance().Kernel().GetCurrentProcess()->handle_table.Get(handle); if (thread == nullptr) return ERR_INVALID_HANDLE; // Note: The kernel uses the current process's resource limit instead of // the one from the thread owner's resource limit. - SharedPtr& resource_limit = g_current_process->resource_limit; + SharedPtr& resource_limit = + Core::System::GetInstance().Kernel().GetCurrentProcess()->resource_limit; if (resource_limit->GetMaxResourceValue(ResourceTypes::PRIORITY) > priority) { return ERR_NOT_AUTHORIZED; } @@ -828,9 +852,10 @@ static ResultCode SetThreadPriority(Handle handle, u32 priority) { /// Create a mutex static ResultCode CreateMutex(Handle* out_handle, u32 initial_locked) { - SharedPtr mutex = Mutex::Create(initial_locked != 0); + KernelSystem& kernel = Core::System::GetInstance().Kernel(); + SharedPtr mutex = kernel.CreateMutex(initial_locked != 0); mutex->name = fmt::format("mutex-{:08x}", Core::CPU().GetReg(14)); - CASCADE_RESULT(*out_handle, g_handle_table.Create(std::move(mutex))); + CASCADE_RESULT(*out_handle, kernel.GetCurrentProcess()->handle_table.Create(std::move(mutex))); LOG_TRACE(Kernel_SVC, "called initial_locked={} : created handle=0x{:08X}", initial_locked ? "true" : "false", *out_handle); @@ -842,18 +867,22 @@ static ResultCode CreateMutex(Handle* out_handle, u32 initial_locked) { static ResultCode ReleaseMutex(Handle handle) { LOG_TRACE(Kernel_SVC, "called handle=0x{:08X}", handle); - SharedPtr mutex = g_handle_table.Get(handle); + KernelSystem& kernel = Core::System::GetInstance().Kernel(); + + SharedPtr mutex = kernel.GetCurrentProcess()->handle_table.Get(handle); if (mutex == nullptr) return ERR_INVALID_HANDLE; - return mutex->Release(GetCurrentThread()); + return mutex->Release(kernel.GetThreadManager().GetCurrentThread()); } /// Get the ID of the specified process static ResultCode GetProcessId(u32* process_id, Handle process_handle) { LOG_TRACE(Kernel_SVC, "called process=0x{:08X}", process_handle); - const SharedPtr process = g_handle_table.Get(process_handle); + const SharedPtr process = + Core::System::GetInstance().Kernel().GetCurrentProcess()->handle_table.Get( + process_handle); if (process == nullptr) return ERR_INVALID_HANDLE; @@ -865,7 +894,9 @@ static ResultCode GetProcessId(u32* process_id, Handle process_handle) { static ResultCode GetProcessIdOfThread(u32* process_id, Handle thread_handle) { LOG_TRACE(Kernel_SVC, "called thread=0x{:08X}", thread_handle); - const SharedPtr thread = g_handle_table.Get(thread_handle); + const SharedPtr thread = + Core::System::GetInstance().Kernel().GetCurrentProcess()->handle_table.Get( + thread_handle); if (thread == nullptr) return ERR_INVALID_HANDLE; @@ -881,7 +912,8 @@ static ResultCode GetProcessIdOfThread(u32* process_id, Handle thread_handle) { static ResultCode GetThreadId(u32* thread_id, Handle handle) { LOG_TRACE(Kernel_SVC, "called thread=0x{:08X}", handle); - const SharedPtr thread = g_handle_table.Get(handle); + const SharedPtr thread = + Core::System::GetInstance().Kernel().GetCurrentProcess()->handle_table.Get(handle); if (thread == nullptr) return ERR_INVALID_HANDLE; @@ -891,9 +923,12 @@ static ResultCode GetThreadId(u32* thread_id, Handle handle) { /// Creates a semaphore static ResultCode CreateSemaphore(Handle* out_handle, s32 initial_count, s32 max_count) { - CASCADE_RESULT(SharedPtr semaphore, Semaphore::Create(initial_count, max_count)); + KernelSystem& kernel = Core::System::GetInstance().Kernel(); + CASCADE_RESULT(SharedPtr semaphore, + kernel.CreateSemaphore(initial_count, max_count)); semaphore->name = fmt::format("semaphore-{:08x}", Core::CPU().GetReg(14)); - CASCADE_RESULT(*out_handle, g_handle_table.Create(std::move(semaphore))); + CASCADE_RESULT(*out_handle, + kernel.GetCurrentProcess()->handle_table.Create(std::move(semaphore))); LOG_TRACE(Kernel_SVC, "called initial_count={}, max_count={}, created handle=0x{:08X}", initial_count, max_count, *out_handle); @@ -904,7 +939,9 @@ static ResultCode CreateSemaphore(Handle* out_handle, s32 initial_count, s32 max static ResultCode ReleaseSemaphore(s32* count, Handle handle, s32 release_count) { LOG_TRACE(Kernel_SVC, "called release_count={}, handle=0x{:08X}", release_count, handle); - SharedPtr semaphore = g_handle_table.Get(handle); + SharedPtr semaphore = + Core::System::GetInstance().Kernel().GetCurrentProcess()->handle_table.Get( + handle); if (semaphore == nullptr) return ERR_INVALID_HANDLE; @@ -916,13 +953,15 @@ static ResultCode ReleaseSemaphore(s32* count, Handle handle, s32 release_count) /// Query process memory static ResultCode QueryProcessMemory(MemoryInfo* memory_info, PageInfo* page_info, Handle process_handle, u32 addr) { - SharedPtr process = g_handle_table.Get(process_handle); + SharedPtr process = + Core::System::GetInstance().Kernel().GetCurrentProcess()->handle_table.Get( + process_handle); if (process == nullptr) return ERR_INVALID_HANDLE; auto vma = process->vm_manager.FindVMA(addr); - if (vma == g_current_process->vm_manager.vma_map.end()) + if (vma == process->vm_manager.vma_map.end()) return ERR_INVALID_ADDRESS; memory_info->base_address = vma->second.base; @@ -942,9 +981,10 @@ static ResultCode QueryMemory(MemoryInfo* memory_info, PageInfo* page_info, u32 /// Create an event static ResultCode CreateEvent(Handle* out_handle, u32 reset_type) { - SharedPtr evt = Event::Create(static_cast(reset_type), - fmt::format("event-{:08x}", Core::CPU().GetReg(14))); - CASCADE_RESULT(*out_handle, g_handle_table.Create(std::move(evt))); + KernelSystem& kernel = Core::System::GetInstance().Kernel(); + SharedPtr evt = kernel.CreateEvent(static_cast(reset_type), + fmt::format("event-{:08x}", Core::CPU().GetReg(14))); + CASCADE_RESULT(*out_handle, kernel.GetCurrentProcess()->handle_table.Create(std::move(evt))); LOG_TRACE(Kernel_SVC, "called reset_type=0x{:08X} : created handle=0x{:08X}", reset_type, *out_handle); @@ -953,7 +993,9 @@ static ResultCode CreateEvent(Handle* out_handle, u32 reset_type) { /// Duplicates a kernel handle static ResultCode DuplicateHandle(Handle* out, Handle handle) { - CASCADE_RESULT(*out, g_handle_table.Duplicate(handle)); + CASCADE_RESULT( + *out, + Core::System::GetInstance().Kernel().GetCurrentProcess()->handle_table.Duplicate(handle)); LOG_TRACE(Kernel_SVC, "duplicated 0x{:08X} to 0x{:08X}", handle, *out); return RESULT_SUCCESS; } @@ -962,7 +1004,8 @@ static ResultCode DuplicateHandle(Handle* out, Handle handle) { static ResultCode SignalEvent(Handle handle) { LOG_TRACE(Kernel_SVC, "called event=0x{:08X}", handle); - SharedPtr evt = g_handle_table.Get(handle); + SharedPtr evt = + Core::System::GetInstance().Kernel().GetCurrentProcess()->handle_table.Get(handle); if (evt == nullptr) return ERR_INVALID_HANDLE; @@ -975,7 +1018,8 @@ static ResultCode SignalEvent(Handle handle) { static ResultCode ClearEvent(Handle handle) { LOG_TRACE(Kernel_SVC, "called event=0x{:08X}", handle); - SharedPtr evt = g_handle_table.Get(handle); + SharedPtr evt = + Core::System::GetInstance().Kernel().GetCurrentProcess()->handle_table.Get(handle); if (evt == nullptr) return ERR_INVALID_HANDLE; @@ -985,9 +1029,10 @@ static ResultCode ClearEvent(Handle handle) { /// Creates a timer static ResultCode CreateTimer(Handle* out_handle, u32 reset_type) { - SharedPtr timer = Timer::Create(static_cast(reset_type), - fmt ::format("timer-{:08x}", Core::CPU().GetReg(14))); - CASCADE_RESULT(*out_handle, g_handle_table.Create(std::move(timer))); + KernelSystem& kernel = Core::System::GetInstance().Kernel(); + SharedPtr timer = kernel.CreateTimer( + static_cast(reset_type), fmt ::format("timer-{:08x}", Core::CPU().GetReg(14))); + CASCADE_RESULT(*out_handle, kernel.GetCurrentProcess()->handle_table.Create(std::move(timer))); LOG_TRACE(Kernel_SVC, "called reset_type=0x{:08X} : created handle=0x{:08X}", reset_type, *out_handle); @@ -998,7 +1043,8 @@ static ResultCode CreateTimer(Handle* out_handle, u32 reset_type) { static ResultCode ClearTimer(Handle handle) { LOG_TRACE(Kernel_SVC, "called timer=0x{:08X}", handle); - SharedPtr timer = g_handle_table.Get(handle); + SharedPtr timer = + Core::System::GetInstance().Kernel().GetCurrentProcess()->handle_table.Get(handle); if (timer == nullptr) return ERR_INVALID_HANDLE; @@ -1014,7 +1060,8 @@ static ResultCode SetTimer(Handle handle, s64 initial, s64 interval) { return ERR_OUT_OF_RANGE_KERNEL; } - SharedPtr timer = g_handle_table.Get(handle); + SharedPtr timer = + Core::System::GetInstance().Kernel().GetCurrentProcess()->handle_table.Get(handle); if (timer == nullptr) return ERR_INVALID_HANDLE; @@ -1027,7 +1074,8 @@ static ResultCode SetTimer(Handle handle, s64 initial, s64 interval) { static ResultCode CancelTimer(Handle handle) { LOG_TRACE(Kernel_SVC, "called timer=0x{:08X}", handle); - SharedPtr timer = g_handle_table.Get(handle); + SharedPtr timer = + Core::System::GetInstance().Kernel().GetCurrentProcess()->handle_table.Get(handle); if (timer == nullptr) return ERR_INVALID_HANDLE; @@ -1040,25 +1088,29 @@ static ResultCode CancelTimer(Handle handle) { static void SleepThread(s64 nanoseconds) { LOG_TRACE(Kernel_SVC, "called nanoseconds={}", nanoseconds); + KernelSystem& kernel = Core::System::GetInstance().Kernel(); + ThreadManager& thread_manager = kernel.GetThreadManager(); + // Don't attempt to yield execution if there are no available threads to run, // this way we avoid a useless reschedule to the idle thread. - if (nanoseconds == 0 && !HaveReadyThreads()) + if (nanoseconds == 0 && !thread_manager.HaveReadyThreads()) return; // Sleep current thread and check for next thread to schedule - WaitCurrentThread_Sleep(); + thread_manager.WaitCurrentThread_Sleep(); // Create an event to wake the thread up after the specified nanosecond delay has passed - GetCurrentThread()->WakeAfterDelay(nanoseconds); + thread_manager.GetCurrentThread()->WakeAfterDelay(nanoseconds); Core::System::GetInstance().PrepareReschedule(); } /// This returns the total CPU ticks elapsed since the CPU was powered-on static s64 GetSystemTick() { - s64 result = CoreTiming::GetTicks(); + s64 result = Core::System::GetInstance().CoreTiming().GetTicks(); // Advance time to defeat dumb games (like Cubic Ninja) that busy-wait for the frame to end. - CoreTiming::AddTicks(150); // Measured time between two calls on a 9.2 o3DS with Ninjhax 1.1b + // Measured time between two calls on a 9.2 o3DS with Ninjhax 1.1b + Core::System::GetInstance().CoreTiming().AddTicks(150); return result; } @@ -1096,18 +1148,20 @@ static ResultCode CreateMemoryBlock(Handle* out_handle, u32 addr, u32 size, u32 return ERR_INVALID_ADDRESS; } + SharedPtr current_process = Core::System::GetInstance().Kernel().GetCurrentProcess(); + // When trying to create a memory block with address = 0, // if the process has the Shared Device Memory flag in the exheader, // then we have to allocate from the same region as the caller process instead of the BASE // region. MemoryRegion region = MemoryRegion::BASE; - if (addr == 0 && g_current_process->flags.shared_device_mem) - region = g_current_process->flags.memory_region; + if (addr == 0 && current_process->flags.shared_device_mem) + region = current_process->flags.memory_region; - shared_memory = - SharedMemory::Create(g_current_process, size, static_cast(my_permission), - static_cast(other_permission), addr, region); - CASCADE_RESULT(*out_handle, g_handle_table.Create(std::move(shared_memory))); + shared_memory = Core::System::GetInstance().Kernel().CreateSharedMemory( + current_process.get(), size, static_cast(my_permission), + static_cast(other_permission), addr, region); + CASCADE_RESULT(*out_handle, current_process->handle_table.Create(std::move(shared_memory))); LOG_WARNING(Kernel_SVC, "called addr=0x{:08X}", addr); return RESULT_SUCCESS; @@ -1118,70 +1172,82 @@ static ResultCode CreatePort(Handle* server_port, Handle* client_port, VAddr nam // TODO(Subv): Implement named ports. ASSERT_MSG(name_address == 0, "Named ports are currently unimplemented"); - auto ports = ServerPort::CreatePortPair(max_sessions); - CASCADE_RESULT(*client_port, - g_handle_table.Create(std::move(std::get>(ports)))); + KernelSystem& kernel = Core::System::GetInstance().Kernel(); + SharedPtr current_process = kernel.GetCurrentProcess(); + + auto ports = kernel.CreatePortPair(max_sessions); + CASCADE_RESULT(*client_port, current_process->handle_table.Create( + std::move(std::get>(ports)))); // Note: The 3DS kernel also leaks the client port handle if the server port handle fails to be // created. - CASCADE_RESULT(*server_port, - g_handle_table.Create(std::move(std::get>(ports)))); + CASCADE_RESULT(*server_port, current_process->handle_table.Create( + std::move(std::get>(ports)))); LOG_TRACE(Kernel_SVC, "called max_sessions={}", max_sessions); return RESULT_SUCCESS; } static ResultCode CreateSessionToPort(Handle* out_client_session, Handle client_port_handle) { - SharedPtr client_port = g_handle_table.Get(client_port_handle); + SharedPtr current_process = Core::System::GetInstance().Kernel().GetCurrentProcess(); + SharedPtr client_port = + current_process->handle_table.Get(client_port_handle); if (client_port == nullptr) return ERR_INVALID_HANDLE; CASCADE_RESULT(auto session, client_port->Connect()); - CASCADE_RESULT(*out_client_session, g_handle_table.Create(std::move(session))); + CASCADE_RESULT(*out_client_session, current_process->handle_table.Create(std::move(session))); return RESULT_SUCCESS; } static ResultCode CreateSession(Handle* server_session, Handle* client_session) { - auto sessions = ServerSession::CreateSessionPair(); + KernelSystem& kernel = Core::System::GetInstance().Kernel(); + auto sessions = kernel.CreateSessionPair(); + + SharedPtr current_process = kernel.GetCurrentProcess(); auto& server = std::get>(sessions); - CASCADE_RESULT(*server_session, g_handle_table.Create(std::move(server))); + CASCADE_RESULT(*server_session, current_process->handle_table.Create(std::move(server))); auto& client = std::get>(sessions); - CASCADE_RESULT(*client_session, g_handle_table.Create(std::move(client))); + CASCADE_RESULT(*client_session, current_process->handle_table.Create(std::move(client))); LOG_TRACE(Kernel_SVC, "called"); return RESULT_SUCCESS; } static ResultCode AcceptSession(Handle* out_server_session, Handle server_port_handle) { - SharedPtr server_port = g_handle_table.Get(server_port_handle); + SharedPtr current_process = Core::System::GetInstance().Kernel().GetCurrentProcess(); + SharedPtr server_port = + current_process->handle_table.Get(server_port_handle); if (server_port == nullptr) return ERR_INVALID_HANDLE; CASCADE_RESULT(auto session, server_port->Accept()); - CASCADE_RESULT(*out_server_session, g_handle_table.Create(std::move(session))); + CASCADE_RESULT(*out_server_session, current_process->handle_table.Create(std::move(session))); return RESULT_SUCCESS; } static ResultCode GetSystemInfo(s64* out, u32 type, s32 param) { LOG_TRACE(Kernel_SVC, "called type={} param={}", type, param); + KernelSystem& kernel = Core::System::GetInstance().Kernel(); + switch ((SystemInfoType)type) { case SystemInfoType::REGION_MEMORY_USAGE: switch ((SystemInfoMemUsageRegion)param) { case SystemInfoMemUsageRegion::ALL: - *out = GetMemoryRegion(MemoryRegion::APPLICATION)->used + - GetMemoryRegion(MemoryRegion::SYSTEM)->used + - GetMemoryRegion(MemoryRegion::BASE)->used; + *out = kernel.GetMemoryRegion(MemoryRegion::APPLICATION)->used + + kernel.GetMemoryRegion(MemoryRegion::SYSTEM)->used + + kernel.GetMemoryRegion(MemoryRegion::BASE)->used; break; case SystemInfoMemUsageRegion::APPLICATION: - *out = GetMemoryRegion(MemoryRegion::APPLICATION)->used; + *out = kernel.GetMemoryRegion(MemoryRegion::APPLICATION)->used; break; case SystemInfoMemUsageRegion::SYSTEM: - *out = GetMemoryRegion(MemoryRegion::SYSTEM)->used; + *out = kernel.GetMemoryRegion(MemoryRegion::SYSTEM)->used; break; case SystemInfoMemUsageRegion::BASE: - *out = GetMemoryRegion(MemoryRegion::BASE)->used; + *out = kernel.GetMemoryRegion(MemoryRegion::BASE)->used; break; default: LOG_ERROR(Kernel_SVC, "unknown GetSystemInfo type=0 region: param={}", param); @@ -1209,7 +1275,9 @@ static ResultCode GetSystemInfo(s64* out, u32 type, s32 param) { static ResultCode GetProcessInfo(s64* out, Handle process_handle, u32 type) { LOG_TRACE(Kernel_SVC, "called process=0x{:08X} type={}", process_handle, type); - SharedPtr process = g_handle_table.Get(process_handle); + SharedPtr process = + Core::System::GetInstance().Kernel().GetCurrentProcess()->handle_table.Get( + process_handle); if (process == nullptr) return ERR_INVALID_HANDLE; @@ -1218,7 +1286,7 @@ static ResultCode GetProcessInfo(s64* out, Handle process_handle, u32 type) { case 2: // TODO(yuriks): Type 0 returns a slightly higher number than type 2, but I'm not sure // what's the difference between them. - *out = process->heap_used + process->linear_heap_used + process->misc_memory_used; + *out = process->memory_used; if (*out % Memory::PAGE_SIZE != 0) { LOG_ERROR(Kernel_SVC, "called, memory size not page-aligned"); return ERR_MISALIGNED_SIZE; @@ -1406,8 +1474,9 @@ void CallSVC(u32 immediate) { // Lock the global kernel mutex when we enter the kernel HLE. std::lock_guard lock(HLE::g_hle_lock); - ASSERT_MSG(g_current_process->status == ProcessStatus::Running, - "Running threads from exiting processes is unimplemented"); + DEBUG_ASSERT_MSG(Core::System::GetInstance().Kernel().GetCurrentProcess()->status == + ProcessStatus::Running, + "Running threads from exiting processes is unimplemented"); const FunctionDef* info = GetSVCInfo(immediate); if (info) { diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 50fa061c4..7f1749a5f 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -4,16 +4,15 @@ #include #include +#include #include #include "common/assert.h" #include "common/common_types.h" #include "common/logging/log.h" #include "common/math_util.h" -#include "common/thread_queue_list.h" #include "core/arm/arm_interface.h" #include "core/arm/skyeye_common/armstate.h" #include "core/core.h" -#include "core/core_timing.h" #include "core/hle/kernel/errors.h" #include "core/hle/kernel/handle_table.h" #include "core/hle/kernel/kernel.h" @@ -26,9 +25,6 @@ namespace Kernel { -/// Event type for the thread wake up event -static CoreTiming::EventType* ThreadWakeupEventType = nullptr; - bool Thread::ShouldWait(Thread* thread) const { return status != ThreadStatus::Dead; } @@ -37,46 +33,29 @@ void Thread::Acquire(Thread* thread) { ASSERT_MSG(!ShouldWait(thread), "object unavailable!"); } -// TODO(yuriks): This can be removed if Thread objects are explicitly pooled in the future, allowing -// us to simply use a pool index or similar. -static Kernel::HandleTable wakeup_callback_handle_table; - -// Lists all thread ids that aren't deleted/etc. -static std::vector> thread_list; - -// Lists only ready thread ids. -static Common::ThreadQueueList ready_queue; - -static SharedPtr current_thread; - -// The first available thread id at startup -static u32 next_thread_id; - -/** - * Creates a new thread ID - * @return The new thread ID - */ -inline static u32 const NewThreadId() { +u32 ThreadManager::NewThreadId() { return next_thread_id++; } -Thread::Thread() : context(Core::CPU().NewContext()) {} +Thread::Thread(KernelSystem& kernel) + : WaitObject(kernel), context(Core::CPU().NewContext()), + thread_manager(kernel.GetThreadManager()) {} Thread::~Thread() {} -Thread* GetCurrentThread() { +Thread* ThreadManager::GetCurrentThread() const { return current_thread.get(); } void Thread::Stop() { // Cancel any outstanding wakeup events for this thread - CoreTiming::UnscheduleEvent(ThreadWakeupEventType, callback_handle); - wakeup_callback_handle_table.Close(callback_handle); - callback_handle = 0; + Core::System::GetInstance().CoreTiming().UnscheduleEvent(thread_manager.ThreadWakeupEventType, + thread_id); + thread_manager.wakeup_callback_table.erase(thread_id); // Clean up thread from ready queue // This is only needed when the thread is termintated forcefully (SVC TerminateProcess) if (status == ThreadStatus::Ready) { - ready_queue.remove(current_priority, this); + thread_manager.ready_queue.remove(current_priority, this); } status = ThreadStatus::Dead; @@ -96,19 +75,17 @@ void Thread::Stop() { u32 tls_page = (tls_address - Memory::TLS_AREA_VADDR) / Memory::PAGE_SIZE; u32 tls_slot = ((tls_address - Memory::TLS_AREA_VADDR) % Memory::PAGE_SIZE) / Memory::TLS_ENTRY_SIZE; - Kernel::g_current_process->tls_slots[tls_page].reset(tls_slot); + owner_process->tls_slots[tls_page].reset(tls_slot); } -/** - * Switches the CPU's active thread context to that of the specified thread - * @param new_thread The thread to switch to - */ -static void SwitchContext(Thread* new_thread) { +void ThreadManager::SwitchContext(Thread* new_thread) { Thread* previous_thread = GetCurrentThread(); + Core::Timing& timing = Core::System::GetInstance().CoreTiming(); + // Save context for previous thread if (previous_thread) { - previous_thread->last_running_ticks = CoreTiming::GetTicks(); + previous_thread->last_running_ticks = timing.GetTicks(); Core::CPU().SaveContext(previous_thread->context); if (previous_thread->status == ThreadStatus::Running) { @@ -125,9 +102,9 @@ static void SwitchContext(Thread* new_thread) { "Thread must be ready to become running."); // Cancel any outstanding wakeup events for this thread - CoreTiming::UnscheduleEvent(ThreadWakeupEventType, new_thread->callback_handle); + timing.UnscheduleEvent(ThreadWakeupEventType, new_thread->thread_id); - auto previous_process = Kernel::g_current_process; + auto previous_process = Core::System::GetInstance().Kernel().GetCurrentProcess(); current_thread = new_thread; @@ -135,8 +112,8 @@ static void SwitchContext(Thread* new_thread) { new_thread->status = ThreadStatus::Running; if (previous_process != current_thread->owner_process) { - Kernel::g_current_process = current_thread->owner_process; - SetCurrentPageTable(&Kernel::g_current_process->vm_manager.page_table); + Core::System::GetInstance().Kernel().SetCurrentProcess(current_thread->owner_process); + SetCurrentPageTable(¤t_thread->owner_process->vm_manager.page_table); } Core::CPU().LoadContext(new_thread->context); @@ -148,11 +125,7 @@ static void SwitchContext(Thread* new_thread) { } } -/** - * Pops and returns the next thread from the thread queue - * @return A pointer to the next ready thread - */ -static Thread* PopNextReadyThread() { +Thread* ThreadManager::PopNextReadyThread() { Thread* next; Thread* thread = GetCurrentThread(); @@ -171,27 +144,22 @@ static Thread* PopNextReadyThread() { return next; } -void WaitCurrentThread_Sleep() { +void ThreadManager::WaitCurrentThread_Sleep() { Thread* thread = GetCurrentThread(); thread->status = ThreadStatus::WaitSleep; } -void ExitCurrentThread() { +void ThreadManager::ExitCurrentThread() { Thread* thread = GetCurrentThread(); thread->Stop(); thread_list.erase(std::remove(thread_list.begin(), thread_list.end(), thread), thread_list.end()); } -/** - * Callback that will wake up the thread it was scheduled for - * @param thread_handle The handle of the thread that's been awoken - * @param cycles_late The number of CPU cycles that have passed since the desired wakeup time - */ -static void ThreadWakeupCallback(u64 thread_handle, s64 cycles_late) { - SharedPtr thread = wakeup_callback_handle_table.Get((Handle)thread_handle); +void ThreadManager::ThreadWakeupCallback(u64 thread_id, s64 cycles_late) { + SharedPtr thread = wakeup_callback_table.at(thread_id); if (thread == nullptr) { - LOG_CRITICAL(Kernel, "Callback fired for invalid thread {:08X}", (Handle)thread_handle); + LOG_CRITICAL(Kernel, "Callback fired for invalid thread {:08X}", thread_id); return; } @@ -217,7 +185,8 @@ void Thread::WakeAfterDelay(s64 nanoseconds) { if (nanoseconds == -1) return; - CoreTiming::ScheduleEvent(nsToCycles(nanoseconds), ThreadWakeupEventType, callback_handle); + Core::System::GetInstance().CoreTiming().ScheduleEvent( + nsToCycles(nanoseconds), thread_manager.ThreadWakeupEventType, thread_id); } void Thread::ResumeFromWait() { @@ -253,15 +222,12 @@ void Thread::ResumeFromWait() { wakeup_callback = nullptr; - ready_queue.push_back(current_priority, this); + thread_manager.ready_queue.push_back(current_priority, this); status = ThreadStatus::Ready; Core::System::GetInstance().PrepareReschedule(); } -/** - * Prints the thread queue for debugging purposes - */ -static void DebugThreadQueue() { +void ThreadManager::DebugThreadQueue() { Thread* thread = GetCurrentThread(); if (!thread) { LOG_DEBUG(Kernel, "Current: NO CURRENT THREAD"); @@ -320,9 +286,9 @@ static void ResetThreadContext(const std::unique_ptrSetCpsr(USER32MODE | ((entry_point & 1) << 5)); // Usermode and THUMB mode } -ResultVal> Thread::Create(std::string name, VAddr entry_point, u32 priority, - u32 arg, s32 processor_id, VAddr stack_top, - SharedPtr owner_process) { +ResultVal> KernelSystem::CreateThread(std::string name, VAddr entry_point, + u32 priority, u32 arg, s32 processor_id, + VAddr stack_top, Process& owner_process) { // Check if priority is in ranged. Lowest priority -> highest priority id. if (priority > ThreadPrioLowest) { LOG_ERROR(Kernel_SVC, "Invalid thread priority: {}", priority); @@ -336,33 +302,33 @@ ResultVal> Thread::Create(std::string name, VAddr entry_point, // TODO(yuriks): Other checks, returning 0xD9001BEA - if (!Memory::IsValidVirtualAddress(*owner_process, entry_point)) { + if (!Memory::IsValidVirtualAddress(owner_process, entry_point)) { LOG_ERROR(Kernel_SVC, "(name={}): invalid entry {:08x}", name, entry_point); // TODO: Verify error return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::Kernel, ErrorSummary::InvalidArgument, ErrorLevel::Permanent); } - SharedPtr thread(new Thread); + SharedPtr thread(new Thread(*this)); - thread_list.push_back(thread); - ready_queue.prepare(priority); + thread_manager->thread_list.push_back(thread); + thread_manager->ready_queue.prepare(priority); - thread->thread_id = NewThreadId(); + thread->thread_id = thread_manager->NewThreadId(); thread->status = ThreadStatus::Dormant; thread->entry_point = entry_point; thread->stack_top = stack_top; thread->nominal_priority = thread->current_priority = priority; - thread->last_running_ticks = CoreTiming::GetTicks(); + thread->last_running_ticks = Core::System::GetInstance().CoreTiming().GetTicks(); thread->processor_id = processor_id; thread->wait_objects.clear(); thread->wait_address = 0; thread->name = std::move(name); - thread->callback_handle = wakeup_callback_handle_table.Create(thread).Unwrap(); - thread->owner_process = owner_process; + thread_manager->wakeup_callback_table[thread->thread_id] = thread.get(); + thread->owner_process = &owner_process; // Find the next available TLS index, and mark it as used - auto& tls_slots = owner_process->tls_slots; + auto& tls_slots = owner_process.tls_slots; auto [available_page, available_slot, needs_allocation] = GetFreeThreadLocalSlot(tls_slots); @@ -370,32 +336,26 @@ ResultVal> Thread::Create(std::string name, VAddr entry_point, // There are no already-allocated pages with free slots, lets allocate a new one. // TLS pages are allocated from the BASE region in the linear heap. MemoryRegionInfo* memory_region = GetMemoryRegion(MemoryRegion::BASE); - auto& linheap_memory = memory_region->linear_heap_memory; - if (linheap_memory->size() + Memory::PAGE_SIZE > memory_region->size) { + // Allocate some memory from the end of the linear heap for this region. + auto offset = memory_region->LinearAllocate(Memory::PAGE_SIZE); + if (!offset) { LOG_ERROR(Kernel_SVC, "Not enough space in region to allocate a new TLS page for thread"); return ERR_OUT_OF_MEMORY; } - - std::size_t offset = linheap_memory->size(); - - // Allocate some memory from the end of the linear heap for this region. - linheap_memory->insert(linheap_memory->end(), Memory::PAGE_SIZE, 0); - memory_region->used += Memory::PAGE_SIZE; - owner_process->linear_heap_used += Memory::PAGE_SIZE; + owner_process.memory_used += Memory::PAGE_SIZE; tls_slots.emplace_back(0); // The page is completely available at the start available_page = tls_slots.size() - 1; available_slot = 0; // Use the first slot in the new page - auto& vm_manager = owner_process->vm_manager; - vm_manager.RefreshMemoryBlockMappings(linheap_memory.get()); + auto& vm_manager = owner_process.vm_manager; // Map the page to the current process' address space. - // TODO(Subv): Find the correct MemoryState for this region. - vm_manager.MapMemoryBlock(Memory::TLS_AREA_VADDR + available_page * Memory::PAGE_SIZE, - linheap_memory, offset, Memory::PAGE_SIZE, MemoryState::Private); + vm_manager.MapBackingMemory(Memory::TLS_AREA_VADDR + available_page * Memory::PAGE_SIZE, + Memory::fcram.data() + *offset, Memory::PAGE_SIZE, + MemoryState::Locked); } // Mark the slot as used @@ -403,11 +363,13 @@ ResultVal> Thread::Create(std::string name, VAddr entry_point, thread->tls_address = Memory::TLS_AREA_VADDR + available_page * Memory::PAGE_SIZE + available_slot * Memory::TLS_ENTRY_SIZE; + Memory::ZeroBlock(owner_process, thread->tls_address, Memory::TLS_ENTRY_SIZE); + // TODO(peachum): move to ScheduleThread() when scheduler is added so selected core is used // to initialize the context ResetThreadContext(thread->context, stack_top, entry_point, arg); - ready_queue.push_back(thread->current_priority, thread.get()); + thread_manager->ready_queue.push_back(thread->current_priority, thread.get()); thread->status = ThreadStatus::Ready; return MakeResult>(std::move(thread)); @@ -418,9 +380,9 @@ void Thread::SetPriority(u32 priority) { "Invalid priority value."); // If thread was ready, adjust queues if (status == ThreadStatus::Ready) - ready_queue.move(this, current_priority, priority); + thread_manager.ready_queue.move(this, current_priority, priority); else - ready_queue.prepare(priority); + thread_manager.ready_queue.prepare(priority); nominal_priority = current_priority = priority; } @@ -437,17 +399,18 @@ void Thread::UpdatePriority() { void Thread::BoostPriority(u32 priority) { // If thread was ready, adjust queues if (status == ThreadStatus::Ready) - ready_queue.move(this, current_priority, priority); + thread_manager.ready_queue.move(this, current_priority, priority); else - ready_queue.prepare(priority); + thread_manager.ready_queue.prepare(priority); current_priority = priority; } -SharedPtr SetupMainThread(u32 entry_point, u32 priority, SharedPtr owner_process) { +SharedPtr SetupMainThread(KernelSystem& kernel, u32 entry_point, u32 priority, + SharedPtr owner_process) { // Initialize new "main" thread auto thread_res = - Thread::Create("main", entry_point, priority, 0, owner_process->ideal_processor, - Memory::HEAP_VADDR_END, owner_process); + kernel.CreateThread("main", entry_point, priority, 0, owner_process->ideal_processor, + Memory::HEAP_VADDR_END, *owner_process); SharedPtr thread = std::move(thread_res).Unwrap(); @@ -458,11 +421,11 @@ SharedPtr SetupMainThread(u32 entry_point, u32 priority, SharedPtrStop(); } - thread_list.clear(); - ready_queue.clear(); - ClearProcessList(); } -const std::vector>& GetThreadList() { +const std::vector>& ThreadManager::GetThreadList() { return thread_list; } diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index 3fd72563f..acb75a374 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -10,7 +10,9 @@ #include #include #include "common/common_types.h" +#include "common/thread_queue_list.h" #include "core/arm/arm_interface.h" +#include "core/core_timing.h" #include "core/hle/kernel/object.h" #include "core/hle/kernel/wait_object.h" #include "core/hle/result.h" @@ -53,23 +55,89 @@ enum class ThreadWakeupReason { Timeout // The thread was woken up due to a wait timeout. }; +class ThreadManager { +public: + ThreadManager(); + ~ThreadManager(); + + /** + * Creates a new thread ID + * @return The new thread ID + */ + u32 NewThreadId(); + + /** + * Gets the current thread + */ + Thread* GetCurrentThread() const; + + /** + * Reschedules to the next available thread (call after current thread is suspended) + */ + void Reschedule(); + + /** + * Prints the thread queue for debugging purposes + */ + void DebugThreadQueue(); + + /** + * Returns whether there are any threads that are ready to run. + */ + bool HaveReadyThreads(); + + /** + * Waits the current thread on a sleep + */ + void WaitCurrentThread_Sleep(); + + /** + * Stops the current thread and removes it from the thread_list + */ + void ExitCurrentThread(); + + /** + * Get a const reference to the thread list for debug use + */ + const std::vector>& GetThreadList(); + +private: + /** + * Switches the CPU's active thread context to that of the specified thread + * @param new_thread The thread to switch to + */ + void SwitchContext(Thread* new_thread); + + /** + * Pops and returns the next thread from the thread queue + * @return A pointer to the next ready thread + */ + Thread* PopNextReadyThread(); + + /** + * Callback that will wake up the thread it was scheduled for + * @param thread_id The ID of the thread that's been awoken + * @param cycles_late The number of CPU cycles that have passed since the desired wakeup time + */ + void ThreadWakeupCallback(u64 thread_id, s64 cycles_late); + + u32 next_thread_id = 1; + SharedPtr current_thread; + Common::ThreadQueueList ready_queue; + std::unordered_map wakeup_callback_table; + + /// Event type for the thread wake up event + Core::TimingEventType* ThreadWakeupEventType = nullptr; + + // Lists all threadsthat aren't deleted. + std::vector> thread_list; + + friend class Thread; + friend class KernelSystem; +}; + class Thread final : public WaitObject { public: - /** - * Creates and returns a new thread. The new thread is immediately scheduled - * @param name The friendly name desired for the thread - * @param entry_point The address at which the thread should start execution - * @param priority The thread's priority - * @param arg User data to pass to the thread - * @param processor_id The ID(s) of the processors on which the thread is desired to be run - * @param stack_top The address of the thread's stack top - * @param owner_process The parent process for the thread - * @return A shared pointer to the newly created thread - */ - static ResultVal> Create(std::string name, VAddr entry_point, u32 priority, - u32 arg, s32 processor_id, VAddr stack_top, - SharedPtr owner_process); - std::string GetName() const override { return name; } @@ -204,7 +272,7 @@ public: /// Mutexes that this thread is currently waiting for. boost::container::flat_set> pending_mutexes; - SharedPtr owner_process; ///< Process that owns this thread + Process* owner_process; ///< Process that owns this thread /// Objects that the thread is waiting on, in the same order as they were // passed to WaitSynchronization1/N. @@ -214,9 +282,6 @@ public: std::string name; - /// Handle used as userdata to reference this object when inserting into the CoreTiming queue. - Handle callback_handle; - using WakeupCallback = void(ThreadWakeupReason reason, SharedPtr thread, SharedPtr object); // Callback that will be invoked when the thread is resumed from a waiting state. If the thread @@ -225,69 +290,23 @@ public: std::function wakeup_callback; private: - Thread(); + explicit Thread(KernelSystem&); ~Thread() override; + + ThreadManager& thread_manager; + + friend class KernelSystem; }; /** * Sets up the primary application thread + * @param kernel The kernel instance on which the thread is created * @param entry_point The address at which the thread should start execution * @param priority The priority to give the main thread * @param owner_process The parent process for the main thread * @return A shared pointer to the main thread */ -SharedPtr SetupMainThread(u32 entry_point, u32 priority, SharedPtr owner_process); - -/** - * Returns whether there are any threads that are ready to run. - */ -bool HaveReadyThreads(); - -/** - * Reschedules to the next available thread (call after current thread is suspended) - */ -void Reschedule(); - -/** - * Arbitrate the highest priority thread that is waiting - * @param address The address for which waiting threads should be arbitrated - */ -Thread* ArbitrateHighestPriorityThread(u32 address); - -/** - * Arbitrate all threads currently waiting. - * @param address The address for which waiting threads should be arbitrated - */ -void ArbitrateAllThreads(u32 address); - -/** - * Gets the current thread - */ -Thread* GetCurrentThread(); - -/** - * Waits the current thread on a sleep - */ -void WaitCurrentThread_Sleep(); - -/** - * Stops the current thread and removes it from the thread_list - */ -void ExitCurrentThread(); - -/** - * Initialize threading - */ -void ThreadingInit(); - -/** - * Shutdown threading - */ -void ThreadingShutdown(); - -/** - * Get a const reference to the thread list for debug use - */ -const std::vector>& GetThreadList(); +SharedPtr SetupMainThread(KernelSystem& kernel, u32 entry_point, u32 priority, + SharedPtr owner_process); } // namespace Kernel diff --git a/src/core/hle/kernel/timer.cpp b/src/core/hle/kernel/timer.cpp index f1965365c..98e180aa6 100644 --- a/src/core/hle/kernel/timer.cpp +++ b/src/core/hle/kernel/timer.cpp @@ -3,9 +3,10 @@ // Refer to the license.txt file included. #include +#include #include "common/assert.h" #include "common/logging/log.h" -#include "core/core_timing.h" +#include "core/core.h" #include "core/hle/kernel/handle_table.h" #include "core/hle/kernel/object.h" #include "core/hle/kernel/thread.h" @@ -13,24 +14,22 @@ namespace Kernel { -/// The event type of the generic timer callback event -static CoreTiming::EventType* timer_callback_event_type = nullptr; -// TODO(yuriks): This can be removed if Timer objects are explicitly pooled in the future, allowing -// us to simply use a pool index or similar. -static Kernel::HandleTable timer_callback_handle_table; +Timer::Timer(KernelSystem& kernel) : WaitObject(kernel), timer_manager(kernel.GetTimerManager()) {} +Timer::~Timer() { + Cancel(); + timer_manager.timer_callback_table.erase(callback_id); +} -Timer::Timer() {} -Timer::~Timer() {} - -SharedPtr Timer::Create(ResetType reset_type, std::string name) { - SharedPtr timer(new Timer); +SharedPtr KernelSystem::CreateTimer(ResetType reset_type, std::string name) { + SharedPtr timer(new Timer(*this)); timer->reset_type = reset_type; timer->signaled = false; timer->name = std::move(name); timer->initial_delay = 0; timer->interval_delay = 0; - timer->callback_handle = timer_callback_handle_table.Create(timer).Unwrap(); + timer->callback_id = ++timer_manager->next_timer_callback_id; + timer_manager->timer_callback_table[timer->callback_id] = timer.get(); return timer; } @@ -57,12 +56,14 @@ void Timer::Set(s64 initial, s64 interval) { // Immediately invoke the callback Signal(0); } else { - CoreTiming::ScheduleEvent(nsToCycles(initial), timer_callback_event_type, callback_handle); + Core::System::GetInstance().CoreTiming().ScheduleEvent( + nsToCycles(initial), timer_manager.timer_callback_event_type, callback_id); } } void Timer::Cancel() { - CoreTiming::UnscheduleEvent(timer_callback_event_type, callback_handle); + Core::System::GetInstance().CoreTiming().UnscheduleEvent( + timer_manager.timer_callback_event_type, callback_id); } void Timer::Clear() { @@ -86,29 +87,28 @@ void Timer::Signal(s64 cycles_late) { if (interval_delay != 0) { // Reschedule the timer with the interval delay - CoreTiming::ScheduleEvent(nsToCycles(interval_delay) - cycles_late, - timer_callback_event_type, callback_handle); + Core::System::GetInstance().CoreTiming().ScheduleEvent( + nsToCycles(interval_delay) - cycles_late, timer_manager.timer_callback_event_type, + callback_id); } } /// The timer callback event, called when a timer is fired -static void TimerCallback(u64 timer_handle, s64 cycles_late) { - SharedPtr timer = - timer_callback_handle_table.Get(static_cast(timer_handle)); +void TimerManager::TimerCallback(u64 callback_id, s64 cycles_late) { + SharedPtr timer = timer_callback_table.at(callback_id); if (timer == nullptr) { - LOG_CRITICAL(Kernel, "Callback fired for invalid timer {:08x}", timer_handle); + LOG_CRITICAL(Kernel, "Callback fired for invalid timer {:016x}", callback_id); return; } timer->Signal(cycles_late); } -void TimersInit() { - timer_callback_handle_table.Clear(); - timer_callback_event_type = CoreTiming::RegisterEvent("TimerCallback", TimerCallback); +TimerManager::TimerManager() { + timer_callback_event_type = Core::System::GetInstance().CoreTiming().RegisterEvent( + "TimerCallback", + [this](u64 thread_id, s64 cycle_late) { TimerCallback(thread_id, cycle_late); }); } -void TimersShutdown() {} - } // namespace Kernel diff --git a/src/core/hle/kernel/timer.h b/src/core/hle/kernel/timer.h index 6607f6058..3e446675c 100644 --- a/src/core/hle/kernel/timer.h +++ b/src/core/hle/kernel/timer.h @@ -5,21 +5,32 @@ #pragma once #include "common/common_types.h" +#include "core/core_timing.h" #include "core/hle/kernel/object.h" #include "core/hle/kernel/wait_object.h" namespace Kernel { +class TimerManager { +public: + TimerManager(); + +private: + /// The timer callback event, called when a timer is fired + void TimerCallback(u64 callback_id, s64 cycles_late); + + /// The event type of the generic timer callback event + Core::TimingEventType* timer_callback_event_type = nullptr; + + u64 next_timer_callback_id = 0; + std::unordered_map timer_callback_table; + + friend class Timer; + friend class KernelSystem; +}; + class Timer final : public WaitObject { public: - /** - * Creates a timer - * @param reset_type ResetType describing how to create the timer - * @param name Optional name of timer - * @return The created Timer - */ - static SharedPtr Create(ResetType reset_type, std::string name = "Unknown"); - std::string GetTypeName() const override { return "Timer"; } @@ -68,7 +79,7 @@ public: void Signal(s64 cycles_late); private: - Timer(); + explicit Timer(KernelSystem& kernel); ~Timer() override; ResetType reset_type; ///< The ResetType of this timer @@ -79,13 +90,12 @@ private: bool signaled; ///< Whether the timer has been signaled or not std::string name; ///< Name of timer (optional) - /// Handle used as userdata to reference this object when inserting into the CoreTiming queue. - Handle callback_handle; + /// ID used as userdata to reference this object when inserting into the CoreTiming queue. + u64 callback_id; + + TimerManager& timer_manager; + + friend class KernelSystem; }; -/// Initializes the required variables for timers -void TimersInit(); -/// Tears down the timer variables -void TimersShutdown(); - } // namespace Kernel diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp index 36e24682b..bab8891bc 100644 --- a/src/core/hle/kernel/vm_manager.cpp +++ b/src/core/hle/kernel/vm_manager.cpp @@ -408,4 +408,25 @@ void VMManager::UpdatePageTableForVMA(const VirtualMemoryArea& vma) { break; } } + +ResultVal>> VMManager::GetBackingBlocksForRange(VAddr address, + u32 size) { + std::vector> backing_blocks; + VAddr interval_target = address; + while (interval_target != address + size) { + auto vma = FindVMA(interval_target); + if (vma->second.type != VMAType::BackingMemory) { + LOG_ERROR(Kernel, "Trying to use already freed memory"); + return ERR_INVALID_ADDRESS_STATE; + } + + VAddr interval_end = std::min(address + size, vma->second.base + vma->second.size); + u32 interval_size = interval_end - interval_target; + u8* backing_memory = vma->second.backing_memory + (interval_target - vma->second.base); + backing_blocks.push_back({backing_memory, interval_size}); + + interval_target += interval_size; + } + return MakeResult(backing_blocks); +} } // namespace Kernel diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h index 7ac5c3b01..5464ad50b 100644 --- a/src/core/hle/kernel/vm_manager.h +++ b/src/core/hle/kernel/vm_manager.h @@ -6,6 +6,7 @@ #include #include +#include #include #include "common/common_types.h" #include "core/hle/result.h" @@ -213,6 +214,9 @@ public: /// Dumps the address space layout to the log, for debugging void LogLayout(Log::Level log_level) const; + /// Gets a list of backing memory blocks for the specified range + ResultVal>> GetBackingBlocksForRange(VAddr address, u32 size); + /// Each VMManager has its own page table, which is set as the main one when the owning process /// is scheduled. Memory::PageTable page_table; diff --git a/src/core/hle/kernel/wait_object.cpp b/src/core/hle/kernel/wait_object.cpp index 12699f85e..d64b7def7 100644 --- a/src/core/hle/kernel/wait_object.cpp +++ b/src/core/hle/kernel/wait_object.cpp @@ -5,7 +5,6 @@ #include #include "common/assert.h" #include "common/logging/log.h" -#include "core/hle/config_mem.h" #include "core/hle/kernel/errors.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/memory.h" @@ -13,7 +12,6 @@ #include "core/hle/kernel/resource_limit.h" #include "core/hle/kernel/thread.h" #include "core/hle/kernel/timer.h" -#include "core/hle/shared_page.h" namespace Kernel { diff --git a/src/core/hle/kernel/wait_object.h b/src/core/hle/kernel/wait_object.h index 2b9a9393b..01fc40a0d 100644 --- a/src/core/hle/kernel/wait_object.h +++ b/src/core/hle/kernel/wait_object.h @@ -16,6 +16,8 @@ class Thread; /// Class that represents a Kernel object that a thread can be waiting on class WaitObject : public Object { public: + using Object::Object; + /** * Check if the specified thread should wait until the object is available * @param thread The thread about which we're deciding. diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 35e3c0ad9..972c826a9 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -1026,8 +1026,8 @@ void Module::Interface::BeginImportProgram(Kernel::HLERequestContext& ctx) { // Create our CIAFile handle for the app to write to, and while the app writes // Citra will store contents out to sdmc/nand const FileSys::Path cia_path = {}; - auto file = - std::make_shared(std::make_unique(media_type), cia_path); + auto file = std::make_shared( + am->system, std::make_unique(media_type), cia_path); am->cia_installing = true; @@ -1053,8 +1053,8 @@ void Module::Interface::BeginImportProgramTemporarily(Kernel::HLERequestContext& // Create our CIAFile handle for the app to write to, and while the app writes Citra will store // contents out to sdmc/nand const FileSys::Path cia_path = {}; - auto file = std::make_shared(std::make_unique(FS::MediaType::NAND), - cia_path); + auto file = std::make_shared( + am->system, std::make_unique(FS::MediaType::NAND), cia_path); am->cia_installing = true; @@ -1455,16 +1455,16 @@ void Module::Interface::GetMetaDataFromCia(Kernel::HLERequestContext& ctx) { rb.PushMappedBuffer(output_buffer); } -Module::Module() { +Module::Module(Core::System& system) : system(system) { ScanForAllTitles(); - system_updater_mutex = Kernel::Mutex::Create(false, "AM::SystemUpdaterMutex"); + system_updater_mutex = system.Kernel().CreateMutex(false, "AM::SystemUpdaterMutex"); } Module::~Module() = default; void InstallInterfaces(Core::System& system) { auto& service_manager = system.ServiceManager(); - auto am = std::make_shared(); + auto am = std::make_shared(system); std::make_shared(am)->InstallAsService(service_manager); std::make_shared(am)->InstallAsService(service_manager); std::make_shared(am)->InstallAsService(service_manager); diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h index 543dbd8fe..e00e16abd 100644 --- a/src/core/hle/service/am/am.h +++ b/src/core/hle/service/am/am.h @@ -149,7 +149,7 @@ std::string GetMediaTitlePath(Service::FS::MediaType media_type); class Module final { public: - Module(); + explicit Module(Core::System& system); ~Module(); class Interface : public ServiceFramework { @@ -573,6 +573,7 @@ private: */ void ScanForAllTitles(); + Core::System& system; bool cia_installing = false; std::array, 3> am_title_list; Kernel::SharedPtr system_updater_mutex; diff --git a/src/core/hle/service/apt/applet_manager.cpp b/src/core/hle/service/apt/applet_manager.cpp index 1abf1e521..076f5f6cf 100644 --- a/src/core/hle/service/apt/applet_manager.cpp +++ b/src/core/hle/service/apt/applet_manager.cpp @@ -258,7 +258,7 @@ ResultVal AppletManager::Initialize(AppletId ap slot_data->applet_id = static_cast(app_id); // Note: In the real console the title id of a given applet slot is set by the APT module when // calling StartApplication. - slot_data->title_id = Kernel::g_current_process->codeset->program_id; + slot_data->title_id = system.Kernel().GetCurrentProcess()->codeset->program_id; slot_data->attributes.raw = attributes.raw; if (slot_data->applet_id == AppletId::Application || @@ -572,9 +572,9 @@ AppletManager::AppletManager(Core::System& system) : system(system) { slot_data.registered = false; slot_data.loaded = false; slot_data.notification_event = - Kernel::Event::Create(Kernel::ResetType::OneShot, "APT:Notification"); + system.Kernel().CreateEvent(Kernel::ResetType::OneShot, "APT:Notification"); slot_data.parameter_event = - Kernel::Event::Create(Kernel::ResetType::OneShot, "APT:Parameter"); + system.Kernel().CreateEvent(Kernel::ResetType::OneShot, "APT:Parameter"); } HLE::Applets::Init(); } diff --git a/src/core/hle/service/apt/apt.cpp b/src/core/hle/service/apt/apt.cpp index a673fe97e..44b81a0fd 100644 --- a/src/core/hle/service/apt/apt.cpp +++ b/src/core/hle/service/apt/apt.cpp @@ -207,10 +207,17 @@ void Module::Interface::GetSharedFont(Kernel::HLERequestContext& ctx) { // The shared font has to be relocated to the new address before being passed to the // application. - auto maybe_vaddr = - Memory::PhysicalToVirtualAddress(apt->shared_font_mem->linear_heap_phys_address); - ASSERT(maybe_vaddr); - VAddr target_address = *maybe_vaddr; + + // Note: the target address is still in the old linear heap region even on new firmware + // versions. This exception is made for shared font to resolve the following compatibility + // issue: + // The linear heap region changes depending on the kernel version marked in application's + // exheader (not the actual version the application is running on). If an application with old + // kernel version and an applet with new kernel version run at the same time, and they both use + // shared font, different linear heap region would have required shared font to relocate + // according to two different addresses at the same time, which is impossible. + VAddr target_address = + apt->shared_font_mem->linear_heap_phys_offset + Memory::LINEAR_HEAP_VADDR; if (!apt->shared_font_relocated) { BCFNT::RelocateSharedFont(apt->shared_font_mem, target_address); apt->shared_font_relocated = true; @@ -855,11 +862,11 @@ Module::Module(Core::System& system) : system(system) { using Kernel::MemoryPermission; shared_font_mem = - Kernel::SharedMemory::Create(nullptr, 0x332000, // 3272 KB - MemoryPermission::ReadWrite, MemoryPermission::Read, 0, - Kernel::MemoryRegion::SYSTEM, "APT:SharedFont"); + system.Kernel().CreateSharedMemory(nullptr, 0x332000, // 3272 KB + MemoryPermission::ReadWrite, MemoryPermission::Read, 0, + Kernel::MemoryRegion::SYSTEM, "APT:SharedFont"); - lock = Kernel::Mutex::Create(false, "APT_U:Lock"); + lock = system.Kernel().CreateMutex(false, "APT_U:Lock"); } Module::~Module() {} diff --git a/src/core/hle/service/boss/boss.cpp b/src/core/hle/service/boss/boss.cpp index 8dd21ab3b..63df57427 100644 --- a/src/core/hle/service/boss/boss.cpp +++ b/src/core/hle/service/boss/boss.cpp @@ -903,15 +903,16 @@ void Module::Interface::GetNsDataNewFlagPrivileged(Kernel::HLERequestContext& ct Module::Interface::Interface(std::shared_ptr boss, const char* name, u32 max_session) : ServiceFramework(name, max_session), boss(std::move(boss)) {} -Module::Module() { +Module::Module(Core::System& system) { using namespace Kernel; // TODO: verify ResetType - task_finish_event = Event::Create(Kernel::ResetType::OneShot, "BOSS::task_finish_event"); + task_finish_event = + system.Kernel().CreateEvent(Kernel::ResetType::OneShot, "BOSS::task_finish_event"); } void InstallInterfaces(Core::System& system) { auto& service_manager = system.ServiceManager(); - auto boss = std::make_shared(); + auto boss = std::make_shared(system); std::make_shared(boss)->InstallAsService(service_manager); std::make_shared(boss)->InstallAsService(service_manager); } diff --git a/src/core/hle/service/boss/boss.h b/src/core/hle/service/boss/boss.h index 222f88066..15a41746e 100644 --- a/src/core/hle/service/boss/boss.h +++ b/src/core/hle/service/boss/boss.h @@ -15,7 +15,7 @@ namespace Service::BOSS { class Module final { public: - Module(); + explicit Module(Core::System& system); ~Module() = default; class Interface : public ServiceFramework { diff --git a/src/core/hle/service/cam/cam.cpp b/src/core/hle/service/cam/cam.cpp index 99dad6980..78d2e268e 100644 --- a/src/core/hle/service/cam/cam.cpp +++ b/src/core/hle/service/cam/cam.cpp @@ -151,7 +151,7 @@ void Module::StartReceiving(int port_id) { // schedules a completion event according to the frame rate. The event will block on the // capture task if it is not finished within the expected time - CoreTiming::ScheduleEvent( + system.CoreTiming().ScheduleEvent( msToCycles(LATENCY_BY_FRAME_RATE[static_cast(camera.frame_rate)]), completion_event_callback, port_id); } @@ -160,7 +160,7 @@ void Module::CancelReceiving(int port_id) { if (!ports[port_id].is_receiving) return; LOG_WARNING(Service_CAM, "tries to cancel an ongoing receiving process."); - CoreTiming::UnscheduleEvent(completion_event_callback, port_id); + system.CoreTiming().UnscheduleEvent(completion_event_callback, port_id); ports[port_id].capture_result.wait(); ports[port_id].is_receiving = false; } @@ -1019,16 +1019,17 @@ void Module::Interface::DriverFinalize(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_CAM, "called"); } -Module::Module() { +Module::Module(Core::System& system) : system(system) { using namespace Kernel; for (PortConfig& port : ports) { - port.completion_event = Event::Create(ResetType::Sticky, "CAM::completion_event"); + port.completion_event = + system.Kernel().CreateEvent(ResetType::Sticky, "CAM::completion_event"); port.buffer_error_interrupt_event = - Event::Create(ResetType::OneShot, "CAM::buffer_error_interrupt_event"); + system.Kernel().CreateEvent(ResetType::OneShot, "CAM::buffer_error_interrupt_event"); port.vsync_interrupt_event = - Event::Create(ResetType::OneShot, "CAM::vsync_interrupt_event"); + system.Kernel().CreateEvent(ResetType::OneShot, "CAM::vsync_interrupt_event"); } - completion_event_callback = CoreTiming::RegisterEvent( + completion_event_callback = system.CoreTiming().RegisterEvent( "CAM::CompletionEventCallBack", [this](u64 userdata, s64 cycles_late) { CompletionEventCallBack(userdata, cycles_late); }); } @@ -1061,7 +1062,7 @@ std::shared_ptr GetModule(Core::System& system) { void InstallInterfaces(Core::System& system) { auto& service_manager = system.ServiceManager(); - auto cam = std::make_shared(); + auto cam = std::make_shared(system); std::make_shared(cam)->InstallAsService(service_manager); std::make_shared(cam)->InstallAsService(service_manager); diff --git a/src/core/hle/service/cam/cam.h b/src/core/hle/service/cam/cam.h index 401a2a203..32b5bb0b4 100644 --- a/src/core/hle/service/cam/cam.h +++ b/src/core/hle/service/cam/cam.h @@ -21,8 +21,8 @@ namespace Camera { class CameraInterface; } -namespace CoreTiming { -struct EventType; +namespace Core { +struct TimingEventType; } namespace Kernel { @@ -241,7 +241,7 @@ static_assert(sizeof(PackageParameterWithContextDetail) == 28, class Module final { public: - Module(); + explicit Module(Core::System& system); ~Module(); void ReloadCameraDevices(); @@ -779,9 +779,10 @@ private: void LoadCameraImplementation(CameraConfig& camera, int camera_id); + Core::System& system; std::array cameras; std::array ports; - CoreTiming::EventType* completion_event_callback; + Core::TimingEventType* completion_event_callback; std::atomic is_camera_reload_pending{false}; }; diff --git a/src/core/hle/service/cecd/cecd.cpp b/src/core/hle/service/cecd/cecd.cpp index 451a5ce84..df217d76e 100644 --- a/src/core/hle/service/cecd/cecd.cpp +++ b/src/core/hle/service/cecd/cecd.cpp @@ -97,7 +97,7 @@ void Module::Interface::Open(Kernel::HLERequestContext& ctx) { if (path_type == CecDataPathType::MboxProgramId) { std::vector program_id(8); - u64_le le_program_id = Kernel::g_current_process->codeset->program_id; + u64_le le_program_id = cecd->system.Kernel().GetCurrentProcess()->codeset->program_id; std::memcpy(program_id.data(), &le_program_id, sizeof(u64)); session_data->file->Write(0, sizeof(u64), true, program_id.data()); session_data->file->Close(); @@ -1351,10 +1351,11 @@ Module::SessionData::~SessionData() { Module::Interface::Interface(std::shared_ptr cecd, const char* name, u32 max_session) : ServiceFramework(name, max_session), cecd(std::move(cecd)) {} -Module::Module() { +Module::Module(Core::System& system) : system(system) { using namespace Kernel; - cecinfo_event = Event::Create(Kernel::ResetType::OneShot, "CECD::cecinfo_event"); - change_state_event = Event::Create(Kernel::ResetType::OneShot, "CECD::change_state_event"); + cecinfo_event = system.Kernel().CreateEvent(Kernel::ResetType::OneShot, "CECD::cecinfo_event"); + change_state_event = + system.Kernel().CreateEvent(Kernel::ResetType::OneShot, "CECD::change_state_event"); std::string nand_directory = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir); FileSys::ArchiveFactory_SystemSaveData systemsavedata_factory(nand_directory); @@ -1433,7 +1434,7 @@ Module::~Module() = default; void InstallInterfaces(Core::System& system) { auto& service_manager = system.ServiceManager(); - auto cecd = std::make_shared(); + auto cecd = std::make_shared(system); std::make_shared(cecd)->InstallAsService(service_manager); std::make_shared(cecd)->InstallAsService(service_manager); std::make_shared(cecd)->InstallAsService(service_manager); diff --git a/src/core/hle/service/cecd/cecd.h b/src/core/hle/service/cecd/cecd.h index 929f0f397..d8023f5cf 100644 --- a/src/core/hle/service/cecd/cecd.h +++ b/src/core/hle/service/cecd/cecd.h @@ -23,7 +23,7 @@ namespace Service::CECD { class Module final { public: - Module(); + explicit Module(Core::System& system); ~Module(); enum class CecCommand : u32 { @@ -610,6 +610,8 @@ private: Kernel::SharedPtr cecinfo_event; Kernel::SharedPtr change_state_event; + + Core::System& system; }; /// Initialize CECD service(s) diff --git a/src/core/hle/service/csnd/csnd_snd.cpp b/src/core/hle/service/csnd/csnd_snd.cpp index 6e0488b3a..ec4f55c0d 100644 --- a/src/core/hle/service/csnd/csnd_snd.cpp +++ b/src/core/hle/service/csnd/csnd_snd.cpp @@ -19,10 +19,10 @@ void CSND_SND::Initialize(Kernel::HLERequestContext& ctx) { const u32 offset3 = rp.Pop(); using Kernel::MemoryPermission; - mutex = Kernel::Mutex::Create(false, "CSND:mutex"); - shared_memory = Kernel::SharedMemory::Create(nullptr, size, MemoryPermission::ReadWrite, - MemoryPermission::ReadWrite, 0, - Kernel::MemoryRegion::BASE, "CSND:SharedMemory"); + mutex = system.Kernel().CreateMutex(false, "CSND:mutex"); + shared_memory = system.Kernel().CreateSharedMemory( + nullptr, size, MemoryPermission::ReadWrite, MemoryPermission::ReadWrite, 0, + Kernel::MemoryRegion::BASE, "CSND:SharedMemory"); IPC::RequestBuilder rb = rp.MakeBuilder(1, 3); rb.Push(RESULT_SUCCESS); @@ -173,7 +173,7 @@ void CSND_SND::Reset(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_CSND, "(STUBBED) called"); } -CSND_SND::CSND_SND() : ServiceFramework("csnd:SND", 4) { +CSND_SND::CSND_SND(Core::System& system) : ServiceFramework("csnd:SND", 4), system(system) { static const FunctionInfo functions[] = { // clang-format off {0x00010140, &CSND_SND::Initialize, "Initialize"}, @@ -196,7 +196,7 @@ CSND_SND::CSND_SND() : ServiceFramework("csnd:SND", 4) { void InstallInterfaces(Core::System& system) { auto& service_manager = system.ServiceManager(); - std::make_shared()->InstallAsService(service_manager); + std::make_shared(system)->InstallAsService(service_manager); } } // namespace Service::CSND diff --git a/src/core/hle/service/csnd/csnd_snd.h b/src/core/hle/service/csnd/csnd_snd.h index 909034e6d..94b1bf3bb 100644 --- a/src/core/hle/service/csnd/csnd_snd.h +++ b/src/core/hle/service/csnd/csnd_snd.h @@ -16,7 +16,7 @@ namespace Service::CSND { class CSND_SND final : public ServiceFramework { public: - CSND_SND(); + explicit CSND_SND(Core::System& system); ~CSND_SND() = default; private: @@ -174,6 +174,8 @@ private: }; static_assert(sizeof(Type0Command) == 0x20, "Type0Command structure size is wrong"); + Core::System& system; + Kernel::SharedPtr mutex = nullptr; Kernel::SharedPtr shared_memory = nullptr; diff --git a/src/core/hle/service/dsp/dsp_dsp.cpp b/src/core/hle/service/dsp/dsp_dsp.cpp index fa1ddd4c9..b15e670d3 100644 --- a/src/core/hle/service/dsp/dsp_dsp.cpp +++ b/src/core/hle/service/dsp/dsp_dsp.cpp @@ -350,7 +350,7 @@ bool DSP_DSP::HasTooManyEventsRegistered() const { return number >= max_number_of_interrupt_events; } -DSP_DSP::DSP_DSP() : ServiceFramework("dsp::DSP", DefaultMaxSessions) { +DSP_DSP::DSP_DSP(Core::System& system) : ServiceFramework("dsp::DSP", DefaultMaxSessions) { static const FunctionInfo functions[] = { // clang-format off {0x00010040, &DSP_DSP::RecvData, "RecvData"}, @@ -391,7 +391,8 @@ DSP_DSP::DSP_DSP() : ServiceFramework("dsp::DSP", DefaultMaxSessions) { RegisterHandlers(functions); - semaphore_event = Kernel::Event::Create(Kernel::ResetType::OneShot, "DSP_DSP::semaphore_event"); + semaphore_event = + system.Kernel().CreateEvent(Kernel::ResetType::OneShot, "DSP_DSP::semaphore_event"); } DSP_DSP::~DSP_DSP() { @@ -401,7 +402,7 @@ DSP_DSP::~DSP_DSP() { void InstallInterfaces(Core::System& system) { auto& service_manager = system.ServiceManager(); - auto dsp = std::make_shared(); + auto dsp = std::make_shared(system); dsp->InstallAsService(service_manager); Core::DSP().SetServiceToInterrupt(std::move(dsp)); } diff --git a/src/core/hle/service/dsp/dsp_dsp.h b/src/core/hle/service/dsp/dsp_dsp.h index 506a1f9f4..7226aeeac 100644 --- a/src/core/hle/service/dsp/dsp_dsp.h +++ b/src/core/hle/service/dsp/dsp_dsp.h @@ -17,7 +17,7 @@ namespace Service::DSP { class DSP_DSP final : public ServiceFramework { public: - DSP_DSP(); + explicit DSP_DSP(Core::System& system); ~DSP_DSP(); /// There are three types of interrupts diff --git a/src/core/hle/service/err_f.cpp b/src/core/hle/service/err_f.cpp index 9a914ee7b..13b2c7e46 100644 --- a/src/core/hle/service/err_f.cpp +++ b/src/core/hle/service/err_f.cpp @@ -244,7 +244,7 @@ ERR_F::~ERR_F() = default; void InstallInterfaces(Core::System& system) { auto errf = std::make_shared(system); - errf->InstallAsNamedPort(); + errf->InstallAsNamedPort(system.Kernel()); } } // namespace Service::ERR diff --git a/src/core/hle/service/fs/archive.cpp b/src/core/hle/service/fs/archive.cpp index 0f2375124..4322f6653 100644 --- a/src/core/hle/service/fs/archive.cpp +++ b/src/core/hle/service/fs/archive.cpp @@ -87,7 +87,7 @@ ResultVal> ArchiveManager::OpenFileFromArchive(ArchiveHand if (backend.Failed()) return backend.Code(); - auto file = std::shared_ptr(new File(std::move(backend).Unwrap(), path)); + auto file = std::shared_ptr(new File(system, std::move(backend).Unwrap(), path)); return MakeResult>(std::move(file)); } @@ -348,7 +348,7 @@ void ArchiveManager::RegisterSelfNCCH(Loader::AppLoader& app_loader) { factory->Register(app_loader); } -ArchiveManager::ArchiveManager() { +ArchiveManager::ArchiveManager(Core::System& system) : system(system) { RegisterArchiveTypes(); } diff --git a/src/core/hle/service/fs/archive.h b/src/core/hle/service/fs/archive.h index ef9b9efc2..f335f650d 100644 --- a/src/core/hle/service/fs/archive.h +++ b/src/core/hle/service/fs/archive.h @@ -24,6 +24,10 @@ namespace Loader { class AppLoader; } +namespace Core { +class System; +} + namespace Service::FS { /// Supported archive types @@ -50,7 +54,8 @@ using FileSys::ArchiveFactory; class ArchiveManager { public: - ArchiveManager(); + explicit ArchiveManager(Core::System& system); + /** * Opens an archive * @param id_code IdCode of the archive to open @@ -224,6 +229,8 @@ public: void RegisterSelfNCCH(Loader::AppLoader& app_loader); private: + Core::System& system; + /** * Registers an Archive type, instances of which can later be opened using its IdCode. * @param factory File system backend interface to the archive diff --git a/src/core/hle/service/fs/file.cpp b/src/core/hle/service/fs/file.cpp index d2f1f2a85..d4beda2c4 100644 --- a/src/core/hle/service/fs/file.cpp +++ b/src/core/hle/service/fs/file.cpp @@ -3,6 +3,7 @@ // Refer to the license.txt file included. #include "common/logging/log.h" +#include "core/core.h" #include "core/file_sys/errors.h" #include "core/file_sys/file_backend.h" #include "core/hle/ipc_helpers.h" @@ -14,8 +15,9 @@ namespace Service::FS { -File::File(std::unique_ptr&& backend, const FileSys::Path& path) - : ServiceFramework("", 1), path(path), backend(std::move(backend)) { +File::File(Core::System& system, std::unique_ptr&& backend, + const FileSys::Path& path) + : ServiceFramework("", 1), path(path), backend(std::move(backend)), system(system) { static const FunctionInfo functions[] = { {0x08010100, &File::OpenSubFile, "OpenSubFile"}, {0x080200C2, &File::Read, "Read"}, @@ -69,7 +71,8 @@ void File::Read(Kernel::HLERequestContext& ctx) { rb.PushMappedBuffer(buffer); std::chrono::nanoseconds read_timeout_ns{backend->GetReadDelayNs(length)}; - ctx.SleepClientThread(Kernel::GetCurrentThread(), "file::read", read_timeout_ns, + ctx.SleepClientThread(system.Kernel().GetThreadManager().GetCurrentThread(), "file::read", + read_timeout_ns, [](Kernel::SharedPtr thread, Kernel::HLERequestContext& ctx, Kernel::ThreadWakeupReason reason) { // Nothing to do here @@ -195,7 +198,7 @@ void File::OpenLinkFile(Kernel::HLERequestContext& ctx) { using Kernel::SharedPtr; IPC::RequestParser rp(ctx, 0x080C, 0, 0); IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); - auto sessions = ServerSession::CreateSessionPair(GetName()); + auto sessions = system.Kernel().CreateSessionPair(GetName()); auto server = std::get>(sessions); ClientConnected(server); @@ -243,7 +246,7 @@ void File::OpenSubFile(Kernel::HLERequestContext& ctx) { using Kernel::ClientSession; using Kernel::ServerSession; using Kernel::SharedPtr; - auto sessions = ServerSession::CreateSessionPair(GetName()); + auto sessions = system.Kernel().CreateSessionPair(GetName()); auto server = std::get>(sessions); ClientConnected(server); @@ -258,7 +261,7 @@ void File::OpenSubFile(Kernel::HLERequestContext& ctx) { } Kernel::SharedPtr File::Connect() { - auto sessions = Kernel::ServerSession::CreateSessionPair(GetName()); + auto sessions = system.Kernel().CreateSessionPair(GetName()); auto server = std::get>(sessions); ClientConnected(server); diff --git a/src/core/hle/service/fs/file.h b/src/core/hle/service/fs/file.h index 2de9add98..b946491b8 100644 --- a/src/core/hle/service/fs/file.h +++ b/src/core/hle/service/fs/file.h @@ -8,6 +8,10 @@ #include "core/hle/kernel/kernel.h" #include "core/hle/service/service.h" +namespace Core { +class System; +} + namespace Service::FS { struct FileSessionSlot : public Kernel::SessionRequestHandler::SessionDataBase { @@ -21,7 +25,8 @@ struct FileSessionSlot : public Kernel::SessionRequestHandler::SessionDataBase { // Consider splitting ServiceFramework interface. class File final : public ServiceFramework { public: - File(std::unique_ptr&& backend, const FileSys::Path& path); + File(Core::System& system, std::unique_ptr&& backend, + const FileSys::Path& path); ~File() = default; std::string GetName() const { @@ -53,6 +58,8 @@ private: void GetPriority(Kernel::HLERequestContext& ctx); void OpenLinkFile(Kernel::HLERequestContext& ctx); void OpenSubFile(Kernel::HLERequestContext& ctx); + + Core::System& system; }; } // namespace Service::FS diff --git a/src/core/hle/service/fs/fs_user.cpp b/src/core/hle/service/fs/fs_user.cpp index e4a7c251d..f9b883fe7 100644 --- a/src/core/hle/service/fs/fs_user.cpp +++ b/src/core/hle/service/fs/fs_user.cpp @@ -286,7 +286,7 @@ void FS_USER::OpenDirectory(Kernel::HLERequestContext& ctx) { rb.Push(dir_res.Code()); if (dir_res.Succeeded()) { std::shared_ptr directory = *dir_res; - auto sessions = ServerSession::CreateSessionPair(directory->GetName()); + auto sessions = system.Kernel().CreateSessionPair(directory->GetName()); directory->ClientConnected(std::get>(sessions)); rb.PushMoveObjects(std::get>(sessions)); } else { @@ -619,7 +619,7 @@ void FS_USER::GetProgramLaunchInfo(Kernel::HLERequestContext& ctx) { // TODO(Subv): The real FS service manages its own process list and only checks the processes // that were registered with the 'fs:REG' service. - auto process = Kernel::GetProcessById(process_id); + auto process = system.Kernel().GetProcessById(process_id); IPC::RequestBuilder rb = rp.MakeBuilder(5, 0); @@ -741,7 +741,8 @@ void FS_USER::GetSaveDataSecureValue(Kernel::HLERequestContext& ctx) { rb.Push(0); // the secure value } -FS_USER::FS_USER(ArchiveManager& archives) : ServiceFramework("fs:USER", 30), archives(archives) { +FS_USER::FS_USER(Core::System& system) + : ServiceFramework("fs:USER", 30), system(system), archives(system.ArchiveManager()) { static const FunctionInfo functions[] = { {0x000100C6, nullptr, "Dummy1"}, {0x040100C4, nullptr, "Control"}, @@ -860,6 +861,6 @@ FS_USER::FS_USER(ArchiveManager& archives) : ServiceFramework("fs:USER", 30), ar void InstallInterfaces(Core::System& system) { auto& service_manager = system.ServiceManager(); - std::make_shared(system.ArchiveManager())->InstallAsService(service_manager); + std::make_shared(system)->InstallAsService(service_manager); } } // namespace Service::FS diff --git a/src/core/hle/service/fs/fs_user.h b/src/core/hle/service/fs/fs_user.h index eb1b5af36..90c0ed7ec 100644 --- a/src/core/hle/service/fs/fs_user.h +++ b/src/core/hle/service/fs/fs_user.h @@ -17,7 +17,7 @@ class ArchiveManager; class FS_USER final : public ServiceFramework { public: - explicit FS_USER(ArchiveManager& archives); + explicit FS_USER(Core::System& system); private: void Initialize(Kernel::HLERequestContext& ctx); @@ -534,6 +534,7 @@ private: u32 priority = -1; ///< For SetPriority and GetPriority service functions + Core::System& system; ArchiveManager& archives; }; diff --git a/src/core/hle/service/gsp/gsp_gpu.cpp b/src/core/hle/service/gsp/gsp_gpu.cpp index be2a7ca64..8ed79ebca 100644 --- a/src/core/hle/service/gsp/gsp_gpu.cpp +++ b/src/core/hle/service/gsp/gsp_gpu.cpp @@ -11,6 +11,7 @@ #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/handle_table.h" #include "core/hle/kernel/shared_memory.h" +#include "core/hle/kernel/shared_page.h" #include "core/hle/result.h" #include "core/hle/service/gsp/gsp_gpu.h" #include "core/hw/gpu.h" @@ -731,7 +732,7 @@ void GSP_GPU::SetLedForceOff(Kernel::HLERequestContext& ctx) { u8 state = rp.Pop(); - system.GetSharedPageHandler()->Set3DLed(state); + system.Kernel().GetSharedPageHandler().Set3DLed(state); IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); rb.Push(RESULT_SUCCESS); @@ -786,9 +787,9 @@ GSP_GPU::GSP_GPU(Core::System& system) : ServiceFramework("gsp::Gpu", 2), system RegisterHandlers(functions); using Kernel::MemoryPermission; - shared_memory = Kernel::SharedMemory::Create(nullptr, 0x1000, MemoryPermission::ReadWrite, - MemoryPermission::ReadWrite, 0, - Kernel::MemoryRegion::BASE, "GSP:SharedMemory"); + shared_memory = system.Kernel().CreateSharedMemory( + nullptr, 0x1000, MemoryPermission::ReadWrite, MemoryPermission::ReadWrite, 0, + Kernel::MemoryRegion::BASE, "GSP:SharedMemory"); first_initialization = true; }; diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index b9876012e..805cc6c91 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -128,7 +128,7 @@ void Module::UpdatePadCallback(u64 userdata, s64 cycles_late) { // If we just updated index 0, provide a new timestamp if (mem->pad.index == 0) { mem->pad.index_reset_ticks_previous = mem->pad.index_reset_ticks; - mem->pad.index_reset_ticks = (s64)CoreTiming::GetTicks(); + mem->pad.index_reset_ticks = (s64)system.CoreTiming().GetTicks(); } mem->touch.index = next_touch_index; @@ -152,7 +152,7 @@ void Module::UpdatePadCallback(u64 userdata, s64 cycles_late) { // If we just updated index 0, provide a new timestamp if (mem->touch.index == 0) { mem->touch.index_reset_ticks_previous = mem->touch.index_reset_ticks; - mem->touch.index_reset_ticks = (s64)CoreTiming::GetTicks(); + mem->touch.index_reset_ticks = (s64)system.CoreTiming().GetTicks(); } // Signal both handles when there's an update to Pad or touch @@ -160,7 +160,7 @@ void Module::UpdatePadCallback(u64 userdata, s64 cycles_late) { event_pad_or_touch_2->Signal(); // Reschedule recurrent event - CoreTiming::ScheduleEvent(pad_update_ticks - cycles_late, pad_update_event); + system.CoreTiming().ScheduleEvent(pad_update_ticks - cycles_late, pad_update_event); } void Module::UpdateAccelerometerCallback(u64 userdata, s64 cycles_late) { @@ -198,13 +198,14 @@ void Module::UpdateAccelerometerCallback(u64 userdata, s64 cycles_late) { // If we just updated index 0, provide a new timestamp if (mem->accelerometer.index == 0) { mem->accelerometer.index_reset_ticks_previous = mem->accelerometer.index_reset_ticks; - mem->accelerometer.index_reset_ticks = (s64)CoreTiming::GetTicks(); + mem->accelerometer.index_reset_ticks = (s64)system.CoreTiming().GetTicks(); } event_accelerometer->Signal(); // Reschedule recurrent event - CoreTiming::ScheduleEvent(accelerometer_update_ticks - cycles_late, accelerometer_update_event); + system.CoreTiming().ScheduleEvent(accelerometer_update_ticks - cycles_late, + accelerometer_update_event); } void Module::UpdateGyroscopeCallback(u64 userdata, s64 cycles_late) { @@ -233,13 +234,13 @@ void Module::UpdateGyroscopeCallback(u64 userdata, s64 cycles_late) { // If we just updated index 0, provide a new timestamp if (mem->gyroscope.index == 0) { mem->gyroscope.index_reset_ticks_previous = mem->gyroscope.index_reset_ticks; - mem->gyroscope.index_reset_ticks = (s64)CoreTiming::GetTicks(); + mem->gyroscope.index_reset_ticks = (s64)system.CoreTiming().GetTicks(); } event_gyroscope->Signal(); // Reschedule recurrent event - CoreTiming::ScheduleEvent(gyroscope_update_ticks - cycles_late, gyroscope_update_event); + system.CoreTiming().ScheduleEvent(gyroscope_update_ticks - cycles_late, gyroscope_update_event); } void Module::Interface::GetIPCHandles(Kernel::HLERequestContext& ctx) { @@ -257,7 +258,8 @@ void Module::Interface::EnableAccelerometer(Kernel::HLERequestContext& ctx) { // Schedules the accelerometer update event if the accelerometer was just enabled if (hid->enable_accelerometer_count == 1) { - CoreTiming::ScheduleEvent(accelerometer_update_ticks, hid->accelerometer_update_event); + hid->system.CoreTiming().ScheduleEvent(accelerometer_update_ticks, + hid->accelerometer_update_event); } IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); @@ -273,7 +275,7 @@ void Module::Interface::DisableAccelerometer(Kernel::HLERequestContext& ctx) { // Unschedules the accelerometer update event if the accelerometer was just disabled if (hid->enable_accelerometer_count == 0) { - CoreTiming::UnscheduleEvent(hid->accelerometer_update_event, 0); + hid->system.CoreTiming().UnscheduleEvent(hid->accelerometer_update_event, 0); } IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); @@ -289,7 +291,7 @@ void Module::Interface::EnableGyroscopeLow(Kernel::HLERequestContext& ctx) { // Schedules the gyroscope update event if the gyroscope was just enabled if (hid->enable_gyroscope_count == 1) { - CoreTiming::ScheduleEvent(gyroscope_update_ticks, hid->gyroscope_update_event); + hid->system.CoreTiming().ScheduleEvent(gyroscope_update_ticks, hid->gyroscope_update_event); } IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); @@ -305,7 +307,7 @@ void Module::Interface::DisableGyroscopeLow(Kernel::HLERequestContext& ctx) { // Unschedules the gyroscope update event if the gyroscope was just disabled if (hid->enable_gyroscope_count == 0) { - CoreTiming::UnscheduleEvent(hid->gyroscope_update_event, 0); + hid->system.CoreTiming().UnscheduleEvent(hid->gyroscope_update_event, 0); } IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); @@ -359,31 +361,33 @@ std::shared_ptr Module::Interface::GetModule() const { Module::Module(Core::System& system) : system(system) { using namespace Kernel; - shared_mem = - SharedMemory::Create(nullptr, 0x1000, MemoryPermission::ReadWrite, MemoryPermission::Read, - 0, MemoryRegion::BASE, "HID:SharedMemory"); + shared_mem = system.Kernel().CreateSharedMemory(nullptr, 0x1000, MemoryPermission::ReadWrite, + MemoryPermission::Read, 0, MemoryRegion::BASE, + "HID:SharedMemory"); // Create event handles - event_pad_or_touch_1 = Event::Create(ResetType::OneShot, "HID:EventPadOrTouch1"); - event_pad_or_touch_2 = Event::Create(ResetType::OneShot, "HID:EventPadOrTouch2"); - event_accelerometer = Event::Create(ResetType::OneShot, "HID:EventAccelerometer"); - event_gyroscope = Event::Create(ResetType::OneShot, "HID:EventGyroscope"); - event_debug_pad = Event::Create(ResetType::OneShot, "HID:EventDebugPad"); + event_pad_or_touch_1 = system.Kernel().CreateEvent(ResetType::OneShot, "HID:EventPadOrTouch1"); + event_pad_or_touch_2 = system.Kernel().CreateEvent(ResetType::OneShot, "HID:EventPadOrTouch2"); + event_accelerometer = system.Kernel().CreateEvent(ResetType::OneShot, "HID:EventAccelerometer"); + event_gyroscope = system.Kernel().CreateEvent(ResetType::OneShot, "HID:EventGyroscope"); + event_debug_pad = system.Kernel().CreateEvent(ResetType::OneShot, "HID:EventDebugPad"); // Register update callbacks + Core::Timing& timing = system.CoreTiming(); pad_update_event = - CoreTiming::RegisterEvent("HID::UpdatePadCallback", [this](u64 userdata, s64 cycles_late) { + timing.RegisterEvent("HID::UpdatePadCallback", [this](u64 userdata, s64 cycles_late) { UpdatePadCallback(userdata, cycles_late); }); - accelerometer_update_event = CoreTiming::RegisterEvent( + accelerometer_update_event = timing.RegisterEvent( "HID::UpdateAccelerometerCallback", [this](u64 userdata, s64 cycles_late) { UpdateAccelerometerCallback(userdata, cycles_late); }); - gyroscope_update_event = CoreTiming::RegisterEvent( - "HID::UpdateGyroscopeCallback", - [this](u64 userdata, s64 cycles_late) { UpdateGyroscopeCallback(userdata, cycles_late); }); + gyroscope_update_event = + timing.RegisterEvent("HID::UpdateGyroscopeCallback", [this](u64 userdata, s64 cycles_late) { + UpdateGyroscopeCallback(userdata, cycles_late); + }); - CoreTiming::ScheduleEvent(pad_update_ticks, pad_update_event); + timing.ScheduleEvent(pad_update_ticks, pad_update_event); } void Module::ReloadInputDevices() { diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h index 45f94bb30..de0b6f0a9 100644 --- a/src/core/hle/service/hid/hid.h +++ b/src/core/hle/service/hid/hid.h @@ -27,8 +27,8 @@ class Event; class SharedMemory; } // namespace Kernel -namespace CoreTiming { -struct EventType; +namespace Core { +struct TimingEventType; }; namespace Service::HID { @@ -325,9 +325,9 @@ private: int enable_accelerometer_count = 0; // positive means enabled int enable_gyroscope_count = 0; // positive means enabled - CoreTiming::EventType* pad_update_event; - CoreTiming::EventType* accelerometer_update_event; - CoreTiming::EventType* gyroscope_update_event; + Core::TimingEventType* pad_update_event; + Core::TimingEventType* accelerometer_update_event; + Core::TimingEventType* gyroscope_update_event; std::atomic is_device_reload_pending{true}; std::array, Settings::NativeButton::NUM_BUTTONS_HID> diff --git a/src/core/hle/service/ir/extra_hid.cpp b/src/core/hle/service/ir/extra_hid.cpp index a53c9fbf7..31b63257e 100644 --- a/src/core/hle/service/ir/extra_hid.cpp +++ b/src/core/hle/service/ir/extra_hid.cpp @@ -4,6 +4,7 @@ #include "common/alignment.h" #include "common/string_util.h" +#include "core/core.h" #include "core/core_timing.h" #include "core/hle/service/ir/extra_hid.h" #include "core/movie.h" @@ -144,11 +145,11 @@ ExtraHID::ExtraHID(SendFunc send_func) : IRDevice(send_func) { 0x65, }}; - hid_polling_callback_id = - CoreTiming::RegisterEvent("ExtraHID::SendHIDStatus", [this](u64, s64 cycles_late) { + hid_polling_callback_id = Core::System::GetInstance().CoreTiming().RegisterEvent( + "ExtraHID::SendHIDStatus", [this](u64, s64 cycles_late) { SendHIDStatus(); - CoreTiming::ScheduleEvent(msToCycles(hid_period) - cycles_late, - hid_polling_callback_id); + Core::System::GetInstance().CoreTiming().ScheduleEvent( + msToCycles(hid_period) - cycles_late, hid_polling_callback_id); }); } @@ -159,7 +160,7 @@ ExtraHID::~ExtraHID() { void ExtraHID::OnConnect() {} void ExtraHID::OnDisconnect() { - CoreTiming::UnscheduleEvent(hid_polling_callback_id, 0); + Core::System::GetInstance().CoreTiming().UnscheduleEvent(hid_polling_callback_id, 0); } void ExtraHID::HandleConfigureHIDPollingRequest(const std::vector& request) { @@ -170,9 +171,10 @@ void ExtraHID::HandleConfigureHIDPollingRequest(const std::vector& request) } // Change HID input polling interval - CoreTiming::UnscheduleEvent(hid_polling_callback_id, 0); + Core::System::GetInstance().CoreTiming().UnscheduleEvent(hid_polling_callback_id, 0); hid_period = request[1]; - CoreTiming::ScheduleEvent(msToCycles(hid_period), hid_polling_callback_id); + Core::System::GetInstance().CoreTiming().ScheduleEvent(msToCycles(hid_period), + hid_polling_callback_id); } void ExtraHID::HandleReadCalibrationDataRequest(const std::vector& request_buf) { diff --git a/src/core/hle/service/ir/extra_hid.h b/src/core/hle/service/ir/extra_hid.h index dc4691d87..f9c2f050b 100644 --- a/src/core/hle/service/ir/extra_hid.h +++ b/src/core/hle/service/ir/extra_hid.h @@ -11,9 +11,9 @@ #include "core/frontend/input.h" #include "core/hle/service/ir/ir_user.h" -namespace CoreTiming { -struct EventType; -} // namespace CoreTiming +namespace Core { +struct TimingEventType; +} // namespace Core namespace Service::IR { @@ -57,7 +57,7 @@ private: void LoadInputDevices(); u8 hid_period; - CoreTiming::EventType* hid_polling_callback_id; + Core::TimingEventType* hid_polling_callback_id; std::array calibration_data; std::unique_ptr zl; std::unique_ptr zr; diff --git a/src/core/hle/service/ir/ir.cpp b/src/core/hle/service/ir/ir.cpp index 0869941f2..5d99908cc 100644 --- a/src/core/hle/service/ir/ir.cpp +++ b/src/core/hle/service/ir/ir.cpp @@ -16,10 +16,10 @@ void InstallInterfaces(Core::System& system) { auto& service_manager = system.ServiceManager(); std::make_shared()->InstallAsService(service_manager); - auto ir_user = std::make_shared(); + auto ir_user = std::make_shared(system); ir_user->InstallAsService(service_manager); - auto ir_rst = std::make_shared(); + auto ir_rst = std::make_shared(system); ir_rst->InstallAsService(service_manager); } diff --git a/src/core/hle/service/ir/ir_rst.cpp b/src/core/hle/service/ir/ir_rst.cpp index 3dbf04722..33e4fc0ff 100644 --- a/src/core/hle/service/ir/ir_rst.cpp +++ b/src/core/hle/service/ir/ir_rst.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "core/core.h" #include "core/core_timing.h" #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/event.h" @@ -99,13 +100,13 @@ void IR_RST::UpdateCallback(u64 userdata, s64 cycles_late) { // If we just updated index 0, provide a new timestamp if (mem->index == 0) { mem->index_reset_ticks_previous = mem->index_reset_ticks; - mem->index_reset_ticks = CoreTiming::GetTicks(); + mem->index_reset_ticks = system.CoreTiming().GetTicks(); } update_event->Signal(); // Reschedule recurrent event - CoreTiming::ScheduleEvent(msToCycles(update_period) - cycles_late, update_callback_id); + system.CoreTiming().ScheduleEvent(msToCycles(update_period) - cycles_late, update_callback_id); } void IR_RST::GetHandles(Kernel::HLERequestContext& ctx) { @@ -125,7 +126,7 @@ void IR_RST::Initialize(Kernel::HLERequestContext& ctx) { next_pad_index = 0; is_device_reload_pending.store(true); - CoreTiming::ScheduleEvent(msToCycles(update_period), update_callback_id); + system.CoreTiming().ScheduleEvent(msToCycles(update_period), update_callback_id); IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); rb.Push(RESULT_SUCCESS); @@ -136,7 +137,7 @@ void IR_RST::Initialize(Kernel::HLERequestContext& ctx) { void IR_RST::Shutdown(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx, 0x03, 0, 0); - CoreTiming::UnscheduleEvent(update_callback_id, 0); + system.CoreTiming().UnscheduleEvent(update_callback_id, 0); UnloadInputDevices(); IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); @@ -144,19 +145,18 @@ void IR_RST::Shutdown(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_IR, "called"); } -IR_RST::IR_RST() : ServiceFramework("ir:rst", 1) { +IR_RST::IR_RST(Core::System& system) : ServiceFramework("ir:rst", 1), system(system) { using namespace Kernel; // Note: these two kernel objects are even available before Initialize service function is // called. - shared_memory = - SharedMemory::Create(nullptr, 0x1000, MemoryPermission::ReadWrite, MemoryPermission::Read, - 0, MemoryRegion::BASE, "IRRST:SharedMemory"); - update_event = Event::Create(ResetType::OneShot, "IRRST:UpdateEvent"); + shared_memory = system.Kernel().CreateSharedMemory(nullptr, 0x1000, MemoryPermission::ReadWrite, + MemoryPermission::Read, 0, + MemoryRegion::BASE, "IRRST:SharedMemory"); + update_event = system.Kernel().CreateEvent(ResetType::OneShot, "IRRST:UpdateEvent"); - update_callback_id = - CoreTiming::RegisterEvent("IRRST:UpdateCallBack", [this](u64 userdata, s64 cycles_late) { - UpdateCallback(userdata, cycles_late); - }); + update_callback_id = system.CoreTiming().RegisterEvent( + "IRRST:UpdateCallBack", + [this](u64 userdata, s64 cycles_late) { UpdateCallback(userdata, cycles_late); }); static const FunctionInfo functions[] = { {0x00010000, &IR_RST::GetHandles, "GetHandles"}, diff --git a/src/core/hle/service/ir/ir_rst.h b/src/core/hle/service/ir/ir_rst.h index 0ba5ea8f7..e72adae7c 100644 --- a/src/core/hle/service/ir/ir_rst.h +++ b/src/core/hle/service/ir/ir_rst.h @@ -18,8 +18,8 @@ class Event; class SharedMemory; } // namespace Kernel -namespace CoreTiming { -struct EventType; +namespace Core { +struct TimingEventType; }; namespace Service::IR { @@ -39,7 +39,7 @@ union PadState { /// Interface to "ir:rst" service class IR_RST final : public ServiceFramework { public: - IR_RST(); + explicit IR_RST(Core::System& system); ~IR_RST(); void ReloadInputDevices(); @@ -77,10 +77,11 @@ private: void UnloadInputDevices(); void UpdateCallback(u64 userdata, s64 cycles_late); + Core::System& system; Kernel::SharedPtr update_event; Kernel::SharedPtr shared_memory; u32 next_pad_index{0}; - CoreTiming::EventType* update_callback_id; + Core::TimingEventType* update_callback_id; std::unique_ptr zl_button; std::unique_ptr zr_button; std::unique_ptr c_stick; diff --git a/src/core/hle/service/ir/ir_user.cpp b/src/core/hle/service/ir/ir_user.cpp index 2edf11c48..e0158f67e 100644 --- a/src/core/hle/service/ir/ir_user.cpp +++ b/src/core/hle/service/ir/ir_user.cpp @@ -6,6 +6,7 @@ #include #include "common/string_util.h" #include "common/swap.h" +#include "core/core.h" #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/event.h" #include "core/hle/kernel/shared_memory.h" @@ -380,7 +381,7 @@ void IR_USER::ReleaseReceivedData(Kernel::HLERequestContext& ctx) { LOG_TRACE(Service_IR, "called, count={}", count); } -IR_USER::IR_USER() : ServiceFramework("ir:USER", 1) { +IR_USER::IR_USER(Core::System& system) : ServiceFramework("ir:USER", 1) { const FunctionInfo functions[] = { {0x00010182, nullptr, "InitializeIrNop"}, {0x00020000, &IR_USER::FinalizeIrNop, "FinalizeIrNop"}, @@ -413,9 +414,9 @@ IR_USER::IR_USER() : ServiceFramework("ir:USER", 1) { using namespace Kernel; - conn_status_event = Event::Create(ResetType::OneShot, "IR:ConnectionStatusEvent"); - send_event = Event::Create(ResetType::OneShot, "IR:SendEvent"); - receive_event = Event::Create(ResetType::OneShot, "IR:ReceiveEvent"); + conn_status_event = system.Kernel().CreateEvent(ResetType::OneShot, "IR:ConnectionStatusEvent"); + send_event = system.Kernel().CreateEvent(ResetType::OneShot, "IR:SendEvent"); + receive_event = system.Kernel().CreateEvent(ResetType::OneShot, "IR:ReceiveEvent"); extra_hid = std::make_unique([this](const std::vector& data) { PutToReceive(data); }); diff --git a/src/core/hle/service/ir/ir_user.h b/src/core/hle/service/ir/ir_user.h index 207a54058..71d5e1630 100644 --- a/src/core/hle/service/ir/ir_user.h +++ b/src/core/hle/service/ir/ir_user.h @@ -14,10 +14,6 @@ class Event; class SharedMemory; } // namespace Kernel -namespace CoreTiming { -struct EventType; -}; - namespace Service::IR { class BufferManager; @@ -55,7 +51,7 @@ private: /// Interface to "ir:USER" service class IR_USER final : public ServiceFramework { public: - IR_USER(); + explicit IR_USER(Core::System& system); ~IR_USER(); void ReloadInputDevices(); diff --git a/src/core/hle/service/ldr_ro/ldr_ro.cpp b/src/core/hle/service/ldr_ro/ldr_ro.cpp index ceae8be20..2313bb188 100644 --- a/src/core/hle/service/ldr_ro/ldr_ro.cpp +++ b/src/core/hle/service/ldr_ro/ldr_ro.cpp @@ -353,9 +353,7 @@ void RO::LoadCRO(Kernel::HLERequestContext& ctx, bool link_on_load_bug_fix) { slot->memory_synchronizer.ResizeMemoryBlock(cro_address, cro_buffer_ptr, fix_size); } - VAddr exe_begin; - u32 exe_size; - std::tie(exe_begin, exe_size) = cro.GetExecutablePages(); + auto [exe_begin, exe_size] = cro.GetExecutablePages(); if (exe_begin) { result = process->vm_manager.ReprotectRange(exe_begin, exe_size, Kernel::VMAPermission::ReadExecute); diff --git a/src/core/hle/service/mic_u.cpp b/src/core/hle/service/mic_u.cpp index 34e68099f..b2c24bd17 100644 --- a/src/core/hle/service/mic_u.cpp +++ b/src/core/hle/service/mic_u.cpp @@ -29,6 +29,11 @@ enum class SampleRate : u8 { }; struct MIC_U::Impl { + explicit Impl(Core::System& system) { + buffer_full_event = + system.Kernel().CreateEvent(Kernel::ResetType::OneShot, "MIC_U::buffer_full_event"); + } + void MapSharedMem(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx, 0x01, 1, 2}; const u32 size = rp.Pop(); @@ -187,8 +192,7 @@ struct MIC_U::Impl { } u32 client_version = 0; - Kernel::SharedPtr buffer_full_event = - Kernel::Event::Create(Kernel::ResetType::OneShot, "MIC_U::buffer_full_event"); + Kernel::SharedPtr buffer_full_event; Kernel::SharedPtr shared_memory; u8 mic_gain = 0; bool mic_power = false; @@ -266,7 +270,8 @@ void MIC_U::SetClientVersion(Kernel::HLERequestContext& ctx) { impl->SetClientVersion(ctx); } -MIC_U::MIC_U() : ServiceFramework{"mic:u", 1}, impl{std::make_unique()} { +MIC_U::MIC_U(Core::System& system) + : ServiceFramework{"mic:u", 1}, impl{std::make_unique(system)} { static const FunctionInfo functions[] = { {0x00010042, &MIC_U::MapSharedMem, "MapSharedMem"}, {0x00020000, &MIC_U::UnmapSharedMem, "UnmapSharedMem"}, @@ -293,7 +298,7 @@ MIC_U::~MIC_U() = default; void InstallInterfaces(Core::System& system) { auto& service_manager = system.ServiceManager(); - std::make_shared()->InstallAsService(service_manager); + std::make_shared(system)->InstallAsService(service_manager); } } // namespace Service::MIC diff --git a/src/core/hle/service/mic_u.h b/src/core/hle/service/mic_u.h index 8fd835f0a..bc4933229 100644 --- a/src/core/hle/service/mic_u.h +++ b/src/core/hle/service/mic_u.h @@ -16,7 +16,7 @@ namespace Service::MIC { class MIC_U final : public ServiceFramework { public: - MIC_U(); + explicit MIC_U(Core::System& system); ~MIC_U(); private: diff --git a/src/core/hle/service/nfc/nfc.cpp b/src/core/hle/service/nfc/nfc.cpp index 80bd6aa72..cadb0fa77 100644 --- a/src/core/hle/service/nfc/nfc.cpp +++ b/src/core/hle/service/nfc/nfc.cpp @@ -5,19 +5,61 @@ #include "core/core.h" #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/event.h" +#include "core/hle/lock.h" #include "core/hle/service/nfc/nfc.h" #include "core/hle/service/nfc/nfc_m.h" #include "core/hle/service/nfc/nfc_u.h" namespace Service::NFC { +struct TagInfo { + u16_le id_offset_size; + u8 unk1; + u8 unk2; + std::array uuid; + INSERT_PADDING_BYTES(0x20); +}; +static_assert(sizeof(TagInfo) == 0x2C, "TagInfo is an invalid size"); + +struct AmiiboConfig { + u16_le lastwritedate_year; + u8 lastwritedate_month; + u8 lastwritedate_day; + u16_le write_counter; + std::array characterID; + u16_le amiiboID; + u8 type; + u8 pagex4_byte3; + u16_le appdata_size; + INSERT_PADDING_BYTES(0x30); +}; +static_assert(sizeof(AmiiboConfig) == 0x40, "AmiiboConfig is an invalid size"); + +struct IdentificationBlockReply { + u16_le char_id; + u8 char_variant; + u8 series; + u16_le model_number; + u8 figure_type; + INSERT_PADDING_BYTES(0x2F); +}; +static_assert(sizeof(IdentificationBlockReply) == 0x36, + "IdentificationBlockReply is an invalid size"); + void Module::Interface::Initialize(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx, 0x01, 1, 0); u8 param = rp.Pop(); + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + if (nfc->nfc_tag_state != TagState::NotInitialized) { + LOG_ERROR(Service_NFC, "Invalid TagState {}", static_cast(nfc->nfc_tag_state.load())); + rb.Push(ResultCode(ErrCodes::CommandInvalidForState, ErrorModule::NFC, + ErrorSummary::InvalidState, ErrorLevel::Status)); + return; + } + nfc->nfc_tag_state = TagState::NotScanning; - IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); rb.Push(RESULT_SUCCESS); LOG_WARNING(Service_NFC, "(STUBBED) called, param={}", param); } @@ -53,35 +95,83 @@ void Module::Interface::StartTagScanning(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx, 0x05, 1, 0); // 0x00050040 u16 in_val = rp.Pop(); - ResultCode result = RESULT_SUCCESS; - - // TODO(shinyquagsire23): Implement NFC tag detection, for now stub result - result = ResultCode(ErrCodes::CommandInvalidForState, ErrorModule::NFC, - ErrorSummary::InvalidState, ErrorLevel::Status); - - if (result == RESULT_SUCCESS) { - nfc->nfc_tag_state = TagState::TagInRange; - nfc->tag_in_range_event->Signal(); + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + if (nfc->nfc_tag_state != TagState::NotScanning && + nfc->nfc_tag_state != TagState::TagOutOfRange) { + LOG_ERROR(Service_NFC, "Invalid TagState {}", static_cast(nfc->nfc_tag_state.load())); + rb.Push(ResultCode(ErrCodes::CommandInvalidForState, ErrorModule::NFC, + ErrorSummary::InvalidState, ErrorLevel::Status)); + return; } - IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); - rb.Push(result); + nfc->nfc_tag_state = TagState::Scanning; + + rb.Push(RESULT_SUCCESS); LOG_WARNING(Service_NFC, "(STUBBED) called, in_val={:04x}", in_val); } +void Module::Interface::GetTagInfo(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x11, 0, 0); + + if (nfc->nfc_tag_state != TagState::TagInRange && + nfc->nfc_tag_state != TagState::TagDataLoaded && nfc->nfc_tag_state != TagState::Unknown6) { + LOG_ERROR(Service_NFC, "Invalid TagState {}", static_cast(nfc->nfc_tag_state.load())); + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(ResultCode(ErrCodes::CommandInvalidForState, ErrorModule::NFC, + ErrorSummary::InvalidState, ErrorLevel::Status)); + return; + } + + TagInfo tag_info{}; + tag_info.uuid = nfc->amiibo_data.uuid; + tag_info.id_offset_size = tag_info.uuid.size(); + tag_info.unk1 = 0x0; + tag_info.unk2 = 0x2; + + IPC::RequestBuilder rb = rp.MakeBuilder(12, 0); + rb.Push(RESULT_SUCCESS); + rb.PushRaw(tag_info); + LOG_WARNING(Service_NFC, "(STUBBED) called"); +} + +void Module::Interface::GetAmiiboConfig(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x18, 0, 0); + + AmiiboConfig amiibo_config{}; + amiibo_config.lastwritedate_year = 2017; + amiibo_config.lastwritedate_month = 10; + amiibo_config.lastwritedate_day = 10; + // TODO(FearlessTobi): Find the right values for the struct + + IPC::RequestBuilder rb = rp.MakeBuilder(17, 0); + rb.Push(RESULT_SUCCESS); + rb.PushRaw(amiibo_config); + LOG_WARNING(Service_NFC, "(STUBBED) called"); +} + void Module::Interface::StopTagScanning(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx, 0x06, 0, 0); + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + if (nfc->nfc_tag_state == TagState::NotInitialized || + nfc->nfc_tag_state == TagState::NotScanning) { + LOG_ERROR(Service_NFC, "Invalid TagState {}", static_cast(nfc->nfc_tag_state.load())); + rb.Push(ResultCode(ErrCodes::CommandInvalidForState, ErrorModule::NFC, + ErrorSummary::InvalidState, ErrorLevel::Status)); + return; + } + nfc->nfc_tag_state = TagState::NotScanning; - IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); rb.Push(RESULT_SUCCESS); - LOG_WARNING(Service_NFC, "(STUBBED) called"); + LOG_DEBUG(Service_NFC, "called"); } void Module::Interface::LoadAmiiboData(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx, 0x07, 0, 0); + // TODO(FearlessTobi): Add state checking when this function gets properly implemented + nfc->nfc_tag_state = TagState::TagDataLoaded; IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); @@ -92,29 +182,52 @@ void Module::Interface::LoadAmiiboData(Kernel::HLERequestContext& ctx) { void Module::Interface::ResetTagScanState(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx, 0x08, 0, 0); - nfc->nfc_tag_state = TagState::NotScanning; - IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + if (nfc->nfc_tag_state != TagState::TagDataLoaded && nfc->nfc_tag_state != TagState::Unknown6) { + LOG_ERROR(Service_NFC, "Invalid TagState {}", static_cast(nfc->nfc_tag_state.load())); + rb.Push(ResultCode(ErrCodes::CommandInvalidForState, ErrorModule::NFC, + ErrorSummary::InvalidState, ErrorLevel::Status)); + return; + } + + nfc->nfc_tag_state = TagState::TagInRange; + rb.Push(RESULT_SUCCESS); - LOG_WARNING(Service_NFC, "(STUBBED) called"); + LOG_DEBUG(Service_NFC, "called"); } void Module::Interface::GetTagInRangeEvent(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx, 0x0B, 0, 0); + if (nfc->nfc_tag_state != TagState::NotScanning) { + LOG_ERROR(Service_NFC, "Invalid TagState {}", static_cast(nfc->nfc_tag_state.load())); + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(ResultCode(ErrCodes::CommandInvalidForState, ErrorModule::NFC, + ErrorSummary::InvalidState, ErrorLevel::Status)); + return; + } + IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); rb.Push(RESULT_SUCCESS); rb.PushCopyObjects(nfc->tag_in_range_event); - LOG_WARNING(Service_NFC, "(STUBBED) called"); + LOG_DEBUG(Service_NFC, "called"); } void Module::Interface::GetTagOutOfRangeEvent(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx, 0x0C, 0, 0); + if (nfc->nfc_tag_state != TagState::NotScanning) { + LOG_ERROR(Service_NFC, "Invalid TagState {}", static_cast(nfc->nfc_tag_state.load())); + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(ResultCode(ErrCodes::CommandInvalidForState, ErrorModule::NFC, + ErrorSummary::InvalidState, ErrorLevel::Status)); + return; + } + IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); rb.Push(RESULT_SUCCESS); rb.PushCopyObjects(nfc->tag_out_of_range_event); - LOG_WARNING(Service_NFC, "(STUBBED) called"); + LOG_DEBUG(Service_NFC, "called"); } void Module::Interface::GetTagState(Kernel::HLERequestContext& ctx) { @@ -122,8 +235,8 @@ void Module::Interface::GetTagState(Kernel::HLERequestContext& ctx) { IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); rb.Push(RESULT_SUCCESS); - rb.PushEnum(nfc->nfc_tag_state); - LOG_DEBUG(Service_NFC, "(STUBBED) called"); + rb.PushEnum(nfc->nfc_tag_state.load()); + LOG_DEBUG(Service_NFC, "called"); } void Module::Interface::CommunicationGetStatus(Kernel::HLERequestContext& ctx) { @@ -135,23 +248,82 @@ void Module::Interface::CommunicationGetStatus(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_NFC, "(STUBBED) called"); } +void Module::Interface::Unknown0x1A(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x1A, 0, 0); + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + if (nfc->nfc_tag_state != TagState::TagInRange) { + LOG_ERROR(Service_NFC, "Invalid TagState {}", static_cast(nfc->nfc_tag_state.load())); + rb.Push(ResultCode(ErrCodes::CommandInvalidForState, ErrorModule::NFC, + ErrorSummary::InvalidState, ErrorLevel::Status)); + return; + } + + nfc->nfc_tag_state = TagState::Unknown6; + + rb.Push(RESULT_SUCCESS); + LOG_DEBUG(Service_NFC, "called"); +} + +void Module::Interface::GetIdentificationBlock(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x1B, 0, 0); + + if (nfc->nfc_tag_state != TagState::TagDataLoaded && nfc->nfc_tag_state != TagState::Unknown6) { + LOG_ERROR(Service_NFC, "Invalid TagState {}", static_cast(nfc->nfc_tag_state.load())); + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(ResultCode(ErrCodes::CommandInvalidForState, ErrorModule::NFC, + ErrorSummary::InvalidState, ErrorLevel::Status)); + return; + } + + IdentificationBlockReply identification_block_reply{}; + identification_block_reply.char_id = nfc->amiibo_data.char_id; + identification_block_reply.char_variant = nfc->amiibo_data.char_variant; + identification_block_reply.series = nfc->amiibo_data.series; + identification_block_reply.model_number = nfc->amiibo_data.model_number; + identification_block_reply.figure_type = nfc->amiibo_data.figure_type; + + IPC::RequestBuilder rb = rp.MakeBuilder(0x1F, 0); + rb.Push(RESULT_SUCCESS); + rb.PushRaw(identification_block_reply); + LOG_DEBUG(Service_NFC, "called"); +} + +std::shared_ptr Module::Interface::GetModule() const { + return nfc; +} + +void Module::Interface::LoadAmiibo(const AmiiboData& amiibo_data) { + std::lock_guard lock(HLE::g_hle_lock); + nfc->amiibo_data = amiibo_data; + nfc->nfc_tag_state = Service::NFC::TagState::TagInRange; + nfc->tag_in_range_event->Signal(); +} + +void Module::Interface::RemoveAmiibo() { + std::lock_guard lock(HLE::g_hle_lock); + nfc->nfc_tag_state = Service::NFC::TagState::TagOutOfRange; + nfc->tag_out_of_range_event->Signal(); + nfc->amiibo_data = {}; +} + Module::Interface::Interface(std::shared_ptr nfc, const char* name, u32 max_session) : ServiceFramework(name, max_session), nfc(std::move(nfc)) {} Module::Interface::~Interface() = default; -Module::Module() { +Module::Module(Core::System& system) { tag_in_range_event = - Kernel::Event::Create(Kernel::ResetType::OneShot, "NFC::tag_in_range_event"); + system.Kernel().CreateEvent(Kernel::ResetType::OneShot, "NFC::tag_in_range_event"); tag_out_of_range_event = - Kernel::Event::Create(Kernel::ResetType::OneShot, "NFC::tag_out_range_event"); + system.Kernel().CreateEvent(Kernel::ResetType::OneShot, "NFC::tag_out_range_event"); } Module::~Module() = default; void InstallInterfaces(Core::System& system) { auto& service_manager = system.ServiceManager(); - auto nfc = std::make_shared(); + auto nfc = std::make_shared(system); std::make_shared(nfc)->InstallAsService(service_manager); std::make_shared(nfc)->InstallAsService(service_manager); } diff --git a/src/core/hle/service/nfc/nfc.h b/src/core/hle/service/nfc/nfc.h index d942e4b63..afa05d60e 100644 --- a/src/core/hle/service/nfc/nfc.h +++ b/src/core/hle/service/nfc/nfc.h @@ -4,6 +4,7 @@ #pragma once +#include #include #include "common/common_types.h" #include "core/hle/kernel/kernel.h" @@ -25,6 +26,19 @@ enum { }; } // namespace ErrCodes +// TODO(FearlessTobi): Add more members to this struct +struct AmiiboData { + std::array uuid; + INSERT_PADDING_BYTES(0x4D); + u16_le char_id; + u8 char_variant; + u8 figure_type; + u16_be model_number; + u8 series; + INSERT_PADDING_BYTES(0x1C1); +}; +static_assert(sizeof(AmiiboData) == 0x21C, "AmiiboData is an invalid size"); + enum class TagState : u8 { NotInitialized = 0, NotScanning = 1, @@ -32,6 +46,7 @@ enum class TagState : u8 { TagInRange = 3, TagOutOfRange = 4, TagDataLoaded = 5, + Unknown6 = 6, }; enum class CommunicationStatus : u8 { @@ -41,7 +56,7 @@ enum class CommunicationStatus : u8 { class Module final { public: - Module(); + explicit Module(Core::System& system); ~Module(); class Interface : public ServiceFramework { @@ -49,6 +64,12 @@ public: Interface(std::shared_ptr nfc, const char* name, u32 max_session); ~Interface(); + std::shared_ptr GetModule() const; + + void LoadAmiibo(const AmiiboData& amiibo_data); + + void RemoveAmiibo(); + protected: /** * NFC::Initialize service function @@ -167,6 +188,45 @@ public: */ void CommunicationGetStatus(Kernel::HLERequestContext& ctx); + /** + * NFC::GetTagInfo service function + * Inputs: + * 0 : Header code [0x00110000] + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2-12 : 0x2C-byte struct + */ + void GetTagInfo(Kernel::HLERequestContext& ctx); + + /** + * NFC::GetAmiiboConfig service function + * Inputs: + * 0 : Header code [0x00180000] + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2-17 : 0x40-byte config struct + */ + void GetAmiiboConfig(Kernel::HLERequestContext& ctx); + + /** + * NFC::Unknown0x1A service function + * Inputs: + * 0 : Header code [0x001A0000] + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ + void Unknown0x1A(Kernel::HLERequestContext& ctx); + + /** + * NFC::GetIdentificationBlock service function + * Inputs: + * 0 : Header code [0x001B0000] + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2-31 : 0x36-byte struct + */ + void GetIdentificationBlock(Kernel::HLERequestContext& ctx); + private: std::shared_ptr nfc; }; @@ -174,8 +234,10 @@ public: private: Kernel::SharedPtr tag_in_range_event; Kernel::SharedPtr tag_out_of_range_event; - TagState nfc_tag_state = TagState::NotInitialized; + std::atomic nfc_tag_state = TagState::NotInitialized; CommunicationStatus nfc_status = CommunicationStatus::NfcInitialized; + + AmiiboData amiibo_data{}; }; void InstallInterfaces(Core::System& system); diff --git a/src/core/hle/service/nfc/nfc_m.cpp b/src/core/hle/service/nfc/nfc_m.cpp index d91b6a31a..310490b8f 100644 --- a/src/core/hle/service/nfc/nfc_m.cpp +++ b/src/core/hle/service/nfc/nfc_m.cpp @@ -24,15 +24,17 @@ NFC_M::NFC_M(std::shared_ptr nfc) : Module::Interface(std::move(nfc), "n {0x000D0000, &NFC_M::GetTagState, "GetTagState"}, {0x000F0000, &NFC_M::CommunicationGetStatus, "CommunicationGetStatus"}, {0x00100000, nullptr, "GetTagInfo2"}, - {0x00110000, nullptr, "GetTagInfo"}, + {0x00110000, &NFC_M::GetTagInfo, "GetTagInfo"}, {0x00120000, nullptr, "CommunicationGetResult"}, {0x00130040, nullptr, "OpenAppData"}, {0x00140384, nullptr, "InitializeWriteAppData"}, {0x00150040, nullptr, "ReadAppData"}, {0x00160242, nullptr, "WriteAppData"}, {0x00170000, nullptr, "GetAmiiboSettings"}, - {0x00180000, nullptr, "GetAmiiboConfig"}, + {0x00180000, &NFC_M::GetAmiiboConfig, "GetAmiiboConfig"}, {0x00190000, nullptr, "GetAppDataInitStruct"}, + {0x001A0000, &NFC_M::Unknown0x1A, "Unknown0x1A"}, + {0x001B0000, &NFC_M::GetIdentificationBlock, "GetIdentificationBlock"}, // nfc:m {0x04040A40, nullptr, "SetAmiiboSettings"} // clang-format on diff --git a/src/core/hle/service/nfc/nfc_u.cpp b/src/core/hle/service/nfc/nfc_u.cpp index 8cdcca287..a6e99ace2 100644 --- a/src/core/hle/service/nfc/nfc_u.cpp +++ b/src/core/hle/service/nfc/nfc_u.cpp @@ -23,15 +23,17 @@ NFC_U::NFC_U(std::shared_ptr nfc) : Module::Interface(std::move(nfc), "n {0x000D0000, &NFC_U::GetTagState, "GetTagState"}, {0x000F0000, &NFC_U::CommunicationGetStatus, "CommunicationGetStatus"}, {0x00100000, nullptr, "GetTagInfo2"}, - {0x00110000, nullptr, "GetTagInfo"}, + {0x00110000, &NFC_U::GetTagInfo, "GetTagInfo"}, {0x00120000, nullptr, "CommunicationGetResult"}, {0x00130040, nullptr, "OpenAppData"}, {0x00140384, nullptr, "InitializeWriteAppData"}, {0x00150040, nullptr, "ReadAppData"}, {0x00160242, nullptr, "WriteAppData"}, {0x00170000, nullptr, "GetAmiiboSettings"}, - {0x00180000, nullptr, "GetAmiiboConfig"}, + {0x00180000, &NFC_U::GetAmiiboConfig, "GetAmiiboConfig"}, {0x00190000, nullptr, "GetAppDataInitStruct"}, + {0x001A0000, &NFC_U::Unknown0x1A, "Unknown0x1A"}, + {0x001B0000, &NFC_U::GetIdentificationBlock, "GetIdentificationBlock"}, // clang-format on }; RegisterHandlers(functions); diff --git a/src/core/hle/service/nim/nim.cpp b/src/core/hle/service/nim/nim.cpp index 25dafaa6e..a0b84bd03 100644 --- a/src/core/hle/service/nim/nim.cpp +++ b/src/core/hle/service/nim/nim.cpp @@ -14,7 +14,7 @@ void InstallInterfaces(Core::System& system) { auto& service_manager = system.ServiceManager(); std::make_shared()->InstallAsService(service_manager); std::make_shared()->InstallAsService(service_manager); - std::make_shared()->InstallAsService(service_manager); + std::make_shared(system)->InstallAsService(service_manager); } } // namespace Service::NIM diff --git a/src/core/hle/service/nim/nim_u.cpp b/src/core/hle/service/nim/nim_u.cpp index acf29cfe0..dd0e4d31c 100644 --- a/src/core/hle/service/nim/nim_u.cpp +++ b/src/core/hle/service/nim/nim_u.cpp @@ -2,13 +2,14 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "core/core.h" #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/event.h" #include "core/hle/service/nim/nim_u.h" namespace Service::NIM { -NIM_U::NIM_U() : ServiceFramework("nim:u", 2) { +NIM_U::NIM_U(Core::System& system) : ServiceFramework("nim:u", 2) { const FunctionInfo functions[] = { {0x00010000, nullptr, "StartSysUpdate"}, {0x00020000, nullptr, "GetUpdateDownloadProgress"}, @@ -20,7 +21,7 @@ NIM_U::NIM_U() : ServiceFramework("nim:u", 2) { }; RegisterHandlers(functions); nim_system_update_event = - Kernel::Event::Create(Kernel::ResetType::OneShot, "NIM System Update Event"); + system.Kernel().CreateEvent(Kernel::ResetType::OneShot, "NIM System Update Event"); } NIM_U::~NIM_U() = default; diff --git a/src/core/hle/service/nim/nim_u.h b/src/core/hle/service/nim/nim_u.h index 610688fc2..0f4338f16 100644 --- a/src/core/hle/service/nim/nim_u.h +++ b/src/core/hle/service/nim/nim_u.h @@ -6,11 +6,15 @@ #include "core/hle/service/service.h" +namespace Core { +class System; +} + namespace Service::NIM { class NIM_U final : public ServiceFramework { public: - NIM_U(); + explicit NIM_U(Core::System& system); ~NIM_U(); private: diff --git a/src/core/hle/service/nwm/nwm_uds.cpp b/src/core/hle/service/nwm/nwm_uds.cpp index fe9d09b98..85b309f43 100644 --- a/src/core/hle/service/nwm/nwm_uds.cpp +++ b/src/core/hle/service/nwm/nwm_uds.cpp @@ -20,6 +20,7 @@ #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/event.h" #include "core/hle/kernel/shared_memory.h" +#include "core/hle/kernel/shared_page.h" #include "core/hle/lock.h" #include "core/hle/result.h" #include "core/hle/service/nwm/nwm_uds.h" @@ -87,7 +88,7 @@ struct Node { static std::map node_map; // Event that will generate and send the 802.11 beacon frames. -static CoreTiming::EventType* beacon_broadcast_event; +static Core::TimingEventType* beacon_broadcast_event; // Callback identifier for the OnWifiPacketReceived event. static Network::RoomMember::CallbackHandle wifi_packet_received; @@ -839,8 +840,8 @@ void NWM_UDS::Bind(Kernel::HLERequestContext& ctx) { } // Create a new event for this bind node. - auto event = Kernel::Event::Create(Kernel::ResetType::OneShot, - "NWM::BindNodeEvent" + std::to_string(bind_node_id)); + auto event = system.Kernel().CreateEvent(Kernel::ResetType::OneShot, + "NWM::BindNodeEvent" + std::to_string(bind_node_id)); std::lock_guard lock(connection_status_mutex); ASSERT(channel_data.find(data_channel) == channel_data.end()); @@ -954,8 +955,8 @@ void NWM_UDS::BeginHostingNetwork(Kernel::HLERequestContext& ctx) { connection_status_event->Signal(); // Start broadcasting the network, send a beacon frame every 102.4ms. - CoreTiming::ScheduleEvent(msToCycles(DefaultBeaconInterval * MillisecondsPerTU), - beacon_broadcast_event, 0); + system.CoreTiming().ScheduleEvent(msToCycles(DefaultBeaconInterval * MillisecondsPerTU), + beacon_broadcast_event, 0); LOG_DEBUG(Service_NWM, "An UDS network has been created."); @@ -975,7 +976,7 @@ void NWM_UDS::DestroyNetwork(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx, 0x08, 0, 0); // Unschedule the beacon broadcast event. - CoreTiming::UnscheduleEvent(beacon_broadcast_event, 0); + system.CoreTiming().UnscheduleEvent(beacon_broadcast_event, 0); // Only a host can destroy std::lock_guard lock(connection_status_mutex); @@ -1231,7 +1232,8 @@ void NWM_UDS::ConnectToNetwork(Kernel::HLERequestContext& ctx) { static constexpr std::chrono::nanoseconds UDSConnectionTimeout{300000000}; connection_event = ctx.SleepClientThread( - Kernel::GetCurrentThread(), "uds::ConnectToNetwork", UDSConnectionTimeout, + system.Kernel().GetThreadManager().GetCurrentThread(), "uds::ConnectToNetwork", + UDSConnectionTimeout, [](Kernel::SharedPtr thread, Kernel::HLERequestContext& ctx, Kernel::ThreadWakeupReason reason) { // TODO(B3N30): Add error handling for host full and timeout @@ -1334,7 +1336,7 @@ void NWM_UDS::DecryptBeaconData(Kernel::HLERequestContext& ctx) { } // Sends a 802.11 beacon frame with information about the current network. -static void BeaconBroadcastCallback(u64 userdata, s64 cycles_late) { +void NWM_UDS::BeaconBroadcastCallback(u64 userdata, s64 cycles_late) { // Don't do anything if we're not actually hosting a network if (connection_status.status != static_cast(NetworkStatus::ConnectedAsHost)) return; @@ -1351,11 +1353,12 @@ static void BeaconBroadcastCallback(u64 userdata, s64 cycles_late) { SendPacket(packet); // Start broadcasting the network, send a beacon frame every 102.4ms. - CoreTiming::ScheduleEvent(msToCycles(DefaultBeaconInterval * MillisecondsPerTU) - cycles_late, - beacon_broadcast_event, 0); + system.CoreTiming().ScheduleEvent(msToCycles(DefaultBeaconInterval * MillisecondsPerTU) - + cycles_late, + beacon_broadcast_event, 0); } -NWM_UDS::NWM_UDS(Core::System& system) : ServiceFramework("nwm::UDS") { +NWM_UDS::NWM_UDS(Core::System& system) : ServiceFramework("nwm::UDS"), system(system) { static const FunctionInfo functions[] = { {0x000102C2, nullptr, "Initialize (deprecated)"}, {0x00020000, nullptr, "Scrap"}, @@ -1388,12 +1391,13 @@ NWM_UDS::NWM_UDS(Core::System& system) : ServiceFramework("nwm::UDS") { {0x00220402, nullptr, "ScanOnConnection"}, }; connection_status_event = - Kernel::Event::Create(Kernel::ResetType::OneShot, "NWM::connection_status_event"); + system.Kernel().CreateEvent(Kernel::ResetType::OneShot, "NWM::connection_status_event"); RegisterHandlers(functions); - beacon_broadcast_event = - CoreTiming::RegisterEvent("UDS::BeaconBroadcastCallback", BeaconBroadcastCallback); + beacon_broadcast_event = system.CoreTiming().RegisterEvent( + "UDS::BeaconBroadcastCallback", + [this](u64 userdata, s64 cycles_late) { BeaconBroadcastCallback(userdata, cycles_late); }); CryptoPP::AutoSeededRandomPool rng; auto mac = SharedPage::DefaultMac; @@ -1406,8 +1410,8 @@ NWM_UDS::NWM_UDS(Core::System& system) : ServiceFramework("nwm::UDS") { } } - system.GetSharedPageHandler()->SetMacAddress(mac); - system.GetSharedPageHandler()->SetWifiLinkLevel(SharedPage::WifiLinkLevel::BEST); + system.Kernel().GetSharedPageHandler().SetMacAddress(mac); + system.Kernel().GetSharedPageHandler().SetWifiLinkLevel(SharedPage::WifiLinkLevel::BEST); } NWM_UDS::~NWM_UDS() { @@ -1426,7 +1430,7 @@ NWM_UDS::~NWM_UDS() { if (auto room_member = Network::GetRoomMember().lock()) room_member->Unbind(wifi_packet_received); - CoreTiming::UnscheduleEvent(beacon_broadcast_event, 0); + system.CoreTiming().UnscheduleEvent(beacon_broadcast_event, 0); } } // namespace Service::NWM diff --git a/src/core/hle/service/nwm/nwm_uds.h b/src/core/hle/service/nwm/nwm_uds.h index 2c79aa674..3f050b5a5 100644 --- a/src/core/hle/service/nwm/nwm_uds.h +++ b/src/core/hle/service/nwm/nwm_uds.h @@ -112,6 +112,8 @@ public: ~NWM_UDS(); private: + Core::System& system; + void UpdateNetworkAttribute(Kernel::HLERequestContext& ctx); /** @@ -350,6 +352,8 @@ private: * 2, 3: output buffer return descriptor & ptr */ void DecryptBeaconData(Kernel::HLERequestContext& ctx); + + void BeaconBroadcastCallback(u64 userdata, s64 cycles_late); }; } // namespace Service::NWM diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index 5755d0335..d4112fc21 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp @@ -61,8 +61,6 @@ using Kernel::SharedPtr; namespace Service { -std::unordered_map> g_kernel_named_ports; - const std::array service_module_map{ {{"FS", 0x00040130'00001102, FS::InstallInterfaces}, {"PM", 0x00040130'00001202, PM::InstallInterfaces}, @@ -143,13 +141,13 @@ void ServiceFrameworkBase::InstallAsService(SM::ServiceManager& service_manager) port->SetHleHandler(shared_from_this()); } -void ServiceFrameworkBase::InstallAsNamedPort() { +void ServiceFrameworkBase::InstallAsNamedPort(Kernel::KernelSystem& kernel) { ASSERT(port == nullptr); SharedPtr server_port; SharedPtr client_port; - std::tie(server_port, client_port) = ServerPort::CreatePortPair(max_sessions, service_name); + std::tie(server_port, client_port) = kernel.CreatePortPair(max_sessions, service_name); server_port->SetHleHandler(shared_from_this()); - AddNamedPort(service_name, std::move(client_port)); + kernel.AddNamedPort(service_name, std::move(client_port)); } void ServiceFrameworkBase::RegisterHandlersBase(const FunctionInfoBase* functions, std::size_t n) { @@ -179,7 +177,10 @@ void ServiceFrameworkBase::ReportUnimplementedFunction(u32* cmd_buf, const Funct } void ServiceFrameworkBase::HandleSyncRequest(SharedPtr server_session) { - u32* cmd_buf = Kernel::GetCommandBuffer(); + Kernel::KernelSystem& kernel = Core::System::GetInstance().Kernel(); + auto thread = kernel.GetThreadManager().GetCurrentThread(); + // TODO(wwylele): avoid GetPointer + u32* cmd_buf = reinterpret_cast(Memory::GetPointer(thread->GetCommandBufferAddress())); u32 header_code = cmd_buf[0]; auto itr = handlers.find(header_code); @@ -188,35 +189,29 @@ void ServiceFrameworkBase::HandleSyncRequest(SharedPtr server_ses return ReportUnimplementedFunction(cmd_buf, info); } + Kernel::SharedPtr current_process = kernel.GetCurrentProcess(); + // TODO(yuriks): The kernel should be the one handling this as part of translation after // everything else is migrated Kernel::HLERequestContext context(std::move(server_session)); - context.PopulateFromIncomingCommandBuffer(cmd_buf, *Kernel::g_current_process, - Kernel::g_handle_table); + context.PopulateFromIncomingCommandBuffer(cmd_buf, *current_process); LOG_TRACE(Service, "{}", MakeFunctionString(info->name, GetServiceName().c_str(), cmd_buf)); handler_invoker(this, info->handler_callback, context); - auto thread = Kernel::GetCurrentThread(); ASSERT(thread->status == Kernel::ThreadStatus::Running || thread->status == Kernel::ThreadStatus::WaitHleEvent); // Only write the response immediately if the thread is still running. If the HLE handler put // the thread to sleep then the writing of the command buffer will be deferred to the wakeup // callback. if (thread->status == Kernel::ThreadStatus::Running) { - context.WriteToOutgoingCommandBuffer(cmd_buf, *Kernel::g_current_process, - Kernel::g_handle_table); + context.WriteToOutgoingCommandBuffer(cmd_buf, *current_process); } } //////////////////////////////////////////////////////////////////////////////////////////////////// // Module interface -// TODO(yuriks): Move to kernel -void AddNamedPort(std::string name, SharedPtr port) { - g_kernel_named_ports.emplace(std::move(name), std::move(port)); -} - static bool AttemptLLE(const ServiceModuleInfo& service_module) { if (!Settings::values.lle_modules.at(service_module.name)) return false; @@ -235,8 +230,8 @@ static bool AttemptLLE(const ServiceModuleInfo& service_module) { } /// Initialize ServiceManager -void Init(Core::System& core, std::shared_ptr& sm) { - SM::ServiceManager::InstallInterfaces(sm); +void Init(Core::System& core) { + SM::ServiceManager::InstallInterfaces(core); for (const auto& service_module : service_module_map) { if (!AttemptLLE(service_module) && service_module.init_function != nullptr) @@ -245,10 +240,4 @@ void Init(Core::System& core, std::shared_ptr& sm) { LOG_DEBUG(Service, "initialized OK"); } -/// Shutdown ServiceManager -void Shutdown() { - - g_kernel_named_ports.clear(); - LOG_DEBUG(Service, "shutdown OK"); -} } // namespace Service diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h index 3a8634251..5f36f98de 100644 --- a/src/core/hle/service/service.h +++ b/src/core/hle/service/service.h @@ -8,7 +8,6 @@ #include #include #include -#include #include #include "common/common_types.h" #include "core/hle/kernel/hle_ipc.h" @@ -20,6 +19,7 @@ class System; } namespace Kernel { +class KernelSystem; class ClientPort; class ServerPort; class ServerSession; @@ -59,7 +59,7 @@ public: /// Creates a port pair and registers this service with the given ServiceManager. void InstallAsService(SM::ServiceManager& service_manager); /// Creates a port pair and registers it on the kernel's global port registry. - void InstallAsNamedPort(); + void InstallAsNamedPort(Kernel::KernelSystem& kernel); void HandleSyncRequest(Kernel::SharedPtr server_session) override; @@ -184,13 +184,7 @@ private: }; /// Initialize ServiceManager -void Init(Core::System& system, std::shared_ptr& sm); - -/// Shutdown ServiceManager -void Shutdown(); - -/// Map of named ports managed by the kernel, which can be retrieved using the ConnectToPort SVC. -extern std::unordered_map> g_kernel_named_ports; +void Init(Core::System& system); struct ServiceModuleInfo { std::string name; @@ -200,7 +194,4 @@ struct ServiceModuleInfo { extern const std::array service_module_map; -/// Adds a port to the named port table -void AddNamedPort(std::string name, Kernel::SharedPtr port); - } // namespace Service diff --git a/src/core/hle/service/sm/sm.cpp b/src/core/hle/service/sm/sm.cpp index 588de7daa..81819c9bd 100644 --- a/src/core/hle/service/sm/sm.cpp +++ b/src/core/hle/service/sm/sm.cpp @@ -4,6 +4,7 @@ #include #include "common/assert.h" +#include "core/core.h" #include "core/hle/kernel/client_session.h" #include "core/hle/result.h" #include "core/hle/service/sm/sm.h" @@ -21,12 +22,14 @@ static ResultCode ValidateServiceName(const std::string& name) { return RESULT_SUCCESS; } -void ServiceManager::InstallInterfaces(std::shared_ptr self) { - ASSERT(self->srv_interface.expired()); +ServiceManager::ServiceManager(Core::System& system) : system(system) {} - auto srv = std::make_shared(self); - srv->InstallAsNamedPort(); - self->srv_interface = srv; +void ServiceManager::InstallInterfaces(Core::System& system) { + ASSERT(system.ServiceManager().srv_interface.expired()); + + auto srv = std::make_shared(system); + srv->InstallAsNamedPort(system.Kernel()); + system.ServiceManager().srv_interface = srv; } ResultVal> ServiceManager::RegisterService( @@ -39,7 +42,7 @@ ResultVal> ServiceManager::RegisterService Kernel::SharedPtr server_port; Kernel::SharedPtr client_port; - std::tie(server_port, client_port) = Kernel::ServerPort::CreatePortPair(max_sessions, name); + std::tie(server_port, client_port) = system.Kernel().CreatePortPair(max_sessions, name); registered_services.emplace(std::move(name), std::move(client_port)); return MakeResult>(std::move(server_port)); diff --git a/src/core/hle/service/sm/sm.h b/src/core/hle/service/sm/sm.h index 9e9164a66..f3cf40756 100644 --- a/src/core/hle/service/sm/sm.h +++ b/src/core/hle/service/sm/sm.h @@ -14,6 +14,10 @@ #include "core/hle/result.h" #include "core/hle/service/service.h" +namespace Core { +class System; +} + namespace Kernel { class ClientSession; class SessionRequestHandler; @@ -39,7 +43,9 @@ constexpr ResultCode ERR_ALREADY_REGISTERED(ErrorDescription::AlreadyExists, Err class ServiceManager { public: - static void InstallInterfaces(std::shared_ptr self); + static void InstallInterfaces(Core::System& system); + + explicit ServiceManager(Core::System& system); ResultVal> RegisterService(std::string name, unsigned int max_sessions); @@ -63,6 +69,7 @@ public: } private: + Core::System& system; std::weak_ptr srv_interface; /// Map of registered services, retrieved using GetServicePort or ConnectToService. diff --git a/src/core/hle/service/sm/srv.cpp b/src/core/hle/service/sm/srv.cpp index ca723745f..dc7e8297e 100644 --- a/src/core/hle/service/sm/srv.cpp +++ b/src/core/hle/service/sm/srv.cpp @@ -5,6 +5,7 @@ #include #include "common/common_types.h" #include "common/logging/log.h" +#include "core/core.h" #include "core/hle/ipc.h" #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/client_port.h" @@ -62,7 +63,7 @@ void SRV::EnableNotification(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx, 0x2, 0, 0); notification_semaphore = - Kernel::Semaphore::Create(0, MAX_PENDING_NOTIFICATIONS, "SRV:Notification").Unwrap(); + system.Kernel().CreateSemaphore(0, MAX_PENDING_NOTIFICATIONS, "SRV:Notification").Unwrap(); IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); rb.Push(RESULT_SUCCESS); @@ -103,7 +104,7 @@ void SRV::GetServiceHandle(Kernel::HLERequestContext& ctx) { Kernel::HLERequestContext& ctx, Kernel::ThreadWakeupReason reason) { LOG_ERROR(Service_SRV, "called service={} wakeup", name); - auto client_port = service_manager->GetServicePort(name); + auto client_port = system.ServiceManager().GetServicePort(name); auto session = client_port.Unwrap()->Connect(); if (session.Succeeded()) { @@ -122,13 +123,13 @@ void SRV::GetServiceHandle(Kernel::HLERequestContext& ctx) { } }; - auto client_port = service_manager->GetServicePort(name); + auto client_port = system.ServiceManager().GetServicePort(name); if (client_port.Failed()) { if (wait_until_available && client_port.Code() == ERR_SERVICE_NOT_REGISTERED) { LOG_INFO(Service_SRV, "called service={} delayed", name); Kernel::SharedPtr get_service_handle_event = - ctx.SleepClientThread(Kernel::GetCurrentThread(), "GetServiceHandle", - std::chrono::nanoseconds(-1), get_handle); + ctx.SleepClientThread(system.Kernel().GetThreadManager().GetCurrentThread(), + "GetServiceHandle", std::chrono::nanoseconds(-1), get_handle); get_service_handle_delayed_map[name] = std::move(get_service_handle_event); return; } else { @@ -223,7 +224,7 @@ void SRV::RegisterService(Kernel::HLERequestContext& ctx) { std::string name(name_buf.data(), std::min(name_len, name_buf.size())); - auto port = service_manager->RegisterService(name, max_sessions); + auto port = system.ServiceManager().RegisterService(name, max_sessions); if (port.Failed()) { IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); @@ -243,8 +244,7 @@ void SRV::RegisterService(Kernel::HLERequestContext& ctx) { rb.PushMoveObjects(port.Unwrap()); } -SRV::SRV(std::shared_ptr service_manager) - : ServiceFramework("srv:", 4), service_manager(std::move(service_manager)) { +SRV::SRV(Core::System& system) : ServiceFramework("srv:", 4), system(system) { static const FunctionInfo functions[] = { {0x00010002, &SRV::RegisterClient, "RegisterClient"}, {0x00020000, &SRV::EnableNotification, "EnableNotification"}, diff --git a/src/core/hle/service/sm/srv.h b/src/core/hle/service/sm/srv.h index 066d00eac..02d72ec49 100644 --- a/src/core/hle/service/sm/srv.h +++ b/src/core/hle/service/sm/srv.h @@ -8,6 +8,10 @@ #include "core/hle/kernel/kernel.h" #include "core/hle/service/service.h" +namespace Core { +class System; +} + namespace Kernel { class HLERequestContext; class Semaphore; @@ -18,7 +22,7 @@ namespace Service::SM { /// Interface to "srv:" service class SRV final : public ServiceFramework { public: - explicit SRV(std::shared_ptr service_manager); + explicit SRV(Core::System& system); ~SRV(); private: @@ -30,7 +34,7 @@ private: void PublishToSubscriber(Kernel::HLERequestContext& ctx); void RegisterService(Kernel::HLERequestContext& ctx); - std::shared_ptr service_manager; + Core::System& system; Kernel::SharedPtr notification_semaphore; std::unordered_map> get_service_handle_delayed_map; diff --git a/src/core/hle/service/y2r_u.cpp b/src/core/hle/service/y2r_u.cpp index cd20b57bd..0de5cf8d5 100644 --- a/src/core/hle/service/y2r_u.cpp +++ b/src/core/hle/service/y2r_u.cpp @@ -632,7 +632,7 @@ void Y2R_U::GetPackageParameter(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_Y2R, "called"); } -Y2R_U::Y2R_U() : ServiceFramework("y2r:u", 1) { +Y2R_U::Y2R_U(Core::System& system) : ServiceFramework("y2r:u", 1) { static const FunctionInfo functions[] = { {0x00010040, &Y2R_U::SetInputFormat, "SetInputFormat"}, {0x00020000, &Y2R_U::GetInputFormat, "GetInputFormat"}, @@ -682,14 +682,14 @@ Y2R_U::Y2R_U() : ServiceFramework("y2r:u", 1) { }; RegisterHandlers(functions); - completion_event = Kernel::Event::Create(Kernel::ResetType::OneShot, "Y2R:Completed"); + completion_event = system.Kernel().CreateEvent(Kernel::ResetType::OneShot, "Y2R:Completed"); } Y2R_U::~Y2R_U() = default; void InstallInterfaces(Core::System& system) { auto& service_manager = system.ServiceManager(); - std::make_shared()->InstallAsService(service_manager); + std::make_shared(system)->InstallAsService(service_manager); } } // namespace Service::Y2R diff --git a/src/core/hle/service/y2r_u.h b/src/core/hle/service/y2r_u.h index e32763925..007782dd5 100644 --- a/src/core/hle/service/y2r_u.h +++ b/src/core/hle/service/y2r_u.h @@ -149,7 +149,7 @@ static_assert(sizeof(ConversionParameters) == 12, "ConversionParameters struct h class Y2R_U final : public ServiceFramework { public: - Y2R_U(); + explicit Y2R_U(Core::System& system); ~Y2R_U() override; private: diff --git a/src/core/hw/aes/key.cpp b/src/core/hw/aes/key.cpp index 99e1b7fb5..7f034f9a3 100644 --- a/src/core/hw/aes/key.cpp +++ b/src/core/hw/aes/key.cpp @@ -6,11 +6,15 @@ #include #include #include +#include +#include #include #include "common/common_paths.h" #include "common/file_util.h" #include "common/logging/log.h" #include "common/string_util.h" +#include "core/file_sys/archive_ncch.h" +#include "core/hle/service/fs/archive.h" #include "core/hw/aes/arithmetic128.h" #include "core/hw/aes/key.h" @@ -19,7 +23,14 @@ namespace AES { namespace { -std::optional generator_constant; +// The generator constant was calculated using the 0x39 KeyX and KeyY retrieved from a 3DS and the +// normal key dumped from a Wii U solving the equation: +// NormalKey = (((KeyX ROL 2) XOR KeyY) + constant) ROL 87 +// On a real 3DS the generation for the normal key is hardware based, and thus the constant can't +// get dumped . generated normal keys are also not accesible on a 3DS. The used formula for +// calculating the constant is a software implementation of what the hardware generator does. +constexpr AESKey generator_constant = {{0x1F, 0xF9, 0xE9, 0xAA, 0xC5, 0xFE, 0x04, 0x08, 0x02, 0x45, + 0x91, 0xDC, 0x5D, 0x52, 0x76, 0x8A}}; struct KeyDesc { char key_type; @@ -28,6 +39,19 @@ struct KeyDesc { bool same_as_before; }; +AESKey HexToKey(const std::string& hex) { + if (hex.size() < 32) { + throw std::invalid_argument("hex string is too short"); + } + + AESKey key; + for (std::size_t i = 0; i < key.size(); ++i) { + key[i] = static_cast(std::stoi(hex.substr(i * 2, 2), 0, 16)); + } + + return key; +} + struct KeySlot { std::optional x; std::optional y; @@ -48,8 +72,8 @@ struct KeySlot { } void GenerateNormalKey() { - if (x && y && generator_constant) { - normal = Lrot128(Add128(Xor128(Lrot128(*x, 2), *y), *generator_constant), 87); + if (x && y) { + normal = Lrot128(Add128(Xor128(Lrot128(*x, 2), *y), generator_constant), 87); } else { normal = {}; } @@ -65,17 +89,46 @@ struct KeySlot { std::array key_slots; std::array, 6> common_key_y_slots; -AESKey HexToKey(const std::string& hex) { - if (hex.size() < 32) { - throw std::invalid_argument("hex string is too short"); - } +enum class FirmwareType : u32 { + ARM9 = 0, // uses NDMA + ARM11 = 1, // uses XDMA +}; - AESKey key; - for (std::size_t i = 0; i < key.size(); ++i) { - key[i] = static_cast(std::stoi(hex.substr(i * 2, 2), 0, 16)); - } +struct FirmwareSectionHeader { + u32_le offset; + u32_le phys_address; + u32_le size; + enum_le firmware_type; + std::array hash; // SHA-256 hash +}; - return key; +struct FIRM_Header { + u32_le magic; // FIRM + u32_le boot_priority; // Usually 0 + u32_le arm11_entrypoint; + u32_le arm9_entrypoint; + INSERT_PADDING_BYTES(0x30); // Reserved + std::array section_headers; // 1st ARM11?, 3rd ARM9 + std::array signature; // RSA-2048 signature of the FIRM header's hash +}; + +struct ARM9_Header { + AESKey enc_key_x; + AESKey key_y; + AESKey CTR; + std::array size; // in ASCII + INSERT_PADDING_BYTES(8); // Unknown + std::array control_block; + std::array hardware_debug_info; + std::array enc_key_x_slot_16; +}; + +std::string KeyToString(AESKey& key) { + std::string s; + for (auto pos : key) { + s += fmt::format("{:02X}", pos); + } + return s; } void LoadBootromKeys() { @@ -130,11 +183,8 @@ void LoadBootromKeys() { } } - std::string s; - for (auto pos : new_key) { - s += fmt::format("{:02X}", pos); - } - LOG_DEBUG(HW_AES, "Loaded Slot{:#02x} Key{}: {}", key.slot_id, key.key_type, s); + LOG_DEBUG(HW_AES, "Loaded Slot{:#02x} Key{}: {}", key.slot_id, key.key_type, + KeyToString(new_key)); switch (key.key_type) { case 'X': @@ -153,6 +203,174 @@ void LoadBootromKeys() { } } +void LoadNativeFirmKeysOld3DS() { + // Use the save mode native firm instead of the normal mode since there are only 2 version of it + // and thus we can use fixed offsets + + constexpr u64 save_mode_native_firm_id = 0x00040138'00000003; + + // TODO(B3N30): Add the 0x25 KeyX that gets initalized by native_firm + + FileSys::NCCHArchive archive(save_mode_native_firm_id, Service::FS::MediaType::NAND); + std::array exefs_filepath = {'.', 'f', 'i', 'r', 'm', 0, 0, 0}; + FileSys::Path file_path = FileSys::MakeNCCHFilePath( + FileSys::NCCHFileOpenType::NCCHData, 0, FileSys::NCCHFilePathType::ExeFS, exefs_filepath); + FileSys::Mode open_mode = {}; + open_mode.read_flag.Assign(1); + auto file_result = archive.OpenFile(file_path, open_mode); + if (file_result.Failed()) + return; + + auto firm = std::move(file_result).Unwrap(); + const std::size_t size = firm->GetSize(); + if (size != 843776) { + LOG_ERROR(HW_AES, "save mode native firm has wrong size {}", size); + return; + } + std::vector firm_buffer(size); + firm->Read(0, firm_buffer.size(), firm_buffer.data()); + firm->Close(); + + AESKey key; + constexpr std::size_t SLOT_0x31_KEY_Y_OFFSET = 817672; + std::memcpy(key.data(), firm_buffer.data() + SLOT_0x31_KEY_Y_OFFSET, sizeof(key)); + key_slots.at(0x31).SetKeyY(key); + LOG_DEBUG(HW_AES, "Loaded Slot0x31 KeyY: {}", KeyToString(key)); + + auto LoadCommonKey = [&firm_buffer](std::size_t key_slot) -> AESKey { + constexpr std::size_t START_OFFSET = 836533; + constexpr std::size_t OFFSET = 0x14; // 0x10 bytes for key + 4 bytes between keys + AESKey key; + std::memcpy(key.data(), firm_buffer.data() + START_OFFSET + OFFSET * key_slot, sizeof(key)); + return key; + }; + + for (std::size_t key_slot{0}; key_slot < 6; ++key_slot) { + AESKey key = LoadCommonKey(key_slot); + common_key_y_slots[key_slot] = key; + LOG_DEBUG(HW_AES, "Loaded common key{}: {}", key_slot, KeyToString(key)); + } +} + +void LoadNativeFirmKeysNew3DS() { + // The first 0x10 bytes of the secret_sector are used as a key to decrypt a KeyX from the + // native_firm + const std::string filepath = + FileUtil::GetUserPath(FileUtil::UserPath::SysDataDir) + SECRET_SECTOR; + auto secret = FileUtil::IOFile(filepath, "rb"); + if (!secret) { + return; + } + ASSERT(secret.GetSize() > 0x10); + + AESKey secret_key; + secret.ReadArray(secret_key.data(), secret_key.size()); + + // Use the save mode native firm instead of the normal mode since there are only 1 version of it + // and thus we can use fixed offsets + constexpr u64 save_mode_native_firm_id = 0x00040138'20000003; + + // TODO(B3N30): Add the 0x25 KeyX that gets initalized by native_firm + + // TODO(B3N30): Add the 0x18 - 0x1F KeyX that gets initalized by native_firm. This probably + // requires the normal native firm with version > 9.6.0-X + + FileSys::NCCHArchive archive(save_mode_native_firm_id, Service::FS::MediaType::NAND); + std::array exefs_filepath = {'.', 'f', 'i', 'r', 'm', 0, 0, 0}; + FileSys::Path file_path = FileSys::MakeNCCHFilePath( + FileSys::NCCHFileOpenType::NCCHData, 0, FileSys::NCCHFilePathType::ExeFS, exefs_filepath); + FileSys::Mode open_mode = {}; + open_mode.read_flag.Assign(1); + auto file_result = archive.OpenFile(file_path, open_mode); + if (file_result.Failed()) + return; + + auto firm = std::move(file_result).Unwrap(); + std::vector firm_buffer(firm->GetSize()); + firm->Read(0, firm_buffer.size(), firm_buffer.data()); + firm->Close(); + + FIRM_Header header; + std::memcpy(&header, firm_buffer.data(), sizeof(header)); + + auto MakeMagic = [](char a, char b, char c, char d) -> u32 { + return a | b << 8 | c << 16 | d << 24; + }; + if (MakeMagic('F', 'I', 'R', 'M') != header.magic) { + LOG_ERROR(HW_AES, "N3DS SAVE MODE Native Firm has wrong header {}", header.magic); + return; + } + + u32 arm9_offset(0); + u32 arm9_size(0); + for (auto section_header : header.section_headers) { + if (section_header.firmware_type == FirmwareType::ARM9) { + arm9_offset = section_header.offset; + arm9_size = section_header.size; + break; + } + } + + if (arm9_offset != 0x66800) { + LOG_ERROR(HW_AES, "ARM9 binary at wrong offset: {}", arm9_offset); + return; + } + if (arm9_size != 0x8BA00) { + LOG_ERROR(HW_AES, "ARM9 binary has wrong size: {}", arm9_size); + return; + } + + ARM9_Header arm9_header; + std::memcpy(&arm9_header, firm_buffer.data() + arm9_offset, sizeof(arm9_header)); + + AESKey keyX_slot0x15; + CryptoPP::ECB_Mode::Decryption d; + d.SetKey(secret_key.data(), secret_key.size()); + d.ProcessData(keyX_slot0x15.data(), arm9_header.enc_key_x.data(), arm9_header.enc_key_x.size()); + + key_slots.at(0x15).SetKeyX(keyX_slot0x15); + key_slots.at(0x15).SetKeyY(arm9_header.key_y); + auto normal_key_slot0x15 = key_slots.at(0x15).normal; + if (!normal_key_slot0x15) { + LOG_ERROR(HW_AES, "Failed to get normal key for slot id 0x15"); + return; + } + + constexpr u32 ARM9_BINARY_OFFSET = 0x800; // From the beginning of the ARM9 section + std::vector enc_arm9_binary; + enc_arm9_binary.resize(arm9_size - ARM9_BINARY_OFFSET); + ASSERT(enc_arm9_binary.size() + arm9_offset + ARM9_BINARY_OFFSET < firm_buffer.size()); + std::memcpy(enc_arm9_binary.data(), firm_buffer.data() + arm9_offset + ARM9_BINARY_OFFSET, + enc_arm9_binary.size()); + + std::vector arm9_binary; + arm9_binary.resize(enc_arm9_binary.size()); + CryptoPP::CTR_Mode::Decryption d2; + d2.SetKeyWithIV(normal_key_slot0x15->data(), normal_key_slot0x15->size(), + arm9_header.CTR.data(), arm9_header.CTR.size()); + d2.ProcessData(arm9_binary.data(), enc_arm9_binary.data(), enc_arm9_binary.size()); + + AESKey key; + constexpr std::size_t SLOT_0x31_KEY_Y_OFFSET = 517368; + std::memcpy(key.data(), arm9_binary.data() + SLOT_0x31_KEY_Y_OFFSET, sizeof(key)); + key_slots.at(0x31).SetKeyY(key); + LOG_DEBUG(HW_AES, "Loaded Slot0x31 KeyY: {}", KeyToString(key)); + + auto LoadCommonKey = [&arm9_binary](std::size_t key_slot) -> AESKey { + constexpr std::size_t START_OFFSET = 541065; + constexpr std::size_t OFFSET = 0x14; // 0x10 bytes for key + 4 bytes between keys + AESKey key; + std::memcpy(key.data(), arm9_binary.data() + START_OFFSET + OFFSET * key_slot, sizeof(key)); + return key; + }; + + for (std::size_t key_slot{0}; key_slot < 6; ++key_slot) { + AESKey key = LoadCommonKey(key_slot); + common_key_y_slots[key_slot] = key; + LOG_DEBUG(HW_AES, "Loaded common key{}: {}", key_slot, KeyToString(key)); + } +} + void LoadPresetKeys() { const std::string filepath = FileUtil::GetUserPath(FileUtil::UserPath::SysDataDir) + AES_KEYS; FileUtil::CreateFullPath(filepath); // Create path if not already created @@ -181,11 +399,6 @@ void LoadPresetKeys() { continue; } - if (name == "generator") { - generator_constant = key; - continue; - } - std::size_t common_key_index; if (std::sscanf(name.c_str(), "common%zd", &common_key_index) == 1) { if (common_key_index >= common_key_y_slots.size()) { @@ -232,14 +445,12 @@ void InitKeys() { if (initialized) return; LoadBootromKeys(); + LoadNativeFirmKeysOld3DS(); + LoadNativeFirmKeysNew3DS(); LoadPresetKeys(); initialized = true; } -void SetGeneratorConstant(const AESKey& key) { - generator_constant = key; -} - void SetKeyX(std::size_t slot_id, const AESKey& key) { key_slots.at(slot_id).SetKeyX(key); } diff --git a/src/core/hw/gpu.cpp b/src/core/hw/gpu.cpp index d68068cbb..47bb852f8 100644 --- a/src/core/hw/gpu.cpp +++ b/src/core/hw/gpu.cpp @@ -31,7 +31,7 @@ Regs g_regs; /// 268MHz CPU clocks / 60Hz frames per second const u64 frame_ticks = static_cast(BASE_CLOCK_RATE_ARM11 / SCREEN_REFRESH_RATE); /// Event id for CoreTiming -static CoreTiming::EventType* vblank_event; +static Core::TimingEventType* vblank_event; template inline void Read(T& var, const u32 raw_addr) { @@ -522,7 +522,7 @@ static void VBlankCallback(u64 userdata, s64 cycles_late) { Service::GSP::SignalInterrupt(Service::GSP::InterruptId::PDC1); // Reschedule recurrent event - CoreTiming::ScheduleEvent(frame_ticks - cycles_late, vblank_event); + Core::System::GetInstance().CoreTiming().ScheduleEvent(frame_ticks - cycles_late, vblank_event); } /// Initialize hardware @@ -555,8 +555,9 @@ void Init() { framebuffer_sub.color_format.Assign(Regs::PixelFormat::RGB8); framebuffer_sub.active_fb = 0; - vblank_event = CoreTiming::RegisterEvent("GPU::VBlankCallback", VBlankCallback); - CoreTiming::ScheduleEvent(frame_ticks, vblank_event); + Core::Timing& timing = Core::System::GetInstance().CoreTiming(); + vblank_event = timing.RegisterEvent("GPU::VBlankCallback", VBlankCallback); + timing.ScheduleEvent(frame_ticks, vblank_event); LOG_DEBUG(HW_GPU, "initialized OK"); } diff --git a/src/core/loader/3dsx.cpp b/src/core/loader/3dsx.cpp index d791c085c..1b3905b02 100644 --- a/src/core/loader/3dsx.cpp +++ b/src/core/loader/3dsx.cpp @@ -217,7 +217,7 @@ static THREEDSX_Error Load3DSXFile(FileUtil::IOFile& file, u32 base_addr, } // Create the CodeSet - SharedPtr code_set = CodeSet::Create("", 0); + SharedPtr code_set = Core::System::GetInstance().Kernel().CreateCodeSet("", 0); code_set->CodeSegment().offset = loadinfo.seg_ptrs[0] - program_image.data(); code_set->CodeSegment().addr = loadinfo.seg_addrs[0]; @@ -267,13 +267,13 @@ ResultStatus AppLoader_THREEDSX::Load(Kernel::SharedPtr& proces return ResultStatus::Error; codeset->name = filename; - process = Kernel::Process::Create(std::move(codeset)); + process = Core::System::GetInstance().Kernel().CreateProcess(std::move(codeset)); process->svc_access_mask.set(); process->address_mappings = default_address_mappings; // Attach the default resource limit (APPLICATION) to the process - process->resource_limit = - Kernel::ResourceLimit::GetForCategory(Kernel::ResourceLimitCategory::APPLICATION); + process->resource_limit = Core::System::GetInstance().Kernel().ResourceLimit().GetForCategory( + Kernel::ResourceLimitCategory::APPLICATION); process->Run(48, Kernel::DEFAULT_STACK_SIZE); diff --git a/src/core/loader/elf.cpp b/src/core/loader/elf.cpp index fd2bb23b3..26470ff9d 100644 --- a/src/core/loader/elf.cpp +++ b/src/core/loader/elf.cpp @@ -8,6 +8,7 @@ #include "common/common_types.h" #include "common/file_util.h" #include "common/logging/log.h" +#include "core/core.h" #include "core/hle/kernel/process.h" #include "core/hle/kernel/resource_limit.h" #include "core/loader/elf.h" @@ -299,7 +300,7 @@ SharedPtr ElfReader::LoadInto(u32 vaddr) { std::vector program_image(total_image_size); std::size_t current_image_position = 0; - SharedPtr codeset = CodeSet::Create("", 0); + SharedPtr codeset = Core::System::GetInstance().Kernel().CreateCodeSet("", 0); for (unsigned int i = 0; i < header->e_phnum; ++i) { Elf32_Phdr* p = &segments[i]; @@ -395,13 +396,13 @@ ResultStatus AppLoader_ELF::Load(Kernel::SharedPtr& process) { SharedPtr codeset = elf_reader.LoadInto(Memory::PROCESS_IMAGE_VADDR); codeset->name = filename; - process = Kernel::Process::Create(std::move(codeset)); + process = Core::System::GetInstance().Kernel().CreateProcess(std::move(codeset)); process->svc_access_mask.set(); process->address_mappings = default_address_mappings; // Attach the default resource limit (APPLICATION) to the process - process->resource_limit = - Kernel::ResourceLimit::GetForCategory(Kernel::ResourceLimitCategory::APPLICATION); + process->resource_limit = Core::System::GetInstance().Kernel().ResourceLimit().GetForCategory( + Kernel::ResourceLimitCategory::APPLICATION); process->Run(48, Kernel::DEFAULT_STACK_SIZE); diff --git a/src/core/loader/ncch.cpp b/src/core/loader/ncch.cpp index 9fa1d05f1..34f49663a 100644 --- a/src/core/loader/ncch.cpp +++ b/src/core/loader/ncch.cpp @@ -75,7 +75,8 @@ ResultStatus AppLoader_NCCH::LoadExec(Kernel::SharedPtr& proces std::string process_name = Common::StringFromFixedZeroTerminatedBuffer( (const char*)overlay_ncch->exheader_header.codeset_info.name, 8); - SharedPtr codeset = CodeSet::Create(process_name, program_id); + SharedPtr codeset = + Core::System::GetInstance().Kernel().CreateCodeSet(process_name, program_id); codeset->CodeSegment().offset = 0; codeset->CodeSegment().addr = overlay_ncch->exheader_header.codeset_info.text.address; @@ -103,12 +104,13 @@ ResultStatus AppLoader_NCCH::LoadExec(Kernel::SharedPtr& proces codeset->entrypoint = codeset->CodeSegment().addr; codeset->memory = std::make_shared>(std::move(code)); - process = Kernel::Process::Create(std::move(codeset)); + process = Core::System::GetInstance().Kernel().CreateProcess(std::move(codeset)); // Attach a resource limit to the process based on the resource limit category process->resource_limit = - Kernel::ResourceLimit::GetForCategory(static_cast( - overlay_ncch->exheader_header.arm11_system_local_caps.resource_limit_category)); + Core::System::GetInstance().Kernel().ResourceLimit().GetForCategory( + static_cast( + overlay_ncch->exheader_header.arm11_system_local_caps.resource_limit_category)); // Set the default CPU core for this process process->ideal_processor = diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 52252c932..ed5a04c9a 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -23,6 +23,7 @@ namespace Memory { static std::array vram; static std::array n3ds_extra_ram; +std::array fcram; static PageTable* current_page_table = nullptr; @@ -111,7 +112,7 @@ static u8* GetPointerFromVMA(const Kernel::Process& process, VAddr vaddr) { * using a VMA from the current process. */ static u8* GetPointerFromVMA(VAddr vaddr) { - return GetPointerFromVMA(*Kernel::g_current_process, vaddr); + return GetPointerFromVMA(*Core::System::GetInstance().Kernel().GetCurrentProcess(), vaddr); } /** @@ -128,7 +129,8 @@ static MMIORegionPointer GetMMIOHandler(const PageTable& page_table, VAddr vaddr } static MMIORegionPointer GetMMIOHandler(VAddr vaddr) { - const PageTable& page_table = Kernel::g_current_process->vm_manager.page_table; + const PageTable& page_table = + Core::System::GetInstance().Kernel().GetCurrentProcess()->vm_manager.page_table; return GetMMIOHandler(page_table, vaddr); } @@ -229,7 +231,7 @@ bool IsValidVirtualAddress(const Kernel::Process& process, const VAddr vaddr) { } bool IsValidVirtualAddress(const VAddr vaddr) { - return IsValidVirtualAddress(*Kernel::g_current_process, vaddr); + return IsValidVirtualAddress(*Core::System::GetInstance().Kernel().GetCurrentProcess(), vaddr); } bool IsValidPhysicalAddress(const PAddr paddr) { @@ -304,15 +306,7 @@ u8* GetPhysicalPointer(PAddr address) { target_pointer = Core::DSP().GetDspMemory().data() + offset_into_region; break; case FCRAM_PADDR: - for (const auto& region : Kernel::memory_regions) { - if (offset_into_region >= region.base && - offset_into_region < region.base + region.size) { - target_pointer = - region.linear_heap_memory->data() + offset_into_region - region.base; - break; - } - } - ASSERT_MSG(target_pointer != nullptr, "Invalid FCRAM address"); + target_pointer = fcram.data() + offset_into_region; break; case N3DS_EXTRA_RAM_PADDR: target_pointer = n3ds_extra_ram.data() + offset_into_region; @@ -324,6 +318,25 @@ u8* GetPhysicalPointer(PAddr address) { return target_pointer; } +/// For a rasterizer-accessible PAddr, gets a list of all possible VAddr +static std::vector PhysicalToVirtualAddressForRasterizer(PAddr addr) { + if (addr >= VRAM_PADDR && addr < VRAM_PADDR_END) { + return {addr - VRAM_PADDR + VRAM_VADDR}; + } + if (addr >= FCRAM_PADDR && addr < FCRAM_PADDR_END) { + return {addr - FCRAM_PADDR + LINEAR_HEAP_VADDR, addr - FCRAM_PADDR + NEW_LINEAR_HEAP_VADDR}; + } + if (addr >= FCRAM_PADDR_END && addr < FCRAM_N3DS_PADDR_END) { + return {addr - FCRAM_PADDR + NEW_LINEAR_HEAP_VADDR}; + } + // While the physical <-> virtual mapping is 1:1 for the regions supported by the cache, + // some games (like Pokemon Super Mystery Dungeon) will try to use textures that go beyond + // the end address of VRAM, causing the Virtual->Physical translation to fail when flushing + // parts of the texture. + LOG_ERROR(HW_Memory, "Trying to use invalid physical address for rasterizer: {:08X}", addr); + return {}; +} + void RasterizerMarkRegionCached(PAddr start, u32 size, bool cached) { if (start == 0) { return; @@ -333,57 +346,46 @@ void RasterizerMarkRegionCached(PAddr start, u32 size, bool cached) { PAddr paddr = start; for (unsigned i = 0; i < num_pages; ++i, paddr += PAGE_SIZE) { - std::optional maybe_vaddr = PhysicalToVirtualAddress(paddr); - // While the physical <-> virtual mapping is 1:1 for the regions supported by the cache, - // some games (like Pokemon Super Mystery Dungeon) will try to use textures that go beyond - // the end address of VRAM, causing the Virtual->Physical translation to fail when flushing - // parts of the texture. - if (!maybe_vaddr) { - LOG_ERROR(HW_Memory, - "Trying to flush a cached region to an invalid physical address {:08X}", - paddr); - continue; - } - VAddr vaddr = *maybe_vaddr; + for (VAddr vaddr : PhysicalToVirtualAddressForRasterizer(paddr)) { + PageType& page_type = current_page_table->attributes[vaddr >> PAGE_BITS]; - PageType& page_type = current_page_table->attributes[vaddr >> PAGE_BITS]; - - if (cached) { - // Switch page type to cached if now cached - switch (page_type) { - case PageType::Unmapped: - // It is not necessary for a process to have this region mapped into its address - // space, for example, a system module need not have a VRAM mapping. - break; - case PageType::Memory: - page_type = PageType::RasterizerCachedMemory; - current_page_table->pointers[vaddr >> PAGE_BITS] = nullptr; - break; - default: - UNREACHABLE(); - } - } else { - // Switch page type to uncached if now uncached - switch (page_type) { - case PageType::Unmapped: - // It is not necessary for a process to have this region mapped into its address - // space, for example, a system module need not have a VRAM mapping. - break; - case PageType::RasterizerCachedMemory: { - u8* pointer = GetPointerFromVMA(vaddr & ~PAGE_MASK); - if (pointer == nullptr) { - // It's possible that this function has been called while updating the pagetable - // after unmapping a VMA. In that case the underlying VMA will no longer exist, - // and we should just leave the pagetable entry blank. - page_type = PageType::Unmapped; - } else { - page_type = PageType::Memory; - current_page_table->pointers[vaddr >> PAGE_BITS] = pointer; + if (cached) { + // Switch page type to cached if now cached + switch (page_type) { + case PageType::Unmapped: + // It is not necessary for a process to have this region mapped into its address + // space, for example, a system module need not have a VRAM mapping. + break; + case PageType::Memory: + page_type = PageType::RasterizerCachedMemory; + current_page_table->pointers[vaddr >> PAGE_BITS] = nullptr; + break; + default: + UNREACHABLE(); + } + } else { + // Switch page type to uncached if now uncached + switch (page_type) { + case PageType::Unmapped: + // It is not necessary for a process to have this region mapped into its address + // space, for example, a system module need not have a VRAM mapping. + break; + case PageType::RasterizerCachedMemory: { + u8* pointer = GetPointerFromVMA(vaddr & ~PAGE_MASK); + if (pointer == nullptr) { + // It's possible that this function has been called while updating the + // pagetable after unmapping a VMA. In that case the underlying VMA will no + // longer exist, and we should just leave the pagetable entry blank. + page_type = PageType::Unmapped; + } else { + page_type = PageType::Memory; + current_page_table->pointers[vaddr >> PAGE_BITS] = pointer; + } + break; + } + default: + UNREACHABLE(); } - break; - } - default: - UNREACHABLE(); } } } @@ -524,7 +526,8 @@ void ReadBlock(const Kernel::Process& process, const VAddr src_addr, void* dest_ } void ReadBlock(const VAddr src_addr, void* dest_buffer, const std::size_t size) { - ReadBlock(*Kernel::g_current_process, src_addr, dest_buffer, size); + ReadBlock(*Core::System::GetInstance().Kernel().GetCurrentProcess(), src_addr, dest_buffer, + size); } void Write8(const VAddr addr, const u8 data) { @@ -592,7 +595,8 @@ void WriteBlock(const Kernel::Process& process, const VAddr dest_addr, const voi } void WriteBlock(const VAddr dest_addr, const void* src_buffer, const std::size_t size) { - WriteBlock(*Kernel::g_current_process, dest_addr, src_buffer, size); + WriteBlock(*Core::System::GetInstance().Kernel().GetCurrentProcess(), dest_addr, src_buffer, + size); } void ZeroBlock(const Kernel::Process& process, const VAddr dest_addr, const std::size_t size) { @@ -644,7 +648,7 @@ void ZeroBlock(const Kernel::Process& process, const VAddr dest_addr, const std: } void ZeroBlock(const VAddr dest_addr, const std::size_t size) { - ZeroBlock(*Kernel::g_current_process, dest_addr, size); + ZeroBlock(*Core::System::GetInstance().Kernel().GetCurrentProcess(), dest_addr, size); } void CopyBlock(const Kernel::Process& process, VAddr dest_addr, VAddr src_addr, @@ -699,7 +703,59 @@ void CopyBlock(const Kernel::Process& process, VAddr dest_addr, VAddr src_addr, } void CopyBlock(VAddr dest_addr, VAddr src_addr, const std::size_t size) { - CopyBlock(*Kernel::g_current_process, dest_addr, src_addr, size); + CopyBlock(*Core::System::GetInstance().Kernel().GetCurrentProcess(), dest_addr, src_addr, size); +} + +void CopyBlock(const Kernel::Process& src_process, const Kernel::Process& dest_process, + VAddr src_addr, VAddr dest_addr, std::size_t size) { + auto& page_table = src_process.vm_manager.page_table; + std::size_t remaining_size = size; + std::size_t page_index = src_addr >> PAGE_BITS; + std::size_t page_offset = src_addr & PAGE_MASK; + + while (remaining_size > 0) { + const std::size_t copy_amount = std::min(PAGE_SIZE - page_offset, remaining_size); + const VAddr current_vaddr = static_cast((page_index << PAGE_BITS) + page_offset); + + switch (page_table.attributes[page_index]) { + case PageType::Unmapped: { + LOG_ERROR(HW_Memory, + "unmapped CopyBlock @ 0x{:08X} (start address = 0x{:08X}, size = {})", + current_vaddr, src_addr, size); + ZeroBlock(dest_process, dest_addr, copy_amount); + break; + } + case PageType::Memory: { + DEBUG_ASSERT(page_table.pointers[page_index]); + const u8* src_ptr = page_table.pointers[page_index] + page_offset; + WriteBlock(dest_process, dest_addr, src_ptr, copy_amount); + break; + } + case PageType::Special: { + MMIORegionPointer handler = GetMMIOHandler(page_table, current_vaddr); + DEBUG_ASSERT(handler); + std::vector buffer(copy_amount); + handler->ReadBlock(current_vaddr, buffer.data(), buffer.size()); + WriteBlock(dest_process, dest_addr, buffer.data(), buffer.size()); + break; + } + case PageType::RasterizerCachedMemory: { + RasterizerFlushVirtualRegion(current_vaddr, static_cast(copy_amount), + FlushMode::Flush); + WriteBlock(dest_process, dest_addr, GetPointerFromVMA(src_process, current_vaddr), + copy_amount); + break; + } + default: + UNREACHABLE(); + } + + page_index++; + page_offset = 0; + dest_addr += static_cast(copy_amount); + src_addr += static_cast(copy_amount); + remaining_size -= copy_amount; + } } template <> @@ -772,22 +828,9 @@ PAddr VirtualToPhysicalAddress(const VAddr addr) { return *paddr; } -std::optional PhysicalToVirtualAddress(const PAddr addr) { - if (addr == 0) { - return 0; - } else if (addr >= VRAM_PADDR && addr < VRAM_PADDR_END) { - return addr - VRAM_PADDR + VRAM_VADDR; - } else if (addr >= FCRAM_PADDR && addr < FCRAM_PADDR_END) { - return addr - FCRAM_PADDR + Kernel::g_current_process->GetLinearHeapAreaAddress(); - } else if (addr >= DSP_RAM_PADDR && addr < DSP_RAM_PADDR_END) { - return addr - DSP_RAM_PADDR + DSP_RAM_VADDR; - } else if (addr >= IO_AREA_PADDR && addr < IO_AREA_PADDR_END) { - return addr - IO_AREA_PADDR + IO_AREA_VADDR; - } else if (addr >= N3DS_EXTRA_RAM_PADDR && addr < N3DS_EXTRA_RAM_PADDR_END) { - return addr - N3DS_EXTRA_RAM_PADDR + N3DS_EXTRA_RAM_VADDR; - } - - return {}; +u32 GetFCRAMOffset(u8* pointer) { + ASSERT(pointer >= fcram.data() && pointer < fcram.data() + fcram.size()); + return pointer - fcram.data(); } } // namespace Memory diff --git a/src/core/memory.h b/src/core/memory.h index 64a260899..58e8a6a80 100644 --- a/src/core/memory.h +++ b/src/core/memory.h @@ -75,7 +75,7 @@ struct PageTable { enum : PAddr { /// IO register area IO_AREA_PADDR = 0x10100000, - IO_AREA_SIZE = 0x01000000, ///< IO area size (16MB) + IO_AREA_SIZE = 0x00400000, ///< IO area size (4MB) IO_AREA_PADDR_END = IO_AREA_PADDR + IO_AREA_SIZE, /// MPCore internal memory region @@ -176,6 +176,8 @@ enum : VAddr { NEW_LINEAR_HEAP_VADDR_END = NEW_LINEAR_HEAP_VADDR + NEW_LINEAR_HEAP_SIZE, }; +extern std::array fcram; + /// Currently active page table void SetCurrentPageTable(PageTable* page_table); PageTable* GetCurrentPageTable(); @@ -205,6 +207,8 @@ void ZeroBlock(const Kernel::Process& process, VAddr dest_addr, const std::size_ void ZeroBlock(VAddr dest_addr, const std::size_t size); void CopyBlock(const Kernel::Process& process, VAddr dest_addr, VAddr src_addr, std::size_t size); void CopyBlock(VAddr dest_addr, VAddr src_addr, std::size_t size); +void CopyBlock(const Kernel::Process& src_process, const Kernel::Process& dest_process, + VAddr src_addr, VAddr dest_addr, std::size_t size); u8* GetPointer(VAddr vaddr); @@ -224,11 +228,6 @@ std::optional TryVirtualToPhysicalAddress(VAddr addr); */ PAddr VirtualToPhysicalAddress(VAddr addr); -/** - * Undoes a mapping performed by VirtualToPhysicalAddress(). - */ -std::optional PhysicalToVirtualAddress(PAddr paddr); - /** * Gets a pointer to the memory region beginning at the specified physical address. */ @@ -269,4 +268,7 @@ enum class FlushMode { */ void RasterizerFlushVirtualRegion(VAddr start, u32 size, FlushMode mode); +/// Gets offset in FCRAM from a pointer inside FCRAM range +u32 GetFCRAMOffset(u8* pointer); + } // namespace Memory diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp index 94270dbfe..93e17b268 100644 --- a/src/core/telemetry_session.cpp +++ b/src/core/telemetry_session.cpp @@ -7,6 +7,7 @@ #include "common/assert.h" #include "common/file_util.h" +#include "common/logging/log.h" #include "common/scm_rev.h" #ifdef ARCHITECTURE_x86_64 #include "common/x64/cpu_detect.h" @@ -202,4 +203,13 @@ TelemetrySession::~TelemetrySession() { backend = nullptr; } +bool TelemetrySession::SubmitTestcase() { +#ifdef ENABLE_WEB_SERVICE + field_collection.Accept(*backend); + return backend->SubmitTestcase(); +#else + return false; +#endif +} + } // namespace Core diff --git a/src/core/telemetry_session.h b/src/core/telemetry_session.h index bcf206b1b..12bb04c43 100644 --- a/src/core/telemetry_session.h +++ b/src/core/telemetry_session.h @@ -5,6 +5,7 @@ #pragma once #include +#include #include "common/telemetry.h" namespace Core { @@ -30,7 +31,11 @@ public: field_collection.AddField(type, name, std::move(value)); } - static void FinalizeAsyncJob(); + /** + * Submits a Testcase. + * @returns A bool indicating whether the submission succeeded + */ + bool SubmitTestcase(); private: Telemetry::FieldCollection field_collection; ///< Tracks all added fields for the session @@ -53,7 +58,6 @@ u64 RegenerateTelemetryId(); * Verifies the username and token. * @param username Citra username to use for authentication. * @param token Citra token to use for authentication. - * @param func A function that gets exectued when the verification is finished * @returns Future with bool indicating whether the verification succeeded */ bool VerifyLogin(const std::string& username, const std::string& token); diff --git a/src/tests/core/arm/arm_test_common.cpp b/src/tests/core/arm/arm_test_common.cpp index 23c0411e7..58b60f506 100644 --- a/src/tests/core/arm/arm_test_common.cpp +++ b/src/tests/core/arm/arm_test_common.cpp @@ -3,6 +3,7 @@ // Refer to the license.txt file included. #include "core/core.h" +#include "core/core_timing.h" #include "core/hle/kernel/process.h" #include "core/memory.h" #include "core/memory_setup.h" @@ -15,8 +16,15 @@ static Memory::PageTable* page_table = nullptr; TestEnvironment::TestEnvironment(bool mutable_memory_) : mutable_memory(mutable_memory_), test_memory(std::make_shared(this)) { - Kernel::g_current_process = Kernel::Process::Create(Kernel::CodeSet::Create("", 0)); - page_table = &Kernel::g_current_process->vm_manager.page_table; + // HACK: some memory functions are currently referring kernel from the global instance, + // so we need to create the kernel object there. + // Change this when all global states are eliminated. + Core::System::GetInstance().timing = std::make_unique(); + Core::System::GetInstance().kernel = std::make_unique(0); + kernel = Core::System::GetInstance().kernel.get(); + + kernel->SetCurrentProcess(kernel->CreateProcess(kernel->CreateCodeSet("", 0))); + page_table = &kernel->GetCurrentProcess()->vm_manager.page_table; page_table->pointers.fill(nullptr); page_table->attributes.fill(Memory::PageType::Unmapped); diff --git a/src/tests/core/arm/arm_test_common.h b/src/tests/core/arm/arm_test_common.h index 44c63818a..b52d26e3c 100644 --- a/src/tests/core/arm/arm_test_common.h +++ b/src/tests/core/arm/arm_test_common.h @@ -5,8 +5,8 @@ #include #include #include - #include "common/common_types.h" +#include "core/hle/kernel/kernel.h" #include "core/mmio.h" namespace ArmTests { @@ -79,6 +79,8 @@ private: bool mutable_memory; std::shared_ptr test_memory; std::vector write_records; + + Kernel::KernelSystem* kernel; }; } // namespace ArmTests diff --git a/src/tests/core/core_timing.cpp b/src/tests/core/core_timing.cpp index 2242c14cf..1cfd9e971 100644 --- a/src/tests/core/core_timing.cpp +++ b/src/tests/core/core_timing.cpp @@ -28,100 +28,90 @@ void CallbackTemplate(u64 userdata, s64 cycles_late) { REQUIRE(lateness == cycles_late); } -class ScopeInit final { -public: - ScopeInit() { - CoreTiming::Init(); - } - ~ScopeInit() { - CoreTiming::Shutdown(); - } -}; - -static void AdvanceAndCheck(u32 idx, int downcount, int expected_lateness = 0, +static void AdvanceAndCheck(Core::Timing& timing, u32 idx, int downcount, int expected_lateness = 0, int cpu_downcount = 0) { callbacks_ran_flags = 0; expected_callback = CB_IDS[idx]; lateness = expected_lateness; - CoreTiming::AddTicks(CoreTiming::GetDowncount() - - cpu_downcount); // Pretend we executed X cycles of instructions. - CoreTiming::Advance(); + timing.AddTicks(timing.GetDowncount() - + cpu_downcount); // Pretend we executed X cycles of instructions. + timing.Advance(); REQUIRE(decltype(callbacks_ran_flags)().set(idx) == callbacks_ran_flags); - REQUIRE(downcount == CoreTiming::GetDowncount()); + REQUIRE(downcount == timing.GetDowncount()); } TEST_CASE("CoreTiming[BasicOrder]", "[core]") { - ScopeInit guard; + Core::Timing timing; - CoreTiming::EventType* cb_a = CoreTiming::RegisterEvent("callbackA", CallbackTemplate<0>); - CoreTiming::EventType* cb_b = CoreTiming::RegisterEvent("callbackB", CallbackTemplate<1>); - CoreTiming::EventType* cb_c = CoreTiming::RegisterEvent("callbackC", CallbackTemplate<2>); - CoreTiming::EventType* cb_d = CoreTiming::RegisterEvent("callbackD", CallbackTemplate<3>); - CoreTiming::EventType* cb_e = CoreTiming::RegisterEvent("callbackE", CallbackTemplate<4>); + Core::TimingEventType* cb_a = timing.RegisterEvent("callbackA", CallbackTemplate<0>); + Core::TimingEventType* cb_b = timing.RegisterEvent("callbackB", CallbackTemplate<1>); + Core::TimingEventType* cb_c = timing.RegisterEvent("callbackC", CallbackTemplate<2>); + Core::TimingEventType* cb_d = timing.RegisterEvent("callbackD", CallbackTemplate<3>); + Core::TimingEventType* cb_e = timing.RegisterEvent("callbackE", CallbackTemplate<4>); // Enter slice 0 - CoreTiming::Advance(); + timing.Advance(); // D -> B -> C -> A -> E - CoreTiming::ScheduleEvent(1000, cb_a, CB_IDS[0]); - REQUIRE(1000 == CoreTiming::GetDowncount()); - CoreTiming::ScheduleEvent(500, cb_b, CB_IDS[1]); - REQUIRE(500 == CoreTiming::GetDowncount()); - CoreTiming::ScheduleEvent(800, cb_c, CB_IDS[2]); - REQUIRE(500 == CoreTiming::GetDowncount()); - CoreTiming::ScheduleEvent(100, cb_d, CB_IDS[3]); - REQUIRE(100 == CoreTiming::GetDowncount()); - CoreTiming::ScheduleEvent(1200, cb_e, CB_IDS[4]); - REQUIRE(100 == CoreTiming::GetDowncount()); + timing.ScheduleEvent(1000, cb_a, CB_IDS[0]); + REQUIRE(1000 == timing.GetDowncount()); + timing.ScheduleEvent(500, cb_b, CB_IDS[1]); + REQUIRE(500 == timing.GetDowncount()); + timing.ScheduleEvent(800, cb_c, CB_IDS[2]); + REQUIRE(500 == timing.GetDowncount()); + timing.ScheduleEvent(100, cb_d, CB_IDS[3]); + REQUIRE(100 == timing.GetDowncount()); + timing.ScheduleEvent(1200, cb_e, CB_IDS[4]); + REQUIRE(100 == timing.GetDowncount()); - AdvanceAndCheck(3, 400); - AdvanceAndCheck(1, 300); - AdvanceAndCheck(2, 200); - AdvanceAndCheck(0, 200); - AdvanceAndCheck(4, MAX_SLICE_LENGTH); + AdvanceAndCheck(timing, 3, 400); + AdvanceAndCheck(timing, 1, 300); + AdvanceAndCheck(timing, 2, 200); + AdvanceAndCheck(timing, 0, 200); + AdvanceAndCheck(timing, 4, MAX_SLICE_LENGTH); } TEST_CASE("CoreTiming[Threadsave]", "[core]") { - ScopeInit guard; + Core::Timing timing; - CoreTiming::EventType* cb_a = CoreTiming::RegisterEvent("callbackA", CallbackTemplate<0>); - CoreTiming::EventType* cb_b = CoreTiming::RegisterEvent("callbackB", CallbackTemplate<1>); - CoreTiming::EventType* cb_c = CoreTiming::RegisterEvent("callbackC", CallbackTemplate<2>); - CoreTiming::EventType* cb_d = CoreTiming::RegisterEvent("callbackD", CallbackTemplate<3>); - CoreTiming::EventType* cb_e = CoreTiming::RegisterEvent("callbackE", CallbackTemplate<4>); + Core::TimingEventType* cb_a = timing.RegisterEvent("callbackA", CallbackTemplate<0>); + Core::TimingEventType* cb_b = timing.RegisterEvent("callbackB", CallbackTemplate<1>); + Core::TimingEventType* cb_c = timing.RegisterEvent("callbackC", CallbackTemplate<2>); + Core::TimingEventType* cb_d = timing.RegisterEvent("callbackD", CallbackTemplate<3>); + Core::TimingEventType* cb_e = timing.RegisterEvent("callbackE", CallbackTemplate<4>); // Enter slice 0 - CoreTiming::Advance(); + timing.Advance(); // D -> B -> C -> A -> E - CoreTiming::ScheduleEventThreadsafe(1000, cb_a, CB_IDS[0]); + timing.ScheduleEventThreadsafe(1000, cb_a, CB_IDS[0]); // Manually force since ScheduleEventThreadsafe doesn't call it - CoreTiming::ForceExceptionCheck(1000); - REQUIRE(1000 == CoreTiming::GetDowncount()); - CoreTiming::ScheduleEventThreadsafe(500, cb_b, CB_IDS[1]); + timing.ForceExceptionCheck(1000); + REQUIRE(1000 == timing.GetDowncount()); + timing.ScheduleEventThreadsafe(500, cb_b, CB_IDS[1]); // Manually force since ScheduleEventThreadsafe doesn't call it - CoreTiming::ForceExceptionCheck(500); - REQUIRE(500 == CoreTiming::GetDowncount()); - CoreTiming::ScheduleEventThreadsafe(800, cb_c, CB_IDS[2]); + timing.ForceExceptionCheck(500); + REQUIRE(500 == timing.GetDowncount()); + timing.ScheduleEventThreadsafe(800, cb_c, CB_IDS[2]); // Manually force since ScheduleEventThreadsafe doesn't call it - CoreTiming::ForceExceptionCheck(800); - REQUIRE(500 == CoreTiming::GetDowncount()); - CoreTiming::ScheduleEventThreadsafe(100, cb_d, CB_IDS[3]); + timing.ForceExceptionCheck(800); + REQUIRE(500 == timing.GetDowncount()); + timing.ScheduleEventThreadsafe(100, cb_d, CB_IDS[3]); // Manually force since ScheduleEventThreadsafe doesn't call it - CoreTiming::ForceExceptionCheck(100); - REQUIRE(100 == CoreTiming::GetDowncount()); - CoreTiming::ScheduleEventThreadsafe(1200, cb_e, CB_IDS[4]); + timing.ForceExceptionCheck(100); + REQUIRE(100 == timing.GetDowncount()); + timing.ScheduleEventThreadsafe(1200, cb_e, CB_IDS[4]); // Manually force since ScheduleEventThreadsafe doesn't call it - CoreTiming::ForceExceptionCheck(1200); - REQUIRE(100 == CoreTiming::GetDowncount()); + timing.ForceExceptionCheck(1200); + REQUIRE(100 == timing.GetDowncount()); - AdvanceAndCheck(3, 400); - AdvanceAndCheck(1, 300); - AdvanceAndCheck(2, 200); - AdvanceAndCheck(0, 200); - AdvanceAndCheck(4, MAX_SLICE_LENGTH); + AdvanceAndCheck(timing, 3, 400); + AdvanceAndCheck(timing, 1, 300); + AdvanceAndCheck(timing, 2, 200); + AdvanceAndCheck(timing, 0, 200); + AdvanceAndCheck(timing, 4, MAX_SLICE_LENGTH); } namespace SharedSlotTest { @@ -141,97 +131,98 @@ void FifoCallback(u64 userdata, s64 cycles_late) { TEST_CASE("CoreTiming[SharedSlot]", "[core]") { using namespace SharedSlotTest; - ScopeInit guard; + Core::Timing timing; - CoreTiming::EventType* cb_a = CoreTiming::RegisterEvent("callbackA", FifoCallback<0>); - CoreTiming::EventType* cb_b = CoreTiming::RegisterEvent("callbackB", FifoCallback<1>); - CoreTiming::EventType* cb_c = CoreTiming::RegisterEvent("callbackC", FifoCallback<2>); - CoreTiming::EventType* cb_d = CoreTiming::RegisterEvent("callbackD", FifoCallback<3>); - CoreTiming::EventType* cb_e = CoreTiming::RegisterEvent("callbackE", FifoCallback<4>); + Core::TimingEventType* cb_a = timing.RegisterEvent("callbackA", FifoCallback<0>); + Core::TimingEventType* cb_b = timing.RegisterEvent("callbackB", FifoCallback<1>); + Core::TimingEventType* cb_c = timing.RegisterEvent("callbackC", FifoCallback<2>); + Core::TimingEventType* cb_d = timing.RegisterEvent("callbackD", FifoCallback<3>); + Core::TimingEventType* cb_e = timing.RegisterEvent("callbackE", FifoCallback<4>); - CoreTiming::ScheduleEvent(1000, cb_a, CB_IDS[0]); - CoreTiming::ScheduleEvent(1000, cb_b, CB_IDS[1]); - CoreTiming::ScheduleEvent(1000, cb_c, CB_IDS[2]); - CoreTiming::ScheduleEvent(1000, cb_d, CB_IDS[3]); - CoreTiming::ScheduleEvent(1000, cb_e, CB_IDS[4]); + timing.ScheduleEvent(1000, cb_a, CB_IDS[0]); + timing.ScheduleEvent(1000, cb_b, CB_IDS[1]); + timing.ScheduleEvent(1000, cb_c, CB_IDS[2]); + timing.ScheduleEvent(1000, cb_d, CB_IDS[3]); + timing.ScheduleEvent(1000, cb_e, CB_IDS[4]); // Enter slice 0 - CoreTiming::Advance(); - REQUIRE(1000 == CoreTiming::GetDowncount()); + timing.Advance(); + REQUIRE(1000 == timing.GetDowncount()); callbacks_ran_flags = 0; counter = 0; lateness = 0; - CoreTiming::AddTicks(CoreTiming::GetDowncount()); - CoreTiming::Advance(); - REQUIRE(MAX_SLICE_LENGTH == CoreTiming::GetDowncount()); + timing.AddTicks(timing.GetDowncount()); + timing.Advance(); + REQUIRE(MAX_SLICE_LENGTH == timing.GetDowncount()); REQUIRE(0x1FULL == callbacks_ran_flags.to_ullong()); } TEST_CASE("CoreTiming[PredictableLateness]", "[core]") { - ScopeInit guard; + Core::Timing timing; - CoreTiming::EventType* cb_a = CoreTiming::RegisterEvent("callbackA", CallbackTemplate<0>); - CoreTiming::EventType* cb_b = CoreTiming::RegisterEvent("callbackB", CallbackTemplate<1>); + Core::TimingEventType* cb_a = timing.RegisterEvent("callbackA", CallbackTemplate<0>); + Core::TimingEventType* cb_b = timing.RegisterEvent("callbackB", CallbackTemplate<1>); // Enter slice 0 - CoreTiming::Advance(); + timing.Advance(); - CoreTiming::ScheduleEvent(100, cb_a, CB_IDS[0]); - CoreTiming::ScheduleEvent(200, cb_b, CB_IDS[1]); + timing.ScheduleEvent(100, cb_a, CB_IDS[0]); + timing.ScheduleEvent(200, cb_b, CB_IDS[1]); - AdvanceAndCheck(0, 90, 10, -10); // (100 - 10) - AdvanceAndCheck(1, MAX_SLICE_LENGTH, 50, -50); + AdvanceAndCheck(timing, 0, 90, 10, -10); // (100 - 10) + AdvanceAndCheck(timing, 1, MAX_SLICE_LENGTH, 50, -50); } namespace ChainSchedulingTest { static int reschedules = 0; -static void RescheduleCallback(u64 userdata, s64 cycles_late) { +static void RescheduleCallback(Core::Timing& timing, u64 userdata, s64 cycles_late) { --reschedules; REQUIRE(reschedules >= 0); REQUIRE(lateness == cycles_late); if (reschedules > 0) - CoreTiming::ScheduleEvent(1000, reinterpret_cast(userdata), - userdata); + timing.ScheduleEvent(1000, reinterpret_cast(userdata), userdata); } } // namespace ChainSchedulingTest TEST_CASE("CoreTiming[ChainScheduling]", "[core]") { using namespace ChainSchedulingTest; - ScopeInit guard; + Core::Timing timing; - CoreTiming::EventType* cb_a = CoreTiming::RegisterEvent("callbackA", CallbackTemplate<0>); - CoreTiming::EventType* cb_b = CoreTiming::RegisterEvent("callbackB", CallbackTemplate<1>); - CoreTiming::EventType* cb_c = CoreTiming::RegisterEvent("callbackC", CallbackTemplate<2>); - CoreTiming::EventType* cb_rs = - CoreTiming::RegisterEvent("callbackReschedule", RescheduleCallback); + Core::TimingEventType* cb_a = timing.RegisterEvent("callbackA", CallbackTemplate<0>); + Core::TimingEventType* cb_b = timing.RegisterEvent("callbackB", CallbackTemplate<1>); + Core::TimingEventType* cb_c = timing.RegisterEvent("callbackC", CallbackTemplate<2>); + Core::TimingEventType* cb_rs = + timing.RegisterEvent("callbackReschedule", [&timing](u64 userdata, s64 cycles_late) { + RescheduleCallback(timing, userdata, cycles_late); + }); // Enter slice 0 - CoreTiming::Advance(); + timing.Advance(); - CoreTiming::ScheduleEvent(800, cb_a, CB_IDS[0]); - CoreTiming::ScheduleEvent(1000, cb_b, CB_IDS[1]); - CoreTiming::ScheduleEvent(2200, cb_c, CB_IDS[2]); - CoreTiming::ScheduleEvent(1000, cb_rs, reinterpret_cast(cb_rs)); - REQUIRE(800 == CoreTiming::GetDowncount()); + timing.ScheduleEvent(800, cb_a, CB_IDS[0]); + timing.ScheduleEvent(1000, cb_b, CB_IDS[1]); + timing.ScheduleEvent(2200, cb_c, CB_IDS[2]); + timing.ScheduleEvent(1000, cb_rs, reinterpret_cast(cb_rs)); + REQUIRE(800 == timing.GetDowncount()); reschedules = 3; - AdvanceAndCheck(0, 200); // cb_a - AdvanceAndCheck(1, 1000); // cb_b, cb_rs + AdvanceAndCheck(timing, 0, 200); // cb_a + AdvanceAndCheck(timing, 1, 1000); // cb_b, cb_rs REQUIRE(2 == reschedules); - CoreTiming::AddTicks(CoreTiming::GetDowncount()); - CoreTiming::Advance(); // cb_rs + timing.AddTicks(timing.GetDowncount()); + timing.Advance(); // cb_rs REQUIRE(1 == reschedules); - REQUIRE(200 == CoreTiming::GetDowncount()); + REQUIRE(200 == timing.GetDowncount()); - AdvanceAndCheck(2, 800); // cb_c + AdvanceAndCheck(timing, 2, 800); // cb_c - CoreTiming::AddTicks(CoreTiming::GetDowncount()); - CoreTiming::Advance(); // cb_rs + timing.AddTicks(timing.GetDowncount()); + timing.Advance(); // cb_rs REQUIRE(0 == reschedules); - REQUIRE(MAX_SLICE_LENGTH == CoreTiming::GetDowncount()); + REQUIRE(MAX_SLICE_LENGTH == timing.GetDowncount()); } diff --git a/src/tests/core/hle/kernel/hle_ipc.cpp b/src/tests/core/hle/kernel/hle_ipc.cpp index d0bf44a58..2e404a32d 100644 --- a/src/tests/core/hle/kernel/hle_ipc.cpp +++ b/src/tests/core/hle/kernel/hle_ipc.cpp @@ -3,6 +3,8 @@ // Refer to the license.txt file included. #include +#include "core/core.h" +#include "core/core_timing.h" #include "core/hle/ipc.h" #include "core/hle/kernel/client_port.h" #include "core/hle/kernel/client_session.h" @@ -14,23 +16,25 @@ namespace Kernel { -static SharedPtr MakeObject() { - return Event::Create(ResetType::OneShot); +static SharedPtr MakeObject(Kernel::KernelSystem& kernel) { + return kernel.CreateEvent(ResetType::OneShot); } TEST_CASE("HLERequestContext::PopulateFromIncomingCommandBuffer", "[core][kernel]") { - auto session = std::get>(ServerSession::CreateSessionPair()); + // HACK: see comments of member timing + Core::System::GetInstance().timing = std::make_unique(); + Kernel::KernelSystem kernel(0); + auto session = std::get>(kernel.CreateSessionPair()); HLERequestContext context(std::move(session)); - auto process = Process::Create(CodeSet::Create("", 0)); - HandleTable handle_table; + auto process = kernel.CreateProcess(kernel.CreateCodeSet("", 0)); SECTION("works with empty cmdbuf") { const u32_le input[]{ IPC::MakeHeader(0x1234, 0, 0), }; - context.PopulateFromIncomingCommandBuffer(input, *process, handle_table); + context.PopulateFromIncomingCommandBuffer(input, *process); REQUIRE(context.CommandBuffer()[0] == 0x12340000); } @@ -43,7 +47,7 @@ TEST_CASE("HLERequestContext::PopulateFromIncomingCommandBuffer", "[core][kernel 0xAABBCCDD, }; - context.PopulateFromIncomingCommandBuffer(input, *process, handle_table); + context.PopulateFromIncomingCommandBuffer(input, *process); auto* output = context.CommandBuffer(); REQUIRE(output[1] == 0x12345678); @@ -52,48 +56,51 @@ TEST_CASE("HLERequestContext::PopulateFromIncomingCommandBuffer", "[core][kernel } SECTION("translates move handles") { - auto a = MakeObject(); - Handle a_handle = handle_table.Create(a).Unwrap(); + auto a = MakeObject(kernel); + Handle a_handle = process->handle_table.Create(a).Unwrap(); const u32_le input[]{ IPC::MakeHeader(0, 0, 2), IPC::MoveHandleDesc(1), a_handle, }; - context.PopulateFromIncomingCommandBuffer(input, *process, handle_table); + context.PopulateFromIncomingCommandBuffer(input, *process); auto* output = context.CommandBuffer(); REQUIRE(context.GetIncomingHandle(output[2]) == a); - REQUIRE(handle_table.GetGeneric(a_handle) == nullptr); + REQUIRE(process->handle_table.GetGeneric(a_handle) == nullptr); } SECTION("translates copy handles") { - auto a = MakeObject(); - Handle a_handle = handle_table.Create(a).Unwrap(); + auto a = MakeObject(kernel); + Handle a_handle = process->handle_table.Create(a).Unwrap(); const u32_le input[]{ IPC::MakeHeader(0, 0, 2), IPC::CopyHandleDesc(1), a_handle, }; - context.PopulateFromIncomingCommandBuffer(input, *process, handle_table); + context.PopulateFromIncomingCommandBuffer(input, *process); auto* output = context.CommandBuffer(); REQUIRE(context.GetIncomingHandle(output[2]) == a); - REQUIRE(handle_table.GetGeneric(a_handle) == a); + REQUIRE(process->handle_table.GetGeneric(a_handle) == a); } SECTION("translates multi-handle descriptors") { - auto a = MakeObject(); - auto b = MakeObject(); - auto c = MakeObject(); + auto a = MakeObject(kernel); + auto b = MakeObject(kernel); + auto c = MakeObject(kernel); const u32_le input[]{ - IPC::MakeHeader(0, 0, 5), IPC::MoveHandleDesc(2), - handle_table.Create(a).Unwrap(), handle_table.Create(b).Unwrap(), - IPC::MoveHandleDesc(1), handle_table.Create(c).Unwrap(), + IPC::MakeHeader(0, 0, 5), + IPC::MoveHandleDesc(2), + process->handle_table.Create(a).Unwrap(), + process->handle_table.Create(b).Unwrap(), + IPC::MoveHandleDesc(1), + process->handle_table.Create(c).Unwrap(), }; - context.PopulateFromIncomingCommandBuffer(input, *process, handle_table); + context.PopulateFromIncomingCommandBuffer(input, *process); auto* output = context.CommandBuffer(); REQUIRE(context.GetIncomingHandle(output[2]) == a); @@ -108,7 +115,7 @@ TEST_CASE("HLERequestContext::PopulateFromIncomingCommandBuffer", "[core][kernel 0, }; - auto result = context.PopulateFromIncomingCommandBuffer(input, *process, handle_table); + auto result = context.PopulateFromIncomingCommandBuffer(input, *process); REQUIRE(result == RESULT_SUCCESS); auto* output = context.CommandBuffer(); @@ -122,7 +129,7 @@ TEST_CASE("HLERequestContext::PopulateFromIncomingCommandBuffer", "[core][kernel 0x98989898, }; - context.PopulateFromIncomingCommandBuffer(input, *process, handle_table); + context.PopulateFromIncomingCommandBuffer(input, *process); REQUIRE(context.CommandBuffer()[2] == process->process_id); } @@ -142,7 +149,7 @@ TEST_CASE("HLERequestContext::PopulateFromIncomingCommandBuffer", "[core][kernel target_address, }; - context.PopulateFromIncomingCommandBuffer(input, *process, handle_table); + context.PopulateFromIncomingCommandBuffer(input, *process); CHECK(context.GetStaticBuffer(0) == *buffer); @@ -163,7 +170,7 @@ TEST_CASE("HLERequestContext::PopulateFromIncomingCommandBuffer", "[core][kernel target_address, }; - context.PopulateFromIncomingCommandBuffer(input, *process, handle_table); + context.PopulateFromIncomingCommandBuffer(input, *process); std::vector other_buffer(buffer->size()); context.GetMappedBuffer(0).Read(other_buffer.data(), 0, buffer->size()); @@ -190,13 +197,13 @@ TEST_CASE("HLERequestContext::PopulateFromIncomingCommandBuffer", "[core][kernel buffer_mapped->size(), MemoryState::Private); REQUIRE(result.Code() == RESULT_SUCCESS); - auto a = MakeObject(); + auto a = MakeObject(kernel); const u32_le input[]{ IPC::MakeHeader(0, 2, 8), 0x12345678, 0xABCDEF00, IPC::MoveHandleDesc(1), - handle_table.Create(a).Unwrap(), + process->handle_table.Create(a).Unwrap(), IPC::CallingPidDesc(), 0, IPC::StaticBufferDesc(buffer_static->size(), 0), @@ -205,7 +212,7 @@ TEST_CASE("HLERequestContext::PopulateFromIncomingCommandBuffer", "[core][kernel target_address_mapped, }; - context.PopulateFromIncomingCommandBuffer(input, *process, handle_table); + context.PopulateFromIncomingCommandBuffer(input, *process); auto* output = context.CommandBuffer(); CHECK(output[1] == 0x12345678); @@ -225,18 +232,20 @@ TEST_CASE("HLERequestContext::PopulateFromIncomingCommandBuffer", "[core][kernel } TEST_CASE("HLERequestContext::WriteToOutgoingCommandBuffer", "[core][kernel]") { - auto session = std::get>(ServerSession::CreateSessionPair()); + // HACK: see comments of member timing + Core::System::GetInstance().timing = std::make_unique(); + Kernel::KernelSystem kernel(0); + auto session = std::get>(kernel.CreateSessionPair()); HLERequestContext context(std::move(session)); - auto process = Process::Create(CodeSet::Create("", 0)); - HandleTable handle_table; + auto process = kernel.CreateProcess(kernel.CreateCodeSet("", 0)); auto* input = context.CommandBuffer(); u32_le output[IPC::COMMAND_BUFFER_LENGTH]; SECTION("works with empty cmdbuf") { input[0] = IPC::MakeHeader(0x1234, 0, 0); - context.WriteToOutgoingCommandBuffer(output, *process, handle_table); + context.WriteToOutgoingCommandBuffer(output, *process); REQUIRE(output[0] == 0x12340000); } @@ -247,7 +256,7 @@ TEST_CASE("HLERequestContext::WriteToOutgoingCommandBuffer", "[core][kernel]") { input[2] = 0x21122112; input[3] = 0xAABBCCDD; - context.WriteToOutgoingCommandBuffer(output, *process, handle_table); + context.WriteToOutgoingCommandBuffer(output, *process); REQUIRE(output[1] == 0x12345678); REQUIRE(output[2] == 0x21122112); @@ -255,18 +264,18 @@ TEST_CASE("HLERequestContext::WriteToOutgoingCommandBuffer", "[core][kernel]") { } SECTION("translates move/copy handles") { - auto a = MakeObject(); - auto b = MakeObject(); + auto a = MakeObject(kernel); + auto b = MakeObject(kernel); input[0] = IPC::MakeHeader(0, 0, 4); input[1] = IPC::MoveHandleDesc(1); input[2] = context.AddOutgoingHandle(a); input[3] = IPC::CopyHandleDesc(1); input[4] = context.AddOutgoingHandle(b); - context.WriteToOutgoingCommandBuffer(output, *process, handle_table); + context.WriteToOutgoingCommandBuffer(output, *process); - REQUIRE(handle_table.GetGeneric(output[2]) == a); - REQUIRE(handle_table.GetGeneric(output[4]) == b); + REQUIRE(process->handle_table.GetGeneric(output[2]) == a); + REQUIRE(process->handle_table.GetGeneric(output[4]) == b); } SECTION("translates null handles") { @@ -274,16 +283,16 @@ TEST_CASE("HLERequestContext::WriteToOutgoingCommandBuffer", "[core][kernel]") { input[1] = IPC::MoveHandleDesc(1); input[2] = context.AddOutgoingHandle(nullptr); - auto result = context.WriteToOutgoingCommandBuffer(output, *process, handle_table); + auto result = context.WriteToOutgoingCommandBuffer(output, *process); REQUIRE(result == RESULT_SUCCESS); REQUIRE(output[2] == 0); } SECTION("translates multi-handle descriptors") { - auto a = MakeObject(); - auto b = MakeObject(); - auto c = MakeObject(); + auto a = MakeObject(kernel); + auto b = MakeObject(kernel); + auto c = MakeObject(kernel); input[0] = IPC::MakeHeader(0, 0, 5); input[1] = IPC::MoveHandleDesc(2); input[2] = context.AddOutgoingHandle(a); @@ -291,11 +300,11 @@ TEST_CASE("HLERequestContext::WriteToOutgoingCommandBuffer", "[core][kernel]") { input[4] = IPC::CopyHandleDesc(1); input[5] = context.AddOutgoingHandle(c); - context.WriteToOutgoingCommandBuffer(output, *process, handle_table); + context.WriteToOutgoingCommandBuffer(output, *process); - REQUIRE(handle_table.GetGeneric(output[2]) == a); - REQUIRE(handle_table.GetGeneric(output[3]) == b); - REQUIRE(handle_table.GetGeneric(output[5]) == c); + REQUIRE(process->handle_table.GetGeneric(output[2]) == a); + REQUIRE(process->handle_table.GetGeneric(output[3]) == b); + REQUIRE(process->handle_table.GetGeneric(output[5]) == c); } SECTION("translates StaticBuffer descriptors") { @@ -322,7 +331,7 @@ TEST_CASE("HLERequestContext::WriteToOutgoingCommandBuffer", "[core][kernel]") { IPC::StaticBufferDesc(output_buffer->size(), 0); output_cmdbuff[IPC::COMMAND_BUFFER_LENGTH + 1] = target_address; - context.WriteToOutgoingCommandBuffer(output_cmdbuff.data(), *process, handle_table); + context.WriteToOutgoingCommandBuffer(output_cmdbuff.data(), *process); CHECK(*output_buffer == input_buffer); REQUIRE(process->vm_manager.UnmapRange(target_address, output_buffer->size()) == @@ -345,7 +354,7 @@ TEST_CASE("HLERequestContext::WriteToOutgoingCommandBuffer", "[core][kernel]") { target_address, }; - context.PopulateFromIncomingCommandBuffer(input_cmdbuff, *process, handle_table); + context.PopulateFromIncomingCommandBuffer(input_cmdbuff, *process); context.GetMappedBuffer(0).Write(input_buffer.data(), 0, input_buffer.size()); @@ -353,7 +362,7 @@ TEST_CASE("HLERequestContext::WriteToOutgoingCommandBuffer", "[core][kernel]") { input[1] = IPC::MappedBufferDesc(output_buffer->size(), IPC::W); input[2] = 0; - context.WriteToOutgoingCommandBuffer(output, *process, handle_table); + context.WriteToOutgoingCommandBuffer(output, *process); CHECK(output[1] == IPC::MappedBufferDesc(output_buffer->size(), IPC::W)); CHECK(output[2] == target_address); diff --git a/src/tests/core/memory/memory.cpp b/src/tests/core/memory/memory.cpp index c69340865..196cd78ef 100644 --- a/src/tests/core/memory/memory.cpp +++ b/src/tests/core/memory/memory.cpp @@ -3,14 +3,19 @@ // Refer to the license.txt file included. #include +#include "core/core.h" +#include "core/core_timing.h" #include "core/hle/kernel/memory.h" #include "core/hle/kernel/process.h" -#include "core/hle/shared_page.h" +#include "core/hle/kernel/shared_page.h" #include "core/memory.h" TEST_CASE("Memory::IsValidVirtualAddress", "[core][memory]") { + // HACK: see comments of member timing + Core::System::GetInstance().timing = std::make_unique(); + Kernel::KernelSystem kernel(0); SECTION("these regions should not be mapped on an empty process") { - auto process = Kernel::Process::Create(Kernel::CodeSet::Create("", 0)); + auto process = kernel.CreateProcess(kernel.CreateCodeSet("", 0)); CHECK(Memory::IsValidVirtualAddress(*process, Memory::PROCESS_IMAGE_VADDR) == false); CHECK(Memory::IsValidVirtualAddress(*process, Memory::HEAP_VADDR) == false); CHECK(Memory::IsValidVirtualAddress(*process, Memory::LINEAR_HEAP_VADDR) == false); @@ -21,14 +26,14 @@ TEST_CASE("Memory::IsValidVirtualAddress", "[core][memory]") { } SECTION("CONFIG_MEMORY_VADDR and SHARED_PAGE_VADDR should be valid after mapping them") { - auto process = Kernel::Process::Create(Kernel::CodeSet::Create("", 0)); - Kernel::MapSharedPages(process->vm_manager); + auto process = kernel.CreateProcess(kernel.CreateCodeSet("", 0)); + kernel.MapSharedPages(process->vm_manager); CHECK(Memory::IsValidVirtualAddress(*process, Memory::CONFIG_MEMORY_VADDR) == true); CHECK(Memory::IsValidVirtualAddress(*process, Memory::SHARED_PAGE_VADDR) == true); } SECTION("special regions should be valid after mapping them") { - auto process = Kernel::Process::Create(Kernel::CodeSet::Create("", 0)); + auto process = kernel.CreateProcess(kernel.CreateCodeSet("", 0)); SECTION("VRAM") { Kernel::HandleSpecialMapping(process->vm_manager, {Memory::VRAM_VADDR, Memory::VRAM_SIZE, false, false}); @@ -43,8 +48,8 @@ TEST_CASE("Memory::IsValidVirtualAddress", "[core][memory]") { } SECTION("Unmapping a VAddr should make it invalid") { - auto process = Kernel::Process::Create(Kernel::CodeSet::Create("", 0)); - Kernel::MapSharedPages(process->vm_manager); + auto process = kernel.CreateProcess(kernel.CreateCodeSet("", 0)); + kernel.MapSharedPages(process->vm_manager); process->vm_manager.UnmapRange(Memory::CONFIG_MEMORY_VADDR, Memory::CONFIG_MEMORY_SIZE); CHECK(Memory::IsValidVirtualAddress(*process, Memory::CONFIG_MEMORY_VADDR) == false); } diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index f83d8d91f..b3c83ca78 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp @@ -146,7 +146,8 @@ void RendererOpenGL::SwapBuffers() { render_window.PollEvents(); render_window.SwapBuffers(); - Core::System::GetInstance().frame_limiter.DoFrameLimiting(CoreTiming::GetGlobalTimeUs()); + Core::System::GetInstance().frame_limiter.DoFrameLimiting( + Core::System::GetInstance().CoreTiming().GetGlobalTimeUs()); Core::System::GetInstance().perf_stats.BeginSystemFrame(); prev_state.Apply(); diff --git a/src/web_service/CMakeLists.txt b/src/web_service/CMakeLists.txt index 993ba9ac6..71bf8977d 100644 --- a/src/web_service/CMakeLists.txt +++ b/src/web_service/CMakeLists.txt @@ -12,7 +12,7 @@ add_library(web_service STATIC create_target_directory_groups(web_service) get_directory_property(OPENSSL_LIBS - DIRECTORY ${CMAKE_SOURCE_DIR}/externals/libressl + DIRECTORY ${PROJECT_SOURCE_DIR}/externals/libressl DEFINITION OPENSSL_LIBS) target_compile_definitions(web_service PRIVATE -DCPPHTTPLIB_OPENSSL_SUPPORT) target_link_libraries(web_service PRIVATE common json-headers ${OPENSSL_LIBS} httplib lurlparser) diff --git a/src/web_service/telemetry_json.cpp b/src/web_service/telemetry_json.cpp index 033ea1ea4..9156ce802 100644 --- a/src/web_service/telemetry_json.cpp +++ b/src/web_service/telemetry_json.cpp @@ -2,98 +2,127 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include -#include "common/assert.h" +#include #include "common/detached_tasks.h" +#include "common/web_result.h" #include "web_service/telemetry_json.h" #include "web_service/web_backend.h" namespace WebService { -TelemetryJson::TelemetryJson(const std::string& host, const std::string& username, - const std::string& token) - : host(std::move(host)), username(std::move(username)), token(std::move(token)) {} +struct TelemetryJson::Impl { + Impl(std::string host, std::string username, std::string token) + : host{std::move(host)}, username{std::move(username)}, token{std::move(token)} {} + + nlohmann::json& TopSection() { + return sections[static_cast(Telemetry::FieldType::None)]; + } + + const nlohmann::json& TopSection() const { + return sections[static_cast(Telemetry::FieldType::None)]; + } + + template + void Serialize(Telemetry::FieldType type, const std::string& name, T value) { + sections[static_cast(type)][name] = value; + } + + void SerializeSection(Telemetry::FieldType type, const std::string& name) { + TopSection()[name] = sections[static_cast(type)]; + } + + nlohmann::json output; + std::array sections; + std::string host; + std::string username; + std::string token; +}; + +TelemetryJson::TelemetryJson(std::string host, std::string username, std::string token) + : impl{std::make_unique(std::move(host), std::move(username), std::move(token))} {} TelemetryJson::~TelemetryJson() = default; -template -void TelemetryJson::Serialize(Telemetry::FieldType type, const std::string& name, T value) { - sections[static_cast(type)][name] = value; -} - -void TelemetryJson::SerializeSection(Telemetry::FieldType type, const std::string& name) { - TopSection()[name] = sections[static_cast(type)]; -} - void TelemetryJson::Visit(const Telemetry::Field& field) { - Serialize(field.GetType(), field.GetName(), field.GetValue()); + impl->Serialize(field.GetType(), field.GetName(), field.GetValue()); } void TelemetryJson::Visit(const Telemetry::Field& field) { - Serialize(field.GetType(), field.GetName(), field.GetValue()); + impl->Serialize(field.GetType(), field.GetName(), field.GetValue()); } void TelemetryJson::Visit(const Telemetry::Field& field) { - Serialize(field.GetType(), field.GetName(), field.GetValue()); + impl->Serialize(field.GetType(), field.GetName(), field.GetValue()); } void TelemetryJson::Visit(const Telemetry::Field& field) { - Serialize(field.GetType(), field.GetName(), field.GetValue()); + impl->Serialize(field.GetType(), field.GetName(), field.GetValue()); } void TelemetryJson::Visit(const Telemetry::Field& field) { - Serialize(field.GetType(), field.GetName(), field.GetValue()); + impl->Serialize(field.GetType(), field.GetName(), field.GetValue()); } void TelemetryJson::Visit(const Telemetry::Field& field) { - Serialize(field.GetType(), field.GetName(), field.GetValue()); + impl->Serialize(field.GetType(), field.GetName(), field.GetValue()); } void TelemetryJson::Visit(const Telemetry::Field& field) { - Serialize(field.GetType(), field.GetName(), field.GetValue()); + impl->Serialize(field.GetType(), field.GetName(), field.GetValue()); } void TelemetryJson::Visit(const Telemetry::Field& field) { - Serialize(field.GetType(), field.GetName(), field.GetValue()); + impl->Serialize(field.GetType(), field.GetName(), field.GetValue()); } void TelemetryJson::Visit(const Telemetry::Field& field) { - Serialize(field.GetType(), field.GetName(), field.GetValue()); + impl->Serialize(field.GetType(), field.GetName(), field.GetValue()); } void TelemetryJson::Visit(const Telemetry::Field& field) { - Serialize(field.GetType(), field.GetName(), field.GetValue()); + impl->Serialize(field.GetType(), field.GetName(), field.GetValue()); } void TelemetryJson::Visit(const Telemetry::Field& field) { - Serialize(field.GetType(), field.GetName(), field.GetValue()); + impl->Serialize(field.GetType(), field.GetName(), field.GetValue()); } void TelemetryJson::Visit(const Telemetry::Field& field) { - Serialize(field.GetType(), field.GetName(), field.GetValue()); + impl->Serialize(field.GetType(), field.GetName(), field.GetValue()); } void TelemetryJson::Visit(const Telemetry::Field& field) { - Serialize(field.GetType(), field.GetName(), std::string(field.GetValue())); + impl->Serialize(field.GetType(), field.GetName(), std::string(field.GetValue())); } void TelemetryJson::Visit(const Telemetry::Field& field) { - Serialize(field.GetType(), field.GetName(), field.GetValue().count()); + impl->Serialize(field.GetType(), field.GetName(), field.GetValue().count()); } void TelemetryJson::Complete() { - SerializeSection(Telemetry::FieldType::App, "App"); - SerializeSection(Telemetry::FieldType::Session, "Session"); - SerializeSection(Telemetry::FieldType::Performance, "Performance"); - SerializeSection(Telemetry::FieldType::UserFeedback, "UserFeedback"); - SerializeSection(Telemetry::FieldType::UserConfig, "UserConfig"); - SerializeSection(Telemetry::FieldType::UserSystem, "UserSystem"); + impl->SerializeSection(Telemetry::FieldType::App, "App"); + impl->SerializeSection(Telemetry::FieldType::Session, "Session"); + impl->SerializeSection(Telemetry::FieldType::Performance, "Performance"); + impl->SerializeSection(Telemetry::FieldType::UserConfig, "UserConfig"); + impl->SerializeSection(Telemetry::FieldType::UserSystem, "UserSystem"); - auto content = TopSection().dump(); + auto content = impl->TopSection().dump(); // Send the telemetry async but don't handle the errors since they were written to the log - Common::DetachedTasks::AddTask( - [host{this->host}, username{this->username}, token{this->token}, content]() { - Client{host, username, token}.PostJson("/telemetry", content, true); - }); + Common::DetachedTasks::AddTask([host{impl->host}, content]() { + Client{host, "", ""}.PostJson("/telemetry", content, true); + }); +} + +bool TelemetryJson::SubmitTestcase() { + impl->SerializeSection(Telemetry::FieldType::App, "App"); + impl->SerializeSection(Telemetry::FieldType::Session, "Session"); + impl->SerializeSection(Telemetry::FieldType::UserFeedback, "UserFeedback"); + impl->SerializeSection(Telemetry::FieldType::UserSystem, "UserSystem"); + + auto content = impl->TopSection().dump(); + Client client(impl->host, impl->username, impl->token); + auto value = client.PostJson("/gamedb/testcase", content, false); + + return value.result_code == Common::WebResult::Code::Success; } } // namespace WebService diff --git a/src/web_service/telemetry_json.h b/src/web_service/telemetry_json.h index 42a15b6a4..5b6d3cfb5 100644 --- a/src/web_service/telemetry_json.h +++ b/src/web_service/telemetry_json.h @@ -4,9 +4,8 @@ #pragma once -#include +#include #include -#include #include "common/announce_multiplayer_room.h" #include "common/telemetry.h" @@ -18,8 +17,8 @@ namespace WebService { */ class TelemetryJson : public Telemetry::VisitorInterface { public: - TelemetryJson(const std::string& host, const std::string& username, const std::string& token); - ~TelemetryJson(); + TelemetryJson(std::string host, std::string username, std::string token); + ~TelemetryJson() override; void Visit(const Telemetry::Field& field) override; void Visit(const Telemetry::Field& field) override; @@ -37,22 +36,11 @@ public: void Visit(const Telemetry::Field& field) override; void Complete() override; + bool SubmitTestcase() override; private: - nlohmann::json& TopSection() { - return sections[static_cast(Telemetry::FieldType::None)]; - } - - template - void Serialize(Telemetry::FieldType type, const std::string& name, T value); - - void SerializeSection(Telemetry::FieldType type, const std::string& name); - - nlohmann::json output; - std::array sections; - std::string host; - std::string username; - std::string token; + struct Impl; + std::unique_ptr impl; }; } // namespace WebService diff --git a/src/web_service/verify_login.cpp b/src/web_service/verify_login.cpp index 124aa3863..ca4b43b93 100644 --- a/src/web_service/verify_login.cpp +++ b/src/web_service/verify_login.cpp @@ -3,6 +3,7 @@ // Refer to the license.txt file included. #include +#include "common/web_result.h" #include "web_service/verify_login.h" #include "web_service/web_backend.h" diff --git a/src/web_service/web_backend.cpp b/src/web_service/web_backend.cpp index 9b824e47d..84d6105d7 100644 --- a/src/web_service/web_backend.cpp +++ b/src/web_service/web_backend.cpp @@ -2,13 +2,15 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include #include +#include #include -#include #include #include -#include "common/announce_multiplayer_room.h" +#include "common/common_types.h" #include "common/logging/log.h" +#include "common/web_result.h" #include "web_service/web_backend.h" namespace WebService { @@ -20,101 +22,132 @@ constexpr int HTTPS_PORT = 443; constexpr std::size_t TIMEOUT_SECONDS = 30; -Client::JWTCache Client::jwt_cache{}; - -Client::Client(const std::string& host, const std::string& username, const std::string& token) - : host(host), username(username), token(token) { - std::lock_guard lock(jwt_cache.mutex); - if (username == jwt_cache.username && token == jwt_cache.token) { - jwt = jwt_cache.jwt; - } -} - -Client::~Client() = default; - -Common::WebResult Client::GenericJson(const std::string& method, const std::string& path, - const std::string& data, const std::string& jwt, - const std::string& username, const std::string& token) { - if (cli == nullptr) { - auto parsedUrl = LUrlParser::clParseURL::ParseURL(host); - int port; - if (parsedUrl.m_Scheme == "http") { - if (!parsedUrl.GetPort(&port)) { - port = HTTP_PORT; - } - cli = - std::make_unique(parsedUrl.m_Host.c_str(), port, TIMEOUT_SECONDS); - } else if (parsedUrl.m_Scheme == "https") { - if (!parsedUrl.GetPort(&port)) { - port = HTTPS_PORT; - } - cli = std::make_unique(parsedUrl.m_Host.c_str(), port, - TIMEOUT_SECONDS); - } else { - LOG_ERROR(WebService, "Bad URL scheme {}", parsedUrl.m_Scheme); - return Common::WebResult{Common::WebResult::Code::InvalidURL, "Bad URL scheme"}; +struct Client::Impl { + Impl(std::string host, std::string username, std::string token) + : host{std::move(host)}, username{std::move(username)}, token{std::move(token)} { + std::lock_guard lock(jwt_cache.mutex); + if (this->username == jwt_cache.username && this->token == jwt_cache.token) { + jwt = jwt_cache.jwt; } } - if (cli == nullptr) { - LOG_ERROR(WebService, "Invalid URL {}", host + path); - return Common::WebResult{Common::WebResult::Code::InvalidURL, "Invalid URL"}; + + /// A generic function handles POST, GET and DELETE request together + Common::WebResult GenericJson(const std::string& method, const std::string& path, + const std::string& data, bool allow_anonymous) { + if (jwt.empty()) { + UpdateJWT(); + } + + if (jwt.empty() && !allow_anonymous) { + LOG_ERROR(WebService, "Credentials must be provided for authenticated requests"); + return Common::WebResult{Common::WebResult::Code::CredentialsMissing, + "Credentials needed"}; + } + + auto result = GenericJson(method, path, data, jwt); + if (result.result_string == "401") { + // Try again with new JWT + UpdateJWT(); + result = GenericJson(method, path, data, jwt); + } + + return result; } - httplib::Headers params; - if (!jwt.empty()) { - params = { - {std::string("Authorization"), fmt::format("Bearer {}", jwt)}, - }; - } else if (!username.empty()) { - params = { - {std::string("x-username"), username}, - {std::string("x-token"), token}, + /** + * A generic function with explicit authentication method specified + * JWT is used if the jwt parameter is not empty + * username + token is used if jwt is empty but username and token are + * not empty anonymous if all of jwt, username and token are empty + */ + Common::WebResult GenericJson(const std::string& method, const std::string& path, + const std::string& data, const std::string& jwt = "", + const std::string& username = "", const std::string& token = "") { + if (cli == nullptr) { + auto parsedUrl = LUrlParser::clParseURL::ParseURL(host); + int port; + if (parsedUrl.m_Scheme == "http") { + if (!parsedUrl.GetPort(&port)) { + port = HTTP_PORT; + } + cli = std::make_unique(parsedUrl.m_Host.c_str(), port, + TIMEOUT_SECONDS); + } else if (parsedUrl.m_Scheme == "https") { + if (!parsedUrl.GetPort(&port)) { + port = HTTPS_PORT; + } + cli = std::make_unique(parsedUrl.m_Host.c_str(), port, + TIMEOUT_SECONDS); + } else { + LOG_ERROR(WebService, "Bad URL scheme {}", parsedUrl.m_Scheme); + return Common::WebResult{Common::WebResult::Code::InvalidURL, "Bad URL scheme"}; + } + } + if (cli == nullptr) { + LOG_ERROR(WebService, "Invalid URL {}", host + path); + return Common::WebResult{Common::WebResult::Code::InvalidURL, "Invalid URL"}; + } + + httplib::Headers params; + if (!jwt.empty()) { + params = { + {std::string("Authorization"), fmt::format("Bearer {}", jwt)}, + }; + } else if (!username.empty()) { + params = { + {std::string("x-username"), username}, + {std::string("x-token"), token}, + }; + } + + params.emplace(std::string("api-version"), + std::string(API_VERSION.begin(), API_VERSION.end())); + if (method != "GET") { + params.emplace(std::string("Content-Type"), std::string("application/json")); }; + + httplib::Request request; + request.method = method; + request.path = path; + request.headers = params; + request.body = data; + + httplib::Response response; + + if (!cli->send(request, response)) { + LOG_ERROR(WebService, "{} to {} returned null", method, host + path); + return Common::WebResult{Common::WebResult::Code::LibError, "Null response"}; + } + + if (response.status >= 400) { + LOG_ERROR(WebService, "{} to {} returned error status code: {}", method, host + path, + response.status); + return Common::WebResult{Common::WebResult::Code::HttpError, + std::to_string(response.status)}; + } + + auto content_type = response.headers.find("content-type"); + + if (content_type == response.headers.end()) { + LOG_ERROR(WebService, "{} to {} returned no content", method, host + path); + return Common::WebResult{Common::WebResult::Code::WrongContent, ""}; + } + + if (content_type->second.find("application/json") == std::string::npos && + content_type->second.find("text/html; charset=utf-8") == std::string::npos) { + LOG_ERROR(WebService, "{} to {} returned wrong content: {}", method, host + path, + content_type->second); + return Common::WebResult{Common::WebResult::Code::WrongContent, "Wrong content"}; + } + return Common::WebResult{Common::WebResult::Code::Success, "", response.body}; } - params.emplace(std::string("api-version"), std::string(API_VERSION.begin(), API_VERSION.end())); - if (method != "GET") { - params.emplace(std::string("Content-Type"), std::string("application/json")); - }; + // Retrieve a new JWT from given username and token + void UpdateJWT() { + if (username.empty() || token.empty()) { + return; + } - httplib::Request request; - request.method = method; - request.path = path; - request.headers = params; - request.body = data; - - httplib::Response response; - - if (!cli->send(request, response)) { - LOG_ERROR(WebService, "{} to {} returned null", method, host + path); - return Common::WebResult{Common::WebResult::Code::LibError, "Null response"}; - } - - if (response.status >= 400) { - LOG_ERROR(WebService, "{} to {} returned error status code: {}", method, host + path, - response.status); - return Common::WebResult{Common::WebResult::Code::HttpError, - std::to_string(response.status)}; - } - - auto content_type = response.headers.find("content-type"); - - if (content_type == response.headers.end()) { - LOG_ERROR(WebService, "{} to {} returned no content", method, host + path); - return Common::WebResult{Common::WebResult::Code::WrongContent, ""}; - } - - if (content_type->second.find("application/json") == std::string::npos && - content_type->second.find("text/html; charset=utf-8") == std::string::npos) { - LOG_ERROR(WebService, "{} to {} returned wrong content: {}", method, host + path, - content_type->second); - return Common::WebResult{Common::WebResult::Code::WrongContent, "Wrong content"}; - } - return Common::WebResult{Common::WebResult::Code::Success, "", response.body}; -} - -void Client::UpdateJWT() { - if (!username.empty() && !token.empty()) { auto result = GenericJson("POST", "/jwt/internal", "", "", username, token); if (result.result_code != Common::WebResult::Code::Success) { LOG_ERROR(WebService, "UpdateJWT failed"); @@ -125,27 +158,39 @@ void Client::UpdateJWT() { jwt_cache.jwt = jwt = result.returned_data; } } + + std::string host; + std::string username; + std::string token; + std::string jwt; + std::unique_ptr cli; + + struct JWTCache { + std::mutex mutex; + std::string username; + std::string token; + std::string jwt; + }; + static inline JWTCache jwt_cache; +}; + +Client::Client(std::string host, std::string username, std::string token) + : impl{std::make_unique(std::move(host), std::move(username), std::move(token))} {} + +Client::~Client() = default; + +Common::WebResult Client::PostJson(const std::string& path, const std::string& data, + bool allow_anonymous) { + return impl->GenericJson("POST", path, data, allow_anonymous); } -Common::WebResult Client::GenericJson(const std::string& method, const std::string& path, - const std::string& data, bool allow_anonymous) { - if (jwt.empty()) { - UpdateJWT(); - } +Common::WebResult Client::GetJson(const std::string& path, bool allow_anonymous) { + return impl->GenericJson("GET", path, "", allow_anonymous); +} - if (jwt.empty() && !allow_anonymous) { - LOG_ERROR(WebService, "Credentials must be provided for authenticated requests"); - return Common::WebResult{Common::WebResult::Code::CredentialsMissing, "Credentials needed"}; - } - - auto result = GenericJson(method, path, data, jwt); - if (result.result_string == "401") { - // Try again with new JWT - UpdateJWT(); - result = GenericJson(method, path, data, jwt); - } - - return result; +Common::WebResult Client::DeleteJson(const std::string& path, const std::string& data, + bool allow_anonymous) { + return impl->GenericJson("DELETE", path, data, allow_anonymous); } } // namespace WebService diff --git a/src/web_service/web_backend.h b/src/web_service/web_backend.h index 6cdf35b48..c637e09df 100644 --- a/src/web_service/web_backend.h +++ b/src/web_service/web_backend.h @@ -4,22 +4,18 @@ #pragma once -#include -#include +#include #include -#include -#include "common/announce_multiplayer_room.h" -#include "common/common_types.h" -namespace httplib { -class Client; +namespace Common { +struct WebResult; } namespace WebService { class Client { public: - Client(const std::string& host, const std::string& username, const std::string& token); + Client(std::string host, std::string username, std::string token); ~Client(); /** @@ -30,9 +26,7 @@ public: * @return the result of the request. */ Common::WebResult PostJson(const std::string& path, const std::string& data, - bool allow_anonymous) { - return GenericJson("POST", path, data, allow_anonymous); - } + bool allow_anonymous); /** * Gets JSON from the specified path. @@ -40,9 +34,7 @@ public: * @param allow_anonymous If true, allow anonymous unauthenticated requests. * @return the result of the request. */ - Common::WebResult GetJson(const std::string& path, bool allow_anonymous) { - return GenericJson("GET", path, "", allow_anonymous); - } + Common::WebResult GetJson(const std::string& path, bool allow_anonymous); /** * Deletes JSON to the specified path. @@ -52,41 +44,11 @@ public: * @return the result of the request. */ Common::WebResult DeleteJson(const std::string& path, const std::string& data, - bool allow_anonymous) { - return GenericJson("DELETE", path, data, allow_anonymous); - } + bool allow_anonymous); private: - /// A generic function handles POST, GET and DELETE request together - Common::WebResult GenericJson(const std::string& method, const std::string& path, - const std::string& data, bool allow_anonymous); - - /** - * A generic function with explicit authentication method specified - * JWT is used if the jwt parameter is not empty - * username + token is used if jwt is empty but username and token are not empty - * anonymous if all of jwt, username and token are empty - */ - Common::WebResult GenericJson(const std::string& method, const std::string& path, - const std::string& data, const std::string& jwt = "", - const std::string& username = "", const std::string& token = ""); - - // Retrieve a new JWT from given username and token - void UpdateJWT(); - - std::string host; - std::string username; - std::string token; - std::string jwt; - std::unique_ptr cli; - - struct JWTCache { - std::mutex mutex; - std::string username; - std::string token; - std::string jwt; - }; - static JWTCache jwt_cache; + struct Impl; + std::unique_ptr impl; }; } // namespace WebService