mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-09-20 09:26:53 +00:00
swift reworks
This commit is contained in:
@@ -6,6 +6,7 @@
|
|||||||
import UIKit
|
import UIKit
|
||||||
import Foundation
|
import Foundation
|
||||||
import QuartzCore.CAMetalLayer
|
import QuartzCore.CAMetalLayer
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
public struct AppUI {
|
public struct AppUI {
|
||||||
public static let shared = AppUI()
|
public static let shared = AppUI()
|
||||||
@@ -101,3 +102,12 @@ public struct AppUI {
|
|||||||
appUIObjC.settingsChanged()
|
appUIObjC.settingsChanged()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct EdenApp: App {
|
||||||
|
@StateObject private var core = EmulationViewModel()
|
||||||
|
var body: some Scene {
|
||||||
|
WindowGroup {
|
||||||
|
ContentView(core: core)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,7 +8,7 @@ import SwiftUI
|
|||||||
import Metal
|
import Metal
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
class EmulationViewModel: ObservableObject {
|
class EmulationSessionViewModel: ObservableObject {
|
||||||
@Published var isShowingCustomButton = true
|
@Published var isShowingCustomButton = true
|
||||||
@State var should = false
|
@State var should = false
|
||||||
var device: MTLDevice?
|
var device: MTLDevice?
|
||||||
|
|||||||
@@ -349,7 +349,7 @@ class Haptics {
|
|||||||
|
|
||||||
struct ButtonView: View {
|
struct ButtonView: View {
|
||||||
var button: VirtualControllerButtonType
|
var button: VirtualControllerButtonType
|
||||||
@StateObject private var viewModel: EmulationViewModel = EmulationViewModel(game: nil)
|
@StateObject private var viewModel: EmulationViewModel = EmulationViewModel()
|
||||||
let appui = AppUI.shared
|
let appui = AppUI.shared
|
||||||
@State var mtkView: MTKView?
|
@State var mtkView: MTKView?
|
||||||
@State var width: CGFloat = 45
|
@State var width: CGFloat = 45
|
||||||
@@ -446,7 +446,7 @@ struct EmulationView: View {
|
|||||||
@Environment(\.scenePhase) var scenePhase
|
@Environment(\.scenePhase) var scenePhase
|
||||||
|
|
||||||
init(game: EmulationGame?) {
|
init(game: EmulationGame?) {
|
||||||
_viewModel = StateObject(wrappedValue: EmulationViewModel(game: game))
|
_viewModel = StateObject(wrappedValue: EmulationViewModel())
|
||||||
}
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
|
|||||||
+63
-109
@@ -10,7 +10,7 @@ import UniformTypeIdentifiers
|
|||||||
import Combine
|
import Combine
|
||||||
|
|
||||||
struct SettingsView: View {
|
struct SettingsView: View {
|
||||||
@State var core: Core
|
@ObservedObject var core: EmulationViewModel
|
||||||
@State var showprompt = false
|
@State var showprompt = false
|
||||||
|
|
||||||
@AppStorage("icon") var iconused = 1
|
@AppStorage("icon") var iconused = 1
|
||||||
@@ -78,116 +78,70 @@ struct GameIconView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct BottomMenuView: View {
|
// Remove or refactor HomeView and GameCarouselView to not use Core or BottomMenuView(core: core) with Core.
|
||||||
@State var core: Core
|
// If these are not used in the iOS UI, comment them out to avoid build errors.
|
||||||
var body: some View {
|
// struct HomeView: View {
|
||||||
HStack(spacing: 40) {
|
// @State private var selectedGame: EmulationGame? = nil
|
||||||
Button {
|
|
||||||
|
|
||||||
} label: {
|
// @State var core: Core
|
||||||
Circle()
|
|
||||||
.overlay {
|
|
||||||
Image(systemName: "message").font(.system(size: 30)).foregroundColor(.red)
|
|
||||||
}
|
|
||||||
.frame(width: 50, height: 50)
|
|
||||||
.foregroundColor(Color.init(uiColor: .lightGray))
|
|
||||||
}
|
|
||||||
Button {
|
|
||||||
|
|
||||||
} label: {
|
// init(selectedGame: EmulationGame? = nil, core: Core) {
|
||||||
Circle()
|
// _core = State(wrappedValue: core)
|
||||||
.overlay {
|
// self.selectedGame = selectedGame
|
||||||
Image(systemName: "photo").font(.system(size: 30)).foregroundColor(.blue)
|
// refreshcore()
|
||||||
}
|
// }
|
||||||
.frame(width: 50, height: 50)
|
|
||||||
.foregroundColor(Color.init(uiColor: .lightGray))
|
|
||||||
}
|
|
||||||
NavigationLink(destination: SettingsView(core: core)) {
|
|
||||||
Circle()
|
|
||||||
.overlay {
|
|
||||||
Image(systemName: "gearshape").foregroundColor(Color.init(uiColor: .darkGray)).font(.system(size: 30))
|
|
||||||
}
|
|
||||||
.frame(width: 50, height: 50)
|
|
||||||
.foregroundColor(Color.init(uiColor: .lightGray))
|
|
||||||
}
|
|
||||||
|
|
||||||
Button {
|
// var body: some View {
|
||||||
|
// NavigationStack {
|
||||||
|
// GeometryReader { geometry in
|
||||||
|
// VStack {
|
||||||
|
// GameCarouselView(core: core, selectedGame: $selectedGame)
|
||||||
|
// Spacer()
|
||||||
|
// BottomMenuView(core: core)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// .background(Color.gray.opacity(0.1))
|
||||||
|
// .edgesIgnoringSafeArea(.all)
|
||||||
|
// .onAppear {
|
||||||
|
// refreshcore()
|
||||||
|
// if let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first {
|
||||||
|
// let romsFolderURL = documentsDirectory.appendingPathComponent("roms")
|
||||||
|
// let folderMonitor = FolderMonitor(folderURL: romsFolderURL) {
|
||||||
|
// do {
|
||||||
|
// core = Core(games: [], root: documentsDirectory)
|
||||||
|
// core = try LibraryManager.shared.library()
|
||||||
|
// } catch {
|
||||||
|
// print("Error refreshing core: \(error)")
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
} label: {
|
// func refreshcore() {
|
||||||
Circle()
|
// print("Loading library...")
|
||||||
.overlay {
|
// do {
|
||||||
Image(systemName: "power").foregroundColor(Color.init(uiColor: .darkGray)).font(.system(size: 30))
|
// core = try LibraryManager.shared.library()
|
||||||
}
|
// print(core.games)
|
||||||
.frame(width: 50, height: 50)
|
// } catch {
|
||||||
.foregroundColor(Color.init(uiColor: .lightGray))
|
// print("Failed to fetch library: \(error)")
|
||||||
}
|
// return
|
||||||
}
|
// }
|
||||||
.padding(.bottom, 20)
|
// }
|
||||||
}
|
// }
|
||||||
}
|
|
||||||
|
|
||||||
struct HomeView: View {
|
// struct GameCarouselView: View {
|
||||||
@State private var selectedGame: EmulationGame? = nil
|
// // let games: [EmulationGame]
|
||||||
|
// @State var core: Core
|
||||||
@State var core: Core
|
// @Binding var selectedGame: EmulationGame?
|
||||||
|
// var body: some View {
|
||||||
init(selectedGame: EmulationGame? = nil, core: Core) {
|
// ScrollView(.horizontal, showsIndicators: false) {
|
||||||
_core = State(wrappedValue: core)
|
// HStack(spacing: 20) {
|
||||||
self.selectedGame = selectedGame
|
// ForEach(core.games) { game in
|
||||||
refreshcore()
|
// GameIconView(game: game, selectedGame: $selectedGame)
|
||||||
}
|
// }
|
||||||
|
// }
|
||||||
var body: some View {
|
// }
|
||||||
NavigationStack {
|
// }
|
||||||
GeometryReader { geometry in
|
// }
|
||||||
VStack {
|
|
||||||
GameCarouselView(core: core, selectedGame: $selectedGame)
|
|
||||||
Spacer()
|
|
||||||
BottomMenuView(core: core)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.background(Color.gray.opacity(0.1))
|
|
||||||
.edgesIgnoringSafeArea(.all)
|
|
||||||
.onAppear {
|
|
||||||
refreshcore()
|
|
||||||
if let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first {
|
|
||||||
let romsFolderURL = documentsDirectory.appendingPathComponent("roms")
|
|
||||||
let folderMonitor = FolderMonitor(folderURL: romsFolderURL) {
|
|
||||||
do {
|
|
||||||
core = Core(games: [], root: documentsDirectory)
|
|
||||||
core = try LibraryManager.shared.library()
|
|
||||||
} catch {
|
|
||||||
print("Error refreshing core: \(error)")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func refreshcore() {
|
|
||||||
print("Loading library...")
|
|
||||||
do {
|
|
||||||
core = try LibraryManager.shared.library()
|
|
||||||
print(core.games)
|
|
||||||
} catch {
|
|
||||||
print("Failed to fetch library: \(error)")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct GameCarouselView: View {
|
|
||||||
// let games: [EmulationGame]
|
|
||||||
@State var core: Core
|
|
||||||
@Binding var selectedGame: EmulationGame?
|
|
||||||
var body: some View {
|
|
||||||
ScrollView(.horizontal, showsIndicators: false) {
|
|
||||||
HStack(spacing: 20) {
|
|
||||||
ForEach(core.games) { game in
|
|
||||||
GameIconView(game: game, selectedGame: $selectedGame)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+71
-166
@@ -1,184 +1,89 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
// SPDX-FileCopyrightText: Copyright 2024 Pomelo, Stossy11
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
import CryptoKit
|
import Foundation
|
||||||
|
|
||||||
struct LibraryView: View {
|
struct LibraryView: View {
|
||||||
@Binding var core: Core
|
@State private var selectedGame: EmulationGame? = nil
|
||||||
@State var isGridView: Bool = true
|
@Binding var urlgame: EmulationGame?
|
||||||
@State var doesitexist = (false, false)
|
@ObservedObject var core: EmulationViewModel
|
||||||
@State var importedgame: EmulationGame? = nil
|
|
||||||
@State var importgame: Bool = false
|
var binding: Binding<Bool> {
|
||||||
@State var isimportingfirm: Bool = false
|
Binding(
|
||||||
@State var launchGame: Bool = false
|
get: { urlgame != nil },
|
||||||
|
set: { newValue in
|
||||||
|
if !newValue {
|
||||||
|
urlgame = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
NavigationStack {
|
NavigationView {
|
||||||
if let importedgame = importedgame {
|
GeometryReader { geometry in
|
||||||
|
VStack {
|
||||||
|
TopBarView()
|
||||||
|
|
||||||
|
if UIDevice.current.userInterfaceIdiom == .pad {
|
||||||
|
Spacer()
|
||||||
|
}
|
||||||
|
|
||||||
|
ScrollView {
|
||||||
|
LazyVGrid(columns: [
|
||||||
|
GridItem(.adaptive(minimum: 140), spacing: 10)
|
||||||
|
], spacing: 10) {
|
||||||
|
ForEach(core.games, id: \EmulationGame.id) { game in
|
||||||
|
GameCardView(game: game)
|
||||||
|
.frame(width: 140, height: 160)
|
||||||
|
.onTapGesture {
|
||||||
|
selectedGame = game
|
||||||
|
urlgame = game
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.padding()
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
|
||||||
|
BottomMenuView(core: core)
|
||||||
|
}
|
||||||
|
|
||||||
NavigationLink(
|
NavigationLink(
|
||||||
isActive: $launchGame,
|
destination: EmulationView(game: urlgame),
|
||||||
destination: {
|
isActive: binding,
|
||||||
EmulationView(game: importedgame).toolbar(.hidden, for: .tabBar)
|
|
||||||
},
|
|
||||||
label: {
|
label: {
|
||||||
EmptyView() // This keeps the link hidden
|
EmptyView()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
.hidden()
|
||||||
|
|
||||||
VStack {
|
|
||||||
if doesitexist.0, doesitexist.1 {
|
|
||||||
HomeView(core: core)
|
|
||||||
} else {
|
|
||||||
let (doesKeyExist, doesProdExist) = doesKeysExist()
|
|
||||||
ScrollView {
|
|
||||||
Text("You Are Missing These Files:")
|
|
||||||
.font(.headline)
|
|
||||||
.foregroundColor(.red)
|
|
||||||
HStack {
|
|
||||||
if !doesProdExist {
|
|
||||||
Text("Prod.keys")
|
|
||||||
.font(.subheadline)
|
|
||||||
.foregroundColor(.red)
|
|
||||||
}
|
|
||||||
if !doesKeyExist {
|
|
||||||
Text("Title.keys")
|
|
||||||
.font(.subheadline)
|
|
||||||
.foregroundColor(.red)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Text("These goes into the Keys folder")
|
|
||||||
.font(.caption)
|
|
||||||
.foregroundColor(.red)
|
|
||||||
.padding(.bottom)
|
|
||||||
|
|
||||||
if !LibraryManager.shared.homebrewroms().isEmpty {
|
|
||||||
Text("Homebrew Roms:")
|
|
||||||
.font(.headline)
|
|
||||||
LazyVGrid(columns: [GridItem(.adaptive(minimum: 160))], spacing: 10) {
|
|
||||||
ForEach(LibraryManager.shared.homebrewroms()) { game in
|
|
||||||
NavigationLink(destination: EmulationView(game: game).toolbar(.hidden, for: .tabBar)) {
|
|
||||||
// GameButtonView(game: game)
|
|
||||||
// .frame(maxWidth: .infinity, minHeight: 200)
|
|
||||||
}
|
|
||||||
.contextMenu {
|
|
||||||
NavigationLink(destination: EmulationView(game: game)) {
|
|
||||||
Text("Launch")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.refreshable {
|
|
||||||
doesitexist = doesKeysExist()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
.fileImporter(isPresented: $isimportingfirm, allowedContentTypes: [.zip], onCompletion: { result in
|
|
||||||
switch result {
|
|
||||||
case .success(let elements):
|
|
||||||
core.AddFirmware(at: elements)
|
|
||||||
case .failure(let error):
|
|
||||||
|
|
||||||
print(error.localizedDescription)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.fileImporter(isPresented: $importgame, allowedContentTypes: [.item], onCompletion: { result in
|
|
||||||
switch result {
|
|
||||||
case .success(let elements):
|
|
||||||
let iscustom = elements.startAccessingSecurityScopedResource()
|
|
||||||
let information = AppUI.shared.information(for: elements)
|
|
||||||
|
|
||||||
let game = EmulationGame(developer: information.developer, fileURL: elements,
|
|
||||||
imageData: information.iconData,
|
|
||||||
title: information.title)
|
|
||||||
|
|
||||||
importedgame = game
|
|
||||||
|
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
|
|
||||||
if iscustom {
|
|
||||||
elements.stopAccessingSecurityScopedResource()
|
|
||||||
}
|
|
||||||
|
|
||||||
launchGame = true
|
|
||||||
}
|
|
||||||
case .failure(let error):
|
|
||||||
|
|
||||||
print(error.localizedDescription)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.onAppear() {
|
|
||||||
doesitexist = doesKeysExist()
|
|
||||||
}
|
|
||||||
.navigationBarTitle("Library", displayMode: .inline)
|
|
||||||
.toolbar {
|
|
||||||
ToolbarItem(placement: .navigationBarLeading) { // why did this take me so long to figure out lmfao
|
|
||||||
Button(action: {
|
|
||||||
isGridView.toggle()
|
|
||||||
}) {
|
|
||||||
Image(systemName: isGridView ? "rectangle.grid.1x2" : "square.grid.2x2")
|
|
||||||
.imageScale(.large)
|
|
||||||
.padding()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ToolbarItem(placement: .navigationBarTrailing) { // funsies
|
|
||||||
Menu {
|
|
||||||
Button(action: {
|
|
||||||
importgame = true // this part took a while
|
|
||||||
|
|
||||||
}) {
|
|
||||||
Text("Launch Game")
|
|
||||||
}
|
|
||||||
|
|
||||||
Button(action: {
|
|
||||||
isimportingfirm = true
|
|
||||||
}) {
|
|
||||||
Text("Import Firmware")
|
|
||||||
}
|
|
||||||
} label: {
|
|
||||||
Image(systemName: "plus.circle.fill")
|
|
||||||
.imageScale(.large)
|
|
||||||
.padding()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
.background(Color.gray.opacity(0.1))
|
||||||
|
.edgesIgnoringSafeArea(.all)
|
||||||
func doesKeysExist() -> (Bool, Bool) {
|
.onAppear {
|
||||||
var doesprodexist = false
|
|
||||||
var doestitleexist = false
|
|
||||||
let title = core.root.appendingPathComponent("keys").appendingPathComponent("title.keys")
|
|
||||||
let prod = core.root.appendingPathComponent("keys").appendingPathComponent("prod.keys")
|
|
||||||
let fileManager = FileManager.default
|
|
||||||
if fileManager.fileExists(atPath: prod.path) {
|
|
||||||
doesprodexist = true
|
|
||||||
} else {
|
|
||||||
print("File does not exist")
|
|
||||||
}
|
}
|
||||||
if fileManager.fileExists(atPath: title.path) {
|
|
||||||
doestitleexist = true
|
|
||||||
} else {
|
|
||||||
print("File does not exist")
|
|
||||||
}
|
|
||||||
return (doestitleexist, doesprodexist)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func getDeveloperNames() -> String {
|
struct GameCardView: View {
|
||||||
// guard let s = infoDictionary?["CFBundleIdentifier"] as? String else {
|
let game: EmulationGame
|
||||||
// return "Unknown"
|
|
||||||
// }
|
var body: some View {
|
||||||
// return s
|
VStack {
|
||||||
return "what"
|
Rectangle()
|
||||||
|
.fill(Color.gray)
|
||||||
|
.frame(height: 120)
|
||||||
|
|
||||||
|
Text(game.title)
|
||||||
|
.font(.caption)
|
||||||
|
.lineLimit(1)
|
||||||
|
.truncationMode(.tail)
|
||||||
|
}
|
||||||
|
.background(Color.white)
|
||||||
|
.cornerRadius(8)
|
||||||
|
.shadow(radius: 4)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+2
-20
@@ -50,28 +50,10 @@ class YuzuFileManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ContentView: View {
|
|
||||||
@State var core = Core(games: [], root: FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0])
|
|
||||||
var body: some View {
|
|
||||||
HomeView(core: core).onAppear() {
|
|
||||||
do {
|
|
||||||
try YuzuFileManager.shared.createdirectories() // this took a while to create the proper directories
|
|
||||||
do {
|
|
||||||
core = try LibraryManager.shared.library() // this shit is like you tried to throw a egg into a blender with no lid on
|
|
||||||
} catch {
|
|
||||||
print("Failed to fetch library: \(error)") // aaaaaaaaa
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
print("Failed to create directories: \(error)") // i wonder why hmmmmmmm
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@main
|
@main
|
||||||
struct PomeloApp: App {
|
struct PomeloApp: App {
|
||||||
|
@StateObject private var core = EmulationViewModel(game: nil)
|
||||||
var body: some Scene {
|
var body: some Scene {
|
||||||
WindowGroup { ContentView() }
|
WindowGroup { ContentView(core: core) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+1
-1
@@ -13,7 +13,7 @@ SPDX-License-Identifier: GPL-2.0-or-later
|
|||||||
<key>CFBundleDevelopmentRegion</key>
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
<string>English</string>
|
<string>English</string>
|
||||||
<key>CFBundleExecutable</key>
|
<key>CFBundleExecutable</key>
|
||||||
<string>${EXECUTABLE_NAME}</string>
|
<string>$(EXECUTABLE_NAME)</string>
|
||||||
<key>CFBundleGetInfoString</key>
|
<key>CFBundleGetInfoString</key>
|
||||||
<string></string>
|
<string></string>
|
||||||
<key>CFBundleIconFile</key>
|
<key>CFBundleIconFile</key>
|
||||||
|
|||||||
Reference in New Issue
Block a user