Added a lot of stuff lmao
@ -131,7 +131,7 @@
|
||||
38C8467F2C1DCD2200331706 /* VirtualControllerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VirtualControllerView.swift; sourceTree = "<group>"; };
|
||||
38C846962C1DCDC100331706 /* EmulationVirtualControllerController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmulationVirtualControllerController.swift; sourceTree = "<group>"; };
|
||||
38C846972C1DCDC100331706 /* EmulationScreensController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmulationScreensController.swift; sourceTree = "<group>"; };
|
||||
38C8469C2C1DCDC100331706 /* SudachiEmulationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SudachiEmulationController.swift; sourceTree = "<group>"; };
|
||||
38C8469C2C1DCDC100331706 /* SudachiEmulationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SudachiEmulationController.swift; sourceTree = "<group>"; wrapsLines = 1; };
|
||||
38C846A12C1DCDC100331706 /* INIEditController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = INIEditController.swift; sourceTree = "<group>"; };
|
||||
38C846AC2C1DCE8900331706 /* MoltenVK.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; path = MoltenVK.xcframework; sourceTree = "<group>"; };
|
||||
38C846AD2C1DCE8900331706 /* libavcodec.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; path = libavcodec.xcframework; sourceTree = "<group>"; };
|
||||
@ -670,6 +670,7 @@
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = Pomelo/Pomelo.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
@ -708,6 +709,7 @@
|
||||
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
|
||||
SUPPORTS_MACCATALYST = NO;
|
||||
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES;
|
||||
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = YES;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
@ -719,6 +721,7 @@
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = Pomelo/Pomelo.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
@ -757,6 +760,7 @@
|
||||
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
|
||||
SUPPORTS_MACCATALYST = NO;
|
||||
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES;
|
||||
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = YES;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
|
@ -14,8 +14,8 @@
|
||||
filePath = "Pomelo/Classes/LibraryManager.swift"
|
||||
startingColumnNumber = "9223372036854775807"
|
||||
endingColumnNumber = "9223372036854775807"
|
||||
startingLineNumber = "143"
|
||||
endingLineNumber = "143"
|
||||
startingLineNumber = "145"
|
||||
endingLineNumber = "145"
|
||||
landmarkName = "createMissingDirectoriesInDocumentsDirectory()"
|
||||
landmarkType = "7">
|
||||
</BreakpointContent>
|
||||
|
BIN
Pomelo/.DS_Store
vendored
BIN
Pomelo/Assets.xcassets/.DS_Store
vendored
38
Pomelo/Assets.xcassets/AppIcon-1.appiconset/Contents.json
Normal file
@ -0,0 +1,38 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "IMG_1799 1.jpg",
|
||||
"idiom" : "universal",
|
||||
"platform" : "ios",
|
||||
"size" : "1024x1024"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"filename" : "IMG_1808.jpg",
|
||||
"idiom" : "universal",
|
||||
"platform" : "ios",
|
||||
"size" : "1024x1024"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "tinted"
|
||||
}
|
||||
],
|
||||
"filename" : "IMG_1799.jpg",
|
||||
"idiom" : "universal",
|
||||
"platform" : "ios",
|
||||
"size" : "1024x1024"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
BIN
Pomelo/Assets.xcassets/AppIcon-1.appiconset/IMG_1799 1.jpg
Normal file
After Width: | Height: | Size: 118 KiB |
BIN
Pomelo/Assets.xcassets/AppIcon-1.appiconset/IMG_1799.jpg
Normal file
After Width: | Height: | Size: 118 KiB |
BIN
Pomelo/Assets.xcassets/AppIcon-1.appiconset/IMG_1808.jpg
Normal file
After Width: | Height: | Size: 102 KiB |
21
Pomelo/Assets.xcassets/AppIcon-Secondary-inapp.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "IMG_1800.jpg",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
BIN
Pomelo/Assets.xcassets/AppIcon-Secondary-inapp.imageset/IMG_1800.jpg
vendored
Normal file
After Width: | Height: | Size: 121 KiB |
21
Pomelo/Assets.xcassets/AppIcon-inapp.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "IMG_1799.jpg",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
BIN
Pomelo/Assets.xcassets/AppIcon-inapp.imageset/IMG_1799.jpg
vendored
Normal file
After Width: | Height: | Size: 118 KiB |
21
Pomelo/Assets.xcassets/AppIcon-inverted-inapp.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "image 1.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
BIN
Pomelo/Assets.xcassets/AppIcon-inverted-inapp.imageset/image 1.png
vendored
Normal file
After Width: | Height: | Size: 1.1 MiB |
@ -1,54 +1,34 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "mac",
|
||||
"scale" : "1x",
|
||||
"size" : "16x16"
|
||||
"filename" : "IMG_1799 1.jpg",
|
||||
"idiom" : "universal",
|
||||
"platform" : "ios",
|
||||
"size" : "1024x1024"
|
||||
},
|
||||
{
|
||||
"idiom" : "mac",
|
||||
"scale" : "2x",
|
||||
"size" : "16x16"
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"filename" : "IMG_1808.jpg",
|
||||
"idiom" : "universal",
|
||||
"platform" : "ios",
|
||||
"size" : "1024x1024"
|
||||
},
|
||||
{
|
||||
"idiom" : "mac",
|
||||
"scale" : "1x",
|
||||
"size" : "32x32"
|
||||
},
|
||||
{
|
||||
"idiom" : "mac",
|
||||
"scale" : "2x",
|
||||
"size" : "32x32"
|
||||
},
|
||||
{
|
||||
"idiom" : "mac",
|
||||
"scale" : "1x",
|
||||
"size" : "128x128"
|
||||
},
|
||||
{
|
||||
"idiom" : "mac",
|
||||
"scale" : "2x",
|
||||
"size" : "128x128"
|
||||
},
|
||||
{
|
||||
"idiom" : "mac",
|
||||
"scale" : "1x",
|
||||
"size" : "256x256"
|
||||
},
|
||||
{
|
||||
"idiom" : "mac",
|
||||
"scale" : "2x",
|
||||
"size" : "256x256"
|
||||
},
|
||||
{
|
||||
"idiom" : "mac",
|
||||
"scale" : "1x",
|
||||
"size" : "512x512"
|
||||
},
|
||||
{
|
||||
"idiom" : "mac",
|
||||
"scale" : "2x",
|
||||
"size" : "512x512"
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "tinted"
|
||||
}
|
||||
],
|
||||
"filename" : "IMG_1799.jpg",
|
||||
"idiom" : "universal",
|
||||
"platform" : "ios",
|
||||
"size" : "1024x1024"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
|
BIN
Pomelo/Assets.xcassets/AppIcon.appiconset/IMG_1799 1.jpg
Normal file
After Width: | Height: | Size: 118 KiB |
BIN
Pomelo/Assets.xcassets/AppIcon.appiconset/IMG_1799.jpg
Normal file
After Width: | Height: | Size: 118 KiB |
BIN
Pomelo/Assets.xcassets/AppIcon.appiconset/IMG_1808.jpg
Normal file
After Width: | Height: | Size: 102 KiB |
@ -0,0 +1,38 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "IMG_1800.jpg",
|
||||
"idiom" : "universal",
|
||||
"platform" : "ios",
|
||||
"size" : "1024x1024"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"filename" : "IMG_1800 1.jpg",
|
||||
"idiom" : "universal",
|
||||
"platform" : "ios",
|
||||
"size" : "1024x1024"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "tinted"
|
||||
}
|
||||
],
|
||||
"filename" : "IMG_1800 2.jpg",
|
||||
"idiom" : "universal",
|
||||
"platform" : "ios",
|
||||
"size" : "1024x1024"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 121 KiB |
After Width: | Height: | Size: 121 KiB |
BIN
Pomelo/Assets.xcassets/Appicon-Secondary.appiconset/IMG_1800.jpg
Normal file
After Width: | Height: | Size: 121 KiB |
@ -0,0 +1,38 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "image.png",
|
||||
"idiom" : "universal",
|
||||
"platform" : "ios",
|
||||
"size" : "1024x1024"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"filename" : "image 1.png",
|
||||
"idiom" : "universal",
|
||||
"platform" : "ios",
|
||||
"size" : "1024x1024"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "tinted"
|
||||
}
|
||||
],
|
||||
"filename" : "image 2.png",
|
||||
"idiom" : "universal",
|
||||
"platform" : "ios",
|
||||
"size" : "1024x1024"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
BIN
Pomelo/Assets.xcassets/Appicon-inverted.appiconset/image 1.png
Normal file
After Width: | Height: | Size: 1.1 MiB |
BIN
Pomelo/Assets.xcassets/Appicon-inverted.appiconset/image 2.png
Normal file
After Width: | Height: | Size: 1.1 MiB |
BIN
Pomelo/Assets.xcassets/Appicon-inverted.appiconset/image.png
Normal file
After Width: | Height: | Size: 1.1 MiB |
BIN
Pomelo/Classes/.DS_Store
vendored
@ -75,12 +75,13 @@ struct Core : Comparable, Hashable {
|
||||
var colors: [VirtualControllerButton.ButtonType: UIColor] = [:]
|
||||
|
||||
// Load theme
|
||||
if let theme = ThemeLoader.shared.loadTheme() {
|
||||
if let theme = ThemeLoader.shared.loadThemes() {
|
||||
colors = [
|
||||
.a: theme.color(for: .a) ?? .systemGray,
|
||||
.b: theme.color(for: .b) ?? .systemGray,
|
||||
.x: theme.color(for: .x) ?? .systemGray,
|
||||
.y: theme.color(for: .y) ?? .systemGray,
|
||||
|
||||
// Add more buttons as needed
|
||||
]
|
||||
} else {
|
||||
@ -115,7 +116,8 @@ class DirectoriesManager {
|
||||
|
||||
func directories() -> [String : [String : MissingFile.FileImportance]] {
|
||||
[
|
||||
"themes" : ["theme.json": .optional],
|
||||
"themes" : [
|
||||
"theme.json": .optional],
|
||||
"amiibo" : [:],
|
||||
"cache" : [:],
|
||||
"config" : [:],
|
||||
@ -147,11 +149,17 @@ class DirectoriesManager {
|
||||
try FileManager.default.createDirectory(at: coreDirectory, withIntermediateDirectories: false)
|
||||
}
|
||||
|
||||
var isDirectory: ObjCBool = true
|
||||
|
||||
// Create theme.json file if it doesn't exist
|
||||
if directory == "themes" {
|
||||
let themeFileURL = coreDirectory.appendingPathComponent("theme.json")
|
||||
let themeimagefoler = coreDirectory.appendingPathComponent("images/")
|
||||
if !FileManager.default.fileExists(atPath: themeimagefoler.path, isDirectory: &isDirectory) {
|
||||
try FileManager.default.createDirectory(at: themeimagefoler, withIntermediateDirectories: false)
|
||||
}
|
||||
if !FileManager.default.fileExists(atPath: themeFileURL.path) {
|
||||
let defaultTheme = Theme(color: "", a: "", b: "", x: "", y: "")
|
||||
let defaultTheme = Theme(background: "", a: "", b: "", x: "", y: "")
|
||||
if let jsonData = try? JSONEncoder().encode(defaultTheme) {
|
||||
FileManager.default.createFile(atPath: themeFileURL.path, contents: jsonData, attributes: nil)
|
||||
}
|
||||
@ -163,18 +171,14 @@ class DirectoriesManager {
|
||||
func scanDirectoriesForRequiredFiles(for core: inout Core) {
|
||||
let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
|
||||
|
||||
directories().forEach { directory in
|
||||
directory.value.forEach { subdirectory, fileNames in
|
||||
let coreSubdirectory = documentsDirectory.appendingPathComponent(directory.key, conformingTo: .folder)
|
||||
.appendingPathComponent(subdirectory, conformingTo: .folder)
|
||||
directories().forEach { directory, fileNames in
|
||||
let coreDirectory = documentsDirectory.appendingPathComponent(directory, conformingTo: .folder)
|
||||
|
||||
fileNames.forEach { (fileName, fileImportance) in
|
||||
let fileURL = coreDirectory.appendingPathComponent(fileName, conformingTo: .fileURL)
|
||||
|
||||
// Ensure fileNames is treated as a dictionary of [String: MissingFile.FileImportance]
|
||||
if let fileNamesDict = fileNames as? [String: MissingFile.FileImportance] {
|
||||
fileNamesDict.forEach { (fileName, fileImportance) in
|
||||
if !FileManager.default.fileExists(atPath: coreSubdirectory.appendingPathComponent(fileName, conformingTo: .fileURL).path) {
|
||||
core.missingFiles.append(.init(coreName: core.name, directory: coreSubdirectory, fileImportance: fileImportance, fileName: fileName))
|
||||
}
|
||||
}
|
||||
if !FileManager.default.fileExists(atPath: fileURL.path) {
|
||||
core.missingFiles.append(.init(coreName: core.name, directory: coreDirectory, fileImportance: fileImportance, fileName: fileName))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ import SwiftUI
|
||||
import Foundation
|
||||
|
||||
struct Theme: Codable {
|
||||
let color: String
|
||||
let background: String
|
||||
let a: String?
|
||||
let b: String?
|
||||
let x: String?
|
||||
@ -37,10 +37,10 @@ struct Theme: Codable {
|
||||
class ThemeLoader {
|
||||
static let shared = ThemeLoader()
|
||||
|
||||
func loadTheme(completion: @escaping (UIColor?) -> Void) {
|
||||
func loadTheme(completion: @escaping (UIColor?, URL?) -> Void) {
|
||||
let fileManager = FileManager.default
|
||||
guard let documentsDirectory = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first else {
|
||||
completion(nil)
|
||||
completion(nil, nil)
|
||||
print("Unable to get documents directory.")
|
||||
return
|
||||
}
|
||||
@ -51,21 +51,33 @@ class ThemeLoader {
|
||||
do {
|
||||
let data = try Data(contentsOf: themeURL)
|
||||
let theme = try JSONDecoder().decode(Theme.self, from: data)
|
||||
let color = UIColor(hex: theme.color)
|
||||
let fileExtension = (theme.background as NSString).pathExtension.lowercased()
|
||||
let color = UIColor(hex: theme.background)
|
||||
DispatchQueue.main.async {
|
||||
print("Loaded theme color: \(color) from \(themeURL)")
|
||||
completion(color)
|
||||
if fileExtension == "png" || fileExtension == "jpg" || fileExtension == "jpeg" {
|
||||
let themeURLs = documentsDirectory.appendingPathComponent("themes/images/\(theme.background)")
|
||||
if fileManager.fileExists(atPath: themeURLs.path) {
|
||||
print(themeURLs.path)
|
||||
completion(nil, themeURLs)
|
||||
} else {
|
||||
print("Unable To get image trying to load theme colors: \(color) from \(themeURL)")
|
||||
completion(color, nil)
|
||||
}
|
||||
} else {
|
||||
print("Loaded theme color: \(color) from \(themeURL)")
|
||||
completion(color, nil)
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
DispatchQueue.main.async {
|
||||
print("Error loading theme: \(error.localizedDescription)")
|
||||
completion(nil)
|
||||
completion(nil, nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func loadTheme() -> Theme? {
|
||||
func loadThemes() -> Theme? {
|
||||
let fileManager = FileManager.default
|
||||
guard let documentsDirectory = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first else {
|
||||
return nil
|
||||
@ -82,6 +94,10 @@ class ThemeLoader {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func LoadBackgroundImage() {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
extension UIColor {
|
||||
|
@ -55,15 +55,3 @@ struct ContentView: View {
|
||||
}
|
||||
|
||||
|
||||
struct BootOSView: View {
|
||||
@State var core: Core?
|
||||
var body: some View {
|
||||
Text("beans")
|
||||
.onAppear {
|
||||
if let core = core {
|
||||
let PomeloGame = SudachiGame(core: core, developer: "", fileURL: URL(string: "{")!, imageData: Data(), title: "")
|
||||
presentPomeloEmulation(PomeloGame: PomeloGame)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ struct ScreenConfiguration {
|
||||
static let cornerRadius: CGFloat = 10
|
||||
}
|
||||
|
||||
class EmulationScreensController : EmulationVirtualControllerController {
|
||||
class EmulationScreensController: EmulationVirtualControllerController {
|
||||
var primaryScreen, secondaryScreen: UIView!
|
||||
var primaryBlurredScreen, secondaryBlurredScreen: UIView!
|
||||
fileprivate var visualEffectView: UIVisualEffectView!
|
||||
@ -26,9 +26,23 @@ class EmulationScreensController : EmulationVirtualControllerController {
|
||||
|
||||
fileprivate var portraitConstraints, landscapeConstraints: [NSLayoutConstraint]!
|
||||
|
||||
fileprivate var customButtonPortraitConstraints, customButtonLandscapeConstraints: [NSLayoutConstraint]!
|
||||
|
||||
let customButton: UIButton = {
|
||||
let button = UIButton(type: .system)
|
||||
// Set the system image for the button
|
||||
let image = UIImage(systemName: "x.square")
|
||||
button.setImage(image, for: .normal)
|
||||
// Adjust button appearance if necessary
|
||||
button.tintColor = .white
|
||||
button.transform = CGAffineTransform(scaleX: 1.5, y: 1.5)
|
||||
button.translatesAutoresizingMaskIntoConstraints = false
|
||||
return button
|
||||
}()
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
view.backgroundColor = .systemBackground
|
||||
let userDefaults = UserDefaults.standard
|
||||
|
||||
visualEffectView = .init(effect: UIBlurEffect(style: .systemMaterial))
|
||||
visualEffectView.translatesAutoresizingMaskIntoConstraints = false
|
||||
@ -57,21 +71,30 @@ class EmulationScreensController : EmulationVirtualControllerController {
|
||||
if #available(iOS 17, *) {
|
||||
registerForTraitChanges([UITraitUserInterfaceStyle.self], action: #selector(traitDidChange))
|
||||
}
|
||||
|
||||
// Add the custom button
|
||||
if userDefaults.bool(forKey: "exitgame") {
|
||||
addCustomButton()
|
||||
|
||||
// Add the initial constraints for the custom button
|
||||
applyCustomButtonConstraints(for: view.bounds.size)
|
||||
}
|
||||
}
|
||||
|
||||
override func viewWillTransition(to size: CGSize, with coordinator: any UIViewControllerTransitionCoordinator) {
|
||||
super.viewWillTransition(to: size, with: coordinator)
|
||||
let userDefaults = UserDefaults.standard
|
||||
|
||||
if userDefaults.bool(forKey: "exitgame") {
|
||||
// Update the custom button constraints based on the new size
|
||||
applyCustomButtonConstraints(for: size)
|
||||
}
|
||||
|
||||
if userDefaults.bool(forKey: "isfullscreen") {
|
||||
if UIApplication.shared.statusBarOrientation == .portrait {
|
||||
view.removeConstraints(fullScreenConstraints)
|
||||
view.addConstraints(portraitConstraints)
|
||||
} else {
|
||||
view.removeConstraints(fullScreenConstraints)
|
||||
view.addConstraints(fullScreenConstraints)
|
||||
}
|
||||
view.removeConstraints(fullScreenConstraints)
|
||||
view.addConstraints(fullScreenConstraints)
|
||||
} else {
|
||||
if UIApplication.shared.statusBarOrientation == .portrait || UIApplication.shared.statusBarOrientation == .portraitUpsideDown {
|
||||
if UIApplication.shared.statusBarOrientation.isPortrait {
|
||||
view.removeConstraints(landscapeConstraints)
|
||||
view.addConstraints(portraitConstraints)
|
||||
} else {
|
||||
@ -111,13 +134,6 @@ class EmulationScreensController : EmulationVirtualControllerController {
|
||||
primaryScreen.bottomAnchor.constraint(equalTo: view.bottomAnchor)
|
||||
]
|
||||
|
||||
portraitConstraints = [
|
||||
primaryScreen.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 10),
|
||||
primaryScreen.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 10),
|
||||
primaryScreen.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -10),
|
||||
primaryScreen.heightAnchor.constraint(equalTo: primaryScreen.widthAnchor, multiplier: 9 / 16),
|
||||
]
|
||||
|
||||
view.addConstraints(fullScreenConstraints)
|
||||
}
|
||||
|
||||
@ -163,8 +179,7 @@ class EmulationScreensController : EmulationVirtualControllerController {
|
||||
primaryBlurredScreen.trailingAnchor.constraint(equalTo: primaryScreen.trailingAnchor, constant: 10)
|
||||
]
|
||||
|
||||
view.addConstraints(UIApplication.shared.statusBarOrientation == .portrait ||
|
||||
UIApplication.shared.statusBarOrientation == .portraitUpsideDown ? portraitConstraints : landscapeConstraints)
|
||||
view.addConstraints(UIApplication.shared.statusBarOrientation.isPortrait ? portraitConstraints : landscapeConstraints)
|
||||
}
|
||||
|
||||
@objc fileprivate func traitDidChange() {
|
||||
@ -198,4 +213,34 @@ class EmulationScreensController : EmulationVirtualControllerController {
|
||||
|
||||
return imageRef
|
||||
}
|
||||
|
||||
func addCustomButton() {
|
||||
customButton.translatesAutoresizingMaskIntoConstraints = false
|
||||
view.addSubview(customButton)
|
||||
|
||||
customButtonPortraitConstraints = [
|
||||
customButton.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 10),
|
||||
customButton.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -10),
|
||||
customButton.widthAnchor.constraint(equalToConstant: 100), // Increased width
|
||||
customButton.heightAnchor.constraint(equalToConstant: 100) // Increased height
|
||||
]
|
||||
|
||||
customButtonLandscapeConstraints = [
|
||||
customButton.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 10),
|
||||
customButton.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -10),
|
||||
customButton.widthAnchor.constraint(equalToConstant: 100), // Increased width
|
||||
customButton.heightAnchor.constraint(equalToConstant: 100) // Increased height
|
||||
]
|
||||
}
|
||||
|
||||
func applyCustomButtonConstraints(for size: CGSize) {
|
||||
view.removeConstraints(customButtonPortraitConstraints)
|
||||
view.removeConstraints(customButtonLandscapeConstraints)
|
||||
|
||||
if size.width > size.height {
|
||||
view.addConstraints(customButtonLandscapeConstraints)
|
||||
} else {
|
||||
view.addConstraints(customButtonPortraitConstraints)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -12,6 +12,7 @@ import Foundation
|
||||
import GameController
|
||||
import MetalKit.MTKView
|
||||
import UIKit
|
||||
import SwiftUI
|
||||
|
||||
class SudachiEmulationController : EmulationScreensController {
|
||||
fileprivate var thread: Thread!
|
||||
@ -28,7 +29,7 @@ class SudachiEmulationController : EmulationScreensController {
|
||||
sudachiGame = game
|
||||
|
||||
thread = .init(block: step)
|
||||
thread.name = "Sudachi"
|
||||
thread.name = "Pomelo"
|
||||
thread.qualityOfService = .userInteractive
|
||||
thread.threadPriority = 0.9
|
||||
}
|
||||
@ -39,14 +40,50 @@ class SudachiEmulationController : EmulationScreensController {
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
let oldsaved = UserDefaults.standard.color(forKey: "color")
|
||||
if let savedColor = oldsaved {
|
||||
print("\(savedColor)" + "fun theme")
|
||||
let userDefaults = UserDefaults.standard
|
||||
|
||||
if let urlString = userDefaults.string(forKey: "background") {
|
||||
let fileManager = FileManager.default
|
||||
let backgroundURL = URL(fileURLWithPath: urlString)
|
||||
|
||||
let exists = fileManager.fileExists(atPath: urlString)
|
||||
|
||||
if exists {
|
||||
if let image = UIImage(contentsOfFile: urlString) {
|
||||
let backgroundImageView = UIImageView(image: image)
|
||||
backgroundImageView.contentMode = .scaleAspectFill
|
||||
self.view.addSubview(backgroundImageView)
|
||||
self.view.sendSubviewToBack(backgroundImageView)
|
||||
} else {
|
||||
print("Error: Unable to load image from path: \(urlString)")
|
||||
if let savedColor = userDefaults.color(forKey: "color") {
|
||||
print("\(savedColor)" + " fun theme")
|
||||
view.backgroundColor = savedColor
|
||||
} else {
|
||||
print("fun theme nil")
|
||||
view.backgroundColor = .systemBackground // Default color
|
||||
}
|
||||
}
|
||||
} else {
|
||||
print("Error: File does not exist or is a directory at path: \(urlString)")
|
||||
if let savedColor = userDefaults.color(forKey: "color") {
|
||||
print("\(savedColor)" + " fun theme")
|
||||
view.backgroundColor = savedColor
|
||||
} else {
|
||||
print("fun theme nil")
|
||||
view.backgroundColor = .systemBackground // Default color
|
||||
}
|
||||
}
|
||||
} else if let savedColor = userDefaults.color(forKey: "color") {
|
||||
print("\(savedColor)" + " fun theme")
|
||||
view.backgroundColor = savedColor
|
||||
} else {
|
||||
print("\(oldsaved)" + "fun theme nil")
|
||||
print("fun theme nil")
|
||||
view.backgroundColor = .systemBackground // Default color
|
||||
}
|
||||
if userDefaults.bool(forKey: "exitgame") {
|
||||
customButton.addTarget(self, action: #selector(customButtonTapped), for: .touchUpInside)
|
||||
}
|
||||
}
|
||||
|
||||
override func viewDidLayoutSubviews() {
|
||||
@ -80,6 +117,18 @@ class SudachiEmulationController : EmulationScreensController {
|
||||
}
|
||||
}
|
||||
|
||||
@objc func customButtonTapped() {
|
||||
stopEmulation()
|
||||
}
|
||||
func stopEmulation() {
|
||||
if isRunning {
|
||||
self.dismiss(animated: true)
|
||||
isRunning = false
|
||||
sudachi.bootOS1()
|
||||
thread.cancel()
|
||||
}
|
||||
}
|
||||
|
||||
@objc fileprivate func step() {
|
||||
while true {
|
||||
sudachi.step()
|
||||
@ -301,6 +350,21 @@ class SudachiEmulationController : EmulationScreensController {
|
||||
}
|
||||
}
|
||||
|
||||
struct SudachiEmulationViewController: UIViewControllerRepresentable {
|
||||
var game: SudachiGame
|
||||
@Binding var shouldStopEmulation: Bool
|
||||
|
||||
func makeUIViewController(context: Context) -> SudachiEmulationController {
|
||||
let controller = SudachiEmulationController(game: game)
|
||||
controller.modalPresentationStyle = .fullScreen
|
||||
return controller
|
||||
}
|
||||
|
||||
func updateUIViewController(_ uiViewController: SudachiEmulationController, context: Context) {
|
||||
if shouldStopEmulation {
|
||||
uiViewController.stopEmulation()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
BIN
Pomelo/Dependencies/.DS_Store
vendored
@ -6,5 +6,67 @@
|
||||
<true/>
|
||||
<key>UIRequiresPersistentWiFi</key>
|
||||
<true/>
|
||||
<key>UTExportedTypeDeclarations</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>UTTypeDescription</key>
|
||||
<string>NRO file</string>
|
||||
<key>UTTypeIdentifier</key>
|
||||
<string>com.stossy11.nro</string>
|
||||
<key>UTTypeTagSpecification</key>
|
||||
<dict/>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>UTTypeDescription</key>
|
||||
<string>NCA file</string>
|
||||
<key>UTTypeIdentifier</key>
|
||||
<string>com.stossy11.nca</string>
|
||||
<key>UTTypeTagSpecification</key>
|
||||
<dict/>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>UTTypeDescription</key>
|
||||
<string>NSO file</string>
|
||||
<key>UTTypeIdentifier</key>
|
||||
<string>com.stossy11.nso</string>
|
||||
<key>UTTypeTagSpecification</key>
|
||||
<dict/>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>UTTypeDescription</key>
|
||||
<string>NRO file</string>
|
||||
<key>UTTypeIdentifier</key>
|
||||
<string>com.stossy11.nsp</string>
|
||||
<key>UTTypeTagSpecification</key>
|
||||
<dict/>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>UTTypeDescription</key>
|
||||
<string>NRO file</string>
|
||||
<key>UTTypeIdentifier</key>
|
||||
<string>com.stossy11.xci</string>
|
||||
<key>UTTypeTagSpecification</key>
|
||||
<dict/>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>UTTypeDescription</key>
|
||||
<string>NRO file</string>
|
||||
<key>UTTypeIdentifier</key>
|
||||
<string>com.stossy11.keys</string>
|
||||
<key>UTTypeTagSpecification</key>
|
||||
<dict/>
|
||||
</dict>
|
||||
</array>
|
||||
<key>UTImportedTypeDeclarations</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>UTTypeDescription</key>
|
||||
<string>nro</string>
|
||||
<key>UTTypeIconFiles</key>
|
||||
<array/>
|
||||
<key>UTTypeTagSpecification</key>
|
||||
<dict/>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
|
@ -8,6 +8,7 @@
|
||||
import SwiftUI
|
||||
import Foundation
|
||||
import UIKit
|
||||
import UniformTypeIdentifiers
|
||||
|
||||
struct Help : Comparable, Hashable, Identifiable {
|
||||
var id = UUID()
|
||||
@ -30,19 +31,6 @@ struct Help : Comparable, Hashable, Identifiable {
|
||||
}
|
||||
|
||||
|
||||
struct CoreRowView: View {
|
||||
var core: Core
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading) {
|
||||
Text(core.name.rawValue)
|
||||
.font(.headline)
|
||||
Text(core.console.rawValue)
|
||||
.font(.subheadline)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct GameRowView: View {
|
||||
var game: SudachiGame
|
||||
|
||||
@ -86,6 +74,7 @@ struct CoreDetailView: View {
|
||||
@State var core: Core
|
||||
@State private var searchText = ""
|
||||
@State var ispoped = false
|
||||
@State var game: SudachiGame? = nil
|
||||
|
||||
var body: some View {
|
||||
let filteredGames = core.games.filter { game in
|
||||
@ -101,6 +90,8 @@ struct CoreDetailView: View {
|
||||
ForEach(0..<filteredGames.count, id: \.self) { index in
|
||||
if let game = core.games[index] as? SudachiGame {
|
||||
Button {
|
||||
self.game = game
|
||||
// ispoped = true
|
||||
presentPomeloEmulation(PomeloGame: game)
|
||||
} label: {
|
||||
GameRowView(game: game)
|
||||
@ -115,6 +106,14 @@ struct CoreDetailView: View {
|
||||
}
|
||||
.padding()
|
||||
}
|
||||
.onAppear() {
|
||||
core = Core(console: Core.Console.nSwitch, name: .Sudachi, games: [], missingFiles: [], root: URL(string: "[]")!)
|
||||
do {
|
||||
core = try LibraryManager.shared.library()
|
||||
} catch {
|
||||
print("Failed to fetch library: \(error)")
|
||||
}
|
||||
}
|
||||
.refreshable {
|
||||
core = Core(console: Core.Console.nSwitch, name: .Sudachi, games: [], missingFiles: [], root: URL(string: "[]")!)
|
||||
do {
|
||||
@ -200,10 +199,10 @@ struct InfoView: View {
|
||||
}
|
||||
|
||||
struct SideJITServerSettings: View {
|
||||
@AppStorage("sidejitserver-enable") var sidejitserver: Bool = false
|
||||
@AppStorage("sidejitserver-NavigationLink") var showAlert: Bool = false
|
||||
@AppStorage("sidejitserver-ip") var ip: String = ""
|
||||
@AppStorage("sidejitserver-udid") var udid: String = ""
|
||||
@AppStorage("sidejitserver-enable-true") var sidejitserver: Bool = false
|
||||
@AppStorage("sidejitserver-NavigationLink-true") var showAlert: Bool = false
|
||||
@AppStorage("sidejitserver-ip-true") var ip: String = ""
|
||||
@AppStorage("sidejitserver-udid-true") var udid: String = ""
|
||||
@AppStorage("sidejitserver-enable-auto") var sidejitserverauto: Bool = false
|
||||
@AppStorage("alertstring") var alertstring = ""
|
||||
@AppStorage("alert") var alert = false
|
||||
@ -214,10 +213,11 @@ struct SideJITServerSettings: View {
|
||||
.font(.largeTitle)
|
||||
TextField("SideJITServer IP", text: $ip)
|
||||
.textFieldStyle(RoundedBorderTextFieldStyle())
|
||||
.padding()
|
||||
.padding(.top)
|
||||
Text("This is not needed if SideJITServer has already been detected")
|
||||
.font(.subheadline)
|
||||
.foregroundColor(.secondary)
|
||||
.padding(.bottom)
|
||||
SecureField("UDID", text: $udid)
|
||||
.textFieldStyle(RoundedBorderTextFieldStyle())
|
||||
.padding()
|
||||
@ -343,11 +343,33 @@ struct SideJITServerSettings: View {
|
||||
}
|
||||
}
|
||||
|
||||
extension UTType {
|
||||
static var nro: UTType {
|
||||
UTType(exportedAs: "com.stossy11.nro")
|
||||
}
|
||||
static var nca: UTType {
|
||||
UTType(exportedAs: "com.stossy11.nca")
|
||||
}
|
||||
static var nso: UTType {
|
||||
UTType(exportedAs: "com.stossy11.nso")
|
||||
}
|
||||
static var nsp: UTType {
|
||||
UTType(exportedAs: "com.stossy11.nsp")
|
||||
}
|
||||
static var xci: UTType {
|
||||
UTType(exportedAs: "com.stossy11.xci")
|
||||
}
|
||||
static var keys: UTType {
|
||||
UTType(exportedAs: "com.stossy11.keys")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
struct LibraryView: View {
|
||||
@Binding var core: Core
|
||||
@State var showingEditConfig = false
|
||||
@State private var isActive: Bool = false
|
||||
@State private var isimport: Bool = false
|
||||
@State private var showprompt: Bool = false
|
||||
@AppStorage("sidejitserver-enable") var sidejitserver: Bool = false
|
||||
@AppStorage("sidejitserver-NavigationLink") var showAlert: Bool = false
|
||||
@ -359,7 +381,7 @@ struct LibraryView: View {
|
||||
@AppStorage("issue") var issue = false
|
||||
|
||||
var body: some View {
|
||||
NavigationView {
|
||||
NavigationStack {
|
||||
VStack {
|
||||
let (doesKeyExist, doesProdExist) = doeskeysexist()
|
||||
if doesKeyExist && doesProdExist {
|
||||
@ -389,39 +411,93 @@ struct LibraryView: View {
|
||||
}
|
||||
|
||||
}
|
||||
.fileImporter(isPresented: $isimport, allowedContentTypes: [.data], onCompletion: { result in
|
||||
switch result {
|
||||
case .success(let file):
|
||||
if file.startAccessingSecurityScopedResource() {
|
||||
moveFileToAppropriateFolder(file)
|
||||
file.stopAccessingSecurityScopedResource()
|
||||
} else {
|
||||
print("Failed to access the file")
|
||||
}
|
||||
|
||||
case .failure(let error):
|
||||
isimport = false
|
||||
print(error.localizedDescription)
|
||||
}
|
||||
//moveFileToAppropriateFolder()
|
||||
})
|
||||
.navigationBarTitle("Library", displayMode: .inline)
|
||||
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .navigationBarTrailing) {
|
||||
Button {
|
||||
isimport = true
|
||||
} label: {
|
||||
Image(systemName: "folder.badge.plus")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private func moveFileToAppropriateFolder(_ fileURL: URL) {
|
||||
let fileManager = FileManager.default
|
||||
let documentsDirectory = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first!
|
||||
|
||||
let romsDirectory = documentsDirectory.appendingPathComponent("roms")
|
||||
let keysDirectory = documentsDirectory.appendingPathComponent("keys")
|
||||
|
||||
let fileExtension = fileURL.pathExtension.lowercased()
|
||||
if ["nca", "nro", "nso", "nsp", "xci"].contains(fileExtension) {
|
||||
do {
|
||||
try fileManager.moveItem(at: fileURL, to: romsDirectory.appendingPathComponent(fileURL.lastPathComponent))
|
||||
} catch {
|
||||
print("Error moving file to roms folder: \(error.localizedDescription)")
|
||||
}
|
||||
} else if fileExtension == "keys" {
|
||||
do {
|
||||
try fileManager.moveItem(at: fileURL, to: keysDirectory.appendingPathComponent(fileURL.lastPathComponent))
|
||||
} catch {
|
||||
print("Error moving file to keys folder: \(error.localizedDescription)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func doeskeysexist() -> (Bool, Bool) {
|
||||
var doesprodexist = false
|
||||
var doestitleexist = false
|
||||
var bean: [MissingFile] = []
|
||||
|
||||
if core != nil {
|
||||
let title = core.root.appendingPathComponent("keys").appendingPathComponent("title.keys")
|
||||
let prod = core.root.appendingPathComponent("keys").appendingPathComponent("prod.keys")
|
||||
let fileManager = FileManager.default
|
||||
let documentsDirectory = fileManager.urls(for: .documentDirectory, in: .userDomainMask)[0]
|
||||
|
||||
if fileManager.fileExists(atPath: prod.path) {
|
||||
doesprodexist = true
|
||||
} else {
|
||||
print("File does not exist")
|
||||
do {
|
||||
bean = try LibraryManager.shared.library().missingFiles
|
||||
} catch {
|
||||
print("uhoh stinky")
|
||||
}
|
||||
|
||||
if fileManager.fileExists(atPath: title.path) {
|
||||
doestitleexist = true
|
||||
} else {
|
||||
print("File does not exist")
|
||||
print(bean.count)
|
||||
print(bean)
|
||||
|
||||
// Check if "prod.keys" is missing
|
||||
doesprodexist = !bean.contains { $0.fileName == "prod.keys" && $0.directory.lastPathComponent == "keys" }
|
||||
if !doesprodexist {
|
||||
print("prod.keys does not exist")
|
||||
}
|
||||
|
||||
// Check if "title.keys" is missing
|
||||
doestitleexist = !bean.contains { $0.fileName == "title.keys" && $0.directory.lastPathComponent == "keys" }
|
||||
if (!doestitleexist) {
|
||||
print("title.keys does not exist")
|
||||
}
|
||||
}
|
||||
return((doestitleexist, doesprodexist))
|
||||
return (doestitleexist, doesprodexist)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
struct INIEditControllerWrapper: UIViewControllerRepresentable {
|
||||
let console: Core.Console // Replace Console with your actual type
|
||||
let configURL: URL
|
||||
|
@ -8,15 +8,16 @@
|
||||
import SwiftUI
|
||||
import Foundation
|
||||
import UIKit
|
||||
import Sudachi
|
||||
|
||||
@main
|
||||
struct PomeloApp: App {
|
||||
@State var cores: Core = Core(console: .nSwitch, name: .Sudachi, games: [], missingFiles: [], root: URL(fileURLWithPath: "/"))
|
||||
@AppStorage("entitlementNotExists") private var entitlementNotExists: Bool = false
|
||||
@AppStorage("sidejitserver-enable") var sidejitserver: Bool = false
|
||||
@AppStorage("sidejitserver-NavigationLink") var showAlert: Bool = false
|
||||
@AppStorage("sidejitserver-ip") var ip: String = ""
|
||||
@AppStorage("sidejitserver-udid") var udid: String = ""
|
||||
@AppStorage("sidejitserver-enable-true") var sidejitserver: Bool = false
|
||||
@AppStorage("sidejitserver-NavigationLink-true") var showAlert: Bool = false
|
||||
@AppStorage("sidejitserver-ip-true") var ip: String = ""
|
||||
@AppStorage("sidejitserver-udid-true") var udid: String = ""
|
||||
@AppStorage("sidejitserver-enable-auto") var sidejitserverauto: Bool = false
|
||||
@State private var latestVersion: String?
|
||||
@State var alertstring = ""
|
||||
@ -74,7 +75,7 @@ struct PomeloApp: App {
|
||||
.alert(isPresented: $alert) {
|
||||
Alert(
|
||||
title: Text("SideJITServer"),
|
||||
message: Text("SideJITServer has been found on your network would you like to enable support."),
|
||||
message: Text("SideJITServer has been found on your network would you like to enable support. (Please Configure SideJITServer in Settings)"),
|
||||
primaryButton: .default(Text("OK"), action: {
|
||||
UserDefaults.standard.set(true, forKey: "sidejitserver-enable")
|
||||
}),
|
||||
@ -91,36 +92,64 @@ struct PomeloApp: App {
|
||||
|
||||
struct NavView: View {
|
||||
@Binding var cores: Core
|
||||
let sudachi = Sudachi.shared
|
||||
@State private var selectedTab = 0
|
||||
@State private var isTabDisabled = false
|
||||
@State private var showAlert = false
|
||||
|
||||
var body: some View {
|
||||
TabView {
|
||||
TabView(selection: $selectedTab) {
|
||||
LibraryView(core: $cores)
|
||||
.tabItem {
|
||||
Label("Games", systemImage: "rectangle.on.rectangle")
|
||||
}
|
||||
BootOSView(core: cores)
|
||||
.tabItem {
|
||||
Label("Home Menu", systemImage: "house")
|
||||
}
|
||||
.tag(0)
|
||||
if sudachi.canGetFullPath() {
|
||||
LibraryView(core: $cores)
|
||||
.onChange(of: selectedTab) { newValue in
|
||||
if newValue == 1 {
|
||||
selectedTab = 0
|
||||
let PomeloGame = SudachiGame(core: cores, developer: "", fileURL: URL(string: "{")!, imageData: Data(), title: "")
|
||||
presentPomeloEmulation(PomeloGame: PomeloGame)
|
||||
}
|
||||
}
|
||||
.tabItem {
|
||||
Label("Home Menu", systemImage: "house")
|
||||
}
|
||||
.tag(1)
|
||||
} else {
|
||||
NoBootOS()
|
||||
.tabItem {
|
||||
Label("Home Menu", systemImage: "house")
|
||||
}
|
||||
.tag(1)
|
||||
}
|
||||
SettingsView(core: cores)
|
||||
.tabItem {
|
||||
Label("Settings", systemImage: "gear")
|
||||
}
|
||||
.tag(2)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func presentPomeloEmulation(PomeloGame: SudachiGame) {
|
||||
var backgroundColor: UIColor = .systemBackground
|
||||
let PomeloEmulationController = SudachiEmulationController(game: PomeloGame)
|
||||
PomeloEmulationController.modalPresentationStyle = .fullScreen
|
||||
|
||||
UIDevice.current.setValue(UIInterfaceOrientation.portrait.rawValue, forKey: "orientation")
|
||||
|
||||
ThemeLoader.shared.loadTheme { color in
|
||||
ThemeLoader.shared.loadTheme { color, image in
|
||||
let userdefaults = UserDefaults.standard
|
||||
if let color = color {
|
||||
UserDefaults.standard.setValue(nil, forKey: "color")
|
||||
UserDefaults.standard.setColor(color, forKey: "color")
|
||||
userdefaults.setValue(nil, forKey: "color")
|
||||
userdefaults.setValue(nil, forKey: "background")
|
||||
userdefaults.setColor(color, forKey: "color")
|
||||
} else if let image = image {
|
||||
userdefaults.setValue(nil, forKey: "color")
|
||||
userdefaults.setValue(nil, forKey: "background")
|
||||
userdefaults.setValue(image.path, forKey: "background")
|
||||
}
|
||||
}
|
||||
|
||||
@ -130,3 +159,17 @@ func presentPomeloEmulation(PomeloGame: SudachiGame) {
|
||||
rootViewController.present(PomeloEmulationController, animated: true, completion: nil)
|
||||
}
|
||||
}
|
||||
|
||||
struct NoBootOS: View {
|
||||
let sudachi = Sudachi.shared
|
||||
|
||||
var body: some View {
|
||||
VStack {
|
||||
Text("Unable Launch Switch OS")
|
||||
.font(.largeTitle)
|
||||
.padding()
|
||||
Text("You do not have the Switch Home Menu Files Needed to launch the Ηome Menu")
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10,15 +10,38 @@ import SwiftUI
|
||||
struct SettingsView: View {
|
||||
@State var core: Core
|
||||
@State var showprompt = false
|
||||
@AppStorage("sidejitserver-enable-true") var sidejitserver: Bool = false
|
||||
@AppStorage("sidejitserver-NavigationLink-true") var showAlert: Bool = false
|
||||
@AppStorage("sidejitserver-ip-true") var ip: String = ""
|
||||
@AppStorage("sidejitserver-udid-true") var udid: String = ""
|
||||
|
||||
@AppStorage("icon") var iconused = 1
|
||||
var body: some View {
|
||||
NavigationView {
|
||||
NavigationStack {
|
||||
ScrollView {
|
||||
VStack(alignment: .leading) {
|
||||
VStack {
|
||||
Text("Welcome to Pomelo")
|
||||
.font(.title)
|
||||
VStack(alignment: .center) {
|
||||
if iconused == 1 {
|
||||
Image("AppIcon-inapp")
|
||||
.resizable()
|
||||
.frame(width: 200, height: 200)
|
||||
.cornerRadius(20)
|
||||
} else if iconused == 2 {
|
||||
Image(.appIconSecondaryInapp)
|
||||
.resizable()
|
||||
.frame(width: 200, height: 200)
|
||||
.cornerRadius(20)
|
||||
} else {
|
||||
Image(.appIconInvertedInapp)
|
||||
.resizable()
|
||||
.frame(width: 200, height: 200)
|
||||
.cornerRadius(20)
|
||||
}
|
||||
.padding()
|
||||
Text("Welcome To Pomelo")
|
||||
.padding()
|
||||
.font(.title)
|
||||
}
|
||||
.padding()
|
||||
VStack(alignment: .leading) {
|
||||
|
||||
NavigationLink(destination: InfoView()) {
|
||||
Rectangle()
|
||||
@ -88,23 +111,55 @@ struct SettingsView: View {
|
||||
.foregroundColor(.primary)
|
||||
.padding()
|
||||
} else {
|
||||
NavigationLink(destination: SideJITServerSettings()) {
|
||||
Rectangle()
|
||||
.fill(Color(uiColor: UIColor.secondarySystemBackground)) // Set the fill color (optional)
|
||||
.cornerRadius(10) // Apply rounded corners
|
||||
.frame(width: .infinity, height: 50) // Set the desired dimensions
|
||||
.overlay() {
|
||||
HStack {
|
||||
Text("SideJITServer Settings")
|
||||
.foregroundColor(.primary)
|
||||
.padding()
|
||||
Spacer()
|
||||
}
|
||||
|
||||
Rectangle()
|
||||
.fill(Color(uiColor: UIColor.secondarySystemBackground)) // Set the fill color (optional)
|
||||
.cornerRadius(10) // Apply rounded corners
|
||||
.frame(width: .infinity, height: 50) // Set the desired dimensions
|
||||
.overlay() {
|
||||
Toggle(isOn: $sidejitserver) {
|
||||
Text("SideJITServer")
|
||||
}
|
||||
.padding()
|
||||
}
|
||||
.padding()
|
||||
|
||||
if sidejitserver {
|
||||
NavigationLink(destination: SideJITServerSettings()) {
|
||||
Rectangle()
|
||||
.fill(Color(uiColor: UIColor.secondarySystemBackground)) // Set the fill color (optional)
|
||||
.cornerRadius(10) // Apply rounded corners
|
||||
.frame(width: .infinity, height: 50) // Set the desired dimensions
|
||||
.overlay() {
|
||||
HStack {
|
||||
Text("SideJITServer Settings")
|
||||
.foregroundColor(.primary)
|
||||
.padding()
|
||||
Spacer()
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
}
|
||||
.padding()
|
||||
}
|
||||
|
||||
|
||||
NavigationLink(destination: AppIconView()) {
|
||||
Rectangle()
|
||||
.fill(Color(uiColor: UIColor.secondarySystemBackground)) // Set the fill color (optional)
|
||||
.cornerRadius(10) // Apply rounded corners
|
||||
.frame(width: .infinity, height: 50) // Set the desired dimensions
|
||||
.overlay() {
|
||||
HStack {
|
||||
Text("App Icon")
|
||||
.foregroundColor(.primary)
|
||||
.padding()
|
||||
Spacer()
|
||||
}
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
|
||||
// NavigationLink(
|
||||
NavigationLink(destination: AdvancedSettingsView()) {
|
||||
Rectangle()
|
||||
@ -140,6 +195,7 @@ struct SettingsView: View {
|
||||
|
||||
struct AdvancedSettingsView: View {
|
||||
@AppStorage("isfullscreen") var isFullScreen: Bool = false
|
||||
@AppStorage("exitgame") var exitgame: Bool = false
|
||||
var body: some View {
|
||||
ScrollView {
|
||||
Rectangle()
|
||||
@ -156,10 +212,91 @@ struct AdvancedSettingsView: View {
|
||||
.padding(.bottom)
|
||||
.font(.footnote)
|
||||
.foregroundColor(.gray)
|
||||
|
||||
Rectangle()
|
||||
.fill(Color(uiColor: UIColor.secondarySystemBackground)) // Set the fill color (optional)
|
||||
.cornerRadius(10) // Apply rounded corners
|
||||
.frame(width: .infinity, height: 50) // Set the desired dimensions
|
||||
.overlay() {
|
||||
HStack {
|
||||
Toggle("Exit Game Button", isOn: $exitgame)
|
||||
.padding()
|
||||
}
|
||||
}
|
||||
Text("This is very unstable and can lead to game freezing and overall bad preformance after you exit a game")
|
||||
.padding(.bottom)
|
||||
.font(.footnote)
|
||||
.foregroundColor(.gray)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct AppIconView: View {
|
||||
let icons = ["AppIcon-1", "Appicon-Secondary", "Appicon-inverted"] // Replace with your icon names
|
||||
@AppStorage("icon") var iconused = 1
|
||||
|
||||
var body: some View {
|
||||
ScrollView {
|
||||
LazyVGrid(columns: [GridItem(.flexible()), GridItem(.flexible())], spacing: 16) {
|
||||
ForEach(icons, id: \.self) { iconName in
|
||||
Button {
|
||||
if iconName == "AppIcon" {
|
||||
iconused = 1
|
||||
UIApplication.shared.setAlternateIconName(iconName) { error in
|
||||
if let error = error {
|
||||
print("fuck \(error.localizedDescription)")
|
||||
}
|
||||
}
|
||||
} else if iconName == "Appicon-Secondary" {
|
||||
iconused = 2
|
||||
UIApplication.shared.setAlternateIconName(iconName) { error in
|
||||
if let error = error {
|
||||
print("fuck \(error.localizedDescription)")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
UIApplication.shared.setAlternateIconName(iconName) { error in
|
||||
if let error = error {
|
||||
print("fuck \(error.localizedDescription)")
|
||||
}
|
||||
}
|
||||
}
|
||||
} label: {
|
||||
VStack {
|
||||
if iconName == "AppIcon-1" {
|
||||
Image(.appIconInapp)
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.frame(width: 80, height: 80) // Adjust size as needed
|
||||
.clipShape(RoundedRectangle(cornerRadius: 16))
|
||||
Text("Main App Icon")
|
||||
} else if iconName == "Appicon-Secondary" {
|
||||
Image(.appIconSecondaryInapp)
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.frame(width: 80, height: 80) // Adjust size as needed
|
||||
.clipShape(RoundedRectangle(cornerRadius: 16))
|
||||
Text("Secondary App Icon")
|
||||
} else if iconName == "Appicon-inverted" {
|
||||
Image(.appIconInvertedInapp)
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.frame(width: 80, height: 80) // Adjust size as needed
|
||||
.clipShape(RoundedRectangle(cornerRadius: 16))
|
||||
Text("Inverted Main App Icon")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
.navigationTitle("App Icons")
|
||||
Spacer()
|
||||
Text("All of these icons were generously made by ZxATHER")
|
||||
.padding(.bottom)
|
||||
.font(.footnote)
|
||||
.foregroundColor(.gray)
|
||||
}
|
||||
}
|
||||
// let userDefaults = UserDefaults.standard
|
||||
// userDefaults.set(true, forKey: "isfullscreen")
|
||||
|
@ -34,6 +34,30 @@ public struct Sudachi {
|
||||
sudachiObjC.bootOS()
|
||||
}
|
||||
|
||||
public func pause() {
|
||||
sudachiObjC.pause()
|
||||
}
|
||||
|
||||
public func play() {
|
||||
sudachiObjC.play()
|
||||
}
|
||||
|
||||
public func togglepause() {
|
||||
if sudachiObjC.ispaused() {
|
||||
sudachiObjC.play()
|
||||
} else {
|
||||
sudachiObjC.pause()
|
||||
}
|
||||
}
|
||||
|
||||
public func ispaused() -> Bool {
|
||||
return sudachiObjC.ispaused()
|
||||
}
|
||||
|
||||
public func canGetFullPath() -> Bool {
|
||||
return sudachiObjC.canGetFullPath()
|
||||
}
|
||||
|
||||
public func bootOS1() {
|
||||
sudachiObjC.quit()
|
||||
}
|
||||
|
@ -314,7 +314,7 @@ Core::SystemResultStatus EmulationSession::BootOS() {
|
||||
// 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();
|
||||
// EmulationSession::GetInstance().HaltEmulation();
|
||||
});
|
||||
|
||||
OnEmulationStarted();
|
||||
|
@ -23,7 +23,7 @@ void EmulationWindow::OnSurfaceChanged(CA::MetalLayer* surface, CGSize size) {
|
||||
m_window_height = size.height;
|
||||
|
||||
// Ensures that we emulate with the correct aspect ratio.
|
||||
UpdateCurrentFramebufferLayout(m_window_width, m_window_height);
|
||||
// UpdateCurrentFramebufferLayout(m_window_width, m_window_height);
|
||||
|
||||
window_info.render_surface = reinterpret_cast<void*>(surface);
|
||||
window_info.render_surface_scale = [[UIScreen mainScreen] nativeScale];
|
||||
|
@ -50,6 +50,10 @@ typedef NS_ENUM(NSUInteger, VirtualControllerButtonType) {
|
||||
+(SudachiObjC *) 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:));
|
||||
|
@ -14,6 +14,52 @@
|
||||
#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/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>
|
||||
|
||||
@ -46,7 +92,49 @@
|
||||
return sharedInstance;
|
||||
}
|
||||
|
||||
- (BOOL)ispaused {
|
||||
return EmulationSession::GetInstance().IsPaused();
|
||||
}
|
||||
|
||||
-(void) pause {
|
||||
EmulationSession::GetInstance().System().Pause();
|
||||
void(EmulationSession::GetInstance().PauseEmulation());
|
||||
}
|
||||
|
||||
-(void) play {
|
||||
EmulationSession::GetInstance().System().Run();
|
||||
void(EmulationSession::GetInstance().UnPauseEmulation());
|
||||
}
|
||||
|
||||
- (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().HaltEmulation();
|
||||
EmulationSession::GetInstance().System().Exit();
|
||||
void(EmulationSession::GetInstance().ShutdownEmulation());
|
||||
}
|
||||
|
||||
|