public IList <IEvent> Decode(IEnumerable <Step> steps, IPanelMapper panelMapper) { IEnumerable <IEvent> Do() { if (panelMapper == null) { throw new RhythmCodexException("Panel mapper cannot be null"); } var stepList = steps.AsList(); foreach (var step in stepList) { var panels = step.Panels; var freeze = false; var metricOffset = (BigRational)step.MetricOffset / SsqConstants.MeasureLength; if (panels == 0x00) { freeze = true; panels = step.ExtraPanels ?? 0; } if ((panels & 0x0F) == 0x0F) { yield return(new Event { [NumericData.MetricOffset] = metricOffset, [NumericData.Player] = 0, [FlagData.Shock] = true }); panels &= 0xF0; } if ((panels & 0xF0) == 0xF0) { yield return(new Event { [NumericData.MetricOffset] = metricOffset, [NumericData.Player] = 1, [FlagData.Shock] = true }); panels &= 0x0F; } foreach (var panelNumber in _stepPanelSplitter.Split(panels)) { var mappedPanel = panelMapper.Map(panelNumber); var isMapped = mappedPanel != null; yield return(new Event { [NumericData.MetricOffset] = metricOffset, [NumericData.SourceColumn] = panelNumber, [NumericData.Column] = isMapped ? mappedPanel.Panel : (BigRational?)null, [NumericData.Player] = isMapped ? mappedPanel.Player : (BigRational?)null, [FlagData.Freeze] = freeze ? true : (bool?)null, [FlagData.Note] = freeze ? (bool?)null : true }); } } } return(Do().ToList()); }
private IEnumerable <IChart> DecodeInternal(IEnumerable <Step1Chunk> data) { var chunks = data.AsList(); if (!chunks.Any()) { throw new RhythmCodexException("No chunks to decode."); } var timings = _timingChunkDecoder.Convert(chunks[0].Data); foreach (var chunk in chunks.Skip(1)) { var timingEvents = _timingEventDecoder.Decode(timings); // Decode the raw steps. var steps = _stepChunkDecoder.Convert(chunk.Data); // Old charts store singles charts twice, as if it was a couples chart. So, check for that. int?panelCount = null; var steps1 = steps.Select(s => s.Panels & 0xF).ToArray(); var steps2 = steps.Select(s => s.Panels >> 4).ToArray(); var isSingle = steps1.SequenceEqual(steps2); int?playerCount; if (isSingle) { playerCount = 1; panelCount = 4; foreach (var step in steps) { step.Panels &= 0xF; } } else { // Bit of a hack to make solo charts work. playerCount = steps.Any(s => (s.Panels & 0xA0) != 0) ? 2 : 1; panelCount = _stepPanelSplitter.Split(steps.Aggregate(0, (i, s) => i | s.Panels)).Count() / playerCount; } // Determine what kind of chart this is based on the panels used. var mapper = _panelMapperSelector.Select(steps, new ChartInfo { PanelCount = panelCount, PlayerCount = playerCount }); // Convert the steps. var stepEvents = _stepEventDecoder.Decode(steps, mapper); var events = timingEvents.Concat(stepEvents).ToList(); var info = _chartInfoDecoder.Decode(Bitter.ToInt32(chunk.Data.AsSpan(0)), mapper.PlayerCount, mapper.PanelCount); // Output metadata. var difficulty = info.Difficulty; var type = $"{SmGameTypes.Dance}-{info.Type}"; var description = $"step1 - {events.Count(ev => ev[FlagData.Note] == true)} panels - {steps.Count(s => s.Panels != 0)} steps"; var chart = new Chart { Events = events, [StringData.Difficulty] = difficulty, [StringData.Type] = type, [StringData.Description] = description }; var firstTiming = timings.Timings.OrderBy(t => t.LinearOffset).First(); chart[NumericData.LinearOffset] = chart.GetZeroLinearReference( (BigRational)firstTiming.LinearOffset / timings.Rate, (BigRational)firstTiming.MetricOffset / SsqConstants.MeasureLength); // Have a chart. :3 yield return(chart); } }