mirror of
https://git.eden-emu.dev/eden-emu/eden
synced 2026-04-26 11:39:01 +02:00
sudachi ios stuff
This commit is contained in:
parent
ac181b756f
commit
feaedd02f7
14 changed files with 2644 additions and 0 deletions
11
src/ios/Eden/AppUI-Bridging-Header.h
Normal file
11
src/ios/Eden/AppUI-Bridging-Header.h
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
//
|
||||||
|
// AppUI-Bridging-Header.h - Sudachi
|
||||||
|
// Created by Jarrod Norwell on 4/3/2024.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef AppUI_Bridging_Header_h
|
||||||
|
#define AppUI_Bridging_Header_h
|
||||||
|
|
||||||
|
#import "Wrapper/AppUIObjC.h"
|
||||||
|
|
||||||
|
#endif /* AppUI_Bridging_Header_h */
|
||||||
108
src/ios/Eden/AppUI.swift
Normal file
108
src/ios/Eden/AppUI.swift
Normal file
|
|
@ -0,0 +1,108 @@
|
||||||
|
//
|
||||||
|
// AppUI.swift - Sudachi
|
||||||
|
// Created by Jarrod Norwell on 4/3/2024.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import QuartzCore.CAMetalLayer
|
||||||
|
|
||||||
|
public struct AppUI {
|
||||||
|
|
||||||
|
public static let shared = AppUI()
|
||||||
|
|
||||||
|
fileprivate let appUIObjC = AppUIObjC.shared()
|
||||||
|
|
||||||
|
public func configure(layer: CAMetalLayer, with size: CGSize) {
|
||||||
|
appUIObjC.configure(layer: layer, with: size)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func information(for url: URL) -> AppUIInformation {
|
||||||
|
appUIObjC.gameInformation.information(for: url)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func insert(game url: URL) {
|
||||||
|
appUIObjC.insert(game: url)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func insert(games urls: [URL]) {
|
||||||
|
appUIObjC.insert(games: urls)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func bootOS() {
|
||||||
|
appUIObjC.bootOS()
|
||||||
|
}
|
||||||
|
|
||||||
|
public func pause() {
|
||||||
|
appUIObjC.pause()
|
||||||
|
}
|
||||||
|
|
||||||
|
public func play() {
|
||||||
|
appUIObjC.play()
|
||||||
|
}
|
||||||
|
|
||||||
|
public func ispaused() -> Bool {
|
||||||
|
return appUIObjC.ispaused()
|
||||||
|
}
|
||||||
|
|
||||||
|
public func FirstFrameShowed() -> Bool {
|
||||||
|
return appUIObjC.hasfirstfame()
|
||||||
|
}
|
||||||
|
|
||||||
|
public func canGetFullPath() -> Bool {
|
||||||
|
return appUIObjC.canGetFullPath()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public func exit() {
|
||||||
|
appUIObjC.quit()
|
||||||
|
}
|
||||||
|
|
||||||
|
public func step() {
|
||||||
|
appUIObjC.step()
|
||||||
|
}
|
||||||
|
|
||||||
|
public func orientationChanged(orientation: UIInterfaceOrientation, with layer: CAMetalLayer, size: CGSize) {
|
||||||
|
appUIObjC.orientationChanged(orientation: orientation, with: layer, size: size)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func touchBegan(at point: CGPoint, for index: UInt) {
|
||||||
|
appUIObjC.touchBegan(at: point, for: index)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func touchEnded(for index: UInt) {
|
||||||
|
appUIObjC.touchEnded(for: index)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func touchMoved(at point: CGPoint, for index: UInt) {
|
||||||
|
appUIObjC.touchMoved(at: point, for: index)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func gyroMoved(x: Float, y: Float, z: Float, accelX: Float, accelY: Float, accelZ: Float, controllerId: Int32, deltaTimestamp: Int32) {
|
||||||
|
// Calling the Objective-C function with both gyroscope and accelerometer data
|
||||||
|
appUIObjC.virtualControllerGyro(controllerId,
|
||||||
|
deltaTimestamp: deltaTimestamp,
|
||||||
|
gyroX: x,
|
||||||
|
gyroY: y,
|
||||||
|
gyroZ: z,
|
||||||
|
accelX: accelX,
|
||||||
|
accelY: accelY,
|
||||||
|
accelZ: accelZ)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public func thumbstickMoved(analog: VirtualControllerAnalogType, x: Float, y: Float, controllerid: Int) {
|
||||||
|
appUIObjC.thumbstickMoved(analog, x: CGFloat(x), y: CGFloat(y), controllerId: Int32(controllerid))
|
||||||
|
}
|
||||||
|
|
||||||
|
public func virtualControllerButtonDown(button: VirtualControllerButtonType, controllerid: Int) {
|
||||||
|
appUIObjC.virtualControllerButtonDown(button, controllerId: Int32(controllerid))
|
||||||
|
}
|
||||||
|
|
||||||
|
public func virtualControllerButtonUp(button: VirtualControllerButtonType, controllerid: Int) {
|
||||||
|
appUIObjC.virtualControllerButtonUp(button, controllerId: Int32(controllerid))
|
||||||
|
}
|
||||||
|
|
||||||
|
public func settingsSaved() {
|
||||||
|
appUIObjC.settingsChanged()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
//
|
||||||
|
// AppUIGameInformation.h - Sudachi
|
||||||
|
// Created by Jarrod Norwell on 1/20/24.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
@interface AppUIInformation : NSObject
|
||||||
|
@property (nonatomic, strong) NSString *developer;
|
||||||
|
@property (nonatomic, strong) NSData *iconData;
|
||||||
|
@property (nonatomic) BOOL isHomebrew;
|
||||||
|
@property (nonatomic) uint64_t programID;
|
||||||
|
@property (nonatomic, strong) NSString *title, *version;
|
||||||
|
|
||||||
|
-(AppUIInformation *) initWithDeveloper:(NSString *)developer iconData:(NSData *)iconData isHomebrew:(BOOL)isHomebrew programID:(uint64_t)programID title:(NSString *)title version:(NSString *)version;
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface AppUIGameInformation : NSObject
|
||||||
|
+(AppUIGameInformation *) sharedInstance NS_SWIFT_NAME(shared());
|
||||||
|
|
||||||
|
-(AppUIInformation *) informationForGame:(NSURL *)url NS_SWIFT_NAME(information(for:));
|
||||||
|
@end
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
||||||
|
|
@ -0,0 +1,434 @@
|
||||||
|
//
|
||||||
|
// AppUIGameInformation.mm - Sudachi
|
||||||
|
// Created by Jarrod Norwell on 1/20/24.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "AppUIGameInformation.h"
|
||||||
|
#import "../DirectoryManager/DirectoryManager.h"
|
||||||
|
#import "../EmulationSession/EmulationSession.h"
|
||||||
|
|
||||||
|
#include "common/fs/fs.h"
|
||||||
|
#include "common/fs/path_util.h"
|
||||||
|
#include "core/core.h"
|
||||||
|
#include "core/file_sys/fs_filesystem.h"
|
||||||
|
#include "core/file_sys/patch_manager.h"
|
||||||
|
#include "core/loader/loader.h"
|
||||||
|
#include "core/loader/nro.h"
|
||||||
|
#include "frontend_common/yuzu_config.h"
|
||||||
|
|
||||||
|
struct GameMetadata {
|
||||||
|
std::string title;
|
||||||
|
u64 programId;
|
||||||
|
std::string developer;
|
||||||
|
std::string version;
|
||||||
|
std::vector<u8> icon;
|
||||||
|
bool isHomebrew;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class SdlConfig final : public YuzuConfig {
|
||||||
|
public:
|
||||||
|
explicit SdlConfig(std::optional<std::string> config_path);
|
||||||
|
~SdlConfig() override;
|
||||||
|
|
||||||
|
void ReloadAllValues() override;
|
||||||
|
void SaveAllValues() override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void ReadSdlValues();
|
||||||
|
void ReadSdlPlayerValues(std::size_t player_index);
|
||||||
|
void ReadSdlControlValues();
|
||||||
|
void ReadHidbusValues() override;
|
||||||
|
void ReadDebugControlValues() override;
|
||||||
|
void ReadPathValues() override {}
|
||||||
|
void ReadShortcutValues() override {}
|
||||||
|
void ReadUIValues() override {}
|
||||||
|
void ReadUIGamelistValues() override {}
|
||||||
|
void ReadUILayoutValues() override {}
|
||||||
|
void ReadMultiplayerValues() override {}
|
||||||
|
|
||||||
|
void SaveSdlValues();
|
||||||
|
void SaveSdlPlayerValues(std::size_t player_index);
|
||||||
|
void SaveSdlControlValues();
|
||||||
|
void SaveHidbusValues() override;
|
||||||
|
void SaveDebugControlValues() override;
|
||||||
|
void SavePathValues() override {}
|
||||||
|
void SaveShortcutValues() override {}
|
||||||
|
void SaveUIValues() override {}
|
||||||
|
void SaveUIGamelistValues() override {}
|
||||||
|
void SaveUILayoutValues() override {}
|
||||||
|
void SaveMultiplayerValues() override {}
|
||||||
|
|
||||||
|
std::vector<YuzuSettings::BasicSetting*>& FindRelevantList(YuzuSettings::Category category) override;
|
||||||
|
|
||||||
|
public:
|
||||||
|
static const std::array<int, YuzuSettings::NativeButton::NumButtons> default_buttons;
|
||||||
|
static const std::array<int, YuzuSettings::NativeMotion::NumMotions> default_motions;
|
||||||
|
static const std::array<std::array<int, 4>, YuzuSettings::NativeAnalog::NumAnalogs> default_analogs;
|
||||||
|
static const std::array<int, 2> default_stick_mod;
|
||||||
|
static const std::array<int, 2> default_ringcon_analogs;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#define SDL_MAIN_HANDLED
|
||||||
|
#include <SDL.h>
|
||||||
|
|
||||||
|
#include "common/logging/log.h"
|
||||||
|
#include "input_common/main.h"
|
||||||
|
|
||||||
|
const std::array<int, YuzuSettings::NativeButton::NumButtons> SdlConfig::default_buttons = {
|
||||||
|
SDL_SCANCODE_A, SDL_SCANCODE_S, SDL_SCANCODE_Z, SDL_SCANCODE_X, SDL_SCANCODE_T,
|
||||||
|
SDL_SCANCODE_G, SDL_SCANCODE_F, SDL_SCANCODE_H, SDL_SCANCODE_Q, SDL_SCANCODE_W,
|
||||||
|
SDL_SCANCODE_M, SDL_SCANCODE_N, SDL_SCANCODE_1, SDL_SCANCODE_2, SDL_SCANCODE_B,
|
||||||
|
};
|
||||||
|
|
||||||
|
const std::array<int, YuzuSettings::NativeMotion::NumMotions> SdlConfig::default_motions = {
|
||||||
|
SDL_SCANCODE_7,
|
||||||
|
SDL_SCANCODE_8,
|
||||||
|
};
|
||||||
|
|
||||||
|
const std::array<std::array<int, 4>, YuzuSettings::NativeAnalog::NumAnalogs> SdlConfig::default_analogs{
|
||||||
|
{
|
||||||
|
{
|
||||||
|
SDL_SCANCODE_UP,
|
||||||
|
SDL_SCANCODE_DOWN,
|
||||||
|
SDL_SCANCODE_LEFT,
|
||||||
|
SDL_SCANCODE_RIGHT,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
SDL_SCANCODE_I,
|
||||||
|
SDL_SCANCODE_K,
|
||||||
|
SDL_SCANCODE_J,
|
||||||
|
SDL_SCANCODE_L,
|
||||||
|
},
|
||||||
|
}};
|
||||||
|
|
||||||
|
const std::array<int, 2> SdlConfig::default_stick_mod = {
|
||||||
|
SDL_SCANCODE_D,
|
||||||
|
0,
|
||||||
|
};
|
||||||
|
|
||||||
|
const std::array<int, 2> SdlConfig::default_ringcon_analogs{{
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
}};
|
||||||
|
|
||||||
|
SdlConfig::SdlConfig(const std::optional<std::string> config_path) {
|
||||||
|
Initialize(config_path);
|
||||||
|
ReadSdlValues();
|
||||||
|
SaveSdlValues();
|
||||||
|
}
|
||||||
|
|
||||||
|
SdlConfig::~SdlConfig() {
|
||||||
|
if (global) {
|
||||||
|
SdlConfig::SaveAllValues();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SdlConfig::ReloadAllValues() {
|
||||||
|
Reload();
|
||||||
|
ReadSdlValues();
|
||||||
|
SaveSdlValues();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SdlConfig::SaveAllValues() {
|
||||||
|
SaveValues();
|
||||||
|
SaveSdlValues();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SdlConfig::ReadSdlValues() {
|
||||||
|
ReadSdlControlValues();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SdlConfig::ReadSdlControlValues() {
|
||||||
|
BeginGroup(YuzuSettings::TranslateCategory(YuzuSettings::Category::Controls));
|
||||||
|
|
||||||
|
YuzuSettings::values.players.SetGlobal(!IsCustomConfig());
|
||||||
|
for (std::size_t p = 0; p < YuzuSettings::values.players.GetValue().size(); ++p) {
|
||||||
|
ReadSdlPlayerValues(p);
|
||||||
|
}
|
||||||
|
if (IsCustomConfig()) {
|
||||||
|
EndGroup();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ReadDebugControlValues();
|
||||||
|
ReadHidbusValues();
|
||||||
|
|
||||||
|
EndGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SdlConfig::ReadSdlPlayerValues(const std::size_t player_index) {
|
||||||
|
std::string player_prefix;
|
||||||
|
if (type != ConfigType::InputProfile) {
|
||||||
|
player_prefix.append("player_").append(ToString(player_index)).append("_");
|
||||||
|
}
|
||||||
|
|
||||||
|
auto& player = YuzuSettings::values.players.GetValue()[player_index];
|
||||||
|
if (IsCustomConfig()) {
|
||||||
|
const auto profile_name =
|
||||||
|
ReadStringSetting(std::string(player_prefix).append("profile_name"));
|
||||||
|
if (profile_name.empty()) {
|
||||||
|
// Use the global input config
|
||||||
|
player = YuzuSettings::values.players.GetValue(true)[player_index];
|
||||||
|
player.profile_name = "";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < YuzuSettings::NativeButton::NumButtons; ++i) {
|
||||||
|
const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
|
||||||
|
auto& player_buttons = player.buttons[i];
|
||||||
|
|
||||||
|
player_buttons = ReadStringSetting(
|
||||||
|
std::string(player_prefix).append(YuzuSettings::NativeButton::mapping[i]), default_param);
|
||||||
|
if (player_buttons.empty()) {
|
||||||
|
player_buttons = default_param;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < YuzuSettings::NativeAnalog::NumAnalogs; ++i) {
|
||||||
|
const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
|
||||||
|
default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
|
||||||
|
default_analogs[i][3], default_stick_mod[i], 0.5f);
|
||||||
|
auto& player_analogs = player.analogs[i];
|
||||||
|
|
||||||
|
player_analogs = ReadStringSetting(
|
||||||
|
std::string(player_prefix).append(YuzuSettings::NativeAnalog::mapping[i]), default_param);
|
||||||
|
if (player_analogs.empty()) {
|
||||||
|
player_analogs = default_param;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < YuzuSettings::NativeMotion::NumMotions; ++i) {
|
||||||
|
const std::string default_param = InputCommon::GenerateKeyboardParam(default_motions[i]);
|
||||||
|
auto& player_motions = player.motions[i];
|
||||||
|
|
||||||
|
player_motions = ReadStringSetting(
|
||||||
|
std::string(player_prefix).append(YuzuSettings::NativeMotion::mapping[i]), default_param);
|
||||||
|
if (player_motions.empty()) {
|
||||||
|
player_motions = default_param;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SdlConfig::ReadDebugControlValues() {
|
||||||
|
for (int i = 0; i < YuzuSettings::NativeButton::NumButtons; ++i) {
|
||||||
|
const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
|
||||||
|
auto& debug_pad_buttons = YuzuSettings::values.debug_pad_buttons[i];
|
||||||
|
debug_pad_buttons = ReadStringSetting(
|
||||||
|
std::string("debug_pad_").append(YuzuSettings::NativeButton::mapping[i]), default_param);
|
||||||
|
if (debug_pad_buttons.empty()) {
|
||||||
|
debug_pad_buttons = default_param;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int i = 0; i < YuzuSettings::NativeAnalog::NumAnalogs; ++i) {
|
||||||
|
const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
|
||||||
|
default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
|
||||||
|
default_analogs[i][3], default_stick_mod[i], 0.5f);
|
||||||
|
auto& debug_pad_analogs = YuzuSettings::values.debug_pad_analogs[i];
|
||||||
|
debug_pad_analogs = ReadStringSetting(
|
||||||
|
std::string("debug_pad_").append(YuzuSettings::NativeAnalog::mapping[i]), default_param);
|
||||||
|
if (debug_pad_analogs.empty()) {
|
||||||
|
debug_pad_analogs = default_param;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SdlConfig::ReadHidbusValues() {
|
||||||
|
const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
|
||||||
|
0, 0, default_ringcon_analogs[0], default_ringcon_analogs[1], 0, 0.05f);
|
||||||
|
auto& ringcon_analogs = YuzuSettings::values.ringcon_analogs;
|
||||||
|
|
||||||
|
ringcon_analogs = ReadStringSetting(std::string("ring_controller"), default_param);
|
||||||
|
if (ringcon_analogs.empty()) {
|
||||||
|
ringcon_analogs = default_param;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SdlConfig::SaveSdlValues() {
|
||||||
|
LOG_DEBUG(Config, "Saving SDL configuration values");
|
||||||
|
SaveSdlControlValues();
|
||||||
|
|
||||||
|
WriteToIni();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SdlConfig::SaveSdlControlValues() {
|
||||||
|
BeginGroup(YuzuSettings::TranslateCategory(YuzuSettings::Category::Controls));
|
||||||
|
|
||||||
|
YuzuSettings::values.players.SetGlobal(!IsCustomConfig());
|
||||||
|
for (std::size_t p = 0; p < YuzuSettings::values.players.GetValue().size(); ++p) {
|
||||||
|
SaveSdlPlayerValues(p);
|
||||||
|
}
|
||||||
|
if (IsCustomConfig()) {
|
||||||
|
EndGroup();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
SaveDebugControlValues();
|
||||||
|
SaveHidbusValues();
|
||||||
|
|
||||||
|
EndGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SdlConfig::SaveSdlPlayerValues(const std::size_t player_index) {
|
||||||
|
std::string player_prefix;
|
||||||
|
if (type != ConfigType::InputProfile) {
|
||||||
|
player_prefix = std::string("player_").append(ToString(player_index)).append("_");
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& player = YuzuSettings::values.players.GetValue()[player_index];
|
||||||
|
if (IsCustomConfig() && player.profile_name.empty()) {
|
||||||
|
// No custom profile selected
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < YuzuSettings::NativeButton::NumButtons; ++i) {
|
||||||
|
const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
|
||||||
|
WriteStringSetting(std::string(player_prefix).append(YuzuSettings::NativeButton::mapping[i]),
|
||||||
|
player.buttons[i], std::make_optional(default_param));
|
||||||
|
}
|
||||||
|
for (int i = 0; i < YuzuSettings::NativeAnalog::NumAnalogs; ++i) {
|
||||||
|
const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
|
||||||
|
default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
|
||||||
|
default_analogs[i][3], default_stick_mod[i], 0.5f);
|
||||||
|
WriteStringSetting(std::string(player_prefix).append(YuzuSettings::NativeAnalog::mapping[i]),
|
||||||
|
player.analogs[i], std::make_optional(default_param));
|
||||||
|
}
|
||||||
|
for (int i = 0; i < YuzuSettings::NativeMotion::NumMotions; ++i) {
|
||||||
|
const std::string default_param = InputCommon::GenerateKeyboardParam(default_motions[i]);
|
||||||
|
WriteStringSetting(std::string(player_prefix).append(YuzuSettings::NativeMotion::mapping[i]),
|
||||||
|
player.motions[i], std::make_optional(default_param));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SdlConfig::SaveDebugControlValues() {
|
||||||
|
for (int i = 0; i < YuzuSettings::NativeButton::NumButtons; ++i) {
|
||||||
|
const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
|
||||||
|
WriteStringSetting(std::string("debug_pad_").append(YuzuSettings::NativeButton::mapping[i]),
|
||||||
|
YuzuSettings::values.debug_pad_buttons[i],
|
||||||
|
std::make_optional(default_param));
|
||||||
|
}
|
||||||
|
for (int i = 0; i < YuzuSettings::NativeAnalog::NumAnalogs; ++i) {
|
||||||
|
const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
|
||||||
|
default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
|
||||||
|
default_analogs[i][3], default_stick_mod[i], 0.5f);
|
||||||
|
WriteStringSetting(std::string("debug_pad_").append(YuzuSettings::NativeAnalog::mapping[i]),
|
||||||
|
YuzuSettings::values.debug_pad_analogs[i],
|
||||||
|
std::make_optional(default_param));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SdlConfig::SaveHidbusValues() {
|
||||||
|
const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
|
||||||
|
0, 0, default_ringcon_analogs[0], default_ringcon_analogs[1], 0, 0.05f);
|
||||||
|
WriteStringSetting(std::string("ring_controller"), YuzuSettings::values.ringcon_analogs,
|
||||||
|
std::make_optional(default_param));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<YuzuSettings::BasicSetting*>& SdlConfig::FindRelevantList(YuzuSettings::Category category) {
|
||||||
|
return YuzuSettings::values.linkage.by_category[category];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
std::unordered_map<std::string, GameMetadata> m_game_metadata_cache;
|
||||||
|
|
||||||
|
GameMetadata CacheGameMetadata(const std::string& path) {
|
||||||
|
const auto file =
|
||||||
|
Core::GetGameFileFromPath(EmulationSession::GetInstance().System().GetFilesystem(), path);
|
||||||
|
auto loader = Loader::GetLoader(EmulationSession::GetInstance().System(), file, 0, 0);
|
||||||
|
|
||||||
|
GameMetadata entry;
|
||||||
|
loader->ReadTitle(entry.title);
|
||||||
|
loader->ReadProgramId(entry.programId);
|
||||||
|
loader->ReadIcon(entry.icon);
|
||||||
|
|
||||||
|
const FileSys::PatchManager pm{
|
||||||
|
entry.programId, EmulationSession::GetInstance().System().GetFileSystemController(),
|
||||||
|
EmulationSession::GetInstance().System().GetContentProvider()};
|
||||||
|
const auto control = pm.GetControlMetadata();
|
||||||
|
|
||||||
|
if (control.first != nullptr) {
|
||||||
|
entry.developer = control.first->GetDeveloperName();
|
||||||
|
entry.version = control.first->GetVersionString();
|
||||||
|
} else {
|
||||||
|
FileSys::NACP nacp;
|
||||||
|
if (loader->ReadControlData(nacp) == Loader::ResultStatus::Success) {
|
||||||
|
entry.developer = nacp.GetDeveloperName();
|
||||||
|
} else {
|
||||||
|
entry.developer = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
entry.version = "1.0.0";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (loader->GetFileType() == Loader::FileType::NRO) {
|
||||||
|
auto loader_nro = reinterpret_cast<Loader::AppLoader_NRO*>(loader.get());
|
||||||
|
entry.isHomebrew = loader_nro->IsHomebrew();
|
||||||
|
} else {
|
||||||
|
entry.isHomebrew = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_game_metadata_cache[path] = entry;
|
||||||
|
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
GameMetadata GameMetadata(const std::string& path, bool reload = false) {
|
||||||
|
if (!EmulationSession::GetInstance().IsInitialized()) {
|
||||||
|
Common::FS::SetAppDirectory(DirectoryManager::AppUIDirectory());
|
||||||
|
|
||||||
|
EmulationSession::GetInstance().System().Initialize();
|
||||||
|
EmulationSession::GetInstance().InitializeSystem(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reload) {
|
||||||
|
return CacheGameMetadata(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (auto search = m_game_metadata_cache.find(path); search != m_game_metadata_cache.end()) {
|
||||||
|
return search->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
return CacheGameMetadata(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@implementation AppUIInformation
|
||||||
|
-(AppUIInformation *) initWithDeveloper:(NSString *)developer iconData:(NSData *)iconData isHomebrew:(BOOL)isHomebrew programID:(uint64_t)programID
|
||||||
|
title:(NSString *)title version:(NSString *)version {
|
||||||
|
if (self = [super init]) {
|
||||||
|
self.developer = developer;
|
||||||
|
self.iconData = iconData;
|
||||||
|
self.isHomebrew = isHomebrew;
|
||||||
|
self.programID = programID;
|
||||||
|
self.title = title;
|
||||||
|
self.version = version;
|
||||||
|
} return self;
|
||||||
|
}
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation AppUIGameInformation
|
||||||
|
+(AppUIGameInformation *) sharedInstance {
|
||||||
|
static AppUIGameInformation *sharedInstance = NULL;
|
||||||
|
static dispatch_once_t onceToken;
|
||||||
|
dispatch_once(&onceToken, ^{
|
||||||
|
sharedInstance = [[self alloc] init];
|
||||||
|
});
|
||||||
|
return sharedInstance;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
-(AppUIInformation *) informationForGame:(NSURL *)url {
|
||||||
|
auto gameMetadata = GameMetadata([url.path UTF8String]);
|
||||||
|
|
||||||
|
return [[AppUIInformation alloc] initWithDeveloper:[NSString stringWithCString:gameMetadata.developer.c_str() encoding:NSUTF8StringEncoding]
|
||||||
|
iconData:[NSData dataWithBytes:gameMetadata.icon.data() length:gameMetadata.icon.size()]
|
||||||
|
isHomebrew:gameMetadata.isHomebrew programID:gameMetadata.programId
|
||||||
|
title:[NSString stringWithCString:gameMetadata.title.c_str() encoding:NSUTF8StringEncoding]
|
||||||
|
version:[NSString stringWithCString:gameMetadata.version.c_str() encoding:NSUTF8StringEncoding]];
|
||||||
|
}
|
||||||
|
@end
|
||||||
92
src/ios/Eden/Wrapper/AppUIObjC.h
Normal file
92
src/ios/Eden/Wrapper/AppUIObjC.h
Normal file
|
|
@ -0,0 +1,92 @@
|
||||||
|
//
|
||||||
|
// AppUIObjC.h - Sudachi
|
||||||
|
// Created by Jarrod Norwell on 1/8/24.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
#import <MetalKit/MetalKit.h>
|
||||||
|
|
||||||
|
#import "AppUIGameInformation/AppUIGameInformation.h"
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
typedef NS_ENUM(NSUInteger, VirtualControllerAnalogType) {
|
||||||
|
VirtualControllerAnalogTypeLeft = 0,
|
||||||
|
VirtualControllerAnalogTypeRight = 1
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef NS_ENUM(NSUInteger, VirtualControllerButtonType) {
|
||||||
|
VirtualControllerButtonTypeA = 0,
|
||||||
|
VirtualControllerButtonTypeB = 1,
|
||||||
|
VirtualControllerButtonTypeX = 2,
|
||||||
|
VirtualControllerButtonTypeY = 3,
|
||||||
|
VirtualControllerButtonTypeL = 4,
|
||||||
|
VirtualControllerButtonTypeR = 5,
|
||||||
|
VirtualControllerButtonTypeTriggerL = 6,
|
||||||
|
VirtualControllerButtonTypeTriggerR = 7,
|
||||||
|
VirtualControllerButtonTypeTriggerZL = 8,
|
||||||
|
VirtualControllerButtonTypeTriggerZR = 9,
|
||||||
|
VirtualControllerButtonTypePlus = 10,
|
||||||
|
VirtualControllerButtonTypeMinus = 11,
|
||||||
|
VirtualControllerButtonTypeDirectionalPadLeft = 12,
|
||||||
|
VirtualControllerButtonTypeDirectionalPadUp = 13,
|
||||||
|
VirtualControllerButtonTypeDirectionalPadRight = 14,
|
||||||
|
VirtualControllerButtonTypeDirectionalPadDown = 15,
|
||||||
|
VirtualControllerButtonTypeSL = 16,
|
||||||
|
VirtualControllerButtonTypeSR = 17,
|
||||||
|
VirtualControllerButtonTypeHome = 18,
|
||||||
|
VirtualControllerButtonTypeCapture = 19
|
||||||
|
};
|
||||||
|
|
||||||
|
@interface AppUIObjC : NSObject {
|
||||||
|
CAMetalLayer *_layer;
|
||||||
|
CGSize _size;
|
||||||
|
}
|
||||||
|
|
||||||
|
@property (nonatomic, strong) AppUIGameInformation *gameInformation;
|
||||||
|
|
||||||
|
+(AppUIObjC *) sharedInstance NS_SWIFT_NAME(shared());
|
||||||
|
-(void) configureLayer:(CAMetalLayer *)layer withSize:(CGSize)size NS_SWIFT_NAME(configure(layer:with:));
|
||||||
|
-(void) bootOS;
|
||||||
|
-(void) pause;
|
||||||
|
-(void) play;
|
||||||
|
-(BOOL) ispaused;
|
||||||
|
-(BOOL) canGetFullPath;
|
||||||
|
-(void) quit;
|
||||||
|
-(void) insertGame:(NSURL *)url NS_SWIFT_NAME(insert(game:));
|
||||||
|
-(void) insertGames:(NSArray<NSURL *> *)games NS_SWIFT_NAME(insert(games:));
|
||||||
|
-(void) step;
|
||||||
|
-(BOOL) hasfirstfame;
|
||||||
|
|
||||||
|
-(void) touchBeganAtPoint:(CGPoint)point index:(NSUInteger)index NS_SWIFT_NAME(touchBegan(at:for:));
|
||||||
|
-(void) touchEndedForIndex:(NSUInteger)index;
|
||||||
|
-(void) touchMovedAtPoint:(CGPoint)point index:(NSUInteger)index NS_SWIFT_NAME(touchMoved(at:for:));
|
||||||
|
|
||||||
|
-(void) thumbstickMoved:(VirtualControllerAnalogType)analog
|
||||||
|
x:(CGFloat)x
|
||||||
|
y:(CGFloat)y
|
||||||
|
controllerId:(int)controllerId;
|
||||||
|
|
||||||
|
-(void) virtualControllerGyro:(int)controllerId
|
||||||
|
deltaTimestamp:(int)delta_timestamp
|
||||||
|
gyroX:(float)gyro_x
|
||||||
|
gyroY:(float)gyro_y
|
||||||
|
gyroZ:(float)gyro_z
|
||||||
|
accelX:(float)accel_x
|
||||||
|
accelY:(float)accel_y
|
||||||
|
accelZ:(float)accel_z;
|
||||||
|
|
||||||
|
-(void) virtualControllerButtonDown:(VirtualControllerButtonType)button
|
||||||
|
controllerId:(int)controllerId;
|
||||||
|
|
||||||
|
-(void) virtualControllerButtonUp:(VirtualControllerButtonType)button
|
||||||
|
controllerId:(int)controllerId;
|
||||||
|
|
||||||
|
|
||||||
|
-(void) orientationChanged:(UIInterfaceOrientation)orientation with:(CAMetalLayer *)layer size:(CGSize)size NS_SWIFT_NAME(orientationChanged(orientation:with:size:));
|
||||||
|
|
||||||
|
-(void) settingsChanged;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
||||||
258
src/ios/Eden/Wrapper/AppUIObjC.mm
Normal file
258
src/ios/Eden/Wrapper/AppUIObjC.mm
Normal file
|
|
@ -0,0 +1,258 @@
|
||||||
|
//
|
||||||
|
// AppUIObjC.mm - Sudachi
|
||||||
|
// Created by Jarrod Norwell on 1/8/24.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "AppUIObjC.h"
|
||||||
|
|
||||||
|
#import "Config/Config.h"
|
||||||
|
#import "EmulationSession/EmulationSession.h"
|
||||||
|
#import "DirectoryManager/DirectoryManager.h"
|
||||||
|
|
||||||
|
#include "common/fs/fs.h"
|
||||||
|
#include "common/fs/path_util.h"
|
||||||
|
#include "common/settings.h"
|
||||||
|
#include "common/fs/fs.h"
|
||||||
|
#include "core/file_sys/patch_manager.h"
|
||||||
|
#include "core/file_sys/savedata_factory.h"
|
||||||
|
#include "core/loader/nro.h"
|
||||||
|
#include "frontend_common/content_manager.h"
|
||||||
|
#include "common/settings_enums.h"
|
||||||
|
#include "network/announce_multiplayer_session.h"
|
||||||
|
#include "common/announce_multiplayer_room.h"
|
||||||
|
#include "network/network.h"
|
||||||
|
|
||||||
|
#include "common/detached_tasks.h"
|
||||||
|
#include "common/dynamic_library.h"
|
||||||
|
#include "common/fs/path_util.h"
|
||||||
|
#include "common/logging/backend.h"
|
||||||
|
#include "common/logging/log.h"
|
||||||
|
#include "common/microprofile.h"
|
||||||
|
#include "common/scm_rev.h"
|
||||||
|
#include "common/scope_exit.h"
|
||||||
|
#include "common/settings.h"
|
||||||
|
#include "common/string_util.h"
|
||||||
|
#include "core/core.h"
|
||||||
|
#include "core/cpu_manager.h"
|
||||||
|
#include "core/crypto/key_manager.h"
|
||||||
|
#include "core/file_sys/card_image.h"
|
||||||
|
#include "core/file_sys/content_archive.h"
|
||||||
|
#include "core/file_sys/fs_filesystem.h"
|
||||||
|
#include "core/file_sys/submission_package.h"
|
||||||
|
#include "core/file_sys/vfs/vfs.h"
|
||||||
|
#include "core/file_sys/vfs/vfs_real.h"
|
||||||
|
#include "core/frontend/applets/cabinet.h"
|
||||||
|
#include "core/frontend/applets/controller.h"
|
||||||
|
#include "core/frontend/applets/error.h"
|
||||||
|
#include "core/frontend/applets/general.h"
|
||||||
|
#include "core/frontend/applets/mii_edit.h"
|
||||||
|
#include "core/frontend/applets/profile_select.h"
|
||||||
|
#include "core/frontend/applets/software_keyboard.h"
|
||||||
|
#include "core/frontend/applets/web_browser.h"
|
||||||
|
#include "core/hle/service/am/applet_manager.h"
|
||||||
|
#include "core/hle/service/am/frontend/applets.h"
|
||||||
|
#include "core/hle/service/filesystem/filesystem.h"
|
||||||
|
#include "core/loader/loader.h"
|
||||||
|
#include "frontend_common/yuzu_config.h"
|
||||||
|
#include "hid_core/frontend/emulated_controller.h"
|
||||||
|
#include "hid_core/hid_core.h"
|
||||||
|
#include "hid_core/hid_types.h"
|
||||||
|
#include "video_core/renderer_base.h"
|
||||||
|
#include "video_core/renderer_vulkan/renderer_vulkan.h"
|
||||||
|
#include "video_core/vulkan_common/vulkan_instance.h"
|
||||||
|
#include "video_core/vulkan_common/vulkan_surface.h"
|
||||||
|
|
||||||
|
|
||||||
|
#import <mach/mach.h>
|
||||||
|
|
||||||
|
@implementation AppUIObjC
|
||||||
|
-(AppUIObjC *) init {
|
||||||
|
if (self = [super init]) {
|
||||||
|
_gameInformation = [AppUIGameInformation sharedInstance];
|
||||||
|
|
||||||
|
|
||||||
|
Common::FS::SetAppDirectory(DirectoryManager::AppUIDirectory());
|
||||||
|
Config{"config", Config::ConfigType::GlobalConfig};
|
||||||
|
|
||||||
|
EmulationSession::GetInstance().System().Initialize();
|
||||||
|
EmulationSession::GetInstance().InitializeSystem(false);
|
||||||
|
EmulationSession::GetInstance().InitializeGpuDriver();
|
||||||
|
|
||||||
|
|
||||||
|
YuzuSettings::values.dump_shaders.SetValue(true);
|
||||||
|
YuzuSettings::values.use_asynchronous_shaders.SetValue(true);
|
||||||
|
// YuzuSettings::values.astc_recompression.SetValue(YuzuSettings::AstcRecompression::Bc3);
|
||||||
|
YuzuSettings::values.shader_backend.SetValue(YuzuSettings::ShaderBackend::SpirV);
|
||||||
|
// YuzuSettings::values.resolution_setup.SetValue(YuzuSettings::ResolutionSetup::Res1X);
|
||||||
|
// YuzuSettings::values.scaling_filter.SetValue(YuzuSettings::ScalingFilter::Bilinear);
|
||||||
|
} return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
+(AppUIObjC *) sharedInstance {
|
||||||
|
static AppUIObjC *sharedInstance = NULL;
|
||||||
|
static dispatch_once_t onceToken;
|
||||||
|
dispatch_once(&onceToken, ^{
|
||||||
|
sharedInstance = [[self alloc] init];
|
||||||
|
});
|
||||||
|
return sharedInstance;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)ispaused {
|
||||||
|
return EmulationSession::GetInstance().IsPaused();
|
||||||
|
}
|
||||||
|
|
||||||
|
-(void) pause {
|
||||||
|
EmulationSession::GetInstance().System().Pause();
|
||||||
|
EmulationSession::GetInstance().HaltEmulation();
|
||||||
|
EmulationSession::GetInstance().PauseEmulation();
|
||||||
|
}
|
||||||
|
|
||||||
|
-(void) play {
|
||||||
|
|
||||||
|
EmulationSession::GetInstance().System().Run();
|
||||||
|
EmulationSession::GetInstance().RunEmulation();
|
||||||
|
EmulationSession::GetInstance().UnPauseEmulation();
|
||||||
|
}
|
||||||
|
|
||||||
|
-(BOOL)hasfirstfame {
|
||||||
|
@try {
|
||||||
|
auto* window = &EmulationSession::GetInstance().Window();
|
||||||
|
if (window && window->HasFirstFrame()) {
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@catch (NSException *exception) {
|
||||||
|
NSLog(@"Exception occurred: %@", exception);
|
||||||
|
// Handle the exception, maybe return a default value
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)canGetFullPath {
|
||||||
|
@try {
|
||||||
|
Core::System& system = EmulationSession::GetInstance().System();
|
||||||
|
auto bis_system = system.GetFileSystemController().GetSystemNANDContents();
|
||||||
|
|
||||||
|
if (bis_system == nullptr) {
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr u64 QLaunchId = static_cast<u64>(Service::AM::AppletProgramId::QLaunch);
|
||||||
|
auto qlaunch_applet_nca = bis_system->GetEntry(QLaunchId, FileSys::ContentRecordType::Program);
|
||||||
|
|
||||||
|
if (qlaunch_applet_nca == nullptr) {
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto filename = qlaunch_applet_nca->GetFullPath();
|
||||||
|
|
||||||
|
// If GetFullPath() is successful
|
||||||
|
return YES;
|
||||||
|
} @catch (NSException *exception) {
|
||||||
|
// Handle the exception if needed
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
-(void) quit {
|
||||||
|
EmulationSession::GetInstance().ShutdownEmulation();
|
||||||
|
}
|
||||||
|
|
||||||
|
-(void) configureLayer:(CAMetalLayer *)layer withSize:(CGSize)size {
|
||||||
|
_layer = layer;
|
||||||
|
_size = size;
|
||||||
|
EmulationSession::GetInstance().SetNativeWindow((__bridge CA::MetalLayer*)layer, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
-(void) bootOS {
|
||||||
|
EmulationSession::GetInstance().BootOS();
|
||||||
|
}
|
||||||
|
|
||||||
|
-(void) insertGame:(NSURL *)url {
|
||||||
|
EmulationSession::GetInstance().InitializeEmulation([url.path UTF8String], [_gameInformation informationForGame:url].programID, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
-(void) insertGames:(NSArray<NSURL *> *)games {
|
||||||
|
for (NSURL *url in games) {
|
||||||
|
EmulationSession::GetInstance().ConfigureFilesystemProvider([url.path UTF8String]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
-(void) step {
|
||||||
|
void(EmulationSession::GetInstance().System().Run());
|
||||||
|
}
|
||||||
|
|
||||||
|
-(void) touchBeganAtPoint:(CGPoint)point index:(NSUInteger)index {
|
||||||
|
float h_ratio, w_ratio;
|
||||||
|
h_ratio = EmulationSession::GetInstance().Window().GetFramebufferLayout().height / (_size.height * [[UIScreen mainScreen] nativeScale]);
|
||||||
|
w_ratio = EmulationSession::GetInstance().Window().GetFramebufferLayout().width / (_size.width * [[UIScreen mainScreen] nativeScale]);
|
||||||
|
|
||||||
|
EmulationSession::GetInstance().Window().OnTouchPressed([[NSNumber numberWithUnsignedInteger:index] intValue],
|
||||||
|
(point.x) * [[UIScreen mainScreen] nativeScale] * w_ratio,
|
||||||
|
((point.y) * [[UIScreen mainScreen] nativeScale] * h_ratio));
|
||||||
|
}
|
||||||
|
|
||||||
|
-(void) touchEndedForIndex:(NSUInteger)index {
|
||||||
|
EmulationSession::GetInstance().Window().OnTouchReleased([[NSNumber numberWithUnsignedInteger:index] intValue]);
|
||||||
|
}
|
||||||
|
|
||||||
|
-(void) touchMovedAtPoint:(CGPoint)point index:(NSUInteger)index {
|
||||||
|
float h_ratio, w_ratio;
|
||||||
|
h_ratio = EmulationSession::GetInstance().Window().GetFramebufferLayout().height / (_size.height * [[UIScreen mainScreen] nativeScale]);
|
||||||
|
w_ratio = EmulationSession::GetInstance().Window().GetFramebufferLayout().width / (_size.width * [[UIScreen mainScreen] nativeScale]);
|
||||||
|
|
||||||
|
EmulationSession::GetInstance().Window().OnTouchMoved([[NSNumber numberWithUnsignedInteger:index] intValue],
|
||||||
|
(point.x) * [[UIScreen mainScreen] nativeScale] * w_ratio,
|
||||||
|
((point.y) * [[UIScreen mainScreen] nativeScale] * h_ratio));
|
||||||
|
}
|
||||||
|
|
||||||
|
-(void) thumbstickMoved:(VirtualControllerAnalogType)analog
|
||||||
|
x:(CGFloat)x
|
||||||
|
y:(CGFloat)y
|
||||||
|
controllerId:(int)controllerId {
|
||||||
|
EmulationSession::GetInstance().OnGamepadConnectEvent(controllerId);
|
||||||
|
EmulationSession::GetInstance().Window().OnGamepadJoystickEvent(controllerId, [[NSNumber numberWithUnsignedInteger:analog] intValue], CGFloat(x), CGFloat(y));
|
||||||
|
}
|
||||||
|
|
||||||
|
-(void) virtualControllerButtonDown:(VirtualControllerButtonType)button
|
||||||
|
controllerId:(int)controllerId {
|
||||||
|
EmulationSession::GetInstance().OnGamepadConnectEvent(controllerId);
|
||||||
|
EmulationSession::GetInstance().Window().OnGamepadButtonEvent(controllerId, [[NSNumber numberWithUnsignedInteger:button] intValue], true);
|
||||||
|
}
|
||||||
|
|
||||||
|
-(void) virtualControllerGyro:(int)controllerId
|
||||||
|
deltaTimestamp:(int)delta_timestamp
|
||||||
|
gyroX:(float)gyro_x
|
||||||
|
gyroY:(float)gyro_y
|
||||||
|
gyroZ:(float)gyro_z
|
||||||
|
accelX:(float)accel_x
|
||||||
|
accelY:(float)accel_y
|
||||||
|
accelZ:(float)accel_z
|
||||||
|
{
|
||||||
|
EmulationSession::GetInstance().OnGamepadConnectEvent(controllerId);
|
||||||
|
EmulationSession::GetInstance().Window().OnGamepadMotionEvent(controllerId, delta_timestamp, gyro_x, gyro_y, gyro_z, accel_x, accel_y, accel_z);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
-(void) virtualControllerButtonUp:(VirtualControllerButtonType)button
|
||||||
|
controllerId:(int)controllerId {
|
||||||
|
EmulationSession::GetInstance().OnGamepadConnectEvent(controllerId);
|
||||||
|
EmulationSession::GetInstance().Window().OnGamepadButtonEvent(controllerId, [[NSNumber numberWithUnsignedInteger:button] intValue], false);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
-(void) orientationChanged:(UIInterfaceOrientation)orientation with:(CAMetalLayer *)layer size:(CGSize)size {
|
||||||
|
_layer = layer;
|
||||||
|
_size = size;
|
||||||
|
EmulationSession::GetInstance().Window().OnSurfaceChanged((__bridge CA::MetalLayer*)layer, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
-(void) settingsChanged {
|
||||||
|
Config{"config", Config::ConfigType::GlobalConfig};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@end
|
||||||
568
src/ios/Eden/Wrapper/Config/Config.h
Normal file
568
src/ios/Eden/Wrapper/Config/Config.h
Normal file
|
|
@ -0,0 +1,568 @@
|
||||||
|
//
|
||||||
|
// Config.h
|
||||||
|
// AppUI
|
||||||
|
//
|
||||||
|
// Created by Jarrod Norwell on 13/3/2024.
|
||||||
|
//
|
||||||
|
|
||||||
|
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace DefaultINI {
|
||||||
|
|
||||||
|
const char* android_config_file = R"(
|
||||||
|
|
||||||
|
[ControlsP0]
|
||||||
|
# The input devices and parameters for each Switch native input
|
||||||
|
# The config section determines the player number where the config will be applied on. For example "ControlsP0", "ControlsP1", ...
|
||||||
|
# It should be in the format of "engine:[engine_name],[param1]:[value1],[param2]:[value2]..."
|
||||||
|
# Escape characters $0 (for ':'), $1 (for ',') and $2 (for '$') can be used in values
|
||||||
|
|
||||||
|
# Indicates if this player should be connected at boot
|
||||||
|
connected=
|
||||||
|
|
||||||
|
# for button input, the following devices are available:
|
||||||
|
# - "keyboard" (default) for keyboard input. Required parameters:
|
||||||
|
# - "code": the code of the key to bind
|
||||||
|
# - "sdl" for joystick input using SDL. Required parameters:
|
||||||
|
# - "guid": SDL identification GUID of the joystick
|
||||||
|
# - "port": the index of the joystick to bind
|
||||||
|
# - "button"(optional): the index of the button to bind
|
||||||
|
# - "hat"(optional): the index of the hat to bind as direction buttons
|
||||||
|
# - "axis"(optional): the index of the axis to bind
|
||||||
|
# - "direction"(only used for hat): the direction name of the hat to bind. Can be "up", "down", "left" or "right"
|
||||||
|
# - "threshold"(only used for axis): a float value in (-1.0, 1.0) which the button is
|
||||||
|
# triggered if the axis value crosses
|
||||||
|
# - "direction"(only used for axis): "+" means the button is triggered when the axis value
|
||||||
|
# is greater than the threshold; "-" means the button is triggered when the axis value
|
||||||
|
# is smaller than the threshold
|
||||||
|
button_a=
|
||||||
|
button_b=
|
||||||
|
button_x=
|
||||||
|
button_y=
|
||||||
|
button_lstick=
|
||||||
|
button_rstick=
|
||||||
|
button_l=
|
||||||
|
button_r=
|
||||||
|
button_zl=
|
||||||
|
button_zr=
|
||||||
|
button_plus=
|
||||||
|
button_minus=
|
||||||
|
button_dleft=
|
||||||
|
button_dup=
|
||||||
|
button_dright=
|
||||||
|
button_ddown=
|
||||||
|
button_lstick_left=
|
||||||
|
button_lstick_up=
|
||||||
|
button_lstick_right=
|
||||||
|
button_lstick_down=
|
||||||
|
button_sl=
|
||||||
|
button_sr=
|
||||||
|
button_home=
|
||||||
|
button_screenshot=
|
||||||
|
|
||||||
|
# for analog input, the following devices are available:
|
||||||
|
# - "analog_from_button" (default) for emulating analog input from direction buttons. Required parameters:
|
||||||
|
# - "up", "down", "left", "right": sub-devices for each direction.
|
||||||
|
# Should be in the format as a button input devices using escape characters, for example, "engine$0keyboard$1code$00"
|
||||||
|
# - "modifier": sub-devices as a modifier.
|
||||||
|
# - "modifier_scale": a float number representing the applied modifier scale to the analog input.
|
||||||
|
# Must be in range of 0.0-1.0. Defaults to 0.5
|
||||||
|
# - "sdl" for joystick input using SDL. Required parameters:
|
||||||
|
# - "guid": SDL identification GUID of the joystick
|
||||||
|
# - "port": the index of the joystick to bind
|
||||||
|
# - "axis_x": the index of the axis to bind as x-axis (default to 0)
|
||||||
|
# - "axis_y": the index of the axis to bind as y-axis (default to 1)
|
||||||
|
lstick=
|
||||||
|
rstick=
|
||||||
|
|
||||||
|
# for motion input, the following devices are available:
|
||||||
|
# - "keyboard" (default) for emulating random motion input from buttons. Required parameters:
|
||||||
|
# - "code": the code of the key to bind
|
||||||
|
# - "sdl" for motion input using SDL. Required parameters:
|
||||||
|
# - "guid": SDL identification GUID of the joystick
|
||||||
|
# - "port": the index of the joystick to bind
|
||||||
|
# - "motion": the index of the motion sensor to bind
|
||||||
|
# - "cemuhookudp" for motion input using Cemu Hook protocol. Required parameters:
|
||||||
|
# - "guid": the IP address of the cemu hook server encoded to a hex string. for example 192.168.0.1 = "c0a80001"
|
||||||
|
# - "port": the port of the cemu hook server
|
||||||
|
# - "pad": the index of the joystick
|
||||||
|
# - "motion": the index of the motion sensor of the joystick to bind
|
||||||
|
motionleft=
|
||||||
|
motionright=
|
||||||
|
|
||||||
|
[ControlsGeneral]
|
||||||
|
# To use the debug_pad, prepend debug_pad_ before each button setting above.
|
||||||
|
# i.e. debug_pad_button_a=
|
||||||
|
|
||||||
|
# Enable debug pad inputs to the guest
|
||||||
|
# 0 (default): Disabled, 1: Enabled
|
||||||
|
debug_pad_enabled =
|
||||||
|
|
||||||
|
# Whether to enable or disable vibration
|
||||||
|
# 0: Disabled, 1 (default): Enabled
|
||||||
|
vibration_enabled =
|
||||||
|
|
||||||
|
# Whether to enable or disable accurate vibrations
|
||||||
|
# 0 (default): Disabled, 1: Enabled
|
||||||
|
enable_accurate_vibrations =
|
||||||
|
|
||||||
|
# Enables controller motion inputs
|
||||||
|
# 0: Disabled, 1 (default): Enabled
|
||||||
|
motion_enabled =
|
||||||
|
|
||||||
|
# Defines the udp device's touch screen coordinate system for cemuhookudp devices
|
||||||
|
# - "min_x", "min_y", "max_x", "max_y"
|
||||||
|
touch_device=
|
||||||
|
|
||||||
|
# for mapping buttons to touch inputs.
|
||||||
|
#touch_from_button_map=1
|
||||||
|
#touch_from_button_maps_0_name=default
|
||||||
|
#touch_from_button_maps_0_count=2
|
||||||
|
#touch_from_button_maps_0_bind_0=foo
|
||||||
|
#touch_from_button_maps_0_bind_1=bar
|
||||||
|
# etc.
|
||||||
|
|
||||||
|
# List of Cemuhook UDP servers, delimited by ','.
|
||||||
|
# Default: 127.0.0.1:26760
|
||||||
|
# Example: 127.0.0.1:26760,123.4.5.67:26761
|
||||||
|
udp_input_servers =
|
||||||
|
|
||||||
|
# Enable controlling an axis via a mouse input.
|
||||||
|
# 0 (default): Off, 1: On
|
||||||
|
mouse_panning =
|
||||||
|
|
||||||
|
# Set mouse sensitivity.
|
||||||
|
# Default: 1.0
|
||||||
|
mouse_panning_sensitivity =
|
||||||
|
|
||||||
|
# Emulate an analog control stick from keyboard inputs.
|
||||||
|
# 0 (default): Disabled, 1: Enabled
|
||||||
|
emulate_analog_keyboard =
|
||||||
|
|
||||||
|
# Enable mouse inputs to the guest
|
||||||
|
# 0 (default): Disabled, 1: Enabled
|
||||||
|
mouse_enabled =
|
||||||
|
|
||||||
|
# Enable keyboard inputs to the guest
|
||||||
|
# 0 (default): Disabled, 1: Enabled
|
||||||
|
keyboard_enabled =
|
||||||
|
|
||||||
|
[Core]
|
||||||
|
# Whether to use multi-core for CPU emulation
|
||||||
|
# 0: Disabled, 1 (default): Enabled
|
||||||
|
use_multi_core =
|
||||||
|
|
||||||
|
# Enable unsafe extended guest system memory layout (8GB DRAM)
|
||||||
|
# 0 (default): Disabled, 1: Enabled
|
||||||
|
use_unsafe_extended_memory_layout =
|
||||||
|
|
||||||
|
[Cpu]
|
||||||
|
# Adjusts various optimizations.
|
||||||
|
# Auto-select mode enables choice unsafe optimizations.
|
||||||
|
# Accurate enables only safe optimizations.
|
||||||
|
# Unsafe allows any unsafe optimizations.
|
||||||
|
# 0 (default): Auto-select, 1: Accurate, 2: Enable unsafe optimizations
|
||||||
|
cpu_accuracy =
|
||||||
|
|
||||||
|
# Allow disabling safe optimizations.
|
||||||
|
# 0 (default): Disabled, 1: Enabled
|
||||||
|
cpu_debug_mode =
|
||||||
|
|
||||||
|
# Enable inline page tables optimization (faster guest memory access)
|
||||||
|
# 0: Disabled, 1 (default): Enabled
|
||||||
|
cpuopt_page_tables =
|
||||||
|
|
||||||
|
# Enable block linking CPU optimization (reduce block dispatcher use during predictable jumps)
|
||||||
|
# 0: Disabled, 1 (default): Enabled
|
||||||
|
cpuopt_block_linking =
|
||||||
|
|
||||||
|
# Enable return stack buffer CPU optimization (reduce block dispatcher use during predictable returns)
|
||||||
|
# 0: Disabled, 1 (default): Enabled
|
||||||
|
cpuopt_return_stack_buffer =
|
||||||
|
|
||||||
|
# Enable fast dispatcher CPU optimization (use a two-tiered dispatcher architecture)
|
||||||
|
# 0: Disabled, 1 (default): Enabled
|
||||||
|
cpuopt_fast_dispatcher =
|
||||||
|
|
||||||
|
# Enable context elimination CPU Optimization (reduce host memory use for guest context)
|
||||||
|
# 0: Disabled, 1 (default): Enabled
|
||||||
|
cpuopt_context_elimination =
|
||||||
|
|
||||||
|
# Enable constant propagation CPU optimization (basic IR optimization)
|
||||||
|
# 0: Disabled, 1 (default): Enabled
|
||||||
|
cpuopt_const_prop =
|
||||||
|
|
||||||
|
# Enable miscellaneous CPU optimizations (basic IR optimization)
|
||||||
|
# 0: Disabled, 1 (default): Enabled
|
||||||
|
cpuopt_misc_ir =
|
||||||
|
|
||||||
|
# Enable reduction of memory misalignment checks (reduce memory fallbacks for misaligned access)
|
||||||
|
# 0: Disabled, 1 (default): Enabled
|
||||||
|
cpuopt_reduce_misalign_checks =
|
||||||
|
|
||||||
|
# Enable Host MMU Emulation (faster guest memory access)
|
||||||
|
# 0: Disabled, 1 (default): Enabled
|
||||||
|
cpuopt_fastmem =
|
||||||
|
|
||||||
|
# Enable Host MMU Emulation for exclusive memory instructions (faster guest memory access)
|
||||||
|
# 0: Disabled, 1 (default): Enabled
|
||||||
|
cpuopt_fastmem_exclusives =
|
||||||
|
|
||||||
|
# Enable fallback on failure of fastmem of exclusive memory instructions (faster guest memory access)
|
||||||
|
# 0: Disabled, 1 (default): Enabled
|
||||||
|
cpuopt_recompile_exclusives =
|
||||||
|
|
||||||
|
# Enable optimization to ignore invalid memory accesses (faster guest memory access)
|
||||||
|
# 0: Disabled, 1 (default): Enabled
|
||||||
|
cpuopt_ignore_memory_aborts =
|
||||||
|
|
||||||
|
# Enable unfuse FMA (improve performance on CPUs without FMA)
|
||||||
|
# Only enabled if cpu_accuracy is set to Unsafe. Automatically chosen with cpu_accuracy = Auto-select.
|
||||||
|
# 0: Disabled, 1 (default): Enabled
|
||||||
|
cpuopt_unsafe_unfuse_fma =
|
||||||
|
|
||||||
|
# Enable faster FRSQRTE and FRECPE
|
||||||
|
# Only enabled if cpu_accuracy is set to Unsafe.
|
||||||
|
# 0: Disabled, 1 (default): Enabled
|
||||||
|
cpuopt_unsafe_reduce_fp_error =
|
||||||
|
|
||||||
|
# Enable faster ASIMD instructions (32 bits only)
|
||||||
|
# Only enabled if cpu_accuracy is set to Unsafe. Automatically chosen with cpu_accuracy = Auto-select.
|
||||||
|
# 0: Disabled, 1 (default): Enabled
|
||||||
|
cpuopt_unsafe_ignore_standard_fpcr =
|
||||||
|
|
||||||
|
# Enable inaccurate NaN handling
|
||||||
|
# Only enabled if cpu_accuracy is set to Unsafe. Automatically chosen with cpu_accuracy = Auto-select.
|
||||||
|
# 0: Disabled, 1 (default): Enabled
|
||||||
|
cpuopt_unsafe_inaccurate_nan =
|
||||||
|
|
||||||
|
# Disable address space checks (64 bits only)
|
||||||
|
# Only enabled if cpu_accuracy is set to Unsafe. Automatically chosen with cpu_accuracy = Auto-select.
|
||||||
|
# 0: Disabled, 1 (default): Enabled
|
||||||
|
cpuopt_unsafe_fastmem_check =
|
||||||
|
|
||||||
|
# Enable faster exclusive instructions
|
||||||
|
# Only enabled if cpu_accuracy is set to Unsafe. Automatically chosen with cpu_accuracy = Auto-select.
|
||||||
|
# 0: Disabled, 1 (default): Enabled
|
||||||
|
cpuopt_unsafe_ignore_global_monitor =
|
||||||
|
|
||||||
|
[Renderer]
|
||||||
|
# Which backend API to use.
|
||||||
|
# 0: OpenGL (unsupported), 1 (default): Vulkan, 2: Null
|
||||||
|
backend =
|
||||||
|
|
||||||
|
# Whether to enable asynchronous presentation (Vulkan only)
|
||||||
|
# 0: Off, 1 (default): On
|
||||||
|
async_presentation =
|
||||||
|
|
||||||
|
# Forces the GPU to run at the maximum possible clocks (thermal constraints will still be applied).
|
||||||
|
# 0 (default): Disabled, 1: Enabled
|
||||||
|
force_max_clock =
|
||||||
|
|
||||||
|
# Enable graphics API debugging mode.
|
||||||
|
# 0 (default): Disabled, 1: Enabled
|
||||||
|
debug =
|
||||||
|
|
||||||
|
# Enable shader feedback.
|
||||||
|
# 0 (default): Disabled, 1: Enabled
|
||||||
|
renderer_shader_feedback =
|
||||||
|
|
||||||
|
# Enable Nsight Aftermath crash dumps
|
||||||
|
# 0 (default): Disabled, 1: Enabled
|
||||||
|
nsight_aftermath =
|
||||||
|
|
||||||
|
# Disable shader loop safety checks, executing the shader without loop logic changes
|
||||||
|
# 0 (default): Disabled, 1: Enabled
|
||||||
|
disable_shader_loop_safety_checks =
|
||||||
|
|
||||||
|
# Which Vulkan physical device to use (defaults to 0)
|
||||||
|
vulkan_device =
|
||||||
|
|
||||||
|
# 0: 0.5x (360p/540p) [EXPERIMENTAL]
|
||||||
|
# 1: 0.75x (540p/810p) [EXPERIMENTAL]
|
||||||
|
# 2 (default): 1x (720p/1080p)
|
||||||
|
# 3: 2x (1440p/2160p)
|
||||||
|
# 4: 3x (2160p/3240p)
|
||||||
|
# 5: 4x (2880p/4320p)
|
||||||
|
# 6: 5x (3600p/5400p)
|
||||||
|
# 7: 6x (4320p/6480p)
|
||||||
|
resolution_setup =
|
||||||
|
|
||||||
|
# Pixel filter to use when up- or down-sampling rendered frames.
|
||||||
|
# 0: Nearest Neighbor
|
||||||
|
# 1 (default): Bilinear
|
||||||
|
# 2: Bicubic
|
||||||
|
# 3: Gaussian
|
||||||
|
# 4: ScaleForce
|
||||||
|
# 5: AMD FidelityFX™️ Super Resolution [Vulkan Only]
|
||||||
|
scaling_filter =
|
||||||
|
|
||||||
|
# Anti-Aliasing (AA)
|
||||||
|
# 0 (default): None, 1: FXAA
|
||||||
|
anti_aliasing =
|
||||||
|
|
||||||
|
# Whether to use fullscreen or borderless window mode
|
||||||
|
# 0 (Windows default): Borderless window, 1 (All other default): Exclusive fullscreen
|
||||||
|
fullscreen_mode =
|
||||||
|
|
||||||
|
# Aspect ratio
|
||||||
|
# 0: Default (16:9), 1: Force 4:3, 2: Force 21:9, 3: Force 16:10, 4: Stretch to Window
|
||||||
|
aspect_ratio =
|
||||||
|
|
||||||
|
# Anisotropic filtering
|
||||||
|
# 0: Default, 1: 2x, 2: 4x, 3: 8x, 4: 16x
|
||||||
|
max_anisotropy =
|
||||||
|
|
||||||
|
# Whether to enable VSync or not.
|
||||||
|
# OpenGL: Values other than 0 enable VSync
|
||||||
|
# Vulkan: FIFO is selected if the requested mode is not supported by the driver.
|
||||||
|
# FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen refresh rate.
|
||||||
|
# FIFO Relaxed is similar to FIFO but allows tearing as it recovers from a slow down.
|
||||||
|
# Mailbox can have lower latency than FIFO and does not tear but may drop frames.
|
||||||
|
# Immediate (no synchronization) just presents whatever is available and can exhibit tearing.
|
||||||
|
# 0: Immediate (Off), 1 (Default): Mailbox (On), 2: FIFO, 3: FIFO Relaxed
|
||||||
|
use_vsync =
|
||||||
|
|
||||||
|
# Selects the OpenGL shader backend. NV_gpu_program5 is required for GLASM. If NV_gpu_program5 is
|
||||||
|
# not available and GLASM is selected, GLSL will be used.
|
||||||
|
# 0: GLSL, 1 (default): GLASM, 2: SPIR-V
|
||||||
|
shader_backend =
|
||||||
|
|
||||||
|
# Whether to allow asynchronous shader building.
|
||||||
|
# 0 (default): Off, 1: On
|
||||||
|
use_asynchronous_shaders =
|
||||||
|
|
||||||
|
# Uses reactive flushing instead of predictive flushing. Allowing a more accurate syncing of memory.
|
||||||
|
# 0 (default): Off, 1: On
|
||||||
|
use_reactive_flushing =
|
||||||
|
|
||||||
|
# NVDEC emulation.
|
||||||
|
# 0: Disabled, 1: CPU Decoding, 2 (default): GPU Decoding
|
||||||
|
nvdec_emulation =
|
||||||
|
|
||||||
|
# Accelerate ASTC texture decoding.
|
||||||
|
# 0 (default): Off, 1: On
|
||||||
|
accelerate_astc =
|
||||||
|
|
||||||
|
# Turns on the speed limiter, which will limit the emulation speed to the desired speed limit value
|
||||||
|
# 0: Off, 1: On (default)
|
||||||
|
use_speed_limit =
|
||||||
|
|
||||||
|
# Limits the speed of the game to run no faster than this value as a percentage of target speed
|
||||||
|
# 1 - 9999: Speed limit as a percentage of target game speed. 100 (default)
|
||||||
|
speed_limit =
|
||||||
|
|
||||||
|
# Whether to use disk based shader cache
|
||||||
|
# 0: Off, 1 (default): On
|
||||||
|
use_disk_shader_cache =
|
||||||
|
|
||||||
|
# Which gpu accuracy level to use
|
||||||
|
# 0 (default): Normal, 1: High, 2: Extreme (Very slow)
|
||||||
|
gpu_accuracy =
|
||||||
|
|
||||||
|
# Whether to use asynchronous GPU emulation
|
||||||
|
# 0 : Off (slow), 1 (default): On (fast)
|
||||||
|
use_asynchronous_gpu_emulation =
|
||||||
|
|
||||||
|
# Inform the guest that GPU operations completed more quickly than they did.
|
||||||
|
# 0: Off, 1 (default): On
|
||||||
|
use_fast_gpu_time =
|
||||||
|
|
||||||
|
# Force unmodified buffers to be flushed, which can cost performance.
|
||||||
|
# 0: Off (default), 1: On
|
||||||
|
use_pessimistic_flushes =
|
||||||
|
|
||||||
|
# Whether to use garbage collection or not for GPU caches.
|
||||||
|
# 0 (default): Off, 1: On
|
||||||
|
use_caches_gc =
|
||||||
|
|
||||||
|
# The clear color for the renderer. What shows up on the sides of the bottom screen.
|
||||||
|
# Must be in range of 0-255. Defaults to 0 for all.
|
||||||
|
bg_red =
|
||||||
|
bg_blue =
|
||||||
|
bg_green =
|
||||||
|
|
||||||
|
[Audio]
|
||||||
|
# Which audio output engine to use.
|
||||||
|
# auto (default): Auto-select
|
||||||
|
# cubeb: Cubeb audio engine (if available)
|
||||||
|
# sdl2: SDL2 audio engine (if available)
|
||||||
|
# null: No audio output
|
||||||
|
output_engine =
|
||||||
|
|
||||||
|
# Which audio device to use.
|
||||||
|
# auto (default): Auto-select
|
||||||
|
output_device =
|
||||||
|
|
||||||
|
# Output volume.
|
||||||
|
# 100 (default): 100%, 0; mute
|
||||||
|
volume =
|
||||||
|
|
||||||
|
[Data Storage]
|
||||||
|
# Whether to create a virtual SD card.
|
||||||
|
# 1: Yes, 0 (default): No
|
||||||
|
use_virtual_sd =
|
||||||
|
|
||||||
|
# Whether or not to enable gamecard emulation
|
||||||
|
# 1: Yes, 0 (default): No
|
||||||
|
gamecard_inserted =
|
||||||
|
|
||||||
|
# Whether or not the gamecard should be emulated as the current game
|
||||||
|
# If 'gamecard_inserted' is 0 this setting is irrelevant
|
||||||
|
# 1: Yes, 0 (default): No
|
||||||
|
gamecard_current_game =
|
||||||
|
|
||||||
|
# Path to an XCI file to use as the gamecard
|
||||||
|
# If 'gamecard_inserted' is 0 this setting is irrelevant
|
||||||
|
# If 'gamecard_current_game' is 1 this setting is irrelevant
|
||||||
|
gamecard_path =
|
||||||
|
|
||||||
|
[System]
|
||||||
|
# Whether the system is docked
|
||||||
|
# 1: Yes, 0 (default): No
|
||||||
|
use_docked_mode =
|
||||||
|
|
||||||
|
# Sets the seed for the RNG generator built into the switch
|
||||||
|
# rng_seed will be ignored and randomly generated if rng_seed_enabled is false
|
||||||
|
rng_seed_enabled =
|
||||||
|
rng_seed =
|
||||||
|
|
||||||
|
# Sets the current time (in seconds since 12:00 AM Jan 1, 1970) that will be used by the time service
|
||||||
|
# This will auto-increment, with the time set being the time the game is started
|
||||||
|
# This override will only occur if custom_rtc_enabled is true, otherwise the current time is used
|
||||||
|
custom_rtc_enabled =
|
||||||
|
custom_rtc =
|
||||||
|
|
||||||
|
# Sets the systems language index
|
||||||
|
# 0: Japanese, 1: English (default), 2: French, 3: German, 4: Italian, 5: Spanish, 6: Chinese,
|
||||||
|
# 7: Korean, 8: Dutch, 9: Portuguese, 10: Russian, 11: Taiwanese, 12: British English, 13: Canadian French,
|
||||||
|
# 14: Latin American Spanish, 15: Simplified Chinese, 16: Traditional Chinese, 17: Brazilian Portuguese
|
||||||
|
language_index =
|
||||||
|
|
||||||
|
# The system region that yuzu will use during emulation
|
||||||
|
# -1: Auto-select (default), 0: Japan, 1: USA, 2: Europe, 3: Australia, 4: China, 5: Korea, 6: Taiwan
|
||||||
|
region_index =
|
||||||
|
|
||||||
|
# The system time zone that yuzu will use during emulation
|
||||||
|
# 0: Auto-select (default), 1: Default (system archive value), Others: Index for specified time zone
|
||||||
|
time_zone_index =
|
||||||
|
|
||||||
|
# Sets the sound output mode.
|
||||||
|
# 0: Mono, 1 (default): Stereo, 2: Surround
|
||||||
|
sound_index =
|
||||||
|
|
||||||
|
[Miscellaneous]
|
||||||
|
# A filter which removes logs below a certain logging level.
|
||||||
|
# Examples: *:Debug Kernel.SVC:Trace Service.*:Critical
|
||||||
|
log_filter = *:Trace
|
||||||
|
|
||||||
|
# Use developer keys
|
||||||
|
# 0 (default): Disabled, 1: Enabled
|
||||||
|
use_dev_keys =
|
||||||
|
|
||||||
|
[Debugging]
|
||||||
|
# Record frame time data, can be found in the log directory. Boolean value
|
||||||
|
record_frame_times =
|
||||||
|
# Determines whether or not yuzu will dump the ExeFS of all games it attempts to load while loading them
|
||||||
|
dump_exefs=false
|
||||||
|
# Determines whether or not yuzu will dump all NSOs it attempts to load while loading them
|
||||||
|
dump_nso=false
|
||||||
|
# Determines whether or not yuzu will save the filesystem access log.
|
||||||
|
enable_fs_access_log=false
|
||||||
|
# Enables verbose reporting services
|
||||||
|
reporting_services =
|
||||||
|
# Determines whether or not yuzu will report to the game that the emulated console is in Kiosk Mode
|
||||||
|
# false: Retail/Normal Mode (default), true: Kiosk Mode
|
||||||
|
quest_flag =
|
||||||
|
# Determines whether debug asserts should be enabled, which will throw an exception on asserts.
|
||||||
|
# false: Disabled (default), true: Enabled
|
||||||
|
use_debug_asserts =
|
||||||
|
# Determines whether unimplemented HLE service calls should be automatically stubbed.
|
||||||
|
# false: Disabled (default), true: Enabled
|
||||||
|
use_auto_stub =
|
||||||
|
# Enables/Disables the macro JIT compiler
|
||||||
|
disable_macro_jit=false
|
||||||
|
# Determines whether to enable the GDB stub and wait for the debugger to attach before running.
|
||||||
|
# false: Disabled (default), true: Enabled
|
||||||
|
use_gdbstub=false
|
||||||
|
# The port to use for the GDB server, if it is enabled.
|
||||||
|
gdbstub_port=6543
|
||||||
|
|
||||||
|
[WebService]
|
||||||
|
# Whether or not to enable telemetry
|
||||||
|
# 0: No, 1 (default): Yes
|
||||||
|
enable_telemetry =
|
||||||
|
# URL for Web API
|
||||||
|
web_api_url =
|
||||||
|
# Username and token for yuzu Web Service
|
||||||
|
# See https://profile.yuzu-emu.org/ for more info
|
||||||
|
yuzu_username =
|
||||||
|
yuzu_token =
|
||||||
|
|
||||||
|
[Network]
|
||||||
|
# Name of the network interface device to use with yuzu LAN play.
|
||||||
|
# e.g. On *nix: 'enp7s0', 'wlp6s0u1u3u3', 'lo'
|
||||||
|
# e.g. On Windows: 'Ethernet', 'Wi-Fi'
|
||||||
|
network_interface =
|
||||||
|
|
||||||
|
[AddOns]
|
||||||
|
# Used to disable add-ons
|
||||||
|
# List of title IDs of games that will have add-ons disabled (separated by '|'):
|
||||||
|
title_ids =
|
||||||
|
# For each title ID, have a key/value pair called `disabled_<title_id>` equal to the names of the add-ons to disable (sep. by '|')
|
||||||
|
# e.x. disabled_0100000000010000 = Update|DLC <- disables Updates and
|
||||||
|
on Super Mario Odyssey
|
||||||
|
)";
|
||||||
|
} // namespace DefaultINI
|
||||||
|
|
||||||
|
|
||||||
|
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <filesystem>
|
||||||
|
#include <memory>
|
||||||
|
#include <optional>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "common/settings.h"
|
||||||
|
|
||||||
|
class INIReader;
|
||||||
|
|
||||||
|
class Config {
|
||||||
|
bool LoadINI(const std::string& default_contents = "", bool retry = true);
|
||||||
|
|
||||||
|
public:
|
||||||
|
enum class ConfigType {
|
||||||
|
GlobalConfig,
|
||||||
|
PerGameConfig,
|
||||||
|
InputProfile,
|
||||||
|
};
|
||||||
|
|
||||||
|
explicit Config(const std::string& config_name = "config",
|
||||||
|
ConfigType config_type = ConfigType::GlobalConfig);
|
||||||
|
~Config();
|
||||||
|
|
||||||
|
void Initialize(const std::string& config_name);
|
||||||
|
|
||||||
|
private:
|
||||||
|
/**
|
||||||
|
* Applies a value read from the config to a Setting.
|
||||||
|
*
|
||||||
|
* @param group The name of the INI group
|
||||||
|
* @param setting The yuzu setting to modify
|
||||||
|
*/
|
||||||
|
template <typename Type, bool ranged>
|
||||||
|
void ReadSetting(const std::string& group, YuzuSettings::Setting<Type, ranged>& setting);
|
||||||
|
|
||||||
|
void ReadValues();
|
||||||
|
|
||||||
|
const ConfigType type;
|
||||||
|
std::unique_ptr<INIReader> config;
|
||||||
|
std::string config_loc;
|
||||||
|
const bool global;
|
||||||
|
};
|
||||||
330
src/ios/Eden/Wrapper/Config/Config.mm
Normal file
330
src/ios/Eden/Wrapper/Config/Config.mm
Normal file
|
|
@ -0,0 +1,330 @@
|
||||||
|
//
|
||||||
|
// Config.m - Sudachi
|
||||||
|
// Created by Jarrod Norwell on 13/3/2024.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "Config.h"
|
||||||
|
|
||||||
|
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <optional>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
#include <INIReader.h>
|
||||||
|
#include "common/fs/file.h"
|
||||||
|
#include "common/fs/fs.h"
|
||||||
|
#include "common/fs/path_util.h"
|
||||||
|
#include "common/logging/log.h"
|
||||||
|
#include "common/settings.h"
|
||||||
|
#include "common/settings_enums.h"
|
||||||
|
#include "core/hle/service/acc/profile_manager.h"
|
||||||
|
#include "input_common/main.h"
|
||||||
|
|
||||||
|
namespace FS = Common::FS;
|
||||||
|
|
||||||
|
Config::Config(const std::string& config_name, ConfigType config_type)
|
||||||
|
: type(config_type), global{config_type == ConfigType::GlobalConfig} {
|
||||||
|
Initialize(config_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
Config::~Config() = default;
|
||||||
|
|
||||||
|
bool Config::LoadINI(const std::string& default_contents, bool retry) {
|
||||||
|
void(FS::CreateParentDir(config_loc));
|
||||||
|
config = std::make_unique<INIReader>(FS::PathToUTF8String(config_loc));
|
||||||
|
const auto config_loc_str = FS::PathToUTF8String(config_loc);
|
||||||
|
if (config->ParseError() < 0) {
|
||||||
|
if (retry) {
|
||||||
|
LOG_WARNING(Config, "Failed to load {}. Creating file from defaults...",
|
||||||
|
config_loc_str);
|
||||||
|
|
||||||
|
void(FS::CreateParentDir(config_loc));
|
||||||
|
void(FS::WriteStringToFile(config_loc, FS::FileType::TextFile, default_contents));
|
||||||
|
|
||||||
|
config = std::make_unique<INIReader>(config_loc_str);
|
||||||
|
|
||||||
|
return LoadINI(default_contents, false);
|
||||||
|
}
|
||||||
|
LOG_ERROR(Config, "Failed.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
LOG_INFO(Config, "Successfully loaded {}", config_loc_str);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
void Config::ReadSetting(const std::string& group, YuzuSettings::Setting<std::string>& setting) {
|
||||||
|
std::string setting_value = config->Get(group, setting.GetLabel(), setting.GetDefault());
|
||||||
|
if (setting_value.empty()) {
|
||||||
|
setting_value = setting.GetDefault();
|
||||||
|
}
|
||||||
|
setting = std::move(setting_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
void Config::ReadSetting(const std::string& group, YuzuSettings::Setting<bool>& setting) {
|
||||||
|
setting = config->GetBoolean(group, setting.GetLabel(), setting.GetDefault());
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Type, bool ranged>
|
||||||
|
void Config::ReadSetting(const std::string& group, YuzuSettings::Setting<Type, ranged>& setting) {
|
||||||
|
setting = static_cast<Type>(
|
||||||
|
config->GetInteger(group, setting.GetLabel(), static_cast<long>(setting.GetDefault())));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Config::ReadValues() {
|
||||||
|
ReadSetting("ControlsGeneral", YuzuSettings::values.mouse_enabled);
|
||||||
|
ReadSetting("ControlsGeneral", YuzuSettings::values.touch_device);
|
||||||
|
ReadSetting("ControlsGeneral", YuzuSettings::values.keyboard_enabled);
|
||||||
|
ReadSetting("ControlsGeneral", YuzuSettings::values.debug_pad_enabled);
|
||||||
|
ReadSetting("ControlsGeneral", YuzuSettings::values.vibration_enabled);
|
||||||
|
ReadSetting("ControlsGeneral", YuzuSettings::values.enable_accurate_vibrations);
|
||||||
|
ReadSetting("ControlsGeneral", YuzuSettings::values.motion_enabled);
|
||||||
|
YuzuSettings::values.touchscreen.enabled =
|
||||||
|
config->GetBoolean("ControlsGeneral", "touch_enabled", true);
|
||||||
|
YuzuSettings::values.touchscreen.rotation_angle =
|
||||||
|
config->GetInteger("ControlsGeneral", "touch_angle", 0);
|
||||||
|
YuzuSettings::values.touchscreen.diameter_x =
|
||||||
|
config->GetInteger("ControlsGeneral", "touch_diameter_x", 15);
|
||||||
|
YuzuSettings::values.touchscreen.diameter_y =
|
||||||
|
config->GetInteger("ControlsGeneral", "touch_diameter_y", 15);
|
||||||
|
|
||||||
|
int num_touch_from_button_maps =
|
||||||
|
config->GetInteger("ControlsGeneral", "touch_from_button_map", 0);
|
||||||
|
if (num_touch_from_button_maps > 0) {
|
||||||
|
for (int i = 0; i < num_touch_from_button_maps; ++i) {
|
||||||
|
YuzuSettings::TouchFromButtonMap map;
|
||||||
|
map.name = config->Get("ControlsGeneral",
|
||||||
|
std::string("touch_from_button_maps_") + std::to_string(i) +
|
||||||
|
std::string("_name"),
|
||||||
|
"default");
|
||||||
|
const int num_touch_maps = config->GetInteger(
|
||||||
|
"ControlsGeneral",
|
||||||
|
std::string("touch_from_button_maps_") + std::to_string(i) + std::string("_count"),
|
||||||
|
0);
|
||||||
|
map.buttons.reserve(num_touch_maps);
|
||||||
|
|
||||||
|
for (int j = 0; j < num_touch_maps; ++j) {
|
||||||
|
std::string touch_mapping =
|
||||||
|
config->Get("ControlsGeneral",
|
||||||
|
std::string("touch_from_button_maps_") + std::to_string(i) +
|
||||||
|
std::string("_bind_") + std::to_string(j),
|
||||||
|
"");
|
||||||
|
map.buttons.emplace_back(std::move(touch_mapping));
|
||||||
|
}
|
||||||
|
|
||||||
|
YuzuSettings::values.touch_from_button_maps.emplace_back(std::move(map));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
YuzuSettings::values.touch_from_button_maps.emplace_back(
|
||||||
|
YuzuSettings::TouchFromButtonMap{"default", {}});
|
||||||
|
num_touch_from_button_maps = 1;
|
||||||
|
}
|
||||||
|
YuzuSettings::values.touch_from_button_map_index = std::clamp(
|
||||||
|
YuzuSettings::values.touch_from_button_map_index.GetValue(), 0, num_touch_from_button_maps - 1);
|
||||||
|
|
||||||
|
ReadSetting("ControlsGeneral", YuzuSettings::values.udp_input_servers);
|
||||||
|
|
||||||
|
// Data Storage
|
||||||
|
ReadSetting("Data Storage", YuzuSettings::values.use_virtual_sd);
|
||||||
|
FS::SetYuzuPath(FS::YuzuPath::NANDDir,
|
||||||
|
config->Get("Data Storage", "nand_directory",
|
||||||
|
FS::GetYuzuPathString(FS::YuzuPath::NANDDir)));
|
||||||
|
FS::SetYuzuPath(FS::YuzuPath::SDMCDir,
|
||||||
|
config->Get("Data Storage", "sdmc_directory",
|
||||||
|
FS::GetYuzuPathString(FS::YuzuPath::SDMCDir)));
|
||||||
|
FS::SetYuzuPath(FS::YuzuPath::LoadDir,
|
||||||
|
config->Get("Data Storage", "load_directory",
|
||||||
|
FS::GetYuzuPathString(FS::YuzuPath::LoadDir)));
|
||||||
|
FS::SetYuzuPath(FS::YuzuPath::DumpDir,
|
||||||
|
config->Get("Data Storage", "dump_directory",
|
||||||
|
FS::GetYuzuPathString(FS::YuzuPath::DumpDir)));
|
||||||
|
ReadSetting("Data Storage", YuzuSettings::values.gamecard_inserted);
|
||||||
|
ReadSetting("Data Storage", YuzuSettings::values.gamecard_current_game);
|
||||||
|
ReadSetting("Data Storage", YuzuSettings::values.gamecard_path);
|
||||||
|
|
||||||
|
// System
|
||||||
|
ReadSetting("System", YuzuSettings::values.current_user);
|
||||||
|
YuzuSettings::values.current_user = std::clamp<int>(YuzuSettings::values.current_user.GetValue(), 0,
|
||||||
|
Service::Account::MAX_USERS - 1);
|
||||||
|
|
||||||
|
// Enable docked mode by default on iOS
|
||||||
|
YuzuSettings::values.use_docked_mode.SetValue(config->GetBoolean("System", "use_docked_mode", false)
|
||||||
|
? YuzuSettings::ConsoleMode::Docked
|
||||||
|
: YuzuSettings::ConsoleMode::Handheld);
|
||||||
|
|
||||||
|
const auto rng_seed_enabled = config->GetBoolean("System", "rng_seed_enabled", false);
|
||||||
|
if (rng_seed_enabled) {
|
||||||
|
YuzuSettings::values.rng_seed.SetValue(config->GetInteger("System", "rng_seed", 0));
|
||||||
|
} else {
|
||||||
|
YuzuSettings::values.rng_seed.SetValue(0);
|
||||||
|
}
|
||||||
|
YuzuSettings::values.rng_seed_enabled.SetValue(rng_seed_enabled);
|
||||||
|
|
||||||
|
const auto custom_rtc_enabled = config->GetBoolean("System", "custom_rtc_enabled", false);
|
||||||
|
if (custom_rtc_enabled) {
|
||||||
|
YuzuSettings::values.custom_rtc = config->GetInteger("System", "custom_rtc", 0);
|
||||||
|
} else {
|
||||||
|
YuzuSettings::values.custom_rtc = 0;
|
||||||
|
}
|
||||||
|
YuzuSettings::values.custom_rtc_enabled = custom_rtc_enabled;
|
||||||
|
|
||||||
|
ReadSetting("System", YuzuSettings::values.language_index);
|
||||||
|
ReadSetting("System", YuzuSettings::values.region_index);
|
||||||
|
ReadSetting("System", YuzuSettings::values.time_zone_index);
|
||||||
|
ReadSetting("System", YuzuSettings::values.sound_index);
|
||||||
|
|
||||||
|
// Core
|
||||||
|
ReadSetting("Core", YuzuSettings::values.use_multi_core);
|
||||||
|
ReadSetting("Core", YuzuSettings::values.memory_layout_mode);
|
||||||
|
|
||||||
|
// Cpu
|
||||||
|
ReadSetting("Cpu", YuzuSettings::values.cpu_accuracy);
|
||||||
|
ReadSetting("Cpu", YuzuSettings::values.cpu_debug_mode);
|
||||||
|
ReadSetting("Cpu", YuzuSettings::values.cpuopt_page_tables);
|
||||||
|
ReadSetting("Cpu", YuzuSettings::values.cpuopt_block_linking);
|
||||||
|
ReadSetting("Cpu", YuzuSettings::values.cpuopt_return_stack_buffer);
|
||||||
|
ReadSetting("Cpu", YuzuSettings::values.cpuopt_fast_dispatcher);
|
||||||
|
ReadSetting("Cpu", YuzuSettings::values.cpuopt_context_elimination);
|
||||||
|
ReadSetting("Cpu", YuzuSettings::values.cpuopt_const_prop);
|
||||||
|
ReadSetting("Cpu", YuzuSettings::values.cpuopt_misc_ir);
|
||||||
|
ReadSetting("Cpu", YuzuSettings::values.cpuopt_reduce_misalign_checks);
|
||||||
|
ReadSetting("Cpu", YuzuSettings::values.cpuopt_fastmem);
|
||||||
|
ReadSetting("Cpu", YuzuSettings::values.cpuopt_fastmem_exclusives);
|
||||||
|
ReadSetting("Cpu", YuzuSettings::values.cpuopt_recompile_exclusives);
|
||||||
|
ReadSetting("Cpu", YuzuSettings::values.cpuopt_ignore_memory_aborts);
|
||||||
|
ReadSetting("Cpu", YuzuSettings::values.cpuopt_unsafe_unfuse_fma);
|
||||||
|
ReadSetting("Cpu", YuzuSettings::values.cpuopt_unsafe_reduce_fp_error);
|
||||||
|
ReadSetting("Cpu", YuzuSettings::values.cpuopt_unsafe_ignore_standard_fpcr);
|
||||||
|
ReadSetting("Cpu", YuzuSettings::values.cpuopt_unsafe_inaccurate_nan);
|
||||||
|
ReadSetting("Cpu", YuzuSettings::values.cpuopt_unsafe_fastmem_check);
|
||||||
|
ReadSetting("Cpu", YuzuSettings::values.cpuopt_unsafe_ignore_global_monitor);
|
||||||
|
|
||||||
|
// Renderer
|
||||||
|
ReadSetting("Renderer", YuzuSettings::values.renderer_backend);
|
||||||
|
ReadSetting("Renderer", YuzuSettings::values.renderer_debug);
|
||||||
|
ReadSetting("Renderer", YuzuSettings::values.renderer_shader_feedback);
|
||||||
|
ReadSetting("Renderer", YuzuSettings::values.enable_nsight_aftermath);
|
||||||
|
ReadSetting("Renderer", YuzuSettings::values.disable_shader_loop_safety_checks);
|
||||||
|
ReadSetting("Renderer", YuzuSettings::values.vulkan_device);
|
||||||
|
|
||||||
|
ReadSetting("Renderer", YuzuSettings::values.resolution_setup);
|
||||||
|
ReadSetting("Renderer", YuzuSettings::values.scaling_filter);
|
||||||
|
ReadSetting("Renderer", YuzuSettings::values.fsr_sharpening_slider);
|
||||||
|
ReadSetting("Renderer", YuzuSettings::values.anti_aliasing);
|
||||||
|
ReadSetting("Renderer", YuzuSettings::values.fullscreen_mode);
|
||||||
|
ReadSetting("Renderer", YuzuSettings::values.aspect_ratio);
|
||||||
|
ReadSetting("Renderer", YuzuSettings::values.max_anisotropy);
|
||||||
|
ReadSetting("Renderer", YuzuSettings::values.use_speed_limit);
|
||||||
|
ReadSetting("Renderer", YuzuSettings::values.speed_limit);
|
||||||
|
ReadSetting("Renderer", YuzuSettings::values.use_disk_shader_cache);
|
||||||
|
ReadSetting("Renderer", YuzuSettings::values.use_asynchronous_gpu_emulation);
|
||||||
|
ReadSetting("Renderer", YuzuSettings::values.vsync_mode);
|
||||||
|
ReadSetting("Renderer", YuzuSettings::values.shader_backend);
|
||||||
|
ReadSetting("Renderer", YuzuSettings::values.use_asynchronous_shaders);
|
||||||
|
ReadSetting("Renderer", YuzuSettings::values.nvdec_emulation);
|
||||||
|
ReadSetting("Renderer", YuzuSettings::values.use_fast_gpu_time);
|
||||||
|
ReadSetting("Renderer", YuzuSettings::values.use_vulkan_driver_pipeline_cache);
|
||||||
|
|
||||||
|
ReadSetting("Renderer", YuzuSettings::values.bg_red);
|
||||||
|
ReadSetting("Renderer", YuzuSettings::values.bg_green);
|
||||||
|
ReadSetting("Renderer", YuzuSettings::values.bg_blue);
|
||||||
|
|
||||||
|
// Use GPU accuracy normal by default on Android
|
||||||
|
YuzuSettings::values.gpu_accuracy = static_cast<YuzuSettings::GpuAccuracy>(config->GetInteger(
|
||||||
|
"Renderer", "gpu_accuracy", static_cast<u32>(YuzuSettings::GpuAccuracy::Normal)));
|
||||||
|
|
||||||
|
// Use GPU default anisotropic filtering on Android
|
||||||
|
YuzuSettings::values.max_anisotropy =
|
||||||
|
static_cast<YuzuSettings::AnisotropyMode>(config->GetInteger("Renderer", "max_anisotropy", 1));
|
||||||
|
|
||||||
|
// Disable ASTC compute by default on iOS
|
||||||
|
YuzuSettings::values.accelerate_astc.SetValue(
|
||||||
|
config->GetBoolean("Renderer", "accelerate_astc", false) ? YuzuSettings::AstcDecodeMode::Gpu
|
||||||
|
: YuzuSettings::AstcDecodeMode::Cpu);
|
||||||
|
|
||||||
|
// Enable asynchronous presentation by default on Android
|
||||||
|
YuzuSettings::values.async_presentation =
|
||||||
|
config->GetBoolean("Renderer", "async_presentation", true);
|
||||||
|
|
||||||
|
// Disable force_max_clock by default on Android
|
||||||
|
YuzuSettings::values.renderer_force_max_clock =
|
||||||
|
config->GetBoolean("Renderer", "force_max_clock", false);
|
||||||
|
|
||||||
|
// Disable use_reactive_flushing by default on Android
|
||||||
|
YuzuSettings::values.use_reactive_flushing =
|
||||||
|
config->GetBoolean("Renderer", "use_reactive_flushing", false);
|
||||||
|
|
||||||
|
// Audio
|
||||||
|
ReadSetting("Audio", YuzuSettings::values.sink_id);
|
||||||
|
ReadSetting("Audio", YuzuSettings::values.audio_output_device_id);
|
||||||
|
ReadSetting("Audio", YuzuSettings::values.volume);
|
||||||
|
|
||||||
|
// Miscellaneous
|
||||||
|
// log_filter has a different default here than from common
|
||||||
|
YuzuSettings::values.log_filter = "*:Info";
|
||||||
|
ReadSetting("Miscellaneous", YuzuSettings::values.use_dev_keys);
|
||||||
|
|
||||||
|
// Debugging
|
||||||
|
YuzuSettings::values.record_frame_times =
|
||||||
|
config->GetBoolean("Debugging", "record_frame_times", false);
|
||||||
|
ReadSetting("Debugging", YuzuSettings::values.dump_exefs);
|
||||||
|
ReadSetting("Debugging", YuzuSettings::values.dump_nso);
|
||||||
|
ReadSetting("Debugging", YuzuSettings::values.enable_fs_access_log);
|
||||||
|
ReadSetting("Debugging", YuzuSettings::values.reporting_services);
|
||||||
|
ReadSetting("Debugging", YuzuSettings::values.quest_flag);
|
||||||
|
ReadSetting("Debugging", YuzuSettings::values.use_debug_asserts);
|
||||||
|
ReadSetting("Debugging", YuzuSettings::values.use_auto_stub);
|
||||||
|
ReadSetting("Debugging", YuzuSettings::values.disable_macro_jit);
|
||||||
|
ReadSetting("Debugging", YuzuSettings::values.disable_macro_hle);
|
||||||
|
ReadSetting("Debugging", YuzuSettings::values.use_gdbstub);
|
||||||
|
ReadSetting("Debugging", YuzuSettings::values.gdbstub_port);
|
||||||
|
|
||||||
|
const auto title_list = config->Get("AddOns", "title_ids", "");
|
||||||
|
std::stringstream ss(title_list);
|
||||||
|
std::string line;
|
||||||
|
while (std::getline(ss, line, '|')) {
|
||||||
|
const auto title_id = std::strtoul(line.c_str(), nullptr, 16);
|
||||||
|
const auto disabled_list = config->Get("AddOns", "disabled_" + line, "");
|
||||||
|
|
||||||
|
std::stringstream inner_ss(disabled_list);
|
||||||
|
std::string inner_line;
|
||||||
|
std::vector<std::string> out;
|
||||||
|
while (std::getline(inner_ss, inner_line, '|')) {
|
||||||
|
out.push_back(inner_line);
|
||||||
|
}
|
||||||
|
|
||||||
|
YuzuSettings::values.disabled_addons.insert_or_assign(title_id, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Web Service
|
||||||
|
ReadSetting("WebService", YuzuSettings::values.enable_telemetry);
|
||||||
|
ReadSetting("WebService", YuzuSettings::values.web_api_url);
|
||||||
|
ReadSetting("WebService", YuzuSettings::values.yuzu_username);
|
||||||
|
ReadSetting("WebService", YuzuSettings::values.yuzu_token);
|
||||||
|
|
||||||
|
// Network
|
||||||
|
ReadSetting("Network", YuzuSettings::values.network_interface);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Config::Initialize(const std::string& config_name) {
|
||||||
|
const auto fs_config_loc = FS::GetYuzuPath(FS::YuzuPath::ConfigDir);
|
||||||
|
const auto config_file = fmt::format("{}.ini", config_name);
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case ConfigType::GlobalConfig:
|
||||||
|
config_loc = FS::PathToUTF8String(fs_config_loc / config_file);
|
||||||
|
break;
|
||||||
|
case ConfigType::PerGameConfig:
|
||||||
|
config_loc = FS::PathToUTF8String(fs_config_loc / "custom" / FS::ToU8String(config_file));
|
||||||
|
break;
|
||||||
|
case ConfigType::InputProfile:
|
||||||
|
config_loc = FS::PathToUTF8String(fs_config_loc / "input" / config_file);
|
||||||
|
LoadINI(DefaultINI::android_config_file);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
LoadINI(DefaultINI::android_config_file);
|
||||||
|
ReadValues();
|
||||||
|
}
|
||||||
10
src/ios/Eden/Wrapper/DirectoryManager/DirectoryManager.h
Normal file
10
src/ios/Eden/Wrapper/DirectoryManager/DirectoryManager.h
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
//
|
||||||
|
// DirectoryManager.h - Sudachi
|
||||||
|
// Created by Jarrod Norwell on 1/18/24.
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace DirectoryManager {
|
||||||
|
const char* AppUIDirectory(void);
|
||||||
|
}
|
||||||
16
src/ios/Eden/Wrapper/DirectoryManager/DirectoryManager.mm
Normal file
16
src/ios/Eden/Wrapper/DirectoryManager/DirectoryManager.mm
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
//
|
||||||
|
// DirectoryManager.mm - Sudachi
|
||||||
|
// Created by Jarrod Norwell on 1/18/24.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
#import "DirectoryManager.h"
|
||||||
|
|
||||||
|
NSURL *DocumentsDirectory() {
|
||||||
|
return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] firstObject];
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* DirectoryManager::AppUIDirectory(void) {
|
||||||
|
return [[DocumentsDirectory() path] UTF8String];
|
||||||
|
}
|
||||||
98
src/ios/Eden/Wrapper/EmulationSession/EmulationSession.h
Normal file
98
src/ios/Eden/Wrapper/EmulationSession/EmulationSession.h
Normal file
|
|
@ -0,0 +1,98 @@
|
||||||
|
//
|
||||||
|
// EmulationSession.h - Sudachi
|
||||||
|
// Created by Jarrod Norwell on 1/20/24.
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#import <QuartzCore/CAMetalLayer.h>
|
||||||
|
|
||||||
|
#import <Metal/Metal.hpp>
|
||||||
|
#import "../EmulationWindow/EmulationWindow.h"
|
||||||
|
|
||||||
|
#include "common/detached_tasks.h"
|
||||||
|
#include "core/core.h"
|
||||||
|
#include "core/file_sys/registered_cache.h"
|
||||||
|
#include "core/hle/service/acc/profile_manager.h"
|
||||||
|
#include "core/perf_stats.h"
|
||||||
|
#include "frontend_common/content_manager.h"
|
||||||
|
#include "video_core/rasterizer_interface.h"
|
||||||
|
|
||||||
|
class EmulationSession final {
|
||||||
|
public:
|
||||||
|
explicit EmulationSession();
|
||||||
|
~EmulationSession() = default;
|
||||||
|
|
||||||
|
static EmulationSession& GetInstance();
|
||||||
|
const Core::System& System() const;
|
||||||
|
Core::System& System();
|
||||||
|
FileSys::ManualContentProvider* GetContentProvider();
|
||||||
|
InputCommon::InputSubsystem& GetInputSubsystem();
|
||||||
|
|
||||||
|
const EmulationWindow& Window() const;
|
||||||
|
EmulationWindow& Window();
|
||||||
|
CA::MetalLayer* NativeWindow() const;
|
||||||
|
void SetNativeWindow(CA::MetalLayer* native_window, CGSize size);
|
||||||
|
void SurfaceChanged();
|
||||||
|
|
||||||
|
void InitializeGpuDriver();
|
||||||
|
|
||||||
|
bool IsRunning() const;
|
||||||
|
bool IsPaused() const;
|
||||||
|
void PauseEmulation();
|
||||||
|
void UnPauseEmulation();
|
||||||
|
void HaltEmulation();
|
||||||
|
void RunEmulation();
|
||||||
|
void ShutdownEmulation();
|
||||||
|
|
||||||
|
const Core::PerfStatsResults& PerfStats();
|
||||||
|
void ConfigureFilesystemProvider(const std::string& filepath);
|
||||||
|
void InitializeSystem(bool reload);
|
||||||
|
void SetAppletId(int applet_id);
|
||||||
|
Core::SystemResultStatus InitializeEmulation(const std::string& filepath,
|
||||||
|
const std::size_t program_index,
|
||||||
|
const bool frontend_initiated);
|
||||||
|
Core::SystemResultStatus BootOS();
|
||||||
|
|
||||||
|
static void OnEmulationStarted();
|
||||||
|
static u64 GetProgramId(std::string programId);
|
||||||
|
bool IsInitialized() { return is_initialized; };
|
||||||
|
|
||||||
|
bool IsHandheldOnly();
|
||||||
|
void SetDeviceType([[maybe_unused]] int index, int type);
|
||||||
|
void OnGamepadConnectEvent([[maybe_unused]] int index);
|
||||||
|
void OnGamepadDisconnectEvent([[maybe_unused]] int index);
|
||||||
|
private:
|
||||||
|
static void LoadDiskCacheProgress(VideoCore::LoadCallbackStage stage, int progress, int max);
|
||||||
|
static void OnEmulationStopped(Core::SystemResultStatus result);
|
||||||
|
static void ChangeProgram(std::size_t program_index);
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Window management
|
||||||
|
std::unique_ptr<EmulationWindow> m_window;
|
||||||
|
CA::MetalLayer* m_native_window{};
|
||||||
|
|
||||||
|
// Core emulation
|
||||||
|
Core::System m_system;
|
||||||
|
InputCommon::InputSubsystem m_input_subsystem;
|
||||||
|
Common::DetachedTasks m_detached_tasks;
|
||||||
|
Core::PerfStatsResults m_perf_stats{};
|
||||||
|
std::shared_ptr<FileSys::VfsFilesystem> m_vfs;
|
||||||
|
Core::SystemResultStatus m_load_result{Core::SystemResultStatus::ErrorNotInitialized};
|
||||||
|
std::atomic<bool> m_is_running = false;
|
||||||
|
std::atomic<bool> m_is_paused = false;
|
||||||
|
std::unique_ptr<FileSys::ManualContentProvider> m_manual_provider;
|
||||||
|
int m_applet_id{1};
|
||||||
|
|
||||||
|
// GPU driver parameters
|
||||||
|
std::shared_ptr<Common::DynamicLibrary> m_vulkan_library;
|
||||||
|
|
||||||
|
// Synchronization
|
||||||
|
std::condition_variable_any m_cv;
|
||||||
|
mutable std::mutex m_mutex;
|
||||||
|
bool is_initialized = false;
|
||||||
|
CGSize m_size;
|
||||||
|
|
||||||
|
// Program index for next boot
|
||||||
|
std::atomic<s32> m_next_program_index = -1;
|
||||||
|
};
|
||||||
528
src/ios/Eden/Wrapper/EmulationSession/EmulationSession.mm
Normal file
528
src/ios/Eden/Wrapper/EmulationSession/EmulationSession.mm
Normal file
|
|
@ -0,0 +1,528 @@
|
||||||
|
//
|
||||||
|
// EmulationSession.m - Sudachi
|
||||||
|
// Created by Jarrod Norwell on 1/20/24.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "EmulationSession.h"
|
||||||
|
|
||||||
|
#include <SDL.h>
|
||||||
|
|
||||||
|
#include <codecvt>
|
||||||
|
#include <locale>
|
||||||
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
#include <dlfcn.h>
|
||||||
|
|
||||||
|
#include "common/fs/fs.h"
|
||||||
|
#include "core/file_sys/patch_manager.h"
|
||||||
|
#include "core/file_sys/savedata_factory.h"
|
||||||
|
#include "core/loader/nro.h"
|
||||||
|
#include "frontend_common/content_manager.h"
|
||||||
|
|
||||||
|
#include "common/detached_tasks.h"
|
||||||
|
#include "common/dynamic_library.h"
|
||||||
|
#include "common/fs/path_util.h"
|
||||||
|
#include "common/logging/backend.h"
|
||||||
|
#include "common/logging/log.h"
|
||||||
|
#include "common/microprofile.h"
|
||||||
|
#include "common/scm_rev.h"
|
||||||
|
#include "common/scope_exit.h"
|
||||||
|
#include "common/settings.h"
|
||||||
|
#include "common/string_util.h"
|
||||||
|
#include "core/core.h"
|
||||||
|
#include "core/cpu_manager.h"
|
||||||
|
#include "core/crypto/key_manager.h"
|
||||||
|
#include "core/file_sys/card_image.h"
|
||||||
|
#include "core/file_sys/content_archive.h"
|
||||||
|
#include "core/file_sys/fs_filesystem.h"
|
||||||
|
#include "core/file_sys/submission_package.h"
|
||||||
|
#include "core/file_sys/vfs/vfs.h"
|
||||||
|
#include "core/file_sys/vfs/vfs_real.h"
|
||||||
|
#include "core/frontend/applets/cabinet.h"
|
||||||
|
#include "core/frontend/applets/controller.h"
|
||||||
|
#include "core/frontend/applets/error.h"
|
||||||
|
#include "core/frontend/applets/general.h"
|
||||||
|
#include "core/frontend/applets/mii_edit.h"
|
||||||
|
#include "core/frontend/applets/profile_select.h"
|
||||||
|
#include "core/frontend/applets/software_keyboard.h"
|
||||||
|
#include "core/frontend/applets/web_browser.h"
|
||||||
|
#include "core/hle/service/am/applet_manager.h"
|
||||||
|
#include "core/hle/service/am/frontend/applets.h"
|
||||||
|
#include "core/hle/service/filesystem/filesystem.h"
|
||||||
|
#include "core/loader/loader.h"
|
||||||
|
#include "frontend_common/yuzu_config.h"
|
||||||
|
#include "hid_core/frontend/emulated_controller.h"
|
||||||
|
#include "hid_core/hid_core.h"
|
||||||
|
#include "hid_core/hid_types.h"
|
||||||
|
#include "video_core/renderer_base.h"
|
||||||
|
#include "video_core/renderer_vulkan/renderer_vulkan.h"
|
||||||
|
#include "video_core/vulkan_common/vulkan_instance.h"
|
||||||
|
#include "video_core/vulkan_common/vulkan_surface.h"
|
||||||
|
|
||||||
|
#define jconst [[maybe_unused]] const auto
|
||||||
|
#define jauto [[maybe_unused]] auto
|
||||||
|
|
||||||
|
static EmulationSession s_instance;
|
||||||
|
|
||||||
|
EmulationSession::EmulationSession() {
|
||||||
|
m_vfs = std::make_shared<FileSys::RealVfsFilesystem>();
|
||||||
|
}
|
||||||
|
|
||||||
|
EmulationSession& EmulationSession::GetInstance() {
|
||||||
|
return s_instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Core::System& EmulationSession::System() const {
|
||||||
|
return m_system;
|
||||||
|
}
|
||||||
|
|
||||||
|
Core::System& EmulationSession::System() {
|
||||||
|
return m_system;
|
||||||
|
}
|
||||||
|
|
||||||
|
FileSys::ManualContentProvider* EmulationSession::GetContentProvider() {
|
||||||
|
return m_manual_provider.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
InputCommon::InputSubsystem& EmulationSession::GetInputSubsystem() {
|
||||||
|
return m_input_subsystem;
|
||||||
|
}
|
||||||
|
|
||||||
|
const EmulationWindow& EmulationSession::Window() const {
|
||||||
|
return *m_window;
|
||||||
|
}
|
||||||
|
|
||||||
|
EmulationWindow& EmulationSession::Window() {
|
||||||
|
return *m_window;
|
||||||
|
}
|
||||||
|
|
||||||
|
CA::MetalLayer* EmulationSession::NativeWindow() const {
|
||||||
|
return m_native_window;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmulationSession::SetNativeWindow(CA::MetalLayer* native_window, CGSize size) {
|
||||||
|
m_native_window = native_window;
|
||||||
|
m_size = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmulationSession::InitializeGpuDriver() {
|
||||||
|
m_vulkan_library = std::make_shared<Common::DynamicLibrary>(dlopen("@executable_path/Frameworks/MoltenVK", RTLD_NOW));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EmulationSession::IsRunning() const {
|
||||||
|
return m_is_running;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EmulationSession::IsPaused() const {
|
||||||
|
return m_is_running && m_is_paused;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Core::PerfStatsResults& EmulationSession::PerfStats() {
|
||||||
|
m_perf_stats = m_system.GetAndResetPerfStats();
|
||||||
|
return m_perf_stats;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmulationSession::SurfaceChanged() {
|
||||||
|
if (!IsRunning()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_window->OnSurfaceChanged(m_native_window, m_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmulationSession::ConfigureFilesystemProvider(const std::string& filepath) {
|
||||||
|
const auto file = m_system.GetFilesystem()->OpenFile(filepath, FileSys::OpenMode::Read);
|
||||||
|
if (!file) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto loader = Loader::GetLoader(m_system, file);
|
||||||
|
if (!loader) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto file_type = loader->GetFileType();
|
||||||
|
if (file_type == Loader::FileType::Unknown || file_type == Loader::FileType::Error) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 program_id = 0;
|
||||||
|
const auto res2 = loader->ReadProgramId(program_id);
|
||||||
|
if (res2 == Loader::ResultStatus::Success && file_type == Loader::FileType::NCA) {
|
||||||
|
m_manual_provider->AddEntry(FileSys::TitleType::Application,
|
||||||
|
FileSys::GetCRTypeFromNCAType(FileSys::NCA{file}.GetType()),
|
||||||
|
program_id, file);
|
||||||
|
} else if (res2 == Loader::ResultStatus::Success &&
|
||||||
|
(file_type == Loader::FileType::XCI || file_type == Loader::FileType::NSP)) {
|
||||||
|
const auto nsp = file_type == Loader::FileType::NSP
|
||||||
|
? std::make_shared<FileSys::NSP>(file)
|
||||||
|
: FileSys::XCI{file}.GetSecurePartitionNSP();
|
||||||
|
for (const auto& title : nsp->GetNCAs()) {
|
||||||
|
for (const auto& entry : title.second) {
|
||||||
|
m_manual_provider->AddEntry(entry.first.first, entry.first.second, title.first,
|
||||||
|
entry.second->GetBaseFile());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmulationSession::InitializeSystem(bool reload) {
|
||||||
|
if (!reload) {
|
||||||
|
SDL_SetMainReady();
|
||||||
|
|
||||||
|
// Initialize logging system
|
||||||
|
Common::Log::Initialize();
|
||||||
|
Common::Log::SetColorConsoleBackendEnabled(true);
|
||||||
|
Common::Log::Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize filesystem.
|
||||||
|
m_system.SetFilesystem(m_vfs);
|
||||||
|
m_system.GetUserChannel().clear();
|
||||||
|
m_manual_provider = std::make_unique<FileSys::ManualContentProvider>();
|
||||||
|
m_system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>());
|
||||||
|
m_system.RegisterContentProvider(FileSys::ContentProviderUnionSlot::FrontendManual,
|
||||||
|
m_manual_provider.get());
|
||||||
|
m_system.GetFileSystemController().CreateFactories(*m_vfs);
|
||||||
|
|
||||||
|
is_initialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmulationSession::SetAppletId(int applet_id) {
|
||||||
|
m_applet_id = applet_id;
|
||||||
|
m_system.GetFrontendAppletHolder().SetCurrentAppletId(
|
||||||
|
static_cast<Service::AM::AppletId>(m_applet_id));
|
||||||
|
}
|
||||||
|
|
||||||
|
Core::SystemResultStatus EmulationSession::InitializeEmulation(const std::string& filepath,
|
||||||
|
const std::size_t program_index,
|
||||||
|
const bool frontend_initiated) {
|
||||||
|
std::scoped_lock lock(m_mutex);
|
||||||
|
|
||||||
|
// Create the render window.
|
||||||
|
m_window = std::make_unique<EmulationWindow>(&m_input_subsystem, m_native_window, m_size, m_vulkan_library);
|
||||||
|
|
||||||
|
// Initialize system.
|
||||||
|
m_system.SetShuttingDown(false);
|
||||||
|
m_system.ApplySettings();
|
||||||
|
YuzuSettings::LogSettings();
|
||||||
|
m_system.HIDCore().ReloadInputDevices();
|
||||||
|
m_system.SetFrontendAppletSet({
|
||||||
|
nullptr, // Amiibo Settings
|
||||||
|
nullptr, // Controller Selector
|
||||||
|
nullptr, // Error Display
|
||||||
|
nullptr, // Mii Editor
|
||||||
|
nullptr, // Parental Controls
|
||||||
|
nullptr, // Photo Viewer
|
||||||
|
nullptr, // Profile Selector
|
||||||
|
nullptr, // std::move(android_keyboard), // Software Keyboard
|
||||||
|
nullptr, // Web Browser
|
||||||
|
});
|
||||||
|
|
||||||
|
// Initialize filesystem.
|
||||||
|
ConfigureFilesystemProvider(filepath);
|
||||||
|
|
||||||
|
// Load the ROM.
|
||||||
|
Service::AM::FrontendAppletParameters params{
|
||||||
|
.applet_id = static_cast<Service::AM::AppletId>(m_applet_id),
|
||||||
|
.launch_type = frontend_initiated ? Service::AM::LaunchType::FrontendInitiated
|
||||||
|
: Service::AM::LaunchType::ApplicationInitiated,
|
||||||
|
.program_index = static_cast<s32>(program_index),
|
||||||
|
};
|
||||||
|
m_load_result = m_system.Load(EmulationSession::GetInstance().Window(), filepath, params);
|
||||||
|
if (m_load_result != Core::SystemResultStatus::Success) {
|
||||||
|
return m_load_result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Complete initialization.
|
||||||
|
m_system.GPU().Start();
|
||||||
|
m_system.GetCpuManager().OnGpuReady();
|
||||||
|
m_system.RegisterExitCallback([&] { HaltEmulation(); });
|
||||||
|
|
||||||
|
if (YuzuSettings::values.use_disk_shader_cache.GetValue()) {
|
||||||
|
m_system.Renderer().ReadRasterizer()->LoadDiskResources(
|
||||||
|
m_system.GetApplicationProcessProgramID(), std::stop_token{},
|
||||||
|
[](VideoCore::LoadCallbackStage, size_t value, size_t total) {});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register an ExecuteProgram callback such that Core can execute a sub-program
|
||||||
|
m_system.RegisterExecuteProgramCallback([&](std::size_t program_index_) {
|
||||||
|
m_next_program_index = program_index_;
|
||||||
|
EmulationSession::GetInstance().HaltEmulation();
|
||||||
|
ChangeProgram(m_next_program_index);
|
||||||
|
});
|
||||||
|
|
||||||
|
OnEmulationStarted();
|
||||||
|
return Core::SystemResultStatus::Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Core::SystemResultStatus EmulationSession::BootOS() {
|
||||||
|
std::scoped_lock lock(m_mutex);
|
||||||
|
|
||||||
|
// Create the render window.
|
||||||
|
m_window = std::make_unique<EmulationWindow>(&m_input_subsystem, m_native_window, m_size, m_vulkan_library);
|
||||||
|
|
||||||
|
// Initialize system.
|
||||||
|
m_system.SetShuttingDown(false);
|
||||||
|
m_system.ApplySettings();
|
||||||
|
YuzuSettings::LogSettings();
|
||||||
|
m_system.HIDCore().ReloadInputDevices();
|
||||||
|
m_system.SetFrontendAppletSet({
|
||||||
|
nullptr, // Amiibo Settings
|
||||||
|
nullptr, // Controller Selector
|
||||||
|
nullptr, // Error Display
|
||||||
|
nullptr, // Mii Editor
|
||||||
|
nullptr, // Parental Controls
|
||||||
|
nullptr, // Photo Viewer
|
||||||
|
nullptr, // Profile Selector
|
||||||
|
nullptr, // std::move(android_keyboard), // Software Keyboard
|
||||||
|
nullptr, // Web Browser
|
||||||
|
});
|
||||||
|
|
||||||
|
constexpr u64 QLaunchId = static_cast<u64>(Service::AM::AppletProgramId::QLaunch);
|
||||||
|
auto bis_system = m_system.GetFileSystemController().GetSystemNANDContents();
|
||||||
|
|
||||||
|
auto qlaunch_applet_nca = bis_system->GetEntry(QLaunchId, FileSys::ContentRecordType::Program);
|
||||||
|
|
||||||
|
m_system.GetFrontendAppletHolder().SetCurrentAppletId(Service::AM::AppletId::QLaunch);
|
||||||
|
|
||||||
|
const auto filename = qlaunch_applet_nca->GetFullPath();
|
||||||
|
|
||||||
|
auto params = Service::AM::FrontendAppletParameters {
|
||||||
|
.program_id = QLaunchId,
|
||||||
|
.applet_id = Service::AM::AppletId::QLaunch,
|
||||||
|
.applet_type = Service::AM::AppletType::LibraryApplet
|
||||||
|
};
|
||||||
|
|
||||||
|
m_load_result = m_system.Load(EmulationSession::GetInstance().Window(), filename, params);
|
||||||
|
|
||||||
|
if (m_load_result != Core::SystemResultStatus::Success) {
|
||||||
|
return m_load_result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Complete initialization.
|
||||||
|
m_system.GPU().Start();
|
||||||
|
m_system.GetCpuManager().OnGpuReady();
|
||||||
|
m_system.RegisterExitCallback([&] { HaltEmulation(); });
|
||||||
|
|
||||||
|
if (YuzuSettings::values.use_disk_shader_cache.GetValue()) {
|
||||||
|
m_system.Renderer().ReadRasterizer()->LoadDiskResources(
|
||||||
|
m_system.GetApplicationProcessProgramID(), std::stop_token{},
|
||||||
|
[](VideoCore::LoadCallbackStage, size_t value, size_t total) {});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register an ExecuteProgram callback such that Core can execute a sub-program
|
||||||
|
m_system.RegisterExecuteProgramCallback([&](std::size_t program_index_) {
|
||||||
|
m_next_program_index = program_index_;
|
||||||
|
EmulationSession::GetInstance().HaltEmulation();
|
||||||
|
});
|
||||||
|
|
||||||
|
OnEmulationStarted();
|
||||||
|
return Core::SystemResultStatus::Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmulationSession::ShutdownEmulation() {
|
||||||
|
std::scoped_lock lock(m_mutex);
|
||||||
|
|
||||||
|
if (m_next_program_index != -1) {
|
||||||
|
ChangeProgram(m_next_program_index);
|
||||||
|
m_next_program_index = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_is_running = false;
|
||||||
|
|
||||||
|
// Unload user input.
|
||||||
|
m_system.HIDCore().UnloadInputDevices();
|
||||||
|
|
||||||
|
// Enable all controllers
|
||||||
|
m_system.HIDCore().SetSupportedStyleTag({Core::HID::NpadStyleSet::All});
|
||||||
|
|
||||||
|
// Shutdown the main emulated process
|
||||||
|
if (m_load_result == Core::SystemResultStatus::Success) {
|
||||||
|
m_system.DetachDebugger();
|
||||||
|
m_system.ShutdownMainProcess();
|
||||||
|
m_detached_tasks.WaitForAllTasks();
|
||||||
|
m_load_result = Core::SystemResultStatus::ErrorNotInitialized;
|
||||||
|
m_window.reset();
|
||||||
|
OnEmulationStopped(Core::SystemResultStatus::Success);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tear down the render window.
|
||||||
|
m_window.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmulationSession::PauseEmulation() {
|
||||||
|
std::scoped_lock lock(m_mutex);
|
||||||
|
m_system.Pause();
|
||||||
|
m_is_paused = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmulationSession::UnPauseEmulation() {
|
||||||
|
std::scoped_lock lock(m_mutex);
|
||||||
|
m_system.Run();
|
||||||
|
m_is_paused = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmulationSession::HaltEmulation() {
|
||||||
|
std::scoped_lock lock(m_mutex);
|
||||||
|
m_is_running = false;
|
||||||
|
m_cv.notify_one();
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmulationSession::RunEmulation() {
|
||||||
|
{
|
||||||
|
std::scoped_lock lock(m_mutex);
|
||||||
|
m_is_running = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load the disk shader cache.
|
||||||
|
if (YuzuSettings::values.use_disk_shader_cache.GetValue()) {
|
||||||
|
LoadDiskCacheProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0);
|
||||||
|
m_system.Renderer().ReadRasterizer()->LoadDiskResources(
|
||||||
|
m_system.GetApplicationProcessProgramID(), std::stop_token{}, LoadDiskCacheProgress);
|
||||||
|
LoadDiskCacheProgress(VideoCore::LoadCallbackStage::Complete, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void(m_system.Run());
|
||||||
|
|
||||||
|
if (m_system.DebuggerEnabled()) {
|
||||||
|
m_system.InitializeDebugger();
|
||||||
|
}
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
{
|
||||||
|
[[maybe_unused]] std::unique_lock lock(m_mutex);
|
||||||
|
if (m_cv.wait_for(lock, std::chrono::milliseconds(800),
|
||||||
|
[&]() { return !m_is_running; })) {
|
||||||
|
// Emulation halted.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset current applet ID.
|
||||||
|
m_applet_id = static_cast<int>(Service::AM::AppletId::Application);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmulationSession::LoadDiskCacheProgress(VideoCore::LoadCallbackStage stage, int progress,
|
||||||
|
int max) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmulationSession::OnEmulationStarted() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmulationSession::OnEmulationStopped(Core::SystemResultStatus result) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmulationSession::ChangeProgram(std::size_t program_index) {
|
||||||
|
LOG_INFO(Frontend, "Trying To Switch Program");
|
||||||
|
// Halt current emulation session
|
||||||
|
EmulationSession::GetInstance().HaltEmulation();
|
||||||
|
// Save the current state if necessary
|
||||||
|
|
||||||
|
// Shutdown the current emulation session cleanly
|
||||||
|
// Update the program index
|
||||||
|
EmulationSession::GetInstance().m_next_program_index = program_index;
|
||||||
|
|
||||||
|
// Initialize the new program
|
||||||
|
// Start the new emulation session
|
||||||
|
EmulationSession::GetInstance().RunEmulation();
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 EmulationSession::GetProgramId(std::string programId) {
|
||||||
|
try {
|
||||||
|
return std::stoull(programId);
|
||||||
|
} catch (...) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Core::SystemResultStatus RunEmulation(const std::string& filepath,
|
||||||
|
const size_t program_index,
|
||||||
|
const bool frontend_initiated) {
|
||||||
|
MicroProfileOnThreadCreate("EmuThread");
|
||||||
|
SCOPE_EXIT {
|
||||||
|
MicroProfileShutdown();
|
||||||
|
};
|
||||||
|
|
||||||
|
LOG_INFO(Frontend, "starting");
|
||||||
|
|
||||||
|
if (filepath.empty()) {
|
||||||
|
LOG_CRITICAL(Frontend, "failed to load: filepath empty!");
|
||||||
|
return Core::SystemResultStatus::ErrorLoader;
|
||||||
|
}
|
||||||
|
|
||||||
|
SCOPE_EXIT {
|
||||||
|
EmulationSession::GetInstance().ShutdownEmulation();
|
||||||
|
};
|
||||||
|
|
||||||
|
jconst result = EmulationSession::GetInstance().InitializeEmulation(filepath, program_index,
|
||||||
|
frontend_initiated);
|
||||||
|
if (result != Core::SystemResultStatus::Success) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
EmulationSession::GetInstance().RunEmulation();
|
||||||
|
|
||||||
|
return Core::SystemResultStatus::Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
bool EmulationSession::IsHandheldOnly() {
|
||||||
|
jconst npad_style_set = m_system.HIDCore().GetSupportedStyleTag();
|
||||||
|
|
||||||
|
if (npad_style_set.fullkey == 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (npad_style_set.handheld == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return !YuzuSettings::IsDockedMode();
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmulationSession::SetDeviceType([[maybe_unused]] int index, int type) {
|
||||||
|
jauto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index);
|
||||||
|
controller->SetNpadStyleIndex(static_cast<Core::HID::NpadStyleIndex>(type));
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmulationSession::OnGamepadConnectEvent([[maybe_unused]] int index) {
|
||||||
|
jauto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index);
|
||||||
|
|
||||||
|
// Ensure that player1 is configured correctly and handheld disconnected
|
||||||
|
if (controller->GetNpadIdType() == Core::HID::NpadIdType::Player1) {
|
||||||
|
jauto handheld = m_system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld);
|
||||||
|
|
||||||
|
if (controller->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::Handheld) {
|
||||||
|
handheld->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Fullkey);
|
||||||
|
controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Fullkey);
|
||||||
|
handheld->Disconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure that handheld is configured correctly and player 1 disconnected
|
||||||
|
if (controller->GetNpadIdType() == Core::HID::NpadIdType::Handheld) {
|
||||||
|
jauto player1 = m_system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1);
|
||||||
|
|
||||||
|
if (controller->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::Handheld) {
|
||||||
|
player1->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Handheld);
|
||||||
|
controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Handheld);
|
||||||
|
player1->Disconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!controller->IsConnected()) {
|
||||||
|
controller->Connect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmulationSession::OnGamepadDisconnectEvent([[maybe_unused]] int index) {
|
||||||
|
jauto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index);
|
||||||
|
controller->Disconnect();
|
||||||
|
}
|
||||||
80
src/ios/Eden/Wrapper/EmulationWindow/EmulationWindow.h
Normal file
80
src/ios/Eden/Wrapper/EmulationWindow/EmulationWindow.h
Normal file
|
|
@ -0,0 +1,80 @@
|
||||||
|
//
|
||||||
|
// EmulationWindow.h - Sudachi
|
||||||
|
// Created by Jarrod Norwell on 1/18/24.
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#import <Metal/Metal.hpp>
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
#import <UIKit/UIKit.h>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <span>
|
||||||
|
|
||||||
|
#include "core/frontend/emu_window.h"
|
||||||
|
#include "core/frontend/graphics_context.h"
|
||||||
|
#include "input_common/main.h"
|
||||||
|
|
||||||
|
class GraphicsContext_Apple final : public Core::Frontend::GraphicsContext {
|
||||||
|
public:
|
||||||
|
explicit GraphicsContext_Apple(std::shared_ptr<Common::DynamicLibrary> driver_library)
|
||||||
|
: m_driver_library{driver_library} {}
|
||||||
|
|
||||||
|
~GraphicsContext_Apple() = default;
|
||||||
|
|
||||||
|
std::shared_ptr<Common::DynamicLibrary> GetDriverLibrary() override {
|
||||||
|
return m_driver_library;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::shared_ptr<Common::DynamicLibrary> m_driver_library;
|
||||||
|
};
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
class EmulationWindow final : public Core::Frontend::EmuWindow {
|
||||||
|
public:
|
||||||
|
EmulationWindow(InputCommon::InputSubsystem* input_subsystem, CA::MetalLayer* surface, CGSize size,
|
||||||
|
std::shared_ptr<Common::DynamicLibrary> driver_library);
|
||||||
|
|
||||||
|
~EmulationWindow() = default;
|
||||||
|
|
||||||
|
void OnSurfaceChanged(CA::MetalLayer* surface, CGSize size);
|
||||||
|
void OrientationChanged(UIInterfaceOrientation orientation);
|
||||||
|
void OnFrameDisplayed() override;
|
||||||
|
|
||||||
|
void OnTouchPressed(int id, float x, float y);
|
||||||
|
void OnTouchMoved(int id, float x, float y);
|
||||||
|
void OnTouchReleased(int id);
|
||||||
|
|
||||||
|
void OnGamepadButtonEvent(int player_index, int button_id, bool pressed);
|
||||||
|
void OnGamepadJoystickEvent(int player_index, int stick_id, float x, float y);
|
||||||
|
void OnGamepadMotionEvent(int player_index, u64 delta_timestamp, float gyro_x, float gyro_y, float gyro_z, float accel_x, float accel_y, float accel_z);
|
||||||
|
|
||||||
|
std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override {
|
||||||
|
return {std::make_unique<GraphicsContext_Apple>(m_driver_library)};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool HasFirstFrame() const {
|
||||||
|
return m_first_frame;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsShown() const override {
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
float m_window_width{};
|
||||||
|
float m_window_height{};
|
||||||
|
CGSize m_size;
|
||||||
|
bool is_portrait = true;
|
||||||
|
|
||||||
|
InputCommon::InputSubsystem* m_input_subsystem{};
|
||||||
|
std::shared_ptr<Common::DynamicLibrary> m_driver_library;
|
||||||
|
|
||||||
|
bool m_first_frame = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
||||||
85
src/ios/Eden/Wrapper/EmulationWindow/EmulationWindow.mm
Normal file
85
src/ios/Eden/Wrapper/EmulationWindow/EmulationWindow.mm
Normal file
|
|
@ -0,0 +1,85 @@
|
||||||
|
//
|
||||||
|
// EmulationWindow.mm - Sudachi
|
||||||
|
// Created by Jarrod Norwell on 1/18/24.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "EmulationWindow.h"
|
||||||
|
#import "../EmulationSession/EmulationSession.h"
|
||||||
|
|
||||||
|
#include <SDL.h>
|
||||||
|
|
||||||
|
#include "common/logging/log.h"
|
||||||
|
#include "input_common/drivers/touch_screen.h"
|
||||||
|
#include "input_common/drivers/virtual_amiibo.h"
|
||||||
|
#include "input_common/drivers/virtual_gamepad.h"
|
||||||
|
#include "input_common/main.h"
|
||||||
|
|
||||||
|
void EmulationWindow::OnSurfaceChanged(CA::MetalLayer* surface, CGSize size) {
|
||||||
|
m_size = size;
|
||||||
|
|
||||||
|
m_window_width = size.width;
|
||||||
|
m_window_height = size.height;
|
||||||
|
|
||||||
|
// Ensures that we emulate with the correct aspect ratio.
|
||||||
|
// UpdateCurrentFramebufferLayout(m_window_width, m_window_height);
|
||||||
|
|
||||||
|
window_info.render_surface = reinterpret_cast<void*>(surface);
|
||||||
|
window_info.render_surface_scale = [[UIScreen mainScreen] nativeScale];
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmulationWindow::OrientationChanged(UIInterfaceOrientation orientation) {
|
||||||
|
is_portrait = orientation == UIInterfaceOrientationPortrait;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmulationWindow::OnTouchPressed(int id, float x, float y) {
|
||||||
|
const auto [touch_x, touch_y] = MapToTouchScreen(x, y);
|
||||||
|
EmulationSession::GetInstance().GetInputSubsystem().GetTouchScreen()->TouchPressed(touch_x,
|
||||||
|
touch_y, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmulationWindow::OnTouchMoved(int id, float x, float y) {
|
||||||
|
const auto [touch_x, touch_y] = MapToTouchScreen(x, y);
|
||||||
|
EmulationSession::GetInstance().GetInputSubsystem().GetTouchScreen()->TouchMoved(touch_x,
|
||||||
|
touch_y, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmulationWindow::OnTouchReleased(int id) {
|
||||||
|
EmulationSession::GetInstance().GetInputSubsystem().GetTouchScreen()->TouchReleased(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmulationWindow::OnGamepadButtonEvent(int player_index, int button_id, bool pressed) {
|
||||||
|
m_input_subsystem->GetVirtualGamepad()->SetButtonState(player_index, button_id, pressed);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmulationWindow::OnGamepadJoystickEvent(int player_index, int stick_id, float x, float y) {
|
||||||
|
m_input_subsystem->GetVirtualGamepad()->SetStickPosition(player_index, stick_id, x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmulationWindow::OnGamepadMotionEvent(int player_index, u64 delta_timestamp, float gyro_x,
|
||||||
|
float gyro_y, float gyro_z, float accel_x,
|
||||||
|
float accel_y, float accel_z) {
|
||||||
|
m_input_subsystem->GetVirtualGamepad()->SetMotionState(player_index, delta_timestamp, gyro_x, gyro_y, gyro_z, accel_x, accel_y, accel_z);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmulationWindow::OnFrameDisplayed() {
|
||||||
|
if (!m_first_frame) {
|
||||||
|
m_first_frame = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
EmulationWindow::EmulationWindow(InputCommon::InputSubsystem* input_subsystem, CA::MetalLayer* surface, CGSize size,
|
||||||
|
std::shared_ptr<Common::DynamicLibrary> driver_library)
|
||||||
|
: m_input_subsystem{input_subsystem}, m_size{size}, m_driver_library{driver_library} {
|
||||||
|
LOG_INFO(Frontend, "initializing");
|
||||||
|
|
||||||
|
if (!surface) {
|
||||||
|
LOG_CRITICAL(Frontend, "surface is nullptr");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
OnSurfaceChanged(surface, m_size);
|
||||||
|
window_info.render_surface_scale = [[UIScreen mainScreen] nativeScale];
|
||||||
|
window_info.type = Core::Frontend::WindowSystemType::Cocoa;
|
||||||
|
|
||||||
|
m_input_subsystem->Initialize();
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue