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:
parent
68ebe98fde
commit
022465f546
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
30
osu.Game/IO/Archives/LegacyByteArrayReader.cs
Normal file
30
osu.Game/IO/Archives/LegacyByteArrayReader.cs
Normal 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 };
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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));
|
||||
}));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user