Fixes sharing games + exporting save files
* Makes temporary copy instead of symbolic link when exporting game * Sanitizes game name before exporting game * Fixes prematurely deleting temporary game * Exported save file names now match exported game names
This commit is contained in:
parent
9e437797d9
commit
edab6ea432
@ -165,6 +165,7 @@
|
|||||||
BFFDF03723E3BB2600931B96 /* libSnes9x.a in Frameworks */ = {isa = PBXBuildFile; fileRef = BFFDF03623E3BB2600931B96 /* libSnes9x.a */; };
|
BFFDF03723E3BB2600931B96 /* libSnes9x.a in Frameworks */ = {isa = PBXBuildFile; fileRef = BFFDF03623E3BB2600931B96 /* libSnes9x.a */; };
|
||||||
BFFDF03F23E3C28A00931B96 /* libGambatte.a in Frameworks */ = {isa = PBXBuildFile; fileRef = BFFDF03D23E3C0F000931B96 /* libGambatte.a */; };
|
BFFDF03F23E3C28A00931B96 /* libGambatte.a in Frameworks */ = {isa = PBXBuildFile; fileRef = BFFDF03D23E3C0F000931B96 /* libGambatte.a */; };
|
||||||
BFFDF04623E3D3A600931B96 /* libMupen64Plus.a in Frameworks */ = {isa = PBXBuildFile; fileRef = BFFDF04523E3D3A600931B96 /* libMupen64Plus.a */; };
|
BFFDF04623E3D3A600931B96 /* libMupen64Plus.a in Frameworks */ = {isa = PBXBuildFile; fileRef = BFFDF04523E3D3A600931B96 /* libMupen64Plus.a */; };
|
||||||
|
D5011C48281B6E8B00A0760B /* CharacterSet+Filename.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5011C47281B6E8B00A0760B /* CharacterSet+Filename.swift */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXContainerItemProxy section */
|
/* Begin PBXContainerItemProxy section */
|
||||||
@ -358,6 +359,7 @@
|
|||||||
BFFDF03D23E3C0F000931B96 /* libGambatte.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libGambatte.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
BFFDF03D23E3C0F000931B96 /* libGambatte.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libGambatte.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
BFFDF04523E3D3A600931B96 /* libMupen64Plus.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libMupen64Plus.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
BFFDF04523E3D3A600931B96 /* libMupen64Plus.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libMupen64Plus.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
C786AF1D2DDB6223BE2063CC /* Pods-Delta.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Delta.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Delta/Pods-Delta.debug.xcconfig"; sourceTree = "<group>"; };
|
C786AF1D2DDB6223BE2063CC /* Pods-Delta.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Delta.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Delta/Pods-Delta.debug.xcconfig"; sourceTree = "<group>"; };
|
||||||
|
D5011C47281B6E8B00A0760B /* CharacterSet+Filename.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CharacterSet+Filename.swift"; sourceTree = "<group>"; };
|
||||||
DC866E433B3BA9AE18ABA1EC /* libPods-Delta.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Delta.a"; sourceTree = BUILT_PRODUCTS_DIR; };
|
DC866E433B3BA9AE18ABA1EC /* libPods-Delta.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Delta.a"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
@ -403,6 +405,7 @@
|
|||||||
BF647A6922FB8FCE0061D76D /* Bundle+SwizzleBundleID.swift */,
|
BF647A6922FB8FCE0061D76D /* Bundle+SwizzleBundleID.swift */,
|
||||||
BFD1EF3F2336BD8800D197CF /* UIDevice+Processor.swift */,
|
BFD1EF3F2336BD8800D197CF /* UIDevice+Processor.swift */,
|
||||||
BFE56E1823EB7BE00014FECD /* UIImage+SymbolFallback.swift */,
|
BFE56E1823EB7BE00014FECD /* UIImage+SymbolFallback.swift */,
|
||||||
|
D5011C47281B6E8B00A0760B /* CharacterSet+Filename.swift */,
|
||||||
);
|
);
|
||||||
path = Extensions;
|
path = Extensions;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -1114,6 +1117,7 @@
|
|||||||
BF525EEA1FF6CD12004AA849 /* DeepLink.swift in Sources */,
|
BF525EEA1FF6CD12004AA849 /* DeepLink.swift in Sources */,
|
||||||
BF8A334621A4926F00A42FD4 /* GameSyncStatusViewController.swift in Sources */,
|
BF8A334621A4926F00A42FD4 /* GameSyncStatusViewController.swift in Sources */,
|
||||||
BF59427E1E09BC830051894B /* Game.swift in Sources */,
|
BF59427E1E09BC830051894B /* Game.swift in Sources */,
|
||||||
|
D5011C48281B6E8B00A0760B /* CharacterSet+Filename.swift in Sources */,
|
||||||
BFE593CC21F3F8C2003412A6 /* _GameSave.swift in Sources */,
|
BFE593CC21F3F8C2003412A6 /* _GameSave.swift in Sources */,
|
||||||
BF63A1A321A4AAAE00EE8F61 /* RecordSyncStatusViewController.swift in Sources */,
|
BF63A1A321A4AAAE00EE8F61 /* RecordSyncStatusViewController.swift in Sources */,
|
||||||
BFAA1FED1B8AA4FA00495943 /* Settings.swift in Sources */,
|
BFAA1FED1B8AA4FA00495943 /* Settings.swift in Sources */,
|
||||||
|
|||||||
22
Delta/Extensions/CharacterSet+Filename.swift
Normal file
22
Delta/Extensions/CharacterSet+Filename.swift
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
//
|
||||||
|
// CharacterSet+Filename.swift
|
||||||
|
// Delta
|
||||||
|
//
|
||||||
|
// Created by Riley Testut on 4/28/22.
|
||||||
|
// Copyright © 2022 Riley Testut. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
extension CharacterSet
|
||||||
|
{
|
||||||
|
// Different than .urlPathAllowed
|
||||||
|
// Copied from https://stackoverflow.com/a/39443252
|
||||||
|
static var urlFilenameAllowed: CharacterSet {
|
||||||
|
var illegalCharacters = CharacterSet(charactersIn: ":/")
|
||||||
|
illegalCharacters.formUnion(.newlines)
|
||||||
|
illegalCharacters.formUnion(.illegalCharacters)
|
||||||
|
illegalCharacters.formUnion(.controlCharacters)
|
||||||
|
return illegalCharacters.inverted
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -705,28 +705,36 @@ private extension GameCollectionViewController
|
|||||||
|
|
||||||
func share(_ game: Game)
|
func share(_ game: Game)
|
||||||
{
|
{
|
||||||
let temporaryDirectory = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString)
|
let temporaryDirectory = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString, isDirectory: true)
|
||||||
let symbolicURL = temporaryDirectory.appendingPathComponent(game.name + "." + game.fileURL.pathExtension)
|
|
||||||
|
let sanitizedName = game.name.components(separatedBy: .urlFilenameAllowed.inverted).joined()
|
||||||
|
let temporaryURL = temporaryDirectory.appendingPathComponent(sanitizedName + "." + game.fileURL.pathExtension, isDirectory: false)
|
||||||
|
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
try FileManager.default.createDirectory(at: temporaryDirectory, withIntermediateDirectories: true, attributes: nil)
|
try FileManager.default.createDirectory(at: temporaryDirectory, withIntermediateDirectories: true, attributes: nil)
|
||||||
|
|
||||||
// Create a symbolic link so we can control the file name used when sharing.
|
// Make a temporary copy so we can control the filename used when sharing.
|
||||||
// Otherwise, if we just passed in game.fileURL to UIActivityViewController, the file name would be the game's SHA1 hash.
|
// Otherwise, if we just passed in game.fileURL to UIActivityViewController, the file name would be the game's SHA1 hash.
|
||||||
try FileManager.default.createSymbolicLink(at: symbolicURL, withDestinationURL: game.fileURL)
|
try FileManager.default.copyItem(at: game.fileURL, to: temporaryURL, shouldReplace: true)
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
print(error)
|
let alertController = UIAlertController(title: NSLocalizedString("Could Not Share Game", comment: ""), error: error)
|
||||||
|
self.present(alertController, animated: true, completion: nil)
|
||||||
|
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let copyDeepLinkActivity = CopyDeepLinkActivity()
|
let copyDeepLinkActivity = CopyDeepLinkActivity()
|
||||||
|
|
||||||
let activityViewController = UIActivityViewController(activityItems: [symbolicURL, game], applicationActivities: [copyDeepLinkActivity])
|
let activityViewController = UIActivityViewController(activityItems: [temporaryURL, game], applicationActivities: [copyDeepLinkActivity])
|
||||||
activityViewController.popoverPresentationController?.sourceView = self._popoverSourceView?.superview
|
activityViewController.popoverPresentationController?.sourceView = self._popoverSourceView?.superview
|
||||||
activityViewController.popoverPresentationController?.sourceRect = self._popoverSourceView?.frame ?? .zero
|
activityViewController.popoverPresentationController?.sourceRect = self._popoverSourceView?.frame ?? .zero
|
||||||
activityViewController.completionWithItemsHandler = { (activityType, finished, returnedItems, error) in
|
activityViewController.completionWithItemsHandler = { (activityType, finished, returnedItems, error) in
|
||||||
|
// Make sure the user either shared the game or cancelled before deleting temporaryDirectory.
|
||||||
|
guard finished || activityType == nil else { return }
|
||||||
|
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
try FileManager.default.removeItem(at: temporaryDirectory)
|
try FileManager.default.removeItem(at: temporaryDirectory)
|
||||||
@ -736,6 +744,7 @@ private extension GameCollectionViewController
|
|||||||
print(error)
|
print(error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.present(activityViewController, animated: true, completion: nil)
|
self.present(activityViewController, animated: true, completion: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -791,8 +800,7 @@ private extension GameCollectionViewController
|
|||||||
{
|
{
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
let illegalCharacterSet = CharacterSet(charactersIn: "\"\\/?<>:*|")
|
let sanitizedFilename = game.name.components(separatedBy: .urlFilenameAllowed.inverted).joined()
|
||||||
let sanitizedFilename = game.name.components(separatedBy: illegalCharacterSet).joined() + "." + game.gameSaveURL.pathExtension
|
|
||||||
|
|
||||||
let temporaryURL = FileManager.default.temporaryDirectory.appendingPathComponent(sanitizedFilename)
|
let temporaryURL = FileManager.default.temporaryDirectory.appendingPathComponent(sanitizedFilename)
|
||||||
try FileManager.default.copyItem(at: game.gameSaveURL, to: temporaryURL, shouldReplace: true)
|
try FileManager.default.copyItem(at: game.gameSaveURL, to: temporaryURL, shouldReplace: true)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user