Fixed UTF-8 file save bug Update to circle start handler Started two
[dasher.git] / Src / DasherCore / Alphabet / AlphIO.cpp
blob1d5cfa72cda212ea77a31a9bdd11e77a531d682f
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(string SystemLocation, string UserLocation, vector <string >Filenames)
28 :BlankInfo(), SystemLocation(SystemLocation), UserLocation(UserLocation), Filenames(Filenames), LoadMutable(false), CData("") {
29 CreateDefault();
31 typedef pair < Opts::AlphabetTypes, 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 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 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 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 XML_Char *name, const 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;
406 return;
409 if(strcmp(name, "orientation") == 0) {
410 while(*atts != 0) {
411 if(!strcmp(*atts, "type")) {
412 atts++;
413 if(!strcmp(*atts, "RL")) {
414 Me->InputInfo.Orientation = Opts::RightToLeft;
416 else if(!strcmp(*atts, "TB")) {
417 Me->InputInfo.Orientation = Opts::TopToBottom;
419 else if(!strcmp(*atts, "BT")) {
420 Me->InputInfo.Orientation = Opts::BottomToTop;
422 else
423 Me->InputInfo.Orientation = Opts::LeftToRight;
424 atts--;
426 atts += 2;
428 return;
431 if(strcmp(name, "encoding") == 0) {
432 while(*atts != 0) {
433 if(strcmp(*atts, "type") == 0) {
434 atts++;
435 Me->InputInfo.Type = Me->StoT[*atts];
436 atts--;
438 atts += 2;
440 return;
443 if(strcmp(name, "space") == 0) {
444 while(*atts != 0) {
445 if(strcmp(*atts, "t") == 0) {
446 atts++;
447 Me->InputInfo.SpaceCharacter.Text = *atts;
448 atts--;
450 if(strcmp(*atts, "d") == 0) {
451 atts++;
452 Me->InputInfo.SpaceCharacter.Display = *atts;
453 atts--;
455 if(strcmp(*atts, "b") == 0) {
456 atts++;
457 Me->InputInfo.SpaceCharacter.Colour = atoi(*atts);
458 atts--;
460 if(strcmp(*atts, "f") == 0) {
461 atts++;
462 Me->InputInfo.SpaceCharacter.Foreground = *atts;
463 atts--;
465 atts += 2;
467 return;
469 if(strcmp(name, "paragraph") == 0) {
470 while(*atts != 0) {
471 if(strcmp(*atts, "d") == 0) {
472 atts++;
473 Me->InputInfo.ParagraphCharacter.Display = *atts;
474 #ifdef WIN32
475 Me->InputInfo.ParagraphCharacter.Text = "\r\n";
476 #else
477 Me->InputInfo.ParagraphCharacter.Text = "\n";
478 #endif
479 atts--;
481 if(strcmp(*atts, "b") == 0) {
482 atts++;
483 Me->InputInfo.ParagraphCharacter.Colour = atoi(*atts);
484 atts--;
486 if(strcmp(*atts, "f") == 0) {
487 atts++;
488 Me->InputInfo.ParagraphCharacter.Foreground = *atts;
489 atts--;
491 atts += 2;
493 return;
495 if(strcmp(name, "control") == 0) {
496 while(*atts != 0) {
497 if(strcmp(*atts, "t") == 0) {
498 atts++;
499 Me->InputInfo.ControlCharacter.Text = *atts;
500 atts--;
502 if(strcmp(*atts, "d") == 0) {
503 atts++;
504 Me->InputInfo.ControlCharacter.Display = *atts;
505 atts--;
507 if(strcmp(*atts, "b") == 0) {
508 atts++;
509 Me->InputInfo.ControlCharacter.Colour = atoi(*atts);
510 atts--;
512 if(strcmp(*atts, "f") == 0) {
513 atts++;
514 Me->InputInfo.ControlCharacter.Foreground = *atts;
515 atts--;
517 atts += 2;
519 return;
522 if(strcmp(name, "group") == 0) {
523 SGroupInfo *pNewGroup(new SGroupInfo);
524 pNewGroup->iColour = (Me->iGroupIdx % 3) + 110;
525 ++Me->iGroupIdx;
527 if(Me->bFirstGroup) {
528 pNewGroup->bVisible = false;
529 Me->bFirstGroup = false;
531 else {
532 pNewGroup->bVisible = true;
535 while(*atts != 0) {
536 if(strcmp(*atts, "name") == 0) {
537 // TODO: Fix this, or remove if names aren't needed
539 // atts++;
540 // Me->InputInfo.Groups.back().Description = *atts;
541 // atts--;
543 if(strcmp(*atts, "b") == 0) {
544 atts++;
545 pNewGroup->iColour = atoi(*atts);
546 atts--;
548 if(strcmp(*atts, "visible") == 0) {
549 atts++;
550 if(!strcmp(*atts, "yes") || !strcmp(*atts, "on"))
551 pNewGroup->bVisible = true;
552 else if(!strcmp(*atts, "no") || !strcmp(*atts, "off"))
553 pNewGroup->bVisible = false;
554 atts--;
556 if(strcmp(*atts, "label") == 0) {
557 atts++;
558 pNewGroup->strLabel = *atts;
559 atts--;
561 atts += 2;
564 pNewGroup->iStart = Me->InputInfo.m_iCharacters;
566 pNewGroup->pChild = NULL;
568 if(Me->InputInfo.m_vGroups.size() > 0) {
569 pNewGroup->pNext = Me->InputInfo.m_vGroups.back()->pChild;
570 Me->InputInfo.m_vGroups.back()->pChild = pNewGroup;
572 else {
573 pNewGroup->pNext = Me->InputInfo.m_pBaseGroup;
574 Me->InputInfo.m_pBaseGroup = pNewGroup;
578 Me->InputInfo.m_vGroups.push_back(pNewGroup);
580 return;
583 // Special characters for character composition
584 if(strcmp(name, "convert") == 0) {
585 while(*atts != 0) {
586 if(strcmp(*atts, "t") == 0) {
587 atts++;
588 Me->InputInfo.StartConvertCharacter.Text = *atts;
589 atts--;
591 if(strcmp(*atts, "d") == 0) {
592 atts++;
593 Me->InputInfo.StartConvertCharacter.Display = *atts;
594 atts--;
596 if(strcmp(*atts, "b") == 0) {
597 atts++;
598 Me->InputInfo.StartConvertCharacter.Colour = atoi(*atts);
599 atts--;
601 if(strcmp(*atts, "f") == 0) {
602 atts++;
603 Me->InputInfo.StartConvertCharacter.Foreground = *atts;
604 atts--;
606 atts += 2;
608 return;
611 if(strcmp(name, "protect") == 0) {
612 while(*atts != 0) {
613 if(strcmp(*atts, "t") == 0) {
614 atts++;
615 Me->InputInfo.EndConvertCharacter.Text = *atts;
616 atts--;
618 if(strcmp(*atts, "d") == 0) {
619 atts++;
620 Me->InputInfo.EndConvertCharacter.Display = *atts;
621 atts--;
623 if(strcmp(*atts, "b") == 0) {
624 atts++;
625 Me->InputInfo.EndConvertCharacter.Colour = atoi(*atts);
626 atts--;
628 if(strcmp(*atts, "f") == 0) {
629 atts++;
630 Me->InputInfo.EndConvertCharacter.Foreground = *atts;
631 atts--;
633 atts += 2;
635 return;
638 if(strcmp(name, "s") == 0) {
639 AlphInfo::character NewCharacter;
641 ++Me->InputInfo.m_iCharacters;
643 NewCharacter.Colour = -1;
645 Me->InputInfo.m_vCharacters.push_back(NewCharacter);
646 AlphInfo::character &Ch = Me->InputInfo.m_vCharacters.back();
648 // FIXME - need to do a more sensible job of ensuring that
649 // defaults are correct (plus more generally fixing behaviour when
650 // incomplete/invalid XML is supplied)
651 Ch.Colour=-1;
653 while(*atts != 0) {
654 if(strcmp(*atts, "t") == 0) {
655 atts++;
656 Ch.Text = *atts;
657 atts--;
659 if(strcmp(*atts, "d") == 0) {
660 atts++;
661 Ch.Display = *atts;
662 atts--;
664 if(strcmp(*atts, "b") == 0) {
665 atts++;
666 Ch.Colour = atoi(*atts);
667 atts--;
669 if(strcmp(*atts, "f") == 0) {
670 atts++;
671 Ch.Foreground = *atts;
672 atts--;
674 atts += 2;
676 return;
680 void CAlphIO::XML_EndElement(void *userData, const XML_Char *name) {
681 CAlphIO *Me = (CAlphIO *) userData;
683 if(strcmp(name, "alphabet") == 0) {
684 Me->Alphabets[Me->InputInfo.AlphID] = Me->InputInfo;
685 return;
688 if(strcmp(name, "train") == 0) {
689 Me->InputInfo.TrainingFile = Me->CData;
690 return;
693 if(strcmp(name, "gamemode") == 0) {
694 Me->InputInfo.GameModeFile = Me->CData;
695 return;
698 if(strcmp(name, "palette") == 0) {
699 Me->InputInfo.PreferredColours = Me->CData;
700 return;
703 if(!strcmp(name, "group")) {
704 Me->InputInfo.m_vGroups.back()->iEnd = Me->InputInfo.m_iCharacters;
705 Me->InputInfo.m_vGroups.pop_back();
706 return;
710 void CAlphIO::XML_CharacterData(void *userData, const XML_Char *s, int len) {
711 // CAREFUL: s points to a string which is NOT null-terminated.
713 CAlphIO *Me = (CAlphIO *) userData;
715 Me->CData.append(s, len);