6 Windows, Messages, SysUtils, Classes,
7 GikoSystem, BoardGroup;
10 TGikoRoundType = (grtBoard, grtItem);
13 TRoundList = class(TObject)
15 FOldFileRead: Boolean;
18 function GetCount(RoundType: TGikoRoundType): Integer;
19 function GetRoundItem(Index: Integer; RoundType: TGikoRoundType): TRoundItem;
20 function ParseRoundBoardLine(Line: string): Boolean;
21 function ParseRoundThreadLine(Line: string): Boolean;
22 function ParseOldRoundBoardLine(Line: string): Boolean;
23 function ParseOldRoundThreadLine(Line: string): Boolean;
25 RoundNameList: TStringList;
28 destructor Destroy; override;
29 function Add(Board: TBoard): Integer; overload;
30 function Add(ThreadItem: TThreadItem): Integer; overload;
31 procedure Delete(Board: TBoard); overload;
32 procedure Delete(ThreadItem: TThreadItem); overload;
33 procedure Delete(URL: string; RoundType: TGikoRoundType); overload;
35 function Find(Board: TBoard): Integer; overload;
36 function Find(ThreadItem: TThreadItem): Integer; overload;
37 function Find(URL: string; RoundType: TGikoRoundType): Integer; overload;
38 property Count[RoundType: TGikoRoundType]: Integer read GetCount;
39 property OldFileRead: Boolean read FOldFileRead;
40 property Items[Index: integer; RoundType: TGikoRoundType]: TRoundItem read GetRoundItem;
41 procedure LoadRoundBoardFile;
42 procedure LoadRoundThreadFile;
43 procedure SaveRoundFile;
45 procedure URLReplace(oldURLs: TStringList; newURLs :TStringList);
48 TRoundItem = class(TObject)
51 FRoundType: TGikoRoundType;
53 FBoolData: Boolean; //
\82¢
\82ë
\82¢
\82ë
\8eg
\82¤
\82å
\82£
54 function GetBoardTitle : string;
55 function GetThreadTitle : string;
56 function GetURL : string;
57 function GetFileName : string;
58 //!
\8f\84\89ñ
\96¼
\8eæ
\93¾
59 function GetRoundName : string;
62 property Item : TObject read FItem;
63 property RoundName: string read GetRoundName;
64 property RoundType: TGikoRoundType read FRoundType write FRoundType;
65 property URL : string read GetURL;
66 property TmpURL : string read FTmpURL write FTmpURL;
67 property BoardTitle: string read GetBoardTitle;
68 property ThreadTitle: string read GetThreadTitle;
69 property FileName: string read GEtFileName;
70 property BoolData: Boolean read FBoolData write FBoolData;
74 RoundList: TRoundList;
78 ROUND_BOARD_FILENAME: string = 'RoundBoard.2ch'; //
\82 \82Æ
\82ÅBoardGroup
\82Ö
\88Ú
\93®
79 ROUND_ITEM_FILENAME: string = 'RoundItem.2ch'; //
\93¯
\8fã
80 ROUND_INDEX_VERSION: string = '2.00';
81 ERROR_BOARD_FILENAME: string = 'ErrorBoard.2ch'; //Error
\8ds
\82ð
\95Û
\8aÇ
\82·
\82é
82 ERROR_ITEM_FILENAME: string = 'ErrorItem.2ch'; //Error
\8ds
\82ð
\95Û
\8aÇ
\82·
\82é
83 //!
\8f\84\89ñ
\83A
\83C
\83e
\83\80\83R
\83\93\83X
\83g
\83\89\83N
\83^
84 constructor TRoundItem.Create;
89 function TRoundItem.GetBoardTitle : string;
92 if( Self.FItem <> nil) then begin
93 if( Self.FItem is TBoard) then begin
94 Result := TBoard(Self.FItem).Title;
95 end else if( Self.FItem is TThreadItem) then begin
96 Result := TThreadItem(Self.FItem).ParentBoard.Title;
100 //!
\83X
\83\8c\83b
\83h
\96¼
\8eæ
\93¾
101 function TRoundItem.GetThreadTitle : string;
104 if( Self.FItem <> nil) then begin
105 if( Self.FItem is TThreadItem) then begin
106 Result := TThreadItem(Self.FItem).Title;
111 function TRoundItem.GetURL : string;
114 if( Self.FItem <> nil) then begin
115 if( Self.FItem is TBoard) then begin
116 Result := TBoard(Self.FItem).URL;
117 end else if( Self.FItem is TThreadItem) then begin
118 Result := TThreadItem(Self.FItem).URL;
122 //!
\83X
\83\8c\83b
\83h
\82Ì
\83t
\83@
\83C
\96¼
\8eæ
\93¾
123 function TRoundItem.GetFileName : string;
126 if( Self.FItem <> nil) then begin
127 if( Self.FItem is TThreadItem) then begin
128 Result := TThreadItem(Self.FItem).FileName;
132 //!
\8f\84\89ñ
\96¼
\8eæ
\93¾
133 function TRoundItem.GetRoundName : string;
136 if( Self.FItem <> nil) then begin
137 if( Self.FItem is TBoard) then begin
138 Result := TBoard(Self.FItem).RoundName;
139 end else if( Self.FItem is TThreadItem) then begin
140 Result := TThreadItem(Self.FItem).RoundName;
144 //!
\8f\84\89ñ
\83\8a\83X
\83g
\83R
\83\93\83X
\83g
\83\89\83N
\83^
145 constructor TRoundList.Create;
148 FBoardList := TList.Create;
149 FItemList := TList.Create;
150 RoundNameList := TStringList.Create;
151 RoundNameList.Sorted := True;
152 RoundNameList.Duplicates := dupIgnore;
153 FOldFileRead := false;
155 //!
\8f\84\89ñ
\83\8a\83X
\83g
\83f
\83X
\83g
\83\89\83N
\83^
156 destructor TRoundList.Destroy;
164 //!
\8f\84\89ñ
\97\
\96ñ
\92Ç
\89Á
\81i
\94Â
\81j
165 function TRoundList.Add(Board: TBoard): Integer;
172 if idx = -1 then begin
173 Item := TRoundItem.Create;
175 // Item.BBSType := gbt2ch; //
\82Æ
\82è
\82 \82¦
\82¸
176 Item.RoundType := grtBoard;
177 Result := FBoardList.Add(Item);
180 //!
\8f\84\89ñ
\97\
\96ñ
\92Ç
\89Á
\81i
\83X
\83\8c\83b
\83h
\81j
181 function TRoundList.Add(ThreadItem: TThreadItem): Integer;
187 idx := Find(ThreadItem);
188 if idx = -1 then begin
189 Item := TRoundItem.Create;
190 Item.FItem := ThreadItem;
191 // Item.BBSType := gbt2ch; //
\82Æ
\82è
\82 \82¦
\82¸
192 Item.RoundType := grtItem;
193 Result := FItemList.Add(Item);
196 //!
\8f\84\89ñ
\97\
\96ñ
\8dí
\8f\9c\81i
\94Â
\81j
197 procedure TRoundList.Delete(Board: TBoard);
202 if idx <> -1 then begin
203 TBoard(TRoundItem(FBoardList[idx]).FItem).RoundName := '';
204 TRoundItem(FBoardList[idx]).Free;
205 FBoardList.Delete(idx);
208 //!
\8f\84\89ñ
\97\
\96ñ
\8dí
\8f\9c\81i
\83X
\83\8c\83b
\83h
\81j
209 procedure TRoundList.Delete(ThreadItem: TThreadItem);
213 idx := Find(ThreadItem);
214 if idx <> -1 then begin
215 TThreadItem(TRoundItem(FItemList[idx]).FItem).RoundName := '';
216 TRoundItem(FItemList[idx]).Free;
217 FItemList.Delete(idx);
220 //!
\8f\84\89ñ
\97\
\96ñ
\8fÁ
\8b\8e
221 procedure TRoundList.Clear;
225 for i := FBoardList.Count - 1 downto 0 do begin
226 if FBoardList[i] <> nil then
227 TRoundItem(FBoardList[i]).Free;
228 FBoardList.Delete(i);
230 FBoardList.Capacity := FBoardList.Count;
231 for i := FItemList.Count - 1 downto 0 do begin
232 if FItemList[i] <> nil then
233 TRoundItem(FItemList[i]).Free;
236 FItemList.Capacity := FItemList.Count;
238 //!
\8f\84\89ñ
\97\
\96ñ
\8c\9f\8dõ
\81i
\94Â
\81j
239 function TRoundList.Find(Board: TBoard): Integer;
245 for i := 0 to FBoardList.Count - 1 do begin
246 Item := TRoundItem(FBoardList[i]);
247 if Item.FRoundType <> grtBoard then Continue;
248 if Item.FItem = Board then begin
254 //!
\8f\84\89ñ
\97\
\96ñ
\8c\9f\8dõ
\81i
\83X
\83\8c\83b
\83h
\81j
255 function TRoundList.Find(ThreadItem: TThreadItem): Integer;
261 for i := 0 to FItemList.Count - 1 do begin
262 Item := TRoundItem(FItemList[i]);
263 if Item.FRoundType <> grtItem then Continue;
264 if Item.FItem = ThreadItem then begin
270 //!
\8f\84\89ñ
\97\
\96ñ
\8c\9f\8dõ
\81iURL
\81{
\83A
\83C
\83e
\83\80\83^
\83C
\83v
\81j
271 function TRoundList.Find(URL: string; RoundType: TGikoRoundType): Integer;
277 if RoundType = grtItem then begin
278 for i := 0 to FItemList.Count - 1 do begin
279 Item := TRoundItem(FItemList[i]);
280 if Item.FRoundType <> RoundType then Continue;
281 if Item.URL = URL then begin
287 for i := 0 to FBoardList.Count - 1 do begin
288 Item := TRoundItem(FBoardList[i]);
289 if Item.FRoundType <> RoundType then Continue;
290 if Item.URL = URL then begin
297 //!
\8f\84\89ñ
\97\
\96ñ
\8dí
\8f\9c\81iURL
\81{
\83A
\83C
\83e
\83\80\83^
\83C
\83v
\81j
298 procedure TRoundList.Delete(URL: string; RoundType: TGikoRoundType);
303 threadItem: TThreadItem;
305 idx := Find(URL, RoundType);
306 if idx <> -1 then begin
308 if RoundType = grtBoard then begin
309 Item := TRoundItem(FBoardList[idx]);
310 board := TBoard(Item);
312 FBoardList.Delete(idx);
313 if board <> nil then begin
314 board.Round := False;
315 board.RoundName := '';
318 Item := TRoundItem(FItemList[idx]);
319 threadItem := TThreadItem(Item.FItem);
321 FItemList.Delete(idx);
323 if threadItem <> nil then begin
324 threadItem.Round := false;
325 threadItem.RoundName := '';
330 //!
\8f\84\89ñ
\97\
\96ñ
\90\94\8eæ
\93¾
331 function TRoundList.GetCount(RoundType: TGikoRoundType): Integer;
334 if RoundType = grtBoard then
335 Result := FBoardList.Count
336 else if RoundType = grtItem then
337 Result := FItemList.Count;
339 //!
\8f\84\89ñ
\97\
\96ñ
\8eæ
\93¾
340 function TRoundList.GetRoundItem(Index: Integer; RoundType: TGikoRoundType): TRoundItem;
343 if RoundType = grtBoard then begin
344 if (Index >= 0) and (Index < FBoardList.Count) then
345 Result := TRoundItem(FBoardList[Index]);
346 end else if RoundType = grtItem then begin
347 if (Index >= 0) and (Index < FItemList.Count) then
348 Result := TRoundItem(FItemList[Index]);
351 //!
\83{
\81[
\83h
\8f\84\89ñ
\97\
\96ñ
\83t
\83@
\83C
\83\8b\93Ç
\82Ý
\8d\9e\82Ý
352 procedure TRoundList.LoadRoundBoardFile;
356 FileName, bFileName: string;
357 errorSl: TStringList;
358 errorFileName: string;
361 sl := TStringList.Create;
362 errorSl := TStringList.Create;
363 errorSl.Duplicates := dupIgnore;
365 //
\83{
\81[
\83h
\8f\84\89ñ
\83t
\83@
\83C
\83\8b\93Ç
\82Ý
\8d\9e\82Ý
366 FileName := GikoSys.GetConfigDir + ROUND_BOARD_FILENAME;
367 bFileName := GikoSys.GetConfigDir + '~' + ROUND_BOARD_FILENAME;
368 //
\83G
\83\89\81[
\8ds
\95Û
\91¶
\83t
\83@
\83C
\83\8b\93Ç
\82Ý
\8d\9e\82Ý
369 errorFileName := GikoSys.GetConfigDir + ERROR_BOARD_FILENAME;
371 if FileExists(FileName) then begin
372 sl.LoadFromFile(FileName);
373 if FileExists(bFileName) then
374 DeleteFile(bFileName);
375 //
\83o
\83b
\83N
\83A
\83b
\83v
\97p
\82Ì
\83t
\83@
\83C
\83\8b\82ð
\8dì
\90¬
\82·
\82é
376 sl.SaveToFile(bFileName);
377 if FileExists(errorFileName) then begin
379 errorSl.LoadFromFile(errorFileName);
383 //Item := TRoundItem.Create;
385 if sl.Count = 0 then begin
386 //
\83G
\83\89\81[
\97\8e\82¿
\82·
\82é
\82È
\82Ç
\82µ
\82Ä
\83t
\83@
\83C
\83\8b\82Ì
\93à
\97e
\82ª
\8bó
\82¾
\82Æ
\83G
\83\89\81[
\82É
\82È
\82é
\91Î
\8dô
387 sl.Add(ROUND_INDEX_VERSION);
391 //
\82P
\8ds
\96Ú
\82Í
\83o
\81[
\83W
\83\87\83\93
392 if sl[0] = ROUND_INDEX_VERSION then begin
393 for i := 1 to sl.Count - 1 do begin
394 if not ParseRoundBoardLine(sl[i - delCount]) then begin
395 errorSl.Add( sl[i - delCount] );
396 sl.Delete(i- delCount);
401 if FOldFileRead then begin //
\83M
\83R
\83i
\83r
\96{
\91Ì
\82ª
\83{
\81[
\83h
\83t
\83@
\83C
\83\8b\82ð
\82æ
\82Ý
\82Æ
\82Á
\82½
\8cã
\82¶
\82á
\82È
\82¢
\82Æ
\83N
\83\89\83b
\83V
\83\85\82·
\82é
\82Ì
\82Å
402 for i := 1 to sl.Count - 1 do begin
403 if not ParseOldRoundBoardLine(sl[i - delCount]) then begin
404 errorSl.Add( sl[i- delCount] );
405 sl.Delete(i- delCount);
410 FOldFileRead := true;
413 if errorSl.Count > 0 then
414 errorSl.SaveToFile(errorFileName);
420 //!
\83X
\83\8c\83b
\83h
\8f\84\89ñ
\97\
\96ñ
\83t
\83@
\83C
\83\8b\93Ç
\82Ý
\8d\9e\82Ý
421 procedure TRoundList.LoadRoundThreadFile;
425 FileName, bFileName: string;
426 errorSl: TStringList;
427 errorFileName: string;
430 errorSl := TStringList.Create;
431 errorSl.Duplicates := dupIgnore;
432 sl := TStringList.Create;
434 //
\83X
\83\8c\8f\84\89ñ
\83t
\83@
\83C
\83\8b\93Ç
\82Ý
\8d\9e\82Ý
435 FileName := GikoSys.GetConfigDir + ROUND_ITEM_FILENAME;
436 bFileName := GikoSys.GetConfigDir + '~' + ROUND_ITEM_FILENAME;
437 //
\83G
\83\89\81[
\8ds
\95Û
\91¶
\83t
\83@
\83C
\83\8b\93Ç
\82Ý
\8d\9e\82Ý
438 errorFileName := GikoSys.GetConfigDir + ERROR_ITEM_FILENAME;
439 if FileExists(FileName) then begin
440 sl.LoadFromFile(FileName);
441 if FileExists(bFileName) then
442 DeleteFile(bFileName);
443 sl.SaveToFile(bFileName);
444 if FileExists(errorFileName) then begin
446 errorSl.LoadFromFile(errorFileName);
450 //Item := TRoundItem.Create;
451 if sl.Count = 0 then begin
452 //
\83G
\83\89\81[
\97\8e\82¿
\82·
\82é
\82È
\82Ç
\82µ
\82Ä
\83t
\83@
\83C
\83\8b\82Ì
\93à
\97e
\82ª
\8bó
\82¾
\82Æ
\83G
\83\89\81[
\82É
\82È
\82é
\91Î
\8dô
453 sl.Add(ROUND_INDEX_VERSION);
457 //
\82P
\8ds
\96Ú
\82Í
\83o
\81[
\83W
\83\87\83\93
458 if sl[0] = ROUND_INDEX_VERSION then begin
459 for i := 1 to sl.Count - 1 do begin
460 if not ParseRoundThreadLine(sl[i - delCount]) then begin
461 errorSl.Add(sl[i - delCount]);
462 sl.Delete(i - delCount);
468 for i := 1 to sl.Count - 1 do begin
469 if not ParseOldRoundThreadLine(sl[i - delCount]) then begin
470 errorSl.Add(sl[i - delCount]);
471 sl.Delete(i - delCount);
476 if errorSl.Count > 0 then
477 errorSl.SaveToFile(errorFileName);
484 //!
\8f\84\89ñ
\97\
\96ñ
\83t
\83@
\83C
\83\8b\95Û
\91¶
485 procedure TRoundList.SaveRoundFile;
493 GikoSys.ForceDirectoriesEx(GikoSys.GetConfigDir);
495 sl := TStringList.Create;
497 FileName := GikoSys.GetConfigDir + ROUND_BOARD_FILENAME;
498 sl.Add(ROUND_INDEX_VERSION);
499 for i := 0 to FBoardList.Count - 1 do begin
500 Item := TRoundItem(FBoardList[i]);
502 if Item.TmpURL <> '' then begin
503 s := Item.TmpURL + #1
504 + Item.BoardTitle + #1
508 + Item.BoardTitle + #1
515 sl.SaveToFile(FileName);
517 FileName := GikoSys.GetConfigDir + ROUND_ITEM_FILENAME;
518 sl.Add(ROUND_INDEX_VERSION);
519 for i := 0 to FItemList.Count - 1 do begin
520 Item := TRoundItem(FItemList[i]);
522 if Item.TmpURL <> '' then begin
523 s := Item.TmpURL + #1
524 + Item.BoardTitle + #1
526 + Item.ThreadTitle + #1
530 + Item.BoardTitle + #1
532 + Item.ThreadTitle + #1
539 sl.SaveToFile(FileName);
544 function TRoundList.ParseRoundBoardLine(Line: string): Boolean;
551 //Result := TRoundItem.Create;
552 //Result.ThreadTitle := '';
553 //Result.FileName := '';
554 //Result.RoundType := grtBoard;
556 for i := 0 to 2 do begin
557 s := GikoSys.GetTokenIndex(Line, #1, i);
562 board := BBSsFindBoardFromURL(s);
565 //1: Result.BoardTitle := s;
573 if( board <> nil ) then begin
574 if not board.Round then begin
575 board.RoundName := roundname;
576 RoundNameList.Add(roundname);
577 //RoundNameList.Find(roundname, i);
578 //board.RoundName := PChar(RoundNameList[i]);
587 function TRoundList.ParseRoundThreadLine(Line: string): Boolean;
591 threadItem: TThreadItem;
593 // threadItem: TThreadItem;
595 //Result := TRoundItem.Create;
596 //Result.RoundType := grtItem;
598 for i := 0 to 4 do begin
599 s := GikoSys.GetTokenIndex(Line, #1, i);
605 threadItem := BBSsFindThreadFromURL( s );
606 //if threadItem <> nil then begin
607 // BoardList.Add( threadItem.ParentBoard.URL );
610 //1: Result.BoardTitle := s;
611 //2: Result.FileName := s;
612 //3: Result.ThreadTitle := s;
620 if( threadItem <> nil ) then begin
621 if not threadItem.Round then begin
622 threadItem.RoundName := roundname;
623 RoundNameList.Add(roundname);
624 //RoundNameList.Find(roundname, i);
625 //threadItem.RoundName := PChar(RoundNameList[i]);
626 threadItem.Round := True;
634 function TRoundList.ParseOldRoundBoardLine(Line: string): Boolean;
641 //Result := TRoundItem.Create;
642 //Result.ThreadTitle := '';
643 //Result.FileName := '';
644 //Result.RoundType := grtBoard;
646 for i := 0 to 2 do begin
647 s := GikoSys.GetTokenIndex(Line, #1, i);
652 board := BBSs[ 0 ].FindBBSID( s );
653 if board = nil then begin
654 raise Exception.Create('
\82±
\82Ì
\8f\84\89ñ
\82Í
\93Ç
\82Ý
\8d\9e\82ß
\82È
\82¢
\82æ
\81i
\91½
\95ª
\8aO
\95\94\94Â
\81j');
657 //1: Result.FBoardTitle := s;
665 if( board <> nil ) then begin
666 if not board.Round then begin
667 board.RoundName := roundname;
668 RoundNameList.Add(roundname);
669 //RoundNameList.Find(roundname, i);
670 //board.RoundName := PChar(RoundNameList[i]);
679 function TRoundList.ParseOldRoundThreadLine(Line: string): Boolean;
686 threadItem: TThreadItem;
689 // Result := TRoundItem.Create;
690 // Result.RoundType := grtItem;
692 for i := 0 to 4 do begin
693 s := GikoSys.GetTokenIndex(Line, #1, i);
697 //1: Result.BoardTitle := s;
700 //Result.FileName := s;
701 board := BBSs[ 0 ].FindBBSID(bbsID);
702 if board <> nil then begin
703 buf := Copy(board.GetSendURL,1,LastDelimiter('/', board.GetSendURL)-1);
704 buf := buf + '/read.cgi/'+ board.BBSID+ '/' +ChangeFileExt(s,'') + '/l50';
705 threadItem := BBSsFindThreadFromURL(buf);
707 raise Exception.Create('
\82±
\82Ì
\8f\84\89ñ
\82Í
\93Ç
\82Ý
\8d\9e\82ß
\82È
\82¢
\82æ');
710 //3: Result.ThreadTitle := s;
718 if( threadItem <> nil ) then begin
719 if not threadItem.Round then begin
720 threadItem.RoundName := roundname;
721 RoundNameList.Add(roundname);
722 //RoundNameList.Find(roundname, i);
723 //threadItem.RoundName := PChar(RoundNameList[i]);
724 threadItem.Round := true;
732 procedure TRoundList.URLReplace(oldURLs: TStringList; newURLs :TStringList);
739 oldBoardName: string;
741 newBoardName: string;
743 if oldURLs.Count <> newURLs.Count then
745 //
\82±
\82±
\82©
\82ç
\81ABoard
\82ÌURL
\82Ì
\95Ï
\8dX
746 for j :=0 to oldURLs.Count - 1 do begin
747 for i :=0 to FBoardList.Count - 1 do begin
748 if TRoundItem(FBoardList[i]).URL = oldURLs[j] then
749 TRoundItem(FBoardList[i]).TmpURL := newURLs[j];
752 //
\82±
\82±
\82Ü
\82Å
\81ABoard
\82ÌURL
\82Ì
\95Ï
\8dX
754 //
\82±
\82±
\82©
\82ç
\81AThread
\82ÌURL
\82Ì
\95Ï
\8dX
755 //
\96Ê
\93|
\82¾
\82¯
\82Çthread
\82Í
\82»
\82ê
\82¼
\82êURL
\82ð
\83`
\83\83\83b
\83N
\82µ
\82È
\82ª
\82ç
\82â
\82Á
\82Ä
\82©
\82È
\82«
\82á
\82¢
\82¯
\82È
\82¢
\81B
756 for i := 0 to oldURLs.Count - 1 do begin
757 tmpURL := Copy(oldURLs[i], 1, Length(oldURLs[i]) -1);
758 oldHost := Copy(tmpURL, 1, LastDelimiter('/', tmpURL) );
759 oldBoardName := Copy(tmpURL, LastDelimiter('/', tmpURL), Length(tmpURL) ) + '/';
760 tmpURL := Copy(newURLs[i], 1, Length(newURLs[i]) -1);
761 newHost := Copy(tmpURL, 1, LastDelimiter('/', tmpURL) );
762 newBoardName := Copy(tmpURL, LastDelimiter('/', tmpURL), Length(tmpURL) ) + '/';
764 for j := 0 to FItemList.Count - 1 do begin
765 tempString := TRoundItem(FItemList[j]).URL;
766 if ( AnsiPos(oldBoardName, tempString) <> 0 ) and ( AnsiPos(oldHost, tempString ) <> 0 ) then begin
767 tempString := StringReplace(tempString, oldHost, newHost,[]);
768 TRoundItem(FItemList[j]).TmpURL := tempString;
772 //
\82±
\82±
\82Ü
\82Å
\81AThread
\82ÌURL
\82Ì
\95Ï
\8dX