The Great Rename (Part 1)
[gemrb.git] / gemrb / plugins / TLKImporter / TLKImporter.cpp
blob83e7d6626d42ae514fdb94b5b37a8398f5736973
1 /* GemRB - Infinity Engine Emulator
2 * Copyright (C) 2003 The GemRB Project
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.
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.
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.
18 * $Id$
22 #include "../../includes/win32def.h"
23 #include "TLKImporter.h"
24 #include "../Core/Interface.h"
25 #include "../Core/Audio.h"
26 #include "../Core/Game.h"
27 #include "../Core/GameControl.h"
28 #include "../Core/Calendar.h"
30 //static int *monthnames=NULL;
31 //static int *days=NULL;
32 //static int monthnamecount=0;
33 //set this to -1 if charname is gabber (iwd2)
34 static int charname=0;
36 TLKImp::TLKImp(void)
38 if (core->HasFeature(GF_CHARNAMEISGABBER)) {
39 charname=-1;
40 } else {
41 charname=0;
43 str = NULL;
44 override = NULL;
45 autoFree = false;
47 if (monthnamecount==0) {
48 int i;
49 AutoTable tab("months");
50 if (!tab) {
51 monthnamecount=-1;
52 return;
54 monthnamecount = tab->GetRowCount();
55 monthnames = (int *) malloc(sizeof(int) * monthnamecount);
56 days = (int *) malloc(sizeof(int) * monthnamecount);
57 for(i=0;i<monthnamecount;i++) {
58 days[i]=atoi(tab->QueryField(i,0));
59 monthnames[i]=atoi(tab->QueryField(i,1));
65 TLKImp::~TLKImp(void)
68 if (monthnames) free(monthnames);
69 if (days) free(days);
70 monthnamecount=0;
72 if (str && autoFree) {
73 delete( str );
75 CloseAux();
78 void TLKImp::CloseAux()
80 if (override) {
81 delete override;
83 override = NULL;
86 void TLKImp::OpenAux()
88 CloseAux();
89 override = new CTlkOverride();
90 if (override) {
91 if (!override->Init()) {
92 CloseAux();
93 printMessage("TlkImporter","Cannot open tlk override!\n", LIGHT_RED);
98 bool TLKImp::Open(DataStream* stream, bool autoFree)
100 if (stream == NULL) {
101 return false;
103 if (str && this->autoFree) {
104 delete( str );
106 str = stream;
107 this->autoFree = autoFree;
108 char Signature[8];
109 str->Read( Signature, 8 );
110 if (strncmp( Signature, "TLK\x20V1\x20\x20", 8 ) != 0) {
111 printMessage( "TLKImporter","Not a valid TLK File.\n", LIGHT_RED );
112 return false;
114 str->Seek( 2, GEM_CURRENT_POS );
115 str->ReadDword( &StrRefCount );
116 str->ReadDword( &Offset );
117 return true;
120 inline char* mystrncpy(char* dest, const char* source, int maxlength,
121 char delim)
123 while (*source && ( *source != delim ) && maxlength--) {
124 *dest++ = *source++;
126 *dest = 0;
127 return dest;
130 /* -1 - GABBER
131 0 - PROTAGONIST
132 1-9 - PLAYERx
134 inline Actor *GetActorFromSlot(int slot)
136 if (slot==-1) {
137 GameControl *gc = core->GetGameControl();
138 if (gc) {
139 return gc->GetSpeaker();
141 return NULL;
143 Game *game = core->GetGame();
144 if (!game) {
145 return NULL;
147 if (slot==0) {
148 return game->GetPC(0, false); //protagonist
150 return game->FindPC(slot);
153 char *TLKImp::Gabber()
155 Actor *act;
157 act=core->GetGameControl()->GetSpeaker();
158 if (act) {
159 return override->CS(act->LongName);
161 return override->CS("?");
164 char *TLKImp::CharName(int slot)
166 Actor *act;
168 act=GetActorFromSlot(slot);
169 if (act) {
170 return override->CS(act->LongName);
172 return override->CS("?");
175 int TLKImp::RaceStrRef(int slot)
177 Actor *act;
178 int race;
180 act=GetActorFromSlot(slot);
181 if (act) {
182 race=act->GetStat(IE_RACE);
183 } else {
184 race=0;
187 AutoTable tab("races");
188 if (!tab) {
189 return -1;
191 int row = tab->FindTableValue(3, race, 0);
192 return atoi(tab->QueryField(row,0) );
195 int TLKImp::GenderStrRef(int slot, int malestrref, int femalestrref)
197 Actor *act;
199 act = GetActorFromSlot(slot);
200 if (act && (act->GetStat(IE_SEX)==SEX_FEMALE) ) {
201 return femalestrref;
203 return malestrref;
207 void TLKImp::GetMonthName(int dayandmonth)
209 int month=1;
211 for(int i=0;i<monthnamecount;i++) {
212 if (dayandmonth<days[i]) {
213 char *tmp;
214 char tmpstr[10];
216 sprintf(tmpstr,"%d", dayandmonth+1);
217 core->GetTokenDictionary()->SetAtCopy("DAY", tmpstr);
219 tmp = GetString( monthnames[i] );
220 core->GetTokenDictionary()->SetAt("MONTHNAME",tmp);
221 //must not free tmp, SetAt doesn't copy the pointer!
223 sprintf(tmpstr,"%d", month);
224 core->GetTokenDictionary()->SetAtCopy("MONTH",tmpstr);
225 return;
227 dayandmonth-=days[i];
228 //ignoring single days (they are not months)
229 if (days[i]!=1) month++;
234 //if this function returns -1 then it is not a built in token, dest may be NULL
235 int TLKImp::BuiltinToken(char* Token, char* dest)
237 char* Decoded = NULL;
238 int TokenLength; //decoded token length
240 //these are hardcoded, all engines are the same or don't use them
241 if (!strcmp( Token, "DAYANDMONTH")) {
242 ieDword dayandmonth=0;
243 core->GetDictionary()->Lookup("DAYANDMONTH",dayandmonth);
244 //preparing sub-tokens
245 core->GetCalendar()->GetMonthName((int) dayandmonth);
246 Decoded = GetString( 15981, 0 );
247 goto exit_function;
250 if (!strcmp( Token, "FIGHTERTYPE" )) {
251 Decoded = GetString( 10174, 0 );
252 goto exit_function;
254 if (!strcmp( Token, "RACE" )) {
255 Decoded = GetString( RaceStrRef(-1), 0);
256 goto exit_function;
258 if (!strcmp( Token, "SIRMAAM" )) {
259 Decoded = GetString( GenderStrRef(-1,27473,27475), 0);
260 goto exit_function;
262 if (!strcmp( Token, "GIRLBOY" )) {
263 Decoded = GetString( GenderStrRef(-1,27477,27476), 0);
264 goto exit_function;
266 if (!strcmp( Token, "BROTHERSISTER" )) {
267 Decoded = GetString( GenderStrRef(-1,27478,27479), 0);
268 goto exit_function;
270 if (!strcmp( Token, "LADYLORD" )) {
271 Decoded = GetString( GenderStrRef(-1,27481,27480), 0);
272 goto exit_function;
274 if (!strcmp( Token, "MALEFEMALE" )) {
275 Decoded = GetString( GenderStrRef(-1,27482,27483), 0);
276 goto exit_function;
278 if (!strcmp( Token, "HESHE" )) {
279 Decoded = GetString( GenderStrRef(-1,27484,27485), 0);
280 goto exit_function;
282 if (!strcmp( Token, "HISHER" )) {
283 Decoded = GetString( GenderStrRef(-1,27486,27487), 0);
284 goto exit_function;
286 if (!strcmp( Token, "HIMHER" )) {
287 Decoded = GetString( GenderStrRef(-1,27488,27487), 0);
288 goto exit_function;
290 if (!strcmp( Token, "MANWOMAN" )) {
291 Decoded = GetString( GenderStrRef(-1,27489,27490), 0);
292 goto exit_function;
294 if (!strncmp( Token, "PLAYER",6 )) {
295 Decoded = CharName(Token[6]-'1');
296 goto exit_function;
299 if (!strcmp( Token, "GABBER" )) {
300 Decoded = Gabber();
301 goto exit_function;
303 if (!strcmp( Token, "CHARNAME" )) {
304 Decoded = CharName(charname);
305 goto exit_function;
307 if (!strcmp( Token, "PRO_RACE" )) {
308 Decoded = GetString( RaceStrRef(0), 0);
309 goto exit_function;
311 if (!strcmp( Token, "PRO_SIRMAAM" )) {
312 Decoded = GetString( GenderStrRef(0,27473,27475), 0);
313 goto exit_function;
315 if (!strcmp( Token, "PRO_GIRLBOY" )) {
316 Decoded = GetString( GenderStrRef(0,27477,27476), 0);
317 goto exit_function;
319 if (!strcmp( Token, "PRO_BROTHERSISTER" )) {
320 Decoded = GetString( GenderStrRef(0,27478,27479), 0);
321 goto exit_function;
323 if (!strcmp( Token, "PRO_LADYLORD" )) {
324 Decoded = GetString( GenderStrRef(0,27481,27480), 0);
325 goto exit_function;
327 if (!strcmp( Token, "PRO_MALEFEMALE" )) {
328 Decoded = GetString( GenderStrRef(0,27482,27483), 0);
329 goto exit_function;
331 if (!strcmp( Token, "PRO_HESHE" )) {
332 Decoded = GetString( GenderStrRef(0,27484,27485), 0);
333 goto exit_function;
335 if (!strcmp( Token, "PRO_HISHER" )) {
336 Decoded = GetString( GenderStrRef(0,27486,27487), 0);
337 goto exit_function;
339 if (!strcmp( Token, "PRO_HIMHER" )) {
340 Decoded = GetString( GenderStrRef(0,27488,27487), 0);
341 goto exit_function;
343 if (!strcmp( Token, "PRO_MANWOMAN" )) {
344 Decoded = GetString( GenderStrRef(0,27489,27490), 0);
345 goto exit_function;
348 if (!strcmp( Token, "MAGESCHOOL" )) {
349 ieDword row = 0; //default value is 0 (generalist)
350 //this is subject to change, the row number in magesch.2da
351 core->GetDictionary()->Lookup( "MAGESCHOOL", row );
352 AutoTable tm("magesch");
353 if (tm) {
354 const char* value = tm->QueryField( row, 2 );
355 Decoded = GetString( atoi( value ), 0 );
356 goto exit_function;
360 return -1; //not decided
362 exit_function:
363 if (Decoded) {
364 TokenLength = ( int ) strlen( Decoded );
365 if (dest) {
366 memcpy( dest, Decoded, TokenLength );
368 //Decoded is always a copy
369 free( Decoded );
370 return TokenLength;
372 return -1;
375 bool TLKImp::ResolveTags(char* dest, char* source, int Length)
377 int NewLength;
378 char Token[MAX_VARIABLE_LENGTH + 1];
380 NewLength = 0;
381 for (int i = 0; source[i]; i++) {
382 if (source[i] == '<') {
383 i += (int) (mystrncpy( Token, source + i + 1, MAX_VARIABLE_LENGTH, '>' ) - Token) + 1;
384 int TokenLength = BuiltinToken( Token, dest + NewLength );
385 if (TokenLength == -1) {
386 TokenLength = core->GetTokenDictionary()->GetValueLength( Token );
387 if (TokenLength) {
388 if (TokenLength + NewLength > Length)
389 return false;
390 core->GetTokenDictionary()->Lookup( Token, dest + NewLength, TokenLength );
393 NewLength += TokenLength;
394 } else {
395 if (source[i] == '[') {
396 const char* tmppoi = strchr( source + i + 1, ']' );
397 if (tmppoi)
398 i = (int) (tmppoi - source);
399 else
400 break;
401 } else
402 dest[NewLength++] = source[i];
403 if (NewLength > Length)
404 return false;
407 dest[NewLength] = 0;
408 return true;
411 bool TLKImp::GetNewStringLength(char* string, int& Length)
413 int NewLength;
414 bool lChange;
415 char Token[MAX_VARIABLE_LENGTH + 1];
417 lChange = false;
418 NewLength = 0;
419 for (int i = 0; i < Length; i++) {
420 if (string[i] == '<') {
421 // token
422 lChange = true;
423 i += (int) (mystrncpy( Token, string + i + 1, MAX_VARIABLE_LENGTH, '>' ) - Token) + 1;
424 int TokenLength = BuiltinToken( Token, NULL );
425 if (TokenLength == -1) {
426 NewLength += core->GetTokenDictionary()->GetValueLength( Token );
427 } else {
428 NewLength += TokenLength;
430 } else {
431 if (string[i] == '[') {
432 //voice actor directives
433 lChange = true;
434 const char* tmppoi = strchr( string + i + 1, ']' );
435 if (tmppoi)
436 i += (int) (tmppoi - string) - i;
437 else
438 break;
439 } else {
440 NewLength++;
444 Length = NewLength;
445 return lChange;
448 ieStrRef TLKImp::UpdateString(ieStrRef strref, const char *newvalue)
450 if (!override) {
451 printMessage("TLKImp", "Custom string is not supported by this game format.\n", LIGHT_RED);
452 return 0xffffffff;
455 if(strref>STRREF_START || (strref>=BIO_START && strref<=BIO_END) ) {
456 return override->UpdateString(strref, newvalue);
459 printMessage("TLKImp", "Cannot set custom string.\n", LIGHT_RED);
460 return 0xffffffff;
463 char* TLKImp::GetString(ieStrRef strref, ieDword flags)
465 char* string;
467 if (!(flags&IE_STR_ALLOW_ZERO) && !strref) {
468 goto empty;
470 ieWord type;
471 int Length;
472 ieResRef SoundResRef;
474 if((strref>=STRREF_START) || (strref>=BIO_START && strref<=BIO_END) ) {
475 empty:
476 string = override->ResolveAuxString(strref, Length);
477 type = 0;
478 SoundResRef[0]=0;
479 } else {
480 ieDword Volume, Pitch, StrOffset;
481 ieDword l;
482 str->Seek( 18 + ( strref * 0x1A ), GEM_STREAM_START );
483 str->ReadWord( &type );
484 str->ReadResRef( SoundResRef );
485 str->ReadDword( &Volume );
486 str->ReadDword( &Pitch );
487 str->ReadDword( &StrOffset );
488 str->ReadDword( &l );
489 if (l > 65535) {
490 Length = 65535; //safety limit, it could be a dword actually
492 else {
493 Length = l;
496 if (type & 1) {
497 str->Seek( StrOffset + Offset, GEM_STREAM_START );
498 string = ( char * ) malloc( Length + 1 );
499 str->Read( string, Length );
500 } else {
501 Length = 0;
502 string = ( char * ) malloc( 1 );
504 string[Length] = 0;
507 //tagged text, bg1 and iwd don't mark them specifically, all entries are tagged
508 if (core->HasFeature( GF_ALL_STRINGS_TAGGED ) || ( type & 4 )) {
509 //GetNewStringLength will look in string and return true
510 //if the new Length will change due to tokens
511 //if there is no new length, we are done
512 while (GetNewStringLength( string, Length )) {
513 char* string2 = ( char* ) malloc( Length + 1 );
514 //ResolveTags will copy string to string2
515 ResolveTags( string2, string, Length );
516 free( string );
517 string = string2;
520 if (( type & 2 ) && ( flags & IE_STR_SOUND )) {
521 //if flags&IE_STR_SOUND play soundresref
522 if (SoundResRef[0] != 0) {
523 int xpos = 0;
524 int ypos = 0;
525 unsigned int flag = GEM_SND_RELATIVE | (flags&GEM_SND_SPEECH);
526 //IE_STR_SPEECH will stop the previous sound source
527 core->GetAudioDrv()->Play( SoundResRef, xpos, ypos, flag);
530 if (flags & IE_STR_STRREFON) {
531 char* string2 = ( char* ) malloc( Length + 11 );
532 sprintf( string2, "%d: %s", strref, string );
533 free( string );
534 return string2;
536 // remove the linefeed and carriage return if requested
537 if ((flags & IE_STR_REMOVE_NEWLINE)) {
538 core->StripLine( string, Length);
540 return string;
543 StringBlock TLKImp::GetStringBlock(ieStrRef strref, unsigned int flags)
545 StringBlock sb;
547 if (!(flags&IE_STR_ALLOW_ZERO) && !strref) {
548 goto empty;
550 if (strref >= StrRefCount) {
551 empty:
552 sb.text = ( char * ) malloc( 1 );
553 sb.text[0] = 0;
554 sb.Sound[0] = 0;
555 return sb;
557 sb.text = GetString( strref, flags );
558 ieWord type;
559 str->Seek( 18 + ( strref * 0x1A ), GEM_STREAM_START );
560 str->ReadWord( &type );
561 str->ReadResRef( sb.Sound );
562 return sb;
565 void TLKImp::FreeString(char *str)
567 free(str);
571 #include "../../includes/plugindef.h"
573 GEMRB_PLUGIN(0xBB6F380, "TLK File Importer")
574 PLUGIN_CLASS(IE_TLK_CLASS_ID, TLKImp)
575 END_PLUGIN()