1 #include "loadmgef.hpp"
2
3 #include <sstream>
4
5 #include "esmreader.hpp"
6 #include "esmwriter.hpp"
7 #include "defs.hpp"
8
9 namespace
10 {
11 static const char *sIds[ESM::MagicEffect::Length] =
12 {
13 "WaterBreathing",
14 "SwiftSwim",
15 "WaterWalking",
16 "Shield",
17 "FireShield",
18 "LightningShield",
19 "FrostShield",
20 "Burden",
21 "Feather",
22 "Jump",
23 "Levitate",
24 "SlowFall",
25 "Lock",
26 "Open",
27 "FireDamage",
28 "ShockDamage",
29 "FrostDamage",
30 "DrainAttribute",
31 "DrainHealth",
32 "DrainMagicka",
33 "DrainFatigue",
34 "DrainSkill",
35 "DamageAttribute",
36 "DamageHealth",
37 "DamageMagicka",
38 "DamageFatigue",
39 "DamageSkill",
40 "Poison",
41 "WeaknessToFire",
42 "WeaknessToFrost",
43 "WeaknessToShock",
44 "WeaknessToMagicka",
45 "WeaknessToCommonDisease",
46 "WeaknessToBlightDisease",
47 "WeaknessToCorprusDisease",
48 "WeaknessToPoison",
49 "WeaknessToNormalWeapons",
50 "DisintegrateWeapon",
51 "DisintegrateArmor",
52 "Invisibility",
53 "Chameleon",
54 "Light",
55 "Sanctuary",
56 "NightEye",
57 "Charm",
58 "Paralyze",
59 "Silence",
60 "Blind",
61 "Sound",
62 "CalmHumanoid",
63 "CalmCreature",
64 "FrenzyHumanoid",
65 "FrenzyCreature",
66 "DemoralizeHumanoid",
67 "DemoralizeCreature",
68 "RallyHumanoid",
69 "RallyCreature",
70 "Dispel",
71 "Soultrap",
72 "Telekinesis",
73 "Mark",
74 "Recall",
75 "DivineIntervention",
76 "AlmsiviIntervention",
77 "DetectAnimal",
78 "DetectEnchantment",
79 "DetectKey",
80 "SpellAbsorption",
81 "Reflect",
82 "CureCommonDisease",
83 "CureBlightDisease",
84 "CureCorprusDisease",
85 "CurePoison",
86 "CureParalyzation",
87 "RestoreAttribute",
88 "RestoreHealth",
89 "RestoreMagicka",
90 "RestoreFatigue",
91 "RestoreSkill",
92 "FortifyAttribute",
93 "FortifyHealth",
94 "FortifyMagicka",
95 "FortifyFatigue",
96 "FortifySkill",
97 "FortifyMaximumMagicka",
98 "AbsorbAttribute",
99 "AbsorbHealth",
100 "AbsorbMagicka",
101 "AbsorbFatigue",
102 "AbsorbSkill",
103 "ResistFire",
104 "ResistFrost",
105 "ResistShock",
106 "ResistMagicka",
107 "ResistCommonDisease",
108 "ResistBlightDisease",
109 "ResistCorprusDisease",
110 "ResistPoison",
111 "ResistNormalWeapons",
112 "ResistParalysis",
113 "RemoveCurse",
114 "TurnUndead",
115 "SummonScamp",
116 "SummonClannfear",
117 "SummonDaedroth",
118 "SummonDremora",
119 "SummonAncestralGhost",
120 "SummonSkeletalMinion",
121 "SummonBonewalker",
122 "SummonGreaterBonewalker",
123 "SummonBonelord",
124 "SummonWingedTwilight",
125 "SummonHunger",
126 "SummonGoldenSaint",
127 "SummonFlameAtronach",
128 "SummonFrostAtronach",
129 "SummonStormAtronach",
130 "FortifyAttack",
131 "CommandCreature",
132 "CommandHumanoid",
133 "BoundDagger",
134 "BoundLongsword",
135 "BoundMace",
136 "BoundBattleAxe",
137 "BoundSpear",
138 "BoundLongbow",
139 "ExtraSpell",
140 "BoundCuirass",
141 "BoundHelm",
142 "BoundBoots",
143 "BoundShield",
144 "BoundGloves",
145 "Corprus",
146 "Vampirism",
147 "SummonCenturionSphere",
148 "SunDamage",
149 "StuntedMagicka",
150
151 // Tribunal only
152 "SummonFabricant",
153
154 // Bloodmoon only
155 "SummonWolf",
156 "SummonBear",
157 "SummonBonewolf",
158 "SummonCreature04",
159 "SummonCreature05"
160 };
161
162 const int NumberOfHardcodedFlags = 143;
163 const int HardcodedFlags[NumberOfHardcodedFlags] = {
164 0x11c8, 0x11c0, 0x11c8, 0x11e0, 0x11e0, 0x11e0, 0x11e0, 0x11d0,
165 0x11c0, 0x11c0, 0x11e0, 0x11c0, 0x11184, 0x11184, 0x1f0, 0x1f0,
166 0x1f0, 0x11d2, 0x11f0, 0x11d0, 0x11d0, 0x11d1, 0x1d2, 0x1f0,
167 0x1d0, 0x1d0, 0x1d1, 0x1f0, 0x11d0, 0x11d0, 0x11d0, 0x11d0,
168 0x11d0, 0x11d0, 0x11d0, 0x11d0, 0x11d0, 0x1d0, 0x1d0, 0x11c8,
169 0x31c0, 0x11c0, 0x11c0, 0x11c0, 0x1180, 0x11d8, 0x11d8, 0x11d0,
170 0x11d0, 0x11180, 0x11180, 0x11180, 0x11180, 0x11180, 0x11180, 0x11180,
171 0x11180, 0x11c4, 0x111b8, 0x1040, 0x104c, 0x104c, 0x104c, 0x104c,
172 0x1040, 0x1040, 0x1040, 0x11c0, 0x11c0, 0x1cc, 0x1cc, 0x1cc,
173 0x1cc, 0x1cc, 0x1c2, 0x1c0, 0x1c0, 0x1c0, 0x1c1, 0x11c2,
174 0x11c0, 0x11c0, 0x11c0, 0x11c1, 0x11c0, 0x21192, 0x20190, 0x20190,
175 0x20190, 0x21191, 0x11c0, 0x11c0, 0x11c0, 0x11c0, 0x11c0, 0x11c0,
176 0x11c0, 0x11c0, 0x11c0, 0x11c0, 0x1c0, 0x11190, 0x9048, 0x9048,
177 0x9048, 0x9048, 0x9048, 0x9048, 0x9048, 0x9048, 0x9048, 0x9048,
178 0x9048, 0x9048, 0x9048, 0x9048, 0x9048, 0x11c0, 0x1180, 0x1180,
179 0x5048, 0x5048, 0x5048, 0x5048, 0x5048, 0x5048, 0x1188, 0x5048,
180 0x5048, 0x5048, 0x5048, 0x5048, 0x1048, 0x104c, 0x1048, 0x40,
181 0x11c8, 0x1048, 0x1048, 0x1048, 0x1048, 0x1048, 0x1048
182 };
183 }
184
185 namespace ESM
186 {
187 unsigned int MagicEffect::sRecordId = REC_MGEF;
188
load(ESMReader & esm,bool & isDeleted)189 void MagicEffect::load(ESMReader &esm, bool &isDeleted)
190 {
191 isDeleted = false; // MagicEffect record can't be deleted now (may be changed in the future)
192
193 esm.getHNT(mIndex, "INDX");
194
195 mId = indexToId (mIndex);
196
197 esm.getHNT(mData, "MEDT", 36);
198 if (esm.getFormat() == 0)
199 {
200 // don't allow mods to change fixed flags in the legacy format
201 mData.mFlags &= (AllowSpellmaking | AllowEnchanting | NegativeLight);
202 if (mIndex>=0 && mIndex<NumberOfHardcodedFlags)
203 mData.mFlags |= HardcodedFlags[mIndex];
204 }
205
206 // vanilla MW accepts the _SND subrecords before or after DESC... I hope
207 // this isn't true for other records, or we have to do a mass-refactor
208 while (esm.hasMoreSubs())
209 {
210 esm.getSubName();
211 switch (esm.retSubName().intval)
212 {
213 case ESM::FourCC<'I','T','E','X'>::value:
214 mIcon = esm.getHString();
215 break;
216 case ESM::FourCC<'P','T','E','X'>::value:
217 mParticle = esm.getHString();
218 break;
219 case ESM::FourCC<'B','S','N','D'>::value:
220 mBoltSound = esm.getHString();
221 break;
222 case ESM::FourCC<'C','S','N','D'>::value:
223 mCastSound = esm.getHString();
224 break;
225 case ESM::FourCC<'H','S','N','D'>::value:
226 mHitSound = esm.getHString();
227 break;
228 case ESM::FourCC<'A','S','N','D'>::value:
229 mAreaSound = esm.getHString();
230 break;
231 case ESM::FourCC<'C','V','F','X'>::value:
232 mCasting = esm.getHString();
233 break;
234 case ESM::FourCC<'B','V','F','X'>::value:
235 mBolt = esm.getHString();
236 break;
237 case ESM::FourCC<'H','V','F','X'>::value:
238 mHit = esm.getHString();
239 break;
240 case ESM::FourCC<'A','V','F','X'>::value:
241 mArea = esm.getHString();
242 break;
243 case ESM::FourCC<'D','E','S','C'>::value:
244 mDescription = esm.getHString();
245 break;
246 default:
247 esm.fail("Unknown subrecord");
248 }
249 }
250 }
save(ESMWriter & esm,bool) const251 void MagicEffect::save(ESMWriter &esm, bool /*isDeleted*/) const
252 {
253 esm.writeHNT("INDX", mIndex);
254
255 esm.writeHNT("MEDT", mData, 36);
256
257 esm.writeHNOCString("ITEX", mIcon);
258 esm.writeHNOCString("PTEX", mParticle);
259 esm.writeHNOCString("BSND", mBoltSound);
260 esm.writeHNOCString("CSND", mCastSound);
261 esm.writeHNOCString("HSND", mHitSound);
262 esm.writeHNOCString("ASND", mAreaSound);
263
264 esm.writeHNOCString("CVFX", mCasting);
265 esm.writeHNOCString("BVFX", mBolt);
266 esm.writeHNOCString("HVFX", mHit);
267 esm.writeHNOCString("AVFX", mArea);
268
269 esm.writeHNOString("DESC", mDescription);
270 }
271
getResistanceEffect(short effect)272 short MagicEffect::getResistanceEffect(short effect)
273 {
274 // Source https://wiki.openmw.org/index.php?title=Research:Magic#Effect_attribute
275
276 // <Effect, Effect providing resistance against first effect>
277 static std::map<short, short> effects;
278 if (effects.empty())
279 {
280 effects[DisintegrateArmor] = Sanctuary;
281 effects[DisintegrateWeapon] = Sanctuary;
282
283 for (int i=0; i<5; ++i)
284 effects[DrainAttribute+i] = ResistMagicka;
285 for (int i=0; i<5; ++i)
286 effects[DamageAttribute+i] = ResistMagicka;
287 for (int i=0; i<5; ++i)
288 effects[AbsorbAttribute+i] = ResistMagicka;
289 for (int i=0; i<10; ++i)
290 effects[WeaknessToFire+i] = ResistMagicka;
291
292 effects[Burden] = ResistMagicka;
293 effects[Charm] = ResistMagicka;
294 effects[Silence] = ResistMagicka;
295 effects[Blind] = ResistMagicka;
296 effects[Sound] = ResistMagicka;
297
298 for (int i=0; i<2; ++i)
299 {
300 effects[CalmHumanoid+i] = ResistMagicka;
301 effects[FrenzyHumanoid+i] = ResistMagicka;
302 effects[DemoralizeHumanoid+i] = ResistMagicka;
303 effects[RallyHumanoid+i] = ResistMagicka;
304 }
305
306 effects[TurnUndead] = ResistMagicka;
307
308 effects[FireDamage] = ResistFire;
309 effects[FrostDamage] = ResistFrost;
310 effects[ShockDamage] = ResistShock;
311 effects[Vampirism] = ResistCommonDisease;
312 effects[Corprus] = ResistCorprusDisease;
313 effects[Poison] = ResistPoison;
314 effects[Paralyze] = ResistParalysis;
315 }
316
317 if (effects.find(effect) != effects.end())
318 return effects[effect];
319 else
320 return -1;
321 }
322
getWeaknessEffect(short effect)323 short MagicEffect::getWeaknessEffect(short effect)
324 {
325 static std::map<short, short> effects;
326 if (effects.empty())
327 {
328 for (int i=0; i<5; ++i)
329 effects[DrainAttribute+i] = WeaknessToMagicka;
330 for (int i=0; i<5; ++i)
331 effects[DamageAttribute+i] = WeaknessToMagicka;
332 for (int i=0; i<5; ++i)
333 effects[AbsorbAttribute+i] = WeaknessToMagicka;
334 for (int i=0; i<10; ++i)
335 effects[WeaknessToFire+i] = WeaknessToMagicka;
336
337 effects[Burden] = WeaknessToMagicka;
338 effects[Charm] = WeaknessToMagicka;
339 effects[Silence] = WeaknessToMagicka;
340 effects[Blind] = WeaknessToMagicka;
341 effects[Sound] = WeaknessToMagicka;
342
343 for (int i=0; i<2; ++i)
344 {
345 effects[CalmHumanoid+i] = WeaknessToMagicka;
346 effects[FrenzyHumanoid+i] = WeaknessToMagicka;
347 effects[DemoralizeHumanoid+i] = WeaknessToMagicka;
348 effects[RallyHumanoid+i] = WeaknessToMagicka;
349 }
350
351 effects[TurnUndead] = WeaknessToMagicka;
352
353 effects[FireDamage] = WeaknessToFire;
354 effects[FrostDamage] = WeaknessToFrost;
355 effects[ShockDamage] = WeaknessToShock;
356 effects[Vampirism] = WeaknessToCommonDisease;
357 effects[Corprus] = WeaknessToCorprusDisease;
358 effects[Poison] = WeaknessToPoison;
359
360 effects[Paralyze] = -1;
361 }
362
363 if (effects.find(effect) != effects.end())
364 return effects[effect];
365 else
366 return -1;
367 }
368
genNameMap()369 static std::map<short,std::string> genNameMap()
370 {
371 // Map effect ID to GMST name
372 // http://www.uesp.net/morrow/hints/mweffects.shtml
373 std::map<short, std::string> names;
374 names[85] ="sEffectAbsorbAttribute";
375 names[88] ="sEffectAbsorbFatigue";
376 names[86] ="sEffectAbsorbHealth";
377 names[87] ="sEffectAbsorbSpellPoints";
378 names[89] ="sEffectAbsorbSkill";
379 names[63] ="sEffectAlmsiviIntervention";
380 names[47] ="sEffectBlind";
381 names[123] ="sEffectBoundBattleAxe";
382 names[129] ="sEffectBoundBoots";
383 names[127] ="sEffectBoundCuirass";
384 names[120] ="sEffectBoundDagger";
385 names[131] ="sEffectBoundGloves";
386 names[128] ="sEffectBoundHelm";
387 names[125] ="sEffectBoundLongbow";
388 names[126] ="sEffectExtraSpell";
389 names[121] ="sEffectBoundLongsword";
390 names[122] ="sEffectBoundMace";
391 names[130] ="sEffectBoundShield";
392 names[124] ="sEffectBoundSpear";
393 names[7] ="sEffectBurden";
394 names[50] ="sEffectCalmCreature";
395 names[49] ="sEffectCalmHumanoid";
396 names[40] ="sEffectChameleon";
397 names[44] ="sEffectCharm";
398 names[118] ="sEffectCommandCreatures";
399 names[119] ="sEffectCommandHumanoids";
400 names[132] ="sEffectCorpus"; // NB this typo. (bethesda made it)
401 names[70] ="sEffectCureBlightDisease";
402 names[69] ="sEffectCureCommonDisease";
403 names[71] ="sEffectCureCorprusDisease";
404 names[73] ="sEffectCureParalyzation";
405 names[72] ="sEffectCurePoison";
406 names[22] ="sEffectDamageAttribute";
407 names[25] ="sEffectDamageFatigue";
408 names[23] ="sEffectDamageHealth";
409 names[24] ="sEffectDamageMagicka";
410 names[26] ="sEffectDamageSkill";
411 names[54] ="sEffectDemoralizeCreature";
412 names[53] ="sEffectDemoralizeHumanoid";
413 names[64] ="sEffectDetectAnimal";
414 names[65] ="sEffectDetectEnchantment";
415 names[66] ="sEffectDetectKey";
416 names[38] ="sEffectDisintegrateArmor";
417 names[37] ="sEffectDisintegrateWeapon";
418 names[57] ="sEffectDispel";
419 names[62] ="sEffectDivineIntervention";
420 names[17] ="sEffectDrainAttribute";
421 names[20] ="sEffectDrainFatigue";
422 names[18] ="sEffectDrainHealth";
423 names[19] ="sEffectDrainSpellpoints";
424 names[21] ="sEffectDrainSkill";
425 names[8] ="sEffectFeather";
426 names[14] ="sEffectFireDamage";
427 names[4] ="sEffectFireShield";
428 names[117] ="sEffectFortifyAttackBonus";
429 names[79] ="sEffectFortifyAttribute";
430 names[82] ="sEffectFortifyFatigue";
431 names[80] ="sEffectFortifyHealth";
432 names[81] ="sEffectFortifySpellpoints";
433 names[84] ="sEffectFortifyMagickaMultiplier";
434 names[83] ="sEffectFortifySkill";
435 names[52] ="sEffectFrenzyCreature";
436 names[51] ="sEffectFrenzyHumanoid";
437 names[16] ="sEffectFrostDamage";
438 names[6] ="sEffectFrostShield";
439 names[39] ="sEffectInvisibility";
440 names[9] ="sEffectJump";
441 names[10] ="sEffectLevitate";
442 names[41] ="sEffectLight";
443 names[5] ="sEffectLightningShield";
444 names[12] ="sEffectLock";
445 names[60] ="sEffectMark";
446 names[43] ="sEffectNightEye";
447 names[13] ="sEffectOpen";
448 names[45] ="sEffectParalyze";
449 names[27] ="sEffectPoison";
450 names[56] ="sEffectRallyCreature";
451 names[55] ="sEffectRallyHumanoid";
452 names[61] ="sEffectRecall";
453 names[68] ="sEffectReflect";
454 names[100] ="sEffectRemoveCurse";
455 names[95] ="sEffectResistBlightDisease";
456 names[94] ="sEffectResistCommonDisease";
457 names[96] ="sEffectResistCorprusDisease";
458 names[90] ="sEffectResistFire";
459 names[91] ="sEffectResistFrost";
460 names[93] ="sEffectResistMagicka";
461 names[98] ="sEffectResistNormalWeapons";
462 names[99] ="sEffectResistParalysis";
463 names[97] ="sEffectResistPoison";
464 names[92] ="sEffectResistShock";
465 names[74] ="sEffectRestoreAttribute";
466 names[77] ="sEffectRestoreFatigue";
467 names[75] ="sEffectRestoreHealth";
468 names[76] ="sEffectRestoreSpellPoints";
469 names[78] ="sEffectRestoreSkill";
470 names[42] ="sEffectSanctuary";
471 names[3] ="sEffectShield";
472 names[15] ="sEffectShockDamage";
473 names[46] ="sEffectSilence";
474 names[11] ="sEffectSlowFall";
475 names[58] ="sEffectSoultrap";
476 names[48] ="sEffectSound";
477 names[67] ="sEffectSpellAbsorption";
478 names[136] ="sEffectStuntedMagicka";
479 names[106] ="sEffectSummonAncestralGhost";
480 names[110] ="sEffectSummonBonelord";
481 names[108] ="sEffectSummonLeastBonewalker";
482 names[134] ="sEffectSummonCenturionSphere";
483 names[103] ="sEffectSummonClannfear";
484 names[104] ="sEffectSummonDaedroth";
485 names[105] ="sEffectSummonDremora";
486 names[114] ="sEffectSummonFlameAtronach";
487 names[115] ="sEffectSummonFrostAtronach";
488 names[113] ="sEffectSummonGoldenSaint";
489 names[109] ="sEffectSummonGreaterBonewalker";
490 names[112] ="sEffectSummonHunger";
491 names[102] ="sEffectSummonScamp";
492 names[107] ="sEffectSummonSkeletalMinion";
493 names[116] ="sEffectSummonStormAtronach";
494 names[111] ="sEffectSummonWingedTwilight";
495 names[135] ="sEffectSunDamage";
496 names[1] ="sEffectSwiftSwim";
497 names[59] ="sEffectTelekinesis";
498 names[101] ="sEffectTurnUndead";
499 names[133] ="sEffectVampirism";
500 names[0] ="sEffectWaterBreathing";
501 names[2] ="sEffectWaterWalking";
502 names[33] ="sEffectWeaknesstoBlightDisease";
503 names[32] ="sEffectWeaknesstoCommonDisease";
504 names[34] ="sEffectWeaknesstoCorprusDisease";
505 names[28] ="sEffectWeaknesstoFire";
506 names[29] ="sEffectWeaknesstoFrost";
507 names[31] ="sEffectWeaknesstoMagicka";
508 names[36] ="sEffectWeaknesstoNormalWeapons";
509 names[35] ="sEffectWeaknesstoPoison";
510 names[30] ="sEffectWeaknesstoShock";
511
512 // bloodmoon
513 names[138] ="sEffectSummonCreature01";
514 names[139] ="sEffectSummonCreature02";
515 names[140] ="sEffectSummonCreature03";
516 names[141] ="sEffectSummonCreature04";
517 names[142] ="sEffectSummonCreature05";
518
519 // tribunal
520 names[137] ="sEffectSummonFabricant";
521
522 return names;
523 }
524 const std::map<short,std::string> MagicEffect::sNames = genNameMap();
525
effectIdToString(short effectID)526 const std::string &MagicEffect::effectIdToString(short effectID)
527 {
528 std::map<short,std::string>::const_iterator name = sNames.find(effectID);
529 if(name == sNames.end())
530 throw std::runtime_error(std::string("Unimplemented effect ID ")+std::to_string(effectID));
531
532 return name->second;
533 }
534
535 class FindSecond {
536 const std::string &mName;
537
538 public:
FindSecond(const std::string & name)539 FindSecond(const std::string &name) : mName(name) { }
540
operator ()(const std::pair<short,std::string> & item) const541 bool operator()(const std::pair<short,std::string> &item) const
542 {
543 if(Misc::StringUtils::ciEqual(item.second, mName))
544 return true;
545 return false;
546 }
547 };
548
effectStringToId(const std::string & effect)549 short MagicEffect::effectStringToId(const std::string &effect)
550 {
551 std::map<short,std::string>::const_iterator name;
552
553 name = std::find_if(sNames.begin(), sNames.end(), FindSecond(effect));
554 if(name == sNames.end())
555 throw std::runtime_error(std::string("Unimplemented effect ")+effect);
556
557 return name->first;
558 }
559
getMagnitudeDisplayType() const560 MagicEffect::MagnitudeDisplayType MagicEffect::getMagnitudeDisplayType() const {
561 if ( mData.mFlags & NoMagnitude )
562 return MDT_None;
563 if ( mIndex == 84 )
564 return MDT_TimesInt;
565 if ( mIndex == 59 ||
566 ( mIndex >= 64 && mIndex <= 66) )
567 return MDT_Feet;
568 if ( mIndex == 118 || mIndex == 119 )
569 return MDT_Level;
570 if ( ( mIndex >= 28 && mIndex <= 36 )
571 || ( mIndex >= 90 && mIndex <= 99 )
572 || mIndex == 40 || mIndex == 47
573 || mIndex == 57 || mIndex == 68 )
574 return MDT_Percentage;
575
576 return MDT_Points;
577 }
578
blank()579 void MagicEffect::blank()
580 {
581 mData.mSchool = 0;
582 mData.mBaseCost = 0;
583 mData.mFlags = 0;
584 mData.mRed = 0;
585 mData.mGreen = 0;
586 mData.mBlue = 0;
587 mData.mSpeed = 0;
588
589 mIcon.clear();
590 mParticle.clear();
591 mCasting.clear();
592 mHit.clear();
593 mArea.clear();
594 mBolt.clear();
595 mCastSound.clear();
596 mBoltSound.clear();
597 mHitSound.clear();
598 mAreaSound.clear();
599 mDescription.clear();
600 }
601
indexToId(int index)602 std::string MagicEffect::indexToId (int index)
603 {
604 std::ostringstream stream;
605
606 if (index!=-1)
607 {
608 stream << "#";
609
610 if (index<100)
611 {
612 stream << "0";
613
614 if (index<10)
615 stream << "0";
616 }
617
618 stream << index;
619
620 if (index>=0 && index<Length)
621 stream << sIds[index];
622 }
623
624 return stream.str();
625 }
626 }
627