注册 | 登录

游侠NETSHOW论坛





查看: 7098|回复: 7

[数据研究] 三国志9消息文件LS11格式编码解码c#类 [复制链接]

帖子
92
精华
0
积分
169
金钱
1374
荣誉
12
人气
32
评议
0
发表于 2014-12-31 11:30:19 |显示全部楼层
本帖最后由 8403 于 2014-12-31 11:35 编辑

之前汉化San9 V1.3日文版时,研究了下消息文件(M_msg.s9、M_MsgPK.s9、M_Rtdn.s9、M_RtdnPK.s9)的LS11编码格式,文件都需要解码后才能看到具体内容,修改后再进行编码,分享一下解码编码的类。


  1. using System;
  2. using System.Collections.Generic;
  3. using System.Text;
  4. using System.Text.RegularExpressions;

  5. namespace Pub.Utils
  6. {
  7.     public class LS11
  8.     {
  9.         /// <summary>
  10.         /// 解码
  11.         /// </summary>
  12.         /// <param name="strFilePath"></param>
  13.         /// <returns></returns>
  14.         public static byte[] Decode(string strFilePath)
  15.         {
  16.             List<byte> lstDeCon = new List<byte>();//解压后内容
  17.             using (System.IO.FileStream ls11FileReader = new System.IO.FileStream(strFilePath, System.IO.FileMode.Open, System.IO.FileAccess.Read, System.IO.FileShare.ReadWrite))
  18.             {
  19.                 byte[] bytes16 = new byte[16];//头16字节 LS11格式
  20.                 ls11FileReader.Seek(0, 0);
  21.                 ls11FileReader.Read(bytes16, 0, bytes16.Length);
  22.                 byte[] bytesDic = new byte[256];//256位字典
  23.                 ls11FileReader.Read(bytesDic, 0, bytesDic.Length);
  24.                 byte[] bytesLen1 = new byte[4];//压缩后长度
  25.                 ls11FileReader.Read(bytesLen1, 0, bytesLen1.Length);
  26.                 Array.Reverse(bytesLen1);//反转bytes
  27.                 int intLen1 = System.BitConverter.ToInt32(bytesLen1, 0);
  28.                 byte[] bytesLen2 = new byte[4];//解压后长度
  29.                 ls11FileReader.Read(bytesLen2, 0, bytesLen2.Length);
  30.                 Array.Reverse(bytesLen2);//反转bytes
  31.                 int intLen2 = System.BitConverter.ToInt32(bytesLen2, 0);
  32.                 byte[] bytesConStart = new byte[4];//文件起始位置
  33.                 ls11FileReader.Read(bytesConStart, 0, bytesConStart.Length);
  34.                 Array.Reverse(bytesConStart);//反转bytes
  35.                 int intConStart = System.BitConverter.ToInt32(bytesConStart, 0);
  36.                 byte[] bytesZero = new byte[4];//4个0
  37.                 ls11FileReader.Read(bytesConStart, 0, bytesConStart.Length);
  38.                 byte[] bytesCon = new byte[intLen1];//压缩后的正文
  39.                 ls11FileReader.Read(bytesCon, 0, bytesCon.Length);
  40.                 ls11FileReader.Close();
  41.                 //内容转成二进制
  42.                 StringBuilder sbConBinary = new StringBuilder();
  43.                 foreach (byte byteValue in bytesCon)
  44.                 {
  45.                     sbConBinary.Append(Convert.ToString(byteValue, 2).PadLeft(8, '0'));//8位,不够前补0
  46.                 }
  47.                 string strConBinary = sbConBinary.ToString();

  48.                 #region 得到解压后索引列表
  49.                 int intPos = 0;
  50.                 List<int> lstDeConIndex = new List<int>();//解压后的索引
  51.                 List<char> lstTempSeg1 = new List<char>();//临时段1
  52.                 List<char> lstTempSeg2 = new List<char>();//临时段2
  53.                 while (intPos < strConBinary.Length)
  54.                 {
  55.                     char charValue = strConBinary[intPos];
  56.                     intPos++;
  57.                     lstTempSeg1.Add(charValue);
  58.                     if (charValue.Equals('0'))//遇0则段1结束
  59.                     {
  60.                         for (int i = 0; i < lstTempSeg1.Count; i++)//取出对应长度段2
  61.                         {
  62.                             lstTempSeg2.Add(strConBinary[intPos]);
  63.                             intPos++;
  64.                         }
  65.                         String strSeg1 = new String(lstTempSeg1.ToArray());
  66.                         String strSeg2 = new String(lstTempSeg2.ToArray());
  67.                         int intItemValue = Convert.ToInt32(strSeg1, 2) + Convert.ToInt32(strSeg2, 2);
  68.                         lstDeConIndex.Add(intItemValue);//段1+段2=实际值索引
  69.                         lstTempSeg1.Clear();
  70.                         lstTempSeg2.Clear();
  71.                     }
  72.                 }
  73.                 #endregion

  74.                 #region 遍历索引列表,从字典获取实际值
  75.                 for (int i = 0; i < lstDeConIndex.Count; i++)
  76.                 {
  77.                     if (lstDeCon.Count >= intLen2) break;
  78.                     int intIndex = lstDeConIndex[i];
  79.                     if (intIndex < 256)//字典范围内直接取值
  80.                     {
  81.                         lstDeCon.Add(bytesDic[intIndex]);
  82.                         continue;
  83.                     }
  84.                     int intOffset = intIndex - 256;//大于255则向前复制取值
  85.                     int intLen = lstDeConIndex[i + 1] + 3;//下一个索引值加3为取值长度
  86.                     i++;
  87.                     for (int j = 0; j < intLen; j++)
  88.                     {
  89.                         byte byteTemp = lstDeCon[lstDeCon.Count - intOffset];
  90.                         lstDeCon.Add(byteTemp);
  91.                     }
  92.                 }
  93.                 #endregion
  94.             }
  95.             return lstDeCon.ToArray();
  96.         }

  97.         /// <summary>
  98.         /// 编码
  99.         /// </summary>
  100.         /// <param name="bytesCon"></param>
  101.         /// <returns></returns>
  102.         public static byte[] Encode(byte[] bytesCon)
  103.         {
  104.             byte[] bytesDic = new byte[256];//字典

  105.             #region 统计字节使用频率,生成字典
  106.             for (int i = 0; i <= 0xFF; i++)
  107.             {
  108.                 bytesDic[i] = (byte)i;
  109.             }
  110.             int[] intFreq = new int[256];
  111.             foreach (byte byteValue in bytesCon)
  112.             {
  113.                 intFreq[byteValue]++;//出现次数累计
  114.             }
  115.             Array.Sort(intFreq, bytesDic);//按频率排序
  116.             Array.Reverse(bytesDic);//倒序(从高到低)

  117.             #endregion

  118.             //Array.Sort(bytesDic);

  119.             StringBuilder sbConBinary = new StringBuilder(); //索引号二进制

  120.             #region 分解索引号二进制
  121.             foreach (byte byteValue in bytesCon)
  122.             {
  123.                 StringBuilder strSeg1 = new StringBuilder();//分解段1
  124.                 StringBuilder strSeg2 = new StringBuilder();//分解段2
  125.                 //获得内容索引号
  126.                 int intIndex = Array.IndexOf(bytesDic, byteValue);
  127.                
  128.                 //分解索引号二进制
  129.                 string strBinary = Convert.ToString(intIndex, 2);
  130.                 if (Regex.IsMatch(strBinary, "^1+[        DISCUZ_CODE_0        ]quot;, RegexOptions.Multiline))//全是1,如111->110+001
  131.                 {
  132.                     for (int i = 0; i < strBinary.Length - 1; i++)
  133.                     {
  134.                         strSeg1.Append("1");
  135.                         strSeg2.Append("0");
  136.                     }
  137.                     strSeg1.Append("0");
  138.                     strSeg2.Append("1");
  139.                 }
  140.                 else if (Regex.IsMatch(strBinary, "^1*0[        DISCUZ_CODE_0        ]quot;, RegexOptions.Multiline))//全是1结尾0,如1110->1110+0000
  141.                 {
  142.                     strSeg1.Append(strBinary);
  143.                     for (int i = 0; i < strBinary.Length; i++)
  144.                     {
  145.                         strSeg2.Append("0");
  146.                     }
  147.                 }
  148.                 else //seg1为总长度-2个1,加一个0,减去seg1对应数值为seg2,如1001011->111110+001101
  149.                 {
  150.                     for (int i = 0; i < strBinary.Length - 2; i++)
  151.                     {
  152.                         strSeg1.Append("1");
  153.                     }
  154.                     strSeg1.Append("0");
  155.                     int intSeg1 = Convert.ToInt32(strSeg1.ToString(), 2);
  156.                     int intSeg2 = intIndex - intSeg1;
  157.                     strSeg2.Append(Convert.ToString(intSeg2, 2).PadLeft(strSeg1.Length,'0'));
  158.                 }
  159.                
  160.                 sbConBinary.Append(strSeg1);
  161.                 sbConBinary.Append(strSeg2);
  162.             }
  163.             #endregion

  164.             List<byte> lstEnConIndex = new List<byte>(); //压缩后的索引

  165.             #region 将连续的二制转为bytes,得到压缩后的内容
  166.             string strConBinary = sbConBinary.ToString();
  167.             int intPos = 0;
  168.             while (intPos < strConBinary.Length)
  169.             {
  170.                 //8位一个字节
  171.                 StringBuilder strBinaryCon = new StringBuilder();

  172.                 for (int i = 0; i < 8; i++)
  173.                 {
  174.                     if (intPos > strConBinary.Length - 1)
  175.                     {
  176.                         strBinaryCon.Append("0");
  177.                     }
  178.                     else
  179.                     {
  180.                         strBinaryCon.Append(strConBinary[intPos]);
  181.                     }
  182.                     intPos++;
  183.                 }
  184.                 lstEnConIndex.Add(Convert.ToByte(strBinaryCon.ToString(), 2));
  185.             }
  186.             #endregion

  187.             List<byte> lstEnCon = new List<byte>(); //文件内容

  188.             #region 组合文件全部内容
  189.             //头16字节 LS11格式
  190.             lstEnCon.AddRange(new byte[] { 0x4C, 0x53, 0x31, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 });
  191.             //256位字典
  192.             lstEnCon.AddRange(bytesDic);
  193.             //内容压缩后长度
  194.             byte[] bytesLen1 = BitConverter.GetBytes(lstEnConIndex.Count);
  195.             Array.Reverse(bytesLen1);//反序
  196.             lstEnCon.AddRange(bytesLen1);
  197.             //内容解压后长度
  198.             byte[] bytesLen2 = BitConverter.GetBytes(bytesCon.Length);
  199.             Array.Reverse(bytesLen2);//反序
  200.             lstEnCon.AddRange(bytesLen2);
  201.             //文件起始位置
  202.             lstEnCon.AddRange(new byte[] { 0x00, 0x00, 0x01, 0x20 });
  203.             //4个0
  204.             lstEnCon.AddRange(new byte[] { 0x00, 0x00, 0x00, 0x00 });
  205.             //压缩后的正文
  206.             lstEnCon.AddRange(lstEnConIndex);
  207.             #endregion

  208.             return lstEnCon.ToArray();
  209.         }
  210.     }
  211. }
复制代码


附件: 你需要登录才可以下载或查看附件。没有帐号?注册

使用道具 举报

帖子
170
精华
0
积分
85
金钱
1897
荣誉
0
人气
0
评议
0
发表于 2015-1-1 15:47:04 |显示全部楼层
这东西用C会快很多,楼主很牛,能找出算法不容易。

使用道具 举报

帖子
3627
精华
0
积分
2216
金钱
12372
荣誉
38
人气
228
评议
0
发表于 2015-1-4 20:46:15 |显示全部楼层
我手里还有一个VB编写的压缩解压程序。
修改san6的文本就靠它解压压缩了。
附件: 你需要登录才可以下载或查看附件。没有帐号?注册
  人杰惟追古解良,士民争拜汉云长。
  桃园一日兄和弟,俎豆千秋帝与王。
  气挟风雷无匹敌,志垂日月有光芒。
  至今庙貌盈天下,古木寒鸦几夕阳。

使用道具 举报

帖子
3627
精华
0
积分
2216
金钱
12372
荣誉
38
人气
228
评议
0
发表于 2015-1-4 20:48:14 |显示全部楼层
另外,LS11和LS12是相似的,估计也能编码解码。
我找到的那个VB程序可以解压ls12,压缩回去是ls11,但是游戏里面也能用。
  人杰惟追古解良,士民争拜汉云长。
  桃园一日兄和弟,俎豆千秋帝与王。
  气挟风雷无匹敌,志垂日月有光芒。
  至今庙貌盈天下,古木寒鸦几夕阳。

使用道具 举报

帖子
92
精华
0
积分
169
金钱
1374
荣誉
12
人气
32
评议
0
发表于 2015-1-5 20:04:44 |显示全部楼层
關雲長 发表于 2015-1-4 20:48
另外,LS11和LS12是相似的,估计也能编码解码。
我找到的那个VB程序可以解压ls12,压缩回去是ls11,但是游 ...

惭愧,没研究过LS12算法,LS11也是参考轩辕论坛的文章的。

使用道具 举报

帖子
3627
精华
0
积分
2216
金钱
12372
荣誉
38
人气
228
评议
0
发表于 2015-1-6 17:25:22 |显示全部楼层
8403 发表于 2015-1-5 20:04
惭愧,没研究过LS12算法,LS11也是参考轩辕论坛的文章的。

有没有C/C++源码的?
  人杰惟追古解良,士民争拜汉云长。
  桃园一日兄和弟,俎豆千秋帝与王。
  气挟风雷无匹敌,志垂日月有光芒。
  至今庙貌盈天下,古木寒鸦几夕阳。

使用道具 举报

帖子
92
精华
0
积分
169
金钱
1374
荣誉
12
人气
32
评议
0
发表于 2015-1-8 08:54:47 |显示全部楼层
關雲長 发表于 2015-1-6 17:25
有没有C/C++源码的?

不会写C/C++

使用道具 举报

帖子
1
精华
0
积分
1
金钱
0
荣誉
0
人气
0
评议
0
发表于 2018-2-9 21:06:13 来自手机 |显示全部楼层
伸手党多谢

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

手机版|Archiver|游侠NETSHOW论坛 ( 苏ICP备2023007791号 )

GMT+8, 2024-3-29 19:58 , Processed in 0.304050 second(s), 11 queries , Gzip On, Memcache On.

Powered by Discuz! X2

© 2001-2011 Comsenz Inc.

分享到