so after a review of this game i came to some interesting conclusion, maybe not perfect but still could be a help for many...
first of all the problem lies in the script for bms unpacking the .public files, they still use lzma2 but has some extra parameters that introduces a jump on the files that basically if you don't account it for your verts/textures will get totally ducked up. But, here the workaround for that, just go and grab the steam depot that is from 14 march 2017(google a bit and be careful of the tools you use, given r3e is free, i'd use another steam account to download the old depot; for you cannot use the steam console anymore for that). Once you have the old depot, the files will unpack correctly for that version, although there are not special tools that are able to unpack it from the get go I was able to code in python a script for mrp that works more or less good, this is by no means a final script and only works under my control of variables, if anyone is savvy enough of python and thinks they can modify the code to make it work, be welcome. (Just in case, I wont provide unpacked converted files in here.)
tip: there are 2 functions here I use to parse the file before I even can extract the geometry, one is to search for DDS textures and extract them to a "curfolder"; that you will need to change it(works for car_template, cockpit_template and liveries once all unpacked from .public) the second function that is for the positions will print in console the positions of the meshes in the file and generate the geometry based on the API of MRP.-
tip 2: this doesn't work for tracks for tracks have massive sizes, before being able to process tracks there will be need a better split method or using pure python(I think MRP is not suited to work on such big files).
Code: Select all
import mrp
import sys
from nines import *
f = mrp.get_bfile()
flag_array = [0x76 ,0x64 ,0x65 ,0x63 ,0x6C ,0x2F]
dds_array = [0x44 ,0x53 ,0x20]
dds_pos = []
positions = []
meshes = []
f.seek(0,2)
eof = f.tell()
f.seek(0)
def positions_search():
for i in range(eof):
try:
byte_test = f.readByte()
if byte_test == 0x2f:
test_array = [f.readByte() for i in range(6)]
f.seek(-6,1)
if test_array == flag_array:
positions.append(f.tell()-1)
except:
pass #end of file
print(positions)
#------------------
#car silvia
#positions = [1054460, 1141186, 1182168, 1206030, 1241211, 2814624, 3583656, 3907010, 4327617, 4491844, 4536153, 4541726, 4560902, 4599182, 4599864, 4615904, 4729584, 4797377, 4815129, 4832881, 4850633, 4850950, 4853883, 4854130, 4858982, 4864270, 4867707, 4979600, 5012269, 5026962, 5063495, 5073563, 5073880, 5076837, 5086905, 5091793, 5095230, 5207243, 5239768, 5254461, 5290778, 5358372, 5387142, 5454736, 5483322, 5483639, 5486500, 5486697, 5491549, 5496837, 5500274, 5612167, 5644116, 5658809, 5695342, 5705410, 5705727, 5708708, 5718776, 5723664, 5727101, 5839090, 5871183, 5885876, 5922193, 7151679, 7154567, 7598201, 7598601, 7601533, 7854963, 8010845]
#cockpit silvia
#positions = [12586361, 12643287, 12839298, 13446622, 13448259, 13777777, 13779158, 13906890, 14082034, 14438510, 14766485, 14782807, 14783844, 14818022, 14819059, 14819976, 14821013, 14822050, 14823087, 14823284, 14824441, 14843166]
#-------------------
#car mustang imsa
positions = [1054468, 1141194, 1182176, 1206038, 1241648, 1286949, 1292109, 1324735, 1328663, 3431297, 3955457, 4644783, 5035256, 5165772, 5415566, 5422351, 5437343, 5453951, 5462387, 5622325, 5628637, 5766067, 5770855, 5908319, 5987404, 6010812, 6034220, 6057628, 6057945, 6064106, 6067123, 6084024, 6170573, 6198358, 6261989, 6264897, 6265214, 6271375, 6274283, 6282363, 6285380, 6286244, 6303145, 6389694, 6418055, 6481276, 6524838, 6557252, 6600814, 6633136, 6663608, 6694080, 6724552, 6724869, 6731030, 6734047, 6750948, 6837497, 6865474, 6928599, 6931507, 6931824, 6937985, 6940893, 6948973, 6951990, 6952854, 6969755, 7056304, 7085049, 7148270, 7160566, 7278224, 7351918, 7362814]
curfolder = "E:\\Escritorio\\silvia\\car_model\\sylvia old\\output\\textures\\cockpit\\"
#curfolder = "E:\\Escritorio\\silvia\\car_model\\sylvia old\\output\\skins\\team a\\"
def dds_search():
for i in range(eof):
try:
byte_test = f.readByte()
if byte_test == 0x44:
test_array = [f.readByte() for i in range(3)]
f.seek(-3,1)
if test_array == dds_array:
dds_pos.append(f.tell()-1)
except:
pass #end of file
for i in range(len(dds_pos)):
cur = i
f.seek(dds_pos[cur]-4)
size = f.readInt()
buffer = []
cur_dds = curfolder+"texture_"+str(cur)+".dds"
for i in range(size):
buffer.append(f.readByte())
file = open(cur_dds, 'w+b')
binary_body = bytearray(buffer)
file.write(binary_body)
file.close()
#positions_search()
#dds_search()
#print(len(positions))
#print(hexl(flag_array[:-1]))
#sys.exit()
for i in range(len(positions)):
cur = i
f.seek(positions[cur]+7)
loop = True
cur_loop = 0
while loop:
cur_loop = cur_loop + 1
try:
byte_test = f.readByte()
if byte_test == 0x2f:
test_array = [f.readByte() for i in range(5)]
f.seek(-5,1)
if test_array == flag_array[:-1]:
f.seek(5,1)
loop = False
#print("header size = ", cur_loop+5)
#print("current end of header = ",f.tellHex(),"\t int after = ",hex(f.readInt()))
except:
print("some fucking error happened in item ", cur, positions[cur])
pass #end of file
if cur_loop == 100:
print("loop limit exceeded")
loop = False
break
#print(hex(f.readInt()))
stride = f.readInt()
version = f.readInt()#????
vc = f.readInt()
unknown = f.readInt()
fic = f.readInt()
verts = []
faces = []
uvs = []
print("mesh "+str(cur),"special flag = "+str(version),f.tellHex(),stride,vc,fic)
#print(f.tellHex())
for i in range(vc):
vert = tuple([round(f.readHalfFloat(),4) for i in range(3)])
#print(vert)
verts.append(vert)
if stride > 20 and stride != 60:
f.seek(14,1)#20
uv = tuple([round(f.readHalfFloat(),4) for i in range(2)])
uvs.append(uv)
f.seek(stride-24,1)
elif stride <= 20:
f.seek(2,1)
uv = tuple([round(f.readHalfFloat(),4) for i in range(2)])
uvs.append(uv)
f.seek(stride-12,1)
elif stride == 60:
f.seek(14,1)
uv = tuple([round(f.readFloat(),4) for i in range(2)])
uvs.append(uv)
f.seek(stride-28,1)
else:
sys.exit("what the fuck are these uvs?")
#print(f.tellHex())
for i in range(fic):
fi = f.read3Int()
faces.append(fi)
curmesh = "Mesh_"+str(cur)
mrp.create_mesh(curmesh)
mesh = mrp.get_mesh(curmesh)
mesh.set_vertices(verts)
mesh.set_faces(faces)#,fm="TStripFF")
mesh.set_uvs(uvs)#, tp="Float")
mesh.set_uvs_indices(faces,fm="Triangles")#,fm="TStripFF")
#mesh.set_normals(normals)
mrp.render(curmesh)
mrp.print_mesh(curmesh)
meshes.append(curmesh)
mrp.render(meshes)
sys.exit("end of code")
finally some screens of final result if done properly...(my script doesn't export materials, so you will have to find textures manually)
lastly another pro tip, the sounds could be theoretically extracted using ram dumps; I haven't tested if the march 2017 build allows to dump the sounds using the old method, it could work, I haven't tried yet.