• 酷狗 KRC 文件的解析


    清理硬盘发现以前写过一个进行一半的代码,这次补全并从硬盘删掉。

    格式说明来自 https://shansing.com/read/392/

    krc解码并解压缩后得到一个字符串,例子:

    [id:$00000000]
    [ar:信乐团]
    [ti:北京一夜]
    [by:韩佯Τé]
    [hash:766fe295bf2722a9ede2abdd61d580c1]
    [total:278438]
    [sign:大家去北京玩一夜吧!!!!]
    [53883,3092]<0,632,0>One <632,784,0>Night <1416,372,0>in <1788,548,0>北<2336,755,0>京
    [56675,3539]<0,560,0>我<560,416,0>留<976,392,0>下<1368,412,0>许<1780,392,0>多<2172,1366,0>情
    [59914,2577]<0,549,0>不<549,276,0>管<825,252,0>你<1077,214,0>爱<1291,182,0>与<1473,212,0>不 <1685,887,0>爱
    [62191,3344]<0,560,0>都<560,210,0>是<770,210,0>历<980,204,0>史<1184,202,0>的<1386,564,0>尘<1950,1387,0>埃

    开头的几行就不用解释了,lrc也有。

    其中快速匹配歌词的可能方式是靠计算歌曲文件的hash,以及匹配歌词与歌曲的total

    歌词开始的行格式:

    [此行开始时刻距0时刻的毫秒数,此行持续的毫秒数]<0,此字持续的毫秒数,0>歌<此字开始的时刻距此行开始时刻的毫秒数,此字持续的毫秒数,0>词<此字开始的时刻距此行开始时刻的毫秒数,此字持续的毫秒数,0>正<此字开始的时刻距此行开始时刻的毫秒数,此字持续的毫秒数,0>文

    具体代码如下:

      1 using System;
      2 using System.Collections.Generic;
      3 using System.Linq;
      4 using System.Text;
      5 using System.IO;
      6 using System.IO.Compression;
      7 using ICSharpCode.SharpZipLib.Zip.Compression;
      8 using ICSharpCode.SharpZipLib.Zip.Compression.Streams;
      9 using System.Diagnostics;
     10 
     11 namespace KRC.KRCLib
     12 {
     13     public static class KRCFile
     14     {
     15         /// <summary>
     16         /// 异或加密 密钥
     17         /// </summary>
     18         public static readonly char[] KRCFileXorKey = { '@', 'G', 'a', 'w', '^', '2', 't', 'G', 'Q', '6', '1', '-', 'Î', 'Ò', 'n', 'i' };
     19 
     20         /// <summary>
     21         /// KRC 文件头
     22         /// </summary>
     23         public static readonly char[] KRCFileHead = { 'k', 'r', 'c', '1' };
     24 
     25         /// <summary>
     26         /// KRC 文件头的字节
     27         /// </summary>
     28         public static readonly byte[] KRCFileHeadBytes = { 0x6B, 0x72, 0x63, 0x31 };
     29 
     30 
     31         /// <summary>
     32         /// 解码
     33         /// </summary>
     34         public static string DecodeFileToString(string krcFilePath)
     35         {
     36             //krc1
     37             var headBytes = new byte[4];
     38             byte[] encodedBytes;
     39             byte[] zipedBytes;
     40 
     41             using (var krcfs = new FileStream(krcFilePath, FileMode.Open))
     42             {
     43                 encodedBytes = new byte[krcfs.Length - headBytes.Length];
     44                 zipedBytes = new byte[krcfs.Length - headBytes.Length];
     45 
     46                 //读文件头标记
     47                 krcfs.Read(headBytes, 0, headBytes.Length);
     48 
     49                 //读XOR加密的内容
     50                 krcfs.Read(encodedBytes, 0, encodedBytes.Length);
     51 
     52                 //关闭文件
     53                 krcfs.Close();
     54             }
     55 
     56             for (var i = 0; i < encodedBytes.Length; i++)
     57             {
     58                 zipedBytes[i] = (byte)(encodedBytes[i] ^ KRCFileXorKey[i % 16]);
     59             }
     60 
     61             //前面3字节是 UTF-8 的 BOM
     62             var unzipedBytes = Decompress(zipedBytes);
     63 
     64             //编码器带有BOM输出时多了3字节,所以跳过开头的3字节bom
     65             var text = RemoveBom(Encoding.UTF8.GetString(unzipedBytes));
     66 
     67             return text;
     68         }
     69 
     70         /// <summary>
     71         /// 编码到字节数组
     72         /// </summary>
     73         /// <param name="inText"></param>
     74         /// <returns></returns>
     75         public static byte[] EncodeStringToBytes(string inText)
     76         {
     77             //用默认的,编码时带有UTF-8的BOM
     78             byte[] inbytes = Encoding.UTF8.GetPreamble().Concat(Encoding.UTF8.GetBytes(inText)).ToArray();
     79 
     80             byte[] zipedBytes = Compress(inbytes);
     81 
     82             int encodedBytesLength = zipedBytes.Length;
     83 
     84             var encodedBytes = new byte[zipedBytes.Length];
     85 
     86 
     87             for (int i = 0; i < encodedBytesLength; i++)
     88             {
     89                 int l = i % 16;
     90 
     91                 encodedBytes[i] = (byte)(zipedBytes[i] ^ KRCFileXorKey[l]);
     92             }
     93 
     94             byte[] byets = null;
     95 
     96             using (var ms = new MemoryStream())
     97             {
     98                 ms.Write(KRCFileHeadBytes, 0, KRCFileHeadBytes.Length);
     99                 ms.Write(encodedBytes, 0, encodedBytes.Length);
    100                 ms.Flush();
    101                 byets = ms.ToArray();
    102             }
    103 
    104             return byets;
    105         }
    106 
    107         /// <summary>
    108         /// 移除UTF-8 BOM
    109         /// </summary>
    110         /// <param name="p"></param>
    111         /// <returns></returns>
    112         private static string RemoveBom(string p)
    113         {
    114             string bomMarkUtf8 = Encoding.UTF8.GetString(Encoding.UTF8.GetPreamble());
    115             if (p.StartsWith(bomMarkUtf8))
    116                 p = p.Remove(0, bomMarkUtf8.Length);
    117             return p.Replace("", "");
    118         }
    119 
    120         #region 压缩 解压缩
    121         private static byte[] Compress(byte[] pBytes)
    122         {
    123             byte[] outdata = null;
    124             using (var mMemory = new MemoryStream(pBytes))
    125             using (var mStream = new DeflaterOutputStream(mMemory, new Deflater(Deflater.DEFAULT_COMPRESSION), 131072))
    126             {
    127                 mStream.Write(pBytes, 0, pBytes.Length);
    128                 mStream.Flush();
    129                 mMemory.Flush();
    130                 outdata = mMemory.ToArray();
    131             }
    132             return outdata;
    133         }
    134 
    135         /// <summary>
    136         /// 解压缩
    137         /// </summary>
    138         /// <param name="data"></param>
    139         /// <returns></returns>
    140         private static byte[] Decompress(byte[] data)
    141         {
    142             byte[] outdata = null;
    143             using (var ms = new MemoryStream())
    144             using (var inputStream = new InflaterInputStream(new MemoryStream(data), new Inflater(false)))
    145             {
    146                 inputStream.CopyTo(ms);
    147                 ms.Flush();
    148 
    149                 outdata = ms.ToArray();
    150                 ms.Close();
    151             }
    152             return outdata;
    153         }
    154         #endregion
    155 
    156 
    157     }
    158 }
    KRCFile
     1 using System;
     2 using System.CodeDom;
     3 using System.Diagnostics;
     4 using System.Text.RegularExpressions;
     5 
     6 namespace KRC.KRCLib
     7 {
     8     /// <summary>
     9     /// KRC文件行字符
    10     /// </summary>
    11     [DebuggerDisplay("{DebuggerDisplay}")]
    12     public class KRCLyricsChar
    13     {
    14         /// <summary>
    15         /// 字符
    16         /// </summary>
    17         public char Char { get; set; }
    18 
    19         /// <summary>
    20         /// 字符KRC字符串
    21         /// </summary>
    22         public string KRCCharString
    23         {
    24             get
    25             {
    26                 return string.Format(@"<{0},{1},{2}>{3}", this.CharStart.TotalMilliseconds, this.CharDuring.TotalMilliseconds, 0, this.Char);
    27             }
    28         }
    29 
    30         /// <summary>
    31         /// 字符起始时间(计算时加上字符所属行的起始时间)
    32         /// </summary>
    33         public TimeSpan CharStart { get; set; }
    34 
    35         /// <summary>
    36         /// 字符时长
    37         /// </summary>
    38         public TimeSpan CharDuring { get; set; }
    39 
    40         public KRCLyricsChar()
    41         {
    42             this.CharStart = TimeSpan.Zero;
    43             this.CharDuring = TimeSpan.Zero;
    44         }
    45 
    46         public KRCLyricsChar(string krcCharString)
    47             : this()
    48         {
    49             var chars = Regex.Match(krcCharString, @"<(d+),(d+),(d+)>(.?)");
    50 
    51             if (chars.Success)
    52             {
    53                 if (chars.Groups.Count >= 4)
    54                 {
    55                     var charstart = chars.Groups[1].Value;
    56                     var charduring = chars.Groups[2].Value;
    57                     var unknowAlwaysZero = chars.Groups[3].Value;
    58 
    59                     this.CharStart = TimeSpan.FromMilliseconds(double.Parse(charstart));
    60                     this.CharDuring = TimeSpan.FromMilliseconds(double.Parse(charduring));
    61 
    62                     if (chars.Groups.Count >= 5)
    63                     {
    64                         var charchar = chars.Groups[4].Value;
    65                         this.Char = char.Parse(charchar);
    66                     }
    67                     else
    68                     {
    69                         this.Char = char.Parse(" ");
    70                     }
    71                 }
    72             }
    73         }
    74 
    75         public string DebuggerDisplay
    76         {
    77             get
    78             {
    79                 return string.Format(@"{0:hh:mm:ss.fff} {1:hh:mm:ss.fff} {2}", this.CharStart, this.CharDuring, this.Char);
    80             }
    81         }
    82     }
    83 }
    KRCLyricsChar
     1 using System;
     2 using System.Collections.Generic;
     3 using System.Diagnostics;
     4 using System.Linq;
     5 using System.Text.RegularExpressions;
     6 
     7 namespace KRC.KRCLib
     8 {
     9     /// <summary>
    10     /// KRC文件行
    11     /// </summary>
    12     [DebuggerDisplay("{DebuggerDisplay}")]
    13     public class KRCLyricsLine
    14     {
    15         private readonly List<KRCLyricsChar> _chars = new List<KRCLyricsChar>();
    16 
    17         /// <summary>
    18         /// 行字符串
    19         /// </summary>
    20         public string KRCLineString 
    21         {
    22             get
    23             {
    24                 return string.Format(@"[{0},{1}]{2}", this.LineStart.TotalMilliseconds, this.LineDuring.TotalMilliseconds,
    25                     string.Join("", this.Chars.Select(x => x.KRCCharString)));
    26             } 
    27         }
    28 
    29         /// <summary>
    30         /// 行开始事件
    31         /// </summary>
    32         public TimeSpan LineStart { get; set; }
    33 
    34         /// <summary>
    35         /// 行总时间
    36         /// </summary>
    37         public TimeSpan LineDuring 
    38         {
    39             get
    40             {
    41                 //计算行时间
    42                 var sum = this.Chars.Select(x => x.CharDuring.TotalMilliseconds).Sum();
    43                 return TimeSpan.FromMilliseconds(sum);
    44             }
    45         }
    46 
    47         /// <summary>
    48         /// 行内字符
    49         /// </summary>
    50 
    51         public List<KRCLyricsChar> Chars
    52         {
    53             get { return _chars; }
    54         }
    55 
    56         public KRCLyricsLine()
    57         {
    58             this.LineStart = TimeSpan.Zero;
    59         }
    60 
    61 
    62         public KRCLyricsLine(string krclinestring):this()
    63         {
    64             var regLineTime = new Regex(@"^[(.*),(.*)](.*)");
    65 
    66             var m1 = regLineTime.Match(krclinestring);
    67             if (m1.Success && m1.Groups.Count == 4)
    68             {
    69                 var linestart = m1.Groups[1].Value;
    70                 var linelength = m1.Groups[2].Value;
    71 
    72                 this.LineStart = TimeSpan.FromMilliseconds(double.Parse(linestart));
    73                 //this.LineDuring = TimeSpan.FromMilliseconds(double.Parse(linelength));
    74                 
    75                 var linecontent = m1.Groups[3].Value;
    76 
    77                 var chars = Regex.Matches(linecontent, @"<(d+),(d+),(d+)>(.?)");
    78 
    79                 foreach (Match m in chars)
    80                 {
    81                     this.Chars.Add(new KRCLyricsChar(m.Value));
    82                 }
    83             }
    84         }
    85 
    86         public string DebuggerDisplay
    87         {
    88             get
    89             {
    90                 return string.Format(@"{0:hh:mm:ss.fff} {1:hh:mm:ss.fff} {2}", this.LineStart, this.LineDuring,
    91                     string.Join(",", this.Chars.Select(x => x.Char.ToString())));
    92             }
    93         }
    94     }
    95 }
    KRCLyricsLine
      1 using System;
      2 using System.Collections.Generic;
      3 using System.IO;
      4 using System.Linq;
      5 using System.Text;
      6 using System.Text.RegularExpressions;
      7 
      8 namespace KRC.KRCLib
      9 {
     10     /// <summary>
     11     /// KRC歌词文件
     12     /// </summary>
     13     public class KRCLyrics
     14     {
     15         public List<KRCLyricsLine> Lines
     16         {
     17             get { return _lines; }
     18         }
     19 
     20         /// <summary>
     21         /// 歌词文本
     22         /// </summary>
     23         public string KRCString { get; set; }
     24 
     25         /// <summary>
     26         /// ID (总是$00000000,意义未知)
     27         /// </summary>
     28         public string ID { get; set; }
     29 
     30         /// <summary>
     31         /// 艺术家
     32         /// </summary>
     33         public string Ar { get; set; }
     34 
     35         /// <summary>
     36         /// 
     37         /// </summary>
     38         public string Al { get; set; }
     39 
     40         /// <summary>
     41         /// 标题
     42         /// </summary>
     43         public string Title { get; set; }
     44 
     45         /// <summary>
     46         /// 歌词文件作者
     47         /// </summary>
     48         public string By { get; set; }
     49 
     50         /// <summary>
     51         /// 歌曲文件Hash
     52         /// </summary>
     53         public string Hash { get; set; }
     54 
     55         /// <summary>
     56         /// 总时长
     57         /// </summary>
     58         public TimeSpan Total 
     59         {
     60             get
     61             {
     62                 //计算总时间=所有行时间
     63                 var sum = this.Lines.Select(x => x.LineDuring.TotalMilliseconds).Sum();
     64                 return TimeSpan.FromMilliseconds(sum);
     65             }
     66         }
     67 
     68         /// <summary>
     69         /// 偏移
     70         /// </summary>
     71         public TimeSpan Offset { get; set; }
     72 
     73         private readonly List<KRCLyricsLine> _lines = new List<KRCLyricsLine>();
     74         private readonly List<Tuple<Regex, Action<string>>> _properties;
     75         private readonly Regex _regGetValueFromKeyValuePair = new Regex(@"[(.*):(.*)]");
     76 
     77         /// <summary>
     78         /// 默认构造
     79         /// </summary>
     80         public KRCLyrics()
     81         {
     82             //this.Total = TimeSpan.Zero;
     83             this.Offset = TimeSpan.Zero;
     84 
     85             this._properties = new List<Tuple<Regex, Action<string>>>()
     86             {
     87                 new Tuple<Regex, Action<string>>(new Regex("\[id:[^\]]+\]"), (s) => { this.ID = s; }),
     88                 new Tuple<Regex, Action<string>>(new Regex("\[al:[^\n]+\n"), (s) => { this.Al = s; }),
     89                 new Tuple<Regex, Action<string>>(new Regex("\[ar:[^\]]+\]"), (s) => { this.Ar = s; }),
     90                 new Tuple<Regex, Action<string>>(new Regex("\[ti:[^\]]+\]"), (s) => { this.Title = s; }),
     91                 new Tuple<Regex, Action<string>>(new Regex("\[hash:[^\n]+\n"), (s) => { this.Hash = s; }),
     92                 new Tuple<Regex, Action<string>>(new Regex("\[by:[^\n]+\n"), (s) => { this.By = s; }),
     93                 new Tuple<Regex, Action<string>>(new Regex("\[total:[^\n]+\n"), (s) =>
     94                 {
     95                     //this.Total = TimeSpan.FromMilliseconds(double.Parse(s));
     96                 }),
     97                 new Tuple<Regex, Action<string>>(new Regex("\[offset:[^\n]+\n"), (s) =>
     98                 {
     99                     this.Offset = TimeSpan.FromMilliseconds(double.Parse(s));
    100                 }),
    101             };
    102         }
    103 
    104         /// <summary>
    105         /// 构造
    106         /// </summary>
    107         /// <param name="krcstring">KRC字符文本</param>
    108         private KRCLyrics(string krcstring):this()
    109         {
    110             this.KRCString = krcstring;
    111             this.LoadProperties();
    112             this.LoadLines();
    113         }
    114 
    115         /// <summary>
    116         /// 加载KRC属性
    117         /// </summary>
    118         private void LoadProperties()
    119         {
    120             foreach (var prop in _properties)
    121             {
    122                 var m = prop.Item1.Match(this.KRCString);
    123                 if (m.Success)
    124                 {
    125                     var mm = _regGetValueFromKeyValuePair.Match(m.Value);
    126 
    127                     if (mm.Success && mm.Groups.Count == 3)
    128                     {
    129                         prop.Item2(mm.Groups[2].Value);
    130                     }
    131                 }
    132             }
    133         }
    134 
    135         /// <summary>
    136         /// 加载KRC所有行数据
    137         /// </summary>
    138         private void LoadLines()
    139         {
    140             var linesMachCollection = Regex.Matches(this.KRCString, @"[d{1,}[^
    ]+
    ");
    141             foreach (Match m in linesMachCollection)
    142             {
    143                 this.Lines.Add(new KRCLyricsLine(m.Value));
    144             }
    145         }
    146 
    147         /// <summary>
    148         /// 保存到文件
    149         /// </summary>
    150         /// <param name="outputFilePath"></param>
    151         public void SaveToFile(string outputFilePath)
    152         {
    153             var sb = new StringBuilder();
    154             sb.AppendLine(string.Format("[id:{0}]", this.ID));
    155 
    156 
    157             if (!string.IsNullOrEmpty(this.Al))
    158             {
    159                 sb.AppendLine(string.Format("[al:{0}]", this.Al));
    160             }
    161 
    162             if (!string.IsNullOrEmpty(this.Ar))
    163             {
    164                 sb.AppendLine(string.Format("[ar:{0}]", this.Ar));
    165             }
    166 
    167             if (!string.IsNullOrEmpty(this.Title))
    168             {
    169                 sb.AppendLine(string.Format("[ti:{0}]", this.Title));
    170             }
    171 
    172             if (!string.IsNullOrEmpty(this.Hash))
    173             {
    174                 sb.AppendLine(string.Format("[hash:{0}]", this.Hash));
    175             }
    176 
    177             if (!string.IsNullOrEmpty(this.By))
    178             {
    179                 sb.AppendLine(string.Format("[by:{0}]", this.By));
    180             }
    181 
    182             if (this.Total!= TimeSpan.Zero)
    183             {
    184                 sb.AppendLine(string.Format("[total:{0}]", this.Total.TotalMilliseconds));
    185             }
    186 
    187             if (this.Offset != TimeSpan.Zero)
    188             {
    189                 sb.AppendLine(string.Format("[offset:{0}]", this.Offset.TotalMilliseconds));
    190             }
    191 
    192 
    193             foreach (var line in this.Lines)
    194             {
    195                 sb.AppendLine(line.KRCLineString);
    196             }
    197 
    198 
    199             var bytes = KRCFile.EncodeStringToBytes(sb.ToString());
    200 
    201 
    202             File.WriteAllBytes(outputFilePath, bytes);
    203 
    204         }
    205 
    206         /// <summary>
    207         /// 从文件加载
    208         /// </summary>
    209         /// <param name="inputFilePath"></param>
    210         /// <returns></returns>
    211         public static KRCLyrics LoadFromFile(string inputFilePath)
    212         {
    213             var str = KRCFile.DecodeFileToString(inputFilePath);
    214 
    215             return LoadFromString(str);
    216         }
    217 
    218         /// <summary>
    219         /// 从文本加载
    220         /// </summary>
    221         /// <param name="krcstring"></param>
    222         /// <returns></returns>
    223         public static KRCLyrics LoadFromString(string krcstring)
    224         {
    225             var aa = new KRCLyrics(krcstring);
    226             return aa;
    227         }
    228     }
    229 }
    KRCLyrics
     1 using System;
     2 using System.Collections.Generic;
     3 using System.ComponentModel;
     4 using System.Linq;
     5 using System.Runtime.InteropServices;
     6 using System.Text;
     7 using System.Text.RegularExpressions;
     8 using KRC.KRCLib;
     9 
    10 namespace KRC.Test
    11 {
    12     class Program
    13     {
    14         static void Main(string[] args)
    15         {
    16             string inputFile = @"杨钰莹.桃花运-b0c4014bd991a6a637445defa56822f9.krc";
    17             string outputFile = @"123.krc";
    18             KRCLyrics krc = KRCLyrics.LoadFromFile(inputFile);
    19             Console.WriteLine("解码 [{0}] 完毕。", inputFile);
    20             krc.SaveToFile(outputFile);
    21             Console.WriteLine("另存为 [{0}] 完毕。", outputFile);
    22             Console.ReadLine();
    23         }
    24     }
    25 }
  • 相关阅读:
    思科交换机密码的破解
    windows server 2008 R2 NPS(网络连接策略服务)设置radius,实现telent登陆交换机路由器权限分配
    Windows Server 2008 R2服务器系统安全设置参考指南
    svn 从文件上次修改以来没有任何文件修改或加入。
    fragment The specified child already has a parent. You must call removeView()
    Android初级教程_获取Android控件的宽和高
    Caused by: java.lang.IllegalStateException: commit already called
    Android 仿携程活动列表边框布局
    Android 解决在初次打开Activity加载布局文件时,ScrollView滚动条不在顶部的问题
    中国首个开源 HTML5 跨屏前端框架 分享
  • 原文地址:https://www.cnblogs.com/binsys/p/4106153.html
Copyright © 2020-2023  润新知