[c#]自己制作类似winrar的打包程序(1)
在好久以前,我就想写一个像暴雪的MPQ的文件结构,由于MPQ是不开源的,在网上找的资料又不是很全,像《MPQ技术内幕》只有那么一点,而且是C++,所以说令我很烦恼,但是捏,我在偶然间发现了一个很简单的文件结构,于是乎我就灵光一现,做了一个小的音乐播放器.....播放自己特有“格式”的音乐。
利用了以下插件:
/* ICSharpCode.SharpZipLib libzplay */
目前为止可以实现用SharpZipLib来压缩/解压缩单个文件,用LibZPlay来播放OGG格式的文件(大家可以去google一下LibZPlay,灰常强大!)。首先,我们要有自己的文件结构,源码如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.IO; using System.Runtime.Serialization.Formatters.Binary; namespace DSTFS { public class DSTFS { private PackFile _pf; private List<string> _pathList = new List<string>(); #region 添加文件 /// <summary> /// 添加文件 /// </summary> /// <param name="source">要添加的文件的名字</param> public void AddSourceFile(string source) { if (File.Exists(source)) this._pathList.Add(source); else throw new FileNotFoundException(source); } #endregion 添加文件 #region 创建新文件 /// <summary> /// 创建一个新文件 /// </summary> /// <param name="path">要创建的文件完整路径</param> public void Build(string path) { using (FileStream fs = new FileStream(path, FileMode.Create, FileAccess.Write)) { BinaryWriter bw = new BinaryWriter(fs); bw.Write("DST_PackFile_V1"); bw.Write(this._pathList.Count); foreach (string f in this._pathList) { FileInfo fi = new FileInfo(f); bw.Write(fi.Length); fi = null; } foreach (string f in this._pathList) { bw.Write(Path.GetFileName(f)); } foreach (string f in this._pathList) { bw.Write(File.ReadAllBytes(f)); bw.Flush(); } } } #endregion 创建新文件 #region 读取文件 /// <summary> /// 读取文件 /// </summary> /// <param name="path">要读取的文件的完整路径</param> public void LoadPackFile(string path) { if (!File.Exists(path)) { throw new FileNotFoundException(path); } if (_pf != null) { _pf.Close(); _pf = null; } FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read); BinaryReader br = new BinaryReader(fs); if (br.ReadString() != "DST_PackFile_V1") { throw new InvalidCoalescentFileException("该文件不是有效的包文件"); } this._pf = new PackFile(fs, br); } #endregion 读取文件 #region Properties /// <summary> /// Properties /// </summary> public PackFile CurrentPackFile { get { return this._pf; } } #endregion Properties } /// <summary> /// 文件部分 /// </summary> public class PackFile { private FileStream _sourceFile; private BinaryReader _br; private long _contentStartPos; private int _fileCount; private List<long> _fileLengthList = new List<long>(); private List<string> _shortNameList = new List<string>(); internal PackFile(FileStream srcFile, BinaryReader br) { this._sourceFile = srcFile; _br = br; this._fileCount = _br.ReadInt32();//取文件数 for (int i = 1; i <= _fileCount; i++) { this._fileLengthList.Add(_br.ReadInt64()); } for (int i = 1; i <= _fileCount; i++) { this._shortNameList.Add(_br.ReadString()); } this._contentStartPos = _sourceFile.Position;//设置实体文件内容的起始位置 } public MemoryStream GetStream(int index) { return new MemoryStream(GetBytes(index)); } public byte[] GetBytes(int index) { long startPos = this._contentStartPos; for (int i = 0; i < index; i++) { startPos += this._fileLengthList[i]; } _sourceFile.Position = startPos; return _br.ReadBytes((int)_fileLengthList[index]); } public void OutputAllToDirectory(string dir) { if (!Directory.Exists(dir)) { Directory.CreateDirectory(dir); } for (int i = 0; i < _fileCount; i++) { File.WriteAllBytes(dir + this._shortNameList[i], GetBytes(i)); } } /// <summary> /// 以新名字输出某个内容到文件 /// </summary> /// <param name="index"></param> /// <param name="file"></param> public void OutputOneToFile(int index, string file) { File.WriteAllBytes(file, GetBytes(index)); } /// <summary> /// 用原始文件名输出某个内容到指定文件夹 /// </summary> /// <param name="index"></param> /// <param name="dir"></param> public void OutputOneToDirectory(int index, string dir) { string name = _shortNameList[index]; File.WriteAllBytes(Path.Combine(dir, name), GetBytes(index)); } internal void Close() { if (_sourceFile != null) { _br.Close(); _br = null; _sourceFile.Close(); _sourceFile = null; } } #region 属性 //源包文件 public string CurrentPackFile { get { return this._sourceFile.Name; } } public int FileCount { get { return this._fileCount; } } public string[] NameList { get { return this._shortNameList.ToArray(); } } #endregion } public class InvalidCoalescentFileException : Exception { public InvalidCoalescentFileException(string text) : base(text) { } } }
好了,我们就可以用这个结构来创造自己独特的文件格式了。
现在我们新建一个项目,引用这个类,首先在全局声明一个对象:
DSTPackFile.DSTPF dst = new DSTPackFile.DSTPF();
用以下代码创建一个文件:
dst.AddSourceFile("1.ogg"); dst.Build(textBox3.Text); //添加多个文件的话需要多次添加SourceFile,最后执行Build。
好了,我们现在有一个灰常特殊的音乐文件了,那么肿么播放捏?我们用LibZPlay来实现播放功能。但是,首先我们要把文件中的音乐读出来:
DSTPackFile.DSTPF dst = new DSTPackFile.DSTPF(); dst.LoadPackFile("c:\\1.DST"); var cf = dst.CurrentPackFile; var ms = dst.CurrentPackFile.GetStream(0); //我想把这个方法增加一个重载,不用数字编号了,直接用文件名就类似GetStream("1.ogg")一样,还在努力实现中....
因为我们把音乐读取到内存当中了,所以我们就用流的方式播放:
libZPlay.ZPlay player = new ZPlay(); long numBytes = ms.Length; System.IO.BinaryReader br = new System.IO.BinaryReader(ms); byte[] stream_data = null; stream_data = br.ReadBytes(System.Convert.ToInt32((int)(numBytes))); if (!(player.OpenStream(true, false, ref stream_data, System.Convert.ToUInt32(numBytes), TStreamFormat.sfOgg))) { MessageBox.Show(player.GetError(), string.Empty, MessageBoxButtons.OK, MessageBoxIcon.Error); } player.StartPlayback(); br.Close();
好了,我们就能听到音乐了。
说实话,播放流的这段代码是我google来的,因为头一次用LibZPlay,所以一直播放不了用SharpZipLib压缩过的文件,我已经解压缩到内存里了,但是就是播放不了,所以没用到压缩。
到现在我有一个设想:
1.这个文件结构只是最简单的,我想它不光能打包文件,还能把文件夹打包进去。
2.能用流的方式读出包文件里的东西,而且不需要太大的内存。
3.能实现压缩功能(在不借助第三方插件),把声音、图片等等的文件有单独的压缩算法,并且解压的时候不是很费时。
4.。。。。。。。。
其实还有好多....只是现在想不起来了,等有新进展再接着写。要是各位大牛有神马批评、指导。请PM我~~