Add Mii Maker (and try and fail to add the Software controller)

This commit is contained in:
Stossy11 2024-12-19 10:31:56 +11:00
parent c5736f9b15
commit 438c1a896f
19 changed files with 1674 additions and 38 deletions

View File

@ -8,7 +8,7 @@
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
4E0DED342D05695D00FEF007 /* SwiftUIJoystick in Frameworks */ = {isa = PBXBuildFile; productRef = 4E0DED332D05695D00FEF007 /* SwiftUIJoystick */; }; 4E0DED342D05695D00FEF007 /* SwiftUIJoystick in Frameworks */ = {isa = PBXBuildFile; productRef = 4E0DED332D05695D00FEF007 /* SwiftUIJoystick */; };
4E551F202CF128540096A2DF /* GameController.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4E80AA622CD7122800029585 /* GameController.framework */; }; 4E4854022D138D7600A446A6 /* GameController.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4E80AA622CD7122800029585 /* GameController.framework */; };
/* End PBXBuildFile section */ /* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */ /* Begin PBXContainerItemProxy section */
@ -65,6 +65,10 @@
"Dependencies/Dynamic Libraries/Ryujinx.Headless.SDL2.dylib" = ( "Dependencies/Dynamic Libraries/Ryujinx.Headless.SDL2.dylib" = (
CodeSignOnCopy, CodeSignOnCopy,
); );
"Dependencies/Dynamic Libraries/SoftwareKeyboard.framework" = (
CodeSignOnCopy,
RemoveHeadersOnCopy,
);
"Dependencies/Dynamic Libraries/libMoltenVK.dylib" = ( "Dependencies/Dynamic Libraries/libMoltenVK.dylib" = (
CodeSignOnCopy, CodeSignOnCopy,
); );
@ -121,6 +125,7 @@
"Dependencies/Dynamic Libraries/libavutil.dylib", "Dependencies/Dynamic Libraries/libavutil.dylib",
"Dependencies/Dynamic Libraries/libMoltenVK.dylib", "Dependencies/Dynamic Libraries/libMoltenVK.dylib",
"Dependencies/Dynamic Libraries/Ryujinx.Headless.SDL2.dylib", "Dependencies/Dynamic Libraries/Ryujinx.Headless.SDL2.dylib",
"Dependencies/Dynamic Libraries/SoftwareKeyboard.framework",
Dependencies/XCFrameworks/libavcodec.xcframework, Dependencies/XCFrameworks/libavcodec.xcframework,
Dependencies/XCFrameworks/libavfilter.xcframework, Dependencies/XCFrameworks/libavfilter.xcframework,
Dependencies/XCFrameworks/libavformat.xcframework, Dependencies/XCFrameworks/libavformat.xcframework,
@ -146,8 +151,8 @@
isa = PBXFrameworksBuildPhase; isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
4E4854022D138D7600A446A6 /* GameController.framework in Frameworks */,
4E0DED342D05695D00FEF007 /* SwiftUIJoystick in Frameworks */, 4E0DED342D05695D00FEF007 /* SwiftUIJoystick in Frameworks */,
4E551F202CF128540096A2DF /* GameController.framework in Frameworks */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@ -515,6 +520,8 @@
FRAMEWORK_SEARCH_PATHS = ( FRAMEWORK_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"$(PROJECT_DIR)/MeloNX/Dependencies/XCFrameworks", "$(PROJECT_DIR)/MeloNX/Dependencies/XCFrameworks",
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
); );
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = MeloNX/Info.plist; INFOPLIST_FILE = MeloNX/Info.plist;
@ -643,6 +650,10 @@
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
); );
MARKETING_VERSION = 0.0.8; MARKETING_VERSION = 0.0.8;
PRODUCT_BUNDLE_IDENTIFIER = com.stossy11.MeloNX; PRODUCT_BUNDLE_IDENTIFIER = com.stossy11.MeloNX;
@ -669,6 +680,8 @@
FRAMEWORK_SEARCH_PATHS = ( FRAMEWORK_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"$(PROJECT_DIR)/MeloNX/Dependencies/XCFrameworks", "$(PROJECT_DIR)/MeloNX/Dependencies/XCFrameworks",
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
); );
GCC_OPTIMIZATION_LEVEL = 3; GCC_OPTIMIZATION_LEVEL = 3;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
@ -798,6 +811,10 @@
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
); );
MARKETING_VERSION = 0.0.8; MARKETING_VERSION = 0.0.8;
PRODUCT_BUNDLE_IDENTIFIER = com.stossy11.MeloNX; PRODUCT_BUNDLE_IDENTIFIER = com.stossy11.MeloNX;

View File

@ -14,8 +14,8 @@
filePath = "MeloNX/Views/GamesList/GameListView.swift" filePath = "MeloNX/Views/GamesList/GameListView.swift"
startingColumnNumber = "9223372036854775807" startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807"
startingLineNumber = "264" startingLineNumber = "271"
endingLineNumber = "264" endingLineNumber = "271"
landmarkName = "loadGames()" landmarkName = "loadGames()"
landmarkType = "7"> landmarkType = "7">
</BreakpointContent> </BreakpointContent>

View File

@ -0,0 +1,18 @@
//
// SoftwareKeyboard.h
// SoftwareKeyboard
//
// Created by Stossy11 on 19/12/2024.
//
#import <Foundation/Foundation.h>
//! Project version number for SoftwareKeyboard.
FOUNDATION_EXPORT double SoftwareKeyboardVersionNumber;
//! Project version string for SoftwareKeyboard.
FOUNDATION_EXPORT const unsigned char SoftwareKeyboardVersionString[];
// In this header, you should import all the public headers of your framework using statements like #import <SoftwareKeyboard/PublicHeader.h>

View File

@ -0,0 +1,38 @@
// swift-interface-format-version: 1.0
// swift-compiler-version: Apple Swift version 6.0.3 effective-5.10 (swiftlang-6.0.3.1.4 clang-1600.0.30)
// swift-module-flags: -target arm64-apple-ios14.0 -enable-objc-interop -enable-library-evolution -swift-version 5 -enforce-exclusivity=checked -Onone -enable-experimental-feature OpaqueTypeErasure -enable-bare-slash-regex -module-name SoftwareKeyboard
@_exported import SoftwareKeyboard
import Swift
import UIKit
import _Concurrency
import _StringProcessing
import _SwiftConcurrencyShims
@objc public enum KeyboardMode : Swift.UInt32 {
case `default` = 0
case numeric = 1
case ascii = 2
case fullLatin = 3
case alphabet = 4
case simplifiedChinese = 5
case traditionalChinese = 6
case korean = 7
case languageSet2 = 8
case languageSet2Latin = 9
public init?(rawValue: Swift.UInt32)
public typealias RawValue = Swift.UInt32
public var rawValue: Swift.UInt32 {
get
}
}
public struct SoftwareKeyboardUiArgs {
public var keyboardMode: SoftwareKeyboard.KeyboardMode
public var headerText: Swift.String
public var subtitleText: Swift.String
public var submitText: Swift.String
public var stringLengthMin: Swift.Int32
public var stringLengthMax: Swift.Int32
public var initialText: Swift.String?
}
extension SoftwareKeyboard.KeyboardMode : Swift.Equatable {}
extension SoftwareKeyboard.KeyboardMode : Swift.Hashable {}
extension SoftwareKeyboard.KeyboardMode : Swift.RawRepresentable {}

View File

@ -0,0 +1,38 @@
// swift-interface-format-version: 1.0
// swift-compiler-version: Apple Swift version 6.0.3 effective-5.10 (swiftlang-6.0.3.1.4 clang-1600.0.30)
// swift-module-flags: -target arm64-apple-ios14.0 -enable-objc-interop -enable-library-evolution -swift-version 5 -enforce-exclusivity=checked -Onone -enable-experimental-feature OpaqueTypeErasure -enable-bare-slash-regex -module-name SoftwareKeyboard
@_exported import SoftwareKeyboard
import Swift
import UIKit
import _Concurrency
import _StringProcessing
import _SwiftConcurrencyShims
@objc public enum KeyboardMode : Swift.UInt32 {
case `default` = 0
case numeric = 1
case ascii = 2
case fullLatin = 3
case alphabet = 4
case simplifiedChinese = 5
case traditionalChinese = 6
case korean = 7
case languageSet2 = 8
case languageSet2Latin = 9
public init?(rawValue: Swift.UInt32)
public typealias RawValue = Swift.UInt32
public var rawValue: Swift.UInt32 {
get
}
}
public struct SoftwareKeyboardUiArgs {
public var keyboardMode: SoftwareKeyboard.KeyboardMode
public var headerText: Swift.String
public var subtitleText: Swift.String
public var submitText: Swift.String
public var stringLengthMin: Swift.Int32
public var stringLengthMax: Swift.Int32
public var initialText: Swift.String?
}
extension SoftwareKeyboard.KeyboardMode : Swift.Equatable {}
extension SoftwareKeyboard.KeyboardMode : Swift.Hashable {}
extension SoftwareKeyboard.KeyboardMode : Swift.RawRepresentable {}

View File

@ -0,0 +1,6 @@
framework module SoftwareKeyboard {
umbrella header "SoftwareKeyboard.h"
export *
module * { export * }
}

View File

@ -12,6 +12,7 @@ import Darwin
import UIKit import UIKit
import MetalKit import MetalKit
// import SDL // import SDL
import SoftwareKeyboard
struct MoltenVKSettings: Codable, Hashable { struct MoltenVKSettings: Codable, Hashable {
let string: String let string: String

View File

@ -134,6 +134,13 @@ struct GameLibraryView: View {
} label: { } label: {
Text("Remove Firmware") Text("Remove Firmware")
} }
Button {
self.startemu = URL(string: "MiiMaker")
} label: {
Text("Mii Maker")
}
} }
Button { Button {
@ -147,7 +154,7 @@ struct GameLibraryView: View {
Text("Show MeloNX Folder") Text("Show MeloNX Folder")
} }
} label: { } label: {
Image(systemName: "plus") Image(systemName: "ellipsis")
.foregroundColor(.blue) .foregroundColor(.blue)
} }
} }

View File

@ -19,7 +19,7 @@ namespace Ryujinx.HLE.HOS.Applets
{ {
internal class SoftwareKeyboardApplet : IApplet internal class SoftwareKeyboardApplet : IApplet
{ {
private const string DefaultInputText = "Ryujinx"; private const string DefaultInputText = "MeloNX";
private const int StandardBufferSize = 0x7D8; private const int StandardBufferSize = 0x7D8;
private const int InteractiveBufferSize = 0x7D4; private const int InteractiveBufferSize = 0x7D4;
@ -180,6 +180,10 @@ namespace Ryujinx.HLE.HOS.Applets
return _keyboardRenderer?.DrawTo(destination, position) ?? false; return _keyboardRenderer?.DrawTo(destination, position) ?? false;
} }
[DllImport("SoftwareKeyboard.framework/SoftwareKeyboard", EntryPoint = "displayInputDialog", CallingConvention = CallingConvention.Cdecl)]
public static extern void DisplayInputDialog(ref SoftwareKeyboardUiArgs args, out IntPtr userInput);
private void ExecuteForegroundKeyboard() private void ExecuteForegroundKeyboard()
{ {
string initialText = null; string initialText = null;
@ -188,23 +192,57 @@ namespace Ryujinx.HLE.HOS.Applets
// InitialStringOffset points to the memory offset and InitialStringLength is the number of UTF-16 characters // InitialStringOffset points to the memory offset and InitialStringLength is the number of UTF-16 characters
if (_transferMemory != null && _keyboardForegroundConfig.InitialStringLength > 0) if (_transferMemory != null && _keyboardForegroundConfig.InitialStringLength > 0)
{ {
initialText = Encoding.Unicode.GetString(_transferMemory, _keyboardForegroundConfig.InitialStringOffset, initialText = Encoding.Unicode.GetString(
_transferMemory,
_keyboardForegroundConfig.InitialStringOffset,
2 * _keyboardForegroundConfig.InitialStringLength); 2 * _keyboardForegroundConfig.InitialStringLength);
} }
// If the max string length is 0, we set it to a large default // If the max string length is 0, we set it to a large default length.
// length.
if (_keyboardForegroundConfig.StringLengthMax == 0) if (_keyboardForegroundConfig.StringLengthMax == 0)
{ {
_keyboardForegroundConfig.StringLengthMax = 100; _keyboardForegroundConfig.StringLengthMax = 100;
} }
// If no GUI handler is set, fallback to default behavior.
if (_device.UiHandler == null) if (_device.UiHandler == null)
{ {
Logger.Warning?.Print(LogClass.Application, "GUI Handler is not set. Falling back to default"); Logger.Warning?.Print(LogClass.Application, "GUI Handler is not set. Falling back to default");
_textValue = DefaultInputText; // Prepare the SoftwareKeyboardUiArgs struct
_lastResult = KeyboardResult.Accept; var args = new SoftwareKeyboardUiArgs
{
KeyboardMode = _keyboardForegroundConfig.Mode,
HeaderText = StripUnicodeControlCodes(_keyboardForegroundConfig.HeaderText),
SubtitleText = StripUnicodeControlCodes(_keyboardForegroundConfig.SubtitleText),
SubmitText = !string.IsNullOrWhiteSpace(_keyboardForegroundConfig.SubmitText)
? _keyboardForegroundConfig.SubmitText
: "OK",
StringLengthMin = _keyboardForegroundConfig.StringLengthMin,
StringLengthMax = _keyboardForegroundConfig.StringLengthMax,
InitialText = initialText,
};
IntPtr userInputPtr;
DisplayInputDialog(ref args, out userInputPtr);
if (userInputPtr != IntPtr.Zero)
{
// Convert the IntPtr to a string
string userInput = Marshal.PtrToStringAnsi(userInputPtr);
_textValue = userInput ?? DefaultInputText;
_lastResult = KeyboardResult.Accept;
Console.WriteLine($"User input: {userInput}");
}
else
{
Console.WriteLine("No input was received or input was canceled.");
_textValue = DefaultInputText;
_lastResult = KeyboardResult.Cancel;
}
} }
else else
{ {
@ -226,46 +264,36 @@ namespace Ryujinx.HLE.HOS.Applets
_textValue ??= initialText ?? DefaultInputText; _textValue ??= initialText ?? DefaultInputText;
} }
// If the game requests a string with a minimum length less // Ensure the text meets the minimum length requirement
// than our default text, repeat our default text until we meet
// the minimum length requirement.
// This should always be done before the text truncation step.
while (_textValue.Length < _keyboardForegroundConfig.StringLengthMin) while (_textValue.Length < _keyboardForegroundConfig.StringLengthMin)
{ {
_textValue = String.Join(" ", _textValue, _textValue); _textValue = string.Join(" ", _textValue, _textValue);
} }
// If our default text is longer than the allowed length, // Truncate the text if it exceeds the maximum length
// we truncate it.
if (_textValue.Length > _keyboardForegroundConfig.StringLengthMax) if (_textValue.Length > _keyboardForegroundConfig.StringLengthMax)
{ {
_textValue = _textValue[.._keyboardForegroundConfig.StringLengthMax]; _textValue = _textValue[.._keyboardForegroundConfig.StringLengthMax];
} }
// Does the application want to validate the text itself? // Handle text validation if required
if (_keyboardForegroundConfig.CheckText) if (_keyboardForegroundConfig.CheckText)
{ {
// The application needs to validate the response, so we // Submit text for validation
// submit it to the interactive output buffer, and poll it
// for validation. Once validated, the application will submit
// back a validation status, which is handled in OnInteractiveDataPushIn.
_foregroundState = SoftwareKeyboardState.ValidationPending; _foregroundState = SoftwareKeyboardState.ValidationPending;
PushForegroundResponse(true); PushForegroundResponse(true);
} }
else else
{ {
// If the application doesn't need to validate the response, // Submit text as complete
// we push the data to the non-interactive output buffer
// and poll it for completion.
_foregroundState = SoftwareKeyboardState.Complete; _foregroundState = SoftwareKeyboardState.Complete;
PushForegroundResponse(false); PushForegroundResponse(false);
AppletStateChanged?.Invoke(this, null); AppletStateChanged?.Invoke(this, null);
} }
} }
private void OnInteractiveData(object sender, EventArgs e) private void OnInteractiveData(object sender, EventArgs e)
{ {
// Obtain the validation status response. // Obtain the validation status response.

View File

@ -40,7 +40,7 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc
{ {
byte[] defaultUserImage = EmbeddedResources.Read("Ryujinx.HLE/HOS/Services/Account/Acc/DefaultUserImage.jpg"); byte[] defaultUserImage = EmbeddedResources.Read("Ryujinx.HLE/HOS/Services/Account/Acc/DefaultUserImage.jpg");
AddUser("RyuPlayer", defaultUserImage, DefaultUserId); AddUser("MeloNX", defaultUserImage, DefaultUserId);
OpenUser(DefaultUserId); OpenUser(DefaultUserId);
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 16 KiB

View File

@ -1100,6 +1100,12 @@ namespace Ryujinx.Headless.SDL2
return; return;
} }
if (option.InputPath == "MiiMaker") {
string contentPath = _contentManager.GetInstalledContentPath(0x0100000000001009, StorageId.BuiltInSystem, NcaContentType.Program);
option.InputPath = contentPath;
}
_inputConfiguration = new List<InputConfig>(); _inputConfiguration = new List<InputConfig>();
_enableKeyboard = option.EnableKeyboard; _enableKeyboard = option.EnableKeyboard;
_enableMouse = option.EnableMouse; _enableMouse = option.EnableMouse;
@ -1324,6 +1330,17 @@ namespace Ryujinx.Headless.SDL2
Logger.Notice.Print(LogClass.Application, $"Using Firmware Version: {firmwareVersion?.VersionString}"); Logger.Notice.Print(LogClass.Application, $"Using Firmware Version: {firmwareVersion?.VersionString}");
bool isFirmwareTitle = false;
if (path.StartsWith("@SystemContent"))
{
path = VirtualFileSystem.SwitchPathToSystemPath(path);
isFirmwareTitle = true;
}
if (Directory.Exists(path)) if (Directory.Exists(path))
{ {
string[] romFsFiles = Directory.GetFiles(path, "*.istorage"); string[] romFsFiles = Directory.GetFiles(path, "*.istorage");
@ -1392,23 +1409,35 @@ namespace Ryujinx.Headless.SDL2
} }
break; break;
default: default:
Logger.Info?.Print(LogClass.Application, "Loading as Homebrew."); if (isFirmwareTitle) {
try Logger.Info?.Print(LogClass.Application, "Loading as Firmware Title (NCA).");
{
if (!_emulationContext.LoadProgram(path)) if (!_emulationContext.LoadNca(path))
{ {
_emulationContext.Dispose(); _emulationContext.Dispose();
return false; return false;
} }
} }
catch (ArgumentOutOfRangeException) else {
{ Logger.Info?.Print(LogClass.Application, "Loading as Homebrew.");
Logger.Error?.Print(LogClass.Application, "The specified file is not supported by Ryujinx."); try
{
if (!_emulationContext.LoadProgram(path))
{
_emulationContext.Dispose();
_emulationContext.Dispose(); return false;
}
}
catch (ArgumentOutOfRangeException)
{
Logger.Error?.Print(LogClass.Application, "The specified file is not supported by Ryujinx.");
return false; _emulationContext.Dispose();
return false;
}
} }
break; break;
} }