I’ve been working with binary data in a custom format. The first thing I did was to define the data types. You’ll also need to import some, or all if you’d like, of the “struct” module.
from struct import Struct, calcsize, pack, unpack
try:
from struct import *
except:
calcsize= unpack= None
ByteChar = 'B'
LongWordChar = '<I'
SingleChar = '<f'
WordChar = '<H'
IntegerChar = '<i'
Byte = struct.Struct(ByteChar)
Char = struct.Struct('<c') #Single Character
Word = struct.Struct(WordChar) #Unsigned Integer, 16 bits
Single = struct.Struct(SingleChar) #Floating Point, 32 bits
Integer = struct.Struct(IntegerChar) #Signed Integer, 32 bits
LongWord = struct.Struct(LongWordChar) #Unsigned Integer, 32 bits
You will have to define the data types needed for your project.
I then created a class I called TGenericData, which provides a mechanism to store the data, and to read and write to it.
class TGenericData():
Offset = LongWord
Index = LongWord
InternalOffset1 = LongWord
InternalOffset2 = LongWord
InternalOffset3 = LongWord
InternalOffset4 = LongWord
Count = LongWord
InternalCount = LongWord
Data = bytearray()
def __init__(self):
self.Offset = 0
self.Index = 0
self.Count = 0
self.InternalCount = 0
self.InternalOffset = 0
self.InternalOffset1 = 0
self.InternalOffset2 = 0
self.InternalOffset3 = 0
self.InternalOffset4 = 0
self.Data = bytearray()
def ReadByte(self):
self.Offset += 1
self.Index += 1
return self.Data[self.Offset-1]
def AddByte(self, value):
self.Data.extend(Byte.pack(value))
self.Index += 1
self.Offset += 1
def ReadWord(self):
self.Offset += 2
self.Index += 1
return struct.unpack(WordChar, self.Data[self.Offset-2:self.Offset])
def AddWord(self, value):
self.Data.extend(Word.pack(value))
self.Index += 1
self.Offset += 2
def ReadInteger(self):
self.Index += 1
self.Offset += 4
return struct.unpack(IntegerChar , self.Data[self.Offset-4:self.Offset])
def AddInteger(self, value):
self.Data.extend(Integer.pack(value))
self.Index += 1
self.Offset += 4
def ReadLongWord(self):
self.Index += 1
self.Offset += 4
return struct.unpack(LongWordChar , self.Data[self.Offset-4:self.Offset])
def AddLongWord(self, value):
self.Data.extend(LongWord.pack(value))
self.Index += 1
self.Offset += 4
def InsertLongWord(self, i, value):
v=LongWord.pack(value)
self.Data[i]=v[0]
self.Data[i+1]=v[1]
self.Data[i+2]=v[2]
self.Data[i+3]=v[3]
def ReadSingle(self):
self.Index += 1
self.Offset += 4
return struct.unpack(SingleChar , self.Data[self.Offset-4:self.Offset])
def AddSingle(self, value):
self.Data.extend(Single.pack(value))
self.Index += 1
self.Offset += 4
def AddUV(self,value):
self.Data.extend(Single.pack(value.u))
self.Data.extend(Single.pack(value.v))
self.Index += 2
self.Offset += 8
def AddUVIndex(self,value):
self.Data.extend(Single.pack(value[0]))
self.Data.extend(Single.pack(value[1]))
self.Index += 2
self.Offset += 8
def ReadXZY(self):
value = MyStuff.TPoint3D()
value.X = self.ReadSingle()
value.Z = self.ReadSingle()
value.Y = self.ReadSingle()
return value
def AddXZYas021(self,Coordinates):
self.Data.extend(Single.pack(Coordinates[0]))
self.Data.extend(Single.pack(Coordinates[2]))
self.Data.extend(Single.pack(Coordinates[1]))
self.Index += 3
self.Offset += 12
def AddXZY(self,Coordinates):
self.Data.extend(Single.pack(Coordinates.X))
self.Data.extend(Single.pack(Coordinates.Z))
self.Data.extend(Single.pack(Coordinates.Y))
self.Index += 3
self.Offset += 12
def ReadColor(self):
value = TMyColor()
value.R = self.ReadSingle()
value.G = self.ReadSingle()
value.B = self.ReadSingle()
value.A = self.ReadSingle()
return value
def ReadVColor(self):
value = TMyVColor(0,0,0,0)
value.R = self.ReadByte()
value.G = self.ReadByte()
value.B = self.ReadByte()
value.A = self.ReadByte()
return value
def AddVColor(self,VColor):
self.Data.extend(Byte.pack(VColor.R))
self.Data.extend(Byte.pack(VColor.G))
self.Data.extend(Byte.pack(VColor.B))
self.Data.extend(Byte.pack(VColor.A))
self.Index += 4
self.Offset += 4
def AddColorIndex(self,Color):
self.Data.extend(Single.pack(Color[0]))
self.Data.extend(Single.pack(Color[1]))
self.Data.extend(Single.pack(Color[2]))
self.Data.extend(Single.pack(Color[3]))
self.Index += 4
self.Offset += 16
def AddColorRGBA(self,Color):
self.Data.extend(Single.pack(Color[0]))
self.Data.extend(Single.pack(Color[1]))
self.Data.extend(Single.pack(Color[2]))
self.Data.extend(Single.pack(Color[3]))
self.Index += 4
self.Offset += 16
So, in your code, you’d have something along the lines of “MyObject = TGenericData()” to declare your data structure. You’d then write to it by “MyObject.AddByte(100)” or whatever variable contains the byte data. The same would go for the other data types as well. Reading a value from MyObject would be along the lines of “MySingle = MyObject.ReadSingle()”, etc. In my project, some data is not known until after the fact-- and after its spot in the data stream has been defined, so I wrote the “InsertLongWord(Offset, Value)” function to handle those cases. It overwrites the data at a particular location. You might not need that. I convert text to byte values, then write the byte values. Lastly, I have several different data blocks to deal with, so that is why there are multiple variables in TGenericData() to keep track of offsets within the data structure.
You can check your work by reading existing files and plugging the data in to Blender.
Reading data from a file is along the lines of:
file=open(filenane,'rb')
MyObject.Data = file.read(-1)
file.close()
Writing data to a file is along the lines of:
MyFile = open(filename,'wb')
MyFile.write(MyObject.Data)
MyFile.close()
That’s the basic start, and the approach I took. Those more skilled in Python may have a different or better method. I, too, learned Python through the course of my project.