Biped Preview image as TGA included in .bip files


#1

Hi :slight_smile:

Just found out that the Biped preview image(s) are included in the .bip files.
They are encrypted as TGA’s and have DATA-TYPE 10 format ending with TRUEVISION-XFILE
So whoever (like me) wants to use this information to create previews without opening the Biped in the scene can just extract the data out of the file and safe it as a TGA to show it.

Here is a short code example:

From here:

To here:

Cheers


#2

Hi maT-Star,

How are you taking this data and saving out a TGA from it?


#3

You could open the file binary (openFile <filename_string> mode:“rb”), read the file with readDelimitedString <filestream> "TRUEVISION-XFILE" to read until the end tag of TGA and just define the starting position, as it is different in Biped files. Then just format it out to a different file (create a file with createFile, name it .tga and I would use openFile <filename_string> mode:“wb” afterwards to open the new file binary).

I did not test this, but I think it might work :slight_smile:


#4

Ok, I got it now working.

What it does:

  1. Reads the file and gets the end point of the TGA part
  2. Writes the TGA out to a seperate file which can then be loaded in an rollout (btw. the bitmap control can load TGA too)
function extractBipedTGA bipPath =
 (
 	
 		--// open the file to read binary
 	binFile = fopen bipPath "rb"
 	
 		--// create saveTo file if not available
 	if (getFiles "C:\	est_tga.tga").count == 0 then
 		createFile "C:\	est_tga.tga"
 	
 		--// open saveTo file binary
 	binSaveFile = fopen "C:\	est_tga2.tga" "wb"
 	
 		--// retrieve the end of file
 	fseek binFile 0 #seek_end
 	endFile = ftell binFile
 	
 		--// TGA header start
 	fseek binFile 24 #seek_set
 	
 		--// indication of TGA
 	tempString = "TRUEVISION-XFILE"
 	
 	TGAEnd = 0	
 	foundTill = 1
 	
 	
 		--// going through the file and getting position of the TRUEVISION-XFILE
 	while ftell binFile < endFile - 1 do
 	(
 				--// checks, if the last indicator is found
 		if foundTill <= tempString.count then
 		(
 				--// comapres the indexed indicator with the current readed char
 			if tempString[foundTill] == bit.intAsChar (readByte binFile #unsigned) then
 				--// increase indicator to the next char
 				foundTill += 1
 				--// otherwise reset, to start from the beginning
 			else
 				foundTill = 1
 		)
 		else
 		(
 				--// set the TGA ending
 			TGAEnd = ftell binFile
 			exit
 		)
 	)
 	
 		--// TGA file end
 	TGAEnd += 2
 	
 		--// TGA header start
 	fseek binFile 24 #seek_set
 	
 	
 	while ftell binFile < TGAEnd do
 		writeByte binSaveFile (readByte binFile #unsigned) #unsigned
 	
 
 	fclose binFile
 	fclose binSaveFile
 )

#5

Mathias, you’re my hero :wink:


#6

This works only for the first preview image of the Biped file.
If you want to access the rest of them (max. 5 I think) then you need to the same steps according to the start point of the TGA).

(.max files have the preview image as bitmap format at the end of the file btw. ^^)


#7

Here is the function to extract the bitmap from the max file.
Is quite fast too :slight_smile:

I hope Autodesk does not change their format now :wink:

function extractMaxBMP maxPath =
(
		--// open the file to read binary
	binFile = fopen maxPath "rb"
	
		--// create saveTo file if not available
	if (getFiles "C:\	est_preview.bmp").count == 0 then
		createFile "C:\	est_preview.bmp"
	
		--// open saveTo file binary
	binSaveFile = fopen "C:\	est_preview.bmp" "wb"
	
		--// retrieve the end of file
	fseek binFile 0 #seek_end
	endFile = ftell binFile
	
		--// rough guess where to start the search for the BMP header
	fseek binFile (endFile - 70000) #seek_set
	
		--// indication of BMP start
	startIndicator = #(255, 255,255,255,3,0,0,0,1,0,128)  --// FF FF FF FF 03 00 00 00 01 00 80 as integer
	
	BMPStart = 0	
	foundTill = 1
	
	
		--// going through the file and getting position of the BMP start indicator
	while ftell binFile < endFile - 1 do
	(
				--// checks, if the last indicator is found
		if foundTill <= startIndicator.count then
		(
				--// comapres the indexed indicator with the current readed char
			if startIndicator[foundTill] == (readByte binFile #unsigned) then
				--// increase indicator to the next char
				foundTill += 1
				--// otherwise reset, to start from the beginning
			else
				foundTill = 1
		)
		else
		(
				--// set the BMP Start
			BMPStart = ftell binFile
			exit
		)
	)
	
		--// BMP file start
	BMPStart += 37
	
		--// BMP header start
	fseek binFile BMPStart #seek_set
	
	
		--// writing the BMP part to a new file
	while ftell binFile < endFile do
		writeByte binSaveFile (readByte binFile #unsigned) #unsigned
	
		--// moving to the start of the BMP file
	fseek binSaveFile 0 #seek_set
	
		--// new header as the max file had funny stuff in it
	local addedValues = #(66,77,0,0,0,0,0,0,0,0,54)
	
		--// writing new header
	for i = 1 to addedValues.count do
		writeByte binSaveFile addedValues[i] #unsigned
	
	
		--// closing binStreams
	fclose binFile
	fclose binSaveFile
)

#8

Hi Mathias,

That is great stuff, brilliant. I wish you had been here before I tried to do this with DotNet! In your comments you have a part where you take a rough guess to the start of the file. I assume that is because the size of the thumb can vary?

Do you think this method could be converted to a DotNet language like c# or VB? I would love to be able to take this method and embed it into a class library, as my method involved installing a COM class to retrieve the file from IpropertyStorage. I’m not sure, but I would have thought that the method could be converted. any thoughts?
Seriously, good work my friend. thankyou.:applause::bowdown:


#9

Here we go, a VB.net conversion - only useful if you are wanting to extract max thumbs for class libraries. This function should be pasted into a visualstudio.net project and called with the maxfile path as a string

What i’d like to look into is to to be able to extract straight to an image object, but it works. thanks again Mathias, you are a binary god. :beer:

 
         Public Sub GetMaxThumb(ByVal file As String)

		Dim SaveFileStream As New FileStream("C:\\_MThumb.bmp", FileMode.Create, FileAccess.Write)
		Dim MaxFileStream = New FileStream(file, FileMode.Open, FileAccess.Read)
		Dim binfile As New StreamReader(MaxFileStream)
		Dim endfile As Long = binfile.BaseStream.Seek(0, SeekOrigin.End)
		binfile.BaseStream.Position = endfile - 70000
		Dim startIndicator As Byte() = {255, 255, 255, 255, 3, 0, 0, 0, 1, 0, 128}
		Dim BMPStart As Long = 0
		Dim foundTill As Long = 1

		While binfile.BaseStream.Position < endfile - 1

			If foundTill <= startIndicator.Length - 1 Then

				If startIndicator.GetValue(foundTill) = binfile.BaseStream.ReadByte() Then
					foundTill += 1
				Else
					foundTill = 0
				End If

			Else
				BMPStart = binfile.BaseStream.Position
				Exit While
			End If

		End While

		BMPStart += 37
		binfile.BaseStream.Position = BMPStart

		Dim BinWriter As New StreamWriter(SaveFileStream)

		While binfile.BaseStream.Position < endfile
			BinWriter.BaseStream.WriteByte(binfile.BaseStream.ReadByte())
		End While

		BinWriter.BaseStream.Seek(0, SeekOrigin.Begin)
		Dim addedValues As Byte() = {66, 77, 0, 0, 0, 0, 0, 0, 0, 0, 54}
	  
		'For Each addval In addedValues
		'	BinWriter.BaseStream.WriteByte(addval)
		'Next

		' this seems to be better

		BinWriter.BaseStream.Write(addedValues, 0, addedValues.Length)


		binfile.Close()
		BinWriter.Close()
		SaveFileStream.Close()
		MaxFileStream.Close()

	End Sub

    
    	End Sub
    

there’s possibly a few methods with the streamwriter class that may well do things a bit more directly, but i haven’t been able to suss them out yet. if anyone who is good with this thing could take a look and see if it’s the most efficient way, please let me know.


#10

a ha, in vb.net i can get a bitmap from a filestream like so -

Dim newbmp As New System.Drawing.Bitmap(BinWriter.BaseStream)

I’m getting a few access issues with running my code in a loop to find all the maxthumbs from a folder of files - it seems that the previous image hasn’t finished saving before the next read is attempted. Is that a disk limitation or do i have to code it to wait until the save is performed? I’m working a bit in the dark with these methods so any pointers welcome.

p.s. whoo hoo, getting close to 4 figure post count, better think of something good to say…:smiley:


#11

:smiley:
Ohh, nice work man! :smiley:
Well, I got it only working so far with MAXScript, but it would make more sense to actually have it available outside of Max too :slight_smile:
Unfortunetly I’m not so familiar with VB or C++ and can’t help you in that case I think.

Arg, I got no mail that there are replies in my thread. So, sorry for the late reply ^^

Ahh, and yes, the image size can vary and the max files somehow have different endings and so it was quite hard to actually get the right part out of it. It is still a bit slow in MAXScript, but maybe I find a way with a better performance :slight_smile:

Cheers,
maT-Star


#12

Hi,

I will try to create a MAXScript extension in C++ with the SDK to do that stuff if you are interested (thumbnailUtils.dlx would be a good name I think).


#13

hi mathias, yannick,

the method on my website using dsofile is fast, since you dont have to search and build the thumbnail, you just retrieve it. I am using it in a few custom ui controls and it generates thumbnails amost immediately. but i havent been able to test a 64 bit version yet, just 32 bit. I dont like having to register a com dll for it though, i think its problematic for most users. I know its possible to automate this in an installer but i’ve not got rond to that yet.

but id love it, yannick if you could look at this and make something functional in max,and or dotnet. can you use a c++ dll in visual studio? its way over my head. Im still looking for a viable dotnet implementation for something like this, but im not sure its possible without the com class im using as i dont think vb dotnet does not allow access to ipropertystorage. when i say that i spend a while researching this for my solution and didnt find anything else, but that obviously doesnt mean its not possible!

thanks chaps, really interesting thread.


#14

Hi Pete,

I’ll see what’s possible to do. I’m now working in a 3D CGI studio (as you can see the change in my profile) so I’m very busy but I’ll let you know.


#15

hi Yannick,

Firstly, Congratulations on your new job!

And I dont know if this will help, but I was able to figure out my dotnet approach (even without knowing any C++) by the information in the comments of the propertytest project in the SDK. it’s found in \maxsdk\samples\utilities\propertytest
I’ll look forward to anything you find out. thanks!


#16

Thanks Pete,

I’ll take a look at this PropertyTest Utility sample.


#17

This thread has been automatically closed as it remained inactive for 12 months. If you wish to continue the discussion, please create a new thread in the appropriate forum.