// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. #nullable disable using System; using System.Collections.Generic; using System.IO; using System.Linq; using osu.Framework.Platform; using osu.Game.Extensions; using osu.Game.Utils; using SharpCompress.Archives.Zip; namespace osu.Game.Database { /// /// A class which handles exporting legacy user data of a single type from osu-stable. /// public abstract class LegacyExporter where TModel : class, IHasNamedFiles { /// /// Max length of filename (including extension) /// /// /// This constant is smaller 256 because adds additional "_" to the end of the path /// private const int max_path = 255 - (32 + 4 + 2); //max path - (Guid + Guid "D" format chars + Storage.CreateFileSafely chars) /// /// The file extension for exports (including the leading '.'). /// protected abstract string FileExtension { get; } protected readonly Storage UserFileStorage; private readonly Storage exportStorage; protected LegacyExporter(Storage storage) { exportStorage = storage.GetStorageForDirectory(@"exports"); UserFileStorage = storage.GetStorageForDirectory(@"files"); } protected virtual string GetFilename(TModel item) { string fileName = item.GetDisplayString(); int fileNameLength = fileName.Length - FileExtension.Length; if (fileNameLength > max_path) fileName = fileName.Remove(max_path - FileExtension.Length); //Truncating the name to fit the path limit return fileName; } /// /// Exports an item to a legacy (.zip based) package. /// /// The item to export. public void Export(TModel item) { string itemFilename = GetFilename(item).GetValidFilename(); IEnumerable existingExports = exportStorage .GetFiles(string.Empty, $"{itemFilename}*{FileExtension}") .Concat(exportStorage.GetDirectories(string.Empty)); string filename = NamingUtils.GetNextBestFilename(existingExports, $"{itemFilename}{FileExtension}"); using (var stream = exportStorage.CreateFileSafely(filename)) ExportModelTo(item, stream); exportStorage.PresentFileExternally(filename); } /// /// Exports an item to the given output stream. /// /// The item to export. /// The output stream to export to. public virtual void ExportModelTo(TModel model, Stream outputStream) { using (var archive = ZipArchive.Create()) { foreach (var file in model.Files) archive.AddEntry(file.Filename, UserFileStorage.GetStream(file.File.GetStoragePath())); archive.SaveTo(outputStream); } } } }