Still having issues with 1.5 with the one stubborn dds file, did some digging in the source code trying to figure out what was up. Found out the headers weren't included in the 1.5 src drop, so no chance to build and/or debug it.
That said, since I do not know specifics about the composition of the .meta file structure which you might, I am not sure it is a problem, but it looks like the patcher assumes that the data stays int aligned throughout the file, since it scans the file in as an array of ints and then compares those ints to the hash rather than working with individual bytes. If something caused the serialization to shift by a word, it would make the scan stop working until the next word shift to re-align it.
Example, in case I am unclear, using 32 bit ints (since the hashes seemed to be signed 32 bit ints, though negative hash values seemed to be 16 bits of FFFF FFFF and then 16 bits of value?):
Search int value (as hex): 00AB CDEF
File contents: 0000 00AB CDEF 1234
In this case, the search value would be in the file, but the scan would not find it since the two individual int values are 0000 00AB and CDEF 1234, whereas scanning for the byte pattern 00AB CDEF would find the pattern starting at the second word.
The specific problem I am trying to solve is with pkw_00_uw_0001.dds, which is the maehwa default underwear texture. There is a second texture pkw_00_uw_0001_dec.dds that is the same texture with an alpha layer, which seems to have some usage in game, since when it is patched in as a blank texture with 100% alpha, some of the trim goes away as well as the bra, but not the panties. If I enable resorep to determine what textures are being loaded, it shows both being loaded. I was thinking the two could be referenced in the same data block, they might not be strictly aligned to int within that block, even if the block as a whole is a multiple of int in size to not cause alignment errors with the rest of the file.
My bad, here is the Source Code with all the files you need and some other helpful stuff:
https://www.undertow.club/attachmen...3/?temp_hash=edbb04fc41f14d4d434733fc47fe7554
This is a very valid point, and I thought about that a lot, since you seem interested to know how the meta file is structured, let me explain to you what I figured it out so far.
This is a very simplified version of the pad00000.meta file:
This is a small 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 a number that I don't know what's for.
- ZSIZE and SIZE are still a mistery for me. Maybe is the size of the files.
- I'm not sure if I got the "file names" part right, it's strange they didn't put the fileNumber before the file name like they did with the folder names. If you want to see if I missed something, please see the quick bms code I posted in the end of this post.
This is the same file as the first image, but with all the numbers converted from hex to dec:
Meta injector, works only to find those "FILE_HASH" numbers. As you can see, before them, there are only ints, so even though some bytes get "shifted" after the strings with the folder names and file names, this is irrelevant because the information we are interested is before that, where there was no possibility for shifting.
And even if there was the 16bit - 32 bit problem you mentioned. I manually searched for those bytes using the hex editor, and the hashes are simply not there. And all the others the program finds them just fine.
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 bytes.
Warning, don't do this mistake:
25 A3 C9 51 -> 15 9C 3A 52
it's:
25 A3 C9 51 - > 51 C9 A3 25
In my program, I read all bytes in blocks of 4 bytes (ints) so the conversion, for the program, is not necessary, but it is if you are doing some tests by hand and you want to check the numbers.
Lastly, but the most interesting thing. Let's take a look at the hash of the files that Meta Injector fails to patch:
Here are the conversion to hex:
Decimal - Raw conversion - Separated ------- Converted to Little Endian Notation
79844008 ---- 4C252A8 ----------- 4 C2 52 A8 --------------- A8 52 C2 04
5067198 ------- 4D51BE ------------- 4D 51 BE ------------- BE 51 4D 00
57369800 ---- 36B64C8 ----------- 3 6B 64 C8 --------------- C8 64 6B 03
87826479 ---- 53C202F------------- 5 3C 20 2F -------------- AF 20 3C 05
10383715 ---- 9E7163----------------- 9E 71 63 ---------------- 63 71 93 00
Apparently all the dec numbers that when converted to hex, have less than 8 digits, the entry simply doesn't exist in the meta file. (You can try to find it yourself if you want). Also, the folder number with that fileNumer as well is also non existent.
Example:
folder: 2745 file: 59798 = B9 0A 00 00 96 E9 00 00 (Doesn't exist)
but
folder: 2745 file: 59797 = B9 0A 00 00 95 E9 00 00 (Exists): Hash: BE 1D 0A 67 -> 670A1DBE -> 1728716222
All this information about the meta file, I learned by studying the blackdesert.bms file:
# Black Desert (script 0.2.2)
# super thanks to Ekey
# http://forum.xentax.com/viewtopic.php?f=10&t=10879
# script for QuickBMS http://quickbms.aluigi.org
# set the following to 1 for extracting the old archives
math OLD_ENCRYPTION = 0
quickbmsver "0.7.4"
comtype blackdesert
get EXT extension
if EXT == "meta"
# pre-allocation, improves speed (doesn't matter if the values are bigger)
putarray 0 0x4000 "" # PAZs
putarray 1 0x2000 "" # folders
putarray 2 0x80000 "" # files
get DUMMY long
get pPAZCount long
for i = 0 < pPAZCount
get PAZ_NUM long
get HASH long
get PAZ_SIZE long
string PAZ_NAME p= "PAD%05d.PAZ" PAZ_NUM
putarray 0 PAZ_NUM PAZ_NAME
next i
get FILES long
savepos OFFSET
xmath TMP "OFFSET + (FILES * 0x1c)"
goto TMP
get FOLDERS_SIZE long
savepos TMP
math TMP_SIZE = FOLDERS_SIZE
callfunction SET_ENCRYPTION 1
log MEMORY_FILE TMP FOLDERS_SIZE
encryption "" ""
math TMP += FOLDERS_SIZE
goto TMP
get NAMES_SIZE long
savepos TMP
math TMP_SIZE = NAMES_SIZE
callfunction SET_ENCRYPTION 1
log MEMORY_FILE2 TMP NAMES_SIZE
encryption "" ""
#print "collect folder names..."
math FOLDERS_SIZE -= 8 # lame
math i = 0
for TMP = 0 < FOLDERS_SIZE
get INDEX_NUM long MEMORY_FILE
get SUB_FOLDERS long MEMORY_FILE
get NAME string MEMORY_FILE
if NAME == ""
break
endif
putarray 1 i NAME
savepos TMP MEMORY_FILE
next i
#print "collect file names..."
math i = 0
for TMP = 0 < NAMES_SIZE
get NAME string MEMORY_FILE2
if NAME == ""
break
endif
putarray 2 i NAME
savepos TMP MEMORY_FILE2
next i
goto OFFSET
for i = 0 < FILES
get HASH long
get FOLDER_NUM long # 48c
get FILE_NUM long # 9ea5 e18d
get PAZ_NUM long # 9ec c20
get OFFSET long # 4603c 261cc
get ZSIZE long # 4a70 1970
get SIZE long # 5f13 2000
getarray PAZ_NAME 0 PAZ_NUM
getarray NAME 1 FOLDER_NUM
getarray TMP 2 FILE_NUM
string NAME += TMP
open FDSE PAZ_NAME 1
math TMP_SIZE = ZSIZE
callfunction SET_ENCRYPTION 1
if SIZE > ZSIZE
clog NAME OFFSET ZSIZE SIZE 1
else # yeah SIZE is < ZSIZE
log NAME OFFSET SIZE 1
endif
encryption "" ""
next i
else # PAZ
get DUMMY long
get PAZ_FILES long
get NAMES_SIZE long
savepos OFFSET
xmath OFFSET "OFFSET + (PAZ_FILES * 4 * 6)"
math TMP_SIZE = NAMES_SIZE
callfunction SET_ENCRYPTION 1
log MEMORY_FILE OFFSET NAMES_SIZE
encryption "" ""
math i = 0
for TMP = 0 < NAMES_SIZE
get NAME string MEMORY_FILE
if NAME == ""
break
endif
putarray 0 i NAME
savepos TMP MEMORY_FILE
next i
for i = 0 < PAZ_FILES
get HASH long
get FOLDER_NUM long
get FILE_NUM long
get OFFSET long
get ZSIZE long
get SIZE long
getarray NAME 0 FOLDER_NUM
getarray TMP 0 FILE_NUM
string NAME += TMP
math TMP_SIZE = ZSIZE
callfunction SET_ENCRYPTION 1
if SIZE > ZSIZE
clog NAME OFFSET ZSIZE SIZE
else # yeah SIZE is < ZSIZE
log NAME OFFSET SIZE
endif
encryption "" ""
next i
endif
startfunction SET_ENCRYPTION
if OLD_ENCRYPTION != 0
encryption aes_128_cbc "\xF3\xA1\x0D\xF2\x47\xCC\x30\xC5\xEB\x11\x12\xAE\x07\x01\x52\x13"
else
encryption ice "\x51\xF3\x0F\x11\x04\x24\x6A\x00"
endif
endfunction
If you need help understanding quickbms scripts, please refer to http://aluigi.altervista.org/papers/quickbms.txt, everything you need is there.
If anyone else can help me out to understand why this is happening, please let me know. You have all the tools and knowledge I have right now.