OSDN Git Service

半角スペースを含むゴースト名がghost.txtに存在すると誤動作する問題の修正。
[winbottle/svg-dll.git] / SVG.dpr
diff --git a/SVG.dpr b/SVG.dpr
index a30ffbd..115100e 100644 (file)
--- a/SVG.dpr
+++ b/SVG.dpr
@@ -1,9 +1,14 @@
+{
+SSTP Bottle Surface Preview Plug-In for "SVG"
+(C)2003-2004 WinBottle Project / SSTP Bottle
+}
+
 library SVG;
 
 {$R *.res}
 
 uses
-  Windows, Classes, SysUtils, Graphics, IniFiles;
+  Windows, Classes, SysUtils, Graphics, IniFiles, Dialogs, PngImage, jpeg;
 
 const
   SurfaceWidth  = 48;
@@ -13,7 +18,10 @@ const
 var
   MyPath: String;
   GhostFile: String;
-  Ghost2File: TStringList;
+  Ghost2File: THashedStringList;
+
+  Pic: TPicture;       // \93Ç\82Ý\8eæ\82Á\82½\89æ\91\9c(*.png, *.bmp, *.jpg)
+  PicFileName: String; // \82»\82Ì\83t\83@\83C\83\8b\96¼
 
 // DLL\83\8d\81[\83h\8e\9e\81A\82¨\82æ\82ÑDLL\8dÄ\8f\89\8aú\89»\8e\9e\82É\8cÄ\82Î\82ê\82é\81B
 // \93Ç\82Ý\8d\9e\82Ü\82ê\82½\89æ\91\9c\82Í\81ABottle Client\91¤\82Å\81u\83T\81[\83t\83B\83X\8dÄ\93Ç\82Ý\8d\9e\82Ý\81v\82ð
@@ -31,14 +39,18 @@ begin
   finally
     Ini.Free;
   end;
-  Ghost2File := TStringList.Create;
+  if not FileExists(GhostFile) then
+    ShowMessage('SVG.dll Warning: Ghost file is not specified');
+  Ghost2File := THashedStringList.Create;
+  Pic := TPicture.Create;
+  PicFileName := '';
 end;
 
 // DLL\83A\83\93\83\8d\81[\83h\8e\9e\82É\8cÄ\82Î\82ê\82é\81B
-// \82½\82Ô\82ñ\82â\82é\82±\82Æ\82Í\82È\82¢\81B
 procedure Unload; cdecl;
 begin
   Ghost2File.Free;
+  Pic.Free;
 end;
 
 // DLL\82Ì\96¼\91O\82¨\82æ\82Ñ\83o\81[\83W\83\87\83\93\82ð\95Ô\82·\81B
@@ -50,7 +62,7 @@ end;
 // CanConfigure\82Í\81AConfigure\82ð\8cÄ\82Î\82ê\82Ä\82â\82é\82±\82Æ\82ª\82 \82é\82©\82Ç\82¤\82©\82ð\95Ô\82·\81B
 function GetVersion(DLLName: PChar; NameLen: integer;
   var Version: integer; var CanConfigure: boolean): integer; cdecl;
-const ThisDLL = 'SVG Surface Loader';
+const ThisDLL = 'SVG Surface Loader Ver. 2.6';
 begin
   Version := 1;
   CanConfigure := true;
@@ -58,63 +70,232 @@ begin
   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: integer;
+function LoadDefinitionFileVer2(Ghost: String;
+  Surface: integer; Lines: TStringList; const FileName: String;
+  out Pos: integer): String;
+var ALine, SurfaceMap: TStringList;
+    i, j, p, sur, oldpos: integer;
+    posstr: String;
 begin
   // \93à\95\94\8aÖ\90\94\81B\83S\81[\83X\83g\92è\8b`\83t\83@\83C\83\8b\82ð\89ð\90Í\82µ\82Ä\81A
   // \96Ú\93I\82Ì\83S\81[\83X\83g\82Ì\8aÜ\82Ü\82ê\82½\83t\83@\83C\83\8b\82©\82Ç\82¤\82©\94»\92f\82·\82é
-  Lines := TStringList.Create;
+  ALine := TStringList.Create;
   try
-    Lines.LoadFromFile(FileName);
-    ALine := TStringList.Create;
+    ALine.CommaText := Lines[0];
+    if (ALine[0] <> 'GHOST') then
+      Exit;
+
+    Ghost2File.Values[Ghost] := FileName; // \8e\9f\82©\82ç\82Ì\83V\83\87\81[\83g\83J\83b\83g
+    if (ALine[1] <> Ghost) then
+    begin
+      Exit;
+    end;
+
+    // \96Ú\93I\82Ì\83S\81[\83X\83g\94­\8c©
+    oldpos := 0;
+    SurfaceMap := TStringList.Create;
     try
-      ALine.CommaText := Lines[0];
-      if (ALine[0] <> 'GHOST') or (ALine[1] <> Ghost) then
+      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\83t\83@\83C\83\8b\96¼
+              Exit;
+            end else
+              oldpos := Pos;
+          except
+            on EConvertError do; // nothing. \92P\82È\82é\83R\83\81\83\93\83g\8ds\88µ\82¢
+          end;
+        end;
+      end;
+    finally
+      SurfaceMap.Free;
+    end;
+
+    // BASIC\82Ì\8fê\8d\87\82Ì\8f\88\97\9d
+    if AnsiCompareText('BASIC', ALine[3]) = 0 then
+    begin
+      if (Surface >= 0) and (Surface <= 11) then
+      begin
+        Result := ALine[4]; // BMP\83t\83@\83C\83\8b\96¼
+        Pos := Surface; // BASIC\82Ì\8fê\8d\87\82Í\88Ê\92u\82Æ\83T\81[\83t\83B\83X\94Ô\8d\86\82ª\88ê\91Î\88ê\91Î\89\9e\82µ\82Ä\82¢\82é
         Exit;
-      // \96Ú\93I\82Ì\83S\81[\83X\83g\94­\8c©
+      end;
+    end;
+
+  finally
+    ALine.Free;
+  end;
+end;
 
-      SurfaceMap := TStringList.Create;
-      try
-        for i := 1 to Lines.Count-1 do
+procedure ParseKeyVal(const Line: String; out Key, Val: String);
+var p: integer;
+begin
+  // Key=Value\8c`\8e®\82Ì\95\8e\9a\97ñ\82©\82çKey\82ÆValue\82ð\8eæ\82è\8fo\82·
+  // Value\82Í""\82Å\88Í\82ñ\82Å\82à\82æ\82¢\81BKey\82Í\8f¬\95\8e\9a\82É\82±\82Ì\92i\8aK\82Å\93\9d\88ê
+  p := Pos('=', Line);
+  if p > 0 then
+  begin
+    Key := AnsiLowerCase(Copy(Line, 1, p-1));
+    Val := AnsiDequotedStr(Copy(Line, p+1, High(integer)), '"');
+  end else begin
+    Key := '';
+    Val := AnsiDequotedStr(Line, '"');
+  end;
+end;
+
+procedure SplitBlocks(Lines, Blocks: TStringList);
+var
+  Str, Block: String;
+  i: integer;
+  Start: integer; // \8c»\8dÝ\92\8d\96Ú\82µ\82Ä\82¢\82é\83u\83\8d\83b\83N\82Ì\90æ\93ª\82Ì\95\8e\9a\83C\83\93\83f\83b\83N\83X
+  InLead: boolean; // DBCS\82ÌLeadBytes\82ð\8c©\82Ä\82¢\82é\82©\82Ç\82¤\82©\82ð\95Û\8e\9d
+begin
+  // SVG\82Ì\8ae\8ds\82ð\83u\83\8d\83b\83N\82É\95Ï\8a·
+  // \83R\83\81\83\93\83g\82ð\8eæ\82è\8aO\82µ\81A/EOF\88È\8d~\82ð\93K\90Ø\82É\96³\8e\8b\82·\82é
+  Str := Lines.Text;
+  i := 0;
+  Start := 1;
+  InLead := false;
+  while (i < Length(Str)) do
+  begin
+    Inc(i);
+    if InLead then
+    begin
+      InLead := false;
+      Continue;
+    end else if Str[i] in LeadBytes then
+    begin
+      InLead := true;
+      Continue;
+    end else
+    begin
+      if Str[i] in [',', #13, #10] then
+      begin
+        if Start < i then
         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;
-              if StrToInt(Copy(SurfaceMap[j], 1, p-1)) = Surface then
-              begin
-                Pos := StrToInt(Copy(SurfaceMap[j], p+1, High(integer)));
-                Result := ALine[4]; // BMP\83t\83@\83C\83\8b\96¼
-                Exit;
-              end;
-            except
-              on EConvertError do; // nothing. \92P\82È\82é\83R\83\81\83\93\83g\8ds\88µ\82¢
-            end;
-          end;
+          // \90Ø\82è\8fo\82µ
+          Block := Copy(Str, Start, i-Start);
+          if Block = '/EOF' then
+            Break
+          else if Block[1] <> '/' then // \83R\83\81\83\93\83g\82Í\96³\8e\8b
+            Blocks.Add(Block);
         end;
-      finally
-        SurfaceMap.Free;
+        Start := i+1;
       end;
+    end;
+  end;
+end;
 
-      // BASIC\82Ì\8fê\8d\87\82Ì\8f\88\97\9d
-      if AnsiCompareText('BASIC', ALine[3]) = 0 then
+function LoadDefinitionFileVer3(Ghost: String;
+  Surface: integer; Lines: TStringList; const FileName: String;
+  out Pos: integer): String;
+var i, k, smin, smax, oldsur: integer;
+    Key, Val, SurStr, PosStr, SakuraName: String;
+    Blocks, Sur2Pos: TStringList;
+begin
+  oldsur := -1;
+  Sur2Pos := TStringList.Create;
+  Blocks := TStringList.Create;
+  try
+    Lines.Delete(0);
+    SplitBlocks(Lines, Blocks);
+    for i := 0 to Blocks.Count-1 do
+    begin
+      ParseKeyVal(Blocks[i], Key, Val);
+      if Key = 'sakura' then
       begin
-        if (Surface >= 0) and (Surface <= 11) then
+        SakuraName := Val;
+        Ghost2File.Values[SakuraName] := FileName; //\8e\9f\82©\82ç\82Ì\83V\83\87\81[\83g\83J\83b\83g
+        if SakuraName <> Ghost then //\95Ê\83S\81[\83X\83g\82Ì\92è\8b`\83t\83@\83C\83\8b\82È\82Ì\82Å\83p\83X
         begin
-          Result := ALine[4]; // BMP\83t\83@\83C\83\8b\96¼
-          Pos := Surface; // BASIC\82Ì\8fê\8d\87\82Í\88Ê\92u\82Æ\83T\81[\83t\83B\83X\94Ô\8d\86\82ª\88ê\91Î\88ê\91Î\89\9e\82µ\82Ä\82¢\82é
+          Result := '';
           Exit;
         end;
+      end else if Key = 'surfacefile' then
+      begin
+        Result := Val;
+      end else if (Key = 'surface') or (Length(Key) = 0) then
+      begin
+        // \83T\81[\83t\83B\83X
+        if System.Pos(':', Val) <= 0 then
+          Continue;
+        SurStr := Copy(Val, 1, System.Pos(':', Val)-1);
+        PosStr := Copy(Val, System.Pos(':', Val)+1, High(integer));
+        try
+          if System.Pos('-', SurStr) > 0 then
+          begin
+            smin := StrToInt(Copy(SurStr, 1, System.Pos('-', SurStr)-1));
+            smax := StrToInt(Copy(SurStr, System.Pos('-', SurStr)+1, High(integer)));
+          end else
+          begin
+            smin := StrToInt(SurStr);
+            smax := smin;
+          end;
+          for k := smin to smax do
+          begin
+            if PosStr = '*' then
+            begin
+              Sur2Pos.Values[IntToStr(k)] := IntToStr(k);
+              oldsur := k;
+            end else if PosStr = '-2' then
+            begin
+              Sur2Pos.Values[IntToStr(k)] := '' // \92è\8b`\89ð\8f\9c
+            end else if PosStr = '+' then
+            begin
+              Inc(oldsur);
+              Sur2Pos.Values[IntToStr(k)] := IntToStr(oldsur);
+            end else if StrToInt(PosStr) >= 0 then
+            begin
+              Sur2Pos.Values[IntToStr(k)] := PosStr;
+              oldsur := StrToInt(PosStr);
+            end;
+          end;
+        except
+          Continue;
+        end;
       end;
-
-    finally
-      ALine.Free;
     end;
+    // ShowMessage(SakuraName + #13#10 + Sur2Pos.Text);
+    if SakuraName <> Ghost then //sakura=??\82Ì\8ew\92è\82ª\94²\82¯\82Ä\82¢\82é\92è\8b`\83t\83@\83C\83\8b\82Ì\8fê\8d\87
+      Result := ''
+    else
+    begin
+      if Sur2Pos.Values[IntToStr(Surface)] <> '' then
+        Pos := StrToInt(Sur2Pos.Values[IntToStr(Surface)])
+      else
+        Result := '';
+    end;
+  finally
+    Sur2Pos.Free;
+    Blocks.Free;
+  end;
+end;
+
+function LoadDefinitionFile(Ghost: String;
+  Surface: integer; FileName: String; out Pos: integer): String;
+var Lines: TStringList;
+begin
+  Lines := TStringList.Create;
+  Lines.LoadFromFile(FileName);
+  try
+    if System.Pos('SVG3', Lines[0]) > 0 then
+      Result := LoadDefinitionFileVer3(Ghost, Surface, Lines, FileName, Pos)
+    else
+      Result := LoadDefinitionFileVer2(Ghost, Surface, Lines, FileName, Pos);
   finally
     Lines.Free;
   end;
@@ -126,46 +307,58 @@ var Ghosts, AGhost: TStringList;
     i, dum: integer;
     Dir, DefFileName, BmpFileName: String;
 begin
-  if Ghost2File.Values[Ghost] <> '' then // \82·\82Å\82É\82»\82Ì\83S\81[\83X\83g\82Ì\83t\83@\83C\83\8b\82ð\8bL\89¯\82µ\82Ä\82é
+  Result := '';
+  if Ghost2File.Values[Ghost] <> '' then // \82·\82Å\82É\82»\82Ì\83S\81[\83X\83g\82É\82Â\82¢\82Ä\92²\8d¸\8dÏ\82Ý
   begin
     DefFileName := Ghost2File.Values[Ghost];
-    BmpFileName := LoadDefinitionFile(Ghost, Surface,
-      DefFileName, dum);
-    if BmpFileName <> '' then
+    if DefFileName = '*' then // \82»\82Ì\83S\81[\83X\83g\82Í\92m\82ç\82È\82¢\81A\82Æ\8bL\89¯\82µ\82Ä\82¢\82é
     begin
-      Result := ExtractFilePath(DefFileName) + BmpFileName;
-      Pos := dum;
       Exit;
+    end else // \82»\82Ì\83S\81[\83X\83g\82ð\92m\82Á\82Ä\82¢\82é
+    begin
+      BmpFileName := LoadDefinitionFile(Ghost, Surface,
+        DefFileName, dum);
+      if BmpFileName <> '' then
+      begin
+        Result := ExtractFilePath(DefFileName) + BmpFileName;
+        Pos := dum;
+        Exit;
+      end;
     end;
   end;
 
+  // \83S\81[\83X\83g\82ª\8c©\82Â\82©\82ç\82È\82©\82Á\82½\8c\8b\89Ê\82ð\8bL\89¯\81B
+  // \8c©\82Â\82©\82Á\82½\8fê\8d\87\82É\82Í\83t\83@\83C\83\8b\96¼\82Å\8fã\8f\91\82«\82³\82ê\82é
+  if Result = '' then
+    Ghost2File.Values[Ghost] := '*';
+
   // \93à\95\94\8aÖ\90\94\81Bghost.txt\82ð\93Ç\82Ý\8d\9e\82Þ\81B
   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];
+    AGhost := TStringList.Create;
+    try
+      for i := 0 to Ghosts.Count-1 do
+      begin
+        AGhost.Text := StringReplace(Ghosts[i], ',', #13#10, [rfReplaceAll]);
+        if AGhost.Count = 0 then
+          Continue;
         if AGhost[0] <> 'GHOST' then
           Continue;
-        DefFileName := Dir + AGhost[3];
+        DefFileName := Dir + StringReplace(AGhost[3], '"', '', [rfReplaceAll]);
         // \92è\8b`\83t\83@\83C\83\8b\82ð\93Ç\82Ý\8d\9e\82Þ
         BmpFileName := LoadDefinitionFile(Ghost, Surface,
           DefFileName, dum);
         if BmpFileName <> '' then
         begin
-          // \94­\8c©!
-          Ghost2File.Values[Ghost] := DefFileName; // \8e\9f\82©\82ç\82Ì\82½\82ß\82É\92è\8b`\83t\83@\83C\83\8b\96¼\82ð\8bL\89¯
           Result := ExtractFilePath(DefFileName) + BmpFileName;
           Pos := dum;
           Break;
         end;
-      finally
-        AGhost.Free;
       end;
+    finally
+      AGhost.Free;
     end;
   finally
     Ghosts.Free;
@@ -175,7 +368,7 @@ end;
 // Ghost\82Å\8ew\92è\82³\82ê\82é\83S\81[\83X\83g\82ÌSurface\94Ô\82Ì\83T\81[\83t\83B\83X\83C\83\81\81[\83W\82ð\8eÀ\8dÛ\82É\93Ç\82Ý\8fo\82·\81B
 // H\82Å\8ew\92è\82³\82ê\82Ä\82¢\82é\83r\83b\83g\83}\83b\83v\82É\8f\91\82«\8fo\82·\82±\82Æ\81B
 function GetImage(Ghost: PChar; Surface: integer; H: HBITMAP): integer; cdecl;
-var Bmp, Bmp2: TBitmap;
+var Bmp: TBitmap;
     BmpFile: String;
     Position, x, y, nCol: integer;
 begin
@@ -186,36 +379,34 @@ begin
       Result := 1;
       Exit;
     end;
-  except
-    on Exception do // \83t\83@\83C\83\8b\82ª\8c©\82Â\82©\82Á\82Ä\82È\82¢\89Â\94\\90«\82ª\82à\82Á\82Æ\82à\8d\82\82¢
-    begin
-      Result := 1;
-      Exit;
-    end;
-  end;
-
-  Bmp := TBitmap.Create;
-  try
-    Bmp.Handle := H;
-    Bmp2 := TBitmap.Create;
+    Bmp := TBitmap.Create;
     try
-      Bmp2.LoadFromFile(BmpFile);
-      nCol := Bmp2.Width div SurfaceWidth;
+      Bmp.Handle := H;
+      if PicFileName <> BmpFile then
+      begin
+        try
+          Pic.LoadFromFile(BmpFile);
+          PicFileName := BmpFile;
+        except
+          // \8dì\82è\92¼\82µ
+          FreeAndNil(Pic);
+          Pic := TPicture.Create;
+          PicFileName := '';
+          raise;
+        end;
+      end;
+      nCol := Pic.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)
-      );
+      Bmp.Canvas.Draw(-x, -y, Pic.Graphic);
     finally
-      Bmp2.Free;
+      Bmp.ReleaseHandle;
+      Bmp.Free;
     end;
-  finally
-    Bmp.ReleaseHandle;
-    Bmp.Free;
+    Result := 0;
+  except
+    Result := 1;
   end;
-  Result := 0;
 end;
 
 // \83C\83\81\81[\83W\82Ì\91å\82«\82³\82ð\95Ô\82·\81B
@@ -234,7 +425,34 @@ end;
 // DLL\8cÅ\97L\82Ì\90Ý\92è\82ð\8ds\82¤\81B
 //
 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