Skip to content

Commit

Permalink
Support for BDO mobile
Browse files Browse the repository at this point in the history
  • Loading branch information
AMGarkin authored Sep 6, 2018
1 parent 5c9c14e commit cb9f4a3
Show file tree
Hide file tree
Showing 7 changed files with 154 additions and 76 deletions.
4 changes: 4 additions & 0 deletions BDOFiles-boost.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include "IceKey.h"
#include "BDO.h"
#include "Utility.h"
#include "zlib.h"


namespace BDO
Expand Down Expand Up @@ -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:
Expand All @@ -75,6 +78,7 @@ namespace BDO
bool bRenameFiles;
bool bOverwriteFiles;
bool bCreatePath;
bool bMobile;
boost::filesystem::path ArchivePath;
};

Expand Down
4 changes: 4 additions & 0 deletions BDOFiles-exp.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include "IceKey.h"
#include "BDO.h"
#include "Utility.h"
#include "zlib.h"


namespace BDO
Expand Down Expand Up @@ -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:
Expand All @@ -75,6 +78,7 @@ namespace BDO
bool bRenameFiles;
bool bOverwriteFiles;
bool bCreatePath;
bool bMobile;
std::experimental::filesystem::path ArchivePath;
};

Expand Down
166 changes: 110 additions & 56 deletions BDOFiles.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,32 +12,33 @@ 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("")
{};


/// ***** BDOFile Class ***** ///

///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);
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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())) {
Expand Down Expand Up @@ -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<char *>(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<char *>(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<char *>(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<uLongf *>(&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<char *>(decrypted), uiOriginalSize);
ofsFile.close();

Expand All @@ -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);

Expand Down Expand Up @@ -506,7 +543,13 @@ void MetaFile::ReadSource(fs::path FileName)

ifsMetaFile.read(reinterpret_cast<char *>(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;
Expand All @@ -528,15 +571,20 @@ void MetaFile::ReadSource(fs::path FileName)
ifsMetaFile.read(reinterpret_cast<char *>(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<char *>(pFileNamesEncrypted), uiFileNamesLength);
if (!this->GetMobile()) {
pFileNamesEncrypted = new uint8_t[uiFileNamesLength];
if (pFileNamesEncrypted == 0) this->exitError(-3);

ifsMetaFile.read(reinterpret_cast<char *>(pFileNamesEncrypted), uiFileNamesLength);
ICEdecrypt(pFileNamesEncrypted, pFileNamesDecrypted, uiFileNamesLength);

ICEdecrypt(pFileNamesEncrypted, pFileNamesDecrypted, uiFileNamesLength);
delete[] pFileNamesEncrypted;
delete[] pFileNamesEncrypted;
} else {
ifsMetaFile.read(reinterpret_cast<char *>(pFileNamesDecrypted), uiFileNamesLength);
}

ptr = pFileNamesDecrypted;
pEnd = ptr + uiFileNamesLength;
Expand Down Expand Up @@ -641,7 +689,13 @@ void PazFile::ReadSource(fs::path FileName)

ifsPazFile.read(reinterpret_cast<char *>(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;
Expand Down
38 changes: 19 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
# Garkin's UnPAZ v1.1
Tool for extracting Black Desert Online archives.

### UnPAZ \<input file\> \<commands\>

*\<input file\>*: name of .meta or .paz file (default: pad00000.meta)
*\<commands\>*:
&nbsp;&nbsp;*-f \<mask\>*: Filter, this argument must be followed by mask. Mask supports wildcards * and ?.
&nbsp;&nbsp;*-o \<path\>*: Output folder, this argument must be followed by path.
&nbsp;&nbsp;*-h*: Print this help text
&nbsp;&nbsp;*-l*: List file names without extracting them
&nbsp;&nbsp;*-n*: No folder structure, extract files directly to output folder.
&nbsp;&nbsp;*-y*: Yes to all questions (creating folders, overwritting files).
&nbsp;&nbsp;*-q*: Quiet (limit printed messages to file names)

### Examples:
&nbsp;&nbsp;UnPAZ pad00001.paz -f *.luac
&nbsp;&nbsp;UnPAZ pad00000.meta -y -f *languagedata_??.txt -o Extracted
&nbsp;&nbsp;UnPAZ D:\Games\BlackDesert\live\Paz\pad00000.meta -l
# Garkin's UnPAZ v1.2
Tool for extracting Black Desert Online archives.

### UnPAZ \<input file\> \<commands\>

*\<input file\>*: name of .meta or .paz file (default: pad00000.meta)
*\<commands\>*:
&nbsp;&nbsp;*-f \<mask\>*: Filter, this argument must be followed by mask. Mask supports wildcards * and ?.
&nbsp;&nbsp;*-o \<path\>*: Output folder, this argument must be followed by path.
&nbsp;&nbsp;*-h*: Print this help text
&nbsp;&nbsp;*-l*: List file names without extracting them
&nbsp;&nbsp;*-n*: No folder structure, extract files directly to output folder.
&nbsp;&nbsp;*-y*: Yes to all questions (creating folders, overwritting files).
&nbsp;&nbsp;*-q*: Quiet (limit printed messages to file names)

### Examples:
&nbsp;&nbsp;UnPAZ pad00001.paz -f *.luac
&nbsp;&nbsp;UnPAZ pad00000.meta -y -f *languagedata_??.txt -o Extracted
&nbsp;&nbsp;UnPAZ D:\Games\BlackDesert\live\Paz\pad00000.meta -l
2 changes: 1 addition & 1 deletion UnPAZ.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 8 additions & 0 deletions build-x64.cmd
Original file line number Diff line number Diff line change
@@ -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
8 changes: 8 additions & 0 deletions build-x86.cmd
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit cb9f4a3

Please sign in to comment.