Compare commits
1 Commits
android-11
...
android-94
Author | SHA1 | Date | |
---|---|---|---|
9ffb91e8f4 |
@ -5,6 +5,6 @@
|
|||||||
|
|
||||||
GITDATE="`git show -s --date=short --format='%ad' | sed 's/-//g'`"
|
GITDATE="`git show -s --date=short --format='%ad' | sed 's/-//g'`"
|
||||||
GITREV="`git show -s --format='%h'`"
|
GITREV="`git show -s --format='%h'`"
|
||||||
ARTIFACTS_DIR="$PWD/artifacts"
|
ARTIFACTS_DIR="artifacts"
|
||||||
|
|
||||||
mkdir -p "${ARTIFACTS_DIR}/"
|
mkdir -p "${ARTIFACTS_DIR}/"
|
||||||
|
@ -11,7 +11,7 @@ ccache -s
|
|||||||
mkdir build || true && cd build
|
mkdir build || true && cd build
|
||||||
cmake .. \
|
cmake .. \
|
||||||
-DBoost_USE_STATIC_LIBS=ON \
|
-DBoost_USE_STATIC_LIBS=ON \
|
||||||
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
|
-DCMAKE_BUILD_TYPE=Release \
|
||||||
-DCMAKE_CXX_FLAGS="-march=x86-64-v2" \
|
-DCMAKE_CXX_FLAGS="-march=x86-64-v2" \
|
||||||
-DCMAKE_CXX_COMPILER=/usr/lib/ccache/g++ \
|
-DCMAKE_CXX_COMPILER=/usr/lib/ccache/g++ \
|
||||||
-DCMAKE_C_COMPILER=/usr/lib/ccache/gcc \
|
-DCMAKE_C_COMPILER=/usr/lib/ccache/gcc \
|
||||||
@ -31,19 +31,6 @@ ccache -s
|
|||||||
|
|
||||||
ctest -VV -C Release
|
ctest -VV -C Release
|
||||||
|
|
||||||
# Separate debug symbols from specified executables
|
|
||||||
for EXE in yuzu; do
|
|
||||||
EXE_PATH="bin/$EXE"
|
|
||||||
# Copy debug symbols out
|
|
||||||
objcopy --only-keep-debug $EXE_PATH $EXE_PATH.debug
|
|
||||||
# Add debug link and strip debug symbols
|
|
||||||
objcopy -g --add-gnu-debuglink=$EXE_PATH.debug $EXE_PATH $EXE_PATH.out
|
|
||||||
# Overwrite original with stripped copy
|
|
||||||
mv $EXE_PATH.out $EXE_PATH
|
|
||||||
done
|
|
||||||
# Strip debug symbols from all executables
|
|
||||||
find bin/ -type f -not -regex '.*.debug' -exec strip -g {} ';'
|
|
||||||
|
|
||||||
DESTDIR="$PWD/AppDir" ninja install
|
DESTDIR="$PWD/AppDir" ninja install
|
||||||
rm -vf AppDir/usr/bin/yuzu-cmd AppDir/usr/bin/yuzu-tester
|
rm -vf AppDir/usr/bin/yuzu-cmd AppDir/usr/bin/yuzu-tester
|
||||||
|
|
||||||
|
@ -59,9 +59,4 @@ if [ "${RELEASE_NAME}" = "mainline" ] || [ "${RELEASE_NAME}" = "early-access" ];
|
|||||||
cp "build/${APPIMAGE_NAME}" "${DIR_NAME}/yuzu-${RELEASE_NAME}.AppImage"
|
cp "build/${APPIMAGE_NAME}" "${DIR_NAME}/yuzu-${RELEASE_NAME}.AppImage"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Copy debug symbols to artifacts
|
|
||||||
cd build/bin
|
|
||||||
tar $COMPRESSION_FLAGS "${ARTIFACTS_DIR}/${REV_NAME}-debug.tar.xz" *.debug
|
|
||||||
cd -
|
|
||||||
|
|
||||||
. .ci/scripts/common/post-upload.sh
|
. .ci/scripts/common/post-upload.sh
|
||||||
|
@ -3,4 +3,4 @@
|
|||||||
|
|
||||||
[codespell]
|
[codespell]
|
||||||
skip = ./.git,./build,./dist,./Doxyfile,./externals,./LICENSES,./src/android/app/src/main/res
|
skip = ./.git,./build,./dist,./Doxyfile,./externals,./LICENSES,./src/android/app/src/main/res
|
||||||
ignore-words-list = aci,allright,ba,canonicalizations,deques,froms,hda,inout,lod,masia,nam,nax,nd,optin,pullrequests,pullrequest,te,transfered,unstall,uscaled,vas,zink
|
ignore-words-list = aci,allright,ba,canonicalizations,deques,froms,hda,inout,lod,masia,nam,nax,nd,optin,pullrequests,pullrequest,te,transfered,unstall,uscaled,zink
|
||||||
|
@ -11,6 +11,7 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/externals/cmake-modul
|
|||||||
include(DownloadExternals)
|
include(DownloadExternals)
|
||||||
include(CMakeDependentOption)
|
include(CMakeDependentOption)
|
||||||
include(CTest)
|
include(CTest)
|
||||||
|
include(FetchContent)
|
||||||
|
|
||||||
# Set bundled sdl2/qt as dependent options.
|
# Set bundled sdl2/qt as dependent options.
|
||||||
# OFF by default, but if ENABLE_SDL2 and MSVC are true then ON
|
# OFF by default, but if ENABLE_SDL2 and MSVC are true then ON
|
||||||
@ -98,8 +99,47 @@ if (ANDROID AND YUZU_DOWNLOAD_ANDROID_VVL)
|
|||||||
DESTINATION "${vvl_lib_path}")
|
DESTINATION "${vvl_lib_path}")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
# On Android, fetch and compile libcxx before doing anything else
|
||||||
if (ANDROID)
|
if (ANDROID)
|
||||||
set(CMAKE_SKIP_INSTALL_RULES ON)
|
set(CMAKE_SKIP_INSTALL_RULES ON)
|
||||||
|
set(LLVM_VERSION "15.0.6")
|
||||||
|
|
||||||
|
# Note: even though libcxx and libcxxabi have separate releases on the project page,
|
||||||
|
# the separated releases cannot be compiled. Only in-tree builds work. Therefore we
|
||||||
|
# must fetch the source release for the entire llvm tree.
|
||||||
|
FetchContent_Declare(llvm
|
||||||
|
URL "https://github.com/llvm/llvm-project/releases/download/llvmorg-${LLVM_VERSION}/llvm-project-${LLVM_VERSION}.src.tar.xz"
|
||||||
|
URL_HASH SHA256=9d53ad04dc60cb7b30e810faf64c5ab8157dadef46c8766f67f286238256ff92
|
||||||
|
TLS_VERIFY TRUE
|
||||||
|
)
|
||||||
|
FetchContent_MakeAvailable(llvm)
|
||||||
|
|
||||||
|
# libcxx has support for most of the range library, but it's gated behind a flag:
|
||||||
|
add_compile_definitions(_LIBCPP_ENABLE_EXPERIMENTAL)
|
||||||
|
|
||||||
|
# Disable standard header inclusion
|
||||||
|
set(ANDROID_STL "none")
|
||||||
|
|
||||||
|
# libcxxabi
|
||||||
|
set(LIBCXXABI_INCLUDE_TESTS OFF)
|
||||||
|
set(LIBCXXABI_ENABLE_SHARED FALSE)
|
||||||
|
set(LIBCXXABI_ENABLE_STATIC TRUE)
|
||||||
|
set(LIBCXXABI_LIBCXX_INCLUDES "${LIBCXX_TARGET_INCLUDE_DIRECTORY}" CACHE STRING "" FORCE)
|
||||||
|
add_subdirectory("${llvm_SOURCE_DIR}/libcxxabi" "${llvm_BINARY_DIR}/libcxxabi")
|
||||||
|
link_libraries(cxxabi_static)
|
||||||
|
|
||||||
|
# libcxx
|
||||||
|
set(LIBCXX_ABI_NAMESPACE "__ndk1" CACHE STRING "" FORCE)
|
||||||
|
set(LIBCXX_CXX_ABI "libcxxabi")
|
||||||
|
set(LIBCXX_INCLUDE_TESTS OFF)
|
||||||
|
set(LIBCXX_INCLUDE_BENCHMARKS OFF)
|
||||||
|
set(LIBCXX_INCLUDE_DOCS OFF)
|
||||||
|
set(LIBCXX_ENABLE_SHARED FALSE)
|
||||||
|
set(LIBCXX_ENABLE_STATIC TRUE)
|
||||||
|
set(LIBCXX_ENABLE_ASSERTIONS FALSE)
|
||||||
|
add_subdirectory("${llvm_SOURCE_DIR}/libcxx" "${llvm_BINARY_DIR}/libcxx")
|
||||||
|
set_target_properties(cxx-headers PROPERTIES INTERFACE_COMPILE_OPTIONS "-isystem${CMAKE_BINARY_DIR}/${LIBCXX_INSTALL_INCLUDE_DIR}")
|
||||||
|
link_libraries(cxx_static cxx-headers)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (YUZU_USE_BUNDLED_VCPKG)
|
if (YUZU_USE_BUNDLED_VCPKG)
|
||||||
@ -289,7 +329,7 @@ find_package(Boost 1.79.0 REQUIRED context)
|
|||||||
find_package(enet 1.3 MODULE)
|
find_package(enet 1.3 MODULE)
|
||||||
find_package(fmt 9 REQUIRED)
|
find_package(fmt 9 REQUIRED)
|
||||||
find_package(inih 52 MODULE COMPONENTS INIReader)
|
find_package(inih 52 MODULE COMPONENTS INIReader)
|
||||||
find_package(LLVM 17.0.2 MODULE COMPONENTS Demangle)
|
find_package(LLVM 17 MODULE COMPONENTS Demangle)
|
||||||
find_package(lz4 REQUIRED)
|
find_package(lz4 REQUIRED)
|
||||||
find_package(nlohmann_json 3.8 REQUIRED)
|
find_package(nlohmann_json 3.8 REQUIRED)
|
||||||
find_package(Opus 1.3 MODULE)
|
find_package(Opus 1.3 MODULE)
|
||||||
@ -360,9 +400,6 @@ function(set_yuzu_qt_components)
|
|||||||
if (ENABLE_QT_TRANSLATION)
|
if (ENABLE_QT_TRANSLATION)
|
||||||
list(APPEND YUZU_QT_COMPONENTS2 LinguistTools)
|
list(APPEND YUZU_QT_COMPONENTS2 LinguistTools)
|
||||||
endif()
|
endif()
|
||||||
if (USE_DISCORD_PRESENCE)
|
|
||||||
list(APPEND YUZU_QT_COMPONENTS2 Network)
|
|
||||||
endif()
|
|
||||||
set(YUZU_QT_COMPONENTS ${YUZU_QT_COMPONENTS2} PARENT_SCOPE)
|
set(YUZU_QT_COMPONENTS ${YUZU_QT_COMPONENTS2} PARENT_SCOPE)
|
||||||
endfunction(set_yuzu_qt_components)
|
endfunction(set_yuzu_qt_components)
|
||||||
|
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
| Pull Request | Commit | Title | Author | Merged? |
|
| Pull Request | Commit | Title | Author | Merged? |
|
||||||
|----|----|----|----|----|
|
|----|----|----|----|----|
|
||||||
| [11827](https://github.com/yuzu-emu/yuzu//pull/11827) | [`689f346e9`](https://github.com/yuzu-emu/yuzu//pull/11827/files) | nvnflinger: fix reporting and freeing of preallocated buffers | [liamwhite](https://github.com/liamwhite/) | Yes |
|
|
||||||
|
|
||||||
|
|
||||||
End of merge log. You can find the original README.md below the break.
|
End of merge log. You can find the original README.md below the break.
|
||||||
|
4
dist/qt_themes/default/style.qss
vendored
4
dist/qt_themes/default/style.qss
vendored
@ -120,10 +120,6 @@ QWidget#connectedControllers {
|
|||||||
background: transparent;
|
background: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
QWidget#closeButtons {
|
|
||||||
background: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
QWidget#playersSupported,
|
QWidget#playersSupported,
|
||||||
QWidget#controllersSupported,
|
QWidget#controllersSupported,
|
||||||
QWidget#controllerSupported1,
|
QWidget#controllerSupported1,
|
||||||
|
4
dist/qt_themes/qdarkstyle/style.qss
vendored
4
dist/qt_themes/qdarkstyle/style.qss
vendored
@ -1380,10 +1380,6 @@ QWidget#connectedControllers {
|
|||||||
background: transparent;
|
background: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
QWidget#closeButtons {
|
|
||||||
background: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
QWidget#playersSupported,
|
QWidget#playersSupported,
|
||||||
QWidget#controllersSupported,
|
QWidget#controllersSupported,
|
||||||
QWidget#controllerSupported1,
|
QWidget#controllerSupported1,
|
||||||
|
@ -2305,10 +2305,6 @@ QWidget#connectedControllers {
|
|||||||
background: transparent;
|
background: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
QWidget#closeButtons {
|
|
||||||
background: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
QWidget#playersSupported,
|
QWidget#playersSupported,
|
||||||
QWidget#controllersSupported,
|
QWidget#controllersSupported,
|
||||||
QWidget#controllerSupported1,
|
QWidget#controllerSupported1,
|
||||||
|
2
externals/CMakeLists.txt
vendored
2
externals/CMakeLists.txt
vendored
@ -168,7 +168,7 @@ if (NOT TARGET LLVM::Demangle)
|
|||||||
add_library(LLVM::Demangle ALIAS demangle)
|
add_library(LLVM::Demangle ALIAS demangle)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
add_library(stb stb/stb_dxt.cpp stb/stb_image.cpp stb/stb_image_resize.cpp)
|
add_library(stb stb/stb_dxt.cpp)
|
||||||
target_include_directories(stb PUBLIC ./stb)
|
target_include_directories(stb PUBLIC ./stb)
|
||||||
|
|
||||||
add_library(bc_decoder bc_decoder/bc_decoder.cpp)
|
add_library(bc_decoder bc_decoder/bc_decoder.cpp)
|
||||||
|
2
externals/nx_tzdb/CMakeLists.txt
vendored
2
externals/nx_tzdb/CMakeLists.txt
vendored
@ -27,7 +27,7 @@ if (CMAKE_SYSTEM_NAME STREQUAL "Windows" OR ANDROID)
|
|||||||
set(CAN_BUILD_NX_TZDB false)
|
set(CAN_BUILD_NX_TZDB false)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
set(NX_TZDB_VERSION "221202")
|
set(NX_TZDB_VERSION "220816")
|
||||||
set(NX_TZDB_ARCHIVE "${CMAKE_CURRENT_BINARY_DIR}/${NX_TZDB_VERSION}.zip")
|
set(NX_TZDB_ARCHIVE "${CMAKE_CURRENT_BINARY_DIR}/${NX_TZDB_VERSION}.zip")
|
||||||
|
|
||||||
set(NX_TZDB_ROMFS_DIR "${CMAKE_CURRENT_BINARY_DIR}/nx_tzdb")
|
set(NX_TZDB_ROMFS_DIR "${CMAKE_CURRENT_BINARY_DIR}/nx_tzdb")
|
||||||
|
2
externals/nx_tzdb/tzdb_to_nx
vendored
2
externals/nx_tzdb/tzdb_to_nx
vendored
Submodule externals/nx_tzdb/tzdb_to_nx updated: 0d17dd066d...212afa2394
7529
externals/stb/stb_image.cpp
vendored
7529
externals/stb/stb_image.cpp
vendored
File diff suppressed because it is too large
Load Diff
772
externals/stb/stb_image.h
vendored
772
externals/stb/stb_image.h
vendored
@ -1,772 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: stb http://nothings.org/stb
|
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
/* stb_image - v2.28 - public domain image loader - http://nothings.org/stb
|
|
||||||
no warranty implied; use at your own risk
|
|
||||||
|
|
||||||
Do this:
|
|
||||||
#define STB_IMAGE_IMPLEMENTATION
|
|
||||||
before you include this file in *one* C or C++ file to create the implementation.
|
|
||||||
|
|
||||||
// i.e. it should look like this:
|
|
||||||
#include ...
|
|
||||||
#include ...
|
|
||||||
#include ...
|
|
||||||
#define STB_IMAGE_IMPLEMENTATION
|
|
||||||
#include "stb_image.h"
|
|
||||||
|
|
||||||
You can #define STBI_ASSERT(x) before the #include to avoid using assert.h.
|
|
||||||
And #define STBI_MALLOC, STBI_REALLOC, and STBI_FREE to avoid using malloc,realloc,free
|
|
||||||
|
|
||||||
|
|
||||||
QUICK NOTES:
|
|
||||||
Primarily of interest to game developers and other people who can
|
|
||||||
avoid problematic images and only need the trivial interface
|
|
||||||
|
|
||||||
JPEG baseline & progressive (12 bpc/arithmetic not supported, same as stock IJG lib)
|
|
||||||
PNG 1/2/4/8/16-bit-per-channel
|
|
||||||
|
|
||||||
TGA (not sure what subset, if a subset)
|
|
||||||
BMP non-1bpp, non-RLE
|
|
||||||
PSD (composited view only, no extra channels, 8/16 bit-per-channel)
|
|
||||||
|
|
||||||
GIF (*comp always reports as 4-channel)
|
|
||||||
HDR (radiance rgbE format)
|
|
||||||
PIC (Softimage PIC)
|
|
||||||
PNM (PPM and PGM binary only)
|
|
||||||
|
|
||||||
Animated GIF still needs a proper API, but here's one way to do it:
|
|
||||||
http://gist.github.com/urraka/685d9a6340b26b830d49
|
|
||||||
|
|
||||||
- decode from memory or through FILE (define STBI_NO_STDIO to remove code)
|
|
||||||
- decode from arbitrary I/O callbacks
|
|
||||||
- SIMD acceleration on x86/x64 (SSE2) and ARM (NEON)
|
|
||||||
|
|
||||||
Full documentation under "DOCUMENTATION" below.
|
|
||||||
|
|
||||||
|
|
||||||
LICENSE
|
|
||||||
|
|
||||||
See end of file for license information.
|
|
||||||
|
|
||||||
RECENT REVISION HISTORY:
|
|
||||||
|
|
||||||
2.28 (2023-01-29) many error fixes, security errors, just tons of stuff
|
|
||||||
2.27 (2021-07-11) document stbi_info better, 16-bit PNM support, bug fixes
|
|
||||||
2.26 (2020-07-13) many minor fixes
|
|
||||||
2.25 (2020-02-02) fix warnings
|
|
||||||
2.24 (2020-02-02) fix warnings; thread-local failure_reason and flip_vertically
|
|
||||||
2.23 (2019-08-11) fix clang static analysis warning
|
|
||||||
2.22 (2019-03-04) gif fixes, fix warnings
|
|
||||||
2.21 (2019-02-25) fix typo in comment
|
|
||||||
2.20 (2019-02-07) support utf8 filenames in Windows; fix warnings and platform ifdefs
|
|
||||||
2.19 (2018-02-11) fix warning
|
|
||||||
2.18 (2018-01-30) fix warnings
|
|
||||||
2.17 (2018-01-29) bugfix, 1-bit BMP, 16-bitness query, fix warnings
|
|
||||||
2.16 (2017-07-23) all functions have 16-bit variants; optimizations; bugfixes
|
|
||||||
2.15 (2017-03-18) fix png-1,2,4; all Imagenet JPGs; no runtime SSE detection on GCC
|
|
||||||
2.14 (2017-03-03) remove deprecated STBI_JPEG_OLD; fixes for Imagenet JPGs
|
|
||||||
2.13 (2016-12-04) experimental 16-bit API, only for PNG so far; fixes
|
|
||||||
2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes
|
|
||||||
2.11 (2016-04-02) 16-bit PNGS; enable SSE2 in non-gcc x64
|
|
||||||
RGB-format JPEG; remove white matting in PSD;
|
|
||||||
allocate large structures on the stack;
|
|
||||||
correct channel count for PNG & BMP
|
|
||||||
2.10 (2016-01-22) avoid warning introduced in 2.09
|
|
||||||
2.09 (2016-01-16) 16-bit TGA; comments in PNM files; STBI_REALLOC_SIZED
|
|
||||||
|
|
||||||
See end of file for full revision history.
|
|
||||||
|
|
||||||
|
|
||||||
============================ Contributors =========================
|
|
||||||
|
|
||||||
Image formats Extensions, features
|
|
||||||
Sean Barrett (jpeg, png, bmp) Jetro Lauha (stbi_info)
|
|
||||||
Nicolas Schulz (hdr, psd) Martin "SpartanJ" Golini (stbi_info)
|
|
||||||
Jonathan Dummer (tga) James "moose2000" Brown (iPhone PNG)
|
|
||||||
Jean-Marc Lienher (gif) Ben "Disch" Wenger (io callbacks)
|
|
||||||
Tom Seddon (pic) Omar Cornut (1/2/4-bit PNG)
|
|
||||||
Thatcher Ulrich (psd) Nicolas Guillemot (vertical flip)
|
|
||||||
Ken Miller (pgm, ppm) Richard Mitton (16-bit PSD)
|
|
||||||
github:urraka (animated gif) Junggon Kim (PNM comments)
|
|
||||||
Christopher Forseth (animated gif) Daniel Gibson (16-bit TGA)
|
|
||||||
socks-the-fox (16-bit PNG)
|
|
||||||
Jeremy Sawicki (handle all ImageNet JPGs)
|
|
||||||
Optimizations & bugfixes Mikhail Morozov (1-bit BMP)
|
|
||||||
Fabian "ryg" Giesen Anael Seghezzi (is-16-bit query)
|
|
||||||
Arseny Kapoulkine Simon Breuss (16-bit PNM)
|
|
||||||
John-Mark Allen
|
|
||||||
Carmelo J Fdez-Aguera
|
|
||||||
|
|
||||||
Bug & warning fixes
|
|
||||||
Marc LeBlanc David Woo Guillaume George Martins Mozeiko
|
|
||||||
Christpher Lloyd Jerry Jansson Joseph Thomson Blazej Dariusz Roszkowski
|
|
||||||
Phil Jordan Dave Moore Roy Eltham
|
|
||||||
Hayaki Saito Nathan Reed Won Chun
|
|
||||||
Luke Graham Johan Duparc Nick Verigakis the Horde3D community
|
|
||||||
Thomas Ruf Ronny Chevalier github:rlyeh
|
|
||||||
Janez Zemva John Bartholomew Michal Cichon github:romigrou
|
|
||||||
Jonathan Blow Ken Hamada Tero Hanninen github:svdijk
|
|
||||||
Eugene Golushkov Laurent Gomila Cort Stratton github:snagar
|
|
||||||
Aruelien Pocheville Sergio Gonzalez Thibault Reuille github:Zelex
|
|
||||||
Cass Everitt Ryamond Barbiero github:grim210
|
|
||||||
Paul Du Bois Engin Manap Aldo Culquicondor github:sammyhw
|
|
||||||
Philipp Wiesemann Dale Weiler Oriol Ferrer Mesia github:phprus
|
|
||||||
Josh Tobin Neil Bickford Matthew Gregan github:poppolopoppo
|
|
||||||
Julian Raschke Gregory Mullen Christian Floisand github:darealshinji
|
|
||||||
Baldur Karlsson Kevin Schmidt JR Smith github:Michaelangel007
|
|
||||||
Brad Weinberger Matvey Cherevko github:mosra
|
|
||||||
Luca Sas Alexander Veselov Zack Middleton [reserved]
|
|
||||||
Ryan C. Gordon [reserved] [reserved]
|
|
||||||
DO NOT ADD YOUR NAME HERE
|
|
||||||
|
|
||||||
Jacko Dirks
|
|
||||||
|
|
||||||
To add your name to the credits, pick a random blank space in the middle and fill it.
|
|
||||||
80% of merge conflicts on stb PRs are due to people adding their name at the end
|
|
||||||
of the credits.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef STBI_INCLUDE_STB_IMAGE_H
|
|
||||||
#define STBI_INCLUDE_STB_IMAGE_H
|
|
||||||
|
|
||||||
// DOCUMENTATION
|
|
||||||
//
|
|
||||||
// Limitations:
|
|
||||||
// - no 12-bit-per-channel JPEG
|
|
||||||
// - no JPEGs with arithmetic coding
|
|
||||||
// - GIF always returns *comp=4
|
|
||||||
//
|
|
||||||
// Basic usage (see HDR discussion below for HDR usage):
|
|
||||||
// int x,y,n;
|
|
||||||
// unsigned char *data = stbi_load(filename, &x, &y, &n, 0);
|
|
||||||
// // ... process data if not NULL ...
|
|
||||||
// // ... x = width, y = height, n = # 8-bit components per pixel ...
|
|
||||||
// // ... replace '0' with '1'..'4' to force that many components per pixel
|
|
||||||
// // ... but 'n' will always be the number that it would have been if you said 0
|
|
||||||
// stbi_image_free(data);
|
|
||||||
//
|
|
||||||
// Standard parameters:
|
|
||||||
// int *x -- outputs image width in pixels
|
|
||||||
// int *y -- outputs image height in pixels
|
|
||||||
// int *channels_in_file -- outputs # of image components in image file
|
|
||||||
// int desired_channels -- if non-zero, # of image components requested in result
|
|
||||||
//
|
|
||||||
// The return value from an image loader is an 'unsigned char *' which points
|
|
||||||
// to the pixel data, or NULL on an allocation failure or if the image is
|
|
||||||
// corrupt or invalid. The pixel data consists of *y scanlines of *x pixels,
|
|
||||||
// with each pixel consisting of N interleaved 8-bit components; the first
|
|
||||||
// pixel pointed to is top-left-most in the image. There is no padding between
|
|
||||||
// image scanlines or between pixels, regardless of format. The number of
|
|
||||||
// components N is 'desired_channels' if desired_channels is non-zero, or
|
|
||||||
// *channels_in_file otherwise. If desired_channels is non-zero,
|
|
||||||
// *channels_in_file has the number of components that _would_ have been
|
|
||||||
// output otherwise. E.g. if you set desired_channels to 4, you will always
|
|
||||||
// get RGBA output, but you can check *channels_in_file to see if it's trivially
|
|
||||||
// opaque because e.g. there were only 3 channels in the source image.
|
|
||||||
//
|
|
||||||
// An output image with N components has the following components interleaved
|
|
||||||
// in this order in each pixel:
|
|
||||||
//
|
|
||||||
// N=#comp components
|
|
||||||
// 1 grey
|
|
||||||
// 2 grey, alpha
|
|
||||||
// 3 red, green, blue
|
|
||||||
// 4 red, green, blue, alpha
|
|
||||||
//
|
|
||||||
// If image loading fails for any reason, the return value will be NULL,
|
|
||||||
// and *x, *y, *channels_in_file will be unchanged. The function
|
|
||||||
// stbi_failure_reason() can be queried for an extremely brief, end-user
|
|
||||||
// unfriendly explanation of why the load failed. Define STBI_NO_FAILURE_STRINGS
|
|
||||||
// to avoid compiling these strings at all, and STBI_FAILURE_USERMSG to get slightly
|
|
||||||
// more user-friendly ones.
|
|
||||||
//
|
|
||||||
// Paletted PNG, BMP, GIF, and PIC images are automatically depalettized.
|
|
||||||
//
|
|
||||||
// To query the width, height and component count of an image without having to
|
|
||||||
// decode the full file, you can use the stbi_info family of functions:
|
|
||||||
//
|
|
||||||
// int x,y,n,ok;
|
|
||||||
// ok = stbi_info(filename, &x, &y, &n);
|
|
||||||
// // returns ok=1 and sets x, y, n if image is a supported format,
|
|
||||||
// // 0 otherwise.
|
|
||||||
//
|
|
||||||
// Note that stb_image pervasively uses ints in its public API for sizes,
|
|
||||||
// including sizes of memory buffers. This is now part of the API and thus
|
|
||||||
// hard to change without causing breakage. As a result, the various image
|
|
||||||
// loaders all have certain limits on image size; these differ somewhat
|
|
||||||
// by format but generally boil down to either just under 2GB or just under
|
|
||||||
// 1GB. When the decoded image would be larger than this, stb_image decoding
|
|
||||||
// will fail.
|
|
||||||
//
|
|
||||||
// Additionally, stb_image will reject image files that have any of their
|
|
||||||
// dimensions set to a larger value than the configurable STBI_MAX_DIMENSIONS,
|
|
||||||
// which defaults to 2**24 = 16777216 pixels. Due to the above memory limit,
|
|
||||||
// the only way to have an image with such dimensions load correctly
|
|
||||||
// is for it to have a rather extreme aspect ratio. Either way, the
|
|
||||||
// assumption here is that such larger images are likely to be malformed
|
|
||||||
// or malicious. If you do need to load an image with individual dimensions
|
|
||||||
// larger than that, and it still fits in the overall size limit, you can
|
|
||||||
// #define STBI_MAX_DIMENSIONS on your own to be something larger.
|
|
||||||
//
|
|
||||||
// ===========================================================================
|
|
||||||
//
|
|
||||||
// UNICODE:
|
|
||||||
//
|
|
||||||
// If compiling for Windows and you wish to use Unicode filenames, compile
|
|
||||||
// with
|
|
||||||
// #define STBI_WINDOWS_UTF8
|
|
||||||
// and pass utf8-encoded filenames. Call stbi_convert_wchar_to_utf8 to convert
|
|
||||||
// Windows wchar_t filenames to utf8.
|
|
||||||
//
|
|
||||||
// ===========================================================================
|
|
||||||
//
|
|
||||||
// Philosophy
|
|
||||||
//
|
|
||||||
// stb libraries are designed with the following priorities:
|
|
||||||
//
|
|
||||||
// 1. easy to use
|
|
||||||
// 2. easy to maintain
|
|
||||||
// 3. good performance
|
|
||||||
//
|
|
||||||
// Sometimes I let "good performance" creep up in priority over "easy to maintain",
|
|
||||||
// and for best performance I may provide less-easy-to-use APIs that give higher
|
|
||||||
// performance, in addition to the easy-to-use ones. Nevertheless, it's important
|
|
||||||
// to keep in mind that from the standpoint of you, a client of this library,
|
|
||||||
// all you care about is #1 and #3, and stb libraries DO NOT emphasize #3 above all.
|
|
||||||
//
|
|
||||||
// Some secondary priorities arise directly from the first two, some of which
|
|
||||||
// provide more explicit reasons why performance can't be emphasized.
|
|
||||||
//
|
|
||||||
// - Portable ("ease of use")
|
|
||||||
// - Small source code footprint ("easy to maintain")
|
|
||||||
// - No dependencies ("ease of use")
|
|
||||||
//
|
|
||||||
// ===========================================================================
|
|
||||||
//
|
|
||||||
// I/O callbacks
|
|
||||||
//
|
|
||||||
// I/O callbacks allow you to read from arbitrary sources, like packaged
|
|
||||||
// files or some other source. Data read from callbacks are processed
|
|
||||||
// through a small internal buffer (currently 128 bytes) to try to reduce
|
|
||||||
// overhead.
|
|
||||||
//
|
|
||||||
// The three functions you must define are "read" (reads some bytes of data),
|
|
||||||
// "skip" (skips some bytes of data), "eof" (reports if the stream is at the end).
|
|
||||||
//
|
|
||||||
// ===========================================================================
|
|
||||||
//
|
|
||||||
// SIMD support
|
|
||||||
//
|
|
||||||
// The JPEG decoder will try to automatically use SIMD kernels on x86 when
|
|
||||||
// supported by the compiler. For ARM Neon support, you must explicitly
|
|
||||||
// request it.
|
|
||||||
//
|
|
||||||
// (The old do-it-yourself SIMD API is no longer supported in the current
|
|
||||||
// code.)
|
|
||||||
//
|
|
||||||
// On x86, SSE2 will automatically be used when available based on a run-time
|
|
||||||
// test; if not, the generic C versions are used as a fall-back. On ARM targets,
|
|
||||||
// the typical path is to have separate builds for NEON and non-NEON devices
|
|
||||||
// (at least this is true for iOS and Android). Therefore, the NEON support is
|
|
||||||
// toggled by a build flag: define STBI_NEON to get NEON loops.
|
|
||||||
//
|
|
||||||
// If for some reason you do not want to use any of SIMD code, or if
|
|
||||||
// you have issues compiling it, you can disable it entirely by
|
|
||||||
// defining STBI_NO_SIMD.
|
|
||||||
//
|
|
||||||
// ===========================================================================
|
|
||||||
//
|
|
||||||
// HDR image support (disable by defining STBI_NO_HDR)
|
|
||||||
//
|
|
||||||
// stb_image supports loading HDR images in general, and currently the Radiance
|
|
||||||
// .HDR file format specifically. You can still load any file through the existing
|
|
||||||
// interface; if you attempt to load an HDR file, it will be automatically remapped
|
|
||||||
// to LDR, assuming gamma 2.2 and an arbitrary scale factor defaulting to 1;
|
|
||||||
// both of these constants can be reconfigured through this interface:
|
|
||||||
//
|
|
||||||
// stbi_hdr_to_ldr_gamma(2.2f);
|
|
||||||
// stbi_hdr_to_ldr_scale(1.0f);
|
|
||||||
//
|
|
||||||
// (note, do not use _inverse_ constants; stbi_image will invert them
|
|
||||||
// appropriately).
|
|
||||||
//
|
|
||||||
// Additionally, there is a new, parallel interface for loading files as
|
|
||||||
// (linear) floats to preserve the full dynamic range:
|
|
||||||
//
|
|
||||||
// float *data = stbi_loadf(filename, &x, &y, &n, 0);
|
|
||||||
//
|
|
||||||
// If you load LDR images through this interface, those images will
|
|
||||||
// be promoted to floating point values, run through the inverse of
|
|
||||||
// constants corresponding to the above:
|
|
||||||
//
|
|
||||||
// stbi_ldr_to_hdr_scale(1.0f);
|
|
||||||
// stbi_ldr_to_hdr_gamma(2.2f);
|
|
||||||
//
|
|
||||||
// Finally, given a filename (or an open file or memory block--see header
|
|
||||||
// file for details) containing image data, you can query for the "most
|
|
||||||
// appropriate" interface to use (that is, whether the image is HDR or
|
|
||||||
// not), using:
|
|
||||||
//
|
|
||||||
// stbi_is_hdr(char *filename);
|
|
||||||
//
|
|
||||||
// ===========================================================================
|
|
||||||
//
|
|
||||||
// iPhone PNG support:
|
|
||||||
//
|
|
||||||
// We optionally support converting iPhone-formatted PNGs (which store
|
|
||||||
// premultiplied BGRA) back to RGB, even though they're internally encoded
|
|
||||||
// differently. To enable this conversion, call
|
|
||||||
// stbi_convert_iphone_png_to_rgb(1).
|
|
||||||
//
|
|
||||||
// Call stbi_set_unpremultiply_on_load(1) as well to force a divide per
|
|
||||||
// pixel to remove any premultiplied alpha *only* if the image file explicitly
|
|
||||||
// says there's premultiplied data (currently only happens in iPhone images,
|
|
||||||
// and only if iPhone convert-to-rgb processing is on).
|
|
||||||
//
|
|
||||||
// ===========================================================================
|
|
||||||
//
|
|
||||||
// ADDITIONAL CONFIGURATION
|
|
||||||
//
|
|
||||||
// - You can suppress implementation of any of the decoders to reduce
|
|
||||||
// your code footprint by #defining one or more of the following
|
|
||||||
// symbols before creating the implementation.
|
|
||||||
//
|
|
||||||
// STBI_NO_JPEG
|
|
||||||
// STBI_NO_PNG
|
|
||||||
// STBI_NO_BMP
|
|
||||||
// STBI_NO_PSD
|
|
||||||
// STBI_NO_TGA
|
|
||||||
// STBI_NO_GIF
|
|
||||||
// STBI_NO_HDR
|
|
||||||
// STBI_NO_PIC
|
|
||||||
// STBI_NO_PNM (.ppm and .pgm)
|
|
||||||
//
|
|
||||||
// - You can request *only* certain decoders and suppress all other ones
|
|
||||||
// (this will be more forward-compatible, as addition of new decoders
|
|
||||||
// doesn't require you to disable them explicitly):
|
|
||||||
//
|
|
||||||
// STBI_ONLY_JPEG
|
|
||||||
// STBI_ONLY_PNG
|
|
||||||
// STBI_ONLY_BMP
|
|
||||||
// STBI_ONLY_PSD
|
|
||||||
// STBI_ONLY_TGA
|
|
||||||
// STBI_ONLY_GIF
|
|
||||||
// STBI_ONLY_HDR
|
|
||||||
// STBI_ONLY_PIC
|
|
||||||
// STBI_ONLY_PNM (.ppm and .pgm)
|
|
||||||
//
|
|
||||||
// - If you use STBI_NO_PNG (or _ONLY_ without PNG), and you still
|
|
||||||
// want the zlib decoder to be available, #define STBI_SUPPORT_ZLIB
|
|
||||||
//
|
|
||||||
// - If you define STBI_MAX_DIMENSIONS, stb_image will reject images greater
|
|
||||||
// than that size (in either width or height) without further processing.
|
|
||||||
// This is to let programs in the wild set an upper bound to prevent
|
|
||||||
// denial-of-service attacks on untrusted data, as one could generate a
|
|
||||||
// valid image of gigantic dimensions and force stb_image to allocate a
|
|
||||||
// huge block of memory and spend disproportionate time decoding it. By
|
|
||||||
// default this is set to (1 << 24), which is 16777216, but that's still
|
|
||||||
// very big.
|
|
||||||
|
|
||||||
#ifndef STBI_NO_STDIO
|
|
||||||
#include <stdio.h>
|
|
||||||
#endif // STBI_NO_STDIO
|
|
||||||
|
|
||||||
#define STBI_VERSION 1
|
|
||||||
|
|
||||||
enum
|
|
||||||
{
|
|
||||||
STBI_default = 0, // only used for desired_channels
|
|
||||||
|
|
||||||
STBI_grey = 1,
|
|
||||||
STBI_grey_alpha = 2,
|
|
||||||
STBI_rgb = 3,
|
|
||||||
STBI_rgb_alpha = 4
|
|
||||||
};
|
|
||||||
|
|
||||||
#include <stdlib.h>
|
|
||||||
typedef unsigned char stbi_uc;
|
|
||||||
typedef unsigned short stbi_us;
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef STBIDEF
|
|
||||||
#ifdef STB_IMAGE_STATIC
|
|
||||||
#define STBIDEF static
|
|
||||||
#else
|
|
||||||
#define STBIDEF extern
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// PRIMARY API - works on images of any type
|
|
||||||
//
|
|
||||||
|
|
||||||
//
|
|
||||||
// load image by filename, open file, or memory buffer
|
|
||||||
//
|
|
||||||
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
int (*read) (void *user,char *data,int size); // fill 'data' with 'size' bytes. return number of bytes actually read
|
|
||||||
void (*skip) (void *user,int n); // skip the next 'n' bytes, or 'unget' the last -n bytes if negative
|
|
||||||
int (*eof) (void *user); // returns nonzero if we are at end of file/data
|
|
||||||
} stbi_io_callbacks;
|
|
||||||
|
|
||||||
////////////////////////////////////
|
|
||||||
//
|
|
||||||
// 8-bits-per-channel interface
|
|
||||||
//
|
|
||||||
|
|
||||||
STBIDEF stbi_uc *stbi_load_from_memory (stbi_uc const *buffer, int len , int *x, int *y, int *channels_in_file, int desired_channels);
|
|
||||||
STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk , void *user, int *x, int *y, int *channels_in_file, int desired_channels);
|
|
||||||
|
|
||||||
#ifndef STBI_NO_STDIO
|
|
||||||
STBIDEF stbi_uc *stbi_load (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels);
|
|
||||||
STBIDEF stbi_uc *stbi_load_from_file (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels);
|
|
||||||
// for stbi_load_from_file, file pointer is left pointing immediately after image
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef STBI_NO_GIF
|
|
||||||
STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef STBI_WINDOWS_UTF8
|
|
||||||
STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
////////////////////////////////////
|
|
||||||
//
|
|
||||||
// 16-bits-per-channel interface
|
|
||||||
//
|
|
||||||
|
|
||||||
STBIDEF stbi_us *stbi_load_16_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels);
|
|
||||||
STBIDEF stbi_us *stbi_load_16_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels);
|
|
||||||
|
|
||||||
#ifndef STBI_NO_STDIO
|
|
||||||
STBIDEF stbi_us *stbi_load_16 (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels);
|
|
||||||
STBIDEF stbi_us *stbi_load_from_file_16(FILE *f, int *x, int *y, int *channels_in_file, int desired_channels);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
////////////////////////////////////
|
|
||||||
//
|
|
||||||
// float-per-channel interface
|
|
||||||
//
|
|
||||||
#ifndef STBI_NO_LINEAR
|
|
||||||
STBIDEF float *stbi_loadf_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels);
|
|
||||||
STBIDEF float *stbi_loadf_from_callbacks (stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels);
|
|
||||||
|
|
||||||
#ifndef STBI_NO_STDIO
|
|
||||||
STBIDEF float *stbi_loadf (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels);
|
|
||||||
STBIDEF float *stbi_loadf_from_file (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels);
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef STBI_NO_HDR
|
|
||||||
STBIDEF void stbi_hdr_to_ldr_gamma(float gamma);
|
|
||||||
STBIDEF void stbi_hdr_to_ldr_scale(float scale);
|
|
||||||
#endif // STBI_NO_HDR
|
|
||||||
|
|
||||||
#ifndef STBI_NO_LINEAR
|
|
||||||
STBIDEF void stbi_ldr_to_hdr_gamma(float gamma);
|
|
||||||
STBIDEF void stbi_ldr_to_hdr_scale(float scale);
|
|
||||||
#endif // STBI_NO_LINEAR
|
|
||||||
|
|
||||||
// stbi_is_hdr is always defined, but always returns false if STBI_NO_HDR
|
|
||||||
STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user);
|
|
||||||
STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len);
|
|
||||||
#ifndef STBI_NO_STDIO
|
|
||||||
STBIDEF int stbi_is_hdr (char const *filename);
|
|
||||||
STBIDEF int stbi_is_hdr_from_file(FILE *f);
|
|
||||||
#endif // STBI_NO_STDIO
|
|
||||||
|
|
||||||
|
|
||||||
// get a VERY brief reason for failure
|
|
||||||
// on most compilers (and ALL modern mainstream compilers) this is threadsafe
|
|
||||||
STBIDEF const char *stbi_failure_reason (void);
|
|
||||||
|
|
||||||
// free the loaded image -- this is just free()
|
|
||||||
STBIDEF void stbi_image_free (void *retval_from_stbi_load);
|
|
||||||
|
|
||||||
// get image dimensions & components without fully decoding
|
|
||||||
STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp);
|
|
||||||
STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp);
|
|
||||||
STBIDEF int stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len);
|
|
||||||
STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *clbk, void *user);
|
|
||||||
|
|
||||||
#ifndef STBI_NO_STDIO
|
|
||||||
STBIDEF int stbi_info (char const *filename, int *x, int *y, int *comp);
|
|
||||||
STBIDEF int stbi_info_from_file (FILE *f, int *x, int *y, int *comp);
|
|
||||||
STBIDEF int stbi_is_16_bit (char const *filename);
|
|
||||||
STBIDEF int stbi_is_16_bit_from_file(FILE *f);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// for image formats that explicitly notate that they have premultiplied alpha,
|
|
||||||
// we just return the colors as stored in the file. set this flag to force
|
|
||||||
// unpremultiplication. results are undefined if the unpremultiply overflow.
|
|
||||||
STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply);
|
|
||||||
|
|
||||||
// indicate whether we should process iphone images back to canonical format,
|
|
||||||
// or just pass them through "as-is"
|
|
||||||
STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert);
|
|
||||||
|
|
||||||
// flip the image vertically, so the first pixel in the output array is the bottom left
|
|
||||||
STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip);
|
|
||||||
|
|
||||||
// as above, but only applies to images loaded on the thread that calls the function
|
|
||||||
// this function is only available if your compiler supports thread-local variables;
|
|
||||||
// calling it will fail to link if your compiler doesn't
|
|
||||||
STBIDEF void stbi_set_unpremultiply_on_load_thread(int flag_true_if_should_unpremultiply);
|
|
||||||
STBIDEF void stbi_convert_iphone_png_to_rgb_thread(int flag_true_if_should_convert);
|
|
||||||
STBIDEF void stbi_set_flip_vertically_on_load_thread(int flag_true_if_should_flip);
|
|
||||||
|
|
||||||
// ZLIB client - used by PNG, available for other purposes
|
|
||||||
|
|
||||||
STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen);
|
|
||||||
STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header);
|
|
||||||
STBIDEF char *stbi_zlib_decode_malloc(const char *buffer, int len, int *outlen);
|
|
||||||
STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, const char *ibuffer, int ilen);
|
|
||||||
|
|
||||||
STBIDEF char *stbi_zlib_decode_noheader_malloc(const char *buffer, int len, int *outlen);
|
|
||||||
STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen);
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//// end header file /////////////////////////////////////////////////////
|
|
||||||
#endif // STBI_INCLUDE_STB_IMAGE_H
|
|
||||||
|
|
||||||
/*
|
|
||||||
revision history:
|
|
||||||
2.20 (2019-02-07) support utf8 filenames in Windows; fix warnings and platform ifdefs
|
|
||||||
2.19 (2018-02-11) fix warning
|
|
||||||
2.18 (2018-01-30) fix warnings
|
|
||||||
2.17 (2018-01-29) change sbti__shiftsigned to avoid clang -O2 bug
|
|
||||||
1-bit BMP
|
|
||||||
*_is_16_bit api
|
|
||||||
avoid warnings
|
|
||||||
2.16 (2017-07-23) all functions have 16-bit variants;
|
|
||||||
STBI_NO_STDIO works again;
|
|
||||||
compilation fixes;
|
|
||||||
fix rounding in unpremultiply;
|
|
||||||
optimize vertical flip;
|
|
||||||
disable raw_len validation;
|
|
||||||
documentation fixes
|
|
||||||
2.15 (2017-03-18) fix png-1,2,4 bug; now all Imagenet JPGs decode;
|
|
||||||
warning fixes; disable run-time SSE detection on gcc;
|
|
||||||
uniform handling of optional "return" values;
|
|
||||||
thread-safe initialization of zlib tables
|
|
||||||
2.14 (2017-03-03) remove deprecated STBI_JPEG_OLD; fixes for Imagenet JPGs
|
|
||||||
2.13 (2016-11-29) add 16-bit API, only supported for PNG right now
|
|
||||||
2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes
|
|
||||||
2.11 (2016-04-02) allocate large structures on the stack
|
|
||||||
remove white matting for transparent PSD
|
|
||||||
fix reported channel count for PNG & BMP
|
|
||||||
re-enable SSE2 in non-gcc 64-bit
|
|
||||||
support RGB-formatted JPEG
|
|
||||||
read 16-bit PNGs (only as 8-bit)
|
|
||||||
2.10 (2016-01-22) avoid warning introduced in 2.09 by STBI_REALLOC_SIZED
|
|
||||||
2.09 (2016-01-16) allow comments in PNM files
|
|
||||||
16-bit-per-pixel TGA (not bit-per-component)
|
|
||||||
info() for TGA could break due to .hdr handling
|
|
||||||
info() for BMP to shares code instead of sloppy parse
|
|
||||||
can use STBI_REALLOC_SIZED if allocator doesn't support realloc
|
|
||||||
code cleanup
|
|
||||||
2.08 (2015-09-13) fix to 2.07 cleanup, reading RGB PSD as RGBA
|
|
||||||
2.07 (2015-09-13) fix compiler warnings
|
|
||||||
partial animated GIF support
|
|
||||||
limited 16-bpc PSD support
|
|
||||||
#ifdef unused functions
|
|
||||||
bug with < 92 byte PIC,PNM,HDR,TGA
|
|
||||||
2.06 (2015-04-19) fix bug where PSD returns wrong '*comp' value
|
|
||||||
2.05 (2015-04-19) fix bug in progressive JPEG handling, fix warning
|
|
||||||
2.04 (2015-04-15) try to re-enable SIMD on MinGW 64-bit
|
|
||||||
2.03 (2015-04-12) extra corruption checking (mmozeiko)
|
|
||||||
stbi_set_flip_vertically_on_load (nguillemot)
|
|
||||||
fix NEON support; fix mingw support
|
|
||||||
2.02 (2015-01-19) fix incorrect assert, fix warning
|
|
||||||
2.01 (2015-01-17) fix various warnings; suppress SIMD on gcc 32-bit without -msse2
|
|
||||||
2.00b (2014-12-25) fix STBI_MALLOC in progressive JPEG
|
|
||||||
2.00 (2014-12-25) optimize JPG, including x86 SSE2 & NEON SIMD (ryg)
|
|
||||||
progressive JPEG (stb)
|
|
||||||
PGM/PPM support (Ken Miller)
|
|
||||||
STBI_MALLOC,STBI_REALLOC,STBI_FREE
|
|
||||||
GIF bugfix -- seemingly never worked
|
|
||||||
STBI_NO_*, STBI_ONLY_*
|
|
||||||
1.48 (2014-12-14) fix incorrectly-named assert()
|
|
||||||
1.47 (2014-12-14) 1/2/4-bit PNG support, both direct and paletted (Omar Cornut & stb)
|
|
||||||
optimize PNG (ryg)
|
|
||||||
fix bug in interlaced PNG with user-specified channel count (stb)
|
|
||||||
1.46 (2014-08-26)
|
|
||||||
fix broken tRNS chunk (colorkey-style transparency) in non-paletted PNG
|
|
||||||
1.45 (2014-08-16)
|
|
||||||
fix MSVC-ARM internal compiler error by wrapping malloc
|
|
||||||
1.44 (2014-08-07)
|
|
||||||
various warning fixes from Ronny Chevalier
|
|
||||||
1.43 (2014-07-15)
|
|
||||||
fix MSVC-only compiler problem in code changed in 1.42
|
|
||||||
1.42 (2014-07-09)
|
|
||||||
don't define _CRT_SECURE_NO_WARNINGS (affects user code)
|
|
||||||
fixes to stbi__cleanup_jpeg path
|
|
||||||
added STBI_ASSERT to avoid requiring assert.h
|
|
||||||
1.41 (2014-06-25)
|
|
||||||
fix search&replace from 1.36 that messed up comments/error messages
|
|
||||||
1.40 (2014-06-22)
|
|
||||||
fix gcc struct-initialization warning
|
|
||||||
1.39 (2014-06-15)
|
|
||||||
fix to TGA optimization when req_comp != number of components in TGA;
|
|
||||||
fix to GIF loading because BMP wasn't rewinding (whoops, no GIFs in my test suite)
|
|
||||||
add support for BMP version 5 (more ignored fields)
|
|
||||||
1.38 (2014-06-06)
|
|
||||||
suppress MSVC warnings on integer casts truncating values
|
|
||||||
fix accidental rename of 'skip' field of I/O
|
|
||||||
1.37 (2014-06-04)
|
|
||||||
remove duplicate typedef
|
|
||||||
1.36 (2014-06-03)
|
|
||||||
convert to header file single-file library
|
|
||||||
if de-iphone isn't set, load iphone images color-swapped instead of returning NULL
|
|
||||||
1.35 (2014-05-27)
|
|
||||||
various warnings
|
|
||||||
fix broken STBI_SIMD path
|
|
||||||
fix bug where stbi_load_from_file no longer left file pointer in correct place
|
|
||||||
fix broken non-easy path for 32-bit BMP (possibly never used)
|
|
||||||
TGA optimization by Arseny Kapoulkine
|
|
||||||
1.34 (unknown)
|
|
||||||
use STBI_NOTUSED in stbi__resample_row_generic(), fix one more leak in tga failure case
|
|
||||||
1.33 (2011-07-14)
|
|
||||||
make stbi_is_hdr work in STBI_NO_HDR (as specified), minor compiler-friendly improvements
|
|
||||||
1.32 (2011-07-13)
|
|
||||||
support for "info" function for all supported filetypes (SpartanJ)
|
|
||||||
1.31 (2011-06-20)
|
|
||||||
a few more leak fixes, bug in PNG handling (SpartanJ)
|
|
||||||
1.30 (2011-06-11)
|
|
||||||
added ability to load files via callbacks to accomidate custom input streams (Ben Wenger)
|
|
||||||
removed deprecated format-specific test/load functions
|
|
||||||
removed support for installable file formats (stbi_loader) -- would have been broken for IO callbacks anyway
|
|
||||||
error cases in bmp and tga give messages and don't leak (Raymond Barbiero, grisha)
|
|
||||||
fix inefficiency in decoding 32-bit BMP (David Woo)
|
|
||||||
1.29 (2010-08-16)
|
|
||||||
various warning fixes from Aurelien Pocheville
|
|
||||||
1.28 (2010-08-01)
|
|
||||||
fix bug in GIF palette transparency (SpartanJ)
|
|
||||||
1.27 (2010-08-01)
|
|
||||||
cast-to-stbi_uc to fix warnings
|
|
||||||
1.26 (2010-07-24)
|
|
||||||
fix bug in file buffering for PNG reported by SpartanJ
|
|
||||||
1.25 (2010-07-17)
|
|
||||||
refix trans_data warning (Won Chun)
|
|
||||||
1.24 (2010-07-12)
|
|
||||||
perf improvements reading from files on platforms with lock-heavy fgetc()
|
|
||||||
minor perf improvements for jpeg
|
|
||||||
deprecated type-specific functions so we'll get feedback if they're needed
|
|
||||||
attempt to fix trans_data warning (Won Chun)
|
|
||||||
1.23 fixed bug in iPhone support
|
|
||||||
1.22 (2010-07-10)
|
|
||||||
removed image *writing* support
|
|
||||||
stbi_info support from Jetro Lauha
|
|
||||||
GIF support from Jean-Marc Lienher
|
|
||||||
iPhone PNG-extensions from James Brown
|
|
||||||
warning-fixes from Nicolas Schulz and Janez Zemva (i.stbi__err. Janez (U+017D)emva)
|
|
||||||
1.21 fix use of 'stbi_uc' in header (reported by jon blow)
|
|
||||||
1.20 added support for Softimage PIC, by Tom Seddon
|
|
||||||
1.19 bug in interlaced PNG corruption check (found by ryg)
|
|
||||||
1.18 (2008-08-02)
|
|
||||||
fix a threading bug (local mutable static)
|
|
||||||
1.17 support interlaced PNG
|
|
||||||
1.16 major bugfix - stbi__convert_format converted one too many pixels
|
|
||||||
1.15 initialize some fields for thread safety
|
|
||||||
1.14 fix threadsafe conversion bug
|
|
||||||
header-file-only version (#define STBI_HEADER_FILE_ONLY before including)
|
|
||||||
1.13 threadsafe
|
|
||||||
1.12 const qualifiers in the API
|
|
||||||
1.11 Support installable IDCT, colorspace conversion routines
|
|
||||||
1.10 Fixes for 64-bit (don't use "unsigned long")
|
|
||||||
optimized upsampling by Fabian "ryg" Giesen
|
|
||||||
1.09 Fix format-conversion for PSD code (bad global variables!)
|
|
||||||
1.08 Thatcher Ulrich's PSD code integrated by Nicolas Schulz
|
|
||||||
1.07 attempt to fix C++ warning/errors again
|
|
||||||
1.06 attempt to fix C++ warning/errors again
|
|
||||||
1.05 fix TGA loading to return correct *comp and use good luminance calc
|
|
||||||
1.04 default float alpha is 1, not 255; use 'void *' for stbi_image_free
|
|
||||||
1.03 bugfixes to STBI_NO_STDIO, STBI_NO_HDR
|
|
||||||
1.02 support for (subset of) HDR files, float interface for preferred access to them
|
|
||||||
1.01 fix bug: possible bug in handling right-side up bmps... not sure
|
|
||||||
fix bug: the stbi__bmp_load() and stbi__tga_load() functions didn't work at all
|
|
||||||
1.00 interface to zlib that skips zlib header
|
|
||||||
0.99 correct handling of alpha in palette
|
|
||||||
0.98 TGA loader by lonesock; dynamically add loaders (untested)
|
|
||||||
0.97 jpeg errors on too large a file; also catch another malloc failure
|
|
||||||
0.96 fix detection of invalid v value - particleman@mollyrocket forum
|
|
||||||
0.95 during header scan, seek to markers in case of padding
|
|
||||||
0.94 STBI_NO_STDIO to disable stdio usage; rename all #defines the same
|
|
||||||
0.93 handle jpegtran output; verbose errors
|
|
||||||
0.92 read 4,8,16,24,32-bit BMP files of several formats
|
|
||||||
0.91 output 24-bit Windows 3.0 BMP files
|
|
||||||
0.90 fix a few more warnings; bump version number to approach 1.0
|
|
||||||
0.61 bugfixes due to Marc LeBlanc, Christopher Lloyd
|
|
||||||
0.60 fix compiling as c++
|
|
||||||
0.59 fix warnings: merge Dave Moore's -Wall fixes
|
|
||||||
0.58 fix bug: zlib uncompressed mode len/nlen was wrong endian
|
|
||||||
0.57 fix bug: jpg last huffman symbol before marker was >9 bits but less than 16 available
|
|
||||||
0.56 fix bug: zlib uncompressed mode len vs. nlen
|
|
||||||
0.55 fix bug: restart_interval not initialized to 0
|
|
||||||
0.54 allow NULL for 'int *comp'
|
|
||||||
0.53 fix bug in png 3->4; speedup png decoding
|
|
||||||
0.52 png handles req_comp=3,4 directly; minor cleanup; jpeg comments
|
|
||||||
0.51 obey req_comp requests, 1-component jpegs return as 1-component,
|
|
||||||
on 'test' only check type, not whether we support this variant
|
|
||||||
0.50 (2006-11-19)
|
|
||||||
first released version
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
------------------------------------------------------------------------------
|
|
||||||
This software is available under 2 licenses -- choose whichever you prefer.
|
|
||||||
------------------------------------------------------------------------------
|
|
||||||
ALTERNATIVE A - MIT License
|
|
||||||
Copyright (c) 2017 Sean Barrett
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
||||||
this software and associated documentation files (the "Software"), to deal in
|
|
||||||
the Software without restriction, including without limitation the rights to
|
|
||||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
|
||||||
of the Software, and to permit persons to whom the Software is furnished to do
|
|
||||||
so, subject to the following conditions:
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
------------------------------------------------------------------------------
|
|
||||||
ALTERNATIVE B - Public Domain (www.unlicense.org)
|
|
||||||
This is free and unencumbered software released into the public domain.
|
|
||||||
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
|
|
||||||
software, either in source code form or as a compiled binary, for any purpose,
|
|
||||||
commercial or non-commercial, and by any means.
|
|
||||||
In jurisdictions that recognize copyright laws, the author or authors of this
|
|
||||||
software dedicate any and all copyright interest in the software to the public
|
|
||||||
domain. We make this dedication for the benefit of the public at large and to
|
|
||||||
the detriment of our heirs and successors. We intend this dedication to be an
|
|
||||||
overt act of relinquishment in perpetuity of all present and future rights to
|
|
||||||
this software under copyright law.
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
|
||||||
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
||||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
------------------------------------------------------------------------------
|
|
||||||
*/
|
|
2282
externals/stb/stb_image_resize.cpp
vendored
2282
externals/stb/stb_image_resize.cpp
vendored
File diff suppressed because it is too large
Load Diff
426
externals/stb/stb_image_resize.h
vendored
426
externals/stb/stb_image_resize.h
vendored
@ -1,426 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: Jorge L Rodriguez
|
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
/* stb_image_resize - v0.97 - public domain image resizing
|
|
||||||
by Jorge L Rodriguez (@VinoBS) - 2014
|
|
||||||
http://github.com/nothings/stb
|
|
||||||
|
|
||||||
Written with emphasis on usability, portability, and efficiency. (No
|
|
||||||
SIMD or threads, so it be easily outperformed by libs that use those.)
|
|
||||||
Only scaling and translation is supported, no rotations or shears.
|
|
||||||
Easy API downsamples w/Mitchell filter, upsamples w/cubic interpolation.
|
|
||||||
|
|
||||||
COMPILING & LINKING
|
|
||||||
In one C/C++ file that #includes this file, do this:
|
|
||||||
#define STB_IMAGE_RESIZE_IMPLEMENTATION
|
|
||||||
before the #include. That will create the implementation in that file.
|
|
||||||
|
|
||||||
QUICKSTART
|
|
||||||
stbir_resize_uint8( input_pixels , in_w , in_h , 0,
|
|
||||||
output_pixels, out_w, out_h, 0, num_channels)
|
|
||||||
stbir_resize_float(...)
|
|
||||||
stbir_resize_uint8_srgb( input_pixels , in_w , in_h , 0,
|
|
||||||
output_pixels, out_w, out_h, 0,
|
|
||||||
num_channels , alpha_chan , 0)
|
|
||||||
stbir_resize_uint8_srgb_edgemode(
|
|
||||||
input_pixels , in_w , in_h , 0,
|
|
||||||
output_pixels, out_w, out_h, 0,
|
|
||||||
num_channels , alpha_chan , 0, STBIR_EDGE_CLAMP)
|
|
||||||
// WRAP/REFLECT/ZERO
|
|
||||||
|
|
||||||
FULL API
|
|
||||||
See the "header file" section of the source for API documentation.
|
|
||||||
|
|
||||||
ADDITIONAL DOCUMENTATION
|
|
||||||
|
|
||||||
SRGB & FLOATING POINT REPRESENTATION
|
|
||||||
The sRGB functions presume IEEE floating point. If you do not have
|
|
||||||
IEEE floating point, define STBIR_NON_IEEE_FLOAT. This will use
|
|
||||||
a slower implementation.
|
|
||||||
|
|
||||||
MEMORY ALLOCATION
|
|
||||||
The resize functions here perform a single memory allocation using
|
|
||||||
malloc. To control the memory allocation, before the #include that
|
|
||||||
triggers the implementation, do:
|
|
||||||
|
|
||||||
#define STBIR_MALLOC(size,context) ...
|
|
||||||
#define STBIR_FREE(ptr,context) ...
|
|
||||||
|
|
||||||
Each resize function makes exactly one call to malloc/free, so to use
|
|
||||||
temp memory, store the temp memory in the context and return that.
|
|
||||||
|
|
||||||
ASSERT
|
|
||||||
Define STBIR_ASSERT(boolval) to override assert() and not use assert.h
|
|
||||||
|
|
||||||
OPTIMIZATION
|
|
||||||
Define STBIR_SATURATE_INT to compute clamp values in-range using
|
|
||||||
integer operations instead of float operations. This may be faster
|
|
||||||
on some platforms.
|
|
||||||
|
|
||||||
DEFAULT FILTERS
|
|
||||||
For functions which don't provide explicit control over what filters
|
|
||||||
to use, you can change the compile-time defaults with
|
|
||||||
|
|
||||||
#define STBIR_DEFAULT_FILTER_UPSAMPLE STBIR_FILTER_something
|
|
||||||
#define STBIR_DEFAULT_FILTER_DOWNSAMPLE STBIR_FILTER_something
|
|
||||||
|
|
||||||
See stbir_filter in the header-file section for the list of filters.
|
|
||||||
|
|
||||||
NEW FILTERS
|
|
||||||
A number of 1D filter kernels are used. For a list of
|
|
||||||
supported filters see the stbir_filter enum. To add a new filter,
|
|
||||||
write a filter function and add it to stbir__filter_info_table.
|
|
||||||
|
|
||||||
PROGRESS
|
|
||||||
For interactive use with slow resize operations, you can install
|
|
||||||
a progress-report callback:
|
|
||||||
|
|
||||||
#define STBIR_PROGRESS_REPORT(val) some_func(val)
|
|
||||||
|
|
||||||
The parameter val is a float which goes from 0 to 1 as progress is made.
|
|
||||||
|
|
||||||
For example:
|
|
||||||
|
|
||||||
static void my_progress_report(float progress);
|
|
||||||
#define STBIR_PROGRESS_REPORT(val) my_progress_report(val)
|
|
||||||
|
|
||||||
#define STB_IMAGE_RESIZE_IMPLEMENTATION
|
|
||||||
#include "stb_image_resize.h"
|
|
||||||
|
|
||||||
static void my_progress_report(float progress)
|
|
||||||
{
|
|
||||||
printf("Progress: %f%%\n", progress*100);
|
|
||||||
}
|
|
||||||
|
|
||||||
MAX CHANNELS
|
|
||||||
If your image has more than 64 channels, define STBIR_MAX_CHANNELS
|
|
||||||
to the max you'll have.
|
|
||||||
|
|
||||||
ALPHA CHANNEL
|
|
||||||
Most of the resizing functions provide the ability to control how
|
|
||||||
the alpha channel of an image is processed. The important things
|
|
||||||
to know about this:
|
|
||||||
|
|
||||||
1. The best mathematically-behaved version of alpha to use is
|
|
||||||
called "premultiplied alpha", in which the other color channels
|
|
||||||
have had the alpha value multiplied in. If you use premultiplied
|
|
||||||
alpha, linear filtering (such as image resampling done by this
|
|
||||||
library, or performed in texture units on GPUs) does the "right
|
|
||||||
thing". While premultiplied alpha is standard in the movie CGI
|
|
||||||
industry, it is still uncommon in the videogame/real-time world.
|
|
||||||
|
|
||||||
If you linearly filter non-premultiplied alpha, strange effects
|
|
||||||
occur. (For example, the 50/50 average of 99% transparent bright green
|
|
||||||
and 1% transparent black produces 50% transparent dark green when
|
|
||||||
non-premultiplied, whereas premultiplied it produces 50%
|
|
||||||
transparent near-black. The former introduces green energy
|
|
||||||
that doesn't exist in the source image.)
|
|
||||||
|
|
||||||
2. Artists should not edit premultiplied-alpha images; artists
|
|
||||||
want non-premultiplied alpha images. Thus, art tools generally output
|
|
||||||
non-premultiplied alpha images.
|
|
||||||
|
|
||||||
3. You will get best results in most cases by converting images
|
|
||||||
to premultiplied alpha before processing them mathematically.
|
|
||||||
|
|
||||||
4. If you pass the flag STBIR_FLAG_ALPHA_PREMULTIPLIED, the
|
|
||||||
resizer does not do anything special for the alpha channel;
|
|
||||||
it is resampled identically to other channels. This produces
|
|
||||||
the correct results for premultiplied-alpha images, but produces
|
|
||||||
less-than-ideal results for non-premultiplied-alpha images.
|
|
||||||
|
|
||||||
5. If you do not pass the flag STBIR_FLAG_ALPHA_PREMULTIPLIED,
|
|
||||||
then the resizer weights the contribution of input pixels
|
|
||||||
based on their alpha values, or, equivalently, it multiplies
|
|
||||||
the alpha value into the color channels, resamples, then divides
|
|
||||||
by the resultant alpha value. Input pixels which have alpha=0 do
|
|
||||||
not contribute at all to output pixels unless _all_ of the input
|
|
||||||
pixels affecting that output pixel have alpha=0, in which case
|
|
||||||
the result for that pixel is the same as it would be without
|
|
||||||
STBIR_FLAG_ALPHA_PREMULTIPLIED. However, this is only true for
|
|
||||||
input images in integer formats. For input images in float format,
|
|
||||||
input pixels with alpha=0 have no effect, and output pixels
|
|
||||||
which have alpha=0 will be 0 in all channels. (For float images,
|
|
||||||
you can manually achieve the same result by adding a tiny epsilon
|
|
||||||
value to the alpha channel of every image, and then subtracting
|
|
||||||
or clamping it at the end.)
|
|
||||||
|
|
||||||
6. You can suppress the behavior described in #5 and make
|
|
||||||
all-0-alpha pixels have 0 in all channels by #defining
|
|
||||||
STBIR_NO_ALPHA_EPSILON.
|
|
||||||
|
|
||||||
7. You can separately control whether the alpha channel is
|
|
||||||
interpreted as linear or affected by the colorspace. By default
|
|
||||||
it is linear; you almost never want to apply the colorspace.
|
|
||||||
(For example, graphics hardware does not apply sRGB conversion
|
|
||||||
to the alpha channel.)
|
|
||||||
|
|
||||||
CONTRIBUTORS
|
|
||||||
Jorge L Rodriguez: Implementation
|
|
||||||
Sean Barrett: API design, optimizations
|
|
||||||
Aras Pranckevicius: bugfix
|
|
||||||
Nathan Reed: warning fixes
|
|
||||||
|
|
||||||
REVISIONS
|
|
||||||
0.97 (2020-02-02) fixed warning
|
|
||||||
0.96 (2019-03-04) fixed warnings
|
|
||||||
0.95 (2017-07-23) fixed warnings
|
|
||||||
0.94 (2017-03-18) fixed warnings
|
|
||||||
0.93 (2017-03-03) fixed bug with certain combinations of heights
|
|
||||||
0.92 (2017-01-02) fix integer overflow on large (>2GB) images
|
|
||||||
0.91 (2016-04-02) fix warnings; fix handling of subpixel regions
|
|
||||||
0.90 (2014-09-17) first released version
|
|
||||||
|
|
||||||
LICENSE
|
|
||||||
See end of file for license information.
|
|
||||||
|
|
||||||
TODO
|
|
||||||
Don't decode all of the image data when only processing a partial tile
|
|
||||||
Don't use full-width decode buffers when only processing a partial tile
|
|
||||||
When processing wide images, break processing into tiles so data fits in L1 cache
|
|
||||||
Installable filters?
|
|
||||||
Resize that respects alpha test coverage
|
|
||||||
(Reference code: FloatImage::alphaTestCoverage and FloatImage::scaleAlphaToCoverage:
|
|
||||||
https://code.google.com/p/nvidia-texture-tools/source/browse/trunk/src/nvimage/FloatImage.cpp )
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef STBIR_INCLUDE_STB_IMAGE_RESIZE_H
|
|
||||||
#define STBIR_INCLUDE_STB_IMAGE_RESIZE_H
|
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
|
||||||
typedef unsigned char stbir_uint8;
|
|
||||||
typedef unsigned short stbir_uint16;
|
|
||||||
typedef unsigned int stbir_uint32;
|
|
||||||
#else
|
|
||||||
#include <stdint.h>
|
|
||||||
typedef uint8_t stbir_uint8;
|
|
||||||
typedef uint16_t stbir_uint16;
|
|
||||||
typedef uint32_t stbir_uint32;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef STBIRDEF
|
|
||||||
#ifdef STB_IMAGE_RESIZE_STATIC
|
|
||||||
#define STBIRDEF static
|
|
||||||
#else
|
|
||||||
#ifdef __cplusplus
|
|
||||||
#define STBIRDEF extern "C"
|
|
||||||
#else
|
|
||||||
#define STBIRDEF extern
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// Easy-to-use API:
|
|
||||||
//
|
|
||||||
// * "input pixels" points to an array of image data with 'num_channels' channels (e.g. RGB=3, RGBA=4)
|
|
||||||
// * input_w is input image width (x-axis), input_h is input image height (y-axis)
|
|
||||||
// * stride is the offset between successive rows of image data in memory, in bytes. you can
|
|
||||||
// specify 0 to mean packed continuously in memory
|
|
||||||
// * alpha channel is treated identically to other channels.
|
|
||||||
// * colorspace is linear or sRGB as specified by function name
|
|
||||||
// * returned result is 1 for success or 0 in case of an error.
|
|
||||||
// #define STBIR_ASSERT() to trigger an assert on parameter validation errors.
|
|
||||||
// * Memory required grows approximately linearly with input and output size, but with
|
|
||||||
// discontinuities at input_w == output_w and input_h == output_h.
|
|
||||||
// * These functions use a "default" resampling filter defined at compile time. To change the filter,
|
|
||||||
// you can change the compile-time defaults by #defining STBIR_DEFAULT_FILTER_UPSAMPLE
|
|
||||||
// and STBIR_DEFAULT_FILTER_DOWNSAMPLE, or you can use the medium-complexity API.
|
|
||||||
|
|
||||||
STBIRDEF int stbir_resize_uint8( const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes,
|
|
||||||
unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes,
|
|
||||||
int num_channels);
|
|
||||||
|
|
||||||
STBIRDEF int stbir_resize_float( const float *input_pixels , int input_w , int input_h , int input_stride_in_bytes,
|
|
||||||
float *output_pixels, int output_w, int output_h, int output_stride_in_bytes,
|
|
||||||
int num_channels);
|
|
||||||
|
|
||||||
|
|
||||||
// The following functions interpret image data as gamma-corrected sRGB.
|
|
||||||
// Specify STBIR_ALPHA_CHANNEL_NONE if you have no alpha channel,
|
|
||||||
// or otherwise provide the index of the alpha channel. Flags value
|
|
||||||
// of 0 will probably do the right thing if you're not sure what
|
|
||||||
// the flags mean.
|
|
||||||
|
|
||||||
#define STBIR_ALPHA_CHANNEL_NONE -1
|
|
||||||
|
|
||||||
// Set this flag if your texture has premultiplied alpha. Otherwise, stbir will
|
|
||||||
// use alpha-weighted resampling (effectively premultiplying, resampling,
|
|
||||||
// then unpremultiplying).
|
|
||||||
#define STBIR_FLAG_ALPHA_PREMULTIPLIED (1 << 0)
|
|
||||||
// The specified alpha channel should be handled as gamma-corrected value even
|
|
||||||
// when doing sRGB operations.
|
|
||||||
#define STBIR_FLAG_ALPHA_USES_COLORSPACE (1 << 1)
|
|
||||||
|
|
||||||
STBIRDEF int stbir_resize_uint8_srgb(const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes,
|
|
||||||
unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes,
|
|
||||||
int num_channels, int alpha_channel, int flags);
|
|
||||||
|
|
||||||
|
|
||||||
typedef enum
|
|
||||||
{
|
|
||||||
STBIR_EDGE_CLAMP = 1,
|
|
||||||
STBIR_EDGE_REFLECT = 2,
|
|
||||||
STBIR_EDGE_WRAP = 3,
|
|
||||||
STBIR_EDGE_ZERO = 4,
|
|
||||||
} stbir_edge;
|
|
||||||
|
|
||||||
// This function adds the ability to specify how requests to sample off the edge of the image are handled.
|
|
||||||
STBIRDEF int stbir_resize_uint8_srgb_edgemode(const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes,
|
|
||||||
unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes,
|
|
||||||
int num_channels, int alpha_channel, int flags,
|
|
||||||
stbir_edge edge_wrap_mode);
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// Medium-complexity API
|
|
||||||
//
|
|
||||||
// This extends the easy-to-use API as follows:
|
|
||||||
//
|
|
||||||
// * Alpha-channel can be processed separately
|
|
||||||
// * If alpha_channel is not STBIR_ALPHA_CHANNEL_NONE
|
|
||||||
// * Alpha channel will not be gamma corrected (unless flags&STBIR_FLAG_GAMMA_CORRECT)
|
|
||||||
// * Filters will be weighted by alpha channel (unless flags&STBIR_FLAG_ALPHA_PREMULTIPLIED)
|
|
||||||
// * Filter can be selected explicitly
|
|
||||||
// * uint16 image type
|
|
||||||
// * sRGB colorspace available for all types
|
|
||||||
// * context parameter for passing to STBIR_MALLOC
|
|
||||||
|
|
||||||
typedef enum
|
|
||||||
{
|
|
||||||
STBIR_FILTER_DEFAULT = 0, // use same filter type that easy-to-use API chooses
|
|
||||||
STBIR_FILTER_BOX = 1, // A trapezoid w/1-pixel wide ramps, same result as box for integer scale ratios
|
|
||||||
STBIR_FILTER_TRIANGLE = 2, // On upsampling, produces same results as bilinear texture filtering
|
|
||||||
STBIR_FILTER_CUBICBSPLINE = 3, // The cubic b-spline (aka Mitchell-Netrevalli with B=1,C=0), gaussian-esque
|
|
||||||
STBIR_FILTER_CATMULLROM = 4, // An interpolating cubic spline
|
|
||||||
STBIR_FILTER_MITCHELL = 5, // Mitchell-Netrevalli filter with B=1/3, C=1/3
|
|
||||||
} stbir_filter;
|
|
||||||
|
|
||||||
typedef enum
|
|
||||||
{
|
|
||||||
STBIR_COLORSPACE_LINEAR,
|
|
||||||
STBIR_COLORSPACE_SRGB,
|
|
||||||
|
|
||||||
STBIR_MAX_COLORSPACES,
|
|
||||||
} stbir_colorspace;
|
|
||||||
|
|
||||||
// The following functions are all identical except for the type of the image data
|
|
||||||
|
|
||||||
STBIRDEF int stbir_resize_uint8_generic( const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes,
|
|
||||||
unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes,
|
|
||||||
int num_channels, int alpha_channel, int flags,
|
|
||||||
stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space,
|
|
||||||
void *alloc_context);
|
|
||||||
|
|
||||||
STBIRDEF int stbir_resize_uint16_generic(const stbir_uint16 *input_pixels , int input_w , int input_h , int input_stride_in_bytes,
|
|
||||||
stbir_uint16 *output_pixels , int output_w, int output_h, int output_stride_in_bytes,
|
|
||||||
int num_channels, int alpha_channel, int flags,
|
|
||||||
stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space,
|
|
||||||
void *alloc_context);
|
|
||||||
|
|
||||||
STBIRDEF int stbir_resize_float_generic( const float *input_pixels , int input_w , int input_h , int input_stride_in_bytes,
|
|
||||||
float *output_pixels , int output_w, int output_h, int output_stride_in_bytes,
|
|
||||||
int num_channels, int alpha_channel, int flags,
|
|
||||||
stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space,
|
|
||||||
void *alloc_context);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// Full-complexity API
|
|
||||||
//
|
|
||||||
// This extends the medium API as follows:
|
|
||||||
//
|
|
||||||
// * uint32 image type
|
|
||||||
// * not typesafe
|
|
||||||
// * separate filter types for each axis
|
|
||||||
// * separate edge modes for each axis
|
|
||||||
// * can specify scale explicitly for subpixel correctness
|
|
||||||
// * can specify image source tile using texture coordinates
|
|
||||||
|
|
||||||
typedef enum
|
|
||||||
{
|
|
||||||
STBIR_TYPE_UINT8 ,
|
|
||||||
STBIR_TYPE_UINT16,
|
|
||||||
STBIR_TYPE_UINT32,
|
|
||||||
STBIR_TYPE_FLOAT ,
|
|
||||||
|
|
||||||
STBIR_MAX_TYPES
|
|
||||||
} stbir_datatype;
|
|
||||||
|
|
||||||
STBIRDEF int stbir_resize( const void *input_pixels , int input_w , int input_h , int input_stride_in_bytes,
|
|
||||||
void *output_pixels, int output_w, int output_h, int output_stride_in_bytes,
|
|
||||||
stbir_datatype datatype,
|
|
||||||
int num_channels, int alpha_channel, int flags,
|
|
||||||
stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical,
|
|
||||||
stbir_filter filter_horizontal, stbir_filter filter_vertical,
|
|
||||||
stbir_colorspace space, void *alloc_context);
|
|
||||||
|
|
||||||
STBIRDEF int stbir_resize_subpixel(const void *input_pixels , int input_w , int input_h , int input_stride_in_bytes,
|
|
||||||
void *output_pixels, int output_w, int output_h, int output_stride_in_bytes,
|
|
||||||
stbir_datatype datatype,
|
|
||||||
int num_channels, int alpha_channel, int flags,
|
|
||||||
stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical,
|
|
||||||
stbir_filter filter_horizontal, stbir_filter filter_vertical,
|
|
||||||
stbir_colorspace space, void *alloc_context,
|
|
||||||
float x_scale, float y_scale,
|
|
||||||
float x_offset, float y_offset);
|
|
||||||
|
|
||||||
STBIRDEF int stbir_resize_region( const void *input_pixels , int input_w , int input_h , int input_stride_in_bytes,
|
|
||||||
void *output_pixels, int output_w, int output_h, int output_stride_in_bytes,
|
|
||||||
stbir_datatype datatype,
|
|
||||||
int num_channels, int alpha_channel, int flags,
|
|
||||||
stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical,
|
|
||||||
stbir_filter filter_horizontal, stbir_filter filter_vertical,
|
|
||||||
stbir_colorspace space, void *alloc_context,
|
|
||||||
float s0, float t0, float s1, float t1);
|
|
||||||
// (s0, t0) & (s1, t1) are the top-left and bottom right corner (uv addressing style: [0, 1]x[0, 1]) of a region of the input image to use.
|
|
||||||
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//// end header file /////////////////////////////////////////////////////
|
|
||||||
#endif // STBIR_INCLUDE_STB_IMAGE_RESIZE_H
|
|
||||||
|
|
||||||
/*
|
|
||||||
------------------------------------------------------------------------------
|
|
||||||
This software is available under 2 licenses -- choose whichever you prefer.
|
|
||||||
------------------------------------------------------------------------------
|
|
||||||
ALTERNATIVE A - MIT License
|
|
||||||
Copyright (c) 2017 Sean Barrett
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
||||||
this software and associated documentation files (the "Software"), to deal in
|
|
||||||
the Software without restriction, including without limitation the rights to
|
|
||||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
|
||||||
of the Software, and to permit persons to whom the Software is furnished to do
|
|
||||||
so, subject to the following conditions:
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
------------------------------------------------------------------------------
|
|
||||||
ALTERNATIVE B - Public Domain (www.unlicense.org)
|
|
||||||
This is free and unencumbered software released into the public domain.
|
|
||||||
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
|
|
||||||
software, either in source code form or as a compiled binary, for any purpose,
|
|
||||||
commercial or non-commercial, and by any means.
|
|
||||||
In jurisdictions that recognize copyright laws, the author or authors of this
|
|
||||||
software dedicate any and all copyright interest in the software to the public
|
|
||||||
domain. We make this dedication for the benefit of the public at large and to
|
|
||||||
the detriment of our heirs and successors. We intend this dedication to be an
|
|
||||||
overt act of relinquishment in perpetuity of all present and future rights to
|
|
||||||
this software under copyright law.
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
|
||||||
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
||||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
------------------------------------------------------------------------------
|
|
||||||
*/
|
|
@ -27,7 +27,7 @@ android {
|
|||||||
namespace = "org.yuzu.yuzu_emu"
|
namespace = "org.yuzu.yuzu_emu"
|
||||||
|
|
||||||
compileSdkVersion = "android-34"
|
compileSdkVersion = "android-34"
|
||||||
ndkVersion = "26.1.10909125"
|
ndkVersion = "25.2.9519653"
|
||||||
|
|
||||||
buildFeatures {
|
buildFeatures {
|
||||||
viewBinding = true
|
viewBinding = true
|
||||||
@ -203,23 +203,23 @@ ktlint {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation("androidx.core:core-ktx:1.12.0")
|
implementation("androidx.core:core-ktx:1.10.1")
|
||||||
implementation("androidx.appcompat:appcompat:1.6.1")
|
implementation("androidx.appcompat:appcompat:1.6.1")
|
||||||
implementation("androidx.recyclerview:recyclerview:1.3.1")
|
implementation("androidx.recyclerview:recyclerview:1.3.0")
|
||||||
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
|
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
|
||||||
implementation("androidx.fragment:fragment-ktx:1.6.1")
|
implementation("androidx.fragment:fragment-ktx:1.6.0")
|
||||||
implementation("androidx.documentfile:documentfile:1.0.1")
|
implementation("androidx.documentfile:documentfile:1.0.1")
|
||||||
implementation("com.google.android.material:material:1.9.0")
|
implementation("com.google.android.material:material:1.9.0")
|
||||||
implementation("androidx.preference:preference-ktx:1.2.1")
|
implementation("androidx.preference:preference:1.2.0")
|
||||||
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.2")
|
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.1")
|
||||||
implementation("io.coil-kt:coil:2.2.2")
|
implementation("io.coil-kt:coil:2.2.2")
|
||||||
implementation("androidx.core:core-splashscreen:1.0.1")
|
implementation("androidx.core:core-splashscreen:1.0.1")
|
||||||
implementation("androidx.window:window:1.2.0-beta03")
|
implementation("androidx.window:window:1.2.0-beta03")
|
||||||
implementation("org.ini4j:ini4j:0.5.4")
|
implementation("org.ini4j:ini4j:0.5.4")
|
||||||
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
|
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
|
||||||
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")
|
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")
|
||||||
implementation("androidx.navigation:navigation-fragment-ktx:2.7.4")
|
implementation("androidx.navigation:navigation-fragment-ktx:2.6.0")
|
||||||
implementation("androidx.navigation:navigation-ui-ktx:2.7.4")
|
implementation("androidx.navigation:navigation-ui-ktx:2.6.0")
|
||||||
implementation("info.debatty:java-string-similarity:2.0.0")
|
implementation("info.debatty:java-string-similarity:2.0.0")
|
||||||
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.0")
|
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.0")
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
|||||||
android:appCategory="game"
|
android:appCategory="game"
|
||||||
android:localeConfig="@xml/locales_config"
|
android:localeConfig="@xml/locales_config"
|
||||||
android:banner="@drawable/tv_banner"
|
android:banner="@drawable/tv_banner"
|
||||||
|
android:extractNativeLibs="true"
|
||||||
android:fullBackupContent="@xml/data_extraction_rules"
|
android:fullBackupContent="@xml/data_extraction_rules"
|
||||||
android:dataExtractionRules="@xml/data_extraction_rules_api_31"
|
android:dataExtractionRules="@xml/data_extraction_rules_api_31"
|
||||||
android:enableOnBackInvokedCallback="true">
|
android:enableOnBackInvokedCallback="true">
|
||||||
|
@ -15,9 +15,13 @@ import androidx.annotation.Keep
|
|||||||
import androidx.fragment.app.DialogFragment
|
import androidx.fragment.app.DialogFragment
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import java.lang.ref.WeakReference
|
import java.lang.ref.WeakReference
|
||||||
|
import org.yuzu.yuzu_emu.YuzuApplication.Companion.appContext
|
||||||
import org.yuzu.yuzu_emu.activities.EmulationActivity
|
import org.yuzu.yuzu_emu.activities.EmulationActivity
|
||||||
import org.yuzu.yuzu_emu.utils.DocumentsTree.Companion.isNativePath
|
import org.yuzu.yuzu_emu.utils.DocumentsTree.Companion.isNativePath
|
||||||
import org.yuzu.yuzu_emu.utils.FileUtil
|
import org.yuzu.yuzu_emu.utils.FileUtil.exists
|
||||||
|
import org.yuzu.yuzu_emu.utils.FileUtil.getFileSize
|
||||||
|
import org.yuzu.yuzu_emu.utils.FileUtil.isDirectory
|
||||||
|
import org.yuzu.yuzu_emu.utils.FileUtil.openContentUri
|
||||||
import org.yuzu.yuzu_emu.utils.Log
|
import org.yuzu.yuzu_emu.utils.Log
|
||||||
import org.yuzu.yuzu_emu.utils.SerializableHelper.serializable
|
import org.yuzu.yuzu_emu.utils.SerializableHelper.serializable
|
||||||
|
|
||||||
@ -71,7 +75,7 @@ object NativeLibrary {
|
|||||||
return if (isNativePath(path!!)) {
|
return if (isNativePath(path!!)) {
|
||||||
YuzuApplication.documentsTree!!.openContentUri(path, openmode)
|
YuzuApplication.documentsTree!!.openContentUri(path, openmode)
|
||||||
} else {
|
} else {
|
||||||
FileUtil.openContentUri(path, openmode)
|
openContentUri(appContext, path, openmode)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -81,7 +85,7 @@ object NativeLibrary {
|
|||||||
return if (isNativePath(path!!)) {
|
return if (isNativePath(path!!)) {
|
||||||
YuzuApplication.documentsTree!!.getFileSize(path)
|
YuzuApplication.documentsTree!!.getFileSize(path)
|
||||||
} else {
|
} else {
|
||||||
FileUtil.getFileSize(path)
|
getFileSize(appContext, path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -91,7 +95,7 @@ object NativeLibrary {
|
|||||||
return if (isNativePath(path!!)) {
|
return if (isNativePath(path!!)) {
|
||||||
YuzuApplication.documentsTree!!.exists(path)
|
YuzuApplication.documentsTree!!.exists(path)
|
||||||
} else {
|
} else {
|
||||||
FileUtil.exists(path)
|
exists(appContext, path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -101,7 +105,7 @@ object NativeLibrary {
|
|||||||
return if (isNativePath(path!!)) {
|
return if (isNativePath(path!!)) {
|
||||||
YuzuApplication.documentsTree!!.isDirectory(path)
|
YuzuApplication.documentsTree!!.isDirectory(path)
|
||||||
} else {
|
} else {
|
||||||
FileUtil.isDirectory(path)
|
isDirectory(appContext, path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,7 +47,7 @@ class YuzuApplication : Application() {
|
|||||||
application = this
|
application = this
|
||||||
documentsTree = DocumentsTree()
|
documentsTree = DocumentsTree()
|
||||||
DirectoryInitialization.start()
|
DirectoryInitialization.start()
|
||||||
GpuDriverHelper.initializeDriverParameters()
|
GpuDriverHelper.initializeDriverParameters(applicationContext)
|
||||||
NativeLibrary.logDeviceInfo()
|
NativeLibrary.logDeviceInfo()
|
||||||
|
|
||||||
createNotificationChannels()
|
createNotificationChannels()
|
||||||
|
@ -1,117 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
|
|
||||||
package org.yuzu.yuzu_emu.adapters
|
|
||||||
|
|
||||||
import android.text.TextUtils
|
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.View
|
|
||||||
import android.view.ViewGroup
|
|
||||||
import androidx.recyclerview.widget.AsyncDifferConfig
|
|
||||||
import androidx.recyclerview.widget.DiffUtil
|
|
||||||
import androidx.recyclerview.widget.ListAdapter
|
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
|
||||||
import org.yuzu.yuzu_emu.R
|
|
||||||
import org.yuzu.yuzu_emu.databinding.CardDriverOptionBinding
|
|
||||||
import org.yuzu.yuzu_emu.model.DriverViewModel
|
|
||||||
import org.yuzu.yuzu_emu.utils.GpuDriverHelper
|
|
||||||
import org.yuzu.yuzu_emu.utils.GpuDriverMetadata
|
|
||||||
|
|
||||||
class DriverAdapter(private val driverViewModel: DriverViewModel) :
|
|
||||||
ListAdapter<Pair<String, GpuDriverMetadata>, DriverAdapter.DriverViewHolder>(
|
|
||||||
AsyncDifferConfig.Builder(DiffCallback()).build()
|
|
||||||
) {
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DriverViewHolder {
|
|
||||||
val binding =
|
|
||||||
CardDriverOptionBinding.inflate(LayoutInflater.from(parent.context), parent, false)
|
|
||||||
return DriverViewHolder(binding)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getItemCount(): Int = currentList.size
|
|
||||||
|
|
||||||
override fun onBindViewHolder(holder: DriverViewHolder, position: Int) =
|
|
||||||
holder.bind(currentList[position])
|
|
||||||
|
|
||||||
private fun onSelectDriver(position: Int) {
|
|
||||||
driverViewModel.setSelectedDriverIndex(position)
|
|
||||||
notifyItemChanged(driverViewModel.previouslySelectedDriver)
|
|
||||||
notifyItemChanged(driverViewModel.selectedDriver)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun onDeleteDriver(driverData: Pair<String, GpuDriverMetadata>, position: Int) {
|
|
||||||
if (driverViewModel.selectedDriver > position) {
|
|
||||||
driverViewModel.setSelectedDriverIndex(driverViewModel.selectedDriver - 1)
|
|
||||||
}
|
|
||||||
if (GpuDriverHelper.customDriverData == driverData.second) {
|
|
||||||
driverViewModel.setSelectedDriverIndex(0)
|
|
||||||
}
|
|
||||||
driverViewModel.driversToDelete.add(driverData.first)
|
|
||||||
driverViewModel.removeDriver(driverData)
|
|
||||||
notifyItemRemoved(position)
|
|
||||||
notifyItemChanged(driverViewModel.selectedDriver)
|
|
||||||
}
|
|
||||||
|
|
||||||
inner class DriverViewHolder(val binding: CardDriverOptionBinding) :
|
|
||||||
RecyclerView.ViewHolder(binding.root) {
|
|
||||||
private lateinit var driverData: Pair<String, GpuDriverMetadata>
|
|
||||||
|
|
||||||
fun bind(driverData: Pair<String, GpuDriverMetadata>) {
|
|
||||||
this.driverData = driverData
|
|
||||||
val driver = driverData.second
|
|
||||||
|
|
||||||
binding.apply {
|
|
||||||
radioButton.isChecked = driverViewModel.selectedDriver == bindingAdapterPosition
|
|
||||||
root.setOnClickListener {
|
|
||||||
onSelectDriver(bindingAdapterPosition)
|
|
||||||
}
|
|
||||||
buttonDelete.setOnClickListener {
|
|
||||||
onDeleteDriver(driverData, bindingAdapterPosition)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delay marquee by 3s
|
|
||||||
title.postDelayed(
|
|
||||||
{
|
|
||||||
title.isSelected = true
|
|
||||||
title.ellipsize = TextUtils.TruncateAt.MARQUEE
|
|
||||||
version.isSelected = true
|
|
||||||
version.ellipsize = TextUtils.TruncateAt.MARQUEE
|
|
||||||
description.isSelected = true
|
|
||||||
description.ellipsize = TextUtils.TruncateAt.MARQUEE
|
|
||||||
},
|
|
||||||
3000
|
|
||||||
)
|
|
||||||
if (driver.name == null) {
|
|
||||||
title.setText(R.string.system_gpu_driver)
|
|
||||||
description.text = ""
|
|
||||||
version.text = ""
|
|
||||||
version.visibility = View.GONE
|
|
||||||
description.visibility = View.GONE
|
|
||||||
buttonDelete.visibility = View.GONE
|
|
||||||
} else {
|
|
||||||
title.text = driver.name
|
|
||||||
version.text = driver.version
|
|
||||||
description.text = driver.description
|
|
||||||
version.visibility = View.VISIBLE
|
|
||||||
description.visibility = View.VISIBLE
|
|
||||||
buttonDelete.visibility = View.VISIBLE
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class DiffCallback : DiffUtil.ItemCallback<Pair<String, GpuDriverMetadata>>() {
|
|
||||||
override fun areItemsTheSame(
|
|
||||||
oldItem: Pair<String, GpuDriverMetadata>,
|
|
||||||
newItem: Pair<String, GpuDriverMetadata>
|
|
||||||
): Boolean {
|
|
||||||
return oldItem.first == newItem.first
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun areContentsTheSame(
|
|
||||||
oldItem: Pair<String, GpuDriverMetadata>,
|
|
||||||
newItem: Pair<String, GpuDriverMetadata>
|
|
||||||
): Boolean {
|
|
||||||
return oldItem.second == newItem.second
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,186 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
|
|
||||||
package org.yuzu.yuzu_emu.fragments
|
|
||||||
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.View
|
|
||||||
import android.view.ViewGroup
|
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
|
||||||
import androidx.core.view.ViewCompat
|
|
||||||
import androidx.core.view.WindowInsetsCompat
|
|
||||||
import androidx.core.view.updatePadding
|
|
||||||
import androidx.fragment.app.Fragment
|
|
||||||
import androidx.fragment.app.activityViewModels
|
|
||||||
import androidx.lifecycle.lifecycleScope
|
|
||||||
import androidx.navigation.findNavController
|
|
||||||
import androidx.recyclerview.widget.GridLayoutManager
|
|
||||||
import com.google.android.material.transition.MaterialSharedAxis
|
|
||||||
import kotlinx.coroutines.flow.collectLatest
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import org.yuzu.yuzu_emu.R
|
|
||||||
import org.yuzu.yuzu_emu.adapters.DriverAdapter
|
|
||||||
import org.yuzu.yuzu_emu.databinding.FragmentDriverManagerBinding
|
|
||||||
import org.yuzu.yuzu_emu.model.DriverViewModel
|
|
||||||
import org.yuzu.yuzu_emu.model.HomeViewModel
|
|
||||||
import org.yuzu.yuzu_emu.utils.FileUtil
|
|
||||||
import org.yuzu.yuzu_emu.utils.GpuDriverHelper
|
|
||||||
import java.io.File
|
|
||||||
import java.io.IOException
|
|
||||||
|
|
||||||
class DriverManagerFragment : Fragment() {
|
|
||||||
private var _binding: FragmentDriverManagerBinding? = null
|
|
||||||
private val binding get() = _binding!!
|
|
||||||
|
|
||||||
private val homeViewModel: HomeViewModel by activityViewModels()
|
|
||||||
private val driverViewModel: DriverViewModel by activityViewModels()
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
|
||||||
super.onCreate(savedInstanceState)
|
|
||||||
enterTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
|
|
||||||
returnTransition = MaterialSharedAxis(MaterialSharedAxis.X, false)
|
|
||||||
reenterTransition = MaterialSharedAxis(MaterialSharedAxis.X, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreateView(
|
|
||||||
inflater: LayoutInflater,
|
|
||||||
container: ViewGroup?,
|
|
||||||
savedInstanceState: Bundle?
|
|
||||||
): View {
|
|
||||||
_binding = FragmentDriverManagerBinding.inflate(inflater)
|
|
||||||
return binding.root
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
|
||||||
super.onViewCreated(view, savedInstanceState)
|
|
||||||
homeViewModel.setNavigationVisibility(visible = false, animated = true)
|
|
||||||
homeViewModel.setStatusBarShadeVisibility(visible = false)
|
|
||||||
|
|
||||||
if (!driverViewModel.isInteractionAllowed) {
|
|
||||||
DriversLoadingDialogFragment().show(
|
|
||||||
childFragmentManager,
|
|
||||||
DriversLoadingDialogFragment.TAG
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
binding.toolbarDrivers.setNavigationOnClickListener {
|
|
||||||
binding.root.findNavController().popBackStack()
|
|
||||||
}
|
|
||||||
|
|
||||||
binding.buttonInstall.setOnClickListener {
|
|
||||||
getDriver.launch(arrayOf("application/zip"))
|
|
||||||
}
|
|
||||||
|
|
||||||
binding.listDrivers.apply {
|
|
||||||
layoutManager = GridLayoutManager(
|
|
||||||
requireContext(),
|
|
||||||
resources.getInteger(R.integer.grid_columns)
|
|
||||||
)
|
|
||||||
adapter = DriverAdapter(driverViewModel)
|
|
||||||
}
|
|
||||||
|
|
||||||
viewLifecycleOwner.lifecycleScope.apply {
|
|
||||||
launch {
|
|
||||||
driverViewModel.driverList.collectLatest {
|
|
||||||
(binding.listDrivers.adapter as DriverAdapter).submitList(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
launch {
|
|
||||||
driverViewModel.newDriverInstalled.collect {
|
|
||||||
if (_binding != null && it) {
|
|
||||||
(binding.listDrivers.adapter as DriverAdapter).apply {
|
|
||||||
notifyItemChanged(driverViewModel.previouslySelectedDriver)
|
|
||||||
notifyItemChanged(driverViewModel.selectedDriver)
|
|
||||||
driverViewModel.setNewDriverInstalled(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setInsets()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start installing requested driver
|
|
||||||
override fun onStop() {
|
|
||||||
super.onStop()
|
|
||||||
driverViewModel.onCloseDriverManager()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun setInsets() =
|
|
||||||
ViewCompat.setOnApplyWindowInsetsListener(
|
|
||||||
binding.root
|
|
||||||
) { _: View, windowInsets: WindowInsetsCompat ->
|
|
||||||
val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
|
|
||||||
val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout())
|
|
||||||
|
|
||||||
val leftInsets = barInsets.left + cutoutInsets.left
|
|
||||||
val rightInsets = barInsets.right + cutoutInsets.right
|
|
||||||
|
|
||||||
val mlpAppBar = binding.toolbarDrivers.layoutParams as ViewGroup.MarginLayoutParams
|
|
||||||
mlpAppBar.leftMargin = leftInsets
|
|
||||||
mlpAppBar.rightMargin = rightInsets
|
|
||||||
binding.toolbarDrivers.layoutParams = mlpAppBar
|
|
||||||
|
|
||||||
val mlplistDrivers = binding.listDrivers.layoutParams as ViewGroup.MarginLayoutParams
|
|
||||||
mlplistDrivers.leftMargin = leftInsets
|
|
||||||
mlplistDrivers.rightMargin = rightInsets
|
|
||||||
binding.listDrivers.layoutParams = mlplistDrivers
|
|
||||||
|
|
||||||
val fabSpacing = resources.getDimensionPixelSize(R.dimen.spacing_fab)
|
|
||||||
val mlpFab =
|
|
||||||
binding.buttonInstall.layoutParams as ViewGroup.MarginLayoutParams
|
|
||||||
mlpFab.leftMargin = leftInsets + fabSpacing
|
|
||||||
mlpFab.rightMargin = rightInsets + fabSpacing
|
|
||||||
mlpFab.bottomMargin = barInsets.bottom + fabSpacing
|
|
||||||
binding.buttonInstall.layoutParams = mlpFab
|
|
||||||
|
|
||||||
binding.listDrivers.updatePadding(
|
|
||||||
bottom = barInsets.bottom +
|
|
||||||
resources.getDimensionPixelSize(R.dimen.spacing_bottom_list_fab)
|
|
||||||
)
|
|
||||||
|
|
||||||
windowInsets
|
|
||||||
}
|
|
||||||
|
|
||||||
private val getDriver =
|
|
||||||
registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
|
|
||||||
if (result == null) {
|
|
||||||
return@registerForActivityResult
|
|
||||||
}
|
|
||||||
|
|
||||||
IndeterminateProgressDialogFragment.newInstance(
|
|
||||||
requireActivity(),
|
|
||||||
R.string.installing_driver,
|
|
||||||
false
|
|
||||||
) {
|
|
||||||
val driverPath =
|
|
||||||
"${GpuDriverHelper.driverStoragePath}/${FileUtil.getFilename(result)}"
|
|
||||||
val driverFile = File(driverPath)
|
|
||||||
|
|
||||||
// Ignore file exceptions when a user selects an invalid zip
|
|
||||||
try {
|
|
||||||
if (!GpuDriverHelper.copyDriverToInternalStorage(result)) {
|
|
||||||
throw IOException("Driver failed validation!")
|
|
||||||
}
|
|
||||||
} catch (_: IOException) {
|
|
||||||
if (driverFile.exists()) {
|
|
||||||
driverFile.delete()
|
|
||||||
}
|
|
||||||
return@newInstance getString(R.string.select_gpu_driver_error)
|
|
||||||
}
|
|
||||||
|
|
||||||
val driverData = GpuDriverHelper.getMetadataFromZip(driverFile)
|
|
||||||
val driverInList =
|
|
||||||
driverViewModel.driverList.value.firstOrNull { it.second == driverData }
|
|
||||||
if (driverInList != null) {
|
|
||||||
return@newInstance getString(R.string.driver_already_installed)
|
|
||||||
} else {
|
|
||||||
driverViewModel.addDriver(Pair(driverPath, driverData))
|
|
||||||
driverViewModel.setNewDriverInstalled(true)
|
|
||||||
}
|
|
||||||
return@newInstance Any()
|
|
||||||
}.show(childFragmentManager, IndeterminateProgressDialogFragment.TAG)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,75 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
|
|
||||||
package org.yuzu.yuzu_emu.fragments
|
|
||||||
|
|
||||||
import android.app.Dialog
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.View
|
|
||||||
import android.view.ViewGroup
|
|
||||||
import androidx.fragment.app.DialogFragment
|
|
||||||
import androidx.fragment.app.activityViewModels
|
|
||||||
import androidx.lifecycle.Lifecycle
|
|
||||||
import androidx.lifecycle.lifecycleScope
|
|
||||||
import androidx.lifecycle.repeatOnLifecycle
|
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import org.yuzu.yuzu_emu.R
|
|
||||||
import org.yuzu.yuzu_emu.databinding.DialogProgressBarBinding
|
|
||||||
import org.yuzu.yuzu_emu.model.DriverViewModel
|
|
||||||
|
|
||||||
class DriversLoadingDialogFragment : DialogFragment() {
|
|
||||||
private val driverViewModel: DriverViewModel by activityViewModels()
|
|
||||||
|
|
||||||
private lateinit var binding: DialogProgressBarBinding
|
|
||||||
|
|
||||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
|
||||||
binding = DialogProgressBarBinding.inflate(layoutInflater)
|
|
||||||
binding.progressBar.isIndeterminate = true
|
|
||||||
|
|
||||||
isCancelable = false
|
|
||||||
|
|
||||||
return MaterialAlertDialogBuilder(requireContext())
|
|
||||||
.setTitle(R.string.loading)
|
|
||||||
.setView(binding.root)
|
|
||||||
.create()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreateView(
|
|
||||||
inflater: LayoutInflater,
|
|
||||||
container: ViewGroup?,
|
|
||||||
savedInstanceState: Bundle?
|
|
||||||
): View = binding.root
|
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
|
||||||
super.onViewCreated(view, savedInstanceState)
|
|
||||||
viewLifecycleOwner.lifecycleScope.apply {
|
|
||||||
launch {
|
|
||||||
repeatOnLifecycle(Lifecycle.State.RESUMED) {
|
|
||||||
driverViewModel.areDriversLoading.collect { checkForDismiss() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
launch {
|
|
||||||
repeatOnLifecycle(Lifecycle.State.RESUMED) {
|
|
||||||
driverViewModel.isDriverReady.collect { checkForDismiss() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
launch {
|
|
||||||
repeatOnLifecycle(Lifecycle.State.RESUMED) {
|
|
||||||
driverViewModel.isDeletingDrivers.collect { checkForDismiss() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun checkForDismiss() {
|
|
||||||
if (driverViewModel.isInteractionAllowed) {
|
|
||||||
dismiss()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
const val TAG = "DriversLoadingDialogFragment"
|
|
||||||
}
|
|
||||||
}
|
|
@ -39,7 +39,6 @@ import androidx.window.layout.WindowLayoutInfo
|
|||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import com.google.android.material.slider.Slider
|
import com.google.android.material.slider.Slider
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.flow.collect
|
|
||||||
import kotlinx.coroutines.flow.collectLatest
|
import kotlinx.coroutines.flow.collectLatest
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.yuzu.yuzu_emu.HomeNavigationDirections
|
import org.yuzu.yuzu_emu.HomeNavigationDirections
|
||||||
@ -51,7 +50,6 @@ import org.yuzu.yuzu_emu.databinding.DialogOverlayAdjustBinding
|
|||||||
import org.yuzu.yuzu_emu.databinding.FragmentEmulationBinding
|
import org.yuzu.yuzu_emu.databinding.FragmentEmulationBinding
|
||||||
import org.yuzu.yuzu_emu.features.settings.model.IntSetting
|
import org.yuzu.yuzu_emu.features.settings.model.IntSetting
|
||||||
import org.yuzu.yuzu_emu.features.settings.model.Settings
|
import org.yuzu.yuzu_emu.features.settings.model.Settings
|
||||||
import org.yuzu.yuzu_emu.model.DriverViewModel
|
|
||||||
import org.yuzu.yuzu_emu.model.Game
|
import org.yuzu.yuzu_emu.model.Game
|
||||||
import org.yuzu.yuzu_emu.model.EmulationViewModel
|
import org.yuzu.yuzu_emu.model.EmulationViewModel
|
||||||
import org.yuzu.yuzu_emu.overlay.InputOverlay
|
import org.yuzu.yuzu_emu.overlay.InputOverlay
|
||||||
@ -72,7 +70,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
|||||||
private lateinit var game: Game
|
private lateinit var game: Game
|
||||||
|
|
||||||
private val emulationViewModel: EmulationViewModel by activityViewModels()
|
private val emulationViewModel: EmulationViewModel by activityViewModels()
|
||||||
private val driverViewModel: DriverViewModel by activityViewModels()
|
|
||||||
|
|
||||||
private var isInFoldableLayout = false
|
private var isInFoldableLayout = false
|
||||||
|
|
||||||
@ -302,21 +299,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
launch {
|
|
||||||
repeatOnLifecycle(Lifecycle.State.RESUMED) {
|
|
||||||
driverViewModel.isDriverReady.collect {
|
|
||||||
if (it && !emulationState.isRunning) {
|
|
||||||
if (!DirectoryInitialization.areDirectoriesReady) {
|
|
||||||
DirectoryInitialization.start()
|
|
||||||
}
|
|
||||||
|
|
||||||
updateScreenLayout()
|
|
||||||
|
|
||||||
emulationState.run(emulationActivity!!.isActivityRecreated)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -350,6 +332,17 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onResume() {
|
||||||
|
super.onResume()
|
||||||
|
if (!DirectoryInitialization.areDirectoriesReady) {
|
||||||
|
DirectoryInitialization.start()
|
||||||
|
}
|
||||||
|
|
||||||
|
updateScreenLayout()
|
||||||
|
|
||||||
|
emulationState.run(emulationActivity!!.isActivityRecreated)
|
||||||
|
}
|
||||||
|
|
||||||
override fun onPause() {
|
override fun onPause() {
|
||||||
if (emulationState.isRunning && emulationActivity?.isInPictureInPictureMode != true) {
|
if (emulationState.isRunning && emulationActivity?.isInPictureInPictureMode != true) {
|
||||||
emulationState.pause()
|
emulationState.pause()
|
||||||
|
@ -5,6 +5,7 @@ package org.yuzu.yuzu_emu.fragments
|
|||||||
|
|
||||||
import android.Manifest
|
import android.Manifest
|
||||||
import android.content.ActivityNotFoundException
|
import android.content.ActivityNotFoundException
|
||||||
|
import android.content.DialogInterface
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
@ -27,6 +28,7 @@ import androidx.fragment.app.activityViewModels
|
|||||||
import androidx.navigation.findNavController
|
import androidx.navigation.findNavController
|
||||||
import androidx.navigation.fragment.findNavController
|
import androidx.navigation.fragment.findNavController
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import com.google.android.material.transition.MaterialSharedAxis
|
import com.google.android.material.transition.MaterialSharedAxis
|
||||||
import org.yuzu.yuzu_emu.BuildConfig
|
import org.yuzu.yuzu_emu.BuildConfig
|
||||||
import org.yuzu.yuzu_emu.HomeNavigationDirections
|
import org.yuzu.yuzu_emu.HomeNavigationDirections
|
||||||
@ -35,7 +37,6 @@ import org.yuzu.yuzu_emu.adapters.HomeSettingAdapter
|
|||||||
import org.yuzu.yuzu_emu.databinding.FragmentHomeSettingsBinding
|
import org.yuzu.yuzu_emu.databinding.FragmentHomeSettingsBinding
|
||||||
import org.yuzu.yuzu_emu.features.DocumentProvider
|
import org.yuzu.yuzu_emu.features.DocumentProvider
|
||||||
import org.yuzu.yuzu_emu.features.settings.model.Settings
|
import org.yuzu.yuzu_emu.features.settings.model.Settings
|
||||||
import org.yuzu.yuzu_emu.model.DriverViewModel
|
|
||||||
import org.yuzu.yuzu_emu.model.HomeSetting
|
import org.yuzu.yuzu_emu.model.HomeSetting
|
||||||
import org.yuzu.yuzu_emu.model.HomeViewModel
|
import org.yuzu.yuzu_emu.model.HomeViewModel
|
||||||
import org.yuzu.yuzu_emu.ui.main.MainActivity
|
import org.yuzu.yuzu_emu.ui.main.MainActivity
|
||||||
@ -49,7 +50,6 @@ class HomeSettingsFragment : Fragment() {
|
|||||||
private lateinit var mainActivity: MainActivity
|
private lateinit var mainActivity: MainActivity
|
||||||
|
|
||||||
private val homeViewModel: HomeViewModel by activityViewModels()
|
private val homeViewModel: HomeViewModel by activityViewModels()
|
||||||
private val driverViewModel: DriverViewModel by activityViewModels()
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
@ -107,17 +107,13 @@ class HomeSettingsFragment : Fragment() {
|
|||||||
)
|
)
|
||||||
add(
|
add(
|
||||||
HomeSetting(
|
HomeSetting(
|
||||||
R.string.gpu_driver_manager,
|
R.string.install_gpu_driver,
|
||||||
R.string.install_gpu_driver_description,
|
R.string.install_gpu_driver_description,
|
||||||
R.drawable.ic_build,
|
R.drawable.ic_exit,
|
||||||
{
|
{ driverInstaller() },
|
||||||
binding.root.findNavController()
|
|
||||||
.navigate(R.id.action_homeSettingsFragment_to_driverManagerFragment)
|
|
||||||
},
|
|
||||||
{ GpuDriverHelper.supportsCustomDriverLoading() },
|
{ GpuDriverHelper.supportsCustomDriverLoading() },
|
||||||
R.string.custom_driver_not_supported,
|
R.string.custom_driver_not_supported,
|
||||||
R.string.custom_driver_not_supported_description,
|
R.string.custom_driver_not_supported_description
|
||||||
driverViewModel.selectedDriverMetadata
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
add(
|
add(
|
||||||
@ -296,6 +292,31 @@ class HomeSettingsFragment : Fragment() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun driverInstaller() {
|
||||||
|
// Get the driver name for the dialog message.
|
||||||
|
var driverName = GpuDriverHelper.customDriverName
|
||||||
|
if (driverName == null) {
|
||||||
|
driverName = getString(R.string.system_gpu_driver)
|
||||||
|
}
|
||||||
|
|
||||||
|
MaterialAlertDialogBuilder(requireContext())
|
||||||
|
.setTitle(getString(R.string.select_gpu_driver_title))
|
||||||
|
.setMessage(driverName)
|
||||||
|
.setNegativeButton(android.R.string.cancel, null)
|
||||||
|
.setNeutralButton(R.string.select_gpu_driver_default) { _: DialogInterface?, _: Int ->
|
||||||
|
GpuDriverHelper.installDefaultDriver(requireContext())
|
||||||
|
Toast.makeText(
|
||||||
|
requireContext(),
|
||||||
|
R.string.select_gpu_driver_use_default,
|
||||||
|
Toast.LENGTH_SHORT
|
||||||
|
).show()
|
||||||
|
}
|
||||||
|
.setPositiveButton(R.string.select_gpu_driver_install) { _: DialogInterface?, _: Int ->
|
||||||
|
mainActivity.getDriver.launch(arrayOf("application/zip"))
|
||||||
|
}
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
|
||||||
private fun shareLog() {
|
private fun shareLog() {
|
||||||
val file = DocumentFile.fromSingleUri(
|
val file = DocumentFile.fromSingleUri(
|
||||||
mainActivity,
|
mainActivity,
|
||||||
|
@ -10,8 +10,8 @@ import android.view.View
|
|||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.appcompat.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.fragment.app.DialogFragment
|
import androidx.fragment.app.DialogFragment
|
||||||
import androidx.fragment.app.FragmentActivity
|
|
||||||
import androidx.fragment.app.activityViewModels
|
import androidx.fragment.app.activityViewModels
|
||||||
import androidx.lifecycle.Lifecycle
|
import androidx.lifecycle.Lifecycle
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
@ -78,10 +78,6 @@ class IndeterminateProgressDialogFragment : DialogFragment() {
|
|||||||
requireActivity().supportFragmentManager,
|
requireActivity().supportFragmentManager,
|
||||||
MessageDialogFragment.TAG
|
MessageDialogFragment.TAG
|
||||||
)
|
)
|
||||||
|
|
||||||
else -> {
|
|
||||||
// Do nothing
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
taskViewModel.clear()
|
taskViewModel.clear()
|
||||||
}
|
}
|
||||||
@ -119,7 +115,7 @@ class IndeterminateProgressDialogFragment : DialogFragment() {
|
|||||||
private const val CANCELLABLE = "Cancellable"
|
private const val CANCELLABLE = "Cancellable"
|
||||||
|
|
||||||
fun newInstance(
|
fun newInstance(
|
||||||
activity: FragmentActivity,
|
activity: AppCompatActivity,
|
||||||
titleId: Int,
|
titleId: Int,
|
||||||
cancellable: Boolean = false,
|
cancellable: Boolean = false,
|
||||||
task: () -> Any
|
task: () -> Any
|
||||||
|
@ -1,158 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
|
|
||||||
package org.yuzu.yuzu_emu.model
|
|
||||||
|
|
||||||
import androidx.lifecycle.ViewModel
|
|
||||||
import androidx.lifecycle.viewModelScope
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import kotlinx.coroutines.withContext
|
|
||||||
import org.yuzu.yuzu_emu.R
|
|
||||||
import org.yuzu.yuzu_emu.YuzuApplication
|
|
||||||
import org.yuzu.yuzu_emu.utils.FileUtil
|
|
||||||
import org.yuzu.yuzu_emu.utils.GpuDriverHelper
|
|
||||||
import org.yuzu.yuzu_emu.utils.GpuDriverMetadata
|
|
||||||
import java.io.BufferedOutputStream
|
|
||||||
import java.io.File
|
|
||||||
|
|
||||||
class DriverViewModel : ViewModel() {
|
|
||||||
private val _areDriversLoading = MutableStateFlow(false)
|
|
||||||
val areDriversLoading: StateFlow<Boolean> get() = _areDriversLoading
|
|
||||||
|
|
||||||
private val _isDriverReady = MutableStateFlow(true)
|
|
||||||
val isDriverReady: StateFlow<Boolean> get() = _isDriverReady
|
|
||||||
|
|
||||||
private val _isDeletingDrivers = MutableStateFlow(false)
|
|
||||||
val isDeletingDrivers: StateFlow<Boolean> get() = _isDeletingDrivers
|
|
||||||
|
|
||||||
private val _driverList = MutableStateFlow(mutableListOf<Pair<String, GpuDriverMetadata>>())
|
|
||||||
val driverList: StateFlow<MutableList<Pair<String, GpuDriverMetadata>>> get() = _driverList
|
|
||||||
|
|
||||||
var previouslySelectedDriver = 0
|
|
||||||
var selectedDriver = -1
|
|
||||||
|
|
||||||
private val _selectedDriverMetadata =
|
|
||||||
MutableStateFlow(
|
|
||||||
GpuDriverHelper.customDriverData.name
|
|
||||||
?: YuzuApplication.appContext.getString(R.string.system_gpu_driver)
|
|
||||||
)
|
|
||||||
val selectedDriverMetadata: StateFlow<String> get() = _selectedDriverMetadata
|
|
||||||
|
|
||||||
private val _newDriverInstalled = MutableStateFlow(false)
|
|
||||||
val newDriverInstalled: StateFlow<Boolean> get() = _newDriverInstalled
|
|
||||||
|
|
||||||
val driversToDelete = mutableListOf<String>()
|
|
||||||
|
|
||||||
val isInteractionAllowed
|
|
||||||
get() = !areDriversLoading.value && isDriverReady.value && !isDeletingDrivers.value
|
|
||||||
|
|
||||||
init {
|
|
||||||
_areDriversLoading.value = true
|
|
||||||
viewModelScope.launch {
|
|
||||||
withContext(Dispatchers.IO) {
|
|
||||||
val drivers = GpuDriverHelper.getDrivers()
|
|
||||||
val currentDriverMetadata = GpuDriverHelper.customDriverData
|
|
||||||
for (i in drivers.indices) {
|
|
||||||
if (drivers[i].second == currentDriverMetadata) {
|
|
||||||
setSelectedDriverIndex(i)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If a user had installed a driver before the manager was implemented, this zips
|
|
||||||
// the installed driver to UserData/gpu_drivers/CustomDriver.zip so that it can
|
|
||||||
// be indexed and exported as expected.
|
|
||||||
if (selectedDriver == -1) {
|
|
||||||
val driverToSave =
|
|
||||||
File(GpuDriverHelper.driverStoragePath, "CustomDriver.zip")
|
|
||||||
driverToSave.createNewFile()
|
|
||||||
FileUtil.zipFromInternalStorage(
|
|
||||||
File(GpuDriverHelper.driverInstallationPath!!),
|
|
||||||
GpuDriverHelper.driverInstallationPath!!,
|
|
||||||
BufferedOutputStream(driverToSave.outputStream())
|
|
||||||
)
|
|
||||||
drivers.add(Pair(driverToSave.path, currentDriverMetadata))
|
|
||||||
setSelectedDriverIndex(drivers.size - 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
_driverList.value = drivers
|
|
||||||
_areDriversLoading.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun setSelectedDriverIndex(value: Int) {
|
|
||||||
if (selectedDriver != -1) {
|
|
||||||
previouslySelectedDriver = selectedDriver
|
|
||||||
}
|
|
||||||
selectedDriver = value
|
|
||||||
}
|
|
||||||
|
|
||||||
fun setNewDriverInstalled(value: Boolean) {
|
|
||||||
_newDriverInstalled.value = value
|
|
||||||
}
|
|
||||||
|
|
||||||
fun addDriver(driverData: Pair<String, GpuDriverMetadata>) {
|
|
||||||
val driverIndex = _driverList.value.indexOfFirst { it == driverData }
|
|
||||||
if (driverIndex == -1) {
|
|
||||||
setSelectedDriverIndex(_driverList.value.size)
|
|
||||||
_driverList.value.add(driverData)
|
|
||||||
_selectedDriverMetadata.value = driverData.second.name
|
|
||||||
?: YuzuApplication.appContext.getString(R.string.system_gpu_driver)
|
|
||||||
} else {
|
|
||||||
setSelectedDriverIndex(driverIndex)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun removeDriver(driverData: Pair<String, GpuDriverMetadata>) {
|
|
||||||
_driverList.value.remove(driverData)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun onCloseDriverManager() {
|
|
||||||
_isDeletingDrivers.value = true
|
|
||||||
viewModelScope.launch {
|
|
||||||
withContext(Dispatchers.IO) {
|
|
||||||
driversToDelete.forEach {
|
|
||||||
val driver = File(it)
|
|
||||||
if (driver.exists()) {
|
|
||||||
driver.delete()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
driversToDelete.clear()
|
|
||||||
_isDeletingDrivers.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (GpuDriverHelper.customDriverData == driverList.value[selectedDriver].second) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
_isDriverReady.value = false
|
|
||||||
viewModelScope.launch {
|
|
||||||
withContext(Dispatchers.IO) {
|
|
||||||
if (selectedDriver == 0) {
|
|
||||||
GpuDriverHelper.installDefaultDriver()
|
|
||||||
setDriverReady()
|
|
||||||
return@withContext
|
|
||||||
}
|
|
||||||
|
|
||||||
val driverToInstall = File(driverList.value[selectedDriver].first)
|
|
||||||
if (driverToInstall.exists()) {
|
|
||||||
GpuDriverHelper.installCustomDriver(driverToInstall)
|
|
||||||
} else {
|
|
||||||
GpuDriverHelper.installDefaultDriver()
|
|
||||||
}
|
|
||||||
setDriverReady()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun setDriverReady() {
|
|
||||||
_isDriverReady.value = true
|
|
||||||
_selectedDriverMetadata.value = GpuDriverHelper.customDriverData.name
|
|
||||||
?: YuzuApplication.appContext.getString(R.string.system_gpu_driver)
|
|
||||||
}
|
|
||||||
}
|
|
@ -29,10 +29,12 @@ import androidx.navigation.fragment.NavHostFragment
|
|||||||
import androidx.navigation.ui.setupWithNavController
|
import androidx.navigation.ui.setupWithNavController
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
import com.google.android.material.color.MaterialColors
|
import com.google.android.material.color.MaterialColors
|
||||||
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import com.google.android.material.navigation.NavigationBarView
|
import com.google.android.material.navigation.NavigationBarView
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.FilenameFilter
|
import java.io.FilenameFilter
|
||||||
|
import java.io.IOException
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
@ -41,6 +43,7 @@ import org.yuzu.yuzu_emu.NativeLibrary
|
|||||||
import org.yuzu.yuzu_emu.R
|
import org.yuzu.yuzu_emu.R
|
||||||
import org.yuzu.yuzu_emu.activities.EmulationActivity
|
import org.yuzu.yuzu_emu.activities.EmulationActivity
|
||||||
import org.yuzu.yuzu_emu.databinding.ActivityMainBinding
|
import org.yuzu.yuzu_emu.databinding.ActivityMainBinding
|
||||||
|
import org.yuzu.yuzu_emu.databinding.DialogProgressBarBinding
|
||||||
import org.yuzu.yuzu_emu.features.DocumentProvider
|
import org.yuzu.yuzu_emu.features.DocumentProvider
|
||||||
import org.yuzu.yuzu_emu.features.settings.model.Settings
|
import org.yuzu.yuzu_emu.features.settings.model.Settings
|
||||||
import org.yuzu.yuzu_emu.fragments.IndeterminateProgressDialogFragment
|
import org.yuzu.yuzu_emu.fragments.IndeterminateProgressDialogFragment
|
||||||
@ -340,10 +343,11 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
|||||||
|
|
||||||
val dstPath = DirectoryInitialization.userDirectory + "/keys/"
|
val dstPath = DirectoryInitialization.userDirectory + "/keys/"
|
||||||
if (FileUtil.copyUriToInternalStorage(
|
if (FileUtil.copyUriToInternalStorage(
|
||||||
|
applicationContext,
|
||||||
result,
|
result,
|
||||||
dstPath,
|
dstPath,
|
||||||
"prod.keys"
|
"prod.keys"
|
||||||
) != null
|
)
|
||||||
) {
|
) {
|
||||||
if (NativeLibrary.reloadKeys()) {
|
if (NativeLibrary.reloadKeys()) {
|
||||||
Toast.makeText(
|
Toast.makeText(
|
||||||
@ -442,10 +446,11 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
|||||||
|
|
||||||
val dstPath = DirectoryInitialization.userDirectory + "/keys/"
|
val dstPath = DirectoryInitialization.userDirectory + "/keys/"
|
||||||
if (FileUtil.copyUriToInternalStorage(
|
if (FileUtil.copyUriToInternalStorage(
|
||||||
|
applicationContext,
|
||||||
result,
|
result,
|
||||||
dstPath,
|
dstPath,
|
||||||
"key_retail.bin"
|
"key_retail.bin"
|
||||||
) != null
|
)
|
||||||
) {
|
) {
|
||||||
if (NativeLibrary.reloadKeys()) {
|
if (NativeLibrary.reloadKeys()) {
|
||||||
Toast.makeText(
|
Toast.makeText(
|
||||||
@ -464,6 +469,59 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val getDriver =
|
||||||
|
registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
|
||||||
|
if (result == null) {
|
||||||
|
return@registerForActivityResult
|
||||||
|
}
|
||||||
|
|
||||||
|
val takeFlags =
|
||||||
|
Intent.FLAG_GRANT_WRITE_URI_PERMISSION or Intent.FLAG_GRANT_READ_URI_PERMISSION
|
||||||
|
contentResolver.takePersistableUriPermission(
|
||||||
|
result,
|
||||||
|
takeFlags
|
||||||
|
)
|
||||||
|
|
||||||
|
val progressBinding = DialogProgressBarBinding.inflate(layoutInflater)
|
||||||
|
progressBinding.progressBar.isIndeterminate = true
|
||||||
|
val installationDialog = MaterialAlertDialogBuilder(this)
|
||||||
|
.setTitle(R.string.installing_driver)
|
||||||
|
.setView(progressBinding.root)
|
||||||
|
.show()
|
||||||
|
|
||||||
|
lifecycleScope.launch {
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
// Ignore file exceptions when a user selects an invalid zip
|
||||||
|
try {
|
||||||
|
GpuDriverHelper.installCustomDriver(applicationContext, result)
|
||||||
|
} catch (_: IOException) {
|
||||||
|
}
|
||||||
|
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
installationDialog.dismiss()
|
||||||
|
|
||||||
|
val driverName = GpuDriverHelper.customDriverName
|
||||||
|
if (driverName != null) {
|
||||||
|
Toast.makeText(
|
||||||
|
applicationContext,
|
||||||
|
getString(
|
||||||
|
R.string.select_gpu_driver_install_success,
|
||||||
|
driverName
|
||||||
|
),
|
||||||
|
Toast.LENGTH_SHORT
|
||||||
|
).show()
|
||||||
|
} else {
|
||||||
|
Toast.makeText(
|
||||||
|
applicationContext,
|
||||||
|
R.string.select_gpu_driver_error,
|
||||||
|
Toast.LENGTH_LONG
|
||||||
|
).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val installGameUpdate = registerForActivityResult(
|
val installGameUpdate = registerForActivityResult(
|
||||||
ActivityResultContracts.OpenMultipleDocuments()
|
ActivityResultContracts.OpenMultipleDocuments()
|
||||||
) { documents: List<Uri> ->
|
) { documents: List<Uri> ->
|
||||||
|
@ -7,6 +7,7 @@ import android.net.Uri
|
|||||||
import androidx.documentfile.provider.DocumentFile
|
import androidx.documentfile.provider.DocumentFile
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
import org.yuzu.yuzu_emu.YuzuApplication
|
||||||
import org.yuzu.yuzu_emu.model.MinimalDocumentFile
|
import org.yuzu.yuzu_emu.model.MinimalDocumentFile
|
||||||
|
|
||||||
class DocumentsTree {
|
class DocumentsTree {
|
||||||
@ -21,7 +22,7 @@ class DocumentsTree {
|
|||||||
|
|
||||||
fun openContentUri(filepath: String, openMode: String?): Int {
|
fun openContentUri(filepath: String, openMode: String?): Int {
|
||||||
val node = resolvePath(filepath) ?: return -1
|
val node = resolvePath(filepath) ?: return -1
|
||||||
return FileUtil.openContentUri(node.uri.toString(), openMode)
|
return FileUtil.openContentUri(YuzuApplication.appContext, node.uri.toString(), openMode)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getFileSize(filepath: String): Long {
|
fun getFileSize(filepath: String): Long {
|
||||||
@ -29,7 +30,7 @@ class DocumentsTree {
|
|||||||
return if (node == null || node.isDirectory) {
|
return if (node == null || node.isDirectory) {
|
||||||
0
|
0
|
||||||
} else {
|
} else {
|
||||||
FileUtil.getFileSize(node.uri.toString())
|
FileUtil.getFileSize(YuzuApplication.appContext, node.uri.toString())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -66,7 +67,7 @@ class DocumentsTree {
|
|||||||
* @param parent parent node of this level
|
* @param parent parent node of this level
|
||||||
*/
|
*/
|
||||||
private fun structTree(parent: DocumentsNode) {
|
private fun structTree(parent: DocumentsNode) {
|
||||||
val documents = FileUtil.listFiles(parent.uri!!)
|
val documents = FileUtil.listFiles(YuzuApplication.appContext, parent.uri!!)
|
||||||
for (document in documents) {
|
for (document in documents) {
|
||||||
val node = DocumentsNode(document)
|
val node = DocumentsNode(document)
|
||||||
node.parent = parent
|
node.parent = parent
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
package org.yuzu.yuzu_emu.utils
|
package org.yuzu.yuzu_emu.utils
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
import android.database.Cursor
|
import android.database.Cursor
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.provider.DocumentsContract
|
import android.provider.DocumentsContract
|
||||||
@ -10,6 +11,7 @@ import androidx.documentfile.provider.DocumentFile
|
|||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import java.io.BufferedInputStream
|
import java.io.BufferedInputStream
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
import java.io.FileOutputStream
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import java.net.URLDecoder
|
import java.net.URLDecoder
|
||||||
@ -19,8 +21,6 @@ import org.yuzu.yuzu_emu.YuzuApplication
|
|||||||
import org.yuzu.yuzu_emu.model.MinimalDocumentFile
|
import org.yuzu.yuzu_emu.model.MinimalDocumentFile
|
||||||
import org.yuzu.yuzu_emu.model.TaskState
|
import org.yuzu.yuzu_emu.model.TaskState
|
||||||
import java.io.BufferedOutputStream
|
import java.io.BufferedOutputStream
|
||||||
import java.lang.NullPointerException
|
|
||||||
import java.nio.charset.StandardCharsets
|
|
||||||
import java.util.zip.ZipOutputStream
|
import java.util.zip.ZipOutputStream
|
||||||
|
|
||||||
object FileUtil {
|
object FileUtil {
|
||||||
@ -29,8 +29,6 @@ object FileUtil {
|
|||||||
const val APPLICATION_OCTET_STREAM = "application/octet-stream"
|
const val APPLICATION_OCTET_STREAM = "application/octet-stream"
|
||||||
const val TEXT_PLAIN = "text/plain"
|
const val TEXT_PLAIN = "text/plain"
|
||||||
|
|
||||||
private val context get() = YuzuApplication.appContext
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a file from directory with filename.
|
* Create a file from directory with filename.
|
||||||
* @param context Application context
|
* @param context Application context
|
||||||
@ -38,11 +36,11 @@ object FileUtil {
|
|||||||
* @param filename file display name.
|
* @param filename file display name.
|
||||||
* @return boolean
|
* @return boolean
|
||||||
*/
|
*/
|
||||||
fun createFile(directory: String?, filename: String): DocumentFile? {
|
fun createFile(context: Context?, directory: String?, filename: String): DocumentFile? {
|
||||||
var decodedFilename = filename
|
var decodedFilename = filename
|
||||||
try {
|
try {
|
||||||
val directoryUri = Uri.parse(directory)
|
val directoryUri = Uri.parse(directory)
|
||||||
val parent = DocumentFile.fromTreeUri(context, directoryUri) ?: return null
|
val parent = DocumentFile.fromTreeUri(context!!, directoryUri) ?: return null
|
||||||
decodedFilename = URLDecoder.decode(decodedFilename, DECODE_METHOD)
|
decodedFilename = URLDecoder.decode(decodedFilename, DECODE_METHOD)
|
||||||
var mimeType = APPLICATION_OCTET_STREAM
|
var mimeType = APPLICATION_OCTET_STREAM
|
||||||
if (decodedFilename.endsWith(".txt")) {
|
if (decodedFilename.endsWith(".txt")) {
|
||||||
@ -58,15 +56,16 @@ object FileUtil {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a directory from directory with filename.
|
* Create a directory from directory with filename.
|
||||||
|
* @param context Application context
|
||||||
* @param directory parent path for directory.
|
* @param directory parent path for directory.
|
||||||
* @param directoryName directory display name.
|
* @param directoryName directory display name.
|
||||||
* @return boolean
|
* @return boolean
|
||||||
*/
|
*/
|
||||||
fun createDir(directory: String?, directoryName: String?): DocumentFile? {
|
fun createDir(context: Context?, directory: String?, directoryName: String?): DocumentFile? {
|
||||||
var decodedDirectoryName = directoryName
|
var decodedDirectoryName = directoryName
|
||||||
try {
|
try {
|
||||||
val directoryUri = Uri.parse(directory)
|
val directoryUri = Uri.parse(directory)
|
||||||
val parent = DocumentFile.fromTreeUri(context, directoryUri) ?: return null
|
val parent = DocumentFile.fromTreeUri(context!!, directoryUri) ?: return null
|
||||||
decodedDirectoryName = URLDecoder.decode(decodedDirectoryName, DECODE_METHOD)
|
decodedDirectoryName = URLDecoder.decode(decodedDirectoryName, DECODE_METHOD)
|
||||||
val isExist = parent.findFile(decodedDirectoryName)
|
val isExist = parent.findFile(decodedDirectoryName)
|
||||||
return isExist ?: parent.createDirectory(decodedDirectoryName)
|
return isExist ?: parent.createDirectory(decodedDirectoryName)
|
||||||
@ -78,12 +77,13 @@ object FileUtil {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Open content uri and return file descriptor to JNI.
|
* Open content uri and return file descriptor to JNI.
|
||||||
|
* @param context Application context
|
||||||
* @param path Native content uri path
|
* @param path Native content uri path
|
||||||
* @param openMode will be one of "r", "r", "rw", "wa", "rwa"
|
* @param openMode will be one of "r", "r", "rw", "wa", "rwa"
|
||||||
* @return file descriptor
|
* @return file descriptor
|
||||||
*/
|
*/
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun openContentUri(path: String, openMode: String?): Int {
|
fun openContentUri(context: Context, path: String, openMode: String?): Int {
|
||||||
try {
|
try {
|
||||||
val uri = Uri.parse(path)
|
val uri = Uri.parse(path)
|
||||||
val parcelFileDescriptor = context.contentResolver.openFileDescriptor(uri, openMode!!)
|
val parcelFileDescriptor = context.contentResolver.openFileDescriptor(uri, openMode!!)
|
||||||
@ -103,10 +103,11 @@ object FileUtil {
|
|||||||
/**
|
/**
|
||||||
* Reference: https://stackoverflow.com/questions/42186820/documentfile-is-very-slow
|
* Reference: https://stackoverflow.com/questions/42186820/documentfile-is-very-slow
|
||||||
* This function will be faster than DoucmentFile.listFiles
|
* This function will be faster than DoucmentFile.listFiles
|
||||||
|
* @param context Application context
|
||||||
* @param uri Directory uri.
|
* @param uri Directory uri.
|
||||||
* @return CheapDocument lists.
|
* @return CheapDocument lists.
|
||||||
*/
|
*/
|
||||||
fun listFiles(uri: Uri): Array<MinimalDocumentFile> {
|
fun listFiles(context: Context, uri: Uri): Array<MinimalDocumentFile> {
|
||||||
val resolver = context.contentResolver
|
val resolver = context.contentResolver
|
||||||
val columns = arrayOf(
|
val columns = arrayOf(
|
||||||
DocumentsContract.Document.COLUMN_DOCUMENT_ID,
|
DocumentsContract.Document.COLUMN_DOCUMENT_ID,
|
||||||
@ -144,7 +145,7 @@ object FileUtil {
|
|||||||
* @param path Native content uri path
|
* @param path Native content uri path
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
fun exists(path: String?): Boolean {
|
fun exists(context: Context, path: String?): Boolean {
|
||||||
var c: Cursor? = null
|
var c: Cursor? = null
|
||||||
try {
|
try {
|
||||||
val mUri = Uri.parse(path)
|
val mUri = Uri.parse(path)
|
||||||
@ -164,7 +165,7 @@ object FileUtil {
|
|||||||
* @param path content uri path
|
* @param path content uri path
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
fun isDirectory(path: String): Boolean {
|
fun isDirectory(context: Context, path: String): Boolean {
|
||||||
val resolver = context.contentResolver
|
val resolver = context.contentResolver
|
||||||
val columns = arrayOf(
|
val columns = arrayOf(
|
||||||
DocumentsContract.Document.COLUMN_MIME_TYPE
|
DocumentsContract.Document.COLUMN_MIME_TYPE
|
||||||
@ -209,10 +210,10 @@ object FileUtil {
|
|||||||
return filename
|
return filename
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getFilesName(path: String): Array<String> {
|
fun getFilesName(context: Context, path: String): Array<String> {
|
||||||
val uri = Uri.parse(path)
|
val uri = Uri.parse(path)
|
||||||
val files: MutableList<String> = ArrayList()
|
val files: MutableList<String> = ArrayList()
|
||||||
for (file in listFiles(uri)) {
|
for (file in listFiles(context, uri)) {
|
||||||
files.add(file.filename)
|
files.add(file.filename)
|
||||||
}
|
}
|
||||||
return files.toTypedArray()
|
return files.toTypedArray()
|
||||||
@ -224,7 +225,7 @@ object FileUtil {
|
|||||||
* @return long file size
|
* @return long file size
|
||||||
*/
|
*/
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun getFileSize(path: String): Long {
|
fun getFileSize(context: Context, path: String): Long {
|
||||||
val resolver = context.contentResolver
|
val resolver = context.contentResolver
|
||||||
val columns = arrayOf(
|
val columns = arrayOf(
|
||||||
DocumentsContract.Document.COLUMN_SIZE
|
DocumentsContract.Document.COLUMN_SIZE
|
||||||
@ -244,38 +245,44 @@ object FileUtil {
|
|||||||
return size
|
return size
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates an input stream with a given [Uri] and copies its data to the given path. This will
|
|
||||||
* overwrite any pre-existing files.
|
|
||||||
*
|
|
||||||
* @param sourceUri The [Uri] to copy data from
|
|
||||||
* @param destinationParentPath Destination directory
|
|
||||||
* @param destinationFilename Optionally renames the file once copied
|
|
||||||
*/
|
|
||||||
fun copyUriToInternalStorage(
|
fun copyUriToInternalStorage(
|
||||||
sourceUri: Uri,
|
context: Context,
|
||||||
|
sourceUri: Uri?,
|
||||||
destinationParentPath: String,
|
destinationParentPath: String,
|
||||||
destinationFilename: String = ""
|
destinationFilename: String
|
||||||
): File? =
|
): Boolean {
|
||||||
|
var input: InputStream? = null
|
||||||
|
var output: FileOutputStream? = null
|
||||||
try {
|
try {
|
||||||
val fileName =
|
input = context.contentResolver.openInputStream(sourceUri!!)
|
||||||
if (destinationFilename == "") getFilename(sourceUri) else "/$destinationFilename"
|
output = FileOutputStream("$destinationParentPath/$destinationFilename")
|
||||||
val inputStream = context.contentResolver.openInputStream(sourceUri)!!
|
val buffer = ByteArray(1024)
|
||||||
|
var len: Int
|
||||||
val destinationFile = File("$destinationParentPath$fileName")
|
while (input!!.read(buffer).also { len = it } != -1) {
|
||||||
if (destinationFile.exists()) {
|
output.write(buffer, 0, len)
|
||||||
destinationFile.delete()
|
|
||||||
}
|
}
|
||||||
|
output.flush()
|
||||||
destinationFile.outputStream().use { fos ->
|
return true
|
||||||
inputStream.use { it.copyTo(fos) }
|
} catch (e: Exception) {
|
||||||
|
Log.error("[FileUtil]: Cannot copy file, error: " + e.message)
|
||||||
|
} finally {
|
||||||
|
if (input != null) {
|
||||||
|
try {
|
||||||
|
input.close()
|
||||||
|
} catch (e: IOException) {
|
||||||
|
Log.error("[FileUtil]: Cannot close input file, error: " + e.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (output != null) {
|
||||||
|
try {
|
||||||
|
output.close()
|
||||||
|
} catch (e: IOException) {
|
||||||
|
Log.error("[FileUtil]: Cannot close output file, error: " + e.message)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
destinationFile
|
|
||||||
} catch (e: IOException) {
|
|
||||||
null
|
|
||||||
} catch (e: NullPointerException) {
|
|
||||||
null
|
|
||||||
}
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extracts the given zip file into the given directory.
|
* Extracts the given zip file into the given directory.
|
||||||
@ -361,12 +368,4 @@ object FileUtil {
|
|||||||
return fileName.substring(fileName.lastIndexOf(".") + 1)
|
return fileName.substring(fileName.lastIndexOf(".") + 1)
|
||||||
.lowercase()
|
.lowercase()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(IOException::class)
|
|
||||||
fun getStringFromFile(file: File): String =
|
|
||||||
String(file.readBytes(), StandardCharsets.UTF_8)
|
|
||||||
|
|
||||||
@Throws(IOException::class)
|
|
||||||
fun getStringFromInputStream(stream: InputStream): String =
|
|
||||||
String(stream.readBytes(), StandardCharsets.UTF_8)
|
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,7 @@ object GameHelper {
|
|||||||
// Ensure keys are loaded so that ROM metadata can be decrypted.
|
// Ensure keys are loaded so that ROM metadata can be decrypted.
|
||||||
NativeLibrary.reloadKeys()
|
NativeLibrary.reloadKeys()
|
||||||
|
|
||||||
addGamesRecursive(games, FileUtil.listFiles(gamesUri), 3)
|
addGamesRecursive(games, FileUtil.listFiles(context, gamesUri), 3)
|
||||||
|
|
||||||
// Cache list of games found on disk
|
// Cache list of games found on disk
|
||||||
val serializedGames = mutableSetOf<String>()
|
val serializedGames = mutableSetOf<String>()
|
||||||
@ -58,7 +58,7 @@ object GameHelper {
|
|||||||
if (it.isDirectory) {
|
if (it.isDirectory) {
|
||||||
addGamesRecursive(
|
addGamesRecursive(
|
||||||
games,
|
games,
|
||||||
FileUtil.listFiles(it.uri),
|
FileUtil.listFiles(YuzuApplication.appContext, it.uri),
|
||||||
depth - 1
|
depth - 1
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
|
@ -3,33 +3,64 @@
|
|||||||
|
|
||||||
package org.yuzu.yuzu_emu.utils
|
package org.yuzu.yuzu_emu.utils
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Build
|
|
||||||
import java.io.BufferedInputStream
|
import java.io.BufferedInputStream
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
import java.io.FileInputStream
|
||||||
|
import java.io.FileOutputStream
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
import java.util.zip.ZipInputStream
|
||||||
import org.yuzu.yuzu_emu.NativeLibrary
|
import org.yuzu.yuzu_emu.NativeLibrary
|
||||||
import org.yuzu.yuzu_emu.YuzuApplication
|
import org.yuzu.yuzu_emu.utils.FileUtil.copyUriToInternalStorage
|
||||||
import java.util.zip.ZipException
|
|
||||||
import java.util.zip.ZipFile
|
|
||||||
|
|
||||||
object GpuDriverHelper {
|
object GpuDriverHelper {
|
||||||
private const val META_JSON_FILENAME = "meta.json"
|
private const val META_JSON_FILENAME = "meta.json"
|
||||||
|
private const val DRIVER_INTERNAL_FILENAME = "gpu_driver.zip"
|
||||||
private var fileRedirectionPath: String? = null
|
private var fileRedirectionPath: String? = null
|
||||||
var driverInstallationPath: String? = null
|
private var driverInstallationPath: String? = null
|
||||||
private var hookLibPath: String? = null
|
private var hookLibPath: String? = null
|
||||||
|
|
||||||
val driverStoragePath get() = DirectoryInitialization.userDirectory!! + "/gpu_drivers/"
|
@Throws(IOException::class)
|
||||||
|
private fun unzip(zipFilePath: String, destDir: String) {
|
||||||
|
val dir = File(destDir)
|
||||||
|
|
||||||
fun initializeDriverParameters() {
|
// Create output directory if it doesn't exist
|
||||||
|
if (!dir.exists()) dir.mkdirs()
|
||||||
|
|
||||||
|
// Unpack the files.
|
||||||
|
val inputStream = FileInputStream(zipFilePath)
|
||||||
|
val zis = ZipInputStream(BufferedInputStream(inputStream))
|
||||||
|
val buffer = ByteArray(1024)
|
||||||
|
var ze = zis.nextEntry
|
||||||
|
while (ze != null) {
|
||||||
|
val newFile = File(destDir, ze.name)
|
||||||
|
val canonicalPath = newFile.canonicalPath
|
||||||
|
if (!canonicalPath.startsWith(destDir + ze.name)) {
|
||||||
|
throw SecurityException("Zip file attempted path traversal! " + ze.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
newFile.parentFile!!.mkdirs()
|
||||||
|
val fos = FileOutputStream(newFile)
|
||||||
|
var len: Int
|
||||||
|
while (zis.read(buffer).also { len = it } > 0) {
|
||||||
|
fos.write(buffer, 0, len)
|
||||||
|
}
|
||||||
|
fos.close()
|
||||||
|
zis.closeEntry()
|
||||||
|
ze = zis.nextEntry
|
||||||
|
}
|
||||||
|
zis.closeEntry()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun initializeDriverParameters(context: Context) {
|
||||||
try {
|
try {
|
||||||
// Initialize the file redirection directory.
|
// Initialize the file redirection directory.
|
||||||
fileRedirectionPath = YuzuApplication.appContext
|
fileRedirectionPath =
|
||||||
.getExternalFilesDir(null)!!.canonicalPath + "/gpu/vk_file_redirect/"
|
context.getExternalFilesDir(null)!!.canonicalPath + "/gpu/vk_file_redirect/"
|
||||||
|
|
||||||
// Initialize the driver installation directory.
|
// Initialize the driver installation directory.
|
||||||
driverInstallationPath = YuzuApplication.appContext
|
driverInstallationPath = context.filesDir.canonicalPath + "/gpu_driver/"
|
||||||
.filesDir.canonicalPath + "/gpu_driver/"
|
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
throw RuntimeException(e)
|
throw RuntimeException(e)
|
||||||
}
|
}
|
||||||
@ -38,169 +69,68 @@ object GpuDriverHelper {
|
|||||||
initializeDirectories()
|
initializeDirectories()
|
||||||
|
|
||||||
// Initialize hook libraries directory.
|
// Initialize hook libraries directory.
|
||||||
hookLibPath = YuzuApplication.appContext.applicationInfo.nativeLibraryDir + "/"
|
hookLibPath = context.applicationInfo.nativeLibraryDir + "/"
|
||||||
|
|
||||||
// Initialize GPU driver.
|
// Initialize GPU driver.
|
||||||
NativeLibrary.initializeGpuDriver(
|
NativeLibrary.initializeGpuDriver(
|
||||||
hookLibPath,
|
hookLibPath,
|
||||||
driverInstallationPath,
|
driverInstallationPath,
|
||||||
customDriverData.libraryName,
|
customDriverLibraryName,
|
||||||
fileRedirectionPath
|
fileRedirectionPath
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getDrivers(): MutableList<Pair<String, GpuDriverMetadata>> {
|
fun installDefaultDriver(context: Context) {
|
||||||
val driverZips = File(driverStoragePath).listFiles()
|
|
||||||
val drivers: MutableList<Pair<String, GpuDriverMetadata>> =
|
|
||||||
driverZips
|
|
||||||
?.mapNotNull {
|
|
||||||
val metadata = getMetadataFromZip(it)
|
|
||||||
metadata.name?.let { _ -> Pair(it.path, metadata) }
|
|
||||||
}
|
|
||||||
?.sortedByDescending { it: Pair<String, GpuDriverMetadata> -> it.second.name }
|
|
||||||
?.distinct()
|
|
||||||
?.toMutableList() ?: mutableListOf()
|
|
||||||
|
|
||||||
// TODO: Get system driver information
|
|
||||||
drivers.add(0, Pair("", GpuDriverMetadata()))
|
|
||||||
return drivers
|
|
||||||
}
|
|
||||||
|
|
||||||
fun installDefaultDriver() {
|
|
||||||
// Removing the installed driver will result in the backend using the default system driver.
|
// Removing the installed driver will result in the backend using the default system driver.
|
||||||
File(driverInstallationPath!!).deleteRecursively()
|
val driverInstallationDir = File(driverInstallationPath!!)
|
||||||
initializeDriverParameters()
|
deleteRecursive(driverInstallationDir)
|
||||||
|
initializeDriverParameters(context)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun copyDriverToInternalStorage(driverUri: Uri): Boolean {
|
fun installCustomDriver(context: Context, driverPathUri: Uri?) {
|
||||||
// Ensure we have directories.
|
|
||||||
initializeDirectories()
|
|
||||||
|
|
||||||
// Copy the zip file URI to user data
|
|
||||||
val copiedFile =
|
|
||||||
FileUtil.copyUriToInternalStorage(driverUri, driverStoragePath) ?: return false
|
|
||||||
|
|
||||||
// Validate driver
|
|
||||||
val metadata = getMetadataFromZip(copiedFile)
|
|
||||||
if (metadata.name == null) {
|
|
||||||
copiedFile.delete()
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if (metadata.minApi > Build.VERSION.SDK_INT) {
|
|
||||||
copiedFile.delete()
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Copies driver zip into user data directory so that it can be exported along with
|
|
||||||
* other user data and also unzipped into the installation directory
|
|
||||||
*/
|
|
||||||
fun installCustomDriver(driverUri: Uri): Boolean {
|
|
||||||
// Revert to system default in the event the specified driver is bad.
|
// Revert to system default in the event the specified driver is bad.
|
||||||
installDefaultDriver()
|
installDefaultDriver(context)
|
||||||
|
|
||||||
// Ensure we have directories.
|
// Ensure we have directories.
|
||||||
initializeDirectories()
|
initializeDirectories()
|
||||||
|
|
||||||
// Copy the zip file URI to user data
|
// Copy the zip file URI into our private storage.
|
||||||
val copiedFile =
|
copyUriToInternalStorage(
|
||||||
FileUtil.copyUriToInternalStorage(driverUri, driverStoragePath) ?: return false
|
context,
|
||||||
|
driverPathUri,
|
||||||
// Validate driver
|
driverInstallationPath!!,
|
||||||
val metadata = getMetadataFromZip(copiedFile)
|
DRIVER_INTERNAL_FILENAME
|
||||||
if (metadata.name == null) {
|
)
|
||||||
copiedFile.delete()
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if (metadata.minApi > Build.VERSION.SDK_INT) {
|
|
||||||
copiedFile.delete()
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unzip the driver.
|
// Unzip the driver.
|
||||||
try {
|
try {
|
||||||
FileUtil.unzipToInternalStorage(
|
unzip(driverInstallationPath + DRIVER_INTERNAL_FILENAME, driverInstallationPath!!)
|
||||||
BufferedInputStream(copiedFile.inputStream()),
|
|
||||||
File(driverInstallationPath!!)
|
|
||||||
)
|
|
||||||
} catch (e: SecurityException) {
|
} catch (e: SecurityException) {
|
||||||
return false
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize the driver parameters.
|
// Initialize the driver parameters.
|
||||||
initializeDriverParameters()
|
initializeDriverParameters(context)
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Unzips driver into installation directory
|
|
||||||
*/
|
|
||||||
fun installCustomDriver(driver: File): Boolean {
|
|
||||||
// Revert to system default in the event the specified driver is bad.
|
|
||||||
installDefaultDriver()
|
|
||||||
|
|
||||||
// Ensure we have directories.
|
|
||||||
initializeDirectories()
|
|
||||||
|
|
||||||
// Validate driver
|
|
||||||
val metadata = getMetadataFromZip(driver)
|
|
||||||
if (metadata.name == null) {
|
|
||||||
driver.delete()
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unzip the driver to the private installation directory
|
|
||||||
try {
|
|
||||||
FileUtil.unzipToInternalStorage(
|
|
||||||
BufferedInputStream(driver.inputStream()),
|
|
||||||
File(driverInstallationPath!!)
|
|
||||||
)
|
|
||||||
} catch (e: SecurityException) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize the driver parameters.
|
|
||||||
initializeDriverParameters()
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Takes in a zip file and reads the meta.json file for presentation to the UI
|
|
||||||
*
|
|
||||||
* @param driver Zip containing driver and meta.json file
|
|
||||||
* @return A non-null [GpuDriverMetadata] instance that may have null members
|
|
||||||
*/
|
|
||||||
fun getMetadataFromZip(driver: File): GpuDriverMetadata {
|
|
||||||
try {
|
|
||||||
ZipFile(driver).use { zf ->
|
|
||||||
val entries = zf.entries()
|
|
||||||
while (entries.hasMoreElements()) {
|
|
||||||
val entry = entries.nextElement()
|
|
||||||
if (!entry.isDirectory && entry.name.lowercase().contains(".json")) {
|
|
||||||
zf.getInputStream(entry).use {
|
|
||||||
return GpuDriverMetadata(it, entry.size)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (_: ZipException) {
|
|
||||||
}
|
|
||||||
return GpuDriverMetadata()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
external fun supportsCustomDriverLoading(): Boolean
|
external fun supportsCustomDriverLoading(): Boolean
|
||||||
|
|
||||||
// Parse the custom driver metadata to retrieve the name.
|
// Parse the custom driver metadata to retrieve the name.
|
||||||
val customDriverData: GpuDriverMetadata
|
val customDriverName: String?
|
||||||
get() = GpuDriverMetadata(File(driverInstallationPath + META_JSON_FILENAME))
|
get() {
|
||||||
|
val metadata = GpuDriverMetadata(driverInstallationPath + META_JSON_FILENAME)
|
||||||
|
return metadata.name
|
||||||
|
}
|
||||||
|
|
||||||
fun initializeDirectories() {
|
// Parse the custom driver metadata to retrieve the library name.
|
||||||
|
private val customDriverLibraryName: String?
|
||||||
|
get() {
|
||||||
|
// Parse the custom driver metadata to retrieve the library name.
|
||||||
|
val metadata = GpuDriverMetadata(driverInstallationPath + META_JSON_FILENAME)
|
||||||
|
return metadata.libraryName
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun initializeDirectories() {
|
||||||
// Ensure the file redirection directory exists.
|
// Ensure the file redirection directory exists.
|
||||||
val fileRedirectionDir = File(fileRedirectionPath!!)
|
val fileRedirectionDir = File(fileRedirectionPath!!)
|
||||||
if (!fileRedirectionDir.exists()) {
|
if (!fileRedirectionDir.exists()) {
|
||||||
@ -211,10 +141,14 @@ object GpuDriverHelper {
|
|||||||
if (!driverInstallationDir.exists()) {
|
if (!driverInstallationDir.exists()) {
|
||||||
driverInstallationDir.mkdirs()
|
driverInstallationDir.mkdirs()
|
||||||
}
|
}
|
||||||
// Ensure the driver storage directory exists
|
}
|
||||||
val driverStorageDirectory = File(driverStoragePath)
|
|
||||||
if (!driverStorageDirectory.exists()) {
|
private fun deleteRecursive(fileOrDirectory: File) {
|
||||||
driverStorageDirectory.mkdirs()
|
if (fileOrDirectory.isDirectory) {
|
||||||
|
for (child in fileOrDirectory.listFiles()!!) {
|
||||||
|
deleteRecursive(child)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
fileOrDirectory.delete()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,116 +4,44 @@
|
|||||||
package org.yuzu.yuzu_emu.utils
|
package org.yuzu.yuzu_emu.utils
|
||||||
|
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
import java.nio.charset.StandardCharsets
|
||||||
|
import java.nio.file.Files
|
||||||
|
import java.nio.file.Paths
|
||||||
import org.json.JSONException
|
import org.json.JSONException
|
||||||
import org.json.JSONObject
|
import org.json.JSONObject
|
||||||
import java.io.File
|
|
||||||
import java.io.InputStream
|
|
||||||
|
|
||||||
class GpuDriverMetadata {
|
|
||||||
/**
|
|
||||||
* Tries to get driver metadata information from a meta.json [File]
|
|
||||||
*
|
|
||||||
* @param metadataFile meta.json file provided with a GPU driver
|
|
||||||
*/
|
|
||||||
constructor(metadataFile: File) {
|
|
||||||
if (metadataFile.length() > MAX_META_SIZE_BYTES) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
val json = JSONObject(FileUtil.getStringFromFile(metadataFile))
|
|
||||||
name = json.getString("name")
|
|
||||||
description = json.getString("description")
|
|
||||||
author = json.getString("author")
|
|
||||||
vendor = json.getString("vendor")
|
|
||||||
version = json.getString("driverVersion")
|
|
||||||
minApi = json.getInt("minApi")
|
|
||||||
libraryName = json.getString("libraryName")
|
|
||||||
} catch (e: JSONException) {
|
|
||||||
// JSON is malformed, ignore and treat as unsupported metadata.
|
|
||||||
} catch (e: IOException) {
|
|
||||||
// File is inaccessible, ignore and treat as unsupported metadata.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tries to get driver metadata information from an input stream that's intended to be
|
|
||||||
* from a zip file
|
|
||||||
*
|
|
||||||
* @param metadataStream ZipEntry input stream
|
|
||||||
* @param size Size of the file in bytes
|
|
||||||
*/
|
|
||||||
constructor(metadataStream: InputStream, size: Long) {
|
|
||||||
if (size > MAX_META_SIZE_BYTES) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
val json = JSONObject(FileUtil.getStringFromInputStream(metadataStream))
|
|
||||||
name = json.getString("name")
|
|
||||||
description = json.getString("description")
|
|
||||||
author = json.getString("author")
|
|
||||||
vendor = json.getString("vendor")
|
|
||||||
version = json.getString("driverVersion")
|
|
||||||
minApi = json.getInt("minApi")
|
|
||||||
libraryName = json.getString("libraryName")
|
|
||||||
} catch (e: JSONException) {
|
|
||||||
// JSON is malformed, ignore and treat as unsupported metadata.
|
|
||||||
} catch (e: IOException) {
|
|
||||||
// File is inaccessible, ignore and treat as unsupported metadata.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates an empty metadata instance
|
|
||||||
*/
|
|
||||||
constructor()
|
|
||||||
|
|
||||||
override fun equals(other: Any?): Boolean {
|
|
||||||
if (other !is GpuDriverMetadata) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return other.name == name &&
|
|
||||||
other.description == description &&
|
|
||||||
other.author == author &&
|
|
||||||
other.vendor == vendor &&
|
|
||||||
other.version == version &&
|
|
||||||
other.minApi == minApi &&
|
|
||||||
other.libraryName == libraryName
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun hashCode(): Int {
|
|
||||||
var result = name?.hashCode() ?: 0
|
|
||||||
result = 31 * result + (description?.hashCode() ?: 0)
|
|
||||||
result = 31 * result + (author?.hashCode() ?: 0)
|
|
||||||
result = 31 * result + (vendor?.hashCode() ?: 0)
|
|
||||||
result = 31 * result + (version?.hashCode() ?: 0)
|
|
||||||
result = 31 * result + minApi
|
|
||||||
result = 31 * result + (libraryName?.hashCode() ?: 0)
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun toString(): String =
|
|
||||||
"""
|
|
||||||
Name - $name
|
|
||||||
Description - $description
|
|
||||||
Author - $author
|
|
||||||
Vendor - $vendor
|
|
||||||
Version - $version
|
|
||||||
Min API - $minApi
|
|
||||||
Library Name - $libraryName
|
|
||||||
""".trimMargin().trimIndent()
|
|
||||||
|
|
||||||
|
class GpuDriverMetadata(metadataFilePath: String) {
|
||||||
var name: String? = null
|
var name: String? = null
|
||||||
var description: String? = null
|
var description: String? = null
|
||||||
var author: String? = null
|
var author: String? = null
|
||||||
var vendor: String? = null
|
var vendor: String? = null
|
||||||
var version: String? = null
|
var driverVersion: String? = null
|
||||||
var minApi = 0
|
var minApi = 0
|
||||||
var libraryName: String? = null
|
var libraryName: String? = null
|
||||||
|
|
||||||
|
init {
|
||||||
|
try {
|
||||||
|
val json = JSONObject(getStringFromFile(metadataFilePath))
|
||||||
|
name = json.getString("name")
|
||||||
|
description = json.getString("description")
|
||||||
|
author = json.getString("author")
|
||||||
|
vendor = json.getString("vendor")
|
||||||
|
driverVersion = json.getString("driverVersion")
|
||||||
|
minApi = json.getInt("minApi")
|
||||||
|
libraryName = json.getString("libraryName")
|
||||||
|
} catch (e: JSONException) {
|
||||||
|
// JSON is malformed, ignore and treat as unsupported metadata.
|
||||||
|
} catch (e: IOException) {
|
||||||
|
// File is inaccessible, ignore and treat as unsupported metadata.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val MAX_META_SIZE_BYTES = 500000
|
@Throws(IOException::class)
|
||||||
|
private fun getStringFromFile(filePath: String): String {
|
||||||
|
val path = Paths.get(filePath)
|
||||||
|
val bytes = Files.readAllBytes(path)
|
||||||
|
return String(bytes, StandardCharsets.UTF_8)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +0,0 @@
|
|||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:width="24dp"
|
|
||||||
android:height="24dp"
|
|
||||||
android:viewportWidth="24"
|
|
||||||
android:viewportHeight="24">
|
|
||||||
<path
|
|
||||||
android:fillColor="?attr/colorControlNormal"
|
|
||||||
android:pathData="M22.7,19l-9.1,-9.1c0.9,-2.3 0.4,-5 -1.5,-6.9 -2,-2 -5,-2.4 -7.4,-1.3L9,6 6,9 1.6,4.7C0.4,7.1 0.9,10.1 2.9,12.1c1.9,1.9 4.6,2.4 6.9,1.5l9.1,9.1c0.4,0.4 1,0.4 1.4,0l2.3,-2.3c0.5,-0.4 0.5,-1.1 0.1,-1.4z" />
|
|
||||||
</vector>
|
|
@ -1,9 +0,0 @@
|
|||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:width="24dp"
|
|
||||||
android:height="24dp"
|
|
||||||
android:viewportWidth="24"
|
|
||||||
android:viewportHeight="24">
|
|
||||||
<path
|
|
||||||
android:fillColor="?attr/colorControlNormal"
|
|
||||||
android:pathData="M6,19c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2V7H6v12zM19,4h-3.5l-1,-1h-5l-1,1H5v2h14V4z" />
|
|
||||||
</vector>
|
|
@ -1,89 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
style="?attr/materialCardViewOutlinedStyle"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginHorizontal="16dp"
|
|
||||||
android:layout_marginVertical="12dp"
|
|
||||||
android:background="?attr/selectableItemBackground"
|
|
||||||
android:clickable="true"
|
|
||||||
android:focusable="true">
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="horizontal"
|
|
||||||
android:layout_gravity="center"
|
|
||||||
android:padding="16dp">
|
|
||||||
|
|
||||||
<RadioButton
|
|
||||||
android:id="@+id/radio_button"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_gravity="center_vertical"
|
|
||||||
android:clickable="false"
|
|
||||||
android:checked="false" />
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:layout_gravity="center_vertical">
|
|
||||||
|
|
||||||
<com.google.android.material.textview.MaterialTextView
|
|
||||||
android:id="@+id/title"
|
|
||||||
style="@style/TextAppearance.Material3.TitleMedium"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:ellipsize="none"
|
|
||||||
android:marqueeRepeatLimit="marquee_forever"
|
|
||||||
android:requiresFadingEdge="horizontal"
|
|
||||||
android:singleLine="true"
|
|
||||||
android:textAlignment="viewStart"
|
|
||||||
tools:text="@string/select_gpu_driver_default" />
|
|
||||||
|
|
||||||
<com.google.android.material.textview.MaterialTextView
|
|
||||||
android:id="@+id/version"
|
|
||||||
style="@style/TextAppearance.Material3.BodyMedium"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="6dp"
|
|
||||||
android:ellipsize="none"
|
|
||||||
android:marqueeRepeatLimit="marquee_forever"
|
|
||||||
android:requiresFadingEdge="horizontal"
|
|
||||||
android:singleLine="true"
|
|
||||||
android:textAlignment="viewStart"
|
|
||||||
tools:text="@string/install_gpu_driver_description" />
|
|
||||||
|
|
||||||
<com.google.android.material.textview.MaterialTextView
|
|
||||||
android:id="@+id/description"
|
|
||||||
style="@style/TextAppearance.Material3.BodyMedium"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="6dp"
|
|
||||||
android:ellipsize="none"
|
|
||||||
android:marqueeRepeatLimit="marquee_forever"
|
|
||||||
android:requiresFadingEdge="horizontal"
|
|
||||||
android:singleLine="true"
|
|
||||||
android:textAlignment="viewStart"
|
|
||||||
tools:text="@string/install_gpu_driver_description" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/button_delete"
|
|
||||||
style="@style/Widget.Material3.Button.IconButton"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_gravity="center_vertical"
|
|
||||||
android:contentDescription="@string/delete"
|
|
||||||
android:tooltipText="@string/delete"
|
|
||||||
app:icon="@drawable/ic_delete"
|
|
||||||
app:iconTint="?attr/colorControlNormal" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
</com.google.android.material.card.MaterialCardView>
|
|
@ -1,48 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
android:id="@+id/coordinator_licenses"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:background="?attr/colorSurface">
|
|
||||||
|
|
||||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent">
|
|
||||||
|
|
||||||
<com.google.android.material.appbar.AppBarLayout
|
|
||||||
android:id="@+id/appbar_drivers"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:fitsSystemWindows="true"
|
|
||||||
app:liftOnScrollTargetViewId="@id/list_drivers">
|
|
||||||
|
|
||||||
<com.google.android.material.appbar.MaterialToolbar
|
|
||||||
android:id="@+id/toolbar_drivers"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="?attr/actionBarSize"
|
|
||||||
app:navigationIcon="@drawable/ic_back"
|
|
||||||
app:title="@string/gpu_driver_manager" />
|
|
||||||
|
|
||||||
</com.google.android.material.appbar.AppBarLayout>
|
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
|
||||||
android:id="@+id/list_drivers"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:clipToPadding="false"
|
|
||||||
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
|
|
||||||
|
|
||||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
|
||||||
|
|
||||||
<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
|
|
||||||
android:id="@+id/button_install"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_gravity="bottom|end"
|
|
||||||
android:text="@string/install"
|
|
||||||
app:icon="@drawable/ic_add"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent" />
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -22,9 +22,6 @@
|
|||||||
<action
|
<action
|
||||||
android:id="@+id/action_homeSettingsFragment_to_installableFragment"
|
android:id="@+id/action_homeSettingsFragment_to_installableFragment"
|
||||||
app:destination="@id/installableFragment" />
|
app:destination="@id/installableFragment" />
|
||||||
<action
|
|
||||||
android:id="@+id/action_homeSettingsFragment_to_driverManagerFragment"
|
|
||||||
app:destination="@id/driverManagerFragment" />
|
|
||||||
</fragment>
|
</fragment>
|
||||||
|
|
||||||
<fragment
|
<fragment
|
||||||
@ -98,9 +95,5 @@
|
|||||||
android:id="@+id/installableFragment"
|
android:id="@+id/installableFragment"
|
||||||
android:name="org.yuzu.yuzu_emu.fragments.InstallableFragment"
|
android:name="org.yuzu.yuzu_emu.fragments.InstallableFragment"
|
||||||
android:label="InstallableFragment" />
|
android:label="InstallableFragment" />
|
||||||
<fragment
|
|
||||||
android:id="@+id/driverManagerFragment"
|
|
||||||
android:name="org.yuzu.yuzu_emu.fragments.DriverManagerFragment"
|
|
||||||
android:label="DriverManagerFragment" />
|
|
||||||
|
|
||||||
</navigation>
|
</navigation>
|
||||||
|
@ -168,7 +168,9 @@
|
|||||||
<string name="select_gpu_driver_title">Möchtest du deinen aktuellen GPU-Treiber ersetzen?</string>
|
<string name="select_gpu_driver_title">Möchtest du deinen aktuellen GPU-Treiber ersetzen?</string>
|
||||||
<string name="select_gpu_driver_install">Installieren</string>
|
<string name="select_gpu_driver_install">Installieren</string>
|
||||||
<string name="select_gpu_driver_default">Standard</string>
|
<string name="select_gpu_driver_default">Standard</string>
|
||||||
|
<string name="select_gpu_driver_install_success">%s wurde installiert</string>
|
||||||
<string name="select_gpu_driver_use_default">Standard GPU-Treiber wird verwendet</string>
|
<string name="select_gpu_driver_use_default">Standard GPU-Treiber wird verwendet</string>
|
||||||
|
<string name="select_gpu_driver_error">Ungültiger Treiber ausgewählt, Standard-Treiber wird verwendet!</string>
|
||||||
<string name="system_gpu_driver">System GPU-Treiber</string>
|
<string name="system_gpu_driver">System GPU-Treiber</string>
|
||||||
<string name="installing_driver">Treiber wird installiert...</string>
|
<string name="installing_driver">Treiber wird installiert...</string>
|
||||||
|
|
||||||
|
@ -171,7 +171,9 @@
|
|||||||
<string name="select_gpu_driver_title">¿Quiere reemplazar el driver de GPU actual?</string>
|
<string name="select_gpu_driver_title">¿Quiere reemplazar el driver de GPU actual?</string>
|
||||||
<string name="select_gpu_driver_install">Instalar</string>
|
<string name="select_gpu_driver_install">Instalar</string>
|
||||||
<string name="select_gpu_driver_default">Predeterminado</string>
|
<string name="select_gpu_driver_default">Predeterminado</string>
|
||||||
|
<string name="select_gpu_driver_install_success">Instalado %s</string>
|
||||||
<string name="select_gpu_driver_use_default">Usando el driver de GPU por defecto </string>
|
<string name="select_gpu_driver_use_default">Usando el driver de GPU por defecto </string>
|
||||||
|
<string name="select_gpu_driver_error">¡Driver no válido, utilizando el predeterminado del sistema!</string>
|
||||||
<string name="system_gpu_driver">Driver GPU del sistema</string>
|
<string name="system_gpu_driver">Driver GPU del sistema</string>
|
||||||
<string name="installing_driver">Instalando driver...</string>
|
<string name="installing_driver">Instalando driver...</string>
|
||||||
|
|
||||||
|
@ -171,7 +171,9 @@
|
|||||||
<string name="select_gpu_driver_title">Souhaitez vous remplacer votre pilote actuel ?</string>
|
<string name="select_gpu_driver_title">Souhaitez vous remplacer votre pilote actuel ?</string>
|
||||||
<string name="select_gpu_driver_install">Installer</string>
|
<string name="select_gpu_driver_install">Installer</string>
|
||||||
<string name="select_gpu_driver_default">Défaut</string>
|
<string name="select_gpu_driver_default">Défaut</string>
|
||||||
|
<string name="select_gpu_driver_install_success">%s Installé</string>
|
||||||
<string name="select_gpu_driver_use_default">Utilisation du pilote de GPU par défaut</string>
|
<string name="select_gpu_driver_use_default">Utilisation du pilote de GPU par défaut</string>
|
||||||
|
<string name="select_gpu_driver_error">Pilote non valide sélectionné, utilisation du paramètre par défaut du système !</string>
|
||||||
<string name="system_gpu_driver">Pilote du GPU du système</string>
|
<string name="system_gpu_driver">Pilote du GPU du système</string>
|
||||||
<string name="installing_driver">Installation du pilote...</string>
|
<string name="installing_driver">Installation du pilote...</string>
|
||||||
|
|
||||||
|
@ -171,7 +171,9 @@
|
|||||||
<string name="select_gpu_driver_title">Vuoi sostituire il driver della tua GPU attuale?</string>
|
<string name="select_gpu_driver_title">Vuoi sostituire il driver della tua GPU attuale?</string>
|
||||||
<string name="select_gpu_driver_install">Installa</string>
|
<string name="select_gpu_driver_install">Installa</string>
|
||||||
<string name="select_gpu_driver_default">Predefinito</string>
|
<string name="select_gpu_driver_default">Predefinito</string>
|
||||||
|
<string name="select_gpu_driver_install_success">Installato%s</string>
|
||||||
<string name="select_gpu_driver_use_default">Utilizza il driver predefinito della GPU.</string>
|
<string name="select_gpu_driver_use_default">Utilizza il driver predefinito della GPU.</string>
|
||||||
|
<string name="select_gpu_driver_error">Il driver selezionato è invalido, è in utilizzo quello predefinito di sistema!</string>
|
||||||
<string name="system_gpu_driver">Driver GPU del sistema</string>
|
<string name="system_gpu_driver">Driver GPU del sistema</string>
|
||||||
<string name="installing_driver">Installando i driver...</string>
|
<string name="installing_driver">Installando i driver...</string>
|
||||||
|
|
||||||
|
@ -170,7 +170,9 @@
|
|||||||
<string name="select_gpu_driver_title">現在のGPUドライバーを置き換えますか?</string>
|
<string name="select_gpu_driver_title">現在のGPUドライバーを置き換えますか?</string>
|
||||||
<string name="select_gpu_driver_install">インストール</string>
|
<string name="select_gpu_driver_install">インストール</string>
|
||||||
<string name="select_gpu_driver_default">デフォルト</string>
|
<string name="select_gpu_driver_default">デフォルト</string>
|
||||||
|
<string name="select_gpu_driver_install_success">%s をインストールしました</string>
|
||||||
<string name="select_gpu_driver_use_default">デフォルトのGPUドライバーを使用します</string>
|
<string name="select_gpu_driver_use_default">デフォルトのGPUドライバーを使用します</string>
|
||||||
|
<string name="select_gpu_driver_error">選択されたドライバが無効なため、システムのデフォルトを使用します!</string>
|
||||||
<string name="system_gpu_driver">システムのGPUドライバ</string>
|
<string name="system_gpu_driver">システムのGPUドライバ</string>
|
||||||
<string name="installing_driver">インストール中…</string>
|
<string name="installing_driver">インストール中…</string>
|
||||||
|
|
||||||
|
@ -171,7 +171,9 @@
|
|||||||
<string name="select_gpu_driver_title">현재 사용 중인 GPU 드라이버를 교체하겠습니까?</string>
|
<string name="select_gpu_driver_title">현재 사용 중인 GPU 드라이버를 교체하겠습니까?</string>
|
||||||
<string name="select_gpu_driver_install">설치</string>
|
<string name="select_gpu_driver_install">설치</string>
|
||||||
<string name="select_gpu_driver_default">기본값</string>
|
<string name="select_gpu_driver_default">기본값</string>
|
||||||
|
<string name="select_gpu_driver_install_success">설치된 %s</string>
|
||||||
<string name="select_gpu_driver_use_default">기본 GPU 드라이버 사용</string>
|
<string name="select_gpu_driver_use_default">기본 GPU 드라이버 사용</string>
|
||||||
|
<string name="select_gpu_driver_error">시스템 기본값을 사용하여 잘못된 드라이버를 선택했습니다!</string>
|
||||||
<string name="system_gpu_driver">시스템 GPU 드라이버</string>
|
<string name="system_gpu_driver">시스템 GPU 드라이버</string>
|
||||||
<string name="installing_driver">드라이버 설치 중...</string>
|
<string name="installing_driver">드라이버 설치 중...</string>
|
||||||
|
|
||||||
|
@ -171,7 +171,9 @@
|
|||||||
<string name="select_gpu_driver_title">Ønsker du å bytte ut din nåværende GPU-driver?</string>
|
<string name="select_gpu_driver_title">Ønsker du å bytte ut din nåværende GPU-driver?</string>
|
||||||
<string name="select_gpu_driver_install">Installer</string>
|
<string name="select_gpu_driver_install">Installer</string>
|
||||||
<string name="select_gpu_driver_default">Standard</string>
|
<string name="select_gpu_driver_default">Standard</string>
|
||||||
|
<string name="select_gpu_driver_install_success">Installert %s</string>
|
||||||
<string name="select_gpu_driver_use_default">Bruk av standard GPU-driver</string>
|
<string name="select_gpu_driver_use_default">Bruk av standard GPU-driver</string>
|
||||||
|
<string name="select_gpu_driver_error">Ugyldig driver valgt, bruker systemstandard!</string>
|
||||||
<string name="system_gpu_driver">Systemets GPU-driver</string>
|
<string name="system_gpu_driver">Systemets GPU-driver</string>
|
||||||
<string name="installing_driver">Installerer driver...</string>
|
<string name="installing_driver">Installerer driver...</string>
|
||||||
|
|
||||||
|
@ -171,7 +171,9 @@
|
|||||||
<string name="select_gpu_driver_title">Chcesz zastąpić obecny sterownik układu graficznego?</string>
|
<string name="select_gpu_driver_title">Chcesz zastąpić obecny sterownik układu graficznego?</string>
|
||||||
<string name="select_gpu_driver_install">Zainstaluj</string>
|
<string name="select_gpu_driver_install">Zainstaluj</string>
|
||||||
<string name="select_gpu_driver_default">Domyślne</string>
|
<string name="select_gpu_driver_default">Domyślne</string>
|
||||||
|
<string name="select_gpu_driver_install_success">Zainstalowano %s</string>
|
||||||
<string name="select_gpu_driver_use_default">Aktywny domyślny sterownik GPU</string>
|
<string name="select_gpu_driver_use_default">Aktywny domyślny sterownik GPU</string>
|
||||||
|
<string name="select_gpu_driver_error">Wybrano błędny sterownik, powrót do domyślnego. </string>
|
||||||
<string name="system_gpu_driver">Systemowy sterownik GPU</string>
|
<string name="system_gpu_driver">Systemowy sterownik GPU</string>
|
||||||
<string name="installing_driver">Instalowanie sterownika...</string>
|
<string name="installing_driver">Instalowanie sterownika...</string>
|
||||||
|
|
||||||
|
@ -171,7 +171,9 @@
|
|||||||
<string name="select_gpu_driver_title">Queres substituir o driver do GPU atual? </string>
|
<string name="select_gpu_driver_title">Queres substituir o driver do GPU atual? </string>
|
||||||
<string name="select_gpu_driver_install">Instalar</string>
|
<string name="select_gpu_driver_install">Instalar</string>
|
||||||
<string name="select_gpu_driver_default">Padrão</string>
|
<string name="select_gpu_driver_default">Padrão</string>
|
||||||
|
<string name="select_gpu_driver_install_success">Instalado%s</string>
|
||||||
<string name="select_gpu_driver_use_default">Usar o driver padrão do GPU</string>
|
<string name="select_gpu_driver_use_default">Usar o driver padrão do GPU</string>
|
||||||
|
<string name="select_gpu_driver_error">Driver selecionado inválido, a usar o padrão do sistema!</string>
|
||||||
<string name="system_gpu_driver">Driver do GPU padrão</string>
|
<string name="system_gpu_driver">Driver do GPU padrão</string>
|
||||||
<string name="installing_driver">A instalar o Driver...</string>
|
<string name="installing_driver">A instalar o Driver...</string>
|
||||||
|
|
||||||
|
@ -171,7 +171,9 @@
|
|||||||
<string name="select_gpu_driver_title">Queres substituir o driver do GPU atual? </string>
|
<string name="select_gpu_driver_title">Queres substituir o driver do GPU atual? </string>
|
||||||
<string name="select_gpu_driver_install">Instalar</string>
|
<string name="select_gpu_driver_install">Instalar</string>
|
||||||
<string name="select_gpu_driver_default">Padrão</string>
|
<string name="select_gpu_driver_default">Padrão</string>
|
||||||
|
<string name="select_gpu_driver_install_success">Instalado%s</string>
|
||||||
<string name="select_gpu_driver_use_default">Usar o driver padrão do GPU</string>
|
<string name="select_gpu_driver_use_default">Usar o driver padrão do GPU</string>
|
||||||
|
<string name="select_gpu_driver_error">Driver selecionado inválido, a usar o padrão do sistema!</string>
|
||||||
<string name="system_gpu_driver">Driver do GPU padrão</string>
|
<string name="system_gpu_driver">Driver do GPU padrão</string>
|
||||||
<string name="installing_driver">A instalar o Driver...</string>
|
<string name="installing_driver">A instalar o Driver...</string>
|
||||||
|
|
||||||
|
@ -171,7 +171,9 @@
|
|||||||
<string name="select_gpu_driver_title">Хотите заменить текущий драйвер ГП?</string>
|
<string name="select_gpu_driver_title">Хотите заменить текущий драйвер ГП?</string>
|
||||||
<string name="select_gpu_driver_install">Установить</string>
|
<string name="select_gpu_driver_install">Установить</string>
|
||||||
<string name="select_gpu_driver_default">По умолчанию</string>
|
<string name="select_gpu_driver_default">По умолчанию</string>
|
||||||
|
<string name="select_gpu_driver_install_success">Установлено %s</string>
|
||||||
<string name="select_gpu_driver_use_default">Используется стандартный драйвер ГП </string>
|
<string name="select_gpu_driver_use_default">Используется стандартный драйвер ГП </string>
|
||||||
|
<string name="select_gpu_driver_error">Выбран неверный драйвер, используется стандартный системный!</string>
|
||||||
<string name="system_gpu_driver">Системный драйвер ГП</string>
|
<string name="system_gpu_driver">Системный драйвер ГП</string>
|
||||||
<string name="installing_driver">Установка драйвера...</string>
|
<string name="installing_driver">Установка драйвера...</string>
|
||||||
|
|
||||||
|
@ -171,7 +171,9 @@
|
|||||||
<string name="select_gpu_driver_title">Хочете замінити поточний драйвер ГП?</string>
|
<string name="select_gpu_driver_title">Хочете замінити поточний драйвер ГП?</string>
|
||||||
<string name="select_gpu_driver_install">Встановити</string>
|
<string name="select_gpu_driver_install">Встановити</string>
|
||||||
<string name="select_gpu_driver_default">За замовчуванням</string>
|
<string name="select_gpu_driver_default">За замовчуванням</string>
|
||||||
|
<string name="select_gpu_driver_install_success">Встановлено %s</string>
|
||||||
<string name="select_gpu_driver_use_default">Використовується стандартний драйвер ГП</string>
|
<string name="select_gpu_driver_use_default">Використовується стандартний драйвер ГП</string>
|
||||||
|
<string name="select_gpu_driver_error">Обрано неправильний драйвер, використовується стандартний системний!</string>
|
||||||
<string name="system_gpu_driver">Системний драйвер ГП</string>
|
<string name="system_gpu_driver">Системний драйвер ГП</string>
|
||||||
<string name="installing_driver">Встановлення драйвера...</string>
|
<string name="installing_driver">Встановлення драйвера...</string>
|
||||||
|
|
||||||
|
@ -171,7 +171,9 @@
|
|||||||
<string name="select_gpu_driver_title">要取代您当前的 GPU 驱动程序吗?</string>
|
<string name="select_gpu_driver_title">要取代您当前的 GPU 驱动程序吗?</string>
|
||||||
<string name="select_gpu_driver_install">安装</string>
|
<string name="select_gpu_driver_install">安装</string>
|
||||||
<string name="select_gpu_driver_default">系统默认</string>
|
<string name="select_gpu_driver_default">系统默认</string>
|
||||||
|
<string name="select_gpu_driver_install_success">已安装 %s</string>
|
||||||
<string name="select_gpu_driver_use_default">使用默认 GPU 驱动程序</string>
|
<string name="select_gpu_driver_use_default">使用默认 GPU 驱动程序</string>
|
||||||
|
<string name="select_gpu_driver_error">选择的驱动程序无效,将使用系统默认的驱动程序!</string>
|
||||||
<string name="system_gpu_driver">系统 GPU 驱动程序</string>
|
<string name="system_gpu_driver">系统 GPU 驱动程序</string>
|
||||||
<string name="installing_driver">正在安装驱动程序…</string>
|
<string name="installing_driver">正在安装驱动程序…</string>
|
||||||
|
|
||||||
|
@ -171,7 +171,9 @@
|
|||||||
<string name="select_gpu_driver_title">要取代您目前的 GPU 驅動程式嗎?</string>
|
<string name="select_gpu_driver_title">要取代您目前的 GPU 驅動程式嗎?</string>
|
||||||
<string name="select_gpu_driver_install">安裝</string>
|
<string name="select_gpu_driver_install">安裝</string>
|
||||||
<string name="select_gpu_driver_default">預設</string>
|
<string name="select_gpu_driver_default">預設</string>
|
||||||
|
<string name="select_gpu_driver_install_success">已安裝 %s</string>
|
||||||
<string name="select_gpu_driver_use_default">使用預設 GPU 驅動程式</string>
|
<string name="select_gpu_driver_use_default">使用預設 GPU 驅動程式</string>
|
||||||
|
<string name="select_gpu_driver_error">選取的驅動程式無效,將使用系統預設驅動程式!</string>
|
||||||
<string name="system_gpu_driver">系統 GPU 驅動程式</string>
|
<string name="system_gpu_driver">系統 GPU 驅動程式</string>
|
||||||
<string name="installing_driver">正在安裝驅動程式…</string>
|
<string name="installing_driver">正在安裝驅動程式…</string>
|
||||||
|
|
||||||
|
@ -13,8 +13,6 @@
|
|||||||
<dimen name="menu_width">256dp</dimen>
|
<dimen name="menu_width">256dp</dimen>
|
||||||
<dimen name="card_width">165dp</dimen>
|
<dimen name="card_width">165dp</dimen>
|
||||||
<dimen name="icon_inset">24dp</dimen>
|
<dimen name="icon_inset">24dp</dimen>
|
||||||
<dimen name="spacing_bottom_list_fab">72dp</dimen>
|
|
||||||
<dimen name="spacing_fab">24dp</dimen>
|
|
||||||
|
|
||||||
<dimen name="dialog_margin">20dp</dimen>
|
<dimen name="dialog_margin">20dp</dimen>
|
||||||
<dimen name="elevated_app_bar">3dp</dimen>
|
<dimen name="elevated_app_bar">3dp</dimen>
|
||||||
|
@ -72,7 +72,6 @@
|
|||||||
<string name="invalid_keys_error">Invalid encryption keys</string>
|
<string name="invalid_keys_error">Invalid encryption keys</string>
|
||||||
<string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string>
|
<string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string>
|
||||||
<string name="install_keys_failure_description">The selected file is incorrect or corrupt. Please redump your keys.</string>
|
<string name="install_keys_failure_description">The selected file is incorrect or corrupt. Please redump your keys.</string>
|
||||||
<string name="gpu_driver_manager">GPU Driver Manager</string>
|
|
||||||
<string name="install_gpu_driver">Install GPU driver</string>
|
<string name="install_gpu_driver">Install GPU driver</string>
|
||||||
<string name="install_gpu_driver_description">Install alternative drivers for potentially better performance or accuracy</string>
|
<string name="install_gpu_driver_description">Install alternative drivers for potentially better performance or accuracy</string>
|
||||||
<string name="advanced_settings">Advanced settings</string>
|
<string name="advanced_settings">Advanced settings</string>
|
||||||
@ -235,17 +234,15 @@
|
|||||||
<string name="export_failed">Export failed</string>
|
<string name="export_failed">Export failed</string>
|
||||||
<string name="import_failed">Import failed</string>
|
<string name="import_failed">Import failed</string>
|
||||||
<string name="cancelling">Cancelling</string>
|
<string name="cancelling">Cancelling</string>
|
||||||
<string name="install">Install</string>
|
|
||||||
<string name="delete">Delete</string>
|
|
||||||
|
|
||||||
<!-- GPU driver installation -->
|
<!-- GPU driver installation -->
|
||||||
<string name="select_gpu_driver">Select GPU driver</string>
|
<string name="select_gpu_driver">Select GPU driver</string>
|
||||||
<string name="select_gpu_driver_title">Would you like to replace your current GPU driver?</string>
|
<string name="select_gpu_driver_title">Would you like to replace your current GPU driver?</string>
|
||||||
<string name="select_gpu_driver_install">Install</string>
|
<string name="select_gpu_driver_install">Install</string>
|
||||||
<string name="select_gpu_driver_default">Default</string>
|
<string name="select_gpu_driver_default">Default</string>
|
||||||
|
<string name="select_gpu_driver_install_success">Installed %s</string>
|
||||||
<string name="select_gpu_driver_use_default">Using default GPU driver</string>
|
<string name="select_gpu_driver_use_default">Using default GPU driver</string>
|
||||||
<string name="select_gpu_driver_error">Invalid driver selected</string>
|
<string name="select_gpu_driver_error">Invalid driver selected, using system default!</string>
|
||||||
<string name="driver_already_installed">Driver already installed</string>
|
|
||||||
<string name="system_gpu_driver">System GPU driver</string>
|
<string name="system_gpu_driver">System GPU driver</string>
|
||||||
<string name="installing_driver">Installing driver…</string>
|
<string name="installing_driver">Installing driver…</string>
|
||||||
|
|
||||||
|
@ -3,8 +3,8 @@
|
|||||||
|
|
||||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||||
plugins {
|
plugins {
|
||||||
id("com.android.application") version "8.1.2" apply false
|
id("com.android.application") version "8.0.2" apply false
|
||||||
id("com.android.library") version "8.1.2" apply false
|
id("com.android.library") version "8.0.2" apply false
|
||||||
id("org.jetbrains.kotlin.android") version "1.8.21" apply false
|
id("org.jetbrains.kotlin.android") version "1.8.21" apply false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,7 +77,6 @@ void AudioRenderer::Wait() {
|
|||||||
"{}, got {}",
|
"{}, got {}",
|
||||||
Message::RenderResponse, msg);
|
Message::RenderResponse, msg);
|
||||||
}
|
}
|
||||||
PostDSPClearCommandBuffer();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioRenderer::Send(Direction dir, u32 message) {
|
void AudioRenderer::Send(Direction dir, u32 message) {
|
||||||
@ -97,14 +96,6 @@ void AudioRenderer::SetCommandBuffer(s32 session_id, CpuAddr buffer, u64 size, u
|
|||||||
command_buffers[session_id].reset_buffer = reset;
|
command_buffers[session_id].reset_buffer = reset;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioRenderer::PostDSPClearCommandBuffer() noexcept {
|
|
||||||
for (auto& buffer : command_buffers) {
|
|
||||||
buffer.buffer = 0;
|
|
||||||
buffer.size = 0;
|
|
||||||
buffer.reset_buffer = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 AudioRenderer::GetRemainCommandCount(s32 session_id) const noexcept {
|
u32 AudioRenderer::GetRemainCommandCount(s32 session_id) const noexcept {
|
||||||
return command_buffers[session_id].remaining_command_count;
|
return command_buffers[session_id].remaining_command_count;
|
||||||
}
|
}
|
||||||
|
@ -85,8 +85,6 @@ private:
|
|||||||
*/
|
*/
|
||||||
void CreateSinkStreams();
|
void CreateSinkStreams();
|
||||||
|
|
||||||
void PostDSPClearCommandBuffer() noexcept;
|
|
||||||
|
|
||||||
/// Core system
|
/// Core system
|
||||||
Core::System& system;
|
Core::System& system;
|
||||||
/// The output sink the AudioRenderer will send samples to
|
/// The output sink the AudioRenderer will send samples to
|
||||||
|
@ -204,10 +204,6 @@ void SinkStream::ProcessAudioOutAndRender(std::span<s16> output_buffer, std::siz
|
|||||||
// paused and we'll desync, so just play silence.
|
// paused and we'll desync, so just play silence.
|
||||||
if (system.IsPaused() || system.IsShuttingDown()) {
|
if (system.IsPaused() || system.IsShuttingDown()) {
|
||||||
if (system.IsShuttingDown()) {
|
if (system.IsShuttingDown()) {
|
||||||
{
|
|
||||||
std::scoped_lock lk{release_mutex};
|
|
||||||
queued_buffers.store(0);
|
|
||||||
}
|
|
||||||
release_cv.notify_one();
|
release_cv.notify_one();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -189,14 +189,6 @@ if(ARCHITECTURE_x86_64)
|
|||||||
target_link_libraries(common PRIVATE xbyak::xbyak)
|
target_link_libraries(common PRIVATE xbyak::xbyak)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (ARCHITECTURE_arm64 AND (ANDROID OR LINUX))
|
|
||||||
target_sources(common
|
|
||||||
PRIVATE
|
|
||||||
arm64/native_clock.cpp
|
|
||||||
arm64/native_clock.h
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if (MSVC)
|
if (MSVC)
|
||||||
target_compile_definitions(common PRIVATE
|
target_compile_definitions(common PRIVATE
|
||||||
# The standard library doesn't provide any replacement for codecvt yet
|
# The standard library doesn't provide any replacement for codecvt yet
|
||||||
|
@ -1,72 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
|
|
||||||
#include "common/arm64/native_clock.h"
|
|
||||||
|
|
||||||
namespace Common::Arm64 {
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
NativeClock::FactorType GetFixedPointFactor(u64 num, u64 den) {
|
|
||||||
return (static_cast<NativeClock::FactorType>(num) << 64) / den;
|
|
||||||
}
|
|
||||||
|
|
||||||
u64 MultiplyHigh(u64 m, NativeClock::FactorType factor) {
|
|
||||||
return static_cast<u64>((m * factor) >> 64);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
NativeClock::NativeClock() {
|
|
||||||
const u64 host_cntfrq = GetHostCNTFRQ();
|
|
||||||
ns_cntfrq_factor = GetFixedPointFactor(NsRatio::den, host_cntfrq);
|
|
||||||
us_cntfrq_factor = GetFixedPointFactor(UsRatio::den, host_cntfrq);
|
|
||||||
ms_cntfrq_factor = GetFixedPointFactor(MsRatio::den, host_cntfrq);
|
|
||||||
guest_cntfrq_factor = GetFixedPointFactor(CNTFRQ, host_cntfrq);
|
|
||||||
gputick_cntfrq_factor = GetFixedPointFactor(GPUTickFreq, host_cntfrq);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::chrono::nanoseconds NativeClock::GetTimeNS() const {
|
|
||||||
return std::chrono::nanoseconds{MultiplyHigh(GetHostTicksElapsed(), ns_cntfrq_factor)};
|
|
||||||
}
|
|
||||||
|
|
||||||
std::chrono::microseconds NativeClock::GetTimeUS() const {
|
|
||||||
return std::chrono::microseconds{MultiplyHigh(GetHostTicksElapsed(), us_cntfrq_factor)};
|
|
||||||
}
|
|
||||||
|
|
||||||
std::chrono::milliseconds NativeClock::GetTimeMS() const {
|
|
||||||
return std::chrono::milliseconds{MultiplyHigh(GetHostTicksElapsed(), ms_cntfrq_factor)};
|
|
||||||
}
|
|
||||||
|
|
||||||
u64 NativeClock::GetCNTPCT() const {
|
|
||||||
return MultiplyHigh(GetHostTicksElapsed(), guest_cntfrq_factor);
|
|
||||||
}
|
|
||||||
|
|
||||||
u64 NativeClock::GetGPUTick() const {
|
|
||||||
return MultiplyHigh(GetHostTicksElapsed(), gputick_cntfrq_factor);
|
|
||||||
}
|
|
||||||
|
|
||||||
u64 NativeClock::GetHostTicksNow() const {
|
|
||||||
u64 cntvct_el0 = 0;
|
|
||||||
asm volatile("dsb ish\n\t"
|
|
||||||
"mrs %[cntvct_el0], cntvct_el0\n\t"
|
|
||||||
"dsb ish\n\t"
|
|
||||||
: [cntvct_el0] "=r"(cntvct_el0));
|
|
||||||
return cntvct_el0;
|
|
||||||
}
|
|
||||||
|
|
||||||
u64 NativeClock::GetHostTicksElapsed() const {
|
|
||||||
return GetHostTicksNow();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool NativeClock::IsNative() const {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
u64 NativeClock::GetHostCNTFRQ() {
|
|
||||||
u64 cntfrq_el0 = 0;
|
|
||||||
asm("mrs %[cntfrq_el0], cntfrq_el0" : [cntfrq_el0] "=r"(cntfrq_el0));
|
|
||||||
return cntfrq_el0;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Common::Arm64
|
|
@ -1,47 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "common/wall_clock.h"
|
|
||||||
|
|
||||||
namespace Common::Arm64 {
|
|
||||||
|
|
||||||
class NativeClock final : public WallClock {
|
|
||||||
public:
|
|
||||||
explicit NativeClock();
|
|
||||||
|
|
||||||
std::chrono::nanoseconds GetTimeNS() const override;
|
|
||||||
|
|
||||||
std::chrono::microseconds GetTimeUS() const override;
|
|
||||||
|
|
||||||
std::chrono::milliseconds GetTimeMS() const override;
|
|
||||||
|
|
||||||
u64 GetCNTPCT() const override;
|
|
||||||
|
|
||||||
u64 GetGPUTick() const override;
|
|
||||||
|
|
||||||
u64 GetHostTicksNow() const override;
|
|
||||||
|
|
||||||
u64 GetHostTicksElapsed() const override;
|
|
||||||
|
|
||||||
bool IsNative() const override;
|
|
||||||
|
|
||||||
static u64 GetHostCNTFRQ();
|
|
||||||
|
|
||||||
public:
|
|
||||||
using FactorType = unsigned __int128;
|
|
||||||
|
|
||||||
FactorType GetGuestCNTFRQFactor() const {
|
|
||||||
return guest_cntfrq_factor;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
FactorType ns_cntfrq_factor;
|
|
||||||
FactorType us_cntfrq_factor;
|
|
||||||
FactorType ms_cntfrq_factor;
|
|
||||||
FactorType guest_cntfrq_factor;
|
|
||||||
FactorType gputick_cntfrq_factor;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace Common::Arm64
|
|
@ -39,12 +39,8 @@
|
|||||||
#define Crash() exit(1)
|
#define Crash() exit(1)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define LTO_NOINLINE __attribute__((noinline))
|
|
||||||
|
|
||||||
#else // _MSC_VER
|
#else // _MSC_VER
|
||||||
|
|
||||||
#define LTO_NOINLINE
|
|
||||||
|
|
||||||
// Locale Cross-Compatibility
|
// Locale Cross-Compatibility
|
||||||
#define locale_t _locale_t
|
#define locale_t _locale_t
|
||||||
|
|
||||||
|
@ -211,11 +211,6 @@ struct Elf64_Rela {
|
|||||||
Elf64_Sxword r_addend; /* Addend */
|
Elf64_Sxword r_addend; /* Addend */
|
||||||
};
|
};
|
||||||
|
|
||||||
/* RELR relocation table entry */
|
|
||||||
|
|
||||||
using Elf32_Relr = Elf32_Word;
|
|
||||||
using Elf64_Relr = Elf64_Xword;
|
|
||||||
|
|
||||||
/* How to extract and insert information held in the r_info field. */
|
/* How to extract and insert information held in the r_info field. */
|
||||||
|
|
||||||
static inline u32 Elf32RelSymIndex(Elf32_Word r_info) {
|
static inline u32 Elf32RelSymIndex(Elf32_Word r_info) {
|
||||||
@ -333,9 +328,6 @@ constexpr u32 ElfDtFiniArray = 26; /* Array with addresses of fini fct */
|
|||||||
constexpr u32 ElfDtInitArraySz = 27; /* Size in bytes of DT_INIT_ARRAY */
|
constexpr u32 ElfDtInitArraySz = 27; /* Size in bytes of DT_INIT_ARRAY */
|
||||||
constexpr u32 ElfDtFiniArraySz = 28; /* Size in bytes of DT_FINI_ARRAY */
|
constexpr u32 ElfDtFiniArraySz = 28; /* Size in bytes of DT_FINI_ARRAY */
|
||||||
constexpr u32 ElfDtSymtabShndx = 34; /* Address of SYMTAB_SHNDX section */
|
constexpr u32 ElfDtSymtabShndx = 34; /* Address of SYMTAB_SHNDX section */
|
||||||
constexpr u32 ElfDtRelrsz = 35; /* Size of RELR relative relocations */
|
|
||||||
constexpr u32 ElfDtRelr = 36; /* Address of RELR relative relocations */
|
|
||||||
constexpr u32 ElfDtRelrent = 37; /* Size of one RELR relative relocation */
|
|
||||||
|
|
||||||
} // namespace ELF
|
} // namespace ELF
|
||||||
} // namespace Common
|
} // namespace Common
|
||||||
|
@ -18,12 +18,10 @@
|
|||||||
#define LOAD_DIR "load"
|
#define LOAD_DIR "load"
|
||||||
#define LOG_DIR "log"
|
#define LOG_DIR "log"
|
||||||
#define NAND_DIR "nand"
|
#define NAND_DIR "nand"
|
||||||
#define PLAY_TIME_DIR "play_time"
|
|
||||||
#define SCREENSHOTS_DIR "screenshots"
|
#define SCREENSHOTS_DIR "screenshots"
|
||||||
#define SDMC_DIR "sdmc"
|
#define SDMC_DIR "sdmc"
|
||||||
#define SHADER_DIR "shader"
|
#define SHADER_DIR "shader"
|
||||||
#define TAS_DIR "tas"
|
#define TAS_DIR "tas"
|
||||||
#define ICONS_DIR "icons"
|
|
||||||
|
|
||||||
// yuzu-specific files
|
// yuzu-specific files
|
||||||
|
|
||||||
|
@ -124,12 +124,10 @@ public:
|
|||||||
GenerateYuzuPath(YuzuPath::LoadDir, yuzu_path / LOAD_DIR);
|
GenerateYuzuPath(YuzuPath::LoadDir, yuzu_path / LOAD_DIR);
|
||||||
GenerateYuzuPath(YuzuPath::LogDir, yuzu_path / LOG_DIR);
|
GenerateYuzuPath(YuzuPath::LogDir, yuzu_path / LOG_DIR);
|
||||||
GenerateYuzuPath(YuzuPath::NANDDir, yuzu_path / NAND_DIR);
|
GenerateYuzuPath(YuzuPath::NANDDir, yuzu_path / NAND_DIR);
|
||||||
GenerateYuzuPath(YuzuPath::PlayTimeDir, yuzu_path / PLAY_TIME_DIR);
|
|
||||||
GenerateYuzuPath(YuzuPath::ScreenshotsDir, yuzu_path / SCREENSHOTS_DIR);
|
GenerateYuzuPath(YuzuPath::ScreenshotsDir, yuzu_path / SCREENSHOTS_DIR);
|
||||||
GenerateYuzuPath(YuzuPath::SDMCDir, yuzu_path / SDMC_DIR);
|
GenerateYuzuPath(YuzuPath::SDMCDir, yuzu_path / SDMC_DIR);
|
||||||
GenerateYuzuPath(YuzuPath::ShaderDir, yuzu_path / SHADER_DIR);
|
GenerateYuzuPath(YuzuPath::ShaderDir, yuzu_path / SHADER_DIR);
|
||||||
GenerateYuzuPath(YuzuPath::TASDir, yuzu_path / TAS_DIR);
|
GenerateYuzuPath(YuzuPath::TASDir, yuzu_path / TAS_DIR);
|
||||||
GenerateYuzuPath(YuzuPath::IconsDir, yuzu_path / ICONS_DIR);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -20,12 +20,10 @@ enum class YuzuPath {
|
|||||||
LoadDir, // Where cheat/mod files are stored.
|
LoadDir, // Where cheat/mod files are stored.
|
||||||
LogDir, // Where log files are stored.
|
LogDir, // Where log files are stored.
|
||||||
NANDDir, // Where the emulated NAND is stored.
|
NANDDir, // Where the emulated NAND is stored.
|
||||||
PlayTimeDir, // Where play time data is stored.
|
|
||||||
ScreenshotsDir, // Where yuzu screenshots are stored.
|
ScreenshotsDir, // Where yuzu screenshots are stored.
|
||||||
SDMCDir, // Where the emulated SDMC is stored.
|
SDMCDir, // Where the emulated SDMC is stored.
|
||||||
ShaderDir, // Where shaders are stored.
|
ShaderDir, // Where shaders are stored.
|
||||||
TASDir, // Where TAS scripts are stored.
|
TASDir, // Where TAS scripts are stored.
|
||||||
IconsDir, // Where Icons for Windows shortcuts are stored.
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -15,13 +15,12 @@
|
|||||||
#include <condition_variable>
|
#include <condition_variable>
|
||||||
#include <stop_token>
|
#include <stop_token>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <utility>
|
|
||||||
|
|
||||||
namespace Common {
|
namespace Common {
|
||||||
|
|
||||||
template <typename Condvar, typename Lock, typename Pred>
|
template <typename Condvar, typename Lock, typename Pred>
|
||||||
void CondvarWait(Condvar& cv, std::unique_lock<Lock>& lk, std::stop_token token, Pred&& pred) {
|
void CondvarWait(Condvar& cv, std::unique_lock<Lock>& lk, std::stop_token token, Pred&& pred) {
|
||||||
cv.wait(lk, token, std::forward<Pred>(pred));
|
cv.wait(lk, token, std::move(pred));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Rep, typename Period>
|
template <typename Rep, typename Period>
|
||||||
@ -110,7 +109,7 @@ public:
|
|||||||
|
|
||||||
// Insert the callback.
|
// Insert the callback.
|
||||||
stop_state_callback ret = ++m_next_callback;
|
stop_state_callback ret = ++m_next_callback;
|
||||||
m_callbacks.emplace(ret, std::move(f));
|
m_callbacks.emplace(ret, move(f));
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -163,7 +162,7 @@ private:
|
|||||||
friend class stop_source;
|
friend class stop_source;
|
||||||
template <typename Callback>
|
template <typename Callback>
|
||||||
friend class stop_callback;
|
friend class stop_callback;
|
||||||
stop_token(shared_ptr<polyfill::stop_state> stop_state) : m_stop_state(std::move(stop_state)) {}
|
stop_token(shared_ptr<polyfill::stop_state> stop_state) : m_stop_state(move(stop_state)) {}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
shared_ptr<polyfill::stop_state> m_stop_state;
|
shared_ptr<polyfill::stop_state> m_stop_state;
|
||||||
@ -199,7 +198,7 @@ public:
|
|||||||
private:
|
private:
|
||||||
friend class jthread;
|
friend class jthread;
|
||||||
explicit stop_source(shared_ptr<polyfill::stop_state> stop_state)
|
explicit stop_source(shared_ptr<polyfill::stop_state> stop_state)
|
||||||
: m_stop_state(std::move(stop_state)) {}
|
: m_stop_state(move(stop_state)) {}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
shared_ptr<polyfill::stop_state> m_stop_state;
|
shared_ptr<polyfill::stop_state> m_stop_state;
|
||||||
@ -219,16 +218,16 @@ public:
|
|||||||
C&& cb) noexcept(is_nothrow_constructible_v<Callback, C>)
|
C&& cb) noexcept(is_nothrow_constructible_v<Callback, C>)
|
||||||
: m_stop_state(st.m_stop_state) {
|
: m_stop_state(st.m_stop_state) {
|
||||||
if (m_stop_state) {
|
if (m_stop_state) {
|
||||||
m_callback = m_stop_state->insert_callback(std::move(cb));
|
m_callback = m_stop_state->insert_callback(move(cb));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
template <typename C>
|
template <typename C>
|
||||||
requires constructible_from<Callback, C>
|
requires constructible_from<Callback, C>
|
||||||
explicit stop_callback(stop_token&& st,
|
explicit stop_callback(stop_token&& st,
|
||||||
C&& cb) noexcept(is_nothrow_constructible_v<Callback, C>)
|
C&& cb) noexcept(is_nothrow_constructible_v<Callback, C>)
|
||||||
: m_stop_state(std::move(st.m_stop_state)) {
|
: m_stop_state(move(st.m_stop_state)) {
|
||||||
if (m_stop_state) {
|
if (m_stop_state) {
|
||||||
m_callback = m_stop_state->insert_callback(std::move(cb));
|
m_callback = m_stop_state->insert_callback(move(cb));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
~stop_callback() {
|
~stop_callback() {
|
||||||
@ -261,7 +260,7 @@ public:
|
|||||||
typename = enable_if_t<!is_same_v<remove_cvref_t<F>, jthread>>>
|
typename = enable_if_t<!is_same_v<remove_cvref_t<F>, jthread>>>
|
||||||
explicit jthread(F&& f, Args&&... args)
|
explicit jthread(F&& f, Args&&... args)
|
||||||
: m_stop_state(make_shared<polyfill::stop_state>()),
|
: m_stop_state(make_shared<polyfill::stop_state>()),
|
||||||
m_thread(make_thread(std::forward<F>(f), std::forward<Args>(args)...)) {}
|
m_thread(make_thread(move(f), move(args)...)) {}
|
||||||
|
|
||||||
~jthread() {
|
~jthread() {
|
||||||
if (joinable()) {
|
if (joinable()) {
|
||||||
@ -318,9 +317,9 @@ private:
|
|||||||
template <typename F, typename... Args>
|
template <typename F, typename... Args>
|
||||||
thread make_thread(F&& f, Args&&... args) {
|
thread make_thread(F&& f, Args&&... args) {
|
||||||
if constexpr (is_invocable_v<decay_t<F>, stop_token, decay_t<Args>...>) {
|
if constexpr (is_invocable_v<decay_t<F>, stop_token, decay_t<Args>...>) {
|
||||||
return thread(std::forward<F>(f), get_stop_token(), std::forward<Args>(args)...);
|
return thread(move(f), get_stop_token(), move(args)...);
|
||||||
} else {
|
} else {
|
||||||
return thread(std::forward<F>(f), std::forward<Args>(args)...);
|
return thread(move(f), move(args)...);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,7 +45,6 @@ SWITCHABLE(CpuAccuracy, true);
|
|||||||
SWITCHABLE(FullscreenMode, true);
|
SWITCHABLE(FullscreenMode, true);
|
||||||
SWITCHABLE(GpuAccuracy, true);
|
SWITCHABLE(GpuAccuracy, true);
|
||||||
SWITCHABLE(Language, true);
|
SWITCHABLE(Language, true);
|
||||||
SWITCHABLE(MemoryLayout, true);
|
|
||||||
SWITCHABLE(NvdecEmulation, false);
|
SWITCHABLE(NvdecEmulation, false);
|
||||||
SWITCHABLE(Region, true);
|
SWITCHABLE(Region, true);
|
||||||
SWITCHABLE(RendererBackend, true);
|
SWITCHABLE(RendererBackend, true);
|
||||||
@ -62,10 +61,6 @@ SWITCHABLE(u32, false);
|
|||||||
SWITCHABLE(u8, false);
|
SWITCHABLE(u8, false);
|
||||||
SWITCHABLE(u8, true);
|
SWITCHABLE(u8, true);
|
||||||
|
|
||||||
// Used in UISettings
|
|
||||||
// TODO see if we can move this to uisettings.cpp
|
|
||||||
SWITCHABLE(ConfirmStop, true);
|
|
||||||
|
|
||||||
#undef SETTING
|
#undef SETTING
|
||||||
#undef SWITCHABLE
|
#undef SWITCHABLE
|
||||||
#endif
|
#endif
|
||||||
|
@ -67,7 +67,6 @@ SWITCHABLE(CpuAccuracy, true);
|
|||||||
SWITCHABLE(FullscreenMode, true);
|
SWITCHABLE(FullscreenMode, true);
|
||||||
SWITCHABLE(GpuAccuracy, true);
|
SWITCHABLE(GpuAccuracy, true);
|
||||||
SWITCHABLE(Language, true);
|
SWITCHABLE(Language, true);
|
||||||
SWITCHABLE(MemoryLayout, true);
|
|
||||||
SWITCHABLE(NvdecEmulation, false);
|
SWITCHABLE(NvdecEmulation, false);
|
||||||
SWITCHABLE(Region, true);
|
SWITCHABLE(Region, true);
|
||||||
SWITCHABLE(RendererBackend, true);
|
SWITCHABLE(RendererBackend, true);
|
||||||
@ -84,10 +83,6 @@ SWITCHABLE(u32, false);
|
|||||||
SWITCHABLE(u8, false);
|
SWITCHABLE(u8, false);
|
||||||
SWITCHABLE(u8, true);
|
SWITCHABLE(u8, true);
|
||||||
|
|
||||||
// Used in UISettings
|
|
||||||
// TODO see if we can move this to uisettings.h
|
|
||||||
SWITCHABLE(ConfirmStop, true);
|
|
||||||
|
|
||||||
#undef SETTING
|
#undef SETTING
|
||||||
#undef SWITCHABLE
|
#undef SWITCHABLE
|
||||||
#endif
|
#endif
|
||||||
|
@ -133,8 +133,6 @@ ENUM(CpuAccuracy, Auto, Accurate, Unsafe, Paranoid);
|
|||||||
|
|
||||||
ENUM(MemoryLayout, Memory_4Gb, Memory_6Gb, Memory_8Gb);
|
ENUM(MemoryLayout, Memory_4Gb, Memory_6Gb, Memory_8Gb);
|
||||||
|
|
||||||
ENUM(ConfirmStop, Ask_Always, Ask_Based_On_Game, Ask_Never);
|
|
||||||
|
|
||||||
ENUM(FullscreenMode, Borderless, Exclusive);
|
ENUM(FullscreenMode, Borderless, Exclusive);
|
||||||
|
|
||||||
ENUM(NvdecEmulation, Off, Cpu, Gpu);
|
ENUM(NvdecEmulation, Off, Cpu, Gpu);
|
||||||
|
@ -11,7 +11,6 @@
|
|||||||
#include <mach/mach.h>
|
#include <mach/mach.h>
|
||||||
#elif defined(_WIN32)
|
#elif defined(_WIN32)
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include "common/string_util.h"
|
|
||||||
#else
|
#else
|
||||||
#if defined(__Bitrig__) || defined(__DragonFly__) || defined(__FreeBSD__) || defined(__OpenBSD__)
|
#if defined(__Bitrig__) || defined(__DragonFly__) || defined(__FreeBSD__) || defined(__OpenBSD__)
|
||||||
#include <pthread_np.h>
|
#include <pthread_np.h>
|
||||||
@ -83,8 +82,29 @@ void SetCurrentThreadPriority(ThreadPriority new_priority) {
|
|||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
|
|
||||||
// Sets the debugger-visible name of the current thread.
|
// Sets the debugger-visible name of the current thread.
|
||||||
|
// Uses trick documented in:
|
||||||
|
// https://docs.microsoft.com/en-us/visualstudio/debugger/how-to-set-a-thread-name-in-native-code
|
||||||
void SetCurrentThreadName(const char* name) {
|
void SetCurrentThreadName(const char* name) {
|
||||||
SetThreadDescription(GetCurrentThread(), UTF8ToUTF16W(name).data());
|
static const DWORD MS_VC_EXCEPTION = 0x406D1388;
|
||||||
|
|
||||||
|
#pragma pack(push, 8)
|
||||||
|
struct THREADNAME_INFO {
|
||||||
|
DWORD dwType; // must be 0x1000
|
||||||
|
LPCSTR szName; // pointer to name (in user addr space)
|
||||||
|
DWORD dwThreadID; // thread ID (-1=caller thread)
|
||||||
|
DWORD dwFlags; // reserved for future use, must be zero
|
||||||
|
} info;
|
||||||
|
#pragma pack(pop)
|
||||||
|
|
||||||
|
info.dwType = 0x1000;
|
||||||
|
info.szName = name;
|
||||||
|
info.dwThreadID = std::numeric_limits<DWORD>::max();
|
||||||
|
info.dwFlags = 0;
|
||||||
|
|
||||||
|
__try {
|
||||||
|
RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), (ULONG_PTR*)&info);
|
||||||
|
} __except (EXCEPTION_CONTINUE_EXECUTION) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#else // !MSVC_VER, so must be POSIX threads
|
#else // !MSVC_VER, so must be POSIX threads
|
||||||
|
@ -10,10 +10,6 @@
|
|||||||
#include "common/x64/rdtsc.h"
|
#include "common/x64/rdtsc.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(ARCHITECTURE_arm64) && defined(__linux__)
|
|
||||||
#include "common/arm64/native_clock.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace Common {
|
namespace Common {
|
||||||
|
|
||||||
class StandardWallClock final : public WallClock {
|
class StandardWallClock final : public WallClock {
|
||||||
@ -57,7 +53,7 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
std::unique_ptr<WallClock> CreateOptimalClock() {
|
std::unique_ptr<WallClock> CreateOptimalClock() {
|
||||||
#if defined(ARCHITECTURE_x86_64)
|
#ifdef ARCHITECTURE_x86_64
|
||||||
const auto& caps = GetCPUCaps();
|
const auto& caps = GetCPUCaps();
|
||||||
|
|
||||||
if (caps.invariant_tsc && caps.tsc_frequency >= std::nano::den) {
|
if (caps.invariant_tsc && caps.tsc_frequency >= std::nano::den) {
|
||||||
@ -68,8 +64,6 @@ std::unique_ptr<WallClock> CreateOptimalClock() {
|
|||||||
// - Is not more precise than 1 GHz (1ns resolution)
|
// - Is not more precise than 1 GHz (1ns resolution)
|
||||||
return std::make_unique<StandardWallClock>();
|
return std::make_unique<StandardWallClock>();
|
||||||
}
|
}
|
||||||
#elif defined(ARCHITECTURE_arm64) && defined(__linux__)
|
|
||||||
return std::make_unique<Arm64::NativeClock>();
|
|
||||||
#else
|
#else
|
||||||
return std::make_unique<StandardWallClock>();
|
return std::make_unique<StandardWallClock>();
|
||||||
#endif
|
#endif
|
||||||
|
@ -466,18 +466,14 @@ add_library(core STATIC
|
|||||||
hle/service/caps/caps_a.h
|
hle/service/caps/caps_a.h
|
||||||
hle/service/caps/caps_c.cpp
|
hle/service/caps/caps_c.cpp
|
||||||
hle/service/caps/caps_c.h
|
hle/service/caps/caps_c.h
|
||||||
hle/service/caps/caps_manager.cpp
|
hle/service/caps/caps_u.cpp
|
||||||
hle/service/caps/caps_manager.h
|
hle/service/caps/caps_u.h
|
||||||
hle/service/caps/caps_result.h
|
|
||||||
hle/service/caps/caps_sc.cpp
|
hle/service/caps/caps_sc.cpp
|
||||||
hle/service/caps/caps_sc.h
|
hle/service/caps/caps_sc.h
|
||||||
hle/service/caps/caps_ss.cpp
|
hle/service/caps/caps_ss.cpp
|
||||||
hle/service/caps/caps_ss.h
|
hle/service/caps/caps_ss.h
|
||||||
hle/service/caps/caps_su.cpp
|
hle/service/caps/caps_su.cpp
|
||||||
hle/service/caps/caps_su.h
|
hle/service/caps/caps_su.h
|
||||||
hle/service/caps/caps_types.h
|
|
||||||
hle/service/caps/caps_u.cpp
|
|
||||||
hle/service/caps/caps_u.h
|
|
||||||
hle/service/erpt/erpt.cpp
|
hle/service/erpt/erpt.cpp
|
||||||
hle/service/erpt/erpt.h
|
hle/service/erpt/erpt.h
|
||||||
hle/service/es/es.cpp
|
hle/service/es/es.cpp
|
||||||
|
@ -116,8 +116,11 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return FileSys::ConcatenatedVfsFile::MakeConcatenatedFile(dir->GetName(),
|
if (concat.empty()) {
|
||||||
std::move(concat));
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return FileSys::ConcatenatedVfsFile::MakeConcatenatedFile(concat, dir->GetName());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Common::FS::IsDir(path)) {
|
if (Common::FS::IsDir(path)) {
|
||||||
|
@ -822,13 +822,11 @@ void GDBStub::HandleRcmd(const std::vector<u8>& command) {
|
|||||||
const char i = True(mem_info.attribute & MemoryAttribute::IpcLocked) ? 'I' : '-';
|
const char i = True(mem_info.attribute & MemoryAttribute::IpcLocked) ? 'I' : '-';
|
||||||
const char d = True(mem_info.attribute & MemoryAttribute::DeviceShared) ? 'D' : '-';
|
const char d = True(mem_info.attribute & MemoryAttribute::DeviceShared) ? 'D' : '-';
|
||||||
const char u = True(mem_info.attribute & MemoryAttribute::Uncached) ? 'U' : '-';
|
const char u = True(mem_info.attribute & MemoryAttribute::Uncached) ? 'U' : '-';
|
||||||
const char p =
|
|
||||||
True(mem_info.attribute & MemoryAttribute::PermissionLocked) ? 'P' : '-';
|
|
||||||
|
|
||||||
reply += fmt::format(" {:#012x} - {:#012x} {} {} {}{}{}{}{} [{}, {}]\n",
|
reply +=
|
||||||
mem_info.base_address,
|
fmt::format(" {:#012x} - {:#012x} {} {} {}{}{}{} [{}, {}]\n",
|
||||||
mem_info.base_address + mem_info.size - 1, perm, state, l, i,
|
mem_info.base_address, mem_info.base_address + mem_info.size - 1,
|
||||||
d, u, p, mem_info.ipc_count, mem_info.device_count);
|
perm, state, l, i, d, u, mem_info.ipc_count, mem_info.device_count);
|
||||||
}
|
}
|
||||||
|
|
||||||
const uintptr_t next_address = mem_info.base_address + mem_info.size;
|
const uintptr_t next_address = mem_info.base_address + mem_info.size;
|
||||||
|
@ -107,56 +107,62 @@ static u64 romfs_get_hash_table_count(u64 num_entries) {
|
|||||||
|
|
||||||
void RomFSBuildContext::VisitDirectory(VirtualDir romfs_dir, VirtualDir ext_dir,
|
void RomFSBuildContext::VisitDirectory(VirtualDir romfs_dir, VirtualDir ext_dir,
|
||||||
std::shared_ptr<RomFSBuildDirectoryContext> parent) {
|
std::shared_ptr<RomFSBuildDirectoryContext> parent) {
|
||||||
for (auto& child_romfs_file : romfs_dir->GetFiles()) {
|
std::vector<std::shared_ptr<RomFSBuildDirectoryContext>> child_dirs;
|
||||||
const auto name = child_romfs_file->GetName();
|
|
||||||
const auto child = std::make_shared<RomFSBuildFileContext>();
|
|
||||||
// Set child's path.
|
|
||||||
child->cur_path_ofs = parent->path_len + 1;
|
|
||||||
child->path_len = child->cur_path_ofs + static_cast<u32>(name.size());
|
|
||||||
child->path = parent->path + "/" + name;
|
|
||||||
|
|
||||||
if (ext_dir != nullptr && ext_dir->GetFile(name + ".stub") != nullptr) {
|
const auto entries = romfs_dir->GetEntries();
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sanity check on path_len
|
for (const auto& kv : entries) {
|
||||||
ASSERT(child->path_len < FS_MAX_PATH);
|
if (kv.second == VfsEntryType::Directory) {
|
||||||
|
const auto child = std::make_shared<RomFSBuildDirectoryContext>();
|
||||||
|
// Set child's path.
|
||||||
|
child->cur_path_ofs = parent->path_len + 1;
|
||||||
|
child->path_len = child->cur_path_ofs + static_cast<u32>(kv.first.size());
|
||||||
|
child->path = parent->path + "/" + kv.first;
|
||||||
|
|
||||||
child->source = std::move(child_romfs_file);
|
if (ext_dir != nullptr && ext_dir->GetFile(kv.first + ".stub") != nullptr) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (ext_dir != nullptr) {
|
// Sanity check on path_len
|
||||||
if (const auto ips = ext_dir->GetFile(name + ".ips")) {
|
ASSERT(child->path_len < FS_MAX_PATH);
|
||||||
if (auto patched = PatchIPS(child->source, ips)) {
|
|
||||||
child->source = std::move(patched);
|
if (AddDirectory(parent, child)) {
|
||||||
|
child_dirs.push_back(child);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const auto child = std::make_shared<RomFSBuildFileContext>();
|
||||||
|
// Set child's path.
|
||||||
|
child->cur_path_ofs = parent->path_len + 1;
|
||||||
|
child->path_len = child->cur_path_ofs + static_cast<u32>(kv.first.size());
|
||||||
|
child->path = parent->path + "/" + kv.first;
|
||||||
|
|
||||||
|
if (ext_dir != nullptr && ext_dir->GetFile(kv.first + ".stub") != nullptr) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sanity check on path_len
|
||||||
|
ASSERT(child->path_len < FS_MAX_PATH);
|
||||||
|
|
||||||
|
child->source = romfs_dir->GetFile(kv.first);
|
||||||
|
|
||||||
|
if (ext_dir != nullptr) {
|
||||||
|
if (const auto ips = ext_dir->GetFile(kv.first + ".ips")) {
|
||||||
|
if (auto patched = PatchIPS(child->source, ips)) {
|
||||||
|
child->source = std::move(patched);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
child->size = child->source->GetSize();
|
||||||
|
|
||||||
|
AddFile(parent, child);
|
||||||
}
|
}
|
||||||
|
|
||||||
child->size = child->source->GetSize();
|
|
||||||
|
|
||||||
AddFile(parent, child);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto& child_romfs_dir : romfs_dir->GetSubdirectories()) {
|
for (auto& child : child_dirs) {
|
||||||
const auto name = child_romfs_dir->GetName();
|
auto subdir_name = std::string_view(child->path).substr(child->cur_path_ofs);
|
||||||
const auto child = std::make_shared<RomFSBuildDirectoryContext>();
|
auto child_romfs_dir = romfs_dir->GetSubdirectory(subdir_name);
|
||||||
// Set child's path.
|
auto child_ext_dir = ext_dir != nullptr ? ext_dir->GetSubdirectory(subdir_name) : nullptr;
|
||||||
child->cur_path_ofs = parent->path_len + 1;
|
|
||||||
child->path_len = child->cur_path_ofs + static_cast<u32>(name.size());
|
|
||||||
child->path = parent->path + "/" + name;
|
|
||||||
|
|
||||||
if (ext_dir != nullptr && ext_dir->GetFile(name + ".stub") != nullptr) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sanity check on path_len
|
|
||||||
ASSERT(child->path_len < FS_MAX_PATH);
|
|
||||||
|
|
||||||
if (!AddDirectory(parent, child)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto child_ext_dir = ext_dir != nullptr ? ext_dir->GetSubdirectory(name) : nullptr;
|
|
||||||
this->VisitDirectory(child_romfs_dir, child_ext_dir, child);
|
this->VisitDirectory(child_romfs_dir, child_ext_dir, child);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -287,7 +293,7 @@ std::multimap<u64, VirtualFile> RomFSBuildContext::Build() {
|
|||||||
|
|
||||||
cur_entry.name_size = name_size;
|
cur_entry.name_size = name_size;
|
||||||
|
|
||||||
out.emplace(cur_file->offset + ROMFS_FILEPARTITION_OFS, std::move(cur_file->source));
|
out.emplace(cur_file->offset + ROMFS_FILEPARTITION_OFS, cur_file->source);
|
||||||
std::memcpy(file_table.data() + cur_file->entry_offset, &cur_entry, sizeof(RomFSFileEntry));
|
std::memcpy(file_table.data() + cur_file->entry_offset, &cur_entry, sizeof(RomFSFileEntry));
|
||||||
std::memset(file_table.data() + cur_file->entry_offset + sizeof(RomFSFileEntry), 0,
|
std::memset(file_table.data() + cur_file->entry_offset + sizeof(RomFSFileEntry), 0,
|
||||||
Common::AlignUp(cur_entry.name_size, 4));
|
Common::AlignUp(cur_entry.name_size, 4));
|
||||||
|
@ -377,16 +377,16 @@ static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType t
|
|||||||
|
|
||||||
auto romfs_dir = FindSubdirectoryCaseless(subdir, "romfs");
|
auto romfs_dir = FindSubdirectoryCaseless(subdir, "romfs");
|
||||||
if (romfs_dir != nullptr)
|
if (romfs_dir != nullptr)
|
||||||
layers.emplace_back(std::make_shared<CachedVfsDirectory>(std::move(romfs_dir)));
|
layers.push_back(std::make_shared<CachedVfsDirectory>(romfs_dir));
|
||||||
|
|
||||||
auto ext_dir = FindSubdirectoryCaseless(subdir, "romfs_ext");
|
auto ext_dir = FindSubdirectoryCaseless(subdir, "romfs_ext");
|
||||||
if (ext_dir != nullptr)
|
if (ext_dir != nullptr)
|
||||||
layers_ext.emplace_back(std::make_shared<CachedVfsDirectory>(std::move(ext_dir)));
|
layers_ext.push_back(std::make_shared<CachedVfsDirectory>(ext_dir));
|
||||||
|
|
||||||
if (type == ContentRecordType::HtmlDocument) {
|
if (type == ContentRecordType::HtmlDocument) {
|
||||||
auto manual_dir = FindSubdirectoryCaseless(subdir, "manual_html");
|
auto manual_dir = FindSubdirectoryCaseless(subdir, "manual_html");
|
||||||
if (manual_dir != nullptr)
|
if (manual_dir != nullptr)
|
||||||
layers.emplace_back(std::make_shared<CachedVfsDirectory>(std::move(manual_dir)));
|
layers.push_back(std::make_shared<CachedVfsDirectory>(manual_dir));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -400,7 +400,7 @@ static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType t
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
layers.emplace_back(std::move(extracted));
|
layers.push_back(std::move(extracted));
|
||||||
|
|
||||||
auto layered = LayeredVfsDirectory::MakeLayeredDirectory(std::move(layers));
|
auto layered = LayeredVfsDirectory::MakeLayeredDirectory(std::move(layers));
|
||||||
if (layered == nullptr) {
|
if (layered == nullptr) {
|
||||||
|
@ -322,8 +322,7 @@ VirtualFile RegisteredCache::OpenFileOrDirectoryConcat(const VirtualDir& open_di
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto name = concat.front()->GetName();
|
return ConcatenatedVfsFile::MakeConcatenatedFile(concat, concat.front()->GetName());
|
||||||
return ConcatenatedVfsFile::MakeConcatenatedFile(std::move(name), std::move(concat));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
VirtualFile RegisteredCache::GetFileAtID(NcaID id) const {
|
VirtualFile RegisteredCache::GetFileAtID(NcaID id) const {
|
||||||
|
@ -133,7 +133,7 @@ VirtualDir ExtractRomFS(VirtualFile file, RomFSExtractionType type) {
|
|||||||
out = out->GetSubdirectories().front();
|
out = out->GetSubdirectories().front();
|
||||||
}
|
}
|
||||||
|
|
||||||
return std::make_shared<CachedVfsDirectory>(std::move(out));
|
return std::make_shared<CachedVfsDirectory>(out);
|
||||||
}
|
}
|
||||||
|
|
||||||
VirtualFile CreateRomFS(VirtualDir dir, VirtualDir ext) {
|
VirtualFile CreateRomFS(VirtualDir dir, VirtualDir ext) {
|
||||||
@ -141,7 +141,8 @@ VirtualFile CreateRomFS(VirtualDir dir, VirtualDir ext) {
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
RomFSBuildContext ctx{dir, ext};
|
RomFSBuildContext ctx{dir, ext};
|
||||||
return ConcatenatedVfsFile::MakeConcatenatedFile(0, dir->GetName(), ctx.Build());
|
auto file_map = ctx.Build();
|
||||||
|
return ConcatenatedVfsFile::MakeConcatenatedFile(0, file_map, dir->GetName());
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace FileSys
|
} // namespace FileSys
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#include "common/logging/log.h"
|
|
||||||
#include "core/file_sys/system_archive/system_version.h"
|
#include "core/file_sys/system_archive/system_version.h"
|
||||||
#include "core/file_sys/vfs_vector.h"
|
#include "core/file_sys/vfs_vector.h"
|
||||||
#include "core/hle/api_version.h"
|
#include "core/hle/api_version.h"
|
||||||
@ -13,9 +12,6 @@ std::string GetLongDisplayVersion() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
VirtualDir SystemVersion() {
|
VirtualDir SystemVersion() {
|
||||||
LOG_WARNING(Common_Filesystem, "called - Using hardcoded firmware version '{}'",
|
|
||||||
GetLongDisplayVersion());
|
|
||||||
|
|
||||||
VirtualFile file = std::make_shared<VectorVfsFile>(std::vector<u8>(0x100), "file");
|
VirtualFile file = std::make_shared<VectorVfsFile>(std::vector<u8>(0x100), "file");
|
||||||
file->WriteObject(HLE::ApiVersion::HOS_VERSION_MAJOR, 0);
|
file->WriteObject(HLE::ApiVersion::HOS_VERSION_MAJOR, 0);
|
||||||
file->WriteObject(HLE::ApiVersion::HOS_VERSION_MINOR, 1);
|
file->WriteObject(HLE::ApiVersion::HOS_VERSION_MINOR, 1);
|
||||||
|
@ -6,13 +6,13 @@
|
|||||||
|
|
||||||
namespace FileSys {
|
namespace FileSys {
|
||||||
|
|
||||||
CachedVfsDirectory::CachedVfsDirectory(VirtualDir&& source_dir)
|
CachedVfsDirectory::CachedVfsDirectory(VirtualDir& source_dir)
|
||||||
: name(source_dir->GetName()), parent(source_dir->GetParentDirectory()) {
|
: name(source_dir->GetName()), parent(source_dir->GetParentDirectory()) {
|
||||||
for (auto& dir : source_dir->GetSubdirectories()) {
|
for (auto& dir : source_dir->GetSubdirectories()) {
|
||||||
dirs.emplace(dir->GetName(), std::make_shared<CachedVfsDirectory>(std::move(dir)));
|
dirs.emplace(dir->GetName(), std::make_shared<CachedVfsDirectory>(dir));
|
||||||
}
|
}
|
||||||
for (auto& file : source_dir->GetFiles()) {
|
for (auto& file : source_dir->GetFiles()) {
|
||||||
files.emplace(file->GetName(), std::move(file));
|
files.emplace(file->GetName(), file);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ namespace FileSys {
|
|||||||
|
|
||||||
class CachedVfsDirectory : public ReadOnlyVfsDirectory {
|
class CachedVfsDirectory : public ReadOnlyVfsDirectory {
|
||||||
public:
|
public:
|
||||||
CachedVfsDirectory(VirtualDir&& source_directory);
|
CachedVfsDirectory(VirtualDir& source_directory);
|
||||||
|
|
||||||
~CachedVfsDirectory() override;
|
~CachedVfsDirectory() override;
|
||||||
VirtualFile GetFile(std::string_view file_name) const override;
|
VirtualFile GetFile(std::string_view file_name) const override;
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
|
|
||||||
namespace FileSys {
|
namespace FileSys {
|
||||||
|
|
||||||
ConcatenatedVfsFile::ConcatenatedVfsFile(std::string&& name_, ConcatenationMap&& concatenation_map_)
|
ConcatenatedVfsFile::ConcatenatedVfsFile(ConcatenationMap&& concatenation_map_, std::string&& name_)
|
||||||
: concatenation_map(std::move(concatenation_map_)), name(std::move(name_)) {
|
: concatenation_map(std::move(concatenation_map_)), name(std::move(name_)) {
|
||||||
DEBUG_ASSERT(this->VerifyContinuity());
|
DEBUG_ASSERT(this->VerifyContinuity());
|
||||||
}
|
}
|
||||||
@ -30,8 +30,8 @@ bool ConcatenatedVfsFile::VerifyContinuity() const {
|
|||||||
|
|
||||||
ConcatenatedVfsFile::~ConcatenatedVfsFile() = default;
|
ConcatenatedVfsFile::~ConcatenatedVfsFile() = default;
|
||||||
|
|
||||||
VirtualFile ConcatenatedVfsFile::MakeConcatenatedFile(std::string&& name,
|
VirtualFile ConcatenatedVfsFile::MakeConcatenatedFile(const std::vector<VirtualFile>& files,
|
||||||
std::vector<VirtualFile>&& files) {
|
std::string&& name) {
|
||||||
// Fold trivial cases.
|
// Fold trivial cases.
|
||||||
if (files.empty()) {
|
if (files.empty()) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
@ -46,21 +46,20 @@ VirtualFile ConcatenatedVfsFile::MakeConcatenatedFile(std::string&& name,
|
|||||||
u64 last_offset = 0;
|
u64 last_offset = 0;
|
||||||
|
|
||||||
for (auto& file : files) {
|
for (auto& file : files) {
|
||||||
const auto size = file->GetSize();
|
|
||||||
|
|
||||||
concatenation_map.emplace_back(ConcatenationEntry{
|
concatenation_map.emplace_back(ConcatenationEntry{
|
||||||
.offset = last_offset,
|
.offset = last_offset,
|
||||||
.file = std::move(file),
|
.file = file,
|
||||||
});
|
});
|
||||||
|
|
||||||
last_offset += size;
|
last_offset += file->GetSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
return VirtualFile(new ConcatenatedVfsFile(std::move(name), std::move(concatenation_map)));
|
return VirtualFile(new ConcatenatedVfsFile(std::move(concatenation_map), std::move(name)));
|
||||||
}
|
}
|
||||||
|
|
||||||
VirtualFile ConcatenatedVfsFile::MakeConcatenatedFile(u8 filler_byte, std::string&& name,
|
VirtualFile ConcatenatedVfsFile::MakeConcatenatedFile(u8 filler_byte,
|
||||||
std::multimap<u64, VirtualFile>&& files) {
|
const std::multimap<u64, VirtualFile>& files,
|
||||||
|
std::string&& name) {
|
||||||
// Fold trivial cases.
|
// Fold trivial cases.
|
||||||
if (files.empty()) {
|
if (files.empty()) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
@ -77,8 +76,6 @@ VirtualFile ConcatenatedVfsFile::MakeConcatenatedFile(u8 filler_byte, std::strin
|
|||||||
|
|
||||||
// Iteration of a multimap is ordered, so offset will be strictly non-decreasing.
|
// Iteration of a multimap is ordered, so offset will be strictly non-decreasing.
|
||||||
for (auto& [offset, file] : files) {
|
for (auto& [offset, file] : files) {
|
||||||
const auto size = file->GetSize();
|
|
||||||
|
|
||||||
if (offset > last_offset) {
|
if (offset > last_offset) {
|
||||||
concatenation_map.emplace_back(ConcatenationEntry{
|
concatenation_map.emplace_back(ConcatenationEntry{
|
||||||
.offset = last_offset,
|
.offset = last_offset,
|
||||||
@ -88,13 +85,13 @@ VirtualFile ConcatenatedVfsFile::MakeConcatenatedFile(u8 filler_byte, std::strin
|
|||||||
|
|
||||||
concatenation_map.emplace_back(ConcatenationEntry{
|
concatenation_map.emplace_back(ConcatenationEntry{
|
||||||
.offset = offset,
|
.offset = offset,
|
||||||
.file = std::move(file),
|
.file = file,
|
||||||
});
|
});
|
||||||
|
|
||||||
last_offset = offset + size;
|
last_offset = offset + file->GetSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
return VirtualFile(new ConcatenatedVfsFile(std::move(name), std::move(concatenation_map)));
|
return VirtualFile(new ConcatenatedVfsFile(std::move(concatenation_map), std::move(name)));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string ConcatenatedVfsFile::GetName() const {
|
std::string ConcatenatedVfsFile::GetName() const {
|
||||||
|
@ -24,20 +24,22 @@ private:
|
|||||||
};
|
};
|
||||||
using ConcatenationMap = std::vector<ConcatenationEntry>;
|
using ConcatenationMap = std::vector<ConcatenationEntry>;
|
||||||
|
|
||||||
explicit ConcatenatedVfsFile(std::string&& name,
|
explicit ConcatenatedVfsFile(std::vector<ConcatenationEntry>&& concatenation_map,
|
||||||
std::vector<ConcatenationEntry>&& concatenation_map);
|
std::string&& name);
|
||||||
bool VerifyContinuity() const;
|
bool VerifyContinuity() const;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
~ConcatenatedVfsFile() override;
|
~ConcatenatedVfsFile() override;
|
||||||
|
|
||||||
/// Wrapper function to allow for more efficient handling of files.size() == 0, 1 cases.
|
/// Wrapper function to allow for more efficient handling of files.size() == 0, 1 cases.
|
||||||
static VirtualFile MakeConcatenatedFile(std::string&& name, std::vector<VirtualFile>&& files);
|
static VirtualFile MakeConcatenatedFile(const std::vector<VirtualFile>& files,
|
||||||
|
std::string&& name);
|
||||||
|
|
||||||
/// Convenience function that turns a map of offsets to files into a concatenated file, filling
|
/// Convenience function that turns a map of offsets to files into a concatenated file, filling
|
||||||
/// gaps with a given filler byte.
|
/// gaps with a given filler byte.
|
||||||
static VirtualFile MakeConcatenatedFile(u8 filler_byte, std::string&& name,
|
static VirtualFile MakeConcatenatedFile(u8 filler_byte,
|
||||||
std::multimap<u64, VirtualFile>&& files);
|
const std::multimap<u64, VirtualFile>& files,
|
||||||
|
std::string&& name);
|
||||||
|
|
||||||
std::string GetName() const override;
|
std::string GetName() const override;
|
||||||
std::size_t GetSize() const override;
|
std::size_t GetSize() const override;
|
||||||
|
@ -38,7 +38,7 @@ VirtualDir LayeredVfsDirectory::GetDirectoryRelative(std::string_view path) cons
|
|||||||
for (const auto& layer : dirs) {
|
for (const auto& layer : dirs) {
|
||||||
auto dir = layer->GetDirectoryRelative(path);
|
auto dir = layer->GetDirectoryRelative(path);
|
||||||
if (dir != nullptr) {
|
if (dir != nullptr) {
|
||||||
out.emplace_back(std::move(dir));
|
out.push_back(std::move(dir));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -62,11 +62,11 @@ std::vector<VirtualFile> LayeredVfsDirectory::GetFiles() const {
|
|||||||
std::set<std::string, std::less<>> out_names;
|
std::set<std::string, std::less<>> out_names;
|
||||||
|
|
||||||
for (const auto& layer : dirs) {
|
for (const auto& layer : dirs) {
|
||||||
for (auto& file : layer->GetFiles()) {
|
for (const auto& file : layer->GetFiles()) {
|
||||||
auto file_name = file->GetName();
|
auto file_name = file->GetName();
|
||||||
if (!out_names.contains(file_name)) {
|
if (!out_names.contains(file_name)) {
|
||||||
out_names.emplace(std::move(file_name));
|
out_names.emplace(std::move(file_name));
|
||||||
out.emplace_back(std::move(file));
|
out.push_back(file);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -86,7 +86,7 @@ std::vector<VirtualDir> LayeredVfsDirectory::GetSubdirectories() const {
|
|||||||
std::vector<VirtualDir> out;
|
std::vector<VirtualDir> out;
|
||||||
out.reserve(names.size());
|
out.reserve(names.size());
|
||||||
for (const auto& subdir : names)
|
for (const auto& subdir : names)
|
||||||
out.emplace_back(GetSubdirectory(subdir));
|
out.push_back(GetSubdirectory(subdir));
|
||||||
|
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
@ -106,7 +106,7 @@ static_assert(KernelPageBufferAdditionalSize ==
|
|||||||
/// memory.
|
/// memory.
|
||||||
static KPhysicalAddress TranslateSlabAddrToPhysical(KMemoryLayout& memory_layout,
|
static KPhysicalAddress TranslateSlabAddrToPhysical(KMemoryLayout& memory_layout,
|
||||||
KVirtualAddress slab_addr) {
|
KVirtualAddress slab_addr) {
|
||||||
slab_addr -= memory_layout.GetSlabRegion().GetAddress();
|
slab_addr -= GetInteger(memory_layout.GetSlabRegionAddress());
|
||||||
return GetInteger(slab_addr) + Core::DramMemoryMap::SlabHeapBase;
|
return GetInteger(slab_addr) + Core::DramMemoryMap::SlabHeapBase;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -196,12 +196,7 @@ void InitializeSlabHeaps(Core::System& system, KMemoryLayout& memory_layout) {
|
|||||||
auto& kernel = system.Kernel();
|
auto& kernel = system.Kernel();
|
||||||
|
|
||||||
// Get the start of the slab region, since that's where we'll be working.
|
// Get the start of the slab region, since that's where we'll be working.
|
||||||
const KMemoryRegion& slab_region = memory_layout.GetSlabRegion();
|
KVirtualAddress address = memory_layout.GetSlabRegionAddress();
|
||||||
KVirtualAddress address = slab_region.GetAddress();
|
|
||||||
|
|
||||||
// Clear the slab region.
|
|
||||||
// TODO: implement access to kernel VAs.
|
|
||||||
// std::memset(device_ptr, 0, slab_region.GetSize());
|
|
||||||
|
|
||||||
// Initialize slab type array to be in sorted order.
|
// Initialize slab type array to be in sorted order.
|
||||||
std::array<KSlabType, KSlabType_Count> slab_types;
|
std::array<KSlabType, KSlabType_Count> slab_types;
|
||||||
|
@ -19,8 +19,4 @@ static inline KPhysicalAddress GetInitialProcessBinaryPhysicalAddress() {
|
|||||||
MainMemoryAddress);
|
MainMemoryAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline size_t GetInitialProcessBinarySize() {
|
|
||||||
return InitialProcessBinarySizeMax;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Kernel
|
} // namespace Kernel
|
||||||
|
@ -36,7 +36,6 @@ enum class KMemoryState : u32 {
|
|||||||
FlagCanChangeAttribute = (1 << 24),
|
FlagCanChangeAttribute = (1 << 24),
|
||||||
FlagCanCodeMemory = (1 << 25),
|
FlagCanCodeMemory = (1 << 25),
|
||||||
FlagLinearMapped = (1 << 26),
|
FlagLinearMapped = (1 << 26),
|
||||||
FlagCanPermissionLock = (1 << 27),
|
|
||||||
|
|
||||||
FlagsData = FlagCanReprotect | FlagCanUseIpc | FlagCanUseNonDeviceIpc | FlagCanUseNonSecureIpc |
|
FlagsData = FlagCanReprotect | FlagCanUseIpc | FlagCanUseNonDeviceIpc | FlagCanUseNonSecureIpc |
|
||||||
FlagMapped | FlagCanAlias | FlagCanTransfer | FlagCanQueryPhysical |
|
FlagMapped | FlagCanAlias | FlagCanTransfer | FlagCanQueryPhysical |
|
||||||
@ -51,16 +50,12 @@ enum class KMemoryState : u32 {
|
|||||||
FlagLinearMapped,
|
FlagLinearMapped,
|
||||||
|
|
||||||
Free = static_cast<u32>(Svc::MemoryState::Free),
|
Free = static_cast<u32>(Svc::MemoryState::Free),
|
||||||
|
Io = static_cast<u32>(Svc::MemoryState::Io) | FlagMapped | FlagCanDeviceMap |
|
||||||
IoMemory = static_cast<u32>(Svc::MemoryState::Io) | FlagMapped | FlagCanDeviceMap |
|
FlagCanAlignedDeviceMap,
|
||||||
FlagCanAlignedDeviceMap,
|
|
||||||
IoRegister =
|
|
||||||
static_cast<u32>(Svc::MemoryState::Io) | FlagCanDeviceMap | FlagCanAlignedDeviceMap,
|
|
||||||
|
|
||||||
Static = static_cast<u32>(Svc::MemoryState::Static) | FlagMapped | FlagCanQueryPhysical,
|
Static = static_cast<u32>(Svc::MemoryState::Static) | FlagMapped | FlagCanQueryPhysical,
|
||||||
Code = static_cast<u32>(Svc::MemoryState::Code) | FlagsCode | FlagCanMapProcess,
|
Code = static_cast<u32>(Svc::MemoryState::Code) | FlagsCode | FlagCanMapProcess,
|
||||||
CodeData = static_cast<u32>(Svc::MemoryState::CodeData) | FlagsData | FlagCanMapProcess |
|
CodeData = static_cast<u32>(Svc::MemoryState::CodeData) | FlagsData | FlagCanMapProcess |
|
||||||
FlagCanCodeMemory | FlagCanPermissionLock,
|
FlagCanCodeMemory,
|
||||||
Normal = static_cast<u32>(Svc::MemoryState::Normal) | FlagsData | FlagCanCodeMemory,
|
Normal = static_cast<u32>(Svc::MemoryState::Normal) | FlagsData | FlagCanCodeMemory,
|
||||||
Shared = static_cast<u32>(Svc::MemoryState::Shared) | FlagMapped | FlagReferenceCounted |
|
Shared = static_cast<u32>(Svc::MemoryState::Shared) | FlagMapped | FlagReferenceCounted |
|
||||||
FlagLinearMapped,
|
FlagLinearMapped,
|
||||||
@ -70,8 +65,7 @@ enum class KMemoryState : u32 {
|
|||||||
AliasCode = static_cast<u32>(Svc::MemoryState::AliasCode) | FlagsCode | FlagCanMapProcess |
|
AliasCode = static_cast<u32>(Svc::MemoryState::AliasCode) | FlagsCode | FlagCanMapProcess |
|
||||||
FlagCanCodeAlias,
|
FlagCanCodeAlias,
|
||||||
AliasCodeData = static_cast<u32>(Svc::MemoryState::AliasCodeData) | FlagsData |
|
AliasCodeData = static_cast<u32>(Svc::MemoryState::AliasCodeData) | FlagsData |
|
||||||
FlagCanMapProcess | FlagCanCodeAlias | FlagCanCodeMemory |
|
FlagCanMapProcess | FlagCanCodeAlias | FlagCanCodeMemory,
|
||||||
FlagCanPermissionLock,
|
|
||||||
|
|
||||||
Ipc = static_cast<u32>(Svc::MemoryState::Ipc) | FlagsMisc | FlagCanAlignedDeviceMap |
|
Ipc = static_cast<u32>(Svc::MemoryState::Ipc) | FlagsMisc | FlagCanAlignedDeviceMap |
|
||||||
FlagCanUseIpc | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc,
|
FlagCanUseIpc | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc,
|
||||||
@ -79,7 +73,7 @@ enum class KMemoryState : u32 {
|
|||||||
Stack = static_cast<u32>(Svc::MemoryState::Stack) | FlagsMisc | FlagCanAlignedDeviceMap |
|
Stack = static_cast<u32>(Svc::MemoryState::Stack) | FlagsMisc | FlagCanAlignedDeviceMap |
|
||||||
FlagCanUseIpc | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc,
|
FlagCanUseIpc | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc,
|
||||||
|
|
||||||
ThreadLocal = static_cast<u32>(Svc::MemoryState::ThreadLocal) | FlagLinearMapped,
|
ThreadLocal = static_cast<u32>(Svc::MemoryState::ThreadLocal) | FlagMapped | FlagLinearMapped,
|
||||||
|
|
||||||
Transfered = static_cast<u32>(Svc::MemoryState::Transfered) | FlagsMisc |
|
Transfered = static_cast<u32>(Svc::MemoryState::Transfered) | FlagsMisc |
|
||||||
FlagCanAlignedDeviceMap | FlagCanChangeAttribute | FlagCanUseIpc |
|
FlagCanAlignedDeviceMap | FlagCanChangeAttribute | FlagCanUseIpc |
|
||||||
@ -100,7 +94,7 @@ enum class KMemoryState : u32 {
|
|||||||
NonDeviceIpc =
|
NonDeviceIpc =
|
||||||
static_cast<u32>(Svc::MemoryState::NonDeviceIpc) | FlagsMisc | FlagCanUseNonDeviceIpc,
|
static_cast<u32>(Svc::MemoryState::NonDeviceIpc) | FlagsMisc | FlagCanUseNonDeviceIpc,
|
||||||
|
|
||||||
Kernel = static_cast<u32>(Svc::MemoryState::Kernel),
|
Kernel = static_cast<u32>(Svc::MemoryState::Kernel) | FlagMapped,
|
||||||
|
|
||||||
GeneratedCode = static_cast<u32>(Svc::MemoryState::GeneratedCode) | FlagMapped |
|
GeneratedCode = static_cast<u32>(Svc::MemoryState::GeneratedCode) | FlagMapped |
|
||||||
FlagReferenceCounted | FlagCanDebug | FlagLinearMapped,
|
FlagReferenceCounted | FlagCanDebug | FlagLinearMapped,
|
||||||
@ -111,36 +105,34 @@ enum class KMemoryState : u32 {
|
|||||||
|
|
||||||
Insecure = static_cast<u32>(Svc::MemoryState::Insecure) | FlagMapped | FlagReferenceCounted |
|
Insecure = static_cast<u32>(Svc::MemoryState::Insecure) | FlagMapped | FlagReferenceCounted |
|
||||||
FlagLinearMapped | FlagCanChangeAttribute | FlagCanDeviceMap |
|
FlagLinearMapped | FlagCanChangeAttribute | FlagCanDeviceMap |
|
||||||
FlagCanAlignedDeviceMap | FlagCanQueryPhysical | FlagCanUseNonSecureIpc |
|
FlagCanAlignedDeviceMap | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc,
|
||||||
FlagCanUseNonDeviceIpc,
|
|
||||||
};
|
};
|
||||||
DECLARE_ENUM_FLAG_OPERATORS(KMemoryState);
|
DECLARE_ENUM_FLAG_OPERATORS(KMemoryState);
|
||||||
|
|
||||||
static_assert(static_cast<u32>(KMemoryState::Free) == 0x00000000);
|
static_assert(static_cast<u32>(KMemoryState::Free) == 0x00000000);
|
||||||
static_assert(static_cast<u32>(KMemoryState::IoMemory) == 0x00182001);
|
static_assert(static_cast<u32>(KMemoryState::Io) == 0x00182001);
|
||||||
static_assert(static_cast<u32>(KMemoryState::IoRegister) == 0x00180001);
|
|
||||||
static_assert(static_cast<u32>(KMemoryState::Static) == 0x00042002);
|
static_assert(static_cast<u32>(KMemoryState::Static) == 0x00042002);
|
||||||
static_assert(static_cast<u32>(KMemoryState::Code) == 0x04DC7E03);
|
static_assert(static_cast<u32>(KMemoryState::Code) == 0x04DC7E03);
|
||||||
static_assert(static_cast<u32>(KMemoryState::CodeData) == 0x0FFEBD04);
|
static_assert(static_cast<u32>(KMemoryState::CodeData) == 0x07FEBD04);
|
||||||
static_assert(static_cast<u32>(KMemoryState::Normal) == 0x077EBD05);
|
static_assert(static_cast<u32>(KMemoryState::Normal) == 0x077EBD05);
|
||||||
static_assert(static_cast<u32>(KMemoryState::Shared) == 0x04402006);
|
static_assert(static_cast<u32>(KMemoryState::Shared) == 0x04402006);
|
||||||
|
|
||||||
static_assert(static_cast<u32>(KMemoryState::AliasCode) == 0x04DD7E08);
|
static_assert(static_cast<u32>(KMemoryState::AliasCode) == 0x04DD7E08);
|
||||||
static_assert(static_cast<u32>(KMemoryState::AliasCodeData) == 0x0FFFBD09);
|
static_assert(static_cast<u32>(KMemoryState::AliasCodeData) == 0x07FFBD09);
|
||||||
static_assert(static_cast<u32>(KMemoryState::Ipc) == 0x045C3C0A);
|
static_assert(static_cast<u32>(KMemoryState::Ipc) == 0x045C3C0A);
|
||||||
static_assert(static_cast<u32>(KMemoryState::Stack) == 0x045C3C0B);
|
static_assert(static_cast<u32>(KMemoryState::Stack) == 0x045C3C0B);
|
||||||
static_assert(static_cast<u32>(KMemoryState::ThreadLocal) == 0x0400000C);
|
static_assert(static_cast<u32>(KMemoryState::ThreadLocal) == 0x0400200C);
|
||||||
static_assert(static_cast<u32>(KMemoryState::Transfered) == 0x055C3C0D);
|
static_assert(static_cast<u32>(KMemoryState::Transfered) == 0x055C3C0D);
|
||||||
static_assert(static_cast<u32>(KMemoryState::SharedTransfered) == 0x045C380E);
|
static_assert(static_cast<u32>(KMemoryState::SharedTransfered) == 0x045C380E);
|
||||||
static_assert(static_cast<u32>(KMemoryState::SharedCode) == 0x0440380F);
|
static_assert(static_cast<u32>(KMemoryState::SharedCode) == 0x0440380F);
|
||||||
static_assert(static_cast<u32>(KMemoryState::Inaccessible) == 0x00000010);
|
static_assert(static_cast<u32>(KMemoryState::Inaccessible) == 0x00000010);
|
||||||
static_assert(static_cast<u32>(KMemoryState::NonSecureIpc) == 0x045C3811);
|
static_assert(static_cast<u32>(KMemoryState::NonSecureIpc) == 0x045C3811);
|
||||||
static_assert(static_cast<u32>(KMemoryState::NonDeviceIpc) == 0x044C2812);
|
static_assert(static_cast<u32>(KMemoryState::NonDeviceIpc) == 0x044C2812);
|
||||||
static_assert(static_cast<u32>(KMemoryState::Kernel) == 0x00000013);
|
static_assert(static_cast<u32>(KMemoryState::Kernel) == 0x00002013);
|
||||||
static_assert(static_cast<u32>(KMemoryState::GeneratedCode) == 0x04402214);
|
static_assert(static_cast<u32>(KMemoryState::GeneratedCode) == 0x04402214);
|
||||||
static_assert(static_cast<u32>(KMemoryState::CodeOut) == 0x04402015);
|
static_assert(static_cast<u32>(KMemoryState::CodeOut) == 0x04402015);
|
||||||
static_assert(static_cast<u32>(KMemoryState::Coverage) == 0x00002016);
|
static_assert(static_cast<u32>(KMemoryState::Coverage) == 0x00002016);
|
||||||
static_assert(static_cast<u32>(KMemoryState::Insecure) == 0x055C3817);
|
static_assert(static_cast<u32>(KMemoryState::Insecure) == 0x05583817);
|
||||||
|
|
||||||
enum class KMemoryPermission : u8 {
|
enum class KMemoryPermission : u8 {
|
||||||
None = 0,
|
None = 0,
|
||||||
@ -190,9 +182,8 @@ enum class KMemoryAttribute : u8 {
|
|||||||
IpcLocked = static_cast<u8>(Svc::MemoryAttribute::IpcLocked),
|
IpcLocked = static_cast<u8>(Svc::MemoryAttribute::IpcLocked),
|
||||||
DeviceShared = static_cast<u8>(Svc::MemoryAttribute::DeviceShared),
|
DeviceShared = static_cast<u8>(Svc::MemoryAttribute::DeviceShared),
|
||||||
Uncached = static_cast<u8>(Svc::MemoryAttribute::Uncached),
|
Uncached = static_cast<u8>(Svc::MemoryAttribute::Uncached),
|
||||||
PermissionLocked = static_cast<u8>(Svc::MemoryAttribute::PermissionLocked),
|
|
||||||
|
|
||||||
SetMask = Uncached | PermissionLocked,
|
SetMask = Uncached,
|
||||||
};
|
};
|
||||||
DECLARE_ENUM_FLAG_OPERATORS(KMemoryAttribute);
|
DECLARE_ENUM_FLAG_OPERATORS(KMemoryAttribute);
|
||||||
|
|
||||||
@ -270,10 +261,6 @@ struct KMemoryInfo {
|
|||||||
return m_state;
|
return m_state;
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr Svc::MemoryState GetSvcState() const {
|
|
||||||
return static_cast<Svc::MemoryState>(m_state & KMemoryState::Mask);
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr KMemoryPermission GetPermission() const {
|
constexpr KMemoryPermission GetPermission() const {
|
||||||
return m_permission;
|
return m_permission;
|
||||||
}
|
}
|
||||||
@ -339,10 +326,6 @@ public:
|
|||||||
return this->GetEndAddress() - 1;
|
return this->GetEndAddress() - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr KMemoryState GetState() const {
|
|
||||||
return m_memory_state;
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr u16 GetIpcLockCount() const {
|
constexpr u16 GetIpcLockCount() const {
|
||||||
return m_ipc_lock_count;
|
return m_ipc_lock_count;
|
||||||
}
|
}
|
||||||
@ -460,13 +443,6 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr void UpdateAttribute(KMemoryAttribute mask, KMemoryAttribute attr) {
|
|
||||||
ASSERT(False(mask & KMemoryAttribute::IpcLocked));
|
|
||||||
ASSERT(False(mask & KMemoryAttribute::DeviceShared));
|
|
||||||
|
|
||||||
m_attribute = (m_attribute & ~mask) | attr;
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr void Split(KMemoryBlock* block, KProcessAddress addr) {
|
constexpr void Split(KMemoryBlock* block, KProcessAddress addr) {
|
||||||
ASSERT(this->GetAddress() < addr);
|
ASSERT(this->GetAddress() < addr);
|
||||||
ASSERT(this->Contains(addr));
|
ASSERT(this->Contains(addr));
|
||||||
|
@ -160,8 +160,8 @@ void KMemoryBlockManager::Update(KMemoryBlockManagerUpdateAllocator* allocator,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Update block state.
|
// Update block state.
|
||||||
it->Update(state, perm, attr, it->GetAddress() == address,
|
it->Update(state, perm, attr, cur_address == address, static_cast<u8>(set_disable_attr),
|
||||||
static_cast<u8>(set_disable_attr), static_cast<u8>(clear_disable_attr));
|
static_cast<u8>(clear_disable_attr));
|
||||||
cur_address += cur_info.GetSize();
|
cur_address += cur_info.GetSize();
|
||||||
remaining_pages -= cur_info.GetNumPages();
|
remaining_pages -= cur_info.GetNumPages();
|
||||||
}
|
}
|
||||||
@ -175,9 +175,7 @@ void KMemoryBlockManager::UpdateIfMatch(KMemoryBlockManagerUpdateAllocator* allo
|
|||||||
KProcessAddress address, size_t num_pages,
|
KProcessAddress address, size_t num_pages,
|
||||||
KMemoryState test_state, KMemoryPermission test_perm,
|
KMemoryState test_state, KMemoryPermission test_perm,
|
||||||
KMemoryAttribute test_attr, KMemoryState state,
|
KMemoryAttribute test_attr, KMemoryState state,
|
||||||
KMemoryPermission perm, KMemoryAttribute attr,
|
KMemoryPermission perm, KMemoryAttribute attr) {
|
||||||
KMemoryBlockDisableMergeAttribute set_disable_attr,
|
|
||||||
KMemoryBlockDisableMergeAttribute clear_disable_attr) {
|
|
||||||
// Ensure for auditing that we never end up with an invalid tree.
|
// Ensure for auditing that we never end up with an invalid tree.
|
||||||
KScopedMemoryBlockManagerAuditor auditor(this);
|
KScopedMemoryBlockManagerAuditor auditor(this);
|
||||||
ASSERT(Common::IsAligned(GetInteger(address), PageSize));
|
ASSERT(Common::IsAligned(GetInteger(address), PageSize));
|
||||||
@ -216,8 +214,7 @@ void KMemoryBlockManager::UpdateIfMatch(KMemoryBlockManagerUpdateAllocator* allo
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Update block state.
|
// Update block state.
|
||||||
it->Update(state, perm, attr, false, static_cast<u8>(set_disable_attr),
|
it->Update(state, perm, attr, false, 0, 0);
|
||||||
static_cast<u8>(clear_disable_attr));
|
|
||||||
cur_address += cur_info.GetSize();
|
cur_address += cur_info.GetSize();
|
||||||
remaining_pages -= cur_info.GetNumPages();
|
remaining_pages -= cur_info.GetNumPages();
|
||||||
} else {
|
} else {
|
||||||
@ -287,65 +284,6 @@ void KMemoryBlockManager::UpdateLock(KMemoryBlockManagerUpdateAllocator* allocat
|
|||||||
this->CoalesceForUpdate(allocator, address, num_pages);
|
this->CoalesceForUpdate(allocator, address, num_pages);
|
||||||
}
|
}
|
||||||
|
|
||||||
void KMemoryBlockManager::UpdateAttribute(KMemoryBlockManagerUpdateAllocator* allocator,
|
|
||||||
KProcessAddress address, size_t num_pages,
|
|
||||||
KMemoryAttribute mask, KMemoryAttribute attr) {
|
|
||||||
// Ensure for auditing that we never end up with an invalid tree.
|
|
||||||
KScopedMemoryBlockManagerAuditor auditor(this);
|
|
||||||
ASSERT(Common::IsAligned(GetInteger(address), PageSize));
|
|
||||||
|
|
||||||
KProcessAddress cur_address = address;
|
|
||||||
size_t remaining_pages = num_pages;
|
|
||||||
iterator it = this->FindIterator(address);
|
|
||||||
|
|
||||||
while (remaining_pages > 0) {
|
|
||||||
const size_t remaining_size = remaining_pages * PageSize;
|
|
||||||
KMemoryInfo cur_info = it->GetMemoryInfo();
|
|
||||||
|
|
||||||
if ((it->GetAttribute() & mask) != attr) {
|
|
||||||
// If we need to, create a new block before and insert it.
|
|
||||||
if (cur_info.GetAddress() != GetInteger(cur_address)) {
|
|
||||||
KMemoryBlock* new_block = allocator->Allocate();
|
|
||||||
|
|
||||||
it->Split(new_block, cur_address);
|
|
||||||
it = m_memory_block_tree.insert(*new_block);
|
|
||||||
it++;
|
|
||||||
|
|
||||||
cur_info = it->GetMemoryInfo();
|
|
||||||
cur_address = cur_info.GetAddress();
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we need to, create a new block after and insert it.
|
|
||||||
if (cur_info.GetSize() > remaining_size) {
|
|
||||||
KMemoryBlock* new_block = allocator->Allocate();
|
|
||||||
|
|
||||||
it->Split(new_block, cur_address + remaining_size);
|
|
||||||
it = m_memory_block_tree.insert(*new_block);
|
|
||||||
|
|
||||||
cur_info = it->GetMemoryInfo();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update block state.
|
|
||||||
it->UpdateAttribute(mask, attr);
|
|
||||||
cur_address += cur_info.GetSize();
|
|
||||||
remaining_pages -= cur_info.GetNumPages();
|
|
||||||
} else {
|
|
||||||
// If we already have the right attributes, just advance.
|
|
||||||
if (cur_address + remaining_size < cur_info.GetEndAddress()) {
|
|
||||||
remaining_pages = 0;
|
|
||||||
cur_address += remaining_size;
|
|
||||||
} else {
|
|
||||||
remaining_pages =
|
|
||||||
(cur_address + remaining_size - cur_info.GetEndAddress()) / PageSize;
|
|
||||||
cur_address = cur_info.GetEndAddress();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
it++;
|
|
||||||
}
|
|
||||||
|
|
||||||
this->CoalesceForUpdate(allocator, address, num_pages);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Debug.
|
// Debug.
|
||||||
bool KMemoryBlockManager::CheckState() const {
|
bool KMemoryBlockManager::CheckState() const {
|
||||||
// Loop over every block, ensuring that we are sorted and coalesced.
|
// Loop over every block, ensuring that we are sorted and coalesced.
|
||||||
|
@ -115,11 +115,7 @@ public:
|
|||||||
void UpdateIfMatch(KMemoryBlockManagerUpdateAllocator* allocator, KProcessAddress address,
|
void UpdateIfMatch(KMemoryBlockManagerUpdateAllocator* allocator, KProcessAddress address,
|
||||||
size_t num_pages, KMemoryState test_state, KMemoryPermission test_perm,
|
size_t num_pages, KMemoryState test_state, KMemoryPermission test_perm,
|
||||||
KMemoryAttribute test_attr, KMemoryState state, KMemoryPermission perm,
|
KMemoryAttribute test_attr, KMemoryState state, KMemoryPermission perm,
|
||||||
KMemoryAttribute attr, KMemoryBlockDisableMergeAttribute set_disable_attr,
|
KMemoryAttribute attr);
|
||||||
KMemoryBlockDisableMergeAttribute clear_disable_attr);
|
|
||||||
|
|
||||||
void UpdateAttribute(KMemoryBlockManagerUpdateAllocator* allocator, KProcessAddress address,
|
|
||||||
size_t num_pages, KMemoryAttribute mask, KMemoryAttribute attr);
|
|
||||||
|
|
||||||
iterator FindIterator(KProcessAddress address) const {
|
iterator FindIterator(KProcessAddress address) const {
|
||||||
return m_memory_block_tree.find(KMemoryBlock(
|
return m_memory_block_tree.find(KMemoryBlock(
|
||||||
|
@ -137,9 +137,11 @@ public:
|
|||||||
return GetStackTopAddress(core_id, KMemoryRegionType_KernelMiscExceptionStack);
|
return GetStackTopAddress(core_id, KMemoryRegionType_KernelMiscExceptionStack);
|
||||||
}
|
}
|
||||||
|
|
||||||
const KMemoryRegion& GetSlabRegion() const {
|
KVirtualAddress GetSlabRegionAddress() const {
|
||||||
return Dereference(GetVirtualMemoryRegionTree().FindByType(KMemoryRegionType_KernelSlab));
|
return Dereference(GetVirtualMemoryRegionTree().FindByType(KMemoryRegionType_KernelSlab))
|
||||||
|
.GetAddress();
|
||||||
}
|
}
|
||||||
|
|
||||||
const KMemoryRegion& GetDeviceRegion(KMemoryRegionType type) const {
|
const KMemoryRegion& GetDeviceRegion(KMemoryRegionType type) const {
|
||||||
return Dereference(GetPhysicalMemoryRegionTree().FindFirstDerived(type));
|
return Dereference(GetPhysicalMemoryRegionTree().FindFirstDerived(type));
|
||||||
}
|
}
|
||||||
|
@ -119,8 +119,7 @@ void KMemoryManager::Initialize(KVirtualAddress management_region, size_t manage
|
|||||||
// Free each region to its corresponding heap.
|
// Free each region to its corresponding heap.
|
||||||
size_t reserved_sizes[MaxManagerCount] = {};
|
size_t reserved_sizes[MaxManagerCount] = {};
|
||||||
const KPhysicalAddress ini_start = GetInitialProcessBinaryPhysicalAddress();
|
const KPhysicalAddress ini_start = GetInitialProcessBinaryPhysicalAddress();
|
||||||
const size_t ini_size = GetInitialProcessBinarySize();
|
const KPhysicalAddress ini_end = ini_start + InitialProcessBinarySizeMax;
|
||||||
const KPhysicalAddress ini_end = ini_start + ini_size;
|
|
||||||
const KPhysicalAddress ini_last = ini_end - 1;
|
const KPhysicalAddress ini_last = ini_end - 1;
|
||||||
for (const auto& it : m_system.Kernel().MemoryLayout().GetPhysicalMemoryRegionTree()) {
|
for (const auto& it : m_system.Kernel().MemoryLayout().GetPhysicalMemoryRegionTree()) {
|
||||||
if (it.IsDerivedFrom(KMemoryRegionType_DramUserPool)) {
|
if (it.IsDerivedFrom(KMemoryRegionType_DramUserPool)) {
|
||||||
@ -138,13 +137,13 @@ void KMemoryManager::Initialize(KVirtualAddress management_region, size_t manage
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Open/reserve the ini memory.
|
// Open/reserve the ini memory.
|
||||||
manager.OpenFirst(ini_start, ini_size / PageSize);
|
manager.OpenFirst(ini_start, InitialProcessBinarySizeMax / PageSize);
|
||||||
reserved_sizes[it.GetAttributes()] += ini_size;
|
reserved_sizes[it.GetAttributes()] += InitialProcessBinarySizeMax;
|
||||||
|
|
||||||
// Free memory after the ini to the heap.
|
// Free memory after the ini to the heap.
|
||||||
if (ini_last != cur_last) {
|
if (ini_last != cur_last) {
|
||||||
ASSERT(cur_end != 0);
|
ASSERT(cur_end != 0);
|
||||||
manager.Free(ini_end, (cur_end - ini_end) / PageSize);
|
manager.Free(ini_end, cur_end - ini_end);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Ensure there's no partial overlap with the ini image.
|
// Ensure there's no partial overlap with the ini image.
|
||||||
|
@ -190,15 +190,9 @@ static_assert(KMemoryRegionType_DramKernelInitPt.GetValue() ==
|
|||||||
constexpr inline auto KMemoryRegionType_DramKernelSecureAppletMemory =
|
constexpr inline auto KMemoryRegionType_DramKernelSecureAppletMemory =
|
||||||
KMemoryRegionType_DramKernelBase.DeriveSparse(1, 3, 0).SetAttribute(
|
KMemoryRegionType_DramKernelBase.DeriveSparse(1, 3, 0).SetAttribute(
|
||||||
KMemoryRegionAttr_LinearMapped);
|
KMemoryRegionAttr_LinearMapped);
|
||||||
constexpr inline const auto KMemoryRegionType_DramKernelSecureUnknown =
|
|
||||||
KMemoryRegionType_DramKernelBase.DeriveSparse(1, 3, 1).SetAttribute(
|
|
||||||
KMemoryRegionAttr_LinearMapped);
|
|
||||||
static_assert(KMemoryRegionType_DramKernelSecureAppletMemory.GetValue() ==
|
static_assert(KMemoryRegionType_DramKernelSecureAppletMemory.GetValue() ==
|
||||||
(0x18E | KMemoryRegionAttr_CarveoutProtected | KMemoryRegionAttr_NoUserMap |
|
(0x18E | KMemoryRegionAttr_CarveoutProtected | KMemoryRegionAttr_NoUserMap |
|
||||||
KMemoryRegionAttr_LinearMapped));
|
KMemoryRegionAttr_LinearMapped));
|
||||||
static_assert(KMemoryRegionType_DramKernelSecureUnknown.GetValue() ==
|
|
||||||
(0x28E | KMemoryRegionAttr_CarveoutProtected | KMemoryRegionAttr_NoUserMap |
|
|
||||||
KMemoryRegionAttr_LinearMapped));
|
|
||||||
|
|
||||||
constexpr inline auto KMemoryRegionType_DramReservedEarly =
|
constexpr inline auto KMemoryRegionType_DramReservedEarly =
|
||||||
KMemoryRegionType_DramReservedBase.DeriveAttribute(KMemoryRegionAttr_NoUserMap);
|
KMemoryRegionType_DramReservedBase.DeriveAttribute(KMemoryRegionAttr_NoUserMap);
|
||||||
@ -223,18 +217,16 @@ constexpr inline auto KMemoryRegionType_DramPoolPartition =
|
|||||||
static_assert(KMemoryRegionType_DramPoolPartition.GetValue() ==
|
static_assert(KMemoryRegionType_DramPoolPartition.GetValue() ==
|
||||||
(0x26 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap));
|
(0x26 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap));
|
||||||
|
|
||||||
// UNUSED: .Derive(4, 1);
|
constexpr inline auto KMemoryRegionType_DramPoolManagement =
|
||||||
// UNUSED: .Derive(4, 2);
|
KMemoryRegionType_DramPoolPartition.DeriveTransition(0, 2).DeriveTransition().SetAttribute(
|
||||||
constexpr inline const auto KMemoryRegionType_DramPoolManagement =
|
|
||||||
KMemoryRegionType_DramPoolPartition.Derive(4, 0).SetAttribute(
|
|
||||||
KMemoryRegionAttr_CarveoutProtected);
|
KMemoryRegionAttr_CarveoutProtected);
|
||||||
constexpr inline const auto KMemoryRegionType_DramUserPool =
|
constexpr inline auto KMemoryRegionType_DramUserPool =
|
||||||
KMemoryRegionType_DramPoolPartition.Derive(4, 3);
|
KMemoryRegionType_DramPoolPartition.DeriveTransition(1, 2).DeriveTransition();
|
||||||
static_assert(KMemoryRegionType_DramPoolManagement.GetValue() ==
|
static_assert(KMemoryRegionType_DramPoolManagement.GetValue() ==
|
||||||
(0xE6 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap |
|
(0x166 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap |
|
||||||
KMemoryRegionAttr_CarveoutProtected));
|
KMemoryRegionAttr_CarveoutProtected));
|
||||||
static_assert(KMemoryRegionType_DramUserPool.GetValue() ==
|
static_assert(KMemoryRegionType_DramUserPool.GetValue() ==
|
||||||
(0x266 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap));
|
(0x1A6 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap));
|
||||||
|
|
||||||
constexpr inline auto KMemoryRegionType_DramApplicationPool =
|
constexpr inline auto KMemoryRegionType_DramApplicationPool =
|
||||||
KMemoryRegionType_DramUserPool.Derive(4, 0);
|
KMemoryRegionType_DramUserPool.Derive(4, 0);
|
||||||
@ -245,63 +237,60 @@ constexpr inline auto KMemoryRegionType_DramSystemNonSecurePool =
|
|||||||
constexpr inline auto KMemoryRegionType_DramSystemPool =
|
constexpr inline auto KMemoryRegionType_DramSystemPool =
|
||||||
KMemoryRegionType_DramUserPool.Derive(4, 3).SetAttribute(KMemoryRegionAttr_CarveoutProtected);
|
KMemoryRegionType_DramUserPool.Derive(4, 3).SetAttribute(KMemoryRegionAttr_CarveoutProtected);
|
||||||
static_assert(KMemoryRegionType_DramApplicationPool.GetValue() ==
|
static_assert(KMemoryRegionType_DramApplicationPool.GetValue() ==
|
||||||
(0xE66 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap));
|
(0x7A6 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap));
|
||||||
static_assert(KMemoryRegionType_DramAppletPool.GetValue() ==
|
static_assert(KMemoryRegionType_DramAppletPool.GetValue() ==
|
||||||
(0x1666 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap));
|
(0xBA6 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap));
|
||||||
static_assert(KMemoryRegionType_DramSystemNonSecurePool.GetValue() ==
|
static_assert(KMemoryRegionType_DramSystemNonSecurePool.GetValue() ==
|
||||||
(0x1A66 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap));
|
(0xDA6 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap));
|
||||||
static_assert(KMemoryRegionType_DramSystemPool.GetValue() ==
|
static_assert(KMemoryRegionType_DramSystemPool.GetValue() ==
|
||||||
(0x2666 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap |
|
(0x13A6 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap |
|
||||||
KMemoryRegionAttr_CarveoutProtected));
|
KMemoryRegionAttr_CarveoutProtected));
|
||||||
|
|
||||||
constexpr inline auto KMemoryRegionType_VirtualDramHeapBase =
|
constexpr inline auto KMemoryRegionType_VirtualDramHeapBase =
|
||||||
KMemoryRegionType_Dram.DeriveSparse(1, 4, 0);
|
KMemoryRegionType_Dram.DeriveSparse(1, 3, 0);
|
||||||
constexpr inline auto KMemoryRegionType_VirtualDramKernelPtHeap =
|
constexpr inline auto KMemoryRegionType_VirtualDramKernelPtHeap =
|
||||||
KMemoryRegionType_Dram.DeriveSparse(1, 4, 1);
|
KMemoryRegionType_Dram.DeriveSparse(1, 3, 1);
|
||||||
constexpr inline auto KMemoryRegionType_VirtualDramKernelTraceBuffer =
|
constexpr inline auto KMemoryRegionType_VirtualDramKernelTraceBuffer =
|
||||||
KMemoryRegionType_Dram.DeriveSparse(1, 4, 2);
|
KMemoryRegionType_Dram.DeriveSparse(1, 3, 2);
|
||||||
static_assert(KMemoryRegionType_VirtualDramHeapBase.GetValue() == 0x1A);
|
static_assert(KMemoryRegionType_VirtualDramHeapBase.GetValue() == 0x1A);
|
||||||
static_assert(KMemoryRegionType_VirtualDramKernelPtHeap.GetValue() == 0x2A);
|
static_assert(KMemoryRegionType_VirtualDramKernelPtHeap.GetValue() == 0x2A);
|
||||||
static_assert(KMemoryRegionType_VirtualDramKernelTraceBuffer.GetValue() == 0x4A);
|
static_assert(KMemoryRegionType_VirtualDramKernelTraceBuffer.GetValue() == 0x4A);
|
||||||
|
|
||||||
// UNUSED: .Derive(4, 2);
|
// UNUSED: .DeriveSparse(2, 2, 0);
|
||||||
constexpr inline const auto KMemoryRegionType_VirtualDramUnknownDebug =
|
constexpr inline auto KMemoryRegionType_VirtualDramUnknownDebug =
|
||||||
KMemoryRegionType_Dram.Advance(2).Derive(4, 0);
|
KMemoryRegionType_Dram.DeriveSparse(2, 2, 1);
|
||||||
constexpr inline const auto KMemoryRegionType_VirtualDramKernelSecureAppletMemory =
|
static_assert(KMemoryRegionType_VirtualDramUnknownDebug.GetValue() == (0x52));
|
||||||
KMemoryRegionType_Dram.Advance(2).Derive(4, 1);
|
|
||||||
constexpr inline const auto KMemoryRegionType_VirtualDramKernelSecureUnknown =
|
|
||||||
KMemoryRegionType_Dram.Advance(2).Derive(4, 3);
|
|
||||||
static_assert(KMemoryRegionType_VirtualDramUnknownDebug.GetValue() == (0x32));
|
|
||||||
static_assert(KMemoryRegionType_VirtualDramKernelSecureAppletMemory.GetValue() == (0x52));
|
|
||||||
static_assert(KMemoryRegionType_VirtualDramKernelSecureUnknown.GetValue() == (0x92));
|
|
||||||
|
|
||||||
// UNUSED: .Derive(4, 3);
|
constexpr inline auto KMemoryRegionType_VirtualDramKernelSecureAppletMemory =
|
||||||
constexpr inline const auto KMemoryRegionType_VirtualDramKernelInitPt =
|
KMemoryRegionType_Dram.DeriveSparse(3, 1, 0);
|
||||||
KMemoryRegionType_VirtualDramHeapBase.Derive(4, 0);
|
static_assert(KMemoryRegionType_VirtualDramKernelSecureAppletMemory.GetValue() == (0x62));
|
||||||
constexpr inline const auto KMemoryRegionType_VirtualDramPoolManagement =
|
|
||||||
KMemoryRegionType_VirtualDramHeapBase.Derive(4, 1);
|
constexpr inline auto KMemoryRegionType_VirtualDramKernelInitPt =
|
||||||
constexpr inline const auto KMemoryRegionType_VirtualDramUserPool =
|
KMemoryRegionType_VirtualDramHeapBase.Derive(3, 0);
|
||||||
KMemoryRegionType_VirtualDramHeapBase.Derive(4, 2);
|
constexpr inline auto KMemoryRegionType_VirtualDramPoolManagement =
|
||||||
static_assert(KMemoryRegionType_VirtualDramKernelInitPt.GetValue() == 0x31A);
|
KMemoryRegionType_VirtualDramHeapBase.Derive(3, 1);
|
||||||
static_assert(KMemoryRegionType_VirtualDramPoolManagement.GetValue() == 0x51A);
|
constexpr inline auto KMemoryRegionType_VirtualDramUserPool =
|
||||||
static_assert(KMemoryRegionType_VirtualDramUserPool.GetValue() == 0x61A);
|
KMemoryRegionType_VirtualDramHeapBase.Derive(3, 2);
|
||||||
|
static_assert(KMemoryRegionType_VirtualDramKernelInitPt.GetValue() == 0x19A);
|
||||||
|
static_assert(KMemoryRegionType_VirtualDramPoolManagement.GetValue() == 0x29A);
|
||||||
|
static_assert(KMemoryRegionType_VirtualDramUserPool.GetValue() == 0x31A);
|
||||||
|
|
||||||
// NOTE: For unknown reason, the pools are derived out-of-order here.
|
// NOTE: For unknown reason, the pools are derived out-of-order here.
|
||||||
// It's worth eventually trying to understand why Nintendo made this choice.
|
// It's worth eventually trying to understand why Nintendo made this choice.
|
||||||
// UNUSED: .Derive(6, 0);
|
// UNUSED: .Derive(6, 0);
|
||||||
// UNUSED: .Derive(6, 1);
|
// UNUSED: .Derive(6, 1);
|
||||||
constexpr inline const auto KMemoryRegionType_VirtualDramApplicationPool =
|
constexpr inline auto KMemoryRegionType_VirtualDramAppletPool =
|
||||||
KMemoryRegionType_VirtualDramUserPool.Derive(4, 0);
|
KMemoryRegionType_VirtualDramUserPool.Derive(6, 2);
|
||||||
constexpr inline const auto KMemoryRegionType_VirtualDramAppletPool =
|
constexpr inline auto KMemoryRegionType_VirtualDramApplicationPool =
|
||||||
KMemoryRegionType_VirtualDramUserPool.Derive(4, 1);
|
KMemoryRegionType_VirtualDramUserPool.Derive(6, 3);
|
||||||
constexpr inline const auto KMemoryRegionType_VirtualDramSystemNonSecurePool =
|
constexpr inline auto KMemoryRegionType_VirtualDramSystemNonSecurePool =
|
||||||
KMemoryRegionType_VirtualDramUserPool.Derive(4, 2);
|
KMemoryRegionType_VirtualDramUserPool.Derive(6, 4);
|
||||||
constexpr inline const auto KMemoryRegionType_VirtualDramSystemPool =
|
constexpr inline auto KMemoryRegionType_VirtualDramSystemPool =
|
||||||
KMemoryRegionType_VirtualDramUserPool.Derive(4, 3);
|
KMemoryRegionType_VirtualDramUserPool.Derive(6, 5);
|
||||||
static_assert(KMemoryRegionType_VirtualDramApplicationPool.GetValue() == 0x361A);
|
static_assert(KMemoryRegionType_VirtualDramAppletPool.GetValue() == 0x1B1A);
|
||||||
static_assert(KMemoryRegionType_VirtualDramAppletPool.GetValue() == 0x561A);
|
static_assert(KMemoryRegionType_VirtualDramApplicationPool.GetValue() == 0x271A);
|
||||||
static_assert(KMemoryRegionType_VirtualDramSystemNonSecurePool.GetValue() == 0x661A);
|
static_assert(KMemoryRegionType_VirtualDramSystemNonSecurePool.GetValue() == 0x2B1A);
|
||||||
static_assert(KMemoryRegionType_VirtualDramSystemPool.GetValue() == 0x961A);
|
static_assert(KMemoryRegionType_VirtualDramSystemPool.GetValue() == 0x331A);
|
||||||
|
|
||||||
constexpr inline auto KMemoryRegionType_ArchDeviceBase =
|
constexpr inline auto KMemoryRegionType_ArchDeviceBase =
|
||||||
KMemoryRegionType_Kernel.DeriveTransition(0, 1).SetSparseOnly();
|
KMemoryRegionType_Kernel.DeriveTransition(0, 1).SetSparseOnly();
|
||||||
@ -365,14 +354,12 @@ constexpr inline auto KMemoryRegionType_KernelTemp =
|
|||||||
static_assert(KMemoryRegionType_KernelTemp.GetValue() == 0x31);
|
static_assert(KMemoryRegionType_KernelTemp.GetValue() == 0x31);
|
||||||
|
|
||||||
constexpr KMemoryRegionType GetTypeForVirtualLinearMapping(u32 type_id) {
|
constexpr KMemoryRegionType GetTypeForVirtualLinearMapping(u32 type_id) {
|
||||||
if (KMemoryRegionType_DramKernelPtHeap.IsAncestorOf(type_id)) {
|
if (KMemoryRegionType_KernelTraceBuffer.IsAncestorOf(type_id)) {
|
||||||
|
return KMemoryRegionType_VirtualDramKernelTraceBuffer;
|
||||||
|
} else if (KMemoryRegionType_DramKernelPtHeap.IsAncestorOf(type_id)) {
|
||||||
return KMemoryRegionType_VirtualDramKernelPtHeap;
|
return KMemoryRegionType_VirtualDramKernelPtHeap;
|
||||||
} else if (KMemoryRegionType_DramKernelSecureAppletMemory.IsAncestorOf(type_id)) {
|
} else if (KMemoryRegionType_DramKernelSecureAppletMemory.IsAncestorOf(type_id)) {
|
||||||
return KMemoryRegionType_VirtualDramKernelSecureAppletMemory;
|
return KMemoryRegionType_VirtualDramKernelSecureAppletMemory;
|
||||||
} else if (KMemoryRegionType_DramKernelSecureUnknown.IsAncestorOf(type_id)) {
|
|
||||||
return KMemoryRegionType_VirtualDramKernelSecureUnknown;
|
|
||||||
} else if (KMemoryRegionType_KernelTraceBuffer.IsAncestorOf(type_id)) {
|
|
||||||
return KMemoryRegionType_VirtualDramKernelTraceBuffer;
|
|
||||||
} else if ((type_id | KMemoryRegionAttr_ShouldKernelMap) == type_id) {
|
} else if ((type_id | KMemoryRegionAttr_ShouldKernelMap) == type_id) {
|
||||||
return KMemoryRegionType_VirtualDramUnknownDebug;
|
return KMemoryRegionType_VirtualDramUnknownDebug;
|
||||||
} else {
|
} else {
|
||||||
|
@ -183,17 +183,12 @@ private:
|
|||||||
|
|
||||||
class KScopedPageGroup {
|
class KScopedPageGroup {
|
||||||
public:
|
public:
|
||||||
explicit KScopedPageGroup(const KPageGroup* gp, bool not_first = true) : m_pg(gp) {
|
explicit KScopedPageGroup(const KPageGroup* gp) : m_pg(gp) {
|
||||||
if (m_pg) {
|
if (m_pg) {
|
||||||
if (not_first) {
|
m_pg->Open();
|
||||||
m_pg->Open();
|
|
||||||
} else {
|
|
||||||
m_pg->OpenFirst();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
explicit KScopedPageGroup(const KPageGroup& gp, bool not_first = true)
|
explicit KScopedPageGroup(const KPageGroup& gp) : KScopedPageGroup(std::addressof(gp)) {}
|
||||||
: KScopedPageGroup(std::addressof(gp), not_first) {}
|
|
||||||
~KScopedPageGroup() {
|
~KScopedPageGroup() {
|
||||||
if (m_pg) {
|
if (m_pg) {
|
||||||
m_pg->Close();
|
m_pg->Close();
|
||||||
|
@ -505,7 +505,7 @@ Result KPageTable::UnmapCodeMemory(KProcessAddress dst_address, KProcessAddress
|
|||||||
R_TRY(this->CheckMemoryStateContiguous(
|
R_TRY(this->CheckMemoryStateContiguous(
|
||||||
std::addressof(num_dst_allocator_blocks), dst_address, size, KMemoryState::FlagCanCodeAlias,
|
std::addressof(num_dst_allocator_blocks), dst_address, size, KMemoryState::FlagCanCodeAlias,
|
||||||
KMemoryState::FlagCanCodeAlias, KMemoryPermission::None, KMemoryPermission::None,
|
KMemoryState::FlagCanCodeAlias, KMemoryPermission::None, KMemoryPermission::None,
|
||||||
KMemoryAttribute::All & ~KMemoryAttribute::PermissionLocked, KMemoryAttribute::None));
|
KMemoryAttribute::All, KMemoryAttribute::None));
|
||||||
|
|
||||||
// Determine whether any pages being unmapped are code.
|
// Determine whether any pages being unmapped are code.
|
||||||
bool any_code_pages = false;
|
bool any_code_pages = false;
|
||||||
@ -1724,43 +1724,29 @@ Result KPageTable::MapPhysicalMemory(KProcessAddress address, size_t size) {
|
|||||||
PageSize;
|
PageSize;
|
||||||
|
|
||||||
// While we have pages to map, map them.
|
// While we have pages to map, map them.
|
||||||
{
|
while (map_pages > 0) {
|
||||||
// Create a page group for the current mapping range.
|
// Check if we're at the end of the physical block.
|
||||||
KPageGroup cur_pg(m_kernel, m_block_info_manager);
|
if (pg_pages == 0) {
|
||||||
{
|
// Ensure there are more pages to map.
|
||||||
ON_RESULT_FAILURE_2 {
|
ASSERT(pg_it != pg.end());
|
||||||
cur_pg.OpenFirst();
|
|
||||||
cur_pg.Close();
|
|
||||||
};
|
|
||||||
|
|
||||||
size_t remain_pages = map_pages;
|
// Advance our physical block.
|
||||||
while (remain_pages > 0) {
|
++pg_it;
|
||||||
// Check if we're at the end of the physical block.
|
pg_phys_addr = pg_it->GetAddress();
|
||||||
if (pg_pages == 0) {
|
pg_pages = pg_it->GetNumPages();
|
||||||
// Ensure there are more pages to map.
|
|
||||||
ASSERT(pg_it != pg.end());
|
|
||||||
|
|
||||||
// Advance our physical block.
|
|
||||||
++pg_it;
|
|
||||||
pg_phys_addr = pg_it->GetAddress();
|
|
||||||
pg_pages = pg_it->GetNumPages();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add whatever we can to the current block.
|
|
||||||
const size_t cur_pages = std::min(pg_pages, remain_pages);
|
|
||||||
R_TRY(cur_pg.AddBlock(pg_phys_addr +
|
|
||||||
((pg_pages - cur_pages) * PageSize),
|
|
||||||
cur_pages));
|
|
||||||
|
|
||||||
// Advance.
|
|
||||||
remain_pages -= cur_pages;
|
|
||||||
pg_pages -= cur_pages;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Map the pages.
|
// Map whatever we can.
|
||||||
R_TRY(this->Operate(cur_address, map_pages, cur_pg,
|
const size_t cur_pages = std::min(pg_pages, map_pages);
|
||||||
OperationType::MapFirstGroup));
|
R_TRY(Operate(cur_address, cur_pages, KMemoryPermission::UserReadWrite,
|
||||||
|
OperationType::MapFirst, pg_phys_addr));
|
||||||
|
|
||||||
|
// Advance.
|
||||||
|
cur_address += cur_pages * PageSize;
|
||||||
|
map_pages -= cur_pages;
|
||||||
|
|
||||||
|
pg_phys_addr += cur_pages * PageSize;
|
||||||
|
pg_pages -= cur_pages;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1784,11 +1770,7 @@ Result KPageTable::MapPhysicalMemory(KProcessAddress address, size_t size) {
|
|||||||
m_memory_block_manager.UpdateIfMatch(
|
m_memory_block_manager.UpdateIfMatch(
|
||||||
std::addressof(allocator), address, size / PageSize, KMemoryState::Free,
|
std::addressof(allocator), address, size / PageSize, KMemoryState::Free,
|
||||||
KMemoryPermission::None, KMemoryAttribute::None, KMemoryState::Normal,
|
KMemoryPermission::None, KMemoryAttribute::None, KMemoryState::Normal,
|
||||||
KMemoryPermission::UserReadWrite, KMemoryAttribute::None,
|
KMemoryPermission::UserReadWrite, KMemoryAttribute::None);
|
||||||
address == this->GetAliasRegionStart()
|
|
||||||
? KMemoryBlockDisableMergeAttribute::Normal
|
|
||||||
: KMemoryBlockDisableMergeAttribute::None,
|
|
||||||
KMemoryBlockDisableMergeAttribute::None);
|
|
||||||
|
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
@ -1886,13 +1868,6 @@ Result KPageTable::UnmapPhysicalMemory(KProcessAddress address, size_t size) {
|
|||||||
|
|
||||||
// Iterate over the memory, unmapping as we go.
|
// Iterate over the memory, unmapping as we go.
|
||||||
auto it = m_memory_block_manager.FindIterator(cur_address);
|
auto it = m_memory_block_manager.FindIterator(cur_address);
|
||||||
|
|
||||||
const auto clear_merge_attr =
|
|
||||||
(it->GetState() == KMemoryState::Normal &&
|
|
||||||
it->GetAddress() == this->GetAliasRegionStart() && it->GetAddress() == address)
|
|
||||||
? KMemoryBlockDisableMergeAttribute::Normal
|
|
||||||
: KMemoryBlockDisableMergeAttribute::None;
|
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
// Check that the iterator is valid.
|
// Check that the iterator is valid.
|
||||||
ASSERT(it != m_memory_block_manager.end());
|
ASSERT(it != m_memory_block_manager.end());
|
||||||
@ -1930,7 +1905,7 @@ Result KPageTable::UnmapPhysicalMemory(KProcessAddress address, size_t size) {
|
|||||||
m_memory_block_manager.Update(std::addressof(allocator), address, size / PageSize,
|
m_memory_block_manager.Update(std::addressof(allocator), address, size / PageSize,
|
||||||
KMemoryState::Free, KMemoryPermission::None,
|
KMemoryState::Free, KMemoryPermission::None,
|
||||||
KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::None,
|
KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::None,
|
||||||
clear_merge_attr);
|
KMemoryBlockDisableMergeAttribute::None);
|
||||||
|
|
||||||
// We succeeded.
|
// We succeeded.
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
@ -2404,7 +2379,8 @@ Result KPageTable::MapPageGroup(KProcessAddress* out_addr, const KPageGroup& pg,
|
|||||||
KScopedPageTableUpdater updater(this);
|
KScopedPageTableUpdater updater(this);
|
||||||
|
|
||||||
// Perform mapping operation.
|
// Perform mapping operation.
|
||||||
const KPageProperties properties = {perm, false, false, DisableMergeAttribute::DisableHead};
|
const KPageProperties properties = {perm, state == KMemoryState::Io, false,
|
||||||
|
DisableMergeAttribute::DisableHead};
|
||||||
R_TRY(this->MapPageGroupImpl(updater.GetPageList(), addr, pg, properties, false));
|
R_TRY(this->MapPageGroupImpl(updater.GetPageList(), addr, pg, properties, false));
|
||||||
|
|
||||||
// Update the blocks.
|
// Update the blocks.
|
||||||
@ -2446,7 +2422,8 @@ Result KPageTable::MapPageGroup(KProcessAddress addr, const KPageGroup& pg, KMem
|
|||||||
KScopedPageTableUpdater updater(this);
|
KScopedPageTableUpdater updater(this);
|
||||||
|
|
||||||
// Perform mapping operation.
|
// Perform mapping operation.
|
||||||
const KPageProperties properties = {perm, false, false, DisableMergeAttribute::DisableHead};
|
const KPageProperties properties = {perm, state == KMemoryState::Io, false,
|
||||||
|
DisableMergeAttribute::DisableHead};
|
||||||
R_TRY(this->MapPageGroupImpl(updater.GetPageList(), addr, pg, properties, false));
|
R_TRY(this->MapPageGroupImpl(updater.GetPageList(), addr, pg, properties, false));
|
||||||
|
|
||||||
// Update the blocks.
|
// Update the blocks.
|
||||||
@ -2675,18 +2652,11 @@ Result KPageTable::SetMemoryAttribute(KProcessAddress addr, size_t size, u32 mas
|
|||||||
size_t num_allocator_blocks;
|
size_t num_allocator_blocks;
|
||||||
constexpr auto AttributeTestMask =
|
constexpr auto AttributeTestMask =
|
||||||
~(KMemoryAttribute::SetMask | KMemoryAttribute::DeviceShared);
|
~(KMemoryAttribute::SetMask | KMemoryAttribute::DeviceShared);
|
||||||
const KMemoryState state_test_mask =
|
R_TRY(this->CheckMemoryState(
|
||||||
static_cast<KMemoryState>(((mask & static_cast<u32>(KMemoryAttribute::Uncached))
|
std::addressof(old_state), std::addressof(old_perm), std::addressof(old_attr),
|
||||||
? static_cast<u32>(KMemoryState::FlagCanChangeAttribute)
|
std::addressof(num_allocator_blocks), addr, size, KMemoryState::FlagCanChangeAttribute,
|
||||||
: 0) |
|
KMemoryState::FlagCanChangeAttribute, KMemoryPermission::None, KMemoryPermission::None,
|
||||||
((mask & static_cast<u32>(KMemoryAttribute::PermissionLocked))
|
AttributeTestMask, KMemoryAttribute::None, ~AttributeTestMask));
|
||||||
? static_cast<u32>(KMemoryState::FlagCanPermissionLock)
|
|
||||||
: 0));
|
|
||||||
R_TRY(this->CheckMemoryState(std::addressof(old_state), std::addressof(old_perm),
|
|
||||||
std::addressof(old_attr), std::addressof(num_allocator_blocks),
|
|
||||||
addr, size, state_test_mask, state_test_mask,
|
|
||||||
KMemoryPermission::None, KMemoryPermission::None,
|
|
||||||
AttributeTestMask, KMemoryAttribute::None, ~AttributeTestMask));
|
|
||||||
|
|
||||||
// Create an update allocator.
|
// Create an update allocator.
|
||||||
Result allocator_result{ResultSuccess};
|
Result allocator_result{ResultSuccess};
|
||||||
@ -2694,17 +2664,18 @@ Result KPageTable::SetMemoryAttribute(KProcessAddress addr, size_t size, u32 mas
|
|||||||
m_memory_block_slab_manager, num_allocator_blocks);
|
m_memory_block_slab_manager, num_allocator_blocks);
|
||||||
R_TRY(allocator_result);
|
R_TRY(allocator_result);
|
||||||
|
|
||||||
// If we need to, perform a change attribute operation.
|
// Determine the new attribute.
|
||||||
if (True(KMemoryAttribute::Uncached & static_cast<KMemoryAttribute>(mask))) {
|
const KMemoryAttribute new_attr =
|
||||||
// Perform operation.
|
static_cast<KMemoryAttribute>(((old_attr & static_cast<KMemoryAttribute>(~mask)) |
|
||||||
R_TRY(this->Operate(addr, num_pages, old_perm,
|
static_cast<KMemoryAttribute>(attr & mask)));
|
||||||
OperationType::ChangePermissionsAndRefreshAndFlush, 0));
|
|
||||||
}
|
// Perform operation.
|
||||||
|
this->Operate(addr, num_pages, old_perm, OperationType::ChangePermissionsAndRefresh);
|
||||||
|
|
||||||
// Update the blocks.
|
// Update the blocks.
|
||||||
m_memory_block_manager.UpdateAttribute(std::addressof(allocator), addr, num_pages,
|
m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, old_state, old_perm,
|
||||||
static_cast<KMemoryAttribute>(mask),
|
new_attr, KMemoryBlockDisableMergeAttribute::None,
|
||||||
static_cast<KMemoryAttribute>(attr));
|
KMemoryBlockDisableMergeAttribute::None);
|
||||||
|
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
@ -2892,8 +2863,7 @@ Result KPageTable::LockForMapDeviceAddressSpace(bool* out_is_io, KProcessAddress
|
|||||||
&KMemoryBlock::ShareToDevice, KMemoryPermission::None);
|
&KMemoryBlock::ShareToDevice, KMemoryPermission::None);
|
||||||
|
|
||||||
// Set whether the locked memory was io.
|
// Set whether the locked memory was io.
|
||||||
*out_is_io =
|
*out_is_io = old_state == KMemoryState::Io;
|
||||||
static_cast<Svc::MemoryState>(old_state & KMemoryState::Mask) == Svc::MemoryState::Io;
|
|
||||||
|
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
@ -2979,23 +2949,6 @@ Result KPageTable::UnlockForIpcUserBuffer(KProcessAddress address, size_t size)
|
|||||||
KMemoryAttribute::Locked, nullptr));
|
KMemoryAttribute::Locked, nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
Result KPageTable::LockForTransferMemory(KPageGroup* out, KProcessAddress address, size_t size,
|
|
||||||
KMemoryPermission perm) {
|
|
||||||
R_RETURN(this->LockMemoryAndOpen(out, nullptr, address, size, KMemoryState::FlagCanTransfer,
|
|
||||||
KMemoryState::FlagCanTransfer, KMemoryPermission::All,
|
|
||||||
KMemoryPermission::UserReadWrite, KMemoryAttribute::All,
|
|
||||||
KMemoryAttribute::None, perm, KMemoryAttribute::Locked));
|
|
||||||
}
|
|
||||||
|
|
||||||
Result KPageTable::UnlockForTransferMemory(KProcessAddress address, size_t size,
|
|
||||||
const KPageGroup& pg) {
|
|
||||||
R_RETURN(this->UnlockMemory(address, size, KMemoryState::FlagCanTransfer,
|
|
||||||
KMemoryState::FlagCanTransfer, KMemoryPermission::None,
|
|
||||||
KMemoryPermission::None, KMemoryAttribute::All,
|
|
||||||
KMemoryAttribute::Locked, KMemoryPermission::UserReadWrite,
|
|
||||||
KMemoryAttribute::Locked, std::addressof(pg)));
|
|
||||||
}
|
|
||||||
|
|
||||||
Result KPageTable::LockForCodeMemory(KPageGroup* out, KProcessAddress addr, size_t size) {
|
Result KPageTable::LockForCodeMemory(KPageGroup* out, KProcessAddress addr, size_t size) {
|
||||||
R_RETURN(this->LockMemoryAndOpen(
|
R_RETURN(this->LockMemoryAndOpen(
|
||||||
out, nullptr, addr, size, KMemoryState::FlagCanCodeMemory, KMemoryState::FlagCanCodeMemory,
|
out, nullptr, addr, size, KMemoryState::FlagCanCodeMemory, KMemoryState::FlagCanCodeMemory,
|
||||||
@ -3051,10 +3004,9 @@ Result KPageTable::Operate(KProcessAddress addr, size_t num_pages, const KPageGr
|
|||||||
ASSERT(num_pages == page_group.GetNumPages());
|
ASSERT(num_pages == page_group.GetNumPages());
|
||||||
|
|
||||||
switch (operation) {
|
switch (operation) {
|
||||||
case OperationType::MapGroup:
|
case OperationType::MapGroup: {
|
||||||
case OperationType::MapFirstGroup: {
|
|
||||||
// We want to maintain a new reference to every page in the group.
|
// We want to maintain a new reference to every page in the group.
|
||||||
KScopedPageGroup spg(page_group, operation != OperationType::MapFirstGroup);
|
KScopedPageGroup spg(page_group);
|
||||||
|
|
||||||
for (const auto& node : page_group) {
|
for (const auto& node : page_group) {
|
||||||
const size_t size{node.GetNumPages() * PageSize};
|
const size_t size{node.GetNumPages() * PageSize};
|
||||||
@ -3096,6 +3048,7 @@ Result KPageTable::Operate(KProcessAddress addr, size_t num_pages, KMemoryPermis
|
|||||||
m_memory->UnmapRegion(*m_page_table_impl, addr, num_pages * PageSize);
|
m_memory->UnmapRegion(*m_page_table_impl, addr, num_pages * PageSize);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case OperationType::MapFirst:
|
||||||
case OperationType::Map: {
|
case OperationType::Map: {
|
||||||
ASSERT(map_addr);
|
ASSERT(map_addr);
|
||||||
ASSERT(Common::IsAligned(GetInteger(map_addr), PageSize));
|
ASSERT(Common::IsAligned(GetInteger(map_addr), PageSize));
|
||||||
@ -3103,7 +3056,11 @@ Result KPageTable::Operate(KProcessAddress addr, size_t num_pages, KMemoryPermis
|
|||||||
|
|
||||||
// Open references to pages, if we should.
|
// Open references to pages, if we should.
|
||||||
if (IsHeapPhysicalAddress(m_kernel.MemoryLayout(), map_addr)) {
|
if (IsHeapPhysicalAddress(m_kernel.MemoryLayout(), map_addr)) {
|
||||||
m_kernel.MemoryManager().Open(map_addr, num_pages);
|
if (operation == OperationType::MapFirst) {
|
||||||
|
m_kernel.MemoryManager().OpenFirst(map_addr, num_pages);
|
||||||
|
} else {
|
||||||
|
m_kernel.MemoryManager().Open(map_addr, num_pages);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -3113,7 +3070,6 @@ Result KPageTable::Operate(KProcessAddress addr, size_t num_pages, KMemoryPermis
|
|||||||
}
|
}
|
||||||
case OperationType::ChangePermissions:
|
case OperationType::ChangePermissions:
|
||||||
case OperationType::ChangePermissionsAndRefresh:
|
case OperationType::ChangePermissionsAndRefresh:
|
||||||
case OperationType::ChangePermissionsAndRefreshAndFlush:
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
ASSERT(false);
|
ASSERT(false);
|
||||||
@ -3133,79 +3089,79 @@ void KPageTable::FinalizeUpdate(PageLinkedList* page_list) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
KProcessAddress KPageTable::GetRegionAddress(Svc::MemoryState state) const {
|
KProcessAddress KPageTable::GetRegionAddress(KMemoryState state) const {
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case Svc::MemoryState::Free:
|
case KMemoryState::Free:
|
||||||
case Svc::MemoryState::Kernel:
|
case KMemoryState::Kernel:
|
||||||
return m_address_space_start;
|
return m_address_space_start;
|
||||||
case Svc::MemoryState::Normal:
|
case KMemoryState::Normal:
|
||||||
return m_heap_region_start;
|
return m_heap_region_start;
|
||||||
case Svc::MemoryState::Ipc:
|
case KMemoryState::Ipc:
|
||||||
case Svc::MemoryState::NonSecureIpc:
|
case KMemoryState::NonSecureIpc:
|
||||||
case Svc::MemoryState::NonDeviceIpc:
|
case KMemoryState::NonDeviceIpc:
|
||||||
return m_alias_region_start;
|
return m_alias_region_start;
|
||||||
case Svc::MemoryState::Stack:
|
case KMemoryState::Stack:
|
||||||
return m_stack_region_start;
|
return m_stack_region_start;
|
||||||
case Svc::MemoryState::Static:
|
case KMemoryState::Static:
|
||||||
case Svc::MemoryState::ThreadLocal:
|
case KMemoryState::ThreadLocal:
|
||||||
return m_kernel_map_region_start;
|
return m_kernel_map_region_start;
|
||||||
case Svc::MemoryState::Io:
|
case KMemoryState::Io:
|
||||||
case Svc::MemoryState::Shared:
|
case KMemoryState::Shared:
|
||||||
case Svc::MemoryState::AliasCode:
|
case KMemoryState::AliasCode:
|
||||||
case Svc::MemoryState::AliasCodeData:
|
case KMemoryState::AliasCodeData:
|
||||||
case Svc::MemoryState::Transfered:
|
case KMemoryState::Transfered:
|
||||||
case Svc::MemoryState::SharedTransfered:
|
case KMemoryState::SharedTransfered:
|
||||||
case Svc::MemoryState::SharedCode:
|
case KMemoryState::SharedCode:
|
||||||
case Svc::MemoryState::GeneratedCode:
|
case KMemoryState::GeneratedCode:
|
||||||
case Svc::MemoryState::CodeOut:
|
case KMemoryState::CodeOut:
|
||||||
case Svc::MemoryState::Coverage:
|
case KMemoryState::Coverage:
|
||||||
case Svc::MemoryState::Insecure:
|
case KMemoryState::Insecure:
|
||||||
return m_alias_code_region_start;
|
return m_alias_code_region_start;
|
||||||
case Svc::MemoryState::Code:
|
case KMemoryState::Code:
|
||||||
case Svc::MemoryState::CodeData:
|
case KMemoryState::CodeData:
|
||||||
return m_code_region_start;
|
return m_code_region_start;
|
||||||
default:
|
default:
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t KPageTable::GetRegionSize(Svc::MemoryState state) const {
|
size_t KPageTable::GetRegionSize(KMemoryState state) const {
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case Svc::MemoryState::Free:
|
case KMemoryState::Free:
|
||||||
case Svc::MemoryState::Kernel:
|
case KMemoryState::Kernel:
|
||||||
return m_address_space_end - m_address_space_start;
|
return m_address_space_end - m_address_space_start;
|
||||||
case Svc::MemoryState::Normal:
|
case KMemoryState::Normal:
|
||||||
return m_heap_region_end - m_heap_region_start;
|
return m_heap_region_end - m_heap_region_start;
|
||||||
case Svc::MemoryState::Ipc:
|
case KMemoryState::Ipc:
|
||||||
case Svc::MemoryState::NonSecureIpc:
|
case KMemoryState::NonSecureIpc:
|
||||||
case Svc::MemoryState::NonDeviceIpc:
|
case KMemoryState::NonDeviceIpc:
|
||||||
return m_alias_region_end - m_alias_region_start;
|
return m_alias_region_end - m_alias_region_start;
|
||||||
case Svc::MemoryState::Stack:
|
case KMemoryState::Stack:
|
||||||
return m_stack_region_end - m_stack_region_start;
|
return m_stack_region_end - m_stack_region_start;
|
||||||
case Svc::MemoryState::Static:
|
case KMemoryState::Static:
|
||||||
case Svc::MemoryState::ThreadLocal:
|
case KMemoryState::ThreadLocal:
|
||||||
return m_kernel_map_region_end - m_kernel_map_region_start;
|
return m_kernel_map_region_end - m_kernel_map_region_start;
|
||||||
case Svc::MemoryState::Io:
|
case KMemoryState::Io:
|
||||||
case Svc::MemoryState::Shared:
|
case KMemoryState::Shared:
|
||||||
case Svc::MemoryState::AliasCode:
|
case KMemoryState::AliasCode:
|
||||||
case Svc::MemoryState::AliasCodeData:
|
case KMemoryState::AliasCodeData:
|
||||||
case Svc::MemoryState::Transfered:
|
case KMemoryState::Transfered:
|
||||||
case Svc::MemoryState::SharedTransfered:
|
case KMemoryState::SharedTransfered:
|
||||||
case Svc::MemoryState::SharedCode:
|
case KMemoryState::SharedCode:
|
||||||
case Svc::MemoryState::GeneratedCode:
|
case KMemoryState::GeneratedCode:
|
||||||
case Svc::MemoryState::CodeOut:
|
case KMemoryState::CodeOut:
|
||||||
case Svc::MemoryState::Coverage:
|
case KMemoryState::Coverage:
|
||||||
case Svc::MemoryState::Insecure:
|
case KMemoryState::Insecure:
|
||||||
return m_alias_code_region_end - m_alias_code_region_start;
|
return m_alias_code_region_end - m_alias_code_region_start;
|
||||||
case Svc::MemoryState::Code:
|
case KMemoryState::Code:
|
||||||
case Svc::MemoryState::CodeData:
|
case KMemoryState::CodeData:
|
||||||
return m_code_region_end - m_code_region_start;
|
return m_code_region_end - m_code_region_start;
|
||||||
default:
|
default:
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool KPageTable::CanContain(KProcessAddress addr, size_t size, Svc::MemoryState state) const {
|
bool KPageTable::CanContain(KProcessAddress addr, size_t size, KMemoryState state) const {
|
||||||
const KProcessAddress end = addr + size;
|
const KProcessAddress end = addr + size;
|
||||||
const KProcessAddress last = end - 1;
|
const KProcessAddress last = end - 1;
|
||||||
|
|
||||||
@ -3219,32 +3175,32 @@ bool KPageTable::CanContain(KProcessAddress addr, size_t size, Svc::MemoryState
|
|||||||
const bool is_in_alias = !(end <= m_alias_region_start || m_alias_region_end <= addr ||
|
const bool is_in_alias = !(end <= m_alias_region_start || m_alias_region_end <= addr ||
|
||||||
m_alias_region_start == m_alias_region_end);
|
m_alias_region_start == m_alias_region_end);
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case Svc::MemoryState::Free:
|
case KMemoryState::Free:
|
||||||
case Svc::MemoryState::Kernel:
|
case KMemoryState::Kernel:
|
||||||
return is_in_region;
|
return is_in_region;
|
||||||
case Svc::MemoryState::Io:
|
case KMemoryState::Io:
|
||||||
case Svc::MemoryState::Static:
|
case KMemoryState::Static:
|
||||||
case Svc::MemoryState::Code:
|
case KMemoryState::Code:
|
||||||
case Svc::MemoryState::CodeData:
|
case KMemoryState::CodeData:
|
||||||
case Svc::MemoryState::Shared:
|
case KMemoryState::Shared:
|
||||||
case Svc::MemoryState::AliasCode:
|
case KMemoryState::AliasCode:
|
||||||
case Svc::MemoryState::AliasCodeData:
|
case KMemoryState::AliasCodeData:
|
||||||
case Svc::MemoryState::Stack:
|
case KMemoryState::Stack:
|
||||||
case Svc::MemoryState::ThreadLocal:
|
case KMemoryState::ThreadLocal:
|
||||||
case Svc::MemoryState::Transfered:
|
case KMemoryState::Transfered:
|
||||||
case Svc::MemoryState::SharedTransfered:
|
case KMemoryState::SharedTransfered:
|
||||||
case Svc::MemoryState::SharedCode:
|
case KMemoryState::SharedCode:
|
||||||
case Svc::MemoryState::GeneratedCode:
|
case KMemoryState::GeneratedCode:
|
||||||
case Svc::MemoryState::CodeOut:
|
case KMemoryState::CodeOut:
|
||||||
case Svc::MemoryState::Coverage:
|
case KMemoryState::Coverage:
|
||||||
case Svc::MemoryState::Insecure:
|
case KMemoryState::Insecure:
|
||||||
return is_in_region && !is_in_heap && !is_in_alias;
|
return is_in_region && !is_in_heap && !is_in_alias;
|
||||||
case Svc::MemoryState::Normal:
|
case KMemoryState::Normal:
|
||||||
ASSERT(is_in_heap);
|
ASSERT(is_in_heap);
|
||||||
return is_in_region && !is_in_alias;
|
return is_in_region && !is_in_alias;
|
||||||
case Svc::MemoryState::Ipc:
|
case KMemoryState::Ipc:
|
||||||
case Svc::MemoryState::NonSecureIpc:
|
case KMemoryState::NonSecureIpc:
|
||||||
case Svc::MemoryState::NonDeviceIpc:
|
case KMemoryState::NonDeviceIpc:
|
||||||
ASSERT(is_in_alias);
|
ASSERT(is_in_alias);
|
||||||
return is_in_region && !is_in_heap;
|
return is_in_region && !is_in_heap;
|
||||||
default:
|
default:
|
||||||
@ -3308,16 +3264,21 @@ Result KPageTable::CheckMemoryStateContiguous(size_t* out_blocks_needed, KProces
|
|||||||
|
|
||||||
Result KPageTable::CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm,
|
Result KPageTable::CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm,
|
||||||
KMemoryAttribute* out_attr, size_t* out_blocks_needed,
|
KMemoryAttribute* out_attr, size_t* out_blocks_needed,
|
||||||
KMemoryBlockManager::const_iterator it,
|
KProcessAddress addr, size_t size, KMemoryState state_mask,
|
||||||
KProcessAddress last_addr, KMemoryState state_mask,
|
|
||||||
KMemoryState state, KMemoryPermission perm_mask,
|
KMemoryState state, KMemoryPermission perm_mask,
|
||||||
KMemoryPermission perm, KMemoryAttribute attr_mask,
|
KMemoryPermission perm, KMemoryAttribute attr_mask,
|
||||||
KMemoryAttribute attr, KMemoryAttribute ignore_attr) const {
|
KMemoryAttribute attr, KMemoryAttribute ignore_attr) const {
|
||||||
ASSERT(this->IsLockedByCurrentThread());
|
ASSERT(this->IsLockedByCurrentThread());
|
||||||
|
|
||||||
// Get information about the first block.
|
// Get information about the first block.
|
||||||
|
const KProcessAddress last_addr = addr + size - 1;
|
||||||
|
KMemoryBlockManager::const_iterator it = m_memory_block_manager.FindIterator(addr);
|
||||||
KMemoryInfo info = it->GetMemoryInfo();
|
KMemoryInfo info = it->GetMemoryInfo();
|
||||||
|
|
||||||
|
// If the start address isn't aligned, we need a block.
|
||||||
|
const size_t blocks_for_start_align =
|
||||||
|
(Common::AlignDown(GetInteger(addr), PageSize) != info.GetAddress()) ? 1 : 0;
|
||||||
|
|
||||||
// Validate all blocks in the range have correct state.
|
// Validate all blocks in the range have correct state.
|
||||||
const KMemoryState first_state = info.m_state;
|
const KMemoryState first_state = info.m_state;
|
||||||
const KMemoryPermission first_perm = info.m_permission;
|
const KMemoryPermission first_perm = info.m_permission;
|
||||||
@ -3343,6 +3304,10 @@ Result KPageTable::CheckMemoryState(KMemoryState* out_state, KMemoryPermission*
|
|||||||
info = it->GetMemoryInfo();
|
info = it->GetMemoryInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If the end address isn't aligned, we need a block.
|
||||||
|
const size_t blocks_for_end_align =
|
||||||
|
(Common::AlignUp(GetInteger(addr) + size, PageSize) != info.GetEndAddress()) ? 1 : 0;
|
||||||
|
|
||||||
// Write output state.
|
// Write output state.
|
||||||
if (out_state != nullptr) {
|
if (out_state != nullptr) {
|
||||||
*out_state = first_state;
|
*out_state = first_state;
|
||||||
@ -3353,39 +3318,9 @@ Result KPageTable::CheckMemoryState(KMemoryState* out_state, KMemoryPermission*
|
|||||||
if (out_attr != nullptr) {
|
if (out_attr != nullptr) {
|
||||||
*out_attr = static_cast<KMemoryAttribute>(first_attr & ~ignore_attr);
|
*out_attr = static_cast<KMemoryAttribute>(first_attr & ~ignore_attr);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the end address isn't aligned, we need a block.
|
|
||||||
if (out_blocks_needed != nullptr) {
|
if (out_blocks_needed != nullptr) {
|
||||||
const size_t blocks_for_end_align =
|
*out_blocks_needed = blocks_for_start_align + blocks_for_end_align;
|
||||||
(Common::AlignDown(GetInteger(last_addr), PageSize) + PageSize != info.GetEndAddress())
|
|
||||||
? 1
|
|
||||||
: 0;
|
|
||||||
*out_blocks_needed = blocks_for_end_align;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
R_SUCCEED();
|
|
||||||
}
|
|
||||||
|
|
||||||
Result KPageTable::CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm,
|
|
||||||
KMemoryAttribute* out_attr, size_t* out_blocks_needed,
|
|
||||||
KProcessAddress addr, size_t size, KMemoryState state_mask,
|
|
||||||
KMemoryState state, KMemoryPermission perm_mask,
|
|
||||||
KMemoryPermission perm, KMemoryAttribute attr_mask,
|
|
||||||
KMemoryAttribute attr, KMemoryAttribute ignore_attr) const {
|
|
||||||
ASSERT(this->IsLockedByCurrentThread());
|
|
||||||
|
|
||||||
// Check memory state.
|
|
||||||
const KProcessAddress last_addr = addr + size - 1;
|
|
||||||
KMemoryBlockManager::const_iterator it = m_memory_block_manager.FindIterator(addr);
|
|
||||||
R_TRY(this->CheckMemoryState(out_state, out_perm, out_attr, out_blocks_needed, it, last_addr,
|
|
||||||
state_mask, state, perm_mask, perm, attr_mask, attr, ignore_attr));
|
|
||||||
|
|
||||||
// If the start address isn't aligned, we need a block.
|
|
||||||
if (out_blocks_needed != nullptr &&
|
|
||||||
Common::AlignDown(GetInteger(addr), PageSize) != it->GetAddress()) {
|
|
||||||
++(*out_blocks_needed);
|
|
||||||
}
|
|
||||||
|
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3453,11 +3388,6 @@ Result KPageTable::LockMemoryAndOpen(KPageGroup* out_pg, KPhysicalAddress* out_K
|
|||||||
new_attr, KMemoryBlockDisableMergeAttribute::Locked,
|
new_attr, KMemoryBlockDisableMergeAttribute::Locked,
|
||||||
KMemoryBlockDisableMergeAttribute::None);
|
KMemoryBlockDisableMergeAttribute::None);
|
||||||
|
|
||||||
// If we have an output page group, open.
|
|
||||||
if (out_pg) {
|
|
||||||
out_pg->Open();
|
|
||||||
}
|
|
||||||
|
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,9 +104,6 @@ public:
|
|||||||
Result CleanupForIpcServer(KProcessAddress address, size_t size, KMemoryState dst_state);
|
Result CleanupForIpcServer(KProcessAddress address, size_t size, KMemoryState dst_state);
|
||||||
Result CleanupForIpcClient(KProcessAddress address, size_t size, KMemoryState dst_state);
|
Result CleanupForIpcClient(KProcessAddress address, size_t size, KMemoryState dst_state);
|
||||||
|
|
||||||
Result LockForTransferMemory(KPageGroup* out, KProcessAddress address, size_t size,
|
|
||||||
KMemoryPermission perm);
|
|
||||||
Result UnlockForTransferMemory(KProcessAddress address, size_t size, const KPageGroup& pg);
|
|
||||||
Result LockForCodeMemory(KPageGroup* out, KProcessAddress addr, size_t size);
|
Result LockForCodeMemory(KPageGroup* out, KProcessAddress addr, size_t size);
|
||||||
Result UnlockForCodeMemory(KProcessAddress addr, size_t size, const KPageGroup& pg);
|
Result UnlockForCodeMemory(KProcessAddress addr, size_t size, const KPageGroup& pg);
|
||||||
Result MakeAndOpenPageGroup(KPageGroup* out, KProcessAddress address, size_t num_pages,
|
Result MakeAndOpenPageGroup(KPageGroup* out, KProcessAddress address, size_t num_pages,
|
||||||
@ -126,6 +123,8 @@ public:
|
|||||||
return m_block_info_manager;
|
return m_block_info_manager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CanContain(KProcessAddress addr, size_t size, KMemoryState state) const;
|
||||||
|
|
||||||
Result MapPages(KProcessAddress* out_addr, size_t num_pages, size_t alignment,
|
Result MapPages(KProcessAddress* out_addr, size_t num_pages, size_t alignment,
|
||||||
KPhysicalAddress phys_addr, KProcessAddress region_start,
|
KPhysicalAddress phys_addr, KProcessAddress region_start,
|
||||||
size_t region_num_pages, KMemoryState state, KMemoryPermission perm) {
|
size_t region_num_pages, KMemoryState state, KMemoryPermission perm) {
|
||||||
@ -160,21 +159,6 @@ public:
|
|||||||
void RemapPageGroup(PageLinkedList* page_list, KProcessAddress address, size_t size,
|
void RemapPageGroup(PageLinkedList* page_list, KProcessAddress address, size_t size,
|
||||||
const KPageGroup& pg);
|
const KPageGroup& pg);
|
||||||
|
|
||||||
KProcessAddress GetRegionAddress(Svc::MemoryState state) const;
|
|
||||||
size_t GetRegionSize(Svc::MemoryState state) const;
|
|
||||||
bool CanContain(KProcessAddress addr, size_t size, Svc::MemoryState state) const;
|
|
||||||
|
|
||||||
KProcessAddress GetRegionAddress(KMemoryState state) const {
|
|
||||||
return this->GetRegionAddress(static_cast<Svc::MemoryState>(state & KMemoryState::Mask));
|
|
||||||
}
|
|
||||||
size_t GetRegionSize(KMemoryState state) const {
|
|
||||||
return this->GetRegionSize(static_cast<Svc::MemoryState>(state & KMemoryState::Mask));
|
|
||||||
}
|
|
||||||
bool CanContain(KProcessAddress addr, size_t size, KMemoryState state) const {
|
|
||||||
return this->CanContain(addr, size,
|
|
||||||
static_cast<Svc::MemoryState>(state & KMemoryState::Mask));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
struct PageLinkedList {
|
struct PageLinkedList {
|
||||||
private:
|
private:
|
||||||
@ -217,13 +201,12 @@ protected:
|
|||||||
private:
|
private:
|
||||||
enum class OperationType : u32 {
|
enum class OperationType : u32 {
|
||||||
Map = 0,
|
Map = 0,
|
||||||
MapGroup = 1,
|
MapFirst = 1,
|
||||||
MapFirstGroup = 2,
|
MapGroup = 2,
|
||||||
Unmap = 3,
|
Unmap = 3,
|
||||||
ChangePermissions = 4,
|
ChangePermissions = 4,
|
||||||
ChangePermissionsAndRefresh = 5,
|
ChangePermissionsAndRefresh = 5,
|
||||||
ChangePermissionsAndRefreshAndFlush = 6,
|
Separate = 6,
|
||||||
Separate = 7,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static constexpr KMemoryAttribute DefaultMemoryIgnoreAttr =
|
static constexpr KMemoryAttribute DefaultMemoryIgnoreAttr =
|
||||||
@ -242,6 +225,8 @@ private:
|
|||||||
Result Operate(KProcessAddress addr, size_t num_pages, KMemoryPermission perm,
|
Result Operate(KProcessAddress addr, size_t num_pages, KMemoryPermission perm,
|
||||||
OperationType operation, KPhysicalAddress map_addr = 0);
|
OperationType operation, KPhysicalAddress map_addr = 0);
|
||||||
void FinalizeUpdate(PageLinkedList* page_list);
|
void FinalizeUpdate(PageLinkedList* page_list);
|
||||||
|
KProcessAddress GetRegionAddress(KMemoryState state) const;
|
||||||
|
size_t GetRegionSize(KMemoryState state) const;
|
||||||
|
|
||||||
KProcessAddress FindFreeArea(KProcessAddress region_start, size_t region_num_pages,
|
KProcessAddress FindFreeArea(KProcessAddress region_start, size_t region_num_pages,
|
||||||
size_t num_pages, size_t alignment, size_t offset,
|
size_t num_pages, size_t alignment, size_t offset,
|
||||||
@ -262,13 +247,6 @@ private:
|
|||||||
Result CheckMemoryState(const KMemoryInfo& info, KMemoryState state_mask, KMemoryState state,
|
Result CheckMemoryState(const KMemoryInfo& info, KMemoryState state_mask, KMemoryState state,
|
||||||
KMemoryPermission perm_mask, KMemoryPermission perm,
|
KMemoryPermission perm_mask, KMemoryPermission perm,
|
||||||
KMemoryAttribute attr_mask, KMemoryAttribute attr) const;
|
KMemoryAttribute attr_mask, KMemoryAttribute attr) const;
|
||||||
Result CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm,
|
|
||||||
KMemoryAttribute* out_attr, size_t* out_blocks_needed,
|
|
||||||
KMemoryBlockManager::const_iterator it, KProcessAddress last_addr,
|
|
||||||
KMemoryState state_mask, KMemoryState state,
|
|
||||||
KMemoryPermission perm_mask, KMemoryPermission perm,
|
|
||||||
KMemoryAttribute attr_mask, KMemoryAttribute attr,
|
|
||||||
KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const;
|
|
||||||
Result CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm,
|
Result CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm,
|
||||||
KMemoryAttribute* out_attr, size_t* out_blocks_needed,
|
KMemoryAttribute* out_attr, size_t* out_blocks_needed,
|
||||||
KProcessAddress addr, size_t size, KMemoryState state_mask,
|
KProcessAddress addr, size_t size, KMemoryState state_mask,
|
||||||
|
@ -149,7 +149,7 @@ u64 KProcess::GetTotalPhysicalMemoryUsed() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
u64 KProcess::GetTotalPhysicalMemoryUsedWithoutSystemResource() {
|
u64 KProcess::GetTotalPhysicalMemoryUsedWithoutSystemResource() {
|
||||||
return this->GetTotalPhysicalMemoryUsed() - this->GetSystemResourceSize();
|
return this->GetTotalPhysicalMemoryUsed() - this->GetSystemResourceUsage();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool KProcess::ReleaseUserException(KThread* thread) {
|
bool KProcess::ReleaseUserException(KThread* thread) {
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#include "common/scope_exit.h"
|
|
||||||
#include "core/hle/kernel/k_process.h"
|
#include "core/hle/kernel/k_process.h"
|
||||||
#include "core/hle/kernel/k_resource_limit.h"
|
#include "core/hle/kernel/k_resource_limit.h"
|
||||||
#include "core/hle/kernel/k_transfer_memory.h"
|
#include "core/hle/kernel/k_transfer_memory.h"
|
||||||
@ -10,50 +9,28 @@
|
|||||||
namespace Kernel {
|
namespace Kernel {
|
||||||
|
|
||||||
KTransferMemory::KTransferMemory(KernelCore& kernel)
|
KTransferMemory::KTransferMemory(KernelCore& kernel)
|
||||||
: KAutoObjectWithSlabHeapAndContainer{kernel}, m_lock{kernel} {}
|
: KAutoObjectWithSlabHeapAndContainer{kernel} {}
|
||||||
|
|
||||||
KTransferMemory::~KTransferMemory() = default;
|
KTransferMemory::~KTransferMemory() = default;
|
||||||
|
|
||||||
Result KTransferMemory::Initialize(KProcessAddress addr, std::size_t size,
|
Result KTransferMemory::Initialize(KProcessAddress address, std::size_t size,
|
||||||
Svc::MemoryPermission own_perm) {
|
Svc::MemoryPermission owner_perm) {
|
||||||
// Set members.
|
// Set members.
|
||||||
m_owner = GetCurrentProcessPointer(m_kernel);
|
m_owner = GetCurrentProcessPointer(m_kernel);
|
||||||
|
|
||||||
// Get the owner page table.
|
// TODO(bunnei): Lock for transfer memory
|
||||||
auto& page_table = m_owner->GetPageTable();
|
|
||||||
|
|
||||||
// Construct the page group, guarding to make sure our state is valid on exit.
|
|
||||||
m_page_group.emplace(m_kernel, page_table.GetBlockInfoManager());
|
|
||||||
auto pg_guard = SCOPE_GUARD({ m_page_group.reset(); });
|
|
||||||
|
|
||||||
// Lock the memory.
|
|
||||||
R_TRY(page_table.LockForTransferMemory(std::addressof(*m_page_group), addr, size,
|
|
||||||
ConvertToKMemoryPermission(own_perm)));
|
|
||||||
|
|
||||||
// Set remaining tracking members.
|
// Set remaining tracking members.
|
||||||
m_owner->Open();
|
m_owner->Open();
|
||||||
m_owner_perm = own_perm;
|
m_owner_perm = owner_perm;
|
||||||
m_address = addr;
|
m_address = address;
|
||||||
|
m_size = size;
|
||||||
m_is_initialized = true;
|
m_is_initialized = true;
|
||||||
m_is_mapped = false;
|
|
||||||
|
|
||||||
// We succeeded.
|
|
||||||
pg_guard.Cancel();
|
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
void KTransferMemory::Finalize() {
|
void KTransferMemory::Finalize() {}
|
||||||
// Unlock.
|
|
||||||
if (!m_is_mapped) {
|
|
||||||
const size_t size = m_page_group->GetNumPages() * PageSize;
|
|
||||||
ASSERT(R_SUCCEEDED(
|
|
||||||
m_owner->GetPageTable().UnlockForTransferMemory(m_address, size, *m_page_group)));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close the page group.
|
|
||||||
m_page_group->Close();
|
|
||||||
m_page_group->Finalize();
|
|
||||||
}
|
|
||||||
|
|
||||||
void KTransferMemory::PostDestroy(uintptr_t arg) {
|
void KTransferMemory::PostDestroy(uintptr_t arg) {
|
||||||
KProcess* owner = reinterpret_cast<KProcess*>(arg);
|
KProcess* owner = reinterpret_cast<KProcess*>(arg);
|
||||||
@ -61,54 +38,4 @@ void KTransferMemory::PostDestroy(uintptr_t arg) {
|
|||||||
owner->Close();
|
owner->Close();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result KTransferMemory::Map(KProcessAddress address, size_t size, Svc::MemoryPermission map_perm) {
|
|
||||||
// Validate the size.
|
|
||||||
R_UNLESS(m_page_group->GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize);
|
|
||||||
|
|
||||||
// Validate the permission.
|
|
||||||
R_UNLESS(m_owner_perm == map_perm, ResultInvalidState);
|
|
||||||
|
|
||||||
// Lock ourselves.
|
|
||||||
KScopedLightLock lk(m_lock);
|
|
||||||
|
|
||||||
// Ensure we're not already mapped.
|
|
||||||
R_UNLESS(!m_is_mapped, ResultInvalidState);
|
|
||||||
|
|
||||||
// Map the memory.
|
|
||||||
const KMemoryState state = (m_owner_perm == Svc::MemoryPermission::None)
|
|
||||||
? KMemoryState::Transfered
|
|
||||||
: KMemoryState::SharedTransfered;
|
|
||||||
R_TRY(GetCurrentProcess(m_kernel).GetPageTable().MapPageGroup(
|
|
||||||
address, *m_page_group, state, KMemoryPermission::UserReadWrite));
|
|
||||||
|
|
||||||
// Mark ourselves as mapped.
|
|
||||||
m_is_mapped = true;
|
|
||||||
|
|
||||||
R_SUCCEED();
|
|
||||||
}
|
|
||||||
|
|
||||||
Result KTransferMemory::Unmap(KProcessAddress address, size_t size) {
|
|
||||||
// Validate the size.
|
|
||||||
R_UNLESS(m_page_group->GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize);
|
|
||||||
|
|
||||||
// Lock ourselves.
|
|
||||||
KScopedLightLock lk(m_lock);
|
|
||||||
|
|
||||||
// Unmap the memory.
|
|
||||||
const KMemoryState state = (m_owner_perm == Svc::MemoryPermission::None)
|
|
||||||
? KMemoryState::Transfered
|
|
||||||
: KMemoryState::SharedTransfered;
|
|
||||||
R_TRY(GetCurrentProcess(m_kernel).GetPageTable().UnmapPageGroup(address, *m_page_group, state));
|
|
||||||
|
|
||||||
// Mark ourselves as unmapped.
|
|
||||||
ASSERT(m_is_mapped);
|
|
||||||
m_is_mapped = false;
|
|
||||||
|
|
||||||
R_SUCCEED();
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t KTransferMemory::GetSize() const {
|
|
||||||
return m_is_initialized ? m_page_group->GetNumPages() * PageSize : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Kernel
|
} // namespace Kernel
|
||||||
|
@ -3,9 +3,6 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <optional>
|
|
||||||
|
|
||||||
#include "core/hle/kernel/k_page_group.h"
|
|
||||||
#include "core/hle/kernel/slab_helpers.h"
|
#include "core/hle/kernel/slab_helpers.h"
|
||||||
#include "core/hle/kernel/svc_types.h"
|
#include "core/hle/kernel/svc_types.h"
|
||||||
#include "core/hle/result.h"
|
#include "core/hle/result.h"
|
||||||
@ -51,19 +48,16 @@ public:
|
|||||||
return m_address;
|
return m_address;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t GetSize() const;
|
size_t GetSize() const {
|
||||||
|
return m_is_initialized ? m_size : 0;
|
||||||
Result Map(KProcessAddress address, size_t size, Svc::MemoryPermission map_perm);
|
}
|
||||||
Result Unmap(KProcessAddress address, size_t size);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::optional<KPageGroup> m_page_group{};
|
|
||||||
KProcess* m_owner{};
|
KProcess* m_owner{};
|
||||||
KProcessAddress m_address{};
|
KProcessAddress m_address{};
|
||||||
KLightLock m_lock;
|
|
||||||
Svc::MemoryPermission m_owner_perm{};
|
Svc::MemoryPermission m_owner_perm{};
|
||||||
|
size_t m_size{};
|
||||||
bool m_is_initialized{};
|
bool m_is_initialized{};
|
||||||
bool m_is_mapped{};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Kernel
|
} // namespace Kernel
|
||||||
|
@ -373,7 +373,7 @@ struct KernelCore::Impl {
|
|||||||
static inline thread_local u8 host_thread_id = UINT8_MAX;
|
static inline thread_local u8 host_thread_id = UINT8_MAX;
|
||||||
|
|
||||||
/// Sets the host thread ID for the caller.
|
/// Sets the host thread ID for the caller.
|
||||||
LTO_NOINLINE u32 SetHostThreadId(std::size_t core_id) {
|
u32 SetHostThreadId(std::size_t core_id) {
|
||||||
// This should only be called during core init.
|
// This should only be called during core init.
|
||||||
ASSERT(host_thread_id == UINT8_MAX);
|
ASSERT(host_thread_id == UINT8_MAX);
|
||||||
|
|
||||||
@ -384,13 +384,13 @@ struct KernelCore::Impl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the host thread ID for the caller
|
/// Gets the host thread ID for the caller
|
||||||
LTO_NOINLINE u32 GetHostThreadId() const {
|
u32 GetHostThreadId() const {
|
||||||
return host_thread_id;
|
return host_thread_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gets the dummy KThread for the caller, allocating a new one if this is the first time
|
// Gets the dummy KThread for the caller, allocating a new one if this is the first time
|
||||||
LTO_NOINLINE KThread* GetHostDummyThread(KThread* existing_thread) {
|
KThread* GetHostDummyThread(KThread* existing_thread) {
|
||||||
const auto initialize{[](KThread* thread) LTO_NOINLINE {
|
const auto initialize{[](KThread* thread) {
|
||||||
ASSERT(KThread::InitializeDummyThread(thread, nullptr).IsSuccess());
|
ASSERT(KThread::InitializeDummyThread(thread, nullptr).IsSuccess());
|
||||||
return thread;
|
return thread;
|
||||||
}};
|
}};
|
||||||
@ -424,11 +424,11 @@ struct KernelCore::Impl {
|
|||||||
|
|
||||||
static inline thread_local bool is_phantom_mode_for_singlecore{false};
|
static inline thread_local bool is_phantom_mode_for_singlecore{false};
|
||||||
|
|
||||||
LTO_NOINLINE bool IsPhantomModeForSingleCore() const {
|
bool IsPhantomModeForSingleCore() const {
|
||||||
return is_phantom_mode_for_singlecore;
|
return is_phantom_mode_for_singlecore;
|
||||||
}
|
}
|
||||||
|
|
||||||
LTO_NOINLINE void SetIsPhantomModeForSingleCore(bool value) {
|
void SetIsPhantomModeForSingleCore(bool value) {
|
||||||
ASSERT(!is_multicore);
|
ASSERT(!is_multicore);
|
||||||
is_phantom_mode_for_singlecore = value;
|
is_phantom_mode_for_singlecore = value;
|
||||||
}
|
}
|
||||||
@ -439,14 +439,14 @@ struct KernelCore::Impl {
|
|||||||
|
|
||||||
static inline thread_local KThread* current_thread{nullptr};
|
static inline thread_local KThread* current_thread{nullptr};
|
||||||
|
|
||||||
LTO_NOINLINE KThread* GetCurrentEmuThread() {
|
KThread* GetCurrentEmuThread() {
|
||||||
if (!current_thread) {
|
if (!current_thread) {
|
||||||
current_thread = GetHostDummyThread(nullptr);
|
current_thread = GetHostDummyThread(nullptr);
|
||||||
}
|
}
|
||||||
return current_thread;
|
return current_thread;
|
||||||
}
|
}
|
||||||
|
|
||||||
LTO_NOINLINE void SetCurrentEmuThread(KThread* thread) {
|
void SetCurrentEmuThread(KThread* thread) {
|
||||||
current_thread = thread;
|
current_thread = thread;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -623,33 +623,14 @@ struct KernelCore::Impl {
|
|||||||
ASSERT(memory_layout->GetPhysicalMemoryRegionTree().Insert(
|
ASSERT(memory_layout->GetPhysicalMemoryRegionTree().Insert(
|
||||||
GetInteger(slab_start_phys_addr), slab_region_size, KMemoryRegionType_DramKernelSlab));
|
GetInteger(slab_start_phys_addr), slab_region_size, KMemoryRegionType_DramKernelSlab));
|
||||||
|
|
||||||
// Insert a physical region for the secure applet memory.
|
|
||||||
const auto secure_applet_end_phys_addr =
|
|
||||||
slab_end_phys_addr + KSystemControl::SecureAppletMemorySize;
|
|
||||||
if constexpr (KSystemControl::SecureAppletMemorySize > 0) {
|
|
||||||
ASSERT(memory_layout->GetPhysicalMemoryRegionTree().Insert(
|
|
||||||
GetInteger(slab_end_phys_addr), KSystemControl::SecureAppletMemorySize,
|
|
||||||
KMemoryRegionType_DramKernelSecureAppletMemory));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Insert a physical region for the unknown debug2 region.
|
|
||||||
constexpr size_t SecureUnknownRegionSize = 0;
|
|
||||||
const size_t secure_unknown_size = SecureUnknownRegionSize;
|
|
||||||
const auto secure_unknown_end_phys_addr = secure_applet_end_phys_addr + secure_unknown_size;
|
|
||||||
if constexpr (SecureUnknownRegionSize > 0) {
|
|
||||||
ASSERT(memory_layout->GetPhysicalMemoryRegionTree().Insert(
|
|
||||||
GetInteger(secure_applet_end_phys_addr), secure_unknown_size,
|
|
||||||
KMemoryRegionType_DramKernelSecureUnknown));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Determine size available for kernel page table heaps, requiring > 8 MB.
|
// Determine size available for kernel page table heaps, requiring > 8 MB.
|
||||||
const KPhysicalAddress resource_end_phys_addr = slab_start_phys_addr + resource_region_size;
|
const KPhysicalAddress resource_end_phys_addr = slab_start_phys_addr + resource_region_size;
|
||||||
const size_t page_table_heap_size = resource_end_phys_addr - secure_unknown_end_phys_addr;
|
const size_t page_table_heap_size = resource_end_phys_addr - slab_end_phys_addr;
|
||||||
ASSERT(page_table_heap_size / 4_MiB > 2);
|
ASSERT(page_table_heap_size / 4_MiB > 2);
|
||||||
|
|
||||||
// Insert a physical region for the kernel page table heap region
|
// Insert a physical region for the kernel page table heap region
|
||||||
ASSERT(memory_layout->GetPhysicalMemoryRegionTree().Insert(
|
ASSERT(memory_layout->GetPhysicalMemoryRegionTree().Insert(
|
||||||
GetInteger(secure_unknown_end_phys_addr), page_table_heap_size,
|
GetInteger(slab_end_phys_addr), page_table_heap_size,
|
||||||
KMemoryRegionType_DramKernelPtHeap));
|
KMemoryRegionType_DramKernelPtHeap));
|
||||||
|
|
||||||
// All DRAM regions that we haven't tagged by this point will be mapped under the linear
|
// All DRAM regions that we haven't tagged by this point will be mapped under the linear
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user