lib3ddevil1/bindings/py3devil1.py

512 lines
16 KiB
Python
Raw Normal View History

#!/usr/bin/python3
2018-04-25 09:49:44 +05:30
import ctypes, sys
#--------------------------------------+
2018-04-25 09:49:44 +05:30
# Devil 1: PLD Base
#--------------------------------------+
class PldHeader(ctypes.Structure):
_pack_ = 1
_fields_ = [
("numOffset", ctypes.c_int),
("offsets", ctypes.POINTER(ctypes.c_int))
]
2018-04-22 16:09:08 +05:30
class Devil1PLD_FN(ctypes.Structure):
_fields_ = [
("getheader" , ctypes.CFUNCTYPE(
ctypes.c_bool,
ctypes.POINTER(PldHeader),
ctypes.c_char_p)),
("sizeofsector", ctypes.CFUNCTYPE(
ctypes.c_int,
ctypes.POINTER(PldHeader),
ctypes.c_int)),
("printheader" , ctypes.CFUNCTYPE(None,
ctypes.POINTER(PldHeader)))
]
#--------------------------------------+
2018-04-25 09:49:44 +05:30
# Devil 1: TEX Base
#--------------------------------------+
class TexturePack(ctypes.Structure):
2018-04-25 07:45:39 +05:30
_pack_ = 1
_fields_ = [
("id", ctypes.c_char * 4), # fixed length 4, reverse order
("batchNumber", ctypes.c_int),
("firstBatchOffset", ctypes.c_int),
("unknownA", ctypes.c_int)
]
class TextureBatchDescriptor(ctypes.Structure):
2018-04-25 07:45:39 +05:30
_pack_ = 1
_fields_ = [
("batchIdx", ctypes.c_int),
("hash", ctypes.c_int),
("texNumber", ctypes.c_int),
("unknownA", ctypes.c_int * 8),
("textureSize", ctypes.c_int),
("unknownB", ctypes.c_int * 30)
]
class Texture(ctypes.Structure):
2018-04-25 07:45:39 +05:30
_pack_ = 1
_fields_ = [
("data", ctypes.c_ubyte)
]
class TextureBatch(ctypes.Structure):
2018-04-25 07:45:39 +05:30
_pack_ = 1
_fields_ = [
("batch", ctypes.POINTER(Texture))
]
class Devil1TEX_FN(ctypes.Structure):
_fields_ = [
("printheader", ctypes.CFUNCTYPE(
None,
ctypes.POINTER(TexturePack))),
("printbatchdesc", ctypes.CFUNCTYPE(
None,
ctypes.POINTER(TextureBatchDescriptor))),
2018-04-24 06:29:46 +05:30
("getheader", ctypes.CFUNCTYPE(
ctypes.c_bool,
2018-04-24 16:38:52 +05:30
ctypes.POINTER(
ctypes.POINTER(TexturePack)),
2018-04-24 06:29:46 +05:30
ctypes.c_char_p)),
("getbatchdesc", ctypes.CFUNCTYPE(
ctypes.c_bool,
2018-04-24 16:38:52 +05:30
ctypes.POINTER(
ctypes.POINTER(TextureBatchDescriptor)),
ctypes.c_uint,
ctypes.c_char_p,
ctypes.c_uint)),
("getbatch", ctypes.CFUNCTYPE(
ctypes.c_bool,
2018-04-24 16:38:52 +05:30
ctypes.POINTER(
ctypes.POINTER(TextureBatch)),
ctypes.c_uint,
ctypes.c_char_p,
ctypes.c_uint)),
("gettextures", ctypes.CFUNCTYPE(
ctypes.c_bool,
ctypes.POINTER(Texture),
ctypes.c_uint,
ctypes.c_char_p,
ctypes.c_uint))
]
#--------------------------------------+
2018-04-25 09:49:44 +05:30
# Devil 1: GEO Base
#--------------------------------------+
2018-04-22 19:18:19 +05:30
class Header(ctypes.Structure):
2018-04-25 07:45:39 +05:30
_pack_ = 1
2018-04-22 19:18:19 +05:30
_fields_ = [
("numMesh", ctypes.c_ubyte),
("unknownNumberB", ctypes.c_ubyte),
("unknownNumberC", ctypes.c_ubyte),
("unknownNumberD", ctypes.c_ubyte),
("padding", ctypes.c_int),
("unknownOffset", ctypes.c_ulonglong)
]
class MeshHeader(ctypes.Structure):
2018-04-25 07:45:39 +05:30
_pack_ = 1
2018-04-22 19:18:19 +05:30
_fields_ = [
("numBatch", ctypes.c_short),
("numVertex", ctypes.c_short),
("u", ctypes.c_uint),
("offsetBatches", ctypes.c_ulonglong),
("flags", ctypes.c_ulonglong)
]
class Coordinate(ctypes.Structure):
2018-04-25 07:45:39 +05:30
_pack_ = 1
2018-04-22 19:18:19 +05:30
_fields_ = [
("x", ctypes.c_float),
("y", ctypes.c_float),
("z", ctypes.c_float)
]
def __str__(self):
return "(%s, %s, %s)" % (str(self.x), str(self.y), str(self.z))
2018-04-22 19:18:19 +05:30
class UVs(ctypes.Structure):
2018-04-25 07:45:39 +05:30
_pack_ = 1
2018-04-22 19:18:19 +05:30
_fields_ = [
("u", ctypes.c_short),
("v", ctypes.c_short)
]
2018-05-13 22:24:57 +05:30
def __str__(self):
return "(%s, %s)" % (str(self.u), str(self.v))
2018-04-22 19:18:19 +05:30
class BoneIndexes(ctypes.Structure):
2018-04-25 07:45:39 +05:30
_pack_ = 1
2018-04-22 19:18:19 +05:30
_fields_ = [
("indexes", ctypes.c_ubyte * 4),
]
class BoneWeights(ctypes.Structure):
2018-04-25 07:45:39 +05:30
_pack_ = 1
2018-04-22 19:18:19 +05:30
_fields_ = [
("weights", ctypes.c_short)
]
class BatchData(ctypes.Structure):
2018-04-25 07:45:39 +05:30
_pack_ = 1
2018-04-22 19:18:19 +05:30
_fields_ = [
("numVertex", ctypes.c_short),
("uB", ctypes.c_short),
("padding", ctypes.c_uint),
("offsetPositions", ctypes.c_ulonglong),
("offsetNormals", ctypes.c_ulonglong),
("offsetUVs", ctypes.c_ulonglong),
("offsetBoneIndexes", ctypes.c_ulonglong),
("offsetBoneWeights", ctypes.c_ulonglong),
("offsets", ctypes.c_ulonglong)
]
class VertexData(ctypes.Structure):
2018-04-25 07:45:39 +05:30
_pack_ = 1
2018-04-22 19:18:19 +05:30
_fields_ = [
("positions", ctypes.POINTER(Coordinate)),
("normals", ctypes.POINTER(Coordinate)),
("u", ctypes.POINTER(UVs)),
("bi", ctypes.POINTER(BoneIndexes)),
("bw", ctypes.POINTER(BoneWeights))
]
class Batch(ctypes.Structure):
2018-04-25 07:45:39 +05:30
_pack_ = 1
2018-04-22 19:18:19 +05:30
_fields_ = [
("bd", ctypes.POINTER(BatchData)),
("vd", VertexData)
]
class Mesh(ctypes.Structure):
2018-04-25 07:45:39 +05:30
_pack_ = 1
2018-04-22 19:18:19 +05:30
_fields_ = [
("b", ctypes.POINTER(Batch))
]
class Devil1GEO_FN(ctypes.Structure):
_fields_ = [
("printheader", ctypes.CFUNCTYPE(
None,
ctypes.POINTER(Header))),
("printmeshheader", ctypes.CFUNCTYPE(
None,
ctypes.POINTER(MeshHeader))),
("printbatch", ctypes.CFUNCTYPE(
None,
ctypes.POINTER(Batch))),
("printcoordinate", ctypes.CFUNCTYPE(
None,
ctypes.POINTER(Coordinate))),
2018-04-24 06:29:46 +05:30
("getheader", ctypes.CFUNCTYPE(
None,
ctypes.POINTER(ctypes.POINTER(Header)),
ctypes.c_char_p)),
2018-04-22 19:18:19 +05:30
("getmeshheader", ctypes.CFUNCTYPE(
ctypes.c_bool,
2018-04-24 16:28:51 +05:30
ctypes.POINTER(ctypes.POINTER(MeshHeader)),
2018-04-22 19:18:19 +05:30
ctypes.c_uint,
ctypes.c_char_p)),
("getbatch", ctypes.CFUNCTYPE(
ctypes.c_bool,
ctypes.POINTER(Batch),
ctypes.c_uint,
ctypes.c_char_p)),
("getmesh", ctypes.CFUNCTYPE(
ctypes.c_bool,
ctypes.POINTER(Mesh),
ctypes.c_uint,
ctypes.c_char_p,
ctypes.c_uint))
]
2018-04-25 09:49:44 +05:30
#--------------------------------------+
# Python Objs
#--------------------------------------+
sharedlib = './lib3ddevil1.so'
libc = ctypes.cdll.LoadLibrary(sharedlib)
if not libc:
print("Couldn't load %s" % sharedlib)
sys.exit()
del sys
2018-04-25 09:49:44 +05:30
print("\nlib3ddevil1 loaded.")
devil1pld = Devil1PLD_FN.in_dll(libc, "DEVIL1PLD")
devil1tex = Devil1TEX_FN.in_dll(libc, "DEVIL1TEX")
devil1geo = Devil1GEO_FN.in_dll(libc, "DEVIL1GEO")
class PLDHeader:
def __init__(self, filedata = None):
# Store C Struct in order to call C functions
self.cstruct = PldHeader()
if filedata:
devil1pld.getheader(ctypes.byref(self.cstruct), filedata)
self.eof = len(filedata)
def show(self):
devil1pld.printheader(ctypes.byref(self.cstruct))
return
def getnumoffsets(self):
return self.cstruct.numOffsets
# return pythonic list of offsets
def getoffsets(self):
return self.cstruct.offsets[:self.cstruct.numOffset]
def sizeofsector(self, i):
ptr = ctypes.byref(self.cstruct)
return devil1pld.sizeofsector(ptr, i, self.eof)
class TEXturePack:
def __init__(self, filedata):
2018-04-27 17:14:55 +05:30
self.cstruct = ctypes.pointer(TexturePack())
devil1tex.getheader(ctypes.byref(self.cstruct), filedata)
return
def show(self):
2018-04-27 17:14:55 +05:30
devil1tex.printheader(self.cstruct)
def getbatchno(self):
2018-04-27 17:14:55 +05:30
return self.cstruct.contents.batchNumber
def getfirstbatchoffset(self):
2018-04-27 17:14:55 +05:30
return self.cstruct.contents.firstBatchOffset
class TEXtureBatchDescriptor:
def __init__(self, i, filedata):
2018-04-27 17:27:35 +05:30
self.cstruct = ctypes.pointer(TextureBatchDescriptor())
ptrofptr = ctypes.byref(self.cstruct)
if filedata:
devil1tex.getbatchdesc(ptrofptr, i, filedata, len(filedata))
return
2018-04-27 17:27:35 +05:30
def show(self):
devil1tex.printbatchdesc(self.cstruct)
2018-04-27 17:27:35 +05:30
def getbatchidx(self):
return self.cstruct.contents.batchIdx
2018-04-27 17:27:35 +05:30
def gethash(self):
return self.cstruct.contents.hash
2018-04-27 17:27:35 +05:30
def gettexno(self):
return self.cstruct.contents.texNumber
2018-04-27 17:27:35 +05:30
def gettexturesize(self):
return self.cstruct.contents.textureSize
# Needs testing / correction - gettextures will 'return by parameter'
# a dynamic array of textures. Need to be able to access multiple Texture()
class TEXtures:
def __init__(self, i, count, filedata):
self.cstruct = ctypes.byref(Texture())
if filedata:
devil1tex.gettextures(self.cstruct, i, filedata, len(filedata))
return
2018-04-25 09:49:44 +05:30
2018-05-13 13:38:56 +05:30
class GEOHeader:
def __init__(self, filedata):
self.cstruct = ctypes.pointer(Header())
ptrofptr = ctypes.byref(self.cstruct)
if filedata:
devil1geo.getheader(ptrofptr, filedata)
return
def show(self):
devil1geo.printheader(self.cstruct)
pass
2018-05-14 02:23:11 +05:30
2018-05-13 14:03:12 +05:30
class MEShHeader:
def __init__(self, i, filedata):
self.cstruct = ctypes.pointer(MeshHeader())
ptrofptr = ctypes.byref(self.cstruct)
if filedata:
devil1geo.getmeshheader(ptrofptr, i, filedata)
return
pass
def show(self):
devil1geo.printmeshheader(self.cstruct)
2018-05-13 15:14:05 +05:30
def getbatchno(self):
return self.cstruct.contents.numBatch
def getnumvertex(self):
return self.cstruct.contents.numVertex
2018-05-14 02:23:11 +05:30
def getunknown(self):
return hex(self.cstruct.contents.u)
def getoffsetbatches(self):
return self.cstruct.contents.offsetBatches
def getflags(self):
return self.cstruct.contents.flags
2018-05-13 15:14:05 +05:30
class MEsh:
def __init__(self, i, filedata):
self.cstruct = Mesh()
if filedata:
mh = MEShHeader(i, filedata)
# allocate memory for the size of batch * number of batches
memsize = ctypes.sizeof(Batch) * mh.getbatchno()
self.cstruct.b = ctypes.cast(
ctypes.create_string_buffer(memsize),
ctypes.POINTER(Batch))
if not devil1geo.getmesh(
ctypes.byref(self.cstruct),
2018-05-13 15:14:05 +05:30
i,
filedata,
len(filedata)):
print("failed to get mesh")
del mh, memsize
2018-05-13 15:14:05 +05:30
return
def show(self):
if self.cstruct.b:
devil1geo.printbatch(self.cstruct.b)
else:
print("nothing to show")
2018-05-14 02:17:22 +05:30
def getbatchdata(self):
return self.cstruct.b.contents.bd.contents
def getpositions(self):
length = self.cstruct.b.contents.bd.contents.numVertex
return self.cstruct.b.contents.vd.positions[:length]
2018-05-13 16:13:50 +05:30
def getnormals(self):
length = self.cstruct.b.contents.bd.contents.numVertex
return self.cstruct.b.contents.vd.normals[:length]
2018-05-13 22:24:57 +05:30
def getuvs(self):
length = self.cstruct.b.contents.bd.contents.numVertex
return self.cstruct.b.contents.vd.u[:length]
2018-05-13 23:58:23 +05:30
def getboneindexes(self):
length = self.cstruct.b.contents.bd.contents.numVertex
return self.cstruct.b.contents.vd.bi[:length]
def getboneweights(self):
length = self.cstruct.b.contents.bd.contents.numVertex
return self.cstruct.b.contents.vd.bw[:length]
#--------------------------------------+
# Regular Python
#--------------------------------------+
2018-04-22 19:18:19 +05:30
if __name__ == "__main__":
def pldtest(devil1pld, pldheader):
with open("pl01.pld", "rb") as f:
data = f.read()
devil1pld.getheader(ctypes.byref(pldheader), data)
devil1pld.printheader(ctypes.byref(pldheader))
2018-04-27 17:27:35 +05:30
# for offset in pldheader.getoffsets():
# print(hex(offset))
2018-04-22 19:18:19 +05:30
def textest(devil1tex, texheader):
2018-04-27 17:27:35 +05:30
print("texture test")
2018-04-22 19:18:19 +05:30
with open("pl01.pld_1.txp", "rb") as f:
data = f.read()
2018-04-24 16:38:52 +05:30
# texheader = ctypes.cast(data, ctypes.POINTER(TexturePack))
th = ctypes.pointer(texheader)
devil1tex.getheader(ctypes.byref(th), data)
devil1tex.printheader(th)
batchdesc = TextureBatchDescriptor()
bd = ctypes.pointer(batchdesc)
print("\nbatch descriptor:")
devil1tex.getbatchdesc(ctypes.byref(bd), 1, data, len(data))
2018-04-27 17:27:35 +05:30
devil1tex.printbatchdesc(bd)
print(bd.contents.textureSize)
2018-04-22 19:18:19 +05:30
def geotest(devil1geo, geoheader):
2018-04-27 17:27:35 +05:30
print("geo test")
2018-04-22 19:18:19 +05:30
with open("pl00.pld_0", "rb") as f:
data = f.read()
2018-04-24 16:28:51 +05:30
# geoheader = ctypes.cast(data, ctypes.POINTER(Header))
gh = ctypes.pointer(geoheader)
devil1geo.getheader(ctypes.byref(gh), data)
devil1geo.printheader(gh)
2018-04-24 16:28:51 +05:30
meshheader = MeshHeader()
mh = ctypes.pointer(meshheader)
devil1geo.getmeshheader(ctypes.byref(mh), 1, data)
devil1geo.printmeshheader(mh)
2018-04-22 19:18:19 +05:30
def main():
sharedlib='./lib3ddevil1.so'
libc = ctypes.cdll.LoadLibrary(sharedlib)
if (not libc):
print("Couldn't load %s" % sharedlib)
return 1
print("OK")
pldfn = Devil1PLD_FN.in_dll(libc, "DEVIL1PLD")
pldh = PldHeader()
pldtest(pldfn, pldh)
texfn = Devil1TEX_FN.in_dll(libc, "DEVIL1TEX")
texh = TexturePack()
textest(texfn, texh)
geofn = Devil1GEO_FN.in_dll(libc, "DEVIL1GEO")
geoh = Header()
geotest(geofn, geoh)
2018-04-22 16:09:08 +05:30
2018-04-25 09:49:44 +05:30
def mainx():
with open("pl01.pld", "rb") as f:
data = f.read()
pld = PLDHeader(data)
pld.show()
pld2 = PLDHeader()
pld2.show()
with open("pl01.pld_1.txp", "rb") as f:
data = f.read()
txp = TEXturePack(data)
txp.show()
2018-04-27 17:14:55 +05:30
print(txp.getbatchno())
print(txp.getfirstbatchoffset())
2018-04-27 17:27:35 +05:30
tbd = TEXtureBatchDescriptor(1, data)
tbd.show()
print(tbd.gettexturesize())
tx = TEXtures(0, tbd.gettexno(), data)
2018-04-27 17:27:35 +05:30
2018-05-13 13:38:56 +05:30
with open("pl00.pld_0", "rb") as f:
data = f.read()
gh = GEOHeader(data)
gh.show()
2018-05-13 14:03:12 +05:30
mh = MEShHeader(3, data)
mh.show()
2018-05-13 15:14:05 +05:30
m = MEsh(0, data)
m.show()
2018-05-14 02:17:22 +05:30
# p = m.getpositions()
# print("positions:")
# for point in p:
# print(point)
# n = m.getnormals()
# print("normals:")
# for point in n:
# print(point)
# u = m.getuvs()
# print("uvs:")
# for point in u:
# print(point)
dbatch = m.getbatchdata()
print(hex(dbatch.numVertex))
print(hex(dbatch.padding))
2018-04-22 16:09:08 +05:30
#---------------------------------------+
2018-05-13 13:38:56 +05:30
# main()
2018-04-25 09:49:44 +05:30
mainx()
2018-04-22 16:09:08 +05:30