private static DdrDatabaseEntry GetOldRecord(DdrPs2MetadataTableEntry item) { var record = item.Data.AsSpan(); var id = Encodings.CP437.GetStringWithoutNulls(record.Slice(0x00, 5)); if (id == string.Empty) { return(null); } return(new DdrDatabaseEntry { Index = item.Index, Id = id, Type = record[0x06], CdTitle = record[0x07], InternalId = Bitter.ToInt16(record, 0x08), MaxBpm = Bitter.ToInt16(record, 0x10), MinBpm = Bitter.ToInt16(record, 0x12), Unknown014 = Bitter.ToInt16(record, 0x14), SonglistOrder = Bitter.ToInt16(record, 0x16), UnlockNumber = Bitter.ToInt16(record, 0x18), Difficulties = new int[0], Flags = Bitter.ToInt32(record, 0x1C), // Radar0 = Bitter.ToInt16Array(record, 0x20, 6), // Radar1 = Bitter.ToInt16Array(record, 0x2C, 6), // Radar2 = Bitter.ToInt16Array(record, 0x38, 6), // Radar3 = Bitter.ToInt16Array(record, 0x44, 6), // Radar4 = Bitter.ToInt16Array(record, 0x50, 6) }); }
public IsoVolume Decode(ReadOnlySpan <byte> data) { return(new IsoVolume { SystemIdentifier = Encodings.CP437.GetString(data.Slice(8, 32)), VolumeIdentifier = Encodings.CP437.GetString(data.Slice(40, 32)), SpaceSize = Bitter.ToInt32(data.Slice(80, 8)), SetSize = Bitter.ToInt32(data.Slice(120, 4)), SequenceNumber = Bitter.ToInt32(data.Slice(124, 4)), LogicalBlockSize = Bitter.ToInt32(data.Slice(128, 4)), PathTableSize = Bitter.ToInt32(data.Slice(132, 8)), TypeLPathTableLocation = Bitter.ToInt32(data.Slice(140, 4)), OptionalTypeLPathTableLocation = Bitter.ToInt32(data.Slice(144, 4)), TypeMPathTableLocation = Bitter.ToInt32(data.Slice(148, 4)), OptionalTypeMPathTableLocation = Bitter.ToInt32(data.Slice(152, 4)), RootDirectoryRecord = _isoDirectoryRecordDecoder.Decode(new MemoryStream(data.Slice(156, 34).ToArray()), true), VolumeSetIdentifier = Encodings.CP437.GetString(data.Slice(190, 128)), PublisherIdentifier = Encodings.CP437.GetString(data.Slice(318, 128)), DataPreparerIdentifier = Encodings.CP437.GetString(data.Slice(446, 128)), ApplicationIdentifier = Encodings.CP437.GetString(data.Slice(574, 128)), CopyrightFileIdentifier = Encodings.CP437.GetString(data.Slice(702, 38)), AbstractFileIdentifier = Encodings.CP437.GetString(data.Slice(740, 36)), BibliographicFileIdentifier = Encodings.CP437.GetString(data.Slice(776, 37)), ApplicationData = data.Slice(883, 512).ToArray() }); }
public XboxIsoInfo Decode(byte[] sector) { return(new XboxIsoInfo { DirectorySectorNumber = Bitter.ToInt32(sector, 0x14), DirectorySize = Bitter.ToInt32(sector, 0x18) }); }
public WaveFmtChunk Decode(IRiffChunk chunk) { var data = chunk.Data; return(new WaveFmtChunk { Format = Bitter.ToInt16(data, 0), Channels = Bitter.ToInt16(data, 2), SampleRate = Bitter.ToInt32(data, 4), ByteRate = Bitter.ToInt32(data, 8), BlockAlign = Bitter.ToInt16(data, 12), BitsPerSample = Bitter.ToInt16(data, 14), ExtraData = data.Length > 16 ? data.AsSpan(16).ToArray() : new byte[0] }); }
public HeuristicResult Match(IHeuristicReader reader) { if (reader.Length < 0x804) { return(null); } var data = reader.Read(0x2C); if (Bitter.ToInt32(data.Slice(0x00)) != 0x08640001) { return(null); } if (Bitter.ToInt32(data.Slice(0x04)) != 0) { return(null); } var startOffset = Bitter.ToInt32(data.Slice(0x08)); if (startOffset < 0x00000030) { return(null); } var result = new VagHeuristicResult(this) { Start = startOffset, Length = Bitter.ToInt32(data.Slice(0x0C)), LoopStart = Bitter.ToInt32(data.Slice(0x10)), LoopEnd = Bitter.ToInt32(data.Slice(0x14)), SampleRate = Bitter.ToInt32(data.Slice(0x18)), Channels = Bitter.ToInt32(data.Slice(0x1C)), Interleave = Bitter.ToInt32(data.Slice(0x24)), Volume = new BigRational(Bitter.ToInt32(data.Slice(0x28)), 100), }; if (data[0x20] != 0x00 || data[0x21] != 0x00 || data[0x22] != 0x00 || data[0x23] != 0x00) { result.Key = data.Slice(0x20, 4).ToArray(); } return(result); }
public HeuristicResult Match(IHeuristicReader reader) { var data = reader.Read(0x10); if (data.Length < 0x10) { return(null); } var a = Bitter.ToInt32(data); var b = Bitter.ToInt32(data, 4); var c = Bitter.ToInt32(data, 8); var d = Bitter.ToInt32(data, 12); if (a <= 0) { return(null); } if (b <= 0) { return(null); } if (c < 0) { return(null); } if (d != 0) { return(null); } if (a - b - c != 16384) { return(null); } return(new HeuristicResult(this)); }
public HeuristicResult Match(IHeuristicReader reader) { // Must have at least 4 bytes if (reader.Length == null || reader.Length < 4) { return(null); } // Put a hard cap of 64k as a sanity check if (reader.Length > 65536) { return(null); } // Must be divisible by 4 if ((reader.Length & 0x3) != 0) { return(null); } var data = reader.Read((int)reader.Length); // Make sure each chunk length makes sense var thisOffset = 0; while (thisOffset < reader.Length) { var thisChunkLength = Bitter.ToInt32(data, thisOffset); if (thisChunkLength == 0) { break; } if (thisChunkLength < 0) { return(null); } if (thisOffset + thisChunkLength >= reader.Length) { return(null); } if ((thisOffset & 0x3) != 0) { return(null); } thisOffset += thisChunkLength; } // Try to make sense of the timing chunk length var timingChunkLength = Bitter.ToInt32(data); if (timingChunkLength < 20) { return(null); } if (timingChunkLength >= reader.Length) { return(null); } if ((timingChunkLength & 0x3) != 0) { return(null); } // Check both the measure and the second offset, make sure they are increasing var timingMeasure = int.MinValue; var timingSector = int.MinValue; for (var i = 1; i < timingChunkLength >> 2; i += 2) { var thisTimingMeasure = Bitter.ToInt32(data, i << 2); var thisTimingSector = Bitter.ToInt32(data, 0x4 + (i << 2)); if (thisTimingMeasure < timingMeasure) { return(null); } if (thisTimingSector < timingSector) { return(null); } timingMeasure = thisTimingMeasure; timingSector = thisTimingSector; } // No reason to believe this isn't a step1 at this point return(new HeuristicResult(this)); }
public IList <DdrDatabaseEntry> Decode(ReadOnlySpan <byte> database) { var offset = 0; var length = database.Length; var result = new List <DdrDatabaseEntry>(); var shortNameOffsets = new Dictionary <int, int>(); var longNameOffsets = new Dictionary <int, int>(); var index = 0; // Read database entries while (offset < length) { var raw = database.Slice(offset, 0x80); var id = Encodings.CP437.GetStringWithoutNulls(raw.Slice(0x00, 5)); if (id == string.Empty) { break; } var entry = new DdrDatabaseEntry { Index = index, Id = id, Type = raw[0x06], CdTitle = raw[0x07], InternalId = Bitter.ToInt16(raw, 0x08), MaxBpm = Bitter.ToInt16(raw, 0x10), MinBpm = Bitter.ToInt16(raw, 0x12), Unknown014 = Bitter.ToInt16(raw, 0x14), SonglistOrder = Bitter.ToInt16(raw, 0x16), UnlockNumber = Bitter.ToInt16(raw, 0x18), Difficulties = new[] { raw[0x1C] & 0xF, raw[0x1C] >> 4, raw[0x1D] & 0xF, raw[0x1D] >> 4, raw[0x20] & 0xF, raw[0x20] >> 4, raw[0x21] & 0xF, raw[0x21] >> 4, }, Unknown01E = Bitter.ToInt16(raw, 0x1E), Unknown022 = Bitter.ToInt16(raw, 0x22), Flags = Bitter.ToInt32(raw, 0x24), Radar0 = Bitter.ToInt16Array(raw, 0x28, 8), Radar1 = Bitter.ToInt16Array(raw, 0x38, 8), Radar2 = Bitter.ToInt16Array(raw, 0x48, 8), Radar3 = Bitter.ToInt16Array(raw, 0x58, 8), Radar4 = Bitter.ToInt16Array(raw, 0x68, 8) }; longNameOffsets[index] = Bitter.ToInt32(raw, 0x78); shortNameOffsets[index] = Bitter.ToInt32(raw, 0x7C); result.Add(entry); offset += 0x80; index++; } offset += 0x80; // Read string table var strings = database.Slice(offset); foreach (var kv in longNameOffsets) { result[kv.Key].LongName = Encodings.CP437.GetStringWithoutNulls(strings.Slice(kv.Value)); } foreach (var kv in shortNameOffsets) { result[kv.Key].ShortName = Encodings.CP437.GetStringWithoutNulls(strings.Slice(kv.Value)); } return(result); }
private static DdrDatabaseEntry GetNewRecord(DdrPs2MetadataTableEntry records) { var record = records.Data.AsSpan(); var id = Encodings.CP437.GetStringWithoutNulls(record.Slice(0x00, 5)); var mdbIndex = 0; var difficultyOffset = 0; int[] difficulties; if (id == string.Empty) { return(null); } var bpmOffset = 0; var isXDifficulties = Bitter.ToInt16(record, 0x06) == Bitter.ToInt16(record, 0x08) && record.Slice(0x25, 10).ToArray().All(x => x <= 20) && record.Slice(0x25, 10).ToArray().Any(x => x != 0x00); if (isXDifficulties) { mdbIndex = Bitter.ToInt16(record, 0x08); bpmOffset += 0x0C; } else { if (Bitter.ToInt16(record, 0x10) != 0 && Bitter.ToInt16(record, 0x12) == 0) { if (record[0x06] == 0x05 && Bitter.ToInt16(record, 0x08) == Bitter.ToInt16(record, 0x0A)) { // SN JP, SN US, SN2 US mdbIndex = Bitter.ToInt16(record, 0x0C); bpmOffset += 0x10; difficultyOffset += 0x10; } else { // SN2 JP mdbIndex = Bitter.ToInt16(record, 0x08); bpmOffset += 0x0C; } } if (Bitter.ToInt32(record, 0x14 + bpmOffset) != -1) { // SN, SN2 while (Bitter.ToInt32(record, 0x10 + bpmOffset) == 0) { bpmOffset += 4; difficultyOffset += 4; } } if (bpmOffset > 0) { while (Bitter.ToInt32(record, 0x24 + difficultyOffset) == 0) { difficultyOffset += 4; } } } if (isXDifficulties) { difficulties = new[] { record[0x26], record[0x27], record[0x28], record[0x29], record[0x25], 0, 0, 0, record[0x2B], record[0x2C], record[0x2D], record[0x2E], record[0x2A], 0, 0, 0, }; } else { difficulties = new[] { record[0x24 + difficultyOffset] & 0xF, record[0x24 + difficultyOffset] >> 4, record[0x25 + difficultyOffset] & 0xF, record[0x25 + difficultyOffset] >> 4, record[0x26 + difficultyOffset] & 0xF, record[0x26 + difficultyOffset] >> 4, record[0x27 + difficultyOffset] & 0xF, record[0x27 + difficultyOffset] >> 4, record[0x28 + difficultyOffset] & 0xF, record[0x28 + difficultyOffset] >> 4, record[0x29 + difficultyOffset] & 0xF, record[0x29 + difficultyOffset] >> 4, record[0x2A + difficultyOffset] & 0xF, record[0x2A + difficultyOffset] >> 4, record[0x2B + difficultyOffset] & 0xF, record[0x2B + difficultyOffset] >> 4 }; } return(new DdrDatabaseEntry { Index = records.Index, Id = id, Type = record[0x06], CdTitle = record[0x07], InternalId = Bitter.ToInt16(record, 0x08), MaxBpm = Bitter.ToInt16(record, 0x10 + bpmOffset), MinBpm = Bitter.ToInt16(record, 0x12 + bpmOffset), Unknown014 = Bitter.ToInt16(record, 0x14 + bpmOffset), SonglistOrder = Bitter.ToInt16(record, 0x16 + bpmOffset), UnlockNumber = Bitter.ToInt16(record, 0x18 + bpmOffset), Difficulties = difficulties, Flags = Bitter.ToInt32(record, 0x2C + difficultyOffset), AudioTrack = mdbIndex // Radar0 = Bitter.ToInt16Array(record, 0x30, 6), // Radar1 = Bitter.ToInt16Array(record, 0x3C, 6), // Radar2 = Bitter.ToInt16Array(record, 0x48, 6), // Radar3 = Bitter.ToInt16Array(record, 0x54, 6), // Radar4 = Bitter.ToInt16Array(record, 0x60, 6) }); }
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); } }
public HeuristicResult Match(IHeuristicReader reader) { var noteCountMode = true; var hasBpm = false; var hasEnd = false; var hasTerminator = false; var evData = new byte[4]; while (true) { if (reader.Read(evData, 0, 4) < 4) { break; } var eventOffset = Bitter.ToInt16S(evData); var eventValue = evData[2]; var eventCommand = evData[3]; var eventParameter = eventCommand >> 4; var eventType = eventCommand & 0xF; // empty event = invalid if (Bitter.ToInt32(evData) == 0) { return(null); } // positive event offsets only if (eventOffset < 0) { return(null); } // offsets can't be present during note count if (noteCountMode && eventOffset != 0) { return(null); } // disable note count info if another event type shows up if (eventCommand != 0x00 && eventCommand != 0x01) { noteCountMode = false; } // skip the rest of processing if in note count mode if (noteCountMode) { continue; } // terminator bytes if (eventOffset == 0x7FFF) { hasTerminator = true; break; } // make sure we have the bare minimums if (eventType == 6) { hasEnd = true; } else if (eventType == 4 && eventValue + (eventParameter << 8) != 0) { hasBpm = true; } } if (!(hasBpm && hasEnd && hasTerminator)) { return(null); } return(new HeuristicResult(this)); }