// NeL - MMORPG Framework
// Copyright (C) 2010 Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see .
#include "nel/misc/file.h"
#include "nel/misc/config_file.h"
using namespace std;
using namespace NLMISC;
#ifdef NL_DEBUG
#define INFO nlinfo
#else // NL_DEBUG
# if defined(NL_COMP_VC) && NL_COMP_VC_VERSION >= 71
# define INFO __noop
# else
# define INFO 0&&
# endif
#endif // NL_DEBUG
bool readTheFile (const char *filename, vector &fileArray)
{
// Open the file
CIFile file;
if (file.open (filename))
{
// Go to the end
file.seek (0, NLMISC::IStream::end);
// Get file size
sint32 size = file.getPos ();
// Resize the array
fileArray.resize (size);
// Go to the begin
file.seek (0, NLMISC::IStream::begin);
if (size)
{
// Read the file
NLMISC::IStream *stream = &file;
stream->serialBuffer ((uint8*)&fileArray[0], size);
}
return true;
}
else
{
// Error, can't open the file
nlwarning ("Can't open the file %s for reading.", filename);
return false;
}
}
inline bool isASCII (uint8 c)
{
return ( (c>=0x20 /*&& c<=0x7E*/) || (c == 0x09) || (c == 0x0A) || (c == 0x0D) );
}
bool isASCII(vector &fileArray)
{
// Array size
uint size = (uint)fileArray.size();
if (size)
{
// Array pointer
const char *arrayPointer = &fileArray[0];
// Line count
uint line = 0;
for (uint c=0; c='a') && (c<='z') ) || ( (c>='A') && (c<='Z') ) || ( (c>='0') && (c<='9') ) || (c=='-') || (c=='_') || (c=='.') );
}
void removeBeginEndSpaces (string &str)
{
// Remove begin space
sint index=0;
while ( (index<(sint)str.length ()) && (str[index]==' ') )
index++;
if (index != 0)
str = str.substr (index, str.length ()-index);
// Remove end space
index=(sint)str.length ()-1;
while ( (index>0) && (str[index]==' ') )
index--;
str.resize (index+1);
}
void removeChar (string &str, char ch)
{
for (uint c=0; c &extensions)
{
// String size
uint size = (uint)strlen (str);
// For each filter
for (uint i=0; i &fileArray, set &filenameStrings, const vector &extensions)
{
// Array size
uint size = (uint)fileArray.size();
// Array pointer
const char *arrayPointer = &fileArray[0];
// run through the data buffer looking for plausible looking strings
uint i,j;
for (i=0; (j=i+sizeof(uint))0 && len+i<=size)
{
uint k;
for (k=j; k &fileArray, set &filenameStrings, const vector &extensions)
{
// Array size
uint size = (uint)fileArray.size();
if (size)
{
// Array pointer
const char *arrayPointer = &fileArray[0];
// Temp string
string temp;
// Begin of a valid string
const char *begin = arrayPointer;
while ((begin &extensions, set &inputFiles, map &availableFiles)
{
// Get a string
string temp;
string temp2;
// Extensions files
FILE *file = fopen (ext, "r");
if (file)
{
bool cont = true;
char name[512];
while (fgets (name, 512, file))
{
// To string and lower
temp = toLower (string(name));
// Remove return
removeChar (temp, '\n');
// Valid extension ?
if (temp.size() && temp[0] == '.')
// Add the extension
extensions.push_back (temp);
else
{
nlwarning ("ERROR extension %s must begin with a '.' character.", temp.c_str());
cont = false;
break;
}
}
// Close
fclose (file);
if (cont)
{
// Input files
file = fopen (input_files, "r");
if (file)
{
char name[512];
while (fgets (name, 512, file))
{
// To string
temp = name;
// Remove return
removeChar (temp, '\n');
// Add the extension
inputFiles.insert (temp);
}
// Close
fclose (file);
// Available files
file = fopen (available_files, "r");
if (file)
{
char name[512];
while (fgets (name, 512, file))
{
// To lower
temp = toLower (string(name));
temp2 = toLower (string(name));
// Remove space
removeBeginEndSpaces (temp);
removeBeginEndSpaces (temp2);
// Remove return
removeChar (temp, '\n');
removeChar (temp2, '\n');
// Remove directory
removeDirectory (temp);
// Good extension
if (filterExtension (temp.c_str (), extensions))
{
if (validateFilename (temp.c_str ()))
{
// Add the extension
if (!availableFiles.insert (map::value_type (temp, temp2)).second)
{
fprintf (stderr, "DUBLING: %s %s\n", temp.c_str (), temp2.c_str());
}
}
else
{
fprintf (stderr, "INVALIDE NAME: %s\n", temp.c_str ());
}
}
else
{
fprintf (stderr, "INVALIDE EXT: %s\n", temp.c_str ());
}
}
// Close
fclose (file);
// Ok
return true;
}
else
{
nlwarning ("ERROR can't load available files %s", ext);
}
}
else
{
nlwarning ("ERROR can't load input files %s", ext);
}
}
}
else
{
nlwarning ("ERROR can't load extension file %s", ext);
}
return false;
}
#define BAR_LENGTH 21
const char *progressbar[BAR_LENGTH]=
{
"[ ]",
"[. ]",
"[.. ]",
"[... ]",
"[.... ]",
"[..... ]",
"[...... ]",
"[....... ]",
"[........ ]",
"[......... ]",
"[.......... ]",
"[........... ]",
"[............ ]",
"[............. ]",
"[.............. ]",
"[............... ]",
"[................ ]",
"[................. ]",
"[.................. ]",
"[................... ]",
"[....................]"
};
void progress (const char *message, float progress)
{
// Progress bar
char msg[512];
uint pgId= (uint)(progress*(float)BAR_LENGTH);
pgId= min(pgId, (uint)(BAR_LENGTH-1));
sprintf (msg, "\r%s %s", progressbar[pgId], message );
uint i;
for (i=(uint)strlen(msg); i<79; i++)
msg[i]=' ';
msg[i]=0;
printf ("%s", msg);
printf ("\r");
}
int main(int argc, char* argv[])
{
if (argc==4)
{
// Load the config file
printf ("Loading config files...");
// Extensions
vector extensions;
// Extensions
map alternateExtensions;
alternateExtensions.insert (map::value_type (".tga", ".dds"));
// String array
set inputFiles;
// String array
set usedFiles;
// String array
map availableFiles;
if (loadConfigFiles (argv[1], argv[2], argv[3], extensions, inputFiles, availableFiles))
{
// Load the config file
printf ("\rScaning files... \n");
uint size = 0;
uint maxSize = (uint)inputFiles.size();
set::iterator ite = inputFiles.begin();
while (ite != inputFiles.end())
{
// Update
string label = ite->c_str();
// Remove directory
removeDirectory (label);
progress (label.c_str(), (float)size/(float)maxSize);
// String array
set outputFilename;
// File array
vector fileArray;
// Read the file
INFO ("Open file: %s", ite->c_str());
if (readTheFile (ite->c_str(), fileArray))
{
// Is it a ASCII file ?
if (isASCII (fileArray))
{
INFO ("ASCII mode");
// Extract strings
extractStringsFromASCII (fileArray, outputFilename, extensions);
}
else
{
INFO ("BINARY mode");
// Extract strings
extractStringsFromBinary (fileArray, outputFilename, extensions);
}
}
// Check this files exists
set::iterator found = outputFilename.begin();
while (found != outputFilename.end())
{
// Look for this file
if (availableFiles.find (*found) == availableFiles.end())
{
// Found ?
bool foundIt = false;
// Try alternate extension
string ext = getExtension ((*found).c_str());
std::map::iterator alternIte = alternateExtensions.find (ext);
if (alternIte != alternateExtensions.end ())
{
string name = *found;
removeExtension (name);
name += alternIte->second;
if (availableFiles.find (name) != availableFiles.end())
foundIt = true;
}
// Not found ?
if (!foundIt)
{
fprintf (stderr, "MISSING: %s (needed by file %s)\n", found->c_str(), ite->c_str());
}
}
else
{
// Add to used files
usedFiles.insert (*found);
}
// Next file found
found++;
}
size++;
// Next file
ite++;
}
// Look for unused files
map::iterator available = availableFiles.begin();
while (available != availableFiles.end())
{
// It is used ?
if (usedFiles.find (available->first) == usedFiles.end())
{
string temp = toLower (available->second);
fprintf (stderr, "UNUSED: %s\n", temp.c_str());
}
// Next
available++;
}
// Ok
return 1;
}
}
else
{
printf ("extract_filename [extensions.txt] [inputFiles.txt] [availableFiles.txt]\n");
}
return 0;
}