tagging release
[dasher.git] / Src / DasherCore / Alphabet / AlphIO.cpp
blob3ad88234d7ec8fd7e166d6c4cd5e36345366969e
1 // AlphIO.cpp
2 //
3 /////////////////////////////////////////////////////////////////////////////
4 //
5 // Copyright (c) 2002 Iain Murray
6 //
7 /////////////////////////////////////////////////////////////////////////////
9 #include "../../Common/Common.h"
11 #include "AlphIO.h"
13 using namespace Dasher;
14 using namespace std;
15 using namespace expat;
17 // Track memory leaks on Windows to the line that new'd the memory
18 #ifdef _WIN32
19 #ifdef _DEBUG
20 #define DEBUG_NEW new( _NORMAL_BLOCK, THIS_FILE, __LINE__ )
21 #define new DEBUG_NEW
22 #undef THIS_FILE
23 static char THIS_FILE[] = __FILE__;
24 #endif
25 #endif
27 CAlphIO::CAlphIO(std::string SystemLocation, std::string UserLocation, std::vector<std::string> Filenames)
28 :BlankInfo(), SystemLocation(SystemLocation), UserLocation(UserLocation), Filenames(Filenames), LoadMutable(false), CData("") {
29 CreateDefault();
31 typedef pair < Opts::AlphabetTypes, std::string > AT;
32 vector < AT > Types;
33 Types.push_back(AT(Opts::MyNone, "None"));
34 Types.push_back(AT(Opts::Arabic, "Arabic"));
35 Types.push_back(AT(Opts::Baltic, "Baltic"));
36 Types.push_back(AT(Opts::CentralEurope, "CentralEurope"));
37 Types.push_back(AT(Opts::ChineseSimplified, "ChineseSimplified"));
38 Types.push_back(AT(Opts::ChineseTraditional, "ChineseTraditional"));
39 Types.push_back(AT(Opts::Cyrillic, "Cyrillic"));
40 Types.push_back(AT(Opts::Greek, "Greek"));
41 Types.push_back(AT(Opts::Hebrew, "Hebrew"));
42 Types.push_back(AT(Opts::Japanese, "Japanese"));
43 Types.push_back(AT(Opts::Korean, "Korean"));
44 Types.push_back(AT(Opts::Thai, "Thai"));
45 Types.push_back(AT(Opts::Turkish, "Turkish"));
46 Types.push_back(AT(Opts::VietNam, "VietNam"));
47 Types.push_back(AT(Opts::Western, "Western"));
48 for(unsigned int i = 0; i < Types.size(); i++) {
49 StoT[Types[i].second] = Types[i].first;
50 TtoS[Types[i].first] = Types[i].second;
53 LoadMutable = false;
54 ParseFile(SystemLocation + "alphabet.xml");
55 if(Filenames.size() > 0) {
56 for(unsigned int i = 0; i < Filenames.size(); i++) {
57 ParseFile(SystemLocation + Filenames[i]);
60 LoadMutable = true;
61 ParseFile(UserLocation + "alphabet.xml");
62 if(Filenames.size() > 0) {
63 for(unsigned int i = 0; i < Filenames.size(); i++) {
64 ParseFile(UserLocation + Filenames[i]);
69 void CAlphIO::ParseFile(std::string Filename) {
70 FILE *Input;
71 if((Input = fopen(Filename.c_str(), "r")) == (FILE *) 0) {
72 // could not open file
73 return;
76 XML_Parser Parser = XML_ParserCreate(NULL);
78 // Members passed as callbacks must be static, so don't have a "this" pointer.
79 // We give them one through horrible casting so they can effect changes.
80 XML_SetUserData(Parser, this);
82 XML_SetElementHandler(Parser, XML_StartElement, XML_EndElement);
83 XML_SetCharacterDataHandler(Parser, XML_CharacterData);
85 const unsigned int BufferSize = 1024;
86 char Buffer[BufferSize];
87 int Done;
88 do {
89 size_t len = fread(Buffer, 1, sizeof(Buffer), Input);
90 Done = len < sizeof(Buffer);
91 if(XML_Parse(Parser, Buffer, len, Done) == XML_STATUS_ERROR) {
92 break;
94 } while(!Done);
96 XML_ParserFree(Parser);
97 fclose(Input);
100 void CAlphIO::GetAlphabets(std::vector <std::string >*AlphabetList) const {
101 AlphabetList->clear();
103 typedef std::map < std::string, AlphInfo >::const_iterator CI;
104 CI End = Alphabets.end();
106 for(CI Cur = Alphabets.begin(); Cur != End; Cur++)
107 AlphabetList->push_back((*Cur).second.AlphID);
110 std::string CAlphIO::GetDefault() {
111 if(Alphabets.count("English with limited punctuation") != 0) {
112 return "English with limited punctuation";
114 else {
115 return "Default";
119 const CAlphIO::AlphInfo & CAlphIO::GetInfo(const std::string &AlphID) {
120 if(Alphabets.count(AlphID) != 0) {
121 // if we have the alphabet they ask for, return it
122 Alphabets[AlphID].AlphID = AlphID; // Ensure consistency
123 return Alphabets[AlphID];
125 else {
126 // otherwise, give them default - it's better than nothing
127 return Alphabets["Default"];
131 void CAlphIO::SetInfo(const AlphInfo &NewInfo) {
132 Alphabets[NewInfo.AlphID] = NewInfo;
133 Save(NewInfo.AlphID);
136 void CAlphIO::Delete(const std::string &AlphID) {
137 if(Alphabets.find(AlphID) != Alphabets.end()) {
138 Alphabets.erase(AlphID);
139 Save("");
143 void CAlphIO::Save(const std::string &AlphID) {
144 // TODO: We cannot reliably output XML at the moment this will have
145 // to be re-implemented if we ever decide that we need to do this
146 // again
149 // Write an XML file containing all the alphabets that have been defined.
150 // I am not going to indent the XML file as it will just bloat it, and it
151 // is very simple. There are line breaks though as it is very hard to read
152 // without. I'm going to ignore AlphID and save all alphabets as the
153 // overhead doesn't seem to matter and it makes things much easier.
154 char Number[sizeof(int)];
155 FILE *Output;
156 std::string Filename = UserLocation + "alphabet.xml";
157 if((Output = fopen(Filename.c_str(), "w")) == (FILE *) 0) {
158 // could not open file
161 fwrite("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n", sizeof(char), 39, Output);
162 fwrite("<!DOCTYPE alphabets SYSTEM \"alphabet.dtd\">\n", sizeof(char), 43, Output);
163 fwrite("<?xml-stylesheet type=\"text/xsl\" href=\"alphabet.xsl\"?>\n", sizeof(char), 55, Output);
164 fwrite("<alphabets>\n", sizeof(char), 12, Output);
166 typedef std::map < std::string, AlphInfo >::const_iterator CI;
167 CI End = Alphabets.end();
168 for(CI Cur = Alphabets.begin(); Cur != End; Cur++) {
169 AlphInfo Info = (*Cur).second; // Take a copy so that special characters can be escaped
171 if(Info.Mutable == false) // this is a system alphabet, not one we write
172 continue;
174 fwrite("<alphabet name=\"", sizeof(char), 16, Output);
175 XML_Escape(&Info.AlphID, true);
176 fwrite(Info.AlphID.c_str(), sizeof(char), Info.AlphID.size(), Output);
177 fwrite("\">\n", sizeof(char), 3, Output);
179 fwrite("<orientation type=\"", sizeof(char), 19, Output);
180 switch (Info.Orientation) {
181 case Opts::RightToLeft:
182 fwrite("RL", sizeof(char), 2, Output);
183 break;
184 case Opts::TopToBottom:
185 fwrite("TB", sizeof(char), 2, Output);
186 break;
187 case Opts::BottomToTop:
188 fwrite("BT", sizeof(char), 2, Output);
189 break;
190 case Opts::LeftToRight:
191 // deliberate fall through
192 default:
193 fwrite("LR", sizeof(char), 2, Output);
194 break;
196 fwrite("\"/>\n", sizeof(char), 4, Output);
198 // TODO Encoding properly
199 fwrite("<encoding type=\"", sizeof(char), 16, Output);
200 fwrite(TtoS[Info.Type].c_str(), sizeof(char), TtoS[Info.Type].size(), Output);
201 fwrite("\"/>\n", sizeof(char), 4, Output);
203 fwrite("<palette>", sizeof(char), 9, Output);
204 XML_Escape(&Info.PreferredColours, false);
205 fwrite(Info.PreferredColours.c_str(), sizeof(char), Info.PreferredColours.size(), Output);
206 fwrite("</palette>\n", sizeof(char), 11, Output);
208 fwrite("<train>", sizeof(char), 7, Output);
209 XML_Escape(&Info.TrainingFile, false);
210 fwrite(Info.TrainingFile.c_str(), sizeof(char), Info.TrainingFile.size(), Output);
211 fwrite("</train>\n", sizeof(char), 9, Output);
213 fwrite("<gamemode>", sizeof(char), 10, Output);
214 XML_Escape(&Info.GameModeFile, false);
215 fwrite(Info.GameModeFile.c_str(), sizeof(char), Info.GameModeFile.size(), Output);
216 fwrite("</gamemode>\n", sizeof(char), 12, Output);
218 // Write out the space character
219 fwrite("<space d=\"", sizeof(char), 10, Output);
220 XML_Escape(&Info.SpaceCharacter.Display, true);
221 fwrite(Info.SpaceCharacter.Display.c_str(), sizeof(char), Info.SpaceCharacter.Display.size(), Output);
222 fwrite("\" t=\"", sizeof(char), 5, Output);
223 XML_Escape(&Info.SpaceCharacter.Text, true);
224 fwrite(Info.SpaceCharacter.Text.c_str(), sizeof(char), Info.SpaceCharacter.Text.size(), Output);
225 fwrite("\" b=\"", sizeof(char), 5, Output);
226 sprintf(Number, "%d", Info.SpaceCharacter.Colour);
227 fwrite(Number, sizeof(char), strlen(Number), Output);
228 fwrite("\"/>\n", sizeof(char), 4, Output);
230 // Write out the paragraph character
231 fwrite("<paragraph d=\"", sizeof(char), 14, Output);
232 XML_Escape(&Info.ParagraphCharacter.Display, true);
233 fwrite(Info.ParagraphCharacter.Display.c_str(), sizeof(char), Info.ParagraphCharacter.Display.size(), Output);
234 fwrite("\" t=\"", sizeof(char), 5, Output);
235 XML_Escape(&Info.ParagraphCharacter.Text, true);
236 fwrite(Info.ParagraphCharacter.Text.c_str(), sizeof(char), Info.ParagraphCharacter.Text.size(), Output);
237 fwrite("\" b=\"", sizeof(char), 5, Output);
238 sprintf(Number, "%d", Info.ParagraphCharacter.Colour);
239 fwrite(Number, sizeof(char), strlen(Number), Output);
240 fwrite("\"/>\n", sizeof(char), 4, Output);
242 // Write out the control character
243 fwrite("<control d=\"", sizeof(char), 12, Output);
244 XML_Escape(&Info.ControlCharacter.Display, true);
245 fwrite(Info.ControlCharacter.Display.c_str(), sizeof(char), Info.ControlCharacter.Display.size(), Output);
246 fwrite("\" t=\"", sizeof(char), 5, Output);
247 XML_Escape(&Info.ControlCharacter.Text, true);
248 fwrite(Info.ControlCharacter.Text.c_str(), sizeof(char), Info.ControlCharacter.Text.size(), Output);
249 fwrite("\" b=\"", sizeof(char), 5, Output);
250 sprintf(Number, "%d", Info.ControlCharacter.Colour);
251 fwrite(Number, sizeof(char), strlen(Number), Output);
252 fwrite("\"/>\n", sizeof(char), 4, Output);
254 // typedef vector < AlphInfo::group >::iterator gi;
255 // gi LG = Info.Groups.end();
256 // for(gi CG = Info.Groups.begin(); CG != LG; CG++) {
257 // fwrite("<group name=\"", sizeof(char), 13, Output);
258 // XML_Escape(&CG->Description, true);
259 // fwrite(CG->Description.c_str(), sizeof(char), CG->Description.size(), Output);
260 // fwrite("\" b=\"", sizeof(char), 5, Output);
261 // sprintf(Number, "%d", CG->Colour);
262 // fwrite(Number, sizeof(char), strlen(Number), Output);
263 // fwrite("\">\n", sizeof(char), 3, Output);
265 // // Iterate over CG->Characters
266 // typedef vector < AlphInfo::character >::iterator ci;
267 // ci LC = CG->Characters.end();
268 // for(ci CC = CG->Characters.begin(); CC != LC; CC++) {
269 // fwrite("<s d=\"", sizeof(char), 6, Output);
270 // XML_Escape(&CC->Display, true);
271 // fwrite(CC->Display.c_str(), sizeof(char), CC->Display.size(), Output);
272 // fwrite("\" t=\"", sizeof(char), 5, Output);
273 // XML_Escape(&CC->Text, true);
274 // fwrite(CC->Text.c_str(), sizeof(char), CC->Text.size(), Output);
275 // fwrite("\" b=\"", sizeof(char), 5, Output);
276 // sprintf(Number, "%d", CC->Colour);
277 // fwrite(Number, sizeof(char), strlen(Number), Output);
278 // fwrite("\"/>\n", sizeof(char), 4, Output);
279 // }
281 // fwrite("</group>\n", sizeof(char), 9, Output);
282 // }
284 fwrite("</alphabet>\n", sizeof(char), 12, Output);
287 fwrite("</alphabets>\n", sizeof(char), 13, Output);
289 fclose(Output);
292 void CAlphIO::CreateDefault() {
293 // TODO I appreciate these strings should probably be in a resource file.
294 // Not urgent though as this is not intended to be used. It's just a
295 // last ditch effort in case file I/O totally fails.
296 AlphInfo & Default = Alphabets["Default"];
297 Default.AlphID = "Default";
298 Default.Type = Opts::Western;
299 Default.Mutable = false;
300 Default.Orientation = Opts::LeftToRight;
301 Default.ParagraphCharacter.Display = "¶";
302 #ifdef WIN32
303 Default.ParagraphCharacter.Text = "\r\n";
304 #else
305 Default.ParagraphCharacter.Text = "\n";
306 #endif
307 Default.ParagraphCharacter.Colour = 9;
308 Default.SpaceCharacter.Display = "_";
309 Default.SpaceCharacter.Text = " ";
310 Default.SpaceCharacter.Colour = 9;
311 Default.ControlCharacter.Display = "Control";
312 Default.ControlCharacter.Text = "";
313 Default.ControlCharacter.Colour = 8;
314 Default.TrainingFile = "training_english_GB.txt";
315 Default.GameModeFile = "gamemode_english_GB.txt";
316 Default.PreferredColours = "Default";
317 std::string Chars = "abcdefghijklmnopqrstuvwxyz";
319 // // Obsolete
320 // Default.Groups.resize(1);
321 // Default.Groups[0].Description = "Lower case Latin letters";
322 // Default.Groups[0].Characters.resize(Chars.size());
323 // Default.Groups[0].Colour = 0;
324 // Default.m_pBaseGroup = 0;
325 // for(unsigned int i = 0; i < Chars.size(); i++) {
326 // Default.Groups[0].Characters[i].Text = Chars[i];
327 // Default.Groups[0].Characters[i].Display = Chars[i];
328 // Default.Groups[0].Characters[i].Colour = i + 10;
329 // }
330 // ---
331 Default.m_pBaseGroup = 0;
333 Default.m_vCharacters.resize(Chars.size());
334 for(unsigned int i(0); i < Chars.size(); i++) {
335 Default.m_vCharacters[i].Text = Chars[i];
336 Default.m_vCharacters[i].Display = Chars[i];
337 Default.m_vCharacters[i].Colour = i + 10;
341 void CAlphIO::XML_Escape(std::string *Text, bool Attribute) {
342 // The XML "W3C Recommendation" is here: http://www.w3.org/TR/REC-xml
344 std::string & Input = *Text; // Makes syntax less fiddly below
346 for(unsigned int i = 0; i < Text->size(); i++) {
347 // & and < need escaping in XML. In one rare circumstance >
348 // needs escaping too. I'll always do it, as I'm allowed to.
349 if(Input[i] == '&') {
350 Input.replace(i, 1, "&amp;");
351 continue;
353 if(Input[i] == '<') {
354 Input.replace(i, 1, "&lt;");
355 continue;
357 if(Input[i] == '>') {
358 Input.replace(i, 1, "&gt;");
359 continue;
361 // " and ' might need escaping inside attributes, I'll do them all.
362 if(Attribute == false)
363 continue;
365 if(Input[i] == '\'') {
366 Input.replace(i, 1, "&apos;");
367 continue;
369 if(Input[i] == '"') {
370 Input.replace(i, 1, "&quot;");
371 continue;
376 // Below here handlers for the Expat XML input library
377 ////////////////////////////////////////////////////////////////////////////////////
379 void CAlphIO::XML_StartElement(void *userData, const expat::XML_Char *name, const expat::XML_Char **atts) {
380 CAlphIO *Me = (CAlphIO *) userData;
382 Me->CData = "";
384 if(strcmp(name, "alphabet") == 0) {
385 AlphInfo NewInfo;
386 Me->InputInfo = NewInfo;
387 Me->InputInfo.Mutable = Me->LoadMutable;
388 Me->InputInfo.SpaceCharacter.Colour = -1;
389 Me->InputInfo.ParagraphCharacter.Colour = -1;
390 Me->InputInfo.ControlCharacter.Colour = -1;
391 Me->InputInfo.StartConvertCharacter.Text = "";
392 Me->InputInfo.EndConvertCharacter.Text = "";
393 Me->InputInfo.m_iCharacters = 1; // Start at 1 as 0 is the root node symbol
394 Me->InputInfo.m_pBaseGroup = 0;
395 Me->bFirstGroup = true;
396 Me->iGroupIdx = 0;
397 while(*atts != 0) {
398 if(strcmp(*atts, "name") == 0) {
399 atts++;
400 Me->InputInfo.AlphID = *atts;
401 atts--;
403 atts += 2;
405 Me->InputInfo.m_iConversionID = 0;
406 Me->InputInfo.m_strDefaultContext = ". ";
408 return;
411 if(strcmp(name, "orientation") == 0) {
412 while(*atts != 0) {
413 if(!strcmp(*atts, "type")) {
414 atts++;
415 if(!strcmp(*atts, "RL")) {
416 Me->InputInfo.Orientation = Opts::RightToLeft;
418 else if(!strcmp(*atts, "TB")) {
419 Me->InputInfo.Orientation = Opts::TopToBottom;
421 else if(!strcmp(*atts, "BT")) {
422 Me->InputInfo.Orientation = Opts::BottomToTop;
424 else
425 Me->InputInfo.Orientation = Opts::LeftToRight;
426 atts--;
428 atts += 2;
430 return;
433 if(strcmp(name, "encoding") == 0) {
434 while(*atts != 0) {
435 if(strcmp(*atts, "type") == 0) {
436 atts++;
437 Me->InputInfo.Type = Me->StoT[*atts];
438 atts--;
440 atts += 2;
442 return;
445 if(strcmp(name, "space") == 0) {
446 while(*atts != 0) {
447 if(strcmp(*atts, "t") == 0) {
448 atts++;
449 Me->InputInfo.SpaceCharacter.Text = *atts;
450 atts--;
452 if(strcmp(*atts, "d") == 0) {
453 atts++;
454 Me->InputInfo.SpaceCharacter.Display = *atts;
455 atts--;
457 if(strcmp(*atts, "b") == 0) {
458 atts++;
459 Me->InputInfo.SpaceCharacter.Colour = atoi(*atts);
460 atts--;
462 if(strcmp(*atts, "f") == 0) {
463 atts++;
464 Me->InputInfo.SpaceCharacter.Foreground = *atts;
465 atts--;
467 atts += 2;
469 return;
471 if(strcmp(name, "paragraph") == 0) {
472 while(*atts != 0) {
473 if(strcmp(*atts, "d") == 0) {
474 atts++;
475 Me->InputInfo.ParagraphCharacter.Display = *atts;
476 #ifdef WIN32
477 Me->InputInfo.ParagraphCharacter.Text = "\r\n";
478 #else
479 Me->InputInfo.ParagraphCharacter.Text = "\n";
480 #endif
481 atts--;
483 if(strcmp(*atts, "b") == 0) {
484 atts++;
485 Me->InputInfo.ParagraphCharacter.Colour = atoi(*atts);
486 atts--;
488 if(strcmp(*atts, "f") == 0) {
489 atts++;
490 Me->InputInfo.ParagraphCharacter.Foreground = *atts;
491 atts--;
493 atts += 2;
495 return;
497 if(strcmp(name, "control") == 0) {
498 while(*atts != 0) {
499 if(strcmp(*atts, "t") == 0) {
500 atts++;
501 Me->InputInfo.ControlCharacter.Text = *atts;
502 atts--;
504 if(strcmp(*atts, "d") == 0) {
505 atts++;
506 Me->InputInfo.ControlCharacter.Display = *atts;
507 atts--;
509 if(strcmp(*atts, "b") == 0) {
510 atts++;
511 Me->InputInfo.ControlCharacter.Colour = atoi(*atts);
512 atts--;
514 if(strcmp(*atts, "f") == 0) {
515 atts++;
516 Me->InputInfo.ControlCharacter.Foreground = *atts;
517 atts--;
519 atts += 2;
521 return;
524 if(strcmp(name, "group") == 0) {
525 SGroupInfo *pNewGroup(new SGroupInfo);
526 pNewGroup->iColour = (Me->iGroupIdx % 3) + 110;
527 ++Me->iGroupIdx;
529 if(Me->bFirstGroup) {
530 pNewGroup->bVisible = false;
531 Me->bFirstGroup = false;
533 else {
534 pNewGroup->bVisible = true;
537 while(*atts != 0) {
538 if(strcmp(*atts, "name") == 0) {
539 // TODO: Fix this, or remove if names aren't needed
541 // atts++;
542 // Me->InputInfo.Groups.back().Description = *atts;
543 // atts--;
545 if(strcmp(*atts, "b") == 0) {
546 atts++;
547 pNewGroup->iColour = atoi(*atts);
548 atts--;
550 if(strcmp(*atts, "visible") == 0) {
551 atts++;
552 if(!strcmp(*atts, "yes") || !strcmp(*atts, "on"))
553 pNewGroup->bVisible = true;
554 else if(!strcmp(*atts, "no") || !strcmp(*atts, "off"))
555 pNewGroup->bVisible = false;
556 atts--;
558 if(strcmp(*atts, "label") == 0) {
559 atts++;
560 pNewGroup->strLabel = *atts;
561 atts--;
563 atts += 2;
566 pNewGroup->iStart = Me->InputInfo.m_iCharacters;
568 pNewGroup->pChild = NULL;
570 if(Me->InputInfo.m_vGroups.size() > 0) {
571 pNewGroup->pNext = Me->InputInfo.m_vGroups.back()->pChild;
572 Me->InputInfo.m_vGroups.back()->pChild = pNewGroup;
574 else {
575 pNewGroup->pNext = Me->InputInfo.m_pBaseGroup;
576 Me->InputInfo.m_pBaseGroup = pNewGroup;
580 Me->InputInfo.m_vGroups.push_back(pNewGroup);
582 return;
585 if(!strcmp(name, "conversionmode")) {
586 while(*atts != 0) {
587 if(strcmp(*atts, "id") == 0) {
588 atts++;
589 Me->InputInfo.m_iConversionID = atoi(*atts);
590 atts--;
592 atts += 2;
595 return;
598 // Special characters for character composition
599 if(strcmp(name, "convert") == 0) {
600 while(*atts != 0) {
601 if(strcmp(*atts, "t") == 0) {
602 atts++;
603 Me->InputInfo.StartConvertCharacter.Text = *atts;
604 atts--;
606 if(strcmp(*atts, "d") == 0) {
607 atts++;
608 Me->InputInfo.StartConvertCharacter.Display = *atts;
609 atts--;
611 if(strcmp(*atts, "b") == 0) {
612 atts++;
613 Me->InputInfo.StartConvertCharacter.Colour = atoi(*atts);
614 atts--;
616 if(strcmp(*atts, "f") == 0) {
617 atts++;
618 Me->InputInfo.StartConvertCharacter.Foreground = *atts;
619 atts--;
621 atts += 2;
623 return;
626 if(strcmp(name, "protect") == 0) {
627 while(*atts != 0) {
628 if(strcmp(*atts, "t") == 0) {
629 atts++;
630 Me->InputInfo.EndConvertCharacter.Text = *atts;
631 atts--;
633 if(strcmp(*atts, "d") == 0) {
634 atts++;
635 Me->InputInfo.EndConvertCharacter.Display = *atts;
636 atts--;
638 if(strcmp(*atts, "b") == 0) {
639 atts++;
640 Me->InputInfo.EndConvertCharacter.Colour = atoi(*atts);
641 atts--;
643 if(strcmp(*atts, "f") == 0) {
644 atts++;
645 Me->InputInfo.EndConvertCharacter.Foreground = *atts;
646 atts--;
648 atts += 2;
650 return;
653 if(strcmp(name, "context") == 0) {
654 while(*atts != 0) {
655 if(strcmp(*atts, "default") == 0) {
656 atts++;
657 Me->InputInfo.m_strDefaultContext = *atts;
658 atts--;
660 atts += 2;
662 return;
665 if(strcmp(name, "s") == 0) {
666 AlphInfo::character NewCharacter;
668 ++Me->InputInfo.m_iCharacters;
670 NewCharacter.Colour = -1;
672 Me->InputInfo.m_vCharacters.push_back(NewCharacter);
673 AlphInfo::character &Ch = Me->InputInfo.m_vCharacters.back();
675 // FIXME - need to do a more sensible job of ensuring that
676 // defaults are correct (plus more generally fixing behaviour when
677 // incomplete/invalid XML is supplied)
678 Ch.Colour=-1;
680 while(*atts != 0) {
681 if(strcmp(*atts, "t") == 0) {
682 atts++;
683 Ch.Text = *atts;
684 atts--;
686 if(strcmp(*atts, "d") == 0) {
687 atts++;
688 Ch.Display = *atts;
689 atts--;
691 if(strcmp(*atts, "b") == 0) {
692 atts++;
693 Ch.Colour = atoi(*atts);
694 atts--;
696 if(strcmp(*atts, "f") == 0) {
697 atts++;
698 Ch.Foreground = *atts;
699 atts--;
701 atts += 2;
703 return;
707 void CAlphIO::XML_EndElement(void *userData, const expat::XML_Char *name) {
708 CAlphIO *Me = (CAlphIO *) userData;
710 if(strcmp(name, "alphabet") == 0) {
711 Me->Alphabets[Me->InputInfo.AlphID] = Me->InputInfo;
712 return;
715 if(strcmp(name, "train") == 0) {
716 Me->InputInfo.TrainingFile = Me->CData;
717 return;
720 if(strcmp(name, "gamemode") == 0) {
721 Me->InputInfo.GameModeFile = Me->CData;
722 return;
725 if(strcmp(name, "palette") == 0) {
726 Me->InputInfo.PreferredColours = Me->CData;
727 return;
730 if(!strcmp(name, "group")) {
731 Me->InputInfo.m_vGroups.back()->iEnd = Me->InputInfo.m_iCharacters;
732 Me->InputInfo.m_vGroups.pop_back();
733 return;
737 void CAlphIO::XML_CharacterData(void *userData, const expat::XML_Char *s, int len) {
738 // CAREFUL: s points to a string which is NOT null-terminated.
740 CAlphIO *Me = (CAlphIO *) userData;
742 Me->CData.append(s, len);