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.
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;
38 if (core
->HasFeature(GF_CHARNAMEISGABBER
)) {
47 if (monthnamecount==0) {
49 AutoTable tab("months");
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));
68 if (monthnames) free(monthnames);
72 if (str
&& autoFree
) {
78 void TLKImp::CloseAux()
86 void TLKImp::OpenAux()
89 override
= new CTlkOverride();
91 if (!override
->Init()) {
93 printMessage("TlkImporter","Cannot open tlk override!\n", LIGHT_RED
);
98 bool TLKImp::Open(DataStream
* stream
, bool autoFree
)
100 if (stream
== NULL
) {
103 if (str
&& this->autoFree
) {
107 this->autoFree
= autoFree
;
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
);
114 str
->Seek( 2, GEM_CURRENT_POS
);
115 str
->ReadDword( &StrRefCount
);
116 str
->ReadDword( &Offset
);
120 inline char* mystrncpy(char* dest
, const char* source
, int maxlength
,
123 while (*source
&& ( *source
!= delim
) && maxlength
--) {
134 inline Actor
*GetActorFromSlot(int slot
)
137 GameControl
*gc
= core
->GetGameControl();
139 return gc
->GetSpeaker();
143 Game
*game
= core
->GetGame();
148 return game
->GetPC(0, false); //protagonist
150 return game
->FindPC(slot
);
153 char *TLKImp::Gabber()
157 act
=core
->GetGameControl()->GetSpeaker();
159 return override
->CS(act
->LongName
);
161 return override
->CS("?");
164 char *TLKImp::CharName(int slot
)
168 act
=GetActorFromSlot(slot
);
170 return override
->CS(act
->LongName
);
172 return override
->CS("?");
175 int TLKImp::RaceStrRef(int slot
)
180 act
=GetActorFromSlot(slot
);
182 race
=act
->GetStat(IE_RACE
);
187 AutoTable
tab("races");
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
)
199 act
= GetActorFromSlot(slot
);
200 if (act
&& (act
->GetStat(IE_SEX
)==SEX_FEMALE
) ) {
207 void TLKImp::GetMonthName(int dayandmonth)
211 for(int i=0;i<monthnamecount;i++) {
212 if (dayandmonth<days[i]) {
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);
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 );
250 if (!strcmp( Token
, "FIGHTERTYPE" )) {
251 Decoded
= GetString( 10174, 0 );
254 if (!strcmp( Token
, "RACE" )) {
255 Decoded
= GetString( RaceStrRef(-1), 0);
258 if (!strcmp( Token
, "SIRMAAM" )) {
259 Decoded
= GetString( GenderStrRef(-1,27473,27475), 0);
262 if (!strcmp( Token
, "GIRLBOY" )) {
263 Decoded
= GetString( GenderStrRef(-1,27477,27476), 0);
266 if (!strcmp( Token
, "BROTHERSISTER" )) {
267 Decoded
= GetString( GenderStrRef(-1,27478,27479), 0);
270 if (!strcmp( Token
, "LADYLORD" )) {
271 Decoded
= GetString( GenderStrRef(-1,27481,27480), 0);
274 if (!strcmp( Token
, "MALEFEMALE" )) {
275 Decoded
= GetString( GenderStrRef(-1,27482,27483), 0);
278 if (!strcmp( Token
, "HESHE" )) {
279 Decoded
= GetString( GenderStrRef(-1,27484,27485), 0);
282 if (!strcmp( Token
, "HISHER" )) {
283 Decoded
= GetString( GenderStrRef(-1,27486,27487), 0);
286 if (!strcmp( Token
, "HIMHER" )) {
287 Decoded
= GetString( GenderStrRef(-1,27488,27487), 0);
290 if (!strcmp( Token
, "MANWOMAN" )) {
291 Decoded
= GetString( GenderStrRef(-1,27489,27490), 0);
294 if (!strncmp( Token
, "PLAYER",6 )) {
295 Decoded
= CharName(Token
[6]-'1');
299 if (!strcmp( Token
, "GABBER" )) {
303 if (!strcmp( Token
, "CHARNAME" )) {
304 Decoded
= CharName(charname
);
307 if (!strcmp( Token
, "PRO_RACE" )) {
308 Decoded
= GetString( RaceStrRef(0), 0);
311 if (!strcmp( Token
, "PRO_SIRMAAM" )) {
312 Decoded
= GetString( GenderStrRef(0,27473,27475), 0);
315 if (!strcmp( Token
, "PRO_GIRLBOY" )) {
316 Decoded
= GetString( GenderStrRef(0,27477,27476), 0);
319 if (!strcmp( Token
, "PRO_BROTHERSISTER" )) {
320 Decoded
= GetString( GenderStrRef(0,27478,27479), 0);
323 if (!strcmp( Token
, "PRO_LADYLORD" )) {
324 Decoded
= GetString( GenderStrRef(0,27481,27480), 0);
327 if (!strcmp( Token
, "PRO_MALEFEMALE" )) {
328 Decoded
= GetString( GenderStrRef(0,27482,27483), 0);
331 if (!strcmp( Token
, "PRO_HESHE" )) {
332 Decoded
= GetString( GenderStrRef(0,27484,27485), 0);
335 if (!strcmp( Token
, "PRO_HISHER" )) {
336 Decoded
= GetString( GenderStrRef(0,27486,27487), 0);
339 if (!strcmp( Token
, "PRO_HIMHER" )) {
340 Decoded
= GetString( GenderStrRef(0,27488,27487), 0);
343 if (!strcmp( Token
, "PRO_MANWOMAN" )) {
344 Decoded
= GetString( GenderStrRef(0,27489,27490), 0);
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");
354 const char* value
= tm
->QueryField( row
, 2 );
355 Decoded
= GetString( atoi( value
), 0 );
360 return -1; //not decided
364 TokenLength
= ( int ) strlen( Decoded
);
366 memcpy( dest
, Decoded
, TokenLength
);
368 //Decoded is always a copy
375 bool TLKImp::ResolveTags(char* dest
, char* source
, int Length
)
378 char Token
[MAX_VARIABLE_LENGTH
+ 1];
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
);
388 if (TokenLength
+ NewLength
> Length
)
390 core
->GetTokenDictionary()->Lookup( Token
, dest
+ NewLength
, TokenLength
);
393 NewLength
+= TokenLength
;
395 if (source
[i
] == '[') {
396 const char* tmppoi
= strchr( source
+ i
+ 1, ']' );
398 i
= (int) (tmppoi
- source
);
402 dest
[NewLength
++] = source
[i
];
403 if (NewLength
> Length
)
411 bool TLKImp::GetNewStringLength(char* string
, int& Length
)
415 char Token
[MAX_VARIABLE_LENGTH
+ 1];
419 for (int i
= 0; i
< Length
; i
++) {
420 if (string
[i
] == '<') {
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
);
428 NewLength
+= TokenLength
;
431 if (string
[i
] == '[') {
432 //voice actor directives
434 const char* tmppoi
= strchr( string
+ i
+ 1, ']' );
436 i
+= (int) (tmppoi
- string
) - i
;
448 ieStrRef
TLKImp::UpdateString(ieStrRef strref
, const char *newvalue
)
451 printMessage("TLKImp", "Custom string is not supported by this game format.\n", LIGHT_RED
);
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
);
463 char* TLKImp::GetString(ieStrRef strref
, ieDword flags
)
467 if (!(flags
&IE_STR_ALLOW_ZERO
) && !strref
) {
472 ieResRef SoundResRef
;
474 if((strref
>=STRREF_START
) || (strref
>=BIO_START
&& strref
<=BIO_END
) ) {
476 string
= override
->ResolveAuxString(strref
, Length
);
480 ieDword Volume
, Pitch
, StrOffset
;
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
);
490 Length
= 65535; //safety limit, it could be a dword actually
497 str
->Seek( StrOffset
+ Offset
, GEM_STREAM_START
);
498 string
= ( char * ) malloc( Length
+ 1 );
499 str
->Read( string
, Length
);
502 string
= ( char * ) malloc( 1 );
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
);
520 if (( type
& 2 ) && ( flags
& IE_STR_SOUND
)) {
521 //if flags&IE_STR_SOUND play soundresref
522 if (SoundResRef
[0] != 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
);
536 // remove the linefeed and carriage return if requested
537 if ((flags
& IE_STR_REMOVE_NEWLINE
)) {
538 core
->StripLine( string
, Length
);
543 StringBlock
TLKImp::GetStringBlock(ieStrRef strref
, unsigned int flags
)
547 if (!(flags
&IE_STR_ALLOW_ZERO
) && !strref
) {
550 if (strref
>= StrRefCount
) {
552 sb
.text
= ( char * ) malloc( 1 );
557 sb
.text
= GetString( strref
, flags
);
559 str
->Seek( 18 + ( strref
* 0x1A ), GEM_STREAM_START
);
560 str
->ReadWord( &type
);
561 str
->ReadResRef( sb
.Sound
);
565 void TLKImp::FreeString(char *str
)
571 #include "../../includes/plugindef.h"
573 GEMRB_PLUGIN(0xBB6F380, "TLK File Importer")
574 PLUGIN_CLASS(IE_TLK_CLASS_ID
, TLKImp
)