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 March 26 by shak-otay 1
Engineers shak-otay Posted March 27 Engineers Posted March 27 (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 March 31 by shak-otay 2
Engineers h3x3r Posted March 29 Engineers Posted March 29 (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 March 29 by h3x3r 1
Engineers shak-otay Posted March 29 Engineers Posted March 29 (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 March 29 by shak-otay typo 1
DKDave Posted March 29 Posted March 29 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 March 30 Engineers Posted March 30 (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 March 30 by h3x3r 2
Darius6 Posted March 30 Author Posted March 30 (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 March 30 by Darius6 1
Engineers shak-otay Posted March 30 Engineers Posted March 30 (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 March 31 by shak-otay 1
Engineers h3x3r Posted March 31 Engineers Posted March 31 (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 March 31 by h3x3r 1
Engineers shak-otay Posted March 31 Engineers Posted March 31 (edited) On 3/31/2025 at 7:06 AM, 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, from interiors Daytona tested only Exe only - you need the files from the first zip here. edit 3: Daytona, uvs of 3 joined sub meshes Make_H2O-TDUnl-bnk-F3.zip Edited April 1 by shak-otay 1
Engineers h3x3r Posted March 31 Engineers Posted March 31 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...
Engineers shak-otay Posted Sunday at 06:36 PM Engineers Posted Sunday at 06:36 PM (edited) Checking the Rims. The exe in the zip is NOT standalone, you'll need the files from the ..F3 zip here. (If the link doesn't snap in correctly try one post lower.) Checked these rims only: Pontiac Solstice, Triumph Daytona, Audi A4 DTM touring car and the Cadillac Sixteen others may fail (all with more than 1 BVXD block for example). Here's a H2O fix for Rim\DAYTONA_955I_F.bnk: 0x2300 822 Vb1 12 99 0x520 377 121110 0x1CB0 4 Make_H2O-TDUnlimRims.zip The rims with more than 1 BVXD block should be processed by the ..F3.exe. Edited Sunday at 11:17 PM by shak-otay 1
Darius6 Posted yesterday at 12:56 PM Author Posted yesterday at 12:56 PM 18 hours ago, shak-otay said: Checking the Rims. The exe in the zip is NOT standalone, you'll need the files from the ..F3 zip here. (If the link doesn't snap in correctly try one post lower.) Checked these rims only: Pontiac Solstice, Triumph Daytona, Audi A4 DTM touring car and the Cadillac Sixteen others may fail (all with more than 1 BVXD block for example). Here's a H2O fix for Rim\DAYTONA_955I_F.bnk: 0x2300 822 Vb1 12 99 0x520 377 121110 0x1CB0 4 Make_H2O-TDUnlimRims.zip 49.07 kB · 2 downloads The rims with more than 1 BVXD block should be processed by the ..F3.exe. The A4 DTM touring car and the Triumph Daytona 995i rims look fine the uvs are correct too. The Cadillac Sixteen however is missing some polygons and the rims for the Pontiac Solstice are incomplete. Could it be an issue on my end?
Engineers shak-otay Posted yesterday at 01:46 PM Engineers Posted yesterday at 01:46 PM 34 minutes ago, Darius6 said: The Cadillac Sixteen however is missing some polygons and the rims for the Pontiac Solstice are incomplete. Could it be an issue on my end? I fear, no. Checked Cadillac .lst file BVXD, 0 4d0 -> 0x520 Check at 2304, [FF] 0. 520 494, uv 0x2cf0 face index blocks: 0 3710 0. 3760 1270 ------------------------------ and calculated the assumed vertex count: (0x2304-8) /12 dec.= 746 vertices There's invalid vertices at the end then (NANs) so reducing to 637 but point cloud is still uncomplete then. Seems there's a bug in the face index thresholding calculation of the TDU version of hex2obj. Using v0.25b gives better results here. 1
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