OSDN Git Service

まちBBSで削除レスがあるレス番号とスレの件数がずれる不具合を修正
[gikonavigoeson/gikonavi.git] / BoardGroup.pas
index e23081c..cfe191a 100644 (file)
@@ -3,102 +3,50 @@ unit BoardGroup;
 interface
 
 uses
-       Windows, SysUtils, Classes, ComCtrls, IniFiles, {HTTPApp,} YofUtils, IdGlobal,
-       ExternalBoardManager;
+       Windows, SysUtils, Classes, ComCtrls, {HTTPApp,} YofUtils, IdGlobal,
+       ExternalBoardManager, ExternalBoardPlugInMain, StrUtils;
 
 type
        //\83\8a\83X\83g\82Ì\95\\8e¦\83A\83C\83e\83\80\91I\91ð
-       TGikoViewType = (gvtAll, gvtLog, gvtNew, gvtUser);
-       //\83\8a\83X\83g\82Ì\8eæ\93¾\8c\8f\90\94
-       //TGikoListCount = (glc50, glc100, glc200, glc500, glc1000, glcAll);
-       //\8f\84\89ñ\94Ô\8d\86
-       //TGikoRoundNo = (grnNone, grn1, grn2, grn3, grn4, grn5, grnOnce);
+       TGikoViewType = (gvtAll, gvtLog, gvtNew, gvtLive, gvtArch, gvtUser);
        //\83\8a\83X\83g\82Ì\8fã\82°\89º\82°
-       TGikoAgeSage = (gasNone, gasAge, gasSage, gasNew);
-
-{      TFolder = class
-       private
-               FItemList: TList;       //\8eq\83A\83C\83e\83\80\83\8a\83X\83g
-               FLeaf: Boolean;         //\89º\82É\83t\83H\83\8b\83_\82ð\8e\9d\82Â\82±\82Æ\82ª\8fo\97\88\82é\82©
-       public
-               function Add(Item: TFolder): Integer;
-               procedure Clear;
-               procedure Delete(Index: Integer);
-               procedure Exchange(Index1, Index2: Integer);
-               procedure Insert(Index: Integer; Item: TFolder);
-               procedure Move(CurIndex, NewIndex: Integer);
-               function Remove(Item: TFolder): Integer;
-               procedure Sort(Compare: TListSortCompare);
-               property Capacity: Integer read FCapacity write SetCapacity;
-               property Count: Integer read FCount write SetCount;
-               property Items[Index: Integer]: TFolder read Get write Put; default;
-
-               property Leaf: Boolean read FLeaf;
-       end;
-
-       TBBS = class(TFolder)
-       end;
-       TCategory class(TFolder)
-       end;
-       TBoard = class(TFolder)
-       end;
-       TThreadItem = class(TFolder)
-       end;
-}
-
-{
-       TBBS2ch = class(TBBS)
-       end;
-       TBoard2ch = class(TBoard)
-       end;
-       TThreadItem2ch = class(TThreadItem)
-       end;
-}
-
-//     ITest = interface
-//     end;
-//     IBBS = interface
-//     end;
-//     ICategory = interface
-//     end;
-//     IBoard = interface
-//     end;
-//     IThreadItem = interface
-//     end;
+       TGikoAgeSage = (gasNone, gasAge, gasSage, gasNew, gasArch, gasNull);
 
        TCategory = class;
        TBoard = class;
        TThreadItem = class;
 
-       //\82Æ\82è\82 \82¦\82¸\82Q\82¿\82á\82ñ\82Ë\82é\82Ì\83\8b\81[\83g
-       TBBS2ch = class(TList)
+
+       // BBS \82Ì\83\8b\81[\83g
+       TBBS = class(TList)
        private
                FTitle: string;
-               FLogFolder: string;
+               FFilePath : string;                                             // \94Â\83\8a\83X\83g\82Ì\83p\83X
                FExpand: Boolean;
                FKubetsuChk: Boolean;                                   //\8di\8d\9e\82Ý\8e\9e\91å\95\8e\9a\8f¬\95\8e\9a\8bæ\95Ê
                FSelectText: string;                                    //\8di\8d\9e\82Ý\95\8e\9a\97ñ
                FShortSelectText: string;
+               FIsBoardFileRead : Boolean;             // \94Â\83\8a\83X\83g\82Í\93Ç\82Ý\8d\9e\82Ü\82ê\82Ä\82¢\82é\82©\81H
 
                function GetCategory(index: integer): TCategory;
                procedure SetCategory(index: integer; value: TCategory);
                procedure SetSelectText(s: string);
        public
-               constructor Create(LogFolder: string);
+               constructor Create( boardFilePath : string );
                destructor Destroy; override;
 
-               procedure ReadBoardFile;
-
-               function GetBoardFromBBSID(BBSID: string): TBoard;
-
                function Add(item: TCategory): integer;
                procedure Delete(index: integer);
                procedure Clear; override;
                function Find(key: string): TCategory;
-               function FindBBSID(BBSID: string): TBoard;
-               function FindBoardFromTitle(Title: string): TBoard;
-               function FindThreadItem(BBSID: string; FileName: string): TThreadItem;
-               function GetLogFolder: string;
+               function FindBBSID(const BBSID: string): TBoard;
+               function FindBoardFromTitle(const Title: string): TBoard;
+               function FindBoardFromTitleAndCategory(const CategoryTitle: string; const BoardTitle: string): TBoard;
+               function FindBoardFromURL(const inURL: string): TBoard;
+               function FindThreadFromURL(const inURL : string ) : TThreadItem;
+               function FindThreadItem(const BBSID, FileName: string): TThreadItem;
+               function FindCategoryFromTitle(const inTitle : string ) : TCategory;
+               property FilePath : string read FFilePath write FFilePath;
 
                property Items[index: integer]: TCategory read GetCategory write SetCategory;
                property Title: string read FTitle write FTitle;
@@ -107,14 +55,16 @@ type
                property KubetsuChk: Boolean read FKubetsuChk write FKubetsuChk;
                property SelectText: string read FSelectText write SetSelectText;
                property ShortSelectText: string read FShortSelectText write FShortSelectText;
+
+               property        IsBoardFileRead : Boolean read FIsBoardFileRead write FIsBoardFileRead;
        end;
 
-       //\83J\83e\83S\83\8a
-       TCategory = class(TList)
+       // \83J\83e\83S\83\8a(\94 URL \82Ì\83\8a\83X\83g)
+       TCategory = class(TStringList)
        private
                FNo: Integer;
                FTitle: string;
-               FParentBBS2ch: TBBS2ch;
+               FParenTBBS: TBBS;
                FExpand: Boolean;
 
                function GetBoard(index: integer): TBoard;
@@ -126,22 +76,29 @@ type
                property No: Integer read FNo write FNo;
                property Title: string read FTitle write FTitle;
                property Items[index: integer]: TBoard read GetBoard write SetBoard;
-               property ParentBBS2ch: TBBS2ch read FParentBBS2ch write FParentBBS2ch;
+               property ParenTBBS: TBBS read FParenTBBS write FParenTBBS;
 
                function Add(item: TBoard): integer;
                procedure Delete(index: integer);
                procedure Clear; override;
-               function FindName(key: string): TBoard;
-               function FindBBSID(BBSID: string): TBoard;
-               function FindBoardFromTitle(Title: string): TBoard;
+               function FindName(const key: string): TBoard;
+               function FindBBSID(const BBSID: string): TBoard;
+               function FindBoardFromTitle(const Title: string): TBoard;
+               function FindBoardFromURL(const inURL: string): TBoard;
+               function FindThreadFromURL(const inURL : string ) : TThreadItem;
                function IsMidoku: Boolean;
 
                property NodeExpand: Boolean read FExpand write FExpand;
        end;
 
-       //\94Â
-       TBoard = class(TList)
+       //! \83X\83\8c\83b\83h\90\94\83J\83E\83\93\83g\8fð\8c\8f\95
+       TThreadCount = function(Item : TThreadItem): Boolean;
+
+       // \94Â(\83X\83\8c\83b\83h URL \82Ì\83\8a\83X\83g)
+       TBoard = class(TStringList)
        private
+               FContext: DWORD;                                                        // \83v\83\89\83O\83C\83\93\82ª\8e©\97R\82É\90Ý\92è\82µ\82Ä\82¢\82¢\92l(\8eå\82É\83C\83\93\83X\83^\83\93\83X\82ª\93ü\82é)
+
                FNo: Integer;                                                                   //\94Ô\8d\86
                FTitle: string;                                                         //\83{\81[\83h\83^\83C\83g\83\8b
                FBBSID: string;                                                         //BBSID
@@ -153,36 +110,61 @@ type
                FLastGetTime: TDateTime;                        //\83X\83\8c\83b\83h\82Ü\82½\82Í\83X\83\8c\83b\83h\88ê\97\97\82ð\8dÅ\8cã\82É\8dX\90V\82µ\82½\93ú\8e\9e\81i\83T\81[\83o\91¤\93ú\8e\9e\81E\8f\91\82«\8d\9e\82Ý\8e\9e\82É\8eg\97p\82·\82é\81j
                FIsThreadDatRead: Boolean;              //\83X\83\8c\83b\83h\83\8a\83X\83g\82Í\93Ç\82Ý\8d\9e\82Ü\82ê\82Ä\82¢\82é\82©\81H
                FUnRead: Integer;                                                       //\83X\83\8c\83b\83h\96¢\93Ç\90\94
-               FParentCategory: TCategory;      //\90e\83J\83e\83S\83\8a
+               FParentCategory: TCategory;             //\90e\83J\83e\83S\83\8a
                FModified: Boolean;                                             //\8fC\90³\83t\83\89\83O
                FBoolData: Boolean;                                             //\82¢\82ë\82ñ\82È\97p\93r\82É\8eg\82¤yo
                FSPID: string;                                                          //\8f\91\82«\8d\9e\82Ý\97pSPID
                FPON: string;                                                                   //\8f\91\82«\8d\9e\82Ý\97pPON
+               FCookie: string;                                                //\8f\91\82«\8d\9e\82Ý\97pCookie\95\8e\9a\97ñ
+               FExpires: TDateTime;                                    //Cookie\82Ì\97L\8cø\8aú\8cÀ
                FKotehanName: string;                                   //\83R\83e\83n\83\93\96¼\91O
                FKotehanMail: string;                                   //\83R\83e\83n\83\93\83\81\81[\83\8b
 
                FUpdate: Boolean;
                FExpand: Boolean;
 
+               FBoardPlugIn    : TBoardPlugIn; // \82±\82Ì\94Â\82ð\83T\83|\81[\83g\82·\82é\83v\83\89\83O\83C\83\93
+               FFilePath                       : string;                               // \82±\82Ì\83X\83\8c\88ê\97\97\82ª\95Û\91\82³\82ê\82Ä\82¢\82é\83p\83X
+               FIsLogFile              : Boolean;                      // \83\8d\83O\91\8dÝ\83t\83\89\83O
+               FIntData                        : Integer;                      // \8dD\82«\82É\82¢\82\82Á\82Ä\82æ\82µ\81B\82¢\82ë\82ñ\82È\97p\93r\82É\8eg\82¤yo
+               FListData                       : TList;                                // \8dD\82«\82É\82¢\82\82Á\82Ä\82æ\82µ\81B\82¢\82ë\82ñ\82È\97p\93r\82É\8eg\82¤yo
+
+               FSETTINGTXTTime : TDateTime;    //SETTING.TXT\82ð\8eæ\93¾\82µ\82½\93ú\8e\9e
+               FIsSETTINGTXT   : boolean;      //SETTING.TXT\82ð\8eæ\93¾\82µ\82Ä\82¢\82é\82©
+               FHEADTXTTime    : TDateTime;            //HEAD.TXT\82ð\8eæ\93¾\82µ\82½\93ú\8e\9e
+               FIsHEADTXT              : boolean;      //HEAD.TXT\82ð\8eæ\93¾\82µ\82Ä\82¢\82é\82©
+               FTitlePictureURL: string;       //top\8aG\82ÌURL
+               FMultiplicity   : Integer; //\8fd\95¡\82µ\82Ä\82¢\82é\82©\82Ç\82¤\82©\81H
+               FIs2ch                  : Boolean; //host\82ª2ch\82©\82Ç\82¤\82©
+               FNewThreadCount: Integer;       //\90V\92\85\83X\83\8c\83b\83h\82Ì\90\94
+               FLogThreadCount: Integer;       //\83\8d\83O\97L\82è\83X\83\8c\83b\83h\82Ì\90\94
+               FUserThreadCount: Integer;      //\81H
+               FLiveThreadCount: Integer;      //\90\91\83X\83\8c\83b\83h\90\94
+               FArchiveThreadCount: Integer;   //DAT\97\8e\82¿\83X\83\8c\83b\83h\90\94
                function GetThreadItem(index: integer): TThreadItem;
                procedure SetThreadItem(index: integer; value: TThreadItem);
                procedure SetRound(b: Boolean);
                procedure SetRoundName(s: string);
+               //procedure SetRoundName(s: PChar);
                procedure SetLastModified(d: TDateTime);
                procedure SetLastGetTime(d: TDateTime);
                procedure SetUnRead(i: Integer);
                procedure SetKotehanName(s: string);
                procedure SetKotehanMail(s: string);
+               procedure Init;
        public
-               constructor Create;
+               constructor Create( inPlugIn : TBoardPlugIn; inURL : string );
                destructor Destroy; override;
 
+               property Context: DWORD read FContext write FContext;
+
                property Items[index: integer]: TThreadItem read GetThreadItem write SetThreadItem;
                property No: Integer read FNo write FNo;
                property Title: string read FTitle write FTitle;
                property BBSID: string read FBBSID write FBBSID;
                property URL: string read FURL write FURL;
                property Round: Boolean read FRound write SetRound;
+               //property RoundName: PChar read FRoundName write SetRoundName;
                property RoundName: string read FRoundName write SetRoundName;
                property RoundDate: TDateTime read FRoundDate write FRoundDate;
                property LastModified: TDateTime read FLastModified write SetLastModified;
@@ -192,27 +174,44 @@ type
                property IsThreadDatRead: Boolean read FIsThreadDatRead write FIsThreadDatRead;
                property ParentCategory: TCategory read FParentCategory write FParentCategory;
 
+               property        BoardPlugIn     : TBoardPlugIn  read FBoardPlugIn;
+               property        FilePath                : string                                read FFilePath write FFilePath;
+               property        IsLogFile               : Boolean                               read FIsLogFile write FIsLogFile;
+               property        IntData                 : Integer                               read FIntData write FIntData;
+               property        ListData                : TList                                 read FListData write FListData;
+               function        IsBoardPlugInAvailable : Boolean;
+
                function Add(item: TThreadItem): integer;
                procedure Insert(Index: Integer; Item: TThreadItem);
                procedure Delete(index: integer);
                procedure DeleteList(index: integer);
                procedure Clear; override;
-               function Find(ItemFileName: string): TThreadItem;
-               function GetIndex(ItemFileName: string): Integer;
+               function FindThreadFromFileName(const ItemFileName: string): TThreadItem;
+               function FindThreadFromURL(const inURL : string ) : TThreadItem;
+               function GetIndexFromFileName(const ItemFileName: string): Integer;
+               function GetIndexFromURL(const URL: string; reverse : Boolean = False): Integer;
                procedure LoadSettings;
                procedure SaveSettings;
                function GetReadCgiURL: string;
                function GetSubjectFileName: string;
                function GetFolderIndexFileName: string;
+               function GetSETTINGTXTFileName: string;
+               function GETHEADTXTFileName: string;
+               function GetTitlePictureFileName: string;
                function GetSendURL: string;
 
                function GetNewThreadCount: Integer;
                function GetLogThreadCount: Integer;
+               function GetArchiveThreadCount: Integer;
+               function GetLiveThreadCount: Integer;
                function GetUserThreadCount: Integer;
                function GetNewThread(Index: Integer): TThreadItem;
-               function GetLogThread(Index: Integer): TThreadItem;
+               function GetLogThread(Index: Integer): TThreadItem; overload;
+               function GetArchiveThread(Index: Integer): TThreadItem;
+               function GetLiveThread(Index: Integer): TThreadItem;
                function GetUserThread(Index: Integer): TThreadItem;
-
+               function GetThreadCount(func :TThreadCount ): Integer;
+               function GetThread(func :TThreadCount;const Index :Integer ): TThreadItem;
                procedure BeginUpdate;
                procedure EndUpdate;
                property NodeExpand: Boolean read FExpand write FExpand;
@@ -221,13 +220,28 @@ type
                property PON: string read FPON write FPON;
                property KotehanName: string read FKotehanName write SetKotehanName;
                property KotehanMail: string read FKotehanMail write SetKotehanMail;
+
+               property SETTINGTXTTime: TDateTime read FSETTINGTXTTime write FSETTINGTXTTime;
+               property IsSETTINGTXT:  boolean read FIsSETTINGTXT write FIsSETTINGTXT;
+               property HEADTXTTime: TDateTime read FHEADTXTTime write FHEADTXTTime;
+               property IsHEADTXT:     boolean read FIsHEADTXT write FIsHEADTXT;
+               property TitlePictureURL: string read FTitlePictureURL write FTitlePictureURL;
+               property Multiplicity: Integer read FMultiplicity write FMultiplicity;
+               property Is2ch  : boolean       read FIs2ch     write FIs2ch;
+               property NewThreadCount: Integer        read FNewThreadCount write FNewThreadCount;     //\90V\92\85\83X\83\8c\83b\83h\82Ì\90\94
+               property LogThreadCount: Integer        read FLogThreadCount write FLogThreadCount;             //\83\8d\83O\97L\82è\83X\83\8c\83b\83h\82Ì\90\94
+               property UserThreadCount: Integer       read FUserThreadCount write FUserThreadCount;   //\81H
+               property LiveThreadCount: Integer       read FLiveThreadCount write     FLiveThreadCount;
+               property ArchiveThreadCount: Integer read FArchiveThreadCount write FArchiveThreadCount;
+
+               property Cookie: string                         read FCookie write FCookie;
+               property Expires: TDateTime                     read FExpires write FExpires;
        end;
 
        //\83X\83\8c
        TThreadItem = class(TObject)
        private
                FContext: DWORD;                                        // \83v\83\89\83O\83C\83\93\82ª\8e©\97R\82É\90Ý\92è\82µ\82Ä\82¢\82¢\92l(\8eå\82É\83C\83\93\83X\83^\83\93\83X\82ª\93ü\82é)
-
                FNo: Integer;                                                   //\94Ô\8d\86
                FFileName: string;                              //\83X\83\8c\83b\83h\83t\83@\83C\83\8b\96¼
                FTitle: string;                                         //\83X\83\8c\83b\83h\83^\83C\83g\83\8b
@@ -249,31 +263,36 @@ type
                FScrollTop: Integer;                    //\83X\83N\83\8d\81[\83\8b\88Ê\92u
                FDownloadHost: string;          //\8d¡\82Ì\83z\83X\83g\82Æ\88á\82¤\8fê\8d\87\82Ì\83z\83X\83g
                FAgeSage: TGikoAgeSage;         //\83A\83C\83e\83\80\82Ì\8fã\82°\89º\82°
-//             FSPID: string;                                          //\8f\91\82«\8d\9e\82Ý\97pSPID
-
                FUpdate: Boolean;
                FExpand: Boolean;
                FURL                                    : string;                               // \82±\82Ì\83X\83\8c\82ð\83u\83\89\83E\83U\82Å\95\\8e¦\82·\82é\8dÛ\82Ì URL
-               FBoardPlugIn    : TBoardPlugIn; // \82±\82Ì\83X\83\8c\82ð\83T\83|\81[\83g\82·\82é\83v\83\89\83O\83C\83\93
-               FFilePath                       : string;                               // \82±\82Ì\83X\83\8c\82ª\95Û\91\82³\82ê\82Ä\82¢\82é\83p\83X
-               FSizeByte                       : Integer;                      // CreateHTML2 \82ð\8eÀ\8ds\82µ\82Ä\82¢\82é\8dÅ\92\86\82É\88ê\8e\9e\93I\82É\95Û\91\82³\82ê\82é\92l
-
+               FJumpAddress : Integer;         //\83\8c\83X\94Ô\8d\86\8ew\92èURL\82ð\93¥\82ñ\82¾\82Æ\82«\82É\8ew\92è\82³\82ê\82é\83\8c\83X\82Ì\94Ô\8d\86\82ª\93ü\82é
                procedure SetLastModified(d: TDateTime);
                procedure SetRound(b: Boolean);
-               procedure SetRoundName(s: string);
+               procedure SetRoundName(const s: string);
+               //procedure SetRoundName(const s: PChar);
                procedure SetKokomade(i: Integer);
                procedure SetUnRead(b: Boolean);
                procedure SetScrollTop(i: Integer);
-               function GetURLWrapper: string;
+               procedure Init;
+               function GetCreateDate: TDateTime;
+        function GetFilePath: String;
        public
-               constructor Create; overload;
-               constructor Create( inPlugIn : TBoardPlugIn; inURL : string = '' ); overload;
+               constructor Create(const inPlugIn : TBoardPlugIn; const inBoard : TBoard; inURL : string ); overload;
+               constructor Create(const inPlugIn : TBoardPlugIn; const inBoard : TBoard;
+                                        const inURL : string; inExist: Boolean; const inFilename: string ); overload;
+
                destructor Destroy; override;
 
                function GetDatURL: string;
                function GetDatgzURL: string;
 //             function GetOldDatgzURL: string;
-               function GetOfflawCgiURL(SessionID: string): string;
+               function GetOfflawCgiURL(const SessionID: string): string;
+//////////////// 2013/10/13 ShiroKuma\91Î\89\9e zako Start ///////////////////////////
+        function GetOfflaw2SoURL: string;
+//////////////// 2013/10/13 ShiroKuma\91Î\89\9e zako End /////////////////////////////
+        function GetRokkaURL(const SessionID: string): string;  // Rokka\91Î\89\9e  
+               function GetExternalBoardKakoDatURL: string; // \8aO\95\94\94Â\89ß\8b\8e\83\8d\83OURL\8eæ\93¾
                function GetSendURL: string;
                procedure DeleteLogFile;
                function GetThreadFileName: string;
@@ -294,6 +313,7 @@ type
                property Size: Integer read FSize write FSize;
                property Round: Boolean read FRound write SetRound;
                property RoundName: string read FRoundName write SetRoundName;
+               //property RoundName: PChar read FRoundName write SetRoundName;
 
                property IsLogFile: Boolean read FIsLogFile write FIsLogFile;
                property ParentBoard: TBoard read FParentBoard write FParentBoard;
@@ -305,69 +325,279 @@ type
                property Expand: Boolean read FExpand write FExpand;
                property DownloadHost: string read FDownloadHost write FDownloadHost;
                property AgeSage: TGikoAgeSage read FAgeSage write FAgeSage;
-//             property SPID: string read FSPID write FSPID;
-
-               property        URL                                     : string                                read GetURLWrapper write FURL;
-               property        BoardPlugIn     : TBoardPlugIn  read FBoardPlugIn;
-               property        FilePath                : string                                read FFilePath write FFilePath;
-               property        SizeByte                : Integer                               read FSizeByte write FSizeByte;
+               property CreateDate: TDateTime read GetCreateDate;
+               property        URL                                     : string                                read FURL write FURL;
+               property        FilePath                : string        read GetFilePath;
+               property JumpAddress : Integer read FJumpAddress write FJumpAddress;
        end;
 
-
-
-       //\8c\9f\8dõ\8c\8b\89Ê\83\8a\83X\83g
-{      TSearchList = class(TList)
-       private
-               function GetThreadItem(index: integer): TThreadItem;
-               procedure SetThreadItem(index: integer; value: TThreadItem);
-       public
-               constructor Create;
+       TBoardGroup = class(TStringList)
+    private
+       FBoardPlugIn    : TBoardPlugIn; // \82±\82Ì\94Â\82ð\83T\83|\81[\83g\82·\82é\83v\83\89\83O\83C\83\93
+    public
                destructor Destroy; override;
+               procedure       Clear   ; override;
+        property       BoardPlugIn     : TBoardPlugIn  read FBoardPlugIn write FBoardPlugIn;
+    end;
+
+    // \93Á\8eê\97p\93r\97pTBoard
+    TSpecialBoard = class(TBoard)
+    public
+        function Add(item: TThreadItem): integer; overload;
+        procedure Clear; overload;
+    end;
+
+    // \83X\83\8c\83b\83h\96¼NG\83\8f\81[\83h\83\8a\83X\83g
+       TThreadNgList = class(TStringList)
+    private
+        FFilePath: String;
+    public
+               constructor Create;
+        procedure Load;
+        procedure Save;
+        function IsNG(const Title: String): Boolean;
+    end;
 
-               property Items[index: integer]: TThreadItem read GetThreadItem write SetThreadItem;
+       function        BBSsFindBoardFromBBSID( inBBSID : string ) : TBoard;
+       function        BBSsFindBoardFromURL( inURL : string ) : TBoard;
+       function        BBSsFindBoardFromTitle( inTitle : string ) : TBoard;
+       function        BBSsFindThreadFromURL(const inURL : string ) : TThreadItem;
+       function        ConvertDateTimeString( inDateTimeString : string) : TDateTime;
 
-               function Add(item: TThreadItem): integer;
-               procedure Delete(index: integer);
-               procedure Clear; override;
-       end;}
+    procedure    DestorySpecialBBS( inBBS : TBBS );
 
 var
-       BBS2ch: TBBS2ch;
+       BBSs            : array of TBBS;
+    BoardGroups : array of TBoardGroup;
+    SpecialBBS  : TBBS;
+    SpecialBoard: TSpecialBoard;
+    ThreadNgList: TThreadNgList;
 
 implementation
 
 uses
-       GikoSystem, RoundData;
+       GikoSystem, RoundData, MojuUtils, DateUtils, IniFiles;
 
 const
        BBS2CH_NAME:                                     string = '\82Q\82¿\82á\82ñ\82Ë\82é';
        BBS2CH_LOG_FOLDER:               string = '2ch';
+       EXTERNAL_LOG_FOLDER:            string  = 'exboard';
 
        FOLDER_INI_FILENAME:     string = 'Folder.ini';
        FOLDER_INDEX_FILENAME: string   = 'Folder.idx';
        SUBJECT_FILENAME:                       string  = 'subject.txt';
        PATH_DELIM:                                             string  = '\';
+       SETTINGTXT_FILENAME:            string = 'SETTING.TXT';
+    HEADTXT_FILENAME:          string = 'head.html';
        //DEFAULT_LIST_COUNT:           Integer = 100;
+       THREAD_NG_FILE: String = 'ThreadNg.txt';
+    
 
-//     COLUMN_CATEGORY:         array[0..0] of string = ('\83J\83e\83S\83\8a\96¼');
-//     COLUMN_BOARD:                   array[0..3] of string = ('\94Â\96¼', '\8eæ\93¾\90\94', '\8f\84\89ñ\97\\96ñ', '\91O\89ñ\8f\84\89ñ\93ú\8e\9e');
-//     COLUMN_THREADITEM: array[0..3] of string = ('\83X\83\8c\83b\83h\96¼', '\83J\83E\83\93\83g', '\8f\84\89ñ\97\\96ñ', '\91O\89ñ\8f\84\89ñ\93ú\8e\9e');
+//! \83\8d\83O\82ð\8e\9d\82Á\82Ä\82¢\82é\82È\82ç\90^\82ð\95Ô\82·
+function CountLog(Item: TThreadItem): Boolean;
+begin
+       Result := Item.IsLogFile;
+end;
+//! \90V\92\85\82È\82ç\90^\82ð\95Ô\82·
+function CountNew(Item: TThreadItem): Boolean;
+begin
+       Result := Item.NewArrival;
+end;
+//! DAT\97\8e\82¿\82È\82ç\90^\82ð\95Ô\82·
+function CountDat(Item: TThreadItem): Boolean;
+begin
+       Result := (Item.AgeSage = gasArch);
+end;
+//! \90\91\83X\83\8c\82È\82ç\90^\82ð\95Ô\82·
+function CountLive(Item: TThreadItem): Boolean;
+begin
+       Result := (Item.AgeSage <> gasArch);
+end;
+
+//! \8fí\82É\90^
+function CountAll(Item: TThreadItem): Boolean;
+begin
+    Result := True;
+end;
+
+
+
+// BBSID \82ð\97p\82¢\82é 2 \82¿\82á\82ñ\82Ë\82é\82Ì\82Ý\92T\82µ\8fo\82µ\82Ü\82·
+// BBSID \82Ì\8eg\97p\82Í\8bÉ\97Í\94ð\82¯\82Ä\82­\82¾\82³\82¢\81B
+// \89Â\94\\82È\8fê\8d\87\82Í URL \82ð\8eg\97p\82µ\82Ä\82­\82¾\82³\82¢\81B
+function       BBSsFindBoardFromBBSID(
+       inBBSID : string
+) : TBoard;
+var
+       i : Integer;
+       tmpBoard : TBoard;
+begin
+
+//     Result := BBSs[ 0 ].FindBBSID( inBBSID );
+       Result := nil;
+       if Length(BoardGroups) > 0 then begin
+               for i := BoardGroups[0].Count - 1 downto 0 do begin
+                       tmpBoard := TBoard(BoardGroups[0].Objects[i]);
+                       if tmpBoard.Is2ch then begin
+                               if AnsiCompareStr(tmpBoard.BBSID, inBBSID) = 0 then begin
+                                       Result := tmpBoard;
+                                       EXIT;
+                               end;
+                       end;
+               end;
+       end;
+
+end;
+{**********************************************
+\82±\82Ì\8aÖ\90\94\82Í\95K\82¸\94Â\82ÌURL\82Ì\8c`\8e®\82Å\93n\82µ\82Ä\82­\82¾\82³\82¢\81B
+plugin\82ð\8eg\97p\82·\82é\82È\82ç\82Î\81AExtractBoardURL( inURL )
+2ch\82È\82ç\82Î\81AGikoSys.Get2chThreadURL2BoardURL( inURL );
+\82Å\95Ï\8a·\82µ\82Ä\82©\82ç\8cÄ\82Ñ\8fo\82µ\82Ä\82­\82¾\82³\82¢\81B
+**********************************************}
+function       BBSsFindBoardFromURL(
+       inURL   : string
+) : TBoard;
+var
+       i,p                     : Integer;
+       accept          : TAcceptType;
+       protocol, host, path, document, port, bookmark : string;
+begin
+       Result := nil;
+       for i := Length(BoardGroups) - 1 downto 1 do begin
+               accept := BoardGroups[i].BoardPlugIn.AcceptURL(inURL);
+               if (accept = atBoard) or (accept = atThread) then begin
+                       if BoardGroups[i].Find(inURL, p) then begin
+                               Result := TBoard(BoardGroups[i].Objects[p]);
+                               Exit;
+                       end else begin
+                               inURL := BoardGroups[i].BoardPlugIn.ExtractBoardURL(inURL);
+                               if BoardGroups[i].Find(inURL, p) then begin
+                                       Result := TBoard(BoardGroups[i].Objects[p]);
+                                       Exit;
+                               end;
+                       end;
+               end;
+       end;
+       //\82±\82±\82É\82«\82½\82ç\81Aplugin\82ð\8eg\82í\82È\82¢\82â\82Â\82ç\82ð\92²\82×\82é
+       if BoardGroups[0].Find(inURL, p) then
+               Result := TBoard(BoardGroups[0].Objects[p]);
+               
+       if (Result = nil) then begin
+               GikoSys.ParseURI( inURL, protocol, host, path, document, port, bookmark );
+               //\83z\83X\83g\82ª2ch\82È\82çBBSID\82Å\92²\82×\82é
+               if GikoSys.Is2chHost(host) then begin
+                       Result := BBSsFindBoardFromBBSID(GikoSys.URLToID( inURL ));
+               end;
+       end;
+
+end;
+
+function       BBSsFindBoardFromTitle(
+       inTitle : string
+) : TBoard;
+var
+       i,j                             : Integer;
+       tmpBoard                : TBoard;
+begin
+    Result := nil;
+       for i := Length( BBSs ) - 1 downto 0 do begin
+               for j := BoardGroups[i].Count - 1 downto 0 do begin
+                       tmpBoard := TBoard(BoardGroups[i].Objects[j]);
+                       if ( AnsiCompareStr(tmpBoard.Title, inTitle) = 0) then begin
+                               Result := tmpBoard;
+                               Exit;
+                       end;
+               end;
+       end;
+
+end;
+
+function       BBSsFindThreadFromURL(
+       const inURL                     : string
+) : TThreadItem;
+var
+       board                   : TBoard;
+       tmpThread               : TThreadItem;
+       boardURL        : string;
+       protocol, host, path, document, port, bookmark : string;
+       BBSID, BBSKey : string;
+       i, bi : Integer;
+begin
+
+       boardURL        := GikoSys.GetThreadURL2BoardURL( inURL );
+       board                   := BBSsFindBoardFromURL( boardURL );
+       if board = nil then
+               Result := nil
+       else begin
+               Result := board.FindThreadFromURL( inURL );
+               //\82à\82µ\82à2ch\82Ì\94Â\82È\82ç
+               if (Result = nil) and (board.Is2ch) then begin
+                       GikoSys.ParseURI( inURL, protocol, host, path, document, port, bookmark );
+                       GikoSys.Parse2chURL( inURL, path, document, BBSID, BBSKey );
+                       Result := board.FindThreadFromFileName(BBSKey + '.dat');
+               end else if (Result = nil) and not (board.Is2ch) then begin
+               //\83v\83\89\83O\83C\83\93\8cn\82Ì\92T\8dõ\81i\8eå\82ÉURL\82ª\93r\92\86\82Å\95Ï\8dX\82É\82È\82Á\82½\97Þ)
+                       try
+                               bi := Length(BoardGroups) - 1;
+                               for i := 1 to bi do begin
+                                       if (BoardGroups[i].BoardPlugIn <> nil) and (Assigned(Pointer(BoardGroups[i].BoardPlugIn.Module))) then begin
+                                               if BoardGroups[i].BoardPlugIn.AcceptURL( inURL ) = atThread then begin
+                                                       tmpThread               := TThreadItem.Create( BoardGroups[i].BoardPlugIn, Board, inURL );
+                                                       if not board.IsThreadDatRead then begin
+                                                               GikoSys.ReadSubjectFile( board );
+                                                       end;
+                                                       Result := Board.FindThreadFromFileName( tmpThread.FileName );
+                                                       tmpThread.Free;
+                                                       Break;
+                                               end;
+                                       end;
+                               end;
+                       except
+               Result := nil;
+                       end;
+               end;
+       end;
+
+end;
+{!
+\brief \93Á\8eê\97p\93rBBS\8dí\8f\9c
+\param bbs \8dí\8f\9c\82·\82é\93Á\8eê\97p\93rBBS
+}
+procedure DestorySpecialBBS( inBBS : TBBS );
+var
+    sCategory : TCategory;
+    sBoard    : TSpecialBoard;
+begin
+    if inBBS <> nil then begin
+        sCategory := inBBS.Items[0];
+        if sCategory <> nil then begin
+            sBoard := TSpecialBoard(sCategory.Items[0]);
+            if sBoard <> nil then begin
+                sBoard.Modified := False;
+                sBoard.Clear;
+                FreeAndNil(sBoard);
+            end;
+        end;
+        FreeAndNil(inBBS);
+    end;
+end;
 
 (*************************************************************************
  *\8b@\94\\96¼\81FTBBS\83R\83\93\83X\83g\83\89\83N\83^
  *Public
  *************************************************************************)
-constructor TBBS2ch.Create(LogFolder: string);
+constructor TBBS.Create( boardFilePath : string );
 begin
+       inherited Create;
        Title := BBS2CH_NAME;
-       FLogFolder := LogFolder;
+       FFilePath := boardFilePath;
 end;
 
 (*************************************************************************
  *\8b@\94\\96¼\81FTBBS\83f\83X\83g\83\89\83N\83^
  *Public
  *************************************************************************)
-destructor TBBS2ch.Destroy;
+destructor TBBS.Destroy;
 begin
        Clear;
        inherited;
@@ -377,23 +607,23 @@ end;
  *\8b@\94\\96¼\81F
  *Public
  *************************************************************************)
-function TBBS2ch.GetCategory(index: integer): TCategory;
+function TBBS.GetCategory(index: integer): TCategory;
 begin
        Result := TCategory(inherited Items[index]);
 end;
 
-procedure TBBS2ch.SetCategory(index: integer; value: TCategory);
+procedure TBBS.SetCategory(index: integer; value: TCategory);
 begin
        inherited Items[index] := value;
 end;
 
-function TBBS2ch.Add(item: TCategory): integer;
+function TBBS.Add(item: TCategory): integer;
 begin
-       Item.ParentBBS2ch := self;
+       Item.ParenTBBS := self;
        Result := inherited Add(item);
 end;
 
-procedure TBBS2ch.Delete(index: integer);
+procedure TBBS.Delete(index: integer);
 begin
        if Items[index] <> nil then
                TCategory(Items[index]).Free;
@@ -401,54 +631,106 @@ begin
        inherited Delete(index);
 end;
 
-procedure TBBS2ch.Clear;
+procedure TBBS.Clear;
 var
        i: integer;
 begin
        for i := Count - 1 downto 0 do
                Delete(i);
+    Capacity := Count;
 end;
 
-function TBBS2ch.Find(key: string): TCategory;
+function TBBS.Find(key: string): TCategory;
 begin
        Result := nil;
 end;
 
-function TBBS2ch.FindBBSID(BBSID: string): TBoard;
+function TBBS.FindBBSID(const BBSID: string): TBoard;
 var
-       i: Integer;
-       Category: TCategory;
-       Board: TBoard;
+       i       : Integer;
 begin
+       if not IsBoardFileRead then
+       GikoSys.ReadBoardFile( Self );
+       for i := Count - 1 downto 0 do begin
+               Result := Items[ i ].FindBBSID(BBSID);
+               if Result <> nil then
+                       Exit;
+       end;
        Result := nil;
-       for i := 0 to Count - 1 do begin
-               Category := Items[i];
-               Board := Category.FindBBSID(BBSID);
-               if Board <> nil then begin
-                       Result := Board;
+end;
+
+//*************************************************************************
+// \83^\83C\83g\83\8b\82Ì\88ê\92v\82·\82é\94Â\82ð\92T\82·
+//*************************************************************************)
+function TBBS.FindBoardFromTitle(const Title: string): TBoard;
+var
+       i: Integer;
+begin
+       if not IsBoardFileRead then
+       GikoSys.ReadBoardFile( Self );
+       for i := Count - 1 downto 0 do begin
+               Result := Items[ i ].FindBoardFromTitle(Title);
+               if Result <> nil then
                        Exit;
-               end;
        end;
+       Result := nil;
 end;
 
-function TBBS2ch.FindBoardFromTitle(Title: string): TBoard;
+//*************************************************************************
+// \83J\83e\83S\83\8a\96¼\82Æ\94Â\96¼\82Ì\88ê\92v\82·\82é\94Â\82ð\92T\82·
+//*************************************************************************)
+function TBBS.FindBoardFromTitleAndCategory(const CategoryTitle: string; const BoardTitle: string): TBoard;
 var
        i: Integer;
-       Category: TCategory;
-       Board: TBoard;
 begin
+       if not IsBoardFileRead then
+       GikoSys.ReadBoardFile( Self );
+       for i := Count - 1 downto 0 do begin
+        if AnsiCompareStr(Items[ i ].Title, CategoryTitle) = 0 then begin
+            Result := Items[ i ].FindBoardFromTitle(BoardTitle);
+            if Result <> nil then
+                Exit;
+       end;
+       end;
        Result := nil;
-       for i := 0 to Count - 1 do begin
-               Category := Items[i];
-               Board := Category.FindBoardFromTitle(Title);
-               if Board <> nil then begin
-                       Result := Board;
+end;
+
+//*************************************************************************
+// URL \82ð\8eó\82¯\95t\82¯\82é\94Â\82ð\92T\82·
+//*************************************************************************)
+function TBBS.FindBoardFromURL(const inURL: string): TBoard;
+var
+       i                                       : Integer;
+begin
+       if not IsBoardFileRead then
+       GikoSys.ReadBoardFile( Self );
+       for i := Count - 1 downto 0 do begin
+               Result := Items[ i ].FindBoardFromURL( inURL );
+               if Result <> nil then
                        Exit;
-               end;
        end;
+       Result := nil;
+end;
+
+//*************************************************************************
+// URL \82ð\8eó\82¯\95t\82¯\82é\83X\83\8c\83b\83h\82ð\92T\82·
+//*************************************************************************)
+function TBBS.FindThreadFromURL(const inURL: string): TThreadItem;
+var
+       board                   : TBoard;
+       boardURL        : string;
+begin
+
+       boardURL        := GikoSys.GetThreadURL2BoardURL( inURL );
+       board                   := FindBoardFromURL( boardURL );
+       if board = nil then
+               Result := nil
+       else
+               Result := board.FindThreadFromURL( inURL );
+
 end;
 
-function TBBS2ch.FindThreadItem(BBSID: string; FileName: string): TThreadItem;
+function TBBS.FindThreadItem(const BBSID, FileName: string): TThreadItem;
 var
        Board: TBoard;
 begin
@@ -456,116 +738,37 @@ begin
        Board := FindBBSID(BBSID);
        if Board = nil then
                Exit;
-       Result := Board.Find(FileName);
+       Result := Board.FindThreadFromFileName(FileName);
 end;
 
-(*************************************************************************
- *\8b@\94\\96¼\81@\81@\81F\83{\81[\83h\83t\83@\83C\83\8b\93Ç\82Ý\8d\9e\82Ý
- *\89Â\8e\8b\81@\81@\81@\81FPublic
- *\97\9a\97ð\82P\81@\81@\81F\90V\8bK\8dì\90¬
- *************************************************************************)
-procedure TBBS2ch.ReadBoardFile;
+function TBBS.FindCategoryFromTitle(const inTitle : string ) : TCategory;
 var
-       i, j: integer;
-       idx: Integer;
-       ini: TMemIniFile;
-       CategoryList: TStringList;
-       BoardList: TStringList;
-       Category: TCategory;
-       Board: TBoard;
-       inistr: string;
-       RoundItem: TRoundItem;
-       BBSList: TStringList;
-       CustomList: TStringList;
-begin
-       ini := TMemIniFile.Create('');
-       BBSList := TStringList.Create;
-       CustomList := TStringList.Create;
-       try
-               if FileExists(GikoSys.GetBoardFileName) then
-                       BBSList.LoadFromFile(GikoSys.GetBoardFileName);
-               if FileExists(GikoSys.GetCustomBoardFileName) then
-                       CustomList.LoadFromFile(GikoSys.GetCustomBoardFileName);
-               BBSList.AddStrings(CustomList);
-               ini.SetStrings(BBSList);
-
-               CategoryList := TStringList.Create;
-               BoardList := TStringList.Create;
-               try
-                       ini.ReadSections(CategoryList);
-                       for i := 0 to CategoryList.Count - 1 do begin
-
-                               ini.ReadSection(CategoryList[i], BoardList);
-                               Category := TCategory.Create;
-                               Category.No := i + 1;
-                               Category.Title := CategoryList[i];
-
-                               for j := 0 to BoardList.Count - 1 do begin
-                                       inistr := ini.ReadString(CategoryList[i], BoardList[j], '');
-                                       Board := TBoard.Create;
-                                       Board.BeginUpdate;
-                                       Board.No := j + 1;
-                                       Board.Title := BoardList[j];
-                                       Board.BBSID := GikoSys.UrlToID(inistr);
-                                       Board.URL := inistr;
-                                       Board.RoundDate := ZERO_DATE;
-
-                                       idx := RoundList.Find(Board);
-                                       if idx <> -1 then begin
-                                               RoundItem := RoundList.Items[idx, grtBoard];
-                                               Board.Round := True;
-                                               Board.RoundName := RoundItem.RoundName;
-                                       end;
-                                       Category.Add(Board);
-                                       Board.EndUpdate;
-                               end;
-                               BBS2ch.Add(Category);
-                       end;
-               finally
-                       BoardList.Free;
-                       CategoryList.Free;
+       i : Integer;
+begin
+
+       for i := Count - 1 downto 0 do begin
+               if AnsiCompareStr(Items[ i ].Title, inTitle) = 0 then begin
+                       Result := Items[ i ];
+                       Exit;
                end;
-       finally
-               BBSList.Free;
-               CustomList.Free;
-               ini.Free;
        end;
-end;
 
-function TBBS2ch.GetBoardFromBBSID(BBSID: string): TBoard;
-var
-       i: integer;
-begin
        Result := nil;
-       for i := 0 to BBS2ch.Count - 1 do begin
-               Result := BBS2ch.Items[i].FindBBSID(BBSID);
-               if Result <> nil then
-                       Exit;
-       end;
-end;
 
-(*************************************************************************
- *\83\8d\83O\83t\83H\83\8b\83_\8eæ\93¾
- *
- *************************************************************************)
-function TBBS2ch.GetLogFolder: string;
-begin
-       Result := IncludeTrailingPathDelimiter(FLogFolder)
-                                       + BBS2CH_LOG_FOLDER + PATH_DELIM;
 end;
 
-procedure TBBS2ch.SetSelectText(s: string);
+procedure TBBS.SetSelectText(s: string);
 begin
        FSelectText := s;
-       ShortSelectText := GikoSys.ZenToHan(s);
+       ShortSelectText := CustomStringReplace(ZenToHan(s), ' ', '');
 end;
 
-{class function TBBS2ch.GetColumnName(Index: Integer): string;
+{class function TBBS.GetColumnName(Index: Integer): string;
 begin
        Result := COLUMN_CATEGORY[Index];
 end;
 
-class function TBBS2ch.GetColumnCount: Integer;
+class function TBBS.GetColumnCount: Integer;
 begin
        Result := Length(COLUMN_CATEGORY);
 end;}
@@ -575,6 +778,11 @@ end;}
 //===================
 constructor TCategory.Create;
 begin
+       inherited;
+
+       Duplicates              := dupIgnore;
+       CaseSensitive   := False;
+       //Sorted                                := True;
 end;
 
 destructor TCategory.Destroy;
@@ -585,26 +793,24 @@ end;
 
 function TCategory.GetBoard(index: integer): TBoard;
 begin
-       Result := TBoard(inherited Items[index]);
+       Result := TBoard( Objects[index] );
 end;
 
 procedure TCategory.SetBoard(index: integer; value: TBoard);
 begin
-       inherited Items[index] := value;
+       Objects[index] := value;
+       Strings[index] := value.URL
 end;
 
 function TCategory.Add(item: TBoard): integer;
 begin
        Item.ParentCategory := self;
-       Result := inherited Add(item);
+       Result := AddObject( item.URL, item );
 end;
 
 procedure TCategory.Delete(index: integer);
 begin
-       if Items[index] <> nil then
-               TBoard(Items[index]).Free;
-       Items[index] := nil;
-       inherited Delete(index);
+    inherited Delete(index);
 end;
 
 procedure TCategory.Clear;
@@ -613,37 +819,73 @@ var
 begin
        for i := Count - 1 downto 0 do
                Delete(i);
+       Capacity := Count;
 end;
 
-function TCategory.FindName(key: string): TBoard;
+function TCategory.FindName(const key: string): TBoard;
 begin
        Result := nil;
 end;
 
-function TCategory.FindBBSID(BBSID: string): TBoard;
+function TCategory.FindBBSID(const BBSID: string): TBoard;
 var
-       i: integer;
+       i       : integer;
 begin
-       Result := nil;
-       for i := 0 to Count - 1 do begin
-               if Items[i].FBBSID = BBSID then begin
+       for i := Count - 1 downto 0 do begin
+               if AnsiCompareStr(Items[i].FBBSID, BBSID) = 0 then begin
                        Result := Items[i];
                        Exit;
                end;
        end;
+       Result := nil;
 end;
 
-function TCategory.FindBoardFromTitle(Title: string): TBoard;
+//*************************************************************************
+// \83^\83C\83g\83\8b\82Ì\88ê\92v\82·\82é\94Â\82ð\92T\82·
+//*************************************************************************)
+function TCategory.FindBoardFromTitle(const Title: string): TBoard;
 var
-       i: integer;
+       i       : integer;
 begin
-       Result := nil;
-       for i := 0 to Count - 1 do begin
-               if Items[i].FTitle = Title then begin
+       for i := Count - 1 downto 0 do begin
+               if AnsiCompareStr(Items[i].FTitle, Title) = 0 then begin
                        Result := Items[i];
                        Exit;
                end;
        end;
+       Result := nil;
+end;
+
+//*************************************************************************
+// URL \82ð\8eó\82¯\95t\82¯\82é\94Â\82ð\92T\82·
+//*************************************************************************)
+function TCategory.FindBoardFromURL(const inURL: string): TBoard;
+var
+       i       : Integer;
+begin
+       i := IndexOf( inURL );
+       if i >= 0 then
+               Result := TBoard( Objects[ i ] )
+       else
+               Result := nil;
+end;
+
+//*************************************************************************
+// URL \82ð\8eó\82¯\95t\82¯\82é\83X\83\8c\83b\83h\82ð\92T\82·
+//*************************************************************************)
+function TCategory.FindThreadFromURL(const inURL: string): TThreadItem;
+var
+       board                   : TBoard;
+       boardURL        : string;
+begin
+
+       boardURL        := GikoSys.GetThreadURL2BoardURL( inURL );
+       board                   := FindBoardFromURL( boardURL );
+       if board = nil then
+               Result := nil
+       else
+               Result := board.FindThreadFromURL( inURL );
+
 end;
 
 function TCategory.IsMidoku: Boolean;
@@ -680,8 +922,12 @@ end;}
 //===================
 //TBoard
 //===================
-constructor TBoard.Create;
+procedure TBoard.Init;
 begin
+       Duplicates              := dupIgnore;
+       CaseSensitive   := False;
+       //Sorted                                := True;
+
        FNo := 0;
        FTitle := '';
        FBBSID := '';
@@ -692,12 +938,56 @@ begin
        FLastGetTime := ZERO_DATE;
        FIsThreadDatRead := False;
        FUnRead := 0;
+       FMultiplicity := 0;
 //     FListStyle := vsReport;
 //     FItemNoVisible := True;
 
        FUpdate := True;
 end;
 
+// *************************************************************************
+// \8aO\95\94\94Â\83v\83\89\83O\83C\83\93\82ð\8ew\92è\82µ\82½\83R\83\93\83X\83g\83\89\83N\83^
+// *************************************************************************
+constructor TBoard.Create(
+       inPlugIn        : TBoardPlugIn;
+       inURL                   : string
+);
+var
+       protocol, host, path, document, port, bookmark  : string;
+begin
+
+       inherited Create;
+       Init;
+
+       FBoardPlugIn    := inPlugIn;
+       URL                                             := inURL;
+       BBSID                                   := GikoSys.UrlToID( inURL );
+
+       if inPlugIn = nil then begin
+               // subject.txt \82Ì\95Û\91\83p\83X\82ð\90Ý\92è
+               GikoSys.ParseURI( inURL, protocol, host, path, document, port, bookmark );
+               if GikoSys.Is2chHost( host ) then begin
+                       Self.Is2ch := True;
+                       FilePath :=
+                               GikoSys.Setting.LogFolderP  +
+                               BBS2CH_LOG_FOLDER + PATH_DELIM + BBSID + PATH_DELIM + SUBJECT_FILENAME
+               end else begin
+                       Self.Is2ch := False;
+                       FilePath :=
+                               GikoSys.Setting.LogFolderP +
+                               EXTERNAL_LOG_FOLDER + PATH_DELIM + host + PATH_DELIM + BBSID + PATH_DELIM + SUBJECT_FILENAME
+               end;
+       end else begin
+               // \83v\83\89\83O\83C\83\93\82É TBoardItem \82ª\8dì\90¬\82³\82ê\82½\82±\82Æ\82ð\93`\82¦\82é
+               inPlugIn.CreateBoardItem( DWORD( Self ) );
+               //Self.Is2ch := False;  //plugin\91¤\82Å\90Ý\92è\82·\82é
+       end;
+
+end;
+
+// *************************************************************************
+// \83f\83X\83g\83\89\83N\83^
+// *************************************************************************
 destructor TBoard.Destroy;
 begin
        if FModified then begin
@@ -705,30 +995,57 @@ begin
                SaveSettings;
        end;
 
+       // \83v\83\89\83O\83C\83\93\82É TBoardItem \82ª\94j\8aü\82³\82ê\82½\82±\82Æ\82ð\93`\82¦\82é
+       if IsBoardPlugInAvailable then
+               BoardPlugIn.DisposeBoardItem( DWORD( Self ) );
+
        Clear;
        inherited;
 end;
 
+// *************************************************************************
+// \8aO\95\94\94Â\83v\83\89\83O\83C\83\93\82ª\8eg\97p\89Â\94\\82©
+// *************************************************************************
+function       TBoard.IsBoardPlugInAvailable : Boolean;
+begin
+
+       repeat
+               if BoardPlugIn = nil then
+                       Break;
+                       
+               if not Assigned( Pointer( BoardPlugIn.Module ) ) then
+                       Break;
+
+               Result := True;
+               Exit;
+       until True;
+
+       Result := False;
+
+end;
+
 function TBoard.GetThreadItem(index: integer): TThreadItem;
 begin
-       Result := TThreadItem(inherited Items[index]);
+       Result := TThreadItem( Objects[index] );
 end;
 
 procedure TBoard.SetThreadItem(index: integer; value: TThreadItem);
 begin
-       inherited Items[index] := value;
+       Objects[index] := value;
+       Strings[index] := value.URL;
 end;
 
 function TBoard.Add(Item: TThreadItem): Integer;
 begin
        Item.ParentBoard := Self;
-       Result := inherited Add(Item);
+       Result := inherited AddObject(Item.URL, Item);
 end;
 
 procedure TBoard.Insert(Index: Integer; Item: TThreadItem);
 begin
        Item.ParentBoard := Self;
-       inherited Insert(Index, Item);
+       inherited InsertObject(Index, Item.URL, Item);
+
 end;
 
 //Index\82Å\8ew\92è\82³\82ê\82½\83X\83\8c\83b\83h\83I\83u\83W\83F\83N\83g\82ð\94j\8aü
@@ -736,7 +1053,6 @@ procedure TBoard.Delete(index: Integer);
 begin
        if Items[index] <> nil then
                TThreadItem(Items[index]).Free;
-       Items[index] := nil;
        inherited Delete(index);
 end;
 
@@ -753,22 +1069,23 @@ begin
 //     FUnRead := 0;
        for i := Count - 1 downto 0 do
                Delete(i);
+        Capacity := Count;
 end;
 
-function TBoard.Find(ItemFileName: string): TThreadItem;
+function TBoard.FindThreadFromFileName(const ItemFileName: string): TThreadItem;
 var
        i: integer;
 begin
        Result := nil;
        for i := 0 to Count - 1 do begin
-               if Items[i].FileName = ItemFileName then begin
+               if AnsiCompareStr(Items[i].FileName, ItemFileName) = 0 then begin
                        Result := Items[i];
                        Exit;
                end;
        end;
 end;
 
-function TBoard.GetIndex(ItemFileName: string): Integer;
+function TBoard.GetIndexFromFileName(const ItemFileName: string): Integer;
 var
        i: integer;
 begin
@@ -781,6 +1098,39 @@ begin
        end;
 end;
 
+function TBoard.GetIndexFromURL(const URL: string; reverse : Boolean = False): Integer;
+var
+       i : Integer;
+begin
+       if not reverse then
+               Result := IndexOf( URL )
+       else begin
+        Result := -1;
+               for i := Self.Count - 1 downto 0 do begin
+                       if Strings[i] = URL then begin
+                               Result := i;
+                               break;
+                       end;
+               end;
+       end;
+end;
+
+function TBoard.FindThreadFromURL(const inURL : string ) : TThreadItem;
+var
+       i : Integer;
+begin
+
+       if not IsThreadDatRead then
+               GikoSys.ReadSubjectFile( Self );
+
+       i := IndexOf( inURL );
+       if i >= 0 then
+               Result := TThreadItem( Objects[ i ] )
+       else
+               Result := nil;
+
+end;
+
 {function TBoard.GetMidokuCount: Integer;
 var
        i: integer;
@@ -799,26 +1149,44 @@ procedure TBoard.LoadSettings;
 var
        ini: TMemIniFile;
        FileName: string;
+       tmp: string;
 begin
-       FileName := ParentCategory.ParentBBS2ch.GetLogFolder
-                                               + BBSID + PATH_DELIM + FOLDER_INI_FILENAME;
+       if Length( FilePath ) > 0 then
+               FileName := ExtractFilePath( FilePath ) + FOLDER_INI_FILENAME
+       else
+               FileName := GikoSys.Setting.LogFolderP
+                                                       + BBS2CH_LOG_FOLDER + PATH_DELIM + BBSID + PATH_DELIM + FOLDER_INI_FILENAME;
 
        if not FileExists(FileName) then
                Exit;
        ini := TMemIniFile.Create(FileName);
        try
 //             Round := ini.ReadBool('Status', 'Round', False);
-               FRoundDate := ini.ReadDateTime('Status', 'RoundDate', ZERO_DATE);
-               FLastModified := ini.ReadDateTime('Status', 'LastModified', ZERO_DATE);
-               FLastGetTime := ini.ReadDateTime('Status', 'LastGetTime', ZERO_DATE);
+               tmp := ini.ReadString('Status', 'RoundDate', DateTimeToStr(ZERO_DATE));
+               FRoundDate := ConvertDateTimeString(tmp);
+               tmp := ini.ReadString('Status', 'LastModified', DateTimeToStr(ZERO_DATE));
+               FLastModified := ConvertDateTimeString(tmp);
+               tmp := ini.ReadString('Status', 'LastGetTime', DateTimeToStr(ZERO_DATE));
+               FLastGetTime := ConvertDateTimeString(tmp);
+
+               tmp := ini.ReadString('BoardInformation', 'SETTINGTXTTime', DateTimeToStr(ZERO_DATE));
+               FSETTINGTXTTime := ConvertDateTimeString(tmp);
+               tmp := ini.ReadString('BoardInformation', 'HEADTXTTime', DateTimeToStr(ZERO_DATE));
+               FHEADTXTTime := ConvertDateTimeString(tmp);
+
+               FIsSETTINGTXT := ini.ReadBool('BoardInformation', 'IsSETTINGTXT', false);
+               FIsHEADTXT := ini.ReadBool('BoardInformation', 'IsHEADTXT', false);
+               FTitlePictureURL := ini.ReadString('BoardInformation', 'TitlePictureURL', '');
+
                FUnRead := ini.ReadInteger('Status', 'UnRead', 0);
                FSPID := ini.ReadString('Cookie', 'SPID', '');
                FPON := ini.ReadString('Cookie', 'PON', '');
+               FCookie  := ini.ReadString('Cookie', 'Cookie', '');
+               tmp := ini.ReadString('Cookie', 'Expires', DateTimeToStr(ZERO_DATE));
+               FExpires := ConvertDateTimeString(tmp);
                FKotehanName := ini.ReadString('Kotehan', 'Name', '');
                FKotehanMail := ini.ReadString('Kotehan', 'Mail', '');
-//             ListStyle := TViewStyle(Ord(ini.ReadInteger('Status', 'ListStyle', 3)));
-//             ItemNoVisible := ini.ReadBool('Status', 'ItemNoVisible', True);
-//             ViewType := TGikoViewType(Ord(ini.ReadInteger('Status', 'ViewType', 0)));
+
                if UnRead < 0 then
                        UnRead := 0;
        finally
@@ -831,8 +1199,11 @@ var
        ini: TMemIniFile;
        FileName: string;
 begin
-       FileName := ParentCategory.ParentBBS2ch.GetLogFolder
-                                               + BBSID + PATH_DELIM;
+       if Length( FilePath ) > 0 then
+               FileName := ExtractFilePath( FilePath )
+       else
+               FileName := GikoSys.Setting.LogFolderP
+                                                       + BBS2CH_LOG_FOLDER + PATH_DELIM + BBSID + PATH_DELIM;
        if not GikoSys.DirectoryExistsEx(FileName) then
                GikoSys.ForceDirectoriesEx(FileName);
        FileName := FileName + FOLDER_INI_FILENAME;
@@ -847,8 +1218,17 @@ begin
                ini.WriteInteger('Status', 'UnRead', FUnRead);
                ini.WriteString('Cookie', 'SPID', FSPID);
                ini.WriteString('Cookie', 'PON', FPON);
+               ini.WriteString('Cookie', 'Cookie', FCookie);
+               ini.WriteDateTime('Cookie', 'Expires', FExpires);
                ini.WriteString('Kotehan', 'Name', FKotehanName);
                ini.WriteString('Kotehan', 'Mail', FKotehanMail);
+
+               ini.WriteDateTime('BoardInformation', 'SETTINGTXTTime', FSETTINGTXTTime);
+               ini.WriteDateTime('BoardInformation', 'HEADTXTTime', FHEADTXTTime);
+
+               ini.WriteBool('BoardInformation', 'IsSETTINGTXT', FIsSETTINGTXT);
+               ini.WriteBool('BoardInformation', 'IsHEADTXT', FIsHEADTXT);
+               ini.WriteString('BoardInformation', 'TitlePictureURL', FTitlePictureURL);
 //             ini.WriteInteger('Status', 'ListStyle', Ord(ListStyle));
 //             ini.WriteBool('Status', 'ItemNoVisible', ItemNoVisible);
 //             ini.WriteInteger('Status', 'ViewType', Ord(ViewType));
@@ -857,35 +1237,131 @@ begin
                ini.Free;
        end;
 end;
-
-// \83T\83u\83W\83F\83N\83gURL\8eæ\93¾
+//\82Æ\82«\82½\82Ü2003 02 08 0:32:13\82±\82ñ\82È\8c`\8e®\82Ì\93ú\95t\82ª\82 \82é\82Ì\82Å\82»\82ê\82ð
+//        2003/02/08 0:32:13\82É\95Ï\8a·\82·\82é
+function       ConvertDateTimeString( inDateTimeString : string) : TDateTime;
+const
+       ZERO_DATE_STRING : string = '1970/01/01 0:00:00';
+var
+       i : Integer;
+    y: Integer;
+    m: Integer;
+    d: Integer;
+    hour: Integer;
+    min: Integer;
+    sec: Integer;
+begin
+    if inDateTimeString = '' then
+       inDateTimeString := ZERO_DATE_STRING;
+
+    if ( AnsiPos('/', inDateTimeString ) = 0 ) and
+       ( AnsiCompareStr( DateTimeToStr(ZERO_DATE), inDateTimeString) <> 0 ) then begin
+               for i := 0 to 1 do begin
+               Insert('/',inDateTimeString, AnsiPos(' ', inDateTimeString) + 1 );
+               Delete(inDateTimeString, AnsiPos(' ', inDateTimeString), 1);
+       end;
+    end;
+    try
+       Result := StrToDateTime( inDateTimeString );
+    except
+       if( inDateTimeString[5] = '/' ) and ( inDateTimeString[8] = '/' ) then begin
+            y := StrToIntDef( Copy(inDateTimeString, 1, 4), 1970 );
+                       m := StrToIntDef( Copy(inDateTimeString, 6, 2), 1 );
+            d := StrToIntDef( Copy(inDateTimeString, 9, 2), 1 );
+            hour := 0; min  := 0; sec  := 0;
+
+               if Length(inDateTimeString) > 11 then begin
+               if( inDateTimeString[13] = ':' ) and ( inDateTimeString[16] = ':' ) then begin
+                       hour := StrToIntDef( Copy(inDateTimeString, 12, 1), 0 );
+                    min  := StrToIntDef( Copy(inDateTimeString, 14, 2), 0 );
+                    sec  := StrToIntDef( Copy(inDateTimeString, 17, 2), 0 );
+                end else if( inDateTimeString[14] = ':' ) and ( inDateTimeString[17] = ':' ) then begin
+                       hour := StrToIntDef( Copy(inDateTimeString, 12, 2), 0 );
+                    min  := StrToIntDef( Copy(inDateTimeString, 15, 2), 0 );
+                    sec  := StrToIntDef( Copy(inDateTimeString, 18, 2), 0 );
+                end;
+            end;
+            try
+               Result := EncodeDateTime(y ,m, d, hour, min, sec, 0);
+            except
+                Result := ZERO_DATE;
+            end;
+        end else
+               Result := ZERO_DATE;
+    end;
+
+
+   // Result := inDateTimeString;
+end;
+//! \83T\83u\83W\83F\83N\83gURL\8eæ\93¾
 function TBoard.GetReadCgiURL: string;
 begin
-       //Result := URL + SUBJECT_FILENAME;
-       //Result := GikoSys.UrlToServer(URL)
-       //                              + 'test/read.cgi/' + BBSID + '/?raw=0.0';
        Result := URL + SUBJECT_FILENAME;
 
 end;
 
-// \83T\83u\83W\83F\83N\83g\83t\83@\83C\83\8b\96¼\8eæ\93¾\81i\83p\83X\81{\83t\83@\83C\83\8b\96¼\81j
+//! \83T\83u\83W\83F\83N\83g\83t\83@\83C\83\8b\96¼\8eæ\93¾\81i\83p\83X\81{\83t\83@\83C\83\8b\96¼\81j
 function TBoard.GetSubjectFileName: string;
 begin
-       Result := ParentCategory.ParentBBS2ch.GetLogFolder
-                                       + BBSID + PATH_DELIM + SUBJECT_FILENAME;
+       if Length( FilePath ) > 0 then
+               Result := FilePath
+       else
+               Result := GikoSys.Setting.LogFolderP
+                                               + BBS2CH_LOG_FOLDER + PATH_DELIM + BBSID + PATH_DELIM + SUBJECT_FILENAME;
 end;
 
-// \83C\83\93\83f\83b\83N\83X\83t\83@\83C\83\8b\96¼(folder.idx)\8eæ\93¾\81i\83p\83X\81{\83t\83@\83C\83\8b\96¼\81j
+//! \83C\83\93\83f\83b\83N\83X\83t\83@\83C\83\8b\96¼(folder.idx)\8eæ\93¾\81i\83p\83X\81{\83t\83@\83C\83\8b\96¼\81j
 function TBoard.GetFolderIndexFileName: string;
 begin
-       Result := ParentCategory.ParentBBS2ch.GetLogFolder
-                                       + BBSID + PATH_DELIM + FOLDER_INDEX_FILENAME;
+       if Length( FilePath ) > 0 then
+               Result := ExtractFilePath( FilePath ) + FOLDER_INDEX_FILENAME
+       else
+               Result := GikoSys.Setting.LogFolderP
+                                               + BBS2CH_LOG_FOLDER + PATH_DELIM + BBSID + PATH_DELIM + FOLDER_INDEX_FILENAME;
+end;
+//! SETTING.TXT\82Ì\83t\83@\83C\83\8b\96¼\8eæ\93¾
+function TBoard.GetSETTINGTXTFileName: string;
+begin
+       if Length( FilePath ) > 0 then
+               Result := ExtractFilePath( FilePath ) + SETTINGTXT_FILENAME
+       else
+               Result := GikoSys.Setting.LogFolderP
+                                               + BBS2CH_LOG_FOLDER + PATH_DELIM + BBSID + PATH_DELIM + SETTINGTXT_FILENAME;
+end;
+
+function TBoard.GETHEADTXTFileName: string;
+begin
+       if Length( FilePath ) > 0 then
+               Result := ExtractFilePath( FilePath ) + HEADTXT_FILENAME
+       else
+               Result := GikoSys.Setting.LogFolderP
+                                               + BBS2CH_LOG_FOLDER + PATH_DELIM + BBSID + PATH_DELIM + HEADTXT_FILENAME;
+end;
+function TBoard.GetTitlePictureFileName: string;
+var
+       tmpName: string;
+begin
+       if FTitlePictureURL = '' then
+               Result := ''
+       else begin
+               tmpName := Copy(FTitlePictureURL, LastDelimiter('/', FTitlePictureURL) + 1, Length(FTitlePictureURL));
+               if Length( FilePath ) > 0 then
+                       Result := ExtractFilePath( FilePath ) + tmpName
+               else
+                       Result := GikoSys.Setting.LogFolderP
+                                                       + BBS2CH_LOG_FOLDER + PATH_DELIM + BBSID + PATH_DELIM + tmpName;
+       end;
 end;
 
 // \83X\83\8c\97§\82Ä\91\97\90MURL
 function TBoard.GetSendURL: string;
 begin
-       Result := GikoSys.UrlToServer(URL) + 'test/subbbs.cgi';
+    Result := GikoSys.UrlToServer(URL);
+       if Self.Is2ch then
+        Result := Result + 'test/bbs.cgi'
+    else
+        Result := Result + 'test/subbbs.cgi';
+
 end;
 
 procedure TBoard.SetRound(b: Boolean);
@@ -948,175 +1424,114 @@ begin
        if FUpdate then
                FModified := True;
 end;
-
-function TBoard.GetNewThreadCount: Integer;
+//! func\82Ì\8fð\8c\8f\82É\88ê\92v\82·\82é\83X\83\8c\83b\83h\82Ì\90\94\82ð\95Ô\82·
+function TBoard.GetThreadCount(func :TThreadCount ): Integer;
 var
        i: Integer;
 begin
        Result := 0;
-                               if Length( ParentCategory.ParentBBS2ch.ShortSelectText ) = 0 then
-                               begin
-                                       for i := 0 to Count - 1 do begin
-                                               if Items[i].NewArrival then
-                                                       inc(Result);
-                                       end;
-                               end else begin
-                                       for i := 0 to Count - 1 do begin
-                                                                                               if Items[i].NewArrival then
-                                                                                               begin
-                                                                                                                               if Items[i].ShortTitle = '' then
-                                                                                       Items[i].ShortTitle := GikoSys.ZenToHan(Items[i].Title);
-                                                                                                                               if AnsiPos(ParentCategory.ParentBBS2ch.ShortSelectText, Items[i].ShortTitle) <> 0 then
-                                                                                                                                                               inc(Result);
-                                                                                               end;
-                                       end;
-                               end;
+       if Length( ParentCategory.ParenTBBS.ShortSelectText ) = 0 then
+       begin
+               for i := 0 to Count - 1 do begin
+                       if func(Items[i]) then
+                               inc(Result);
+               end;
+       end else begin
+               for i := 0 to Count - 1 do begin
+                       if func(Items[i]) then
+                       begin
+                               if Items[i].ShortTitle = '' then
+                                       Items[i].ShortTitle := CustomStringReplace(ZenToHan(Items[i].Title), ' ', '');
+                               if AnsiPos(ParentCategory.ParenTBBS.ShortSelectText, Items[i].ShortTitle) <> 0 then
+                                       inc(Result);
+                       end;
+               end;
+       end;
 end;
-
+//! \90V\92\85\83X\83\8c\83b\83h\82Ì\90\94\82ð\8eæ\93¾\82·\82é
+function TBoard.GetNewThreadCount: Integer;
+begin
+       Result := GetThreadCount(CountNew);
+end;
+//! \83\8d\83O\97L\82è\83X\83\8c\83b\83h\82Ì\90\94\82ð\8eæ\93¾\82·\82é
 function TBoard.GetLogThreadCount: Integer;
-var
-       i: Integer;
 begin
-       Result := 0;
-                               if Length( ParentCategory.ParentBBS2ch.ShortSelectText ) = 0 then
-                               begin
-                                       for i := 0 to Count - 1 do begin
-                                               if Items[i].IsLogFile then
-                                                       inc(Result);
-                                       end;
-                               end else begin
-                                       for i := 0 to Count - 1 do begin
-                                               if Items[i].IsLogFile then
-                                                                                               begin
-                                                                                                                               if Items[i].ShortTitle = '' then
-                                                                                       Items[i].ShortTitle := GikoSys.ZenToHan(Items[i].Title);
-                                                                                                                               if AnsiPos(ParentCategory.ParentBBS2ch.ShortSelectText, Items[i].ShortTitle) <> 0 then
-                                                                                                                                                               inc(Result);
-                                                                                               end;
-                                       end;
-                               end;
+       Result := GetThreadCount(CountLog);
 end;
-
+//! \8di\8d\9e\82Ý\8fð\8c\8f\82É\88ê\92v\82·\82é\83X\83\8c\83b\83h\82Ì\90\94\82ð\8eæ\93¾\82·\82é
 function TBoard.GetUserThreadCount: Integer;
-var
-       i: Integer;
 begin
-       Result := 0;
-                               if Length( ParentCategory.ParentBBS2ch.ShortSelectText ) = 0 then
-                                                               Result := Count
-                               else
-       for i := 0 to Count - 1 do begin
-               if Items[i].ShortTitle = '' then
-                       Items[i].ShortTitle := GikoSys.ZenToHan(Items[i].Title);
-               if AnsiPos(ParentCategory.ParentBBS2ch.ShortSelectText, Items[i].ShortTitle) <> 0 then
-                       inc(Result);
-       end;
+       Result := GetThreadCount(CountAll);
 end;
-
-function TBoard.GetNewThread(Index: Integer): TThreadItem;
+//! DAT\97\8e\82¿\83X\83\8c\83b\83h\82Ì\90\94\82ð\8eæ\93¾\82·\82é
+function TBoard.GetArchiveThreadCount: Integer;
+begin
+       Result := GetThreadCount(CountDat);
+end;
+//! \90\91\83X\83\8c\83b\83h\82Ì\90\94\82ð\8eæ\93¾\82·\82é
+function TBoard.GetLiveThreadCount: Integer;
+begin
+       Result := GetThreadCount(CountLive);
+end;
+//! func\82Ì\8fð\8c\8f\82É\93K\8d\87\82·\82éIndex\94Ô\96Ú\82Ì\83X\83\8c\83b\83h\82ð\8eæ\93¾\82·\82é
+function TBoard.GetThread(func :TThreadCount;const Index :Integer ): TThreadItem;
 var
        i: Integer;
        Cnt: Integer;
 begin
        Result := nil;
        Cnt := 0;
-                               if Length( ParentCategory.ParentBBS2ch.ShortSelectText ) = 0 then
-                               begin
-                                       for i := 0 to Count - 1 do begin
-                                               if Items[i].NewArrival then
-                                                                                               begin
-                                                       if Index = Cnt then begin
-                                                               Result := Items[i];
-                                                               Exit;
-                                                       end;
-                                                       inc(Cnt);
-                                               end;
-                                       end;
-                               end else begin
-                                       for i := 0 to Count - 1 do begin
-                                               if Items[i].NewArrival then
-                                                                                               begin
-                                                                               if Items[i].ShortTitle = '' then
-                                                                                       Items[i].ShortTitle := GikoSys.ZenToHan(Items[i].Title);
-                                                                               if AnsiPos(ParentCategory.ParentBBS2ch.ShortSelectText, Items[i].ShortTitle) <> 0 then begin
-                                                                                       if Index = Cnt then begin
-                                                                                               Result := Items[i];
-                                                                                               Exit;
-                                                                                       end;
-                                                                                       inc(Cnt);
-                                                                                                                               end;
-                                                                                               end;
+       if Length( ParentCategory.ParenTBBS.ShortSelectText ) = 0 then
+       begin
+               for i := 0 to Count - 1 do begin
+                       if func(Items[i]) then begin
+                               if Index = Cnt then begin
+                                       Result := Items[i];
+                                       Exit;
+                               end;
+                               inc(Cnt);
+                       end;
+               end;
+       end else begin
+               for i := 0 to Count - 1 do begin
+                       if func(Items[i]) then begin
+                               if Length(Items[i].ShortTitle) = 0 then
+                                       Items[i].ShortTitle := CustomStringReplace(ZenToHan(Items[i].Title), ' ', '');
+                               if AnsiPos(ParentCategory.ParenTBBS.ShortSelectText, Items[i].ShortTitle) <> 0 then begin
+                                       if Index = Cnt then begin
+                                               Result := Items[i];
+                                               Exit;
                                        end;
+                                       inc(Cnt);
                                end;
+                       end;
+               end;
+       end;
 end;
-
+//! DAT\97\8e\82¿\83X\83\8c\83b\83h\82ÅIndex\94Ô\96Ú\82Ì\83X\83\8c\83b\83h\82ð\8eæ\93¾\82·\82é
+function TBoard.GetArchiveThread(Index: Integer): TThreadItem;
+begin
+       Result := GetThread(CountDat, Index);
+end;
+//! \90\91\83X\83\8c\83b\83h\82ÅIndex\94Ô\96Ú\82Ì\83X\83\8c\83b\83h\82ð\8eæ\93¾\82·\82é
+function TBoard.GetLiveThread(Index: Integer): TThreadItem;
+begin
+       Result := GetThread(CountLive, Index);
+end;
+//! \90V\92\85\83X\83\8c\83b\83h\82ÅIndex\94Ô\96Ú\82Ì\83X\83\8c\83b\83h\82ð\8eæ\93¾\82·\82é
+function TBoard.GetNewThread(Index: Integer): TThreadItem;
+begin
+       Result := GetThread(CountNew, Index);
+end;
+//! Log\82 \82è\83X\83\8c\83b\83h\82ÌIndex\94Ô\96Ú\82Ì\83X\83\8c\83b\83h\82ð\8eæ\93¾\82·\82é
 function TBoard.GetLogThread(Index: Integer): TThreadItem;
-var
-       i: Integer;
-       Cnt: Integer;
 begin
-                               Cnt := 0;
-                               if Length( ParentCategory.ParentBBS2ch.ShortSelectText ) = 0 then
-                               begin
-                                       for i := 0 to Count - 1 do begin
-                                               if Items[i].IsLogFile then
-                                                                                               begin
-                                                       if Index = Cnt then begin
-                                                               Result := Items[i];
-                                                               Exit;
-                                                       end;
-                                                       inc(Cnt);
-                                               end;
-                                       end;
-                               end else begin
-                                       for i := 0 to Count - 1 do begin
-                                               if Items[i].IsLogFile then
-                                                                                               begin
-                                                                               if Items[i].ShortTitle = '' then
-                                                                                       Items[i].ShortTitle := GikoSys.ZenToHan(Items[i].Title);
-                                                                               if AnsiPos(ParentCategory.ParentBBS2ch.ShortSelectText, Items[i].ShortTitle) <> 0 then begin
-                                                                                       if Index = Cnt then begin
-                                                                                               Result := Items[i];
-                                                                                               Exit;
-                                                                                       end;
-                                                                                       inc(Cnt);
-                                                                                                                               end;
-                                                                                               end;
-                                       end;
-                               end;
-                               Result := nil;
+       Result := GetThread(CountLog, Index);
 end;
-
+//! \8di\8d\9e\82Ý\82ÅIndex\94Ô\96Ú\82Ì\83X\83\8c\83b\83h\82ð\8eæ\93¾\82·\82é
 function TBoard.GetUserThread(Index: Integer): TThreadItem;
-var
-       i: Integer;
-       Cnt: Integer;
 begin
-       Result := nil;
-       Cnt := 0;
-                               if Length( ParentCategory.ParentBBS2ch.ShortSelectText ) = 0 then
-                               begin
-                                                               for i := 0 to Count - 1 do begin
-                                                                                               if Index = Cnt then
-                                                                                               begin
-                                                                                                                               Result := Items[ i ];
-                                                                                                                               Exit;
-                                                                                               end;
-                                                                                               inc( Cnt );
-                                                               end;
-                               end else begin
-                                       for i := 0 to Count - 1 do begin
-                                               if Items[i].ShortTitle = '' then
-                                                       Items[i].ShortTitle := GikoSys.ZenToHan(Items[i].Title);
-                                               if AnsiPos(ParentCategory.ParentBBS2ch.ShortSelectText, Items[i].ShortTitle) <> 0 then begin
-                                                       if Index = Cnt then begin
-                                                               Result := Items[i];
-                                                               Exit;
-                                                       end;
-                                                       inc(Cnt);
-                                               end;
-                                       end;
-                               end;
+       Result := GetThread(CountAll, Index);
 end;
 
 procedure TBoard.BeginUpdate;
@@ -1129,18 +1544,8 @@ begin
        FUpdate := True;
 end;
 
-{class function TBoard.GetColumnName(Index: Integer): string;
-begin
-       Result := COLUMN_THREADITEM[Index];
-end;
-
-class function TBoard.GetColumnCount: Integer;
-begin
-       Result := Length(COLUMN_THREADITEM);
-end;}
-
 //constructor TThreadItem.Create(AOwner: TComponent);
-constructor TThreadItem.Create;
+procedure TThreadItem.Init;
 begin
        FNo := 0;
        FFileName := '';
@@ -1160,33 +1565,88 @@ begin
 
        FUpdate := True;
        FURL := '';
-       FBoardPlugIn := nil;
+       FJumpAddress := 0;
 end;
 
 // *************************************************************************
 // \8aO\95\94\94Â\83v\83\89\83O\83C\83\93\82ð\8ew\92è\82µ\82½\83R\83\93\83X\83g\83\89\83N\83^
 // *************************************************************************
 constructor TThreadItem.Create(
-       inPlugIn        : TBoardPlugIn;
-       inURL                   : string = ''
+       const inPlugIn : TBoardPlugIn;
+       const inBoard : TBoard;
+       inURL : string
 );
-begin
+var
+       foundPos                        : Integer;
+       protocol, host, path, document, port, bookmark  : string;
+       BBSID, BBSKey   : string;
+const
+       READ_PATH                                                       = '/test/read.cgi';
+begin
+
+       inherited Create;
+       Init;
+       FParentBoard    := inBoard;
+       //FBoardPlugIn  := inPlugIn;
+       URL                             := inURL;
+
+       if inPlugIn = nil then begin
+               foundPos := Pos( READ_PATH, inURL );
+               if foundPos > 0 then begin
+                       // dat \82Ì\95Û\91\83p\83X\82ð\90Ý\92è
+                       GikoSys.ParseURI( inURL, protocol, host, path, document, port, bookmark );
+                       GikoSys.Parse2chURL( inURL, path, document, BBSID, BBSKey );
+                       FileName        := BBSKey + '.dat';
+                       IsLogFile       := FileExists( FilePath );
+                       URL                             := GikoSys.Get2chBrowsableThreadURL( inURL );
+               end;
+       end else begin
+               // \83v\83\89\83O\83C\83\93\82É TThreadItem \82ª\8dì\90¬\82³\82ê\82½\82±\82Æ\82ð\93`\82¦\82é
+               inPlugIn.CreateThreadItem( DWORD( Self ) );
+       end;
 
-       Create;
+end;
+// *************************************************************************
+// \8aO\95\94\94Â\83v\83\89\83O\83C\83\93\82ð\8ew\92è\82µ\82½\83R\83\93\83X\83g\83\89\83N\83^ Log\97L\82è\82©\82Ç\82¤\82©\94»\92f\8dÏ\82Ý
+// FileName\82à\8eæ\93¾\8dÏ\82Ý\81@\81¨\81@ReadSubject\97p
+// *************************************************************************
+constructor TThreadItem.Create(
+       const inPlugIn : TBoardPlugIn;
+       const inBoard : TBoard;
+       const inURL : string;
+       inExist: Boolean;
+       const inFilename: string
+);
+begin
 
-       FBoardPlugIn    := inPlugIn;
-       URL                                             := inURL;
+       inherited Create;
+       Init;
+       FParentBoard    := inBoard;
+       URL                             := inURL;
 
-       inPlugIn.CreateThreadItem( DWORD( Self ) );
+       if inPlugIn = nil then begin
+               // dat \82Ì\95Û\91\83p\83X\82ð\90Ý\92è
+               FileName        := inFilename;
+               IsLogFile       := inExist;
+        URL                            := inURL;
+       end else begin
+               // \83v\83\89\83O\83C\83\93\82É TThreadItem \82ª\8dì\90¬\82³\82ê\82½\82±\82Æ\82ð\93`\82¦\82é
+               inPlugIn.CreateThreadItem( DWORD( Self ) );
+       end;
 
 end;
-
+// *************************************************************************
+// \83f\83X\83g\83\89\83N\83^
+// *************************************************************************
 destructor TThreadItem.Destroy;
 begin
-       if Assigned( Pointer( FBoardPlugIn ) ) then
-               FBoardPlugIn.DisposeThreadItem( DWORD( Self ) );
+
+       // \83v\83\89\83O\83C\83\93\82É TThreadItem \82ª\94j\8aü\82³\82ê\82½\82±\82Æ\82ð\93`\82¦\82é
+       if Self.ParentBoard.IsBoardPlugInAvailable then
+               Self.ParentBoard.BoardPlugIn.DisposeThreadItem( DWORD( Self ) );
 
        inherited;
+
 end;
 
 function TThreadItem.GetDatURL: string;
@@ -1252,31 +1712,8 @@ begin
        end;
 end;
 
-{function TThreadItem.GetOldDatgzURL: string;
-var
-       Protocol, Host, Path, Document, Port, Bookmark: string;
-begin
-       Result := Format('%s%s/%.3s/%s.gz', [ParentBoard.URL,
-                                                                                                                                                        'kako',
-                                                                                                                                                        FileName,
-                                                                                                                                                        FileName]);
-       if FDownloadHost <> '' then begin
-               ParseURI(Result, Protocol, Host, Path, Document, Port, Bookmark);
-               Result := Format('%s://%s%s%s', [Protocol,
-                                                                                                                                                DownloadHost,
-                                                                                                                                                Path,
-                                                                                                                                                Document]);
-
-       end;
-end;}
-
-function TThreadItem.GetOfflawCgiURL(SessionID: string): string;
-//var
-//     Protocol, Host, Path, Document, Port, Bookmark: string;
+function TThreadItem.GetOfflawCgiURL(const SessionID: string): string;
 begin
-//     Result := GikoSys.UrlToServer(ParentBoard.URL)
-//                                     + 'test/offlaw.cgi/' + ParentBoard.BBSID + '/'
-//                                     + ChangeFileExt(FileName, '') + '/?raw=.0&sid=' + HttpEncode(SessionID);
        if FDownloadHost = '' then begin
                Result := GikoSys.UrlToServer(ParentBoard.URL)
                                                + 'test/offlaw.cgi/' + ParentBoard.BBSID + '/'
@@ -1287,13 +1724,69 @@ begin
                Result := 'http://' + FDownloadHost
                                                + '/test/offlaw.cgi/' + ParentBoard.BBSID + '/'
                                                + ChangeFileExt(FileName, '') + '/?raw=.0&sid=' + HttpEncode(SessionID);
-//             Result := Format('%s://%s%s%s', [Protocol,
-//                                                                                                                                              DownloadHost,
-//                                                                                                                                              Path,
-//                                                                                                                                              Document]);
        end;
 end;
 
+function TThreadItem.GetOfflaw2SoURL: string;
+begin
+    Result := GikoSys.UrlToServer(ParentBoard.URL)
+                    + 'test/offlaw2.so?shiro=kuma&bbs=' + ParentBoard.BBSID
+                    + '&key=' + ChangeFileExt(FileName, '');
+end;
+
+function TThreadItem.GetRokkaURL(const SessionID: string): string;
+var
+    Domain: string;
+    Host: string;
+    Idx: Integer;
+    HostPos: Integer;
+begin
+       if FDownloadHost = '' then begin
+        Idx := AnsiPos('.2ch.net/', ParentBoard.URL);
+        if (Idx > 0) then begin
+            Domain := '2ch.net';
+        end else begin
+            Idx := AnsiPos('.bbspink.com/', ParentBoard.URL);
+            if (Idx > 0) then
+                Domain := 'bbspink.com';
+        end;
+        if (Idx > 0) then begin
+            HostPos := AnsiPos('://', ParentBoard.URL) + 3;
+            Host := Copy(ParentBoard.URL, HostPos, Idx - HostPos);
+        end;
+    end else begin
+        Idx := AnsiPos('.2ch.net', FDownloadHost);
+        if (Idx > 0) then begin
+            Domain := '2ch.net';
+        end else begin
+            Idx := AnsiPos('.bbspink.com', FDownloadHost);
+            if (Idx > 0) then
+                Domain := 'bbspink.com';
+        end;
+        if (Idx > 0) then begin
+            Host := Copy(FDownloadHost, 1, Idx - 1);
+        end;
+    end;
+
+    if ((Domain = '') or (Host = '')) then
+        Result := ''
+    else
+        Result := 'http://rokka.' + Domain + '/' + Host + '/'
+                + ParentBoard.BBSID + '/' + ChangeFileExt(FileName, '')
+                + '/?sid=' + SessionID;
+end;
+
+// \8aO\95\94\94Â\89ß\8b\8e\83\8d\83OURL\8eæ\93¾
+function TThreadItem.GetExternalBoardKakoDatURL: string;
+var
+       DatNo: string;
+begin
+       DatNo := ChangeFileExt(FileName, '');
+       //http://xxx.vip2ch.com/xxx/kako/1234/12345/1234567890.dat
+       Result := Format('%s%s/%.4s/%.5s/%s.dat', [ParentBoard.URL, 'kako', DatNo, DatNo, DatNo]);
+end;
+// \8aO\95\94\94Â\89ß\8b\8e\83\8d\83OURL\8eæ\93¾
+
 function TThreadItem.GetSendURL: string;
 begin
        Result := GikoSys.UrlToServer(ParentBoard.URL)
@@ -1301,10 +1794,20 @@ begin
 end;
 
 procedure TThreadItem.DeleteLogFile;
+var
+        tmpFileName: String;
 begin
+       ParentBoard.BeginUpdate;
+
+       if FUnRead then
+               ParentBoard.UnRead := ParentBoard.UnRead - 1;
        DeleteFile(GetThreadFileName);
-               if FileExists(ChangeFileExt(GetThreadFileName,'.NG')) = true then
-                       DeleteFile(ChangeFileExt(GetThreadFileName,'.NG'));
+        //\8e\8e\8c±\93I\82Étmp\82à\8dí\8f\9c\82µ\82Ä\82Ý\82é
+        tmpFileName := StringReplace(GetThreadFileName, 'dat', 'tmp', [rfReplaceAll]);
+        DeleteFile(tmpFileName);
+
+       if FileExists(ChangeFileExt(GetThreadFileName,'.NG')) = true then
+               DeleteFile(ChangeFileExt(GetThreadFileName,'.NG'));
        FRoundDate := ZERO_DATE;
        FLastModified := ZERO_DATE;
        FSize := 0;
@@ -1321,6 +1824,9 @@ begin
        FCount := 0;
        FNewResCount := 0;
        FRoundName := '';
+
+       ParentBoard.EndUpdate;
+       ParentBoard.Modified := True;
 end;
 
 function TThreadItem.GetThreadFileName: string;
@@ -1328,8 +1834,8 @@ begin
        if Length( FilePath ) > 0 then
                Result := FilePath
        else
-               Result := ParentBoard.ParentCategory.ParentBBS2ch.GetLogFolder
-                                               + ParentBoard.BBSID + PATH_DELIM + FileName;
+               Result := GikoSys.Setting.LogFolderP
+                                               + BBS2CH_LOG_FOLDER + PATH_DELIM + ParentBoard.BBSID + PATH_DELIM + FileName;
 end;
 
 procedure TThreadItem.SetLastModified(d: TDateTime);
@@ -1340,14 +1846,6 @@ begin
                ParentBoard.FModified := True;
 end;
 
-{procedure TThreadItem.SetRoundNo(i: Integer);
-begin
-       if FRoundNo = i then Exit;
-       FRoundNo := i;
-       if FUpdate and (ParentBoard <> nil) then
-               ParentBoard.FModified := True;
-end;}
-
 procedure TThreadItem.SetRound(b: Boolean);
 begin
        if b then
@@ -1360,7 +1858,7 @@ begin
                ParentBoard.FModified := True;
 end;
 
-procedure TThreadItem.SetRoundName(s: string);
+procedure TThreadItem.SetRoundName(const s: string);
 begin
        if FRoundName = s then Exit;
        FRoundName := s;
@@ -1368,6 +1866,7 @@ begin
                ParentBoard.FModified := True;
 end;
 
+
 procedure TThreadItem.SetKokomade(i: Integer);
 begin
        if FKokomade = i then Exit;
@@ -1380,8 +1879,14 @@ procedure TThreadItem.SetUnRead(b: Boolean);
 begin
        if FUnRead = b then Exit;
        FUnRead := b;
-       if FUpdate and (ParentBoard <> nil) then
+       if FUpdate and (ParentBoard <> nil) then begin
                ParentBoard.FModified := True;
+               if FUnRead then begin
+                       ParentBoard.UnRead := ParentBoard.UnRead + 1;
+               end else begin
+            ParentBoard.UnRead := ParentBoard.UnRead - 1;
+               end;
+       end;
 end;
 
 procedure TThreadItem.SetScrollTop(i: Integer);
@@ -1402,21 +1907,120 @@ begin
        FUpdate := True;
 end;
 
-function TThreadItem.GetURLWrapper: string;
+function TThreadItem.GetCreateDate: TDateTime;
+begin
+       // \83t\83@\83C\83\8b\96¼\82©\82ç\83X\83\8c\8dì\90¬\93ú\8e\9e\82ð\8b\81\82ß\82é
+       try
+               if ( GikoSys.Setting.CreationTimeLogs ) and not IsLogFile  then
+            Result := ZERO_DATE
+        else begin
+            // \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
+            Result := GikoSys.GetCreateDateFromName(FFileName);
+                       if GikoSys.Setting.FutureThread then begin
+                       if CompareDateTime(Result, Now) = 1 then
+                       Result := ZERO_DATE;
+               end;
+        end;
+
+       except
+               on E: Exception do
+                       Result := ZERO_DATE;
+       end;
+end;
+function TThreadItem.GetFilePath: String;
+var
+       path : String;
+begin
+       path := ExtractFilePath(Self.ParentBoard.FilePath) + Self.FileName;
+    Result := path;
+end;
+
+destructor TBoardGroup.Destroy;
+begin
+       Clear;
+       inherited;
+end;
+procedure      TBoardGroup.Clear;
+var
+       i       : Integer;
+begin
+       for i := Self.Count - 1 downto 0 do begin
+               try
+                       TBoard(Self.Objects[i]).Free;
+               except
+               end;
+    end;
+    inherited Clear;
+       Self.Capacity := 0;
+       try
+               if FBoardPlugIn <> nil then
+                       FBoardPlugIn.Free;
+        FBoardPlugIn := nil;
+       except
+       end;
+
+end;
+
+function TSpecialBoard.Add(item: TThreadItem): integer;
+begin
+    Result := inherited AddObject(Item.URL, Item);
+end;
+
+procedure TSpecialBoard.Clear;
+var
+       i: integer;
 begin
+    for i := Count - 1 downto 0 do
+               DeleteList(i);
+    Capacity := 0;
+end;
+
+///////////////
+constructor TThreadNgList.Create;
+begin
+       inherited Create;
+
+       FFilePath := GikoSys.GetNGWordsDir;
+       if not DirectoryExists(FFilePath) then
+               ForceDirectories(FFilePath);
+       if (FFilePath[Length(FFilePath)] <> '\') then
+               FFilePath := FFilePath + '\';
+       FFilePath := FFilePath + THREAD_NG_FILE;
+       Load;
+end;
 
-       If FURL = '' Then
-               Result := GikoSys.GetURL( ParentBoard.BBSID, FileName )
-       Else
-               Result := FURL;
+procedure TThreadNgList.Load;
+begin
+       if (FileExists(FFilePath)) then begin
+               try
+                       LoadFromFile(FFilePath);
+               finally
+               end;
+       end;
+end;
 
+procedure TThreadNgList.Save;
+begin
+       try
+               SaveToFile(FFilePath);
+       finally
+       end;
 end;
 
-{initialization
-       BBS2ch := TBBS2ch.Create;
+function TThreadNgList.IsNG(const Title: String): Boolean;
+var
+    Cnt: Integer;
+    MaxCnt: Integer;
+begin
+    MaxCnt := Count - 1;
+    for Cnt := 0 to MaxCnt do begin
+        if (Pos(Strings[Cnt], Title) > 0) then begin
+            Result := True;
+            Exit;
+        end;
+    end;
+    Result := False;
+end;
 
-finalization
-       if BBS2ch <> nil then
-               BBS2ch.Free;}
 end.