Howto Extract PNG from binary file @ 0x80?

F

flamesrock

Hi,

As a newbie to the language, I have no idea where to start..please bare
with me..

The simcity 4 savegame file has a png image stored at the hex location
0x80. What I want to extract it and create a file with the .png
extension in that directory.

Can somebody explain with a snippet of code how I would accomplish
this? I've looked around but the information is too vague and I don't
know how to start.

-thanks in advance
 
F

Fredrik Lundh

flamesrock said:
As a newbie to the language, I have no idea where to start..please bare
with me..

The simcity 4 savegame file has a png image stored at the hex location
0x80. What I want to extract it and create a file with the .png
extension in that directory.

Can somebody explain with a snippet of code how I would accomplish
this? I've looked around but the information is too vague and I don't
know how to start.

there are *many* ways to ignore the first 128 bytes when you read a file
(you can seek to the right location, you can read 128 bytes and throw them
a way, you can read one byte 128 times and throw each one of them away,
you can read all data and remove the first 128 bytes, etc).

here's a snippet that understands the structure of the PNG, and stops copying
when it reaches the end of the PNG:

import struct

def pngcopy(infile, outfile):

# copy header
header = infile.read(8)
if header != "\211PNG\r\n\032\n":
raise IOError("not a valid PNG file")
outfile.write(header)

# copy chunks, until IEND
while 1:
chunk = infile.read(8)
size, cid = struct.unpack("!l4s", chunk)
outfile.write(chunk)
outfile.write(infile.read(size))
outfile.write(infile.read(4)) # checksum
if cid == "IEND":
break

to use this, open the input file (the simcity file) and the output file
(the png file you want to create) in binary mode, use the "seek"
method to move to the right place in the simcity file, and call "pngcopy"
with the two file objects.

infile = open("mysimcityfile", "rb")
infile.seek(0x80)
outfile = open("myimage.png", "wb")
pngcopy(infile, outfile)
outfile.close()
infile.close()

hope this helps!

</F>
 
A

Aaron

there are *many* ways to ignore the first 128 bytes when you read a file
(you can seek to the right location, you can read 128 bytes and throw them
a way, you can read one byte 128 times and throw each one of them away,
you can read all data and remove the first 128 bytes, etc).

here's a snippet that understands the structure of the PNG, and stops copying
when it reaches the end of the PNG:

import struct

def pngcopy(infile, outfile):

# copy header
header = infile.read(8)
if header != "\211PNG\r\n\032\n":
raise IOError("not a valid PNG file")
outfile.write(header)

# copy chunks, until IEND
while 1:
chunk = infile.read(8)
size, cid = struct.unpack("!l4s", chunk)
outfile.write(chunk)
outfile.write(infile.read(size))
outfile.write(infile.read(4)) # checksum
if cid == "IEND":
break

to use this, open the input file (the simcity file) and the output file
(the png file you want to create) in binary mode, use the "seek"
method to move to the right place in the simcity file, and call "pngcopy"
with the two file objects.

infile = open("mysimcityfile", "rb")
infile.seek(0x80)
outfile = open("myimage.png", "wb")
pngcopy(infile, outfile)
outfile.close()
infile.close()

hope this helps!

</F>

Thanks! And what a pleasant surprise! I just noticed you authored the book
I've been studying - Python Standard Library. Its awesome ;)

Oh- and Sorry for the late response. Google groups wouldn't allow me to
followup. I'm using pan now.
 
F

flamesrock

Well, I've been working on getting this code to work, and I think I
*almost* have it...

First, according to ghex, it seems the PNG starts at the 97th byte of
the file
infile = open("mysimcityfile.sc4", "rb")
infile.seek(97)
print (infile.read(4))
output:
flamesrock@flames:~/score$ python sc4png.py
PNG
(for 5 bytes it outputs an extra line, and for 6- 
(the biology symbol for a male)) So to debug further, I took out the
exception and modified the code like this:

#import struct
#
#def pngcopy(infile, outfile):
#
# # copy header
# header = infile.read(4)
# #if header != "\211PNG\r\n\032\n":
# # raise IOError("not a valid PNG file")
# outfile.write(header)
#
# # copy chunks, until IEND
# while 1:
# chunk = infile.read(8)
# size, cid = struct.unpack("!l4s", chunk)
# outfile.write(chunk)
# outfile.write(infile.read(size))
# outfile.write(infile.read(4)) # checksum
# if cid == "IEND":
# break
#
#
#infile = open("mysimcityfile.sc4", "rb")
#infile.seek(97)
#outfile = open("myimage.png", "wb")
#pngcopy(infile, outfile)
#outfile.close()
#infile.close()

returning the following output:
flamesrock@flames:~/score$ python sc4png.py
Traceback (most recent call last):
File "sc4png.py", line 26, in ?
pngcopy(infile, outfile)
File "sc4png.py", line 14, in pngcopy
size, cid = struct.unpack("!l4s", chunk)
struct.error: unpack str size does not match format

Any ideas on how to fix it? If I understand this page correctly,
http://www.python.org/doc/current/lib/module-struct.html
a png is basically a 'big endian string of 14 chars'? Changing it to
!14b" gives a"ValueError: unpack tuple of wrong size"
-thanks in advance for any help
 
R

Robert Kern

flamesrock wrote:

[snip]
flamesrock@flames:~/score$ python sc4png.py
Traceback (most recent call last):
File "sc4png.py", line 26, in ?
pngcopy(infile, outfile)
File "sc4png.py", line 14, in pngcopy
size, cid = struct.unpack("!l4s", chunk)
struct.error: unpack str size does not match format

Any ideas on how to fix it? If I understand this page correctly,
http://www.python.org/doc/current/lib/module-struct.html
a png is basically a 'big endian string of 14 chars'? Changing it to
!14b" gives a"ValueError: unpack tuple of wrong size"
-thanks in advance for any help

No, the chunk header is specified as "!l4s" where that second character
is a lower-case "L", not one "1". The struct is "a 4-byte long integer
in big-endian format followed by a 4-character string".

I think the error you are running into is that the chunk you are passing
in does not have 8 characters. Is your input truncated somehow?

--
Robert Kern
(e-mail address removed)

"In the fields of hell where the grass grows high
Are the graves of dreams allowed to die."
-- Richard Harter
 
F

flamesrock

Hmm...I'm not sure exactly. One of the things I've read is that
sometimes stuff is compressed in a savegame file, but I don't know why
they'd do that with a png..

I have some code that does the exact same thing in php only I don't
understand php syntax and how they did it. Does it give any clues as to
whether its truncated or not? (assuming you understand php)
http://simcitysphere.com/sc4Extractor.php.txt (original, only extracts
png)
http://simcitysphere.com/regionFileDecode_inc.php.txt (extracts png and
some fancy stuff like image map position)

-thanks
 
R

Robert Kern

Don't seek to position 97. Seek to 96. You're off by one. That's why
there's the test for the header (which you removed).

Files (and Python strings for that matter) are 0-indexed. The first
character is 0, the second 1, and so on. If you want the 97th character,
you have to seek to position 96.

--
Robert Kern
(e-mail address removed)

"In the fields of hell where the grass grows high
Are the graves of dreams allowed to die."
-- Richard Harter
 
F

flamesrock

omg, it works!! thankyou!!!!!
and thankyou again, Frederick for the original code!

I've discovered a weird thing when changing header = infile.read(8) to
header = infile.read(4):
For some reason, the extracted png image is the exact same size as the
savegame archive (10.1 megs.) When reading 8bytes, its closer to
~200kb. Any clue as to why that is?

-thanks

#import struct
#
#def pngcopy(infile, outfile):
#
# # copy header
# header = infile.read(8)
# if header != "\211PNG\r\n\032\n":
# raise IOError("not a valid PNG file")
# outfile.write(header)
#
# # copy chunks, until IEND
# while 1:
# chunk = infile.read(8)
# size, cid = struct.unpack("!l4s", chunk)
# outfile.write(chunk)
# outfile.write(infile.read(size))
# outfile.write(infile.read(4)) # checksum
# if cid == "IEND":
# break
#
#
#infile = open("mysimcityfile.sc4", "rb")
#infile.seek(96)
###print (infile.read(4))
#outfile = open("myimage.png", "wb")
#pngcopy(infile, outfile)
#outfile.close()
#infile.close()

(http://simcitysphere.com/peachville.sc4)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,756
Messages
2,569,535
Members
45,008
Latest member
obedient dusk

Latest Threads

Top