1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
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.
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/>.
18 #include "nel/misc/xml_pack.h"
19 #include "nel/misc/file.h"
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'))
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
;
49 while (it
!= end
&& *text
&& *text
== *it
)
56 // we have advanced up to the end of text, so the match is OK
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')
73 // skip the new line char
74 if (it
!= end
&& *it
== '\n')
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());
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
100 packFile
.open(xmlPackFileName
);
102 uint32 packSize
= packFile
.getFileSize();
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());
118 // advance to next line
119 skipLine(it
, end
, lineCount
);
121 // now enter the sub file loop
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());
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());
138 string::iterator nameBegin
= it
;
139 // advance up to closing quote
140 while (it
!= end
&& *it
!= '\"')
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());
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());
153 // advance to the closing '>'
154 while (it
!= end
&& *it
!= '>')
158 nlwarning("Error : invalid pack file sub header at line %u in '%s', can't found element closing '>'", lineCount
, xmlPackFileName
.c_str());
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
);
174 // we must not be at end of file
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());
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
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());
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
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 '@@'",
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());
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());
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");
272 } // namespace NLMISC