Darius6 Posted March 22 Posted March 22 (edited) Hello. I'm looking in how to extract these bnk files from the Xbox 360 version of Test Drive Unlimited. These bnk files contain the following: 3DG (this is the file format for the meshes) and 2db (this is the file format for the textures) The PC version of this game also has bnk files which can be opened in software like 3dsimed however for the Xbox 360 version of the game they cannot be opened. The main reason as to why they won't open is due to the files from the Xbox 360 version are in big endian whereas the ones in the PC version are in little endian... Is it possible to convert big endian to little endian? And if not is it possible to extract the files individually from these bnk files? I've posted samples below: The _I in these bnk files represent the interior of the said vehicle. Rims are stored separately in a folder. For more context these are the DLC 6 vehicles which was the last DLC for the game and were never ported to the PC version of the game. Vehicles.rar Edited March 22 by Darius6
Engineers shak-otay Posted March 23 Engineers Posted March 23 (edited) On 3/23/2025 at 12:30 AM, Darius6 said: Hello. ... Is it possible to convert big endian to little endian? Speaking of files? Then usually the answer is "no", because you would need the offset of every byte , DWORD, float and string in the source file to treat them accordingly. (If the file were a block of floats, for example, it would be possible.) Getting something useful out of bnk will require some fidling, I guess: Edited March 24 by shak-otay 1
Engineers shak-otay Posted March 25 Engineers Posted March 25 (edited) Harder than expected without a decent magic table of counts (if any): Handlebar had to be positioned manually: Edited Wednesday at 11:57 PM by shak-otay 1
Engineers shak-otay Posted Thursday at 09:32 PM Engineers Posted Thursday at 09:32 PM (edited) I finally managed to get rid of a problem where a wronlgy determined face index count led to corrupted obj files. I didn't find a simple solution with Make_H2O_pub, so I decided to fix that bug using hex2obj (which required a special TDUnlimited version to be created). So if you don't want to trick around with a dozen *.objx files (from old hex2obj versions) it's important to use the updated exe (TDUnl) from here. Said exe will create *_corr.obj (instead of *.objx) which means that they can be imported without renaming. (Heaven will tell whether _corr(ection) is correct, always.) The process is like so (read How-to-use_TDUnl_bnk.txt from zip) : Make_H2O exe creates H2O files from bnk files hex2obj creates obj files from bnk using said H2O files bunch of obj files to be imported into blender using included .py file Found sub meshes might be uncomplete - tested three (+ 2 interior) bnk files only Have fun. edit: forgot to mention that doors, steering wheel etc need a positional correction. (The offsets are probably in the bnk files but I'll leave this to you.) And yeah, uvs are missing - progress: 50% for interior (see exe in ...F2.zip) (For chassis uvs need a recheck.) edit: the above mentioned hex2obj version was optimized for creating multiple H2O files (File\SaveAs Mmesh) from TDunlimited bnk files. The caveat is that handling single H2O files is restricted. So don't press the go1 button here. Just the "mesh" button (and maybe correct the vertex count if you're told to do so). Make_H2O_TDU_bnk.zip F2 - exe only - you need the files from the previous zip! Make_H2O-TDUnl-bnk-F2.zip Edited 3 hours ago by shak-otay 2
Engineers h3x3r Posted Saturday at 08:52 PM Engineers Posted Saturday at 08:52 PM (edited) Honestly these tri-strips with FFFF annoying me so much... How do you deal with them if i may ask? snippet maybe? Anyway I came across: vertices > 3 x float normals > 3 x hfloat + 1 x Null colors > 4 x ubyte uv indices > 2 x hfloat Partialy reversed, PRIM struct is submesh. Multi UV sets. Textures are tiled. No problem for Noesis. Already made a script. Edited Saturday at 09:09 PM by h3x3r 1
Engineers shak-otay Posted Saturday at 10:09 PM Engineers Posted Saturday at 10:09 PM (edited) 1 hour ago, h3x3r said: Honestly these tri-strips with FFFF annoying me so much... How do you deal with them if i may ask? snippet maybe? Welcome to the club. I got a tri strips with terminal (0xFFFF) function from howfie on xentax ages ago. Took me a whole day to make it work. (I'll pm you.) Edited Saturday at 10:29 PM by shak-otay typo 1
DKDave Posted Saturday at 10:22 PM Posted Saturday at 10:22 PM 1 hour ago, h3x3r said: Honestly these tri-strips with FFFF annoying me so much... How do you deal with them if i may ask? snippet maybe? Anyway I came across: vertices > 3 x float normals > 3 x hfloat + 1 x Null colors > 4 x ubyte uv indices > 2 x hfloat Partialy reversed, PRIM struct is submesh. Multi UV sets. Textures are tiled. No problem for Noesis. Already made a script. If you're using Noesis, you can use the rapi.rpgSetStripEnder(0xFFFF) function to set it to 0xFFFF and then just process each tri strip as normal. 1
Engineers h3x3r Posted yesterday at 02:26 PM Engineers Posted yesterday at 02:26 PM (edited) On 3/23/2025 at 12:30 AM, Darius6 said: Is it possible to convert big endian to little endian? And if not is it possible to extract the files individually from these bnk files? Sure if you know full struct. Anyway here's bms for unpacking individual files. I have covered 4 file formats so far... ################################### get BaseFileName basename endian big get Unknown_0_Offset uint32 get Unknown_CRC uint32 idstring BANK getdstring Dummy 0x0C get TotalFileSize uint32 get Unknown_0 uint32 get Unknown_1 uint32 get Unknown_2 uint32 get ResourceCount uint32 get Unknown_3 uint32 get ResourceTableOffset uint32 get Unknown_1_Offset uint32 get Unknown_2_Offset uint32 get Unknown_3_Offset uint32 goto ResourceTableOffset get ResourceTableSize uint32 get Unknown_CRC uint32 for i = 0 < ResourceCount get ResourceOffset uint32 get ResourceSize uint32 get ResourceType uint32 get ResourceNameCRC uint32 savepos cPos goto ResourceOffset getdstring Dummy 0xC getdstring Sign 0x3 if Sign == "DD3" set Ext string "3dd" # Hiearchy elif Sign == "GD3" set Ext string "3dg" # Geometry elif Sign == "MD2" set Ext string "2dm" # Material elif Sign == "BD2" set Ext string "2db" # Texture else set Ext string "dat" # ? endif goto cPos string FileName p= "%s/0x%08x.%s" BaseFileName ResourceOffset Ext log FileName ResourceOffset ResourceSize next i And here Noesis for textures... from inc_noesis import * import noesis import rapi import os def registerNoesisTypes(): handle = noesis.register("Test Drive Unlimited - Xbox 360 Texture", ".2db") noesis.setHandlerTypeCheck(handle, noepyCheckType) noesis.setHandlerLoadRGBA(handle, noepyLoadRGBA) noesis.logPopup() return 1 def noepyCheckType(data): bs = NoeBitStream(data,NOE_BIGENDIAN) if len(data) < 20: return 0 return 1 def noepyLoadRGBA(data, texList): bs = NoeBitStream(data,NOE_BIGENDIAN) BaseName = rapi.getExtensionlessName(rapi.getLocalFileName(rapi.getInputName())) bs.readBytes(40) Width = bs.readUShort() Height = bs.readUShort() Unknown_0 = bs.readUByte() Unknown_1 = bs.readUByte() Unknown_2 = bs.readUByte() Unknown_3 = bs.readUByte() PixelFormat = bs.readUInt() Unknown_5 = bs.readUInt() Unknown_6 = bs.readUInt() Unknown_7 = bs.readUInt() bs.readBytes(4032) if PixelFormat == 132: bs.texSize = Width * Height //2 print("Pixel Format > DXT1") elif PixelFormat == 136: bs.texSize = Width * Height print("Pixel Format > DXT5") elif PixelFormat == 144: bs.texSize = Width * Height * 4 print("Pixel Format > RGBA32") data = bs.readBytes(bs.texSize) if PixelFormat == 132: data = rapi.imageUntile360DXT(rapi.swapEndianArray(data, 2), Width, Height, 8) texFmt = noesis.NOESISTEX_DXT1 elif PixelFormat == 136: data = rapi.imageUntile360DXT(rapi.swapEndianArray(data, 2), Width, Height, 16) texFmt = noesis.NOESISTEX_DXT5 elif PixelFormat == 144: data = rapi.imageUntile360Raw(data, Width, Height, 4) texFmt = noesis.NOESISTEX_RGBA32 texList.append(NoeTexture(rapi.getInputName(), Width, Height, data, texFmt)) return 1 Try to post one sample from x360 and another from pc, files must be same. I mean file name... EDiT: Has Noesis UInt64 data type? Thanks! Edited yesterday at 04:53 PM by h3x3r 2
Darius6 Posted yesterday at 05:31 PM Author Posted yesterday at 05:31 PM (edited) On 3/27/2025 at 10:32 PM, shak-otay said: I finally managed to get rid of a problem where a wronlgy determined face index count led to corrupted obj files. I didn't find a simple solution with Make_H2O_pub, so I decided to fix that bug using hex2obj (which required a special TDUnlimited version to be created). So if you don't want to trick around with a dozen *.objx files (from old hex2obj versions) it's important to use the updated exe (TDUnl) from here. Said exe will create *_corr.obj (instead of *.objx) which means that they can be imported without renaming. (Heaven will tell whether _corr(ection) is correct, always.) The process is like so (read How-to-use_TDUnl_bnk.txt from zip) : Make_H2O exe creates H2O files from bnk files hex2obj creates obj files from bnk using said H2O files bunch of obj files to be imported into blender using included .py file Found sub meshes might be uncomplete - tested three (+ 2 interior) bnk files only Have fun. edit: forgot to mention that doors, steering wheel etc need a positional correction. (The offsets are probably in the bnk files but I'll leave this to you.) And yeah, uvs are missing - maybe later... Make_H2O_TDU_bnk.zip 74.37 kB · 1 download Hey thanks! this works! The only problem however is that the make H20 cannot read some bnk files like for an example: interior for the 350Z Roadster, both files for the Dino, and the interior of the A4 DTM car. As for the rims it only managed to export the rims from the Lamborghini Diablo GT correctly however for the rest when i import in blender i get a small black dot? (its on the upper right) As for the textures they can be dumped with a ripper 4 hours ago, h3x3r said: Sure if you know full struct. Anyway here's bms for unpacking individual files. I have covered 4 file formats so far... ################################### get BaseFileName basename endian big get Unknown_0_Offset uint32 get Unknown_CRC uint32 idstring BANK getdstring Dummy 0x0C get TotalFileSize uint32 get Unknown_0 uint32 get Unknown_1 uint32 get Unknown_2 uint32 get ResourceCount uint32 get Unknown_3 uint32 get ResourceTableOffset uint32 get Unknown_1_Offset uint32 get Unknown_2_Offset uint32 get Unknown_3_Offset uint32 goto ResourceTableOffset get ResourceTableSize uint32 get Unknown_CRC uint32 for i = 0 < ResourceCount get ResourceOffset uint32 get ResourceSize uint32 get ResourceType uint32 get ResourceNameCRC uint32 savepos cPos goto ResourceOffset getdstring Dummy 0xC getdstring Sign 0x3 if Sign == "DD3" set Ext string "3dd" # Hiearchy elif Sign == "GD3" set Ext string "3dg" # Geometry elif Sign == "MD2" set Ext string "2dm" # Material elif Sign == "BD2" set Ext string "2db" # Texture else set Ext string "dat" # ? endif goto cPos string FileName p= "%s/0x%08x.%s" BaseFileName ResourceOffset Ext log FileName ResourceOffset ResourceSize next i And here Noesis for textures... from inc_noesis import * import noesis import rapi import os def registerNoesisTypes(): handle = noesis.register("Test Drive Unlimited - Xbox 360 Texture", ".2db") noesis.setHandlerTypeCheck(handle, noepyCheckType) noesis.setHandlerLoadRGBA(handle, noepyLoadRGBA) noesis.logPopup() return 1 def noepyCheckType(data): bs = NoeBitStream(data,NOE_BIGENDIAN) if len(data) < 20: return 0 return 1 def noepyLoadRGBA(data, texList): bs = NoeBitStream(data,NOE_BIGENDIAN) BaseName = rapi.getExtensionlessName(rapi.getLocalFileName(rapi.getInputName())) bs.readBytes(40) Width = bs.readUShort() Height = bs.readUShort() Unknown_0 = bs.readUByte() Unknown_1 = bs.readUByte() Unknown_2 = bs.readUByte() Unknown_3 = bs.readUByte() PixelFormat = bs.readUInt() Unknown_5 = bs.readUInt() Unknown_6 = bs.readUInt() Unknown_7 = bs.readUInt() bs.readBytes(4032) if PixelFormat == 132: bs.texSize = Width * Height //2 print("Pixel Format > DXT1") elif PixelFormat == 136: bs.texSize = Width * Height print("Pixel Format > DXT5") elif PixelFormat == 144: bs.texSize = Width * Height * 4 print("Pixel Format > RGBA32") data = bs.readBytes(bs.texSize) if PixelFormat == 132: data = rapi.imageUntile360DXT(rapi.swapEndianArray(data, 2), Width, Height, 8) texFmt = noesis.NOESISTEX_DXT1 elif PixelFormat == 136: data = rapi.imageUntile360DXT(rapi.swapEndianArray(data, 2), Width, Height, 16) texFmt = noesis.NOESISTEX_DXT5 elif PixelFormat == 144: data = rapi.imageUntile360Raw(data, Width, Height, 4) texFmt = noesis.NOESISTEX_RGBA32 texList.append(NoeTexture(rapi.getInputName(), Width, Height, data, texFmt)) return 1 Try to post one sample from x360 and another from pc, files must be same. I mean file name... EDiT: Has Noesis UInt64 data type? Thanks! I've posted examples for the B. Engineering Edonis below. One folder is from the PC version of the game whereas the other one is from 360. Edonis.rar Edited yesterday at 07:25 PM by Darius6 1
Engineers shak-otay Posted yesterday at 08:20 PM Engineers Posted yesterday at 08:20 PM (edited) Thanks for reporting! Here's a quick fix (F1) tested with the 350Z Roadster interior bnk only (didn't check the other problem bnks and not the working ones, so keep the first released exe). Surprisingly I found the correct uv address here, for sub mesh 3: edit: shxt, the formula was to simple to apply to the uv addresses of all sub meshes. Only about 50% are correct for 350Z_Rst_I(nterior).bnk, see ...F2.exe in zip here Corrections for chassis uvs are a little bit hard to achieve. Use the updated hex2obj exe (for TDUnlimited) from here. Example for 350Z_Rst.bnk_0042.h2o, that's sub mesh 42 of the 350Z_Rst (uv correction not required any more with Fix3): Correction for sub mesh 39 of the 350Z_Rst steering wheel (uv correction not required any more with Fix3): Edited 3 hours ago by shak-otay 1
Engineers h3x3r Posted 18 hours ago Engineers Posted 18 hours ago (edited) Got some progress too... but now when i enable Normals it pops this error > RuntimeError: Normal buffer would have been read out of bounds by provided indices. And same for the UV. Edited 18 hours ago by h3x3r 1
Engineers shak-otay Posted 15 hours ago Engineers Posted 15 hours ago (edited) 14 hours ago, h3x3r said: Got some progress too... but now when i enable Normals it pops this error > RuntimeError: Normal buffer would have been read out of bounds by provided indices. And same for the UV. Well, had a short discussion with Rick, the MASTER himself, ages ago, and he got a little bit angry about it. In short, he wrote something like, "It's not a problem of Noesis." And yes, he was right, but you need to insert some print lines for logging addresses, counts and buffer sizes to find out the reason. Usually you'll find that one of "your" counts (x FVFsize, for example) exceeds the given buffer size. edit: updated Make_H2O tool to F2 (UVs, 50% of sub meshes correct) (use with interiors) edit 2: Fix 3 is my last version, probably, not all uvs are correct, not tested with interiors Exe only - you need the files from the first zip here. Make_H2O-TDUnl-bnk-F3.zip Edited 3 hours ago by shak-otay 1
Engineers h3x3r Posted 3 hours ago Engineers Posted 3 hours ago I think there is some confusion in endianess. I had same problem with vertices. I just add rapi.rpgSetOption(noesis.RPGOPT_BIGENDIAN, 1) and error with vertices disappear...
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now