mpq操作库

这是一个关于如何使用C#操作MPQ档案的类库,包括打开、创建、添加、删除、重命名档案中的文件,以及解压、获取档案信息等功能。类库通过DllImport导入了StormLib.dll中的API来实现MPQ档案的管理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;
using System.Reflection;

/// <summary>
/// 档案
/// </summary>
public class Archive : IDisposable
{
    #region api声明
    [DllImport("StormLib.dll", EntryPoint = "SFileAddFileEx", SetLastError = true)]
    private static extern bool SFileAddFileEx(IntPtr hMpq, string szFileName, string szArchivedName, uint dwFlags, uint dwCompression, uint CompressionNext);
    [DllImport("StormLib.dll", EntryPoint = "SFileCloseArchive", SetLastError = true)]
    private static extern bool SFileCloseArchive(IntPtr hMpq);
    [DllImport("StormLib.dll", EntryPoint = "SFileCompactArchive", SetLastError = true)]
    private static extern bool SFileCompactArchive(IntPtr hMpq, string szListFile, bool bReserved);
    [DllImport("StormLib.dll", EntryPoint = "SFileCreateArchive", SetLastError = true)]
    private static extern bool SFileCreateArchive(string szMpqName, uint dwFlags, uint dwMaxFileCount, out IntPtr phMPQ);
    [DllImport("StormLib.dll", EntryPoint = "SFileGetFileInfo", SetLastError = true)]
    private static extern bool SFileGetFileInfo(IntPtr hMpqOrFile, uint dwInfoType, StringBuilder pvFileInfo, int cbFileInfo, out uint pcbLengthNeeded);
    [DllImport("StormLib.dll", EntryPoint = "SFileGetFileInfo", SetLastError = true)]
    private static extern bool SFileGetFileInfo(IntPtr hMpqOrFile, uint dwInfoType, ref uint pvFileInfo, uint cbFileInfo, out uint pcbLengthNeeded);
    [DllImport("StormLib.dll", EntryPoint = "SFileHasFile", SetLastError = true)]
    private static extern bool SFileHasFile(IntPtr hMpq, string szFileName);
    [DllImport("StormLib.dll", EntryPoint = "SFileOpenArchive", SetLastError = true)]
    private static extern bool SFileOpenArchive(string szMpqName, uint dwPriority, uint dwFlags, out IntPtr phMPQ);
    [DllImport("StormLib.dll", EntryPoint = "SFileRemoveFile", SetLastError = true)]
    private static extern bool SFileRemoveFile(IntPtr hMpq, string szFileName, uint dwSearchScope);
    [DllImport("StormLib.dll", EntryPoint = "SFileRenameFile", SetLastError = true)]
    private static extern bool SFileRenameFile(IntPtr hMpq, string szOldFileName, string szNewFileName);
    #endregion

    #region 常量
    /// <summary>
    /// 压缩
    /// </summary>
    private const uint MPQ_FILE_COMPRESS = 0x200;
    /// <summary>
    /// 允许覆盖
    /// </summary>
    private const uint MPQ_FILE_REPLACEEXISTING = 0x80000000;
    /// 读取mpq名字
    /// </summary>
    private const uint SFILE_INFO_ARCHIVE_NAME = 1;
    /// <summary>
    /// 读取mpq大小
    /// </summary>
    private const uint SFILE_INFO_ARCHIVE_SIZE = 2;
    #endregion

    #region 属性
    /// <summary>
    /// 档案的句柄
    /// </summary>
    private IntPtr hArchive = IntPtr.Zero;
    public IntPtr HArchive
    {
        get
        {
            return this.hArchive;
        }
    }
    /// <summary>
    /// listfile的列表
    /// </summary>
    private List<string> listfile = new List<string>();
    public List<string> Listfile
    {
        get
        {
            return this.listfile;
        }
    }
    #endregion

    #region 构造函数
    /// <summary>
    /// 构造函数
    /// </summary>
    /// <param name="archivePath">档案路径</param>
    public Archive(string archivePath)
    {
        if (File.Exists(archivePath) == true)// 档案文件存在,尝试打开文件
        {
            if (SFileOpenArchive(archivePath, 0, 0, out this.hArchive) == true)// 打开成功
            {
                if (FileExists("(listfile)") == true)// 存在文件列表
                {
                    if (LoadListFile() == true)// 加载文件列表成功
                    {
                    }
                    else// 加载文件列表失败
                    {
                        this.Dispose();
                        MessageBox.Show("读取文件列表失败");
                    }
                }
                else// 不存在文件列表
                {
                    this.Dispose();
                    MessageBox.Show("找不到文件列表");
                }
            }
            else// 打开失败
            {
                this.hArchive = IntPtr.Zero;
                this.ShowError();
            }
        }
        else// 档案文件不存在,创建档案文件
        {
            if (SFileCreateArchive(archivePath, 0, 0x80000, out this.hArchive) == true)// 创建成功
            {
            }
            else// 创建失败
            {
                this.hArchive = IntPtr.Zero;
                this.ShowError();
            }
        }
    }
    #endregion

    #region 公开方法
    /// <summary>
    /// 添加文件到档案中
    /// </summary>
    /// <param name="localPath">本地路径</param>
    /// <param name="archivePath">档案中需显示的路径</param>
    /// <returns>成功则返回true,失败返回false</returns>
    public bool AddFileWithPath(string localPath, string archivePath)
    {
        if (SFileAddFileEx(this.hArchive, localPath, archivePath, MPQ_FILE_COMPRESS | MPQ_FILE_REPLACEEXISTING, 0, 0) == true)// 添加成功
        {
            listfile.Add(archivePath);
            return true;
        }
        else// 添加失败
        {
            ShowError();
            return false;
        }
    }

    /// <summary>
    /// 关闭档案
    /// </summary>
    public void Dispose()
    {
        if (this.hArchive != IntPtr.Zero)// 之前打开或创建成功
        {
            if (SFileCloseArchive(this.hArchive) == true)// 关闭成功
            {
                this.hArchive = IntPtr.Zero;
            }
            else// 关闭失败
            {
                this.hArchive = IntPtr.Zero;
                this.ShowError();
            }
        }
    }

    /// <summary>
    /// 解压文件
    /// </summary>
    /// <param name="filePath">文件在档案中的路径</param>
    /// <param name="localPath">本地路径</param>
    /// <returns>成功则返回true,失败返回flase</returns>
    public bool ExtractFile(string filePath, string localPath)
    {
        using (ArchiveFile file = this.OpenFile(filePath))// 打开文件
        {
            uint fileSize = file.GetSize();// 获取文件大小
            if (fileSize == uint.MaxValue)// 获取失败
            {
                return false;
            }
            else// 获取成功
            {
                if (fileSize == 0)// 空文件
                {
                    try
                    {
                        File.Create(localPath).Dispose();// 创建空文件
                    }
                    catch (Exception ex)
                    {
                        MessageBox.Show("创建文件失败\n" + ex.ToString());
                        return false;
                    }
                    return true;
                }
                else// 非空文件
                {
                    byte[] bytes = file.ReadAllBytes();// 读取文件
                    if (bytes.Length != 0)// 读取成功
                    {
                        FileStream fs;
                        try
                        {
                            fs = File.Create(localPath);// 创建本地文件
                        }
                        catch
                        {
                            return false;// 创建本地文件失败
                        }
                        fs.Write(bytes, 0, bytes.Length);// 写入到本地文件
                        fs.Dispose();
                        return true;
                    }
                    else// 读取失败
                    {
                        return false;
                    }
                }
            }
        }
    }

    /// <summary>
    /// 档案中是否存在文件
    /// </summary>
    /// <param name="filePath">文件路径</param>
    /// <returns>存在则返回true,否则返回false</returns>
    public bool FileExists(string filePath)
    {
        if (SFileHasFile(this.hArchive, filePath) == true)// 存在
        {
            return true;
        }
        else// 不存在
        {
            return false;
        }
    }

    /// <summary>
    /// 获取档案名字
    /// </summary>
    /// <returns>成功返回档案的名字,失败则返回空字符串</returns>
    public string GetName()
    {
        StringBuilder sb = new StringBuilder(string.Empty, 260);
        uint nameLength;// 读取出的名字长度
        if (SFileGetFileInfo(this.hArchive, SFILE_INFO_ARCHIVE_NAME, sb, sb.Capacity, out nameLength) == true)// 获取成功
        {
            return sb.ToString();
        }
        else// 获取失败
        {
            ShowError();
            return string.Empty;
        }
    }

    /// <summary>
    /// 获取档案大小
    /// </summary>
    /// <returns>成功返回档案的大小(字节),失败则返回0</returns>
    public uint GetSize()
    {
        uint archiveSize = 0, length;
        if (SFileGetFileInfo(this.hArchive, SFILE_INFO_ARCHIVE_SIZE, ref archiveSize, sizeof(uint), out length) == true)// 获取成功
        {
            return archiveSize;
        }
        else// 获取失败
        {
            ShowError();
            return 0;
        }
    }

    /// <summary>
    /// 打开档案中的一个文件
    /// </summary>
    /// <param name="filePath">文件路径</param>
    /// <returns>文件句柄</returns>
    public ArchiveFile OpenFile(string filePath)
    {
        return new ArchiveFile(this.hArchive, filePath);
    }

    /// <summary>
    /// 重压缩档案
    /// </summary>
    /// <returns>成功则返回true,否则返回false</returns>
    public bool ReCompress()
    {
        if (SFileCompactArchive(this.hArchive, null, false) == true)// 重压缩成功
        {
            return true;
        }
        else// 重压缩失败
        {
            ShowError();
            return false;
        }
    }

    /// <summary>
    /// 删除档案中的文件
    /// </summary>
    /// <param name="filePath">文件路径</param>
    /// <returns>成功则返回true,否则返回false</returns>
    public bool RemoveFile(string filePath)
    {
        if (FileExists(filePath) == true)// 文件存在
        {
            if (SFileRemoveFile(this.hArchive, filePath, 0) == true)// 删除成功
            {
                listfile.Remove("filePath");
                return true;
            }
            else// 删除失败
            {
                ShowError();
                return false;
            }
        }
        else// 文件不存在
        {
            return false;
        }
    }

    /// <summary>
    /// 重命名档案中的文件
    /// </summary>
    /// <param name="oldPath">文件路径</param>
    /// <param name="newPath">新的文件路径</param>
    /// <returns>成功则返回true,否则返回false</returns>
    public bool RenameFile(string oldPath, string newPath)
    {
        if (FileExists(oldPath) == true)// 文件存在
        {
            if (FileExists(newPath) == false)// 新文件不存在
            {
                if (SFileRenameFile(this.hArchive, oldPath, newPath))// 重命名成功
                {
                    listfile.Remove(oldPath);
                    listfile.Add(newPath);
                    return true;
                }
                else// 重命名失败
                {
                    ShowError();
                    return false;
                }
            }
            else// 已存在新文件
            {
                return false;
            }
        }
        else// 文件不存在
        {
            return false;
        }
    }
    #endregion

    #region 私有方法
    /// <summary>
    /// 获取listfile的内容
    /// </summary>
    /// <returns>成功则返回true,失败返回false</returns>
    private bool LoadListFile()
    {
        using (ArchiveFile hlistfile = this.OpenFile("(listfile)"))// 打开listfile
        {
            if (hlistfile.HFile.ToInt32() == 0)// 打开失败
            {
                return false;
            }
            else// 打开成功
            {
                string[] str = hlistfile.ReadAllLines();
                if (str.Length == 0)// 读取失败
                {
                    return false;
                }
                else// 读取成功
                {
                    this.listfile = new List<string>(str);// 将文件列表保存
                    return true;
                }
            }
        }
    }
    #endregion

    #region Debug
    private int LastWin32Error = 0;
    private void ShowError()
    {
        LastWin32Error = Marshal.GetLastWin32Error();
        StackTrace ss = new StackTrace(true);
        MethodBase mb = ss.GetFrame(1).GetMethod();
        MessageBox.Show("function:" + mb.Name + "\nerror:" + LastWin32Error.ToString());
    }
    #endregion
}

/// <summary>
/// 档案文件
/// </summary>
public class ArchiveFile : IDisposable
{
    #region api声明
    [DllImport("StormLib.dll", EntryPoint = "SFileCloseFile", SetLastError = true)]
    private static extern bool SFileCloseFile(IntPtr hFile);
    [DllImport("StormLib.dll", EntryPoint = "SFileGetFileName", SetLastError = true)]
    private static extern bool SFileGetFileName(IntPtr hFile, StringBuilder szFileName);
    [DllImport("StormLib.dll", EntryPoint = "SFileGetFileSize", SetLastError = true)]
    private static extern uint SFileGetFileSize(IntPtr hFile, out uint pdwFileSizeHigh);
    [DllImport("StormLib.dll", EntryPoint = "SFileOpenFileEx", SetLastError = true)]
    private static extern bool SFileOpenFileEx(IntPtr hMPQ, string szFileName, uint dwSearchScope, out IntPtr phFile);
    [DllImport("StormLib.dll", EntryPoint = "SFileReadFile", SetLastError = true)]
    private static extern bool SFileReadFile(IntPtr hFile, byte[] lpBuffer, uint dwToRead, out uint pdwRead, ref OVERLAPPED lpOverlapped);
    [DllImport("StormLib.dll", EntryPoint = "SFileRenameFile", SetLastError = true)]
    private static extern bool SFileRenameFile(IntPtr hMpq, string szOldFileName, string szNewFileName);
    #endregion

    #region 常量
    #endregion

    #region 属性
    /// <summary>
    /// 文件句柄
    /// </summary>
    private IntPtr hFile = IntPtr.Zero;
    public IntPtr HFile
    {
        get
        {
            return this.hFile;
        }
    }
    /// <summary>
    /// 维持堆栈平衡
    /// </summary>
    private struct OVERLAPPED
    {
        public uint Internal;
        public uint InternalHigh;
        public uint Offset;
        public uint OffsetHigh;
        public IntPtr hEvent;
    }
    #endregion

    #region 构造函数
    /// <summary>
    /// 构造函数
    /// </summary>
    /// <param name="hArchive">档案的句柄</param>
    /// <param name="filePath">文件的路径</param>
    public ArchiveFile(IntPtr hArchive, string filePath)
    {
        if (SFileOpenFileEx(hArchive, filePath, 0, out this.hFile) == true)// 打开成功
        {
        }
        else// 打开失败
        {
            this.hFile = IntPtr.Zero;
            this.ShowError();
        }
    }
    #endregion

    #region 公开方法
    /// <summary>
    /// 关闭文件
    /// </summary>
    public void Dispose()
    {
        if (this.hFile != IntPtr.Zero)// 之前打开成功
        {
            if (SFileCloseFile(this.hFile) == true)// 关闭成功
            {
                this.hFile = IntPtr.Zero;
            }
            else// 关闭失败
            {
                this.hFile = IntPtr.Zero;
                this.ShowError();
            }
        }
    }

    /// <summary>
    /// 获取文件名
    /// </summary>
    /// <returns>文件的名字</returns>
    public string GetName()
    {
        StringBuilder sb = new StringBuilder(260);
        if (SFileGetFileName(this.hFile, sb) == true)// 获取成功
        {
            return sb.ToString();
        }
        else// 获取失败
        {
            return string.Empty;
        }
    }

    /// <summary>
    /// 获取文件大小
    /// </summary>
    /// <returns>成功则返回文件总共多少字节,否则返回SFILE_INVALID_SIZE(4294967295)无符号整数十进制最大值</returns>
    public uint GetSize()
    {
        uint fileSize, fileSizeHigh;
        fileSize = SFileGetFileSize(this.hFile, out fileSizeHigh);
        if (fileSize != uint.MaxValue)// 获取成功
        {
            return fileSize;
        }
        else// 获取失败
        {
            this.ShowError();
            return fileSize;
        }
    }

    /// <summary>
    /// 读取文件中的所有字节
    /// </summary>
    /// <returns>成功则返回文件内容,失败返回空字节数组</returns>
    public byte[] ReadAllBytes()
    {
        uint fileSize = GetSize();
        if (fileSize == uint.MaxValue)// 获取文件大小失败
        {
            return new byte[0];
        }
        else// 获取文件大小成功
        {
            if (fileSize == 0)// 文件长度为0,即为空文件
            {
                return new byte[0];
            }
            else// 文件长度不为0
            {
                byte[] buffer = new byte[fileSize];// 缓冲
                uint readLength;// 读取出的长度
                OVERLAPPED overlapped = new OVERLAPPED();// 维持堆栈平衡
                if (SFileReadFile(this.hFile, buffer, fileSize, out readLength, ref overlapped) == true)// 读取成功
                {
                    return buffer;
                }
                else// 读取失败
                {
                    this.ShowError();
                    return new byte[0];
                }
            }
        }
    }

    /// <summary>
    /// 以UTF-8编码读取文件中的所有行
    /// </summary>
    /// <returns>成功则返回文件所有行,失败则返回空字符串数组</returns>
    public string[] ReadAllLines()
    {
        string str = ReadAllText();// 读取内容
        if (str == string.Empty)// 读取失败
        {
            return new string[0];
        }
        else// 读取成功
        {
            return str.Split('\r', '\n');
        }
    }

    /// <summary>
    /// 以UTF-8编码读取文件中的所有内容
    /// </summary>
    /// <returns>成功则返回文件内容,失败返回空字符串</returns>
    public string ReadAllText()
    {
        byte[] bytes = this.ReadAllBytes();
        if (bytes.Length != 0)// 读取成功
        {
            return Encoding.UTF8.GetString(bytes);// 解码
        }
        else// 读取失败
        {
            return string.Empty;
        }
    }
    #endregion

    #region 私有方法
    #endregion

    #region Debug
    private int LastWin32Error = 0;
    private void ShowError()
    {
        LastWin32Error = Marshal.GetLastWin32Error();
        StackTrace ss = new StackTrace(true);
        MethodBase mb = ss.GetFrame(1).GetMethod();
        MessageBox.Show("function:" + mb.Name + "\nerror:" + LastWin32Error.ToString());
    }
    #endregion
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值