1 //----------------------------------------------------------------------------
2 //  EDGE Data Definition File Code (Language handling settings)
3 //----------------------------------------------------------------------------
4 //
5 //  Copyright (c) 1999-2008  The EDGE Team.
6 //
7 //  This program is free software; you can redistribute it and/or
8 //  modify it under the terms of the GNU General Public License
9 //  as published by the Free Software Foundation; either version 2
10 //  of the License, or (at your option) any later version.
11 //
12 //  This program is distributed in the hope that it will be useful,
13 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
14 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 //  GNU General Public License for more details.
16 //
17 //----------------------------------------------------------------------------
18 //
19 // Language handling Setup and Parser Code
20 //
21 // 1998/10/29 -KM- Allow cmd line selection of language.
22 //
23 // This is somewhat different to most DDF reading files. In order to read the
24 // language specific strings, it uses the format:
25 //
26 // <RefName>=<String>;
27 //
28 // as opposed to the normal entry, which should be:
29 //
30 // [<Refname>]
31 // STRING=<string>;
32 //
33 // also the file suffix is LDF (Language Def File), this is to avoid confusion with
34 // the oridnary DDF files. The default file is DEFAULT.LDF, which can be subbed by
35 // using -lang <NameOfLangFile>.
36 //
37 
38 #include "local.h"
39 
40 #include "language.h"
41 
42 // ---> ddf buildinfo for language class
43 class ddf_bi_lang_c
44 {
45 public:
ddf_bi_lang_c()46 	ddf_bi_lang_c()
47 	{
48 		treehead = NULL;
49 	}
50 
~ddf_bi_lang_c()51 	~ddf_bi_lang_c()
52 	{
53 		DeleteLangNodes();
54 	}
55 
56 	struct langentry_s
57 	{
langentry_sddf_bi_lang_c::langentry_s58 		langentry_s()
59 		{
60 			// Binary tree for sorting
61 			left = NULL;
62 			right = NULL;
63 
64 			// Next entry with matching ref
65 			lang_next = NULL;
66 
67 			// Linked list
68 			prev = NULL;
69 			next = NULL;
70 		}
71 
72 		int lang;
73 
74 		epi::strent_c ref;
75 		epi::strent_c value;
76 
77 		// A linked list + binary tree is wasteful, but speed efficent and
78 		// avoids stack overflow issues that may occur
79 		struct langentry_s* left;
80 		struct langentry_s* right;
81 
82 		struct langentry_s* prev;
83 		struct langentry_s* next;
84 
85 		// Next entry
86 		struct langentry_s* lang_next;
87 	};
88 
89 private:
90 	langentry_s *treehead;
91 
92 public:
93 	epi::strlist_c langnames;
94 	int currlang;
95 
96 	epi::strlist_c comp_langrefs;   // Compiled language references
97 	epi::strlist_c comp_langvalues; // Compiled language values (1 per lang)
98 
99 	// LANGUAGE ENTRY NODE HANDLING
100 
AddLangNode(const char * _ref,const char * value)101 	void AddLangNode(const char *_ref, const char *value)
102 	{
103 		langentry_s *currnode, *node, *newnode;
104 
105 		int cmp;
106 		enum { CREATE_HEAD, TO_LEFT, TO_RIGHT, ADD, REPLACE } act;
107 
108 		act = CREATE_HEAD;
109 		currnode = NULL;
110 		node = treehead;
111 
112 		while (node && act != REPLACE)
113 		{
114 			currnode = node;
115 
116 			if (act != ADD)
117 				cmp = stricmp(_ref, node->ref);
118 			else
119 				cmp = 0;	// Has to be a match if the last act was ADD
120 
121 			if (cmp == 0)
122 			{
123 				//
124 				// Check to see if the language id matches, if it does then
125 				// replace the entry, else add the node.
126 				//
127 				if (node->lang == currlang)
128 				{
129 					act = REPLACE;
130 				}
131 				else
132 				{
133 					act = ADD;
134 					node = node->lang_next;
135 				}
136 			}
137 			else if (cmp < 0)
138 			{
139 				act = TO_LEFT;
140 				node = node->left;
141 			}
142 			else if (cmp > 0)
143 			{
144 				act = TO_RIGHT;
145 				node = node->right;
146 			}
147 		}
148 
149 		// Handle replace seperately, since we not creating anything
150 		if (act == REPLACE)
151 		{
152 			node->value.Set(value);
153 			return;
154 		}
155 
156 		newnode = new langentry_s;
157 
158 		if (act != ADD) { newnode->ref.Set(_ref); }
159 
160 		newnode->value.Set(value);
161 		newnode->lang = currlang;
162 
163 		switch (act)
164 		{
165 			case CREATE_HEAD:
166 			{
167 				treehead = newnode;
168 				break;
169 			}
170 
171 			case TO_LEFT:
172 			{
173 				// Update the binary tree
174 				currnode->left = newnode;
175 
176 				// Update the linked list (Insert behind current node)
177 				if (currnode->prev)
178 				{
179 					currnode->prev->next = newnode;
180 					newnode->prev = currnode->prev;
181 				}
182 
183 				currnode->prev = newnode;
184 				newnode->next = currnode;
185 				break;
186 			}
187 
188 			case TO_RIGHT:
189 			{
190 				// Update the binary tree
191 				currnode->right = newnode;
192 
193 				// Update the linked list (Insert infront of current node)
194 				if (currnode->next)
195 				{
196 					currnode->next->prev = newnode;
197 					newnode->next = currnode->next;
198 				}
199 
200 				currnode->next = newnode;
201 				newnode->prev = currnode;
202 				break;
203 			}
204 
205 			case ADD:
206 			{
207 				currnode->lang_next = newnode;
208 				break;
209 			}
210 
211 			default:
212 			{
213 				break; /* FIXME!! Throw error */
214 			}
215 		}
216 	}
217 
DeleteLangNodes()218 	void DeleteLangNodes()
219 	{
220 		if (!treehead)
221 			return;
222 
223 		// The node head is the head for the binary tree, so
224 		// we have to find the head by going backwards from
225 		// the treehead
226 
227 		langentry_s *currnode, *savenode;
228 
229 		currnode = treehead;
230 		while (currnode->prev)
231 			currnode = currnode->prev;
232 
233 		while (currnode)
234 		{
235 			savenode = currnode->next;
236 			delete currnode;
237 			currnode = savenode;
238 		}
239 	}
240 
241 	// LANGUAGES
242 
243 	//
244 	// AddLanguage()
245 	//
246 	// Returns if the language already exists
247 	//
AddLanguage(const char * name)248 	bool AddLanguage(const char *name)
249 	{
250 		int langsel = -1;
251 
252 		// Look for an existing language entry if one exists
253 		{
254 			epi::array_iterator_c it;
255 
256 			for (it = langnames.GetBaseIterator(); it.IsValid(); it++)
257 			{
258 				char *it2 = ITERATOR_TO_TYPE(it, char*);
259 
260 				if (stricmp(it2, name) == 0)
261 				{
262 					langsel = it.GetPos();
263 					break;
264 				}
265 			}
266 		}
267 
268 		// Setup the current language index, adding the new entry if needs be
269 		if (langsel < 0)
270 		{
271 			langnames.Insert(name);
272 
273 			currlang = langnames.GetSize() - 1;
274 		}
275 		else
276 		{
277 			currlang = langsel;
278 		}
279 
280 
281 		return (langsel < 0);
282 	}
283 
284 	// LIST COMPILING
285 
CompileLanguageReferences()286 	void CompileLanguageReferences()
287 	{
288 		comp_langrefs.Clear();
289 
290 		if (!treehead)
291 			return;					// Nothing to do
292 
293 		langentry_s *currnode, *head;
294 		int count;
295 
296 		currnode = treehead;
297 		while (currnode->prev)
298 			currnode = currnode->prev;
299 
300 		head = currnode;
301 
302 		// Count the entries so strlist_c does not over-fragment memory
303 		count = 0;
304 		while (currnode)
305 		{
306 			currnode = currnode->next;
307 			count++;
308 		}
309 
310 		comp_langrefs.Size(count);
311 
312 		// Add entries
313 		for (currnode = head; currnode; currnode = currnode->next)
314 			comp_langrefs.Insert(currnode->ref);
315 
316 		return;
317 	}
318 
CompileLanguageValues(int lang)319 	void CompileLanguageValues(int lang)
320 	{
321 		SYS_ASSERT(lang >=0 && lang <= langnames.GetSize());
322 
323 		comp_langvalues.Clear();
324 
325 		if (!treehead)
326 			return;					// Nothing to do
327 
328 		langentry_s *currnode, *head, *langnode;
329 		int count;
330 
331 		// Find me head, son!
332 		currnode = treehead;
333 		while (currnode->prev)
334 			currnode = currnode->prev;
335 
336 		head = currnode;
337 
338 		// Count the entries so strlist_c does not over-fragment memory
339 		count = 0;
340 		while (currnode)
341 		{
342 			currnode = currnode->next;
343 			count++;
344 		}
345 
346 		comp_langvalues.Size(count);
347 
348 		// Add entries
349 		for (currnode = head; currnode; currnode = currnode->next)
350 		{
351 			langnode = currnode;
352 			while (langnode && langnode->lang != lang)
353 				langnode = langnode->lang_next;
354 
355 			if (langnode)
356 				comp_langvalues.Insert(langnode->value);
357 			else
358 				comp_langvalues.Insert(NULL);
359 		}
360 
361 		return;
362 	}
363 };
364 
365 // Globals
366 language_c language;	// -ACB- 2004/07/28 Languages instance
367 
368 // Locals
369 ddf_bi_lang_c* lang_buildinfo;
370 
371 //
372 //  DDF PARSING ROUTINES
373 //
LanguageStartEntry(const char * name,bool extend)374 static void LanguageStartEntry(const char *name, bool extend)
375 {
376 	if (!name || !name[0])
377 	{
378 		DDF_WarnError("New language entry is missing a name!");
379 		name = "DEAD_LANGUAGE";
380 	}
381 
382 	// Note: extension is the norm for LANGUAGES.LDF
383 
384 	lang_buildinfo->AddLanguage(name);
385 }
386 
387 
LanguageParseField(const char * field,const char * contents,int index,bool is_last)388 static void LanguageParseField(const char *field, const char *contents,
389     int index, bool is_last)
390 {
391 #if (DEBUG_DDF)
392 	I_Debugf("LANGUAGE_PARSE: %s = %s;\n", field, contents);
393 #endif
394 
395 	if (! is_last)
396 	{
397 		DDF_WarnError("Unexpected comma `,' in LANGUAGE.LDF\n");
398 		return;
399 	}
400 
401 	lang_buildinfo->AddLangNode(field, contents);
402 }
403 
LanguageFinishEntry(void)404 static void LanguageFinishEntry(void)
405 {
406 	/* ... */
407 }
408 
LanguageClearAll(void)409 static void LanguageClearAll(void)
410 {
411 	// safe to delete all language entries
412 	language.Clear();
413 }
414 
415 
DDF_ReadLangs(void * data,int size)416 bool DDF_ReadLangs(void *data, int size)
417 {
418 	readinfo_t languages;
419 
420 	languages.memfile = (char*)data;
421 	languages.memsize = size;
422 	languages.tag = "LANGUAGES";
423 	languages.entries_per_dot = 1;
424 
425 	if (languages.memfile)
426 	{
427 		languages.message = NULL;
428 		languages.filename = NULL;
429 		languages.lumpname = "DDFLANG";
430 	}
431 	else
432 	{
433 		languages.message = "DDF_InitLanguage";
434 		languages.filename = "language.ldf";
435 		languages.lumpname = NULL;
436 	}
437 
438 	languages.start_entry  = LanguageStartEntry;
439 	languages.parse_field  = LanguageParseField;
440 	languages.finish_entry = LanguageFinishEntry;
441 	languages.clear_all    = LanguageClearAll;
442 
443 	return DDF_MainReadFile(&languages);
444 }
445 
DDF_LanguageInit(void)446 void DDF_LanguageInit(void)
447 {
448 	// FIXME! Copy any existing entries into the new buildinfo
449 
450 	// Clear any existing
451 	language.Clear();
452 
453 	// Create a build info object
454 	lang_buildinfo = new ddf_bi_lang_c;
455 }
456 
DDF_LanguageCleanUp(void)457 void DDF_LanguageCleanUp(void)
458 {
459 	// Convert build info into the language structure
460 	int langcount = lang_buildinfo->langnames.GetSize();
461 	if (langcount == 0)
462 		I_Error("Missing languages !\n");
463 
464 	// Load the choice of languages
465 	language.LoadLanguageChoices(lang_buildinfo->langnames);
466 
467 	// Load the reference table
468 	lang_buildinfo->CompileLanguageReferences();
469 	language.LoadLanguageReferences(lang_buildinfo->comp_langrefs);
470 
471 	// Load the value table for each one of the languages
472 	int i;
473 	for (i=0; i<langcount; i++)
474 	{
475 		lang_buildinfo->CompileLanguageValues(i);
476 		language.LoadLanguageValues(i, lang_buildinfo->comp_langvalues);
477 	}
478 
479 	//language.Dump();
480 	//exit(1);
481 
482 	// Dispose of the data
483 	delete lang_buildinfo;
484 }
485 
486 //
487 // language_c Constructor
488 //
language_c()489 language_c::language_c()
490 {
491 	current = -1;
492 }
493 
494 //
495 // language_c Destructor
496 //
~language_c()497 language_c::~language_c()
498 {
499 	Clear();
500 }
501 
502 //
503 // language_c::Clear()
504 //
505 // Clear the contents
506 //
Clear()507 void language_c::Clear()
508 {
509 	choices.Clear();
510 	refs.Clear();
511 
512 	if (values)
513 	{
514 		delete [] values;
515 		values = NULL;
516 	}
517 
518 	current = -1;
519 }
520 
521 //
522 // int language_c::Find(const char* ref)
523 //
Find(const char * name)524 int language_c::Find(const char *name)
525 {
526 	if (!values || !name)
527 		return -1;
528 
529 	// TODO Optimise search - this list is sorted in order
530 
531 	for (int i = refs.GetSize()-1; i >= 0; i--)
532 		if (stricmp(refs[i], name) == 0)
533 			return i;
534 
535 	return -1; // not found
536 }
537 
538 //
539 // const char* language_c::GetName()
540 //
541 // Returns the current name if idx is isvalid, can be
542 // NULL if the choices table has not been setup
543 //
GetName(int idx)544 const char* language_c::GetName(int idx)
545 {
546 	if (idx < 0)
547 		idx = current;
548 
549 	if (idx < 0 || idx >= choices.GetSize())
550 		return NULL;
551 
552 	return choices[idx];
553 }
554 
555 //
556 // bool language_c::IsValidRef()
557 //
IsValidRef(const char * refname)558 bool language_c::IsValidRef(const char *refname)
559 {
560 	return (Find(refname)>=0);
561 }
562 
563 //
564 // language_c::LoadLanguageChoices()
565 //
LoadLanguageChoices(epi::strlist_c & _langnames)566 void language_c::LoadLanguageChoices(epi::strlist_c& _langnames)
567 {
568 	choices.Set(_langnames);
569 }
570 
571 //
572 // language_c::LoadLanguageReferences()
573 //
LoadLanguageReferences(epi::strlist_c & _refs)574 void language_c::LoadLanguageReferences(epi::strlist_c& _refs)
575 {
576 	if (values)
577 		delete [] values;
578 
579 	refs.Set(_refs);
580 	values = new epi::strbox_c[refs.GetSize()];
581 
582 	return;
583 }
584 
585 //
586 // language_c::LoadLanguageValues()
587 //
LoadLanguageValues(int lang,epi::strlist_c & _values)588 void language_c::LoadLanguageValues(int lang, epi::strlist_c& _values)
589 {
590 	if (_values.GetSize() != refs.GetSize())
591 		return;	// FIXME!! Throw error
592 
593 	if (lang < 0 || lang >= choices.GetSize())
594 		return;	// FIXME!! Throw error
595 
596 	values[lang].Set(_values);
597 
598 	return;
599 }
600 
601 //
602 // language_c::Select() Named Select
603 //
Select(const char * name)604 bool language_c::Select(const char *name)
605 {
606 	int i, max;
607 
608 	for(i=0, max=choices.GetSize(); i<max; i++)
609 	{
610 		if (! DDF_CompareName(name, choices[i]))
611 		{
612 			current = i;
613 			return true;
614 		}
615 	}
616 
617 	// FIXME!! Throw error
618 	return false;
619 }
620 
621 //
622 // language_c::Select() Index Select
623 //
Select(int idx)624 bool language_c::Select(int idx)
625 {
626 	if (idx < 0 || idx >= choices.GetSize())
627 		return false;	// FIXME!! Throw error
628 
629 	current = idx;
630 	return true;
631 }
632 
633 //
634 // const char* language_c::operator[]()
635 //
operator [](const char * refname)636 const char* language_c::operator[](const char *refname)
637 {
638 	int idx;
639 
640 	idx = Find(refname);
641 	if (idx>=0)
642 	{
643 		if (current < 0 || current >= choices.GetSize())
644 			return refname;  // -AJA- preserve old behaviour
645 
646 		char *s = values[current][idx];
647 		if (s != NULL)
648 			return s;
649 
650 		// Look through other language definitions is one does not exist
651 		int i, max;
652 		for (i=0, max=choices.GetSize(); i<max; i++)
653 		{
654 			if (i != current)
655 			{
656 				char *s = values[i][idx];
657 				if (s != NULL)
658 					return values[i][idx];
659 			}
660 		}
661 	}
662 
663 	return refname;  // -AJA- preserve old behaviour
664 }
665 
666 /*
667 void language_c::Dump(void)
668 {
669 	int i,j;
670 	for (i=0; i<choices.GetSize(); i++)
671 	{
672 		I_Printf("Language %d is %s\n", i, choices[i]);
673 	}
674 	I_Printf("\n");
675 
676 	for (i=0; i<refs.GetSize(); i++)
677 	{
678 		I_Printf("Ref %d is %s\n", i, refs[i]);
679 	}
680 
681 	I_Printf("\n");
682 	for (i=0; i<choices.GetSize(); i++)
683 	{
684 		for (j=0; j<values[i].GetSize(); j++)
685 			I_Printf("Value %d/%d (%s) is %s\n", i, j, refs[j], values[i][j]);
686 
687 		I_Printf("\n");
688 	}
689 }
690 */
691 
692 //--- editor settings ---
693 // vi:ts=4:sw=4:noexpandtab
694