From cb9f4a35753bdb6a374a9a68a047d6e53c73e80e Mon Sep 17 00:00:00 2001 From: Garkin Date: Thu, 6 Sep 2018 16:25:39 +0200 Subject: [PATCH] Support for BDO mobile --- BDOFiles-boost.h | 4 ++ BDOFiles-exp.h | 4 ++ BDOFiles.cpp | 166 +++++++++++++++++++++++++++++++---------------- README.md | 38 +++++------ UnPAZ.cpp | 2 +- build-x64.cmd | 8 +++ build-x86.cmd | 8 +++ 7 files changed, 154 insertions(+), 76 deletions(-) create mode 100644 build-x64.cmd create mode 100644 build-x86.cmd diff --git a/BDOFiles-boost.h b/BDOFiles-boost.h index e09c532..21e8f03 100644 --- a/BDOFiles-boost.h +++ b/BDOFiles-boost.h @@ -12,6 +12,7 @@ #include "IceKey.h" #include "BDO.h" #include "Utility.h" +#include "zlib.h" namespace BDO @@ -53,6 +54,8 @@ namespace BDO void SetNoFolders(bool bNoFolders); bool GetYesToAll(); void SetYesToAll(bool bYesToAll); + bool GetMobile(); + void SetMobile(bool bMobile); boost::filesystem::path GetArchivePath(); void SetArchivePath(boost::filesystem::path ArchivePath); protected: @@ -75,6 +78,7 @@ namespace BDO bool bRenameFiles; bool bOverwriteFiles; bool bCreatePath; + bool bMobile; boost::filesystem::path ArchivePath; }; diff --git a/BDOFiles-exp.h b/BDOFiles-exp.h index f8d1055..161fdf0 100644 --- a/BDOFiles-exp.h +++ b/BDOFiles-exp.h @@ -12,6 +12,7 @@ #include "IceKey.h" #include "BDO.h" #include "Utility.h" +#include "zlib.h" namespace BDO @@ -53,6 +54,8 @@ namespace BDO void SetNoFolders(bool bNoFolders); bool GetYesToAll(); void SetYesToAll(bool bYesToAll); + bool GetMobile(); + void SetMobile(bool bMobile); std::experimental::filesystem::path GetArchivePath(); void SetArchivePath(std::experimental::filesystem::path ArchivePath); protected: @@ -75,6 +78,7 @@ namespace BDO bool bRenameFiles; bool bOverwriteFiles; bool bCreatePath; + bool bMobile; std::experimental::filesystem::path ArchivePath; }; diff --git a/BDOFiles.cpp b/BDOFiles.cpp index 209b60c..8284093 100644 --- a/BDOFiles.cpp +++ b/BDOFiles.cpp @@ -12,15 +12,15 @@ using namespace BDO; /// ***** FileEntry Structure ***** /// FileEntry::FileEntry() : - uiFileHash(0), - uiFolderNum(0), - uiFileNum(0), - uiPazNum(0), - uiOffset(0), - uiCompressedSize(0), - uiOriginalSize(0), - sFileName(""), - sFilePath("") + uiFileHash(0), + uiFolderNum(0), + uiFileNum(0), + uiPazNum(0), + uiOffset(0), + uiCompressedSize(0), + uiOriginalSize(0), + sFileName(""), + sFilePath("") {}; @@ -28,16 +28,17 @@ FileEntry::FileEntry() : ///constructor BDOFile::BDOFile() : - IceKey(0), - bQuiet(false), - bNoFolders(false), - bYesToAll(false), - bRenameFiles(false), - bOverwriteFiles(false), - bCreatePath(false), - vFilesTable(), - mPazNames(), - ArchivePath() + IceKey(0), + bQuiet(false), + bNoFolders(false), + bYesToAll(false), + bRenameFiles(false), + bOverwriteFiles(false), + bCreatePath(false), + bMobile(false), + vFilesTable(), + mPazNames(), + ArchivePath() { const unsigned char decrypction_key[8] = { 0x51, 0xF3, 0x0F, 0x11, 0x04, 0x24, 0x6A, 0x00 }; /// The Black Desert ICE decryption key this->set(decrypction_key); @@ -105,9 +106,9 @@ uint32_t BDOFile::ExtractFileMask(std::string sFileMask, fs::path OutputPath) if (WildMatch(sFileMask, it->sFilePath)) { fs::path FilePath = OutputPath; - if (this->GetNoFolders()) { + if (this->GetNoFolders()) { FilePath /= it->sFileName; - } else { + } else { FilePath /= it->sFilePath; } @@ -228,6 +229,15 @@ void BDOFile::SetArchivePath(fs::path ArchivePath) this->mPazNames.clear(); } +bool BDOFile::GetMobile() +{ + return this->bMobile; +} + +void BDOFile::SetMobile(bool bMobile) +{ + this->bMobile = bMobile; +} ///protected functions void BDOFile::exitError(int errCode, std::string sDetail) { @@ -320,8 +330,10 @@ fs::path BDOFile::GetPazName(uint32_t uiPazNum) ///Private functions void BDOFile::internalExtractFile(fs::path FilePath, fs::path PazName, uint32_t uiOffset, uint32_t uiCompressedSize, uint32_t uiOriginalSize) { - if (uiCompressedSize % 8 != 0) - this->exitError(-4); + if (!this->GetMobile()) { + if (uiCompressedSize % 8 != 0) + this->exitError(-4); + } ///make sure that output folder exists if (FilePath.has_parent_path() && !fs::exists(FilePath.parent_path())) { @@ -374,34 +386,59 @@ void BDOFile::internalExtractFile(fs::path FilePath, fs::path PazName, uint32_t } ///decrypt data - uint8_t *encrypted = new uint8_t[uiCompressedSize]; - if (encrypted == 0) exitError(-3); uint8_t *decrypted = new uint8_t[uiCompressedSize]; if (decrypted == 0) exitError(-3); ifsPazFile.seekg(uiOffset); - ifsPazFile.read(reinterpret_cast(encrypted), uiCompressedSize); - - this->ICEdecrypt(encrypted, decrypted, uiCompressedSize); - delete[] encrypted; - - ///check if data have header, valid header is 9 bytes long and contains: - ///- ID (unit8_t) = 0x6E for uncompressed data or 0x6F for compressed data - ///- data size (uint32_t) - ///- original file size (unit32_t) - if ((decrypted[0] == 0x6F || decrypted[0] == 0x6E) && uiCompressedSize > 9) { - uint32_t uiSize = 0; - memcpy(&uiSize, decrypted + 1 + 4, 4); ///copy original file size from decrypted data - if (uiSize == uiOriginalSize) { ///We can consider data header as valid. Size in data header is the same as size in .meta/.paz file. + + if (!this->GetMobile()) { + uint8_t *encrypted = new uint8_t[uiCompressedSize]; + if (encrypted == 0) exitError(-3); + + ifsPazFile.read(reinterpret_cast(encrypted), uiCompressedSize); + this->ICEdecrypt(encrypted, decrypted, uiCompressedSize); + + delete[] encrypted; + + ///check if data have header, valid header is 9 bytes long and contains: + ///- ID (unit8_t) = 0x6E for uncompressed data or 0x6F for compressed data + ///- data size (uint32_t) + ///- original file size (unit32_t) + if ((decrypted[0] == 0x6F || decrypted[0] == 0x6E) && uiCompressedSize > 9) { + uint32_t uiSize = 0; + memcpy(&uiSize, decrypted + 1 + 4, 4); ///copy original file size from decrypted data + if (uiSize == uiOriginalSize) { ///We can consider data header as valid. Size in data header is the same as size in .meta/.paz file. + uint8_t *decompressed = new uint8_t[uiOriginalSize]; + if (decompressed == 0) exitError(-3); + + BDO::decompress(decrypted, decompressed); + delete[] decrypted; + decrypted = decompressed; + } + } + } else { + ifsPazFile.read(reinterpret_cast(decrypted), uiCompressedSize); + + if (uiOriginalSize != uiCompressedSize) { uint8_t *decompressed = new uint8_t[uiOriginalSize]; if (decompressed == 0) exitError(-3); - BDO::decompress(decrypted, decompressed); - delete[] decrypted; - decrypted = decompressed; + int result = uncompress(decompressed, reinterpret_cast(&uiOriginalSize), decrypted, uiCompressedSize); + + if (result == Z_OK) { + delete[] decrypted; + decrypted = decompressed; + } else if (result == Z_MEM_ERROR) { + exitError(-5, "zlib - Not enough memory."); + } else if (result == Z_BUF_ERROR) { + exitError(-5, "zlib - Output buffer is too small."); + } else if (result == Z_DATA_ERROR) { + exitError(-5, "zlib - Input data are corrupted or incomplete."); + } } } + ofsFile.write(reinterpret_cast(decrypted), uiOriginalSize); ofsFile.close(); @@ -415,18 +452,18 @@ void BDOFile::internalExtractFile(fs::path FilePath, fs::path PazName, uint32_t /// ***** MetaFile class ***** /// ///constructor MetaFile::MetaFile() : - uiClientVersion(0), - uiFilesCount(0), - uiFileNamesCount(0), - uiFoldersCount(0) + uiClientVersion(0), + uiFilesCount(0), + uiFileNamesCount(0), + uiFoldersCount(0) { } MetaFile::MetaFile(fs::path FileName, bool bQuiet) : - uiClientVersion(0), - uiFilesCount(0), - uiFileNamesCount(0), - uiFoldersCount(0) + uiClientVersion(0), + uiFilesCount(0), + uiFileNamesCount(0), + uiFoldersCount(0) { this->SetQuiet(bQuiet); @@ -506,7 +543,13 @@ void MetaFile::ReadSource(fs::path FileName) ifsMetaFile.read(reinterpret_cast(pFolderNamesEncrypted), uiFolderNamesLength); - this->ICEdecrypt(pFolderNamesEncrypted, pFolderNamesDecrypted, uiFolderNamesLength); + ///test if meta file is from BDO mobile (names are not encrypted and all folder names starts with "res/") + if (pFolderNamesEncrypted[8] == 'r' && pFolderNamesEncrypted[9] == 'e' && pFolderNamesEncrypted[10] == 's') { + this->SetMobile(true); + memcpy(pFolderNamesDecrypted, pFolderNamesEncrypted, uiFolderNamesLength); + } else { + this->ICEdecrypt(pFolderNamesEncrypted, pFolderNamesDecrypted, uiFolderNamesLength); + } delete[] pFolderNamesEncrypted; ptr = pFolderNamesDecrypted; @@ -528,15 +571,20 @@ void MetaFile::ReadSource(fs::path FileName) ifsMetaFile.read(reinterpret_cast(readBuffer), 4); memcpy(&uiFileNamesLength, readBuffer, 4); - pFileNamesEncrypted = new uint8_t[uiFileNamesLength]; - if (pFileNamesEncrypted == 0) this->exitError(-3); pFileNamesDecrypted = new uint8_t[uiFileNamesLength]; if (pFileNamesDecrypted == 0) this->exitError(-3); - ifsMetaFile.read(reinterpret_cast(pFileNamesEncrypted), uiFileNamesLength); + if (!this->GetMobile()) { + pFileNamesEncrypted = new uint8_t[uiFileNamesLength]; + if (pFileNamesEncrypted == 0) this->exitError(-3); + + ifsMetaFile.read(reinterpret_cast(pFileNamesEncrypted), uiFileNamesLength); + ICEdecrypt(pFileNamesEncrypted, pFileNamesDecrypted, uiFileNamesLength); - ICEdecrypt(pFileNamesEncrypted, pFileNamesDecrypted, uiFileNamesLength); - delete[] pFileNamesEncrypted; + delete[] pFileNamesEncrypted; + } else { + ifsMetaFile.read(reinterpret_cast(pFileNamesDecrypted), uiFileNamesLength); + } ptr = pFileNamesDecrypted; pEnd = ptr + uiFileNamesLength; @@ -641,7 +689,13 @@ void PazFile::ReadSource(fs::path FileName) ifsPazFile.read(reinterpret_cast(pNamesEncrypted), uiNamesLength); - this->ICEdecrypt(pNamesEncrypted, pNamesDecrypted, uiNamesLength); + ///test if meta file is from BDO mobile (names are not encrypted and always starts with folder name "res/") + if (pNamesEncrypted[0] == 'r' && pNamesEncrypted[1] == 'e' && pNamesEncrypted[2] == 's') { + this->SetMobile(true); + memcpy(pNamesDecrypted, pNamesEncrypted, uiNamesLength); + } else { + this->ICEdecrypt(pNamesEncrypted, pNamesDecrypted, uiNamesLength); + } delete[] pNamesEncrypted; ptr = pNamesDecrypted; diff --git a/README.md b/README.md index 9b4e646..20ae591 100644 --- a/README.md +++ b/README.md @@ -1,19 +1,19 @@ -# Garkin's UnPAZ v1.1 -Tool for extracting Black Desert Online archives. - -### UnPAZ \ \ - -*\*: name of .meta or .paz file (default: pad00000.meta) -*\*: -  *-f \*: Filter, this argument must be followed by mask. Mask supports wildcards * and ?. -  *-o \*: Output folder, this argument must be followed by path. -  *-h*: Print this help text -  *-l*: List file names without extracting them -  *-n*: No folder structure, extract files directly to output folder. -  *-y*: Yes to all questions (creating folders, overwritting files). -  *-q*: Quiet (limit printed messages to file names) - -### Examples: -  UnPAZ pad00001.paz -f *.luac -  UnPAZ pad00000.meta -y -f *languagedata_??.txt -o Extracted -  UnPAZ D:\Games\BlackDesert\live\Paz\pad00000.meta -l +# Garkin's UnPAZ v1.2 +Tool for extracting Black Desert Online archives. + +### UnPAZ \ \ + +*\*: name of .meta or .paz file (default: pad00000.meta) +*\*: +  *-f \*: Filter, this argument must be followed by mask. Mask supports wildcards * and ?. +  *-o \*: Output folder, this argument must be followed by path. +  *-h*: Print this help text +  *-l*: List file names without extracting them +  *-n*: No folder structure, extract files directly to output folder. +  *-y*: Yes to all questions (creating folders, overwritting files). +  *-q*: Quiet (limit printed messages to file names) + +### Examples: +  UnPAZ pad00001.paz -f *.luac +  UnPAZ pad00000.meta -y -f *languagedata_??.txt -o Extracted +  UnPAZ D:\Games\BlackDesert\live\Paz\pad00000.meta -l diff --git a/UnPAZ.cpp b/UnPAZ.cpp index 705918c..d69b19b 100644 --- a/UnPAZ.cpp +++ b/UnPAZ.cpp @@ -26,7 +26,7 @@ namespace { ///print version header void printVersion() { - cout << "Garkin's UnPAZ v1.1 - tool for extracting Black Desert Online archives.\n"; + cout << "Garkin's UnPAZ v1.2 - tool for extracting Black Desert Online archives.\n"; } ///print help text diff --git a/build-x64.cmd b/build-x64.cmd new file mode 100644 index 0000000..ed9fac5 --- /dev/null +++ b/build-x64.cmd @@ -0,0 +1,8 @@ +@mkdir obj\x64 2>nul +x86_64-w64-mingw32-g++.exe -O3 -c BDO.cpp -o obj/x64/BDO.o +x86_64-w64-mingw32-g++.exe -O3 -c BDOFiles.cpp -o obj/x64/BDOFiles.o -lz +x86_64-w64-mingw32-g++.exe -O3 -c IceKey.cpp -o obj/x64/IceKey.o +x86_64-w64-mingw32-g++.exe -O3 -c UnPAZ.cpp -o obj/x64/UnPAZ.o +x86_64-w64-mingw32-g++.exe -O3 -c Utility.cpp -o obj/x64/Utility.o +@mkdir bin\x64 2>nul +x86_64-w64-mingw32-g++.exe -o bin/x64/UnPAZ.exe obj/x64/BDO.o obj/x64/BDOFiles.o obj/x64/IceKey.o obj/x64/UnPAZ.o obj/x64/Utility.o -static -s -lboost_filesystem-mt -lboost_system-mt -lz diff --git a/build-x86.cmd b/build-x86.cmd new file mode 100644 index 0000000..3376ab9 --- /dev/null +++ b/build-x86.cmd @@ -0,0 +1,8 @@ +@mkdir obj\x86 2>nul +i686-w64-mingw32-g++.exe -O3 -c BDO.cpp -o obj/x86/BDO.o +i686-w64-mingw32-g++.exe -O3 -c BDOFiles.cpp -o obj/x86/BDOFiles.o -lz +i686-w64-mingw32-g++.exe -O3 -c IceKey.cpp -o obj/x86/IceKey.o +i686-w64-mingw32-g++.exe -O3 -c UnPAZ.cpp -o obj/x86/UnPAZ.o +i686-w64-mingw32-g++.exe -O3 -c Utility.cpp -o obj/x86/Utility.o +@mkdir bin\x86 2>nul +i686-w64-mingw32-g++.exe -o bin/x86/UnPAZ.exe obj/x86/BDO.o obj/x86/BDOFiles.o obj/x86/IceKey.o obj/x86/UnPAZ.o obj/x86/Utility.o -static -s -lboost_filesystem-mt -lboost_system-mt -lz