Skip to content

Commit de19899

Browse files
fix(SaveSystem): self-review fixes for migration guide
- Remove duplicate KnownEmulator from PVPrimitives; use canonical PVLibrary version (proper UIKit, Codable, deltaLite, exportDeepLinkURL) - Add symbolName/systemSummary/installedEmulators as private PVUI extensions on KnownEmulator, keeping display concerns out of PVLibrary - Fix private RetroTheme enum shadowing public RetroTheme from PVUIBase; re-adds retro grid background to migration views - Correct emulator name: "Manic Emu" → "Mantic Emu" throughout - Replace deprecated edgesIgnoringSafeArea with ignoresSafeArea - Fix manualImportSection: replace Button+NavigationLink overlay hack with a plain NavigationLink - Handle .deltaLite case in all switch statements; filter from all-cases list to avoid duplicate Delta entry in UI Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 2b36ccf commit de19899

File tree

4 files changed

+93
-183
lines changed

4 files changed

+93
-183
lines changed

.changelog/3556.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
### Added
2-
- **Third-Party Emulator Migration Guide** — New "Import from Another Emulator" screen in Settings → Library detects installed emulators (Delta, RetroArch, Manic Emu, PPSSPP, Gamma) and provides step-by-step export/import instructions for each.
3-
- **`KnownEmulator` enum**`PVPrimitives` now exposes a `KnownEmulator` enum with metadata (bundle ID, URL scheme, save extensions, system summary) for each supported third-party emulator and a `@MainActor` detection helper using `canOpenURL`.
2+
- **Third-Party Emulator Migration Guide** — New "Import from Another Emulator" screen in Settings → Library detects installed emulators (Delta, RetroArch, Mantic Emu, PPSSPP, Gamma) and provides step-by-step export/import instructions for each.
3+
- **`KnownEmulator` UI extensions**`PVUI` adds `symbolName`, `systemSummary`, and `installedEmulators` helpers on top of the `KnownEmulator` registry defined in `PVLibrary`, keeping display-layer concerns out of the data layer.
44
- **`LSApplicationQueriesSchemes` additions** — All iOS and tvOS Info.plist variants now declare the `delta`, `retroarch`, and `ppsspp` URL schemes so presence detection works at runtime.
55
- **Manual Import Guide** — Covers `.sav`/`.srm`/`.state` imports via Files.app and Provenance's built-in web server for users not migrating from a specific third-party app.
6+
7+
### Fixed
8+
- **RetroTheme shadowing** — Removed private `RetroTheme` enum that was shadowing the real `RetroTheme` from `PVUIBase`, causing the retro grid background to be replaced with a plain black color.
9+
- **Emulator name typo** — "Manic Emu" corrected to "Mantic Emu" throughout the migration guide (bundle ID is `com.manticstudios.ManticEmu`).

PVPrimitives/Sources/PVPrimitives/KnownEmulator.swift

Lines changed: 0 additions & 137 deletions
This file was deleted.

PVUI/Sources/PVSwiftUI/Settings/SettingsSwiftUI.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2454,7 +2454,7 @@ private struct LibrarySection: View {
24542454

24552455
NavigationLink(destination: ExternalEmulatorMigrationView()) {
24562456
SettingsRow(title: "Import from Another Emulator",
2457-
subtitle: "Step-by-step guide to migrate saves from Delta, RetroArch, Manic, PPSSPP, or Gamma.",
2457+
subtitle: "Step-by-step guide to migrate saves from Delta, RetroArch, Mantic Emu, PPSSPP, or Gamma.",
24582458
icon: .sfSymbol("arrow.triangle.2.circlepath"))
24592459
}
24602460
#if os(tvOS)

PVUI/Sources/PVSwiftUI/Settings/Views/ExternalEmulatorMigrationView.swift

Lines changed: 86 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,55 @@
55
// Created by Joseph Mattiello on 3/28/26.
66
// Copyright © 2026 Provenance Emu. All rights reserved.
77
//
8-
// Guides users through importing saves from Delta, RetroArch, Manic Emu,
8+
// Guides users through importing saves from Delta, RetroArch, Mantic Emu,
99
// PPSSPP, and Gamma into Provenance.
1010
//
1111

1212
import SwiftUI
13-
import PVPrimitives
13+
import PVLibrary
14+
import PVUIBase
1415
import PVThemes
1516

17+
// MARK: - KnownEmulator UI extensions
18+
19+
/// UI-layer properties for the migration guide.
20+
/// These belong in PVUI, not in PVLibrary, to keep PVLibrary free of SwiftUI/UIKit display concerns.
21+
private extension KnownEmulator {
22+
/// SF Symbol name that best represents this emulator's primary platform(s).
23+
var symbolName: String {
24+
switch self {
25+
case .delta, .deltaLite: return "gamecontroller.fill"
26+
case .manticEmu: return "bolt.fill"
27+
case .retroArch: return "cpu.fill"
28+
case .ppsspp: return "memorychip"
29+
case .gamma: return "squareshape.dotted.squareshape"
30+
}
31+
}
32+
33+
/// Short description of which systems/games this emulator handles.
34+
var systemSummary: String {
35+
switch self {
36+
case .delta, .deltaLite: return "NES, SNES, N64, GBA, GBC, DS"
37+
case .manticEmu: return "GBA, NES, SNES, Genesis"
38+
case .retroArch: return "60+ systems"
39+
case .ppsspp: return "PlayStation Portable (PSP)"
40+
case .gamma: return "Game Boy, Game Boy Color"
41+
}
42+
}
43+
44+
/// Returns all emulators detected as installed on the current device.
45+
@MainActor
46+
static var installedEmulators: [KnownEmulator] {
47+
KnownEmulator.allCases.filter { $0.isInstalled }
48+
}
49+
}
50+
51+
// MARK: - KnownEmulator + Identifiable (for sheet(item:))
52+
53+
extension KnownEmulator: Identifiable {
54+
public var id: String { rawValue }
55+
}
56+
1657
// MARK: - Root view
1758

1859
/// "Import from Another Emulator" entry in Settings → Library.
@@ -30,7 +71,7 @@ public struct ExternalEmulatorMigrationView: View {
3071
public var body: some View {
3172
ZStack {
3273
RetroTheme.retroBackground
33-
.edgesIgnoringSafeArea(.all)
74+
.ignoresSafeArea()
3475

3576
ScrollView {
3677
VStack(spacing: 20) {
@@ -121,7 +162,7 @@ public struct ExternalEmulatorMigrationView: View {
121162
Image(systemName: "app.badge.questionmark")
122163
.font(.system(size: 32))
123164
.foregroundColor(.secondary)
124-
Text("Delta, RetroArch, PPSSPP, Manic Emu, and Gamma were not found on this device.")
165+
Text("Delta, RetroArch, PPSSPP, Mantic Emu, and Gamma were not found on this device.")
125166
.font(.footnote)
126167
.foregroundColor(.secondary)
127168
.multilineTextAlignment(.center)
@@ -141,7 +182,8 @@ public struct ExternalEmulatorMigrationView: View {
141182
.font(.caption)
142183
.foregroundColor(.secondary)
143184

144-
ForEach(KnownEmulator.allCases, id: \.rawValue) { emulator in
185+
// Show one entry per distinct app (skip deltaLite — same steps as delta)
186+
ForEach(KnownEmulator.allCases.filter { $0 != .deltaLite }, id: \.rawValue) { emulator in
145187
Button {
146188
selectedEmulator = emulator
147189
} label: {
@@ -173,19 +215,40 @@ public struct ExternalEmulatorMigrationView: View {
173215
VStack(alignment: .leading, spacing: 12) {
174216
sectionHeader(title: "Manual Import", icon: "folder.fill", color: .blue)
175217

176-
EmulatorRowView(
177-
icon: "folder.badge.plus",
178-
iconColor: .blue,
179-
title: "Import via Files.app",
180-
subtitle: "Copy .sav / .srm / .state files into Provenance's folder using the Files app",
181-
action: { selectedEmulator = nil }
182-
)
183-
.overlay(
184-
NavigationLink(destination: ManualFileImportGuideView()) {
185-
EmptyView()
218+
NavigationLink(destination: ManualFileImportGuideView()) {
219+
HStack(spacing: 14) {
220+
ZStack {
221+
Circle()
222+
.fill(Color.blue.opacity(0.15))
223+
.frame(width: 44, height: 44)
224+
Image(systemName: "folder.badge.plus")
225+
.font(.system(size: 18))
226+
.foregroundColor(.blue)
227+
}
228+
VStack(alignment: .leading, spacing: 2) {
229+
Text("Import via Files.app")
230+
.font(.subheadline.weight(.semibold))
231+
.foregroundColor(.primary)
232+
Text("Copy .sav / .srm / .state files into Provenance's folder using the Files app")
233+
.font(.caption)
234+
.foregroundColor(.secondary)
235+
.lineLimit(2)
236+
}
237+
Spacer()
238+
Image(systemName: "chevron.right")
239+
.font(.caption.weight(.semibold))
240+
.foregroundColor(.secondary)
186241
}
187-
.opacity(0)
188-
)
242+
.padding(.horizontal, 14)
243+
.padding(.vertical, 12)
244+
.background(Color.white.opacity(0.06))
245+
.cornerRadius(12)
246+
.overlay(
247+
RoundedRectangle(cornerRadius: 12)
248+
.stroke(Color.blue.opacity(0.2), lineWidth: 1)
249+
)
250+
}
251+
.buttonStyle(.plain)
189252
}
190253
}
191254

@@ -211,12 +274,6 @@ public struct ExternalEmulatorMigrationView: View {
211274
}
212275
}
213276

214-
// MARK: - KnownEmulator + Identifiable (for sheet(item:))
215-
216-
extension KnownEmulator: Identifiable {
217-
public var id: String { rawValue }
218-
}
219-
220277
// MARK: - Emulator row
221278

222279
private struct EmulatorRowView: View {
@@ -234,14 +291,6 @@ private struct EmulatorRowView: View {
234291
self.action = action
235292
}
236293

237-
init(icon: String, iconColor: Color, title: String, subtitle: String, action: @escaping () -> Void) {
238-
self.icon = icon
239-
self.iconColor = iconColor
240-
self.title = title
241-
self.subtitle = subtitle
242-
self.action = action
243-
}
244-
245294
var body: some View {
246295
Button(action: action) {
247296
HStack(spacing: 14) {
@@ -293,7 +342,7 @@ struct EmulatorMigrationGuideView: View {
293342
var body: some View {
294343
ZStack {
295344
RetroTheme.retroBackground
296-
.edgesIgnoringSafeArea(.all)
345+
.ignoresSafeArea()
297346

298347
ScrollView {
299348
VStack(alignment: .leading, spacing: 24) {
@@ -378,7 +427,7 @@ struct EmulatorMigrationGuideView: View {
378427
Text("Save File Extensions")
379428
.font(.subheadline.weight(.semibold))
380429
.foregroundColor(.primary)
381-
Text("Look for files ending in: \(emulator.saveExtensions.map { ".\($0)" }.joined(separator: ", "))")
430+
Text("Look for files ending in: \(emulator.saveFileExtensions.map { ".\($0)" }.joined(separator: ", "))")
382431
.font(.caption)
383432
.foregroundColor(.secondary)
384433
if emulator == .retroArch {
@@ -397,7 +446,7 @@ struct EmulatorMigrationGuideView: View {
397446

398447
private var exportSteps: [String] {
399448
switch emulator {
400-
case .delta:
449+
case .delta, .deltaLite:
401450
return [
402451
"Open Delta and go to the game you want to export.",
403452
"Long-press the game thumbnail to reveal the context menu.",
@@ -406,9 +455,9 @@ struct EmulatorMigrationGuideView: View {
406455
"Use the share sheet to send the file to Files.app or AirDrop to your Mac.",
407456
"Repeat for each game you want to migrate."
408457
]
409-
case .manic:
458+
case .manticEmu:
410459
return [
411-
"Open Manic Emu and navigate to your game library.",
460+
"Open Mantic Emu and navigate to your game library.",
412461
"Long-press a game to bring up options.",
413462
"Tap 'Export Save' or 'Share Save'.",
414463
"Save the .sav file to the Files app (iCloud Drive or local storage).",
@@ -462,7 +511,7 @@ struct ManualFileImportGuideView: View {
462511
var body: some View {
463512
ZStack {
464513
RetroTheme.retroBackground
465-
.edgesIgnoringSafeArea(.all)
514+
.ignoresSafeArea()
466515

467516
ScrollView {
468517
VStack(alignment: .leading, spacing: 24) {
@@ -587,9 +636,3 @@ private struct StepRowView: View {
587636
.padding(.vertical, 2)
588637
}
589638
}
590-
591-
// MARK: - RetroTheme helper (matches existing pattern)
592-
593-
private enum RetroTheme {
594-
static var retroBackground: Color { Color.black }
595-
}

0 commit comments

Comments
 (0)