Merge branch 'ryzom/ark-features' into main/gingo-test
[ryzomcore.git] / nel / src / misc / xml_pack.cpp
blobec12eb6bc9eed1d3bac8ae968987c42153d52c99
1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU Affero General Public License as
6 // published by the Free Software Foundation, either version 3 of the
7 // License, or (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU Affero General Public License for more details.
14 // You should have received a copy of the GNU Affero General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
17 #include "stdmisc.h"
18 #include "nel/misc/xml_pack.h"
19 #include "nel/misc/file.h"
21 using namespace std;
23 #ifdef DEBUG_NEW
24 #define new DEBUG_NEW
25 #endif
27 namespace NLMISC
30 NLMISC_SAFE_SINGLETON_IMPL(CXMLPack);
33 // For a simple parser, we read by line, with a limit to 1Ko
34 const uint32 MaxLineSize = 1*1024;
36 /// Consume space and tab characters (but NOT newlines)
37 void CXMLPack::skipWS(string::iterator &it, string::iterator end)
39 while (it != end && (*it == ' ' || *it == '\t'))
40 ++it;
42 /// Try to match the specified text at current position. Return false of no match
43 bool CXMLPack::matchString(string::iterator &it, string::iterator end, const char *text)
45 string::iterator rewind = it;
46 // skip leading WS
47 skipWS(it, end);
49 while (it != end && *text && *text == *it)
51 ++it;
52 ++text;
54 if (*text == 0)
56 // we have advanced up to the end of text, so the match is OK
57 return true;
59 // no match !
60 // rewind
61 it = rewind;
62 return false;
65 /// Advance up to the beginning of the next line, incrementing the in/out param lineCount
66 void CXMLPack::skipLine(string::iterator &it, string::iterator end, uint32 &lineCount)
68 // advance up to end of string or newline char
69 while (it != end && *it != '\n')
71 ++it;
73 // skip the new line char
74 if (it != end && *it == '\n')
76 ++it;
77 ++lineCount;
82 // Add an xml pack to the manager
83 bool CXMLPack::add (const std::string &xmlPackFileName)
85 // prepare the container to store this pack file
86 TStringId packId = CStringMapper::map(xmlPackFileName);
87 TPackList::iterator packIt(_XMLPacks.find(packId));
88 if (packIt != _XMLPacks.end())
90 nlwarning("CXMLPack::add : can't add xml_pack file '%s' because already added", xmlPackFileName.c_str());
91 return false;
93 TXMLPackInfo &packInfo = _XMLPacks[packId];
95 // open the xml pack for later access
96 // packInfo.FileHandler = nlfopen(xmlPackFileName, "rb");
98 // open the xml pack for parsing
99 CIFile packFile;
100 packFile.open(xmlPackFileName);
102 uint32 packSize = packFile.getFileSize();
103 string buffer;
104 buffer.resize(packSize);
106 // read the file in memory for parsing
107 packFile.serialBuffer((uint8*)buffer.data(), packSize);
109 string::iterator it=buffer.begin(), end(buffer.end());
110 uint32 lineCount = 0;
112 // check the xml pack header element
113 if (!matchString(it, end, "<nel:packed_xml>"))
115 nlwarning("Error : invalid pack file '%s', invalid header", xmlPackFileName.c_str());
116 return false;
118 // advance to next line
119 skipLine(it, end, lineCount);
121 // now enter the sub file loop
122 for(;;)
124 TXMLFileInfo fileInfo;
125 // match a sub file header
126 if (!matchString(it, end, "<nel:xml_file"))
128 nlwarning("Error : invalid pack file content at line %u in '%s'", lineCount, xmlPackFileName.c_str());
129 return false;
132 // ok, extract the file name from the header, match 'name' then '=' then '"'
133 if (!matchString(it, end, "name") || !matchString(it, end, "=") || !matchString(it, end, "\""))
135 nlwarning("Error : invalid pack file sub header at line %u in '%s', can't found attribute 'name'", lineCount, xmlPackFileName.c_str());
136 return false;
138 string::iterator nameBegin = it;
139 // advance up to closing quote
140 while (it != end && *it != '\"')
141 ++it;
142 if (it == end)
144 nlwarning("Error : invalid pack file sub header at line %u in '%s', can't found attribute closing quote for name", lineCount, xmlPackFileName.c_str());
145 return false;
147 string subFileName(buffer, nameBegin-buffer.begin(), it-nameBegin);
148 if (subFileName.empty())
150 nlwarning("Error : invalid pack file sub header at line %u in '%s', empty filename", lineCount, xmlPackFileName.c_str());
151 return false;
153 // advance to the closing '>'
154 while (it != end && *it != '>')
155 ++it;
156 if (it == end)
158 nlwarning("Error : invalid pack file sub header at line %u in '%s', can't found element closing '>'", lineCount, xmlPackFileName.c_str());
159 return false;
161 // advance to next line (beginning of sub file)
162 skipLine(it, end, lineCount);
164 string::iterator beginOfFile = it;
165 string::iterator endOfFile = it;
167 // now, advance up to the end of file
168 while (it != end && !matchString(it, end, "</nel:xml_file>"))
170 skipLine(it, end, lineCount);
171 endOfFile = it;
174 // we must not be at end of file
175 if (it == end)
177 nlwarning("Error : invalid sub file at line %u in '%s', reach end of file without closing file and pack elements", lineCount, xmlPackFileName.c_str());
178 return false;
181 // ok, the file is parsed, store it
182 fileInfo.FileName = CStringMapper::map(subFileName);
183 fileInfo.FileOffset = (uint32)(beginOfFile - buffer.begin());
184 fileInfo.FileSize = (uint32)(endOfFile - beginOfFile);
185 // fileInfo.FileHandler = nlfopen(xmlPackFileName, "rb");
186 packInfo._XMLFiles.insert(make_pair(fileInfo.FileName, fileInfo));
188 // advance to next line
189 skipLine(it, end, lineCount);
191 // check for end of pack
192 if (matchString(it, end, "</nel:packed_xml>"))
194 // ok, the parse is over
195 break;
198 // continue to next file in pack
201 nldebug("XMLPack : xml_pack '%s' added to the collection with %u files", xmlPackFileName.c_str(), packInfo._XMLFiles.size());
202 // ok, parsing ended
203 return true;
206 // List all files in an xml_pack file
207 void CXMLPack::list (const std::string &xmlPackFileName, std::vector<std::string> &allFiles)
209 TStringId key = CStringMapper::map(xmlPackFileName);
211 TPackList::const_iterator it(_XMLPacks.find(key));
212 if (it != _XMLPacks.end())
214 const TXMLPackInfo &packInfo = it->second;
215 // we found it, fill the out vector
216 TXMLPackInfo::TFileList::const_iterator first(packInfo._XMLFiles.begin()), last(packInfo._XMLFiles.end());
217 for (; first != last; ++first)
219 const TXMLFileInfo &fileInfo = first->second;
220 allFiles.push_back(CStringMapper::unmap(fileInfo.FileName));
225 // Used by CIFile to get information about the files within the xml pack
226 FILE* CXMLPack::getFile (const std::string &sFileName, uint32 &rFileSize, uint32 &rFileOffset,
227 bool &rCacheFileOnOpen, bool &rAlwaysOpened)
229 // split the name appart from the '@@' separator to get the pack file name
230 // and subfile name
231 vector<string> parts;
232 explode(sFileName, string("@@"), parts, true);
233 if (parts.size() != 2)
235 nlwarning("CXMLPack::getFile : Can't extract pack and filename from '%s', found %u part instead of 2 when spliting apart from '@@'",
236 sFileName.c_str(),
237 parts.size());
238 return NULL;
241 TStringId packId = CStringMapper::map(parts[0]);
242 TStringId fileId = CStringMapper::map(parts[1]);
244 TPackList::iterator packIt(_XMLPacks.find(packId));
245 if (packIt == _XMLPacks.end())
247 nlwarning("CXMLPack::getFile : Can't find xml pack file named '%s' to open '%s'", parts[0].c_str(), sFileName.c_str());
248 return NULL;
250 TXMLPackInfo &packInfo = packIt->second;
251 TXMLPackInfo::TFileList::iterator fileIt = packInfo._XMLFiles.find(fileId);
252 if (fileIt == packInfo._XMLFiles.end())
254 nlwarning("CXMLPack::getFile : Can't find xml file named '%s' in pack '%s'",
255 parts[1].c_str(), parts[0].c_str());
256 return NULL;
259 // ok, we have found it !
260 TXMLFileInfo &fileInfo = fileIt->second;
262 // fill the return value
263 rFileSize = fileInfo.FileSize;
264 rFileOffset = fileInfo.FileOffset;
265 rCacheFileOnOpen = false;
266 rAlwaysOpened = false;
267 FILE *fp = nlfopen(parts[0], "rb");
268 return fp;
272 } // namespace NLMISC