1 /* GemRB - Infinity Engine Emulator
2 * Copyright (C) 2003 The GemRB Project
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 *
18 *
19 */
20
21 #include "CREImporter.h"
22
23 #include "ie_stats.h"
24 #include "voodooconst.h"
25
26 #include "EffectMgr.h"
27 #include "GameData.h"
28 #include "Interface.h"
29 #include "PluginMgr.h"
30 #include "RNG.h"
31 #include "TableMgr.h"
32 #include "GameScript/GameScript.h"
33
34 #include <cassert>
35 #include <cmath>
36
37 using namespace GemRB;
38
39 static unsigned int RandColor = 1;
40 std::vector<std::vector<unsigned char>> randcolors; // it's likely not important enough, so perhaps we should just store the Autotable directly
41
42 //one column, these don't have a level
43 static ieResRef* innlist; //IE_IWD2_SPELL_INNATE
44 static int inncount=-1;
45 static ieResRef* snglist; //IE_IWD2_SPELL_SONG
46 static int sngcount=-1;
47 static ieResRef* shplist; //IE_IWD2_SPELL_SHAPE
48 static int shpcount=-1;
49
50 struct LevelAndKit
51 {
52 unsigned int level;
53 unsigned int kit;
54 };
55
56 class SpellEntry
57 {
58 public:
59 ~SpellEntry();
60 SpellEntry();
61 const ieResRef *GetSpell() const;
62 const ieResRef *FindSpell(unsigned int level, unsigned int kit) const;
63 int FindSpell(unsigned int kit) const;
64 bool Equals(const char *spl) const;
65 void SetSpell(const char *spl);
66 void AddLevel(unsigned int level, unsigned int kit);
67 private:
68 ieResRef spell;
69 LevelAndKit *levels;
70 int count;
71 };
72
SpellEntry()73 SpellEntry::SpellEntry()
74 {
75 levels = NULL;
76 spell[0]=0;
77 count = 0;
78 }
79
~SpellEntry()80 SpellEntry::~SpellEntry()
81 {
82 free(levels);
83 levels = NULL;
84 }
85
GetSpell() const86 const ieResRef *SpellEntry::GetSpell() const
87 {
88 return &spell;
89 }
90
FindSpell(unsigned int level,unsigned int kit) const91 const ieResRef *SpellEntry::FindSpell(unsigned int level, unsigned int kit) const
92 {
93 int i = count;
94 while(i--) {
95 if (levels[i].level==level && levels[i].kit==kit) {
96 return &spell;
97 }
98 }
99 return NULL;
100 }
101
FindSpell(unsigned int kit) const102 int SpellEntry::FindSpell(unsigned int kit) const
103 {
104 int i = count;
105 while(i--) {
106 if (levels[i].kit==kit) {
107 return levels[i].level;
108 }
109 }
110 return -1;
111 }
112
FindSpell(ieResRef spellref,SpellEntry * list,int listsize)113 static int FindSpell(ieResRef spellref, SpellEntry* list, int listsize)
114 {
115 int i = listsize;
116 while (i--) {
117 if (list[i].Equals(spellref)) {
118 return i;
119 }
120 }
121 return -1;
122 }
123
Equals(const char * spl) const124 bool SpellEntry::Equals(const char *spl) const
125 {
126 return !strnicmp(spell, spl, sizeof(ieResRef));
127 }
128
SetSpell(const char * spl)129 void SpellEntry::SetSpell(const char *spl)
130 {
131 strnlwrcpy(spell, spl, 8);
132 }
133
AddLevel(unsigned int level,unsigned int kit)134 void SpellEntry::AddLevel(unsigned int level,unsigned int kit)
135 {
136 if(!level) {
137 return;
138 }
139
140 level--; // convert to 0-based for internal use
141 for(int i=0;i<count;i++) {
142 if(levels[i].kit==kit && levels[i].level==level) {
143 Log(WARNING, "CREImporter", "Skipping duplicate spell list table entry for: %s", spell);
144 return;
145 }
146 }
147 levels = (LevelAndKit *) realloc(levels, sizeof(LevelAndKit) * (count+1) );
148 levels[count].kit=kit;
149 levels[count].level=level;
150 count++;
151 }
152
IsInnate(ieResRef name)153 static int IsInnate(ieResRef name)
154 {
155 for(int i=0;i<inncount;i++) {
156 if(!strnicmp(name, innlist[i], 8) ) {
157 return i;
158 }
159 }
160 return -1;
161 }
162
IsSong(ieResRef name)163 static int IsSong(ieResRef name)
164 {
165 for(int i=0;i<sngcount;i++) {
166 if(!strnicmp(name, snglist[i], 8) ) {
167 return i;
168 }
169 }
170 return -1;
171 }
172
IsShape(ieResRef name)173 static int IsShape(ieResRef name)
174 {
175 for(int i=0;i<shpcount;i++) {
176 if(!strnicmp(name, shplist[i], 8) ) {
177 return i;
178 }
179 }
180 return -1;
181 }
182
183 static SpellEntry* spllist=NULL;
184 static int splcount=-1;
185 static SpellEntry* domlist=NULL;
186 static int domcount=-1;
187 static SpellEntry* maglist=NULL;
188 static int magcount=-1;
189
IsDomain(ieResRef name,unsigned short & level,unsigned int kit)190 static int IsDomain(ieResRef name, unsigned short &level, unsigned int kit)
191 {
192 for(int i=0;i<splcount;i++) {
193 if (domlist[i].Equals(name) ) {
194 int lev = domlist[i].FindSpell(kit);
195 if (lev == -1) return -1;
196 level = lev;
197 return i;
198 }
199 }
200 return -1;
201 }
202
203 /*static int IsSpecial(ieResRef name, unsigned short &level, unsigned int kit)
204 {
205 for(int i=0;i<magcount;i++) {
206 if (maglist[i].Equals(name) ) {
207 level = maglist[i].FindSpell(kit);
208 return i;
209 }
210 }
211 return -1;
212 }*/
213
FindSpellType(char * name,unsigned short & level,unsigned int clsmsk,unsigned int kit) const214 int CREImporter::FindSpellType(char *name, unsigned short &level, unsigned int clsmsk, unsigned int kit) const
215 {
216 level = 0;
217 if (IsSong(name)>=0) return IE_IWD2_SPELL_SONG;
218 if (IsShape(name)>=0) return IE_IWD2_SPELL_SHAPE;
219 if (IsInnate(name)>=0) return IE_IWD2_SPELL_INNATE;
220 // there is no gui page for specialists spells, so let's skip them here
221 // otherwise their overlap causes bards and sorcerers to have their spells
222 // on the wizard page
223 // if (IsSpecial(name, level, kit)>=0) return IE_IWD2_SPELL_WIZARD;
224
225 // strict domain spell check, so we don't steal the spells from other books
226 // still needs to happen first or the laxer check below can misclassify
227 // first translate the actual kit to a column index to make them comparable
228 // luckily they are in order
229 int kit2 = std::log2(kit/0x8000); // 0x8000 is the first cleric kit
230 if (IsDomain(name, level, kit2) >= 0) return IE_IWD2_SPELL_DOMAIN;
231
232 // try harder for the rest
233 for (int i = 0;i<splcount;i++) {
234 if (spllist[i].Equals(name) ) {
235 // iterate over table columns ("kits" - book types)
236 for(int type = IE_IWD2_SPELL_BARD; type < IE_IWD2_SPELL_DOMAIN; type++) {
237 if (clsmsk & (1<<type)) {
238 int level2 = spllist[i].FindSpell(type);
239 if (level2 == -1) {
240 Log(ERROR, "CREImporter", "Spell (%s of type %d) found without a level set! Using 1!", name, type);
241 level2 = 0; // internal 0-indexed level
242 }
243 level = level2;
244 // FIXME: returning the first will misplace spells for multiclasses
245 return type;
246 }
247 }
248 }
249 }
250
251 Log(ERROR, "CREImporter", "Could not find spell (%s) booktype! %d, %d!", name, clsmsk, kit);
252 // pseudorandom fallback
253 return IE_IWD2_SPELL_WIZARD;
254 }
255
256 //int CREImporter::ResolveSpellName(ieResRef name, int level, ieIWD2SpellType type) const
ResolveSpellName(const ieResRef name,int level,ieIWD2SpellType type)257 static int ResolveSpellName(const ieResRef name, int level, ieIWD2SpellType type)
258 {
259 int i;
260
261 if (level>=MAX_SPELL_LEVEL) {
262 return -1;
263 }
264 switch(type)
265 {
266 case IE_IWD2_SPELL_INNATE:
267 for(i=0;i<inncount;i++) {
268 if(!strnicmp(name, innlist[i], 8) ) {
269 return i;
270 }
271 }
272 break;
273 case IE_IWD2_SPELL_SONG:
274 for(i=0;i<sngcount;i++) {
275 if(!strnicmp(name, snglist[i], 8) ) {
276 return i;
277 }
278 }
279 break;
280 case IE_IWD2_SPELL_SHAPE:
281 for(i=0;i<shpcount;i++) {
282 if(!strnicmp(name, shplist[i], 8) ) {
283 return i;
284 }
285 }
286 break;
287 case IE_IWD2_SPELL_DOMAIN:
288 default:
289 for(i=0;i<splcount;i++) {
290 if (spllist[i].Equals(name) ) return i;
291 }
292 }
293 return -1;
294 }
295
296 //input: index, level, type, kit
ResolveSpellIndex(int index,int level,ieIWD2SpellType type,int kit)297 static const ieResRef *ResolveSpellIndex(int index, int level, ieIWD2SpellType type, int kit)
298 {
299 const ieResRef *ret;
300
301 if (level>=MAX_SPELL_LEVEL) {
302 return NULL;
303 }
304
305 switch(type)
306 {
307 case IE_IWD2_SPELL_INNATE:
308 if (index>=inncount) {
309 return NULL;
310 }
311 return &innlist[index];
312 case IE_IWD2_SPELL_SONG:
313 if (index>=sngcount) {
314 return NULL;
315 }
316 return &snglist[index];
317 case IE_IWD2_SPELL_SHAPE:
318 if (index>=shpcount) {
319 return NULL;
320 }
321 return &shplist[index];
322 case IE_IWD2_SPELL_DOMAIN:
323 if (index>=splcount) {
324 return NULL;
325 }
326 // translate the actual kit to a column index to make them comparable
327 // luckily they are in order
328 kit = std::log2(kit/0x8000); // 0x8000 is the first cleric kit
329 ret = domlist[index].FindSpell(level, kit);
330 if (ret) {
331 return ret;
332 }
333 // sigh, retry with wizard spells, since the table does not cover everything npcs have
334 kit = -1;
335 type = IE_IWD2_SPELL_WIZARD;
336 break;
337 case IE_IWD2_SPELL_WIZARD:
338 if (index>=splcount) {
339 break;
340 }
341 // translate the actual kit to a column index to make them comparable
342 kit = std::log2(kit/0x40); // 0x40 is the first mage kit
343 //if it is a specialist spell, return it now
344 ret = maglist[index].FindSpell(level, kit);
345 if ( ret) {
346 return ret;
347 }
348 //fall through
349 default:
350 kit = -1;
351 //comes later
352 break;
353 }
354
355 // type matches the table columns (0-bard to 6-wizard)
356 ret = spllist[index].FindSpell(level, type);
357 if (!ret) {
358 // some npcs have spells at odd levels, so the lookup just failed
359 // eg. slayer knights of xvim with sppr325 at level 2 instead of 3
360 Log(ERROR, "CREImporter", "Spell (%d of type %d) found at unexpected level (%d)!", index, type, level);
361 int level2 = spllist[index].FindSpell(type);
362 // grrr, some rows have no levels set - they're all 0, but with a valid resref, so just return that
363 if (level2 == -1) {
364 Log(DEBUG, "CREImporter", "Spell entry (%d) without any levels set!", index);
365 return spllist[index].GetSpell();
366 }
367 ret = spllist[index].FindSpell(level2, type);
368 if (ret) Log(DEBUG, "CREImporter", "The spell was found at level %d!", level2);
369 }
370 if (ret || (kit==-1) ) {
371 return ret;
372 }
373
374 error("CREImporter", "Doing extra mage spell lookups!");
375 }
376
ReleaseMemoryCRE()377 static void ReleaseMemoryCRE()
378 {
379 randcolors.clear();
380
381 if (spllist) {
382 delete [] spllist;
383 spllist = NULL;
384 }
385 splcount = -1;
386
387 if (domlist) {
388 delete [] domlist;
389 domlist = NULL;
390 }
391 domcount = -1;
392
393 if (maglist) {
394 delete [] maglist;
395 maglist = NULL;
396 }
397 magcount = -1;
398
399 if (innlist) {
400 free(innlist);
401 innlist = NULL;
402 }
403 inncount = -1;
404
405 if(snglist) {
406 free(snglist);
407 snglist = NULL;
408 }
409 sngcount = -1;
410
411 if(shplist) {
412 free(shplist);
413 shplist = NULL;
414 }
415 shpcount = -1;
416 }
417
GetSpellTable(const ieResRef tableresref,int & count)418 static ieResRef *GetSpellTable(const ieResRef tableresref, int &count)
419 {
420 count = 0;
421 AutoTable tab(tableresref);
422 if (!tab)
423 return 0;
424
425 int column = tab->GetColumnCount()-1;
426 if (column<0) {
427 return 0;
428 }
429
430 count = tab->GetRowCount();
431 ieResRef *reslist = (ieResRef *) malloc (sizeof(ieResRef) * count);
432 for(int i = 0; i<count;i++) {
433 strnlwrcpy(reslist[i], tab->QueryField(i, column), 8);
434 }
435 return reslist;
436 }
437
438 // different tables, but all use listspll.2da for the spell indices
GetKitSpell(const ieResRef tableresref,int & count)439 static SpellEntry *GetKitSpell(const ieResRef tableresref, int &count)
440 {
441 count = 0;
442 AutoTable tab(tableresref);
443 if (!tab)
444 return 0;
445
446 int lastCol = tab->GetColumnCount() - 1; // the last column is not numeric, so we'll skip it
447 if (lastCol < 1) {
448 return 0;
449 }
450
451 count = tab->GetRowCount();
452 SpellEntry *reslist;
453 bool indexlist = false;
454 if (!strnicmp(tableresref, "listspll", 8)) {
455 indexlist = true;
456 reslist = new SpellEntry[count];
457 } else {
458 reslist = new SpellEntry[splcount]; // needs to be the same size for the simple index lookup we do!
459 }
460 int index;
461 for(int i = 0;i<count;i++) {
462 if (indexlist) {
463 index = i;
464 } else {
465 // find the correct index in listspll.2da
466 ieResRef spellref;
467 strnlwrcpy(spellref, tab->QueryField(i, lastCol), 8);
468 // the table has disabled spells in it and they all have the first two chars replaced by '*'
469 if (spellref[0] == '*') {
470 continue;
471 }
472 index = FindSpell(spellref, spllist, splcount);
473 assert (index != -1);
474 }
475 reslist[index].SetSpell(tab->QueryField(i, lastCol));
476 for(int col=0; col < lastCol; col++) {
477 reslist[index].AddLevel(atoi(tab->QueryField(i, col)), col);
478 }
479 }
480 return reslist;
481 }
482
InitSpellbook()483 static void InitSpellbook()
484 {
485 if (splcount!=-1) {
486 return;
487 }
488
489 if (core->HasFeature(GF_HAS_SPELLLIST)) {
490 innlist = GetSpellTable("listinnt", inncount);
491 snglist = GetSpellTable("listsong", sngcount);
492 shplist = GetSpellTable("listshap", shpcount);
493 spllist = GetKitSpell("listspll", splcount); // need to init this one first, since the other two rely on it
494 maglist = GetKitSpell("listmage", magcount);
495 domlist = GetKitSpell("listdomn", domcount);
496 }
497 }
498
CREImporter(void)499 CREImporter::CREImporter(void)
500 {
501 str = NULL;
502 TotSCEFF = 0xff;
503 CREVersion = 0xff;
504 InitSpellbook();
505
506 KnownSpellsOffset = SpellMemorizationOffset = MemorizedSpellsOffset = 0;
507 KnownSpellsCount = SpellMemorizationCount = MemorizedSpellsCount = 0;
508 MemorizedIndex = MemorizedCount = EffectsOffset = EffectsCount = 0;
509 CREOffset = ItemSlotsOffset = ItemsOffset = ItemsCount = VariablesCount = 0;
510 OverlayOffset = OverlayMemorySize = QWPCount = QSPCount = QITCount = 0;
511 IsCharacter = false;
512 }
513
~CREImporter(void)514 CREImporter::~CREImporter(void)
515 {
516 delete str;
517 }
518
Open(DataStream * stream)519 bool CREImporter::Open(DataStream* stream)
520 {
521 if (stream == NULL) {
522 return false;
523 }
524 delete str;
525 str = stream;
526 char Signature[8];
527 str->Read( Signature, 8 );
528 IsCharacter = false;
529 if (strncmp( Signature, "CHR ",4) == 0) {
530 IsCharacter = true;
531 //skips chr signature, reads cre signature
532 if (!SeekCreHeader(Signature)) {
533 return false;
534 }
535 } else {
536 CREOffset = 0;
537 }
538 if (strncmp( Signature, "CRE V1.0", 8 ) == 0) {
539 CREVersion = IE_CRE_V1_0;
540 return true;
541 }
542 if (strncmp( Signature, "CRE V1.2", 8 ) == 0) {
543 CREVersion = IE_CRE_V1_2;
544 return true;
545 }
546 if (strncmp( Signature, "CRE V2.2", 8 ) == 0) {
547 CREVersion = IE_CRE_V2_2;
548 return true;
549 }
550 if (strncmp( Signature, "CRE V9.0", 8 ) == 0) {
551 CREVersion = IE_CRE_V9_0;
552 return true;
553 }
554 if (strncmp( Signature, "CRE V0.0", 8 ) == 0) {
555 CREVersion = IE_CRE_GEMRB;
556 return true;
557 }
558
559 Log(ERROR, "CREImporter", "Not a CRE File or File Version not supported: %8.8s", Signature);
560 return false;
561 }
562
SetupSlotCounts()563 void CREImporter::SetupSlotCounts()
564 {
565 switch (CREVersion) {
566 case IE_CRE_V1_2: //pst
567 QWPCount=4;
568 QSPCount=3;
569 QITCount=5;
570 break;
571 case IE_CRE_GEMRB: //own
572 QWPCount=8;
573 QSPCount=9;
574 QITCount=5;
575 break;
576 case IE_CRE_V2_2: //iwd2
577 QWPCount=8;
578 QSPCount=9;
579 QITCount=3;
580 break;
581 default: //others
582 QWPCount=4;
583 QSPCount=3;
584 QITCount=3;
585 break;
586 }
587 }
588
WriteChrHeader(DataStream * stream,Actor * act)589 void CREImporter::WriteChrHeader(DataStream *stream, Actor *act)
590 {
591 char Signature[8];
592 char filling[10];
593 ieVariable name;
594 ieDword tmpDword, CRESize;
595 ieWord tmpWord;
596
597 CRESize = GetStoredFileSize (act);
598 switch (CREVersion) {
599 case IE_CRE_V9_0: //iwd/HoW
600 memcpy(Signature, "CHR V1.0",8);
601 tmpDword = 0x64; //headersize
602 TotSCEFF = 1;
603 break;
604 case IE_CRE_V1_0: //bg1
605 memcpy(Signature, "CHR V1.0",8);
606 tmpDword = 0x64; //headersize
607 TotSCEFF = 0;
608 break;
609 case IE_CRE_V1_1: //bg2 (fake)
610 memcpy(Signature, "CHR V2.0",8);
611 tmpDword = 0x64; //headersize
612 TotSCEFF = 1;
613 break;
614 case IE_CRE_V1_2: //pst
615 memcpy(Signature, "CHR V1.2",8);
616 tmpDword = 0x68; //headersize
617 TotSCEFF = 0;
618 break;
619 case IE_CRE_V2_2: //iwd2
620 memcpy(Signature, "CHR V2.2",8);
621 tmpDword = 0x21c; //headersize
622 TotSCEFF = 1;
623 break;
624 case IE_CRE_GEMRB: //own format
625 memcpy(Signature, "CHR V0.0",8);
626 tmpDword = 0x1dc; //headersize (iwd2-9x8+8)
627 TotSCEFF = 1;
628 break;
629 default:
630 Log(ERROR, "CREImporter", "Unknown CHR version!");
631 return;
632 }
633 stream->Write( Signature, 8);
634 memset( Signature,0,sizeof(Signature));
635 memset( name,0,sizeof(name));
636 strlcpy(name, act->GetName(0), sizeof(name));
637 stream->Write( name, 32);
638
639 stream->WriteDword( &tmpDword); //cre offset (chr header size)
640 stream->WriteDword( &CRESize); //cre size
641
642 SetupSlotCounts();
643 int i;
644 for (i=0;i<QWPCount;i++) {
645 tmpWord = act->PCStats->QuickWeaponSlots[i];
646 stream->WriteWord (&tmpWord);
647 }
648 for (i=0;i<QWPCount;i++) {
649 tmpWord = act->PCStats->QuickWeaponHeaders[i];
650 stream->WriteWord (&tmpWord);
651 }
652 for (i=0;i<QSPCount;i++) {
653 stream->WriteResRef (act->PCStats->QuickSpells[i]);
654 }
655 //This is 9 for IWD2 and GemRB
656 if (QSPCount==9) {
657 //NOTE: the gemrb internal format stores
658 //0xff or 0xfe in case of innates and bardsongs
659 memset(filling,0,sizeof(filling));
660 memcpy(filling,act->PCStats->QuickSpellClass,MAX_QSLOTS);
661 for(i=0;i<MAX_QSLOTS;i++) {
662 if ( (ieByte) filling[i]>=0xfe) filling[i]=0;
663 }
664 stream->Write( filling, 10);
665 }
666 for (i=0;i<QITCount;i++) {
667 tmpWord = act->PCStats->QuickItemSlots[i];
668 stream->WriteWord (&tmpWord);
669 }
670 for (i=0;i<QITCount;i++) {
671 tmpWord = act->PCStats->QuickItemHeaders[i];
672 stream->WriteWord (&tmpWord);
673 }
674 switch (CREVersion) {
675 case IE_CRE_V2_2:
676 //gemrb format doesn't save these redundantly
677 for (i=0;i<QSPCount;i++) {
678 if (act->PCStats->QuickSpellClass[i]==0xff) {
679 stream->WriteResRef (act->PCStats->QuickSpells[i]);
680 } else {
681 //empty field
682 stream->Write( Signature, 8);
683 }
684 }
685 for (i=0;i<QSPCount;i++) {
686 if (act->PCStats->QuickSpellClass[i]==0xfe) {
687 stream->WriteResRef (act->PCStats->QuickSpells[i]);
688 } else {
689 //empty field
690 stream->Write( Signature, 8);
691 }
692 }
693 //fallthrough
694 case IE_CRE_GEMRB:
695 for (i=0;i<QSPCount;i++) {
696 tmpDword = act->PCStats->QSlots[i+3];
697 stream->WriteDword( &tmpDword);
698 }
699 for (i=0;i<13;i++) {
700 stream->WriteWord (&tmpWord);
701 }
702 stream->Write( act->PCStats->SoundFolder, 32);
703 stream->Write( act->PCStats->SoundSet, 8);
704 for (i=0;i<ES_COUNT;i++) {
705 tmpDword = act->PCStats->ExtraSettings[i];
706 stream->WriteDword( &tmpDword);
707 }
708 //Reserved
709 tmpDword = 0;
710 for (i=0;i<16;i++) {
711 stream->WriteDword( &tmpDword);
712 }
713 break;
714 default:
715 break;
716 }
717 }
718
ReadChrHeader(Actor * act)719 void CREImporter::ReadChrHeader(Actor *act)
720 {
721 ieVariable name;
722 char Signature[8];
723 ieDword offset, size;
724 ieDword tmpDword;
725 ieWord tmpWord;
726 ieByte tmpByte;
727
728 act->CreateStats();
729 str->Rewind();
730 str->Read (Signature, 8);
731 str->Read (name, 32);
732 name[32]=0;
733 if (name[0]) {
734 act->SetName( name, 0 ); //setting longname
735 }
736 str->ReadDword( &offset);
737 str->ReadDword( &size);
738 SetupSlotCounts();
739 int i;
740 for (i=0;i<QWPCount;i++) {
741 str->ReadWord (&tmpWord);
742 act->PCStats->QuickWeaponSlots[i]=tmpWord;
743 }
744 for (i=0;i<QWPCount;i++) {
745 str->ReadWord (&tmpWord);
746 act->PCStats->QuickWeaponHeaders[i]=tmpWord;
747 }
748 for (i=0;i<QSPCount;i++) {
749 str->ReadResRef (act->PCStats->QuickSpells[i]);
750 }
751 if (QSPCount==9) {
752 str->Read (act->PCStats->QuickSpellClass,9);
753 str->Read (&tmpByte, 1);
754 }
755 for (i=0;i<QITCount;i++) {
756 str->ReadWord (&tmpWord);
757 act->PCStats->QuickItemSlots[i]=tmpWord;
758 }
759 for (i=0;i<QITCount;i++) {
760 str->ReadWord (&tmpWord);
761 act->PCStats->QuickItemHeaders[i]=tmpWord;
762 }
763
764 //here comes the version specific read
765 switch (CREVersion) {
766 case IE_CRE_V2_2:
767 //gemrb format doesn't save these redundantly
768 ieResRef spell;
769 for (i=0;i<QSPCount;i++) {
770 str->ReadResRef(spell);
771 // FIXME: why is this needed or why do we overwrite it immediately after?
772 if (spell[0]) {
773 act->PCStats->QuickSpellClass[i]=0xff;
774 memcpy(act->PCStats->QuickSpells[i], spell, sizeof(ieResRef));
775 }
776 }
777 for (i=0;i<QSPCount;i++) {
778 str->ReadResRef(spell);
779 if (spell[0]) {
780 act->PCStats->QuickSpellClass[i]=0xfe;
781 memcpy(act->PCStats->QuickSpells[i], spell, sizeof(ieResRef));
782 }
783 }
784 //fallthrough
785 case IE_CRE_GEMRB:
786 for (i=0;i<QSPCount;i++) {
787 str->ReadDword( &tmpDword);
788 act->PCStats->QSlots[i+3] = (ieByte) tmpDword;
789 }
790 str->Seek(26, GEM_CURRENT_POS);
791 str->Read( act->PCStats->SoundFolder, 32);
792 str->Read( act->PCStats->SoundSet, 8);
793 for (i=0;i<ES_COUNT;i++) {
794 str->ReadDword( &act->PCStats->ExtraSettings[i] );
795 }
796 //Reserved
797 str->Seek(64, GEM_CURRENT_POS);
798 break;
799 default:
800 break;
801 }
802 }
803
SeekCreHeader(char * Signature)804 bool CREImporter::SeekCreHeader(char *Signature)
805 {
806 str->Seek(32, GEM_CURRENT_POS);
807 str->ReadDword( &CREOffset );
808 str->Seek(CREOffset, GEM_STREAM_START);
809 str->Read( Signature, 8);
810 return true;
811 }
812
GetMemorizedSpell()813 CREMemorizedSpell* CREImporter::GetMemorizedSpell()
814 {
815 CREMemorizedSpell* spl = new CREMemorizedSpell();
816
817 str->ReadResRef( spl->SpellResRef );
818 str->ReadDword( &spl->Flags );
819
820 return spl;
821 }
822
GetKnownSpell()823 CREKnownSpell* CREImporter::GetKnownSpell()
824 {
825 CREKnownSpell* spl = new CREKnownSpell();
826
827 str->ReadResRef( spl->SpellResRef );
828 str->ReadWord( &spl->Level );
829 str->ReadWord( &spl->Type );
830
831 return spl;
832 }
833
ReadScript(Actor * act,int ScriptLevel)834 void CREImporter::ReadScript(Actor *act, int ScriptLevel)
835 {
836 ieResRef aScript;
837 str->ReadResRef( aScript );
838 act->SetScript( aScript, ScriptLevel, act->InParty!=0);
839 }
840
GetSpellMemorization(Actor * act)841 CRESpellMemorization* CREImporter::GetSpellMemorization(Actor *act)
842 {
843 ieWord Level, Type, Number, Number2;
844
845 str->ReadWord( &Level );
846 str->ReadWord( &Number );
847 str->ReadWord( &Number2 );
848 str->ReadWord( &Type );
849 str->ReadDword( &MemorizedIndex );
850 str->ReadDword( &MemorizedCount );
851
852 CRESpellMemorization* spl = act->spellbook.GetSpellMemorization(Type, Level);
853 assert(spl && spl->SlotCount == 0 && spl->SlotCountWithBonus == 0); // unused
854 spl->SlotCount = Number;
855 spl->SlotCountWithBonus = Number;
856
857 return spl;
858 }
859
SetupColor(ieDword & stat)860 void CREImporter::SetupColor(ieDword &stat)
861 {
862 if (stat < 200 || RandColor == 0) return;
863
864 // unfortunately this can't go to Initializer, since at that point search paths aren't set up yet
865 size_t RandRows = 0;
866 if (randcolors.empty()) {
867 AutoTable rndcol("randcolr", true);
868 if (rndcol) {
869 RandColor = rndcol->GetColumnCount();
870 RandRows = rndcol->GetRowCount();
871 }
872 if (RandRows <= 1 || RandColor == 0) {
873 RandColor = 0;
874 return;
875 }
876
877 randcolors.resize(RandColor);
878 for (int cols = RandColor - 1; cols >= 0; cols--) {
879 randcolors[cols] = std::vector<unsigned char>(RandRows);
880 for (size_t i = 0; i < RandRows; i++) {
881 randcolors[cols][i] = atoi(rndcol->QueryField(static_cast<unsigned int>(i), cols));
882 }
883 randcolors[cols][0] -= 200;
884 }
885 }
886
887 RandRows = randcolors[0].size();
888 stat -= 200;
889 // handle saves with 1pp on installs without it
890 if (stat >= RandRows) {
891 stat = RandRows - 1;
892 }
893 // assuming an ordered list, so looking in the middle first
894 for (int i = (int) stat; i >= 0; i--) {
895 if (randcolors[i][0] == stat) {
896 stat = randcolors[i][RAND(0, RandRows - 1)];
897 return;
898 }
899 }
900 for (unsigned int i = stat + 1; i < RandColor; i++) {
901 if (randcolors[i][0] == stat) {
902 stat = randcolors[i][RAND(0, RandRows - 1)];
903 return;
904 }
905 }
906 }
907
ReadDialog(Actor * act)908 void CREImporter::ReadDialog(Actor *act)
909 {
910 ieResRef Dialog;
911
912 str->ReadResRef(Dialog);
913 //Hacking NONE to no error
914 if (strnicmp(Dialog,"NONE",8) == 0) {
915 Dialog[0]=0;
916 }
917 act->SetDialog(Dialog);
918 }
919
GetActor(unsigned char is_in_party)920 Actor* CREImporter::GetActor(unsigned char is_in_party)
921 {
922 if (!str)
923 return NULL;
924 Actor* act = new Actor();
925 if (!act)
926 return NULL;
927 act->InParty = is_in_party;
928 str->ReadDword( &act->LongStrRef );
929 //Beetle name in IWD needs the allow zero flag
930 char* poi = core->GetCString( act->LongStrRef, IE_STR_ALLOW_ZERO );
931 act->SetName( poi, 1 ); //setting longname
932 free( poi );
933 str->ReadDword( &act->ShortStrRef );
934 if (act->ShortStrRef == (ieStrRef) -1) {
935 act->ShortStrRef = act->LongStrRef;
936 }
937 poi = core->GetCString( act->ShortStrRef );
938 act->SetName( poi, 2 ); //setting shortname (for tooltips)
939 free( poi );
940 act->BaseStats[IE_VISUALRANGE] = VOODOO_VISUAL_RANGE; // not stored anywhere
941 act->BaseStats[IE_DIALOGRANGE] = VOODOO_DIALOG_RANGE;
942 str->ReadDword( &act->BaseStats[IE_MC_FLAGS] );
943 str->ReadDword( &act->BaseStats[IE_XPVALUE] );
944 str->ReadDword( &act->BaseStats[IE_XP] );
945 str->ReadDword( &act->BaseStats[IE_GOLD] );
946 str->ReadDword( &act->BaseStats[IE_STATE_ID] );
947 ieWord tmp;
948 ieWordSigned tmps;
949 str->ReadWordSigned( &tmps );
950 act->BaseStats[IE_HITPOINTS]=(ieDwordSigned)tmps;
951 if (tmps <= 0 && ((ieDwordSigned) act->BaseStats[IE_XPVALUE]) < 0) {
952 act->BaseStats[IE_STATE_ID] |= STATE_DEAD;
953 }
954 str->ReadWord( &tmp );
955 act->BaseStats[IE_MAXHITPOINTS]=tmp;
956 str->ReadDword( &act->BaseStats[IE_ANIMATION_ID] );//animID is a dword
957 ieByte tmp2[7];
958 str->Read( tmp2, 7);
959 for (int i=0;i<7;i++) {
960 ieDword t = tmp2[i];
961 // apply RANDCOLR.2DA transformation
962 SetupColor(t);
963 t |= t << 8;
964 t |= t << 16;
965 act->BaseStats[IE_COLORS+i]=t;
966 }
967
968 str->Read( &TotSCEFF, 1 );
969 if (CREVersion==IE_CRE_V1_0 && TotSCEFF) {
970 CREVersion = IE_CRE_V1_1;
971 }
972 // saving in original version requires the original version
973 // otherwise it is set to 0 at construction time
974 if (core->SaveAsOriginal) {
975 act->version = CREVersion;
976 }
977 str->ReadResRef( act->SmallPortrait );
978 if (act->SmallPortrait[0]==0) {
979 strncpy(act->SmallPortrait, "NONE", 8);
980 }
981 str->ReadResRef( act->LargePortrait );
982 if (act->LargePortrait[0]==0) {
983 strncpy(act->LargePortrait, "NONE", 8);
984 }
985
986 unsigned int Inventory_Size;
987
988 switch(CREVersion) {
989 case IE_CRE_GEMRB:
990 Inventory_Size = GetActorGemRB(act);
991 break;
992 case IE_CRE_V1_2:
993 Inventory_Size=46;
994 GetActorPST(act);
995 break;
996 case IE_CRE_V1_1: //bg2 (fake version)
997 case IE_CRE_V1_0: //bg1 too
998 Inventory_Size=38;
999 GetActorBG(act);
1000 break;
1001 case IE_CRE_V2_2:
1002 Inventory_Size=50;
1003 GetActorIWD2(act);
1004 break;
1005 case IE_CRE_V9_0:
1006 Inventory_Size=38;
1007 GetActorIWD1(act);
1008 break;
1009 default:
1010 Log(ERROR, "CREImporter", "Unknown creature signature: %d\n", CREVersion);
1011 delete act;
1012 return NULL;
1013 }
1014
1015 // Read saved effects
1016 if (core->IsAvailable(IE_EFF_CLASS_ID) ) {
1017 ReadEffects( act );
1018 } else {
1019 Log(ERROR, "CREImporter", "Effect importer is unavailable!");
1020 }
1021 // Reading inventory, spellbook, etc
1022 ReadInventory( act, Inventory_Size );
1023
1024 if (IsCharacter) {
1025 ReadChrHeader(act);
1026 }
1027
1028 act->InitStatsOnLoad();
1029
1030 return act;
1031 }
1032
GetActorPST(Actor * act)1033 void CREImporter::GetActorPST(Actor *act)
1034 {
1035 int i;
1036 ieByte tmpByte;
1037 ieWord tmpWord;
1038
1039 str->Read( &tmpByte, 1 );
1040 act->BaseStats[IE_REPUTATION]=tmpByte;
1041 str->Read( &tmpByte, 1 );
1042 act->BaseStats[IE_HIDEINSHADOWS]=tmpByte;
1043 str->ReadWord( &tmpWord );
1044 //skipping a word
1045 str->ReadWord( &tmpWord );
1046 act->AC.SetNatural((ieWordSigned) tmpWord);
1047 str->ReadWord( &tmpWord );
1048 act->BaseStats[IE_ACCRUSHINGMOD]=(ieWordSigned) tmpWord;
1049 str->ReadWord( &tmpWord );
1050 act->BaseStats[IE_ACMISSILEMOD]=(ieWordSigned) tmpWord;
1051 str->ReadWord( &tmpWord );
1052 act->BaseStats[IE_ACPIERCINGMOD]=(ieWordSigned) tmpWord;
1053 str->ReadWord( &tmpWord );
1054 act->BaseStats[IE_ACSLASHINGMOD]=(ieWordSigned) tmpWord;
1055 str->Read( &tmpByte, 1 );
1056 act->ToHit.SetBase((ieByteSigned) tmpByte);
1057 str->Read( &tmpByte, 1 );
1058 tmpByte = tmpByte * 2;
1059 if (tmpByte>10) tmpByte-=11;
1060 act->BaseStats[IE_NUMBEROFATTACKS]=tmpByte;
1061 str->Read( &tmpByte, 1 );
1062 act->BaseStats[IE_SAVEVSDEATH]=(ieByteSigned) tmpByte;
1063 str->Read( &tmpByte, 1 );
1064 act->BaseStats[IE_SAVEVSWANDS]=(ieByteSigned) tmpByte;
1065 str->Read( &tmpByte, 1 );
1066 act->BaseStats[IE_SAVEVSPOLY]=(ieByteSigned) tmpByte;
1067 str->Read( &tmpByte, 1 );
1068 act->BaseStats[IE_SAVEVSBREATH]=(ieByteSigned) tmpByte;
1069 str->Read( &tmpByte, 1 );
1070 act->BaseStats[IE_SAVEVSSPELL]=(ieByteSigned) tmpByte;
1071 str->Read( &tmpByte, 1 );
1072 act->BaseStats[IE_RESISTFIRE]=(ieByteSigned) tmpByte;
1073 str->Read( &tmpByte, 1 );
1074 act->BaseStats[IE_RESISTCOLD]=(ieByteSigned) tmpByte;
1075 str->Read( &tmpByte, 1 );
1076 act->BaseStats[IE_RESISTELECTRICITY]=(ieByteSigned) tmpByte;
1077 str->Read( &tmpByte, 1 );
1078 act->BaseStats[IE_RESISTACID]=(ieByteSigned) tmpByte;
1079 str->Read( &tmpByte, 1 );
1080 act->BaseStats[IE_RESISTMAGIC]=(ieByteSigned) tmpByte;
1081 str->Read( &tmpByte, 1 );
1082 act->BaseStats[IE_RESISTMAGICFIRE]=(ieByteSigned) tmpByte;
1083 str->Read( &tmpByte, 1 );
1084 act->BaseStats[IE_RESISTMAGICCOLD]=(ieByteSigned) tmpByte;
1085 str->Read( &tmpByte, 1 );
1086 act->BaseStats[IE_RESISTSLASHING]=(ieByteSigned) tmpByte;
1087 str->Read( &tmpByte, 1 );
1088 act->BaseStats[IE_RESISTCRUSHING]=(ieByteSigned) tmpByte;
1089 str->Read( &tmpByte, 1 );
1090 act->BaseStats[IE_RESISTPIERCING]=(ieByteSigned) tmpByte;
1091 str->Read( &tmpByte, 1 );
1092 act->BaseStats[IE_RESISTMISSILE]=(ieByteSigned) tmpByte;
1093 //this is used for unused prof points count
1094 str->Read( &tmpByte, 1 );
1095 act->BaseStats[IE_FREESLOTS]=tmpByte; //using another field than usually
1096 str->Read( &tmpByte, 1 );
1097 act->BaseStats[IE_SETTRAPS]=tmpByte; //this is unused in pst
1098 str->Read( &tmpByte, 1 );
1099 act->BaseStats[IE_LORE]=tmpByte;
1100 str->Read( &tmpByte, 1 );
1101 act->BaseStats[IE_LOCKPICKING]=tmpByte;
1102 str->Read( &tmpByte, 1 );
1103 act->BaseStats[IE_STEALTH]=tmpByte;
1104 str->Read( &tmpByte, 1 );
1105 act->BaseStats[IE_TRAPS]=tmpByte;
1106 str->Read( &tmpByte, 1 );
1107 act->BaseStats[IE_PICKPOCKET]=tmpByte;
1108 str->Read( &tmpByte, 1 );
1109 act->BaseStats[IE_FATIGUE]=tmpByte;
1110 str->Read( &tmpByte, 1 );
1111 act->BaseStats[IE_INTOXICATION]=tmpByte;
1112 str->Read( &tmpByte, 1 );
1113 act->BaseStats[IE_LUCK]=(ieByteSigned) tmpByte;
1114 //last byte is actually an undead level (according to IE dev info)
1115 for (i=0;i<21;i++) {
1116 str->Read( &tmpByte, 1 );
1117 act->BaseStats[IE_PROFICIENCYBASTARDSWORD+i]=tmpByte;
1118 }
1119 str->Read( &tmpByte, 1 );
1120 act->BaseStats[IE_TRACKING]=tmpByte;
1121 //scriptname of tracked creature (according to IE dev info)
1122 str->Seek( 32, GEM_CURRENT_POS );
1123 for (i=0; i<VCONST_COUNT; i++) {
1124 str->ReadDword( &act->StrRefs[i] );
1125 }
1126 str->Read( &tmpByte, 1 );
1127 act->BaseStats[IE_LEVEL]=tmpByte;
1128 str->Read( &tmpByte, 1 );
1129 act->BaseStats[IE_LEVEL2]=tmpByte;
1130 str->Read( &tmpByte, 1 );
1131 act->BaseStats[IE_LEVEL3]=tmpByte;
1132 //this is rumoured to be IE_SEX, but we use the gender field for this
1133 str->Read( &tmpByte, 1 );
1134 //skipping a byte
1135 str->Read( &tmpByte, 1 );
1136 act->BaseStats[IE_STR]=tmpByte;
1137 str->Read( &tmpByte, 1 );
1138 act->BaseStats[IE_STREXTRA]=tmpByte;
1139 str->Read( &tmpByte, 1 );
1140 act->BaseStats[IE_INT]=tmpByte;
1141 str->Read( &tmpByte, 1 );
1142 act->BaseStats[IE_WIS]=tmpByte;
1143 str->Read( &tmpByte, 1 );
1144 act->BaseStats[IE_DEX]=tmpByte;
1145 str->Read( &tmpByte, 1 );
1146 act->BaseStats[IE_CON]=tmpByte;
1147 str->Read( &tmpByte, 1 );
1148 act->BaseStats[IE_CHR]=tmpByte;
1149 str->Read( &tmpByte, 1 );
1150 act->BaseStats[IE_MORALE]=tmpByte;
1151 str->Read( &tmpByte, 1 );
1152 act->BaseStats[IE_MORALEBREAK]=tmpByte;
1153 str->Read( &tmpByte, 1 );
1154 act->BaseStats[IE_HATEDRACE]=tmpByte;
1155 str->Read( &tmpByte, 1 );
1156 act->BaseStats[IE_MORALERECOVERYTIME]=tmpByte;
1157 str->Read( &tmpByte, 1 );
1158 //skipping a byte
1159 str->ReadDword( &act->BaseStats[IE_KIT] );
1160 ReadScript(act, SCR_OVERRIDE);
1161 ReadScript(act, SCR_CLASS);
1162 ReadScript(act, SCR_RACE);
1163 ReadScript(act, SCR_GENERAL);
1164 ReadScript(act, SCR_DEFAULT);
1165
1166 str->Seek( 36, GEM_CURRENT_POS );
1167 //the overlays are not fully decoded yet
1168 //they are a kind of effect block (like our vvclist)
1169 str->ReadDword( &OverlayOffset );
1170 str->ReadDword( &OverlayMemorySize );
1171 str->ReadDword( &act->BaseStats[IE_XP_MAGE] ); // Exp for secondary class
1172 str->ReadDword( &act->BaseStats[IE_XP_THIEF] ); // Exp for tertiary class
1173 for (i = 0; i<10; i++) {
1174 str->ReadWord( &tmpWord );
1175 act->BaseStats[IE_INTERNAL_0+i]=tmpWord;
1176 }
1177 //good, law, lady, murder
1178 for (i=0;i<4;i++) {
1179 str->Read( &tmpByte, 1);
1180 act->DeathCounters[i]=(ieByteSigned) tmpByte;
1181 }
1182 ieVariable KillVar; //use this as needed
1183 str->Read(KillVar,32);
1184 KillVar[32]=0;
1185 str->Seek( 3, GEM_CURRENT_POS ); // dialog radius, feet circle size???
1186
1187 str->Read( &tmpByte, 1 );
1188
1189 str->ReadDword( &act->AppearanceFlags );
1190
1191 // just overwrite the bg1 color stat range, since it's not used in pst
1192 for (i = 0; i < 7; i++) {
1193 str->ReadWord( &tmpWord );
1194 act->BaseStats[IE_COLORS+i] = tmpWord;
1195 }
1196 act->BaseStats[IE_COLORCOUNT] = tmpByte;
1197 str->Read(act->pstColorBytes, 10); // color location in IESDP, sort of a palette index and flags
1198 str->Seek(21, GEM_CURRENT_POS);
1199 str->Read( &tmpByte, 1 );
1200 act->BaseStats[IE_SPECIES]=tmpByte; // offset: 0x311
1201 str->Read( &tmpByte, 1 );
1202 act->BaseStats[IE_TEAM]=tmpByte;
1203 str->Read( &tmpByte, 1 );
1204 act->BaseStats[IE_FACTION]=tmpByte;
1205 str->Read( &tmpByte, 1 );
1206 act->BaseStats[IE_EA]=tmpByte;
1207 str->Read( &tmpByte, 1 );
1208 act->BaseStats[IE_GENERAL]=tmpByte;
1209 str->Read( &tmpByte, 1 );
1210 act->BaseStats[IE_RACE]=tmpByte;
1211 str->Read( &tmpByte, 1 );
1212 act->BaseStats[IE_CLASS]=tmpByte;
1213 str->Read( &tmpByte, 1 );
1214 act->BaseStats[IE_SPECIFIC]=tmpByte;
1215 str->Read( &tmpByte, 1 );
1216 act->BaseStats[IE_SEX]=tmpByte;
1217 str->Seek( 5, GEM_CURRENT_POS );
1218 str->Read( &tmpByte, 1 );
1219 act->BaseStats[IE_ALIGNMENT]=tmpByte;
1220 str->Seek( 4, GEM_CURRENT_POS );
1221 ieVariable scriptname;
1222 str->Read( scriptname, 32);
1223 scriptname[32]=0;
1224 act->SetScriptName(scriptname);
1225 strnspccpy(act->KillVar, KillVar, 32);
1226 memset(act->IncKillVar, 0, 32);
1227
1228 str->ReadDword( &KnownSpellsOffset );
1229 str->ReadDword( &KnownSpellsCount );
1230 str->ReadDword( &SpellMemorizationOffset );
1231 str->ReadDword( &SpellMemorizationCount );
1232 str->ReadDword( &MemorizedSpellsOffset );
1233 str->ReadDword( &MemorizedSpellsCount );
1234
1235 str->ReadDword( &ItemSlotsOffset );
1236 str->ReadDword( &ItemsOffset );
1237 str->ReadDword( &ItemsCount );
1238 str->ReadDword( &EffectsOffset );
1239 str->ReadDword( &EffectsCount ); //also variables
1240
1241 ReadDialog(act);
1242 }
1243
ReadInventory(Actor * act,unsigned int Inventory_Size)1244 void CREImporter::ReadInventory(Actor *act, unsigned int Inventory_Size)
1245 {
1246 ieWord *indices = (ieWord *) calloc(Inventory_Size, sizeof(ieWord));
1247 ieWordSigned eqslot;
1248 ieWord eqheader;
1249
1250 act->inventory.SetSlotCount(Inventory_Size+1);
1251 str->Seek( ItemSlotsOffset+CREOffset, GEM_STREAM_START );
1252
1253 //first read the indices
1254 for (unsigned int i = 0; i < Inventory_Size; i++) {
1255 str->ReadWord(indices+i);
1256 }
1257 //this word contains the equipping info (which slot is selected)
1258 // 0,1,2,3 - weapon slots
1259 // 1000 - fist
1260 // -24,-23,-22,-21 - quiver
1261 // -1 is one of the plain inventory slots, but creatures like belhif.cre have it set as the equipped slot; see below
1262 //the equipping effects are delayed until the actor gets an area
1263 str->ReadWordSigned(&eqslot);
1264 //the equipped slot's selected ability is stored here
1265 str->ReadWord(&eqheader);
1266 act->inventory.SetEquipped(eqslot, eqheader);
1267
1268 //read the item entries based on the previously read indices
1269 //an item entry may be read multiple times if the indices are repeating
1270 for (unsigned int i = 0; i < Inventory_Size;) {
1271 //the index was intentionally increased here, the fist slot isn't saved
1272 ieWord index = indices[i++];
1273 if (index != 0xffff) {
1274 if (index>=ItemsCount) {
1275 Log(ERROR, "CREImporter", "Invalid item index (%d) in creature!", index);
1276 continue;
1277 }
1278 //20 is the size of CREItem on disc (8+2+3x2+4)
1279 str->Seek( ItemsOffset+index*20 + CREOffset, GEM_STREAM_START );
1280 //the core allocates this item data
1281 CREItem *item = core->ReadItem(str);
1282 int Slot = core->QuerySlot(i);
1283 if (item) {
1284 act->inventory.SetSlotItem(item, Slot);
1285 } else {
1286 Log(ERROR, "CREImporter", "Invalid item index (%d) in creature!", index);
1287 }
1288 }
1289 }
1290
1291 // now that we have all items, check if we need to jump through hoops to get a proper equipped slot
1292 // move to fx_summon_creature2 if it turns out something else relies on having nothing equipped
1293 if (eqslot == -1) {
1294 act->inventory.SetEquipped(0, eqheader); // just reset Equipped, so EquipBestWeapon does its job
1295 act->inventory.EquipBestWeapon(EQUIP_MELEE);
1296 }
1297
1298 free (indices);
1299
1300 // Reading spellbook
1301 CREKnownSpell **known_spells=(CREKnownSpell **) calloc(KnownSpellsCount, sizeof(CREKnownSpell *) );
1302 CREMemorizedSpell **memorized_spells=(CREMemorizedSpell **) calloc(MemorizedSpellsCount, sizeof(CREMemorizedSpell *) );
1303
1304 str->Seek( KnownSpellsOffset+CREOffset, GEM_STREAM_START );
1305 for (unsigned int i = 0; i < KnownSpellsCount; i++) {
1306 known_spells[i]=GetKnownSpell();
1307 }
1308
1309 str->Seek( MemorizedSpellsOffset+CREOffset, GEM_STREAM_START );
1310 for (unsigned int i = 0; i < MemorizedSpellsCount; i++) {
1311 memorized_spells[i]=GetMemorizedSpell();
1312 }
1313
1314 str->Seek( SpellMemorizationOffset+CREOffset, GEM_STREAM_START );
1315 for (unsigned int i = 0; i < SpellMemorizationCount; i++) {
1316 CRESpellMemorization* sm = GetSpellMemorization(act);
1317
1318 unsigned int j = KnownSpellsCount;
1319 while(j--) {
1320 CREKnownSpell* spl = known_spells[j];
1321 if (!spl) {
1322 continue;
1323 }
1324 if ((spl->Type == sm->Type) && (spl->Level == sm->Level)) {
1325 sm->known_spells.push_back( spl );
1326 known_spells[j] = NULL;
1327 continue;
1328 }
1329 }
1330 for (j = 0; j < MemorizedCount; j++) {
1331 unsigned int k = MemorizedIndex + j;
1332 assert(k < MemorizedSpellsCount);
1333 if (memorized_spells[k]) {
1334 sm->memorized_spells.push_back( memorized_spells[k]);
1335 memorized_spells[k] = NULL;
1336 continue;
1337 }
1338 Log(WARNING, "CREImporter", "Duplicate memorized spell(%d) in creature!", k);
1339 }
1340 }
1341
1342 unsigned int i = KnownSpellsCount;
1343 while(i--) {
1344 if (known_spells[i]) {
1345 Log(WARNING, "CREImporter", "Dangling spell in creature: %s!",
1346 known_spells[i]->SpellResRef);
1347 delete known_spells[i];
1348 }
1349 }
1350 free(known_spells);
1351
1352 i=MemorizedSpellsCount;
1353 while(i--) {
1354 if (memorized_spells[i]) {
1355 Log(WARNING, "CREImporter", "Dangling spell in creature: %s!",
1356 memorized_spells[i]->SpellResRef);
1357 delete memorized_spells[i];
1358 }
1359 }
1360 free(memorized_spells);
1361 }
1362
ReadEffects(Actor * act)1363 void CREImporter::ReadEffects(Actor *act)
1364 {
1365 unsigned int i;
1366
1367 str->Seek( EffectsOffset+CREOffset, GEM_STREAM_START );
1368
1369 for (i = 0; i < EffectsCount; i++) {
1370 Effect fx;
1371 GetEffect( &fx );
1372 // NOTE: AddEffect() allocates a new effect
1373 act->fxqueue.AddEffect( &fx ); // FIXME: don't reroll dice, time, etc!!
1374 }
1375 }
1376
GetEffect(Effect * fx)1377 void CREImporter::GetEffect(Effect *fx)
1378 {
1379 PluginHolder<EffectMgr> eM(IE_EFF_CLASS_ID);
1380
1381 eM->Open( str, false );
1382 if (TotSCEFF) {
1383 eM->GetEffectV20( fx );
1384 } else {
1385 eM->GetEffectV1( fx );
1386 }
1387 }
1388
GetActorGemRB(Actor * act)1389 ieDword CREImporter::GetActorGemRB(Actor *act)
1390 {
1391 ieByte tmpByte;
1392 ieWord tmpWord;
1393
1394 str->Read( &tmpByte, 1 );
1395 act->BaseStats[IE_REPUTATION]=tmpByte;
1396 str->Read( &tmpByte, 1 );
1397 act->BaseStats[IE_HIDEINSHADOWS]=tmpByte;
1398 //skipping a word( useful for something)
1399 str->ReadWord( &tmpWord );
1400 str->ReadWord( &tmpWord );
1401 act->AC.SetNatural((ieWordSigned) tmpWord);
1402 str->ReadWord( &tmpWord );
1403 act->BaseStats[IE_ACCRUSHINGMOD]=(ieWordSigned) tmpWord;
1404 str->ReadWord( &tmpWord );
1405 act->BaseStats[IE_ACMISSILEMOD]=(ieWordSigned) tmpWord;
1406 str->ReadWord( &tmpWord );
1407 act->BaseStats[IE_ACPIERCINGMOD]=(ieWordSigned) tmpWord;
1408 str->ReadWord( &tmpWord );
1409 act->BaseStats[IE_ACSLASHINGMOD]=(ieWordSigned) tmpWord;
1410 str->Read( &tmpByte, 1 );
1411 act->ToHit.SetBase((ieByteSigned) tmpByte);
1412 str->Read( &tmpByte, 1 );
1413 act->BaseStats[IE_NUMBEROFATTACKS]=tmpByte;
1414 str->Read( &tmpByte, 1 );
1415 act->BaseStats[IE_SAVEVSDEATH]=(ieByteSigned) tmpByte;
1416 str->Read( &tmpByte, 1 );
1417 act->BaseStats[IE_SAVEVSWANDS]=(ieByteSigned) tmpByte;
1418 str->Read( &tmpByte, 1 );
1419 act->BaseStats[IE_SAVEVSPOLY]=(ieByteSigned) tmpByte;
1420 str->Read( &tmpByte, 1 );
1421 act->BaseStats[IE_SAVEVSBREATH]=(ieByteSigned) tmpByte;
1422 str->Read( &tmpByte, 1 );
1423 act->BaseStats[IE_SAVEVSSPELL]=(ieByteSigned) tmpByte;
1424 str->Read( &tmpByte, 1 );
1425 act->BaseStats[IE_RESISTFIRE]=(ieByteSigned) tmpByte;
1426 str->Read( &tmpByte, 1 );
1427 act->BaseStats[IE_RESISTCOLD]=(ieByteSigned) tmpByte;
1428 str->Read( &tmpByte, 1 );
1429 act->BaseStats[IE_RESISTELECTRICITY]=(ieByteSigned) tmpByte;
1430 str->Read( &tmpByte, 1 );
1431 act->BaseStats[IE_RESISTACID]=(ieByteSigned) tmpByte;
1432 str->Read( &tmpByte, 1 );
1433 act->BaseStats[IE_RESISTMAGIC]=(ieByteSigned) tmpByte;
1434 str->Read( &tmpByte, 1 );
1435 act->BaseStats[IE_RESISTMAGICFIRE]=(ieByteSigned) tmpByte;
1436 str->Read( &tmpByte, 1 );
1437 act->BaseStats[IE_RESISTMAGICCOLD]=(ieByteSigned) tmpByte;
1438 str->Read( &tmpByte, 1 );
1439 act->BaseStats[IE_RESISTSLASHING]=(ieByteSigned) tmpByte;
1440 str->Read( &tmpByte, 1 );
1441 act->BaseStats[IE_RESISTCRUSHING]=(ieByteSigned) tmpByte;
1442 str->Read( &tmpByte, 1 );
1443 act->BaseStats[IE_RESISTPIERCING]=(ieByteSigned) tmpByte;
1444 str->Read( &tmpByte, 1 );
1445 act->BaseStats[IE_RESISTMISSILE]=(ieByteSigned) tmpByte;
1446 str->Read( &tmpByte, 1 );
1447 act->BaseStats[IE_DETECTILLUSIONS]=tmpByte;
1448 str->Read( &tmpByte, 1 );
1449 act->BaseStats[IE_SETTRAPS]=tmpByte;
1450 str->Read( &tmpByte, 1 );
1451 act->BaseStats[IE_LORE]=tmpByte;
1452 str->Read( &tmpByte, 1 );
1453 act->BaseStats[IE_LOCKPICKING]=tmpByte;
1454 str->Read( &tmpByte, 1 );
1455 act->BaseStats[IE_STEALTH]=tmpByte;
1456 str->Read( &tmpByte, 1 );
1457 act->BaseStats[IE_TRAPS]=tmpByte;
1458 str->Read( &tmpByte, 1 );
1459 act->BaseStats[IE_PICKPOCKET]=tmpByte;
1460 str->Read( &tmpByte, 1 );
1461 act->BaseStats[IE_FATIGUE]=tmpByte;
1462 str->Read( &tmpByte, 1 );
1463 act->BaseStats[IE_INTOXICATION]=tmpByte;
1464 str->Read( &tmpByte, 1 );
1465 act->BaseStats[IE_LUCK]=(ieByteSigned) tmpByte;
1466 str->Read( &tmpByte, 1 );
1467 //these could be used to save iwd2 skills
1468 //TODO: gemrb format
1469 act->BaseStats[IE_TRACKING]=tmpByte;
1470 for (int i=0; i<VCONST_COUNT; i++) {
1471 str->ReadDword( &act->StrRefs[i] );
1472 }
1473 return 0;
1474 }
1475
GetActorBG(Actor * act)1476 void CREImporter::GetActorBG(Actor *act)
1477 {
1478 int i;
1479 ieByte tmpByte;
1480 ieWord tmpWord;
1481
1482 str->Read( &tmpByte, 1 );
1483 act->BaseStats[IE_REPUTATION]=tmpByte;
1484 str->Read( &tmpByte, 1 );
1485 act->BaseStats[IE_HIDEINSHADOWS]=tmpByte;
1486 str->ReadWord( &tmpWord );
1487 //skipping a word
1488 str->ReadWord( &tmpWord );
1489 act->AC.SetNatural((ieWordSigned) tmpWord);
1490 str->ReadWord( &tmpWord );
1491 act->BaseStats[IE_ACCRUSHINGMOD]=(ieWordSigned) tmpWord;
1492 str->ReadWord( &tmpWord );
1493 act->BaseStats[IE_ACMISSILEMOD]=(ieWordSigned) tmpWord;
1494 str->ReadWord( &tmpWord );
1495 act->BaseStats[IE_ACPIERCINGMOD]=(ieWordSigned) tmpWord;
1496 str->ReadWord( &tmpWord );
1497 act->BaseStats[IE_ACSLASHINGMOD]=(ieWordSigned) tmpWord;
1498 str->Read( &tmpByte, 1 );
1499 act->ToHit.SetBase((ieByteSigned) tmpByte);
1500 str->Read( &tmpByte, 1 );
1501 tmpWord = tmpByte * 2;
1502 if (tmpWord>10) tmpWord-=11;
1503 act->BaseStats[IE_NUMBEROFATTACKS]=(ieByte) tmpWord;
1504 str->Read( &tmpByte, 1 );
1505 act->BaseStats[IE_SAVEVSDEATH]=(ieByteSigned) tmpByte;
1506 str->Read( &tmpByte, 1 );
1507 act->BaseStats[IE_SAVEVSWANDS]=(ieByteSigned) tmpByte;
1508 str->Read( &tmpByte, 1 );
1509 act->BaseStats[IE_SAVEVSPOLY]=(ieByteSigned) tmpByte;
1510 str->Read( &tmpByte, 1 );
1511 act->BaseStats[IE_SAVEVSBREATH]=(ieByteSigned) tmpByte;
1512 str->Read( &tmpByte, 1 );
1513 act->BaseStats[IE_SAVEVSSPELL]=(ieByteSigned) tmpByte;
1514 str->Read( &tmpByte, 1 );
1515 act->BaseStats[IE_RESISTFIRE]=(ieByteSigned) tmpByte;
1516 str->Read( &tmpByte, 1 );
1517 act->BaseStats[IE_RESISTCOLD]=(ieByteSigned) tmpByte;
1518 str->Read( &tmpByte, 1 );
1519 act->BaseStats[IE_RESISTELECTRICITY]=(ieByteSigned) tmpByte;
1520 str->Read( &tmpByte, 1 );
1521 act->BaseStats[IE_RESISTACID]=(ieByteSigned) tmpByte;
1522 str->Read( &tmpByte, 1 );
1523 act->BaseStats[IE_RESISTMAGIC]=(ieByteSigned) tmpByte;
1524 str->Read( &tmpByte, 1 );
1525 act->BaseStats[IE_RESISTMAGICFIRE]=(ieByteSigned) tmpByte;
1526 str->Read( &tmpByte, 1 );
1527 act->BaseStats[IE_RESISTMAGICCOLD]=(ieByteSigned) tmpByte;
1528 str->Read( &tmpByte, 1 );
1529 act->BaseStats[IE_RESISTSLASHING]=(ieByteSigned) tmpByte;
1530 str->Read( &tmpByte, 1 );
1531 act->BaseStats[IE_RESISTCRUSHING]=(ieByteSigned) tmpByte;
1532 str->Read( &tmpByte, 1 );
1533 act->BaseStats[IE_RESISTPIERCING]=(ieByteSigned) tmpByte;
1534 str->Read( &tmpByte, 1 );
1535 act->BaseStats[IE_RESISTMISSILE]=(ieByteSigned) tmpByte;
1536 str->Read( &tmpByte, 1 );
1537 act->BaseStats[IE_DETECTILLUSIONS]=tmpByte;
1538 str->Read( &tmpByte, 1 );
1539 act->BaseStats[IE_SETTRAPS]=tmpByte;
1540 str->Read( &tmpByte, 1 );
1541 act->BaseStats[IE_LORE]=tmpByte;
1542 str->Read( &tmpByte, 1 );
1543 act->BaseStats[IE_LOCKPICKING]=tmpByte;
1544 str->Read( &tmpByte, 1 );
1545 act->BaseStats[IE_STEALTH]=tmpByte;
1546 str->Read( &tmpByte, 1 );
1547 act->BaseStats[IE_TRAPS]=tmpByte;
1548 str->Read( &tmpByte, 1 );
1549 act->BaseStats[IE_PICKPOCKET]=tmpByte;
1550 str->Read( &tmpByte, 1 );
1551 act->BaseStats[IE_FATIGUE]=tmpByte;
1552 str->Read( &tmpByte, 1 );
1553 act->BaseStats[IE_INTOXICATION]=tmpByte;
1554 str->Read( &tmpByte, 1 );
1555 act->BaseStats[IE_LUCK]=(ieByteSigned) tmpByte;
1556 for (i=0;i<21;i++) {
1557 str->Read( &tmpByte, 1 );
1558 act->BaseStats[IE_PROFICIENCYBASTARDSWORD+i]=tmpByte;
1559 }
1560
1561 str->Read( &tmpByte, 1 );
1562 act->BaseStats[IE_TRACKING]=tmpByte;
1563 str->Seek( 32, GEM_CURRENT_POS );
1564 for (i=0; i<VCONST_COUNT; i++) {
1565 str->ReadDword( &act->StrRefs[i] );
1566 }
1567 str->Read( &tmpByte, 1 );
1568 act->BaseStats[IE_LEVEL]=tmpByte;
1569 str->Read( &tmpByte, 1 );
1570 act->BaseStats[IE_LEVEL2]=tmpByte;
1571 str->Read( &tmpByte, 1 );
1572 act->BaseStats[IE_LEVEL3]=tmpByte;
1573 //this is rumoured to be IE_SEX, but we use the gender field for this
1574 str->Read( &tmpByte, 1);
1575 //skipping a byte
1576 str->Read( &tmpByte, 1);
1577 act->BaseStats[IE_STR]=tmpByte;
1578 str->Read( &tmpByte, 1);
1579 act->BaseStats[IE_STREXTRA]=tmpByte;
1580 str->Read( &tmpByte, 1);
1581 act->BaseStats[IE_INT]=tmpByte;
1582 str->Read( &tmpByte, 1);
1583 act->BaseStats[IE_WIS]=tmpByte;
1584 str->Read( &tmpByte, 1);
1585 act->BaseStats[IE_DEX]=tmpByte;
1586 str->Read( &tmpByte, 1);
1587 act->BaseStats[IE_CON]=tmpByte;
1588 str->Read( &tmpByte, 1);
1589 act->BaseStats[IE_CHR]=tmpByte;
1590 str->Read( &tmpByte, 1);
1591 act->BaseStats[IE_MORALE]=tmpByte;
1592 str->Read( &tmpByte, 1);
1593 act->BaseStats[IE_MORALEBREAK]=tmpByte;
1594 str->Read( &tmpByte, 1);
1595 act->BaseStats[IE_HATEDRACE]=tmpByte;
1596 str->Read( &tmpByte, 1);
1597 act->BaseStats[IE_MORALERECOVERYTIME]=tmpByte;
1598 str->Read( &tmpByte, 1);
1599 //skipping a byte
1600 str->ReadDword( &act->BaseStats[IE_KIT] );
1601 act->BaseStats[IE_KIT] = ((act->BaseStats[IE_KIT] & 0xffff) << 16) +
1602 ((act->BaseStats[IE_KIT] & 0xffff0000) >> 16);
1603 ReadScript(act, SCR_OVERRIDE);
1604 ReadScript(act, SCR_CLASS);
1605 ReadScript(act, SCR_RACE);
1606 ReadScript(act, SCR_GENERAL);
1607 ReadScript(act, SCR_DEFAULT);
1608
1609 str->Read( &tmpByte, 1);
1610 act->BaseStats[IE_EA]=tmpByte;
1611 str->Read( &tmpByte, 1);
1612 act->BaseStats[IE_GENERAL]=tmpByte;
1613 str->Read( &tmpByte, 1);
1614 act->BaseStats[IE_RACE]=tmpByte;
1615 str->Read( &tmpByte, 1);
1616 act->BaseStats[IE_CLASS]=tmpByte;
1617 str->Read( &tmpByte, 1);
1618 act->BaseStats[IE_SPECIFIC]=tmpByte;
1619 str->Read( &tmpByte, 1);
1620 act->BaseStats[IE_SEX]=tmpByte;
1621 str->Seek( 5, GEM_CURRENT_POS );
1622 str->Read( &tmpByte, 1);
1623 act->BaseStats[IE_ALIGNMENT]=tmpByte;
1624 str->Seek( 4, GEM_CURRENT_POS );
1625 ieVariable scriptname;
1626 str->Read( scriptname, 32);
1627 scriptname[32]=0;
1628 act->SetScriptName(scriptname);
1629 memset(act->KillVar, 0, 32);
1630 memset(act->IncKillVar, 0, 32);
1631
1632 str->ReadDword( &KnownSpellsOffset );
1633 str->ReadDword( &KnownSpellsCount );
1634 str->ReadDword( &SpellMemorizationOffset );
1635 str->ReadDword( &SpellMemorizationCount );
1636 str->ReadDword( &MemorizedSpellsOffset );
1637 str->ReadDword( &MemorizedSpellsCount );
1638
1639 str->ReadDword( &ItemSlotsOffset );
1640 str->ReadDword( &ItemsOffset );
1641 str->ReadDword( &ItemsCount );
1642 str->ReadDword( &EffectsOffset );
1643 str->ReadDword( &EffectsCount );
1644
1645 ReadDialog(act);
1646 }
1647
GetIWD2Spellpage(Actor * act,ieIWD2SpellType type,int level,int count)1648 void CREImporter::GetIWD2Spellpage(Actor *act, ieIWD2SpellType type, int level, int count)
1649 {
1650 ieDword spellindex;
1651 ieDword totalcount;
1652 ieDword memocount;
1653 ieDword tmpDword;
1654
1655 int check = 0, i = count;
1656 CRESpellMemorization* sm = act->spellbook.GetSpellMemorization(type, level);
1657 assert(sm && sm->SlotCount == 0 && sm->SlotCountWithBonus == 0); // unused
1658 while(i--) {
1659 str->ReadDword(&spellindex);
1660 str->ReadDword(&totalcount);
1661 str->ReadDword(&memocount);
1662 str->ReadDword(&tmpDword);
1663 check+=totalcount;
1664 const ieResRef *tmp = ResolveSpellIndex(spellindex, level, type, act->BaseStats[IE_KIT]);
1665 if (!tmp) {
1666 error("CREImporter", "Unresolved spell index: %d level:%d, type: %d",
1667 spellindex, level+1, type);
1668 }
1669
1670 CREKnownSpell *known = new CREKnownSpell;
1671 known->Level = level;
1672 known->Type = type;
1673 strnlwrcpy(known->SpellResRef, *tmp, 8);
1674 sm->known_spells.push_back(known);
1675 while (memocount--) {
1676 if (totalcount) {
1677 totalcount--;
1678 } else {
1679 Log(ERROR, "CREImporter", "More spells still known than memorised.");
1680 break;
1681 }
1682 CREMemorizedSpell *memory = new CREMemorizedSpell;
1683 memory->Flags = 1;
1684 strnlwrcpy(memory->SpellResRef, *tmp, 8);
1685 sm->memorized_spells.push_back(memory);
1686 }
1687 while(totalcount--) {
1688 CREMemorizedSpell *memory = new CREMemorizedSpell;
1689 memory->Flags = 0;
1690 strnlwrcpy(memory->SpellResRef, *tmp, 8);
1691 sm->memorized_spells.push_back(memory);
1692 }
1693 }
1694 // hacks for domain spells, since their count is not stored and also always 1
1695 // NOTE: luckily this does not cause save game incompatibility
1696 str->ReadDword(&tmpDword);
1697 if (type == IE_IWD2_SPELL_DOMAIN && count > 0) {
1698 sm->SlotCount = 1;
1699 } else {
1700 sm->SlotCount = (ieWord) tmpDword;
1701 }
1702 str->ReadDword(&tmpDword);
1703 if (type == IE_IWD2_SPELL_DOMAIN && count > 0) {
1704 sm->SlotCountWithBonus = 1;
1705 } else {
1706 sm->SlotCountWithBonus = (ieWord) tmpDword;
1707 }
1708 }
1709
GetActorIWD2(Actor * act)1710 void CREImporter::GetActorIWD2(Actor *act)
1711 {
1712 int i;
1713 ieByte tmpByte;
1714 ieWord tmpWord;
1715
1716 str->Read( &tmpByte, 1 );
1717 act->BaseStats[IE_REPUTATION]=tmpByte;
1718 str->Read( &tmpByte, 1 );
1719 act->BaseStats[IE_HIDEINSHADOWS]=tmpByte;
1720 str->ReadWord( &tmpWord );
1721 act->AC.SetNatural((ieWordSigned) tmpWord);
1722 str->ReadWord( &tmpWord );
1723 act->BaseStats[IE_ACCRUSHINGMOD]=(ieWordSigned) tmpWord;
1724 str->ReadWord( &tmpWord );
1725 act->BaseStats[IE_ACMISSILEMOD]=(ieWordSigned) tmpWord;
1726 str->ReadWord( &tmpWord );
1727 act->BaseStats[IE_ACPIERCINGMOD]=(ieWordSigned) tmpWord;
1728 str->ReadWord( &tmpWord );
1729 act->BaseStats[IE_ACSLASHINGMOD]=(ieWordSigned) tmpWord;
1730 str->Read( &tmpByte, 1 );
1731 act->ToHit.SetBase((ieByteSigned) tmpByte);//Unknown in CRE V2.2
1732 str->Read( &tmpByte, 1 );
1733 act->BaseStats[IE_NUMBEROFATTACKS]=tmpByte;//Unknown in CRE V2.2
1734 str->Read( &tmpByte, 1 );
1735 act->BaseStats[IE_SAVEVSDEATH]=(ieByteSigned) tmpByte;//Fortitude Save in V2.2
1736 str->Read( &tmpByte, 1 );
1737 act->BaseStats[IE_SAVEVSWANDS]=(ieByteSigned) tmpByte;//Reflex Save in V2.2
1738 str->Read( &tmpByte, 1 );
1739 act->BaseStats[IE_SAVEVSPOLY]=(ieByteSigned) tmpByte;// will Save in V2.2
1740 str->Read( &tmpByte, 1 );
1741 act->BaseStats[IE_RESISTFIRE]=(ieByteSigned) tmpByte;
1742 str->Read( &tmpByte, 1 );
1743 act->BaseStats[IE_RESISTCOLD]=(ieByteSigned) tmpByte;
1744 str->Read( &tmpByte, 1 );
1745 act->BaseStats[IE_RESISTELECTRICITY]=(ieByteSigned) tmpByte;
1746 str->Read( &tmpByte, 1 );
1747 act->BaseStats[IE_RESISTACID]=(ieByteSigned) tmpByte;
1748 str->Read( &tmpByte, 1 );
1749 act->BaseStats[IE_RESISTMAGIC]=(ieByteSigned) tmpByte;
1750 str->Read( &tmpByte, 1 );
1751 act->BaseStats[IE_RESISTMAGICFIRE]=(ieByteSigned) tmpByte;
1752 str->Read( &tmpByte, 1 );
1753 act->BaseStats[IE_RESISTMAGICCOLD]=(ieByteSigned) tmpByte;
1754 str->Read( &tmpByte, 1 );
1755 act->BaseStats[IE_RESISTSLASHING]=(ieByteSigned) tmpByte;
1756 str->Read( &tmpByte, 1 );
1757 act->BaseStats[IE_RESISTCRUSHING]=(ieByteSigned) tmpByte;
1758 str->Read( &tmpByte, 1 );
1759 act->BaseStats[IE_RESISTPIERCING]=(ieByteSigned) tmpByte;
1760 str->Read( &tmpByte, 1 );
1761 act->BaseStats[IE_RESISTMISSILE]=(ieByteSigned) tmpByte;
1762 str->Read( &tmpByte, 1 );
1763 act->BaseStats[IE_MAGICDAMAGERESISTANCE]=(ieByteSigned) tmpByte;
1764 str->Seek( 4, GEM_CURRENT_POS );
1765 str->Read( &tmpByte, 1 );
1766 act->BaseStats[IE_FATIGUE]=tmpByte;
1767 str->Read( &tmpByte, 1 );
1768 act->BaseStats[IE_INTOXICATION]=tmpByte;
1769 str->Read( &tmpByte, 1 );
1770 act->BaseStats[IE_LUCK]=(ieByteSigned) tmpByte;
1771 str->Seek( 34, GEM_CURRENT_POS ); //unknowns
1772 str->Read( &tmpByte, 1 );
1773 act->BaseStats[IE_CLASSLEVELSUM]=tmpByte; //total levels
1774 str->Read( & tmpByte, 1 );
1775 act->BaseStats[IE_LEVELBARBARIAN]=tmpByte;
1776 str->Read( & tmpByte, 1 );
1777 act->BaseStats[IE_LEVELBARD]=tmpByte;
1778 str->Read( & tmpByte, 1 );
1779 act->BaseStats[IE_LEVELCLERIC]=tmpByte;
1780 str->Read( & tmpByte, 1 );
1781 act->BaseStats[IE_LEVELDRUID]=tmpByte;
1782 str->Read( & tmpByte, 1 );
1783 act->BaseStats[IE_LEVELFIGHTER]=tmpByte;
1784 str->Read( & tmpByte, 1 );
1785 act->BaseStats[IE_LEVELMONK]=tmpByte;
1786 str->Read( & tmpByte, 1 );
1787 act->BaseStats[IE_LEVELPALADIN]=tmpByte;
1788 str->Read( & tmpByte, 1 );
1789 act->BaseStats[IE_LEVELRANGER]=tmpByte;
1790 str->Read( & tmpByte, 1 );
1791 act->BaseStats[IE_LEVELTHIEF]=tmpByte;
1792 str->Read( & tmpByte, 1 );
1793 act->BaseStats[IE_LEVELSORCERER]=tmpByte;
1794 str->Read( & tmpByte, 1 );
1795 act->BaseStats[IE_LEVELMAGE]=tmpByte;
1796 str->Seek( 22, GEM_CURRENT_POS ); //levels for classes
1797 for (i=0;i<64;i++) {
1798 str->ReadDword( &act->StrRefs[i] );
1799 }
1800 ReadScript( act, SCR_SPECIFICS);
1801 ReadScript( act, SCR_AREA);
1802 str->Seek( 4, GEM_CURRENT_POS );
1803 str->ReadDword( &act->BaseStats[IE_FEATS1]);
1804 str->ReadDword( &act->BaseStats[IE_FEATS2]);
1805 str->ReadDword( &act->BaseStats[IE_FEATS3]);
1806 str->Seek( 12, GEM_CURRENT_POS );
1807 //proficiencies
1808 for (i=0;i<26;i++) {
1809 str->Read( &tmpByte, 1);
1810 act->BaseStats[IE_PROFICIENCYBASTARDSWORD+i]=tmpByte;
1811 }
1812 //skills
1813 str->Seek( 38, GEM_CURRENT_POS );
1814 str->Read( &tmpByte, 1);
1815 act->BaseStats[IE_ALCHEMY]=tmpByte;
1816 str->Read( &tmpByte, 1);
1817 act->BaseStats[IE_ANIMALS]=tmpByte;
1818 str->Read( &tmpByte, 1);
1819 act->BaseStats[IE_BLUFF]=tmpByte;
1820 str->Read( &tmpByte, 1);
1821 act->BaseStats[IE_CONCENTRATION]=tmpByte;
1822 str->Read( &tmpByte, 1);
1823 act->BaseStats[IE_DIPLOMACY]=tmpByte;
1824 str->Read( &tmpByte, 1);
1825 act->BaseStats[IE_TRAPS]=tmpByte;
1826 str->Read( &tmpByte, 1);
1827 act->BaseStats[IE_HIDEINSHADOWS]=tmpByte;
1828 str->Read( &tmpByte, 1);
1829 act->BaseStats[IE_INTIMIDATE]=tmpByte;
1830 str->Read( &tmpByte, 1);
1831 act->BaseStats[IE_LORE]=tmpByte;
1832 str->Read( &tmpByte, 1);
1833 act->BaseStats[IE_STEALTH]=tmpByte;
1834 str->Read( &tmpByte, 1);
1835 act->BaseStats[IE_LOCKPICKING]=tmpByte;
1836 str->Read( &tmpByte, 1);
1837 act->BaseStats[IE_PICKPOCKET]=tmpByte;
1838 str->Read( &tmpByte, 1);
1839 act->BaseStats[IE_SEARCH]=tmpByte;
1840 str->Read( &tmpByte, 1);
1841 act->BaseStats[IE_SPELLCRAFT]=tmpByte;
1842 str->Read( &tmpByte, 1);
1843 act->BaseStats[IE_MAGICDEVICE]=tmpByte;
1844 str->Read( &tmpByte, 1);
1845 act->BaseStats[IE_TRACKING]=tmpByte;
1846 str->Seek( 50, GEM_CURRENT_POS );
1847 str->Read( &tmpByte, 1);
1848 act->BaseStats[IE_CR]=tmpByte;
1849 str->Read( &tmpByte, 1 );
1850 act->BaseStats[IE_HATEDRACE]=tmpByte;
1851 //we got 7 more hated races
1852 for (i=0;i<7;i++) {
1853 str->Read( &tmpByte, 1 );
1854 act->BaseStats[IE_HATEDRACE2+i]=tmpByte;
1855 }
1856 str->Read( &tmpByte, 1 );
1857 act->BaseStats[IE_SUBRACE]=tmpByte;
1858 str->ReadWord( &tmpWord );
1859 //skipping 2 bytes, one is SEX (could use it for sounds)
1860 str->Read( &tmpByte, 1 );
1861 act->BaseStats[IE_STR]=tmpByte;
1862 str->Read( &tmpByte, 1 );
1863 act->BaseStats[IE_INT]=tmpByte;
1864 str->Read( &tmpByte, 1 );
1865 act->BaseStats[IE_WIS]=tmpByte;
1866 str->Read( &tmpByte, 1 );
1867 act->BaseStats[IE_DEX]=tmpByte;
1868 str->Read( &tmpByte, 1 );
1869 act->BaseStats[IE_CON]=tmpByte;
1870 str->Read( &tmpByte, 1 );
1871 act->BaseStats[IE_CHR]=tmpByte;
1872 str->Read( &tmpByte, 1 );
1873 act->BaseStats[IE_MORALE]=tmpByte;
1874 str->Read( &tmpByte, 1 );
1875 act->BaseStats[IE_MORALEBREAK]=tmpByte;
1876 str->Read( &tmpByte, 1 );
1877 //HatedRace is a list of races, so this is skipped here
1878 //act->BaseStats[IE_HATEDRACE]=tmpByte;
1879 str->Read( &tmpByte, 1 );
1880 act->BaseStats[IE_MORALERECOVERYTIME]=tmpByte;
1881 //No KIT word order magic for IWD2
1882 str->ReadDword( &act->BaseStats[IE_KIT] );
1883 ReadScript(act, SCR_OVERRIDE);
1884 ReadScript(act, SCR_CLASS);
1885 ReadScript(act, SCR_RACE);
1886 ReadScript(act, SCR_GENERAL);
1887 ReadScript(act, SCR_DEFAULT);
1888 //new scripting flags, one on each byte
1889 str->Read( &tmpByte, 1); //hidden
1890 if (tmpByte) {
1891 act->BaseStats[IE_AVATARREMOVAL]=tmpByte;
1892 }
1893 str->Read( &act->SetDeathVar, 1); //set death variable
1894 str->Read( &act->IncKillCount, 1); //increase kill count
1895 str->Read( &act->UnknownField, 1);
1896 for (i = 0; i<5; i++) {
1897 str->ReadWord( &tmpWord );
1898 act->BaseStats[IE_INTERNAL_0+i]=tmpWord;
1899 }
1900 ieVariable KillVar;
1901 str->Read(KillVar,32);
1902 KillVar[32]=0;
1903 strnspccpy(act->KillVar, KillVar, 32);
1904 str->Read(KillVar,32);
1905 KillVar[32]=0;
1906 strnspccpy(act->IncKillVar, KillVar, 32);
1907 str->Seek( 2, GEM_CURRENT_POS);
1908 str->ReadWord( &tmpWord );
1909 act->BaseStats[IE_SAVEDXPOS] = tmpWord;
1910 str->ReadWord( &tmpWord );
1911 act->BaseStats[IE_SAVEDYPOS] = tmpWord;
1912 str->ReadWord( &tmpWord );
1913 act->BaseStats[IE_SAVEDFACE] = tmpWord;
1914
1915 str->Seek( 15, GEM_CURRENT_POS );
1916 str->Read( &tmpByte, 1);
1917 act->BaseStats[IE_TRANSLUCENT]=tmpByte;
1918 str->Read( &tmpByte, 1); //fade speed
1919 str->Read( &tmpByte, 1); //spec. flags
1920 act->BaseStats[IE_SPECFLAGS] = tmpByte;
1921 str->Read( &tmpByte, 1); //invisible
1922 str->ReadWord( &tmpWord); //unknown
1923 str->Read( &tmpByte, 1); //unused skill points
1924 act->BaseStats[IE_UNUSED_SKILLPTS] = tmpByte;
1925 str->Seek( 124, GEM_CURRENT_POS );
1926 str->Read( &tmpByte, 1);
1927 act->BaseStats[IE_EA]=tmpByte;
1928 str->Read( &tmpByte, 1);
1929 act->BaseStats[IE_GENERAL]=tmpByte;
1930 str->Read( &tmpByte, 1);
1931 act->BaseStats[IE_RACE]=tmpByte;
1932 str->Read( &tmpByte, 1);
1933 act->BaseStats[IE_CLASS]=tmpByte;
1934 str->Read( &tmpByte, 1);
1935 act->BaseStats[IE_SPECIFIC]=tmpByte;
1936 str->Read( &tmpByte, 1);
1937 act->BaseStats[IE_SEX]=tmpByte;
1938 str->Seek( 5, GEM_CURRENT_POS ); // object.ids references that we don't save
1939 str->Read( &tmpByte, 1);
1940 act->BaseStats[IE_ALIGNMENT]=tmpByte;
1941 str->Seek( 4, GEM_CURRENT_POS );
1942 ieVariable scriptname;
1943 str->Read( scriptname, 32);
1944 scriptname[32]=0;
1945 act->SetScriptName(scriptname);
1946
1947 KnownSpellsOffset = 0;
1948 KnownSpellsCount = 0;
1949 SpellMemorizationOffset = 0;
1950 SpellMemorizationCount = 0;
1951 MemorizedSpellsOffset = 0;
1952 MemorizedSpellsCount = 0;
1953 // skipping class (probably redundant), class mask (calculated)
1954 str->Seek(6, GEM_CURRENT_POS);
1955 ieDword ClassSpellOffsets[8*9];
1956
1957 //spellbook spells
1958 for (i=0;i<7*9;i++) {
1959 str->ReadDword(ClassSpellOffsets+i);
1960 }
1961 ieDword ClassSpellCounts[8*9];
1962 for (i=0;i<7*9;i++) {
1963 str->ReadDword(ClassSpellCounts+i);
1964 }
1965
1966 //domain spells
1967 for (i=7*9;i<8*9;i++) {
1968 str->ReadDword(ClassSpellOffsets+i);
1969 }
1970 for (i=7*9;i<8*9;i++) {
1971 str->ReadDword(ClassSpellCounts+i);
1972 }
1973
1974 ieDword InnateOffset, InnateCount;
1975 ieDword SongOffset, SongCount;
1976 ieDword ShapeOffset, ShapeCount;
1977 str->ReadDword( &InnateOffset );
1978 str->ReadDword( &InnateCount );
1979 str->ReadDword( &SongOffset );
1980 str->ReadDword( &SongCount );
1981 str->ReadDword( &ShapeOffset );
1982 str->ReadDword( &ShapeCount );
1983 //str->Seek( 606, GEM_CURRENT_POS);
1984
1985 str->ReadDword( &ItemSlotsOffset );
1986 str->ReadDword( &ItemsOffset );
1987 str->ReadDword( &ItemsCount );
1988 str->ReadDword( &EffectsOffset );
1989 str->ReadDword( &EffectsCount );
1990
1991 ReadDialog(act);
1992
1993 for(i=0;i<8;i++) {
1994 for(int lev=0;lev<9;lev++) {
1995 //if everything is alright, then seeking is not needed
1996 assert(str->GetPos() == CREOffset+ClassSpellOffsets[i*9+lev]);
1997 GetIWD2Spellpage(act, (ieIWD2SpellType) i, lev, ClassSpellCounts[i*9+lev]);
1998 }
1999 }
2000 str->Seek(CREOffset+InnateOffset, GEM_STREAM_START);
2001 GetIWD2Spellpage(act, IE_IWD2_SPELL_INNATE, 0, InnateCount);
2002
2003 str->Seek(CREOffset+SongOffset, GEM_STREAM_START);
2004 GetIWD2Spellpage(act, IE_IWD2_SPELL_SONG, 0, SongCount);
2005
2006 str->Seek(CREOffset+ShapeOffset, GEM_STREAM_START);
2007 GetIWD2Spellpage(act, IE_IWD2_SPELL_SHAPE, 0, ShapeCount);
2008 }
2009
GetActorIWD1(Actor * act)2010 void CREImporter::GetActorIWD1(Actor *act) //9.0
2011 {
2012 int i;
2013 ieByte tmpByte;
2014 ieWord tmpWord;
2015
2016 str->Read( &tmpByte, 1 );
2017 act->BaseStats[IE_REPUTATION]=tmpByte;
2018 str->Read( &tmpByte, 1 );
2019 act->BaseStats[IE_HIDEINSHADOWS]=tmpByte;
2020 str->ReadWord( &tmpWord );
2021 //skipping a word
2022 str->ReadWord( &tmpWord );
2023 act->AC.SetNatural((ieWordSigned) tmpWord);
2024 str->ReadWord( &tmpWord );
2025 act->BaseStats[IE_ACCRUSHINGMOD]=(ieWordSigned) tmpWord;
2026 str->ReadWord( &tmpWord );
2027 act->BaseStats[IE_ACMISSILEMOD]=(ieWordSigned) tmpWord;
2028 str->ReadWord( &tmpWord );
2029 act->BaseStats[IE_ACPIERCINGMOD]=(ieWordSigned) tmpWord;
2030 str->ReadWord( &tmpWord );
2031 act->BaseStats[IE_ACSLASHINGMOD]=(ieWordSigned) tmpWord;
2032 str->Read( &tmpByte, 1 );
2033 act->ToHit.SetBase((ieByteSigned) tmpByte);
2034 str->Read( &tmpByte, 1 );
2035 tmpByte = tmpByte * 2;
2036 if (tmpByte>10) tmpByte-=11;
2037 act->BaseStats[IE_NUMBEROFATTACKS]=tmpByte;
2038 str->Read( &tmpByte, 1 );
2039 act->BaseStats[IE_SAVEVSDEATH]=(ieByteSigned) tmpByte;
2040 str->Read( &tmpByte, 1 );
2041 act->BaseStats[IE_SAVEVSWANDS]=(ieByteSigned) tmpByte;
2042 str->Read( &tmpByte, 1 );
2043 act->BaseStats[IE_SAVEVSPOLY]=(ieByteSigned) tmpByte;
2044 str->Read( &tmpByte, 1 );
2045 act->BaseStats[IE_SAVEVSBREATH]=(ieByteSigned) tmpByte;
2046 str->Read( &tmpByte, 1 );
2047 act->BaseStats[IE_SAVEVSSPELL]=(ieByteSigned) tmpByte;
2048 str->Read( &tmpByte, 1 );
2049 act->BaseStats[IE_RESISTFIRE]=(ieByteSigned) tmpByte;
2050 str->Read( &tmpByte, 1 );
2051 act->BaseStats[IE_RESISTCOLD]=(ieByteSigned) tmpByte;
2052 str->Read( &tmpByte, 1 );
2053 act->BaseStats[IE_RESISTELECTRICITY]=(ieByteSigned) tmpByte;
2054 str->Read( &tmpByte, 1 );
2055 act->BaseStats[IE_RESISTACID]=(ieByteSigned) tmpByte;
2056 str->Read( &tmpByte, 1 );
2057 act->BaseStats[IE_RESISTMAGIC]=(ieByteSigned) tmpByte;
2058 str->Read( &tmpByte, 1 );
2059 act->BaseStats[IE_RESISTMAGICFIRE]=(ieByteSigned) tmpByte;
2060 str->Read( &tmpByte, 1 );
2061 act->BaseStats[IE_RESISTMAGICCOLD]=(ieByteSigned) tmpByte;
2062 str->Read( &tmpByte, 1 );
2063 act->BaseStats[IE_RESISTSLASHING]=(ieByteSigned) tmpByte;
2064 str->Read( &tmpByte, 1 );
2065 act->BaseStats[IE_RESISTCRUSHING]=(ieByteSigned) tmpByte;
2066 str->Read( &tmpByte, 1 );
2067 act->BaseStats[IE_RESISTPIERCING]=(ieByteSigned) tmpByte;
2068 str->Read( &tmpByte, 1 );
2069 act->BaseStats[IE_RESISTMISSILE]=(ieByteSigned) tmpByte;
2070 str->Read( &tmpByte, 1 );
2071 act->BaseStats[IE_DETECTILLUSIONS]=tmpByte;
2072 str->Read( &tmpByte, 1 );
2073 act->BaseStats[IE_SETTRAPS]=tmpByte;
2074 str->Read( &tmpByte, 1 );
2075 act->BaseStats[IE_LORE]=tmpByte;
2076 str->Read( &tmpByte, 1 );
2077 act->BaseStats[IE_LOCKPICKING]=tmpByte;
2078 str->Read( &tmpByte, 1 );
2079 act->BaseStats[IE_STEALTH]=tmpByte;
2080 str->Read( &tmpByte, 1 );
2081 act->BaseStats[IE_TRAPS]=tmpByte;
2082 str->Read( &tmpByte, 1 );
2083 act->BaseStats[IE_PICKPOCKET]=tmpByte;
2084 str->Read( &tmpByte, 1 );
2085 act->BaseStats[IE_FATIGUE]=tmpByte;
2086 str->Read( &tmpByte, 1 );
2087 act->BaseStats[IE_INTOXICATION]=tmpByte;
2088 str->Read( &tmpByte, 1 );
2089 act->BaseStats[IE_LUCK]=(ieByteSigned) tmpByte;
2090 for (i=0;i<21;i++) {
2091 str->Read( &tmpByte, 1 );
2092 act->BaseStats[IE_PROFICIENCYBASTARDSWORD+i]=tmpByte;
2093 }
2094 str->Read( &tmpByte, 1 );
2095 act->BaseStats[IE_TRACKING]=tmpByte;
2096 str->Seek( 32, GEM_CURRENT_POS );
2097 for (i=0; i<VCONST_COUNT; i++) {
2098 str->ReadDword( &act->StrRefs[i] );
2099 }
2100 str->Read( &tmpByte, 1 );
2101 act->BaseStats[IE_LEVEL]=tmpByte;
2102 str->Read( &tmpByte, 1 );
2103 act->BaseStats[IE_LEVEL2]=tmpByte;
2104 str->Read( &tmpByte, 1 );
2105 act->BaseStats[IE_LEVEL3]=tmpByte;
2106 //this is rumoured to be IE_SEX, but we use the gender field for this
2107 str->Read( &tmpByte, 1 );
2108 //skipping a byte
2109 str->Read( &tmpByte, 1 );
2110 act->BaseStats[IE_STR]=tmpByte;
2111 str->Read( &tmpByte, 1 );
2112 act->BaseStats[IE_STREXTRA]=tmpByte;
2113 str->Read( &tmpByte, 1 );
2114 act->BaseStats[IE_INT]=tmpByte;
2115 str->Read( &tmpByte, 1 );
2116 act->BaseStats[IE_WIS]=tmpByte;
2117 str->Read( &tmpByte, 1 );
2118 act->BaseStats[IE_DEX]=tmpByte;
2119 str->Read( &tmpByte, 1 );
2120 act->BaseStats[IE_CON]=tmpByte;
2121 str->Read( &tmpByte, 1 );
2122 act->BaseStats[IE_CHR]=tmpByte;
2123 str->Read( &tmpByte, 1 );
2124 act->BaseStats[IE_MORALE]=tmpByte;
2125 str->Read( &tmpByte, 1 );
2126 act->BaseStats[IE_MORALEBREAK]=tmpByte;
2127 str->Read( &tmpByte, 1 );
2128 act->BaseStats[IE_HATEDRACE]=tmpByte;
2129 str->Read( &tmpByte, 1 );
2130 act->BaseStats[IE_MORALERECOVERYTIME]=tmpByte;
2131 str->Read( &tmpByte, 1 );
2132 //skipping a byte
2133 str->ReadDword( &act->BaseStats[IE_KIT] );
2134 act->BaseStats[IE_KIT] = ((act->BaseStats[IE_KIT] & 0xffff) << 16) +
2135 ((act->BaseStats[IE_KIT] & 0xffff0000) >> 16);
2136 ReadScript(act, SCR_OVERRIDE);
2137 ReadScript(act, SCR_CLASS);
2138 ReadScript(act, SCR_RACE);
2139 ReadScript(act, SCR_GENERAL);
2140 ReadScript(act, SCR_DEFAULT);
2141 //new scripting flags, one on each byte
2142 str->Read( &tmpByte, 1); //hidden
2143 if (tmpByte) {
2144 act->BaseStats[IE_AVATARREMOVAL]=tmpByte;
2145 }
2146 str->Read( &act->SetDeathVar, 1); //set death variable
2147 str->Read( &act->IncKillCount, 1); //increase kill count
2148 str->Read( &act->UnknownField, 1);
2149 for (i = 0; i<5; i++) {
2150 str->ReadWord( &tmpWord );
2151 act->BaseStats[IE_INTERNAL_0+i]=tmpWord;
2152 }
2153 ieVariable KillVar;
2154 str->Read(KillVar,32); //use these as needed
2155 KillVar[32]=0;
2156 strnspccpy(act->KillVar, KillVar, 32);
2157 str->Read(KillVar,32);
2158 KillVar[32]=0;
2159 strnspccpy(act->IncKillVar, KillVar, 32);
2160 str->Seek( 2, GEM_CURRENT_POS);
2161 str->ReadWord( &tmpWord );
2162 act->BaseStats[IE_SAVEDXPOS] = tmpWord;
2163 str->ReadWord( &tmpWord );
2164 act->BaseStats[IE_SAVEDYPOS] = tmpWord;
2165 str->ReadWord( &tmpWord );
2166 act->BaseStats[IE_SAVEDFACE] = tmpWord;
2167 str->Seek( 18, GEM_CURRENT_POS );
2168 str->Read( &tmpByte, 1);
2169 act->BaseStats[IE_EA] = tmpByte;
2170 str->Read( &tmpByte, 1);
2171 act->BaseStats[IE_GENERAL] = tmpByte;
2172 str->Read( &tmpByte, 1);
2173 act->BaseStats[IE_RACE] = tmpByte;
2174 str->Read( &tmpByte, 1);
2175 act->BaseStats[IE_CLASS] = tmpByte;
2176 str->Read( &tmpByte, 1);
2177 act->BaseStats[IE_SPECIFIC] = tmpByte;
2178 str->Read( &tmpByte, 1);
2179 act->BaseStats[IE_SEX] = tmpByte;
2180 str->Seek( 5, GEM_CURRENT_POS );
2181 str->Read( &tmpByte, 1);
2182 act->BaseStats[IE_ALIGNMENT]=tmpByte;
2183 str->Seek( 4, GEM_CURRENT_POS );
2184 ieVariable scriptname;
2185 str->Read( scriptname, 32);
2186 scriptname[32]=0;
2187 act->SetScriptName(scriptname);
2188
2189 str->ReadDword( &KnownSpellsOffset );
2190 str->ReadDword( &KnownSpellsCount );
2191 str->ReadDword( &SpellMemorizationOffset );
2192 str->ReadDword( &SpellMemorizationCount );
2193 str->ReadDword( &MemorizedSpellsOffset );
2194 str->ReadDword( &MemorizedSpellsCount );
2195
2196 str->ReadDword( &ItemSlotsOffset );
2197 str->ReadDword( &ItemsOffset );
2198 str->ReadDword( &ItemsCount );
2199 str->ReadDword( &EffectsOffset );
2200 str->ReadDword( &EffectsCount );
2201
2202 ReadDialog(act);
2203 }
2204
GetStoredFileSize(Actor * actor)2205 int CREImporter::GetStoredFileSize(Actor *actor)
2206 {
2207 int headersize;
2208 unsigned int Inventory_Size;
2209 unsigned int i;
2210
2211 CREVersion = actor->version;
2212 switch (CREVersion) {
2213 case IE_CRE_GEMRB:
2214 headersize = 0x2d4;
2215 //minus fist
2216 Inventory_Size=actor->inventory.GetSlotCount()-1;
2217 TotSCEFF = 1;
2218 break;
2219 case IE_CRE_V1_1://totsc/bg2/tob (still V1.0, but large effects)
2220 case IE_CRE_V1_0://bg1
2221 headersize = 0x2d4;
2222 Inventory_Size=38;
2223 //we should know it is bg1
2224 if (actor->version == IE_CRE_V1_1) {
2225 TotSCEFF = 1;
2226 } else {
2227 TotSCEFF = 0;
2228 }
2229 break;
2230 case IE_CRE_V1_2: //pst
2231 headersize = 0x378;
2232 Inventory_Size=46;
2233 TotSCEFF = 0;
2234 break;
2235 case IE_CRE_V2_2://iwd2
2236 headersize = 0x62e; //with offsets
2237 Inventory_Size=50;
2238 TotSCEFF = 1;
2239 break;
2240 case IE_CRE_V9_0://iwd
2241 headersize = 0x33c;
2242 Inventory_Size=38;
2243 TotSCEFF = 1;
2244 break;
2245 default:
2246 return -1;
2247 }
2248 KnownSpellsOffset = headersize;
2249
2250 if (actor->version==IE_CRE_V2_2) { //iwd2
2251 int type, level;
2252
2253 for (type=IE_IWD2_SPELL_BARD;type<IE_IWD2_SPELL_DOMAIN;type++) for(level=0;level<9;level++) {
2254 headersize += GetIWD2SpellpageSize(actor, (ieIWD2SpellType) type, level)*16+8;
2255 }
2256 for(level=0;level<9;level++) {
2257 headersize += GetIWD2SpellpageSize(actor, IE_IWD2_SPELL_DOMAIN, level)*16+8;
2258 }
2259 for (type=IE_IWD2_SPELL_INNATE;type<NUM_IWD2_SPELLTYPES;type++) {
2260 headersize += GetIWD2SpellpageSize(actor, (ieIWD2SpellType) type, 0)*16+8;
2261 }
2262 } else {//others
2263 //adding known spells
2264 KnownSpellsCount = actor->spellbook.GetTotalKnownSpellsCount();
2265 headersize += KnownSpellsCount * 12;
2266 SpellMemorizationOffset = headersize;
2267
2268 //adding spell pages
2269 SpellMemorizationCount = actor->spellbook.GetTotalPageCount();
2270 headersize += SpellMemorizationCount * 16;
2271 MemorizedSpellsOffset = headersize;
2272
2273 MemorizedSpellsCount = actor->spellbook.GetTotalMemorizedSpellsCount();
2274 headersize += MemorizedSpellsCount * 12;
2275 }
2276 ItemSlotsOffset = headersize;
2277
2278 //adding itemslot table size and equipped slot fields
2279 headersize += (Inventory_Size) * sizeof(ieWord) + sizeof(ieWord) * 2;
2280 ItemsOffset = headersize;
2281
2282 //counting items (calculating item storage)
2283 ItemsCount = 0;
2284 for (i=0;i<Inventory_Size;i++) {
2285 unsigned int j = core->QuerySlot(i+1);
2286 const CREItem *it = actor->inventory.GetSlotItem(j);
2287 if (it) {
2288 ItemsCount++;
2289 }
2290 }
2291 headersize += ItemsCount * 20;
2292
2293 EffectsOffset = headersize;
2294 //adding effects
2295 EffectsCount = actor->fxqueue.GetSavedEffectsCount();
2296 VariablesCount = actor->locals->GetCount();
2297 if (VariablesCount) {
2298 TotSCEFF=1;
2299 }
2300 if (TotSCEFF) {
2301 headersize += (VariablesCount + EffectsCount) * 264;
2302 } else {
2303 //if there are variables, then TotSCEFF is set
2304 headersize += EffectsCount * 48;
2305 }
2306
2307 return headersize;
2308 }
2309
PutInventory(DataStream * stream,const Actor * actor,unsigned int size)2310 int CREImporter::PutInventory(DataStream *stream, const Actor *actor, unsigned int size)
2311 {
2312 unsigned int i;
2313 ieDword tmpDword;
2314 ieWord tmpWord;
2315 ieWord ItemCount = 0;
2316 ieWord *indices =(ieWord *) malloc(size*sizeof(ieWord) );
2317
2318 for (i=0;i<size;i++) {
2319 indices[i]=(ieWord) -1;
2320 }
2321
2322 for (i=0;i<size;i++) {
2323 //ignore first element, getinventorysize makes space for fist
2324 unsigned int j = core->QuerySlot(i+1);
2325 const CREItem *it = actor->inventory.GetSlotItem(j);
2326 if (it) {
2327 indices[i] = ItemCount++;
2328 }
2329 stream->WriteWord( indices+i);
2330 }
2331 free(indices);
2332 tmpWord = (ieWord) actor->inventory.GetEquipped();
2333 stream->WriteWord( &tmpWord);
2334 tmpWord = (ieWord) actor->inventory.GetEquippedHeader();
2335 stream->WriteWord( &tmpWord);
2336
2337 for (i=0;i<size;i++) {
2338 //ignore first element, getinventorysize makes space for fist
2339 unsigned int j = core->QuerySlot(i+1);
2340 const CREItem *it = actor->inventory.GetSlotItem(j);
2341 if (!it) {
2342 continue;
2343 }
2344 stream->WriteResRef( it->ItemResRef);
2345 stream->WriteWord( &it->Expired);
2346 stream->WriteWord( &it->Usages[0]);
2347 stream->WriteWord( &it->Usages[1]);
2348 stream->WriteWord( &it->Usages[2]);
2349 tmpDword = it->Flags;
2350 //IWD uses this bit differently
2351 if (core->HasFeature(GF_MAGICBIT)) {
2352 if (it->Flags&IE_INV_ITEM_MAGICAL) {
2353 tmpDword|=IE_INV_ITEM_UNDROPPABLE;
2354 } else {
2355 tmpDword&=~IE_INV_ITEM_UNDROPPABLE;
2356 }
2357 }
2358 stream->WriteDword( &tmpDword);
2359 }
2360 return 0;
2361 }
2362
PutHeader(DataStream * stream,const Actor * actor)2363 int CREImporter::PutHeader(DataStream *stream, const Actor *actor)
2364 {
2365 char Signature[8];
2366 ieByte tmpByte;
2367 ieWord tmpWord;
2368 ieDword tmpDword;
2369 int i;
2370 char filling[51];
2371
2372 memset(filling,0,sizeof(filling));
2373 memcpy( Signature, "CRE V0.0", 8);
2374 Signature[5]+=CREVersion/10;
2375 if (actor->version!=IE_CRE_V1_1) {
2376 Signature[7]+=CREVersion%10;
2377 }
2378 stream->Write( Signature, 8);
2379 stream->WriteDword( &actor->LongStrRef);
2380 stream->WriteDword( &actor->ShortStrRef);
2381 stream->WriteDword( &actor->BaseStats[IE_MC_FLAGS]);
2382 stream->WriteDword( &actor->BaseStats[IE_XPVALUE]);
2383 stream->WriteDword( &actor->BaseStats[IE_XP]);
2384 stream->WriteDword( &actor->BaseStats[IE_GOLD]);
2385 stream->WriteDword( &actor->BaseStats[IE_STATE_ID]);
2386 tmpWord = actor->BaseStats[IE_HITPOINTS];
2387 //decrease the hp back to the one without constitution bonus
2388 // (but only player classes can have it)
2389 tmpWord = (ieWord) (tmpWord - actor->GetHpAdjustment(actor->GetXPLevel(false), false));
2390 stream->WriteWord( &tmpWord);
2391 tmpWord = actor->BaseStats[IE_MAXHITPOINTS];
2392 stream->WriteWord( &tmpWord);
2393 stream->WriteDword( &actor->BaseStats[IE_ANIMATION_ID]);
2394 for (i=0;i<7;i++) {
2395 Signature[i] = (char) actor->BaseStats[IE_COLORS+i];
2396 }
2397 //old effect type
2398 Signature[7] = TotSCEFF;
2399 stream->Write( Signature, 8);
2400 stream->WriteResRef( actor->SmallPortrait);
2401 stream->WriteResRef( actor->LargePortrait);
2402 tmpByte = actor->BaseStats[IE_REPUTATION];
2403 stream->Write( &tmpByte, 1 );
2404 tmpByte = actor->BaseStats[IE_HIDEINSHADOWS];
2405 stream->Write( &tmpByte, 1 );
2406 //from here it differs, slightly
2407 tmpWord = actor->AC.GetNatural();
2408 stream->WriteWord( &tmpWord);
2409 //iwd2 doesn't store this a second time,
2410 //probably gemrb format shouldn't either?
2411 if (actor->version != IE_CRE_V2_2) {
2412 tmpWord = actor->AC.GetNatural();
2413 stream->WriteWord( &tmpWord);
2414 }
2415 tmpWord = actor->BaseStats[IE_ACCRUSHINGMOD];
2416 stream->WriteWord( &tmpWord);
2417 tmpWord = actor->BaseStats[IE_ACMISSILEMOD];
2418 stream->WriteWord( &tmpWord);
2419 tmpWord = actor->BaseStats[IE_ACPIERCINGMOD];
2420 stream->WriteWord( &tmpWord);
2421 tmpWord = actor->BaseStats[IE_ACSLASHINGMOD];
2422 stream->WriteWord( &tmpWord);
2423 tmpByte = actor->ToHit.GetBase();
2424 stream->Write( &tmpByte, 1);
2425 tmpByte = actor->BaseStats[IE_NUMBEROFATTACKS];
2426 if (actor->version == IE_CRE_V2_2) {
2427 stream->Write( &tmpByte, 1);
2428 tmpByte = actor->BaseStats[IE_SAVEFORTITUDE];
2429 stream->Write( &tmpByte, 1);
2430 tmpByte = actor->BaseStats[IE_SAVEREFLEX];
2431 stream->Write( &tmpByte, 1);
2432 tmpByte = actor->BaseStats[IE_SAVEWILL];
2433 stream->Write( &tmpByte, 1);
2434 } else {
2435 if (actor->version!=IE_CRE_GEMRB) {
2436 if (tmpByte&1) tmpByte = tmpByte/2+6;
2437 else tmpByte /=2;
2438 }
2439 stream->Write( &tmpByte, 1);
2440 tmpByte = actor->BaseStats[IE_SAVEVSDEATH];
2441 stream->Write( &tmpByte, 1);
2442 tmpByte = actor->BaseStats[IE_SAVEVSWANDS];
2443 stream->Write( &tmpByte, 1);
2444 tmpByte = actor->BaseStats[IE_SAVEVSPOLY];
2445 stream->Write( &tmpByte, 1);
2446 tmpByte = actor->BaseStats[IE_SAVEVSBREATH];
2447 stream->Write( &tmpByte, 1);
2448 tmpByte = actor->BaseStats[IE_SAVEVSSPELL];
2449 stream->Write( &tmpByte, 1);
2450 }
2451 tmpByte = actor->BaseStats[IE_RESISTFIRE];
2452 stream->Write( &tmpByte, 1);
2453 tmpByte = actor->BaseStats[IE_RESISTCOLD];
2454 stream->Write( &tmpByte, 1);
2455 tmpByte = actor->BaseStats[IE_RESISTELECTRICITY];
2456 stream->Write( &tmpByte, 1);
2457 tmpByte = actor->BaseStats[IE_RESISTACID];
2458 stream->Write( &tmpByte, 1);
2459 tmpByte = actor->BaseStats[IE_RESISTMAGIC];
2460 stream->Write( &tmpByte, 1);
2461 tmpByte = actor->BaseStats[IE_RESISTMAGICFIRE];
2462 stream->Write( &tmpByte, 1);
2463 tmpByte = actor->BaseStats[IE_RESISTMAGICCOLD];
2464 stream->Write( &tmpByte, 1);
2465 tmpByte = actor->BaseStats[IE_RESISTSLASHING];
2466 stream->Write( &tmpByte, 1);
2467 tmpByte = actor->BaseStats[IE_RESISTCRUSHING];
2468 stream->Write( &tmpByte, 1);
2469 tmpByte = actor->BaseStats[IE_RESISTPIERCING];
2470 stream->Write( &tmpByte, 1);
2471 tmpByte = actor->BaseStats[IE_RESISTMISSILE];
2472 stream->Write( &tmpByte, 1);
2473 if (actor->version == IE_CRE_V2_2) {
2474 tmpByte = actor->BaseStats[IE_MAGICDAMAGERESISTANCE];
2475 stream->Write( &tmpByte, 1);
2476 stream->Write( Signature, 4);
2477 } else {
2478 tmpByte = actor->BaseStats[IE_DETECTILLUSIONS];
2479 stream->Write( &tmpByte, 1);
2480 tmpByte = actor->BaseStats[IE_SETTRAPS];
2481 stream->Write( &tmpByte, 1);
2482 tmpByte = actor->BaseStats[IE_LORE];
2483 stream->Write( &tmpByte, 1);
2484 tmpByte = actor->BaseStats[IE_LOCKPICKING];
2485 stream->Write( &tmpByte, 1);
2486 tmpByte = actor->BaseStats[IE_STEALTH];
2487 stream->Write( &tmpByte, 1);
2488 tmpByte = actor->BaseStats[IE_TRAPS];
2489 stream->Write( &tmpByte, 1);
2490 tmpByte = actor->BaseStats[IE_PICKPOCKET];
2491 stream->Write( &tmpByte, 1);
2492 }
2493 tmpByte = actor->BaseStats[IE_FATIGUE];
2494 stream->Write( &tmpByte, 1);
2495 tmpByte = actor->BaseStats[IE_INTOXICATION];
2496 stream->Write( &tmpByte, 1);
2497 tmpByte = actor->BaseStats[IE_LUCK];
2498 stream->Write( &tmpByte, 1);
2499
2500 if (actor->version == IE_CRE_V2_2) {
2501 //this is rather fuzzy
2502 //turnundead level, + 33 bytes of zero
2503 tmpByte = actor->BaseStats[IE_TURNUNDEADLEVEL];
2504 stream->Write(&tmpByte, 1);
2505 stream->Write( filling,33);
2506 //total levels
2507 tmpByte = actor->BaseStats[IE_CLASSLEVELSUM];
2508 stream->Write( &tmpByte, 1);
2509 tmpByte = actor->BaseStats[IE_LEVELBARBARIAN];
2510 stream->Write( &tmpByte, 1);
2511 tmpByte = actor->BaseStats[IE_LEVELBARD];
2512 stream->Write( &tmpByte, 1);
2513 tmpByte = actor->BaseStats[IE_LEVELCLERIC];
2514 stream->Write( &tmpByte, 1);
2515 tmpByte = actor->BaseStats[IE_LEVELDRUID];
2516 stream->Write( &tmpByte, 1);
2517 tmpByte = actor->BaseStats[IE_LEVELFIGHTER];
2518 stream->Write( &tmpByte, 1);
2519 tmpByte = actor->BaseStats[IE_LEVELMONK];
2520 stream->Write( &tmpByte, 1);
2521 tmpByte = actor->BaseStats[IE_LEVELPALADIN];
2522 stream->Write( &tmpByte, 1);
2523 tmpByte = actor->BaseStats[IE_LEVELRANGER];
2524 stream->Write( &tmpByte, 1);
2525 tmpByte = actor->BaseStats[IE_LEVELTHIEF];
2526 stream->Write( &tmpByte, 1);
2527 tmpByte = actor->BaseStats[IE_LEVELSORCERER];
2528 stream->Write( &tmpByte, 1);
2529 tmpByte = actor->BaseStats[IE_LEVELMAGE];
2530 stream->Write( &tmpByte, 1);
2531 //some stuffing
2532 stream->Write( filling, 22);
2533 //string references
2534 for (i=0;i<64;i++) {
2535 stream->WriteDword( &actor->StrRefs[i]);
2536 }
2537 stream->WriteResRef( actor->GetScript(SCR_AREA) );
2538 stream->WriteResRef( actor->GetScript(SCR_RESERVED) );
2539 //unknowns before feats
2540 stream->Write( filling,4);
2541 //feats
2542 stream->WriteDword( &actor->BaseStats[IE_FEATS1]);
2543 stream->WriteDword( &actor->BaseStats[IE_FEATS2]);
2544 stream->WriteDword( &actor->BaseStats[IE_FEATS3]);
2545 stream->Write( filling, 12);
2546 //proficiencies
2547 for (i=0;i<26;i++) {
2548 tmpByte = actor->BaseStats[IE_PROFICIENCYBASTARDSWORD+i];
2549 stream->Write( &tmpByte, 1);
2550 }
2551 stream->Write( filling, 38);
2552 //alchemy
2553 tmpByte = actor->BaseStats[IE_ALCHEMY];
2554 stream->Write( &tmpByte, 1);
2555 //animals
2556 tmpByte = actor->BaseStats[IE_ANIMALS];
2557 stream->Write( &tmpByte, 1);
2558 //bluff
2559 tmpByte = actor->BaseStats[IE_BLUFF];
2560 stream->Write( &tmpByte, 1);
2561 //concentration
2562 tmpByte = actor->BaseStats[IE_CONCENTRATION];
2563 stream->Write( &tmpByte, 1);
2564 //diplomacy
2565 tmpByte = actor->BaseStats[IE_DIPLOMACY];
2566 stream->Write( &tmpByte, 1);
2567 //disarm trap
2568 tmpByte = actor->BaseStats[IE_TRAPS];
2569 stream->Write( &tmpByte, 1);
2570 //hide
2571 tmpByte = actor->BaseStats[IE_HIDEINSHADOWS];
2572 stream->Write( &tmpByte, 1);
2573 //intimidate
2574 tmpByte = actor->BaseStats[IE_INTIMIDATE];
2575 stream->Write( &tmpByte, 1);
2576 //lore
2577 tmpByte = actor->BaseStats[IE_LORE];
2578 stream->Write( &tmpByte, 1);
2579 //move silently
2580 tmpByte = actor->BaseStats[IE_STEALTH];
2581 stream->Write( &tmpByte, 1);
2582 //open lock
2583 tmpByte = actor->BaseStats[IE_LOCKPICKING];
2584 stream->Write( &tmpByte, 1);
2585 //pickpocket
2586 tmpByte = actor->BaseStats[IE_PICKPOCKET];
2587 stream->Write( &tmpByte, 1);
2588 //search
2589 tmpByte = actor->BaseStats[IE_SEARCH];
2590 stream->Write( &tmpByte, 1);
2591 //spellcraft
2592 tmpByte = actor->BaseStats[IE_SPELLCRAFT];
2593 stream->Write( &tmpByte, 1);
2594 //use magic device
2595 tmpByte = actor->BaseStats[IE_MAGICDEVICE];
2596 stream->Write( &tmpByte, 1);
2597 //tracking
2598 tmpByte = actor->BaseStats[IE_TRACKING];
2599 stream->Write( &tmpByte, 1);
2600 stream->Write( filling, 50);
2601 tmpByte = actor->BaseStats[IE_CR];
2602 stream->Write( &tmpByte, 1);
2603 tmpByte = actor->BaseStats[IE_HATEDRACE];
2604 stream->Write( &tmpByte, 1);
2605 for (i=0;i<7;i++) {
2606 tmpByte = actor->BaseStats[IE_HATEDRACE2+i];
2607 stream->Write( &tmpByte, 1);
2608 }
2609 tmpByte = actor->BaseStats[IE_SUBRACE];
2610 stream->Write( &tmpByte, 1);
2611 stream->Write( filling, 1); //unknown
2612 tmpByte = actor->BaseStats[IE_SEX]; //
2613 stream->Write( &tmpByte, 1);
2614 tmpByte = actor->BaseStats[IE_STR];
2615 stream->Write( &tmpByte, 1);
2616 tmpByte = actor->BaseStats[IE_INT];
2617 stream->Write( &tmpByte, 1);
2618 tmpByte = actor->BaseStats[IE_WIS];
2619 stream->Write( &tmpByte, 1);
2620 tmpByte = actor->BaseStats[IE_DEX];
2621 stream->Write( &tmpByte, 1);
2622 tmpByte = actor->BaseStats[IE_CON];
2623 stream->Write( &tmpByte, 1);
2624 tmpByte = actor->BaseStats[IE_CHR];
2625 stream->Write( &tmpByte, 1);
2626 tmpByte = actor->BaseStats[IE_MORALE];
2627 stream->Write( &tmpByte, 1);
2628 tmpByte = actor->BaseStats[IE_MORALEBREAK];
2629 stream->Write( &tmpByte, 1);
2630 tmpByte = actor->BaseStats[IE_MORALERECOVERYTIME];
2631 stream->Write( &tmpByte, 1);
2632 // unknown byte
2633 stream->Write( &filling,1);
2634 // no kit word order magic for iwd2
2635 stream->WriteDword( &actor->BaseStats[IE_KIT] );
2636 stream->WriteResRef( actor->GetScript(SCR_OVERRIDE) );
2637 stream->WriteResRef( actor->GetScript(SCR_CLASS) );
2638 stream->WriteResRef( actor->GetScript(SCR_RACE) );
2639 stream->WriteResRef( actor->GetScript(SCR_GENERAL) );
2640 stream->WriteResRef( actor->GetScript(SCR_DEFAULT) );
2641 } else {
2642 for (i=0;i<21;i++) {
2643 tmpByte = actor->BaseStats[IE_PROFICIENCYBASTARDSWORD+i];
2644 stream->Write( &tmpByte, 1);
2645 }
2646 tmpByte = actor->BaseStats[IE_TRACKING];
2647 stream->Write( &tmpByte, 1);
2648 stream->Write( filling, 32);
2649 for (i=0; i<VCONST_COUNT; i++) {
2650 stream->WriteDword( &actor->StrRefs[i]);
2651 }
2652 tmpByte = actor->BaseStats[IE_LEVEL];
2653 stream->Write( &tmpByte, 1);
2654 tmpByte = actor->BaseStats[IE_LEVEL2];
2655 stream->Write( &tmpByte, 1);
2656 tmpByte = actor->BaseStats[IE_LEVEL3];
2657 stream->Write( &tmpByte, 1);
2658 tmpByte = actor->BaseStats[IE_SEX]; //
2659 stream->Write( &tmpByte, 1);
2660 tmpByte = actor->BaseStats[IE_STR];
2661 stream->Write( &tmpByte, 1);
2662 tmpByte = actor->BaseStats[IE_STREXTRA];
2663 stream->Write( &tmpByte, 1);
2664 tmpByte = actor->BaseStats[IE_INT];
2665 stream->Write( &tmpByte, 1);
2666 tmpByte = actor->BaseStats[IE_WIS];
2667 stream->Write( &tmpByte, 1);
2668 tmpByte = actor->BaseStats[IE_DEX];
2669 stream->Write( &tmpByte, 1);
2670 tmpByte = actor->BaseStats[IE_CON];
2671 stream->Write( &tmpByte, 1);
2672 tmpByte = actor->BaseStats[IE_CHR];
2673 stream->Write( &tmpByte, 1);
2674 tmpByte = actor->BaseStats[IE_MORALE];
2675 stream->Write( &tmpByte, 1);
2676 tmpByte = actor->BaseStats[IE_MORALEBREAK];
2677 stream->Write( &tmpByte, 1);
2678 tmpByte = actor->BaseStats[IE_HATEDRACE];
2679 stream->Write( &tmpByte, 1);
2680 tmpByte = actor->BaseStats[IE_MORALERECOVERYTIME];
2681 stream->Write( &tmpByte, 1);
2682 // unknown byte
2683 stream->Write( &filling, 1);
2684 tmpDword = ((actor->BaseStats[IE_KIT] & 0xffff) << 16) +
2685 ((actor->BaseStats[IE_KIT] & 0xffff0000) >> 16);
2686 stream->WriteDword( &tmpDword );
2687 stream->WriteResRef( actor->GetScript(SCR_OVERRIDE) );
2688 stream->WriteResRef( actor->GetScript(SCR_CLASS) );
2689 stream->WriteResRef( actor->GetScript(SCR_RACE) );
2690 stream->WriteResRef( actor->GetScript(SCR_GENERAL) );
2691 stream->WriteResRef( actor->GetScript(SCR_DEFAULT) );
2692 }
2693 //now follows the fuzzy part in separate putactor... functions
2694 return 0;
2695 }
2696
PutActorGemRB(DataStream * stream,const Actor * actor,ieDword InvSize)2697 int CREImporter::PutActorGemRB(DataStream *stream, const Actor *actor, ieDword InvSize)
2698 {
2699 ieByte tmpByte;
2700 char filling[5];
2701
2702 memset(filling,0,sizeof(filling));
2703 //similar in all engines
2704 tmpByte = actor->BaseStats[IE_EA];
2705 stream->Write( &tmpByte, 1);
2706 tmpByte = actor->BaseStats[IE_GENERAL];
2707 stream->Write( &tmpByte, 1);
2708 tmpByte = actor->BaseStats[IE_RACE];
2709 stream->Write( &tmpByte, 1);
2710 tmpByte = actor->BaseStats[IE_CLASS];
2711 stream->Write( &tmpByte, 1);
2712 tmpByte = actor->BaseStats[IE_SPECIFIC];
2713 stream->Write( &tmpByte, 1);
2714 tmpByte = actor->BaseStats[IE_SEX];
2715 stream->Write( &tmpByte, 1);
2716 stream->Write( filling, 5); //unknown bytes
2717 tmpByte = actor->BaseStats[IE_ALIGNMENT];
2718 stream->Write( &tmpByte, 1);
2719 stream->WriteDword( &InvSize ); //saving the inventory size to this unused part
2720 stream->Write( actor->GetScriptName(), 32);
2721 return 0;
2722 }
2723
PutActorBG(DataStream * stream,const Actor * actor)2724 int CREImporter::PutActorBG(DataStream *stream, const Actor *actor)
2725 {
2726 ieByte tmpByte;
2727 char filling[5];
2728
2729 memset(filling,0,sizeof(filling));
2730 //similar in all engines
2731 tmpByte = actor->BaseStats[IE_EA];
2732 stream->Write( &tmpByte, 1);
2733 tmpByte = actor->BaseStats[IE_GENERAL];
2734 stream->Write( &tmpByte, 1);
2735 tmpByte = actor->BaseStats[IE_RACE];
2736 stream->Write( &tmpByte, 1);
2737 tmpByte = actor->BaseStats[IE_CLASS];
2738 stream->Write( &tmpByte, 1);
2739 tmpByte = actor->BaseStats[IE_SPECIFIC];
2740 stream->Write( &tmpByte, 1);
2741 tmpByte = actor->BaseStats[IE_SEX];
2742 stream->Write( &tmpByte, 1);
2743 stream->Write( filling, 5); //unknown bytes
2744 tmpByte = actor->BaseStats[IE_ALIGNMENT];
2745 stream->Write( &tmpByte, 1);
2746 stream->Write( filling,4); //this is called ID in iwd2, and contains 2 words
2747 stream->Write( actor->GetScriptName(), 32);
2748 return 0;
2749 }
2750
PutActorPST(DataStream * stream,const Actor * actor)2751 int CREImporter::PutActorPST(DataStream *stream, const Actor *actor)
2752 {
2753 ieByte tmpByte;
2754 ieWord tmpWord;
2755 int i;
2756 char filling[44];
2757
2758 memset(filling,0,sizeof(filling));
2759 stream->Write(filling, 44); //11*4 totally unknown
2760 stream->WriteDword( &actor->BaseStats[IE_XP_MAGE]);
2761 stream->WriteDword( &actor->BaseStats[IE_XP_THIEF]);
2762 for (i = 0; i<10; i++) {
2763 tmpWord = actor->BaseStats[IE_INTERNAL_0];
2764 stream->WriteWord( &tmpWord );
2765 }
2766 for (i = 0; i<4; i++) {
2767 tmpByte = (ieByte) (actor->DeathCounters[i]);
2768 stream->Write( &tmpByte, 1);
2769 }
2770 stream->Write( actor->KillVar, 32);
2771 stream->Write( filling,3); //unknown
2772 tmpByte=actor->BaseStats[IE_COLORCOUNT];
2773 stream->Write( &tmpByte, 1);
2774 stream->WriteDword( &actor->AppearanceFlags);
2775
2776 for (i=0;i<7;i++) {
2777 tmpWord = actor->BaseStats[IE_COLORS+i];
2778 stream->WriteWord( &tmpWord);
2779 }
2780 stream->Write(actor->pstColorBytes, 10);
2781 stream->Write(filling, 21);
2782 tmpByte = actor->BaseStats[IE_SPECIES];
2783 stream->Write( &tmpByte, 1);
2784 tmpByte = actor->BaseStats[IE_TEAM];
2785 stream->Write( &tmpByte, 1);
2786 tmpByte = actor->BaseStats[IE_FACTION];
2787 stream->Write( &tmpByte, 1);
2788 //similar in all engines
2789 tmpByte = actor->BaseStats[IE_EA];
2790 stream->Write( &tmpByte, 1);
2791 tmpByte = actor->BaseStats[IE_GENERAL];
2792 stream->Write( &tmpByte, 1);
2793 tmpByte = actor->BaseStats[IE_RACE];
2794 stream->Write( &tmpByte, 1);
2795 tmpByte = actor->BaseStats[IE_CLASS];
2796 stream->Write( &tmpByte, 1);
2797 tmpByte = actor->BaseStats[IE_SPECIFIC];
2798 stream->Write( &tmpByte, 1);
2799 tmpByte = actor->BaseStats[IE_SEX];
2800 stream->Write( &tmpByte, 1);
2801 stream->Write( filling, 5); //unknown bytes
2802 tmpByte = actor->BaseStats[IE_ALIGNMENT];
2803 stream->Write( &tmpByte, 1);
2804 stream->Write( filling,4); //this is called ID in iwd2, and contains 2 words
2805 stream->Write( actor->GetScriptName(), 32);
2806 return 0;
2807 }
2808
PutActorIWD1(DataStream * stream,const Actor * actor)2809 int CREImporter::PutActorIWD1(DataStream *stream, const Actor *actor)
2810 {
2811 ieByte tmpByte;
2812 ieWord tmpWord;
2813 int i;
2814 char filling[52];
2815
2816 memset(filling,0,sizeof(filling));
2817 tmpByte=(ieByte) actor->BaseStats[IE_AVATARREMOVAL];
2818 stream->Write( &tmpByte, 1);
2819 stream->Write( &actor->SetDeathVar, 1);
2820 stream->Write( &actor->IncKillCount, 1);
2821 stream->Write( &actor->UnknownField, 1); //unknown
2822 for (i=0;i<5;i++) {
2823 tmpWord = actor->BaseStats[IE_INTERNAL_0+i];
2824 stream->WriteWord( &tmpWord);
2825 }
2826 stream->Write( actor->KillVar, 32); //some variable names in iwd
2827 stream->Write( actor->IncKillVar, 32); //some variable names in iwd
2828 stream->Write( filling, 2);
2829 tmpWord = actor->BaseStats[IE_SAVEDXPOS];
2830 stream->WriteWord( &tmpWord);
2831 tmpWord = actor->BaseStats[IE_SAVEDYPOS];
2832 stream->WriteWord( &tmpWord);
2833 tmpWord = actor->BaseStats[IE_SAVEDFACE];
2834 stream->WriteWord( &tmpWord);
2835 stream->Write( filling, 18);
2836 //similar in all engines
2837 tmpByte = actor->BaseStats[IE_EA];
2838 stream->Write( &tmpByte, 1);
2839 tmpByte = actor->BaseStats[IE_GENERAL];
2840 stream->Write( &tmpByte, 1);
2841 tmpByte = actor->BaseStats[IE_RACE];
2842 stream->Write( &tmpByte, 1);
2843 tmpByte = actor->BaseStats[IE_CLASS];
2844 stream->Write( &tmpByte, 1);
2845 tmpByte = actor->BaseStats[IE_SPECIFIC];
2846 stream->Write( &tmpByte, 1);
2847 tmpByte = actor->BaseStats[IE_SEX];
2848 stream->Write( &tmpByte, 1);
2849 stream->Write( filling, 5); //unknown bytes
2850 tmpByte = actor->BaseStats[IE_ALIGNMENT];
2851 stream->Write( &tmpByte, 1);
2852 stream->Write( filling,4); //this is called ID in iwd2, and contains 2 words
2853 stream->Write( actor->GetScriptName(), 32);
2854 return 0;
2855 }
2856
PutActorIWD2(DataStream * stream,const Actor * actor)2857 int CREImporter::PutActorIWD2(DataStream *stream, const Actor *actor)
2858 {
2859 ieByte tmpByte;
2860 ieWord tmpWord;
2861 ieDword tmpDword;
2862 int i;
2863 char filling[124];
2864
2865 memset(filling,0,sizeof(filling));
2866 tmpByte=(ieByte) actor->BaseStats[IE_AVATARREMOVAL];
2867 stream->Write( &tmpByte, 1);
2868 stream->Write( &actor->SetDeathVar, 1);
2869 stream->Write( &actor->IncKillCount, 1);
2870 stream->Write( &actor->UnknownField, 1); //unknown
2871 for (i=0;i<5;i++) {
2872 tmpWord = actor->BaseStats[IE_INTERNAL_0+i];
2873 stream->WriteWord( &tmpWord);
2874 }
2875 stream->Write( actor->KillVar, 32); //some variable names in iwd
2876 stream->Write( actor->IncKillVar, 32); //some variable names in iwd
2877 stream->Write( filling, 2);
2878 tmpWord = actor->BaseStats[IE_SAVEDXPOS];
2879 stream->WriteWord( &tmpWord);
2880 tmpWord = actor->BaseStats[IE_SAVEDYPOS];
2881 stream->WriteWord( &tmpWord);
2882 tmpWord = actor->BaseStats[IE_SAVEDFACE];
2883 stream->WriteWord( &tmpWord);
2884 stream->Write( filling, 15);
2885 tmpByte = actor->BaseStats[IE_TRANSLUCENT];
2886 stream->Write(&tmpByte, 1);
2887 stream->Write(filling, 1); //fade speed
2888 tmpByte = actor->BaseStats[IE_SPECFLAGS];
2889 stream->Write(&tmpByte, 1);
2890 stream->Write(filling, 3); //invisible, 2 unknowns
2891 tmpByte = actor->BaseStats[IE_UNUSED_SKILLPTS];
2892 stream->Write( &tmpByte, 1);
2893 stream->Write( filling, 124);
2894 //similar in all engines
2895 tmpByte = actor->BaseStats[IE_EA];
2896 stream->Write( &tmpByte, 1);
2897 tmpByte = actor->BaseStats[IE_GENERAL];
2898 stream->Write( &tmpByte, 1);
2899 tmpByte = actor->BaseStats[IE_RACE];
2900 stream->Write( &tmpByte, 1);
2901 tmpByte = actor->BaseStats[IE_CLASS];
2902 stream->Write( &tmpByte, 1);
2903 tmpByte = actor->BaseStats[IE_SPECIFIC];
2904 stream->Write( &tmpByte, 1);
2905 tmpByte = actor->BaseStats[IE_SEX];
2906 stream->Write( &tmpByte, 1);
2907 stream->Write( filling, 5); //unknown bytes
2908 tmpByte = actor->BaseStats[IE_ALIGNMENT];
2909 stream->Write( &tmpByte, 1);
2910 stream->Write( filling,4); //this is called ID in iwd2, and contains 2 words
2911 stream->Write( actor->GetScriptName(), 32);
2912 tmpByte = actor->BaseStats[IE_CLASS];
2913 stream->Write(&tmpByte, 1);
2914 stream->Write(filling, 1);
2915 tmpDword = actor->GetClassMask();
2916 stream->WriteDword(&tmpDword);
2917 return 0;
2918 }
2919
PutKnownSpells(DataStream * stream,const Actor * actor)2920 int CREImporter::PutKnownSpells(DataStream *stream, const Actor *actor)
2921 {
2922 int type=actor->spellbook.GetTypes();
2923 for (int i=0;i<type;i++) {
2924 unsigned int level = actor->spellbook.GetSpellLevelCount(i);
2925 for (unsigned int j=0;j<level;j++) {
2926 unsigned int count = actor->spellbook.GetKnownSpellsCount(i, j);
2927 for (int k=count-1;k>=0;k--) {
2928 const CREKnownSpell *ck = actor->spellbook.GetKnownSpell(i, j, k);
2929 assert(ck);
2930 stream->WriteResRef(ck->SpellResRef);
2931 stream->WriteWord( &ck->Level);
2932 stream->WriteWord( &ck->Type);
2933 }
2934 }
2935 }
2936 return 0;
2937 }
2938
PutSpellPages(DataStream * stream,const Actor * actor)2939 int CREImporter::PutSpellPages(DataStream *stream, const Actor *actor)
2940 {
2941 ieWord tmpWord;
2942 ieDword tmpDword;
2943 ieDword SpellIndex = 0;
2944
2945 int type=actor->spellbook.GetTypes();
2946 for (int i=0;i<type;i++) {
2947 unsigned int level = actor->spellbook.GetSpellLevelCount(i);
2948 for (unsigned int j=0;j<level;j++) {
2949 tmpWord = j; //+1
2950 stream->WriteWord( &tmpWord);
2951 tmpWord = actor->spellbook.GetMemorizableSpellsCount(i,j,false);
2952 stream->WriteWord( &tmpWord);
2953 tmpWord = actor->spellbook.GetMemorizableSpellsCount(i,j,true);
2954 stream->WriteWord( &tmpWord);
2955 tmpWord = i;
2956 stream->WriteWord( &tmpWord);
2957 stream->WriteDword( &SpellIndex);
2958 tmpDword = actor->spellbook.GetMemorizedSpellsCount(i,j, false);
2959 stream->WriteDword( &tmpDword);
2960 SpellIndex += tmpDword;
2961 }
2962 }
2963 return 0;
2964 }
2965
PutMemorizedSpells(DataStream * stream,const Actor * actor)2966 int CREImporter::PutMemorizedSpells(DataStream *stream, const Actor *actor)
2967 {
2968 int type=actor->spellbook.GetTypes();
2969 for (int i=0;i<type;i++) {
2970 unsigned int level = actor->spellbook.GetSpellLevelCount(i);
2971 for (unsigned int j=0;j<level;j++) {
2972 unsigned int count = actor->spellbook.GetMemorizedSpellsCount(i,j, false);
2973 for (unsigned int k=0;k<count;k++) {
2974 const CREMemorizedSpell *cm = actor->spellbook.GetMemorizedSpell(i, j, k);
2975 assert(cm);
2976 stream->WriteResRef( cm->SpellResRef);
2977 stream->WriteDword( &cm->Flags);
2978 }
2979 }
2980 }
2981 return 0;
2982 }
2983
PutEffects(DataStream * stream,const Actor * actor)2984 int CREImporter::PutEffects( DataStream *stream, const Actor *actor)
2985 {
2986 PluginHolder<EffectMgr> eM(IE_EFF_CLASS_ID);
2987 assert(eM != nullptr);
2988
2989 std::list< Effect* >::const_iterator f=actor->fxqueue.GetFirstEffect();
2990 for(unsigned int i=0;i<EffectsCount;i++) {
2991 const Effect *fx = actor->fxqueue.GetNextSavedEffect(f);
2992
2993 assert(fx!=NULL);
2994
2995 if (TotSCEFF) {
2996 eM->PutEffectV2(stream, fx);
2997 } else {
2998 ieWord tmpWord;
2999 ieByte tmpByte;
3000 char filling[60];
3001
3002 memset(filling,0,sizeof(filling) );
3003
3004 tmpWord = (ieWord) fx->Opcode;
3005 stream->WriteWord( &tmpWord);
3006 tmpByte = (ieByte) fx->Target;
3007 stream->Write(&tmpByte, 1);
3008 tmpByte = (ieByte) fx->Power;
3009 stream->Write(&tmpByte, 1);
3010 stream->WriteDword( &fx->Parameter1);
3011 stream->WriteDword( &fx->Parameter2);
3012 tmpByte = (ieByte) fx->TimingMode;
3013 stream->Write(&tmpByte, 1);
3014 tmpByte = (ieByte) fx->Resistance;
3015 stream->Write(&tmpByte, 1);
3016 stream->WriteDword( &fx->Duration);
3017 tmpByte = (ieByte) fx->ProbabilityRangeMax;
3018 stream->Write(&tmpByte, 1);
3019 tmpByte = (ieByte) fx->ProbabilityRangeMin;
3020 stream->Write(&tmpByte, 1);
3021 stream->Write(fx->Resource, 8);
3022 stream->WriteDword( &fx->DiceThrown );
3023 stream->WriteDword( &fx->DiceSides );
3024 stream->WriteDword( &fx->SavingThrowType );
3025 stream->WriteDword( &fx->SavingThrowBonus );
3026 //isvariable
3027 stream->Write( filling,4 );
3028 }
3029 }
3030 return 0;
3031 }
3032
3033 //add as effect!
PutVariables(DataStream * stream,const Actor * actor)3034 int CREImporter::PutVariables(DataStream *stream, const Actor *actor)
3035 {
3036 char filling[104];
3037 Variables::iterator pos=NULL;
3038 const char *name;
3039 ieDword tmpDword, value;
3040
3041 for (unsigned int i=0;i<VariablesCount;i++) {
3042 memset(filling,0,sizeof(filling) );
3043 pos = actor->locals->GetNextAssoc( pos, name, value);
3044 stream->Write(filling,8);
3045 tmpDword = FAKE_VARIABLE_OPCODE;
3046 stream->WriteDword( &tmpDword);
3047 stream->Write(filling,8); //type, power
3048 stream->WriteDword( &value); //param #1
3049 stream->Write(filling,4); //param #2
3050 //HoW has an assertion to ensure timing is nonzero (even for variables)
3051 value = 1;
3052 stream->WriteDword( &value); //timing
3053 //duration, chance, resource, dices, saves
3054 stream->Write( filling, 32);
3055 tmpDword = FAKE_VARIABLE_MARKER;
3056 stream->WriteDword( &tmpDword); //variable marker
3057 stream->Write( filling, 92); //23 * 4
3058 strnspccpy(filling, name, 32);
3059 stream->Write( filling, 104); //32 + 72
3060 }
3061 return 0;
3062 }
3063
3064 //Don't forget to add 8 for the totals/bonus fields
GetIWD2SpellpageSize(Actor * actor,ieIWD2SpellType type,int level) const3065 ieDword CREImporter::GetIWD2SpellpageSize(Actor *actor, ieIWD2SpellType type, int level) const
3066 {
3067 CRESpellMemorization* sm = actor->spellbook.GetSpellMemorization(type, level);
3068 ieDword cnt = sm->known_spells.size();
3069 return cnt;
3070 }
3071
PutIWD2Spellpage(DataStream * stream,Actor * actor,ieIWD2SpellType type,int level)3072 int CREImporter::PutIWD2Spellpage(DataStream *stream, Actor *actor, ieIWD2SpellType type, int level)
3073 {
3074 ieDword ID, max, known;
3075
3076 CRESpellMemorization* sm = actor->spellbook.GetSpellMemorization(type, level);
3077 for (unsigned int k = 0; k < sm->known_spells.size(); k++) {
3078 const CREKnownSpell *ck = sm->known_spells[k];
3079 ID = ResolveSpellName(ck->SpellResRef, level, type);
3080 stream->WriteDword ( &ID);
3081 max = actor->spellbook.CountSpells(ck->SpellResRef, type, 1);
3082 known = actor->spellbook.CountSpells(ck->SpellResRef, type, 0);
3083 stream->WriteDword ( &max);
3084 stream->WriteDword ( &known);
3085 //unknown field (always 0)
3086 known = 0;
3087 stream->WriteDword (&known);
3088 }
3089
3090 max = sm->SlotCount;
3091 known = sm->SlotCountWithBonus;
3092 stream->WriteDword ( &max);
3093 stream->WriteDword ( &known);
3094 return 0;
3095 }
3096
3097 /* this function expects GetStoredFileSize to be called before */
PutActor(DataStream * stream,Actor * actor,bool chr)3098 int CREImporter::PutActor(DataStream *stream, Actor *actor, bool chr)
3099 {
3100 ieDword tmpDword=0;
3101 int ret;
3102
3103 if (!stream || !actor) {
3104 return -1;
3105 }
3106
3107 IsCharacter = chr;
3108 if (chr) {
3109 WriteChrHeader( stream, actor );
3110 }
3111 assert(TotSCEFF==0 || TotSCEFF==1);
3112
3113 CREOffset = stream->GetPos(); // for asserts
3114
3115 ret = PutHeader( stream, actor);
3116 if (ret) {
3117 return ret;
3118 }
3119 //here comes the fuzzy part
3120 ieDword Inventory_Size;
3121
3122 switch (CREVersion) {
3123 case IE_CRE_GEMRB:
3124 //don't add fist
3125 Inventory_Size=(ieDword) actor->inventory.GetSlotCount()-1;
3126 ret = PutActorGemRB(stream, actor, Inventory_Size);
3127 break;
3128 case IE_CRE_V1_2:
3129 Inventory_Size=46;
3130 ret = PutActorPST(stream, actor);
3131 break;
3132 case IE_CRE_V1_1:
3133 case IE_CRE_V1_0: //bg1/bg2
3134 Inventory_Size=38;
3135 ret = PutActorBG(stream, actor);
3136 break;
3137 case IE_CRE_V2_2:
3138 Inventory_Size=50;
3139 ret = PutActorIWD2(stream, actor);
3140 break;
3141 case IE_CRE_V9_0:
3142 Inventory_Size=38;
3143 ret = PutActorIWD1(stream, actor);
3144 break;
3145 default:
3146 return -1;
3147 }
3148 if (ret) {
3149 return ret;
3150 }
3151
3152 //writing offsets and counts
3153 if (actor->version==IE_CRE_V2_2) {
3154 int type, level;
3155
3156 //class spells
3157 for (type=IE_IWD2_SPELL_BARD;type<IE_IWD2_SPELL_DOMAIN;type++) for(level=0;level<9;level++) {
3158 tmpDword = GetIWD2SpellpageSize(actor, (ieIWD2SpellType) type, level);
3159 stream->WriteDword(&KnownSpellsOffset);
3160 KnownSpellsOffset+=tmpDword*16+8;
3161 }
3162 for (type=IE_IWD2_SPELL_BARD;type<IE_IWD2_SPELL_DOMAIN;type++) for(level=0;level<9;level++) {
3163 tmpDword = GetIWD2SpellpageSize(actor, (ieIWD2SpellType) type, level);
3164 stream->WriteDword(&tmpDword);
3165 }
3166 //domain spells
3167 for (level=0;level<9;level++) {
3168 tmpDword = GetIWD2SpellpageSize(actor, IE_IWD2_SPELL_DOMAIN, level);
3169 stream->WriteDword(&KnownSpellsOffset);
3170 KnownSpellsOffset+=tmpDword*16+8;
3171 }
3172 for (level=0;level<9;level++) {
3173 tmpDword = GetIWD2SpellpageSize(actor, IE_IWD2_SPELL_DOMAIN, level);
3174 stream->WriteDword(&tmpDword);
3175 }
3176 //innates, shapes, songs
3177 for (type=IE_IWD2_SPELL_INNATE;type<NUM_IWD2_SPELLTYPES;type++) {
3178 tmpDword = GetIWD2SpellpageSize(actor, (ieIWD2SpellType) type, 0);
3179 stream->WriteDword(&KnownSpellsOffset);
3180 KnownSpellsOffset+=tmpDword*16+8;
3181 stream->WriteDword(&tmpDword);
3182 }
3183 } else {
3184 stream->WriteDword( &KnownSpellsOffset);
3185 stream->WriteDword( &KnownSpellsCount);
3186 stream->WriteDword( &SpellMemorizationOffset );
3187 stream->WriteDword( &SpellMemorizationCount );
3188 stream->WriteDword( &MemorizedSpellsOffset );
3189 stream->WriteDword( &MemorizedSpellsCount );
3190 }
3191 stream->WriteDword( &ItemSlotsOffset );
3192 stream->WriteDword( &ItemsOffset );
3193 stream->WriteDword( &ItemsCount );
3194 stream->WriteDword( &EffectsOffset );
3195 tmpDword = EffectsCount+VariablesCount;
3196 stream->WriteDword( &tmpDword );
3197 tmpDword = 0;
3198 stream->WriteResRef( actor->GetDialog(false) );
3199 //spells, spellbook etc
3200
3201 if (actor->version==IE_CRE_V2_2) {
3202 int type, level;
3203
3204 //writing out spell page headers
3205 for (type=IE_IWD2_SPELL_BARD;type<IE_IWD2_SPELL_DOMAIN;type++) for(level=0;level<9;level++) {
3206 PutIWD2Spellpage(stream, actor, (ieIWD2SpellType) type, level);
3207 }
3208
3209 //writing out domain page headers
3210 for (level=0;level<9;level++) {
3211 PutIWD2Spellpage(stream, actor, IE_IWD2_SPELL_DOMAIN, level);
3212 }
3213
3214 //innates, shapes, songs
3215 for (type = IE_IWD2_SPELL_INNATE; type<NUM_IWD2_SPELLTYPES; type ++) {
3216 PutIWD2Spellpage(stream, actor, (ieIWD2SpellType) type, 0);
3217 }
3218 } else {
3219 assert(stream->GetPos() == CREOffset+KnownSpellsOffset);
3220 ret = PutKnownSpells( stream, actor);
3221 if (ret) {
3222 return ret;
3223 }
3224 assert(stream->GetPos() == CREOffset+SpellMemorizationOffset);
3225 ret = PutSpellPages( stream, actor);
3226 if (ret) {
3227 return ret;
3228 }
3229 assert(stream->GetPos() == CREOffset+MemorizedSpellsOffset);
3230 ret = PutMemorizedSpells( stream, actor);
3231 if (ret) {
3232 return ret;
3233 }
3234 }
3235
3236 //items and inventory slots
3237 assert(stream->GetPos() == CREOffset+ItemSlotsOffset);
3238 ret = PutInventory( stream, actor, Inventory_Size);
3239 if (ret) {
3240 return ret;
3241 }
3242
3243 assert(stream->GetPos() == CREOffset+EffectsOffset);
3244 ret = PutEffects(stream, actor);
3245 if (ret) {
3246 return ret;
3247 }
3248 //effects and variables
3249 ret = PutVariables(stream, actor);
3250 if (ret) {
3251 return ret;
3252 }
3253
3254 return 0;
3255 }
3256
3257 #include "plugindef.h"
3258
3259 GEMRB_PLUGIN(0xE507B60, "CRE File Importer")
3260 PLUGIN_CLASS(IE_CRE_CLASS_ID, CREImporter)
3261 PLUGIN_CLEANUP(ReleaseMemoryCRE)
3262 END_PLUGIN()
3263