// NeL - MMORPG Framework
// Copyright (C) 2010 Winch Gate Property Limited
//
// This source file has been modified by the following contributors:
// Copyright (C) 2014-2020 Jan BOON (Kaetemi)
//
// 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/types_nl.h"
#include
#include
#ifdef NL_OS_WINDOWS
# include
# include
#endif
#include
#include
#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"
#include "nel/misc/streamed_package.h"
#include "nel/misc/seven_zip.h"
using namespace std;
using namespace NLMISC;
// ---------------------------------------------------------------------------
class CWildCard
{
public:
string Expression;
bool Not;
};
std::vector WildCards;
std::string SourceDirectory;
std::string PackageFileName;
std::string StreamDirectory;
CStreamedPackage Package;
// ---------------------------------------------------------------------------
bool keepFile (const char *fileName)
{
uint i;
bool ifPresent = false;
bool ifTrue = false;
string file = toLowerAscii(CFile::getFilename (fileName));
for (i=0; i [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 snp file\n");
printf (" snp_make -l \n");
printf (" List the files contained in the snp file\n");
}
// ---------------------------------------------------------------------------
void generateLZMA(const std::string &sourceFile, const std::string &outputFile)
{
NLMISC::packLZMA(sourceFile, outputFile);
/*
std::string cmd="lzma e ";
cmd+=" "+sourceFile+" "+outputFile;
nlinfo("executing system command: %s",cmd.c_str());
#ifdef NL_OS_WINDOWS
_spawnlp(_P_WAIT, "lzma.exe","lzma.exe", "e", sourceFile.c_str(), outputFile.c_str(), NULL);
#else // NL_OS_WINDOWS
sint error = system (cmd.c_str());
if (error)
nlwarning("'%s' failed with error code %d", cmd.c_str(), error);
#endif // NL_OS_WINDOWS
*/
}
// ---------------------------------------------------------------------------
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))
{
if (nNbArg < 5)
{
usage();
return -1;
}
SourceDirectory = ppArgs[2];
PackageFileName = ppArgs[3];
StreamDirectory = ppArgs[4];
readOptions(nNbArg, ppArgs);
nldebug("Make streamed package: '%s'", PackageFileName.c_str());
if (CFile::fileExists(PackageFileName))
{
nldebug("Update existing package");
try
{
CIFile fi;
fi.open(PackageFileName);
fi.serial(Package);
}
catch (Exception &e)
{
nlwarning("ERROR (snp_make) : serial exception: '%s'", e.what());
return -1;
}
}
else
{
nldebug("New package");
}
std::vector pathContent; // contains full pathnames
std::vector nameContent; // only filename
CPath::getPathContent(SourceDirectory, true, false, true, pathContent);
nameContent.reserve(pathContent.size());
for (std::vector::size_type i = 0; i < pathContent.size(); ++i)
{
const std::string &file = pathContent[i];
if (keepFile(file.c_str()))
{
std::string fileName = NLMISC::toLower(CFile::getFilename(file));
// nldebug("File: '%s' ('%s')", file.c_str(), fileName.c_str());
nameContent.push_back(fileName);
nlassert(nameContent.size() == (i + 1));
}
else
{
// Not included in this package
pathContent.erase(pathContent.begin() + i);
--i;
}
}
std::vector packageIndex; // index of file in package
packageIndex.resize(pathContent.size(), -1);
for (CStreamedPackage::TEntries::size_type i = 0; i < Package.Entries.size(); ++i)
{
const CStreamedPackage::CEntry &entry = Package.Entries[i];
sint foundIndex = -1; // find index in found file list
for (std::vector::size_type j = 0; j < pathContent.size(); ++j)
{
if (nameContent[j] == entry.Name)
{
foundIndex = j;
break;
}
}
if (foundIndex < 0)
{
nlinfo("File no longer exists: '%s'", entry.Name.c_str());
Package.Entries.erase(Package.Entries.begin() + i);
--i;
}
else
{
// File still exists, map it
packageIndex[foundIndex] = i;
}
}
for (std::vector::size_type i = 0; i < pathContent.size(); ++i)
{
sint pidx = packageIndex[i];
const std::string &name = nameContent[i];
const std::string &path = pathContent[i];
if (pidx < 0)
{
nlinfo("File added: '%s'", name.c_str());
pidx = Package.Entries.size();
Package.Entries.push_back(CStreamedPackage::CEntry());
Package.Entries[pidx].Name = name;
Package.Entries[pidx].LastModified = 0;
Package.Entries[pidx].Size = 0;
}
else
{
nlinfo("File check for changes: '%s'", name.c_str());
}
CStreamedPackage::CEntry &entry = Package.Entries[pidx];
std::string targetLzmaOld; // in case lzma wasn't made make sure it exists a second run
CStreamedPackage::makePath(targetLzmaOld, entry.Hash);
targetLzmaOld = StreamDirectory + targetLzmaOld + ".lzma";
uint32 lastModified = CFile::getFileModificationDate(path);
uint32 fileSize = CFile::getFileSize(path);
if (lastModified > entry.LastModified || fileSize != entry.Size || !CFile::fileExists(targetLzmaOld))
{
entry.LastModified = lastModified;
nlinfo("Calculate file hash");
CHashKey hash = getSHA1(path, true);
/*nldebug("%s", hash.toString().c_str());
std::string hashPath;
CStreamedPackage::makePath(hashPath, hash);
nldebug("%s", hashPath.c_str());*/
if (hash == entry.Hash && fileSize == entry.Size)
{
// File has not changed
}
else
{
nlinfo("File changed");
entry.Hash = hash;
entry.Size = fileSize;
}
std::string targetLzma; // in case lzma wasn't made make sure it exists a second run
CStreamedPackage::makePath(targetLzma, entry.Hash);
targetLzma = StreamDirectory + targetLzma + ".lzma";
if (!CFile::fileExists(targetLzma))
{
// make the compressed file
nlinfo("%s -> %s", path.c_str(), targetLzma.c_str());
CFile::createDirectoryTree(CFile::getPath(targetLzma));
generateLZMA(path, targetLzma);
}
}
}
try
{
nldebug("Store package '%s'", PackageFileName.c_str());
COFile fo;
fo.open(PackageFileName);
fo.serial(Package);
}
catch (Exception &e)
{
nlwarning("ERROR (snp_make) : serial exception: '%s'", e.what());
return -1;
}
return 0;
}
if ((strcmp(ppArgs[1], "/l") == 0) || (strcmp(ppArgs[1], "/L") == 0) ||
(strcmp(ppArgs[1], "-l") == 0) || (strcmp(ppArgs[1], "-L") == 0))
{
PackageFileName = ppArgs[2];
if (!CFile::fileExists(PackageFileName))
{
nlwarning("ERROR (snp_make) : package doesn't exist: '%s'", PackageFileName.c_str());
return -1;
}
try
{
CIFile fi;
fi.open(PackageFileName);
fi.serial(Package);
}
catch (Exception &e)
{
nlwarning("ERROR (snp_make) : serial exception: '%s'", e.what());
return -1;
}
for (CStreamedPackage::TEntries::const_iterator it(Package.Entries.begin()), end(Package.Entries.end()); it != end; ++it)
{
const CStreamedPackage::CEntry &entry = (*it);
printf("List files in '%s'", PackageFileName.c_str());
printf("%s { Hash: '%s', Size: '%u', LastModified: '%u' }", entry.Name.c_str(), entry.Hash.toString().c_str(), entry.Size, entry.LastModified);
}
return 0;
}
usage ();
return -1;
}