home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 5 / Apprentice-Release5.iso / Source Code / C / Games / Glypha 3v2 / GlyphaIII Code ƒ / Prefs.c < prev    next >
Encoding:
Text File  |  1995-06-30  |  18.3 KB  |  524 lines  |  [TEXT/CWIE]

  1.  
  2. //============================================================================
  3. //----------------------------------------------------------------------------
  4. //                                    Prefs.c
  5. //----------------------------------------------------------------------------
  6. //============================================================================
  7.  
  8. // This is a slick little file that I re-use and re-use.  I wrote it to…
  9. // seemlessly handle System 6 or System 7 with but a single call.  You need…
  10. // to define your own "prefs" struct, but these routines will read and write…
  11. // it to the System folder.
  12.  
  13. #include "Externs.h"
  14. #include <Folders.h>                            // Needed for creating a folder.
  15. #include <GestaltEqu.h>                            // Needed for the Gestalt() call.
  16. #include <Script.h>                                // I can't remember why I needed this.
  17.  
  18.  
  19. #define    kPrefCreatorType    'zade'                // Change this to reflect your apps creator.
  20. #define    kPrefFileType        'zadP'                // Change this to reflect your prefs type.
  21. #define    kPrefFileName        "\pGlypha Prefs"    // Change this to reflect the name for your prefs.
  22. #define    kDefaultPrefFName    "\pPreferences"        // Name of prefs folder (System 6 only).
  23. #define kPrefsStringsID        160                    // For easy localization.
  24. #define    kPrefsFNameIndex    1                    // This one works with the previous constant.
  25.  
  26.  
  27. Boolean CanUseFindFolder (void);
  28. Boolean GetPrefsFPath (long *, short *);
  29. Boolean CreatePrefsFolder (short *);
  30. Boolean GetPrefsFPath6 (short *);
  31. Boolean WritePrefs (long *, short *, prefsInfo *);
  32. Boolean WritePrefs6 (short *, prefsInfo *);
  33. OSErr ReadPrefs (long *, short *, prefsInfo *);
  34. OSErr ReadPrefs6 (short *, prefsInfo *);
  35. Boolean DeletePrefs (long *, short *);
  36. Boolean DeletePrefs6 (short *);
  37.  
  38.  
  39. //==============================================================  Functions
  40. //--------------------------------------------------------------  CanUseFindFolder
  41.  
  42. // Returns TRUE if we can use the FindFolder() call (a System 7 nicety).
  43.  
  44. Boolean CanUseFindFolder (void)
  45. {
  46.     OSErr        theErr;
  47.     long        theFeature;
  48.     
  49.     if (!DoWeHaveGestalt())        // Darn, have to check for Gestalt() first.
  50.         return(FALSE);            // If no Gestalt(), probably don't have FindFolder().
  51.     
  52.     theErr = Gestalt(gestaltFindFolderAttr, &theFeature);
  53.     if (theErr != noErr)        // Use selector for FindFolder() attribute.
  54.         return(FALSE);
  55.                                 // Now do a bit test specifically for FindFolder().
  56.     if (!BitTst(&theFeature, 31 - gestaltFindFolderPresent))
  57.         return(FALSE);
  58.     else
  59.         return(TRUE);
  60. }
  61.  
  62. //--------------------------------------------------------------  GetPrefsFPath
  63.  
  64. // This function gets the file path to the Preferences folder (for System 7).
  65. // It is called only if we can use FindFolder() (see previous function).
  66.  
  67. Boolean GetPrefsFPath (long *prefDirID, short *systemVolRef)
  68. {
  69.     OSErr        theErr;
  70.                                     // Here's the wiley FindFolder() call.
  71.     theErr = FindFolder(kOnSystemDisk, kPreferencesFolderType, kCreateFolder, 
  72.         systemVolRef, prefDirID);    // It returns to us the directory and volume ref.…
  73.     if (theErr != noErr)            // Assuming it worked at all!
  74.         return(FALSE);
  75.     
  76.     return(TRUE);
  77. }
  78.  
  79. //--------------------------------------------------------------  CreatePrefsFolder
  80.  
  81. // This function won't be necessary for System 7, for System 6 though, it creates…
  82. // a folder ("Preferences") in the System folder and returns whether or not it worked.
  83.  
  84. Boolean CreatePrefsFolder (short *systemVolRef)
  85. {
  86.     HFileParam    fileParamBlock;
  87.     Str255        folderName;
  88.     OSErr        theErr;
  89.                                         // Here's our localization.  Rather than…
  90.                                         // hard-code the name "Preferences" in the code…
  91.                                         // we pull up the text from a string resource.
  92.     GetIndString(folderName, kPrefsStringsID, kPrefsFNameIndex);
  93.                                         // Set up a file parameter block.
  94.     fileParamBlock.ioVRefNum = *systemVolRef;
  95.     fileParamBlock.ioDirID = 0;
  96.     fileParamBlock.ioNamePtr = folderName;
  97.     fileParamBlock.ioCompletion = 0L;
  98.                                         // And create a directory (folder).
  99.     theErr = PBDirCreate((HParmBlkPtr)&fileParamBlock, FALSE);
  100.     if (theErr != noErr)                // See that it worked.
  101.     {
  102.         RedAlert("\pPrefs Creation Error");
  103.         return(FALSE);
  104.     }
  105.     return(TRUE);
  106. }
  107.  
  108. //--------------------------------------------------------------  GetPrefsFPath6
  109.  
  110. // If ever there was a case to drop support for System 6 (and require System 7),…
  111. // this is it.  Look at how insidious handling System 6 files can be.  The following…
  112. // function is the "System 6 pedigree" of the above GetPrefsFPath() function.  Note…
  113. // that the GetPrefsFPath() function was ONE CALL!  TWO LINES OF CODE!  The below…
  114. // function is like a page or so.  Anyway, this function is called if Glypha is…
  115. // running under System 6 and essentially returns a volume reference pointing to…
  116. // the preferences folder.
  117.  
  118. Boolean GetPrefsFPath6 (short *systemVolRef)
  119. {
  120.     Str255        folderName, whoCares;
  121.     SysEnvRec    thisWorld;
  122.     CInfoPBRec    catalogInfoPB;
  123.     DirInfo        *directoryInfo = (DirInfo *) &catalogInfoPB;
  124.     HFileInfo    *fileInfo = (HFileInfo *) &catalogInfoPB;
  125.     WDPBRec        workingDirPB;
  126.     long        prefDirID;
  127.     OSErr        theErr;
  128.                                                 // Yokelization.
  129.     GetIndString(folderName, kPrefsStringsID, kPrefsFNameIndex);
  130.                                                 // SysEnvirons() for System folder volRef.
  131.     theErr = SysEnvirons(2, &thisWorld);
  132.     if (theErr != noErr)
  133.         return(FALSE);
  134.                                                 // Okay, here's the volume reference.
  135.     *systemVolRef = thisWorld.sysVRefNum;
  136.     fileInfo->ioVRefNum = *systemVolRef;        // Set up another parameter block.
  137.     fileInfo->ioDirID  = 0;                        // Ignored.
  138.     fileInfo->ioFDirIndex = 0;                    // Irrelevant.
  139.     fileInfo->ioNamePtr = folderName;            // Directory we're looking for.
  140.     fileInfo->ioCompletion = 0L;
  141.     theErr = PBGetCatInfo(&catalogInfoPB, FALSE);
  142.     if (theErr != noErr)                        // Did we fail to find Prefs folder?
  143.     {
  144.         if (theErr != fnfErr)                    // If it WASN'T a file not found error…
  145.         {                                        // then something more sinister is afoot.
  146.             RedAlert("\pPrefs Filepath Error");
  147.         }                                        // Otherwise, need to create prefs folder.
  148.         if (!CreatePrefsFolder(systemVolRef))
  149.             return(FALSE);
  150.                                                 // Again - can we find the prefs folder?
  151.         directoryInfo->ioVRefNum = *systemVolRef;
  152.         directoryInfo->ioFDirIndex = 0;
  153.         directoryInfo->ioNamePtr = folderName;
  154.         theErr = PBGetCatInfo(&catalogInfoPB, FALSE);
  155.         if (theErr != noErr)
  156.         {
  157.             RedAlert("\pPrefs GetCatInfo() Error");
  158.             return(FALSE);
  159.         }
  160.     }
  161.     prefDirID = directoryInfo->ioDrDirID;        // Alright, the dir. ID for prefs folder.
  162.     
  163.     workingDirPB.ioNamePtr = whoCares;            // Now convert working dir. into a "real"…
  164.     workingDirPB.ioVRefNum = *systemVolRef;        // dir. ID so we can get volume number.
  165.     workingDirPB.ioWDIndex = 0;
  166.     workingDirPB.ioWDProcID = 0;
  167.     workingDirPB.ioWDVRefNum = 0;
  168.     workingDirPB.ioCompletion = 0L;
  169.     theErr = PBGetWDInfo(&workingDirPB, FALSE);
  170.     if (theErr != noErr)
  171.     {
  172.         RedAlert("\pPrefs PBGetWDInfo() Error");
  173.     }
  174.                                                 // The volume where directory is located.
  175.     *systemVolRef = workingDirPB.ioWDVRefNum;
  176.     
  177.     workingDirPB.ioNamePtr = whoCares;
  178.     workingDirPB.ioWDDirID = prefDirID;            // Okay, finally, with a directory ID, …
  179.     workingDirPB.ioVRefNum = *systemVolRef;        // and a "hard" volume number…
  180.     workingDirPB.ioWDProcID = 0;                // …
  181.     workingDirPB.ioCompletion = 0L;                // …
  182.     theErr = PBOpenWD(&workingDirPB, FALSE);    // we can create a working directory…
  183.     if (theErr != noErr)                        // control block with which to access…
  184.     {                                            // files in the prefs folder.
  185.         RedAlert("\pPrefs PBOpenWD() Error");
  186.     }
  187.     
  188.     *systemVolRef = workingDirPB.ioVRefNum;
  189.     
  190.     return(TRUE);
  191. }
  192.  
  193. //--------------------------------------------------------------  WritePrefs
  194.  
  195. // This is the System 7 version that handles all the above functions when you…
  196. // want to write out the preferences file.  It is called by SavePrefs() below…
  197. // if we're running under System 7.  It creates an FSSpec record to hold…
  198. // information about where the preferences file is located, creates Glypha's…
  199. // preferences if they are not found, opens the prefences file, writes out…
  200. // the preferences, and the closes the prefs.  Bam, bam, bam.
  201.  
  202. Boolean WritePrefs (long *prefDirID, short *systemVolRef, prefsInfo *thePrefs)
  203. {
  204.     OSErr        theErr;
  205.     short        fileRefNum;
  206.     long        byteCount;
  207.     FSSpec        theSpecs;
  208.     Str255        fileName = kPrefFileName;
  209.                                     // Create FSSpec record from volume ref and dir ID.
  210.     theErr = FSMakeFSSpec(*systemVolRef, *prefDirID, fileName, &theSpecs);
  211.     if (theErr != noErr)            // See if it failed.
  212.     {                                // An fnfErr means file not found error (no prefs).
  213.         if (theErr != fnfErr)        // If that weren't the problem, we're cooked.
  214.             RedAlert("\pPrefs FSMakeFSSpec() Error");
  215.                                     // If it was an fnfErr, create the prefs.
  216.         theErr = FSpCreate(&theSpecs, kPrefCreatorType, kPrefFileType, smSystemScript);
  217.         if (theErr != noErr)        // If we fail to create the prefs, bail.
  218.             RedAlert("\pPrefs FSpCreate() Error");
  219.     }                                // Okay, we either found or made a pref file, open it.
  220.     theErr = FSpOpenDF(&theSpecs, fsRdWrPerm, &fileRefNum);
  221.     if (theErr != noErr)            // As per usual, if we fail, bail.
  222.         RedAlert("\pPrefs FSpOpenDF() Error");
  223.     
  224.     byteCount = sizeof(*thePrefs);    // Get number of bytes to write (your prefs struct).
  225.                                     // And, write out the preferences.
  226.     theErr = FSWrite(fileRefNum, &byteCount, thePrefs);
  227.     if (theErr != noErr)            // Say no more.
  228.         RedAlert("\pPrefs FSWrite() Error");
  229.     
  230.     theErr = FSClose(fileRefNum);    // Close the prefs file.
  231.     if (theErr != noErr)            // Tic, tic.
  232.         RedAlert("\pPrefs FSClose() Error");
  233.     
  234.     return(TRUE);
  235. }
  236.  
  237. //--------------------------------------------------------------  WritePrefs6
  238.  
  239. // This is the System 6 equivalent of the above function.  It handles prefs…
  240. // opening, writing and closing for System 6.
  241.  
  242. Boolean WritePrefs6 (short *systemVolRef, prefsInfo *thePrefs)
  243. {
  244.     OSErr        theErr;
  245.     short        fileRefNum;
  246.     long        byteCount;
  247.     Str255        fileName = kPrefFileName;
  248.                                     // Attempt to open prefs file.
  249.     theErr = FSOpen(fileName, *systemVolRef, &fileRefNum);
  250.     if (theErr != noErr)            // If it failed, maybe the prefs don't exist.
  251.     {                                // An fnfErr means file not found.
  252.         if (theErr != fnfErr)        // See if in fact that WASN'T the reason.
  253.             RedAlert("\pPrefs FSOpen() Error");
  254.                                     // If fnfErr WAS the problem, create the prefs.
  255.         theErr = Create(fileName, *systemVolRef, kPrefCreatorType, kPrefFileType);
  256.         if (theErr != noErr)
  257.             RedAlert("\pPrefs Create() Error");
  258.                                     // Open the prefs file.
  259.         theErr = FSOpen(fileName, *systemVolRef, &fileRefNum);
  260.         if (theErr != noErr)
  261.             RedAlert("\pPrefs FSOpen() Error");
  262.     }
  263.     
  264.     byteCount = sizeof(*thePrefs);    // Get number of bytes to write out.
  265.                                     // Write the prefs out.
  266.     theErr = FSWrite(fileRefNum, &byteCount, thePrefs);
  267.     if (theErr != noErr)
  268.         RedAlert("\pPrefs FSWrite() Error");
  269.                                     // And close the prefs file.
  270.     theErr = FSClose(fileRefNum);
  271.     if (theErr != noErr)
  272.         RedAlert("\pPrefs FSClose() Error");
  273.     
  274.     return(TRUE);
  275. }
  276.  
  277. //--------------------------------------------------------------  SavePrefs
  278.  
  279. // This is the single function called externally to save the preferences.
  280. // You pass it a pointer to your preferences struct and a version number.
  281. // One of the fields in your preferences struct should be a version number…
  282. // (short prefVersion).  This function determines if we're on System 6 or 7…
  283. // and then calls the appropriate routines.  It returns TRUE if all went well…
  284. // or FALSE if any step failed.
  285.  
  286. Boolean SavePrefs (prefsInfo *thePrefs, short versionNow)
  287. {
  288.     long        prefDirID;
  289.     short        systemVolRef;
  290.     Boolean        canUseFSSpecs;
  291.     
  292.     thePrefs->prefVersion = versionNow;            // Set prefVersion to versionNow.
  293.     
  294.     canUseFSSpecs = CanUseFindFolder();            // See if we can use FindFolder().
  295.     if (canUseFSSpecs)                            // If so (System 7) take this route.
  296.     {                                            // Get a path to Preferences folder.
  297.         if (!GetPrefsFPath(&prefDirID, &systemVolRef))
  298.             return(FALSE);
  299.     }
  300.     else                                        // Here's the System 6 version.
  301.     {
  302.         if (!GetPrefsFPath6(&systemVolRef))
  303.             return(FALSE);
  304.     }
  305.     
  306.     if (canUseFSSpecs)                            // Write out the preferences.
  307.     {
  308.         if (!WritePrefs(&prefDirID, &systemVolRef, thePrefs))
  309.             return(FALSE);
  310.     }
  311.     else
  312.     {
  313.         if (!WritePrefs6(&systemVolRef, thePrefs))
  314.             return(FALSE);
  315.     }
  316.     
  317.     return(TRUE);
  318. }
  319.  
  320. //--------------------------------------------------------------  ReadPrefs
  321.  
  322. // This is the System 7 version for reading in the preferences.  It handles…
  323. // opening the prefs, reading in the data to your prefs struct and closing…
  324. // the file.
  325.  
  326. OSErr ReadPrefs (long *prefDirID, short *systemVolRef, prefsInfo *thePrefs)
  327. {
  328.     OSErr        theErr;
  329.     short        fileRefNum;
  330.     long        byteCount;
  331.     FSSpec        theSpecs;
  332.     Str255        fileName = kPrefFileName;
  333.                                     // Get an FSSpec record to the prefs file.
  334.     theErr = FSMakeFSSpec(*systemVolRef, *prefDirID, fileName, &theSpecs);
  335.     if (theErr != noErr)
  336.     {
  337.         if (theErr == fnfErr)        // If it doesn't exist, return - we'll use defaults.
  338.             return(theErr);
  339.         else                        // If some other file error occured, bail.
  340.             RedAlert("\pPrefs FSMakeFSSpec() Error");
  341.     }
  342.                                     // Open the prefs file.
  343.     theErr = FSpOpenDF(&theSpecs, fsRdWrPerm, &fileRefNum);
  344.     if (theErr != noErr)
  345.         RedAlert("\pPrefs FSpOpenDF() Error");
  346.     
  347.     byteCount = sizeof(*thePrefs);    // Determine the number of bytes to read in.
  348.                                     // Read 'em into your prefs struct.
  349.     theErr = FSRead(fileRefNum, &byteCount, thePrefs);
  350.     if (theErr != noErr)            // If there was an error reading the file…
  351.     {                                // close the file and we'll revert to defaults.
  352.         if (theErr == eofErr)
  353.             theErr = FSClose(fileRefNum);
  354.         else                        // If closing failed, bail.
  355.             RedAlert("\pPrefs FSRead() Error");
  356.         return(theErr);
  357.     }
  358.     
  359.     theErr = FSClose(fileRefNum);    // Close the prefs file.
  360.     if (theErr != noErr)
  361.         RedAlert("\pPrefs FSClose() Error");
  362.     
  363.     return(theErr);
  364. }
  365.  
  366. //--------------------------------------------------------------  ReadPrefs6
  367.  
  368. // This is the System 6 version of the above function.  It's basically the same,…
  369. // but doesn't have the luxury of using FSSpec records.
  370.  
  371. OSErr ReadPrefs6 (short *systemVolRef, prefsInfo *thePrefs)
  372. {
  373.     OSErr        theErr;
  374.     short        fileRefNum;
  375.     long        byteCount;
  376.     Str255        fileName = kPrefFileName;
  377.                                 // Attempt to open the prefs file.
  378.     theErr = FSOpen(fileName, *systemVolRef, &fileRefNum);
  379.     if (theErr != noErr)        // Did opening the file fail?
  380.     {
  381.         if (theErr == fnfErr)    // It did - did it fail because it doesn't exist?
  382.             return(theErr);        // Okay, then we'll revert to default settings.
  383.         else                    // Otherwise, we have a more serious problem.
  384.             RedAlert("\pPrefs FSOpen() Error");
  385.     }
  386.                                 // Get number of bytes to read in.
  387.     byteCount = sizeof(*thePrefs);
  388.                                 // Read in the stream of data into prefs struct.
  389.     theErr = FSRead(fileRefNum, &byteCount, thePrefs);
  390.     if (theErr != noErr)        // Did the read fail?
  391.     {                            // Maybe we're reading too much data (new prefs vers).
  392.         if (theErr == eofErr)    // That's okay, we'll use defaults for now.
  393.             theErr = FSClose(fileRefNum);
  394.         else
  395.             RedAlert("\pPrefs FSRead() Error");
  396.         return(theErr);
  397.     }
  398.                                 // Close the prefs file.
  399.     theErr = FSClose(fileRefNum);
  400.     if (theErr != noErr)
  401.         RedAlert("\pPrefs FSClose() Error");
  402.     
  403.     return(theErr);
  404. }
  405.  
  406. //--------------------------------------------------------------  DeletePrefs
  407.  
  408. // It can happen that you introduce a game with only a few preference settings…
  409. // but then later update your game and end up having to add additional settings…
  410. // to be stored in your games preferences.  In this case, the size of the old…
  411. // prefs won't match the size of the new.  Or even if the size is the same, you…
  412. // may have re-ordered the prefs and attempting to load the old prefs will result…
  413. // in garbage.  It is for this reason that I use the "versionNeed" variable and…
  414. // the "prefVersion" field in the prefs struct.  In such a case, the below function…
  415. // will be called to delte the old prefs.  When the prefs are then written out, a…
  416. // new pref file will be created.  This particular function is the System 7 version…
  417. // for deleting the old preferences.
  418.  
  419. Boolean DeletePrefs (long *dirID, short *volRef)
  420. {
  421.     FSSpec        theSpecs;
  422.     Str255        fileName = kPrefFileName;
  423.     OSErr        theErr;
  424.                                             // Create an FSSec record.
  425.     theErr = FSMakeFSSpec(*volRef, *dirID, fileName, &theSpecs);
  426.     if (theErr != noErr)                    // Test to see if it worked.
  427.         return(FALSE);
  428.     else                                    // If it worked…
  429.         theErr = FSpDelete(&theSpecs);        // delete the file.
  430.     
  431.     if (theErr != noErr)
  432.         return(FALSE);
  433.     
  434.     return(TRUE);
  435. }
  436.  
  437. //--------------------------------------------------------------  DeletePrefs6
  438.  
  439. // This is the System 6 version for deleting a preferences file (see above function).
  440.  
  441. Boolean DeletePrefs6 (short *volRef)
  442. {
  443.     Str255        fileName = kPrefFileName;
  444.     OSErr        theErr;
  445.     
  446.     theErr = FSDelete(fileName, *volRef);    // Delete the prefs file.
  447.     
  448.     if (theErr != noErr)
  449.         return(FALSE);
  450.     
  451.     return(TRUE);
  452. }
  453.  
  454. //--------------------------------------------------------------  LoadPrefs
  455.  
  456. // Here is the single call for loading in preferences.  It handles all the…
  457. // above function onvolved with opening and reading in preferences.  It…
  458. // determines whether we are on System 6 or 7 (FSSpecs) and makes the right…
  459. // calls.
  460.  
  461. Boolean LoadPrefs (prefsInfo *thePrefs, short versionNeed)
  462. {
  463.     long        prefDirID;
  464.     OSErr        theErr;
  465.     short        systemVolRef;
  466.     Boolean        canUseFSSpecs, noProblems;
  467.     
  468.     canUseFSSpecs = CanUseFindFolder();    // See if we can use FSSpecs (System 7).
  469.     if (canUseFSSpecs)
  470.     {                                    // Get a path to the prefs file.
  471.         noProblems = GetPrefsFPath(&prefDirID, &systemVolRef);
  472.         if (!noProblems)
  473.             return(FALSE);
  474.     }
  475.     else
  476.     {                                    // Gets path to prefs file (System 6).
  477.         noProblems = GetPrefsFPath6(&systemVolRef);
  478.         if (!noProblems)
  479.             return(FALSE);
  480.     }
  481.     
  482.     if (canUseFSSpecs)
  483.     {                                    // Attempt to read prefs.
  484.         theErr = ReadPrefs(&prefDirID, &systemVolRef, thePrefs);
  485.         if (theErr == eofErr)            // Fail the read?  Maybe an old prefs version.
  486.         {                                // Delete it - we'll create a new one later.
  487.             noProblems = DeletePrefs(&prefDirID, &systemVolRef);
  488.             return(FALSE);                // Meanwhile, we'll use defaults.
  489.         }
  490.         else if (theErr != noErr)
  491.             return(FALSE);
  492.     }
  493.     else
  494.     {                                    // Attempt to read prefs (System 6).
  495.         theErr = ReadPrefs6(&systemVolRef, thePrefs);
  496.         if (theErr == eofErr)            // Fail the read?  Maybe an old prefs version.
  497.         {                                // Delete it - we'll create a new one later.
  498.             noProblems = DeletePrefs6(&systemVolRef);
  499.             return(FALSE);                // Meanwhile, we'll use defaults.
  500.         }
  501.         else if (theErr != noErr)
  502.             return(FALSE);
  503.     }
  504.                                         // Okay, maybe the read worked, but we still…
  505.                                         // need to check the version number to see…
  506.                                         // if it's current.
  507.     if (thePrefs->prefVersion != versionNeed)
  508.     {                                    // We'll delete the file if old version.
  509.         if (canUseFSSpecs)
  510.         {
  511.             noProblems = DeletePrefs(&prefDirID, &systemVolRef);
  512.             return(FALSE);
  513.         }
  514.         else
  515.         {
  516.             noProblems = DeletePrefs6(&systemVolRef);
  517.             return(FALSE);
  518.         }
  519.     }
  520.     
  521.     return(TRUE);
  522. }
  523.  
  524.