// 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(); // value to be serialized uint32 nNbFile2 = nNbFile; #ifdef NL_BIG_ENDIAN NLMISC_BSWAP32(nNbFile2); #endif if (fwrite (&nNbFile2, 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; } uint32 nFileSize = Files[i].Size; #ifdef NL_BIG_ENDIAN NLMISC_BSWAP32(nFileSize); #endif if (fwrite (&nFileSize, sizeof(uint32), 1, f) != 1) { fclose(f); return false; } uint32 nFilePos = Files[i].Pos; #ifdef NL_BIG_ENDIAN NLMISC_BSWAP32(nFilePos); #endif if (fwrite (&nFilePos, sizeof(uint32), 1, f) != 1) { fclose(f); return false; } } uint32 nOffsetFromBeginning = OffsetFromBeginning; #ifdef NL_BIG_ENDIAN NLMISC_BSWAP32(nOffsetFromBeginning); #endif if (fwrite (&nOffsetFromBeginning, 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 nOffsetFromBeginning; if (fread (&nOffsetFromBeginning, sizeof(uint32), 1, f) != 1) { fclose (f); return false; } #ifdef NL_BIG_ENDIAN NLMISC_BSWAP32(nOffsetFromBeginning); #endif if (nlfseek64 (f, nOffsetFromBeginning, 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; }