Exemple #1
0
        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)
     });
 }
Exemple #4
0
        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);
        }
Exemple #6
0
        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);
        }
Exemple #9
0
        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);
            }
        }
Exemple #11
0
        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));
        }