mirror of
https://port.numenaute.org/aleajactaest/khanat-opennel-code.git
synced 2024-12-25 02:10:53 +00:00
549 lines
13 KiB
C++
549 lines
13 KiB
C++
// NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
|
|
// 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 <http://www.gnu.org/licenses/>.
|
|
|
|
#include "nel/misc/types_nl.h"
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
|
|
#ifdef NL_OS_WINDOWS
|
|
# include <io.h>
|
|
# include <direct.h>
|
|
#endif
|
|
|
|
#include <vector>
|
|
#include <string>
|
|
|
|
#include "nel/misc/debug.h"
|
|
#include "nel/misc/file.h"
|
|
#include "nel/misc/path.h"
|
|
#include "nel/misc/algo.h"
|
|
#include "nel/misc/common.h"
|
|
|
|
|
|
using namespace std;
|
|
using namespace NLMISC;
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
class CWildCard
|
|
{
|
|
public:
|
|
string Expression;
|
|
bool Not;
|
|
};
|
|
std::vector<CWildCard> WildCards;
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
bool keepFile (const char *fileName)
|
|
{
|
|
uint i;
|
|
bool ifPresent = false;
|
|
bool ifTrue = false;
|
|
string file = toLower(CFile::getFilename (fileName));
|
|
for (i=0; i<WildCards.size(); i++)
|
|
{
|
|
if (WildCards[i].Not)
|
|
{
|
|
// One ifnot condition met and the file is not added
|
|
if (testWildCard(file.c_str(), WildCards[i].Expression.c_str()))
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
ifPresent = true;
|
|
ifTrue |= testWildCard(file.c_str(), WildCards[i].Expression.c_str());
|
|
}
|
|
}
|
|
|
|
return !ifPresent || ifTrue;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
struct BNPFile
|
|
{
|
|
string Name;
|
|
uint32 Size;
|
|
uint32 Pos;
|
|
};
|
|
|
|
struct BNPHeader
|
|
{
|
|
vector<BNPFile> Files;
|
|
uint32 OffsetFromBeginning;
|
|
|
|
// Append the header to the big file
|
|
bool append (const string &filename)
|
|
{
|
|
FILE *f = fopen (filename.c_str(), "ab");
|
|
if (f == NULL) return false;
|
|
|
|
uint32 nNbFile = (uint32)Files.size();
|
|
if (fwrite (&nNbFile, sizeof(uint32), 1, f) != 1)
|
|
{
|
|
fclose(f);
|
|
return false;
|
|
}
|
|
|
|
for (uint32 i = 0; i < nNbFile; ++i)
|
|
{
|
|
uint8 nStringSize = (uint8)Files[i].Name.size();
|
|
if (fwrite (&nStringSize, 1, 1, f) != 1)
|
|
{
|
|
fclose(f);
|
|
return false;
|
|
}
|
|
|
|
if (fwrite (Files[i].Name.c_str(), 1, nStringSize, f) != nStringSize)
|
|
{
|
|
fclose(f);
|
|
return false;
|
|
}
|
|
|
|
if (fwrite (&Files[i].Size, sizeof(uint32), 1, f) != 1)
|
|
{
|
|
fclose(f);
|
|
return false;
|
|
}
|
|
|
|
if (fwrite (&Files[i].Pos, sizeof(uint32), 1, f) != 1)
|
|
{
|
|
fclose(f);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (fwrite (&OffsetFromBeginning, sizeof(uint32), 1, f) != 1)
|
|
{
|
|
fclose(f);
|
|
return false;
|
|
}
|
|
|
|
fclose (f);
|
|
return true;
|
|
}
|
|
|
|
// Read the header from a big file
|
|
bool read (const string &filename)
|
|
{
|
|
FILE *f = fopen (filename.c_str(), "rb");
|
|
if (f == NULL) return false;
|
|
|
|
nlfseek64 (f, 0, SEEK_END);
|
|
uint32 nFileSize=CFile::getFileSize (filename);
|
|
nlfseek64 (f, nFileSize-sizeof(uint32), SEEK_SET);
|
|
|
|
uint32 nOffsetFromBegining;
|
|
if (fread (&nOffsetFromBegining, sizeof(uint32), 1, f) != 1)
|
|
{
|
|
fclose (f);
|
|
return false;
|
|
}
|
|
|
|
#ifdef NL_BIG_ENDIAN
|
|
NLMISC_BSWAP32(nOffsetFromBegining);
|
|
#endif
|
|
|
|
if (nlfseek64 (f, nOffsetFromBegining, SEEK_SET) != 0)
|
|
{
|
|
fclose (f);
|
|
return false;
|
|
}
|
|
|
|
uint32 nNbFile;
|
|
if (fread (&nNbFile, sizeof(uint32), 1, f) != 1)
|
|
{
|
|
fclose (f);
|
|
return false;
|
|
}
|
|
|
|
#ifdef NL_BIG_ENDIAN
|
|
NLMISC_BSWAP32(nNbFile);
|
|
#endif
|
|
|
|
for (uint32 i = 0; i < nNbFile; ++i)
|
|
{
|
|
uint8 nStringSize;
|
|
char sName[256];
|
|
if (fread (&nStringSize, 1, 1, f) != 1)
|
|
{
|
|
fclose (f);
|
|
return false;
|
|
}
|
|
if (fread (sName, 1, nStringSize, f) != nStringSize)
|
|
{
|
|
fclose (f);
|
|
return false;
|
|
}
|
|
sName[nStringSize] = 0;
|
|
BNPFile tmpBNPFile;
|
|
tmpBNPFile.Name = sName;
|
|
if (fread (&tmpBNPFile.Size, sizeof(uint32), 1, f) != 1)
|
|
{
|
|
fclose (f);
|
|
return false;
|
|
}
|
|
|
|
#ifdef NL_BIG_ENDIAN
|
|
NLMISC_BSWAP32(tmpBNPFile.Size);
|
|
#endif
|
|
|
|
if (fread (&tmpBNPFile.Pos, sizeof(uint32), 1, f) != 1)
|
|
{
|
|
fclose (f);
|
|
return false;
|
|
}
|
|
|
|
#ifdef NL_BIG_ENDIAN
|
|
NLMISC_BSWAP32(tmpBNPFile.Pos);
|
|
#endif
|
|
|
|
Files.push_back (tmpBNPFile);
|
|
}
|
|
|
|
fclose (f);
|
|
return true;
|
|
}
|
|
};
|
|
|
|
string gDestBNPFile;
|
|
BNPHeader gBNPHeader;
|
|
|
|
// ---------------------------------------------------------------------------
|
|
void append(const string &filename1, const string &filename2, uint32 sizeToRead)
|
|
{
|
|
FILE *f1 = fopen(filename1.c_str(), "ab");
|
|
FILE *f2 = fopen(filename2.c_str(), "rb");
|
|
if (f1 == NULL) return;
|
|
if (f2 == NULL) { fclose(f1); return; }
|
|
|
|
uint8 *ptr = new uint8[sizeToRead];
|
|
if (fread (ptr, sizeToRead, 1, f2) != 1)
|
|
nlwarning("%s read error", filename2.c_str());
|
|
if (fwrite (ptr, sizeToRead, 1, f1) != 1)
|
|
nlwarning("%s write error", filename1.c_str());
|
|
delete [] ptr;
|
|
|
|
fclose(f2);
|
|
fclose(f1);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
bool i_comp(const string &s0, const string &s1)
|
|
{
|
|
return nlstricmp (CFile::getFilename(s0).c_str(), CFile::getFilename(s1).c_str()) < 0;
|
|
}
|
|
|
|
void packSubRecurse ()
|
|
{
|
|
vector<string> pathContent;
|
|
|
|
string cp = CPath::getCurrentPath();
|
|
printf ("Treating directory : %s\n", cp.c_str());
|
|
CPath::getPathContent(cp, true, false, true, pathContent);
|
|
|
|
// Sort filename
|
|
sort (pathContent.begin(), pathContent.end(), i_comp);
|
|
|
|
uint i;
|
|
for (i=0; i<pathContent.size(); i++)
|
|
{
|
|
if (keepFile (pathContent[i].c_str()))
|
|
{
|
|
BNPFile ftmp;
|
|
|
|
// Check if we can read the source file
|
|
FILE *f = fopen (pathContent[i].c_str(), "rb");
|
|
if (f != NULL)
|
|
{
|
|
fclose (f);
|
|
ftmp.Name = CFile::getFilename(pathContent[i]);
|
|
ftmp.Size = CFile::getFileSize(pathContent[i]);
|
|
ftmp.Pos = gBNPHeader.OffsetFromBeginning;
|
|
gBNPHeader.Files.push_back(ftmp);
|
|
gBNPHeader.OffsetFromBeginning += ftmp.Size;
|
|
append(gDestBNPFile, pathContent[i].c_str(), ftmp.Size);
|
|
printf("adding %s\n", pathContent[i].c_str());
|
|
}
|
|
else
|
|
{
|
|
printf("error cannot open %s\n", pathContent[i].c_str());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
void unpack (const string &dirName)
|
|
{
|
|
FILE *bnp = fopen (gDestBNPFile.c_str(), "rb");
|
|
FILE *out;
|
|
if (bnp == NULL)
|
|
return;
|
|
|
|
for (uint32 i = 0; i < gBNPHeader.Files.size(); ++i)
|
|
{
|
|
BNPFile &rBNPFile = gBNPHeader.Files[i];
|
|
string filename = dirName + "/" + rBNPFile.Name;
|
|
out = fopen (filename.c_str(), "wb");
|
|
if (out != NULL)
|
|
{
|
|
nlfseek64 (bnp, rBNPFile.Pos, SEEK_SET);
|
|
uint8 *ptr = new uint8[rBNPFile.Size];
|
|
if (fread (ptr, rBNPFile.Size, 1, bnp) != 1)
|
|
nlwarning("%s read error", filename.c_str());
|
|
if (fwrite (ptr, rBNPFile.Size, 1, out) != 1)
|
|
nlwarning("%s write error", filename.c_str());
|
|
fclose (out);
|
|
delete [] ptr;
|
|
}
|
|
}
|
|
fclose (bnp);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
void usage()
|
|
{
|
|
printf ("USAGE : \n");
|
|
printf (" bnp_make /p <directory_name> [<destination_path>] [<destination_filename>] [option] ... [option]\n");
|
|
printf (" option : \n");
|
|
printf (" -if wildcard : add the file if it matches the wilcard (at least one 'if' conditions must be met for a file to be adding)\n");
|
|
printf (" -ifnot wildcard : add the file if it doesn't match the wilcard (all the 'ifnot' conditions must be met for a file to be adding)\n");
|
|
printf (" Pack the directory to a bnp file\n");
|
|
printf (" bnp_make /u <bnp_file>\n");
|
|
printf (" Unpack the bnp file to a directory\n");
|
|
printf (" bnp_make /l <bnp_file>\n");
|
|
printf (" List the files contained in the bnp file\n");
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
uint readOptions (int nNbArg, char **ppArgs)
|
|
{
|
|
uint i;
|
|
uint optionCount = 0;
|
|
for (i=0; i<(uint)nNbArg; i++)
|
|
{
|
|
// If ?
|
|
if ((strcmp (ppArgs[i], "-if") == 0) && ((i+1)<(uint)nNbArg))
|
|
{
|
|
CWildCard card;
|
|
card.Expression = toLower(string(ppArgs[i+1]));
|
|
card.Not = false;
|
|
WildCards.push_back (card);
|
|
optionCount += 2;
|
|
}
|
|
// If not ?
|
|
if ((strcmp (ppArgs[i], "-ifnot") == 0) && ((i+1)<(uint)nNbArg))
|
|
{
|
|
CWildCard card;
|
|
card.Expression = toLower(string(ppArgs[i+1]));
|
|
card.Not = true;
|
|
WildCards.push_back (card);
|
|
optionCount += 2;
|
|
}
|
|
}
|
|
return optionCount;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
int main (int nNbArg, char **ppArgs)
|
|
{
|
|
NLMISC::CApplicationContext myApplicationContext;
|
|
|
|
if (nNbArg < 3)
|
|
{
|
|
usage();
|
|
return -1;
|
|
}
|
|
|
|
if ((strcmp(ppArgs[1], "/p") == 0) || (strcmp(ppArgs[1], "/P") == 0) ||
|
|
(strcmp(ppArgs[1], "-p") == 0) || (strcmp(ppArgs[1], "-P") == 0))
|
|
{
|
|
// Pack a directory
|
|
uint count = readOptions (nNbArg, ppArgs);
|
|
nNbArg -= count;
|
|
|
|
// Read options
|
|
|
|
string sCurDir;
|
|
|
|
if (nNbArg >= 4)
|
|
{
|
|
// store current path
|
|
sCurDir = CPath::getCurrentPath();
|
|
|
|
// go to the dest path
|
|
string sDestDir;
|
|
if (CPath::setCurrentPath(ppArgs[3]))
|
|
{
|
|
sDestDir = CPath::getCurrentPath();
|
|
|
|
bool tmp = CPath::setCurrentPath(sCurDir.c_str());
|
|
// restore current path, should not failed
|
|
nlassert (tmp); // removed in release
|
|
|
|
// go to the source dir
|
|
if (CPath::setCurrentPath(ppArgs[2]))
|
|
{
|
|
sCurDir = CPath::getCurrentPath();
|
|
|
|
gDestBNPFile = CPath::standardizePath(sDestDir);
|
|
|
|
if(nNbArg == 5)
|
|
{
|
|
gDestBNPFile += ppArgs[4];
|
|
// add ext if necessary
|
|
if (string(ppArgs[4]).find(".") == string::npos)
|
|
gDestBNPFile += string(".bnp");
|
|
}
|
|
else
|
|
{
|
|
const char *pos = strrchr (sCurDir.c_str(), '/');
|
|
if (pos != NULL)
|
|
{
|
|
gDestBNPFile += string(pos+1);
|
|
}
|
|
// get the dest file name
|
|
gDestBNPFile += string(".bnp");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
nlwarning ("ERROR (bnp_make) : can't set current directory to %s", ppArgs[2]);
|
|
return -1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
nlwarning ("ERROR (bnp_make) : can't set current directory to %s", ppArgs[3]);
|
|
return -1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (chdir (ppArgs[2]) == -1)
|
|
{
|
|
nlwarning ("ERROR (bnp_make) : can't set current directory to %s", ppArgs[2]);
|
|
return -1;
|
|
}
|
|
//getcwd (sCurDir, MAX_PATH);
|
|
gDestBNPFile = CPath::getCurrentPath() + string(".bnp");
|
|
}
|
|
|
|
remove (gDestBNPFile.c_str());
|
|
gBNPHeader.OffsetFromBeginning = 0;
|
|
packSubRecurse();
|
|
gBNPHeader.append (gDestBNPFile);
|
|
return 0;
|
|
}
|
|
|
|
if ((strcmp(ppArgs[1], "/u") == 0) || (strcmp(ppArgs[1], "/U") == 0) ||
|
|
(strcmp(ppArgs[1], "-u") == 0) || (strcmp(ppArgs[1], "-U") == 0))
|
|
{
|
|
string::size_type i;
|
|
string path;
|
|
gDestBNPFile = ppArgs[2];
|
|
if ((gDestBNPFile.rfind('/') != string::npos) || (gDestBNPFile.rfind('/') != string::npos))
|
|
{
|
|
string::size_type pos = gDestBNPFile.rfind('/');
|
|
if (pos == string::npos)
|
|
pos = gDestBNPFile.rfind('/');
|
|
for (i = 0; i <= pos; ++i)
|
|
path += gDestBNPFile[i];
|
|
string wholeName = gDestBNPFile;
|
|
gDestBNPFile = "";
|
|
for (; i < wholeName.size(); ++i)
|
|
gDestBNPFile += wholeName[i];
|
|
if (CPath::setCurrentPath(path.c_str()))
|
|
{
|
|
path = CPath::getCurrentPath();
|
|
}
|
|
else
|
|
{
|
|
nlwarning ("ERROR (bnp_make) : can't set current directory to %s", path.c_str());
|
|
return -1;
|
|
}
|
|
}
|
|
if (stricmp (gDestBNPFile.c_str()+gDestBNPFile.size()-4, ".bnp") != 0)
|
|
{
|
|
gDestBNPFile += ".bnp";
|
|
}
|
|
string dirName;
|
|
for (i = 0; i < gDestBNPFile.size()-4; ++i)
|
|
dirName += gDestBNPFile[i];
|
|
// Unpack a bnp file
|
|
if (!gBNPHeader.read (gDestBNPFile))
|
|
return -1;
|
|
|
|
//mkdir (dirName.c_str());
|
|
CFile::createDirectory(dirName);
|
|
|
|
unpack (dirName);
|
|
|
|
return 0;
|
|
}
|
|
|
|
if ((strcmp(ppArgs[1], "/l") == 0) || (strcmp(ppArgs[1], "/L") == 0) ||
|
|
(strcmp(ppArgs[1], "-l") == 0) || (strcmp(ppArgs[1], "-L") == 0))
|
|
{
|
|
string::size_type i;
|
|
string path;
|
|
gDestBNPFile = ppArgs[2];
|
|
if ((gDestBNPFile.rfind('/') != string::npos) || (gDestBNPFile.rfind('/') != string::npos))
|
|
{
|
|
string::size_type pos = gDestBNPFile.rfind('/');
|
|
if (pos == string::npos)
|
|
pos = gDestBNPFile.rfind('/');
|
|
for (i = 0; i <= pos; ++i)
|
|
path += gDestBNPFile[i];
|
|
string wholeName = gDestBNPFile;
|
|
gDestBNPFile = "";
|
|
for (; i < wholeName.size(); ++i)
|
|
gDestBNPFile += wholeName[i];
|
|
if (CPath::setCurrentPath(path.c_str()))
|
|
{
|
|
path = CPath::getCurrentPath();
|
|
}
|
|
else
|
|
{
|
|
nlwarning ("ERROR (bnp_make) : can't set current directory to %s", path.c_str());
|
|
return -1;
|
|
}
|
|
}
|
|
if (stricmp (gDestBNPFile.c_str()+gDestBNPFile.size()-4, ".bnp") != 0)
|
|
{
|
|
gDestBNPFile += ".bnp";
|
|
}
|
|
string dirName;
|
|
for (i = 0; i < gDestBNPFile.size()-4; ++i)
|
|
dirName += gDestBNPFile[i];
|
|
|
|
// Unpack a bnp file
|
|
if (!gBNPHeader.read (gDestBNPFile))
|
|
return -1;
|
|
|
|
for (i = 0; i < gBNPHeader.Files.size(); ++i)
|
|
printf ("%s\n", gBNPHeader.Files[i].Name.c_str());
|
|
|
|
return 0;
|
|
}
|
|
|
|
usage ();
|
|
return -1;
|
|
}
|