mirror of
https://github.com/ppy/osu
synced 2024-12-14 10:57:41 +00:00
Merge pull request #21553 from smoogipoo/osr-scoreinfo-data
Retrofit lazer scoring data onto `.osr` files
This commit is contained in:
commit
b7d2e0ae8e
@ -16,7 +16,9 @@ using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Catch;
|
||||
using osu.Game.Rulesets.Mania;
|
||||
using osu.Game.Rulesets.Mania.Mods;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Rulesets.Osu.Mods;
|
||||
using osu.Game.Rulesets.Osu.Replays;
|
||||
using osu.Game.Rulesets.Osu.UI;
|
||||
using osu.Game.Rulesets.Replays;
|
||||
@ -179,6 +181,40 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestSoloScoreData()
|
||||
{
|
||||
var ruleset = new OsuRuleset().RulesetInfo;
|
||||
|
||||
var scoreInfo = TestResources.CreateTestScoreInfo(ruleset);
|
||||
scoreInfo.Mods = new Mod[]
|
||||
{
|
||||
new OsuModDoubleTime { SpeedChange = { Value = 1.1 } }
|
||||
};
|
||||
|
||||
var beatmap = new TestBeatmap(ruleset);
|
||||
var score = new Score
|
||||
{
|
||||
ScoreInfo = scoreInfo,
|
||||
Replay = new Replay
|
||||
{
|
||||
Frames = new List<ReplayFrame>
|
||||
{
|
||||
new OsuReplayFrame(2000, OsuPlayfield.BASE_SIZE / 2, OsuAction.LeftButton)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var decodedAfterEncode = encodeThenDecode(LegacyBeatmapDecoder.LATEST_VERSION, score, beatmap);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(decodedAfterEncode.ScoreInfo.Statistics, Is.EqualTo(scoreInfo.Statistics));
|
||||
Assert.That(decodedAfterEncode.ScoreInfo.MaximumStatistics, Is.EqualTo(scoreInfo.MaximumStatistics));
|
||||
Assert.That(decodedAfterEncode.ScoreInfo.Mods, Is.EqualTo(scoreInfo.Mods));
|
||||
});
|
||||
}
|
||||
|
||||
private static Score encodeThenDecode(int beatmapVersion, Score score, TestBeatmap beatmap)
|
||||
{
|
||||
var encodeStream = new MemoryStream();
|
||||
|
38
osu.Game/Scoring/Legacy/LegacyReplaySoloScoreInfo.cs
Normal file
38
osu.Game/Scoring/Legacy/LegacyReplaySoloScoreInfo.cs
Normal file
@ -0,0 +1,38 @@
|
||||
// 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.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Newtonsoft.Json;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
|
||||
namespace osu.Game.Scoring.Legacy
|
||||
{
|
||||
/// <summary>
|
||||
/// A minified version of <see cref="SoloScoreInfo"/> retrofit onto the end of legacy replay files (.osr),
|
||||
/// containing the minimum data required to support storage of non-legacy replays.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[JsonObject(MemberSerialization.OptIn)]
|
||||
public class LegacyReplaySoloScoreInfo
|
||||
{
|
||||
[JsonProperty("mods")]
|
||||
public APIMod[] Mods { get; set; } = Array.Empty<APIMod>();
|
||||
|
||||
[JsonProperty("statistics")]
|
||||
public Dictionary<HitResult, int> Statistics { get; set; } = new Dictionary<HitResult, int>();
|
||||
|
||||
[JsonProperty("maximum_statistics")]
|
||||
public Dictionary<HitResult, int> MaximumStatistics { get; set; } = new Dictionary<HitResult, int>();
|
||||
|
||||
public static LegacyReplaySoloScoreInfo FromScore(ScoreInfo score) => new LegacyReplaySoloScoreInfo
|
||||
{
|
||||
Mods = score.APIMods,
|
||||
Statistics = score.Statistics.Where(kvp => kvp.Value != 0).ToDictionary(kvp => kvp.Key, kvp => kvp.Value),
|
||||
MaximumStatistics = score.MaximumStatistics.Where(kvp => kvp.Value != 0).ToDictionary(kvp => kvp.Key, kvp => kvp.Value),
|
||||
};
|
||||
}
|
||||
}
|
@ -4,8 +4,10 @@
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Newtonsoft.Json;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.Formats;
|
||||
using osu.Game.Beatmaps.Legacy;
|
||||
@ -91,31 +93,26 @@ namespace osu.Game.Scoring.Legacy
|
||||
else if (version >= 20121008)
|
||||
scoreInfo.OnlineID = sr.ReadInt32();
|
||||
|
||||
byte[] compressedScoreInfo = null;
|
||||
|
||||
if (version >= 30000001)
|
||||
compressedScoreInfo = sr.ReadByteArray();
|
||||
|
||||
if (compressedReplay?.Length > 0)
|
||||
readCompressedData(compressedReplay, reader => readLegacyReplay(score.Replay, reader));
|
||||
|
||||
if (compressedScoreInfo?.Length > 0)
|
||||
{
|
||||
using (var replayInStream = new MemoryStream(compressedReplay))
|
||||
readCompressedData(compressedScoreInfo, reader =>
|
||||
{
|
||||
byte[] properties = new byte[5];
|
||||
if (replayInStream.Read(properties, 0, 5) != 5)
|
||||
throw new IOException("input .lzma is too short");
|
||||
LegacyReplaySoloScoreInfo readScore = JsonConvert.DeserializeObject<LegacyReplaySoloScoreInfo>(reader.ReadToEnd());
|
||||
|
||||
long outSize = 0;
|
||||
Debug.Assert(readScore != null);
|
||||
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
int v = replayInStream.ReadByte();
|
||||
if (v < 0)
|
||||
throw new IOException("Can't Read 1");
|
||||
|
||||
outSize |= (long)(byte)v << (8 * i);
|
||||
}
|
||||
|
||||
long compressedSize = replayInStream.Length - replayInStream.Position;
|
||||
|
||||
using (var lzma = new LzmaStream(properties, replayInStream, compressedSize, outSize))
|
||||
using (var reader = new StreamReader(lzma))
|
||||
readLegacyReplay(score.Replay, reader);
|
||||
}
|
||||
score.ScoreInfo.Statistics = readScore.Statistics;
|
||||
score.ScoreInfo.MaximumStatistics = readScore.MaximumStatistics;
|
||||
score.ScoreInfo.Mods = readScore.Mods.Select(m => m.ToMod(currentRuleset)).ToArray();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -128,6 +125,33 @@ namespace osu.Game.Scoring.Legacy
|
||||
return score;
|
||||
}
|
||||
|
||||
private void readCompressedData(byte[] data, Action<StreamReader> readFunc)
|
||||
{
|
||||
using (var replayInStream = new MemoryStream(data))
|
||||
{
|
||||
byte[] properties = new byte[5];
|
||||
if (replayInStream.Read(properties, 0, 5) != 5)
|
||||
throw new IOException("input .lzma is too short");
|
||||
|
||||
long outSize = 0;
|
||||
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
int v = replayInStream.ReadByte();
|
||||
if (v < 0)
|
||||
throw new IOException("Can't Read 1");
|
||||
|
||||
outSize |= (long)(byte)v << (8 * i);
|
||||
}
|
||||
|
||||
long compressedSize = replayInStream.Length - replayInStream.Position;
|
||||
|
||||
using (var lzma = new LzmaStream(properties, replayInStream, compressedSize, outSize))
|
||||
using (var reader = new StreamReader(lzma))
|
||||
readFunc(reader);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Populates the accuracy of a given <see cref="ScoreInfo"/> from its contained statistics.
|
||||
/// </summary>
|
||||
|
@ -11,6 +11,7 @@ using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.Formats;
|
||||
using osu.Game.Extensions;
|
||||
using osu.Game.IO.Legacy;
|
||||
using osu.Game.IO.Serialization;
|
||||
using osu.Game.Replays.Legacy;
|
||||
using osu.Game.Rulesets.Replays;
|
||||
using osu.Game.Rulesets.Replays.Types;
|
||||
@ -24,7 +25,7 @@ namespace osu.Game.Scoring.Legacy
|
||||
/// Database version in stable-compatible YYYYMMDD format.
|
||||
/// Should be incremented if any changes are made to the format/usage.
|
||||
/// </summary>
|
||||
public const int LATEST_VERSION = FIRST_LAZER_VERSION;
|
||||
public const int LATEST_VERSION = 30000001;
|
||||
|
||||
/// <summary>
|
||||
/// The first stable-compatible YYYYMMDD format version given to lazer usage of replays.
|
||||
@ -77,6 +78,7 @@ namespace osu.Game.Scoring.Legacy
|
||||
sw.WriteByteArray(createReplayData());
|
||||
sw.Write((long)0);
|
||||
writeModSpecificData(score.ScoreInfo, sw);
|
||||
sw.WriteByteArray(createScoreInfoData());
|
||||
}
|
||||
}
|
||||
|
||||
@ -84,9 +86,13 @@ namespace osu.Game.Scoring.Legacy
|
||||
{
|
||||
}
|
||||
|
||||
private byte[] createReplayData()
|
||||
private byte[] createReplayData() => compress(replayStringContent);
|
||||
|
||||
private byte[] createScoreInfoData() => compress(LegacyReplaySoloScoreInfo.FromScore(score.ScoreInfo).Serialize());
|
||||
|
||||
private byte[] compress(string data)
|
||||
{
|
||||
byte[] content = new ASCIIEncoding().GetBytes(replayStringContent);
|
||||
byte[] content = new ASCIIEncoding().GetBytes(data);
|
||||
|
||||
using (var outStream = new MemoryStream())
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user