From 757fb1f6d1be929aef8c6201a32b2960bca11e4b Mon Sep 17 00:00:00 2001
From: Daniil Vinogradov <xitrix@bk.ru>
Date: Sun, 16 Feb 2025 13:16:14 +0100
Subject: [PATCH 1/4] Update manager fix

---
 src/Ryujinx.Headless.SDL2/Program.cs | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/Ryujinx.Headless.SDL2/Program.cs b/src/Ryujinx.Headless.SDL2/Program.cs
index a5f799fac..ad78c40ea 100644
--- a/src/Ryujinx.Headless.SDL2/Program.cs
+++ b/src/Ryujinx.Headless.SDL2/Program.cs
@@ -751,7 +751,8 @@ namespace Ryujinx.Headless.SDL2
 
                     if (File.Exists(titleUpdateMetadataPath))
                     {
-                        updatePath = JsonHelper.DeserializeFromFile(titleUpdateMetadataPath, _titleSerializerContext.TitleUpdateMetadata).Selected;
+                        string updatePathRelative = JsonHelper.DeserializeFromFile(titleUpdateMetadataPath, _titleSerializerContext.TitleUpdateMetadata).Selected;
+                        updatePath = Path.Combine(AppDataManager.BaseDirPath, "updates", updatePathRelative);
 
                         if (File.Exists(updatePath))
                         {

From df2b17ddd6b1fe8c23e281df07cb145f9285ee0d Mon Sep 17 00:00:00 2001
From: Daniil Vinogradov <xitrix@bk.ru>
Date: Sun, 16 Feb 2025 13:16:39 +0100
Subject: [PATCH 2/4] UI enhancements

---
 .../App/Views/GamesList/GameListView.swift    | 164 ++++++++----------
 .../Updates/GameUpdateManagerSheet.swift      |  68 ++++----
 2 files changed, 112 insertions(+), 120 deletions(-)

diff --git a/src/MeloNX/MeloNX/App/Views/GamesList/GameListView.swift b/src/MeloNX/MeloNX/App/Views/GamesList/GameListView.swift
index da759a4b4..96a1e51fb 100644
--- a/src/MeloNX/MeloNX/App/Views/GamesList/GameListView.swift
+++ b/src/MeloNX/MeloNX/App/Views/GamesList/GameListView.swift
@@ -42,72 +42,54 @@ struct GameLibraryView: View {
         }
         return Ryujinx.shared.games.filter {
             $0.titleName.localizedCaseInsensitiveContains(searchText) ||
-            $0.developer.localizedCaseInsensitiveContains(searchText)
+                $0.developer.localizedCaseInsensitiveContains(searchText)
         }
     }
     
     var body: some View {
         iOSNav {
-            ScrollView {
-                LazyVStack(alignment: .leading, spacing: 20) {
-                    if !isSearching {
-                        Text("Games")
-                            .font(.system(size: 34, weight: .bold))
-                            .padding(.horizontal)
-                            .padding(.top, 12)
+            List {
+                if Ryujinx.shared.games.isEmpty {
+                    VStack(spacing: 16) {
+                        Image(systemName: "gamecontroller.fill")
+                            .font(.system(size: 64))
+                            .foregroundColor(.secondary.opacity(0.7))
+                            .padding(.top, 60)
+                        Text("No Games Found")
+                            .font(.title2.bold())
+                            .foregroundColor(.primary)
+                        Text("Add ROM, Keys and Firmware to get started")
+                            .font(.subheadline)
+                            .foregroundColor(.secondary)
                     }
-                    
-                    if Ryujinx.shared.games.isEmpty {
-                        VStack(spacing: 16) {
-                            Image(systemName: "gamecontroller.fill")
-                                .font(.system(size: 64))
-                                .foregroundColor(.secondary.opacity(0.7))
-                                .padding(.top, 60)
-                            Text("No Games Found")
+                    .frame(maxWidth: .infinity)
+                    .padding(.top, 40)
+                } else {
+                    if !isSearching && !recentGames.isEmpty {
+                        VStack(alignment: .leading, spacing: 12) {
+                            Text("Recent")
                                 .font(.title2.bold())
-                                .foregroundColor(.primary)
-                            Text("Add ROM, Keys and Firmware to get started")
-                                .font(.subheadline)
-                                .foregroundColor(.secondary)
-                        }
-                        .frame(maxWidth: .infinity)
-                        .padding(.top, 40)
-                    } else {
-                        if !isSearching && !recentGames.isEmpty {
-                            VStack(alignment: .leading, spacing: 12) {
-                                Text("Recent")
-                                    .font(.title2.bold())
-                                    .padding(.horizontal)
+                                .padding(.horizontal)
                                 
-                                ScrollView(.horizontal, showsIndicators: false) {
-                                    LazyHStack(spacing: 16) {
-                                        ForEach(recentGames) { game in
-                                            RecentGameCard(game: game, startemu: $startemu)
-                                                .onTapGesture {
-                                                    addToRecentGames(game)
-                                                    startemu = game
-                                                }
-                                        }
-                                    }
-                                    .padding(.horizontal)
-                                }
-                            }
-                            
-                            VStack(alignment: .leading, spacing: 12) {
-                                Text("All Games")
-                                    .font(.title2.bold())
-                                    .padding(.horizontal)
-                                
-                                LazyVStack(spacing: 2) {
-                                    ForEach(filteredGames) { game in
-                                        GameListRow(game: game, startemu: $startemu, games: games, isViewingGameInfo: $isViewingGameInfo, isSelectingGameUpdate: $isSelectingGameUpdate, gameInfo: $gameInfo)
+                            ScrollView(.horizontal, showsIndicators: false) {
+                                LazyHStack(spacing: 16) {
+                                    ForEach(recentGames) { game in
+                                        RecentGameCard(game: game, startemu: $startemu)
                                             .onTapGesture {
                                                 addToRecentGames(game)
+                                                startemu = game
                                             }
                                     }
                                 }
+                                .padding(.horizontal)
                             }
-                        } else {
+                        }
+                            
+                        VStack(alignment: .leading, spacing: 12) {
+                            Text("All Games")
+                                .font(.title2.bold())
+                                .padding(.horizontal)
+                                
                             LazyVStack(spacing: 2) {
                                 ForEach(filteredGames) { game in
                                     GameListRow(game: game, startemu: $startemu, games: games, isViewingGameInfo: $isViewingGameInfo, isSelectingGameUpdate: $isSelectingGameUpdate, gameInfo: $gameInfo)
@@ -117,42 +99,51 @@ struct GameLibraryView: View {
                                 }
                             }
                         }
-                    }
-                }
-                .onAppear {
-                    loadRecentGames()
-                    
-                    let firmware = Ryujinx.shared.fetchFirmwareVersion()
-                    firmwareversion = (firmware == "" ? "0" : firmware)
-                }
-                .fileImporter(isPresented: $firmwareInstaller, allowedContentTypes: [.item]) { result in
-                    switch result {
-                    case .success(let url):
-                        do {
-                            let fun = url.startAccessingSecurityScopedResource()
-                            let path = url.path
-                            
-                            Ryujinx.shared.installFirmware(firmwarePath: path)
-                            
-                            firmwareversion = (Ryujinx.shared.fetchFirmwareVersion() == "" ? "0" : Ryujinx.shared.fetchFirmwareVersion())
-                            if fun  {
-                                url.stopAccessingSecurityScopedResource()
-                            }
+                    } else {
+                        ForEach(filteredGames) { game in
+                            GameListRow(game: game, startemu: $startemu, games: games, isViewingGameInfo: $isViewingGameInfo, isSelectingGameUpdate: $isSelectingGameUpdate, gameInfo: $gameInfo)
+                                .onTapGesture {
+                                    addToRecentGames(game)
+                                }
                         }
-                    case .failure(let error):
-                        print(error)
                     }
                 }
             }
+            .navigationTitle("Games")
+            .navigationBarTitleDisplayMode(.large)
+            .onAppear {
+                loadRecentGames()
+                    
+                let firmware = Ryujinx.shared.fetchFirmwareVersion()
+                firmwareversion = (firmware == "" ? "0" : firmware)
+            }
+            .fileImporter(isPresented: $firmwareInstaller, allowedContentTypes: [.item]) { result in
+                switch result {
+                case .success(let url):
+                    do {
+                        let fun = url.startAccessingSecurityScopedResource()
+                        let path = url.path
+                            
+                        Ryujinx.shared.installFirmware(firmwarePath: path)
+                            
+                        firmwareversion = (Ryujinx.shared.fetchFirmwareVersion() == "" ? "0" : Ryujinx.shared.fetchFirmwareVersion())
+                        if fun {
+                            url.stopAccessingSecurityScopedResource()
+                        }
+                    }
+                case .failure(let error):
+                    print(error)
+                }
+            }
             .toolbar {
-                ToolbarItem(placement: .topBarLeading) {
+                ToolbarItem(placement: .topBarTrailing) {
                     Button {
                         isSelectingGameFile.toggle()
                     } label: {
                         Image(systemName: "plus")
                     }
                 }
-                
+
                 ToolbarItem(placement: .topBarLeading) {
                     Menu {
                         Text("Firmware Version: \(firmwareversion)")
@@ -215,7 +206,6 @@ struct GameLibraryView: View {
                 }
             }
         }
-        .background(Color(.systemGroupedBackground))
         .searchable(text: $searchText)
         .onChange(of: searchText) { _ in
             isSearching = !searchText.isEmpty
@@ -296,7 +286,6 @@ struct GameLibraryView: View {
         }
     }
     
-    
     private func addToRecentGames(_ game: Game) {
         recentGames.removeAll { $0.id == game.id }
         
@@ -329,8 +318,7 @@ struct GameLibraryView: View {
         }
     }
 
-    
-// MARK: - Delete Game Function
+    // MARK: - Delete Game Function
     func deleteGame(game: Game) {
         let fileManager = FileManager.default
         do {
@@ -343,7 +331,7 @@ struct GameLibraryView: View {
     }
 }
 
-// MARK: -Game Model
+// MARK: - Game Model
 extension Game: Codable {
     enum CodingKeys: String, CodingKey {
         case titleName, titleId, developer, version, fileURL
@@ -372,7 +360,7 @@ extension Game: Codable {
     }
 }
 
-// MARK: -Recent Game Card
+// MARK: - Recent Game Card
 struct RecentGameCard: View {
     let game: Game
     @Binding var startemu: Game?
@@ -393,7 +381,7 @@ struct RecentGameCard: View {
                     ZStack {
                         RoundedRectangle(cornerRadius: 12)
                             .fill(colorScheme == .dark ?
-                                  Color(.systemGray5) : Color(.systemGray6))
+                                Color(.systemGray5) : Color(.systemGray6))
                             .frame(width: 140, height: 140)
                         
                         Image(systemName: "gamecontroller.fill")
@@ -419,7 +407,7 @@ struct RecentGameCard: View {
     }
 }
 
-// MARK: -Game List Item
+// MARK: - Game List Item
 struct GameListRow: View {
     let game: Game
     @Binding var startemu: Game?
@@ -447,7 +435,7 @@ struct GameListRow: View {
                     ZStack {
                         RoundedRectangle(cornerRadius: 8)
                             .fill(colorScheme == .dark ?
-                                  Color(.systemGray5) : Color(.systemGray6))
+                                Color(.systemGray5) : Color(.systemGray6))
                             .frame(width: 45, height: 45)
                         
                         Image(systemName: "gamecontroller.fill")
@@ -474,9 +462,6 @@ struct GameListRow: View {
                     .foregroundColor(.accentColor)
                     .opacity(0.8)
             }
-            .padding(.horizontal)
-            .padding(.vertical, 8)
-            .background(Color(.systemBackground))
             .contextMenu {
                 Section {
                     Button {
@@ -533,4 +518,3 @@ struct GameListRow: View {
         }
     }
 }
-
diff --git a/src/MeloNX/MeloNX/App/Views/Updates/GameUpdateManagerSheet.swift b/src/MeloNX/MeloNX/App/Views/Updates/GameUpdateManagerSheet.swift
index da5209654..90211058b 100644
--- a/src/MeloNX/MeloNX/App/Views/Updates/GameUpdateManagerSheet.swift
+++ b/src/MeloNX/MeloNX/App/Views/Updates/GameUpdateManagerSheet.swift
@@ -15,39 +15,45 @@ struct UpdateManagerSheet: View {
     @Binding var game: Game?
     @State private var isSelectingGameUpdate = false
     @State private var jsonURL: URL? = nil
-    
+
     var body: some View {
         NavigationView {
-            VStack {
-                List(paths, id: \..self) { item in
-                    Button(action: {
-                        selectItem(item.lastPathComponent)
-                    }) {
-                        HStack {
-                            Text(item.lastPathComponent)
-                            if selectedItem == "\(game!.titleId)/\(item.lastPathComponent)" {
-                                Spacer()
-                                Image(systemName: "checkmark")
-                            }
-                        }
-                    }
-                    .contextMenu {
-                        Button {
-                            removeUpdate(item)
-                        } label: {
-                            Text("Remove Update")
+            List(paths, id: \..self, selection: $selectedItem) { item in
+                Button(action: {
+                    selectItem(item.lastPathComponent)
+                }) {
+                    HStack {
+                        Text(item.lastPathComponent)
+                            .foregroundStyle(Color(uiColor: .label))
+                        Spacer()
+                        if selectedItem == "\(game!.titleId)/\(item.lastPathComponent)" {
+                            Image(systemName: "checkmark.circle.fill")
+                                .foregroundStyle(Color.accentColor)
+                                .font(.system(size: 24))
+                        } else {
+                            Image(systemName: "circle")
+                                .foregroundStyle(Color(uiColor: .secondaryLabel))
+                                .font(.system(size: 24))
                         }
                     }
                 }
+                .contextMenu {
+                    Button {
+                        removeUpdate(item)
+                    } label: {
+                        Text("Remove Update")
+                    }
+                }
             }
-            .onAppear() {
+            .onAppear {
                 print(URL.documentsDirectory.appendingPathComponent("games").appendingPathComponent(game!.titleId).appendingPathComponent("updates.json"))
                 
                 loadJSON(URL.documentsDirectory.appendingPathComponent("games").appendingPathComponent(game!.titleId).appendingPathComponent("updates.json"))
             }
             .navigationTitle("\(game!.titleName) Updates")
+            .navigationBarTitleDisplayMode(.inline)
             .toolbar {
-                Button("+") {
+                Button("Add", systemImage: "plus") {
                     isSelectingGameUpdate = true
                 }
             }
@@ -80,7 +86,8 @@ struct UpdateManagerSheet: View {
                     let destinationURL = romUpdatedDirectory.appendingPathComponent(url.lastPathComponent)
                     try? fileManager.copyItem(at: url, to: destinationURL)
 
-                    Ryujinx.shared.setTitleUpdate(titleId: gameInfo.titleId, updatePath: "\(gameInfo.titleId)/" + url.lastPathComponent)
+                    items.append(gameInfo.titleId + "/" + url.lastPathComponent)
+                    selectItem(url.lastPathComponent)
                     Ryujinx.shared.games = Ryujinx.shared.loadGames()
                     loadJSON(jsonURL!)
                 } catch {
@@ -108,6 +115,7 @@ struct UpdateManagerSheet: View {
         }
         
         saveJSON(selectedItem: selectedItem ?? "")
+        Ryujinx.shared.games = Ryujinx.shared.loadGames()
     }
     
     func saveJSON(selectedItem: String) {
@@ -122,7 +130,6 @@ struct UpdateManagerSheet: View {
     }
     
     func loadJSON(_ json: URL) {
-        
         self.jsonURL = json
         print("Failed to read JSO")
         
@@ -132,16 +139,17 @@ struct UpdateManagerSheet: View {
         do {
             let data = try Data(contentsOf: jsonURL)
             if let jsonDict = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any],
-               let list = jsonDict["paths"] as? [String] {
+               let list = jsonDict["paths"] as? [String]
+            {
                 var urls: [URL] = []
                 
                 for path in list {
                     urls.append(URL.documentsDirectory.appendingPathComponent("updates").appendingPathComponent(path))
                 }
                 
-                self.items = list
-                self.paths = urls
-                self.selectedItem = jsonDict["selected"] as? String
+                items = list
+                paths = urls
+                selectedItem = jsonDict["selected"] as? String
             }
         } catch {
             print("Failed to read JSON: \(error)")
@@ -155,8 +163,8 @@ struct UpdateManagerSheet: View {
         do {
             let newData = try JSONSerialization.data(withJSONObject: defaultData, options: .prettyPrinted)
             try newData.write(to: jsonURL)
-            self.items = []
-            self.selectedItem = ""
+            items = []
+            selectedItem = ""
         } catch {
             print("Failed to create default JSON: \(error)")
         }
@@ -183,9 +191,9 @@ struct UpdateManagerSheet: View {
             
             let newData = try JSONSerialization.data(withJSONObject: jsonDict, options: .prettyPrinted)
             try newData.write(to: jsonURL)
+            Ryujinx.shared.games = Ryujinx.shared.loadGames()
         } catch {
             print("Failed to update JSON: \(error)")
         }
     }
-
 }

From 2a7cfa56508850ba60e481711f4259e5c0a16b02 Mon Sep 17 00:00:00 2001
From: Daniil Vinogradov <xitrix@bk.ru>
Date: Sun, 16 Feb 2025 13:38:41 +0100
Subject: [PATCH 3/4] GameInfo UI enhancement

---
 .../App/Views/GamesList/GameInfoSheet.swift   | 116 ++++++++++--------
 1 file changed, 68 insertions(+), 48 deletions(-)

diff --git a/src/MeloNX/MeloNX/App/Views/GamesList/GameInfoSheet.swift b/src/MeloNX/MeloNX/App/Views/GamesList/GameInfoSheet.swift
index f8fe23945..5c4f9c3c8 100644
--- a/src/MeloNX/MeloNX/App/Views/GamesList/GameInfoSheet.swift
+++ b/src/MeloNX/MeloNX/App/Views/GamesList/GameInfoSheet.swift
@@ -14,45 +14,52 @@ struct GameInfoSheet: View {
     
     var body: some View {
         iOSNav {
-            VStack {
-                if let icon = game.icon {
-                    Image(uiImage: icon)
-                        .resizable()
-                        .aspectRatio(contentMode: .fit)
-                        .frame(width: 250, height: 250)
-                        .cornerRadius(10)
-                        .padding()
-                        .contextMenu {
-                            Button {
-                                UIImageWriteToSavedPhotosAlbum(icon, nil, nil, nil)
-                            } label: {
-                                Label("Save to Photos", systemImage: "square.and.arrow.down")
-                            }
+            List {
+                Section {}
+                header: {
+                    VStack(alignment: .center) {
+                        if let icon = game.icon {
+                            Image(uiImage: icon)
+                                .resizable()
+                                .aspectRatio(contentMode: .fit)
+                                .frame(width: 250, height: 250)
+                                .cornerRadius(10)
+                                .padding()
+                                .contextMenu {
+                                    Button {
+                                        UIImageWriteToSavedPhotosAlbum(icon, nil, nil, nil)
+                                    } label: {
+                                        Label("Save to Photos", systemImage: "square.and.arrow.down")
+                                    }
+                                }
+                        } else {
+                            Image(systemName: "questionmark.circle")
+                                .resizable()
+                                .aspectRatio(contentMode: .fit)
+                                .frame(width: 150, height: 150)
+                                .padding()
                         }
-                } else {
-                    Image(systemName: "questionmark.circle")
-                        .resizable()
-                        .aspectRatio(contentMode: .fit)
-                        .frame(width: 150, height: 150)
-                        .padding()
-                }
-                
-                VStack(alignment: .leading) {
-                    VStack(alignment: .leading) {
-                        Text("**\(game.titleName)** | \(game.titleId.capitalized)")
-                        Text(game.developer)
-                            .font(.caption)
-                            .foregroundStyle(.secondary)
+                        VStack(alignment: .center) {
+                            Text("**\(game.titleName)** | \(game.titleId.capitalized)")
+                                .multilineTextAlignment(.center)
+                            Text(game.developer)
+                                .font(.caption)
+                                .foregroundStyle(.secondary)
+                        }
+                        .padding(.vertical, 3)
                     }
-                    .padding(.vertical, 3)
-                    
-                    VStack(alignment: .leading, spacing: 5) {
-                        Text("Information")
-                            .font(.title2)
-                            .bold()
-                        
-                        Text("**Version:** \(game.version)")
-                        Text("**Title ID:** \(game.titleId)")
+                    .frame(maxWidth: .infinity)
+                }
+
+                Section {
+                    HStack {
+                        Text("**Version**")
+                        Spacer()
+                        Text(game.version)
+                            .foregroundStyle(Color.secondary)
+                    }
+                    HStack {
+                        Text("**Title ID**")
                             .contextMenu {
                                 Button {
                                     UIPasteboard.general.string = game.titleId
@@ -60,15 +67,32 @@ struct GameInfoSheet: View {
                                     Text("Copy Title ID")
                                 }
                             }
-                        Text("**Game Size:** \(fetchFileSize(for: game.fileURL) ?? 0) bytes")
-                        Text("**File Type:** .\(getFileType(game.fileURL))")
-                        Text("**Game URL:** \(trimGameURL(game.fileURL))")
+                        Spacer()
+                        Text(game.titleId)
+                            .foregroundStyle(Color.secondary)
                     }
+                    HStack {
+                        Text("**Game Size**")
+                        Spacer()
+                        Text("\(fetchFileSize(for: game.fileURL) ?? 0) bytes")
+                            .foregroundStyle(Color.secondary)
+                    }
+                    HStack {
+                        Text("**File Type**")
+                        Spacer()
+                        Text(getFileType(game.fileURL))
+                            .foregroundStyle(Color.secondary)
+                    }
+                    VStack(alignment: .leading, spacing: 4) {
+                        Text("**Game URL**")
+                        Text(trimGameURL(game.fileURL))
+                            .foregroundStyle(Color.secondary)
+                    }
+                } header: {
+                    Text("Information")
                 }
-
-                Spacer()
+                .headerProminence(.increased)
             }
-            .padding(.horizontal, 5)
             .navigationTitle(game.titleName)
             .navigationBarTitleDisplayMode(.inline)
             .toolbar {
@@ -103,10 +127,6 @@ struct GameInfoSheet: View {
     }
     
     func getFileType(_ url: URL) -> String {
-        let path = url.path
-        if let range = path.range(of: ".") {
-            return String(path[range.upperBound...])
-        }
-        return "Unknown"
+        url.pathExtension
     }
 }

From 8c6dd455f2c45ff29373a85cb2bd4fdcf927af38 Mon Sep 17 00:00:00 2001
From: Daniil Vinogradov <xitrix@bk.ru>
Date: Sun, 16 Feb 2025 15:07:14 +0100
Subject: [PATCH 4/4] Game update path fix

---
 .../Updates/GameUpdateManagerSheet.swift      | 36 ++++++++++---------
 src/Ryujinx.Headless.SDL2/Program.cs          |  2 +-
 2 files changed, 20 insertions(+), 18 deletions(-)

diff --git a/src/MeloNX/MeloNX/App/Views/Updates/GameUpdateManagerSheet.swift b/src/MeloNX/MeloNX/App/Views/Updates/GameUpdateManagerSheet.swift
index 90211058b..0d7e8fa68 100644
--- a/src/MeloNX/MeloNX/App/Views/Updates/GameUpdateManagerSheet.swift
+++ b/src/MeloNX/MeloNX/App/Views/Updates/GameUpdateManagerSheet.swift
@@ -26,7 +26,7 @@ struct UpdateManagerSheet: View {
                         Text(item.lastPathComponent)
                             .foregroundStyle(Color(uiColor: .label))
                         Spacer()
-                        if selectedItem == "\(game!.titleId)/\(item.lastPathComponent)" {
+                        if selectedItem == "updates/\(game!.titleId)/\(item.lastPathComponent)" {
                             Image(systemName: "checkmark.circle.fill")
                                 .foregroundStyle(Color.accentColor)
                                 .font(.system(size: 24))
@@ -86,7 +86,7 @@ struct UpdateManagerSheet: View {
                     let destinationURL = romUpdatedDirectory.appendingPathComponent(url.lastPathComponent)
                     try? fileManager.copyItem(at: url, to: destinationURL)
 
-                    items.append(gameInfo.titleId + "/" + url.lastPathComponent)
+                    items.append("updates/" + gameInfo.titleId + "/" + url.lastPathComponent)
                     selectItem(url.lastPathComponent)
                     Ryujinx.shared.games = Ryujinx.shared.loadGames()
                     loadJSON(jsonURL!)
@@ -100,7 +100,7 @@ struct UpdateManagerSheet: View {
     }
     
     func removeUpdate(_ game: URL) {
-        let gameString = "\(self.game!.titleId)/\(game.lastPathComponent)"
+        let gameString = "updates/\(self.game!.titleId)/\(game.lastPathComponent)"
         paths.removeAll { $0 == game }
         items.removeAll { $0 == gameString }
         
@@ -131,23 +131,25 @@ struct UpdateManagerSheet: View {
     
     func loadJSON(_ json: URL) {
         self.jsonURL = json
-        print("Failed to read JSO")
         
-        guard let jsonURL = jsonURL else { return }
-        print("Failed to read JSOK")
+        guard let jsonURL else { return }
         
         do {
             let data = try Data(contentsOf: jsonURL)
             if let jsonDict = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any],
                let list = jsonDict["paths"] as? [String]
             {
-                var urls: [URL] = []
-                
-                for path in list {
-                    urls.append(URL.documentsDirectory.appendingPathComponent("updates").appendingPathComponent(path))
+
+                let filteredList = list.filter { relativePath in
+                    let path = URL.documentsDirectory.appendingPathComponent(relativePath)
+                    return FileManager.default.fileExists(atPath: path.path)
                 }
-                
-                items = list
+
+                let urls: [URL] = filteredList.map { relativePath in
+                    URL.documentsDirectory.appendingPathComponent(relativePath)
+                }
+
+                items = filteredList
                 paths = urls
                 selectedItem = jsonDict["selected"] as? String
             }
@@ -171,9 +173,9 @@ struct UpdateManagerSheet: View {
     }
     
     func selectItem(_ item: String) {
-        let newSelection = "\(game!.titleId)/\(item)"
-        
-        guard let jsonURL = jsonURL else { return }
+        let newSelection = "updates/\(game!.titleId)/\(item)"
+
+        guard let jsonURL else { return }
         
         do {
             let data = try Data(contentsOf: jsonURL)
@@ -183,12 +185,12 @@ struct UpdateManagerSheet: View {
                 jsonDict["selected"] = ""
                 selectedItem = ""
             } else {
-                jsonDict["selected"] = newSelection
+                jsonDict["selected"] = "\(newSelection)"
                 selectedItem = newSelection
             }
             
             jsonDict["paths"] = items
-            
+
             let newData = try JSONSerialization.data(withJSONObject: jsonDict, options: .prettyPrinted)
             try newData.write(to: jsonURL)
             Ryujinx.shared.games = Ryujinx.shared.loadGames()
diff --git a/src/Ryujinx.Headless.SDL2/Program.cs b/src/Ryujinx.Headless.SDL2/Program.cs
index ad78c40ea..5c6b84c11 100644
--- a/src/Ryujinx.Headless.SDL2/Program.cs
+++ b/src/Ryujinx.Headless.SDL2/Program.cs
@@ -752,7 +752,7 @@ namespace Ryujinx.Headless.SDL2
                     if (File.Exists(titleUpdateMetadataPath))
                     {
                         string updatePathRelative = JsonHelper.DeserializeFromFile(titleUpdateMetadataPath, _titleSerializerContext.TitleUpdateMetadata).Selected;
-                        updatePath = Path.Combine(AppDataManager.BaseDirPath, "updates", updatePathRelative);
+                        updatePath = Path.Combine(AppDataManager.BaseDirPath, updatePathRelative);
 
                         if (File.Exists(updatePath))
                         {