1
/************************************************************************************
3
AstroMenace (Hardcore 3D space shooter with spaceship upgrade possibilities)
4
Copyright © 2006-2012 Michael Kurinnoy, Viewizard
7
AstroMenace is free software: you can redistribute it and/or modify
8
it under the terms of the GNU General Public License as published by
9
the Free Software Foundation, either version 3 of the License, or
10
(at your option) any later version.
12
AstroMenace 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.
17
You should have received a copy of the GNU General Public License
18
along with AstroMenace. If not, see <http://www.gnu.org/licenses/>.
21
Web Site: http://www.viewizard.com/
22
Project: http://sourceforge.net/projects/openastromenace/
23
E-mail: viewizard@viewizard.com
25
*************************************************************************************/
29
#include "../VirtualFileSystem/VFS.h"
38
// ищем первое вхождение подстроки в строку, передаем позицию, или -1 если не найдена
39
int FindSubString(char *String, const char *SubString)
41
unsigned int lenght = strlen(SubString);
42
for (int i = 0; (String+i)[0] != '\0'; i++)
44
if (!strncmp(String+i, SubString, lenght)) return i;
49
// замена данных, с сохранением перевода на новую строку
50
void EraseSubString(char *String, unsigned int StartPos, unsigned int EndPos)
52
// проверяем, если не символы конце строки \r или \n - меняем на пробелы
53
for (unsigned int i=StartPos; i<EndPos; i++)
55
if ((String[i] != '\r') && (String[i] != '\n')) String[i] = ' ';
59
// считаем кол-во строк до текущего положения буфера
61
unsigned int GetLineNumber(char *String, unsigned int Pos)
63
unsigned int LineNumber = 1;
64
unsigned int CurrentPos = 0;
66
while ((strlen(String) > 0) && (CurrentPos <= Pos))
68
if (String[0] == '\n') LineNumber++;
75
unsigned int GetLineNumber(char *UNUSED(String), unsigned int UNUSED(Pos))
81
// создаем подстроку из строки с выделением памяти
82
char *CreateSubString(char *String, unsigned int StartPos, unsigned int EndPos)
84
if (EndPos <= StartPos) return 0;
86
char * Result = new char[EndPos-StartPos+1];
87
strncpy(Result, String+StartPos, EndPos-StartPos);
88
Result[EndPos-StartPos] = 0;
105
//-----------------------------------------------------------------------------
107
//-----------------------------------------------------------------------------
108
void cXMLDocument::AttachXMLChildEntry(cXMLEntry *ParentXMLEntry, cXMLEntry *ChildXMLEntry)
110
if ((RootXMLEntry == 0) && (ParentXMLEntry == 0))
112
RootXMLEntry = ChildXMLEntry;
117
if (ParentXMLEntry == 0) return;
118
if (ChildXMLEntry == 0) return;
120
// первый в списке...
121
if (ParentXMLEntry->LastChild == 0)
123
ChildXMLEntry->Prev = 0;
124
ChildXMLEntry->Next = 0;
125
ParentXMLEntry->FirstChild = ChildXMLEntry;
126
ParentXMLEntry->LastChild = ChildXMLEntry;
128
else // продолжаем заполнение...
130
ChildXMLEntry->Prev = ParentXMLEntry->LastChild;
131
ChildXMLEntry->Next = 0;
132
ParentXMLEntry->LastChild->Next = ChildXMLEntry;
133
ParentXMLEntry->LastChild = ChildXMLEntry;
137
//-----------------------------------------------------------------------------
138
// Исключаем из списка
139
//-----------------------------------------------------------------------------
140
void cXMLDocument::DetachXMLChildEntry(cXMLEntry *ParentXMLEntry, cXMLEntry *XMLChildEntry)
142
if (ParentXMLEntry == 0) return;
143
if (XMLChildEntry == 0) return;
145
// переустанавливаем указатели...
146
if (ParentXMLEntry->FirstChild == XMLChildEntry) ParentXMLEntry->FirstChild = XMLChildEntry->Next;
147
if (ParentXMLEntry->LastChild == XMLChildEntry) ParentXMLEntry->LastChild = XMLChildEntry->Prev;
150
if (XMLChildEntry->Next != 0) XMLChildEntry->Next->Prev = XMLChildEntry->Prev;
151
else if (XMLChildEntry->Prev != 0) XMLChildEntry->Prev->Next = 0;
152
if (XMLChildEntry->Prev != 0) XMLChildEntry->Prev->Next = XMLChildEntry->Next;
153
else if (XMLChildEntry->Next != 0) XMLChildEntry->Next->Prev = 0;
156
//-----------------------------------------------------------------------------
157
// Включаем в список атрибут
158
//-----------------------------------------------------------------------------
159
void cXMLDocument::AttachXMLAttribute(cXMLEntry *XMLEntry, cXMLAttribute *XMLAttribute)
161
if (XMLEntry == 0) return;
162
if (XMLAttribute == 0) return;
164
// первый в списке...
165
if (XMLEntry->LastAttribute == 0)
167
XMLAttribute->Prev = 0;
168
XMLAttribute->Next = 0;
169
XMLEntry->FirstAttribute = XMLAttribute;
170
XMLEntry->LastAttribute = XMLAttribute;
172
else // продолжаем заполнение...
174
XMLAttribute->Prev = XMLEntry->LastAttribute;
175
XMLAttribute->Next = 0;
176
XMLEntry->LastAttribute->Next = XMLAttribute;
177
XMLEntry->LastAttribute = XMLAttribute;
181
//-----------------------------------------------------------------------------
182
// Исключаем из списка атрибут
183
//-----------------------------------------------------------------------------
184
void cXMLDocument::DetachXMLAttribute(cXMLEntry *XMLEntry, cXMLAttribute *XMLAttribute)
186
if (XMLEntry == 0) return;
187
if (XMLAttribute == 0) return;
189
// переустанавливаем указатели...
190
if (XMLEntry->FirstAttribute == XMLAttribute) XMLEntry->FirstAttribute = XMLAttribute->Next;
191
if (XMLEntry->LastAttribute == XMLAttribute) XMLEntry->LastAttribute = XMLAttribute->Prev;
194
if (XMLAttribute->Next != 0) XMLAttribute->Next->Prev = XMLAttribute->Prev;
195
else if (XMLAttribute->Prev != 0) XMLAttribute->Prev->Next = 0;
196
if (XMLAttribute->Prev != 0) XMLAttribute->Prev->Next = XMLAttribute->Next;
197
else if (XMLAttribute->Next != 0) XMLAttribute->Next->Prev = 0;
212
bool cXMLDocument::ParseTagLine(char *OriginBuffer, unsigned int StartPosition, char *Buffer, cXMLEntry *XMLEntry)
214
// 1 - получаем имя тэга (начинается сразу после символа <, а заканчивается пробелом, >, />, или символом таб)
215
int TagNameEnd = FindSubString(Buffer, " ");
216
if (TagNameEnd == -1 || (FindSubString(Buffer, "\t") != -1 && TagNameEnd > FindSubString(Buffer, "\t"))) TagNameEnd = FindSubString(Buffer, "\t");
217
if (TagNameEnd == -1 || (FindSubString(Buffer, ">") != -1 && TagNameEnd > FindSubString(Buffer, ">"))) TagNameEnd = FindSubString(Buffer, ">");
218
if (TagNameEnd == -1 || (FindSubString(Buffer, "/>") != -1 && TagNameEnd > FindSubString(Buffer, "/>"))) TagNameEnd = FindSubString(Buffer, "/>");
219
XMLEntry->Name = CreateSubString(Buffer, 1, TagNameEnd);
221
// 2 - проверяем наличие атрибутов и заносим их в динамический массив
222
unsigned int i = TagNameEnd;
223
while (((Buffer+i)[0] != '>') || ((Buffer+i)[0] != '\0'))
225
// пропускаем все пробелы и табы
226
while ((((Buffer+i)[0] == ' ') || ((Buffer+i)[0] == '\t')) && ((Buffer+i)[0] != '\0')) i++;
227
if ((Buffer+i)[0] == '\0') {fprintf(stderr, "XML file corrupted, line: %i.", GetLineNumber(OriginBuffer, StartPosition)); break;}
228
// еще раз проверяем, возможно завершение тэга ставили через пробел или таб
229
if (((Buffer+i)[0] == '>') || (!strncmp(Buffer+i, "/>", strlen("/>")))) break;
231
// находим имя атрибута
232
unsigned int AttribNameStart = i;
233
while (((Buffer+i)[0] != '=') && ((Buffer+i)[0] != '\0')) i++;
234
if ((Buffer+i)[0] == '\0') {fprintf(stderr, "XML file corrupted, line: %i.", GetLineNumber(OriginBuffer, StartPosition)); break;}
235
unsigned int AttribNameEnd = i;
236
// пропускаем все до кавычек (они у нас следующие, после знака равенства)
238
unsigned int AttribDataStart = i;
239
while ((((Buffer+i)[0] != '\'') && ((Buffer+i)[0] != '\"')) && ((Buffer+i)[0] != '\0')) i++;
240
if ((Buffer+i)[0] == '\0') {fprintf(stderr, "XML file corrupted, line: %i.", GetLineNumber(OriginBuffer, StartPosition)); break;}
241
unsigned int AttribDataEnd = i;
244
// собираем новый атрибут и подключаем его к элементу
245
cXMLAttribute *XMLAttribute = new cXMLAttribute;
246
AttachXMLAttribute(XMLEntry, XMLAttribute);
247
XMLAttribute->Name = CreateSubString(Buffer, AttribNameStart, AttribNameEnd);
248
XMLAttribute->Data = CreateSubString(Buffer, AttribDataStart, AttribDataEnd);
252
// 3 - определяем и номер строки
253
XMLEntry->LineNumber = GetLineNumber(OriginBuffer, StartPosition);
256
// 4 - определить есть ли в ней атрибут закрытия '/', или у нас есть еще и контент и закрывающий тэг
257
if (FindSubString(Buffer, "/>") != -1) return false;
263
bool cXMLDocument::ParseTagContent(char *OriginBuffer, unsigned int StartPosition, char *Buffer, cXMLEntry *ParentXMLEntry)
265
// проверяем наличие вложенных тэгов
266
bool ChildsFound = true;
267
int DetectTagOpenSymbol = FindSubString(Buffer, "<");
268
// если символа открытия в строке нет - это просто данные, иначе проверяем, что стоит до этого символа
269
if (DetectTagOpenSymbol > 0)
272
while(CurrentPos != DetectTagOpenSymbol)
274
// если до открывающего тэга идут не " ", "\t", "\r", "\n", значит у нас просто данные
275
if (((Buffer+CurrentPos)[0] != ' ') &&
276
((Buffer+CurrentPos)[0] != '\t') &&
277
((Buffer+CurrentPos)[0] != '\r') &&
278
((Buffer+CurrentPos)[0] != '\n'))
288
else ChildsFound = false;
291
// 1 - это просто контент, заносим данные и выходи из рекурсии
294
ParentXMLEntry->Content = CreateSubString(Buffer, 0, strlen(Buffer));
297
// 2 - если в строке нашли открывающий символ тэга - идем на рекурсивную обработку строки с хмл данными
300
// в цикле, пока не достигнем конца обрабатываемой строки:
301
unsigned int CurrentBufferPosition = 0;
302
while(strlen(Buffer) > 0)
304
// находим положение открывающего тэг символа и закрывающего
305
DetectTagOpenSymbol = FindSubString(Buffer, "<");
307
// это может быть комментарий, проверяем
308
if (!strncmp(Buffer+DetectTagOpenSymbol, "<!--", strlen("<!--")))
310
// ищем завершающую часть, и сразу перемещаемся к ней
311
int DetectCommentCloseSymbol = FindSubString(Buffer, "-->");
312
if (DetectCommentCloseSymbol == -1)
314
fprintf(stderr, "XML file corrupted, can't find comment end in line %i.\n", GetLineNumber(OriginBuffer, StartPosition+DetectTagOpenSymbol+CurrentBufferPosition));
317
Buffer += DetectCommentCloseSymbol + strlen("-->");
318
CurrentBufferPosition += DetectCommentCloseSymbol + strlen("-->");
322
// если в строке уже нет открывающих символов - просто выходим, все проверили
323
if (DetectTagOpenSymbol == -1) return true;
324
int DetectTagCloseSymbol = FindSubString(Buffer, ">");
325
// если был открывающий символ, но нет закрывающего - это ошибка структуры документа
326
if (DetectTagCloseSymbol == -1)
328
fprintf(stderr, "XML file corrupted, can't find element end for element in line %i.\n", GetLineNumber(OriginBuffer, StartPosition+DetectTagOpenSymbol+CurrentBufferPosition));
331
DetectTagCloseSymbol += strlen(">");
333
// создаем новый элемент и подключаем его к родительскому
334
cXMLEntry *XMLEntry = new cXMLEntry;
335
AttachXMLChildEntry(ParentXMLEntry, XMLEntry);
337
// полученные данные передаем на обработку и анализ строки элемента
338
char *TagString = CreateSubString(Buffer, DetectTagOpenSymbol, DetectTagCloseSymbol);
339
bool ElementHaveContent = ParseTagLine(OriginBuffer, StartPosition+DetectTagOpenSymbol+CurrentBufferPosition, TagString, XMLEntry);
342
// если у нас закрытый тэг - с этим элементом закончили, идем искать дальше
343
if (!ElementHaveContent)
345
Buffer += DetectTagCloseSymbol;
346
CurrentBufferPosition += DetectTagCloseSymbol;
350
// если тэг открытый - ищем завершающий тэг </имя>
351
char *CloseElement = new char[strlen("</>")+strlen(XMLEntry->Name)+1];
352
strcpy(CloseElement, "</"); strcat(CloseElement, XMLEntry->Name); strcat(CloseElement, ">");
353
CloseElement[strlen("</>")+strlen(XMLEntry->Name)] = 0;
354
int CloseElementPosition = FindSubString(Buffer, CloseElement);
355
delete [] CloseElement;
356
// если закрывающего элемента нет - значит файл поврежден
357
if (CloseElementPosition == -1)
359
fprintf(stderr, "XML file corrupted, can't find element end: %s in line: %i\n", XMLEntry->Name, GetLineNumber(OriginBuffer, StartPosition+DetectTagOpenSymbol+CurrentBufferPosition));
363
// передаем данные на рекурсивную обработку (если закрывающий тэг не стоит сразу после открывающего)
364
if (DetectTagCloseSymbol < CloseElementPosition)
366
char *ElementContent = CreateSubString(Buffer, DetectTagCloseSymbol, CloseElementPosition);
367
if (!ParseTagContent(OriginBuffer, DetectTagCloseSymbol+StartPosition+CurrentBufferPosition, ElementContent, XMLEntry))
369
// вернули с ошибкой, выходим
370
delete [] ElementContent;
373
delete [] ElementContent;
377
Buffer += CloseElementPosition + strlen(XMLEntry->Name) + strlen("</>");
378
CurrentBufferPosition += CloseElementPosition + strlen(XMLEntry->Name) + strlen("</>");
397
//-----------------------------------------------------------------------------
399
//-----------------------------------------------------------------------------
400
bool cXMLDocument::Load(const char *XMLFileName)
402
printf("Open XML file: %s\n", XMLFileName);
404
// если что-то было загружено ранее - освобождаем
405
ReleaseXMLDocument();
408
eFILE *XMLFile = vw_fopen(XMLFileName);
412
fprintf(stderr, "XML file not found: %s\n", XMLFileName);
416
// читаем все данные в буфер
417
XMLFile->fseek(0, SEEK_END);
418
unsigned int DataLength = XMLFile->ftell();
419
XMLFile->fseek(0, SEEK_SET);
420
char *Buffer = new char[DataLength+1];
421
Buffer[DataLength] = 0;
422
XMLFile->fread(Buffer, DataLength, 1);
426
// проверяем заголовок
427
if (FindSubString(Buffer, "<?xml") == -1)
429
fprintf(stderr, "XML file corrupted: %s\n", XMLFileName);
432
if (FindSubString(Buffer, "?>") == -1)
434
fprintf(stderr, "XML file corrupted: %s\n", XMLFileName);
439
// идем на рекурсивную обработку
440
if (!ParseTagContent(Buffer, FindSubString(Buffer, "?>")+strlen("?>"), Buffer+FindSubString(Buffer, "?>")+strlen("?>"), 0))
442
fprintf(stderr, "XML file corrupted: %s\n", XMLFileName);
447
if (RootXMLEntry == 0)
449
fprintf(stderr, "XML file corrupted, root element not found: %s\n", XMLFileName);
466
//-----------------------------------------------------------------------------
468
//-----------------------------------------------------------------------------
469
void cXMLDocument::SaveRecursive(cXMLEntry *XMLEntry, SDL_RWops *File, unsigned int Level)
471
// если это комментарий
472
if (XMLEntry->EntryType == 1)
474
for (unsigned int i=0; i<Level; i++) SDL_RWwrite(File, " ", strlen(" "), 1);
475
SDL_RWwrite(File, "<!--", strlen("<!--"), 1);
476
SDL_RWwrite(File, XMLEntry->Name, strlen(XMLEntry->Name), 1);
477
SDL_RWwrite(File, "-->\r\n", strlen("-->\r\n"), 1);
482
for (unsigned int i=0; i<Level; i++) SDL_RWwrite(File, " ", strlen(" "), 1);
483
SDL_RWwrite(File, "<", strlen("<"), 1);
484
SDL_RWwrite(File, XMLEntry->Name, strlen(XMLEntry->Name), 1);
487
if (XMLEntry->FirstAttribute != 0)
489
cXMLAttribute *TmpAttrib = XMLEntry->FirstAttribute;
490
while (TmpAttrib != 0)
492
SDL_RWwrite(File, " ", strlen(" "), 1);
493
SDL_RWwrite(File, TmpAttrib->Name, strlen(TmpAttrib->Name), 1);
494
SDL_RWwrite(File, "=\"", strlen("=\""), 1);
495
SDL_RWwrite(File, TmpAttrib->Data, strlen(TmpAttrib->Data), 1);
496
SDL_RWwrite(File, "\"", strlen("\""), 1);
497
TmpAttrib = TmpAttrib->Next;
499
SDL_RWwrite(File, " ", strlen(" "), 1);
504
if ((XMLEntry->FirstChild != 0) || (XMLEntry->Content != 0))
506
if (XMLEntry->Content != 0)
508
SDL_RWwrite(File, ">", strlen(">"), 1);
509
SDL_RWwrite(File, XMLEntry->Content, strlen(XMLEntry->Content), 1);
510
SDL_RWwrite(File, "</", strlen("</"), 1);
511
SDL_RWwrite(File, XMLEntry->Name, strlen(XMLEntry->Name), 1);
512
SDL_RWwrite(File, ">\r\n", strlen(">\r\n"), 1);
516
SDL_RWwrite(File, ">\r\n", strlen(">\r\n"), 1);
517
cXMLEntry *Tmp = XMLEntry->FirstChild;
520
SaveRecursive(Tmp, File, Level+1);
523
for (unsigned int i=0; i<Level; i++) SDL_RWwrite(File, " ", strlen(" "), 1);
524
SDL_RWwrite(File, "</", strlen("</"), 1);
525
SDL_RWwrite(File, XMLEntry->Name, strlen(XMLEntry->Name), 1);
526
SDL_RWwrite(File, ">\r\n", strlen(">\r\n"), 1);
531
SDL_RWwrite(File, "/>\r\n", strlen("/>\r\n"), 1);
535
bool cXMLDocument::Save(const char *XMLFileName)
537
printf("Save XML file: %s\n", XMLFileName);
539
SDL_RWops *File = SDL_RWFromFile(XMLFileName, "wb");
542
fprintf(stderr, "Can't open XML file for write %s\n", XMLFileName);
548
SDL_RWwrite(File, "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\r\n", strlen("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\r\n"), 1);
550
if (RootXMLEntry == 0)
556
// рекурсивно пишем все данные
557
SaveRecursive(RootXMLEntry, File, 0);