diff --git a/Makefile b/Makefile index 129833d..52fc691 100644 --- a/Makefile +++ b/Makefile @@ -31,15 +31,19 @@ OUT_RELEASE = bin/Release/v8unpack OBJ_RELEASE = $(OBJDIR_RELEASE)/src/V8File.o $(OBJDIR_RELEASE)/src/main.o PREFIX=$(DESTDIR)/usr/bin +BASH_COMPLETION_PREFIX=$(DESTDIR)/etc/bash_completion.d all: release -install: +install: $(OUT_RELEASE) test -d $(PREFIX) || mkdir -p $(PREFIX) cp bin/Release/v8unpack $(PREFIX)/v8unpack + test -d $(BASH_COMPLETION_PREFIX) || mkdir -p $(BASH_COMPLETION_PREFIX) + cp bash_completion.sh $(BASH_COMPLETION_PREFIX)/v8unpack uninstall: rm $(PREFIX)/v8unpack + rm $(BASH_COMPLETION_PREFIX)/v8unpack clean: clean_release @@ -56,10 +60,10 @@ release: $(OUT_RELEASE) after_release $(OUT_RELEASE): bin/Release $(OBJ_RELEASE) $(DEP_RELEASE) $(LD) $(LIBDIR_RELEASE) -o $(OUT_RELEASE) $(OBJ_RELEASE) $(LDFLAGS_RELEASE) $(LIB_RELEASE) -$(OBJDIR_RELEASE)/src/V8File.o: src/V8File.cpp +$(OBJDIR_RELEASE)/src/V8File.o: src/V8File.cpp src/V8File.h $(CXX) -D__LINUX $(CFLAGS_RELEASE) $(INC_RELEASE) -c src/V8File.cpp -o $(OBJDIR_RELEASE)/src/V8File.o -$(OBJDIR_RELEASE)/src/main.o: src/main.cpp +$(OBJDIR_RELEASE)/src/main.o: src/main.cpp src/V8File.h $(CXX) -D__LINUX $(CFLAGS_RELEASE) $(INC_RELEASE) -c src/main.cpp -o $(OBJDIR_RELEASE)/src/main.o clean_release: diff --git a/VersionInfo.rc b/VersionInfo.rc index 2ad22da..c9dcbd3 100644 --- a/VersionInfo.rc +++ b/VersionInfo.rc @@ -1,6 +1,6 @@ 1 VERSIONINFO -FILEVERSION 3,0,39,1 -PRODUCTVERSION 3,0,39,1 +FILEVERSION 3,0,40,1 +PRODUCTVERSION 3,0,40,1 BEGIN BLOCK "StringFileInfo" BEGIN @@ -8,12 +8,12 @@ BEGIN BEGIN VALUE "CompanyName", "V8Unpack developers community" VALUE "FileDescription", "V8 Unpack Tool" - VALUE "FileVersion", "3.0.39" + VALUE "FileVersion", "3.0.40" VALUE "InternalName", "v8unpack" VALUE "LegalCopyright", "Denis Demidov, Sergey Batanov and others" VALUE "OriginalFilename", "v8unpack.exe" VALUE "ProductName", "V8 Unpack Tool" - VALUE "ProductVersion", "3.0.39" + VALUE "ProductVersion", "3.0.40" END END diff --git a/appveyor.yml b/appveyor.yml index 8e27793..66b2f69 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -version: 3.0.39.{build} +version: 3.0.40.{build} pull_requests: do_not_increment_build_number: true clone_depth: 1 diff --git a/bash_completion.sh b/bash_completion.sh new file mode 100644 index 0000000..03c0e32 --- /dev/null +++ b/bash_completion.sh @@ -0,0 +1,19 @@ +_v8unpack_complete() +{ + local cur opts + COMPREPLY=() + cur="${COMP_WORDS[COMP_CWORD]}" + prev="${COMP_WORDS[COMP_CWORD]}" + opts="-unpack -pack -parse -build -inflate -deflate \ + -list -example -bat -version" + + if [[ ${COMP_CWORD} == 1 ]] ; then + COMPREPLY=( $(compgen -W "$opts" -- ${cur}) ) + return 0 + fi + + COMPREPLY=( $(compgen -f ${cur}) ) + return 0 +} +complete -F _v8unpack_complete v8unpack + diff --git a/debian/changelog b/debian/changelog index 274a86f..30a15ea 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,21 @@ +v8unpack (3.0.40-2~xenial) xenial; urgency=low + + * Fixed build bug + + -- Sergey Batanov Fri, 17 Mar 2017 22:44:00 +0300 + +v8unpack (3.0.40-2~vivid) vivid; urgency=low + + * Fixed build bug + + -- Sergey Batanov Fri, 17 Mar 2017 22:44:00 +0300 + +v8unpack (3.0.40-2~trusty) trusty; urgency=low + + * Fixed build bug + + -- Sergey Batanov Fri, 17 Mar 2017 22:44:00 +0300 + v8unpack (3.0.39-1~xenial) xenial; urgency=low * Added -list mode diff --git a/rpm/v8unpack.spec b/rpm/v8unpack.spec index 584812f..635a4c7 100644 --- a/rpm/v8unpack.spec +++ b/rpm/v8unpack.spec @@ -1,5 +1,5 @@ Name: v8unpack -Version: 3.0.38 +Version: 3.0.40 Release: 1%{?dist} Summary: Enterprise 8 unpack tool diff --git a/src/V8File.cpp b/src/V8File.cpp index 63e9599..9302a2a 100644 --- a/src/V8File.cpp +++ b/src/V8File.cpp @@ -1,7 +1,7 @@ /*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ ///////////////////////////////////////////////////////////////////////////// @@ -28,11 +28,26 @@ at http://mozilla.org/MPL/2.0/. #include #include #include +#include #ifndef MAX_PATH #define MAX_PATH (260) #endif +using namespace std; + + + +template +void full_copy(basic_istream &in_file, basic_ostream &out_file) +{ + copy( + istreambuf_iterator(in_file), + istreambuf_iterator(), + ostreambuf_iterator(out_file) + ); +} + ////////////////////////////////////////////////////////////////////// // Construction/Destruction @@ -81,24 +96,46 @@ CV8Elem::~CV8Elem() } - int Inflate(const std::string &in_filename, const std::string &out_filename) { int ret; - boost::filesystem::path inf(in_filename); - boost::filesystem::ifstream in_file(inf, std::ios_base::binary); + std::shared_ptr input; + + if (in_filename == "-") { + + // считываем стандартный ввод + input.reset(&std::cin, [](...){}); + + } else { + + boost::filesystem::path inf(in_filename); + input.reset(new boost::filesystem::ifstream(inf, std::ios_base::binary)); + + if (!*input) { + return V8UNPACK_DEFLATE_IN_FILE_NOT_FOUND; + } + + } - if (!in_file) - return V8UNPACK_INFLATE_IN_FILE_NOT_FOUND; + std::shared_ptr output; - boost::filesystem::path ouf(out_filename); - boost::filesystem::ofstream out_file(ouf, std::ios_base::binary); + if (out_filename == "-") { - if (!out_file) - return V8UNPACK_INFLATE_OUT_FILE_NOT_CREATED; + // Выводим в стандартый вывод + output.reset(&std::cout, [](...){}); + + } else { + + boost::filesystem::path ouf(out_filename); + output.reset(new boost::filesystem::ofstream (ouf, std::ios_base::binary)); + + if (!*output) { + return V8UNPACK_INFLATE_OUT_FILE_NOT_CREATED; + } + } - ret = Inflate(in_file, out_file); + ret = Inflate(*input, *output); if (ret == Z_DATA_ERROR) return V8UNPACK_INFLATE_DATAERROR; @@ -112,19 +149,42 @@ int Deflate(const std::string &in_filename, const std::string &out_filename) { int ret; - boost::filesystem::path inf(in_filename); - boost::filesystem::ifstream in_file(inf, std::ios_base::binary); + std::shared_ptr input; + + if (in_filename == "-") { + + // считываем стандартный ввод + input.reset(&std::cin, [](...){}); + + } else { + + boost::filesystem::path inf(in_filename); + input.reset(new boost::filesystem::ifstream(inf, std::ios_base::binary)); + + if (!*input) { + return V8UNPACK_DEFLATE_IN_FILE_NOT_FOUND; + } + + } + + std::shared_ptr output; - if (!in_file) - return V8UNPACK_DEFLATE_IN_FILE_NOT_FOUND; + if (out_filename == "-") { - boost::filesystem::path ouf(out_filename); - boost::filesystem::ofstream out_file(ouf, std::ios_base::binary); + // Выводим в стандартый вывод + output.reset(&std::cout, [](...){}); - if (!out_file) - return V8UNPACK_DEFLATE_OUT_FILE_NOT_CREATED; + } else { + + boost::filesystem::path ouf(out_filename); + output.reset(new boost::filesystem::ofstream (ouf, std::ios_base::binary)); + + if (!*output) { + return V8UNPACK_INFLATE_OUT_FILE_NOT_CREATED; + } + } - ret = Deflate(in_file, out_file); + ret = Deflate(*input, *output); if (ret) return V8UNPACK_DEFLATE_ERROR; @@ -132,7 +192,7 @@ int Deflate(const std::string &in_filename, const std::string &out_filename) return 0; } -int Deflate(std::basic_ifstream &source, std::basic_ofstream &dest) +int Deflate(std::istream &source, std::ostream &dest) { int ret, flush; @@ -189,7 +249,7 @@ int Deflate(std::basic_ifstream &source, std::basic_ofstream &dest) return Z_OK; } -int Inflate(std::basic_ifstream &source, std::basic_ofstream &dest) +int Inflate(std::istream &source, std::ostream &dest) { int ret; unsigned have; @@ -407,14 +467,7 @@ int CV8File::LoadFile(char *pFileData, ULONG FileDataSize, bool boolInflate, boo pBlockHeader = (stBlockHeader*) &pFileData[pElemsAddrs[i].elem_header_addr]; - if (pBlockHeader->EOL_0D != 0x0d || - pBlockHeader->EOL_0A != 0x0a || - pBlockHeader->space1 != 0x20 || - pBlockHeader->space2 != 0x20 || - pBlockHeader->space3 != 0x20 || - pBlockHeader->EOL2_0D != 0x0d || - pBlockHeader->EOL2_0A != 0x0a) { - + if (!pBlockHeader->IsCorrect()) { ret = V8UNPACK_HEADER_ELEM_NOT_CORRECT; break; } @@ -473,32 +526,30 @@ int CV8File::LoadFile(char *pFileData, ULONG FileDataSize, bool boolInflate, boo void CV8File::Dispose() { - std::vector::iterator elem; - for (elem = Elems.begin(); elem != Elems.end(); ++elem) { - if (elem->pData) - delete [] elem->pData; - if (elem->pHeader) - delete [] elem->pHeader; - } - Elems.clear(); + std::vector::iterator elem; + for (elem = Elems.begin(); elem != Elems.end(); ++elem) { + elem->Dispose(); + } + Elems.clear(); } //++ dmpas Затычка Issue6 // Нѣкоторый условный предѣл -const size_t SmartLimit = 100 *1024; +const size_t SmartLimit = 200 *1024; +const size_t SmartUnpackedLimit = 20 *1024*1024; /* - Лучше всѣго сжимается текст - Берём степень сжатія текста в 99% (объём распакованных данных в 100 раз больше) - Берём примѣрный порог использованія памяти в 10МБ (в этот объём должы влезть распакованные данные) - Дѣлим 10МБ на 100 и получаем 100 КБ - Упакованные данные размѣром до 100 КБ можно спокойно обрабатывать в памяти + Лучше всѣго сжимается текст + Берём степень сжатія текста в 99% (объём распакованных данных в 100 раз больше) + Берём примѣрный порог использованія памяти в 20МБ (в этот объём должы влезть распакованные данные) + Дѣлим 20МБ на 100 и получаем 200 КБ + Упакованные данные размѣром до 200 КБ можно спокойно обрабатывать в памяти - В дальнейшем этот показатель всё же будет вынесен в параметр командной строки + В дальнейшем этот показатель всё же будет вынесен в параметр командной строки */ -int SmartUnpack(std::basic_ifstream &file, bool NeedUnpack, boost::filesystem::path &elem_path) +int SmartUnpack(std::basic_istream &file, bool NeedUnpack, boost::filesystem::path &elem_path) { CV8File::stBlockHeader header; file.read((char*)&header, sizeof(header)); @@ -533,19 +584,8 @@ int SmartUnpack(std::basic_ifstream &file, bool NeedUnpack, boost::filesys if (ret) { // Файл не распаковывается - записываем, как есть - inf.seekg(0, std::ios_base::beg); - - do { - - const int block_size = 4096; - char data[block_size]; // TODO: Оценить размер блока - int data_size = inf.read(data, block_size).gcount(); - - if (data_size != 0) - out.write(data, data_size); - - } while (inf); - + inf.seekg(0, std::ios_base::beg); + full_copy(inf, out); } inf.close(); @@ -568,7 +608,8 @@ int SmartUnpack(std::basic_ifstream &file, bool NeedUnpack, boost::filesys src.open(src_path, std::ios_base::binary); if (CV8File::IsV8File(src)) { - CV8File::UnpackToDirectoryNoLoad(elem_path.string(), src, false, false); + vector empty_filter; + CV8File::UnpackToDirectoryNoLoad(elem_path.string(), src, empty_filter, false, false); src.close(); boost::filesystem::remove(src_path); } else { @@ -627,7 +668,13 @@ int SmartUnpack(std::basic_ifstream &file, bool NeedUnpack, boost::filesys } //-- dmpas Затычка Issue6 -int CV8File::UnpackToDirectoryNoLoad(const std::string &directory, std::basic_ifstream &file, bool boolInflate, bool UnpackWhenNeed) +bool NameInFilter(const string &name, const vector &filter) +{ + return filter.empty() + || find(filter.begin(), filter.end(), name) != filter.end(); +} + +int CV8File::UnpackToDirectoryNoLoad(const string &directory, basic_istream &file, const vector &filter, bool boolInflate, bool UnpackWhenNeed) { int ret = 0; @@ -668,14 +715,7 @@ int CV8File::UnpackToDirectoryNoLoad(const std::string &directory, std::basic_if file.seekg(pElemsAddrs[i].elem_header_addr, std::ios_base::beg); file.read((char*)&BlockHeader, sizeof(BlockHeader)); - if (pBlockHeader->EOL_0D != 0x0d || - pBlockHeader->EOL_0A != 0x0a || - pBlockHeader->space1 != 0x20 || - pBlockHeader->space2 != 0x20 || - pBlockHeader->space3 != 0x20 || - pBlockHeader->EOL2_0D != 0x0d || - pBlockHeader->EOL2_0A != 0x0a) { - + if (!pBlockHeader->IsCorrect()) { ret = V8UNPACK_HEADER_ELEM_NOT_CORRECT; break; } @@ -683,10 +723,11 @@ int CV8File::UnpackToDirectoryNoLoad(const std::string &directory, std::basic_if CV8Elem elem; ReadBlockData(file, pBlockHeader, elem.pHeader, &elem.HeaderSize); - char ElemName[512]; - UINT ElemNameLen; + string ElemName = elem.GetName(); - elem.GetName(ElemName, &ElemNameLen); + if (!NameInFilter(ElemName, filter)) { + continue; + } boost::filesystem::path elem_path(p_dir / ElemName); elem_path = boost::filesystem::absolute(elem_path); @@ -709,6 +750,59 @@ int CV8File::UnpackToDirectoryNoLoad(const std::string &directory, std::basic_if return ret; } +int CV8File::ListFiles(const std::string &filename) +{ + + boost::filesystem::ifstream file(filename, std::ios_base::binary); + + if (!file) { + std::cerr << "ListFiles `" << filename << "`. Input file not found!" << std::endl; + return -1; + } + + if (!IsV8File(file)) { + return V8UNPACK_NOT_V8_FILE; + } + + stFileHeader FileHeader; + file.read((char*)&FileHeader, sizeof(FileHeader)); + + stBlockHeader BlockHeader; + stBlockHeader *pBlockHeader = &BlockHeader; + + file.read((char*)&BlockHeader, sizeof(BlockHeader)); + + UINT ElemsAddrsSize; + stElemAddr *pElemsAddrs = nullptr; + ReadBlockData(file, pBlockHeader, (char*&)pElemsAddrs, &ElemsAddrsSize); + + unsigned int ElemsNum = ElemsAddrsSize / stElemAddr::Size(); + + for (UINT i = 0; i < ElemsNum; i++) { + + if (pElemsAddrs[i].fffffff != V8_FF_SIGNATURE) { + ElemsNum = i; + break; + } + + file.seekg(pElemsAddrs[i].elem_header_addr, std::ios_base::beg); + file.read((char*)&BlockHeader, sizeof(BlockHeader)); + + if (!pBlockHeader->IsCorrect()) { + continue; + } + + CV8Elem elem; + ReadBlockData(file, pBlockHeader, elem.pHeader, &elem.HeaderSize); + + string ElemName = elem.GetName(); + + std::cout << ElemName << std::endl; + } + + return 0; +} + int CV8File::UnpackToFolder(const std::string &filename_in, const std::string &dirname, const std::string &UnpackElemWithName, bool print_progress) { int ret = 0; @@ -735,7 +829,8 @@ int CV8File::UnpackToFolder(const std::string &filename_in, const std::string &d stFileHeader FileHeader; file.read((char*)&FileHeader, sizeof(FileHeader)); - { + + if (UnpackElemWithName.empty()) { boost::filesystem::path filename_out(dirname); filename_out /= "FileHeader"; boost::filesystem::ofstream file_out(filename_out, std::ios_base::binary); @@ -764,29 +859,20 @@ int CV8File::UnpackToFolder(const std::string &filename_in, const std::string &d file.seekg(pElemsAddrs[i].elem_header_addr, std::ios_base::beg); file.read((char*)&BlockHeader, sizeof(BlockHeader)); - if (pBlockHeader->EOL_0D != 0x0d || - pBlockHeader->EOL_0A != 0x0a || - pBlockHeader->space1 != 0x20 || - pBlockHeader->space2 != 0x20 || - pBlockHeader->space3 != 0x20 || - pBlockHeader->EOL2_0D != 0x0d || - pBlockHeader->EOL2_0A != 0x0a) { - - ret = V8UNPACK_HEADER_ELEM_NOT_CORRECT; + if (!pBlockHeader->IsCorrect()) { + ret = V8UNPACK_HEADER_ELEM_NOT_CORRECT; break; } CV8Elem elem; ReadBlockData(file, pBlockHeader, elem.pHeader, &elem.HeaderSize); - char ElemName[512]; - UINT ElemNameLen; - - elem.GetName(ElemName, &ElemNameLen); + string ElemName = elem.GetName(); // если передано имя блока для распаковки, пропускаем все остальные - if (!UnpackElemWithName.empty() && UnpackElemWithName == ElemName) + if (!UnpackElemWithName.empty() && UnpackElemWithName != ElemName) { continue; + } boost::filesystem::path filename_out; boost::filesystem::ofstream file_out; @@ -892,7 +978,7 @@ int CV8File::ReadBlockData(char *pFileData, stBlockHeader *pBlockHeader, char *& return 0; } -int CV8File::ReadBlockData(std::basic_ifstream &file, stBlockHeader *pBlockHeader, char *&pBlockData, UINT *BlockDataSize) +int CV8File::ReadBlockData(std::basic_istream &file, stBlockHeader *pBlockHeader, char *&pBlockData, UINT *BlockDataSize) { DWORD data_size, page_size, next_page_addr; UINT read_in_bytes, bytes_to_read; @@ -936,7 +1022,7 @@ int CV8File::ReadBlockData(std::basic_ifstream &file, stBlockHeader *pBloc return 0; } -int CV8File::ReadBlockData(std::basic_ifstream &file, stBlockHeader *pBlockHeader, std::basic_ofstream &out, UINT *BlockDataSize) +int CV8File::ReadBlockData(std::basic_istream &file, stBlockHeader *pBlockHeader, std::basic_ostream &out, UINT *BlockDataSize) { DWORD data_size, page_size, next_page_addr; UINT read_in_bytes, bytes_to_read; @@ -986,14 +1072,12 @@ int CV8File::ReadBlockData(std::basic_ifstream &file, stBlockHeader *pBloc return 0; } -bool CV8File::IsV8File(std::basic_ifstream &file) +bool CV8File::IsV8File(std::basic_istream &file) { stFileHeader FileHeader; stBlockHeader BlockHeader; - stBlockHeader *pBlockHeader = &BlockHeader; - - memset(pBlockHeader, 0, sizeof(BlockHeader)); + memset(&BlockHeader, 0, sizeof(BlockHeader)); std::ifstream::pos_type offset = file.tellg(); @@ -1003,18 +1087,7 @@ bool CV8File::IsV8File(std::basic_ifstream &file) file.seekg(offset); file.clear(); - if (pBlockHeader->EOL_0D != 0x0d || - pBlockHeader->EOL_0A != 0x0a || - pBlockHeader->space1 != 0x20 || - pBlockHeader->space2 != 0x20 || - pBlockHeader->space3 != 0x20 || - pBlockHeader->EOL2_0D != 0x0d || - pBlockHeader->EOL2_0A != 0x0a) { - - return false; - } - - return true; + return BlockHeader.IsCorrect(); } bool CV8File::IsV8File(const char *pFileData, ULONG FileDataSize) @@ -1025,27 +1098,14 @@ bool CV8File::IsV8File(const char *pFileData, ULONG FileDataSize) } // проверим чтобы длина файла не была меньше длины заголовка файла и заголовка блока адресов - if (FileDataSize < stFileHeader::Size() + stBlockHeader::Size()) + if (FileDataSize < stFileHeader::Size() + stBlockHeader::Size()) { return false; + } stFileHeader *pFileHeader = (stFileHeader*) pFileData; + stBlockHeader *pBlockHeader = (stBlockHeader*) &pFileHeader[1]; - stBlockHeader *pBlockHeader; - - pBlockHeader = (stBlockHeader*) &pFileHeader[1]; - - if (pBlockHeader->EOL_0D != 0x0d || - pBlockHeader->EOL_0A != 0x0a || - pBlockHeader->space1 != 0x20 || - pBlockHeader->space2 != 0x20 || - pBlockHeader->space3 != 0x20 || - pBlockHeader->EOL2_0D != 0x0d || - pBlockHeader->EOL2_0A != 0x0a) { - - return false; - } - - return true; + return pBlockHeader->IsCorrect(); } struct PackElementEntry { @@ -1055,16 +1115,6 @@ struct PackElementEntry { size_t data_size; }; -template -void full_copy(std::basic_ifstream &in_file, std::basic_ofstream &out_file) -{ - std::copy( - std::istreambuf_iterator(in_file), - std::istreambuf_iterator(), - std::ostreambuf_iterator(out_file) - ); -} - int CV8File::PackFromFolder(const std::string &dirname, const std::string &filename_out) { boost::filesystem::path p_curdir(dirname); @@ -1154,7 +1204,7 @@ int CV8File::PackFromFolder(const std::string &dirname, const std::string &filen return 0; } -int CV8File::SaveBlockData(std::basic_ofstream &file_out, std::basic_ifstream &file_in, UINT BlockDataSize, UINT PageSize) +int CV8File::SaveBlockData(std::basic_ostream &file_out, std::basic_istream &file_in, UINT BlockDataSize, UINT PageSize) { if (PageSize < BlockDataSize) PageSize = BlockDataSize; @@ -1170,7 +1220,7 @@ int CV8File::SaveBlockData(std::basic_ofstream &file_out, std::basic_ifstr return 0; } -int CV8File::SaveBlockData(std::basic_ofstream &file_out, const char *pBlockData, UINT BlockDataSize, UINT PageSize) +int CV8File::SaveBlockData(std::basic_ostream &file_out, const char *pBlockData, UINT BlockDataSize, UINT PageSize) { if (PageSize < BlockDataSize) PageSize = BlockDataSize; @@ -1186,21 +1236,21 @@ int CV8File::SaveBlockData(std::basic_ofstream &file_out, const char *pBlo return 0; } -int CV8File::Parse(const std::string &filename_in, const std::string &dirname) +int CV8File::Parse(const std::string &filename_in, const std::string &dirname, const std::vector< std::string > &filter) { int ret = 0; boost::filesystem::ifstream file_in(filename_in, std::ios_base::binary); if (!file_in) { - std::cerr << "UnpackToFolder. `" << filename_in << "` not found!" << std::endl; + std::cerr << "Parse. `" << filename_in << "` not found!" << std::endl; return -1; } - ret = UnpackToDirectoryNoLoad(dirname, file_in); + ret = UnpackToDirectoryNoLoad(dirname, file_in, filter); if (ret == V8UNPACK_NOT_V8_FILE) { - std::cerr << "UnpackToFolder. `" << filename_in << "` is not V8 file!" << std::endl; + std::cerr << "Parse. `" << filename_in << "` is not V8 file!" << std::endl; return ret; } @@ -1210,25 +1260,20 @@ int CV8File::Parse(const std::string &filename_in, const std::string &dirname) } -int CV8File::SaveFileToFolder(const std::string &dirname) const +int CV8File::SaveFileToFolder(const boost::filesystem::path &directiory) const { int ret = 0; - if (!boost::filesystem::exists(dirname)) { - ret = !boost::filesystem::create_directory(dirname); + if (!boost::filesystem::exists(directiory)) { + ret = !boost::filesystem::create_directory(directiory); if (ret && errno == ENOENT) { - std::cerr << "SaveFileToFolder. Error in creating directory `" << dirname << "` !" << std::endl; + std::cerr << "SaveFileToFolder. Error in creating directory `" << directiory << "` !" << std::endl; return ret; } } ret = 0; - std::string filename_out; - - char ElemName[512]; - UINT ElemNameLen; - bool print_progress = true; UINT one_percent = Elems.size() / 50; if (print_progress && one_percent) { @@ -1248,11 +1293,9 @@ int CV8File::SaveFileToFolder(const std::string &dirname) const std::cout << "."; } - elem->GetName(ElemName, &ElemNameLen); + string ElemName = elem->GetName(); - filename_out = dirname; - filename_out += "/"; - filename_out += ElemName; + boost::filesystem::path filename_out(directiory / ElemName); if (!elem->IsV8File) { boost::filesystem::ofstream file_out(filename_out, std::ios_base::binary); @@ -1275,21 +1318,28 @@ int CV8File::SaveFileToFolder(const std::string &dirname) const return ret; } -int CV8Elem::GetName(char *ElemName, UINT *ElemNameLen) const +string CV8Elem::GetName() const { - *ElemNameLen = (HeaderSize - CV8Elem::stElemHeaderBegin::Size()) / 2; - for (UINT j = 0; j < *ElemNameLen * 2; j += 2) - ElemName[j / 2] = pHeader[CV8Elem::stElemHeaderBegin::Size() + j]; + int ElemNameLen = (HeaderSize - CV8Elem::stElemHeaderBegin::Size()) / 2; + string ElemName; + for (int j = 0; j < ElemNameLen * 2; j += 2) { - return 0; + char currentChar = pHeader[CV8Elem::stElemHeaderBegin::Size() + j]; + if (currentChar != '\0') { + ElemName += currentChar; + } + + } + + return ElemName; } -int CV8Elem::SetName(const char *ElemName, UINT ElemNameLen) +int CV8Elem::SetName(const string &ElemName) { UINT stElemHeaderBeginSize = CV8Elem::stElemHeaderBegin::Size(); - for (UINT j = 0; j < ElemNameLen * 2; j += 2, stElemHeaderBeginSize += 2) { + for (UINT j = 0; j < ElemName.size() * 2; j += 2, stElemHeaderBeginSize += 2) { pHeader[stElemHeaderBeginSize] = ElemName[j/2]; pHeader[stElemHeaderBeginSize + 1] = 0; } @@ -1297,6 +1347,22 @@ int CV8Elem::SetName(const char *ElemName, UINT ElemNameLen) return 0; } +void CV8Elem::Dispose() +{ + if (pData != nullptr) { + delete[] pData; + pData = nullptr; + } + + if (pHeader != nullptr) { + delete[] pHeader; + pHeader = nullptr; + } + + IsV8File = false; + HeaderSize = 0; + DataSize = 0; +} int CV8File::LoadFileFromFolder(const std::string &dirname) @@ -1326,7 +1392,7 @@ int CV8File::LoadFileFromFolder(const std::string &dirname) memset(elem.pHeader, 0, elem.HeaderSize); - elem.SetName(name.c_str(), name.size()); + elem.SetName(name); if (boost::filesystem::is_directory(current_file)) { @@ -1361,7 +1427,6 @@ int CV8File::BuildCfFile(const std::string &in_dirname, const std::string &out_f { //filename can't be empty if (!in_dirname.size()) { - fputs("Argument error - Set of `in_dirname' argument \n", stderr); std::cerr << "Argument error - Set of `in_dirname' argument" << std::endl; return SHOW_USAGE; } @@ -1403,10 +1468,7 @@ int CV8File::BuildCfFile(const std::string &in_dirname, const std::string &out_f DWORD cur_block_addr = stFileHeader::Size() + stBlockHeader::Size(); stElemAddr *pTOC; pTOC = new stElemAddr[ElemsNum]; - if (sizeof(stElemAddr) * ElemsNum < V8_DEFAULT_PAGE_SIZE) - cur_block_addr += V8_DEFAULT_PAGE_SIZE; - else - cur_block_addr += stElemAddr::Size() * ElemsNum; + cur_block_addr += MAX(stElemAddr::Size() * ElemsNum, V8_DEFAULT_PAGE_SIZE); boost::filesystem::ofstream file_out(out_filename, std::ios_base::binary); //Открываем выходной файл контейнер на запись @@ -1456,59 +1518,77 @@ int CV8File::BuildCfFile(const std::string &in_dirname, const std::string &out_f memset(pElem.pHeader, 0, pElem.HeaderSize); - pElem.SetName(name.c_str(), name.size()); - if (boost::filesystem::is_directory(current_file)) { + pElem.SetName(name); - pElem.IsV8File = true; + pTOC[ElemNum].elem_header_addr = file_out.tellp(); + SaveBlockData(file_out, pElem.pHeader, pElem.HeaderSize, pElem.HeaderSize); - std::string new_dirname(in_dirname); - new_dirname += "/"; - new_dirname += name; + pTOC[ElemNum].elem_data_addr = file_out.tellp(); + pTOC[ElemNum].fffffff = V8_FF_SIGNATURE; - pElem.UnpackedData.LoadFileFromFolder(new_dirname); + if (boost::filesystem::is_directory(current_file)) { - } else { + pElem.IsV8File = true; - pElem.IsV8File = false; + std::string new_dirname(in_dirname); + new_dirname += "/"; + new_dirname += name; - pElem.DataSize = boost::filesystem::file_size(current_file); - pElem.pData = new char[pElem.DataSize]; + pElem.UnpackedData.LoadFileFromFolder(new_dirname); + pElem.Pack(!dont_deflate); - boost::filesystem::path p_filename(in_dirname); - p_filename /= name; + SaveBlockData(file_out, pElem.pData, pElem.DataSize); - boost::filesystem::ifstream file_in(p_filename, std::ios_base::binary); - file_in.read(reinterpret_cast(pElem.pData), pElem.DataSize); - } + } else { - //Сжимаем данные - pElem.Pack(!dont_deflate); + pElem.IsV8File = false; - //Добавляем элемент в TOC - pTOC[ElemNum].elem_header_addr = cur_block_addr; - cur_block_addr += sizeof(stBlockHeader) + pElem.HeaderSize; - pTOC[ElemNum].elem_data_addr = cur_block_addr; - cur_block_addr += sizeof(stBlockHeader); - if (pElem.DataSize > V8_DEFAULT_PAGE_SIZE) - cur_block_addr += pElem.DataSize; - else - cur_block_addr += V8_DEFAULT_PAGE_SIZE; - pTOC[ElemNum].fffffff = V8_FF_SIGNATURE; - //Записываем элемент в файл - SaveBlockData(file_out, pElem.pHeader, pElem.HeaderSize, pElem.HeaderSize); - SaveBlockData(file_out, pElem.pData, pElem.DataSize); - - //Освобождаем память - delete[] pElem.pData; - pElem.pData = nullptr; - delete[] pElem.pHeader; - pElem.pHeader = nullptr; - pElem.IsV8File = false; - pElem.HeaderSize = 0; - pElem.DataSize = 0; - - ElemNum++; - } + pElem.DataSize = boost::filesystem::file_size(current_file); + + boost::filesystem::path p_filename(in_dirname); + p_filename /= name; + boost::filesystem::ifstream file_in(p_filename, std::ios_base::binary); + + if (pElem.DataSize < SmartUnpackedLimit) { + + pElem.pData = new char[pElem.DataSize]; + + file_in.read(reinterpret_cast(pElem.pData), pElem.DataSize); + + pElem.Pack(!dont_deflate); + + SaveBlockData(file_out, pElem.pData, pElem.DataSize); + + } else { + + if (dont_deflate) { + SaveBlockData(file_out, file_in, pElem.DataSize); + } else { + // Упаковка через промежуточный файл + boost::filesystem::path tmp_file_path = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path(); + + { + boost::filesystem::ofstream tmp_file(tmp_file_path, std::ios_base::binary); + Deflate(file_in, tmp_file); + tmp_file.close(); + } + + { + pElem.DataSize = boost::filesystem::file_size(tmp_file_path); + boost::filesystem::ifstream tmp_file(tmp_file_path, std::ios_base::binary); + SaveBlockData(file_out, tmp_file, pElem.DataSize); + tmp_file.close(); + + boost::filesystem::remove(tmp_file_path); + } + } + } + } + + pElem.Dispose(); + + ElemNum++; + } //Записывем заголовок файла file_out.seekp(0, std::ios_base::beg); @@ -1526,16 +1606,14 @@ int CV8File::BuildCfFile(const std::string &in_dirname, const std::string &out_f int CV8Elem::Pack(bool deflate) { - char *DeflateBuffer = nullptr; - ULONG DeflateSize = 0; - - char *DataBuffer = nullptr; - ULONG DataBufferSize = 0; - int ret = 0; if (!IsV8File) { if (deflate) { + + char *DeflateBuffer = nullptr; + ULONG DeflateSize = 0; + ret = Deflate(pData, &DeflateBuffer, DataSize, &DeflateSize); if (ret) { return ret; @@ -1545,14 +1623,23 @@ int CV8Elem::Pack(bool deflate) pData = new char[DeflateSize]; DataSize = DeflateSize; memcpy(pData, DeflateBuffer, DeflateSize); + + delete [] DeflateBuffer; } } else { + char *DataBuffer = nullptr; + ULONG DataBufferSize = 0; + UnpackedData.GetData(&DataBuffer, &DataBufferSize); + UnpackedData.Dispose(); if (deflate) { + char *DeflateBuffer = nullptr; + ULONG DeflateSize = 0; + ret = Deflate(DataBuffer, &DeflateBuffer, DataBufferSize, &DeflateSize); if (ret) { return ret; @@ -1562,6 +1649,8 @@ int CV8Elem::Pack(bool deflate) DataSize = DeflateSize; memcpy(pData, DeflateBuffer, DeflateSize); + delete [] DeflateBuffer; + } else { pData = new char[DataBufferSize]; @@ -1570,15 +1659,9 @@ int CV8Elem::Pack(bool deflate) } - IsV8File = false; - } + delete [] DataBuffer; - if (DeflateBuffer) { - free(DeflateBuffer); - } - - if (DataBuffer) { - free(DataBuffer); + IsV8File = false; } return 0; @@ -1690,10 +1773,7 @@ int CV8File::GetData(char **DataBuffer, ULONG *DataBufferSize) pCurrentTempElem = pTempElemsAddrs; DWORD cur_block_addr = stFileHeader::Size() + stBlockHeader::Size(); - if (stElemAddr::Size() * ElemsNum < V8_DEFAULT_PAGE_SIZE) - cur_block_addr += V8_DEFAULT_PAGE_SIZE; - else - cur_block_addr += stElemAddr::Size() * ElemsNum; + cur_block_addr += MAX(V8_DEFAULT_PAGE_SIZE, stElemAddr::Size() * ElemsNum); for (auto elem : Elems) { @@ -1748,7 +1828,7 @@ int CV8File::SaveBlockDataToBuffer(char **cur_pos, const char *pBlockData, UINT if (PageSize < BlockDataSize) PageSize = BlockDataSize; - stBlockHeader CurBlockHeader = stBlockHeader::create(BlockDataSize, PageSize, V8_DEFAULT_PAGE_SIZE); + stBlockHeader CurBlockHeader = stBlockHeader::create(BlockDataSize, PageSize, V8_FF_SIGNATURE); memcpy(*cur_pos, (char*)&CurBlockHeader, stBlockHeader::Size()); *cur_pos += stBlockHeader::Size(); diff --git a/src/V8File.h b/src/V8File.h index 9908522..89ce552 100644 --- a/src/V8File.h +++ b/src/V8File.h @@ -1,9 +1,9 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ ///////////////////////////////////////////////////////////////////////////// // // @@ -11,10 +11,10 @@ at http://mozilla.org/MPL/2.0/. // E-mail: disa_da2@mail.ru // // -///////////////////////////////////////////////////////////////////////////// - -/** - 2014-2017 dmpas sergey(dot)batanov(at)dmpas(dot)ru +///////////////////////////////////////////////////////////////////////////// + +/** + 2014-2017 dmpas sergey(dot)batanov(at)dmpas(dot)ru */ // V8File.h: interface for the CV8File class. @@ -33,19 +33,20 @@ at http://mozilla.org/MPL/2.0/. #include #include -#include "zlib.h" -#include - -#include -#include +#include "zlib.h" +#include + +#include +#include #include - -typedef uint32_t UINT; -typedef uint32_t DWORD; -typedef uint32_t ULONG; +#include + +typedef uint32_t UINT; +typedef uint32_t DWORD; +typedef uint32_t ULONG; typedef uint64_t ULONGLONG; - -const size_t V8_DEFAULT_PAGE_SIZE = 512; + +const size_t V8_DEFAULT_PAGE_SIZE = 512; const uint32_t V8_FF_SIGNATURE = 0x7fffffff; #define CHUNK 16384 @@ -85,8 +86,8 @@ class CV8File DWORD next_page_addr; DWORD page_size; DWORD storage_ver; - DWORD reserved; // всегда 0x00000000 ? - + DWORD reserved; // всегда 0x00000000 ? + static const UINT Size() { return 4 + 4 + 4 + 4; @@ -97,12 +98,12 @@ class CV8File { DWORD elem_header_addr; DWORD elem_data_addr; - DWORD fffffff; //всегда 0x7fffffff ? + DWORD fffffff; //всегда 0x7fffffff ? static const UINT Size() { return 4 + 4 + 4; - } + } }; @@ -131,40 +132,65 @@ class CV8File { return 1 + 1 + 8 + 1 + 8 + 1 + 8 + 1 + 1 + 1; }; + + bool IsCorrect() const + { + return EOL_0D == 0x0d + && EOL_0A == 0x0a + && space1 == 0x20 + && space2 == 0x20 + && space3 == 0x20 + && EOL2_0D == 0x0d + && EOL2_0A == 0x0a; + } }; int GetData(char **DataBufer, ULONG *DataBuferSize); int Pack(); int LoadFileFromFolder(const std::string &dirname); - int LoadFile(char *pFileData, ULONG FileData, bool boolInflate = true, bool UnpackWhenNeed = false); - int SaveFileToFolder(const std::string &dirname) const; + int LoadFile(char *pFileData, ULONG FileData, bool boolInflate = true, bool UnpackWhenNeed = false); + int SaveFileToFolder(const boost::filesystem::path &directiory) const; CV8File(); CV8File(char *pFileData, bool boolUndeflate = true); - virtual ~CV8File(); - - CV8File(const CV8File &src); - + virtual ~CV8File(); + + CV8File(const CV8File &src); + void Dispose(); - + static int PackFromFolder(const std::string &dirname, const std::string &filename); static int BuildCfFile(const std::string &dirname, const std::string &filename, bool dont_deflate = false); - static int SaveBlockData(std::basic_ofstream &file_out, const char *pBlockData, UINT BlockDataSize, UINT PageSize = 512); - static int SaveBlockData(std::basic_ofstream &file_out, std::basic_ifstream &file_in, UINT BlockDataSize, UINT PageSize = 512); + static int SaveBlockData(std::basic_ostream &file_out, const char *pBlockData, UINT BlockDataSize, UINT PageSize = 512); + static int SaveBlockData(std::basic_ostream &file_out, std::basic_istream &file_in, UINT BlockDataSize, UINT PageSize = 512); static int UnpackToFolder(const std::string &filename, const std::string &dirname, const std::string &block_name, bool print_progress = false); - static int UnpackToDirectoryNoLoad(const std::string &directory, std::basic_ifstream &file, bool boolInflate = true, bool UnpackWhenNeed = false); - static int Parse(const std::string &filename, const std::string &dirname); + + static int UnpackToDirectoryNoLoad( + const std::string &directory, + std::basic_istream &file, + const std::vector &filter, + bool boolInflate = true, + bool UnpackWhenNeed = false + ); + + static int Parse( + const std::string &filename, + const std::string &dirname, + const std::vector< std::string > &filter + ); + + static int ListFiles(const std::string &filename); static int SaveBlockDataToBuffer(char** Buffer, const char* pBlockData, UINT BlockDataSize, UINT PageSize = 512); - static bool IsV8File(const char *pFileData, ULONG FileDataSize); - static bool IsV8File(std::basic_ifstream &file); - static int ReadBlockData(char *pFileData, stBlockHeader *pBlockHeader, char *&pBlockData, UINT *BlockDataSize = NULL); - static int ReadBlockData(std::basic_ifstream &file, stBlockHeader *pBlockHeader, char *&pBlockData, UINT *BlockDataSize = NULL); - static int ReadBlockData(std::basic_ifstream &file, stBlockHeader *pBlockHeader, std::basic_ofstream &out, UINT *BlockDataSize = NULL); + static bool IsV8File(const char *pFileData, ULONG FileDataSize); + static bool IsV8File(std::basic_istream &file); + static int ReadBlockData(char *pFileData, stBlockHeader *pBlockHeader, char *&pBlockData, UINT *BlockDataSize = NULL); + static int ReadBlockData(std::basic_istream &file, stBlockHeader *pBlockHeader, char *&pBlockData, UINT *BlockDataSize = NULL); + static int ReadBlockData(std::basic_istream &file, stBlockHeader *pBlockHeader, std::basic_ostream &out, UINT *BlockDataSize = NULL); private: stFileHeader FileHeader; - std::vector ElemsAddrs; - + std::vector ElemsAddrs; + std::vector Elems; bool IsDataPacked; }; @@ -184,29 +210,31 @@ class CV8Elem { return 8 + 8 + 4; }; - }; + }; CV8Elem(const CV8Elem &src); CV8Elem(); - ~CV8Elem(); + ~CV8Elem(); - int Pack(bool deflate = true); - int SetName(const char *ElemName, UINT ElemNameLen); - int GetName(char* ElemName, UINT *ElemNameLen) const; + int Pack(bool deflate = true); + int SetName(const std::string &ElemName); + std::string GetName() const; - char *pHeader; // TODO: Утечка памяти + void Dispose(); + + char *pHeader; UINT HeaderSize; - char *pData; // TODO: Утечка памяти + char *pData; UINT DataSize; CV8File UnpackedData; bool IsV8File; - bool NeedUnpack; - -}; + bool NeedUnpack; + +}; + +int Deflate(std::istream &source, std::ostream &dest); +int Inflate(std::istream &source, std::ostream &dest); -int Deflate(std::basic_ifstream &source, std::basic_ofstream &dest); -int Inflate(std::basic_ifstream &source, std::basic_ofstream &dest); - int Deflate(const std::string &in_filename, const std::string &out_filename); int Inflate(const std::string &in_filename, const std::string &out_filename); diff --git a/src/main.cpp b/src/main.cpp index 6afcc67..a59cee3 100755 --- a/src/main.cpp +++ b/src/main.cpp @@ -57,6 +57,8 @@ int usage(vector &argv) cout << " -B[UILD] [-N[OPACK]] in_dirname out_filename" << endl; cout << " -B[UILD] [-N[OPACK]] -L[IST] listfile" << endl; cout << " -L[IST] listfile" << endl; + + cout << " -LISTFILES|-LF in_filename" << endl; cout << " -E[XAMPLE]" << endl; cout << " -BAT" << endl; @@ -98,7 +100,19 @@ int pack(vector &argv) int parse(vector &argv) { - int ret = CV8File::Parse(argv[0], argv[1]); + vector filter; + for (size_t i = 2; i < argv.size(); i++) { + if (!argv[i].empty()) { + filter.push_back(argv[i]); + } + } + int ret = CV8File::Parse(argv[0], argv[1], filter); + return ret; +} + +int list_files(vector &argv) +{ + int ret = CV8File::ListFiles(argv[0]); return ret; } @@ -252,6 +266,10 @@ handler_t get_run_mode(const vector &args, int &arg_base, bool &allow_li return process_list; } + if (cur_mode == "-listfiles" || cur_mode == "-lf") { + return list_files; + } + return nullptr; } @@ -271,7 +289,7 @@ void read_param_file(const char *filename, vector< vector > &list) current_line.push_back(item); } - while (current_line.size() < 3) { + while (current_line.size() < 5) { // Дополним пустыми строками, чтобы избежать лишних проверок current_line.push_back(""); } diff --git a/src/version.h b/src/version.h index 9cc5ca3..37df557 100644 --- a/src/version.h +++ b/src/version.h @@ -1,9 +1,9 @@ /*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ -#define V8P_VERSION "3.0.39" //FIXME +#define V8P_VERSION "3.0.40" //FIXME #define V8P_RIGHT "\n\t2008 Denis Demidov 2008-03-30\n\t2014-2017 Sergey Batanov"