📄 mirwil.pas
字号:
unit MirWil;
interface
uses
SysUtils, Windows, Graphics, DirectDraw, Assist, ObjFastBlt;
{------------------------------------------------------------------------------}
// WIL 常量定义
{------------------------------------------------------------------------------}
var
UseDIBSurface: Boolean = True; // 是否在创建 WIL Surface 时使用 DIB 绘制
// 如果直接使用 WIL 文件中的位图 Bits 会出现少量颜色
// 显示不正确,在传奇源代码中的结果也是如此。
{------------------------------------------------------------------------------}
// WIL 文件格式定义
{------------------------------------------------------------------------------}
type
// WIL 文件头格式 (56Byte)
PImageHeader = ^TImageHeader;
TImageHeader = record
Title: string[40]; // 库文件标题 'WEMADE Entertainment inc.'
ImageCount: Integer; // 图片数量
ColorCount: Integer; // 色彩数量
PaletteSize: Integer; // 调色板大小
end;
// WIL 图像信息 (注意, 没有 pack record)
PImageInfo = ^TImageInfo;
TImageInfo = record
Width: SmallInt; // 位图宽度
Height: SmallInt; // 位图高度
PX: SmallInt; // 未知,似乎不用也可
PY: SmallInt; // 未知,似乎不用也可
Bits: PByte; // 未使用, 实际从文件读出时,要少读 4 字节, 即是此值未读
end;
// WIX 索引文件头格式
PIndexHeader = ^TIndexHeader;
TIndexHeader = record
Title: string[40]; // 'WEMADE Entertainment inc.'
IndexCount: Integer; // 索引总数
end;
TRGBQuads = array[0..255] of TRGBQuad;
PRGBQuads = ^TRGBQuads;
{------------------------------------------------------------------------------}
// TWilFile class
{------------------------------------------------------------------------------}
// TWilFile
TWilFile = class(TObject)
private
FFileName: string; // WIL 文件名
FFileHandle: THandle; // 文件句柄
FFileMapping: THandle; // 文件映射句柄
FFilePointer: Pointer; // 文件内容指针(使用文件映射)
FIndexArr: array of Integer; // 图片索引数组(从 WIX 文件中读取)
FMainColorTable: PRGBQuads; // 调色板指针(直接指向 WIL 文件中)
FImageCount: Integer; // 图片数量
FSurfaces: array of TDxFastBlt;
procedure LoadIndexFile; // 读入 WIX 文件至 IndexArr 中
procedure CreateMapView; // 创建 WIL 文件映射
function GetSurfaces(AIndex: Integer): TDxFastBlt;
function GetImageInfo(AIndex: Integer): PImageInfo;
public
constructor Create(const AFileName: string);
destructor Destroy; override;
procedure SaveToFile(Index: Integer; const FileName: string);
// 已打开的 WIL 文件名
property FileName: string read FFileName;
// 主调色板指针
property MainColorTable: PRGBQuads read FMainColorTable;
property ImageCount: Integer read FImageCount;
// 图片的 Surface
property Surfaces[AIndex: Integer]: TDxFastBlt read GetSurfaces;
// 图片的信息
property ImageInfo[AIndex: Integer]: PImageInfo read GetImageInfo;
end;
implementation
{ TWilFile }
constructor TWilFile.Create(const AFileName: string);
begin
FFileName := AFileName;
// 读入图片索引文件
LoadIndexFile;
// 创建 WIL 文件的映射
CreateMapView;
// 读入 WIL 文件头数据
// 读入文件头中的图片数量, (图片数量已由 LoadIndexFile 设置, 这是由
// 于 Weapon.wix 中的数量错误导致的修改, savetime 2005.1.3)
// FImageCount := PImageHeader(FFilePointer)^.ImageCount;
// 设置 FSurfaces 数组大小
SetLength(FSurfaces, FImageCount);
// 保存调色板指针
FMainColorTable := IncPointer(FFilePointer, SizeOf(TImageHeader));
end;
procedure TWilFile.CreateMapView;
var
ContentFileName: string;
begin
// WIL 文件名
ContentFileName := FFileName + '.WIL';
// 打开 WIL 文件
FFileHandle := CreateFile(PChar(ContentFileName), GENERIC_READ, FILE_SHARE_READ, nil,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL or FILE_FLAG_RANDOM_ACCESS, 0);
if FFileHandle = INVALID_HANDLE_VALUE then
raise Exception.CreateFmt('打开文件 "%s" 失败!', [ContentFileName]);
// 创建文件映射对象
FFileMapping := CreateFileMapping(FFileHandle, nil, PAGE_READONLY, 0, 0, nil);
if FFileMapping = 0 then
begin
CloseHandle(FFileHandle);
raise Exception.CreateFmt('创建文件映射 "%s" 失败!', [ContentFileName]);
end;
// 映射文件至内存
FFilePointer := MapViewOfFile(FFileMapping, FILE_MAP_READ, 0, 0, 0);
if FFilePointer = nil then
begin
CloseHandle(FFileMapping);
CloseHandle(FFileHandle);
raise Exception.CreateFmt('映射文件 "%s" 失败!', [ContentFileName]);
end;
end;
destructor TWilFile.Destroy;
var
I: Integer;
begin
// 清除索引文件内容
SetLength(FIndexArr, 0);
// 清除 FSurfaces 数组
for I := 0 to Length(FSurfaces) - 1 do begin
if FSurfaces[I] <> nil then begin
FSurfaces[I].Free;
FSurfaces[I] := nil;
end;
end;
SetLength(FSurfaces, 0);
// 关闭文件映射及相关句柄
UnmapViewOfFile(FFilePointer);
CloseHandle(FFileMapping);
CloseHandle(FFileHandle);
inherited;
end;
procedure TWilFile.LoadIndexFile;
var
F: file;
IdxHeader: TIndexHeader;
NumRead: Integer;
IndexFileName: string;
begin
// 索引文件名
IndexFileName := FFileName + '.WIX';
// 打开索引文件
FileMode := fmOpenRead;
AssignFile(F, IndexFileName);
Reset(F, 1);
try
// 读索引文件头
BlockRead(F, IdxHeader, SizeOf(IdxHeader), NumRead);
if NumRead <> SizeOf(IdxHeader) then
raise Exception.CreateFmt('"%s" 文件头错误!', [IndexFileName]);
// 设置索引数组大小
SetLength(FIndexArr, IdxHeader.IndexCount);
// 读索引内存至数组
BlockRead(F, PInteger(FIndexArr)^, IdxHeader.IndexCount * 4, NumRead);
if NumRead = IdxHeader.IndexCount * 4 then
FImageCount := IdxHeader.IndexCount
else
// 由于 Weapon.wix 的索引文件头中的图片数与实际的索引个数不同, 为了兼容,
// 即不触发异常, 详细情况见此文件最上标注.
// raise Exception.CreateFmt('"%s" 文件内容错误!', [IndexFileName]);
FImageCount := NumRead div 4;
finally
CloseFile(F);
end;
end;
function TWilFile.GetSurfaces(AIndex: Integer): TDxFastBlt;
var
InfoPtr: PImageInfo; // 图像信息, 使用临时变量将加快速度
X, Y: Integer; // Surface 的行值
PBits: PByte; // 指向 Bits 的指针
cr, cg, cb: Byte;
PDBits: PByte;
Src: PByte;
begin
// 检查 AIndex 是否合法
if (AIndex < 0) or (AIndex >= FImageCount) then
raise Exception.Create('TWilFile.GetSurfaces 数组超界!');
// 如果索引位置的 FSurface 已经创建, 则直接返回该值
Result := FSurfaces[AIndex];
if FSurfaces[AIndex] <> nil then Exit;
// 否则创建新的 FSurface[AIndex]
InfoPtr := ImageInfo[AIndex];
FSurfaces[AIndex] := TDxFastBlt.Create;
FSurfaces[AIndex].Width := InfoPtr^.Width;
FSurfaces[AIndex].Height := InfoPtr^.Height;
FSurfaces[AIndex].PX := InfoPtr^.PX;
FSurfaces[AIndex].PY := InfoPtr^.PY;
PBits := IncPointer(InfoPtr, (SizeOf(TImageInfo) - 4) +
((InfoPtr^.Height - 1) * InfoPtr^.Width));
//8Bit转32Bit
for Y := 0 to InfoPtr^.Height - 1 do begin
PDBits := Pointer(Integer(FSurfaces[AIndex].PBits) + FSurfaces[AIndex].Pitch * Y);
Src := PBits;
for X := 0 to InfoPtr^.Width - 1 do begin
with FMainColorTable[PByte(Src)^] do begin
cr := rgbRed;
cg := rgbGreen;
cb := rgbBlue;
end;
PInteger(PDBits)^ := ((cr shl 16) and $FF0000) or ((cg shl 8) and $00FF00) or ((cb shr 0) and $0000FF);
Inc(PInteger(PDBits));
Inc(PByte(Src));
end;
Dec(PBits, InfoPtr^.Width);
end;
FSurfaces[AIndex].TransparentColor := clBlack;
Result := FSurfaces[AIndex];
end;
function TWilFile.GetImageInfo(AIndex: Integer): PImageInfo;
begin
// 检查 AIndex 是否合法
if (AIndex < 0) or (AIndex >= FImageCount) then
raise Exception.Create('TWilFile.GetImageInfo 数组超界!');
// 定位到图片位置
Result := IncPointer(FFilePointer, FIndexArr[AIndex]);
end;
procedure TWilFile.SaveToFile(Index: Integer; const FileName: string);
var
FileHeader: BITMAPFILEHEADER;
InfoHeader: BITMAPINFOHEADER;
ColorTableSize: Integer;
InfoPtr: PImageInfo;
PBits: PByte;
F: file;
begin
// 检查 AIndex 是否合法
if (Index < 0) or (Index >= FImageCount) then
raise Exception.Create('TWilFile.SaveToFile 数组超界!');
// 图片信息指针
InfoPtr := ImageInfo[Index];
// 色彩表内存大小
ColorTableSize := SizeOf(TRGBQuad) * 256;
// 位图文件头
FileHeader.bfType := MakeWord(Ord('B'), Ord('M'));
FileHeader.bfSize := SizeOf(FileHeader) + SizeOf(InfoHeader) + ColorTableSize +
InfoPtr^.Width * InfoPtr^.Height;
FileHeader.bfReserved1 := 0;
FileHeader.bfReserved2 := 0;
FileHeader.bfOffBits := SizeOf(FileHeader) + SizeOf(InfoHeader) + ColorTableSize;
// 位图信息头
InfoHeader.biSize := SizeOf(InfoHeader);
InfoHeader.biWidth := InfoPtr^.Width;
InfoHeader.biHeight := InfoPtr^.Height;
InfoHeader.biPlanes := 1;
InfoHeader.biBitCount := 8;
InfoHeader.biCompression := 0;
InfoHeader.biSizeImage := 0;
InfoHeader.biXPelsPerMeter := 0;
InfoHeader.biYPelsPerMeter := 0;
InfoHeader.biClrUsed := 0;
InfoHeader.biClrImportant := 0;
// 开始保存文件
FileMode := fmOpenWrite;
AssignFile(F, FileName);
Rewrite(F, 1);
try
BlockWrite(F, FileHeader, SizeOf(FileHeader));
BlockWrite(F, InfoHeader, SizeOf(InfoHeader));
if ColorTableSize > 0 then
BlockWrite(F, FMainColorTable^, ColorTableSize);
PBits := IncPointer(InfoPtr, SizeOf(TImageInfo) - 4);
BlockWrite(F, PBits^, InfoPtr^.Width * InfoPtr^.Height);
finally
CloseFile(F);
end;
end;
end.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -