1) Distinguishing between ozf2 and ozfx3 should be based on magic
number (first two bytes), not on extension. It's perfectly legit to
have ozfx3 format and ozf2 extension or vice-versa (one such file even
ships with OziExplorer). OZF2 files have magic 0x7778, OZFX3 files
have magic 0x7780. There are also files with 0x7779, but I didn't
encounter any of those and they are probably very rare.
2) Dword, that is call keyblock in the source code contains more than
just decryption byte. It's a signature. There are more of these and
here it's decided, whether the file can be exported (it's different
than in ozf2) and what the decryption depth will be (so there is no
need for bruteforcing it). There is a bunch of possible signatures, I
was able to identify 3 of them:
0x07A431F1 - Ozf2Img 3.00+; unrestricted saving; encryption depth: 16
0xE592C118 - MapMerge 1.05+; no saving; encryption depth: 16
0xC208F454 - MapMerge 1.04; no saving; encryption depth: full
0xB9F423C5 - ?
0x6A59B7A4 - ?
0xF9671483 - ?
0xB214A3C5 - ?
0xE6D4A238 - ?
0xD56207A9 - ?
0x59B7A443 - ?
0xF423C556 - ?
The ones with "?" are unknown, they may be probably various imported
images and some of them may have some restrictions based on that. The
only format, where the encryption depth is full is the third one. It's
16 everywhere else.
3) Decryption function could be simplified to this:
void ozf_decrypt(uchar *s, uint n, uchar key)
{
static uchar constants[] =
{
0x2D, 0x4A, 0x43, 0xF1, 0x27, 0x9B, 0x69, 0x4F,
0x36, 0x52, 0x87, 0xEC, 0x5F, 0x42, 0x53, 0x22,
0x9E, 0x8B, 0x2D, 0x83, 0x3D, 0xD2, 0x84, 0xBA,
0xD8, 0x5B
};
for(uint j = 0; j < n; j++)
s[j] ^= constants[j % sizeof(constants)] + key;
}
There is no need for two of them and for two different arrays. And it
can also be used to encrypt data - it's fully symmetrical.
4) Zoom/scales levels. You may be aware of that, but just in the case
you aren't... After the header and separator (0x77777777 in OZF2 and
two consecuting similar random words in OZFX3), there is a list of
zooms - not their offsets, like at the end of the file, but rather
their size (eg. 100%, 25%, etc). The table has this format:
short Zoom_Levels_Count
float Zoom_Levels[Zoom_Levels_Count + 2]
Yeah, those dwords are actually floats, took me a while to figure it
out. And they are not encrypted in ozfx3. The first one is always
100.0. And why there are two more than the count number suggests? The
last two ones are for preview - the first one is derived from image
resized, so that it's longest side is 130 pixels. The other one is the
same, but for 300 pixels.
5) You missed another part of the header in ozfx3, which is first 0xE
bytes. It's encrypted with the "initial" key at offset 0xA2 and if you
want to decrypt it, you need to start at offset 0 (which is magic
word). The format is exactly the same as in ozf2 file and it would be
good to actually unify the headers, because they are identical - with
one exception (random numbers, more about it later).
The format of those 0xE bytes is:
typedef struct
{
short magic; // set it to 0x7780 for ozfx3 and 0x7778 for
ozf2
long locked; // if set to 1, than ozi refuses to export the
image (doesn't seem to work for ozfx3 files though); just set to 0
short tile_width_height; // set always to 64
short check; // if not set to 1, ozi complains about wrong
version and refuses to load the image
long some_size_or_offset; // set always to 0x436; this has something
to do with files having magic 0x7779, it's header size of some offset
in this kind of files, not sure
} ozf_header_1;
Byte at 0xE is the total number of random bytes, that will follow.
Yes, all those numbers are just junk and can be safely modified
(except for byte at position 0xA2, which serves as initial key).
Minimum number of those random bytes is 0x96 (ozi checks for this
value, subtracts 96 from it and then use it as a size for reading file
- which crashes loading, if it gets negative).
6) The second part of the header. You have both parts together in
struct ozf2_header, but you missed part of it in ozf3_header. It has
format:
typedef struct
{
int header_size; // always 40
int image_width; // width of image in pixels
int image_height; // height of image in pixels
short depth; // set to 1
short bpp; // set to 8
int reserved1; // set to 0
int memory_size; // height * width; probably not used and contains
junk if it exceeds 0xFFFFFFFF (which is perfectly ok)
int reserved2; // set to 0
int reserved3; // set to 0
int unk1; // set to 0x100
int unk2; // set to 0x100
} ozf_header_2;
Again, this is identical in both versions - ozf2 and ozfx3.
Well, that's all for now. I have also created a program, that can
convert between ozf2 and ozfx3 (both directions). Feel free to use
some code from it, if needed:
http://rapidshare.com/files/326089547/OzfConverter.rar
There is probably no way, how to generate the last 5 using various ozi
programs. The 2 last should run only in OziCE, but I wasn't able to
actually make them work, so I can't verify, that encryption depth is
really 16. And when deciding what the decryption key is (in that
switch in ozf_decoder.cpp), you should exit on default (because
decryption will always fail) and not just break.
It has actually something to do with very old *.ozf files (magic
0x7777). It's a total size of the header + size of the palette (which
immediately follows header). After that, there is separator and
compressed tiles.