Pages

Monday, August 4, 2008

Spore's .png format (illustrated)

Well, the obvious just happened, and I found that my work on decoding Spore's image files was in vain. A couple of clever guys from Something Awful have reversed engineered Spore Creature Creator to gain insight on the way it encodes the creature models in .png files, and then published a Python script to decode them. Looking at the encoding algorithm (which includes encryption and permutation), I'm quite sure that I couldn't find it by just examining the image files, and reverse engineering was the right path.

Following is a brief description on the steps taken by spore to encode a creature. Some of the details were omitted, just to make the big picture a bit clearer. If you know your way around Python, you may want to fill those missing details later.

So, lets start with the basics. We are given a creature model, in text format (xml) and a image of the creature. The model file is about 30k in size, and the image's width and height are 128 pixels, making it 16384 pixels long. Each pixel is composed of 4 bytes (for red, green, blue and transparency channels), which makes the image 65536 bytes long. Each byte is composed of 8 bits, where the least significant bit, the one that changes the value of the byte the least, is called the LSB.


Now, that we have our definitions straight, we can turn to the encoding process itself.
First, take the creature model, and compress it (using the deflate algorithm, nothing novel here). This usually shrinks the model size to less than 8192 bits (I'm really sorry for the Powerpoint graphics).




Add to the compressed model a header, footer containing a strong CRC (to make sure you would not open an erroneous model) and pad it with zeros to make it exactly 8192 bits long.

Now, take the image and produce an encryption key using the value of its bytes. This is done by first applying a permutation on the image bytes, and then scrambling them. The final encryption key is 8192 bits long.


Xor the compressed model bits with the key bits to produce an encrypted model. The thing to remember here is that given the key, you can simply Xor the encrypted bits again to gain the decrypted model.

Replace the the LSBs of the bytes in the image with the encrypted model bits. Since there are 65536 bytes in the image, there are 65536/8=8192 LSBs, so we have enough room for the whole creature. One last twist, you don't just replace the n'th LSB with the n'th encrypted model bit. You actually have a premutation function, p, and change the p(n)'th image LSB with the n'th model bit. Since we are changing only the least significant bits, we may not notice any change change in the output picture at all.



One thing to note - when we are creating the encryption key from the image, we don't use the image LSBs (as a matter of fact, we don't use the 3 least significant bits of each byte). This enables us to get the same encryption key from the encoded key as the one we got from the original image, even though we changed the LSBs.

Using the image as the encryption key was a smart move from Maxis, since it disable the evil user from simply taking the LSBs in one image and replacing them with LSBs from another image, to create a creature that looks one way in Sporepedia, and another within Spore. A more sinister user might have encoded a model that would crash your computer within an innocent looking image (for example, he could create a model, that when compressed weighs less than 8192 bits, but when expanded, weighs hundreds of megabytes).

So, is that all? Can we now encode the NSFW creature DonkeyPunch (by Tastyhumanburgers)


in the cute and lovely Crowned Laggie (by vib rib)?



The guys in SomethingAwful also published an encoding procedure, but it usually doesn't work. You can encode your creature in its own image, in a slightly changed version of its own image (you can move a pixel here, delete one there) and even encode the creature in a blank image. But you can't encode one creature in another's image.

We are surely missing something, but I can't tell what, two possible guesses:
1. Spore checks that the creature model resembles the creature in the image file. I doubt it, since you can encode a creature in a blank image.
2. Spore doesn't use the 3 least significant bits in each byte when creating an encryption key for a reason. We know that one of the bits is used to encode the creature, could the other two be used to encode some kind of an additional checksum? I doubt that too, but this post is long enough without going into my reasons.

Ideas?

22 comments:

  1. Thank you for all the hard work you spent working on this posting.

    I haven't a clue when it comes to the technological aspect of things, but off the top of my head - can it be that maxis used the extra LSBs to encode some personal information about the creator of the creature?

    i'll keep pondering on this, my lad

    ReplyDelete
  2. Nice post. Thanks for the info.

    ReplyDelete
  3. This is great work, I can also note that the process doesn't change for the full release. This info has helped a great deal with a program i am working on. Cheers.

    ReplyDelete
  4. It probably has more to do with the creators not wanting you to take the content that their users are creating and putting it into your game. I.e., maybe taking the airplanes and putting it into a flight sim. The only reason why I come to that conclusion is because I believe from reading the past thread that if the png file is loading a bunch of random salt keys from the game then the discovery of one key from my understanding of keys would allow you to import creatures as the one you are proposing but that's just my take on it. Either way I think being able to create your own tools would benefit spore. If a science class for example wanted to import spider species into the game but didn't want to use the Spore tools then someone could create a 3rd party tool more intuned to the needs of the science class. I don't know how advance Spore is for that but it sounded like Will W. wanted to see Spore in the next science class and I'm sure kids interested in this stuff would enjoy even more to see an actual scientific creature imported and used in this engine however right now the game is simply that, just a game for kids and adults that like kids games.

    ReplyDelete
  5. Am I to understand that the problem he was having with the encoder had to do with the IID of the files?

    If so, those are generated using a combined preset DWORD, and a name hash of the file being encoded for DBPF storage (or .png storage in this case)

    We had to learn that one from maxis by asking when we were decoding sims 2.

    ReplyDelete
  6. I really don't like necroposting, but I see no other way to get the info I need. Do you, by any chance, happen to have the python script for decoding Spore .png files anywhere? The link you posted is now dead, and Google is no help -.- Anyways, any help would be greatly appreciated. Thanks in advance.

    ~ Michael

    ReplyDelete
  7. Hi,

    Could you please repost the python scrypt? The link is dead. Thanks !

    ReplyDelete
  8. I'm with these guys.
    Anybody still have a copy? Or, could anyone with a Something Awful subscription help us out?

    ReplyDelete
  9. I found the original code from SA lurking in a hidden folder in my documents (hmmm, I wonder how it got there?). I think its a bit long to post the full code in a comment with it being 597 lines. I can email it to whoever requests it from me through email.

    ReplyDelete
  10. Just a note to the author, I have started on Galactic Adventures. I think that this article should get an update with information on the mission files. The original python program from sa doesn't even want to touch the adventure files. It hangs at the zlib decompression from what I can see. Get back with me, I am looking up as much info as I can.

    ReplyDelete
  11. I just want to say thank for illustrating how the Spore's .png format work. This is really broadening my knowledge. Anyway, if you have time, take a visit to my Free Games website.

    ReplyDelete
  12. http://sporeswap.appspot.com
    If spore won't recognize the result (happens ONLY with pictures you draw yourself and never with spore images), add few extra pixels before encoding. Looks like it's checking for parity (3 LSB's?) of the original image.

    ReplyDelete
  13. First of all: Thank you!

    Second: PNGs which were created in Patchlevel 1.03 and which are just copied into "MyCreation" folder are recognized as "existing" ("you have 190 creatures") but in GA (Patchlevel 1.04) are not shown ("no creatures in this folder") and can not be used, therefore.

    Third: please, a copy of the python script. I will see what I can do to make it work again.

    Regards ...

    ReplyDelete
  14. Can someone PLEASE re-upload the python script. I've been searching for it for hours...

    ReplyDelete
  15. http://sporeswap.appspot.com/scc.txt

    ReplyDelete
  16. It's been quite a while since this post got any comments, but what the heck:

    Rouli copied this post to sporedecoder.appspot.com and added some postscripts.

    It is noted that a blank image always works.
    It is ambiguously stated whether using another Spore-created image is reliable.
    It is clear that any other image is unreliable.

    So my questions:
    1a. Is using other Spore-created images reliable?
    1b. If not, how often do they fail?
    2. And how often do user-supplied images fail?

    If either failure rate is about 50%, that would seem to indicate a simple parity check.

    Now, the data in the LSB is calculated from the rest of the image plus the XML DNA info. We know this part works, and comes after the main image is generated, so it can't be changing the image to include the LSBs' parity.

    I took 4 of my own recent creations, plus 4 recent creations downloaded thru Sporistics.com. After slicing and dicing, the end result was that no single bitplane had the same parity in all 8 files. Adding up *all* the bits other than the LSBs, or all bits other than the bottom 3 LSBs, still no parity in common. No single channel's bits (excluding the LSBs).

    In short, if it's a parity check, I don't know what bits are going in...

    ReplyDelete
  17. @Jerry W Barrington
    Actually, sporedecoder.appspot.com copied MY post, without even contacting me first. You can see in their last paragraph that they accidentally copied my name. I wasn't aware of that site till today!
    wtf ...

    ReplyDelete
  18. How strange. The link above from 2 years ago, sporeswap.appspot.com, now redirects to the one I gave.

    Well, it doesn't seem like anybody has done much on figuring out the workings of Spore in the last 3 years. I guess I came late to the party. :)

    ReplyDelete
  19. Little necropost never hurts :v
    It's been 8 years (I reckon) since the game was released and.. there still isn't much about a way to edit the files easily. I guess Maxis won.

    ReplyDelete
  20. Did you ever see this?
    http://pastebin.com/SXBBZqJ8

    ReplyDelete
  21. thank you for this. the rabbit is really cute. MiniTool Data Recovery is a convenient free data recovery software, and yit is really good for beginners.

    ReplyDelete