在好久以前,我就想写一个像暴雪的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我~~