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