3 /////////////////////////////////////////////////////////////////////////////
5 // Copyright (c) 2002 Iain Murray
7 /////////////////////////////////////////////////////////////////////////////
9 #include "../../Common/Common.h"
13 using namespace Dasher
;
15 using namespace expat
;
17 // Track memory leaks on Windows to the line that new'd the memory
20 #define DEBUG_NEW new( _NORMAL_BLOCK, THIS_FILE, __LINE__ )
23 static char THIS_FILE
[] = __FILE__
;
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("") {
31 typedef pair
< Opts::AlphabetTypes
, std::string
> AT
;
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
;
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
]);
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
) {
71 if((Input
= fopen(Filename
.c_str(), "r")) == (FILE *) 0) {
72 // could not open file
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
];
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
) {
96 XML_ParserFree(Parser
);
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";
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
];
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
);
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
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)];
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
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
);
184 case Opts::TopToBottom
:
185 fwrite("TB", sizeof(char), 2, Output
);
187 case Opts::BottomToTop
:
188 fwrite("BT", sizeof(char), 2, Output
);
190 case Opts::LeftToRight
:
191 // deliberate fall through
193 fwrite("LR", sizeof(char), 2, Output
);
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);
281 // fwrite("</group>\n", sizeof(char), 9, Output);
284 fwrite("</alphabet>\n", sizeof(char), 12, Output
);
287 fwrite("</alphabets>\n", sizeof(char), 13, 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
= "¶";
303 Default
.ParagraphCharacter
.Text
= "\r\n";
305 Default
.ParagraphCharacter
.Text
= "\n";
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";
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;
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, "&");
353 if(Input
[i
] == '<') {
354 Input
.replace(i
, 1, "<");
357 if(Input
[i
] == '>') {
358 Input
.replace(i
, 1, ">");
361 // " and ' might need escaping inside attributes, I'll do them all.
362 if(Attribute
== false)
365 if(Input
[i
] == '\'') {
366 Input
.replace(i
, 1, "'");
369 if(Input
[i
] == '"') {
370 Input
.replace(i
, 1, """);
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
;
384 if(strcmp(name
, "alphabet") == 0) {
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;
398 if(strcmp(*atts
, "name") == 0) {
400 Me
->InputInfo
.AlphID
= *atts
;
405 Me
->InputInfo
.m_iConversionID
= 0;
406 Me
->InputInfo
.m_strDefaultContext
= ". ";
411 if(strcmp(name
, "orientation") == 0) {
413 if(!strcmp(*atts
, "type")) {
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
;
425 Me
->InputInfo
.Orientation
= Opts::LeftToRight
;
433 if(strcmp(name
, "encoding") == 0) {
435 if(strcmp(*atts
, "type") == 0) {
437 Me
->InputInfo
.Type
= Me
->StoT
[*atts
];
445 if(strcmp(name
, "space") == 0) {
447 if(strcmp(*atts
, "t") == 0) {
449 Me
->InputInfo
.SpaceCharacter
.Text
= *atts
;
452 if(strcmp(*atts
, "d") == 0) {
454 Me
->InputInfo
.SpaceCharacter
.Display
= *atts
;
457 if(strcmp(*atts
, "b") == 0) {
459 Me
->InputInfo
.SpaceCharacter
.Colour
= atoi(*atts
);
462 if(strcmp(*atts
, "f") == 0) {
464 Me
->InputInfo
.SpaceCharacter
.Foreground
= *atts
;
471 if(strcmp(name
, "paragraph") == 0) {
473 if(strcmp(*atts
, "d") == 0) {
475 Me
->InputInfo
.ParagraphCharacter
.Display
= *atts
;
477 Me
->InputInfo
.ParagraphCharacter
.Text
= "\r\n";
479 Me
->InputInfo
.ParagraphCharacter
.Text
= "\n";
483 if(strcmp(*atts
, "b") == 0) {
485 Me
->InputInfo
.ParagraphCharacter
.Colour
= atoi(*atts
);
488 if(strcmp(*atts
, "f") == 0) {
490 Me
->InputInfo
.ParagraphCharacter
.Foreground
= *atts
;
497 if(strcmp(name
, "control") == 0) {
499 if(strcmp(*atts
, "t") == 0) {
501 Me
->InputInfo
.ControlCharacter
.Text
= *atts
;
504 if(strcmp(*atts
, "d") == 0) {
506 Me
->InputInfo
.ControlCharacter
.Display
= *atts
;
509 if(strcmp(*atts
, "b") == 0) {
511 Me
->InputInfo
.ControlCharacter
.Colour
= atoi(*atts
);
514 if(strcmp(*atts
, "f") == 0) {
516 Me
->InputInfo
.ControlCharacter
.Foreground
= *atts
;
524 if(strcmp(name
, "group") == 0) {
525 SGroupInfo
*pNewGroup(new SGroupInfo
);
526 pNewGroup
->iColour
= (Me
->iGroupIdx
% 3) + 110;
529 if(Me
->bFirstGroup
) {
530 pNewGroup
->bVisible
= false;
531 Me
->bFirstGroup
= false;
534 pNewGroup
->bVisible
= true;
538 if(strcmp(*atts
, "name") == 0) {
539 // TODO: Fix this, or remove if names aren't needed
542 // Me->InputInfo.Groups.back().Description = *atts;
545 if(strcmp(*atts
, "b") == 0) {
547 pNewGroup
->iColour
= atoi(*atts
);
550 if(strcmp(*atts
, "visible") == 0) {
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;
558 if(strcmp(*atts
, "label") == 0) {
560 pNewGroup
->strLabel
= *atts
;
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
;
575 pNewGroup
->pNext
= Me
->InputInfo
.m_pBaseGroup
;
576 Me
->InputInfo
.m_pBaseGroup
= pNewGroup
;
580 Me
->InputInfo
.m_vGroups
.push_back(pNewGroup
);
585 if(!strcmp(name
, "conversionmode")) {
587 if(strcmp(*atts
, "id") == 0) {
589 Me
->InputInfo
.m_iConversionID
= atoi(*atts
);
598 // Special characters for character composition
599 if(strcmp(name
, "convert") == 0) {
601 if(strcmp(*atts
, "t") == 0) {
603 Me
->InputInfo
.StartConvertCharacter
.Text
= *atts
;
606 if(strcmp(*atts
, "d") == 0) {
608 Me
->InputInfo
.StartConvertCharacter
.Display
= *atts
;
611 if(strcmp(*atts
, "b") == 0) {
613 Me
->InputInfo
.StartConvertCharacter
.Colour
= atoi(*atts
);
616 if(strcmp(*atts
, "f") == 0) {
618 Me
->InputInfo
.StartConvertCharacter
.Foreground
= *atts
;
626 if(strcmp(name
, "protect") == 0) {
628 if(strcmp(*atts
, "t") == 0) {
630 Me
->InputInfo
.EndConvertCharacter
.Text
= *atts
;
633 if(strcmp(*atts
, "d") == 0) {
635 Me
->InputInfo
.EndConvertCharacter
.Display
= *atts
;
638 if(strcmp(*atts
, "b") == 0) {
640 Me
->InputInfo
.EndConvertCharacter
.Colour
= atoi(*atts
);
643 if(strcmp(*atts
, "f") == 0) {
645 Me
->InputInfo
.EndConvertCharacter
.Foreground
= *atts
;
653 if(strcmp(name
, "context") == 0) {
655 if(strcmp(*atts
, "default") == 0) {
657 Me
->InputInfo
.m_strDefaultContext
= *atts
;
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)
681 if(strcmp(*atts
, "t") == 0) {
686 if(strcmp(*atts
, "d") == 0) {
691 if(strcmp(*atts
, "b") == 0) {
693 Ch
.Colour
= atoi(*atts
);
696 if(strcmp(*atts
, "f") == 0) {
698 Ch
.Foreground
= *atts
;
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
;
715 if(strcmp(name
, "train") == 0) {
716 Me
->InputInfo
.TrainingFile
= Me
->CData
;
720 if(strcmp(name
, "gamemode") == 0) {
721 Me
->InputInfo
.GameModeFile
= Me
->CData
;
725 if(strcmp(name
, "palette") == 0) {
726 Me
->InputInfo
.PreferredColours
= Me
->CData
;
730 if(!strcmp(name
, "group")) {
731 Me
->InputInfo
.m_vGroups
.back()->iEnd
= Me
->InputInfo
.m_iCharacters
;
732 Me
->InputInfo
.m_vGroups
.pop_back();
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
);