Summary
Add Save States and Screenshots as browsable categories in the Provenance ROM File Provider extension so users can copy-out save states and screenshots from Files.app.
Current State
PR #3607 completed the ROM File ProviderUI extension skeleton. The main NSFileProviderReplicatedExtension (ROM File Provider target) already exposes the full ROM library (Systems / Publishers / Years / Regions / Ratings) with create, read, rename, and delete support. However, save states and screenshots are not yet surfaced.
Goals
- Save States top-level category in Files.app:
Provenance / Save States / <Game Title> / <save.pvs>
- Screenshots top-level category:
Provenance / Screenshots / <Game Title> / <screenshot.png>
- Read-only: copy-out is allowed; delete is allowed to let users clean up from Files.app
- Each game that has saves/screenshots gets a named sub-folder (
ss-game:<md5> / sc-game:<md5>)
Implementation Notes
1. PVPrimitives/RomFileProviderVirtualPath.swift
Add two new cases to RomFileProviderRootCategory:
case saveStates = "cat:savestates"
case screenshots = "cat:screenshots"
Add prefix constants to RomFileProviderVirtualPath:
public static let saveStateGameFolderPrefix = "ss-game:"
public static let saveStateItemPrefix = "ss:"
public static let screenshotGameFolderPrefix = "sc-game:"
public static let screenshotItemPrefix = "sc:"
2. Extensions/ROM File Provider/FileProviderItem.swift
New Kind cases:
saveStateGameFolder(game: Game, parentItemIdentifier:)
saveStateFile(id: String, game: Game, date: Date, isAutosave: Bool, userDescription: String?, fileURL: URL?, parentItemIdentifier:)
screenshotGameFolder(game: Game, parentItemIdentifier:)
screenshotFile(gameMD5: String, index: Int, imageURL: URL?, parentItemIdentifier:)
3. Extensions/ROM File Provider/RomFileProviderCPDI.swift
Add:
LocalSaveStateEntry struct (game CPDI snapshot + id + fileURL + date + isAutosave + description)
LocalScreenshotEntry struct (gameMD5 + index + imageURL)
loadAllSaveStateEntries() — snapshot all PVSaveState objects (via PVGame.saveStates) that have a local file
loadAllScreenshotEntries() — snapshot all PVGame.screenShots that have a local file
saveStateGameFolders() — distinct game snapshots that have at least one save state
screenshotGameFolders() — distinct game snapshots that have at least one screenshot
4. Extensions/ROM File Provider/FileProviderEnumerator.swift
Wire the new prefixes in buildItems:
cat:savestates → list game folders (ss-game:<md5>) for games that have save states
ss-game:<md5> → list individual save state items (ss:<id>)
cat:screenshots → list game folders (sc-game:<md5>) for games that have screenshots
sc-game:<md5> → list individual screenshot items (sc:<md5>:<index>)
5. Extensions/ROM File Provider/FileProviderExtension.swift
resolveItem — handle ss-game:, ss:, sc-game:, sc: prefixes
fetchContents — handle ss:<id> (serve .file?.url) and sc:<md5>:<index> (serve screenshot URL)
deleteItem — handle save state and screenshot deletion
- Read-only guard on
modifyItem for save state / screenshot items
Acceptance Criteria
Related
Part of #3595
Part of #2659 (Import / Export / Share epic)
Summary
Add Save States and Screenshots as browsable categories in the Provenance ROM File Provider extension so users can copy-out save states and screenshots from Files.app.
Current State
PR #3607 completed the ROM File ProviderUI extension skeleton. The main
NSFileProviderReplicatedExtension(ROM File Providertarget) already exposes the full ROM library (Systems / Publishers / Years / Regions / Ratings) with create, read, rename, and delete support. However, save states and screenshots are not yet surfaced.Goals
Provenance / Save States / <Game Title> / <save.pvs>Provenance / Screenshots / <Game Title> / <screenshot.png>ss-game:<md5>/sc-game:<md5>)Implementation Notes
1.
PVPrimitives/RomFileProviderVirtualPath.swiftAdd two new cases to
RomFileProviderRootCategory:Add prefix constants to
RomFileProviderVirtualPath:2.
Extensions/ROM File Provider/FileProviderItem.swiftNew
Kindcases:saveStateGameFolder(game: Game, parentItemIdentifier:)saveStateFile(id: String, game: Game, date: Date, isAutosave: Bool, userDescription: String?, fileURL: URL?, parentItemIdentifier:)screenshotGameFolder(game: Game, parentItemIdentifier:)screenshotFile(gameMD5: String, index: Int, imageURL: URL?, parentItemIdentifier:)3.
Extensions/ROM File Provider/RomFileProviderCPDI.swiftAdd:
LocalSaveStateEntrystruct (game CPDI snapshot + id + fileURL + date + isAutosave + description)LocalScreenshotEntrystruct (gameMD5 + index + imageURL)loadAllSaveStateEntries()— snapshot allPVSaveStateobjects (viaPVGame.saveStates) that have a local fileloadAllScreenshotEntries()— snapshot allPVGame.screenShotsthat have a local filesaveStateGameFolders()— distinct game snapshots that have at least one save statescreenshotGameFolders()— distinct game snapshots that have at least one screenshot4.
Extensions/ROM File Provider/FileProviderEnumerator.swiftWire the new prefixes in
buildItems:cat:savestates→ list game folders (ss-game:<md5>) for games that have save statesss-game:<md5>→ list individual save state items (ss:<id>)cat:screenshots→ list game folders (sc-game:<md5>) for games that have screenshotssc-game:<md5>→ list individual screenshot items (sc:<md5>:<index>)5.
Extensions/ROM File Provider/FileProviderExtension.swiftresolveItem— handless-game:,ss:,sc-game:,sc:prefixesfetchContents— handless:<id>(serve.file?.url) andsc:<md5>:<index>(serve screenshot URL)deleteItem— handle save state and screenshot deletionmodifyItemfor save state / screenshot itemsAcceptance Criteria
Provenance / Save Statesfolder visible in Files.appProvenance / Screenshotsfolder visible in Files.appRelated
Part of #3595
Part of #2659 (Import / Export / Share epic)