Added aqua_speed for rite geo 50 tryker
[ryzomcore.git] / nel / tools / misc / xml_packer / xml_packer.cpp
blob1cd61f3733470aa09d97f06e73fa964e2984bd8e
1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
3 //
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2015 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
6 //
7 // This program is free software: you can redistribute it and/or modify
8 // it under the terms of the GNU Affero General Public License as
9 // published by the Free Software Foundation, either version 3 of the
10 // License, or (at your option) any later version.
12 // This program 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 Affero General Public License for more details.
17 // You should have received a copy of the GNU Affero General Public License
18 // along with this program. If not, see <http://www.gnu.org/licenses/>.
20 #include "nel/misc/types_nl.h"
21 #include "nel/misc/app_context.h"
22 #include "nel/misc/path.h"
23 #include "nel/misc/common.h"
24 #include "nel/misc/sstring.h"
25 #include "nel/misc/algo.h"
27 using namespace std;
28 using namespace NLMISC;
30 enum TAction
32 pack,
33 unpack,
34 undefined
37 const string DefaultExt("xml_pack");
38 const uint32 MaxLineSize = 16*1024;
40 const string ExcludeFiles(".#*;*.log;*.bin;*.xml_pack_index");
41 const string ExcludeDirs("CVS");
43 bool isExcludedFile(const std::string &fileName)
45 static vector<string> excludeFileVect;
46 static bool init = false;
48 if (!init)
50 explode(ExcludeFiles, string(";"), excludeFileVect, true);
51 init = true;
54 bool excluded = false;
56 for (uint i=0; i<excludeFileVect.size(); ++i)
58 if (testWildCard(fileName, excludeFileVect[i]))
60 excluded = true;
61 break;
65 return excluded;
68 bool isExcludedDir(const std::string &dirName)
70 static vector<string> excludeDirVect;
71 static bool init = false;
73 if (!init)
75 explode(ExcludeDirs, string(";"), excludeDirVect, true);
78 bool excluded = false;
80 for (uint i=0; i<excludeDirVect.size(); ++i)
82 if (testWildCard(dirName, excludeDirVect[i]))
84 excluded = true;
85 break;
89 return excluded;
92 string getLastDirName(const std::string &path)
94 string dirName;
95 string::size_type pos = path.size()-1;
97 // skip any terminal directory separator
98 if (pos > 0 && (path[pos] == '\\' || path[pos] == '/'))
99 --pos;
101 while(pos > 0 && path[pos] != '\\' && path[pos] != '/' )
102 dirName = path[pos--] + dirName;
104 return dirName;
107 int main(int argc, char *argv[])
109 printf("NeL XML Packer/Unpacker V0.3\n");
110 CApplicationContext appContext;
112 TAction action = undefined;
113 bool recursive = false;
115 // compute the current folder name
116 string currentPath = CPath::getCurrentPath();
117 string dirName = getLastDirName(currentPath);;
119 string filename = dirName + "."+DefaultExt;
121 // check the params to choose action
122 for (uint i=0; i<uint(argc); ++i)
124 if (strcmp(argv[i], "-p") == 0)
126 // pack the current folder
127 action = pack;
128 printf("Packing files\n");
130 else if (strcmp(argv[i], "-u") == 0)
132 // unpack the current folder
133 action = unpack;
134 printf("Unpacking files\n");
136 else if (strcmp(argv[i], "-r") == 0)
138 // unpack the current folder
139 recursive = true;
140 printf("Recursive mode\n");
142 // else if (strcmp(argv[i], "-f") == 0)
143 // {
144 // if (uint(argc) < i+i)
145 // {
146 // printf("Error : missing file name after -f\n");
147 // return -1;
148 // }
149 // // use the specified file archive instead of the directory name
150 // filename = argv[++i];
151 // }
154 if (action == undefined)
156 printf("Usage : %s -u|-p [-r]\n", argv[0]);
157 printf(" -p : pack the current folder\n");
158 printf(" -u : unpack the current folder\n");
159 printf(" -r : pack or unpack subdirectories recursively\n");
161 return -1;
164 vector<string> dirStack;
166 printf("Current path is '%s'\n", CPath::getCurrentPath().c_str());
168 // push the current directory to start the loop
169 dirStack.push_back(CPath::getCurrentPath());
172 while(!dirStack.empty())
174 string dirName = dirStack.back();
175 dirStack.pop_back();
176 string filename = dirName+"/"+getLastDirName(dirName) + "."+DefaultExt;
177 switch (action)
179 case pack:
181 printf("Packing directory '%s'...\n", dirName.c_str());
182 // string packFileName = dirName+"/tmp."+DefaultExt;
183 string packFileName = filename;
184 string indexFileName = dirName+"/.xml_pack_index";
186 // get the current directory content
187 vector<string> files;
188 CPath::getPathContent(dirName, false, false, true, files);
190 vector<string> validFiles;
192 // first loop to build the list of valid file
193 for (uint i=0; i<files.size(); ++i)
195 if (files[i].find(DefaultExt) == files[i].size() - DefaultExt.size())
196 continue;
198 string &subFileName = files[i];
200 // check exclude filter
201 if (isExcludedFile(subFileName))
203 continue;
206 // ok, this file is valid
207 validFiles.push_back(subFileName);
210 bool needRepack = true;
212 // if an xml pack already exist in the folder...
213 if (CFile::fileExists(packFileName))
215 breakable
217 if (validFiles.empty())
219 // no file in the directory, erase the pack file
220 CFile::deleteFile(packFileName);
221 break;
224 uint32 packDate = CFile::getFileModificationDate(packFileName);
226 if (!CFile::fileExists(indexFileName) || CFile::getFileModificationDate(indexFileName) < packDate)
228 // no index file or index file older than pack file, repack
229 break;
232 // read the index file
233 set<string> fileInIndex;
234 char lineBuffer[1024];
235 FILE *fp = nlfopen(indexFileName, "rt");
236 while (fgets(lineBuffer, 1024, fp))
237 fileInIndex.insert(CSString(lineBuffer).strip());
239 fclose(fp);
241 // loop to check for file time stamp
242 for (uint i=0; i<validFiles.size(); ++i)
244 uint32 fileDate = CFile::getFileModificationDate(validFiles[i]);
246 if (fileDate >= packDate)
247 // no more to check
248 break;
250 // remove this file from the file index
251 fileInIndex.erase(CFile::getFilename(validFiles[i]));
254 // check if there are some some deleted in the directory
255 if (!fileInIndex.empty())
257 // need to repack, there are erased files
258 break;
261 // all files are older than the pack file ! no repack needed
262 needRepack = false;
266 // we need to repack and have some file to store ?
267 if (!validFiles.empty() && needRepack)
269 // open the pack file
270 // FILE *fp = nlfopen(filename, "wt");
271 FILE *fp = nlfopen(packFileName, "wt");
273 fprintf(fp, "<nel:packed_xml>\n");
275 for (uint i=0; i<validFiles.size(); ++i)
277 string &subFileName = validFiles[i];
279 printf("Adding file '%s'...\n", CFile::getFilename(subFileName).c_str());
280 fprintf(fp, " <nel:xml_file name=\"%s\">\n", CFile::getFilename(subFileName).c_str());
282 FILE *subFp = nlfopen(subFileName, "rt");
283 nlassert(subFp != NULL);
284 char buffer[MaxLineSize];
285 char *result;
286 bool needFinalReturn = false;
287 result = fgets(buffer, MaxLineSize, subFp);
288 needFinalReturn = result != NULL ? buffer[strlen(buffer)-1] != '\n' : true;
289 while(result != 0)
291 fputs(buffer, fp);
292 result = fgets(buffer, MaxLineSize, subFp);
293 needFinalReturn = result != NULL ? buffer[strlen(buffer)-1] != '\n' : needFinalReturn;
295 if (needFinalReturn)
297 const char *finalReturn = "\n";
298 fputs(finalReturn, fp);
301 fclose(subFp);
303 fprintf(fp, " </nel:xml_file>\n");
306 fprintf(fp, "</nel:packed_xml>\n");
308 fclose(fp);
310 // write the disposable index file used by pack to check for erased file
311 fp = nlfopen(indexFileName, "wt");
312 for (uint i=0; i<validFiles.size(); ++i)
314 fprintf(fp, "%s\n", CFile::getFilename(validFiles[i]).c_str());
316 fclose(fp);
317 // set the file 'hidden'n use the plain old system command...
318 sint res = system(toString("attrib +h %s", indexFileName.c_str()).c_str());
319 if (res)
321 nlwarning("attrib failed with return code %d", res);
324 else
326 printf("Directory %s is up to date, no repack\n", dirName.c_str());
329 break;
330 case unpack:
332 printf("Unpacking directory '%s'...\n", dirName.c_str());
333 // open the pack file
334 // FILE *fp = nlfopen(dirName+"/tmp."+DefaultExt, "rt");
335 FILE *fp = nlfopen(filename, "rt");
336 if (!recursive)
338 // if we are not recursive, we MUST have a file here
339 printf("Error : can't find a xml_pack file in current directory\n");
340 exit(-1);
342 else
344 // just continue to recurse, there is no file at this level
345 break;
347 uint linecount = 0;
349 // read the first line
350 char buffer[MaxLineSize];
351 if (!fgets(buffer, MaxLineSize, fp) || strcmp(buffer, "<nel:packed_xml>\n") != 0)
353 printf ("Error : invalid pack file '%s'\n", filename.c_str());
354 return -1;
357 linecount++;
359 char *result = NULL;
362 // read a file line
363 linecount++;
364 if (!fgets(buffer, MaxLineSize, fp))
366 fclose(fp);
367 printf ("Error : invalid pack file '%s' at line %u", filename.c_str(), linecount);
368 return -1;
370 CSString parser(buffer);
371 if (parser.find(" <nel:xml_file name=") != 0)
373 fclose(fp);
375 // end of pack file
376 if (parser.find("</nel:packed_xml>") == 0)
377 break;
379 printf ("Error : invalid pack file '%s' at line %u", filename.c_str(), linecount);
380 return -1;
383 CSString subFileName = parser.leftCrop(sizeof(" <nel:xml_file name=")-1);
384 subFileName = subFileName.matchDelimiters(false, false, true, false);
385 subFileName = subFileName.unquoteIfQuoted();
386 subFileName = dirName + "/" + subFileName.c_str();
388 printf("Extracting file '%s'...\n", CFile::getFilename(subFileName).c_str());
389 // open the output file
390 FILE *output = fopen (subFileName.c_str(), "wt");
391 if (output == NULL)
393 printf ("Error : can not open output file '%s' from pack file '%s'", subFileName.c_str(), filename.c_str());
394 exit(-1);
397 result = fgets(buffer, MaxLineSize, fp);
398 linecount++;
399 while (result != NULL && strcmp(buffer, " </nel:xml_file>\n") != 0)
401 fputs(result, output);
402 // read next line
403 result = fgets(buffer, MaxLineSize, fp);
404 linecount++;
407 fclose(output);
409 } while(result != NULL);
412 break;
413 default:
414 // this shouldn't happen / keep compiler happy
415 break;
418 if (recursive)
420 vector<string> subDirs;
421 CPath::getPathContent(dirName, false, true, false, subDirs);
423 // filter the directories
424 for (uint i=(uint)subDirs.size(); i>0; --i)
426 if (!isExcludedDir(subDirs[i-1]))
427 dirStack.push_back(subDirs[i-1]);
431 return 0;