This commit is contained in:
Timotej Leginus 2023-06-11 20:20:17 +02:00
commit d5129d9347
14 changed files with 380 additions and 494 deletions

View file

@ -3,4 +3,4 @@
[codespell]
skip = ./.git,./build,./dist,./Doxyfile,./externals,./LICENSES
ignore-words-list = aci,allright,ba,deques,froms,hda,inout,lod,masia,nam,nax,nd,pullrequests,pullrequest,te,transfered,unstall,uscaled,zink
ignore-words-list = aci,allright,ba,deques,froms,hda,inout,lod,masia,nam,nax,nd,optin,pullrequests,pullrequest,te,transfered,unstall,uscaled,zink

View file

@ -223,6 +223,8 @@ object NativeLibrary {
external fun getCompany(filename: String): String
external fun isHomebrew(filename: String): Boolean
external fun setAppDirectory(directory: String)
external fun initializeGpuDriver(

View file

@ -6,15 +6,12 @@ package org.yuzu.yuzu_emu.activities
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.content.res.Configuration
import android.graphics.Rect
import android.hardware.Sensor
import android.hardware.SensorEvent
import android.hardware.SensorEventListener
import android.hardware.SensorManager
import android.hardware.display.DisplayManager
import android.os.Bundle
import android.view.Display
import android.view.InputDevice
import android.view.KeyEvent
import android.view.MotionEvent
@ -23,7 +20,6 @@ import android.view.View
import android.view.inputmethod.InputMethodManager
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.getSystemService
import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.WindowInsetsControllerCompat
@ -39,7 +35,6 @@ import org.yuzu.yuzu_emu.features.settings.model.SettingsViewModel
import org.yuzu.yuzu_emu.fragments.EmulationFragment
import org.yuzu.yuzu_emu.model.Game
import org.yuzu.yuzu_emu.utils.ControllerMappingHelper
import org.yuzu.yuzu_emu.utils.EmulationMenuSettings
import org.yuzu.yuzu_emu.utils.ForegroundService
import org.yuzu.yuzu_emu.utils.InputHandler
import org.yuzu.yuzu_emu.utils.NfcReader
@ -148,11 +143,6 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
super.onResume()
nfcReader.startScanning()
startMotionSensorListener()
NativeLibrary.notifyOrientationChange(
EmulationMenuSettings.landscapeScreenLayout,
getAdjustedRotation()
)
}
override fun onPause() {
@ -258,24 +248,6 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
override fun onAccuracyChanged(sensor: Sensor, i: Int) {}
private fun getAdjustedRotation():Int {
val rotation = getSystemService<DisplayManager>()!!.getDisplay(Display.DEFAULT_DISPLAY).rotation
val config: Configuration = resources.configuration
if ((config.screenLayout and Configuration.SCREENLAYOUT_LONG_YES) != 0 ||
(config.screenLayout and Configuration.SCREENLAYOUT_LONG_NO) == 0 ||
(config.screenLayout and Configuration.SCREENLAYOUT_SIZE_SMALL) != 0) {
return rotation
}
when (rotation) {
Surface.ROTATION_0 -> return Surface.ROTATION_90
Surface.ROTATION_90 -> return Surface.ROTATION_0
Surface.ROTATION_180 -> return Surface.ROTATION_270
Surface.ROTATION_270 -> return Surface.ROTATION_180
}
return rotation
}
private fun restoreState(savedInstanceState: Bundle) {
game = savedInstanceState.parcelable(EXTRA_SELECTED_GAME)!!
}

View file

@ -127,13 +127,7 @@ class SearchFragment : Fragment() {
}
}
R.id.chip_homebrew -> {
baseList.filter {
Log.error("Guh - ${it.path}")
FileUtil.hasExtension(it.path, "nro")
|| FileUtil.hasExtension(it.path, "nso")
}
}
R.id.chip_homebrew -> baseList.filter { it.isHomebrew }
R.id.chip_retail -> baseList.filter {
FileUtil.hasExtension(it.path, "xci")

View file

@ -16,7 +16,8 @@ class Game(
val regions: String,
val path: String,
val gameId: String,
val company: String
val company: String,
val isHomebrew: Boolean
) : Parcelable {
val keyAddedToLibraryTime get() = "${gameId}_AddedToLibraryTime"
val keyLastPlayedTime get() = "${gameId}_LastPlayed"
@ -31,6 +32,7 @@ class Game(
&& path == other.path
&& gameId == other.gameId
&& company == other.company
&& isHomebrew == other.isHomebrew
}
companion object {

View file

@ -13,6 +13,8 @@ import androidx.preference.PreferenceManager
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.MissingFieldException
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import org.yuzu.yuzu_emu.NativeLibrary
@ -20,6 +22,7 @@ import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.utils.GameHelper
import java.util.Locale
@OptIn(ExperimentalSerializationApi::class)
class GamesViewModel : ViewModel() {
private val _games = MutableLiveData<List<Game>>(emptyList())
val games: LiveData<List<Game>> get() = _games
@ -49,7 +52,13 @@ class GamesViewModel : ViewModel() {
if (storedGames!!.isNotEmpty()) {
val deserializedGames = mutableSetOf<Game>()
storedGames.forEach {
val game: Game = Json.decodeFromString(it)
val game: Game
try {
game = Json.decodeFromString(it)
} catch (e: MissingFieldException) {
return@forEach
}
val gameExists =
DocumentFile.fromSingleUri(YuzuApplication.appContext, Uri.parse(game.path))
?.exists()

View file

@ -765,8 +765,9 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context
// If we have API access, calculate the safe area to draw the overlay
var cutoutLeft = 0
var cutoutBottom = 0
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
val insets = context.windowManager.currentWindowMetrics.windowInsets.displayCutout
if (insets != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
if (insets != null) {
if (insets.boundingRectTop.bottom != 0 && insets.boundingRectTop.bottom > maxY / 2)
insets.boundingRectTop.bottom.toFloat() else maxY
if (insets.boundingRectRight.left != 0 && insets.boundingRectRight.left > maxX / 2)
@ -778,6 +779,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context
cutoutLeft = insets.boundingRectRight.right - insets.boundingRectRight.left
cutoutBottom = insets.boundingRectTop.top - insets.boundingRectTop.bottom
}
}
// This makes sure that if we have an inset on one side of the screen, we mirror it on
// the other side. Since removing space from one of the max values messes with the scale,

View file

@ -6,7 +6,6 @@ package org.yuzu.yuzu_emu.utils
import android.content.SharedPreferences
import android.net.Uri
import androidx.preference.PreferenceManager
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import org.yuzu.yuzu_emu.NativeLibrary
@ -83,7 +82,8 @@ object GameHelper {
NativeLibrary.getRegions(filePath),
filePath,
gameId,
NativeLibrary.getCompany(filePath)
NativeLibrary.getCompany(filePath),
NativeLibrary.isHomebrew(filePath)
)
val addedTime = preferences.getLong(newGame.keyAddedToLibraryTime, 0L)

View file

@ -13,8 +13,7 @@
#include <android/api-level.h>
#include <android/native_window_jni.h>
#include <network/room_member.h>
#include <network/network.h>
#include <core/loader/nro.h>
#include "common/detached_tasks.h"
#include "common/dynamic_library.h"
@ -95,14 +94,6 @@ public:
m_native_window = native_window;
}
u32 ScreenRotation() const {
return m_screen_rotation;
}
void SetScreenRotation(u32 screen_rotation) {
m_screen_rotation = screen_rotation;
}
void InitializeGpuDriver(const std::string& hook_lib_dir, const std::string& custom_driver_dir,
const std::string& custom_driver_name,
const std::string& file_redirect_dir) {
@ -283,6 +274,10 @@ public:
return GetRomMetadata(path).icon;
}
bool GetIsHomebrew(const std::string& path) {
return GetRomMetadata(path).isHomebrew;
}
void ResetRomMetadata() {
m_rom_metadata_cache.clear();
}
@ -346,33 +341,11 @@ public:
return m_software_keyboard;
}
void DirectConnectToRoom(const std::string& nickname, const char* server_addr = "127.0.0.1",
u16 server_port = Network::DefaultRoomPort,
const std::string& password = "") {
auto room_network = m_system.GetRoomNetwork();
if (const auto member = room_network.GetRoomMember().lock()) {
// Prevent the user from trying to join a room while they are already joining.
if (member->GetState() == Network::RoomMember::State::Joining || member->IsConnected()) {
return;
} else {
member->Join(nickname, server_addr, server_port);
}
}
}
Network::RoomMember::State GetRoomMemberState() {
if (const auto member = m_system.GetRoomNetwork().GetRoomMember().lock()) {
return member->GetState();
} else {
return Network::RoomMember::State::Idle;
}
}
private:
struct RomMetadata {
std::string title;
std::vector<u8> icon;
bool isHomebrew;
};
RomMetadata GetRomMetadata(const std::string& path) {
@ -385,11 +358,17 @@ private:
RomMetadata CacheRomMetadata(const std::string& path) {
const auto file = Core::GetGameFileFromPath(m_vfs, path);
const auto loader = Loader::GetLoader(EmulationSession::GetInstance().System(), file, 0, 0);
auto loader = Loader::GetLoader(EmulationSession::GetInstance().System(), file, 0, 0);
RomMetadata entry;
loader->ReadTitle(entry.title);
loader->ReadIcon(entry.icon);
if (loader->GetFileType() == Loader::FileType::NRO) {
auto loader_nro = dynamic_cast<Loader::AppLoader_NRO*>(loader.get());
entry.isHomebrew = loader_nro->IsHomebrew();
} else {
entry.isHomebrew = false;
}
m_rom_metadata_cache[path] = entry;
@ -413,7 +392,6 @@ private:
// Window management
std::unique_ptr<EmuWindow_Android> m_window;
ANativeWindow* m_native_window{};
u32 m_screen_rotation{};
// Core emulation
Core::System m_system;
@ -439,10 +417,6 @@ private:
} // Anonymous namespace
u32 GetAndroidScreenRotation() {
return EmulationSession::GetInstance().ScreenRotation();
}
static Core::SystemResultStatus RunEmulation(const std::string& filepath) {
Common::Log::Initialize();
Common::Log::SetColorConsoleBackendEnabled(true);
@ -486,13 +460,6 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_surfaceDestroyed(JNIEnv* env,
EmulationSession::GetInstance().SurfaceChanged();
}
void Java_org_yuzu_yuzu_1emu_NativeLibrary_notifyOrientationChange(JNIEnv* env,
[[maybe_unused]] jclass clazz,
jint layout_option,
jint rotation) {
return EmulationSession::GetInstance().SetScreenRotation(static_cast<u32>(rotation));
}
void Java_org_yuzu_yuzu_1emu_NativeLibrary_setAppDirectory(JNIEnv* env,
[[maybe_unused]] jclass clazz,
jstring j_directory) {
@ -687,6 +654,12 @@ jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getCompany([[maybe_unused]] JNIEnv
return env->NewStringUTF("");
}
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isHomebrew([[maybe_unused]] JNIEnv* env,
[[maybe_unused]] jclass clazz,
[[maybe_unused]] jstring j_filename) {
return EmulationSession::GetInstance().GetIsHomebrew(GetJString(env, j_filename));
}
void Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeEmulation
[[maybe_unused]] (JNIEnv* env, [[maybe_unused]] jclass clazz) {
// Create the default config.ini.
@ -796,45 +769,4 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_submitInlineKeyboardInput(JNIEnv* env
EmulationSession::GetInstance().SoftwareKeyboard()->SubmitInlineKeyboardInput(j_key_code);
}
void Java_org_yuzu_yuzu_1emu_NativeLibrary_connectToRoom(JNIEnv* env, jclass clazz,
jstring nickname,
jstring server_addr,
jint server_port,
jstring password) {
EmulationSession::GetInstance().DirectConnectToRoom(
GetJString(env, nickname),
GetJString(env, server_addr).c_str(),
server_port,
GetJString(env, password)
);
}
jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getRoomMemberState(JNIEnv* env, jclass clazz) {
auto state = EmulationSession::GetInstance().GetRoomMemberState();
std::string state_str{};
switch(state) {
using State = Network::RoomMember::State;
case State::Uninitialized:
state_str = "Uninitialized";
break;
case State::Idle:
state_str = "Idle";
break;
case State::Joining:
state_str = "Joining";
break;
case State::Joined:
state_str = "Joined";
break;
case State::Moderator:
state_str = "Moderator";
break;
}
return env->NewStringUTF(state_str.c_str());
}
} // extern "C"

View file

@ -33,7 +33,8 @@ static_assert(sizeof(NroSegmentHeader) == 0x8, "NroSegmentHeader has incorrect s
struct NroHeader {
INSERT_PADDING_BYTES(0x4);
u32_le module_header_offset;
INSERT_PADDING_BYTES(0x8);
u32 magic_ext1;
u32 magic_ext2;
u32_le magic;
INSERT_PADDING_BYTES(0x4);
u32_le file_size;
@ -124,6 +125,16 @@ FileType AppLoader_NRO::IdentifyType(const FileSys::VirtualFile& nro_file) {
return FileType::Error;
}
bool AppLoader_NRO::IsHomebrew() {
// Read NSO header
NroHeader nro_header{};
if (sizeof(NroHeader) != file->ReadObject(&nro_header)) {
return false;
}
return nro_header.magic_ext1 == Common::MakeMagic('H', 'O', 'M', 'E') &&
nro_header.magic_ext2 == Common::MakeMagic('B', 'R', 'E', 'W');
}
static constexpr u32 PageAlignSize(u32 size) {
return static_cast<u32>((size + Core::Memory::YUZU_PAGEMASK) & ~Core::Memory::YUZU_PAGEMASK);
}

View file

@ -38,6 +38,8 @@ public:
*/
static FileType IdentifyType(const FileSys::VirtualFile& nro_file);
bool IsHomebrew();
FileType GetFileType() const override {
return IdentifyType(file);
}

View file

@ -37,10 +37,6 @@
#include "video_core/vulkan_common/vulkan_memory_allocator.h"
#include "video_core/vulkan_common/vulkan_wrapper.h"
#ifdef ANDROID
extern u32 GetAndroidScreenRotation();
#endif
namespace Vulkan {
namespace {
@ -78,47 +74,6 @@ struct ScreenRectVertex {
}
};
#ifdef ANDROID
std::array<f32, 4 * 4> MakeOrthographicMatrix(f32 width, f32 height) {
constexpr u32 ROTATION_0 = 0;
constexpr u32 ROTATION_90 = 1;
constexpr u32 ROTATION_180 = 2;
constexpr u32 ROTATION_270 = 3;
// clang-format off
switch (GetAndroidScreenRotation()) {
case ROTATION_0:
// Desktop
return { 2.f / width, 0.f, 0.f, 0.f,
0.f, 2.f / height, 0.f, 0.f,
0.f, 0.f, 1.f, 0.f,
-1.f, -1.f, 0.f, 1.f};
case ROTATION_180:
// Reverse desktop
return {-2.f / width, 0.f, 0.f, 0.f,
0.f, -2.f / height, 0.f, 0.f,
0.f, 0.f, 1.f, 0.f,
1.f, 1.f, 0.f, 1.f};
case ROTATION_270:
// Reverse landscape
return { 0.f, -2.f / width, 0.f, 0.f,
2.f / height, 0.f, 0.f, 0.f,
0.f, 0.f, 1.f, 0.f,
-1.f, 1.f, 0.f, 1.f};
case ROTATION_90:
default:
// Landscape
return { 0.f, 2.f / width, 0.f, 0.f,
-2.f / height, 0.f, 0.f, 0.f,
0.f, 0.f, 1.f, 0.f,
1.f, -1.f, 0.f, 1.f};
}
// clang-format on
}
#else
std::array<f32, 4 * 4> MakeOrthographicMatrix(f32 width, f32 height) {
// clang-format off
return { 2.f / width, 0.f, 0.f, 0.f,
@ -128,8 +83,6 @@ std::array<f32, 4 * 4> MakeOrthographicMatrix(f32 width, f32 height) {
// clang-format on
}
#endif
u32 GetBytesPerPixel(const Tegra::FramebufferConfig& framebuffer) {
using namespace VideoCore::Surface;
return BytesPerBlock(PixelFormatFromGPUPixelFormat(framebuffer.pixel_format));
@ -1159,7 +1112,7 @@ void BlitScreen::CreateRawImages(const Tegra::FramebufferConfig& framebuffer) {
.pNext = nullptr,
.flags = 0,
.imageType = VK_IMAGE_TYPE_2D,
.format = GetFormat(framebuffer),
.format = used_on_framebuffer ? VK_FORMAT_R16G16B16A16_SFLOAT : GetFormat(framebuffer),
.extent =
{
.width = (up_scale * framebuffer.width) >> down_shift,
@ -1180,14 +1133,14 @@ void BlitScreen::CreateRawImages(const Tegra::FramebufferConfig& framebuffer) {
const auto create_commit = [&](vk::Image& image) {
return memory_allocator.Commit(image, MemoryUsage::DeviceLocal);
};
const auto create_image_view = [&](vk::Image& image) {
const auto create_image_view = [&](vk::Image& image, bool used_on_framebuffer = false) {
return device.GetLogical().CreateImageView(VkImageViewCreateInfo{
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.image = *image,
.viewType = VK_IMAGE_VIEW_TYPE_2D,
.format = GetFormat(framebuffer),
.format = used_on_framebuffer ? VK_FORMAT_R16G16B16A16_SFLOAT : GetFormat(framebuffer),
.components =
{
.r = VK_COMPONENT_SWIZZLE_IDENTITY,
@ -1217,7 +1170,7 @@ void BlitScreen::CreateRawImages(const Tegra::FramebufferConfig& framebuffer) {
const u32 down_shift = Settings::values.resolution_info.down_shift;
aa_image = create_image(true, up_scale, down_shift);
aa_commit = create_commit(aa_image);
aa_image_view = create_image_view(aa_image);
aa_image_view = create_image_view(aa_image, true);
VkExtent2D size{
.width = (up_scale * framebuffer.width) >> down_shift,
.height = (up_scale * framebuffer.height) >> down_shift,

View file

@ -231,7 +231,12 @@ void Swapchain::CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities, bo
.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE,
.queueFamilyIndexCount = 0,
.pQueueFamilyIndices = nullptr,
#ifdef ANDROID
// On Android, do not allow surface rotation to deviate from the frontend.
.preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR,
#else
.preTransform = capabilities.currentTransform,
#endif
.compositeAlpha = alpha_flags,
.presentMode = present_mode,
.clipped = VK_FALSE,

View file

@ -760,6 +760,7 @@ void Config::ReadRendererValues() {
ReadGlobalSetting(Settings::values.use_fast_gpu_time);
ReadGlobalSetting(Settings::values.use_vulkan_driver_pipeline_cache);
ReadGlobalSetting(Settings::values.enable_compute_pipelines);
ReadGlobalSetting(Settings::values.use_video_framerate);
ReadGlobalSetting(Settings::values.bg_red);
ReadGlobalSetting(Settings::values.bg_green);
ReadGlobalSetting(Settings::values.bg_blue);
@ -1415,6 +1416,7 @@ void Config::SaveRendererValues() {
WriteGlobalSetting(Settings::values.use_fast_gpu_time);
WriteGlobalSetting(Settings::values.use_vulkan_driver_pipeline_cache);
WriteGlobalSetting(Settings::values.enable_compute_pipelines);
WriteGlobalSetting(Settings::values.use_video_framerate);
WriteGlobalSetting(Settings::values.bg_red);
WriteGlobalSetting(Settings::values.bg_green);
WriteGlobalSetting(Settings::values.bg_blue);