1
0
mirror of https://github.com/ppy/osu.git synced 2024-11-14 10:57:30 +08:00

Add encoding and import support

This commit is contained in:
Dean Herbert 2020-03-24 14:13:46 +09:00
parent 68ebe98fde
commit 022465f546
9 changed files with 220 additions and 18 deletions

View File

@ -56,5 +56,14 @@ namespace osu.Game.Rulesets.Catch.Replays
Actions.Add(CatchAction.MoveLeft);
}
}
public LegacyReplayFrame ConvertTo(IBeatmap beatmap)
{
ReplayButtonState state = ReplayButtonState.None;
if (Actions.Contains(CatchAction.Dash)) state |= ReplayButtonState.Left1;
return new LegacyReplayFrame(Time, Position * CatchPlayfield.BASE_WIDTH, null, state);
}
}
}

View File

@ -56,5 +56,42 @@ namespace osu.Game.Rulesets.Mania.Replays
activeColumns >>= 1;
}
}
public LegacyReplayFrame ConvertTo(IBeatmap beatmap)
{
int keys = 0;
var converter = new ManiaBeatmapConverter(beatmap, new ManiaRuleset());
var stage = new StageDefinition { Columns = converter.TargetColumns };
var specialColumns = new List<int>();
for (int i = 0; i < converter.TargetColumns; i++)
{
if (stage.IsSpecialColumn(i))
specialColumns.Add(i);
}
foreach (var action in Actions)
{
switch (action)
{
case ManiaAction.Special1:
keys |= 1 << specialColumns[0];
break;
case ManiaAction.Special2:
keys |= 1 << specialColumns[1];
break;
default:
keys |= 1 << (action - ManiaAction.Key1);
break;
}
}
return new LegacyReplayFrame(Time, keys, null, ReplayButtonState.None);
}
}
}

View File

@ -32,5 +32,17 @@ namespace osu.Game.Rulesets.Osu.Replays
if (currentFrame.MouseLeft) Actions.Add(OsuAction.LeftButton);
if (currentFrame.MouseRight) Actions.Add(OsuAction.RightButton);
}
public LegacyReplayFrame ConvertTo(IBeatmap beatmap)
{
ReplayButtonState state = ReplayButtonState.None;
if (Actions.Contains(OsuAction.LeftButton))
state |= ReplayButtonState.Left1;
if (Actions.Contains(OsuAction.RightButton))
state |= ReplayButtonState.Right1;
return new LegacyReplayFrame(Time, Position.X, Position.Y, state);
}
}
}

View File

@ -30,5 +30,17 @@ namespace osu.Game.Rulesets.Taiko.Replays
if (currentFrame.MouseLeft1) Actions.Add(TaikoAction.LeftCentre);
if (currentFrame.MouseLeft2) Actions.Add(TaikoAction.RightCentre);
}
public LegacyReplayFrame ConvertTo(IBeatmap beatmap)
{
ReplayButtonState state = ReplayButtonState.None;
if (Actions.Contains(TaikoAction.LeftRim)) state |= ReplayButtonState.Right1;
if (Actions.Contains(TaikoAction.RightRim)) state |= ReplayButtonState.Right2;
if (Actions.Contains(TaikoAction.LeftCentre)) state |= ReplayButtonState.Left1;
if (Actions.Contains(TaikoAction.RightCentre)) state |= ReplayButtonState.Left2;
return new LegacyReplayFrame(Time, null, null, state);
}
}
}

View File

@ -0,0 +1,30 @@
// 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.Collections.Generic;
using System.IO;
namespace osu.Game.IO.Archives
{
/// <summary>
/// Allows reading a single file from the provided stream.
/// </summary>
public class LegacyByteArrayReader : ArchiveReader
{
private readonly byte[] content;
public LegacyByteArrayReader(byte[] content, string filename)
: base(filename)
{
this.content = content;
}
public override Stream GetStream(string name) => new MemoryStream(content);
public override void Dispose()
{
}
public override IEnumerable<string> Filenames => new[] { Name };
}
}

View File

@ -18,5 +18,11 @@ namespace osu.Game.Rulesets.Replays.Types
/// <param name="beatmap">The beatmap.</param>
/// <param name="lastFrame">The last post-conversion <see cref="ReplayFrame"/>, used to fill in missing delta information. May be null.</param>
void ConvertFrom(LegacyReplayFrame currentFrame, IBeatmap beatmap, ReplayFrame lastFrame = null);
/// <summary>
/// Populates this <see cref="ReplayFrame"/> using values from a <see cref="LegacyReplayFrame"/>.
/// </summary>
/// <param name="beatmap">The beatmap.</param>
LegacyReplayFrame ConvertTo(IBeatmap beatmap);
}
}

View File

@ -268,6 +268,10 @@ namespace osu.Game.Rulesets.UI
throw new InvalidOperationException($"A {nameof(KeyBindingInputManager)} which supports recording is not available");
var recorder = CreateReplayRecorder(recordingReplay);
if (recorder == null)
return;
recorder.ScreenSpaceToGamefield = Playfield.ScreenSpaceToGamefield;
recordingInputHandler.Recorder = recorder;

View File

@ -3,6 +3,14 @@
using System;
using System.IO;
using System.Linq;
using System.Text;
using osu.Framework.Extensions;
using osu.Game.Beatmaps;
using osu.Game.IO.Legacy;
using osu.Game.Replays.Legacy;
using osu.Game.Rulesets.Replays.Types;
using SharpCompress.Compressors.LZMA;
namespace osu.Game.Scoring.Legacy
{
@ -11,25 +19,96 @@ namespace osu.Game.Scoring.Legacy
public const int LATEST_VERSION = 128;
private readonly Score score;
private readonly IBeatmap beatmap;
public LegacyScoreEncoder(Score score)
public LegacyScoreEncoder(Score score, IBeatmap beatmap)
{
this.score = score;
this.beatmap = beatmap;
if (score.ScoreInfo.Beatmap.RulesetID < 0 || score.ScoreInfo.Beatmap.RulesetID > 3)
throw new ArgumentException("Only scores in the osu, taiko, catch, or mania rulesets can be encoded to the legacy score format.", nameof(score));
}
public void Encode(TextWriter writer)
public void Encode(Stream stream)
{
writer.WriteLine($"osu file format v{LATEST_VERSION}");
using (SerializationWriter sw = new SerializationWriter(stream))
{
sw.Write((byte)score.ScoreInfo.RulesetID);
sw.Write(LATEST_VERSION);
sw.Write(score.ScoreInfo.Beatmap.MD5Hash);
sw.Write(score.ScoreInfo.UserString);
sw.Write($"lazer-{score.ScoreInfo.UserString}-{score.ScoreInfo.Date}".ComputeMD5Hash());
sw.Write((ushort)(score.ScoreInfo.GetCount300() ?? 0));
sw.Write((ushort)(score.ScoreInfo.GetCount100() ?? 0));
sw.Write((ushort)(score.ScoreInfo.GetCount50() ?? 0));
sw.Write((ushort)(score.ScoreInfo.GetCountGeki() ?? 0));
sw.Write((ushort)(score.ScoreInfo.GetCountKatu() ?? 0));
sw.Write((ushort)(score.ScoreInfo.GetCountMiss() ?? 0));
sw.Write((int)(score.ScoreInfo.TotalScore));
sw.Write((ushort)score.ScoreInfo.MaxCombo);
sw.Write(score.ScoreInfo.Combo == score.ScoreInfo.MaxCombo);
sw.Write((int)score.ScoreInfo.Ruleset.CreateInstance().ConvertToLegacyMods(score.ScoreInfo.Mods));
writer.WriteLine();
handleGeneral(writer);
sw.Write(getHpGraphFormatted());
sw.Write(score.ScoreInfo.Date.DateTime);
sw.WriteByteArray(createReplayData());
sw.Write((long)0);
writeModSpecificData(score.ScoreInfo, sw);
}
}
private void handleGeneral(TextWriter writer)
private void writeModSpecificData(ScoreInfo score, SerializationWriter sw)
{
}
private byte[] createReplayData()
{
var content = new ASCIIEncoding().GetBytes(replayStringContent);
using (var outStream = new MemoryStream())
{
using (var lzma = new LzmaStream(new LzmaEncoderProperties(false, 1 << 21, 255), false, outStream))
{
outStream.Write(lzma.Properties);
long fileSize = content.Length;
for (int i = 0; i < 8; i++)
outStream.WriteByte((byte)(fileSize >> (8 * i)));
lzma.Write(content);
}
return outStream.ToArray();
}
}
private string replayStringContent
{
get
{
StringBuilder replayData = new StringBuilder();
if (score.Replay != null)
{
LegacyReplayFrame lastF = new LegacyReplayFrame(0, 0, 0, ReplayButtonState.None);
foreach (var f in score.Replay.Frames.OfType<IConvertibleReplayFrame>().Select(f => f.ConvertTo(beatmap)))
{
replayData.Append(FormattableString.Invariant($"{f.Time - lastF.Time}|{f.MouseX}|{f.MouseY}|{(int)f.ButtonState},"));
lastF = f;
}
}
replayData.AppendFormat(@"{0}|{1}|{2}|{3},", -12345, 0, 0, 0);
return replayData.ToString();
}
}
private string getHpGraphFormatted()
{
// todo: implement, maybe?
return string.Empty;
}
}
}

View File

@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Audio;
@ -17,6 +18,7 @@ using osu.Framework.Threading;
using osu.Game.Beatmaps;
using osu.Game.Configuration;
using osu.Game.Graphics.Containers;
using osu.Game.IO.Archives;
using osu.Game.Online.API;
using osu.Game.Overlays;
using osu.Game.Replays;
@ -25,6 +27,7 @@ using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI;
using osu.Game.Scoring;
using osu.Game.Scoring.Legacy;
using osu.Game.Screens.Ranking;
using osu.Game.Skinning;
using osu.Game.Users;
@ -643,19 +646,29 @@ namespace osu.Game.Screens.Play
completionProgressDelegate?.Cancel();
completionProgressDelegate = Schedule(delegate
{
var score = CreateScore();
if (DrawableRuleset.ReplayScore == null)
{
scoreManager.Import(score).ContinueWith(_ => Schedule(() =>
{
// screen may be in the exiting transition phase.
if (this.IsCurrentScreen())
this.Push(CreateResults(score));
}));
}
if (DrawableRuleset.ReplayScore != null)
this.Push(CreateResults(DrawableRuleset.ReplayScore.ScoreInfo));
else
this.Push(CreateResults(score));
{
var score = new Score
{
ScoreInfo = CreateScore(),
Replay = recordingReplay
};
using (var stream = new MemoryStream())
{
new LegacyScoreEncoder(score, gameplayBeatmap).Encode(stream);
scoreManager.Import(score.ScoreInfo, new LegacyByteArrayReader(stream.ToArray(), "replay.osr"))
.ContinueWith(imported => Schedule(() =>
{
// screen may be in the exiting transition phase.
if (this.IsCurrentScreen())
this.Push(CreateResults(imported.Result));
}));
}
}
});
}