[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我~~

原文地址:https://www.cnblogs.com/Hsppl/p/CSharp_PackFile_1.html