Adds ability to import game save files

This commit is contained in:
Riley Testut 2019-03-01 11:42:37 -08:00
parent 43b4aeac51
commit 803d180a9b
3 changed files with 170 additions and 93 deletions

View File

@ -64,6 +64,7 @@ class GameCollectionViewController: UICollectionViewController
private var _renameAction: UIAlertAction?
private var _changingArtworkGame: Game?
private var _importingSaveFileGame: Game?
required init?(coder aDecoder: NSCoder)
{
@ -395,6 +396,10 @@ private extension GameCollectionViewController
self.viewSaveStates(for: game)
})
let importSaveFile = Action(title: NSLocalizedString("Import Save File", comment: ""), style: .default) { [unowned self] _ in
self.importSaveFile(for: game)
}
let deleteAction = Action(title: NSLocalizedString("Delete", comment: ""), style: .destructive, action: { [unowned self] action in
self.delete(game)
})
@ -402,7 +407,7 @@ private extension GameCollectionViewController
switch game.type
{
case GameType.unknown: return [cancelAction, renameAction, changeArtworkAction, shareAction, deleteAction]
default: return [cancelAction, renameAction, changeArtworkAction, shareAction, saveStatesAction, deleteAction]
default: return [cancelAction, renameAction, changeArtworkAction, shareAction, saveStatesAction, importSaveFile, deleteAction]
}
}
@ -481,6 +486,87 @@ private extension GameCollectionViewController
self.present(importController, animated: true, completion: nil)
}
func changeArtwork(for game: Game, toImageAt url: URL?, errors: [Error])
{
var errors = errors
var imageURL: URL?
if let url = url
{
if url.isFileURL
{
do
{
let imageData = try Data(contentsOf: url)
if
let image = UIImage(data: imageData),
let resizedImage = image.resizing(toFit: CGSize(width: 300, height: 300)),
let resizedData = resizedImage.jpegData(compressionQuality: 0.85)
{
let destinationURL = DatabaseManager.artworkURL(for: game)
try resizedData.write(to: destinationURL, options: .atomic)
imageURL = destinationURL
}
}
catch
{
errors.append(error)
}
}
else
{
imageURL = url
}
}
for error in errors
{
print(error)
}
if let imageURL = imageURL
{
DatabaseManager.shared.performBackgroundTask { (context) in
let temporaryGame = context.object(with: game.objectID) as! Game
temporaryGame.artworkURL = imageURL
context.saveWithErrorLogging()
// Local image URLs may not change despite being a different image, so manually mark record as updated.
SyncManager.shared.recordController.updateRecord(for: temporaryGame)
DispatchQueue.main.async {
self.presentedViewController?.dismiss(animated: true, completion: nil)
}
}
}
else
{
DispatchQueue.main.async {
func presentAlertController()
{
let alertController = UIAlertController(title: NSLocalizedString("Unable to Change Artwork", comment: ""), message: NSLocalizedString("The image might be corrupted or in an unsupported format.", comment: ""), preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: RSTSystemLocalizedString("OK"), style: .cancel, handler: nil))
self.present(alertController, animated: true, completion: nil)
}
if let presentedViewController = self.presentedViewController
{
presentedViewController.dismiss(animated: true) {
presentAlertController()
}
}
else
{
presentAlertController()
}
}
}
}
func share(_ game: Game)
{
let temporaryDirectory = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString)
@ -513,6 +599,54 @@ private extension GameCollectionViewController
self.present(activityViewController, animated: true, completion: nil)
}
func importSaveFile(for game: Game)
{
self._importingSaveFileGame = game
let importController = ImportController(documentTypes: [kUTTypeItem as String])
importController.delegate = self
self.present(importController, animated: true, completion: nil)
}
func importSaveFile(for game: Game, from fileURL: URL?, error: Error?)
{
// Dispatch to main queue so we can access game.gameSaveURL on its context's thread (main thread).
DispatchQueue.main.async {
do
{
if let error = error
{
throw error
}
if let fileURL = fileURL
{
try FileManager.default.copyItem(at: fileURL, to: game.gameSaveURL, shouldReplace: true)
if let gameSave = game.gameSave
{
SyncManager.shared.recordController.updateRecord(for: gameSave)
}
}
}
catch
{
let alertController = UIAlertController(title: NSLocalizedString("Failed to Import Save File", comment: ""), error: error)
if let presentedViewController = self.presentedViewController
{
presentedViewController.dismiss(animated: true) {
self.present(alertController, animated: true, completion: nil)
}
}
else
{
self.present(alertController, animated: true, completion: nil)
}
}
}
}
@objc func textFieldTextDidChange(_ textField: UITextField)
{
let text = textField.text ?? ""
@ -618,82 +752,17 @@ extension GameCollectionViewController: ImportControllerDelegate
{
func importController(_ importController: ImportController, didImportItemsAt urls: Set<URL>, errors: [Error])
{
guard let game = self._changingArtworkGame else { return }
var errors = errors
var imageURL: URL?
if let url = urls.first
if let game = self._changingArtworkGame
{
if url.isFileURL
{
do
{
let imageData = try Data(contentsOf: url)
if
let image = UIImage(data: imageData),
let resizedImage = image.resizing(toFit: CGSize(width: 300, height: 300)),
let resizedData = resizedImage.jpegData(compressionQuality: 0.85)
{
let destinationURL = DatabaseManager.artworkURL(for: game)
try resizedData.write(to: destinationURL, options: .atomic)
imageURL = destinationURL
self.changeArtwork(for: game, toImageAt: urls.first, errors: errors)
}
}
catch
else if let game = self._importingSaveFileGame
{
errors.append(error)
}
}
else
{
imageURL = url
}
self.importSaveFile(for: game, from: urls.first, error: errors.first)
}
for error in errors
{
print(error)
}
if let imageURL = imageURL
{
DatabaseManager.shared.performBackgroundTask { (context) in
let temporaryGame = context.object(with: game.objectID) as! Game
temporaryGame.artworkURL = imageURL
context.saveWithErrorLogging()
// Local image URLs may not change despite being a different image, so manually mark record as updated.
SyncManager.shared.recordController.updateRecord(for: temporaryGame)
DispatchQueue.main.async {
self.presentedViewController?.dismiss(animated: true, completion: nil)
}
}
}
else
{
func presentAlertController()
{
let alertController = UIAlertController(title: NSLocalizedString("Unable to Change Artwork", comment: ""), message: NSLocalizedString("The image might be corrupted or in an unsupported format.", comment: ""), preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: RSTSystemLocalizedString("OK"), style: .cancel, handler: nil))
self.present(alertController, animated: true, completion: nil)
}
if let presentedViewController = self.presentedViewController
{
presentedViewController.dismiss(animated: true) {
presentAlertController()
}
}
else
{
presentAlertController()
}
}
self._changingArtworkGame = nil
self._importingSaveFileGame = nil
}
func importControllerDidCancel(_ importController: ImportController)

View File

@ -76,26 +76,20 @@ class ImportController: NSObject
self.finish(with: urls, errors: [])
}
}
}
let filesAction = UIAlertAction(title: NSLocalizedString("Files", comment: ""), style: .default) { (action) in
let cancelButton = UIBarButtonItem(barButtonSystemItem: .cancel, target: self, action: #selector(ImportController.cancel))
let documentBrowserViewController = UIDocumentBrowserViewController(forOpeningFilesWithContentTypes: Array(self.documentTypes))
documentBrowserViewController.delegate = self
documentBrowserViewController.browserUserInterfaceStyle = .dark
documentBrowserViewController.allowsPickingMultipleItems = true
documentBrowserViewController.allowsDocumentCreation = false
documentBrowserViewController.additionalTrailingNavigationBarButtonItems = [cancelButton]
self.presentedViewController = documentBrowserViewController
self.presentingViewController?.present(documentBrowserViewController, animated: true, completion: nil)
self.presentDocumentBrowser()
}
alertController.addAction(filesAction)
self.presentedViewController = alertController
self.presentingViewController?.present(alertController, animated: true, completion: nil)
}
else
{
self.presentDocumentBrowser()
}
}
@objc private func cancel()
{
@ -117,6 +111,21 @@ class ImportController: NSObject
self.presentingViewController?.importController = nil
}
private func presentDocumentBrowser()
{
let cancelButton = UIBarButtonItem(barButtonSystemItem: .cancel, target: self, action: #selector(ImportController.cancel))
let documentBrowserViewController = UIDocumentBrowserViewController(forOpeningFilesWithContentTypes: Array(self.documentTypes))
documentBrowserViewController.delegate = self
documentBrowserViewController.browserUserInterfaceStyle = .dark
documentBrowserViewController.allowsPickingMultipleItems = true
documentBrowserViewController.allowsDocumentCreation = false
documentBrowserViewController.additionalTrailingNavigationBarButtonItems = [cancelButton]
self.presentedViewController = documentBrowserViewController
self.presentingViewController?.present(documentBrowserViewController, animated: true, completion: nil)
}
}
extension ImportController: UIDocumentBrowserViewControllerDelegate
@ -149,8 +158,7 @@ extension ImportController: UIDocumentBrowserViewControllerDelegate
// Use url, not intent.url, to ensure the file name matches what was in the document browser.
let temporaryURL = FileManager.default.temporaryDirectory.appendingPathComponent(url.lastPathComponent)
try FileManager.default.copyItem(at: intent.url, to: temporaryURL)
try FileManager.default.copyItem(at: intent.url, to: temporaryURL, shouldReplace: true)
coordinatedURLs.insert(temporaryURL)
}

2
External/Roxas vendored

@ -1 +1 @@
Subproject commit 91eaa0876c7c0f83c3800873c2dff7f445265d88
Subproject commit 1945d97204d113c635ec959f7777e7200d40cdee