Skip to content

[Feature] File Provider — Save State & Screenshot Enumeration #3617

@github-actions

Description

@github-actions

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

  • Provenance / Save States folder visible in Files.app
  • Save states grouped by game name sub-folder
  • Save state files can be copied out from Files.app
  • Provenance / Screenshots folder visible in Files.app
  • Screenshots grouped by game name sub-folder
  • Screenshot images can be copied out from Files.app
  • Deletion works: deleting a save state from Files.app removes it from Realm + filesystem

Related

Part of #3595
Part of #2659 (Import / Export / Share epic)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Status

    In Progress

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions