Black Desert Online Modding Tools


Content Creator

What is this:
This tool allows you to put modified files inside the game.


Download Link: Meta Injector
requires: BDO Toolkit

1 - Extract both the MetaInjector and the BDOToolkit zip file to your "PAZ\" folder, which is located inside your installation folder.
(Note: For steam users it's usually under: "C:\Program Files (x86)\Steam\steamapps\common\Black Desert Online\").
2 - Run "BDOToolkit Installer.exe" from the folder you extracted.
3 - Create a folder inside this "PAZ\" folder and name it: files_to_patch (it has to be exactly that)
4 - Put all your modified files in the "files_to_patch" folder.
5 - Run "MetaInjector.exe"
6 - Follow the screen instructions.

Note: You don't have to run "BDOToolkit Installer.exe" everytime you want to patch the game. Just run it once and if your download a new version of the Toolkit, then you install it again.

Do this every time a game update is released:
- Restore you last backup using the tool, before you update the game, otherwise you will get a "corrupted files" message from the launcher.
If that happens, close the launcher, use the "Restore a backup" option, and open the launcher again.

Run meta_injector.exe and select the "Restore backup" option.
The latest ones are the most up to date.

Source Code
If you want to understand how this was coded, download one of the previous versions of the tool here, and the source code is included:

Here's a detailed explanation about the source code:
The only files you need to understand what they do (and not all of it), are these ones:
  • main.c : Handles the menus
  • file_operations.c : Handles things like open files, count files, copy files, check files existence, etc.
  • patcher.c : Does all the magic.
  • meta_explorer.c : Reads the pad00000.meta files and retrieves/decrypt the information of this index file and stores all the information in memory.

Let's start with the file main.c:

main.c : It is responsible only for the user interface (menus), it doesn't do anything but to give the user the options to install or restore a backup. If the user chooses the "Install" option, it calls te "runPatcher()" function, located in the "patcher.c" file.

patcher.c : It's the Meta Injector code itself. If reads the files from the "files_to_patch" folder, and patches the pad00000.meta file and then copies the files to their right location.

void runPatcher():
The most important function in the patcher.c file.
The first thing it does is to call the function:
getAllFiles(char* pathToFiles, char* extFilter, long* filesCount)
This is located in the file "file_operations.c" and what it does is: It opens the folder specified in "pathToFiles" and if it's a file, it stores the file names and the path to that file into a variable that I called "FileBlock* fileNames",
If it's not a file, it goes inside that folder by calling the same function recursively, but with the "pathToFiles" changed to that folder.

Warning: It's is important that you save the path to each file into the "fileNames[ i ].originalPath" variable because we are going to use this information to copy the files to their right location later.


Going back to the "runPatcher()" function again, now we have to get information from the pad00000.meta file. For that we call those two functions, located in the "meta_explorer.c" file:
metaFileInfo = getMetaFileInfo(getLatestBackup());
fileBlocks = fillFileBlocks(metaFileInfo);

getLatestBackup() : returns you lastest pad00000[xxxx-xx-xx].meta.backup file name(Implementation located in "utilities.c"). We have to use the backup because we need a clean meta file to retrieve the right information.
If no backup exists, it simply uses the "pad00000.meta" file.

MetaFileInfo* getMetaFileInfo(char* metaFileName)
First, take a look on how the pad00000.meta file is structured:

Now, look at the beginning of the .meta file:

The first 4 bytes are just the client version, so we skip it by doing :

The next 4 bytes, are how many .PAZ file the game has right now, I store this info in "metaFileInfo->pazCount"

Next, I skip this whole part that are just the PAZ_NUM,PAZ_HASH,PAZ_SIZE by doing:
fseek(metaFile,(metaFileInfo->pazCount * (3 * sizeof(long))),SEEK_CUR);

(3 * sizeof(long)) skips one "line"

So now, if you read the next 4 bytes, it gives you how many of the "File Blocks" you are going to have next.

File Blocks are the most important thing in the program, they are this:

But we are not going to mess with it in this function yet, we just need to know how many of them there are, so we just store the read value in the "metaFileInfo->filesCount" variable.

Now, the file pointer should be right at the beginning of the "File Blocks" part,
so we just save that location under "metaFileInfo->fileBlocksStart" so we can jump right into that point later.
We do that by doing:
metaFileInfo->originalFileBlocksStart = ftell(metaFile);

I also calculated where the "File Blocks" should stop, by doing this simple calculation:
File Blocks End =  File Blocks Start + (Files Count * (size of one registry))
Since each "line" (registry) of the file blocks has 7 fields and each fields is has 4 bytes, and 7*4 = 28, I did:
#define ONE_REGISTRY 28

That's the end of the getMetaFileInfo function.
FileBlock* fillFileBlocks(MetaFileInfo* metaFileInfo)
This will read that "File Blocks" section from the .meta file, and also, decrypt the File Names and Folder Names, and store all that in the "fileBlocks" variable:

Now for this part, I'm going to simplify things a little.
The way I did in my code, was because there were some versions of the game that I couldn't read the first 256000 bytes from the "File Blocks" part, but now this is fixed. But the code works either way.

So all the part from:
printf("\nSearching for hash: %ld\n", multiplemodeldesc_hash);
printf("FILE_BLOCKS_COUNT: %ld (%ld missing files)\n", metaFileInfo->fileBlocksCount,metaFileInfo->filesCount -  metaFileInfo->fileBlocksCount);

You can delete it and use this code instead:
    // Allocates the memory for the File Blocks
    fileBlocks = (FileBlock*)calloc(metaFileInfo->filesCount + 1, sizeof(FileBlock));

    // Initialized the variable that counts how many file blocks we have
    metaFileInfo->fileBlocksCount = 0;

    // This will open you last pad00000.meta.backup file,
    // just in case your current pad00000.meta file is already modified.
    FILE* metaFile = openFile(getLatestBackup(),"rb");

    // Go to where the file blocks start

   // Fill the File Blocks
   for (i = 0; i < metaFileInfo->filesCount; i++)
        // Saves the exact byte where this registry begins (VERY IMPORTANT)
        fileBlocks[i].metaOffset = ftell(metaFile);

        // Reads the next 28 bytes of the meta file and stores it

All we did, was reading this:

Now we are going to decrypt and read the folder names (relative paths to the files if they were extracted from the .PAZ file)

     // Seek to file blocks end (optional)

    // Reads how many folder names we will be reading
    long folders_part_length = 0;
    fread(&folders_part_length, sizeof(long),1,metaFile);

For the next part bellow:
/// ******************* FOLDER NAMES DECRYPTION **********************
I simply used the code posted here:

What you have to know about it is that it reads the meta file all the encrypted bytes from the "Folder names" part and stores them in the "ctext" variable. After that it reads this "ctext" variable, 8 bytes at the time, and it decrypts these 8 bytes, storing the decrypted data into the "ptext" variable.

After everything is done, this moves the "ptext" pointer back to the beginning to the memory region where you have the decrypted folder names.
ptext -= folders_part_length;

At this point, if we read the ptext variable, we are going to have this:
[FOLDER_NUM (4bytes) ][SUBFOLDERS_NUM (4bytes) ]character/'\0'[FOLDER_NUM (4bytes) ][SUBFOLDERS_NUM (4bytes) ] character/texture/'\0' ....

And we actually want:
folderNames[0] = "character/";
folderNames[1] = "character/texture/";

So, for the next part
We are basically puting each "folder name" into each position of the array "folderNamesArray".

Like, we are skipping the first 2 numbers:
[FOLDER_NUM (4bytes) ][SUBFOLDERS_NUM (4bytes) ]
for (j = (2 * sizeof(long)) /* Skips the first 2 numbers */; j < folders_part_length; j++)
     j += (2 * sizeof(long)); /* Skips the first 2 numbers */

Reading the string until we find a '\0', and storing it into the folderNamesArray:
        // If it's not a \0
        if (ptext[j] != 0)
        else // If the \0 is found
                // Store the folder name
Bellow this comment:
 // Assigns the right File Name from the fileNameArray to the right File Block, based on the File Num
We basically copy the content of each "folderNamesArray", to the "fileBlocks" structure
(We have to do this because the order from "folderNamesArray" is different from what we read in the "File Blocks" section)

The next thing:
/// ******************* FILE NAMES DECRYPTION **********************
It's the same thing. The only difference is that the "File Names" section doesn't have the "FOLDER_NUM" and "SUBFOLDERS_COUNT" numbers in front of each file, so we don't have to skip them every time.

In the end I do this:
But it's just to make searching in this fileBlocks faster, it's not really necessary.

This is the end of the fillFileBlocks() function

Going back to the "runPatcher()" again:
After you have your MetaFileInfo and your FileBlocks filled, it's time to do what Meta Injector actually does.

What we are going to do now, is to look for each file name we discovered in the "files_to_patch" folder, into our "FileBlock* fileBlocks" structure, and if we have a matching name, we are going to copy some information from that "fileBlock[ i ]" that we don't have in out "filesToPatch[ j ]" that is going to be relevant in the future. And also set the variable "needPatch" from that file in the "filesToPatch" array, to 1.

This "needPatch" flag will be used to check if we need to patch this file block in the meta file or not.

Just like before, the current code in the source code, is a little more complicated than it needs to be for your application, so from the lines bellow:
 printf("\nSearching for files in the meta file...");

Delete the code in between and use this code instead:
   // Searches for the files from the "files_to_patch" folder in the "fileBlocks"
    for (j = 0; j < filesToPatchCount; j++)  // For each file from the "filesToPatch" array
       // Sequential search through all File Blocks
        for (i = 0; i < metaFileInfo->fileBlocksCount; i++)
            // If they have the same file name
            if (strcmpi(fileBlocks[i].fileName,filesToPatch[j].fileName) == 0)
                // Time to get some relevant information from the matched file block:

                // This will be used to patch the meta file
                filesToPatch[j].metaOffset = fileBlocks[i].metaOffset;

                // This will be used to copy the files to where they are suposed to go
                filesToPatch[j].folderName = fileBlocks[i].folderName;
                filesToPatch[j].originalPath = fileBlocks[i].originalPath;

                // Indicates this file block will need to be patched in the meta file
                filesToPatch[j].needPatch = 1;

                // Breaks from the for loop that goes though the file blocks,
                // since we've already found a match for this fileToPatch[j]

Next thing, we call:
copyFilesBack(filesToPatch, filesToPatchCount);
Again, you don't need to do what I did in my code, there is a simple way to do what this function does, and I'm going to teach you right now:

You have to copy the files from the "files_to_patch" folder, in wherever subfolder they are (remember this information was stored in the "originalPath" variable), to the path the game will look from them,
which consist in "Black Desert Installation Folder" + filesToPatch[ i ].folderName

So for example. Let's say you installed your game into:
"C:\Program Files (x86)\Black Desert Online\"

(In my code, I get the current working dir, which gives me the path to the meta_injector.exe, and I remove the "\Paz" at the end of the path, which gives me the exact game path.)

and let's say you have this in your filesToPatch[0]:
fileName = ""
folderName = "character/texture/"
originalPath = "files_to_patch\"

What you are going to do is to make a way to copy "" from ""C:\Program Files (x86)\Black Desert Online\Paz\files_to_patch\" to ""C:\Program Files (x86)\Black Desert Online\character\texture\"

In a sort of code way, you would have to do this:
void copyFilesBack(FileBlock* filesToPatch, int filesToPatchCount)
    char* bdoRootFolder = "C:/Program Files (x86)/Black Desert Online/";
    int i = 0;
    for (i = 0; i < filesToPatchCount; i++)
        if (filesToPatch[i].needPatch)
            copy filesToPatch[i].fileName from filesToPatch[i].originalPath to bdoRootFolder + filesToPatch[i].folderName

After that we call:
 patchMetaFile(filesToPatch, filesToPatchCount, menu1ChosenOption);

Again, here's a simplified version of it:
void patchMetaFile(FileBlock* filesToPatch, int filesToPatchCount)
    int i = 0;

    // This is going to be used to "break" the index file
    long random_folder_num = 1;
    long random_file_num = 60556;

    // Opens the meta file
    FILE* metaFile = fopen("pad00000.meta","rb+");

    // For each file to patch
    for (i = 0; i < filesToPatchCount; i++)
        // Check if it's marked to patch
            // Goes to the right where the file block starts, and skips the first number (the hash)
            fseek(metaFile,filesToPatch[i].metaOffset + sizeof(long), SEEK_SET);

            // Overrites the folder num and file num written there, to random values
            fwrite(&random_folder_num, sizeof(long),1,metaFile);
            fwrite(&random_file_num, sizeof(long),1,metaFile);
Let me explain what we just did. The game, when it's loading a file, it goes to the pad00000.meta file and it reads the "File Blocks" part and all the other things we did for the "meta_explorer.c" part. To find a file, the game looks for a combination of "file number" and "folder number" so it can know in which position of the arrays "folderNames" and "filesNames" it need to look into. (just like we did in the meta explorer).

If you put some absurd values in there, the game will not be able to tell in which "PAD0xxxx.PAZ" the file it needs is located, so it searches for the file, in the game's root folder (That's why we copied the files there before).

So for the random values, we chose 1 for the "folder num" and "60556" for the file num. It could be anything, really, as long as you are sure that there is no combination of folderNames[random_folder_num] and fileNames[random_file_num], that when it reads it, it gives the right folder name and file name for the file you are looking for.
(If fact, if you simply increased one of the original numbers by one, it would already do the trick, because it wouldn't find a match for the file)
And there you go!

This is all that my program does, step by step. Only the important parts.

Send me a PM if you need any further explanation on anything.


    22.8 KB · Views: 2,507
Last edited:


Content Creator
PAZ Browser
With this tool, you can browse ALL the folders and extract ALL the files from the game you can't normally access.

The game has those files compressed, inside the files named "PAD0xxxx.PAZ", located in your "Paz\" folder.
This tool allows you to view what's inside them and extract what you want.

It also has a 3D Model Viewer so you can preview all the game's 3D models.

(You can only view them, to edit them, see
Black Desert Online 3D Model Export / Import Tool down bellow in this post)

Download Link: PAZ Browser

(Source code inside the .zip file, under the folder "source_codes")


1 - Extract the zip file to your "PAZ\" folder, which is located inside your installation folder.
(Note: For steam users it's usually under: "C:\Program Files (x86)\Steam\steamapps\common\Black Desert Online\").
2 - Run "paz_browser.exe" inside that folder.

Browse ALL the folders and ALL the files of the game and choose exactly the files you want to extract:



You can find a specific file you want to extract without having to go through all the folders to find it.
The search allows you to search multiple files at once, as well, search by extension, and even search all the files that has a certain pattern.

Here's some of the features of the search:
For multiple files, separate them by commas.

If you want to list all the files from from an extension, do it like this:
    *.xml,*dds,*pac - Will give you all the files with the extension .xml or .dds or .pac

If you want to list only the files that are in a specific folder, do this:
     character/*.xml  This will give you all the files .xml located in the folder "character/"

You can search for parts of the file name, doing this:
     *_00_ub_0001.pac  This will give you all the files that ends with "_00_ub_0001.pac"

You can also do this to find all the armor parts:
     phw_00_*_0001.pac  This will give you: phw_00_ub_0001.pac,phw_00_lb_0001.pac,phw_00_hand_0001.pac
     (Basically the character * makes the program ignore what's between the words it separates)

Extract the whole game


  • It extracts 100% of the game files.
  • It displays how many files were already extracted, and what percentage of the extraction is already completed.
Real Outfit and Armor Names Listing

Press "N" to toggle between the "Real Names" and the "File Names" when browsing armor files.

This feature uses a .txt file located in "Paz\patcher_resources\" and it's called "real_names.txt":


  • The left side accepts the same things as the "search" in this tool.
  • You can add more into this file, obeying the same pattern and the tool will automatically recognize it the next time you launch it.
  • # is interpreted as a comment and the entire line is ignored
I didn't check if all the file names are correct. If you find any mistake, feel free to post them here and I can fix it in a future release.

Also feel free to post here, updated versions of this file made by you.
As it is written in the program, here's what each color represents:
When not browsing the Outfit and Armors:

  • Green: It's a folder
  • White: It's a file

When browsing the Outfit and Armors:
  • Magenta: Cash-shop Item
  • Blue: Non-Cash-Shop Item
  • Green: Class-Exclusive Item

Yellow: If you have at least 2 backups in your Paz folder, the tool compares the files that are present in your previous backup, with the ones that are present in your game right now. The new files that were added in the newest update, are shown as Yellow:

File Extraction to Folder with Meaningful Names


Now every time you extract a file that has a "Real Name", the folder it's extracted uses that name, as long as which class it belongs. (See picture above for more details)

Change the order the files are displayed


Now to make it easier to find the files you want, you can sort them by:

  • File Name
  • Real Name
  • Most Recent
  • Extension
And choose if you want them sorted in a "ascendant" order, or a "descendant" order.

Sorting by "Most Recent" simply sorts them by "File Number" (The order they were added in the game by the developers, at least in theory). So using "Most Recent - Descendant" will give you better chances to find the file from the newest outfit for example.

Multiple File Selection Made it Easy


Do you want to select multiple files like you are holding "Shift" if you were using a File Explorer? You can do that now by using the key "S" to select both the current file the cursor is pointing and the file bellow.
If you selected more that you wanted by any chance, you can undo that selection by pressing "W".

Extract All The Outfit Parts At Once


Have you ever wanted to extract all the parts of the armor at once (Upperbody, Lowerbody, Shoulder, etc)?
Well, now you can.
Every time you are in the "9_upperbody" folder of any class, when you try to extract one or more files, after prompting where do you want to extract the files to, it asks you if you want to do just that.
If you press 'y' or 'Y' it will extract not only the _ub_ file, but also all the other that are from the same outfit your selected, and then, when it's done, it will open the folder where extracted everything.

Black Desert Online 3D Model Export / Import Tool

Thanks to IndigoDesert now it's possible to export .pac files open them on any 3D Editor like blender, edit them, and then re-import them back to the game.

The process is very simple, and I'm going to try to make the explanation as simple as possible with a lot of images to guide you through the process.

Here we go:
  1. Download Paz Browser and extract ALL THE FILES from the .zip file to: "Black Desert Online\Paz\"
  2. Open your "Paz" folder and run "paz_browser.exe"
  3. Find the .pac file you want to edit and press ENTER to extract and preview the file (Don't close the window that open with the extracted files)

    For the nude body models, using the menus, go to "Your class->Body Mesh":

    Here's the list of the nude body models files of each class:
    • Sorceress : phw_00_nude_0001_noalpha.pac
    • Ranger : pew_00_nude_0001_noalpha.pac
    • Tamer : pbw_00_nude_0001.pac
    • Valkyrie : pvw_00_nude_0001.pac
    • Valkyrie : pvw_00_nude_0001.pac
    • Witch : pww_00_nude_0001.pac
    • Kunoichi : pnw_00_nude_0001.pac
    • Dark Knight : pdw_00_nude_0001.pac
    • Plum(Maehwa) : pkww_00_nude_0002.pac
    • Warrior : phm_00_nude_0001.pac
    • Berserker : pgm_00_nude_0001.pac
    • Blader(Musa) : pkm_00_nude_0001.pac
    • Wizard : pwm_00_nude_0001.pac
    • Ninja : pnm_00_nude_0001.pac
  4. Download and extract all the content to the same folder that contains the .pac file (The folder the program opened after extracting the .pac file)
  5. Right click "pac_to_dae.bat" and then click "Edit"
  6. Replace "PAC_FILE_NAME.pac" with the name of the .pac file you are trying to extract. Also, replace "CLASSPREFIX" with the first 3 letters of the pac file you are converting:
    E.g: If the pac file name is: phw_00_nude_0001_noalpha.pac, replace "CLASSPREFIX_01.pab" with "phw_01.pab"

  7. Save the file and run "pac_to_dae.bat". 3 new files will be created in this folder:
    - Each .pac file has 3 "Level of Detail" (LOD) files.
    - When you convert it to .dae, it separates it in 3 different files:
    • lod0 is the one with more details
    • lod3 is the one with less details.

  8. Open up Blender or any other 3D Editing software, and go to "File->Import->Collada (default) (.dae)" and find your .dae files.

  9. When you are done editing, go to "File->Export->Collada (default) (.dae)" and replace the original file.

    WARNING: I've been told that Blender doesn't export DAE files properly. You must use a program like Maya or 3DSmax.
    If you use blender, it is possible you get the error: "Missing arrays!"

  10. Now go back to your folder, right click "dae_to_pac.bat" and click on "Edit".
  11. Do the same thing you did before, but replace "DAE_FILE_NAME.dae" with the ".dae" file name you just edited and exported, "PAC_FILE_NAME.pac" with the original .pac file name, and "CLASSPREFIX" again with the first 3 letters of the .pac
    Note: If you want all the "Level of Detail" to use the same mesh that you just edited, add "-replaceAllLOD" before the "-r"
  12. Run "dae_to_pac.bat"
  13. Now download Meta Injector and extract all the files to: "Black Desert Online\Paz\"
  14. Place the .pac file you just created into "Black Desert Online\Paz\files_to_patch\"
  15. Run "meta_injector.exe"
  16. Choose "Run Injector" and "Let the program decide where they should go."
If you make new modifications, you have to go through the whole process again, including injecting it with the Meta Injector.


Here you are going to find old things that were posted in this thread. All of them were already incorporated in one of my tools' latest versions, but if by any chance, you want to download some of the old stuff, here they are.
You are going to find here stuff like the "Black Desert Online File Extractor" , "Hashes Generator for Meta Injector" and Ray Wings's "resorep_hasher"
Black Desert Online File Extractor
View attachment 56142
A tool that uses quickbms to extract all files from your game

Download Link:

Source Code:


1 - Extract this zip file to your PAZ folder.
2 - Run "File Extractor.exe"
3 - Use the options on the screen to do what you want.

How to find the texture from an armor
- Download: Black Desert Resorepless Mod
- Extract into your "PAZ" folder.
- Run "resorepless.exe"
- Choose option "Tools-> Get Textures from a file"

Hashes Generator for Meta Injector
Sometimes the tool won't find the new armor just released for the game, to fix that, you need to generate the file "hashes.txt" again.

Download the Hashes Generator for Meta Injector, and follow the instructions inside the .zip.
Files "Not Found"
There are around 2% of game files (8000 files) that can't be patched with this tool.
If you get this message in the end of the program:
View attachment 55448
If it's a new armor you are trying to patch, try using the Hashes Generator. Otherwise unfortunately there is nothing it can be done for those files and they will not be recognized by the game.
All files must have their original name.
This means that if you use Resorep texture names like: won't work.You need to use the original name of the texture like : do that, use this tool created by Ray Wing:
Here is a small tool that can rename your Resorep modded files to original names:
  1. Extract textures from your game's PAZ files.
  2. Unpack somewhere.
  3. Run runme.bat
  4. Choose folder with textures extracted from PAZ.
  5. Choose folder with your Resorep modded textures.
  6. Wait for a while.
If you do not want to extract textures from your game's PAZ files, I also attached txt file with Resorep texture names to original names mapping. (It only includes character textures. If you need same mapping for other textures - let me know.)

Also, if you want to find out a texture name, use my:
Character Textures Catalog
Contais jpegs from all the textures in the "caracter/texture" folder in the game. Good to find out the original name of some textures
Download link:
Note1: If you are going to manually move the files, make sure the files are inside their original folders
For example: file should be under​
...\Black Desert Online\PAZ\files_to_patch\character\texture\
so when you move or copy, it should go to this path:
...\Black Desert Online\character\texture\

(This doesn't matter if you chose options 1 or 2 of the program though, the program does that for you)
PAZ Files Browser
With this tool, you can preview 3D files from the game without having to open or extract the whole game.
This way you can easily find the corresponding file name for an armor that you want to mod, using my other tools.

Notice: This tool only allows you to view the file.
3D Model Exporting and Importing is found further down bellow this page.


The tool uses quickbms to extract the files and 3D Object Coverter to open them.
Source code here:

Download: Black Desert Online - Paz Files Browser

- Extract ALL files to your "PAZ" folder
- Run "paz_browser.exe"
- Use the arrow keys and the ENTER key to navigate through the menu


Important: To visualize the model with textures, click on this icon:


Known issues:
-If you encounter this message:


- Download this: and extract all the files to "Black Desert Online\Paz\patcher_resources\3d_converter\"

- Every file your preview is extracted, both .pac file and texture files under "Black Desert Online\Paz\patcher_resources\extracted_files\".



6---Rename.gif 7---run.gif


    1.9 MB · Views: 1,010
    617.2 KB · Views: 2,973
    618.2 KB · Views: 1,830
  • src - File Extractor
    13.8 KB · Views: 409
Last edited:


Content Creator
Modders Stuff
This section is reserved to gather all the information I was able to discover so far about the files my tools uses from the game, as well some useful small programs that I made.

The META File
The pad00000.meta file, located in your PAZ folder, it's an index file. It tells the game how many .PAZ files there are, which files are in which .PAZ, and their offset, size, etc. When the game needs to load any file, this is where it looks it up to find out where it is.

The file names and folder names in this files are encrypted using a ICE encryption key (can be found way down below), and the actual files, inside the .PAZ are compressed with a specific algorithm for Black Desert (in quickbms it's the compression algorithm 85) and it's also encrypted with the same ICE key.

The rest are basically only integer numbers, and you can read their value by reading blocks of 4 bytes at the time, they are not encrypted or anything.

This is how the pad00000.meta file file is structured:
Notice: The numbers displayed in this image have changed a lot, but the structure of the file is still the same

The most important thing you should focus, is the structure I called "File Blocks", this is what tells most of the information about the files. It has these fields:

HASH: A Unique number that identifies the file, each file has it's own hash number and you will NEVER find the same number in this file twice. (Unique)

FOLDER_NUM: Each folder of the game is assigned a number to it, this is used if you have an array of folder names for example, and you use this number to find the right position in the array, corresponding to the folder we are looking for.

FILE_NUM: Same thing as the folder num. Each file has a number, starting from 0 and ending at the number you read in the PAZ_COUNT variable in the beginning of the meta file.

PAZ_NUM: The number of the .PAZ this file is located. For example, if this number is 1, the file is located in your file PAD00001.PAZ.

OFFSET: First byte in the .PAZ which the file is stored

ZSIZE: The size of the file when it's still compressed, it's how many bytes you have to read in the .PAZ file in order to extract the full compressed file.

SIZE: The size the file will have, once uncompressed.

This is how the pad00000.meta file looks like if you open it with a hex edit program:

This is a simplified version of a meta file, with only 3 .PAZ files, 3 folders and 4 files. This is the basic structure of the meta file. The official meta file follows the same pattern, it just has more files in it ( a lot more).

Before we proceed, this is a few things we need to clarify:
- Each BIG Block represents 4 bytes.
- Each small block represents one byte.
- Red blocks with white background means this is encrypted and they will not appear like this, unless you decrypt those bytes.
- DUMMY is the client version

This is the same file as the first image, but with all the numbers converted from hex to dec:

Now let's understand how the game knows which file to load:

Let's pick the first FILE_HASH: 631490897:

- We can see it belongs to a file, which folder num is 2
- When we search for folder num = 2 in the "folders_part" of the file, we find that the name of the folder is "character/" ,so the file is in the "character" folder".
- next, we know the file number is 0, so the game will look in the "file names part" and when it finds the first '\0', if will know that it's the end of the file name we are looking for. (If file num was == 2, we would have to count 2 '\0', and everything that is between the 2nd and the 3rd '\0' is the file name)

So we discovered that the file with the hash 631490897 is "multiplemodeldesc.xml" and it's located in "character/"

One thing you should be aware of, when converting from hex to dec.
For example, the FILE_HASH we just used: 631490897
When you convert it to hex, you get this:

But when you look at the meta file, you are not going to find 25 A3 C9 51, instead you need to find 51 C9 A3 25

So everytime you convert to hex, remember to change the order in blocks of 2.

Warning, don't do this mistake:
25 A3 C9 51 -> 15 9C 3A 52
25 A3 C9 51 - > 51 C9 A3 25

PAZ File
The .PAZ files are all named like this: PAD0xxxx.PAZ and they contain the actual files of the game, also compressed and encrypted, as usual.

This is how a .PAZ file is structured:

- Light-red colored means encrypted.


Bonus: ICE Decryption for Black Desert Online in C:
(Credits to Miau Lightouch)

// ice cipher source:
#include "ice.h"
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

int main (){

    FILE *fp, *fout;

    int count = 7730712; // copy from folder/file name size
    int count2 = 0;
    fp = fopen("filename.dat","rb"); // encrypted filename block (I dump to a file for test.)
    fout = fopen("output.dat", "wb"); // the file to write out the decrypted text to file.
    uint8_t *ctext = (uint8_t *) malloc(count); // alloc to meet our need.
    uint8_t *ptext = (uint8_t *) malloc(count);

    if (fp==NULL) {
        perror ("Error opening file");
    else {
        // count2 is the real bytes count that program read out, but should be same as "count".
        count2 = fread(ctext, 1, count, fp);
    printf ("Total number of bytes read: %d\n", count2);

    const uint8_t *s = "\x51\xF3\x0F\x11\x04\x24\x6A\x00"; // it's a magic, you know.

    ICE_KEY *ik = ice_key_create(0); // init the key
    ice_key_set(ik, s); // set the key

    long cuts = count2/8;
    while (cuts--){
        ice_key_decrypt(ik, ctext, ptext); // key, in (encrypted text), out (decrypted text)
        ctext +=8;
        ptext +=8;

    ptext -= count2; // reset the pointer back to the begining.

    fwrite(ptext, 1, count, fout);

Quickbms Script Updated and Commented
This is a modified version of the original script found at
The problem with that old script is that sometimes it produced some corrupted files, when extracted.
Thanks to Garkin whith this script modification, we can extract all files and not having then corrupted.

I commented the script line by line so you can understand better what's going on.

Remember, if you have any doubt on why we are doing this, please refer to this image:
# Black Desert (script 0.2.4) modified by Garkin
#  My modifications are based on BlackFire's modified script:
#  Original script:
# If you are using Notepad++, change the Language to "Visual Basic" to get a nice color code

quickbmsver "0.7.4"

# Defines the compression type that will be used, when extracting a file
comtype blackdesert

# Sets the encryption key we are going to use later
set BDO_ICE_KEY binary "\x51\xF3\x0F\x11\x04\x24\x6A\x00"
encryption ice BDO_ICE_KEY

# gets the extension of the opened file
get EXT extension

#If the file extension is .meta
if EXT == "meta"
# pre-allocation, improves speed (doesn't matter if the values are bigger)
   putarray 0 0x4000 ""   # Array that is going to store the PAZs names
   putarray 1 0x2000 ""   # Array that is going to store the folder names
   putarray 2 0x80000 ""  # Array that is going to store the file names

   # Reads the first 4 bytes (long) of the meta file and stores it in the variable VERSION. (In C: fread(&version,sizeof(long),1,metaFile);)
   get VERSION long
   # Reads how many PAZ files your game has. (In C: fread(&pPAZCount,sizeof(long),1,metaFile);)
   get pPAZCount long

   # Paz files informations
   for i = 0 < pPAZCount
       get PAZ_NUM long
       get HASH long
       get PAZ_SIZE long

      # Using the number stored in PAZ_NUM with 5 digits,creates a string that will hold the complete .PAZ file name (E.g.:PAD00001.PAZ) (In C: sprintf(paz_name,"PAD%.5d.PAZ",paz_num);)
       string PAZ_NAME p= "PAD%05d.PAZ" PAZ_NUM
      # Stores in the array 0 in the position [PAZ_NUM], the string we just created (In C: array0[paz_num] = paz_name;)
       putarray 0 PAZ_NUM PAZ_NAME
   next i

   # Reads how many files your game has. (In C: fread(&files_count,sizeof(long),1,metaFile)
   get FILES_COUNT long
   # Saves the current position in the file in a variable. This position is the beginning of the blocks that have the following format: HASH|FOLDER_NUM|FILE_NUM|PAZ_NUM|OFFSET|ZSIZE|SIZE # (In C: long file_blocks_start = ftell(metafile);)
   # Calculates the position which is where the file blocks ended (each file block has 28 bytes (7 * sizeof(long)) (In C: long file_blocks_end = (current pos - 7 * sizeof(long));)
   # Moves the file pointer to the end of the File Blocks, skipping the whole File Blocks section for now

   # Gets the total length of the strings that are comming next, that are the folder names strings (In C: fread(&folder_names_total_length,sizeof(long),1,metaFile);)
   # Saves the position where the folder names strings start (In C: long folder_names_start = ftell(fp);)
   # Saves all the next "FOLDER_NAMES_TOTAL_LENGTH" bytes, starting from the offset "FOLDER_NAMES_START" in a temporary memory called MEMORY_FILE (In C: fread(memory_file,1,folder_names_total_length,metaFile);)

   # Calculates which byte the folder names strings end (In C: long folder_names_end = folder_names_start + folders_name_total_length;)
   # Goes to that position (In C: fseek(metaFile,folder_names_end,SEEK_SET);)

   # Gets the total length of the strings that are comming next, that are the file names strings (In C: fread(&file_names_total_length,sizeof(long),1,metaFile);)
   # Saves the position where the file names strings start (In C: long file_names_start = ftell(fp);)

   # Saves all the next "FILE_NAMES_TOTAL_LENGTH" bytes, starting from the offset "FILE_NAMES_START" in a temporary memory called MEMORY_FILE2 (In C: fread(memory_file2,1,file_names_total_length,metaFile);)

   # Don't know why, but it ignores the last 8 bytes of the folder names string (2 irrelevent longs, maybe?)

   # Reads the string which has all the folder names, assigning each folder name to a different position in the array 1 (folders array)
   math i = 0
       get INDEX_NUM long MEMORY_FILE        # Reads from the MEMORY_FILE the the index number of the current folder
       get SUB_FOLDERS long MEMORY_FILE        # Reads from the MEMORY_FILE the number of subfolder of the current folder
       get FOLDER_NAME string MEMORY_FILE    # Reads from the MEMORY_FILE a folder name, as string, until a '\0' is found. E.g: "character/"

      # If no name was read, means that we reached the end of the folder names
       if FOLDER_NAME == ""

      # Stores the folder name, in the position i of the array 1 (folders array) (In C: array1[i] = folder_name;)
       putarray 1 i FOLDER_NAME
      # Updates TMP with the current position we are going to read now in MEMORY_FILE, this is just so the "for" condition stops when we reach "FOLDER_NAMES_TOTAL_LENGTH"
       savepos TMP MEMORY_FILE
   next i

   math i = 0
   # Reads the string which has all the file names, assigning each file name to a different position in the array 2 (files array)
      # Reads from the MEMORY_FILE a file name, as string, until a '\0' is found.
       get FILE_NAME string MEMORY_FILE2
      # If no name was read, means that we reached the end of the file names
       if FILE_NAME == ""
      # Stores the folder name, in the position i of the array 1 (folders array) (In C: array1[i] = file_name;)
       putarray 2 i FILE_NAME
      # Updates TMP with the current position we are going to read now in MEMORY_FILE
       savepos TMP MEMORY_FILE2
   next i
   # Now we are going to extract the files, combining the information we find in the file blocks, with the arrays filled with the file and folder names

   # Goes back to the beginning of the file blocks
   # For each File Block
   for i = 0 < FILES_COUNT
       get HASH long        # Gets the unique indentifier of this file
       get FOLDER_NUM long  # Gets the index in the array 1 (folders array) which has the folder name from this file
       get FILE_NUM long    # Gets the index in the array 2 (files array) which has the file name from this file
       get PAZ_NUM long        # Gets the number of the .PAZ file that the file is located
       get OFFSET long        # Gets the offset inside the .PAZ file specified which the file starts
       get ZSIZE long        # Gets the compressed size of the file
       get SIZE long        # Gets the uncompressed size of the file

      # Gets the PAZ name at the position [PAZ_NUM] of the array 0 (PAZ names array) and stores is in the PAZ_NAME variable (In C: paz_name     = array0[paz_num])
       getarray PAZ_NAME 0 PAZ_NUM
      # Gets the folder name at the position [FOLDER_NUM] of the array 1 (folders array) and stores is in the FOLDER_NAME variable (In C: folder_name = array1[paz_num];)
       getarray FOLDER_NAME 1 FOLDER_NUM
      # Gets the file name at the position [FILE_NUM] of the array 1 (folders array) and stores is in the FILE_NAME variable (In C: file_name     = array2[paz_num];)
       getarray FILE_NAME 2 FILE_NUM
      # Creates a string and concatenates folder name with the file name strings, so we get the full path to the file. Eg: "character/multiplemodeldesc.xml"
       string FILE_PATH = FOLDER_NAME
       string FILE_PATH += FILE_NAME

      # Opens the .PAZ file specified in the PAZ_NAME variable
       open FDSE PAZ_NAME 1

      # If uncompressed size is greater than compressed size, it means that the file is compressed, so we use "clog" which uncompresses and extracts the file
       if SIZE > ZSIZE
          # FILE_PATH: The path with the file name in the end, where the file will be extracted. Example: character/multiplemodeldesc.xml
          # OFFSET   : The byte in this .PAZ file which the compressed encrypted file starts
          # ZSIZE    : The size of the file should be when still compressed
          # SIZE     : The the file should be, when decompressed
           # 1        : Number of the file associated to the archive
      # If the uncompressed size is 0 (Yes there are files with 0 size)
       elseif SIZE == 0
          # Extract the file without decompressing it, and it will have 0 bytes
           log FILE_PATH 0 0
      # If uncompressed smaller than the compressed size
          # Extracts the file temporaryly in memory so we can do a check first, without decompressing it
           log MEMORY_FILE3 OFFSET ZSIZE 1
          # Gets the first byte from the extracted file to do a check
           get FLAGS byte MEMORY_FILE3
              #Even if decompression algorithm supports header where is size stored as byte, this method is not used in .paz files,
            #so it is safe to omit this method. Valid IDs for method where size is stored as long are 0x6E for uncompressed data
            #and 0x6F for compressed data. Also we have to make sure that data contains at least whole header (9 bytes)
            if (FLAGS == 0x6E || FLAGS == 0x6F) && ZSIZE > 9
                get DUMMY long MEMORY_FILE3 #read compressed data size, I have called variable DUMMY because we don't use it
                get SIZE2 long MEMORY_FILE3 #read decompressed data size
          encryption "" ""                 #turn off encryption because data in MEMORY_FILE3 are already decrypted
            if SIZE == SIZE2                #if decompressed size from .paz/.meta file is the same as decompressed size from data header,
                                            #we will consider data header as valid and data can be sent to decompression function                                   
                clog FILE_PATH 0 ZSIZE SIZE MEMORY_FILE3    #send file to decompression function and store result to specified file
            else                            #if data header is not valid, data are uncompressed without header
                log FILE_PATH 0 SIZE MEMORY_FILE3           #store data with length SIZE to specified file
            encryption ice BDO_ICE_KEY      #turn encryption back on
   next i

# Same thing as the .meta extension, but if you open a .PAZ file to extract
else if EXT == "PAZ"  #If the file extension is .PAZ
   get DUMMY long
   get TOTAL_FILES long


   math i = 0
       get FILE_PATH string MEMORY_FILE

       if FILE_PATH == ""

       putarray 0 i FILE_PATH
       savepos TMP MEMORY_FILE
   next i

   for i = 0 < TOTAL_FILES
       get HASH long
       get FOLDER_NUM long
       get FILE_NUM long
       get OFFSET long
       get ZSIZE long
       get SIZE long

       getarray FOLDER_NAME 0 FOLDER_NUM
       getarray FILE_NAME 0 FILE_NUM
       string FILE_PATH = FOLDER_NAME
       string FILE_PATH += FILE_NAME

       if SIZE > ZSIZE
       elseif SIZE == 0
           log FILE_PATH 0 0
           get FLAGS byte MEMORY_FILE2
            if (FLAGS == 0x6E || FLAGS == 0x6F) && ZSIZE > 9
                get DUMMY long MEMORY_FILE2
                get SIZE2 long MEMORY_FILE2
           encryption "" ""
              if SIZE == SIZE2                                      
                clog FILE_PATH 0 ZSIZE SIZE MEMORY_FILE2
                log FILE_PATH 0 SIZE MEMORY_FILE2      
            encryption ice BDO_ICE_KEY
   next i

You can download this code here:

If you need help understanding quickbms scripts, please refer to, everything you need is there.
The 256000 unreadable bytes:
In some older version of the game, there was a section in the meta file, which was exactly 256000 bytes long, and it contained 8000 file blocks that I simply couldn't read, because if I read them as integers (4 bytes at the time), it only gave me huge or negative numbers.
So far I don't know how to read them, but I discovered that all the 8000 missing files that are there, have something in common:
All of their hashes uses only 7 hexadecimal letters to represent it, instead of 8. Let me explain:
Let's compare the hashes from "multiplemodeldesc.xml" to the "" (One of the missing files)

multiplemodeldesc.xml hash:
DECIMAL: 631490897
HEX (Big Endian): 25 A3 C9 51
HEX (Little Endian): 51 C9 A3 25 hash:
DECIMAL: 25743895
HEX (Big Endian): 188D217 (or 01 88 D2 17)
HEX (Little Endian): 17 D2 88 01

The complete list of all missing files and their hashes and file/folder numbers can be found here:

As you can see, when it's one of those files, it when you convert the number from dec to hex, you always get only 7 letters, because the 8th one is always 0

So what I'm thinking is that, when they made the .meta file, they simply decided not to store this extra "0" to save some space, and since we are always reading 4bytes, it actually reads the seven letters + 1 more of the next field, giving us just nonsense.

This is no longer a problem and nowadays we can read the file blocks like we used to, except for the TW version, but I don't know if that has changed as well.

QUICKBMS Script that skips those 256kb

Back when we had this problem of the unreadable 256kb, you would get this error, when you try to extract game's files using pad00000.meta
Error: incomplete input file 0: C:\Program Files (x86)\Black Desert Online\Paz\PAD00000.meta
       Can't read 525213419 bytes from offset 00ffa5c8.
       Anyway don't worry, it's possible that the BMS script has been written
       to exit in this way if it's reached the end of the archive so check it
       or contact its author or verify that all the files have been extracted.
       Please check the following coverage information to know if it's ok.

  coverage file 0    45%   7539936    16754120

Last script line before the error or that produced the error:

Here is a modified version of the original script, that solves that problem:

This is just like the original script, but with the only difference is that I skip 256000 bytes after we read all the information about the PAZ files, and also, to discover the new "FILE_BLOCKS_START" and "FILE_BLOCKS_END", this is the algorithm I used:

- Search for the hash: 631490897 (from multiplemodeldesc.xml)
- Go back 28 bytes (7 * 4) (nFields(hash,folderNum,fileNum,pazNum,offset,zsize,size) * sizeof(int))
- Read hash,folderNum,fileNum,pazNum,offset,zsize,size
- If pazNum >= 1 or <= TOTAL_PAZ_COUNT
- Go back 28 bytes
- repeat.

Here's the code in C that does that:

So my goal was to find which byte starts the first hash from the "file blocks" section.
Here is the full list of the file blocks, in the order they appear in the pad000.meta file.

I also made a program that sorts this file by fileNum, and outputs the numbers of the files that are missing and how many of them are missing:
Also, the sorted file is this:

I've also made a version of the file that prints FILE_HASH | FOLDER_NUM | FOLE_NUM | FOLDER_NAME | FILE_NAME reading all from the .meta file (also skips the 256000 bytes, so you might want to delete those lines of code if you want it to work in the newest versions)


    3.7 KB · Views: 247
    3.6 KB · Views: 420
Last edited:


Avid Affiliate
I wanna change 'Black garter belt' and find the file of it

( It's used in 'resorep mod')

but when I run 'meta injector'
It failed to find '' ( maybe main part of 'Bra and Panties' )


Potential Patron
hey, this looks really good, but ive been trying to get something but i cant find how, and if its possible
basically i want Le Vladian underware, i want to remove the underware, just leave the stockings and waist/leg chains

is this possible ? it used to be before the last patches (EU), then i moved to resorepless and i couldnt do it any more
any info is appreciated, thanks


Potential Patron
I wanna change Valkyrie's New Clothes texture.
Textures in PAD04089.PAZ(,
How can i do it?


Content Creator
I wanna change 'Black garter belt' and find the file of it

( It's used in 'resorep mod')

but when I run 'meta injector'
It failed to find '' ( maybe main part of 'Bra and Panties' )
Yeah, I tried to patch that file too, unfortunately it doesn't work.
I'm trying to see if I can find an alternative for those cases, but I need to study the pad00000.meta file a little more to see if I can do something about it.

hey, this looks really good, but ive been trying to get something but i cant find how, and if its possible
basically i want Le Vladian underware, i want to remove the underware, just leave the stockings and waist/leg chains

is this possible ? it used to be before the last patches (EU), then i moved to resorepless and i couldnt do it any more
any info is appreciated, thanks
Download this:
Le Vladian underware textures
- Extract to the "files_to_patch" folder.
- Run "Meta Injector Reloaded.exe"
- After done patching, move the folder inside the "files_to_patch" folder to your Black Desert Online root folder.
If you were in
"C:\Program Files (x86)\Black Desert Online\PAZ\files_to_patch\"
you should move the "character" folder to
"C:\Program Files (x86)\Black Desert Online\"

I wanna change Valkyrie's New Clothes texture.
Textures in PAD04089.PAZ(,
How can i do it?
Aparently this file doesn't exist in the NA version yet. I just extracted the whole have and I haven't found it.
You will have to extract it yourself using quickbms and then just follow the instructions from the Meta Injector Reloaded normally.
Let me know if you need help when extracting the files.


  • Le Vladian underware textures
    280.6 KB · Views: 504


Vivacious Visitor
In the interest of removing a step, would it hurt to just make the final step in your patcher copy the files from files_to_patch into the root folder?

Maybe make it optional. One of the reasons I do not like moving the files (and would prefer a copy) is that way files_to_patch files just overwrites whatever was in the target directory and keeps it up to date. If you want to tinker, you do it inside of files_to_patch, then just re-run the patcher and it places everything nicely. It also handles deletes - delete from files_to_patch, re-run patcher, and those entries are unpatched so the files can remain where they are without causing issues.


Potential Patron
thanks a bunch man, works almost perfectly
first pic is with transparent resorepless installed, and it works fine
second pic is the same, but after i press show underware

maybe you know how to fix it ? or maybe i did something wrong
again, thanks a bunch


Content Creator
thanks a bunch man, works almost perfectly
first pic is with transparent resorepless installed, and it works fine
second pic is the same, but after i press show underware
View attachment 55407
maybe you know how to fix it ? or maybe i did something wrong
again, thanks a bunch
Download the Le Vladian underwear textures removed in the post above and follow the instructions I mentioned above.
This should fix your problem (Assuming you don't want to see the Le Vladian underwear


Content Creator
Black Desert Online Texture Extractor
A tool that uses quickbms to extract only the texture files from your .PAZ files.

Download Link:

Source Code: Source Code - Texture
1 - Extract this zip file to your PAZ folder.

2 - Run "Texture Extractor.exe"

3 - Press 1 if you want to create a folder called "extracted_textures" in your "PAZ" folder and extract all the textures there.

4 - Press 2 if you want to specify another folder to extract the textures.

Note: After the textures are extracted, a file called "log-textures.txt" will be created in your PAZ folder which contains the relation between all .PAZ files number and texture names, this can be useful if you want to extract only a specific file in the future without having to extract all .PAZ files again


Content Creator

The program now runs almost twice as fast due to the new copy technique and now in "Part 2" the meta file is loaded in memory instead of accessing the file directly and after all the changes are done, a new file is created and filled with the modified content of the memory.​

- Added options about what to do with the files in the "files_to_patch" folder after the patch is done:

- Added a header to the program showing the version.
- File count and detection is now made before the first menu shows up
- The "Running Meta Injector" was renamed to "Part (1/2)"
- The "Running Meta Patcher" was renamed to "Part (2/2)"
- Part 1 slightly faster than before.
- Part 2 insanely faster than before.
- Now it shows which meta file is chosen in the beginning after the user input in the first menu.
- Fixed a bug where the same file could result in "not found" or "Success!" in "Part 2" depending on which order of the meta files were used.
Last edited:


Potential Patron

i'm KR Server.
Not Found result.

Aparently this file doesn't exist in the NA version yet. I just extracted the whole have and I haven't found it.
You will have to extract it yourself using quickbms and then just follow the instructions from the Meta Injector Reloaded normally.
Let me know if you need help when extracting the files.


  • pvw_076.7z
    3.2 MB · Views: 143


Potential Patron
Since I started using the metainjector I stopped crashing to desktop in heidel.

But I found a new problem. It's been a few times now but I get these strange graphical glitches when a valkyrie is wearing Acher Guard armor, atleast the 2 times I saw it it was a valkyrie. I'll try to screenshot it next time.

Oh yah I don't seem to get the gltich when it is my own valkyrie wearing the armor. (armor swap and pearl shop, dont know if it would be the same if I bought it for real)