program WoWHeadLoader; {$mode objfpc}{$H+} uses {$IFDEF UNIX}{$IFDEF UseCThreads} cthreads, {$ENDIF}{$ENDIF} Classes, SysUtils, CustApp { you can add units after this } , IdHTTP, USqlDatabase ; //poznámka mazání pro testování //DELETE FROM `wowpreklad`.`TextQuest` WHERE `textquest`.`VersionStart` = 17359; //UPDATE `wowpreklad`.`TextQuest` SET `VersionEnd` = 12340 WHERE `textquest`.`VersionEnd` = 17359; type { TWoWHeadLoader } TWoWHeadLoader = class(TCustomApplication) protected procedure DoRun; override; public IdHTTP1: TIdHTTP; Database: TSqlDatabase; constructor Create(TheOwner: TComponent); override; destructor Destroy; override; procedure WriteHelp; virtual; procedure Main; function skipto(part: string; text: string): string; function GetPartText(textname: string; text: string): string; function TextStringReplace(text:string):string; function CompareStrings(textdb: string; textimport: string): boolean; function DelHtmlTags(text:string):string; function SqlPre(text: string):string; procedure UpdateTranslated; function GetMaxIDQuest: integer; procedure GetTextQuest(Id: Integer); procedure UpdateQuest; function GetLastVersion: integer; end; var ImportedVersion: integer; { TWoWHeadLoader } // IdHTTP1.Request.UserAgent:= 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)'; // IdHTTP1.ReadTimeout:= 1000; // IdHTTP1.RedirectCount := 15; // IdHTTP1.HandleRedirects:=true; procedure TWoWHeadLoader.DoRun; var ErrorMsg: String; begin // quick check parameters ErrorMsg:=CheckOptions('h','help'); if ErrorMsg<>'' then begin ShowException(Exception.Create(ErrorMsg)); Terminate; Exit; end; // parse parameters if HasOption('h','help') then begin WriteHelp; Terminate; Exit; end; //inicialization Database := TSqlDatabase.Create; IdHTTP1:= TIdHTTP.Create(nil); if HasOption('s', 'schema') then begin Database.Database := GetOptionValue('s', 'schema'); end else Database.Database := 'wowpreklad'; if HasOption('u', 'user') then begin Database.UserName := GetOptionValue('u', 'user'); end else Database.UserName := 'wowpreklad'; if HasOption('p', 'password') then begin Database.Password := GetOptionValue('p', 'password'); end else Database.Password := 'hezkycesky'; if HasOption('t', 'host') then begin Database.Hostname := GetOptionValue('t', 'host'); end else Database.Hostname := 'localhost'; if HasOption('a', 'Charset') then begin Database.Encoding := GetOptionValue('a', 'Charset'); end else Database.Encoding := 'utf8'; Database.Connect; WriteLn('Nez zacnete aktualizujte udaje v tabulce ClientVersion. Texty se budou importovat jako nejpozdejsi pojmenovana verze!'); // ReadLn; ImportedVersion:=GetLastVersion; Main; { add your program here } Database.Disconnect; Database.Free; IdHTTP1.Free; // stop program loop Terminate; end; constructor TWoWHeadLoader.Create(TheOwner: TComponent); begin inherited Create(TheOwner); StopOnException:=True; end; destructor TWoWHeadLoader.Destroy; begin inherited Destroy; end; procedure TWoWHeadLoader.WriteHelp; // D:\ProgramFiles\lazarus\units\indy-10.2.0.3\fpc\ begin { add your help code here } writeln('Usage: ',ExeName,' -h'); end; procedure TWoWHeadLoader.Main; begin UpdateQuest; //čekej před uzavřením ReadLn; end; procedure TWoWHeadLoader.GetTextQuest(Id: Integer); var part,data, textname,textobject,textdescription, textcomplection,textprogress,textend, textobjective1,textobjective2,textobjective3,textobjective4: string; foundtext: boolean= true; itemfound: boolean = false; DBRows : TDbRows; i: integer; begin try //načtení data := IdHTTP1.Get('http://www.wowhead.com/quest='+IntToStr(Id)); // WriteLn(data); if (pos('This quest was marked obsolete',data) > 0) or (pos('ewrThis quest is no longer available',data) > 0) then begin foundtext:=false; Exit; end; WriteLn(); WriteLn('guest='+IntToStr(Id)); textdescription := TextStringReplace(GetPartText('Description',data)); textcomplection := TextStringReplace(GetPartText('completion',data)); //OfferRewardText textprogress := TextStringReplace(GetPartText('progress',data)); //RequestItemsText if (textprogress = '') then textprogress := TextStringReplace(GetPartText('Progress',data)); //RequestItemsText //skok na text data := skipto('
',data); //name data := skipto('

',data); textname := TextStringReplace(Copy(data,0,Pos('

',data)-1)); //objektiv data := skipto('',data); for i:= 0 to 4 do begin data := StringReplace(data,'
','$B',[rfIgnoreCase]); data := StringReplace(data,'
','$B',[rfIgnoreCase]); end; textobject := TextStringReplace(Copy(data,0,Pos('<',data)-1)); //endtext data := skipto('',data); //mazání podsupin u objektů questu if (0 < Pos('
0) do begin data := StringReplace(data,'0) do begin data := StringReplace(data,'',data); part := Copy(data,0,Pos('',data)-1); if ((Pos('item',part) > 0)) then begin itemfound:=true; textend:=''; end; if ((Pos('<',part) = 0)) then begin textend:=DelHtmlTags(part); end; if ((Pos('object=',part) > 0) or (Pos('javascript',part) > 0) or ((itemfound = false) and (Pos('<',part) = 0))) then begin part := DelHtmlTags(part); //todo if (textobjective1 = '') then textobjective1:=part else if (textobjective2 = '') then textobjective2:=part else if (textobjective3 = '') then textobjective3:=part else if (textobjective4 = '') then textobjective4:=part; end; data := skipto('',data); until (Pos('
',data) = 0); if ((itemfound = false) and (textend <> '')) then begin if (textobjective4 <> '') then textobjective4:='' else if (textobjective3 <> '') then textobjective3:='' else if (textobjective2 <> '') then textobjective2:='' else if (textobjective1 <> '') then textobjective1:=''; end; except on E: EIdHTTPProtocolException do begin if E.ErrorCode = 404 then foundtext := false; end; // on E: EIdSocketError do then // foundtext := false; // end; end; If foundtext then begin DBRows := Database.Query('SELECT * FROM `TextQuest` WHERE `Entry` ='+IntToStr(Id)+' AND `Language` =0 ORDER BY `TextQuest`.`VersionEnd` DESC '); { if not (CompareStrings(DBRows.Data[0].Values['Title'],textname)) then WriteLn('"'+DBRows.Data[0].Values['Title']+'" XXXXX "'+textname+'"'); if not (CompareStrings(DBRows.Data[0].Values['Objectives'],textobject)) then WriteLn('"'+DBRows.Data[0].Values['Objectives']+'" XXXXX "'+textobject+'"'); if not (CompareStrings(DBRows.Data[0].Values['Details'],textdescription)) then WriteLn('"'+DBRows.Data[0].Values['Details']+'" XXXXX "'+textdescription+'"'); if not (CompareStrings(DBRows.Data[0].Values['OfferRewardText'],textcomplection)) then WriteLn('"'+DBRows.Data[0].Values['OfferRewardText']+'" XXXXX "'+textcomplection+'"'); if not (CompareStrings(DBRows.Data[0].Values['RequestItemsText'],textprogress)) then WriteLn('"'+DBRows.Data[0].Values['RequestItemsText']+'" XXXXX "'+textprogress+'"'); if not (CompareStrings(DBRows.Data[0].Values['EndText'],textend)) then WriteLn('"'+DBRows.Data[0].Values['EndText']+'" XXXXX "'+textend+'"'); if not (CompareStrings(DBRows.Data[0].Values['ObjectiveText1'],textobjective1)) then WriteLn('"'+DBRows.Data[0].Values['ObjectiveText1']+'" XXXXX "'+textobjective1+'"'); if not (CompareStrings(DBRows.Data[0].Values['ObjectiveText2'],textobjective2)) then WriteLn('"'+DBRows.Data[0].Values['ObjectiveText2']+'" XXXXX "'+textobjective2+'"'); if not (CompareStrings(DBRows.Data[0].Values['ObjectiveText3'],textobjective3)) then WriteLn('"'+DBRows.Data[0].Values['ObjectiveText3']+'" XXXXX "'+textobjective3+'"'); if not (CompareStrings(DBRows.Data[0].Values['ObjectiveText4'],textobjective4)) then WriteLn('"'+DBRows.Data[0].Values['ObjectiveText4']+'" XXXXX "'+textobjective4+'"'); } if (DBRows.Count > 0) then if ( CompareStrings(DBRows.Data[0].Values['Title'],textname) and CompareStrings(DBRows.Data[0].Values['Objectives'],textobject) and CompareStrings(DBRows.Data[0].Values['Details'],textdescription) and CompareStrings(DBRows.Data[0].Values['OfferRewardText'],textcomplection) and CompareStrings(DBRows.Data[0].Values['RequestItemsText'],textprogress) and CompareStrings(DBRows.Data[0].Values['EndText'],textend) and CompareStrings(DBRows.Data[0].Values['ObjectiveText1'],textobjective1) and CompareStrings(DBRows.Data[0].Values['ObjectiveText2'],textobjective2) and CompareStrings(DBRows.Data[0].Values['ObjectiveText3'],textobjective3) and CompareStrings(DBRows.Data[0].Values['ObjectiveText4'],textobjective4) ) then begin //text je stejný Database.Query('UPDATE `wowpreklad`.`TextQuest` SET `VersionEnd` = '+IntToStr(ImportedVersion)+' WHERE `Id` = '+DBRows.Data[0].Values['Id']+' AND `Entry` ='+IntToStr(Id)+';'); DBRows := Database.Query('UPDATE `wowpreklad`.`TextQuest` SET `VersionEnd` = '+IntToStr(ImportedVersion)+' WHERE `Take` = '+DBRows.Data[0].Values['Id']+' AND `Entry` ='+IntToStr(Id)+';'); //TODO: update caskadově všechny texty vycházející z této verze UpdateTranslated; Exit; end; begin //v databázi je víc textu než v importu -> vytvoř novou verzi a vytvoř nový překlad { WriteLn('INSERT INTO `wowpreklad`.`TextQuest` (' +'`ID` ,`Entry` ,`Title` ,`Details` ,`Objectives` ,`OfferRewardText` ,`RequestItemsText` ,`EndText` ,' +'`ObjectiveText1` ,`ObjectiveText2` ,`ObjectiveText3` ,`ObjectiveText4` ' +',`Language` , `User` , `Complete` ,`CompleteParts` ,`Take` ,`VersionStart` ,`VersionEnd` ,`ModifyTime`)' +'VALUES (NULL,'+IntToStr(Id)+',' +SqlPre(textname)+', ' +SqlPre(textdescription)+', ' +SqlPre(textobject)+', ' +SqlPre(textcomplection)+', ' +SqlPre(textprogress)+', ' +SqlPre(textend)+', ' +SqlPre(textobjective1)+', ' +SqlPre(textobjective2)+', ' +SqlPre(textobjective3)+', ' +SqlPre(textobjective4)+', ' +'0, NULL, 1,0,NULL,'+IntToStr(ImportedVersion)+','+IntToStr(ImportedVersion)+',NOW())'); } Database.Query('INSERT INTO `wowpreklad`.`TextQuest` (' +'`ID` ,`Entry` ,`Title` ,`Details` ,`Objectives` ,`OfferRewardText` ,`RequestItemsText` ,`EndText` ,' +'`ObjectiveText1` ,`ObjectiveText2` ,`ObjectiveText3` ,`ObjectiveText4` ' +',`Language` , `User` , `Complete` ,`CompleteParts` ,`Take` ,`VersionStart` ,`VersionEnd` ,`ModifyTime`)' +'VALUES (NULL,'+IntToStr(Id)+',' +SqlPre(textname)+', ' +SqlPre(textdescription)+', ' +SqlPre(textobject)+', ' +SqlPre(textcomplection)+', ' +SqlPre(textprogress)+', ' +SqlPre(textend)+', ' +SqlPre(textobjective1)+', ' +SqlPre(textobjective2)+', ' +SqlPre(textobjective3)+', ' +SqlPre(textobjective4)+', ' +'0, NULL, 1,0,NULL,'+IntToStr(ImportedVersion)+','+IntToStr(ImportedVersion)+',NOW())'); //TODO: zkopírovat přeložený text pokud je celý anglický přeložený end; if (DBRows.Count = 0) then Exit; { WriteLn('guest='+IntToStr(Id)+' DB "' +DBRows.Data[0].Values['Title']+ '" '+chr(10)+ 'Objektiv:"'+DBRows.Data[0].Values['Objectives']+ '" '+chr(10) +'Description: "'+DBRows.Data[0].Values['Details']+'"'+chr(10) +'Complection: "'+DBRows.Data[0].Values['OfferRewardText']+'"'+chr(10) +'Progress: "'+DBRows.Data[0].Values['RequestItemsText']+'"'+chr(10) +'textend: "'+DBRows.Data[0].Values['EndText']+'"'+chr(10) +'textobjective1: "'+DBRows.Data[0].Values['ObjectiveText1']+'"'+chr(10) +'textobjective2: "'+DBRows.Data[0].Values['ObjectiveText2']+'"'+chr(10) +'textobjective3: "'+DBRows.Data[0].Values['ObjectiveText3']+'"'+chr(10) +'textobjective4: "'+DBRows.Data[0].Values['ObjectiveText4']+'"'+chr(10) ); WriteLn('guest='+IntToStr(Id)+' "'+textname+ '" '+chr(10)+ 'Objektiv:"'+textobject+ '" '+chr(10) +'Description: "'+textdescription+'"'+chr(10) +'Complection: "'+textcomplection+'"'+chr(10) +'Progress: "'+textprogress+'"'+chr(10) +'textend: "'+textend+'"'+chr(10) +'textobjective1: "'+textobjective1+'"'+chr(10) +'textobjective2: "'+textobjective2+'"'+chr(10) +'textobjective3: "'+textobjective3+'"'+chr(10) +'textobjective4: "'+textobjective4+'"'+chr(10) ); } // ReadLn; end; // else WriteLn('guest='+IntToStr(Id)+' Nenalezen'); end; procedure TWoWHeadLoader.UpdateQuest; var data : string; i:integer; begin GetTextQuest(3721); GetTextQuest(4970); GetTextQuest(5501); for i := 6032 to GetMaxIDQuest do begin GetTextQuest(i); write('.'); end; end; function TWoWHeadLoader.GetLastVersion: integer; var DBRows: TDbRows; begin //TODO: automaticky zjišťovat verze http://www.wowwiki.com/Patch_5.4.0 DBRows := Database.Query('SELECT * FROM ClientVersion WHERE `Title` != "" ORDER BY `ClientVersion`.`BuildNumber` DESC LIMIT 0 , 30'); Result := StrToInt(DBRows.Data[0].Values['BuildNumber']); Database.Query('UPDATE `wowpreklad`.`ClientVersion` SET `Imported` = 1 WHERE `clientversion`.`BuildNumber` ='+DBRows.Data[0].Values['BuildNumber']+';'); end; function TWoWHeadLoader.skipto(part: string; text: string): string; begin if (Pos(part,text) > 0) then Result := Copy(text,Pos(part,text)+Length(part),Length(text)) else Result := Copy(text,Pos(part,text),Length(text)) end; function TWoWHeadLoader.GetMaxIDQuest: integer; var data:string; maxid:integer=34000; begin // http://www.wowhead.com/quests?filter=cr=30;crs=1;crv=34000 repeat data := IdHTTP1.Get('http://www.wowhead.com/quests?filter=cr=30;crs=1;crv='+IntToStr(maxid)); result := maxid; maxid := maxid+200; until (Pos('Your criteria did not match',data) > 0); end; function TWoWHeadLoader.GetPartText(textname: string; text: string): string; begin text := skipto('>'+textname+'<',text); text := skipto('-'+textname+'"',text); text := skipto('>',text); text := StringReplace(text,'
','$B',[rfReplaceAll, rfIgnoreCase]); text := StringReplace(text,'
','$B',[rfReplaceAll, rfIgnoreCase]); Result := Copy(text,0,Pos('<',text)-1); // if ((Copy(Result,0,1) = chr(10)) or (Copy(Result,0,1) = chr(13)) or (Copy(Result,0,1) = ' ') )then // Result := Copy(Result,1,Length(Result)); // if ((Copy(Result,0,1) = chr(10)) or (Copy(Result,0,1) = chr(13)) or (Copy(Result,0,1) = ' ')) then // Result := Copy(Result,1,Length(Result)); end; function TWoWHeadLoader.TextStringReplace(text: string): string; var part,part2: string; begin text := StringReplace(text,'
','$B',[rfReplaceAll, rfIgnoreCase]); text := StringReplace(text,'
','$B',[rfReplaceAll, rfIgnoreCase]); text := StringReplace(text,' ','',[rfReplaceAll, rfIgnoreCase]); text := StringReplace(text,'<name>','$N',[rfReplaceAll, rfIgnoreCase]); text := StringReplace(text,'<class>','$C',[rfReplaceAll, rfIgnoreCase]); text := StringReplace(text,'<race>','$R',[rfReplaceAll, rfIgnoreCase]); // text := StringReplace(text,'<name>','$G',[rfReplaceAll, rfIgnoreCase]); //<good sir/my lady> repeat part := Copy(text,Pos('<',text),Pos('>',text)-Pos('<',text)); if (part <> '') then begin if (Pos('/',part) > 0) then begin part2 := '$G'+Copy(part,5,Pos('/',part)-5)+':'+Copy(part,Pos('/',part)+1,Length(part))+';'; text := StringReplace(text,part+'>',part2,[rfReplaceAll, rfIgnoreCase]); end; end; until not (Pos('/',part) > 0); text := StringReplace(text,'<','<',[rfReplaceAll, rfIgnoreCase]); text := StringReplace(text,'>','>',[rfReplaceAll, rfIgnoreCase]); text := StringReplace(text,'"','"',[rfReplaceAll, rfIgnoreCase]); Result := text; while ((Copy(Result,0,1) = chr(10)) or (Copy(Result,0,1) = chr(13)) or (Copy(Result,0,1) = ' ')) do begin Result := Copy(Result,2,Length(Result)); end; while ((Copy(Result,Length(Result),Length(Result)-1) = chr(10)) or (Copy(Result,Length(Result),Length(Result)-1) = chr(13)) or (Copy(Result,Length(Result),Length(Result)-1) = ' ')) do begin Result := Copy(Result,0,Length(Result)-1); end; end; function TWoWHeadLoader.CompareStrings(textdb: string; textimport: string): boolean; begin textdb:=DelHtmlTags(LowerCase(textdb)); textdb := StringReplace(textdb,' ','',[rfReplaceAll, rfIgnoreCase]); textdb := StringReplace(textdb,',','',[rfReplaceAll, rfIgnoreCase]); textdb := StringReplace(textdb,'.','',[rfReplaceAll, rfIgnoreCase]); textdb := StringReplace(textdb,chr(10),'',[rfReplaceAll, rfIgnoreCase]); textdb := StringReplace(textdb,chr(13),'',[rfReplaceAll, rfIgnoreCase]); textdb := StringReplace(textdb,'$b','',[rfReplaceAll, rfIgnoreCase]); textdb := StringReplace(textdb,'$B','',[rfReplaceAll, rfIgnoreCase]); // textdb := StringReplace(textdb,'$n','$N',[rfReplaceAll, rfIgnoreCase]); // textdb := StringReplace(textdb,'$g','$G',[rfReplaceAll, rfIgnoreCase]); // textdb := StringReplace(textdb,'$c','$C',[rfReplaceAll, rfIgnoreCase]); // textdb := StringReplace(textdb,'$r','$R',[rfReplaceAll, rfIgnoreCase]); textdb := StringReplace(textdb,'s'+chr(39)+'s','',[rfReplaceAll, rfIgnoreCase]); textdb := StringReplace(textdb,'s'+chr(39),'',[rfReplaceAll, rfIgnoreCase]); textdb := StringReplace(textdb,chr(39)+'s','',[rfReplaceAll, rfIgnoreCase]); textdb := StringReplace(textdb,chr(39),'',[rfReplaceAll, rfIgnoreCase]); textimport:=DelHtmlTags(LowerCase(textimport)); textimport := StringReplace(textimport,'s'+chr(39)+'s','',[rfReplaceAll, rfIgnoreCase]); textimport := StringReplace(textimport,'s'+chr(39),'',[rfReplaceAll, rfIgnoreCase]); textimport := StringReplace(textimport,chr(39)+'s','',[rfReplaceAll, rfIgnoreCase]); textimport := StringReplace(textimport,chr(39),'',[rfReplaceAll, rfIgnoreCase]); // textimport := StringReplace(textimport,'$n','$N',[rfReplaceAll, rfIgnoreCase]); // textimport := StringReplace(textimport,'$g','$G',[rfReplaceAll, rfIgnoreCase]); // textimport := StringReplace(textimport,'$c','$C',[rfReplaceAll, rfIgnoreCase]); // textimport := StringReplace(textimport,'$r','$R',[rfReplaceAll, rfIgnoreCase]); textimport := StringReplace(textimport,' ','',[rfReplaceAll, rfIgnoreCase]); textimport := StringReplace(textimport,',','',[rfReplaceAll, rfIgnoreCase]); textimport := StringReplace(textimport,'.','',[rfReplaceAll, rfIgnoreCase]); textimport := StringReplace(textimport,chr(10),'',[rfReplaceAll, rfIgnoreCase]); textimport := StringReplace(textimport,chr(13),'',[rfReplaceAll, rfIgnoreCase]); textimport := StringReplace(textimport,'$b','',[rfReplaceAll, rfIgnoreCase]); textimport := StringReplace(textimport,'$B','',[rfReplaceAll, rfIgnoreCase]); if ((textdb = textimport) or ( (textdb <> '') and (textimport = '') )) then Result := true else begin Result:=false; WriteLn(textdb); WriteLn(textimport); // ReadLn; end; end; function TWoWHeadLoader.DelHtmlTags(text: string): string; begin while ((Result = '') and (text <> '')) do begin if (Pos('<',text) <> 0) then begin text := StringReplace(text,Copy(text,Pos('<',text),Pos('>',text)-Pos('<',text)+1),'',[rfReplaceAll, rfIgnoreCase]); Result := Copy(text,0,Pos('<',text)-1); end else Result := text; end; // Result := text; end; function TWoWHeadLoader.SqlPre(text: string): string; begin text := StringReplace(text,'"','\"',[rfReplaceAll, rfIgnoreCase]); text := StringReplace(text,chr(39),'\'+chr(39),[rfReplaceAll, rfIgnoreCase]); Result := chr(39)+text+chr(39); end; procedure TWoWHeadLoader.UpdateTranslated; var i:integer; DBRows : TDbRows; begin { global $TranslationTree, $PatchVersion, $Config; $Output = '

Začínám se synchronizací VersionEnd u přeložených textů
'; foreach($TranslationTree as $Group) { $Output .= '
'.$Group['Name'].' '; $DbResult = $this->System->Database->query('SELECT `gs_tran`.`ID`, '. '`gs_tran`.`VersionEnd` AS `VersionEnd_tran`, '. '`gs_orig`.`VersionEnd` AS `VersionEnd_orig` FROM `'. $Group['TablePrefix'].'` AS `gs_tran` JOIN `'.$Group['TablePrefix']. '` AS `gs_orig` ON `gs_orig`.`ID` = `gs_tran`.`Take` WHERE '. '`gs_tran`.`VersionEnd` <> `gs_orig`.`VersionEnd`'); while($DbRow = $DbResult->fetch_assoc()) { $this->System->Database->query('UPDATE `'.$Group['TablePrefix'].'` SET `VersionEnd` = '.$DbRow['VersionEnd_orig'].' WHERE `ID` = '.$DbRow['ID']); $Output .= '. '; } $Output .= 'Dokončeno.'; } return($Output); } repeat DBRows := Database.Query('SELECT `gs_tran`.`ID` , `gs_tran`.`VersionEnd` AS `VersionEnd_tran` ,' +'`gs_orig`.`VersionEnd` AS `VersionEnd_orig`' +' FROM `textquest` AS `gs_tran` JOIN `textquest` AS `gs_orig` ON `gs_orig`.`ID` = `gs_tran`.`Take`' +'WHERE `gs_tran`.`VersionEnd` <> `gs_orig`.`VersionEnd` '); for i:=0 to DBRows.Count-1 do begin Database.Query('UPDATE `textquest` SET `VersionEnd` = '+DBRows.Data[i].Values['VersionEnd_orig']+' WHERE `ID` = '+DBRows.Data[i].Values['ID']); end; until (DBRows.Count = 0); end; var Application: TWoWHeadLoader; begin Application:=TWoWHeadLoader.Create(nil); Application.Run; Application.Free; end.