1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2020 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
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/file.h"
21 #include "nel/misc/config_file.h"
24 using namespace NLMISC
;
29 # if defined(NL_COMP_VC) && NL_COMP_VC_VERSION >= 71
36 bool readTheFile (const char *filename
, vector
<char> &fileArray
)
40 if (file
.open (filename
))
43 file
.seek (0, NLMISC::IStream::end
);
46 sint32 size
= file
.getPos ();
49 fileArray
.resize (size
);
52 file
.seek (0, NLMISC::IStream::begin
);
57 NLMISC::IStream
*stream
= &file
;
58 stream
->serialBuffer ((uint8
*)&fileArray
[0], size
);
65 // Error, can't open the file
66 nlwarning ("Can't open the file %s for reading.", filename
);
72 inline bool isASCII (uint8 c
)
74 return ( (c
>=0x20 /*&& c<=0x7E*/) || (c
== 0x09) || (c
== 0x0A) || (c
== 0x0D) );
77 bool isASCII(vector
<char> &fileArray
)
80 uint size
= (uint
)fileArray
.size();
85 const char *arrayPointer
= &fileArray
[0];
90 for (uint c
=0; c
<size
; c
++)
92 if (!isASCII (arrayPointer
[c
]))
94 INFO ("Binary character: 0x%02x (line %d)", (uint
)(uint8
)arrayPointer
[c
], line
);
97 if (arrayPointer
[c
] == '\n')
105 inline bool isStringChar (char c
)
107 return ( ( (c
>='a') && (c
<='z') ) || ( (c
>='A') && (c
<='Z') ) || ( (c
>='0') && (c
<='9') ) || (c
=='-') || (c
=='_') || (c
=='.') );
110 void removeBeginEndSpaces (string
&str
)
112 // Remove begin space
114 while ( (index
<(sint
)str
.length ()) && (str
[index
]==' ') )
117 str
= str
.substr (index
, str
.length ()-index
);
120 index
=(sint
)str
.length ()-1;
121 while ( (index
>0) && (str
[index
]==' ') )
123 str
.resize (index
+1);
126 void removeChar (string
&str
, char ch
)
128 for (uint c
=0; c
<str
.size (); c
++)
135 void removeDirectory (string
&str
)
137 // Remove begin space
138 string::size_type pos
= str
.rfind ('\\');
139 string::size_type pos2
= str
.rfind ('/');
141 if (pos
== string::npos
)
144 if (pos
!= string::npos
)
145 str
= str
.substr (pos
+1, str
.size());
148 bool filterExtension (const char *str
, const vector
<string
> &extensions
)
151 uint size
= (uint
)strlen (str
);
154 for (uint i
=0; i
<extensions
.size(); i
++)
156 if ( (str
+(size
- extensions
[i
].size())) == extensions
[i
])
164 bool validateFilename (const char *str
)
167 return (strchr (str
, ' ') == NULL
);
170 const char *getExtension (const char *filename
)
172 return strrchr (filename
, '.');
175 void removeExtension (string
&filename
)
177 string::size_type index
= filename
.rfind ('.');
178 filename
.resize (index
);
181 void extractStringsFromBinary (const vector
<char> &fileArray
, set
<string
> &filenameStrings
, const vector
<string
> &extensions
)
184 uint size
= (uint
)fileArray
.size();
187 const char *arrayPointer
= &fileArray
[0];
189 // run through the data buffer looking for plausible looking strings
191 for (i
=0; (j
=i
+sizeof(uint
))<size
; i
++)
193 // if we're pointing at a string the first 4 bytes ar the string length
194 uint len
=*(uint
*)(arrayPointer
+i
);
196 // if the string length could be valid
197 if (len
>0 && len
+i
<=size
)
200 for (k
=j
; k
<len
+j
&& isASCII (arrayPointer
[k
]);k
++);
210 for (k
=0; k
<len
; k
++)
211 str
[k
] = arrayPointer
[j
+k
];
214 removeBeginEndSpaces (str
);
220 str
= toLowerAscii (str
);
223 if (filterExtension (str
.c_str(), extensions
))
226 removeDirectory (str
);
229 if (validateFilename (str
.c_str()))
232 filenameStrings
.insert (str
);
235 INFO ("Binary filename extracted: \"%s\" (0x%08x)", str
.c_str(), j
);
239 INFO ("Invalid filename: \"%s\" (0x%08x)", str
.c_str(), j
);
248 void extractStringsFromASCII (const vector
<char> &fileArray
, set
<string
> &filenameStrings
, const vector
<string
> &extensions
)
251 uint size
= (uint
)fileArray
.size();
255 const char *arrayPointer
= &fileArray
[0];
260 // Begin of a valid string
261 const char *begin
= arrayPointer
;
262 while ((begin
<arrayPointer
) && (!isStringChar (*begin
)))
264 const char *end
= begin
;
265 while (end
<arrayPointer
+size
)
268 if (isStringChar (*end
))
278 uint size
= (uint
)(end
-begin
);
282 for (uint c
=0; c
<size
; c
++)
286 temp
= toLowerAscii (temp
);
289 if (filterExtension (temp
.c_str(), extensions
))
292 removeDirectory (temp
);
295 if (validateFilename (temp
.c_str()))
298 filenameStrings
.insert (temp
);
301 INFO ("ASCII filename extracted: \"%s\"", temp
.c_str());
305 INFO ("Invalid filename: \"%s\"", temp
.c_str());
316 bool loadConfigFiles (const char *ext
, const char *input_files
, const char *available_files
, vector
<string
> &extensions
, set
<string
> &inputFiles
, map
<string
, string
> &availableFiles
)
323 FILE *file
= fopen (ext
, "r");
328 while (fgets (name
, 512, file
))
330 // To string and lower
331 temp
= toLowerAscii (string(name
));
334 removeChar (temp
, '\n');
337 if (temp
.size() && temp
[0] == '.')
339 extensions
.push_back (temp
);
342 nlwarning ("ERROR extension %s must begin with a '.' character.", temp
.c_str());
353 file
= fopen (input_files
, "r");
357 while (fgets (name
, 512, file
))
363 removeChar (temp
, '\n');
366 inputFiles
.insert (temp
);
373 file
= fopen (available_files
, "r");
377 while (fgets (name
, 512, file
))
380 temp
= toLowerAscii (string(name
));
381 temp2
= toLowerAscii (string(name
));
384 removeBeginEndSpaces (temp
);
385 removeBeginEndSpaces (temp2
);
388 removeChar (temp
, '\n');
389 removeChar (temp2
, '\n');
392 removeDirectory (temp
);
395 if (filterExtension (temp
.c_str (), extensions
))
397 if (validateFilename (temp
.c_str ()))
400 if (!availableFiles
.insert (map
<string
, string
>::value_type (temp
, temp2
)).second
)
402 fprintf (stderr
, "DUBLING: %s %s\n", temp
.c_str (), temp2
.c_str());
407 fprintf (stderr
, "INVALIDE NAME: %s\n", temp
.c_str ());
412 fprintf (stderr
, "INVALIDE EXT: %s\n", temp
.c_str ());
424 nlwarning ("ERROR can't load available files %s", ext
);
429 nlwarning ("ERROR can't load input files %s", ext
);
435 nlwarning ("ERROR can't load extension file %s", ext
);
442 #define BAR_LENGTH 21
444 const char *progressbar
[BAR_LENGTH
]=
461 "[............... ]",
462 "[................ ]",
463 "[................. ]",
464 "[.................. ]",
465 "[................... ]",
466 "[....................]"
469 void progress (const char *message
, float progress
)
473 uint pgId
= (uint
)(progress
*(float)BAR_LENGTH
);
474 pgId
= min(pgId
, (uint
)(BAR_LENGTH
-1));
475 sprintf (msg
, "\r%s %s", progressbar
[pgId
], message
);
477 for (i
=(uint
)strlen(msg
); i
<79; i
++)
484 int main(int argc
, char* argv
[])
488 // Load the config file
489 printf ("Loading config files...");
492 vector
<string
> extensions
;
495 map
<string
, string
> alternateExtensions
;
496 alternateExtensions
.insert (map
<string
, string
>::value_type (".tga", ".dds"));
499 set
<string
> inputFiles
;
502 set
<string
> usedFiles
;
505 map
<string
, string
> availableFiles
;
507 if (loadConfigFiles (argv
[1], argv
[2], argv
[3], extensions
, inputFiles
, availableFiles
))
509 // Load the config file
510 printf ("\rScaning files... \n");
513 uint maxSize
= (uint
)inputFiles
.size();
514 set
<string
>::iterator ite
= inputFiles
.begin();
515 while (ite
!= inputFiles
.end())
518 string label
= ite
->c_str();
521 removeDirectory (label
);
523 progress (label
.c_str(), (float)size
/(float)maxSize
);
526 set
<string
> outputFilename
;
529 vector
<char> fileArray
;
532 INFO ("Open file: %s", ite
->c_str());
533 if (readTheFile (ite
->c_str(), fileArray
))
535 // Is it a ASCII file ?
536 if (isASCII (fileArray
))
541 extractStringsFromASCII (fileArray
, outputFilename
, extensions
);
545 INFO ("BINARY mode");
548 extractStringsFromBinary (fileArray
, outputFilename
, extensions
);
552 // Check this files exists
553 set
<string
>::iterator found
= outputFilename
.begin();
554 while (found
!= outputFilename
.end())
556 // Look for this file
557 if (availableFiles
.find (*found
) == availableFiles
.end())
560 bool foundIt
= false;
562 // Try alternate extension
563 string ext
= getExtension ((*found
).c_str());
564 std::map
<string
, string
>::iterator alternIte
= alternateExtensions
.find (ext
);
565 if (alternIte
!= alternateExtensions
.end ())
567 string name
= *found
;
568 removeExtension (name
);
569 name
+= alternIte
->second
;
570 if (availableFiles
.find (name
) != availableFiles
.end())
577 fprintf (stderr
, "MISSING: %s (needed by file %s)\n", found
->c_str(), ite
->c_str());
583 usedFiles
.insert (*found
);
596 // Look for unused files
597 map
<string
, string
>::iterator available
= availableFiles
.begin();
598 while (available
!= availableFiles
.end())
601 if (usedFiles
.find (available
->first
) == usedFiles
.end())
603 string temp
= toLowerAscii (available
->second
);
604 fprintf (stderr
, "UNUSED: %s\n", temp
.c_str());
617 printf ("extract_filename [extensions.txt] [inputFiles.txt] [availableFiles.txt]\n");