1// INCLUDES 2#include <aknapp.h> 3#include <aknappui.h> 4#include <eikdoc.h> 5#include <sdlepocapi.h> 6#include <bautils.h> 7#include <eikstart.h> 8#include <badesca.h> 9#include <bautils.h> 10#include <apgcli.h> 11#include <sdlmain.h> 12#include <eikedwin.h> 13#include <eiklabel.h> 14#include <sdlexe.rsg> 15#include <aknglobalmsgquery.h> 16#include <apgwgnam.h> 17 18 19 20// FORWARD DECLARATIONS 21class CApaDocument; 22 23 24//const TUid KSDLUID = { 0xF01F605E }; 25 26LOCAL_C void MakeCCmdLineL(const TDesC8& aParam, CDesC8Array& aArray) 27 { 28 29 const TChar dq('\"'); 30 31 TLex8 lex(aParam); 32 TBool in = EFalse; 33 34 lex.SkipSpaceAndMark(); 35 36 while(!lex.Eos()) 37 { 38 TPtrC8 ptr; 39 if(in) 40 { 41 const TPtrC8 rem = lex.RemainderFromMark(); 42 const TInt pos = rem.Locate(dq); 43 if(pos > 0) 44 { 45 lex.Inc(pos); 46 ptr.Set(lex.MarkedToken()); 47 lex.SkipAndMark(1); 48 } 49 else 50 { 51 ptr.Set(rem); 52 } 53 in = EFalse; 54 } 55 else 56 { 57 ptr.Set(lex.NextToken()); 58 const TInt pos = ptr.Locate(dq); 59 if(pos == 0) 60 { 61 lex.UnGetToMark(); 62 lex.SkipAndMark(1); 63 in = ETrue; 64 continue; // back to in brace 65 } 66 else 67 lex.SkipSpaceAndMark(); 68 } 69 70 aArray.AppendL(ptr); 71 72 } 73 } 74 75NONSHARABLE_CLASS(TVirtualCursor) : public MOverlay 76 { 77 public: 78 TVirtualCursor(); 79 void Set(const TRect& aRect, CFbsBitmap* aBmp, CFbsBitmap* aAlpha); 80 void Move(TInt aX, TInt aY); 81 void MakeEvent(TWsEvent& aEvent, const TPoint& aBasePos) const; 82 void Toggle(); 83 TBool IsOn() const; 84 private: 85 void Draw(CBitmapContext& aGc, const TRect& aTargetRect, const TSize& aSize); 86 private: 87 TRect iRect; 88 TPoint iInc; 89 TPoint iPos; 90 TBool iIsOn; 91 CFbsBitmap* iCBmp; 92 CFbsBitmap* iAlpha; 93 }; 94 95 96TVirtualCursor::TVirtualCursor() : iInc(0, 0), iIsOn(EFalse), iCBmp(NULL) 97 { 98 } 99 100const TInt KMaxMove = 10; 101 102void TVirtualCursor::Move(TInt aX, TInt aY) 103 { 104 if(aX > 0 && iInc.iX > 0) 105 ++iInc.iX; 106 else if(aX < 0 && iInc.iX < 0) 107 --iInc.iX; 108 else 109 iInc.iX = aX; 110 111 if(aY > 0 && iInc.iY > 0) 112 ++iInc.iY; 113 else if(aY < 0 && iInc.iY < 0) 114 --iInc.iY; 115 else 116 iInc.iY = aY; 117 118 iInc.iX = Min(KMaxMove, iInc.iX); 119 120 iInc.iX = Max(-KMaxMove, iInc.iX); 121 122 iInc.iY = Min(KMaxMove, iInc.iY); 123 124 iInc.iY =Max(-KMaxMove, iInc.iY); 125 126 const TPoint pos = iPos + iInc; 127 if(iRect.Contains(pos)) 128 { 129 iPos = pos; 130 } 131 else 132 { 133 iInc = TPoint(0, 0); 134 } 135 } 136 137 138void TVirtualCursor::Toggle() 139 { 140 iIsOn = !iIsOn; 141 } 142 143 144TBool TVirtualCursor::IsOn() const 145 { 146 return iIsOn; 147 } 148 149void TVirtualCursor::Set(const TRect& aRect, CFbsBitmap* aBmp, CFbsBitmap* aAlpha) 150 { 151 iRect = aRect; 152 iCBmp = aBmp; 153 iAlpha = aAlpha; 154 } 155 156 157void TVirtualCursor::MakeEvent(TWsEvent& aEvent, const TPoint& aBasePos) const 158 { 159 aEvent.SetType(EEventPointer), 160 aEvent.SetTimeNow(); 161 TPointerEvent& pointer = *aEvent.Pointer(); 162 pointer.iType = TPointerEvent::EButton1Down; 163 pointer.iPosition = iPos; 164 pointer.iParentPosition = aBasePos; 165 } 166 167 168void TVirtualCursor::Draw(CBitmapContext& aGc, const TRect& /*aTargetRect*/, const TSize& /*aSize*/) 169 { 170 if(iIsOn && iCBmp != NULL) 171 { 172 const TRect rect(TPoint(0, 0), iCBmp->SizeInPixels()); 173 aGc.AlphaBlendBitmaps(iPos, iCBmp, rect, iAlpha, TPoint(0, 0)); 174 } 175 176 } 177 178NONSHARABLE_CLASS(TSdlClass) 179 { 180 public: 181 TSdlClass(); 182 void SetMain(const TMainFunc& aFunc, TInt aFlags, MSDLMainObs* aObs, TInt aExeFlags); 183 TInt SdlFlags() const; 184 const TMainFunc& Main() const; 185 void SendEvent(TInt aEvent, TInt aParam, CSDL* aSDL); 186 TInt AppFlags() const; 187 void AppFlags(TInt aFlags); 188 private: 189 TMainFunc iFunc; 190 TInt iSdlFlags; 191 TInt iExeFlags; 192 MSDLMainObs* iObs; 193 }; 194 195 196void TSdlClass::AppFlags(TInt aFlags) 197 { 198 iExeFlags |= aFlags; 199 } 200 201void TSdlClass::SendEvent(TInt aEvent, TInt aParam, CSDL* aSDL) 202 { 203 if(iObs != NULL) 204 iObs->SDLMainEvent(aEvent, aParam, aSDL); 205 } 206 207TInt TSdlClass::AppFlags() const 208 { 209 return iExeFlags; 210 } 211 212void TSdlClass::SetMain(const TMainFunc& aFunc, TInt aFlags, MSDLMainObs* aObs, TInt aExeFlags) 213 { 214 iFunc = aFunc; 215 iSdlFlags = aFlags; 216 iExeFlags = aExeFlags; 217 iObs = aObs; 218 } 219 220const TMainFunc& TSdlClass::Main() const 221 { 222 return iFunc; 223 } 224 225 226 TInt TSdlClass::SdlFlags() const 227 { 228 return iSdlFlags; 229 } 230 231 232 233TSdlClass::TSdlClass() 234 { 235 Mem::FillZ(this, sizeof(this)); 236 } 237 238TSdlClass gSDLClass; 239 240 241//////////////////////////////////////////////////////////////// 242 243NONSHARABLE_CLASS(CSDLApplication) : public CAknApplication 244 { 245 public: 246 CSDLApplication(); 247 private: 248 CApaDocument* CreateDocumentL(); 249 TFileName ResourceFileName() const; 250 TUid AppDllUid() const; 251 void FindMeL(); 252 TUid iUid; 253 }; 254 255NONSHARABLE_CLASS(CSDLDocument) : public CEikDocument 256 { 257 public: 258 CSDLDocument(CEikApplication& aApp); 259 private: 260 CEikAppUi* CreateAppUiL(); 261 262 }; 263 264 //////////////////////////////////////////////////////////////////// 265 266 267NONSHARABLE_CLASS(MExitWait) 268 { 269 public: 270 virtual void DoExit(TInt aErr) = 0; 271 }; 272 273///////////////////////////////////////////////////////////////////////// 274 275NONSHARABLE_CLASS(CExitWait) : public CActive 276 { 277 public: 278 CExitWait(MExitWait& aWait); 279 ~CExitWait(); 280 private: 281 void RunL(); 282 void DoCancel(); 283 private: 284 MExitWait& iWait; 285 TRequestStatus* iStatusPtr; 286 }; 287 288//////////////////////////////////////////////////////////////////////// 289 290 291NONSHARABLE_CLASS(CSDLWin) : public CCoeControl 292 { 293 public: 294 void ConstructL(const TRect& aRect); 295 RWindow& GetWindow() const; 296 void SetNoDraw(); 297 private: 298 void Draw(const TRect& aRect) const; 299 private: 300 TBool iNoDraw; 301 }; 302 303 304//////////////////////////////////////////////////////////////////////////// 305 306NONSHARABLE_CLASS(CSDLAppUi) : public CAknAppUi, public MExitWait, MSDLObserver 307 { 308 public: 309 ~CSDLAppUi(); 310 private: // New functions 311 void ConstructL(); 312 void HandleCommandL(TInt aCommand); 313 void HandleWsEventL(const TWsEvent& aEvent, CCoeControl* aDestination); 314 void HandleResourceChangeL(TInt aType); 315 316 void DoExit(TInt aErr); 317 318 TInt SdlEvent(TInt aEvent, TInt aParam); 319 TInt SdlThreadEvent(TInt aEvent, TInt aParam); 320 321 void StartL(); 322 static TBool StartL(TAny* aThis); 323 324 TBool ParamEditorL(TDes& aCheat); 325 326 TBool ProcessCommandParametersL(CApaCommandLine &aCommandLine); 327 328 void PrepareToExit(); 329 void HandleConsoleWindowL(); 330 void HandleConsoleWindow(); 331 void HandleForegroundEventL(TBool aForeground); 332 333 static TBool IdleRequestL(TAny* aThis); 334 335 TBool HandleKeyL(const TWsEvent& aEvent); 336 337 338 private: 339 CExitWait* iWait; 340 CSDLWin* iSDLWin; 341 CSDL* iSdl; 342 CIdle* iStarter; 343 TBool iExitRequest; 344 CDesC8Array* iParams; 345 TInt iResOffset; 346 CIdle* iIdle; 347 TInt iStdOut; 348 TVirtualCursor iCursor; 349 CFbsBitmap* iCBmp; 350 CFbsBitmap* iAlpha; 351 // TTime iLastPress; 352 // CSDL::TOrientationMode iOrientation; 353 }; 354 355////////////////////////////////////////////////////////////////////////////////////////7 356 357CApaDocument* CSDLApplication::CreateDocumentL() 358 { 359 return new (ELeave) CSDLDocument(*this); 360 } 361 362TUid CSDLApplication::AppDllUid() const 363 { 364 return iUid; 365 } 366 367 368CSDLApplication::CSDLApplication() 369 { 370 TRAPD(err, FindMeL()); 371 ASSERT(err == KErrNone); 372 } 373 374void CSDLApplication::FindMeL() 375 { 376 RApaLsSession apa; 377 User::LeaveIfError(apa.Connect()); 378 CleanupClosePushL(apa); 379 User::LeaveIfError(apa.GetAllApps()); 380 TFileName name = RProcess().FileName(); 381 TApaAppInfo info; 382 while(apa.GetNextApp(info) == KErrNone) 383 { 384 if(info.iFullName.CompareF(name) == 0) 385 { 386 iUid = info.iUid; 387 break; 388 } 389 } 390 CleanupStack::PopAndDestroy(); 391 } 392 393TFileName CSDLApplication::ResourceFileName() const 394 { 395 return KNullDesC(); 396 } 397 398/////////////////////////////////////////////////////////////////////////////////////////// 399 400CExitWait::CExitWait(MExitWait& aWait) : CActive(CActive::EPriorityStandard), iWait(aWait) 401 { 402 CActiveScheduler::Add(this); 403 SetActive(); 404 iStatusPtr = &iStatus; 405 } 406 407CExitWait::~CExitWait() 408 { 409 Cancel(); 410 } 411 412void CExitWait::RunL() 413 { 414 if(iStatusPtr != NULL ) 415 iWait.DoExit(iStatus.Int()); 416 } 417 418void CExitWait::DoCancel() 419 { 420 if(iStatusPtr != NULL ) 421 User::RequestComplete(iStatusPtr , KErrCancel); 422 } 423 424 425////////////////////////////////////////////////////////////////////////////////////////////// 426 427CSDLDocument::CSDLDocument(CEikApplication& aApp) : CEikDocument(aApp) 428 {} 429 430CEikAppUi* CSDLDocument::CreateAppUiL() 431 { 432 return new (ELeave) CSDLAppUi; 433 } 434 435/////////////////////////////////////////////////////////////////////////// 436 437void CSDLWin:: ConstructL(const TRect& aRect) 438 { 439 CreateWindowL(); 440 SetRect(aRect); 441 ActivateL(); 442 } 443 444 445RWindow& CSDLWin::GetWindow() const 446 { 447 return Window(); 448 } 449 450 451void CSDLWin::Draw(const TRect& /*aRect*/) const 452 { 453 if(!iNoDraw) 454 { 455 CWindowGc& gc = SystemGc(); 456 gc.SetPenStyle(CGraphicsContext::ESolidPen); 457 gc.SetPenColor(KRgbGray); 458 gc.SetBrushStyle(CGraphicsContext::ESolidBrush); 459 gc.SetBrushColor(0xaaaaaa); 460 gc.DrawRect(Rect()); 461 } 462 } 463 464void CSDLWin::SetNoDraw() 465 { 466 iNoDraw = ETrue; 467 } 468 469///////////////////////////////////////////////////////////////////////// 470 471CSDLAppUi::~CSDLAppUi() 472 { 473 if(iIdle) 474 iIdle->Cancel(); 475 delete iIdle; 476 if(iStarter != NULL) 477 iStarter->Cancel(); 478 delete iStarter; 479 delete iWait; 480 delete iSdl; 481 delete iSDLWin; 482 delete iParams; 483 delete iCBmp; 484 delete iAlpha; 485 } 486 487 488void CSDLAppUi::ConstructL() 489 { 490 BaseConstructL(ENoAppResourceFile | ENoScreenFurniture); 491 492 493 RLibrary lib; 494 User::LeaveIfError(lib.Load(_L("sdlexe.dll"))); 495 TFileName name = lib.FileName(); 496 lib.Close(); 497 name.Replace(3, name.Length() - 3, _L("resource\\apps\\sdlexe.rsc")); 498 BaflUtils::NearestLanguageFile(iEikonEnv->FsSession(), name); 499 iResOffset = iCoeEnv->AddResourceFileL(name); 500 501 name.Replace(name.Length() - 3, 3, _L("mbm")); 502 503 TEntry e; 504 const TInt err = iEikonEnv->FsSession().Entry(name, e); 505 506 iCBmp = iEikonEnv->CreateBitmapL(name, 0); 507 iAlpha = iEikonEnv->CreateBitmapL(name, 1); 508 509 iIdle = CIdle::NewL(CActive::EPriorityIdle); 510 511 iSDLWin = new (ELeave) CSDLWin; 512 iSDLWin->ConstructL(ApplicationRect()); 513 514 iSdl = CSDL::NewL(gSDLClass.SdlFlags()); 515 516 gSDLClass.SendEvent(MSDLMainObs::ESDLCreated, 0, iSdl); 517 518 iSdl->SetObserver(this); 519 iSdl->DisableKeyBlocking(*this); 520 iSdl->SetContainerWindowL( 521 iSDLWin->GetWindow(), 522 iEikonEnv->WsSession(), 523 *iEikonEnv->ScreenDevice()); 524 iSdl->AppendOverlay(iCursor, 0); 525 526 iCursor.Set(TRect(TPoint(0, 0), iSDLWin->Size()), iCBmp, iAlpha); 527 528 iStarter = CIdle::NewL(CActive::EPriorityLow); 529 iStarter->Start(TCallBack(StartL, this)); 530 531 532 } 533 534 535 536TBool CSDLAppUi::StartL(TAny* aThis) 537 { 538 static_cast<CSDLAppUi*>(aThis)->StartL(); 539 return EFalse; 540 } 541 542 543void CSDLAppUi::PrepareToExit() 544 { 545 CAknAppUiBase::PrepareToExit(); //aknappu::PrepareToExit crashes 546 iCoeEnv->DeleteResourceFile(iResOffset); 547 } 548 549TBool CSDLAppUi::ProcessCommandParametersL(CApaCommandLine &aCommandLine) 550 { 551 const TPtrC8 cmdLine = aCommandLine.TailEnd(); 552 iParams = new (ELeave) CDesC8ArrayFlat(8); 553 MakeCCmdLineL(cmdLine, *iParams); 554 return EFalse; 555 } 556 557 558 TBool CSDLAppUi::ParamEditorL(TDes& aCheat) 559 { 560 CAknTextQueryDialog* query = CAknTextQueryDialog::NewL(aCheat); 561 CleanupStack::PushL(query); 562 query->SetPromptL(_L("Enter parameters")); 563 CleanupStack::Pop(); 564 return query->ExecuteLD(R_PARAMEDITOR); 565 } 566 567 void CSDLAppUi::StartL() 568 { 569 if(gSDLClass.AppFlags() & SDLEnv::EParamQuery) 570 { 571 TBuf8<256> cmd; 572 RFile file; 573 TInt err = file.Open(iEikonEnv->FsSession(), _L("sdl_param.txt"),EFileRead); 574 if(err == KErrNone) 575 { 576 file.Read(cmd); 577 file.Close(); 578 MakeCCmdLineL(cmd, *iParams); 579 } 580 if(err != KErrNone || gSDLClass.AppFlags() & (SDLEnv::EParamQueryDialog ^ SDLEnv::EParamQuery)) 581 { 582 TBuf<256> buffer; 583 if(ParamEditorL(buffer)) 584 { 585 cmd.Copy(buffer); 586 MakeCCmdLineL(cmd, *iParams); 587 } 588 } 589 } 590 iWait = new (ELeave) CExitWait(*this); 591 iSdl->CallMainL(gSDLClass.Main(), &iWait->iStatus, iParams, CSDL::ENoParamFlags, 0xA000); 592 } 593 594void CSDLAppUi::HandleCommandL(TInt aCommand) 595 { 596 switch(aCommand) 597 { 598 case EAknSoftkeyBack: 599 case EAknSoftkeyExit: 600 case EAknCmdExit: 601 case EEikCmdExit: 602 gSDLClass.AppFlags(SDLEnv::EAllowConsoleView); 603 if(iWait == NULL || !iWait->IsActive() || iSdl == NULL) 604 { 605 Exit(); 606 } 607 else if(!iExitRequest) 608 { 609 iExitRequest = ETrue; //trick how SDL can be closed! 610 iSdl->Suspend(); 611 } 612 break; 613 } 614 } 615 616 617 618TBool CSDLAppUi::HandleKeyL(const TWsEvent& aEvent) 619 { 620 const TInt type = aEvent.Type(); 621 if(!(type == EEventKey || type == EEventKeyUp || type == EEventKeyDown)) 622 { 623 return ETrue; 624 } 625 const TKeyEvent& key = *aEvent.Key(); 626 if((key.iScanCode == EStdKeyYes) && (gSDLClass.AppFlags() & SDLEnv::EVirtualMouse)) 627 { 628 if(type == EEventKeyUp) 629 { 630 iCursor.Toggle(); 631 iSdl->RedrawRequest(); 632 } 633 return EFalse; 634 } 635 if(iCursor.IsOn()) 636 { 637 switch(key.iScanCode) 638 { 639 case EStdKeyUpArrow: 640 iCursor.Move(0, -1); 641 break; 642 case EStdKeyDownArrow: 643 iCursor.Move(0, 1); 644 break; 645 case EStdKeyLeftArrow: 646 iCursor.Move(-1, 0); 647 break; 648 case EStdKeyRightArrow: 649 iCursor.Move(1, 0); 650 break; 651 case EStdKeyDevice3: 652 if(type == EEventKeyUp) 653 { 654 TWsEvent event; 655 iCursor.MakeEvent(event, iSDLWin->Position()); 656 iSdl->AppendWsEvent(event); 657 } 658 return EFalse; 659 default: 660 return ETrue; 661 } 662 iSdl->RedrawRequest(); 663 return EFalse; 664 } 665 return ETrue; 666 } 667 668 void CSDLAppUi::HandleWsEventL(const TWsEvent& aEvent, CCoeControl* aDestination) 669 { 670 if(iSdl && iWait && HandleKeyL(aEvent)) 671 iSdl->AppendWsEvent(aEvent); 672 CAknAppUi::HandleWsEventL(aEvent, aDestination); 673 } 674 675 void CSDLAppUi::HandleResourceChangeL(TInt aType) 676 { 677 CAknAppUi::HandleResourceChangeL(aType); 678 if(aType == KEikDynamicLayoutVariantSwitch) 679 { 680 iSDLWin->SetRect(ApplicationRect()); 681 iSdl->SetContainerWindowL( 682 iSDLWin->GetWindow(), 683 iEikonEnv->WsSession(), 684 *iEikonEnv->ScreenDevice()); 685 } 686 } 687 688 689void CSDLAppUi::DoExit(TInt/*Err*/) 690 { 691 iExitRequest = ETrue; 692 Exit(); 693 } 694 695 696 TInt CSDLAppUi::SdlThreadEvent(TInt aEvent, TInt /*aParam*/) 697 { 698 switch(aEvent) 699 { 700 case MSDLObserver::EEventResume: 701 break; 702 case MSDLObserver::EEventSuspend: 703 if(iExitRequest) 704 return MSDLObserver::ESuspendNoSuspend; 705 break; 706 case MSDLObserver::EEventWindowReserved: 707 break; 708 case MSDLObserver::EEventWindowNotAvailable: 709 break; 710 case MSDLObserver::EEventScreenSizeChanged: 711 break; 712 } 713 return MSDLObserver::EParameterNone; 714 } 715 716TInt CSDLAppUi::SdlEvent(TInt aEvent, TInt /*aParam*/) 717 { 718 switch(aEvent) 719 { 720 case MSDLObserver::EEventResume: 721 break; 722 case MSDLObserver::EEventSuspend: 723 if(iExitRequest) 724 return MSDLObserver::ESuspendNoSuspend; 725 break; 726 case MSDLObserver::EEventWindowReserved: 727 break; 728 case MSDLObserver::EEventWindowNotAvailable: 729 { 730 TRAP_IGNORE(HandleConsoleWindowL()); 731 } 732 break; 733 case MSDLObserver::EEventScreenSizeChanged: 734 break; 735 case MSDLObserver::EEventKeyMapInit: 736 break; 737 case MSDLObserver::EEventMainExit: 738 if(iStdOut != 0) 739 { 740 gSDLClass.AppFlags(SDLEnv::EAllowConsoleView); 741 iEikonEnv->WsSession().SetWindowGroupOrdinalPosition(iStdOut, 0); 742 } 743 break; 744 } 745 return MSDLObserver::EParameterNone; 746 } 747 748void CSDLAppUi::HandleForegroundEventL(TBool aForeground) 749 { 750 CAknAppUi::HandleForegroundEventL(aForeground); 751 if(!aForeground) 752 HandleConsoleWindow(); 753 } 754 755void CSDLAppUi::HandleConsoleWindow() 756 { 757 if(!iIdle->IsActive()) 758 iIdle->Start(TCallBack(IdleRequestL, this)); 759 } 760 761TBool CSDLAppUi::IdleRequestL(TAny* aThis) 762 { 763 static_cast<CSDLAppUi*>(aThis)->HandleConsoleWindowL(); 764 return EFalse; 765 } 766 767void CSDLAppUi::HandleConsoleWindowL() 768 { 769 if(gSDLClass.AppFlags() & SDLEnv::EAllowConsoleView) 770 { 771 return; 772 } 773 RWsSession& ses = iEikonEnv->WsSession(); 774 const TInt focus = ses.GetFocusWindowGroup(); 775 CApaWindowGroupName* name = CApaWindowGroupName::NewLC(ses, focus); 776 const TPtrC caption = name->Caption(); 777 if(0 == caption.CompareF(_L("STDOUT"))) 778 { 779 iStdOut = focus; 780 ses.SetWindowGroupOrdinalPosition(iEikonEnv->RootWin().Identifier(), 0); 781 } 782 CleanupStack::PopAndDestroy(); //name 783 } 784 785 786//////////////////////////////////////////////////////////////////////// 787 788 789CApaApplication* NewApplication() 790 { 791 return new CSDLApplication(); 792 } 793 794 795EXPORT_C TInt SDLEnv::SetMain(const TMainFunc& aFunc, TInt aSdlFlags, MSDLMainObs* aObs, TInt aSdlExeFlags) 796 { 797 gSDLClass.SetMain(aFunc, aSdlFlags, aObs, aSdlExeFlags); 798 return EikStart::RunApplication(NewApplication); 799 } 800 801////////////////////////////////////////////////////////////////////// 802 803TInt SDLUiPrint(const TDesC8& /*aInfo*/) 804 { 805 return KErrNotFound; 806 } 807 808 809 810