{ SSTP Bottle Surface Preview Plug-In for "SVG" (C)2003 naru, Mikage Sawatari / SSTP Bottle } library SVG; {$R *.res} uses Windows, Classes, SysUtils, Graphics, IniFiles, Dialogs; const SurfaceWidth = 48; SurfaceHeight = 64; ConfigFile = 'SVG.ini'; var MyPath: String; GhostFile: String; Ghost2File: TStringList; // DLLロード時、およびDLL再初期化時に呼ばれる。 // 読み込まれた画像は、Bottle Client側で「サーフィス再読み込み」を // 明示的に指定されない限りは Bottle Client 側でキャッシュされるので、 // DLLで画像のキャッシュなどを考える必要は基本的にない。 // とはいえサーフィス定義ファイルなどを先に解析しておく必要があるなら、 // ここで読み込んでおくと良い。PathにDLLのパスが渡される。 procedure Load(Path: PChar); cdecl; var Ini: TIniFile; begin MyPath := Path; Ini := TIniFile.Create(MyPath + ConfigFile); try GhostFile := Ini.ReadString('SVG', 'GhostFile', ''); finally Ini.Free; end; if not FileExists(GhostFile) then ShowMessage('SVG.dll Warning: Ghost file is not specified'); Ghost2File := TStringList.Create; end; // DLLアンロード時に呼ばれる。 // たぶんやることはない。 procedure Unload; cdecl; begin Ghost2File.Free; end; // DLLの名前およびバージョンを返す。 // DLLNameにはDLLの名称や作者、DLL本体のバージョンなどをを簡潔に指定する。 // NameLenバイトを超えてはならない。 // versionにはとりあえず1を返すこと。versionはSSTP Bottle Clientとの // 通信のインターフェースのバージョンであってDLLのバージョンとかではないことに // 注意(Bottle ClientはDLLのバージョン番号なぞ興味ないので)。 // CanConfigureは、Configureを呼ばれてやることがあるかどうかを返す。 function GetVersion(DLLName: PChar; NameLen: integer; var Version: integer; var CanConfigure: boolean): integer; cdecl; const ThisDLL = 'SVG Surface Loader Ver. 1.2'; begin Version := 1; CanConfigure := true; StrLCopy(DLLName, ThisDLL, NameLen); Result := Length(ThisDLL) + 1; end; function LoadDefinitionFile(Ghost: String; Surface: integer; FileName: String; out Pos: integer): String; var Lines, ALine, SurfaceMap: TStringList; i, j, p, sur, oldpos: integer; posstr: String; begin // 内部関数。ゴースト定義ファイルを解析して、 // 目的のゴーストの含まれたファイルかどうか判断する Lines := TStringList.Create; try Lines.LoadFromFile(FileName); ALine := TStringList.Create; try ALine.CommaText := Lines[0]; if (ALine[0] <> 'GHOST') then Exit; if (ALine[1] <> Ghost) then begin Ghost2File.Values[Ghost] := FileName; // 次からのショートカット Exit; end; // 目的のゴースト発見 oldpos := 0; SurfaceMap := TStringList.Create; try for i := 1 to Lines.Count-1 do begin SurfaceMap.CommaText := Lines[i]; for j := 0 to SurfaceMap.Count-1 do begin try p := System.Pos(':', SurfaceMap[j]); if p = 0 then Continue; sur := StrToInt(Copy(SurfaceMap[j], 1, p-1)); posstr := Copy(SurfaceMap[j], p+1, High(integer)); if posstr = '+' then Pos := oldpos + 1 else Pos := StrToInt(posstr); if sur = Surface then begin Result := ALine[4]; // BMPファイル名 Exit; end else oldpos := Pos; except on EConvertError do; // nothing. 単なるコメント行扱い end; end; end; finally SurfaceMap.Free; end; // BASICの場合の処理 if AnsiCompareText('BASIC', ALine[3]) = 0 then begin if (Surface >= 0) and (Surface <= 11) then begin Result := ALine[4]; // BMPファイル名 Pos := Surface; // BASICの場合は位置とサーフィス番号が一対一対応している Exit; end; end; finally ALine.Free; end; finally Lines.Free; end; end; function FetchBmpFileAndPosition(Ghost: String; Surface: integer; out Pos: integer): String; var Ghosts, AGhost: TStringList; i, dum: integer; Dir, DefFileName, BmpFileName: String; begin if Ghost2File.Values[Ghost] <> '' then // すでにそのゴーストのファイルを記憶してる begin DefFileName := Ghost2File.Values[Ghost]; BmpFileName := LoadDefinitionFile(Ghost, Surface, DefFileName, dum); if BmpFileName <> '' then begin Result := ExtractFilePath(DefFileName) + BmpFileName; Pos := dum; Exit; end; end; // 内部関数。ghost.txtを読み込む。 Dir := ExtractFilePath(GhostFile); Ghosts := TStringList.Create; try Ghosts.LoadFromFile(GhostFile); for i := 0 to Ghosts.Count-1 do begin AGhost := TStringList.Create; try AGhost.CommaText := Ghosts[i]; if AGhost[0] <> 'GHOST' then Continue; DefFileName := Dir + AGhost[3]; // 定義ファイルを読み込む BmpFileName := LoadDefinitionFile(Ghost, Surface, DefFileName, dum); if BmpFileName <> '' then begin Result := ExtractFilePath(DefFileName) + BmpFileName; Pos := dum; Break; end; finally AGhost.Free; end; end; finally Ghosts.Free; end; end; // Ghostで指定されるゴーストのSurface番のサーフィスイメージを実際に読み出す。 // Hで指定されているビットマップに書き出すこと。 function GetImage(Ghost: PChar; Surface: integer; H: HBITMAP): integer; cdecl; var Bmp, Bmp2: TBitmap; BmpFile: String; Position, x, y, nCol: integer; begin try BmpFile := FetchBmpFileAndPosition(Ghost, Surface, Position); if BmpFile = '' then // そのようなゴーストのそのようなサーフィスは見つからない begin Result := 1; Exit; end; except on Exception do // ファイルが見つかってない可能性がもっとも高い begin Result := 1; Exit; end; end; Bmp := TBitmap.Create; try Bmp.Handle := H; Bmp2 := TBitmap.Create; try Bmp2.LoadFromFile(BmpFile); nCol := Bmp2.Width div SurfaceWidth; x := SurfaceWidth * (Position mod nCol); y := SurfaceHeight * (Position div nCol); Bmp.Canvas.CopyRect( Rect(0, 0, Bmp.Width, Bmp.Height), Bmp2.Canvas, Rect(x, y, x+SurfaceWidth, y+SurfaceHeight) ); finally Bmp2.Free; end; finally Bmp.ReleaseHandle; Bmp.Free; end; Result := 0; end; // イメージの大きさを返す。 // 大抵の場合イメージの大きさはもとから分かっていると思うので、 // それなら単純に定数を返して差し支えない。 // イメージの大きさがゴーストやサーフィス番号によって変わるならそのように。 function GetImageSize(Ghost: PChar; Surface: integer; var w, h: integer): integer; cdecl; begin w := SurfaceWidth; h := SurfaceHeight; Result := 0; end; // DLL固有の設定を行う。 // procedure Configure; cdecl; var OpenDialog: TOpenDialog; Ini: TIniFile; begin try ShowMessage('Specify SSTP Viewer''s ghost file.'); OpenDialog := TOpenDialog.Create(nil); try OpenDialog.Filter := 'Ghost Definition File(ghost.txt)|ghost.txt|' + 'All Files(*.*)|*.*'; OpenDialog.FileName := GhostFile; if OpenDialog.Execute then begin GhostFile := OpenDialog.FileName; Ini := TIniFile.Create(MyPath + ConfigFile); try Ini.WriteString('SVG', 'GhostFile', GhostFile); finally Ini.Free; end; Ghost2File.Clear; end; finally OpenDialog.Free; end; except on E: Exception do ShowMessage(E.Message); end; end; exports Load, Unload, GetVersion, Configure, GetImage, GetImageSize; begin end.