Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations Chris Miller on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

extract BMP images from APP or EXE files 3

Status
Not open for further replies.

Olaf Doschke

Programmer
Oct 13, 2004
14,847
DE
I had a case of lost BMP images from a project folder, but got an APP file including the images. This code detects BMP files by it's simply header "BM" followed by size of the file in 2 bytes.

Code:
#Define ccVFPApp "C:\...\your.exe"
#Define ccImageFolder "C:\extractedBMPs\"

Local lcVFPApp, lnBMPPosition, lcBMPHeader, i

i=1
Do While .T.
   lnBMPPosition = At("BM",lcVFPApp,i)
   If lnBMPPosition=0
      Exit
   Endif

   lcBMPHeader = Substr(lcVFPApp,lnBMPPosition ,4)
   lnSize = CToBin(Right(lcBMPHeader,2),"2RS")

   If lnSize<=4
      * wrong size, surely not a bmp
   Else
      lcFilename = "image"+PADL(i,3,"0")+".bmp"
      StrToFile(Substr(lcVFPApp,lnBMPPosition ,lnSize),Addbs(ccImageFolder)+lcFilename)
   Endif

   i = i + 1
EndDo

Maybe it'll help some other poor soul with the same problem. If you know the inner file structure of GIF, JPG, PNG, etc. you'd be able to find and extract them, too. In my case BMP was just fine.

Bye, Olaf.
 
By the way: Before writing this I tried resourc3e hacker, but that only extracted the fox ico and some other stuff from an EXE, the way VFP embeds images is not compatible to normal resources embedded in EXEs.

Bye, Olaf.
 
Oops,

I wanted to post this as helpful tip, not as question.

I forgot the line
Code:
lcVFPApp = FileToStr(ccVFPApp)
before the loop, therefore the difference in l (local variable) and c (constant).

Yes, it's a bit astonishing this simple thing works, I just tried it, guessing VFP would simply embed the file 1:1 as is, even not compress it.

Bye, Olaf.
 
Very nice trick, my ratting to you!
I propose a small addition to improve image detection.
Teoretically file size = header length + image length. Unfortunatelly I saw BMP with image length not set.
So I propose adding one additional condition: header length between 1 and 65536.

Code:
img=0
Do While .T.
...
	lcBMPHeader = Substr(m.lcVFPApp,m.lnBMPPosition+2 ,4)
	lnSize = CToBin(Right(m.lcBMPHeader,4),"4RS")
	lcBMPHeader2 = Substr(m.lcVFPApp,m.lnBMPPosition+10 ,4) && bmp header length
	lnSize2 = CToBin(Right(m.lcBMPHeader2,4),"4RS")


	If m.lnSize>4 And (Between(m.lnSize2,1,2^16)) && bmp header length probably is bigger than 65536
		img=m.img+1
		lcFilename = "image"+Padl(m.img,5,"0")+".bmp"
		Strtofile(Substr(m.lcVFPApp,m.lnBMPPosition ,m.lnSize),Addbs(m.ccImageFolder)+m.lcFilename)
	Endif
...

Respectfully,
Vilhelm-Ion Praisach
Resita, Romania
 
Yes, you catched me there, as I only used 2 bytes of the file size, in my case it was only small 24x24 icon sized bmps for buttons, so I was lucky file sizes are all below 64K.

German Wikipedia also says the 4 bytes denoting the file size are not reliable and you should read the bitmap info header for detail information. I also didn't check against LEN(lcVFPApp). In my case it worked, and there always is room for improvement. Thanks for doing that, I recommend using the amended code.

Bye, Olaf.
 
I liked very much your idea (if I could I would give you 5 stars [smile]).
Here some raw adaptations for JPG, PNG and GIF
For JPG and PNG i got positive results, but for not GIF (I suspect I have no GIF images in tested EXE, but I'm not sure)

PNG
Code:
Local ccVFPApp,ccImageFolder,lcVFPApp, lnPNGPosition, i, img, lcPNGTerm ,lcFilename
ccVFPApp=Getfile("EXE")
ccImageFolder=Getdir()
If Empty(m.ccVFPApp) Or Empty(m.ccImageFolder)
	Return
Endif
lcVFPApp = Filetostr(m.ccVFPApp)
i=1
img=0
Do While .T.
	lnPNGPosition = At(CHR(0x89)+"PNG"+CHR(13)+CHR(10)+CHR(26)+CHR(10),m.lcVFPApp,i)
	If m.lnPNGPosition=0
		Exit
	Endif
	lcPNGTerm = AT("IEND"+CHR(0xAE)+CHR(0x42)+CHR(0x60)+CHR(0x82),Substr(m.lcVFPApp,m.lnPNGPosition ))
	If m.lcPNGTerm>0
		img=m.img+1
		lcFilename = "image"+Padl(m.img,5,"0")+".PNG"
		Strtofile(Substr(m.lcVFPApp,m.lnPNGPosition ,m.lcPNGTerm+8),Addbs(m.ccImageFolder)+m.lcFilename)
	Endif
	i = m.i + 1
Enddo

JPG
Code:
Local ccVFPApp,ccImageFolder,lcVFPApp, lnJPGPosition, i, img, lcJPGTerm ,lcFilename
ccVFPApp=Getfile("EXE")
ccImageFolder=Getdir()
If Empty(m.ccVFPApp) Or Empty(m.ccImageFolder)
	Return
Endif
lcVFPApp = Filetostr(m.ccVFPApp)
i=1
img=0
Do While .T.
	lnJPGPosition = At(CHR(0xFF)+CHR(0xD8)+CHR(0xFF),m.lcVFPApp,i)
	If m.lnJPGPosition=0
		Exit
	Endif
	lcJPGTerm = AT(CHR(0xFF)+CHR(0xD9),Substr(m.lcVFPApp,m.lnJPGPosition ))
	If m.lcJPGTerm>0
		img=m.img+1
		lcFilename = "image"+Padl(m.img,5,"0")+".JPG"
		Strtofile(Substr(m.lcVFPApp,m.lnJPGPosition ,m.lcJPGTerm+2),Addbs(m.ccImageFolder)+m.lcFilename)
	Endif
	i = m.i + 1
Enddo

GIF
Code:
Local ccVFPApp,ccImageFolder,lcVFPApp, lnGIFPosition, i, img, lcGIFTerm ,lcFilename
ccVFPApp=Getfile("EXE")
ccImageFolder=Getdir()
If Empty(m.ccVFPApp) Or Empty(m.ccImageFolder)
	Return
Endif
lcVFPApp = Filetostr(m.ccVFPApp)
i=1
img=0
Do While .T.
	lnGIFPosition = At("GIF",m.lcVFPApp,i)
	If m.lnGIFPosition=0
		Exit
	Endif
	lcGIFTerm = AT(CHR(0)+CHR(0x3B),Substr(m.lcVFPApp,m.lnGIFPosition ))
	If m.lcGIFTerm>0
		img=m.img+1
		lcFilename = "image"+Padl(m.img,5,"0")+".GIF"
		Strtofile(Substr(m.lcVFPApp,m.lnGIFPosition ,m.lcGIFTerm+2),Addbs(m.ccImageFolder)+m.lcFilename)
	Endif
	i = m.i + 1
Enddo

Respectfully,
Vilhelm-Ion Praisach
Resita, Romania
 
If I had the time for that. In the first place, this could be put into a FAQ here. I'd always pay more focus on form and correctness for articles, than for forum posts. This takes a bit more time and that's the reasoning, I still have never written articles. Admitted, it's also about a comfort zone.

But feel free to take this as a topic.

This weekend I'm learning for a MS certification and next few months are full with project work, too, so even from the time schedule perspective, there is low chances you see me doing more serious community work than forum answers.

I was thinking of writing a VFP beginner book way back 2006, then we all know what happened. I see Hentzenwerke is having a new FP2.6 converion related book. If I go back to the book idea, I'd rather have fun in writing a book titled "Bye, Fox", showing a bit of VFP specialties and telling my own VFP stories, as far as allowed. Maybe that could also be a nice community book idea, I think many people can contribute some nice and entertaining things here.

Bye, Olaf.



 
Tamar said:
this might make a nice article for FoxRockX

Olaf said:
If I had the time for that.

Olaf, if you like, I can publish it on my website (with full credit to yourself, of course). It won't require any of your time. I would also include Vilhelm-Ion's suggestions.

Just a thought.

Mike

__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
Mike,

thanks, that would be nice. Tamar, if you like you can also make this your FoxRocks article, too.
Let me just post a corrected "initial" version of my code as:

Code:
#Define ccVFPApp "C:\...\your.exe"
#Define ccImageFolder "C:\extractedBMPs\"

ExtractBMPs()

Procedure ExtractBMPs()
   LParameters tcVFPApp, tcDestinationImageFolder
   Local lcVFPApp, lnBMPPosition, lcBMPHeader, lnImageNo

   tcVFPApp = Evl(tcVFPApp, ccVFPApp)
   tcDestinationImageFolder = Evl(tcDestinationImageFolder, ccImageFolder)

   lcVFPApp  = Filetostr(m.tcVFPApp)
   lnImageNo = 1
   
   Do While .T.
      * Search for indicator of BMP file: BM
      lnBMPPosition = At("BM",m.lcVFPApp,m.lnImageNo)
      If m.lnBMPPosition=0
         * No further occurrance found, so exit the loop
         Exit
      Endif

      * Read just the 4 bytes containing file length from the BMP header
      lcBMPHeader = Substr(m.lcVFPApp,m.lnBMPPosition+2,4)
      lnSize = CToBin(m.lcBMPHeader,"4RS")

      If m.lnSize<6 Or m.lnSize+lnBMPPosition>Len(lcVFPApp)
         * wrong size, surely not a bmp file
      Else
         lcFilename = "image"+Padl(m.lnImageNo,4,"0")+".bmp"[URL unfurl="true"]http://tek-tips.com/viewthread.cfm?qid=1732378[/URL]
         Strtofile(Substr(m.lcVFPApp, m.lnBMPPosition ,m.lnSize),AddBS(m.tcDestinationImageFolder)+m.lcFilename)
      Endif

      lnImageNo = m.lnImageNo + 1
   Enddo
EndFunc

Of course the approach of Vilhelm-Ion Praisach is fine, too and I'll test his versions for other image formats on a test.exe, when there is time.

There can of course be many places in the EXE starting with BM and not being a BMP file, in texts of an application, for example. So files extracted may not turn out as picture. You'll get a fast overview when configuring the extraction destination folder for picture preview display and can delete files without a preview.

Besides, there is a list of names of embedded files at about the end of an EXE file, so you could even get original file names, but I didn't investigated how they point to a place in the EXE where the file is stored. And of course you know you can extract files from within an EXE, if you include STRTOFILE(FILETOSTR("some.bmp"),"c:\extracted\some.bmp") inside the EXE, but that has to be in the EXE and can't be done from outside of it.

Bye, Olaf.
 
...besides the EndFunc instead of EndProc :)

But it works, as EndProc and Endfunc are optionally anyway.

 
Olaf,

I've been playing with your code. So far, it works perfectly.

Have you looked for a way of saving the BMP under its original filename? Obviously, the filename isn't present in the BMP itself, but I assume it is present somewhere in the EXE. I see that there is a block near the end of the file that contains the names of all the embedded files (forms, reports, etc as well as bitmaps). But I can't see how this links back to the bitmap itself.

This is just a thought. Saving the files as IMAGEnnnn.BMP works fine. Retrieving the original filenames would be the mayonnaise in the sandwich.

Mike

__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
Yes, I saw this too. In Notepad there is nothing (printable) between the file names in this block, but inspecting this in detail (eg in Hexedit) there might be pointers to addresses or block offsets. The corresponding addresses might also be in another block before or after this block of file names. Again, time...

Bye, Olaf.


 
I really only write stuff for publication that I'm doing and fully understand. My hope was to encourage you to write because FoxRockX could use more authors.

Tamar
 
Improved JPG detection.

Code:
Local ccVFPApp,ccImageFolder,lcVFPApp, lnJPGPosition, lnJPGPosition2, i, j, img, lcJPGTerm ,lcFilename, nThumb
ccVFPApp=Getfile("EXE")
ccImageFolder=Getdir()
If Empty(m.ccVFPApp) Or Empty(m.ccImageFolder)
	Return
Endif
lcVFPApp = Filetostr(m.ccVFPApp)
i=1
img=0
Do While .T.
	lnJPGPosition = At(CHR(0xFF)+CHR(0xD8),m.lcVFPApp,i)
	lnJPGPosition2 = At('JFIF'+CHR(0),Substr(m.lcVFPApp,m.lnJPGPosition))
	
	If m.lnJPGPosition = 0
		Exit
	ENDIF
	nThumb = 0
	IF  m.lnJPGPosition2 = 7
		lcJPGTerm = AT(CHR(0xFF)+CHR(0xD9),Substr(m.lcVFPApp,m.lnJPGPosition ))
		IF At(CHR(0xFF)+CHR(0xD8),Substr(m.lcVFPApp,m.lnJPGPosition+2,m.lcJPGTerm-1))>0 && Thumbnail is present
			lcJPGTerm = AT(CHR(0xFF)+CHR(0xD9),Substr(m.lcVFPApp,m.lnJPGPosition ),2)
			nThumb = 1
		ENDIF
		If m.lcJPGTerm>0
			img = m.img + 1
			lcFilename = "image"+Padl(m.img,5,"0")+".JPG"
			Strtofile(Substr(m.lcVFPApp,m.lnJPGPosition ,m.lcJPGTerm+1),Addbs(m.ccImageFolder)+m.lcFilename)
		ELSE
			nThumb = 0
		ENDIF
	ENDIF
	i = m.i + 1 + m.nThumb
Enddo

Respectfully,
Vilhelm-Ion Praisach
Resita, Romania
 
Vilhelm-Ion,

I've run your GIF and improved JPEG code on several EXEs. They both work fine. (The GIFs included some animated GIFs, which are also OK.) I haven't been able to test with PNGs, because I don't use that file type.

I'm not sure I understand the significance of the thumbnail test. But I guess that doesn't matter.

Mike



__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
For PNG I got positive results even with executable files from C:\Windows and C:\Program files, like explorer.exe

About JPG
After some tests instead of the original JPG image, I got a smaller JPG image, the thumbnail.
That's because some JPG include a thumbnail image.
This means that inside the JPG image is another JPG image (smaller).
Both begins and ends with the same pair of bytes.

The algorithm is simple : if between the JPG header and JPG EOF marker is another JPG header, that means the JPG contain a thumbnail, and in this case I look for the next JPG EOF marker.

Respectfully,
Vilhelm-Ion Praisach
Resita, Romania
 
The algorithm is simple : if between the JPG header and JPG EOF marker is another JPG header, that means the JPG contain a thumbnail, and in this case I look for the next JPG EOF marker.

That makes perfect sense.

I haven't tried any of the routines with non-FoxPro executables yet. I'll do that when I can grab a moment.

Mike

__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top