/// <inheritdoc/> public HashResult HashDirectory(string songDirectory, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(songDirectory)) { throw new ArgumentNullException(nameof(songDirectory)); } if (cancellationToken.IsCancellationRequested) { return(HashResult.AsCanceled); } bool missingDiffs = false; string? message = null; DirectoryInfo directory = new DirectoryInfo(songDirectory); if (!directory.Exists) { return(new HashResult(null, $"Directory doesn't exist: '{songDirectory}'", new DirectoryNotFoundException($"Directory doesn't exist: '{songDirectory}'"))); } try { //FileInfo[] files = directory.GetFiles(); // Could theoretically get the wrong hash if there are multiple 'info.dat' files with different cases on linux. string?infoFileName = directory.EnumerateFiles().FirstOrDefault(f => f.Name.Equals("info.dat", StringComparison.OrdinalIgnoreCase))?.FullName; if (infoFileName == null) { return(new HashResult(null, $"Could not find 'info.dat' file in '{songDirectory}'", null)); } string infoFile = Path.Combine(songDirectory, infoFileName); if (!File.Exists(infoFile)) { return(new HashResult(null, $"Could not find 'info.dat' file in '{songDirectory}'", null)); } JObject token = JObject.Parse(File.ReadAllText(infoFile)); if (cancellationToken.IsCancellationRequested) { return(HashResult.AsCanceled); } string[] beatmapFiles = Utilities.GetDifficultyFileNames(token).ToArray(); using ConcatenatedStream streams = new ConcatenatedStream(beatmapFiles.Length + 1); streams.Append(File.OpenRead(infoFile)); for (int i = 0; i < beatmapFiles.Length; i++) { if (cancellationToken.IsCancellationRequested) { return(HashResult.AsCanceled); } string filePath = Path.Combine(songDirectory, beatmapFiles[i]); if (!File.Exists(filePath)) { if (missingDiffs == false) { message = $"Could not find difficulty file '{filePath}'"; } else { message = $"Missing multiple difficulty files in '{songDirectory}'"; } missingDiffs = true; continue; } streams.Append(File.OpenRead(filePath)); } if (cancellationToken.IsCancellationRequested) { return(HashResult.AsCanceled); } string?hash = null; if (streams.StreamCount > 0) { hash = Utilities.CreateSha1FromStream(streams); } return(new HashResult(hash, message, null)); } catch (Exception ex) { return(new HashResult(null, $"Error hashing '{songDirectory}'", ex)); } }
/// <inheritdoc/> public HashResult HashZippedBeatmap(string zipPath, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(zipPath)) { throw new ArgumentNullException(nameof(zipPath)); } if (cancellationToken.IsCancellationRequested) { return(HashResult.AsCanceled); } bool missingDiffs = false; string?message = null; if (!File.Exists(zipPath)) { return(new HashResult(null, $"File doesn't exist: '{zipPath}'", new FileNotFoundException($"File doesn't exist: '{zipPath}'"))); } ZipArchive?zip = null; try { try { zip = ZipFile.OpenRead(zipPath); } catch (Exception ex) { return(new HashResult(null, $"Unable to hash beatmap zip '{zipPath}': {ex.Message}", ex)); } ZipArchiveEntry?infoFile = zip.Entries.FirstOrDefault(e => e.FullName.Equals("info.dat", StringComparison.OrdinalIgnoreCase)); if (infoFile == null) { return(new HashResult(null, $"Could not find 'info.dat' file in '{zipPath}'", null)); } JObject?token = null; using (Stream info = infoFile.Open()) using (StreamReader reader = new StreamReader(info)) using (JsonTextReader jsonReader = new JsonTextReader(reader)) { token = JsonSerializer.Deserialize <JObject>(jsonReader); } if (token == null) { return(new HashResult(null, $"Could not read 'info.dat' file in '{zipPath}'", null)); } if (cancellationToken.IsCancellationRequested) { return(HashResult.AsCanceled); } using Stream infoStream = infoFile.Open(); string[] beatmapFiles = Utilities.GetDifficultyFileNames(token).ToArray(); using ConcatenatedStream streams = new ConcatenatedStream(beatmapFiles.Length + 1); streams.Append(infoStream); for (int i = 0; i < beatmapFiles.Length; i++) { if (cancellationToken.IsCancellationRequested) { return(HashResult.AsCanceled); } ZipArchiveEntry?diff = zip.Entries.FirstOrDefault(e => e.FullName.Equals(beatmapFiles[i], StringComparison.OrdinalIgnoreCase)); if (diff == null) { if (missingDiffs == false) { message = $"Could not find difficulty file '{beatmapFiles[i]}' in '{zipPath}'"; } else { message = $"Missing multiple difficulty files in '{zipPath}'"; } missingDiffs = true; continue; } streams.Append(diff.Open()); } if (cancellationToken.IsCancellationRequested) { return(HashResult.AsCanceled); } string?hash = null; if (streams.StreamCount > 0) { hash = Utilities.CreateSha1FromStream(streams); } return(new HashResult(hash, message, null)); } catch (Exception ex) { return(new HashResult(null, $"Error hashing '{zipPath}'", ex)); } finally { zip?.Dispose(); } }