2020-05-06 09:27:10 +00:00
|
|
|
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
|
|
|
|
// See the LICENCE file in the repository root for full licence text.
|
|
|
|
|
|
2020-07-06 13:03:09 +00:00
|
|
|
|
using System.Diagnostics;
|
2020-05-07 10:00:59 +00:00
|
|
|
|
using System.Linq;
|
2020-05-06 09:27:10 +00:00
|
|
|
|
using osu.Framework.Logging;
|
|
|
|
|
using osu.Framework.Platform;
|
|
|
|
|
using osu.Game.Configuration;
|
|
|
|
|
|
|
|
|
|
namespace osu.Game.IO
|
|
|
|
|
{
|
2020-06-22 09:38:50 +00:00
|
|
|
|
public class OsuStorage : MigratableStorage
|
2020-05-06 09:27:10 +00:00
|
|
|
|
{
|
2020-07-06 13:03:09 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Indicates the error (if any) that occurred when initialising the custom storage during initial startup.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public readonly OsuStorageError Error;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The custom storage path as selected by the user.
|
|
|
|
|
/// </summary>
|
2023-06-23 15:59:36 +00:00
|
|
|
|
public string? CustomStoragePath => storageConfig.Get<string>(StorageConfig.FullPath);
|
2020-07-06 13:03:09 +00:00
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The default storage path to be used if a custom storage path hasn't been selected or is not accessible.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public string DefaultStoragePath => defaultStorage.GetFullPath(".");
|
|
|
|
|
|
2020-05-06 09:31:36 +00:00
|
|
|
|
private readonly GameHost host;
|
|
|
|
|
private readonly StorageConfigManager storageConfig;
|
2020-07-06 13:03:09 +00:00
|
|
|
|
private readonly Storage defaultStorage;
|
2020-05-06 09:31:36 +00:00
|
|
|
|
|
2021-01-21 10:01:58 +00:00
|
|
|
|
public override string[] IgnoreDirectories => new[]
|
|
|
|
|
{
|
|
|
|
|
"cache",
|
|
|
|
|
};
|
2020-06-23 21:34:26 +00:00
|
|
|
|
|
2020-06-24 21:01:56 +00:00
|
|
|
|
public override string[] IgnoreFiles => new[]
|
2020-06-23 21:34:26 +00:00
|
|
|
|
{
|
2020-06-23 22:37:29 +00:00
|
|
|
|
"framework.ini",
|
2021-01-21 10:01:58 +00:00
|
|
|
|
"storage.ini",
|
2022-08-03 08:37:30 +00:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
public override string[] IgnoreSuffixes => new[]
|
|
|
|
|
{
|
|
|
|
|
// Realm pipe files don't play well with copy operations
|
|
|
|
|
".note",
|
|
|
|
|
".lock",
|
|
|
|
|
".management",
|
2020-06-23 22:37:29 +00:00
|
|
|
|
};
|
2020-06-23 21:34:26 +00:00
|
|
|
|
|
2020-07-01 08:12:07 +00:00
|
|
|
|
public OsuStorage(GameHost host, Storage defaultStorage)
|
|
|
|
|
: base(defaultStorage, string.Empty)
|
2020-05-06 09:27:10 +00:00
|
|
|
|
{
|
2020-05-06 09:31:36 +00:00
|
|
|
|
this.host = host;
|
2020-07-06 13:03:09 +00:00
|
|
|
|
this.defaultStorage = defaultStorage;
|
2020-05-06 09:27:10 +00:00
|
|
|
|
|
2020-07-01 08:12:07 +00:00
|
|
|
|
storageConfig = new StorageConfigManager(defaultStorage);
|
2020-05-06 09:27:10 +00:00
|
|
|
|
|
2020-07-06 13:03:09 +00:00
|
|
|
|
if (!string.IsNullOrEmpty(CustomStoragePath))
|
|
|
|
|
TryChangeToCustomStorage(out Error);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Resets the custom storage path, changing the target storage to the default location.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public void ResetCustomStoragePath()
|
|
|
|
|
{
|
2022-03-29 09:00:56 +00:00
|
|
|
|
ChangeDataPath(string.Empty);
|
2020-07-06 13:03:09 +00:00
|
|
|
|
|
|
|
|
|
ChangeTargetStorage(defaultStorage);
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-29 09:00:56 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Updates the target data path without immediately switching.
|
|
|
|
|
/// This does NOT migrate any data.
|
|
|
|
|
/// The game should immediately be restarted after calling this.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public void ChangeDataPath(string newPath)
|
|
|
|
|
{
|
|
|
|
|
storageConfig.SetValue(StorageConfig.FullPath, newPath);
|
|
|
|
|
storageConfig.Save();
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-06 13:03:09 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Attempts to change to the user's custom storage path.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="error">The error that occurred.</param>
|
|
|
|
|
/// <returns>Whether the custom storage path was used successfully. If not, <paramref name="error"/> will be populated with the reason.</returns>
|
|
|
|
|
public bool TryChangeToCustomStorage(out OsuStorageError error)
|
|
|
|
|
{
|
|
|
|
|
Debug.Assert(!string.IsNullOrEmpty(CustomStoragePath));
|
|
|
|
|
|
|
|
|
|
error = OsuStorageError.None;
|
|
|
|
|
Storage lastStorage = UnderlyingStorage;
|
2020-05-06 09:27:10 +00:00
|
|
|
|
|
2022-07-13 07:22:50 +00:00
|
|
|
|
Logger.Log($"Attempting to use custom storage location {CustomStoragePath}");
|
|
|
|
|
|
2020-07-06 13:03:09 +00:00
|
|
|
|
try
|
2020-07-01 08:47:29 +00:00
|
|
|
|
{
|
2020-07-06 13:03:09 +00:00
|
|
|
|
Storage userStorage = host.GetStorage(CustomStoragePath);
|
|
|
|
|
|
2020-07-06 13:41:58 +00:00
|
|
|
|
if (!userStorage.ExistsDirectory(".") || !userStorage.GetFiles(".").Any())
|
2020-07-06 13:03:09 +00:00
|
|
|
|
error = OsuStorageError.AccessibleButEmpty;
|
|
|
|
|
|
|
|
|
|
ChangeTargetStorage(userStorage);
|
2022-07-13 07:22:50 +00:00
|
|
|
|
Logger.Log($"Storage successfully changed to {CustomStoragePath}.");
|
2020-07-06 13:03:09 +00:00
|
|
|
|
}
|
|
|
|
|
catch
|
|
|
|
|
{
|
|
|
|
|
error = OsuStorageError.NotAccessible;
|
|
|
|
|
ChangeTargetStorage(lastStorage);
|
2020-07-01 08:47:29 +00:00
|
|
|
|
}
|
2020-07-06 13:03:09 +00:00
|
|
|
|
|
2022-07-13 07:22:50 +00:00
|
|
|
|
if (error != OsuStorageError.None)
|
|
|
|
|
Logger.Log($"Custom storage location could not be used ({error}).");
|
|
|
|
|
|
2020-07-06 13:03:09 +00:00
|
|
|
|
return error == OsuStorageError.None;
|
2020-05-11 12:38:27 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected override void ChangeTargetStorage(Storage newStorage)
|
|
|
|
|
{
|
2021-07-09 04:17:25 +00:00
|
|
|
|
var lastStorage = UnderlyingStorage;
|
2020-05-11 12:38:27 +00:00
|
|
|
|
base.ChangeTargetStorage(newStorage);
|
2021-07-09 04:17:25 +00:00
|
|
|
|
|
|
|
|
|
if (lastStorage != null)
|
|
|
|
|
{
|
|
|
|
|
// for now we assume that if there was a previous storage, this is a migration operation.
|
|
|
|
|
// the logger shouldn't be set during initialisation as it can cause cross-talk in tests (due to being static).
|
|
|
|
|
Logger.Storage = UnderlyingStorage.GetStorageForDirectory("logs");
|
|
|
|
|
}
|
2020-05-06 09:27:10 +00:00
|
|
|
|
}
|
2020-05-06 09:31:36 +00:00
|
|
|
|
|
2022-02-10 09:48:37 +00:00
|
|
|
|
public override bool Migrate(Storage newStorage)
|
2020-05-06 09:31:36 +00:00
|
|
|
|
{
|
2022-02-10 09:48:37 +00:00
|
|
|
|
bool cleanupSucceeded = base.Migrate(newStorage);
|
|
|
|
|
|
2022-03-29 09:00:56 +00:00
|
|
|
|
ChangeDataPath(newStorage.GetFullPath("."));
|
2022-02-10 09:48:37 +00:00
|
|
|
|
|
|
|
|
|
return cleanupSucceeded;
|
2020-05-07 10:00:59 +00:00
|
|
|
|
}
|
2020-05-06 09:27:10 +00:00
|
|
|
|
}
|
2020-07-06 13:03:09 +00:00
|
|
|
|
|
|
|
|
|
public enum OsuStorageError
|
|
|
|
|
{
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// No error.
|
|
|
|
|
/// </summary>
|
|
|
|
|
None,
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Occurs when the target storage directory is accessible but does not already contain game files.
|
|
|
|
|
/// Only happens when the user changes the storage directory and then moves the files manually or mounts a different device to the same path.
|
|
|
|
|
/// </summary>
|
|
|
|
|
AccessibleButEmpty,
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Occurs when the target storage directory cannot be accessed at all.
|
|
|
|
|
/// </summary>
|
|
|
|
|
NotAccessible,
|
|
|
|
|
}
|
2020-05-06 09:27:10 +00:00
|
|
|
|
}
|