One promising nugget in the Creatures 1 CAOS reference, is the "dde: pict" command.
According to the documentation,
Unfortunately, as many other things this command not only lacks critical bits of explanation but is also inherently broken (at least considering the above definition) and when used produces less than stellar results:
It's a shame we can't use this functionality in our own tools to take pictures as the Owner's kit does.
Or can't we ?
One of the main motives behind the site being uncovering such lost knowledge and abilities of the ancient Shee, come with me and find out how to understand and ultimately take advantage of this lost command.
TL;DR: If you are not interested in the actual process of understanding how the command worked in the first place but just want to use it, jump to the very bottom of the page for a wrap up.
So where do we start ?
All further documentation being forever lost, and the Internet not providing any tutorial on that matter, we will once again have to resort to reverse engineering parts of the original game to find out how things work.
The most obvious candidate for providing a sample of this command is the Owner's kit, since it does allow to take pictures of our creatures and exporting them to BMP's.
So let's fire up our favorite debugger an load the owner's kit in there.
One logical thing to look for would be searching the function attached to the "Take picture" button of the kit.
But Creatures and all associated kits are written as MFC applications, and those use a slightly more complex mechanism to manage interface interactions that what you might be used to.
We will deal with those mechanisms in more detail in a future post, for now let's find an easier way.
Since we are looking for how "dde: pict" works, let's look for it in the application "Strings" window:
Here it is, among other CAOS commands ! |
Double clicking on the entry and following It's references, leads us to the place in the code where the command is actually used, and it looks something like this:
We can see that a series of calls is crafting the final command from well known pieces : " inst,dde: panc, dde: pict" and ",endm".
But something seems to be happening between the "dde: pict" and ",endm" part.
This is most likely some additional parameter, which absence would explain why the command failed when we first tried it:
It might not be obvious at first what this additional data is by reading dead listing.
Let's rather just use the button, and try to intercept the final command right when it is used, shall we ?
To do this, we need to set a breakpoint after we think the command will be completed, and run the game, take a picture, and hope we end up where we thought we would.
Let's add the breakpoint after the command is crafted, but before the first "call" follwing it so nothing has the opportunity to break the chain we're looking for. |
Before performing our stunt we will also need to slightly tweak the interface.
When ran, the Creatures 1 exectuables adopt the old style 8+3 naming scheme instead of the more modern full format we're used to.
Owner kit process name is limited to 8+3 chars |
Going to the "Debugger->Process Options" menu, allows us to change the target process we will be looking for:
This needs to be changed.... |
...to this. |
With this done, let's fire up the game, run the Owner's kit, and pick the "Debugger->Attach to process" option, and proceed to take a pic.The debugger should step in at this point and land us where we expected:
That's it ! |
A quick right click over the memory chunk allows us to "stringify" it for easier reading:
More readable isn't it ? |
So this is the final command the applet runs ?
Letting the debugger resume the Owner's kit process results in what we're used to, and the creature pic is taken.
Then what is that "x|î" mess in there?
This doesn't look like anything we're used to when programming CAOS.
Most CAOS mnemonics, instructions or variables are 4 characters long, so this probably isn't even an internal game pointer or token of any kind.
Also it's barely printable characters where we are used to human readable keywords.
Does this weird command even work ?
Pumping it into a CAOS console gives us the answer:
Seems to work ! |
Soooo, this seems to work (well, at least it didn't kill the game this time :)
, and at this point , we uncovered some significant pieces of information:
- Contrary to what the documentation says, the command requires an additional parameter
- Those parameters seem to be raw values, and not their readable representation.
- The returned picture seems to be a .spr file rather than a BMP as was expected.
The file is stored in the game root directory:
Well, unfortunately this doesn't fit our earlier understanding of the .spr file format and doesn't seem to be a valid.spr file...
But like our favorite small furry and stubborn Norns, we won't let such small details prevent us from doing what we want to.
At this point it's just another round of file format reversing, so let's get this done!
This one looks scary, a huge blob of raw data with no apparent headers or readable strings.
But it's simpler than it looks.
Let's have a look at the three first groups of bytes.
Those stand out as they seem to be 4byte wide and 00 padded contrary to the rest of the file.
It might not seem obvious at first but what if we converted them to ASCII characters ?
(Yes intuition DOES play a major role in reversing), this gives us :
- 0x78 = "x" = 120
- 0xEE = "î" = 238
Are they really ? Could they represent an image height and width?
Lets answer both questions by running the command again, but this time using "ABC" as the parameter:
Good, the game doesn't seem to complain, and the tmp.spr file seems much bigger than earlier ( A=0x41, B=0x42,C=0x43 , and all are higher than the value of a small "x". It's then logical they would result in a bigger file if they are used to specify the picture geometry)
What do we get inside the file itself?
Maybe the games takes some initiatives with the actual values ? Maybe it tries to round them to some practical boundaries?
Let's assume it does and our values are indeed what we think, and try to make a valid .spr file out of this mess by manually appending a valid .spr header to the file, before that big blob of unknown data (we will be working on the original .spr file with the"x|î" values).
It might also be a good time to realize that if we select the whole blob of unknown data that looks like raw image data (yes it DOES !), it's total size is 0x41A0, which happens to be equal to the product of two of the first parameters, further confirming our guess that two of the 3 first values of the file might be width and height information.
0x78 * 0x8c = 0x41A0 |
Let's try to parse the reconstructed .spr file based on those assumptions :
Looks good to me !
We rebuilt a valid header for the file and parsed it using our previously written image manipulation tools.
The picture being upside down is only a minor inconvenience at this point, and we can be glad we recovered the expected picture from that seemingly invalid .spr file :)
So what did we learn so far ?
- The "dde: pict" command expects a parameter
- The parameter is not a readable string, but rather a series of 3 raw bytes.
- Two of those bytes seem to represent the wanted snapshot width and height.
- Running the command produces a temp.spr file that does not conform to the game .spr file format, but does contain enough information to produce a valid one.
- I'll spare you the details, but further exploration shows that in actual game usage this temp file is then read by the Owner's kit, and appended to the creatures photo album stored in a file named something like "4b524630.Photo Album" which we will soon learn to parse too :)
You can get it here.
By using this script, we can play a bit with the 3 parameters we can pass to the the "dde: pict" command by varying them and observing results.
The outcome is that for the command being "dde: pict ABC":
- A is the image Width, between 1 and 255
- C is the image Height ,between 1 and 255
- B didn't make any noticeable difference in the output image when varying it for any value between 1 and 255.I'm a bit ashamed to admit that it took me so long to realize that B is actually just supposed to be " | " which is the commonly used field separator for creatures commands (Yeah talk about intuition...), but that it's actual value doesn't seem to be checked.
There we have it, having understood and documented an useful and long lost command that will allow us to automatically take pictures of our favorite Norns, in arbitrary sizes up to 255*255 and save them in any common file format.
Also by replacing the "camt" CAOS command by an arbitrary camera positioning command before taking the pic, we could take pictures of just any part of the world we might be interested in.
And while we're at it, why not take the opportunity to study the format of files holding the creatures photo albums?
That will be the subject of our next article.But don't worry, it will be much shorter and slightly less technical.
Meanwhile maybe you'd find the following wrap up useful as a reference:
dde: pict usage:
First you need to move the camera to the place where the picture is to be taken.
The "dde: panc" command is used to center the game window on current Norn, but you can use any other camera movement command instead to take pictures of other elements if you wish.
Then you must issue the pict command.
It's format is :
"dde: pict W|H"
Where W and H are the raw values for expected picture width and height.
(Remember not to use a human readable numerical representation but raw values here.)
For example, if you want your pic 100x80 :
- chr(100)= "d"
- chr(80)= "P"
"dde: pict d|P"
The command returns the filename under which the pic was saved.
It happens to always be "temp.spr".
This file isn't a standard game .spr file.
It's format is:
So you need to read out it's width and height from the file, read the data chunk, and use those to compose a viewable image.(how to do that has already been covered in the .spr file format article)
Thanks for this super informative post! I just updated my CAOS plugin to reflect your API discovery. I have never seen any of this sort of reverse engineering before, it was enlightening.
ReplyDelete