如何使用C#从.pak归档文件加载图片到WinForms的PictureBox控件
实现思路与代码示例
首先明确:.pak是一类归档格式的统称,没有统一标准,以下实现基于游戏领域最常用的id Tech系列标准PAK格式(头标识为PACK),如果你使用的是自定义结构的.pak文件,只需调整结构体定义与读取逻辑即可。
核心步骤
- 第一步:解析PAK文件头,获取文件表的偏移量与大小
- 第二步:遍历文件表,匹配目标图片的文件名,拿到该文件在PAK中的偏移与大小
- 第三步:读取对应区块的二进制数据,转为Image对象后赋值给PictureBox控件
代码实现
首先定义PAK结构的映射类:
using System.Runtime.InteropServices; using System.IO; using System.Drawing; // PAK文件头结构 [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] public struct PakHeader { [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 4)] public string Id; // 固定为"PACK" public int FileTableOffset; // 文件表相对于文件开头的偏移 public int FileTableSize; // 文件表总大小 } // PAK文件条目结构 [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] public struct PakFileEntry { [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 56)] public string FileName; // 文件名,最多56字符 public int FileOffset; // 文件在PAK中的偏移 public int FileSize; // 文件大小 }
然后是读取PAK中图片的工具方法:
public Image LoadImageFromPak(string pakFilePath, string targetImageFileName) { using var fs = new FileStream(pakFilePath, FileMode.Open, FileAccess.Read); using var reader = new BinaryReader(fs); // 读取PAK头 var headerBytes = reader.ReadBytes(Marshal.SizeOf<PakHeader>()); var header = BytesToStruct<PakHeader>(headerBytes); if (header.Id != "PACK") { throw new InvalidDataException("非标准PAK格式文件"); } // 读取整个文件表 fs.Seek(header.FileTableOffset, SeekOrigin.Begin); int entryCount = header.FileTableSize / Marshal.SizeOf<PakFileEntry>(); PakFileEntry? targetEntry = null; for (int i = 0; i < entryCount; i++) { var entryBytes = reader.ReadBytes(Marshal.SizeOf<PakFileEntry>()); var entry = BytesToStruct<PakFileEntry>(entryBytes); if (entry.FileName.Equals(targetImageFileName, StringComparison.OrdinalIgnoreCase)) { targetEntry = entry; break; } } if (!targetEntry.HasValue) { throw new FileNotFoundException("PAK中未找到目标图片", targetImageFileName); } // 读取图片二进制数据 fs.Seek(targetEntry.Value.FileOffset, SeekOrigin.Begin); var imageBytes = reader.ReadBytes(targetEntry.Value.FileSize); // 二进制转Image对象 using var ms = new MemoryStream(imageBytes); return Image.FromStream(ms); } // 字节数组转结构体的辅助方法 private T BytesToStruct<T>(byte[] bytes) where T : struct { var handle = GCHandle.Alloc(bytes, GCHandleType.Pinned); try { return Marshal.PtrToStructure<T>(handle.AddrOfPinnedObject()); } finally { handle.Free(); } }
最后调用方法赋值给PictureBox:
// 示例调用,替换为你的PAK路径和图片文件名 try { var image = LoadImageFromPak(@"C:\test\resource.pak", "ui/start_bg.png"); pictureBox1.Image = image; // 可选设置图片显示模式 pictureBox1.SizeMode = PictureBoxSizeMode.Zoom; } catch (Exception ex) { MessageBox.Show($"加载失败:{ex.Message}"); }
常见适配说明
- 如果你使用的是.NET Framework低于4.5的版本,将代码中
Marshal.SizeOf<T>()替换为Marshal.SizeOf(typeof(T))即可 - 如果你的PAK文件是压缩打包的,读取到文件二进制后需要先调用对应解压算法(比如zlib、lz4)解压后再转Image
- 如果是自定义结构的PAK,只需修改
PakHeader和PakFileEntry的结构定义匹配你的归档规则即可 - 若读取的图片是引擎专属格式(比如dds、tga等非GDI+原生支持的格式),需要额外引入对应格式的解析库转换后再赋值
内容的提问来源于stack exchange,提问作者Technika číslo 1 s.r.o
相关产品推荐
相关产品推荐

