public ITask CreateDecodeSsq() { return(Build("Decode SSQ", task => { var files = GetInputFiles(task); if (!files.Any()) { task.Message = "No input files."; return false; } ParallelProgress(task, files, file => { using (var inFile = OpenRead(task, file)) { var chunks = _ssqStreamReader.Read(inFile); var charts = _ssqDecoder.Decode(chunks); var aggregatedInfo = _metadataAggregator.Aggregate(charts); var title = Path.GetFileNameWithoutExtension(file.Name); var globalOffset = Args.Options.ContainsKey("-offset") ? BigRationalParser.ParseString(Args.Options["-offset"].FirstOrDefault() ?? "0") : BigRational.Zero; // This is a temporary hack to make building sets easier for right now // TODO: make this optional via command line switch if (title.EndsWith("_all", StringComparison.InvariantCultureIgnoreCase)) { title = title.Substring(0, title.Length - 4); } var encoded = _smEncoder.Encode(new ChartSet { Metadata = new Metadata { [StringData.Title] = aggregatedInfo[StringData.Title] ?? title, [StringData.Subtitle] = aggregatedInfo[StringData.Subtitle], [StringData.Artist] = aggregatedInfo[StringData.Artist], [ChartTag.MusicTag] = aggregatedInfo[StringData.Music] ?? $"{title}.ogg", [ChartTag.PreviewTag] = aggregatedInfo[StringData.Music] ?? $"{title}-preview.ogg", [ChartTag.OffsetTag] = $"{(decimal) (-aggregatedInfo[NumericData.LinearOffset] + globalOffset)}" }, Charts = charts }); using (var outFile = OpenWriteSingle(task, file, i => $"{i}.sm")) { _smStreamWriter.Write(outFile, encoded); outFile.Flush(); } } }); return true; })); }
private void AddSingularMetadata(IMetadata chart, IList <BmsCommand> commandList, IDictionary <string, NumericData> metadataMap) { foreach (var kv in metadataMap) { var command = commandList .LastOrDefault(c => kv.Key.Equals(c.Name, StringComparison.InvariantCultureIgnoreCase)); if (command != null) { chart[kv.Value] = BigRationalParser.ParseString(command.Value); } } }
public ITask CreateDecodeStep1() { return(Build("Decode STEP", task => { var inputFiles = GetInputFiles(task); if (!inputFiles.Any()) { task.Message = "No input files."; return false; } foreach (var inputFile in inputFiles) { using (var inFile = OpenRead(task, inputFile)) { var chunks = _step1StreamReader.Read(inFile); var charts = _step1Decoder.Decode(chunks); var aggregatedInfo = _metadataAggregator.Aggregate(charts); var title = aggregatedInfo[StringData.Title] ?? Path.GetFileNameWithoutExtension(inputFile.Name); var globalOffset = Args.Options.ContainsKey("-offset") ? BigRationalParser.ParseString(Args.Options["-offset"].FirstOrDefault() ?? "0") : BigRational.Zero; var encoded = _smEncoder.Encode(new ChartSet { Metadata = new Metadata { [StringData.Title] = title, [StringData.Subtitle] = aggregatedInfo[StringData.Subtitle], [StringData.Artist] = aggregatedInfo[StringData.Artist], [ChartTag.MusicTag] = aggregatedInfo[StringData.Music] ?? $"{title}.ogg", [ChartTag.PreviewTag] = aggregatedInfo[StringData.Music] ?? $"{title}-preview.ogg", [ChartTag.OffsetTag] = $"{(decimal) (-aggregatedInfo[NumericData.LinearOffset] + globalOffset)}" }, Charts = charts }); using (var outFile = OpenWriteSingle(task, inputFile, i => $"{i}.sm")) { _smStreamWriter.Write(outFile, encoded); outFile.Flush(); } } } return true; })); }
public void AdjustGapForFolder(string path, double amount) { var smReader = Resolve <ISmStreamReader>(); var smWriter = Resolve <ISmStreamWriter>(); var files = Directory.GetFiles(path, "*.sm", SearchOption.AllDirectories); foreach (var file in files) { IList <Command> commands; using (var stream = File.OpenRead(file)) commands = smReader.Read(stream).ToList(); foreach (var offsetCommand in commands.Where(c => c.Name.Equals("offset", StringComparison.OrdinalIgnoreCase))) { var value = offsetCommand.Values.SingleOrDefault(); if (value == null) { offsetCommand.Values.Add($"{amount}"); } else if (value == string.Empty) { offsetCommand.Values[0] = $"{amount}"; } else { var numValue = BigRationalParser.ParseString(offsetCommand.Values.Single()); numValue += amount; offsetCommand.Values[0] = $"{(double) numValue}"; } } using (var stream = File.Open(file, FileMode.Create, FileAccess.Write)) { smWriter.Write(stream, commands); stream.Flush(); } } }
private (Dictionary <int, BigRational> Map, IList <BmsCommand> Commands) GetNumericMap( IEnumerable <BmsCommand> commands, string mapName) { var map = new Dictionary <int, BigRational>(); var processedCommands = new List <BmsCommand>(); foreach (var command in commands .Where(c => c.Value != null && !c.Name.Equals(mapName, StringComparison.InvariantCultureIgnoreCase) && c.Name.StartsWith(mapName, StringComparison.InvariantCultureIgnoreCase))) { var index = Alphabet.DecodeAlphanumeric(command.Name.AsSpan(mapName.Length)); var bpm = BigRationalParser.ParseString(command.Value); if (bpm != null) { map[index] = bpm.Value; } processedCommands.Add(command); } return(map, processedCommands); }
public BmsChart Decode(IEnumerable <BmsCommand> commands) { var commandList = commands.AsList(); var chart = new Chart { Events = GetEvents(commandList).ToList() }; var initialBpm = chart.Events .Where(c => c[NumericData.MetricOffset] == 0 && c[NumericData.Bpm] != null) .Select(c => c[NumericData.Bpm]) .FirstOrDefault(); if (initialBpm == null) { initialBpm = commandList.Where(c => "bpm".Equals(c.Name, StringComparison.OrdinalIgnoreCase)) .Select(c => BigRationalParser.ParseString(c.Value)) .First(); if (initialBpm != null) { chart.Events.Add(new Event { [NumericData.Bpm] = initialBpm, [NumericData.MetricOffset] = 0 }); } } AddSingularMetadata(chart, commandList, SingularNumericTags); AddSingularMetadata(chart, commandList, SingularStringTags); AddMultiMetadata(chart, commandList, MultiStringTags); return(new BmsChart { Chart = chart, SoundMap = GetStringMap(commandList, "WAV").Map }); }
private IEnumerable <IEvent> GetMeasureEvents(BmsCommand command, IDictionary <int, BigRational> bpmMap) { var measure = Alphabet.DecodeNumeric(command.Name.AsSpan(0, 3)); var lane = Alphabet.DecodeHex(command.Name.AsSpan(3, 2)); switch (lane) { case 0x02: { return(new[] { new Event { [NumericData.MeasureLength] = BigRationalParser.ParseString(command.Value), [NumericData.MetricOffset] = measure } }); } } var events = new List <Event>(); var total = command.Value.Length / 2; for (var index = 0; index < total; index++) { var valueMemory = command.Value.AsMemory(index << 1, 2); var value = valueMemory.Span; if (value[0] == '0' && value[1] == '0') { continue; } void AddEvent(Event ev) { ev[NumericData.MetricOffset] = new BigRational(new BigInteger(measure), new BigInteger(index), new BigInteger(total)); ev[NumericData.SourceData] = Alphabet.DecodeAlphanumeric(valueMemory.Span); ev[NumericData.SourceCommand] = lane; events.Add(ev); } switch (lane) { case 0x01: { AddEvent(new Event { [NumericData.PlaySound] = Alphabet.DecodeAlphanumeric(value) }); break; } case 0x03: { AddEvent(new Event { [NumericData.Bpm] = Alphabet.DecodeHex(value) }); break; } case 0x04: { AddEvent(new Event { [NumericData.BgaBase] = Alphabet.DecodeAlphanumeric(value) }); break; } case 0x05: { AddEvent(new Event { [NumericData.BgaObject] = Alphabet.DecodeAlphanumeric(value) }); break; } case 0x06: { AddEvent(new Event { [NumericData.BgaPoor] = Alphabet.DecodeAlphanumeric(value) }); break; } case 0x07: { AddEvent(new Event { [NumericData.BgaLayer] = Alphabet.DecodeAlphanumeric(value) }); break; } case 0x08: { var number = Alphabet.DecodeAlphanumeric(value); if (bpmMap.ContainsKey(number)) { AddEvent(new Event { [NumericData.Bpm] = bpmMap[number] }); } break; } case 0x11: case 0x12: case 0x13: case 0x14: case 0x15: case 0x21: case 0x22: case 0x23: case 0x24: case 0x25: { AddEvent(new Event { [NumericData.Player] = (lane & 0x20) >> 5, [FlagData.Note] = true, [NumericData.Column] = lane & 0x0F }); break; } case 0x18: case 0x19: case 0x28: case 0x29: { AddEvent(new Event { [NumericData.Player] = (lane & 0x20) >> 5, [FlagData.Note] = true, [NumericData.Column] = (lane & 0x0F) - 2 }); break; } case 0x16: case 0x26: { AddEvent(new Event { [NumericData.Player] = (lane & 0x20) >> 5, [FlagData.Scratch] = true, [FlagData.Note] = true }); break; } case 0x17: case 0x27: { AddEvent(new Event { [NumericData.Player] = (lane & 0x20) >> 5, [FlagData.FreeZone] = true, [FlagData.Note] = true }); break; } case 0x31: case 0x32: case 0x33: case 0x34: case 0x35: case 0x41: case 0x42: case 0x43: case 0x44: case 0x45: { AddEvent(new Event { [NumericData.Player] = (lane & 0x20) >> 5, [NumericData.Column] = lane & 0x0F, [NumericData.LoadSound] = Alphabet.DecodeAlphanumeric(value) }); break; } case 0x38: case 0x39: case 0x48: case 0x49: { AddEvent(new Event { [NumericData.Player] = (lane & 0x20) >> 5, [NumericData.Column] = (lane & 0x0F) - 2, [NumericData.LoadSound] = Alphabet.DecodeAlphanumeric(value) }); break; } case 0x36: case 0x37: case 0x46: case 0x47: { AddEvent(new Event { [NumericData.Player] = (lane & 0x20) >> 5, [FlagData.Scratch] = true, [NumericData.LoadSound] = Alphabet.DecodeAlphanumeric(value) }); break; } } } return(events); }
public ITask CreateDecode1() { return(Build("Decode 1", task => { var rate = new BigRational(1000, 1); var files = GetInputFiles(task); if (!files.Any()) { task.Message = "No input files."; return false; } if (Args.Options.ContainsKey("rate")) { rate = BigRationalParser.ParseString(Args.Options["rate"].Last()) ?? throw new RhythmCodexException($"Invalid rate."); } ParallelProgress(task, files, file => { using (var stream = OpenRead(task, file)) { var charts = _beatmaniaPc1StreamReader.Read(stream, stream.Length).ToList(); var decoded = charts.Select(c => { var newChart = _beatmaniaPc1ChartDecoder.Decode(c.Data, rate); newChart[NumericData.Id] = c.Index; switch (c.Index) { case 0: case 6: { newChart[NumericData.Difficulty] = BeatmaniaDifficultyConstants.NormalId; break; } case 1: case 7: { newChart[NumericData.Difficulty] = BeatmaniaDifficultyConstants.LightId; break; } case 2: case 8: { newChart[NumericData.Difficulty] = BeatmaniaDifficultyConstants.AnotherId; break; } case 3: case 9: { newChart[NumericData.Difficulty] = BeatmaniaDifficultyConstants.BeginnerId; break; } } newChart[StringData.Title] = Path.GetFileNameWithoutExtension(file.Name); return newChart; }).ToList(); if (!EnableExportingCharts) { return; } foreach (var chart in decoded) { chart.PopulateMetricOffsets(); var encoded = _bmsEncoder.Encode(chart); using (var outStream = OpenWriteMulti(task, file, i => $"{Alphabet.EncodeNumeric((int) chart[NumericData.Id], 2)}.bme")) { _bmsStreamWriter.Write(outStream, encoded); } } } }); return true; })); }