OSDN Git Service

4679f9f033290675a8b075bce0461b33d7066ad4
[gikonavigoeson/gikonavi.git] / GikoSystem.pas
1 unit GikoSystem;
2
3 interface
4
5 uses
6         Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
7         ComCtrls, {IniFiles,} ShellAPI, ActnList, Math,
8 {$IF Defined(DELPRO) }
9         SHDocVw,
10         MSHTML,
11 {$ELSE}
12         SHDocVw_TLB,
13         MSHTML_TLB,
14 {$IFEND}
15         {HttpApp,} URLMon, IdGlobal, IdURI, {Masks,}
16         Setting, BoardGroup, gzip, Dolib, bmRegExp, AbonUnit,
17         ExternalBoardManager, ExternalBoardPlugInMain,
18         GikoBayesian, GikoMessage;
19
20 type
21         TVerResourceKey = (
22                         vrComments,         //!< \83R\83\81\83\93\83g
23                         vrCompanyName,      //!< \89ï\8eÐ\96¼
24                         vrFileDescription,  //!< \90à\96¾
25                         vrFileVersion,      //!< \83t\83@\83C\83\8b\83o\81[\83W\83\87\83\93
26                         vrInternalName,     //!< \93à\95\94\96¼
27                         vrLegalCopyright,   //!< \92\98\8dì\8c 
28                         vrLegalTrademarks,  //!< \8f¤\95W
29                         vrOriginalFilename, //!< \90³\8e®\83t\83@\83C\83\8b\96¼
30                         vrPrivateBuild,     //!< \83v\83\89\83C\83x\81[\83g\83r\83\8b\83h\8fî\95ñ
31                         vrProductName,      //!< \90»\95i\96¼
32                         vrProductVersion,   //!< \90»\95i\83o\81[\83W\83\87\83\93
33                         vrSpecialBuild);    //!< \83X\83y\83V\83\83\83\8b\83r\83\8b\83h\8fî\95ñ
34
35         //! BBS\83^\83C\83v
36         TGikoBBSType = (gbt2ch);
37         //! \83\8d\83O\83^\83C\83v
38         TGikoLogType = (glt2chNew, glt2chOld);
39         //! \83\81\83b\83Z\81[\83W\83A\83C\83R\83\93
40         TGikoMessageIcon = (gmiOK, gmiSAD, gmiNG, gmiWhat, gmiNone);
41         //! URL\83I\81[\83v\83\93\83u\83\89\83E\83U\83^\83C\83v
42         TGikoBrowserType = (gbtIE, gbtUserApp, gbtAuto);
43
44
45         TStrTokSeparator = set of Char;
46         TStrTokRec = record
47                 Str: string;
48                 Pos: Integer;
49         end;
50
51         //! \83C\83\93\83f\83b\83N\83X\83t\83@\83C\83\8b\83\8c\83R\81[\83h
52         TIndexRec = record
53                 FNo: Integer;
54                 FFileName: string;
55                 FTitle: string;
56                 FCount: Integer;
57                 FSize: Integer;
58 //              FRoundNo: Integer;
59                 FRoundDate: TDateTime;
60                 FLastModified: TDateTime;
61                 FKokomade: Integer;
62                 FNewReceive: Integer;
63                 FMishiyou: Boolean;     //!< \96¢\8eg\97p
64                 FUnRead: Boolean;
65                 FScrollTop: Integer;
66                 //Index Ver 1.01
67                 FAllResCount: Integer;
68                 FNewResCount: Integer;
69                 FAgeSage: TGikoAgeSage;
70         end;
71
72         //! \83T\83u\83W\83F\83N\83g\83\8c\83R\81[\83h
73         TSubjectRec = record
74                 FFileName: string;
75                 FTitle: string;
76                 FCount: Integer;
77         end;
78
79         //! \83\8c\83X\83\8c\83R\81[\83h\82Ö\82Ì\83|\83C\83\93\83^
80         PResRec = ^TResRec;
81
82         //! \83\8c\83X\83\8c\83R\81[\83h
83         TResRec = record
84                 FTitle: string;
85                 FMailTo: string;
86                 FName: string;
87                 FDateTime: string;
88                 FBody: string;
89                 FType: TGikoLogType;
90         end;
91
92         //! URLPath\83\8c\83R\81[\83h
93         TPathRec = record
94                 FBBS: string;                           //!< BBSID
95                 FKey: string;                           //!< ThreadID
96                 FSt: Int64;                               //!< \8aJ\8en\83\8c\83X\94Ô
97                 FTo: Int64;                               //!< \8fI\97¹\83\8c\83X\94Ô
98                 FFirst: Boolean;                //!< >>1\82Ì\95\\8e¦
99                 FStBegin: Boolean;      //!< 1\81`\95\\8e¦
100                 FToEnd: Boolean;                //!< \81`\8dÅ\8cã\82Ü\82Å\95\\8e¦
101                 FDone: Boolean;                 //!< \90¬\8c÷
102                 FNoParam: Boolean;  //!< \83\8c\83X\94Ô\83p\83\89\83\81\81[\83^\82È\82µ
103         end;
104
105         TGikoSys = class(TObject)
106         private
107                 { Private \90é\8c¾ }
108                 FSetting: TSetting;
109                 FDolib: TDolib;
110                 FAWKStr: TAWKStr;
111                 FResRange : Longint;
112                 FBayesian       : TGikoBayesian;        //!< \83x\83C\83W\83A\83\93\83t\83B\83\8b\83^
113                 FVersion : String;                    //!< \83t\83@\83C\83\8b\83o\81[\83W\83\87\83\93
114                 FGikoMessage: TGikoMessage;
115                 //! \82 \82é\83Z\83p\83\8c\81[\83^\82Å\8bæ\90Ø\82ç\82ê\82½\95\8e\9a\97ñ\82©\82ç\82\8e\94Ô\96Ú\82Ì\95\8e\9a\97ñ\82ð\8eæ\82è\8fo\82·
116                 function ChooseString(const Text, Separator: string; Index: integer): string;
117         //! \88ê\8e\9e\83t\83@\83C\83\8b\82©\82ç\82Ì\95\9c\8b\8c
118         procedure RestoreThreadData(Board: TBoard);
119         public
120                 { Public \90é\8c¾ }
121                 FAbon : TAbon;
122                 FSelectResFilter : TAbon;
123                 //FBoardURLList: TStringList;
124                 constructor Create;
125
126                 destructor Destroy; override;
127                 property ResRange : Longint read FResRange write FResRange;
128                 //! \83o\81[\83W\83\87\83\93\8fî\95ñ
129                 property Version : String read FVersion;
130                 function IsNumeric(s: string): boolean;
131                 function IsFloat(s: string): boolean;
132                 function DirectoryExistsEx(const Name: string): Boolean;
133                 function ForceDirectoriesEx(Dir: string): Boolean;
134
135                 function GetBoardFileName: string;
136                 function GetCustomBoardFileName: string;
137                 function GetHtmlTempFileName: string;
138                 function GetAppDir: string;
139                 function GetTempFolder: string;
140                 function GetSentFileName: string;
141                 function GetConfigDir: string;
142                 function GetSkinDir: string;
143                 function GetSkinHeaderFileName: string;
144                 function GetSkinFooterFileName: string;
145                 function GetSkinResFileName: string;
146                 function GetSkinNewResFileName: string;
147                 function GetSkinBookmarkFileName: string;
148                 function GetSkinNewmarkFileName: string;
149                 function GetStyleSheetDir: string;
150                 function GetOutBoxFileName: string;
151                 function GetUserAgent: string;
152                                 function GetSambaFileName : string;
153
154                 function GetMainKeyFileName : String;
155                 function GetEditorKeyFileName: String;
156                 function GetInputAssistFileName: String;
157                 procedure ReadSubjectFile(Board: TBoard);
158                 procedure CreateThreadDat(Board: TBoard);
159                 procedure WriteThreadDat(Board: TBoard);
160                 function ParseIndexLine(Line: string): TIndexRec;
161                 procedure GetFileList(Path: string; Mask: string; var List: TStringList; SubDir: Boolean; IsPathAdd: Boolean); overload;
162                 procedure GetFileList(Path: string; Mask: string; var List: TStringList; IsPathAdd: Boolean); overload;//\83T\83u\83t\83H\83\8b\83_\82Í\8c\9f\8dõ\82µ\82È\82¢
163                 procedure GetDirectoryList(Path: string; Mask: string; List: TStringList; SubDir: Boolean);
164
165                 function DivideSubject(Line: string): TSubjectRec;
166                 property Setting: TSetting read FSetting write FSetting;
167                 property Dolib: TDolib read FDolib write FDolib;
168
169                 function UrlToID(url: string): string;
170                 function UrlToServer(url: string): string;
171
172                 function StrTokFirst(const s:string; const sep:TStrTokSeparator; var Rec:TStrTokRec):string;
173                 function StrTokNext(const sep:TStrTokSeparator; var Rec:TStrTokRec): string;
174
175                 function GetFileSize(FileName : string) : longint;
176                 function GetFileLineCount(FileName : string): longint;
177                 function IntToDateTime(val: Int64): TDateTime;
178                 function DateTimeToInt(ADate: TDateTime): Int64;
179
180                 function ReadThreadFile(FileName: string; Line: Integer): string;
181
182                 procedure MenuFont(Font: TFont);
183
184                 function RemoveToken(var s:string; const delimiter:string):string;
185                 function GetTokenIndex(s: string; delimiter: string; index: Integer): string;
186
187                 function GetShortName(const LongName: string; ALength: integer): string;
188                 function BoolToInt(b: Boolean): Integer;
189                 function IntToBool(i: Integer): Boolean;
190                 function GzipDecompress(ResStream: TStream; ContentEncoding: string): string;
191                 procedure LoadKeySetting(ActionList: TActionList; FileName: String);
192                 procedure SaveKeySetting(ActionList: TActionList; FileName: String);
193                 procedure CreateProcess(const AppPath: string; const Param: string);
194                 procedure OpenBrowser(URL: string; BrowserType: TGikoBrowserType);
195                 function HTMLDecode(const AStr: String): String;
196                 function GetHRefText(s: string): string;
197                 function Is2chHost(Host: string): Boolean;
198                 function Parse2chURL(const url: string; const path: string; const document: string; var BBSID: string; var BBSKey: string): Boolean;
199                 function Parse2chURL2(URL: string): TPathRec;
200                 procedure ParseURI(const URL : string; var Protocol, Host, Path, Document, Port, Bookmark: string);
201                 function GetVersionBuild: Integer;
202                 function        GetBrowsableThreadURL( inURL : string ) : string;
203                 function        GetThreadURL2BoardURL( inURL : string ) : string;
204                 function        Get2chThreadURL2BoardURL( inURL : string ) : string;
205                 function        Get2chBrowsableThreadURL( inURL : string ) : string;
206                 function        Get2chBoard2ThreadURL( inBoard : TBoard; inKey : string ) : string;
207                 procedure ListBoardFile;
208                 procedure ReadBoardFile( bbs : TBBS );
209
210                 function        GetUnknownCategory : TCategory;
211                 function        GetUnknownBoard( inPlugIn : TBoardPlugIn; inURL : string ) : TBoard;
212
213                 procedure GetPopupResNumber(URL : string; var stRes, endRes : Int64);
214
215                 property Bayesian : TGikoBayesian read FBayesian write FBayesian;
216         function CreateSameIDResAnchor(var Numbers: TStringList; ThreadItem: TThreadItem; limited: Integer):string;
217                 procedure GetSameIDRes(const AID : string; ThreadItem: TThreadItem;var body: TStringList); overload;
218                 procedure GetSameIDRes(AIDNum : Integer; ThreadItem: TThreadItem;var body: TStringList); overload;
219         function GetResID(AIDNum: Integer; ThreadItem: TThreadItem): String;
220                 //! \92P\8cê\89ð\90Í
221                 procedure SpamCountWord( const text : string; wordCount : TWordCount );
222                 //! \8aw\8fK\83N\83\8a\83A
223                 procedure SpamForget( wordCount : TWordCount; isSpam : Boolean );
224                 //! \83X\83p\83\80\8aw\8fK
225                 procedure SpamLearn( wordCount : TWordCount; isSpam : Boolean );
226                 //! \83X\83p\83\80\93x\90\94
227                 function SpamParse( const text : string; wordCount : TWordCount ) : Extended;
228                 //\88ø\90\94\82ª\81A\93ú\95t\82Å\82à\8e\9e\8d\8f\82Å\82à\82È\82¢\82±\82Æ\82ð\92²\82×\82é
229                 function NotDateorTimeString(const AStr : string): boolean;
230
231                 //! \88ø\90\94\82É\91\97\82ç\82ê\82Ä\82«\82½\93ú\95t/ID\95\94\82ÉBE\82Ì\95\8e\9a\97ñ\82ª\82 \82Á\82½\82ç\81A\83v\83\8d\83t\83@\83C\83\8b\82Ö\82Ì\83\8a\83\93\83N\82ð\92Ç\89Á
232                 function AddBeProfileLink(AID : string; ANum: Integer): string;
233                 //! \83o\81[\83W\83\87\83\93\8fî\95ñ\82Ì\8eæ\93¾
234                 function GetVersionInfo(KeyWord: TVerResourceKey): string;
235                 //! Plugin\82Ì\8fî\95ñ\82Ì\8eæ\93¾
236                 function GetPluginsInfo(): string;
237                 //! IE\82Ì\83o\81[\83W\83\87\83\93\8fî\95ñ\82Ì\8eæ\93¾
238                 function GetIEVersion: string;
239                 function SetUserOptionalStyle(): string;
240                 //! \83M\83R\83i\83r\82Ì\83\81\83b\83Z\81[\83W\82ð\90Ý\92è\82·\82é
241                 procedure SetGikoMessage;
242                 //! \83M\83R\83i\83r\82Ì\83\81\83b\83Z\81[\83W\82ð\8eæ\93¾\82·\82é
243                 function GetGikoMessage(MesType: TGikoMessageListType): String;
244                 //! GMT\82Ì\8e\9e\8d\8f\82ðTDateTime\82É\95Ï\8a·\82·\82é
245                 function  DateStrToDateTime(const DateStr: string): TDateTime;
246         //! User32.dll\82ª\97\98\97p\82Å\82«\82é\82©
247         function CanUser32DLL: Boolean;
248         //! OE\88ø\97p\95\84\8eæ\93¾
249         function GetOEIndentChar : string;
250         //! \92u\8a·\90Ý\92è\83t\83@\83C\83\8b\8eæ\93¾
251         function GetReplaceFileName: String;
252         //! \83C\83\93\83f\83b\83N\83X\82É\82È\82¢dat\81i\82Í\82®\82êdat\81j\82Ì\92Ç\89Á
253         procedure AddOutofIndexDat(Board: TBoard; DatList: TStringList; AllCreate: boolean = True);
254         //! \83t\83@\83C\83\8b\96¼\82©\82ç\82Ì\83X\83\8c\83b\83h\8dì\90¬\93ú\82Ì\8eæ\93¾
255         function GetCreateDateFromName(FileName: String): TDateTime;
256         function GetExtpreviewFileName: String;
257
258         procedure ShowRefCount(msg: String; unk: IUnknown);
259         end;
260
261 var
262         GikoSys: TGikoSys;
263 const
264         //LENGTH_RESTITLE                       = 40;
265         ZERO_DATE: Integer      = 25569;
266         BETA_VERSION_NAME_E = 'beta';
267         BETA_VERSION_NAME_J = 'ÊÞÀ';
268         BETA_VERSION                            = 58;
269         BETA_VERSION_BUILD      = '';                           //!< debug\94Å\82È\82Ç
270         APP_NAME                                                = 'gikoNavi';
271         BE_PHP_URL = 'http://be.2ch.net/test/p.php?i=';
272
273
274 implementation
275
276 uses
277         Giko, RoundData, Favorite, Registry, HTMLCreate, MojuUtils, Sort, YofUtils,
278         IniFiles, DateUtils, SkinFiles;
279
280 const
281         FOLDER_INDEX_VERSION                                    = '1.01';
282         USER_AGENT                                                                              = 'Monazilla';
283         DEFAULT_NGWORD_FILE_NAME : String = 'NGword.txt';
284         NGWORDs_DIR_NAME : String               = 'NGwords';
285
286         READ_PATH: string =                     '/test/read.cgi/';
287     HTML_READ_PATH: string =        '/test/read.html/';
288         OLD_READ_PATH: string =         '/test/read.cgi?';
289         KAKO_PATH: string =                     '/kako/';
290
291         KeyWordStr: array [TVerResourceKey] of String = (
292                   'Comments',
293                   'CompanyName',
294                   'FileDescription',
295                   'FileVersion',
296                   'InternalName',
297                   'LegalCopyright',
298                   'LegalTrademarks',
299                   'OriginalFilename',
300                   'PrivateBuild',
301                   'ProductName',
302                   'ProductVersion',
303                   'SpecialBuild');
304
305 // *************************************************************************
306 //! GikoSys\83R\83\93\83X\83g\83\89\83N\83^
307 // *************************************************************************
308 constructor TGikoSys.Create;
309 begin
310     Inherited;
311         FSetting := TSetting.Create;
312         FDolib := TDolib.Create;
313         FAWKStr := TAWKStr.Create(nil);
314         if DirectoryExists(GetConfigDir) = false then begin
315                 CreateDir(GetConfigDir);
316         end;
317         FAbon := TAbon.Create;
318     FAbon.IgnoreKana := FSetting.IgnoreKana;
319         FAbon.Setroot(GetConfigDir+NGWORDs_DIR_NAME);
320         FAbon.GoHome;
321         FAbon.ReturnNGwordLineNum := FSetting.ShowNGLinesNum;
322         FAbon.SetNGResAnchor := FSetting.AddResAnchor;
323         FAbon.DeleteSyria := FSetting.DeleteSyria;
324         FAbon.Deleterlo := FSetting.AbonDeleterlo;
325         FAbon.Replaceul := FSetting.AbonReplaceul;
326         FAbon.AbonPopupRes := FSetting.PopUpAbon;
327
328         FSelectResFilter := TAbon.Create;
329     FSelectResFilter.IgnoreKana := True;
330         // \8di\82è\8d\9e\82Þ\82Æ\82«\82Í\8bÉ\97Í\88ê\97\97\82ª\8c©\82ç\82ê\82é\82Ù\82¤\82ª\82¢\82¢\82Ì\82Å\91¼\82Í\8a®\91S\82É\8dí\8f\9c
331         FSelectResFilter.AbonString := '';
332     //
333         ResRange := FSetting.ResRange;
334         FVersion := Trim(GetVersionInfo(vrFileVersion));
335         FBayesian := TGikoBayesian.Create;
336         //FBoardURLList := TStringList.Create;
337         //\83\81\83b\83Z\81[\83W\82Ì\8dì\90¬
338         FGikoMessage := TGikoMessage.Create;
339 end;
340
341 // *************************************************************************
342 //! GikoSys\83f\83X\83g\83\89\83N\83^
343 // *************************************************************************
344 destructor TGikoSys.Destroy;
345 var
346         i: Integer;
347         FileList: TStringList;
348 begin
349         //\83e\83\93\83|\83\89\83\8aHTML\82ð\8dí\8f\9c
350         FileList := TStringList.Create;
351         try
352         FileList.BeginUpdate;
353                 GetFileList(GetTempFolder, '*.html', FileList, False, True);
354         FileList.EndUpdate;
355                 for i := 0 to FileList.Count - 1 do begin
356                         DeleteFile(FileList[i]);
357                 end;
358         finally
359                 FileList.Free;
360         end;
361     FreeAndNil(FGikoMessage);
362         FreeAndNil(FBayesian);
363         FreeAndNil(FSelectResFilter);
364         FreeAndNil(FAbon);
365         FreeAndNil(FAWKStr);
366         FreeAndNil(FDolib);
367         FreeAndNil(FSetting);
368         inherited;
369 end;
370
371 {!
372 \brief \95\8e\9a\97ñ\90\94\8e\9a\83`\83F\83b\83N
373 \param s \83`\83F\83b\83N\82·\82é\95\8e\9a\97ñ
374 \return s \82ª\95\84\8d\86\95t\82«\90®\90\94\82Æ\82µ\82Ä\94F\8e¯\89Â\94\\82È\82ç True
375 }
376 {$HINTS OFF}
377 function TGikoSys.IsNumeric(s: string): boolean;
378 var
379         e: integer;
380         v: integer;
381 begin
382         Val(s, v, e);
383         Result := e = 0;
384 end;
385 {$HINTS ON}
386
387 {!
388 \brief \95\8e\9a\97ñ\95\82\93®\8f¬\90\94\93_\90\94\8e\9a\83`\83F\83b\83N
389 \param s \83`\83F\83b\83N\82·\82é\95\8e\9a\97ñ
390 \return s \82ª\95\84\8d\86\95t\82«\95\82\93®\8f¬\90\94\82Æ\82µ\82Ä\94F\8e¯\89Â\94\\82È\82ç True
391 }
392 function TGikoSys.IsFloat(s: string): boolean;
393 var
394         v: Extended;
395 begin
396         Result := TextToFloat(PChar(s), v, fvExtended);
397 end;
398
399 // *************************************************************************
400 //! \83{\81[\83h\83t\83@\83C\83\8b\96¼\8eæ\93¾\81i\83p\83X\81{\83t\83@\83C\83\8b\96¼\81j
401 // *************************************************************************
402 function TGikoSys.GetBoardFileName: string;
403 begin
404         Result := Setting.GetBoardFileName;
405 end;
406
407 // *************************************************************************
408 //! \83{\81[\83h\83t\83@\83C\83\8b\96¼\8eæ\93¾\81i\83p\83X\81{\83t\83@\83C\83\8b\96¼\81j
409 // *************************************************************************
410 function TGikoSys.GetCustomBoardFileName: string;
411 begin
412         Result := Setting.GetCustomBoardFileName;
413 end;
414
415 // *************************************************************************
416 //! \83e\83\93\83|\83\89\83\8a\83t\83H\83\8b\83_\81[\96¼\8eæ\93¾
417 // *************************************************************************
418 function TGikoSys.GetHtmlTempFileName: string;
419 begin
420         Result := Setting.GetHtmlTempFileName;
421 end;
422
423
424 // *************************************************************************
425 //! \8eÀ\8ds\83t\83@\83C\83\8b\83t\83H\83\8b\83_\8eæ\93¾
426 // *************************************************************************
427 function TGikoSys.GetAppDir: string;
428 begin
429         Result := Setting.GetAppDir;
430 end;
431
432 // *************************************************************************
433 //! TempHtml\83t\83@\83C\83\8b\96¼\8eæ\93¾\81i\83p\83X\81{\83t\83@\83C\83\8b\96¼\81j
434 // *************************************************************************
435 function TGikoSys.GetTempFolder: string;
436 begin
437         Result := Setting.GetTempFolder;
438 end;
439
440 // *************************************************************************
441 //! sent.ini\83t\83@\83C\83\8b\96¼\8eæ\93¾\81i\83p\83X\81{\83t\83@\83C\83\8b\96¼\81j
442 // *************************************************************************)
443 function TGikoSys.GetSentFileName: string;
444 begin
445         Result := Setting.GetSentFileName;
446 end;
447
448 // *************************************************************************
449 //! outbox.ini\83t\83@\83C\83\8b\96¼\8eæ\93¾\81i\83p\83X\81{\83t\83@\83C\83\8b\96¼\81j
450 // *************************************************************************
451 function TGikoSys.GetOutBoxFileName: string;
452 begin
453         Result := Setting.GetOutBoxFileName;
454 end;
455
456 // *************************************************************************
457 //! Config\83t\83H\83\8b\83_\8eæ\93¾
458 // *************************************************************************
459 function TGikoSys.GetConfigDir: string;
460 begin
461         Result := Setting.GetConfigDir;
462 end;
463
464 //! \83X\83^\83C\83\8b\83V\81[\83g\83t\83H\83\8b\83_
465 function TGikoSys.GetStyleSheetDir: string;
466 begin
467         Result := Setting.GetStyleSheetDir;
468 end;
469
470 //! \83X\83L\83\93\83t\83H\83\8b\83_
471 function TGikoSys.GetSkinDir: string;
472 begin
473         Result := Setting.GetSkinDir;
474 end;
475
476 //! Skin:\83w\83b\83_\82Ì\83t\83@\83C\83\8b\96¼
477 function TGikoSys.GetSkinHeaderFileName: string;
478 begin
479         Result := Setting.SkinFiles.GetSkinHeaderFileName;
480 end;
481
482 //! Skin:\83t\83b\83^\82Ì\83t\83@\83C\83\8b\96¼
483 function TGikoSys.GetSkinFooterFileName: string;
484 begin
485         Result := Setting.SkinFiles.GetSkinFooterFileName;
486 end;
487
488 //! Skin:\90V\92\85\83\8c\83X\82Ì\83t\83@\83C\83\8b\96¼
489 function TGikoSys.GetSkinNewResFileName: string;
490 begin
491         Result := Setting.SkinFiles.GetSkinNewResFileName;
492 end;
493
494 //! Skin:\94ñ\90V\92\85\83\8c\83X\82Ì\83t\83@\83C\83\8b\96¼
495 function TGikoSys.GetSkinResFileName: string;
496 begin
497         Result := Setting.SkinFiles.GetSkinResFileName;
498 end;
499
500 //! Skin:\82µ\82¨\82è(\82±\82±\82Ü\82Å\93Ç\82ñ\82¾)\82Ì\83t\83@\83C\83\8b\96¼
501 function TGikoSys.GetSkinBookmarkFileName: string;
502 begin
503         Result := Setting.SkinFiles.GetSkinBookmarkFileName;
504 end;
505
506 //! Skin:\82µ\82¨\82è(\90V\92\85\83\8c\83X)\82Ì\83t\83@\83C\83\8b\96¼
507 function TGikoSys.GetSkinNewmarkFileName: string;
508 begin
509         Result := Setting.SkinFiles.GetSkinNewmarkFileName;
510 end;
511
512 //! UserAgent\8eæ\93¾
513 function TGikoSys.GetUserAgent: string;
514 begin
515         if Dolib.Connected then begin
516                 Result := Format('%s %s/%s%d/%s', [
517                                                                 Dolib.UserAgent,
518                                                                 APP_NAME,
519                                                                 BETA_VERSION_NAME_E,
520                                                                 BETA_VERSION,
521                                                                 Version]);
522         end else begin
523                 Result := Format('%s/%s %s/%s%d/%s', [
524                                                                 USER_AGENT,
525                                                                 Dolib.Version,
526                                                                 APP_NAME,
527                                                                 BETA_VERSION_NAME_E,
528                                                                 BETA_VERSION,
529                                                                 Version]);
530         end;
531 end;
532
533 {!
534 \brief \8co\89ß\95b\82ð TDateTime \82É\95Ï\8a·
535 \param val 1970/1/1/ 00:00:00 \82©\82ç\82Ì\8co\89ß\95b
536 \return val \82ð\8e¦\82· TDateTime
537 }
538 function TGikoSys.IntToDateTime(val: Int64): TDateTime;
539 begin
540         Result := ZERO_DATE + val / 86400.0;
541 end;
542
543 {!
544 \brief TDateTime \82ð\8co\89ß\95b\82É\95Ï\8a·
545 \param ADate \95Ï\8a·\82·\82é\8e\9e\8d\8f
546 \return 1970/1/1/ 00:00:00 \82©\82ç\82Ì\8co\89ß\95b
547 }
548 function TGikoSys.DateTimeToInt(ADate: TDateTime): Int64;
549 begin
550         Result := Trunc((ADate - ZERO_DATE) * 86400);
551 end;
552
553
554 {!
555 \brief Subject\83t\83@\83C\83\8bRead
556 \param Board \83X\83\8c\88ê\97\97\82ð\8eæ\93¾\82·\82é\94Â
557 }
558 procedure TGikoSys.ReadSubjectFile(Board: TBoard);
559 var
560         ThreadItem: TThreadItem;
561         FileName: string;
562         FileList: TStringList;
563         Index: Integer;
564         sl: TStringList;
565         i: Integer;
566         Rec: TIndexRec;
567         UnRead: Integer;
568         usePlugIn : Boolean;
569         islog : Boolean;
570     urlHead: String;
571     datFileCheck: Boolean;
572         {*
573         FavoThreadItem : TFavoriteThreadItem;
574         Node: TTreeNode;
575         *}
576 {$IFDEF DEBUG}
577     st, rt: Cardinal;
578 {$ENDIF}
579 begin
580 {$IFDEF DEBUG}
581         st := GetTickCount;
582 {$ENDIF}
583         if Board.IsThreadDatRead then
584                 Exit;
585         Board.Clear;
586         UnRead := 0;
587         usePlugIn := Board.IsBoardPlugInAvailable;
588         //server :=  UrlToServer( Board.URL );
589     // \83X\83\8c\83b\83h\82Å\8b¤\92Ê\82ÌURL\95\94
590     if Board.is2ch then begin
591         urlHead := UrlToServer( Board.URL ) + 'test/read.cgi/' + Board.BBSID + '/';
592     end else begin
593         urlHead := UrlToServer( Board.URL ) + 'test/read.cgi?bbs=' + Board.BBSID + '&key=';
594     end;
595
596         FileName := Board.GetFolderIndexFileName;
597
598     //
599     datFileCheck := (Setting.CheckDatFile) or (not FileExists(FileName));
600     if (datFileCheck) then begin
601         FileList := TStringList.Create;
602         FileList.Sorted := True;
603         FileList.BeginUpdate;
604         //IsLogFile\97pDAT\83t\83@\83C\83\8b\83\8a\83X\83g
605         GetFileList(ExtractFileDir(Board.GetFolderIndexFileName), '*.dat', FileList, False);
606         FileList.EndUpdate;
607     end;
608
609         // \8fd\95¡\82ð\96h\82®
610         Board.BeginUpdate;
611         Board.Sorted := True;
612         sl := TStringList.Create;
613         try
614                 if FileExists(FileName) then begin
615                         sl.LoadFromFile(FileName);
616                         //\82Q\8ds\96Ú\82©\82ç\81i\82P\8ds\96Ú\82Í\83o\81[\83W\83\87\83\93\81j
617                         for i := sl.Count - 1 downto 1 do begin
618                                 Rec := ParseIndexLine(sl[i]);
619                 if (datFileCheck) then begin
620                                 islog := FileList.Find( Rec.FFileName, Index );
621                 end else begin
622                     islog := (Rec.FSize <> 0) and (Rec.FCount <> 0);
623                 end;
624                                 if usePlugIn then
625                                         ThreadItem := TThreadItem.Create(
626                                                         Board.BoardPlugIn,
627                                                         Board,
628                                                         Board.BoardPlugIn.FileName2ThreadURL( DWORD( Board ), Rec.FFileName ) )
629                                 else begin
630                                         if Board.is2ch then begin
631                                                 ThreadItem := TThreadItem.Create(
632                                                         nil,
633                                                         Board,
634                                                         urlHead + ChangeFileExt( Rec.FFileName, '' ) + '/l50',
635                                                         islog,
636                                                         Rec.FFileName
637                                                         );
638                                         end else begin
639                                                 ThreadItem := TThreadItem.Create(
640                                                         nil,
641                                                         Board,
642                                                         urlHead + ChangeFileExt( Rec.FFileName, '' ) + '&ls=50',
643                                                         islog,
644                                                         Rec.FFileName
645                                                         );
646                                         end;
647                                 end;
648
649                                 ThreadItem.BeginUpdate;
650                                 if (datFileCheck) and (islog) then
651                                         FileList.Delete( Index );
652
653                                 ThreadItem.No := Rec.FNo;
654                                 ThreadItem.FileName := Rec.FFileName;
655                                 ThreadItem.Title := MojuUtils.UnSanitize(Rec.FTitle);
656                                 ThreadItem.Count := Rec.FCount;
657                                 ThreadItem.Size := Rec.FSize;
658                                 ThreadItem.RoundDate := Rec.FRoundDate;
659                                 ThreadItem.LastModified := Rec.FLastModified;
660                                 ThreadItem.Kokomade := Rec.FKokomade;
661                                 ThreadItem.NewReceive := Rec.FNewReceive;
662                                 ThreadItem.UnRead := Rec.FUnRead;
663                                 ThreadItem.ScrollTop := Rec.FScrollTop;
664                                 ThreadItem.AllResCount := Rec.FAllResCount;
665                                 ThreadItem.NewResCount := Rec.FNewResCount;
666                                 ThreadItem.AgeSage := Rec.FAgeSage;
667                                 ThreadItem.ParentBoard := Board;
668                                 {* \82¨\8bC\82É\93ü\82è\91å\97Ê\90\90¬\83R\81[\83h *}
669                                 {*
670                                 FavoThreadItem := TFavoriteThreadItem.Create( ThreadItem.URL, ThreadItem.Title, ThreadItem );
671                                 Node := FavoriteDM.TreeView.Items.AddChildObject( FavoriteDM.TreeView.Items.Item[0], ThreadItem.Title, FavoThreadItem);
672                                 *}
673
674                                 ThreadItem.EndUpdate;
675                                 Board.Add(ThreadItem);
676
677                                 if (ThreadItem.UnRead) and (ThreadItem.IsLogFile) then
678                                         Inc(UnRead);
679                         end;
680                 end;
681
682                 if UnRead <> Board.UnRead then
683                         Board.UnRead := UnRead;
684
685         if (datFileCheck) then begin
686                     //\83C\83\93\83f\83b\83N\83X\82É\96³\82©\82Á\82½\83\8d\83O\82ð\92Ç\89Á\81i\95\85\82ê\83C\83\93\83f\83b\83N\83X\91Î\89\9e\81j
687             AddOutofIndexDat(Board, FileList);
688         end;
689                 Board.EndUpdate;
690
691         //\91O\89ñ\88Ù\8fí\8fI\97¹\8e\9e\83`\83F\83b\83N
692         RestoreThreadData( Board );
693         finally
694                 sl.Free;
695         if (datFileCheck) then begin
696                 FileList.Free;
697         end;
698                 Board.Sorted := False;
699         end;
700         Board.IsThreadDatRead := True;
701 {$IFDEF DEBUG}
702         rt := GetTickCount - st;
703         Writeln('Read Done.' + IntToStr(rt) + ' ms');
704 {$ENDIF}
705 end;
706 {!
707 \brief \83C\83\93\83f\83b\83N\83X\82É\82È\82¢dat\81i\82Í\82®\82êdat\81j\82Ì\92Ç\89Á
708 \param Board \92Ç\89Á\82·\82é\94Â
709 \param DatList  dat\83t\83@\83C\83\8b\96¼
710 }
711 procedure TGikoSys.AddOutofIndexDat(Board: TBoard; DatList: TStringList; AllCreate: Boolean = True);
712 var
713     i : Integer;
714     Boardpath,FileName : String;
715     ResRec: TResRec;
716     ThreadItem: TThreadItem;
717     create: Boolean;
718 begin
719     create := False;
720     Boardpath := ExtractFilePath(Board.GetFolderIndexFileName);
721     //\83C\83\93\83f\83b\83N\83X\82É\96³\82©\82Á\82½\83\8d\83O\82ð\92Ç\89Á\81i\95\85\82ê\83C\83\93\83f\83b\83N\83X\91Î\89\9e\81j
722     for i := 0 to DatList.Count - 1 do begin
723         FileName := Boardpath + DatList[i];
724         ThreadItem := nil;
725         if (not AllCreate) then begin
726             create := False;
727             ThreadItem := Board.FindThreadFromFileName(DatList[i]);
728             if (ThreadItem = nil) then begin
729                 create := True;
730             end else begin
731                 if Board.IsBoardPlugInAvailable then begin
732                     THTMLCreate.DivideStrLine(Board.BoardPlugIn.GetDat( DWORD( ThreadItem ), 1 ), @ResRec);
733                 end else begin
734                     THTMLCreate.DivideStrLine(ReadThreadFile(FileName, 1), @ResRec);
735                 end;
736             end;
737         end;
738         if (ThreadItem = nil) then begin
739             if Board.IsBoardPlugInAvailable then begin
740                 ThreadItem := TThreadItem.Create(
741                     Board.BoardPlugIn,
742                     Board,
743                     Board.BoardPlugIn.FileName2ThreadURL( DWORD( Board ), DatList[i] ) );
744                 THTMLCreate.DivideStrLine(Board.BoardPlugIn.GetDat( DWORD( ThreadItem ), 1 ), @ResRec);
745             end else begin
746                 ThreadItem := TThreadItem.Create(
747                     nil,
748                     Board,
749                     Get2chBoard2ThreadURL( Board, ChangeFileExt( DatList[i], '' ) ) );
750                 THTMLCreate.DivideStrLine(ReadThreadFile(FileName, 1), @ResRec);
751             end;
752         end;
753         
754
755         ThreadItem.BeginUpdate;
756         ThreadItem.FileName := DatList[i];
757         //ThreadItem.FilePath := FileName;
758         ThreadItem.No := Board.Count + 1;
759         ThreadItem.Title := ResRec.FTitle;
760         ThreadItem.Count := GetFileLineCount(FileName);
761         ThreadItem.AllResCount := ThreadItem.Count;
762         ThreadItem.NewResCount := ThreadItem.Count;
763         ThreadItem.Size := GetFileSize(FileName) - ThreadItem.Count;//1byte\82¸\82ê\82é\82Æ\82«\82ª\82 \82é\82¯\82Ç\82»\82ê\82Í\82 \82«\82ç\82ß\82é
764         ThreadItem.RoundDate := FileDateToDateTime( FileAge( FileName ) );
765         ThreadItem.LastModified := ThreadItem.RoundDate;
766         ThreadItem.Kokomade := -1;
767         ThreadItem.NewReceive := 0;
768         ThreadItem.ParentBoard := Board;
769         ThreadItem.IsLogFile := True;
770         ThreadItem.Round := False;
771         ThreadItem.UnRead := False;
772         ThreadItem.ScrollTop := 0;
773         ThreadItem.AgeSage := gasNone;
774         ThreadItem.EndUpdate;
775         if (AllCreate) or (create) then begin
776             Board.Add(ThreadItem);
777         end;
778     end;
779 end;
780 {!
781 \brief \83X\83\8c\83b\83h\83C\83\93\83f\83b\83N\83X\83t\83@\83C\83\8b(Folder.idx)\8dì\90¬
782 \param Board Folder.idx \82ð\8dì\90¬\82·\82é\94Â
783 }
784 procedure TGikoSys.CreateThreadDat(Board: TBoard);
785 var
786         i: integer;
787         s: string;
788         SubjectList: TStringList;
789         sl: TStringList;
790         Rec: TSubjectRec;
791         FileName: string;
792         cnt: Integer;
793 begin
794         if not FileExists(Board.GetSubjectFileName) then Exit;
795         FileName := Board.GetFolderIndexFileName;
796
797         SubjectList := TStringList.Create;
798         try
799                 SubjectList.LoadFromFile(Board.GetSubjectFileName);
800                 sl := TStringList.Create;
801                 try
802                         cnt := 1;
803                         sl.BeginUpdate;
804                         sl.Add(FOLDER_INDEX_VERSION);
805                         for i := 0 to SubjectList.Count - 1 do begin
806                                 Rec := DivideSubject(SubjectList[i]);
807
808                                 if (Trim(Rec.FFileName) = '') or (Trim(Rec.FTitle) = '') then
809                                         Continue;
810
811                                 {s := Format('%x', [cnt]) + #1                                  //\94Ô\8d\86
812                                          + Rec.FFileName + #1                                                           //\83t\83@\83C\83\8b\96¼
813                                          + Rec.FTitle + #1                                                                      //\83^\83C\83g\83\8b
814                                          + Format('%x', [Rec.FCount]) + #1      //\83J\83E\83\93\83g
815                                          + Format('%x', [0]) + #1                                               //size
816                                          + Format('%x', [0]) + #1                                               //RoundDate
817                                          + Format('%x', [0]) + #1                                               //LastModified
818                                          + Format('%x', [0]) + #1                                               //Kokomade
819                                          + Format('%x', [0]) + #1                                               //NewReceive
820                                          + '0' + #1                                                                                             //\96¢\8eg\97p
821                                          + Format('%x', [0]) + #1                                               //UnRead
822                                          + Format('%x', [0]) + #1                                               //ScrollTop
823                                          + Format('%x', [Rec.FCount]) + #1      //AllResCount
824                                          + Format('%x', [0]) + #1                                               //NewResCount
825                                          + Format('%x', [0]);                                                           //AgeSage
826                                 }
827                                 s := Format('%x'#1'%s'#1'%s'#1'%x'#1'%x'#1'%x'#1'%x'#1'%x'#1'%x'#1 + 
828                                                         '%s'#1'%x'#1'%x'#1'%x'#1'%x'#1'%x',
829                                         [cnt,                   //\94Ô\8d\86
830                                          Rec.FFileName, //\83t\83@\83C\83\8b\96¼
831                                          MojuUtils.Sanitize(Rec.FTitle),    //\83^\83C\83g\83\8b
832                                          Rec.FCount,     //\83J\83E\83\93\83g
833                                          0,             //size
834                                          0,             //RoundDate
835                                          0,                             //LastModified
836                                          0,                             //Kokomade
837                                          0,                             //NewReceive
838                                          '0',                   //\96¢\8eg\97p
839                                          0,                             //UnRead
840                                          0,                             //ScrollTop
841                                          Rec.FCount,    //AllResCount
842                                          0,                             //NewResCount
843                                          0]             //AgeSage
844                                         );
845
846                                 sl.Add(s);
847                                 inc(cnt);
848                         end;
849                         sl.EndUpdate;
850                         sl.SaveToFile(FileName);
851                 finally
852                         sl.Free;
853                 end;
854         finally
855                 SubjectList.Free;
856         end;
857 end;
858
859 {!
860 \brief \83X\83\8c\83b\83h\83C\83\93\83f\83b\83N\83X(Thread.dat)\8f\91\82«\8d\9e\82Ý
861 \param Thread.dat \82ð\8dì\90¬\82·\82é\94Â
862 }
863 procedure TGikoSys.WriteThreadDat(Board: TBoard);
864 //const
865 //      Values: array[Boolean] of string = ('0', '1');
866 var
867         i: integer;
868         FileName: string;
869         sl: TStringList;
870         s: string;
871         TmpFileList: TStringList;
872 begin
873         if not Board.IsThreadDatRead then
874                 Exit;
875         FileName := Board.GetFolderIndexFileName;
876         ForceDirectoriesEx( ExtractFilePath( FileName ) );
877
878         sl := TStringList.Create;
879         TmpFileList := TStringList.Create;
880         TmpFileList.Sorted := true;
881         try
882         TmpFileList.BeginUpdate;
883                 GetFileList(ExtractFileDir(Board.GetFolderIndexFileName), '*.tmp', TmpFileList, false);
884         TmpFileList.EndUpdate;
885                 sl.BeginUpdate;
886                 sl.Add(FOLDER_INDEX_VERSION);
887
888                 // \83X\83\8c\94Ô\8d\86\95Û\91\82Ì\82½\82ß\83\\81[\83g
889                 Sort.SetSortNoFlag(true);
890                 Sort.SetSortOrder(true);
891                 Sort.SetSortIndex(0);
892                 //Sort.SortNonAcquiredCountFlag := GikoSys.Setting.NonAcquiredCount;
893                 Board.CustomSort(ThreadItemSortProc);
894
895                 for i := 0 to Board.Count - 1 do begin
896                         Board.Items[i].No := i + 1;
897                         s := Format('%x'#1'%s'#1'%s'#1'%x'#1'%x'#1'%x'#1'%x'#1'%x'#1'%x'#1 +
898                                                         '%s'#1'%x'#1'%x'#1'%x'#1'%x'#1'%x',
899                                         [Board.Items[i].No,                     //\94Ô\8d\86
900                                          Board.Items[i].FileName, //\83t\83@\83C\83\8b\96¼
901                      MojuUtils.Sanitize(Board.Items[i].Title),    //\83^\83C\83g\83\8b
902                                          Board.Items[i].Count,     //\83J\83E\83\93\83g
903                                          Board.Items[i].Size,             //size
904                                          DateTimeToInt(Board.Items[i].RoundDate),             //RoundDate
905                                          DateTimeToInt(Board.Items[i].LastModified),                            //LastModified
906                                          Board.Items[i].Kokomade,                               //Kokomade
907                                          Board.Items[i].NewReceive,                             //NewReceive
908                                          '0',                   //\96¢\8eg\97p
909                                          BoolToInt(Board.Items[i].UnRead),                              //UnRead
910                                          Board.Items[i].ScrollTop,                              //ScrollTop
911                                          Board.Items[i].AllResCount,    //AllResCount
912                                          Board.Items[i].NewResCount,                            //NewResCount
913                                          Ord(Board.Items[i].AgeSage)]             //AgeSage
914                                         );
915
916                         sl.Add(s);
917                 end;
918                 sl.EndUpdate;
919                 sl.SaveToFile(FileName);
920
921                 for i := 0 to TmpFileList.Count - 1 do begin
922                         DeleteFile(ExtractFilePath(Board.GetFolderIndexFileName) + TmpFileList[i]);
923                 end;
924
925         finally
926                 TmpFileList.Free;
927                 sl.Free;
928         end;
929 end;
930
931 {!
932 \brief Folder.idx \82ð 1 \8ds\89ð\8eß
933 \param Line Folder.idx \82ð\8d\\90¬\82·\82é 1 \8ds
934 \return \83X\83\8c\83b\83h\8fî\95ñ
935 }
936 function TGikoSys.ParseIndexLine(Line: string): TIndexRec;
937 begin
938         Result.FNo := StrToIntDef('$' + MojuUtils.RemoveToken(Line, #1), 0);
939         Result.FFileName := MojuUtils.RemoveToken(Line, #1);
940         Result.FTitle := MojuUtils.UnSanitize(MojuUtils.RemoveToken(Line, #1));
941         Result.FCount := StrToIntDef('$' + MojuUtils.RemoveToken(Line, #1), 0);
942         Result.FSize := StrToIntDef('$' + MojuUtils.RemoveToken(Line, #1), 0);
943         Result.FRoundDate := IntToDateTime(StrToIntDef('$' + MojuUtils.RemoveToken(Line, #1), ZERO_DATE));
944         Result.FLastModified := IntToDateTime(StrToIntDef('$' + MojuUtils.RemoveToken(Line, #1), ZERO_DATE));
945         Result.FKokomade := StrToIntDef('$' + MojuUtils.RemoveToken(Line, #1), -1);
946         Result.FNewReceive := StrToIntDef('$' + MojuUtils.RemoveToken(Line, #1), 0);
947         RemoveToken(Line, #1);//9: ;    //\96¢\8eg\97p
948         Result.FUnRead := IntToBool(StrToIntDef('$' + MojuUtils.RemoveToken(Line, #1), 0));
949         Result.FScrollTop := StrToIntDef('$' + MojuUtils.RemoveToken(Line, #1), 0);
950         Result.FAllResCount := StrToIntDef('$' + MojuUtils.RemoveToken(Line, #1), 0);
951         Result.FNewResCount := StrToIntDef('$' + MojuUtils.RemoveToken(Line, #1), 0);
952         Result.FAgeSage := TGikoAgeSage(StrToIntDef('$' + MojuUtils.RemoveToken(Line, #1), 0));
953
954 end;
955
956 {!
957 \brief \8ew\92è\83t\83H\83\8b\83_\93à\82Ì\8ew\92è\83t\83@\83C\83\8b\88ê\97\97\82ð\8eæ\93¾\82·\82é
958 \param Path      \8bN\93_\82Æ\82È\82é\83t\83H\83\8b\83_\83p\83X
959 \param Mask      \83t\83@\83C\83\8b\96¼\82Ì\83}\83X\83N
960 \param List      OUT:\8eæ\93¾\82³\82ê\82½\83t\83@\83C\83\8b\96¼\88ê\97\97\82ª\95Ô\82é
961 \param SubDir    \92\86\82Ì\83t\83H\83\8b\83_\82Ü\82Å\8dÄ\8bA\93I\82É\83\8a\83X\83g\82·\82é\8fê\8d\87\82Í True
962 \param IsPathAdd \83p\83X\95t\82«\82Å\83\8a\83X\83g\83A\83b\83v\82·\82é\8fê\8d\87\82Í True
963
964 Mask \82ð '*.txt' \82Ì\82æ\82¤\82É\8ew\92è\82·\82é\82±\82Æ\82Å\81A
965 \93Á\92è\82Ì\83t\83@\83C\83\8b\96¼\82â\93Á\92è\82Ì\8ag\92£\8eq\82É\8di\82Á\82½\83\8a\83X\83g\83A\83b\83v\82ª\89Â\94\\82Å\82·\81B
966
967 \par \97á:
968 \code
969 GetFileList('c:\', '*.txt', list, True, True);
970 \endcode
971 }
972 procedure TGikoSys.GetFileList(Path: string; Mask: string; var List: TStringList; SubDir: Boolean; IsPathAdd: Boolean);
973 var
974         rc: Integer;
975         SearchRec : TSearchRec;
976         s: string;
977 begin
978         Path := IncludeTrailingPathDelimiter(Path);
979         rc := FindFirst(Path + '*.*', faAnyfile, SearchRec);
980         try
981                 while rc = 0 do begin
982                         if (SearchRec.Name <> '..') and (SearchRec.Name <> '.') then begin
983                                 s := Path + SearchRec.Name;
984
985                                 if (SearchRec.Attr and faDirectory = 0) and (MatchesMask(s, Mask)) then
986                                                 if IsPathAdd then
987                                                         List.Add(s)
988                                                 else
989                                                         List.Add(SearchRec.Name);
990                                 if SubDir and (SearchRec.Attr and faDirectory > 0) then
991                                         GetFileList(s, Mask, List, True, IsPathAdd);
992                         end;
993                         rc := FindNext(SearchRec);
994                 end;
995         finally
996                 SysUtils.FindClose(SearchRec);
997         end;
998         List.Sort;
999 end;
1000
1001 {!
1002 \breif \8ew\92è\83t\83H\83\8b\83_\93à\82Ì\8ew\92è\83t\83@\83C\83\8b\88ê\97\97\82ð\8eæ\93¾\82·\82é\81B
1003                          \83T\83u\83t\83H\83\8b\83_\82Í\8c\9f\8dõ\82µ\82È\82¢
1004 \param Path      \8bN\93_\82Æ\82È\82é\83t\83H\83\8b\83_\83p\83X
1005 \param Mask      \83t\83@\83C\83\8b\96¼\82Ì\83}\83X\83N
1006 \param List      OUT:\8eæ\93¾\82³\82ê\82½\83t\83@\83C\83\8b\96¼\88ê\97\97\82ª\95Ô\82é
1007 \param IsPathAdd \83p\83X\95t\82«\82Å\83\8a\83X\83g\83A\83b\83v\82·\82é\8fê\8d\87\82Í True
1008 \note \8dÄ\8bN\8ew\92è\89Â\94\\82È GetFileList() \82ª\82 \82é\82Ì\82Å\82±\82Ì\8aÖ\90\94\82Í\95s\97v?
1009 \par \97á
1010 \code
1011 GetFileList('c:\', '*.txt', list, True);
1012 \endcode
1013 }
1014 procedure TGikoSys.GetFileList(Path: string; Mask: string; var List: TStringList; IsPathAdd: Boolean);
1015 var
1016         rc: Integer;
1017         SearchRec : TSearchRec;
1018 begin
1019         Path := IncludeTrailingPathDelimiter(Path);
1020         rc := FindFirst(Path + Mask, faAnyfile, SearchRec);
1021         try
1022                 while rc = 0 do begin
1023                         if (SearchRec.Name <> '..') and (SearchRec.Name <> '.') then begin
1024                                 if (SearchRec.Attr and faDirectory = 0) then begin
1025                     if IsPathAdd then begin
1026                         List.Add(Path + SearchRec.Name)
1027                     end else begin
1028                         List.Add(SearchRec.Name);
1029                     end;
1030                 end;
1031                         end;
1032                         rc := FindNext(SearchRec);
1033                 end;
1034         finally
1035                 SysUtils.FindClose(SearchRec);
1036         end;
1037         List.Sort;
1038 end;
1039
1040 {!
1041 \brief \8ew\92è\83t\83H\83\8b\83_\93à\82Ì\83f\83B\83\8c\83N\83g\83\8a\88ê\97\97\82ð\8eæ\93¾\82·\82é
1042 \param Path      \8bN\93_\82Æ\82È\82é\83t\83H\83\8b\83_\83p\83X
1043 \param Mask      \83t\83H\83\8b\83_\96¼\82Ì\83}\83X\83N
1044 \param List      OUT:\8eæ\93¾\82³\82ê\82½\83t\83H\83\8b\83_\96¼\88ê\97\97\82ª\95Ô\82é
1045 \param SubDir    \92\86\82Ì\83t\83H\83\8b\83_\82Ü\82Å\8dÄ\8bA\93I\82É\83\8a\83X\83g\82·\82é\8fê\8d\87\82Í True
1046
1047 Mask \82ð '*.txt' \82Ì\82æ\82¤\82É\8ew\92è\82·\82é\82±\82Æ\82Å\81A
1048 \93Á\92è\82Ì\83t\83@\83C\83\8b\96¼\82â\93Á\92è\82Ì\8ag\92£\8eq\82É\8di\82Á\82½\83\8a\83X\83g\83A\83b\83v\82ª\89Â\94\\82Å\82·\81B
1049
1050 \par \97á:
1051 \code
1052 GetDirectoryList('c:\', '*.txt', list, True);
1053 \endcode
1054 }
1055 procedure TGikoSys.GetDirectoryList(Path: string; Mask: string; List: TStringList; SubDir: Boolean);
1056 var
1057         rc: Integer;
1058         SearchRec : TSearchRec;
1059         s: string;
1060 begin
1061         Path := IncludeTrailingPathDelimiter(Path);
1062         rc := FindFirst(Path + '*.*', faDirectory, SearchRec);
1063         try
1064                 while rc = 0 do begin
1065                         if (SearchRec.Name <> '..') and (SearchRec.Name <> '.') then begin
1066                                 s := Path + SearchRec.Name;
1067                                 //if (SearchRec.Attr and faDirectory > 0) then
1068                                 //      s := IncludeTrailingPathDelimiter(s)
1069
1070                                 if (SearchRec.Attr and faDirectory > 0) and (MatchesMask(s, Mask)) then
1071                                         List.Add( IncludeTrailingPathDelimiter( s ) );
1072                                 if SubDir and (SearchRec.Attr and faDirectory > 0) then
1073                                         GetDirectoryList(s, Mask, List, True);
1074                         end;
1075                         rc := FindNext(SearchRec);
1076                 end;
1077         finally
1078                 SysUtils.FindClose(SearchRec);
1079         end;
1080 end;
1081
1082
1083 {!
1084 \brief Subject.txt \88ê\8ds\82ð\89ð\8eß
1085 \param Line Subject.txt \82ð\8d\\90¬\82·\82é 1 \8ds
1086 \return     \83X\83\8c\83b\83h\8fî\95ñ
1087 }
1088 function TGikoSys.DivideSubject(Line: string): TSubjectRec;
1089 var
1090         i: integer;
1091         ws: WideString;
1092         Delim: string;
1093         LeftK: string;
1094         RightK: string;
1095 begin
1096         Result.FCount := 0;
1097
1098         if AnsiPos('<>', Line) = 0 then
1099                 Delim := ','
1100         else
1101                 Delim := '<>';
1102         Result.FFileName := RemoveToken(Line, Delim);
1103         Result.FTitle := Trim(RemoveToken(Line, Delim));
1104
1105         ws := Result.FTitle;
1106         if Copy(ws, Length(ws), 1) = ')' then begin
1107                 LeftK := '(';
1108                 RightK := ')';
1109         end else if Copy(ws, Length(ws)-1, 2) = '\81j' then begin
1110                 LeftK := '\81i';
1111                 RightK := '\81j';
1112         end else if Copy(ws, Length(ws), 1) = '>' then begin
1113                 LeftK := '<';
1114                 RightK := '>';
1115         end;
1116         for i := Length(ws) - 1 downto 1 do begin
1117                 if Copy(ws, i, Length(LeftK)) = LeftK then begin
1118                         Result.FTitle := TrimRight(Copy(ws, 1, i - 1));
1119                         ws := Copy(ws, i + Length(LeftK), Length(ws) - i - Length(RightK));
1120                         if IsNumeric(ws) then
1121                                 Result.FCount := StrToInt(ws);
1122                         //Delete(Result.FTitle, i, Length(LeftK) + Length(ws) + Length(RightK));
1123                         break;
1124                 end;
1125         end;
1126 end;
1127
1128 {!
1129 \brief URL\82©\82çBBSID\82ð\8eæ\93¾
1130 \param url BBSID \82ð\8eæ\93¾\82·\82é URL
1131 \return    BBSID
1132 }
1133 function TGikoSys.UrlToID(url: string): string;
1134 var
1135         i: integer;
1136 begin
1137         Result := '';
1138         url := Trim(url);
1139
1140         if url = '' then Exit;
1141         try
1142                 url := Copy(url, 0, Length(url) - 1);
1143                 for i := Length(url) downto 0 do begin
1144                         if url[i] = '/' then begin
1145                                 Result := Copy(url, i + 1, Length(url));
1146                                 Break;
1147                         end;
1148                 end;
1149         except
1150                 Result := '';
1151         end;
1152 end;
1153
1154 {!
1155 \brief URL\82©\82ç\8dÅ\8cã\82Ì\97v\91f\82ð\8dí\8f\9c
1156 \param url \89ð\8eß\82·\82é URL
1157 \return    \90Ø\82è\8eæ\82ç\82ê\82½\8cã\82Ì URL
1158
1159 URL \82©\82ç BBSID\88È\8aO\82Ì\95\94\95ª\82ð\8eæ\93¾\82·\82é\82Ì\82É\8eg\97p\82µ\82Ü\82·\81B
1160 }
1161 function TGikoSys.UrlToServer(url: string): string;
1162 var
1163         i: integer;
1164         wsURL: WideString;
1165 begin
1166         Result := '';
1167         wsURL := url;
1168         wsURL := Trim(wsURL);
1169
1170         if wsURL = '' then exit;
1171
1172         if Copy(wsURL, Length(wsURL), 1) = '/' then
1173                 wsURL := Copy(wsURL, 0, Length(wsURL) - 1);
1174
1175         for i := Length(wsURL) downto 0 do begin
1176                 if wsURL[i] = '/' then begin
1177                         Result := Copy(wsURL, 0, i);
1178                         break;
1179                 end;
1180         end;
1181 end;
1182
1183 {!
1184 \brief \83f\83B\83\8c\83N\83g\83\8a\82ª\91\8dÝ\82·\82é\82©\83`\83F\83b\83N
1185 \param Name \91\8dÝ\82ð\8am\94F\82·\82é\83t\83H\83\8b\83_\83p\83X
1186 \return     \83t\83H\83\8b\83_\82ª\91\8dÝ\82·\82é\82È\82ç True
1187 }
1188 function TGikoSys.DirectoryExistsEx(const Name: string): Boolean;
1189 var
1190         Code: Cardinal;
1191 begin
1192         Code := GetFileAttributes(PChar(Name));
1193         Result := (Code <> Cardinal(-1)) and (FILE_ATTRIBUTE_DIRECTORY and Code <> 0);
1194 end;
1195
1196 {!
1197 \brief \83f\83B\83\8c\83N\83g\83\8a\8dì\90¬\81i\95¡\90\94\8aK\91w\91Î\89\9e\81j
1198 \param Dir \8dì\90¬\82·\82é\83p\83X
1199 \return    \8dì\90¬\82É\90¬\8c÷\82µ\82½\8fê\8d\87\82Í True
1200 }
1201 function TGikoSys.ForceDirectoriesEx(Dir: string): Boolean;
1202 begin
1203         Result := True;
1204         if Length(Dir) = 0 then
1205                 raise Exception.Create('\83t\83H\83\8b\83_\82ª\8dì\90¬\8fo\97\88\82Ü\82¹\82ñ');
1206         Dir := ExcludeTrailingPathDelimiter(Dir);
1207         if (Length(Dir) < 3) or DirectoryExistsEx(Dir)
1208                 or (ExtractFilePath(Dir) = Dir) then Exit; // avoid 'xyz:\' problem.
1209         Result := ForceDirectoriesEx(ExtractFilePath(Dir)) and CreateDir(Dir);
1210 end;
1211
1212 {!
1213 \brief \95\8e\9a\97ñ\82©\82ç\83g\81[\83N\83\93\82Ì\90Ø\82è\8fo\82µ\81i\8f\89\8aú\8f\88\97\9d\81j
1214                          FDelphi\82©\82ç\82Ì\83p\83N\83\8a
1215 \param s   \8c³\82É\82È\82é\83L\83\83\83\89\83N\83^
1216 \param sep \8bæ\90Ø\82è\82É\82È\82é\95\8e\9a\97ñ
1217 \param Rec OUT:\95\8e\9a\97ñ\91\96\8d¸\8fî\95ñ\82ª\95Ô\82é
1218 \return    \90Ø\82è\8fo\82µ\82½\83g\81[\83N\83\93
1219 \todo Split, RemoveToken, GetTokenIndex, NthField \8ds\82«
1220 }
1221 function TGikoSys.StrTokFirst(const s:string; const sep: TStrTokSeparator; var Rec: TStrTokRec): string;
1222 begin
1223         Rec.Str := s;
1224         Rec.Pos := 1;
1225         Result := StrTokNext(sep, Rec);
1226 end;
1227
1228 {!
1229 \brief \95\8e\9a\97ñ\82©\82ç\83g\81[\83N\83\93\82Ì\90Ø\82è\8fo\82µ
1230                          FDelphi\82©\82ç\82Ì\83p\83N\83\8a
1231 \param sep \8bæ\90Ø\82è\82É\82È\82é\83L\83\83\83\89\83N\83^
1232 \param Rec IN/OUT:StrTokFirst\82Å\8dì\90¬\82³\82ê\82½\95\8e\9a\97ñ\91\96\8d¸\8fî\95ñ
1233 \return    \90Ø\82è\8fo\82µ\82½\83g\81[\83N\83\93
1234 \todo Split, RemoveToken, GetTokenIndex, NthField \8ds\82«
1235 }
1236 function TGikoSys.StrTokNext(const sep: TStrTokSeparator; var Rec: TStrTokRec): string;
1237 var
1238         Len, I: Integer;
1239 begin
1240         with Rec do     begin
1241                 Len := Length(Str);
1242                 Result := '';
1243                 if Len >= Pos then begin
1244                         while (Pos <= Len) and (Str[Pos] in sep) do begin
1245                          Inc(Pos);
1246                         end;
1247                         I := Pos;
1248                         while (Pos<= Len) and not (Str[Pos] in sep) do begin
1249                                 if IsDBCSLeadByte(Byte(Str[Pos])) then begin
1250                                         Inc(Pos);
1251                                 end;
1252                                 Inc(Pos);
1253                         end;
1254                         Result := Copy(Str, I, Pos - I);
1255                         while (Pos <= Len) and (Str[Pos] in sep) do begin// \82±\82ê\82Í\82¨\8dD\82Ý
1256                                 Inc(Pos);
1257                         end;
1258                 end;
1259         end;
1260 end;
1261
1262 {!
1263 \brief \83t\83@\83C\83\8b\83T\83C\83Y\8eæ\93¾
1264 \param FileName \83t\83@\83C\83\8b\83T\83C\83Y\82ð\8eæ\93¾\82·\82é\83t\83@\83C\83\8b\83p\83X
1265 \return         \83t\83@\83C\83\8b\83T\83C\83Y(bytes)
1266 }
1267 function TGikoSys.GetFileSize(FileName : string): longint;
1268 var
1269         F : File;
1270 begin
1271         try
1272                 if not FileExists(FileName) then begin
1273                         Result := 0;
1274                         Exit;
1275                 end;
1276                 Assign(F, FileName);
1277                 Reset(F, 1);
1278                 Result := FileSize(F);
1279                 CloseFile(F);
1280         except
1281                 Result := 0;
1282         end;
1283 end;
1284
1285 {!
1286 \brief \83e\83L\83X\83g\83t\83@\83C\83\8b\82Ì\8ds\90\94\82ð\8eæ\93¾
1287 \param FileName \8ds\90\94\82ð\8eæ\93¾\82·\82é\83t\83@\83C\83\8b\83p\83X
1288 \return         \8ds\90\94
1289 \todo \83\81\83\82\83\8a\83}\83b\83v\83h\83t\83@\83C\83\8b\8ds\82«
1290 }
1291 function TGikoSys.GetFileLineCount(FileName : string): longint;
1292 var
1293         sl: TStringList;
1294 begin
1295         sl := TStringList.Create;
1296         try
1297                 try
1298                         sl.LoadFromFile(FileName);
1299                         Result := sl.Count;
1300                 except
1301                         Result := 0;
1302                 end;
1303         finally
1304                 sl.Free;
1305         end;
1306
1307 end;
1308
1309 {!
1310 \brief \83t\83@\83C\83\8b\82©\82ç\8ew\92è\8ds\82ð\8eæ\93¾
1311 \param FileName \83t\83@\83C\83\8b\82Ì\83p\83X
1312 \param Line     \8ew\92è\8ds
1313 \return         \8ew\92è\82³\82ê\82½ 1 \8ds
1314 \todo \83\81\83\82\83\8a\83}\83b\83v\83h\83t\83@\83C\83\8b\8ds\82«
1315 }
1316 function TGikoSys.ReadThreadFile(FileName: string; Line: Integer): string;
1317 var
1318         fileTmp : TStringList;
1319 begin
1320         Result := '';
1321         if FileExists(FileName) then begin
1322                 fileTmp := TStringList.Create;
1323                 try
1324                         try
1325                                 fileTmp.LoadFromFile( FileName );
1326                                 if ( Line       >= 1 ) and ( Line       < fileTmp.Count + 1 ) then begin
1327                                         Result := fileTmp.Strings[ Line-1 ];
1328                                 end;
1329                         except
1330                                 //on EFOpenError do Result := '';
1331                         end;
1332                 finally
1333                         fileTmp.Free;
1334                 end;
1335         end;
1336 end;
1337
1338 {!
1339 \brief \83V\83X\83e\83\80\83\81\83j\83\85\81[\83t\83H\83\93\83g\82Ì\91®\90«\82ð\8eæ\93¾
1340 \param Font OUT:\8eæ\93¾\82µ\82½\83t\83H\83\93\83g\91®\90«\82ª\95Ô\82é
1341 }
1342 procedure TGikoSys.MenuFont(Font: TFont);
1343 var
1344         lf: LOGFONT;
1345         nm: NONCLIENTMETRICS;
1346 begin
1347         nm.cbSize := sizeof(NONCLIENTMETRICS);
1348     SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, @nm, 0);
1349     lf := nm.lfMenuFont;
1350     Font.Name := lf.lfFaceName;
1351     Font.Height := lf.lfHeight;
1352     Font.Style := [];
1353     if lf.lfWeight >= 700 then
1354         Font.Style := Font.Style + [fsBold];
1355     if lf.lfItalic = 1 then
1356         Font.Style := Font.Style + [fsItalic];
1357 end;
1358
1359 {!
1360 \brief \90æ\93ª\82Ì\83g\81[\83N\83\93\82ð\90Ø\82è\8fo\82µ
1361 \param s         IN/OUT:\8c³\82É\82È\82é\95\8e\9a\97ñ\81A\90Ø\82è\8fo\82µ\82½\8cã\82Ì\8ec\82è\82Ì\95\8e\9a\97ñ
1362 \param delimiter \8bæ\90Ø\82è\82É\82È\82é\95\8e\9a\97ñ
1363 \return          \90Ø\82è\8fo\82µ\82½\95\8e\9a\97ñ
1364
1365 \82Ç\82±\82©\82Ì\83T\83C\83g\82©\82ç\82Ì\83p\83N\83\8a
1366 }
1367 function TGikoSys.RemoveToken(var s: string;const delimiter: string): string;
1368 var
1369         p: Integer;
1370 begin
1371         p := AnsiPos(delimiter, s);
1372         if p = 0 then
1373                 Result := s
1374         else
1375                 Result := Copy(s, 1, p - 1);
1376         Delete(s, 1, Length(Result) + Length(delimiter));
1377 end;
1378
1379
1380 {!
1381 \brief n \8cÂ\96Ú\82Ì\83g\81[\83N\83\93\82ð\90Ø\82è\8fo\82µ
1382 \param s     \8c³\82É\82È\82é\95\8e\9a\97ñ
1383 \param index 0 \82©\82ç\8en\82Ü\82é\83C\83\93\83f\83b\83N\83X(n \8cÂ\96Ú\82Ì n)
1384 \return \90Ø\82è\8fo\82µ\82½\83g\81[\83N\83\93
1385
1386 \82Ç\82±\82©\82Ì\83T\83C\83g\82©\82ç\82Ì\83p\83N\83\8a
1387 }
1388 function TGikoSys.GetTokenIndex(s: string; delimiter: string; index: Integer): string;
1389 var
1390         i: Integer;
1391 begin
1392         Result := '';
1393         for i := 0 to index do
1394                 Result := RemoveToken(s, delimiter);
1395 end;
1396
1397
1398 //\83C\83\93\83f\83b\83N\83X\96¢\8dX\90V\83o\83b\83t\83@\82ð\83t\83\89\83b\83V\83\85\81I
1399 {procedure TGikoSys.FlashExitWrite;
1400 var
1401         i: Integer;
1402 begin
1403         //\83X\83\8c\83b\83h\83f\81[\83^\83t\83@\83C\83\8b\82ð\8dX\90V
1404         for i := 0 to FExitWrite.Count - 1 do
1405                 WriteThreadDat(FExitWrite[i]);
1406         FExitWrite.Clear;
1407 end;}
1408
1409 {!
1410 \brief \83X\83\8c\96¼\82È\82Ç\82ð\92Z\82¢\96¼\91O\82É\95Ï\8a·\82·\82é
1411 \param LongName \8c³\82É\82È\82é\95\8e\9a\97ñ
1412 \param ALength  \8eû\82ß\82é\95\8e\9a\97ñ\92·(bytes)
1413 \return         \95Ï\8a·\82³\82ê\82½\95\8e\9a\97ñ
1414
1415 from HotZonu
1416 }
1417 function TGikoSys.GetShortName(const LongName: string; ALength: integer): string;
1418 const
1419         ERASECHAR : array [1..39] of string =
1420                 ('\81\99','\81\9a','\81¡','\81 ','\81\9f','\81\9e','\81Q','\81\94','\81£','\81¥',
1421                  '\81¢','\81¤','\81\9c','\81\9b','\81\9d','\81y','\81z','\81ô','\81s','\81t',
1422                  '\81g','\81h','\81k','\81l','\81e','\81f','\81\83','\81\84','\81á','\81â',
1423                  '\81o','\81p','\81q','\81r','\81w','\81x','\81¬','\81c', '\81@');
1424 var
1425         Chr : array [0..255]    of      char;
1426         S : string;
1427         i : integer;
1428 begin
1429         s := Trim(LongName);
1430         if (Length(s) <= ALength) then begin
1431                 Result := s;
1432         end else begin
1433                 S := s;
1434                 for i := Low(ERASECHAR) to      High(ERASECHAR) do      begin
1435                         S := CustomStringReplace(S, ERASECHAR[i], '');
1436                 end;
1437                 if (Length(S) <= ALength) then begin
1438                         Result := S;
1439                 end else begin
1440                         Windows.LCMapString(
1441                                         GetUserDefaultLCID(),
1442                                         LCMAP_HALFWIDTH,
1443                                         PChar(S),
1444                                         Length(S) + 1,
1445                                         chr,
1446                                         Sizeof(chr)
1447                                         );
1448                         S := Chr;
1449                         S := Copy(S,1,ALength);
1450                         while true do begin
1451                                 if (ByteType(S, Length(S)) = mbLeadByte ) then begin
1452                                         S := Copy(S, 1, Length(S) - 1);
1453                                 end else begin
1454                                         Break;
1455                                 end;
1456                         end;
1457                         Result := S;
1458                 end;
1459         end;
1460 end;
1461
1462 {!
1463 \brief Boolean \82ð Integer \82É\95Ï\8a·
1464 \return False..0, True..1
1465 }
1466 function TGikoSys.BoolToInt(b: Boolean): Integer;
1467 begin
1468         Result := IfThen(b, 1, 0);
1469 end;
1470
1471 {!
1472 \brief Integer \82ð Boolean \82É\95Ï\8a·
1473 \return 1..True, other..False
1474 \todo 0..False, other..True \82Ì\95û\82ª\82¢\82¢\82Ì\82Å\82Í?
1475                         (\82±\82Ì\8ed\97l\82É\88Ë\91\82µ\82Ä\82¢\82é\82©\82à\82µ\82ê\82È\82¢\82Ì\82Å\96¢\8fC\90³)
1476 }
1477 function TGikoSys.IntToBool(i: Integer): Boolean;
1478 begin
1479         Result := i = 1;
1480 end;
1481
1482 {!
1483 \brief gzip\82Å\88³\8fk\82³\82ê\82½\82Ì\82ð\96ß\82·
1484 \param ResStream       \93Ç\82Ý\8d\9e\82Þ\83X\83g\83\8a\81[\83\80
1485 \param ContentEncoding \83G\83\93\83R\81[\83f\83B\83\93\83O
1486 \return                \93W\8aJ\82³\82ê\82½\95\8e\9a\97ñ
1487 }
1488 function TGikoSys.GzipDecompress(ResStream: TStream; ContentEncoding: string): string;
1489 const
1490         BUF_SIZE = 4096;
1491 var
1492         GZipStream: TGzipDecompressStream;
1493         TextStream: TStringStream;
1494         buf: array[0..BUF_SIZE - 1] of Byte;
1495         cnt: Integer;
1496         s: string;
1497         i, ln: Integer;
1498 begin
1499         Result := '';
1500         TextStream := TStringStream.Create('');
1501         try
1502 //\83m\81[\83g\83\93\83E\83\93\83`\83E\83B\83\8b\83X2003\91Î\8dô(x-gzip\82Æ\82©\82É\82È\82é\82Ý\82½\82¢)
1503 //              if LowerCase(Trim(ContentEncoding)) = 'gzip' then begin
1504                 if AnsiPos('gzip', LowerCase(Trim(ContentEncoding))) > 0 then begin
1505                         ResStream.Position := 0;
1506                         GZipStream := TGzipDecompressStream.Create(TextStream);
1507                         try
1508                                 repeat
1509                                         FillChar(buf, BUF_SIZE, 0);
1510                                         cnt := ResStream.Read(buf, BUF_SIZE);
1511                                         if cnt > 0 then
1512                                                 GZipStream.Write(buf, BUF_SIZE);
1513                                 until cnt = 0;
1514                         finally
1515                                 GZipStream.Free;
1516                         end;
1517                 end else begin
1518                         ResStream.Position := 0;
1519                         repeat
1520                                 FillChar(buf, BUF_SIZE, 0);
1521                                 cnt := ResStream.Read(buf, BUF_SIZE);
1522                                 if cnt > 0 then
1523                                         TextStream.Write(buf, BUF_SIZE);
1524                         until cnt = 0;
1525                 end;
1526
1527                 //NULL\95\8e\9a\82ð"*"\82É\82·\82é
1528                 s := TextStream.DataString;
1529                 i := Length(s);
1530                 ln := i;
1531                 while (i > 0) and (s[i] = #0) do
1532                         Dec(i);
1533                 Delete(s, i + 1, ln - i);
1534
1535                 i := Pos(#0, s);
1536                 while i <> 0 do begin
1537                         s[i] := '*';
1538                         i := Pos(#0, s);
1539                 end;
1540
1541                 Result := s;
1542         finally
1543                 TextStream.Free;
1544         end;
1545 end;
1546
1547 {!
1548 \brief \83A\83N\83V\83\87\83\93\82É\83V\83\87\81[\83g\83J\83b\83g\83L\81[\82ð\90Ý\92è
1549 \param ActionList \90Ý\92è\82·\82é\83A\83N\83V\83\87\83\93\88ê\97\97
1550 \param FileName Ini\83t\83@\83C\83\8b\82Ì\96¼\91O
1551 }
1552 procedure TGikoSys.LoadKeySetting(ActionList: TActionList; FileName: String);
1553 const
1554         STD_SEC = 'KeySetting';
1555 var
1556         i: Integer;
1557         ini: TMemIniFile;
1558         ActionName: string;
1559         ActionKey: Integer;
1560         SecList: TStringList;
1561         Component: TComponent;
1562 begin
1563         if not FileExists(fileName) then
1564                 Exit;
1565         SecList := TStringList.Create;
1566         ini := TMemIniFile.Create(fileName);
1567         try
1568                 ini.ReadSection(STD_SEC, SecList);
1569                 for i := 0 to SecList.Count - 1 do begin
1570                         ActionName := SecList[i];
1571                         ActionKey := ini.ReadInteger(STD_SEC, ActionName, -1);
1572                         if ActionKey <> -1 then begin
1573                                 Component := ActionList.Owner.FindComponent(ActionName);
1574                                 if TObject(Component) is TAction then begin
1575                                         TAction(Component).ShortCut := ActionKey;
1576                                 end;
1577                         end;
1578                 end;
1579         finally
1580                 ini.Free;
1581                 SecList.Free;
1582         end;
1583 end;
1584
1585 {!
1586 \brief \83A\83N\83V\83\87\83\93\82É\90Ý\92è\82³\82ê\82Ä\82¢\82é\83V\83\87\81[\83g\83J\83b\83g\83L\81[\82ð\83t\83@\83C\83\8b\82É\95Û\91
1587 \param ActionList \95Û\91\82·\82é\83A\83N\83V\83\87\83\93\88ê\97\97
1588 \param FileName Ini\83t\83@\83C\83\8b\96¼
1589
1590 ActionList \82É\90Ý\92è\82³\82ê\82Ä\82¢\82é\83V\83\87\81[\83g\83J\83b\83g\83L\81[\82ð FileName \82É\95Û\91\82µ\82Ü\82·\81B
1591 }
1592 procedure TGikoSys.SaveKeySetting(ActionList: TActionList; FileName: String);
1593 const
1594         STD_SEC = 'KeySetting';
1595 var
1596         i: Integer;
1597         ini: TMemIniFile;
1598 begin
1599         ini := TMemIniFile.Create(GetConfigDir + FileName);
1600         try
1601                 for i := 0 to ActionList.ActionCount - 1 do begin
1602                         if ActionList.Actions[i].Tag = -1 then
1603                                 Continue;
1604                         ini.WriteInteger(STD_SEC, ActionList.Actions[i].Name, TAction(ActionList.Actions[i]).ShortCut);
1605                 end;
1606                 ini.UpdateFile;
1607         finally
1608                 ini.Free;
1609         end;
1610 end;
1611
1612
1613 {!
1614 \brief \83v\83\8d\83Z\83X\82Ì\90\90¬
1615 \param AppPath \8bN\93®\82·\82é\83v\83\8d\83Z\83X\82Ì\83t\83@\83C\83\8b\83p\83X
1616 \param Param   AppPath \82É\93n\82·\88ø\90\94
1617 }
1618 procedure TGikoSys.CreateProcess(const AppPath: string; const Param: string);
1619 var
1620         PI: TProcessInformation;
1621         SI: TStartupInfo;
1622         Path: string;
1623 begin
1624         Path := '"' + AppPath + '"';
1625         if Param <> '' then
1626                 Path := Path + ' ' + Param;
1627
1628         SI.Cb := SizeOf(Si);
1629         SI.lpReserved   := nil;
1630         SI.lpDesktop     := nil;
1631         SI.lpTitle               := nil;
1632         SI.dwFlags               := 0;
1633         SI.cbReserved2 := 0;
1634         SI.lpReserved2 := nil;
1635         SI.dwysize               := 0;
1636         Windows.CreateProcess(nil,
1637                                                                 PChar(Path),
1638                                                                 nil,
1639                                                                 nil,
1640                                                                 False,
1641                                                                 0,
1642                                                                 nil,
1643                                                                 nil,
1644                                                                 SI,
1645                                                                 PI);
1646 end;
1647
1648 {!
1649 \brief Web \83u\83\89\83E\83U\82ð\8bN\93®
1650 \param URL         Web \83u\83\89\83E\83U\82Å\95\\8e¦\82·\82é URL
1651 \param BrowserType \83u\83\89\83E\83U\82Ì\83^\83C\83v(IE \82©\82Ç\82¤\82©)
1652 }
1653 procedure TGikoSys.OpenBrowser(URL: string; BrowserType: TGikoBrowserType);
1654 begin
1655         case BrowserType of
1656                 gbtIE:
1657                         HlinkNavigateString(nil, PWideChar(WideString(URL)));
1658                 gbtUserApp, gbtAuto:
1659                         if (Setting.URLApp) and (FileExists(Setting.URLAppFile)) then
1660                                 GikoSys.CreateProcess(Setting.URLAppFile, URL)
1661                         else
1662                                 HlinkNavigateString(nil, PWideChar(WideString(URL)));
1663         end;
1664 end;
1665
1666 {!
1667 \brief \95\8e\9a\8eÀ\91Ì\8eQ\8fÆ\82ð\83f\83R\81[\83h
1668 \param AStr \83f\83R\81[\83h\82·\82é\95\8e\9a\97ñ
1669 \return     \83f\83R\81[\83h\82³\82ê\82½\95\8e\9a\97ñ
1670 }
1671 function TGikoSys.HTMLDecode(const AStr: String): String;
1672 var
1673         Sp, Rp, Cp, Tp: PChar;
1674         S: String;
1675         I, Code: Integer;
1676         Num: Boolean;
1677 begin
1678         SetLength(Result, Length(AStr));
1679         Sp := PChar(AStr);
1680         Rp := PChar(Result);
1681         //Cp := Sp;
1682         try
1683                 while Sp^ <> #0 do begin
1684                         case Sp^ of
1685                                 '&': begin
1686                                                          //Cp := Sp;
1687                                                          Inc(Sp);
1688                                                          case Sp^ of
1689                                                                  'a': if AnsiStrPos(Sp, 'amp;') = Sp then
1690                                                                                         begin
1691                                                                                                 Inc(Sp, 3);
1692                                                                                                 Rp^ := '&';
1693                                                                                         end;
1694                                                                  'l',
1695                                                                  'g': if (AnsiStrPos(Sp, 'lt;') = Sp) or (AnsiStrPos(Sp, 'gt;') = Sp) then
1696                                                                                         begin
1697                                                                                                 Cp := Sp;
1698                                                                                                 Inc(Sp, 2);
1699                                                                                                 while (Sp^ <> ';') and (Sp^ <> #0) do
1700                                                                                                         Inc(Sp);
1701                                                                                                 if Cp^ = 'l' then
1702                                                                                                         Rp^ := '<'
1703                                                                                                 else
1704                                                                                                         Rp^ := '>';
1705                                                                                         end;
1706                                                                  'q': if AnsiStrPos(Sp, 'quot;') = Sp then
1707                                                                                         begin
1708                                                                                                 Inc(Sp,4);
1709                                                                                                 Rp^ := '"';
1710                                                                                         end;
1711                                                                  '#': begin
1712                                                                                                 Tp := Sp;
1713                                                                                                 Inc(Tp);
1714                                                                                                 Num := IsNumeric(Copy(Tp, 1, 1));
1715                                                                                                 while (Sp^ <> ';') and (Sp^ <> #0) do begin
1716                                                                                                         if (Num) and (not IsNumeric(Copy(Sp, 1, 1))) then
1717                                                                                                                 Break;
1718                                                                                                         Inc(Sp);
1719                                                                                                 end;
1720                                                                                                 SetString(S, Tp, Sp - Tp);
1721                                                                                                 Val(S, I, Code);
1722                                                                                                 Rp^ := Chr((I));
1723                                                                                         end;
1724                                                          //      else
1725                                                                          //raise EConvertError.CreateFmt(sInvalidHTMLEncodedChar,
1726                                                                                  //[Cp^ + Sp^, Cp - PChar(AStr)])
1727                                                          end;
1728                                          end
1729                         else
1730                                 Rp^ := Sp^;
1731                         end;
1732                         Inc(Rp);
1733                         Inc(Sp);
1734                 end;
1735         except
1736 //              on E:EConvertError do
1737 //                      raise EConvertError.CreateFmt(sInvalidHTMLEncodedChar,
1738 //                              [Cp^ + Sp^, Cp - PChar(AStr)])
1739         end;
1740         SetLength(Result, Rp - PChar(Result));
1741 end;
1742
1743 {!
1744 \brief HTML \82Ì\83A\83\93\83J\81[\83^\83O\82©\82ç URL \82ð\8eæ\93¾
1745 \param s URL \82ð\8eæ\93¾\82·\82é HTML
1746 \return  \8eæ\93¾\82µ\82½ URL
1747 }
1748 function TGikoSys.GetHRefText(s: string): string;
1749 var
1750         Index: Integer;
1751         Index2: Integer;
1752 begin
1753         Result := '';
1754         s := Trim(s);
1755         if s = '' then
1756                 Exit;
1757
1758         Index := AnsiPos('href', LowerCase(s));
1759         if Index = 0 then
1760                 Exit;
1761         s := Trim(Copy(s, Index + 4, Length(s)));
1762         s := Trim(Copy(s, 2, Length(s)));
1763
1764         //\8en\82ß\82Ì\95\8e\9a\82ª'"'\82È\82ç\8eæ\82è\8f\9c\82­
1765         //if Copy(s, 1, 1) = '"' then begin
1766     if s[1]  = '"' then begin
1767                 s := Trim(Copy(s, 2, Length(s)));
1768         end;
1769
1770         Index := AnsiPos('"', s);
1771         if Index <> 0 then begin
1772                 //'"'\82Ü\82ÅURL\82Æ\82·\82é
1773                 s := Copy(s, 1, Index - 1);
1774         end else begin
1775                 //'"'\82ª\96³\82¯\82ê\82Î\83X\83y\81[\83X\82©">"\82Ì\91\81\82¢\95û\82Ü\82Å\82ðURL\82Æ\82·\82é
1776                 Index := AnsiPos(' ', s);
1777                 Index2 := AnsiPos('>', s);
1778                 if Index = 0 then
1779                         Index := Index2;
1780                 if Index > Index2 then
1781                         Index := Index2;
1782                 if Index <> 0 then
1783                         s := Copy(s, 1, Index - 1)
1784                 else
1785                         //\82±\82ê\88È\8fã\82à\82¤\92m\82ç\82ñ\82Ê
1786                         ;
1787         end;
1788         Result := Trim(s);
1789 end;
1790
1791 {!
1792 \brief \83z\83X\83g\96¼\82ª\82Q\82\83\82\88\82©\82Ç\82¤\82©\83`\83F\83b\83N\82·\82é
1793 \param Host \83`\83F\83b\83N\82·\82é\83z\83X\83g\96¼
1794 \return     2\82¿\82á\82ñ\82Ë\82é\82Ì\83z\83X\83g\96¼\82È\82ç True
1795 }
1796 function TGikoSys.Is2chHost(Host: string): Boolean;
1797 const
1798         HOST_NAME: array[0..1] of string = ('2ch.net', 'bbspink.com');
1799 var
1800         i: Integer;
1801 //      Len: Integer;
1802 begin
1803         Result := False;
1804         if RightStr( Host, 1 ) = '/' then
1805                 Host := Copy( Host, 1, Length( Host ) - 1 );
1806         OutputDebugString(pchar(HOST_NAME[0]));
1807         for i := 0 to Length(HOST_NAME) - 1 do begin
1808 //              Len := Length(HOST_NAME[i]);
1809                 if (AnsiPos(HOST_NAME[i], Host) > 0) and
1810                         (AnsiPos(HOST_NAME[i], Host) = (Length(Host) - Length(HOST_NAME[i]) + 1)) then begin
1811                         Result := True;
1812                         Exit;
1813                 end;
1814         end;
1815 end;
1816
1817 {!
1818 \brief 2\82¿\82á\82ñ\82Ë\82é\8c`\8e®\82Ì URL \82ð\95ª\89ð
1819 \param url      2\82¿\82á\82ñ\82Ë\82é\8c`\8e®\82Ì URL
1820 \param path     test/read.cgi \82È\82Ç\82Ì\92\86\8aÔ\83p\83X(ParseURI \82©\82ç\93¾\82é)
1821 \param document index.html \82È\82Ç\82Ì\83h\83L\83\85\83\81\83\93\83g\96¼(ParseURI \82©\82ç\93¾\82é)
1822 \param BBSID    OUT:BBSID \82ª\95Ô\82é(ex. giko)
1823 \param BBSKey   OUT:\83X\83\8c\83b\83h\83L\81[\82ª\95Ô\82é(ex. 10000000000)
1824 \return 2\82¿\82á\82ñ\82Ë\82é\82Ì URL \82Æ\82µ\82Ä\95ª\89ð\82Å\82«\82½\82È\82ç True
1825 }
1826 function TGikoSys.Parse2chURL(const url: string; const path: string; const document: string; var BBSID: string; var BBSKey: string): Boolean;
1827 var
1828         Index: Integer;
1829         s: string;
1830         SList: TStringList;
1831 begin
1832         BBSID := '';
1833         BBSKey := '';
1834         Result := False;
1835
1836         Index := AnsiPos(READ_PATH, path);
1837         if Index <> 0 then begin
1838                 s := Copy(path, Index + Length(READ_PATH), Length(path));
1839     end else begin
1840         Index := AnsiPos(HTML_READ_PATH, path);
1841         if Index <> 0 then begin
1842             s := Copy(path, Index + Length(HTML_READ_PATH), Length(path));
1843         end;
1844     end;
1845     if Index <> 0 then begin
1846                 if (Length(s) > 0) and (s[1] = '/') then
1847                         Delete(s, 1, 1);
1848                 BBSID := GetTokenIndex(s, '/', 0);
1849                 BBSKey := GetTokenIndex(s, '/', 1);
1850                 if BBSKey = '' then
1851                         BBSKey := Document;
1852                 Result := (BBSID <> '') or (BBSKey <> '');
1853                 Exit;
1854         end;
1855         Index := AnsiPos(KAKO_PATH, path);
1856         if Index <> 0 then begin
1857                 s := Copy(path, 2, Length(path));
1858                 BBSID := GetTokenIndex(s, '/', 0);
1859                 if (BBSID = 'log') and (GetTokenIndex(s, '/', 2) = 'kako') then
1860                         BBSID := GetTokenIndex(s, '/', 1);
1861                 BBSKey := ChangeFileExt(Document, '');
1862                 Result := (BBSID <> '') or (BBSKey <> '');
1863                 Exit;
1864         end;
1865         Index := AnsiPos('read.cgi?', URL);
1866         if Index <> 0 then begin
1867                 SList := TStringList.Create;
1868                 try
1869                         try
1870 //                              s := HTMLDecode(Document);
1871                                 ExtractHTTPFields(['?', '&'], [], PChar(URL), SList, False);
1872                                 BBSID := SList.Values['bbs'];
1873                                 BBSKey := SList.Values['key'];
1874                                 Result := (BBSID <> '') or (BBSKey <> '');
1875                                 Exit;
1876                         except
1877                                 Exit;
1878                         end;
1879                 finally
1880                         SList.Free;
1881                 end;
1882         end;
1883 end;
1884
1885 {!
1886 \brief 2ch \8c`\8e®\82Ì URL \82©\82ç\83\8c\83X\94Ô\82ð\8eæ\93¾
1887 \param URL    2\82¿\82á\82ñ\82Ë\82é\8c`\8e®\82Ì URL
1888 \param stRes  OUT:\8aJ\8en\83\8c\83X\94Ô\82ª\95Ô\82é
1889 \param endRes OUT:\8fI\97¹\83\8c\83X\94Ô\82ª\95Ô\82é
1890
1891 http://2ch.net/\92\86\97ª/32-50 \n
1892 \82Ì\8fê\8d\87 stRef = 32, endRes = 50 \82É\82È\82é
1893 }
1894 procedure TGikoSys.GetPopupResNumber(URL : string; var stRes, endRes : Int64);
1895 var
1896         buf : String;
1897         convBuf : String;
1898         ps : Int64;
1899         pch : PChar;
1900 begin
1901         URL := Trim(LowerCase(URL));
1902         if (AnsiPos('&st=', URL ) <> 0) and ( AnsiPos( '&to=',URL) <> 0 ) then begin
1903                 stRes := 0;
1904                 endRes := 0;
1905                 try
1906                         buf := Copy( URL, AnsiPos('&st=', URL ) + 4, AnsiPos( '&to=',URL) - AnsiPos('&st=', URL ) - 4 );
1907                         if buf <> '' then
1908                                 stRes := StrToInt64( buf );
1909                         if AnsiPos( '&nofirst=',URL) <> 0 then begin
1910                                 buf := Copy( URL, AnsiPos('&to=', URL ) + 4, AnsiPos( '&nofirst=',URL) - AnsiPos('&to=', URL ) - 4);
1911                         end else begin
1912                                 buf := Copy( URL, AnsiPos('&to=', URL ) + 4, Length( URL ) - AnsiPos('&to=', URL ) - 4 + 1 );
1913                                 ps := 0;
1914                                 pch := PChar(buf);
1915                                 while  ( ps < Length(buf) )and ( pch[ps] >= '0' ) and ( pch[ps] <= '9' ) do Inc(ps);
1916                                 buf := Copy( buf, 1, ps );
1917                         end;
1918                         try
1919                                 if buf <> '' then
1920                                         endRes := StrToInt64(buf)
1921                         except
1922                                 endRes := 0;
1923                         end;
1924                 except
1925                         stRes := 0;
1926                 end;
1927                 if (stRes <> 0) and (endRes = 0) then
1928                         endRes := stRes + MAX_POPUP_RES
1929                 else if (stRes = 0) and (endRes <> 0) then begin
1930                         stRes := endRes - MAX_POPUP_RES;
1931                         if stRes < 1 then
1932                                 stRes := 1;
1933                 end;
1934                 GikoSys.GetBrowsableThreadURL( URL );
1935         end else if( AnsiPos('&res=', URL ) <> 0 ) then begin
1936                 endRes := 0;
1937                 buf := Copy( URL, AnsiPos('&res=', URL ) + 5, Length( URL ) - AnsiPos('&res=', URL ) - 5 + 1 );
1938                 ps := 0;
1939                 pch := PChar(buf);
1940                 while  ( ps < Length(buf) )and ( pch[ps] >= '0' ) and ( pch[ps] <= '9' ) do Inc(ps);
1941                 buf := Copy( buf, 1, ps );
1942                 try
1943                         if buf <> '' then
1944                                 stRes := StrToInt(buf)
1945                         else begin
1946                                 stRes := 0;
1947                         end;
1948                 except
1949                         stRes := 0;
1950                 end;
1951         end else if (AnsiPos('&start=', URL ) <> 0) and ( AnsiPos( '&end=',URL) <> 0 ) then begin
1952                 try
1953                         stRes := StrToInt64( Copy( URL, AnsiPos('&start=', URL ) + 7, AnsiPos( '&end=',URL) - AnsiPos('&start=', URL ) - 7 ) );
1954                         if AnsiPos( '&nofirst=',URL) <> 0 then begin
1955                                 buf := Copy( URL, AnsiPos('&end=', URL ) + 5, AnsiPos( '&nofirst=',URL) - AnsiPos('&end=', URL ) - 5);
1956                         end else begin
1957                                 buf := Copy( URL, AnsiPos('&end=', URL ) + 5, Length( URL ) - AnsiPos('&to=', URL ) - 5 + 1 );
1958                                 ps := 0;
1959                                 pch := PChar(buf);
1960                                 while  ( ps < Length(buf) )and ( pch[ps] >= '0' ) and ( pch[ps] <= '9' ) do Inc(ps);
1961                                 buf := Copy( buf, 1, ps );
1962                         end;
1963                         try
1964                                 if buf <> '' then
1965                                         endRes := StrToInt64(buf);
1966                         except
1967                                 endRes := 0;
1968                         end;
1969                 except
1970                         stRes := 0;
1971                 end;
1972         end else if ( AnsiPos('.html',URL) <> Length(URL) -4 ) and ( AnsiPos('.htm',URL) <> Length(URL) -3 ) then begin
1973                 buf := Copy(URL, LastDelimiter('/',URL)+1,Length(URL)-LastDelimiter('/',URL)+1);
1974                 if  Length(buf) > 0 then begin
1975                         if AnsiPos('-', buf) = 1 then begin
1976                                 stRes := 0;
1977                                 Delete(buf,1,1);
1978                                 ps := 0;
1979                                 pch := PChar(buf);
1980                                 while  ( ps < Length(buf) )and ( pch[ps] >= '0' ) and ( pch[ps] <= '9' ) do Inc(ps);
1981                                 try
1982                                         convBuf := Copy( buf, 1, ps );
1983                                         if convBuf <> '' then
1984                                                 endRes := StrToInt64(convBuf)
1985                                         else
1986                                                 endRes := 0;
1987                                 except
1988                                         endRes := 0;
1989                                 end;
1990                                 if endRes <> 0 then begin
1991                                         stRes := endRes - MAX_POPUP_RES;
1992                                         if stRes < 1 then
1993                                                 stRes := 1;
1994                                 end;
1995                         end else begin
1996                                 ps := 0;
1997                                 pch := PChar(buf);
1998                                 while  ( ps < Length(buf) )and ( pch[ps] >= '0' ) and ( pch[ps] <= '9' ) do Inc(ps);
1999                                 try
2000                                         convBuf := Copy( buf, 1, ps );
2001                                         if convBuf <> '' then begin
2002                                                 stRes := StrToInt64(convBuf);
2003                                                 Delete(buf,1,ps+1);
2004                                                 ps := 0;
2005                                                 pch := PChar(buf);
2006                                                 while  ( ps < Length(buf) )and ( pch[ps] >= '0' ) and ( pch[ps] <= '9' ) do Inc(ps);
2007                                                 try
2008                                                         convBuf := Copy( buf, 1, ps );
2009                                                         if convBuf <> '' then
2010                                                                 endRes := StrToInt64(convBuf)
2011                                                         else
2012                                                                 endRes := 0;
2013                                                 except
2014                                                         endRes := 0;
2015                                                 end;
2016                                         end else begin
2017                                                 stRes := 0;
2018                                         end;
2019                                 except
2020                                         stRes := 0;
2021                                         endRes := 0;
2022                                 end;
2023                         end;
2024                 end;
2025         end else begin
2026                 //stRes := 0;
2027                 //endRes := 0;
2028         end;
2029 end;
2030
2031 {!
2032 \brief 2\82¿\82á\82ñ\82Ë\82é\8c`\8e®\82Ì URL \82ð\95ª\89ð
2033 \param URL 2\82¿\82á\82ñ\82Ë\82é\8c`\8e®\82Ì URL
2034 \return    \95ª\89ð\82³\82ê\82½\97v\91f
2035 }
2036 function TGikoSys.Parse2chURL2(URL: string): TPathRec;
2037 var
2038         i: Integer;
2039         s: string;
2040 //      buf : String;
2041 //      convBuf : String;
2042         wk: string;
2043         wkMin: Integer;
2044         wkMax: Integer;
2045         wkInt: Integer;
2046         RStart: Integer;
2047         RLength: Integer;
2048 //      ps : Integer;
2049 //      pch : PChar;
2050         SList: TStringList;
2051 begin
2052         URL := Trim(LowerCase(URL));
2053         Result.FBBS := '';
2054         Result.FKey := '';
2055         Result.FSt := 0;
2056         Result.FTo := 0;
2057         Result.FFirst := False;
2058         Result.FStBegin := False;
2059         Result.FToEnd := False;
2060         Result.FDone := False;
2061         Result.FNoParam := False;
2062
2063         wkMin := 0;
2064         wkMax := 1;
2065         if URL[length(URL)] = '\' then
2066                 URL := URL + 'n';
2067         FAWKStr.RegExp := 'http://.+\.(2ch\.net|bbspink\.com)/';
2068         if FAWKStr.Match(FAWKStr.ProcessEscSeq(URL), RStart, RLength) <> 0 then begin
2069                 s := Copy(URL, RStart + RLength - 1, Length(URL));
2070
2071                 //\95W\8f\80\8f\91\8e®
2072                 //\8dÅ\8cã\82Íl50, 10, 10-20, 10n, 10-20n, -10, 10-, 10n- \82È\82Ç
2073                 //http://xxx.2ch.net/test/read.cgi/bbsid/1000000000/
2074                 FAWKStr.RegExp := '/test/read.(cgi|html)/.+/[0-9]+/?.*';
2075                 if FAWKStr.Match(FAWKStr.ProcessEscSeq(s), RStart, RLength) > 0 then begin
2076                         s := Copy(s, 15, Length(s));
2077
2078                         SList := TStringList.Create;
2079                         try
2080                                 SList.Clear;
2081                                 FAWKStr.RegExp := '/';
2082                                 if FAWKStr.Split(FAWKStr.ProcessEscSeq(s), SList) >= 3 then begin
2083                                         Result.FBBS := SList[1];
2084                                         Result.FKey := SList[2];
2085                                         if SList.Count >= 4 then
2086                                                 s := SList[3]
2087                                         else begin
2088                                                 s := '';
2089                                                 Result.FNoParam := true;
2090                                         end;
2091                                 end else
2092                                         Exit;
2093
2094                                 SList.Clear;
2095                                 FAWKStr.LineSeparator := mcls_CRLF;
2096                                 FAWKStr.RegExp := '-';
2097                                 if FAWKStr.Split(FAWKStr.ProcessEscSeq(s), SList) = 0 then begin
2098                                         Result.FFirst := True;
2099                                 end else begin
2100                                         FAWKStr.RegExp := 'l[0-9]+';
2101                                         if FAWKStr.Match(FAWKStr.ProcessEscSeq(s), RStart, RLength) > 0 then begin
2102                                                 Result.FFirst := True;
2103                                         end else begin
2104                                                 for i := 0 to SList.Count - 1 do begin
2105                                                         if Trim(SList[i]) = '' then begin
2106                                                                 if i = 0 then
2107                                                                         Result.FStBegin := True;
2108                                                                 if i = (SList.Count - 1) then
2109                                                                         Result.FToEnd := True;
2110                                                         end else if IsNumeric(SList[i]) then begin
2111                                                                 wkInt := StrToInt(SList[i]);
2112                                                                 wkMax := Max(wkMax, wkInt);
2113                                                                 if wkMin = 0 then
2114                                                                         wkMin := wkInt
2115                                                                 else
2116                                                                         wkMin := Min(wkMin, wkInt);
2117                                                         end else if Trim(SList[i]) = 'n' then begin
2118                                                                 Result.FFirst := True;
2119                                                         end else begin
2120                                                                 FAWKStr.RegExp := '^n[0-9]+$|^[0-9]+n$';
2121                                                                 if FAWKStr.Match(FAWKStr.ProcessEscSeq(SList[i]), RStart, RLength) > 0 then begin
2122                                                                         if Copy(SList[i], 1, 1) = 'n' then
2123                                                                                 wkInt := StrToInt(Copy(SList[i], 2, Length(SList[i])))
2124                                                                         else
2125                                                                                 wkInt := StrToInt(Copy(SList[i], 1, Length(SList[i]) - 1));
2126                                                                         Result.FFirst := True;
2127                                                                         wkMax := Max(wkMax, wkInt);
2128                                                                         if wkMin = 1 then
2129                                                                                 wkMin := wkInt
2130                                                                         else
2131                                                                                 wkMin := Min(wkMin, wkInt);
2132                                                                 end;
2133                                                         end;
2134                                                 end;
2135                                                 if Result.FStBegin and (not Result.FToEnd) then
2136                                                         Result.FSt := wkMin
2137                                                 else if (not Result.FStBegin) and Result.FToEnd then
2138                                                         Result.FTo := wkMax
2139                                                 else if (not Result.FStBegin) and (not Result.FToEnd) then begin
2140                                                         Result.FSt := wkMin;
2141                                                         Result.FTo := wkMax;
2142                                                 end;
2143                                                 //Result.FSt := wkMin;
2144                                                 //Result.FTo := wkMax;
2145                                         end;
2146                                 end;
2147                         finally
2148                                 SList.Free;
2149                         end;
2150                         Result.FDone := True;
2151                         Exit;
2152                 end;
2153
2154                 //\90Vkako\8f\91\8e®
2155                 //http://server.2ch.net/ITA_NAME/kako/1000/10000/1000000000.html
2156                 FAWKStr.RegExp := '/.+/kako/[0-9]+/[0-9]+/[0-9]+\.html';
2157                 if FAWKStr.Match(FAWKStr.ProcessEscSeq(s), RStart, RLength) > 0 then begin
2158                         SList := TStringList.Create;
2159                         try
2160                                 SList.Clear;
2161                                 FAWKStr.RegExp := '/';
2162                                 if FAWKStr.Split(FAWKStr.ProcessEscSeq(s), SList) >= 6 then begin
2163                                         Result.FBBS := SList[1];
2164                                         Result.FKey := ChangeFileExt(SList[5], '');
2165                                         Result.FFirst := True;
2166                                 end else
2167                                         Exit;
2168                         finally
2169                                 SList.Free;
2170                         end;
2171                         Result.FDone := True;
2172                         Exit;
2173                 end;
2174
2175                 //\8b\8ckako\8f\91\8e®
2176                 //http://server.2ch.net/ITA_NAME/kako/999/999999999.html
2177                 FAWKStr.RegExp := '/.+/kako/[0-9]+/[0-9]+\.html';
2178                 if FAWKStr.Match(FAWKStr.ProcessEscSeq(s), RStart, RLength) > 0 then begin
2179                         SList := TStringList.Create;
2180                         try
2181                                 SList.Clear;
2182                                 FAWKStr.RegExp := '/';
2183                                 if FAWKStr.Split(FAWKStr.ProcessEscSeq(s), SList) >= 5 then begin
2184                                         Result.FBBS := SList[1];
2185                                         Result.FKey := ChangeFileExt(SList[4], '');
2186                                         Result.FFirst := True;
2187                                 end else
2188                                         Exit;
2189                         finally
2190                                 SList.Free;
2191                         end;
2192                         Result.FDone := True;
2193                         Exit;
2194                 end;
2195
2196                 //log\8by\82Ñlog2\8f\91\8e®
2197                 //http://server.2ch.net/log/ITA_NAME/kako/999/999999999.html
2198                 //http://server.2ch.net/log2/ITA_NAME/kako/999/999999999.html
2199                 FAWKStr.RegExp := '/log2?/.+/kako/[0-9]+/[0-9]+\.html';
2200                 if FAWKStr.Match(FAWKStr.ProcessEscSeq(s), RStart, RLength) > 0 then begin
2201                         SList := TStringList.Create;
2202                         try
2203                                 SList.Clear;
2204                                 FAWKStr.RegExp := '/';
2205                                 if FAWKStr.Split(FAWKStr.ProcessEscSeq(s), SList) >= 6 then begin
2206                                         Result.FBBS := SList[2];
2207                                         Result.FKey := ChangeFileExt(SList[5], '');
2208                                         Result.FFirst := True;
2209                                 end else
2210                                         Exit;
2211                         finally
2212                                 SList.Free;
2213                         end;
2214                         Result.FDone := True;
2215                         Exit;
2216                 end;
2217
2218
2219                 //\8b\8cURL\8f\91\8e®
2220                 //http://server.2ch.net/test/read.cgi?bbs=ITA_NAME&key=1000000000&st=1&to=5&nofirst=true
2221                 FAWKStr.RegExp := '/test/read\.cgi\?';
2222                 if FAWKStr.Match(FAWKStr.ProcessEscSeq(s), RStart, RLength) > 0 then begin
2223                         s := Copy(s, 16, Length(s));
2224                         SList := TStringList.Create;
2225                         try
2226                                 SList.Clear;
2227                                 FAWKStr.RegExp := '&';
2228                                 if FAWKStr.Split(FAWKStr.ProcessEscSeq(s), SList) >= 2 then begin
2229                                         Result.FFirst := True;
2230                                         for i := 0 to SList.Count - 1 do begin
2231                                                 if Pos('bbs=', SList[i]) = 1 then begin
2232                                                         Result.FBBS := Copy(SList[i], 5, Length(SList[i]));
2233                                                 end else if Pos('key=', SList[i]) = 1 then begin
2234                                                         Result.FKey := Copy(SList[i], 5, Length(SList[i]));
2235                                                 end else if Pos('st=', SList[i]) = 1 then begin
2236                                                         wk := Copy(SList[i], 4, Length(SList[i]));
2237                                                         if IsNumeric(wk) then
2238                                                                 Result.FSt := StrToInt(wk)
2239                                                         else if wk = '' then
2240                                                                 Result.FStBegin := True;
2241                                                 end else if Pos('to=', SList[i]) = 1 then begin
2242                                                         wk := Copy(SList[i], 4, Length(SList[i]));
2243                                                         if IsNumeric(wk) then
2244                                                                 Result.FTo := StrToInt(wk)
2245                                                         else if wk = '' then
2246                                                                 Result.FToEnd := True;
2247                                                 end else if Pos('nofirst=', SList[i]) = 1 then begin
2248                                                         Result.FFirst := False;
2249                                                 end;
2250                                         end;
2251                                 end else
2252                                         Exit;
2253                         finally
2254                                 SList.Free;
2255                         end;
2256
2257                         if (Result.FBBS <> '') and (Result.FKey <> '') then begin
2258                                 Result.FDone := True;
2259                         end;
2260                         Exit;
2261                 end;
2262         end;
2263 end;
2264
2265 {!
2266 \brief URI \82ð\95ª\89ð
2267 \param URL      \95ª\89ð\82·\82é URI
2268 \param Protocol OUT:\83v\83\8d\83g\83R\83\8b\82ª\95Ô\82é(ex. http)
2269 \param Host     OUT:\83z\83X\83g\82ª\95Ô\82é(ex. hoge.com)
2270 \param Path     OUT:\92\86\8aÔ\83p\83X\82ª\95Ô\82é(ex. test/read.cgi)
2271 \param Document OUT:\83h\83L\83\85\83\81\83\93\83g\96¼\82ª\95Ô\82é(ex. index.html)
2272 \param Port     OUT:\83|\81[\83g\82ª\95Ô\82é(ex. 8080)
2273 \param Bookmark OUT:\83u\83b\83N\83}\81[\83N(?)\82ª\95Ô\82é
2274 }
2275 procedure TGikoSys.ParseURI(const URL : string; var Protocol, Host, Path, Document, Port, Bookmark: string);
2276 var
2277         URI: TIdURI;
2278 begin
2279         Protocol := '';
2280         Host := '';
2281         Path := '';
2282         Document := '';
2283         Port := '';
2284         Bookmark := '';
2285         URI := TIdURI.Create(URL);
2286         try
2287                 Protocol := URI.Protocol;
2288                 Host := URI.Host;
2289                 Path := URI.Path;
2290                 Document := URI.Document;
2291                 Port := URI.Port;
2292                 Bookmark := URI.Bookmark;
2293         finally
2294                 URI.Free;
2295         end;
2296 end;
2297
2298 {!
2299 \brief \83M\83R\83i\83r\82Ì\83o\81[\83W\83\87\83\93\82ð\8eæ\93¾
2300 \return \83o\81[\83W\83\87\83\93\82Ì\89º 2 \8c\85(dwFileVersionLS)
2301 }
2302 function TGikoSys.GetVersionBuild: Integer;
2303 var
2304         FixedFileInfo: PVSFixedFileInfo;
2305         VersionHandle, VersionSize: DWORD;
2306         pVersionInfo: Pointer;
2307         ItemLen : UInt;
2308         AppFile: string;
2309 begin
2310         Result := 0;
2311         AppFile := Application.ExeName;
2312         VersionSize := GetFileVersionInfoSize(pChar(AppFile), VersionHandle);
2313         if VersionSize = 0 then
2314                 Exit;
2315         GetMem(pVersionInfo, VersionSize);
2316         try
2317                 if GetFileVersionInfo(PChar(AppFile),VersionHandle,VersionSize, pVersionInfo) then
2318                         if VerQueryValue(pVersionInfo, '\', Pointer(FixedFileInfo), ItemLen) then
2319                                 Result := LOWORD(FixedFileInfo^.dwFileVersionLS);
2320         finally
2321                 FreeMem(pVersionInfo, VersionSize);
2322         end;
2323 end;
2324
2325 {!
2326 \brief \83X\83\8c\83b\83h URL \82Ì\90³\8bK\89»
2327 \param inURL \90³\8bK\89»\82·\82é\83X\83\8c\83b\83h URL
2328 \return      \90³\8bK\89»\82³\82ê\82½\83X\83\8c\83b\83h URL
2329
2330 \83X\83\8c\83b\83h URL \82ð\83M\83R\83i\83r\82Ì\92\86\82Å\88ê\88Ó\82È\82à\82Ì\82É\90³\8bK\89»\82µ\82Ü\82·\81B
2331 \88ê\88Ó\82È URL \82É\82·\82é\8e\96\82Å\81AURL \82©\82ç\83X\83\8c\83b\83h\82ð\93±\82«\8fo\82·\8dì\8bÆ\82ð\8dÅ\93K\89»\82µ\82Ü\82·\81B\n
2332 \90³\8bK\89»\82Ì\95û\90j\82Æ\82µ\82Ä\81A\83T\83C\83g\82ª\90\84\8f§\82·\82é\83f\83t\83H\83\8b\83g\82Ì URL \82É\82È\82é\82æ\82¤\82É\90S\82ª\82¯\82Ü\82·\81B
2333 (1-1000 \82Ì\82æ\82¤\82È\95\89\89×\82ð\82©\82¯\82é\82à\82Ì\82É\82Í\82µ\82È\82¢\82±\82Æ)
2334
2335 \97á(\90³\8bK\89»\91O):\n
2336 http://\92\86\97ª/ \n
2337 http://\92\86\97ª/20-100
2338
2339 (\90³\8bK\89»\8cã):\n
2340 http://\92\86\97ª/l50
2341 }
2342 function        TGikoSys.GetBrowsableThreadURL(
2343         inURL : string
2344 ) : string;
2345 var
2346         threadItem      : TThreadItem;
2347         boardPlugIn     : TBoardPlugIn;
2348     board               : TBoard;
2349         i                                               : Integer;
2350 begin
2351
2352         //===== \83v\83\89\83O\83C\83\93
2353         try
2354                 for i := Length( BoardGroups ) - 1 downto 1 do begin
2355                         if Assigned( Pointer( BoardGroups[i].BoardPlugIn.Module ) ) then begin
2356                                 if BoardGroups[i].BoardPlugIn.AcceptURL( inURL ) = atThread then begin
2357                     board := BBSsFindBoardFromURL( BoardGroups[i].BoardPlugIn.ExtractBoardURL(inURL) );
2358                                         if board <> nil then begin
2359                                                 boardPlugIn := BoardGroups[i].BoardPlugIn;
2360                                                 threadItem      := TThreadItem.Create( boardPlugIn, board, inURL );
2361                                                 Result                  := threadItem.URL;
2362                                                 threadItem.Free;
2363
2364                                         end;
2365                                         Exit;
2366                                 end;
2367                         end;
2368                 end;
2369         except
2370                 // exception \82ª\94­\90\82µ\82½\8fê\8d\87\82Í\93à\95\94\8f\88\97\9d\82É\94C\82¹\82½\82¢\82Ì\82Å\82±\82±\82Å\82Í\89½\82à\82µ\82È\82¢
2371         end;
2372
2373         if Length( Result ) = 0 then
2374                 Result := GikoSys.Get2chBrowsableThreadURL( inURL );
2375
2376 end;
2377
2378 {!
2379 \brief \83X\83\8c\83b\83h URL \82ð\94 URL \82É\95Ï\8a·
2380 \param inURL \83X\83\8c\83b\83h URL
2381 \return      \94 URL
2382 }
2383 function        TGikoSys.GetThreadURL2BoardURL(
2384         inURL : string
2385 ) : string;
2386 var
2387         threadItem      : TThreadItem;
2388         boardPlugIn     : TBoardPlugIn;
2389     board               : TBoard;
2390         i                                               : Integer;
2391 begin
2392
2393         //===== \83v\83\89\83O\83C\83\93
2394         try
2395                 for i := Length( BoardGroups ) - 1 downto 1 do begin
2396                         if Assigned( Pointer( BoardGroups[i].BoardPlugIn.Module ) ) then begin
2397                                 if BoardGroups[i].BoardPlugIn.AcceptURL( inURL ) = atThread then begin
2398                     board               := BBSsFindBoardFromURL(BoardGroups[i].BoardPlugIn.ExtractBoardURL(inURL));
2399                                         boardPlugIn := BoardGroups[i].BoardPlugIn;
2400                                         threadItem      := TThreadItem.Create( boardPlugIn, board, inURL );
2401                                         Result                  := BoardGroups[i].BoardPlugIn.GetBoardURL( Longword( threadItem ) );
2402                                         threadItem.Free;
2403
2404                                         Break;
2405                                 end;
2406                         end;
2407                 end;
2408         except
2409                 // exception \82ª\94­\90\82µ\82½\8fê\8d\87\82Í\93à\95\94\8f\88\97\9d\82É\94C\82¹\82½\82¢\82Ì\82Å\82±\82±\82Å\82Í\89½\82à\82µ\82È\82¢
2410         end;
2411
2412         if Length( Result ) = 0 then
2413                 Result := GikoSys.Get2chThreadURL2BoardURL( inURL );
2414
2415 end;
2416
2417 {!
2418 \brief 2ch\97p:\83X\83\8c\83b\83h URL \82ð\94 URL \82É\95Ï\8a·
2419 \param inURL \83X\83\8c\83b\83h URL
2420 \return      \94 URL
2421 \see TGikoSys.GetThreadURL2BoardURL
2422 }
2423 function        TGikoSys.Get2chThreadURL2BoardURL(
2424         inURL : string
2425 ) : string;
2426 var
2427         Protocol, Host, Path, Document, Port, Bookmark : string;
2428         BBSID, BBSKey : string;
2429         foundPos                        : Integer;
2430 begin
2431
2432         ParseURI( inURL, Protocol, Host, Path, Document, Port, Bookmark );
2433         Parse2chURL( inURL, Path, Document, BBSID, BBSKey );
2434
2435         foundPos := Pos( '/test/read.cgi', inURL );
2436         if {(Is2chHost(Host)) and} (foundPos > 0) then
2437                 Result := Copy( inURL, 1, foundPos ) + BBSID + '/'
2438         else
2439                 Result := Protocol + '://' + Host + '/' + BBSID + '/';
2440
2441 end;
2442
2443 {!
2444 \brief 2ch\97p:\83X\83\8c\83b\83h URL \82Ì\90³\8bK\89»
2445 \param inURL \90³\8bK\89»\82·\82é\83X\83\8c\83b\83h URL
2446 \return      \90³\8bK\89»\82³\82ê\82½\83X\83\8c\83b\83h URL
2447 \see TGikoSys.GetBrowsableThreadURL
2448 }
2449 function        TGikoSys.Get2chBrowsableThreadURL(
2450         inURL                   : string
2451 ) : string;
2452 var
2453         Protocol, Host, Path, Document, Port, Bookmark : string;
2454         BBSID, BBSKey : string;
2455         foundPos        : Integer;
2456 begin
2457
2458 //      if Pos( KAKO_PATH, inURL ) > 0 then begin
2459 //              Result := inURL;
2460 //      end else begin
2461                 ParseURI( inURL, Protocol, Host, Path, Document, Port, Bookmark );
2462                 Parse2chURL( inURL, Path, Document, BBSID, BBSKey );
2463                 foundPos := Pos( '/test/read.cgi', inURL ) - 1;
2464
2465                 if Is2chHost( Host ) then begin
2466                         Result := Protocol + '://' + Host +
2467                                 READ_PATH + BBSID + '/' + BBSKey + '/l50';
2468                 end else begin
2469                         if foundPos > 0 then
2470                                 Result := Copy( inURL, 1, foundPos ) +
2471                                         OLD_READ_PATH + 'bbs=' + BBSID + '&key=' + BBSKey + '&ls=50'
2472                         else
2473                                 Result := Protocol + '://' + Host +
2474                                         OLD_READ_PATH + 'bbs=' + BBSID + '&key=' + BBSKey + '&ls=50';
2475                 end;
2476 //      end;
2477
2478 end;
2479
2480 {!
2481 \brief 2ch\97p:\94 URL \82©\82ç\83X\83\8c\83b\83h URL \82ð\8dì\90¬
2482 \param inBoard \94 URL
2483 \param inKey   \83X\83\8c\83b\83h\83L\81[(ex. 1000000000)
2484 \return        \83X\83\8c\83b\83h URL
2485 }
2486 function        TGikoSys.Get2chBoard2ThreadURL(
2487         inBoard : TBoard;
2488         inKey           : string
2489 ) : string;
2490 var
2491         server  : string;
2492 begin
2493
2494         server := UrlToServer( inBoard.URL );
2495         //if Is2chHost( server ) then
2496         if inBoard.Is2ch then
2497                 Result := server + 'test/read.cgi/' + inBoard.BBSID + '/' + inKey + '/l50'
2498         else
2499                 Result := server + 'test/read.cgi?bbs=' + inBoard.BBSID + '&key=' + inKey + '&ls=50';
2500
2501 end;
2502
2503 {!
2504 \brief \83{\81[\83h\83t\83@\83C\83\8b\97ñ\8b\93
2505
2506 \97ñ\8b\93\82³\82ê\82½ BBS(\83{\81[\83h) \82Í BBSs \82É\93ü\82è\82Ü\82·\81B
2507 }
2508 procedure TGikoSys.ListBoardFile;
2509 var
2510         boardFileList   : TStringList;
2511         i, l                    : Integer;
2512     sCategory       : TCategory;
2513 begin
2514         // BBS \82Ì\8aJ\95ú
2515         try
2516           for i := 0 to Length( BBSs ) - 1 do
2517                 BBSs[ i ].Free;
2518         except
2519         end;
2520         SetLength( BBSs, 0 );
2521
2522         l := 0;
2523         // \94Â\83\8a\83X\83g\82Ì\97ñ\8b\93
2524         if FileExists( GikoSys.GetBoardFileName ) then begin
2525           SetLength( BBSs, l + 1 );
2526           BBSs[ l ]                             := TBBS.Create( GikoSys.GetBoardFileName );
2527           BBSs[ l ].Title       := '\82Q\82¿\82á\82ñ\82Ë\82é';
2528                   Inc( l );
2529         end;
2530
2531         if FileExists( GikoSys.GetCustomBoardFileName ) then begin
2532           SetLength( BBSs, l + 1 );
2533           BBSs[ l ]                             := TBBS.Create( GikoSys.GetCustomBoardFileName );
2534           BBSs[ l ].Title       := '\82»\82Ì\91¼';
2535                   Inc( l );
2536         end;
2537
2538         // Board \83t\83H\83\8b\83_
2539         if DirectoryExists( GikoSys.Setting.GetBoardDir ) then begin
2540           BoardFileList := TStringList.Create;
2541           try
2542         BoardFileList.BeginUpdate;
2543                 GikoSys.GetFileList( GikoSys.Setting.GetBoardDir, '*.txt', BoardFileList, True, True );
2544         BoardFileList.EndUpdate;
2545         SetLength( BBSs, l + BoardFileList.Count );
2546                 for i := BoardFileList.Count - 1 downto 0 do begin
2547                   BBSs[ l ]                             := TBBS.Create( BoardFileList[ i ] );
2548                   BBSs[ l ].Title       := ChangeFileExt( ExtractFileName( BoardFileList[ i ] ), '' );
2549                   Inc( l );
2550                 end;
2551           finally
2552                 BoardFileList.Free;
2553           end;
2554         end;
2555
2556     // \93Á\8eê\97p\93rBBS\90\90¬
2557     // \8aù\82É\91\8dÝ\82·\82é\8fê\8d\87\82Í\8dí\8f\9c\82·\82é
2558     DestorySpecialBBS(BoardGroup.SpecialBBS);
2559     SpecialBBS := TBBS.Create('');
2560     SpecialBBS.Title := '\93Á\8eê\97p\93r(\94ñ\95\\8e¦)';
2561     sCategory := TCategory.Create;
2562     sCategory.No := 1;
2563     sCategory.Title := '\93Á\8eê\97p\93r(\94ñ\95\\8e¦)';
2564     SpecialBBS.Add(sCategory);
2565     BoardGroup.SpecialBoard := TSpecialBoard.Create(nil, 'http://localhost/gikonavi/special/index.html');
2566     BoardGroup.SpecialBoard.Title := '\83^\83u\88ê\97\97';
2567     BoardGroup.SpecialBoard.IsThreadDatRead := True;
2568     sCategory.Add(BoardGroup.SpecialBoard);
2569 end;
2570
2571 {!
2572 \brief \83{\81[\83h\83t\83@\83C\83\8b\93Ç\82Ý\8d\9e\82Ý
2573 \param bbs \83{\81[\83h\83t\83@\83C\83\8b\82ð\93Ç\82Ý\8d\9e\82Þ BBS
2574 }
2575 procedure TGikoSys.ReadBoardFile( bbs : TBBS );
2576 var
2577 //      idx                                             : Integer;
2578         ini                                             : TMemIniFile;
2579         p : Integer;
2580         boardFile                       : TStringList;
2581         CategoryList    : TStringList;
2582         BoardList                       : TStringList;
2583         Category                        : TCategory;
2584         Board                                   : TBoard;
2585         inistr                          : string;
2586         tmpstring                       : string;
2587 //      RoundItem                       : TRoundItem;
2588
2589         i, iBound                       : Integer;
2590         j, jBound                       : Integer;
2591         k, kBound                       : Integer;
2592 begin
2593
2594         if not FileExists( bbs.FilePath ) then
2595                 Exit;
2596
2597         bbs.Clear;
2598         ini := TMemIniFile.Create('');
2599         boardFile := TStringList.Create;
2600
2601         try
2602                 boardFile.LoadFromFile( bbs.FilePath );
2603
2604                 ini.SetStrings( boardFile );
2605                 CategoryList    := TStringList.Create;
2606                 BoardList                       := TStringList.Create;
2607                 try
2608                         ini.ReadSections( CategoryList );
2609
2610                         iBound := CategoryList.Count - 1;
2611                         for i := 0 to iBound do begin
2612                                 ini.ReadSection( CategoryList[i], BoardList );
2613                                 Category                                := TCategory.Create;
2614                                 Category.No                     := i + 1;
2615                                 Category.Title  := CategoryList[i];
2616
2617                                 jBound := BoardList.Count - 1;
2618                                 for j := 0 to jBound do begin
2619                                         Board := nil;
2620                                         inistr := ini.ReadString(CategoryList[i], BoardList[j], '');
2621                                         //'http://'\82ð\8aÜ\82Ü\82È\82¢\95\8e\9a\97ñ\82Ì\8e\9e\82Í\96³\8e\8b\82·\82é
2622                                         if (AnsiPos('http://', AnsiLowerCase(inistr)) = 0) then Continue;
2623                                         //===== \83v\83\89\83O\83C\83\93
2624                                         try
2625                                                 kBound := Length(BoardGroups) - 1;
2626                                                 for k := 1 to kBound do begin  //0\82Í\81A2\82¿\82á\82ñ
2627                                                         if Assigned( Pointer( BoardGroups[k].BoardPlugIn.Module ) ) then begin
2628                                                                 if BoardGroups[k].BoardPlugIn.AcceptURL( inistr ) = atBoard then begin
2629                                                                         if not BoardGroups[k].Find(inistr, p) then begin
2630                                                                                 tmpstring := BoardGroups[k].BoardPlugIn.ExtractBoardURL( inistr );
2631                                                                                 if AnsiCompareStr(tmpString, inistr) <> 0 then begin
2632                                                                                         if not BoardGroups[k].Find(tmpstring, p) then begin
2633                                                                                                 try
2634                                                                                                         Board := TBoard.Create( BoardGroups[k].BoardPlugIn, tmpstring );
2635                                                                                                         BoardGroups[k].AddObject(tmpstring, Board);
2636                                                                                                         Category.Add(Board);
2637                                                                                                 except
2638                                                                                                         //\82±\82±\82É\97\88\82é\82Æ\82µ\82½\82çBoard\82Ì\8dì\90¬\82É\8e¸\94s\82µ\82½\82Æ\82«\82¾\82©\82çBoard\82ðnil\82É\82·\82é
2639                                                                                                         Board := nil;
2640                                                                                                 end;
2641                                                                                         end else begin
2642                                                                                                 Board := TBoard(BoardGroups[k].Objects[p]);
2643                                                                                                 if Board.ParentCategory <> Category then
2644                                                                                                         Category.Add(Board);
2645                                                                                         end;
2646                                                                                 end else begin
2647                                                                                         try
2648                                                                                                 Board := TBoard.Create( BoardGroups[k].BoardPlugIn, tmpstring );
2649                                                                                                 BoardGroups[k].AddObject(tmpstring, Board);
2650                                                                                                 Category.Add(Board);
2651                                                                                         except
2652                                                                                                 //\82±\82±\82É\97\88\82é\82Æ\82µ\82½\82çBoard\82Ì\8dì\90¬\82É\8e¸\94s\82µ\82½\82Æ\82«\82¾\82©\82çBoard\82ðnil\82É\82·\82é
2653                                                                                                 Board := nil;
2654                                                                                         end;
2655                                                                                 end;
2656                                                                         end else begin
2657                                                                                 Board := TBoard(BoardGroups[k].Objects[p]);
2658                                                                                 if Board.ParentCategory <> Category then
2659                                                                                         Category.Add(Board);
2660                                                                         end;
2661                                                                         Break;
2662                                                                 end;
2663                                                         end;
2664                                                 end;
2665                                         except
2666                                                 // exception \82ª\94­\90\82µ\82½\8fê\8d\87\82Í\93à\95\94\8f\88\97\9d\82É\94C\82¹\82½\82¢\82Ì\82Å\82±\82±\82Å\82Í\89½\82à\82µ\82È\82¢
2667                                         end;
2668                                         try
2669                                                 if (Board = nil) then begin
2670                                                         if not BoardGroups[0].Find(inistr,p) then begin
2671                                                                 Board := TBoard.Create( nil, inistr );
2672                                                                 BoardGroups[0].AddObject(inistr, Board);
2673                                                                 Category.Add(Board);
2674                                                         end else begin
2675                                                                 Board := TBoard(BoardGroups[0].Objects[p]);
2676                                                                 if Board.ParentCategory <> Category then
2677                                                                         Category.Add(Board);
2678                                                         end;
2679                                                 end;
2680
2681                                                 if (Board.Multiplicity = 0) then begin
2682                                                         Board.BeginUpdate;
2683                                                         Board.No := j + 1;
2684                             Board.Multiplicity := 1;
2685                                                         Board.Title := BoardList[j];
2686                                                         Board.RoundDate := ZERO_DATE;
2687                                                         Board.LoadSettings;
2688                                                         Board.EndUpdate;
2689                                                 end else begin
2690                                                         Board.No := j + 1;
2691                                                         Board.Multiplicity := Board.Multiplicity + 1;
2692                                                 end;
2693                                         except
2694                                         end;
2695                                 end;
2696                                 bbs.Add( Category );
2697                         end;
2698
2699
2700                   //end;
2701                   bbs.IsBoardFileRead := True;
2702           finally
2703                 BoardList.Free;
2704                 CategoryList.Free;
2705           end;
2706   finally
2707         boardFile.Free;
2708         ini.Free;
2709   end;
2710
2711 end;
2712
2713 {!
2714 \brief \96¼\8fÌ\82ª\95s\96¾\82È\83J\83e\83S\83\8a\82Ì\90\90¬
2715 \return \90\90¬\82³\82ê\82½\83J\83e\83S\83\8a
2716 }
2717 function        TGikoSys.GetUnknownCategory : TCategory;
2718 const
2719         UNKNOWN_CATEGORY = '(\96¼\8fÌ\95s\96¾)';
2720 begin
2721
2722         if Length( BBSs ) < 2 then begin
2723                 Result := nil;
2724                 Exit;
2725         end;
2726
2727         Result := BBSs[ 1 ].FindCategoryFromTitle( UNKNOWN_CATEGORY );
2728         if Result = nil then begin
2729                 Result                          := TCategory.Create;
2730                 Result.Title    := UNKNOWN_CATEGORY;
2731                 BBSs[ 1 ].Add( Result );
2732         end;
2733
2734 end;
2735
2736 {!
2737 \brief \96¼\8fÌ\82ª\95s\96¾\82È BBS \82Ì\90\90¬
2738 \return \90\90¬\82³\82ê\82½ BBS
2739 }
2740 function        TGikoSys.GetUnknownBoard( inPlugIn : TBoardPlugIn; inURL : string ) : TBoard;
2741 var
2742         category : TCategory;
2743 const
2744         UNKNOWN_BOARD = '(\96¼\8fÌ\95s\96¾)';
2745 begin
2746
2747         category := GetUnknownCategory;
2748         if category = nil then begin
2749                 Result := nil;
2750         end else begin
2751                 Result := category.FindBoardFromTitle( UNKNOWN_BOARD );
2752                 if Result = nil then begin
2753                         Result                          := TBoard.Create( inPlugIn, inURL );
2754                         Result.Title    := UNKNOWN_BOARD;
2755                         category.Add( Result );
2756                 end;
2757         end;
2758
2759 end;
2760
2761 //! Samba.ini
2762 function TGikoSys.GetSambaFileName : string;
2763 begin
2764         Result := Setting.GetSambaFileName;
2765 end;
2766 {!
2767 \brief \97ñ\8b\93\82³\82ê\82½\83\8c\83X\94Ô\8d\86\82Ö\82Ì\83A\83\93\83J\81[\97pHTML\8dì\90¬
2768 \param Numbers    \97ñ\8b\93\82³\82ê\82½\83\8c\83X\94Ô\8d\86
2769 \param ThreadItem \97ñ\8b\93\82·\82é\83X\83\8c\83b\83h
2770 \param limited    \97ñ\8b\93\82·\82é\90\94\82ð\90§\8cÀ\82·\82é\82È\82ç1\88È\8fã
2771 \return           \97ñ\8b\93\82³\82ê\82½\83\8c\83X\83A\83\93\83J\81[
2772 }
2773 function TGikoSys.CreateSameIDResAnchor(
2774     var Numbers: TStringList; ThreadItem: TThreadItem;
2775     limited: Integer):string;
2776 var
2777         i: integer;
2778     Res: TResRec;
2779     ResLink : TResLinkRec;
2780 begin
2781     // body\88È\8aO\82Í\8eg\97p\82µ\82È\82¢\82Ì\82Å\8f\89\8aú\89»\82µ\82È\82¢
2782     Res.FBody := '';
2783     Res.FType := glt2chNew;
2784
2785         Result := '';
2786         if (Numbers <> nil) and (Numbers.Count > 0) then begin
2787         if (limited > 0) and (Numbers.Count > limited) then begin
2788             for i := Numbers.Count - limited to Numbers.Count - 1 do begin
2789                 Res.FBody := Res.FBody + '&gt;' + Numbers[i] + ' ';
2790             end;
2791         end else begin
2792             for i := 0 to Numbers.Count - 1 do begin
2793                 Res.FBody := Res.FBody + '&gt;' + Numbers[i] + ' ';
2794             end;
2795         end;
2796         ResLink.FBbs := ThreadItem.ParentBoard.BBSID;
2797         ResLink.FKey := ChangeFileExt(ThreadItem.FileName, '');
2798         HTMLCreater.ConvRes(@Res, @ResLink, false);
2799         Result := Res.FBody;
2800     end;
2801 end;
2802
2803 {!
2804 \brief \93¯\82\93\8a\8de ID \82ð\8e\9d\82Â\83\8c\83X\82ð\97ñ\8b\93
2805 \param AID        \8cÂ\90l\82ð\93Á\92è\82·\82é\93\8a\8de ID
2806 \param ThreadItem \97ñ\8b\93\82·\82é\83X\83\8c\83b\83h
2807 \param body       OUT:\97ñ\8b\93\82³\82ê\82½\83\8c\83X\94Ô\8d\86\82ª\95Ô\82é
2808 }
2809 procedure TGikoSys.GetSameIDRes(const AID : string; ThreadItem: TThreadItem;var body: TStringList);
2810 var
2811         i: integer;
2812         ReadList: TStringList;
2813         Res: TResRec;
2814         boardPlugIn : TBoardPlugIn;
2815
2816     procedure CheckSameID(const AID:String; const Target: String; no: Integer);
2817     var
2818         pos: Integer;
2819     begin
2820         pos := AnsiPos('id:', LowerCase(Target));
2821         if (pos > 0) then begin
2822             if(AnsiPos(AID, Copy(Target, pos-1, Length(Target))) > 0) then begin
2823                 body.Add(IntToStr(no));
2824             end;
2825         end else begin
2826             if(AnsiPos(AID, Target) > 0) then begin
2827                 body.Add(IntToStr(no));
2828             end;
2829         end;
2830     end;
2831 begin
2832         if (not IsNoValidID(AID)) and
2833         (ThreadItem <> nil) and (ThreadItem.IsLogFile) then begin
2834                 //if ThreadItem.IsBoardPlugInAvailable then begin
2835         if ThreadItem.ParentBoard.IsBoardPlugInAvailable then begin
2836                         //===== \83v\83\89\83O\83C\83\93\82É\82æ\82é\95\\8e¦
2837                         //boardPlugIn           := ThreadItem.BoardPlugIn;
2838             boardPlugIn         := ThreadItem.ParentBoard.BoardPlugIn;
2839
2840                         for i := 0 to threadItem.Count - 1 do begin
2841                                 // \83\8c\83X
2842                                 THTMLCreate.DivideStrLine(boardPlugIn.GetDat(DWORD( threadItem ), i + 1), @Res);
2843                 CheckSameID(AID, Res.FDateTime, i+1);
2844                         end;
2845                 end else begin
2846                         ReadList := TStringList.Create;
2847                         try
2848                                 ReadList.LoadFromFile(ThreadItem.GetThreadFileName);
2849                                 for i := 0 to ReadList.Count - 1 do begin
2850                                         THTMLCreate.DivideStrLine(ReadList[i], @Res);
2851                     CheckSameID(AID, Res.FDateTime, i+1);
2852                                 end;
2853                         finally
2854                                 ReadList.Free;
2855                         end;
2856                 end;
2857         end;
2858 end;
2859
2860 {!
2861 \brief \93¯\82\93\8a\8de ID \82ð\8e\9d\82Â\83\8c\83X\82ð\97ñ\8b\93
2862 \param AIDNum     \8cÂ\90l\82ð\93Á\92è\82·\82é\93\8a\8de ID
2863 \param ThreadItem \97ñ\8b\93\82·\82é\83X\83\8c\83b\83h
2864 \param body       OUT:\97ñ\8b\93\82³\82ê\82½\83\8c\83X\94Ô\8d\86\82ª\95Ô\82é
2865 }
2866 procedure TGikoSys.GetSameIDRes(AIDNum : Integer; ThreadItem: TThreadItem;var body: TStringList);
2867 var
2868         AID : String;
2869 begin
2870     AID := GetResID(AIDNum, ThreadItem);
2871     if not IsNoValidID(AID) then begin
2872             GetSameIDRes(AID, ThreadItem, body);
2873         end;
2874 end;
2875 {!
2876 \brief \93\8a\8de ID \8eæ\93¾
2877 \param AIDNum     \93\8a\8d\83\8c\83X\94Ô\8d\86
2878 \param ThreadItem \93\8a\8de\83X\83\8c\83b\83h
2879 \param body       OUT:\93\8a\8deID
2880 }
2881 function TGikoSys.GetResID(AIDNum: Integer; ThreadItem: TThreadItem): String;
2882 var
2883         Res: TResRec;
2884         boardPlugIn : TBoardPlugIn;
2885         stList: TStringList;
2886         i : Integer;
2887 begin
2888     Result := '';
2889         if (ThreadItem <> nil) and (ThreadItem.IsLogFile)
2890                 and (AIDNum > 0) and (AIDNum <= ThreadItem.Count) then begin
2891                 //if ThreadItem.IsBoardPlugInAvailable then begin
2892         if ThreadItem.ParentBoard.IsBoardPlugInAvailable then begin
2893                         //===== \83v\83\89\83O\83C\83\93\82É\82æ\82é\95\\8e¦
2894                         //boardPlugIn           := ThreadItem.BoardPlugIn;
2895             boardPlugIn         := ThreadItem.ParentBoard.BoardPlugIn;
2896                         THTMLCreate.DivideStrLine(boardPlugIn.GetDat(DWORD( threadItem ), AIDNum), @Res);
2897                 end else begin
2898                         THTMLCreate.DivideStrLine( ReadThreadFile(ThreadItem.GetThreadFileName, AIDNum), @Res);
2899                 end;
2900                 Result := Res.FDateTime;
2901                 if AnsiPos('id', AnsiLowerCase(Result)) > 0 then begin
2902                         Result := Copy(Result, AnsiPos('id', AnsiLowerCase(Result)) - 1, 11);
2903             if AnsiPos(' be:', AnsiLowerCase(Result)) > 0 then begin
2904                 Result := Copy(Result, 1, AnsiPos(' BE:', AnsiLowerCase(Result)) - 1)
2905             end;
2906                 end else begin
2907                         stlist := TStringList.Create;
2908                         try
2909                                 stList.DelimitedText := Result;
2910                 Result := '';
2911                                 for i := 0 to stList.Count - 1 do
2912                                         if Length(WideString(stList[i])) = 8 then begin
2913                                                 if NotDateorTimeString(stList[i]) then begin
2914                                                         Result := stList[i];
2915                                                         break;
2916                                                 end;
2917                                         end;
2918                         finally
2919                                 stList.Free;
2920                         end;
2921                 end;
2922         end;
2923 end;
2924
2925 {!
2926 \brief \8e\9e\8d\8f\82ð\8e¦\82·\95\8e\9a\97ñ\82Å\82Í\96³\82¢\82©\82Ç\82¤\82©
2927 \param AStr \92²\82×\82é\95\8e\9a\97ñ
2928 \return     \8e\9e\8d\8f\82Å\82Í\96³\82¢\82È\82ç True
2929 \todo \94Û\92è\8c`(Not)\82æ\82è\8dm\92è\8cn(Is)
2930 }
2931 function TGikoSys.NotDateorTimeString(const AStr : string): boolean;
2932 begin
2933         Result := false;
2934         try
2935                 StrToDate(AStr);
2936         except
2937                 try
2938                         StrToTime(AStr);
2939                         Result := false;
2940                 except
2941                         Result := true;
2942                 end;
2943         end;
2944
2945 end;
2946
2947 {!
2948 \brief \83X\83p\83\80:\8cê\90\94\82ð\83J\83E\83\93\83g
2949 \param text      \8c³\82É\82È\82é\95\8fÍ
2950 \param wordCount OUT:\83J\83E\83\93\83g\82³\82ê\82½\92P\8cê\82Ì\88ê\97\97\82ª\95Ô\82é
2951 }
2952 procedure TGikoSys.SpamCountWord( const text : string; wordCount : TWordCount );
2953 begin
2954
2955         if Setting.SpamFilterAlgorithm = gsfaNone then Exit;
2956         Bayesian.CountWord( text, wordCount );
2957
2958 end;
2959
2960 {!
2961 \brief \83X\83p\83\80:\8aw\8fK\8c\8b\89Ê\82ð\95ú\8aü
2962 \param wordCount \95ú\8aü\82·\82é\92P\8cê\82Ì\88ê\97\97
2963 \param isSpam    wordCount \82ª\83X\83p\83\80\82Æ\82µ\82Ä\8aw\8fK\82³\82ê\82Ä\82¢\82½\82È\82ç True
2964 \warning        \8aw\8fK\8dÏ\82Ý\82Ì\95\8fÍ\82©\82Ç\82¤\82©\82Í\8am\94F\8fo\97\88\82Ü\82¹\82ñ\81B\n
2965                                         Learn \82µ\82Ä\82¢\82È\82¢\95\8fÍ\82â isSpam \82ð\8aÔ\88á\82¦\82Ä\8ew\92è\82·\82é\82Æ
2966                                         \83f\81[\83^\83x\81[\83X\82ª\94j\91¹\82µ\82Ü\82·\81B\n
2967                                         \8aw\8fK\8dÏ\82Ý\82©\82Ç\82¤\82©\82Í\93Æ\8e©\82É\8aÇ\97\9d\82µ\82Ä\82­\82¾\82³\82¢\81B
2968
2969 \91S\82Ä\82Ì\8aw\8fK\8c\8b\89Ê\82ð\83N\83\8a\83A\82·\82é\82í\82¯\82Å\82Í\82 \82è\82Ü\82¹\82ñ\81B\n
2970 wordCount \82ð\93¾\82½\95\8fÍ\82Ì\8aw\8fK\8c\8b\89Ê\82Ì\82Ý\83N\83\8a\83A\82µ\82Ü\82·\81B
2971
2972 \8eå\82É\83X\83p\83\80\82Æ\83n\83\80\82ð\90Ø\82è\91Ö\82¦\82é\82½\82ß\82É Forget -> Learn \82Ì\8f\87\82Å\8eg\97p\82µ\82Ü\82·\81B
2973 }
2974 procedure TGikoSys.SpamForget( wordCount : TWordCount; isSpam : Boolean );
2975 begin
2976
2977         if Setting.SpamFilterAlgorithm = gsfaNone then Exit;
2978         Bayesian.Forget( wordCount, isSpam );
2979
2980 end;
2981
2982 {!
2983 \brief \83X\83p\83\80:\8aw\8fK
2984 \param wordCount \8aw\8fK\82·\82é\92P\8cê\82Ì\88ê\97\97
2985 \param isSpam    \83X\83p\83\80\82Æ\82µ\82Ä\8aw\8fK\82·\82é\82È\82ç True
2986 }
2987 procedure TGikoSys.SpamLearn( wordCount : TWordCount; isSpam : Boolean );
2988 begin
2989
2990         if Setting.SpamFilterAlgorithm = gsfaNone then Exit;
2991         Bayesian.Learn( wordCount, isSpam );
2992
2993 end;
2994
2995 {!
2996 \brief \83X\83p\83\80:\95\8fÍ\82ð\89ð\90Í\82µ\81A\83X\83p\83\80\93x\90\94\82ð\93¾\82é
2997 \param text      \8c³\82É\82È\82é\95\8fÍ
2998 \param wordCount OUT:\83J\83E\83\93\83g\82³\82ê\82½\92P\8cê\82Ì\88ê\97\97\82ª\95Ô\82é(SpamCountWord \82Æ\93¯\93\99)
2999 \return          0\81`1 \82Ì\83X\83p\83\80\93x\90\94
3000 }
3001 function TGikoSys.SpamParse( const text : string; wordCount : TWordCount ) : Extended;
3002 begin
3003
3004         case Setting.SpamFilterAlgorithm of
3005         gsfaNone:                                                               Result := 0;
3006         gsfaPaulGraham:                                 Result := Bayesian.Parse( text, wordCount, gbaPaulGraham );
3007         gsfaGaryRobinson:                               Result := Bayesian.Parse( text, wordCount, gbaGaryRobinson );
3008         gsfaGaryRobinsonFisher: Result := Bayesian.Parse( text, wordCount, gbaGaryRobinsonFisher );
3009         else                                                                            Result := 0;
3010         end;
3011
3012 end;
3013
3014 {!
3015 \brief \83\86\81[\83U\90Ý\92è\82Ì CSS \82ð\90\90¬
3016 \return \90\90¬\82³\82ê\82½ CSS
3017
3018 [\83c\81[\83\8b]\83\81\83j\83\85\81[-[\83I\83v\83V\83\87\83\93]-[CSS \82Æ\83X\83L\83\93]\83^\83u\82Ì
3019 [\83t\83H\83\93\83g\82ð\8ew\92è], [\94w\8ci\90F\82ð\8ew\92è] \82É\89\88\82Á\82½ CSS \82ð\90\90¬\82µ\82Ü\82·\81B
3020 }
3021 function TGikoSys.SetUserOptionalStyle(): string;
3022 begin
3023                 Result := '';
3024         if Length( GikoSys.Setting.BrowserFontName ) > 0 then
3025                 Result := 'font-family:"' + GikoSys.Setting.BrowserFontName + '";';
3026         if GikoSys.Setting.BrowserFontSize <> 0 then
3027                 Result := Result + 'font-size:' + IntToStr( GikoSys.Setting.BrowserFontSize ) + 'pt;';
3028         if GikoSys.Setting.BrowserFontColor <> -1 then
3029                 Result := Result + 'color:#' + IntToHex( GikoSys.Setting.BrowserFontColor, 6 ) + ';';
3030         if GikoSys.Setting.BrowserBackColor <> -1 then
3031                 Result := Result + 'background-color:#' + IntToHex( GikoSys.Setting.BrowserBackColor, 6 ) + ';';
3032         case GikoSys.Setting.BrowserFontBold of
3033                 -1: Result := Result + 'font-weight:normal;';
3034                 1:  Result := Result + 'font-weight:bold;';
3035         end;
3036         case GikoSys.Setting.BrowserFontItalic of
3037                 -1: Result := Result + 'font-style:normal;';
3038                 1:  Result := Result + 'font-style:italic;';
3039         end;
3040 end;
3041
3042 {!
3043 \brief Be \83v\83\8d\83t\83@\83C\83\8b\82Ö\82Ì\83A\83\93\83J\81[\83^\83O\82ð\90\90¬
3044 \param AID  \91Î\8fÛ\82Æ\82È\82é\93ú\95tID\95\8e\9a\97ñ
3045 \param ANum \83\8c\83X\94Ô
3046 \param AURL \82»\82Ì\83X\83\8c\83b\83h\82ÌURL
3047 \return     \90\90¬\82³\82ê\82½\83A\83\93\83J\81[\83^\83O
3048 }
3049 function TGikoSys.AddBeProfileLink(AID : string; ANum: Integer):string ;
3050 var
3051         p : integer;
3052         BNum, BMark : string;
3053 begin
3054         p := AnsiPos('BE:', AnsiUpperCase(AID));
3055         if p > 0 then begin
3056                 BNum := Copy(AID, p, Length(AID));
3057                 AID := Copy(AID, 1, p - 1);
3058                 p := AnsiPos('-', BNum);
3059                 if p > 0 then begin
3060                         BMark := '?' + Trim(Copy(BNum, p + 1, Length(BNum)));
3061                         BNum := Copy(BNum, 1, p - 1);
3062                 end;
3063                 BNum := Trim(BNum);
3064                 Result := AID + ' <a href="'  + BNum + '/' + IntToStr(ANum)
3065                         + '" target=_blank>' + BMark + '</a>';
3066         end else
3067                 Result := AID;
3068 end;
3069
3070 {!
3071 \brief \83o\81[\83W\83\87\83\93\8fî\95ñ\82ð\8eæ\93¾
3072 \param KeyWord \8eæ\93¾\82·\82é\8d\80\96Ú
3073 \return        \83o\81[\83W\83\87\83\93\95\8e\9a\97ñ
3074 }
3075 function TGikoSys.GetVersionInfo(KeyWord: TVerResourceKey): string;
3076 const
3077         Translation = '\VarFileInfo\Translation';
3078         FileInfo = '\StringFileInfo\%0.4s%0.4s\';
3079 var
3080         BufSize, HWnd: DWORD;
3081         VerInfoBuf: Pointer;
3082         VerData: Pointer;
3083         VerDataLen: Longword;
3084         PathLocale: String;
3085 begin
3086         // \95K\97v\82È\83o\83b\83t\83@\82Ì\83T\83C\83Y\82ð\8eæ\93¾
3087         BufSize := GetFileVersionInfoSize(PChar(Application.ExeName), HWnd);
3088         if BufSize <> 0 then begin
3089                 // \83\81\83\82\83\8a\82ð\8am\95Û
3090                 GetMem(VerInfoBuf, BufSize);
3091                 try
3092                         GetFileVersionInfo(PChar(Application.ExeName), 0, BufSize, VerInfoBuf);
3093                         // \95Ï\90\94\8fî\95ñ\83u\83\8d\83b\83N\93à\82Ì\95Ï\8a·\83e\81[\83u\83\8b\82ð\8ew\92è
3094                         VerQueryValue(VerInfoBuf, PChar(Translation), VerData, VerDataLen);
3095
3096                         if not (VerDataLen > 0) then
3097                                 raise Exception.Create('\8fî\95ñ\82Ì\8eæ\93¾\82É\8e¸\94s\82µ\82Ü\82µ\82½');
3098
3099                         // 8\8c\85\82Ì\82P\82U\90i\90\94\82É\95Ï\8a·
3100                         // \81¨'\StringFileInfo\027382\FileDescription'
3101                         PathLocale := Format(FileInfo + KeyWordStr[KeyWord],
3102                         [IntToHex(Integer(VerData^) and $FFFF, 4),
3103                         IntToHex((Integer(VerData^) shr 16) and $FFFF, 4)]);
3104                         VerQueryValue(VerInfoBuf, PChar(PathLocale), VerData, VerDataLen);
3105
3106                         if VerDataLen > 0 then begin
3107                                 // VerData\82Í\83[\83\8d\82Å\8fI\82í\82é\95\8e\9a\97ñ\82Å\82Í\82È\82¢\82±\82Æ\82É\92\8d\88Ó
3108                                 result := '';
3109                                 SetLength(result, VerDataLen);
3110                                 StrLCopy(PChar(result), VerData, VerDataLen);
3111                         end;
3112                 finally
3113                         // \89ð\95ú
3114                         FreeMem(VerInfoBuf);
3115                 end;
3116         end;
3117 end;
3118
3119 {!
3120 \brief Load \82³\82ê\82Ä\82¢\82é\83v\83\89\83O\83C\83\93\82Ì\83o\81[\83W\83\87\83\93\82ð\97ñ\8b\93
3121 \return 1\8ds1plugin
3122 }
3123 function TGikoSys.GetPluginsInfo(): String;
3124 var
3125         i : Integer;
3126         major, minor, revision : Cardinal;
3127         agent, release : String;
3128 begin
3129         //\8c\8b\89Ê\82ð\83N\83\8a\83A\82µ\82Ä\82¨\82­
3130         Result := '';
3131
3132         //BoardGroups\8co\97R\82ÅPlugin\82É\83A\83N\83Z\83X\82·\82é
3133         for  i := 0 to Length(BoardGroups) - 1 do begin
3134                 //BoardGroups\82Ì\92\86\82É\82Í\81APlugin\82ð\8e\9d\82Á\82Ä\82¢\82È\82¢\82Ì\81i2\82¿\82á\82ñ\81j\82ª
3135                 //\82¢\82é\82Ì\82Å\82»\82ê\82ð\8f\9c\82­
3136                 if BoardGroups[i].BoardPlugIn <> nil then begin
3137                         BoardGroups[i].BoardPlugIn.VersionInfo(agent, major, minor, release, revision);
3138
3139
3140                         //"Plugin\82Ì\96¼\91O(major.minor.revision)"
3141                         Result := Result +
3142                                 Format('%s(%d.%d.%d)', [agent, major, minor, revision]) + #13#10;
3143                 end;
3144         end;
3145 end;
3146
3147
3148 //! IE\82Ì\83o\81[\83W\83\87\83\93\82ð\8eæ\93¾\82·\82é
3149 function TGikoSys.GetIEVersion: string;
3150 var
3151         R: TRegistry;
3152 begin
3153         R := TRegistry.Create;
3154         try
3155                 //\93Ç\82Ý\8eæ\82è\90ê\97p\82É\82µ\82È\82¢\82Æ\81A\90§\8cÀUSER\82Æ\82©\82Ì\8fê\8d\87\81A\8aJ\82¯\82È\82¢\82Ý\82½\82¢
3156                 R.Access := KEY_EXECUTE;
3157                 R.RootKey := HKEY_LOCAL_MACHINE;
3158                 R.OpenKey('Software\Microsoft\Internet Explorer', False);
3159                 try
3160                         Result := R.ReadString('version');
3161                 except
3162                         Result := '\83o\81[\83W\83\87\83\93\82Ì\8eæ\93¾\82É\8e¸\94s\82µ\82Ü\82µ\82½\81B';
3163                 end;
3164                 R.CloseKey;
3165         finally
3166                 R.Free;
3167         end;
3168 end;
3169 //! main\83t\83H\81[\83\80\82Ì\83V\83\87\81[\83g\83J\83b\83g\83L\81[\82ÌIni\83t\83@\83C\83\8b\96¼
3170 function TGikoSys.GetMainKeyFileName : String;
3171 begin
3172         Result := Setting.GetMainKeyFileName;
3173 end;
3174 //! Editor\83t\83H\81[\83\80\82Ì\83V\83\87\81[\83g\83J\83b\83g\83L\81[\82ÌIni\83t\83@\83C\83\8b\96¼
3175 function TGikoSys.GetEditorKeyFileName: String;
3176 begin
3177         Result := Setting.GetEditorKeyFileName;
3178 end;
3179 //! \93ü\97Í\83A\83V\83X\83g\82Ì\90Ý\92è\83t\83@\83C\83\8b\96¼
3180 function TGikoSys.GetInputAssistFileName: String;
3181 begin
3182         Result := Setting.GetInputAssistFileName;
3183 end;
3184 //! \83M\83R\83i\83r\82Ì\83\81\83b\83Z\81[\83W\82ð\90Ý\92è\82·\82é
3185 procedure TGikoSys.SetGikoMessage;
3186 begin
3187         if FGikoMessage = nil then begin
3188                 FGikoMessage := TGikoMessage.Create;
3189         end else begin
3190                 FGikoMessage.Clear;
3191         end;
3192
3193         if (Setting.GengoSupport) then begin
3194                 try
3195                         if (FileExists(Setting.GetLanguageFileName)) then begin
3196                                 FGikoMessage.LoadFromFile(Setting.GetLanguageFileName);
3197                         end;
3198                 except
3199                         FGikoMessage.Clear;
3200                 end;
3201         end;
3202 end;
3203 //! \83M\83R\83i\83r\82Ì\83\81\83b\83Z\81[\83W\82ð\8eæ\93¾\82·\82é
3204 function TGikoSys.GetGikoMessage(MesType: TGikoMessageListType): String;
3205 begin
3206     Result := '';
3207         if FGikoMessage <> nil then begin
3208                 Result := FGikoMessage.GetMessage(MesType);
3209         end;
3210 end;
3211
3212 //Tue, 17 Dec 2002 12:18:07 GMT \81¨ TDateTime\82Ö
3213 //MonaUtils\82©\82ç\88Ú\93®
3214 function  TGikoSys.DateStrToDateTime(const DateStr: string): TDateTime;
3215         function  StrMonthToMonth(const s: string): integer;
3216         const
3217                 m: array[1..12] of string = ('Jan','Feb','Mar','Apr','May','Jun', 'Jul','Aug','Sep','Oct','Nov','Dec');
3218         var
3219                 i: integer;
3220         begin
3221                 Result  :=  -1;
3222                 for i :=  Low(m)  to  High(m) do  begin
3223                         if  (SameText(s, m[i]))  then  begin
3224                                 Result  :=  i;
3225                                 Break;
3226                         end;
3227                 end;
3228         end;
3229 var
3230         wDay, wMonth, wYear: word;
3231         wHour, wMinute, wSecond: word;
3232         sTime: string;
3233         d: TDateTime;
3234 begin
3235         wDay    :=  StrToIntDef(ChooseString(DateStr, ' ', 1), 0);
3236         wMonth  :=  StrMonthToMonth(ChooseString(DateStr, ' ', 2));
3237         wYear   :=  StrToIntDef(ChooseString(DateStr, ' ', 3), 0);
3238         sTime   :=  ChooseString(DateStr, ' ', 4);
3239         wHour   :=  StrToIntDef(ChooseString(sTime, ':', 0), 0);
3240         wMinute :=  StrToIntDef(ChooseString(sTime, ':', 1), 0);
3241         wSecond :=  StrToIntDef(ChooseString(sTime, ':', 2), 0);
3242         d :=  EncodeDateTime(wYear, wMonth, wDay, wHour, wMinute, wSecond, 0);
3243         Result  :=  d;
3244 end;
3245 //MonaUtils\82©\82ç\88Ú\93®
3246 //! \82 \82é\83Z\83p\83\8c\81[\83^\82Å\8bæ\90Ø\82ç\82ê\82½\95\8e\9a\97ñ\82©\82ç\82\8e\94Ô\96Ú\82Ì\95\8e\9a\97ñ\82ð\8eæ\82è\8fo\82·
3247 function TGikoSys.ChooseString(const Text, Separator: string; Index: integer): string;
3248 var
3249         S : string;
3250         i, p : integer;
3251 begin
3252         S :=  Text;
3253         for i :=  0 to  Index - 1 do  begin
3254                 if  (AnsiPos(Separator, S) = 0) then  S :=  ''
3255                 else  S :=  Copy(S, AnsiPos(Separator, S) + Length(Separator), Length(S));
3256         end;
3257         p :=  AnsiPos(Separator, S);
3258         if  (p > 0) then  Result  :=  Copy(S, 1, p - 1) else Result :=  S;
3259 end;
3260 //! \88ê\8e\9e\83t\83@\83C\83\8b\82©\82ç\82Ì\95\9c\8b\8c
3261 procedure TGikoSys.RestoreThreadData(Board : TBoard);
3262 const
3263     SECTION = 'Setting';
3264 var
3265     TmpFileList : TStringList;
3266     i : Integer;
3267     ini : TMemIniFile;
3268     ThreadItem : TThreadItem;
3269     Boardpath, tmpStr : string;
3270 begin
3271     Boardpath := ExtractFilePath(Board.GetFolderIndexFileName);
3272
3273         TmpFileList := TStringList.Create;
3274         TmpFileList.Sorted := True;
3275         TmpFileList.BeginUpdate;
3276     try
3277         //\91O\89ñ\88Ù\8fí\8fI\97¹\8e\9e\97pTmp\83t\83@\83C\83\8b\83\8a\83X\83g
3278             GetFileList(Boardpath, '*.tmp', TmpFileList, False);
3279             TmpFileList.EndUpdate;
3280                 //\91O\89ñ\88Ù\8fí\8fI\97¹\8e\9e\83`\83F\83b\83N
3281                 for i := TmpFileList.Count - 1 downto 0 do begin
3282                         ThreadItem := Board.FindThreadFromFileName(ChangeFileExt(TmpFileList[i], '.dat'));
3283                         if ThreadItem <> nil then begin
3284                                 ini := TMemIniFile.Create(Boardpath + TmpFileList[i]);
3285                                 try
3286                                         tmpStr := ini.ReadString(SECTION, 'RoundDate', DateTimeToStr(ZERO_DATE));
3287                                         ThreadItem.RoundDate := ConvertDateTimeString(tmpStr);
3288
3289                                         tmpStr := ini.ReadString(SECTION, 'LastModified', DateTimeToStr(ZERO_DATE));
3290                                         ThreadItem.LastModified := ConvertDateTimeString(tmpStr);
3291                                         ThreadItem.Count := ini.ReadInteger(SECTION, 'Count', 0);
3292                                         ThreadItem.NewReceive := ini.ReadInteger(SECTION, 'NewReceive', 0);
3293
3294                                         ThreadItem.Size := ini.ReadInteger(SECTION, 'Size', 0);
3295                     ThreadItem.IsLogFile := FileExists(ThreadItem.GetThreadFileName);
3296                                         if(ThreadItem.Size = 0) and (ThreadItem.IsLogFile) then begin
3297                                                 try
3298                                                         ThreadItem.Size := GetFileSize(ThreadItem.GetThreadFileName) - ThreadItem.Count;
3299                                                 except
3300                                                 end;
3301                                         end;
3302
3303                     //\8f\84\89ñ\82Ì\90Ý\92è\82ÍRoundData\82Ì\95û\82ª\82â\82é\82©\82ç\8f\9f\8eè\82É\90Ý\92è\82µ\82Ä\82Í\83_\83\81\81I\81@by \82à\82\82ã
3304                                         //ThreadItem.Round := ini.ReadBool('Setting', 'Round', False);
3305                                         //ThreadItem.RoundName := ini.ReadString('Setting', 'RoundName', ThreadItem.RoundName);
3306                                         ThreadItem.UnRead := False;//ini.ReadBool('Setting', 'UnRead', False);
3307                                         ThreadItem.ScrollTop := ini.ReadInteger(SECTION, 'ScrollTop', 0);
3308                                         ThreadItem.AllResCount := ini.ReadInteger(SECTION, 'AllResCount', ThreadItem.Count);
3309                                         ThreadItem.NewResCount := ini.ReadInteger(SECTION, 'NewResCount', 0);
3310                                         ThreadItem.AgeSage := TGikoAgeSage(ini.ReadInteger(SECTION, 'AgeSage', Ord(gasNone)));
3311                                 finally
3312                                         ini.Free;
3313                                 end;
3314                                 DeleteFile(Boardpath + TmpFileList[i]);
3315                         end;
3316                 end;
3317     finally
3318         TmpFileList.Clear;
3319         TmpFileList.Free;
3320     end;
3321 end;
3322 {
3323 \brief User32.dll\82ª\97\98\97p\82Å\82«\82é\82©
3324 \return Boolean \97\98\97p\82Å\82«\82é\8fê\8d\87\82ÍTrue
3325 }
3326 function TGikoSys.CanUser32DLL: Boolean;
3327 var
3328     hUser32 : HINST;
3329 begin
3330     Result := False;
3331         hUser32 := 0;
3332         try
3333                 try
3334                         hUser32 := LoadLibrary('User32.dll');
3335                         if hUser32 <> 0 then begin
3336                                 Result := True;
3337             end;
3338                 except
3339                 Result := false;
3340                 end;
3341         finally
3342                 FreeLibrary(hUser32);
3343         end;
3344 end;
3345 {
3346 \brief  OE\88ø\97p\95\84\8eæ\93¾
3347 \return OE\82Ì\88ø\97p\95\84\81i\90Ý\92è\82³\82ê\82Ä\82¢\82È\82¢\8fê\8d\87\82Í'>')
3348 }
3349 function TGikoSys.GetOEIndentChar : string;
3350 var
3351         regKey                  : TRegistry;
3352         Identities      : string;
3353         IndentChar      : DWORD;
3354 const
3355         DEFAULT_CHAR    = '> ';
3356         OE_MAIL_PATH    = '\Software\Microsoft\Outlook Express\5.0\Mail';
3357         INDENT_CHAR             = 'Indent Char';
3358 begin
3359
3360         Result  := DEFAULT_CHAR;
3361         regKey  := TRegistry.Create;
3362         try
3363                 try
3364                         regKey.RootKey  := HKEY_CURRENT_USER;
3365                         if not regKey.OpenKey( 'Identities', False ) then
3366                                 Exit;
3367                         Identities                      := regKey.ReadString( 'Default User ID' );
3368                         if Identities = '' then
3369                                 Exit;
3370                         if not regKey.OpenKey( Identities + OE_MAIL_PATH, False ) then
3371                                 Exit;
3372                         IndentChar := regKey.ReadInteger( INDENT_CHAR );
3373                         Result := Char( IndentChar ) + ' ';
3374                 except
3375                 end;
3376         finally
3377                 regKey.Free;
3378         end;
3379
3380 end;
3381 //! \92u\8a·\90Ý\92è\83t\83@\83C\83\8b\8eæ\93¾
3382 function TGikoSys.GetReplaceFileName: String;
3383 begin
3384     Result := Setting.GetReplaceFileName;
3385 end;
3386 //! \83v\83\8c\83r\83\85\81[\8ag\92£\82Ì\90Ý\92è\83t\83@\83C\83\8b\8eæ\93¾
3387 function TGikoSys.GetExtpreviewFileName: String;
3388 begin
3389     Result := Setting.GetExtprevieFileName;
3390 end;
3391
3392 //! \83t\83@\83C\83\8b\96¼\82©\82ç\82Ì\83X\83\8c\83b\83h\8dì\90¬\93ú\82Ì\8eæ\93¾
3393 function TGikoSys.GetCreateDateFromName(FileName: String): TDateTime;
3394 var
3395     tmp : String;
3396     unixtime: Int64;  
3397 begin
3398     // \83\8d\83O\83t\83@\83C\83\8b\82Ì\8ag\92£\8eq\82ð\82Í\82¸\82µ\82½\82à\82Ì\82ª\83X\83\8c\8dì\90¬\93ú\8e\9e
3399     tmp := ChangeFileExt(FileName, '');
3400     if AnsiPos('_', tmp) <> 0 then
3401         if AnsiPos('_', tmp) > 9 then
3402             tmp := Copy(tmp, 1, AnsiPos('_', tmp)-1)
3403         else
3404             Delete(tmp, AnsiPos('_', tmp), 1);
3405
3406     if ( Length(tmp) = 9) and ( tmp[1] = '0' ) then
3407         Insert('1', tmp, 1);
3408
3409     unixtime := StrToInt64Def(tmp, ZERO_DATE);
3410     Result := UnixToDateTime(unixtime) + OffsetFromUTC;
3411 end;
3412
3413 procedure TGikoSys.ShowRefCount(msg: String; unk: IUnknown);
3414 {$IFDEF DEBUG}
3415 var
3416     count : integer;
3417 {$ENDIF}
3418 begin
3419     if not Assigned(unk) then
3420         Exit;
3421
3422 {$IFDEF DEBUG}
3423     try
3424         unk._AddRef;
3425         count := unk._Release;
3426
3427                 Writeln(msg + ' RefCount=' + IntToStr(count));
3428     except
3429                 Writeln(msg + ' RefCount=exception!!');
3430         end;
3431 {$ENDIF}
3432 end;
3433
3434 initialization
3435         GikoSys := TGikoSys.Create;
3436
3437 finalization
3438         if GikoSys <> nil then begin
3439                 FreeAndNil(GikoSys);
3440         end;
3441 end.