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