15

There are some unknow images, which I would like to decode to RRGGBBAA format. It was a really hard work, but at the moment I can somewhat understand, that which part of the binary is responsible for what. But I'm stuck at the end, and I have no idea how to continue it. This is what I've found out:

I know, that the size of the first image is a 9*3.

There is some kind of table at the begining of the file:

0x08 0x08 0x10
0x08 0x08 0x08
0x00 0x00 0x00
0x10 0x10 0x18
0x18 0x18 0x20
0x10 0x14 0x18
0x08 0x08 0x10

And this is the image data:

0x09 0x00 0x03 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x01 0x00 0x00 0x00
0x0c 0x00 0x00 0x00 0x00 0x01 0x03 0x09 0x01 0x01 0x02 0x11 0x01 0x01 0x02 0x02
0x01 0x0f 0x0f 0x03 0x0a 0x03 0x08 0x05 0x08 0x04 0x08 0x03 0x09 0x00 0x09 0x00
0x0c 0x00 0x02

The number of elements in the table can be divided by three, so I think it is some kind of RRGGBB palette.

But I have no idea, how to decode the imagedata. It's size is not 9*3, so It may be compressed. It is a really small image, and I think that this is why the compression made the binary bigger than it was before.


Edit:

I've uploaded this file here: Download file

I've colored it, to better understand it's structure.

The first part of the file:

hex1

  • The red section is just some kind of file signature.
  • The brown part is the file length in bytes. (1107)
  • The green shows, how many images are stored in the file (currently one)
  • The blue part shows, where the table begins ( 0x60 = at the 96th byte)
  • The grey part shows, how long the content in the table is. Currently it's 7, so the table has 7*3 = 21 bytes, the other values are CD
  • The table is marked with purple

The second part of the file:

hex2

  • The blue part is the header of the image.
  • The yellow part shows, how long the image is (51bytes)
  • The red is the image width, and the brown is the height (3*9)
  • The 8 zero bytes (selected with green) are the same in each file.

I've uploaded two more files.

The bigger file has a resolution of 800*600 And I suspect it is this one (screenshot):

bacground

At the beginning (from 3C0), only the the first two or three bytes are set in each 4 byte group. From 0xD28, I cannot recognise any pattern.


Edit2:

Spektre's code works with most of the files. But there are some small icons, with transparency, which look distorted.

For example this icon: Download

And this is how it looks like, over a brownish background:

icon

In this case, the scanlines are not fixed-width. And the unknown 252 flags at the beginning of each scanline, and 254 flags after every 32bytes are also different.

I can recognise patterns and symmetry in the binary, but I haven't figured out yet, how it works.

I've colored the scanlines of the icon's imagedata, to have a better overview:

icon hex

Many of them starts with, and all of them ends with 0x02


Edit3:

I uploaded some images, with screenshots:

I uploaded two more images, which I found distorted. The first is an icon, which is almost perfect. And the second one is a dragon, which is barely recognisable. Unfortunately I cannot provide screenshots for this two: Download

I implemented the core algorithm ( from Spektre's answer ) in JS. It can be found, and edited online here: JSFiddle link


Edit4:

I've made some progress with the mountain, and the dragon.

I think, that the first 7 bits of the flag byte shows, on which x coordinate the line starts ( ´xstart = flag>>1;´ ). The least significant bit is a switch, which marks, that the line has this offset or not. You can try/edit the current code here: JSFiddle link

The result is this:

mountain_progress

The expected result would be similar to this (which is not far):

mountain

The distorted parts around the mountain are shadow/transparency, but I still wasn't be able to find any marks/flags about where the blocks with alpha values start and end.


Edit5:

I think, I've found a pattern in the mountain image. After the flag byte, the next byte may show, (flag2>>1), how many (Color, Alpha) blocks are at the beginning of the line.

The left side of the mountain looks slightly better now:

mountain left

Unfortunately this change breaks the other images

  • I take it you don't know what the image actually looks like or anything? Without a header byte string, this will be tough to crack. – barrycarter Aug 7 '16 at 0:28
  • Is this all data in the file? Do you have more samples? (Preferably available for downloading.) – usr2564301 Aug 7 '16 at 7:23
  • I updated the question with more info, and downloadable samples – Iter Ator Aug 21 '16 at 5:38
  • @Spektre I will look at it in detail as soon as arrived home – Iter Ator Aug 25 '16 at 9:11
  • @Spektre The integer at 0x388-0x38B points everywhere to 0x3C0. The integer from 0x38C-0x38F shows, where the image data ends. The 16bit integers at 0x3C0 and 0x3C2 are width and height again. And the bytes from 0x3C4-0x3C7 are always zero. As I see, only the 0x37C-0x383 part - which is still unknown - is different in almost every image. – Iter Ator Aug 25 '16 at 14:25
3

I was able to decode the images. Spektre did a great job detecting the files structure, and the debug view was really helpful in the process. I implemented the algorithm in JS, and the source code is available here: https://github.com/K-Adam/DrekDecoder

Summary

Each line starts with a flag byte. It tells the decoder how many pixels to write and which mode to use. There are three possible write modes:

  • Opaque
  • Transparent
  • Skip

In opaque mode each pixel is represented by one byte, which references an RGB color in the palette. In transparent mode, the pixels are pairs of bytes: [Alpha, ColorIndex]

After each sequence a control byte follows. Its structure is different from the flag byte, and also different in each writing mode. This control byte will tell how many pixels to write and which mode to use next, or if the end of the line is reached.

Details

Flag byte

If the flag is zero (flag == 0x0), then the line is empty.

If the LSB is set (flag & 0b1) then there is an offset at the beginning of the line, and the decoder will start in Skip mode. The rest of the bits will represent the length of the offset (flag >> 1) + 1

If the second bit is set then transparent, otherwise opaque mode will be used:

mode = (flag & 0b10) ? Transparent : Opaque

The number of pixels can be found in the 5 MSB (flag >> 3) + 1

Skip mode

The decoder will simply write n empty pixels to the output. The next byte will be a control byte.

If LSB is set, then the decoder will switch to transarent mode. The other 7 bits represent the number of transparent pixels.

If the three LSB is 0b100 then the decoder will continue in Skip mode, otherwise it will change to Opaque mode. The other bits represent the number of pixels (vv >> 3) + 1

Opaque mode

Each data byte references an RGB color in the palette.

If the LSB is set, then the decoder will switch to transparent mode for (v1 >> 1) + 1 pixels. Otherwise the three LSB of the control byte is the flag:

  • 0b000 End of line
  • 0b110 Continue in opaque mode
  • 0b100 Switch to skip mode

The rest is the number of pixels (v1 >> 3) + 1.

Transparent mode

The transparent blocks are represented by sequence of pairs: [Alpha, Color]. The 5 MSB of the alpha byte is the transparency (0-32). The color byte references an RGB color in the palette.

The two LSB of the control byte is a flag:

  • 0b10 End of the line
  • 0b11 Switch to opaque mode
  • 0b01 Switch to skip mode
  • 0b00 Continue in transparent mode

The rest of the bits is the number of pixels (v1 >> 2) + 1, except for transparent mode, where it is (v1 >> 3) + 1

Decoding process

Since the colors are referenced by index, two bytes are needed to represent a transparent pixel. By looking at the bottom-left part of the mountain, it was clear from those alternating patterns, that it has to be transparent there. The colors around the round icons were also not right, so I suspected that the outer pixels are half transparent as well.

In the large image 0xFE appears after each 32 bytes. It does not reference a color from the palette, so it has to be some kind of control byte.

Then I started to write down the bytes in binary, where the color seemed not right. I grouped these values by where by where they appear, and then I spotted, that the least significant bits are similar on the boundary of transparent and opaque regions.

After further analysis, when I was able to decode the smaller icons by hand, I implemented the algorithm, and tested it for larger ones. From there, it was easy to eliminate the remaining anomalies in the result.

22
+50

[Complete ReEdit3] further progress & shortening the text to fit the 30KB limit

First some input how I got here (for future readers trying to do the same for different format).

  1. Image Data size

    Comparing provided background image to its raw image size xs*ys reveal direct dependence Which implies no compression or one that has always the same pixel to data ratio. I was assuming No compression.

  2. Image Data align.

    First numbers are DWORD aligned with zero padding. By closer look it is always DWORD[ys] so that is implying some info per each ScanLine. The numbers are increasing with constant step. For 800x600 image With step 826 Implying it is ScanLine directory containing relative offsets. Tests confirms it:

    patterns

    The small images are too small to see any details so I ignore them completely.

    The vertical lines are the 26 BYTES inconsistency with xs starting from first BYTE of ScanLine containing some kind of info. After more taught and pixel measuring these lines appears each 33 BYTES probably containing some flag info (like: tile highlight,transparency or effect masks). If skipped The result is closer:

    colors

  3. Colors

    I simply took your ScreenShot and compare first decoded pixel with its. Find the color index in palette matching the color from each image and spotted the difference is simple increment. So I firstly increment the color index while decoding And result was good. After some research I found out that palette data has 257 colors instead of assumed 256 at first so I change the palette offset to compensate (no need for color index change anymore) Implying the first color entry (skipped by offset change) is some kind of special color (see bullet #1). Latter I deduced it is not color at all and does not belong to palette data but to header data. Now the result is:

    final output

    If I would not spot the color dependency then I would do histogram for decoded and screenshot image and then analyze dependency on most occuring colors indexes hoping to spot some pattern.

  4. Comparison

    I pixel match the decoded image with your ScreenShot but the images are not matching. Possibly some post processing or lighting was applied to it or there is some mistake in the decoding. It is hard to decide as I have no access to the original App and or knowledge of its inner workings. You should check more Images to see if there are some artifacts or not. The small images are unusable as you do not know the content and also the size is small for any significant details to reveal ...

  5. Transparent images

    Well Iter Ator spotted that some files are not decoded properly and deduced that it has something to do with transparency (as the images was icons and later also sprites). So some more in depth analysis was take in place to make some progress.

    After some thinking and looping in circles I create special debug views so I can see encoded image data , reference screenshot and the corresponding palette colors in some convenient way to spot patterns or dependency. The result was something like this:

    manual color cross-reference

    I simply render each pixel/BYTE of data as square with color corresponding to the palette entry and render the hex code inside. I render encoded ScanLine and screenshot ScanLine below (with cross matched color code from image palette printed inside. Then I painfully cross-reference both ScanLines in Paint and after comparing all the icons I got start deducing encoding format. The gaps means some kind of flag or prefix, sequence or special command. After implementing these rules into decoder then suddenly the images got looking much better.

    I got in a state where all the icons I got was pixel perfect but after more images came in (sprites) it was obvious that some codes may be variable and more in depth analysis of the chunks in file is needed to search for some code table or dictionary.

    To help with the analysis I rewrite the decoder debug view to separate class with capability to recognize perfect color match (square) and close color match (circle) to better asses the codes on non transparent pixels first.

    better debug view

    I started with rules like this:

    From it I deduced this (not fully OK see the code for actual state):

    first 2 Bytes are flags 03 unknonw flag/command skip flag can follow!

    • also end sequence 23 (flag and pixel follows as 01) 02 end of scanline 01 flag and pixel follows, end of sequence (if not in first 5 pixels) start of sequence: 13,17,23,27,2B pixel follows: 0D,0F,12,15,16,18,1A,1C,1D,1E flag or pixel follows: 06,07,0C,0E?,10

    • sequence can not start as first pixel otherwise it is pixel

    • sequence can stop after 5 pixels already done else 01/03 is just pixel

    I improved it much more later directly in the decoder

  6. ScanLine offsets

    As I find out if the first BYTE of ScanLine was used as offset for transparent images some of the images looks much better. Ite Ator found out that it is the offset and LSB bit is switch of this capability. I found out that if the first BYTE of first ScanLine is 0xFC then it is not transparent image and the decoding is described in first bullets. I introduced mode 0/1 variable to recognize this in the decoder. And The offset is active only in mode 1 (transparency). Also I found out that the offset=(flag>>1)+1 as it makes more sense from encoding efficiency side (test confirms it was correct assumption).

  7. File chunks

    In search for missing code table or dictionary I started to dissect the file-format (each BYTE that is not used yet). After some coding and analysis I added another debug output to decoder that creates text for each file/frame in aligned form so I can easily compare between all the images I got as comparing in two ro more hex-views is not very convenient.

    Here the header data:

    file                          K  E  R  D              file_size        frames image_ofs                                                                                                                                                                                                   palette_ofs used_colors
    dragon              00000000: 4B 45 52 44 C8 00 00 00 A1 BF 01 00 01 00 0B 00 60 03 00 00 01 00 00 00 54 00 00 00 00 00 00 00 00 00 00 00 04 00 00 00 00 00 2E 00 0A 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 60 00 00 00 00 01 00 00 18 CD CD CD 
    mountain            00000000: 4B 45 52 44 C8 00 00 00 58 8C 01 00 01 00 02 00 60 03 00 00 01 00 00 00 54 00 00 00 00 00 00 00 56 8A 01 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 60 00 00 00 00 01 00 00 18 CD CD CD 
    icon0               00000000: 4B 45 52 44 C8 00 00 00 4F 05 00 00 01 00 01 00 60 03 00 00 01 00 00 00 54 00 00 00 00 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 60 00 00 00 5A 00 00 00 18 CD CD CD 
    icon1               00000000: 4B 45 52 44 C8 00 00 00 4F 05 00 00 01 00 01 00 60 03 00 00 01 00 00 00 54 00 00 00 00 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 60 00 00 00 59 00 00 00 18 CD CD CD 
    icon2               00000000: 4B 45 52 44 C8 00 00 00 4D 05 00 00 01 00 01 00 60 03 00 00 01 00 00 00 54 00 00 00 00 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 60 00 00 00 50 00 00 00 18 CD CD CD 
    icon3               00000000: 4B 45 52 44 C8 00 00 00 46 0B 00 00 01 00 01 00 60 03 00 00 01 00 00 00 54 00 00 00 00 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 60 00 00 00 00 01 00 00 18 CD CD CD 
    back                00000000: 4B 45 52 44 C8 00 00 00 78 9D 07 00 01 00 01 00 60 03 00 00 01 00 00 00 54 00 00 00 00 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 60 00 00 00 00 01 00 00 18 CD CD CD 
    

    And here the ImageData:

    file                                                                    frame sz          xs          ys                                                                                                                                                                                                                      xs    ys                
    dragon              00000360: 00 00 00 00 00 00 00 00 00 00 00 00 08 CD 01 00 04 25 00 00 B2 00 00 00 5B 00 00 00 E5 FF FF FF B3 FF FF FF 00 00 00 00 C0 03 00 00 C4 28 00 00 00 00 00 00 00 CD CD CD 00 00 00 00 04 CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD B2 00 5B 00 00 00 00 00 
                        000028C4: 00 00 00 00 00 00 00 00 00 00 00 00 08 00 02 00 A1 26 00 00 B6 00 00 00 5B 00 00 00 DD FF FF FF B4 FF FF FF 00 00 00 00 24 29 00 00 C5 4F 00 00 60 03 00 00 00 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 B6 00 5B 00 00 00 00 00 
                        00004FC5: 00 00 00 00 00 00 00 00 00 00 00 00 08 00 03 00 57 27 00 00 C4 00 00 00 5E 00 00 00 D6 FF FF FF B1 FF FF FF 00 00 00 00 25 50 00 00 7C 77 00 00 C4 28 00 00 00 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 C4 00 5E 00 00 00 00 00 
                        0000777C: 00 00 00 00 00 00 00 00 00 00 00 00 08 00 04 00 26 28 00 00 BF 00 00 00 64 00 00 00 D3 FF FF FF AA FF FF FF 00 00 00 00 DC 77 00 00 02 A0 00 00 C5 4F 00 00 00 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 BF 00 64 00 00 00 00 00 
                        0000A002: 00 00 00 00 00 00 00 00 00 00 00 00 08 00 05 00 70 29 00 00 C3 00 00 00 67 00 00 00 D0 FF FF FF A6 FF FF FF 00 00 00 00 62 A0 00 00 D2 C9 00 00 7C 77 00 00 00 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 C3 00 67 00 00 00 00 00 
                        0000C9D2: 00 00 00 00 00 00 00 00 00 00 00 00 08 00 06 00 DA 29 00 00 C6 00 00 00 6C 00 00 00 CF FF FF FF A2 FF FF FF 00 00 00 00 32 CA 00 00 0C F4 00 00 02 A0 00 00 00 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 C6 00 6C 00 00 00 00 00 
                        0000F40C: 00 00 00 00 00 00 00 00 00 00 00 00 08 00 07 00 33 2A 00 00 C5 00 00 00 6F 00 00 00 D1 FF FF FF 9F FF FF FF 00 00 00 00 6C F4 00 00 9F 1E 01 00 D2 C9 00 00 00 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 C5 00 6F 00 00 00 00 00 
                        00011E9F: 00 00 00 00 00 00 00 00 00 00 00 00 08 00 08 00 1A 2B 00 00 C0 00 00 00 6C 00 00 00 D4 FF FF FF A2 FF FF FF 00 00 00 00 FF 1E 01 00 19 4A 01 00 0C F4 00 00 00 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 C0 00 6C 00 00 00 00 00 
                        00014A19: 00 00 00 00 00 00 00 00 00 00 00 00 08 00 09 00 59 29 00 00 BE 00 00 00 68 00 00 00 DA FF FF FF A7 FF FF FF 00 00 00 00 79 4A 01 00 D2 73 01 00 9F 1E 01 00 00 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 BE 00 68 00 00 00 00 00 
                        000173D2: 00 00 00 00 00 00 00 00 00 00 00 00 08 00 0A 00 E5 26 00 00 B5 00 00 00 62 00 00 00 E1 FF FF FF AB FF FF FF 00 00 00 00 32 74 01 00 17 9B 01 00 19 4A 01 00 00 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 B5 00 62 00 00 00 00 00 
                        00019B17: 00 00 00 00 00 00 00 00 00 00 00 00 08 00 0B 00 CA 23 00 00 AB 00 00 00 5A 00 00 00 EA FF FF FF B3 FF FF FF 00 00 00 00 77 9B 01 00 41 BF 01 00 D2 73 01 00 00 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 AB 00 5A 00 00 00 00 00 
    mountain            00000360: 00 00 00 00 00 00 00 00 00 00 00 00 08 CD 01 00 A7 BA 00 00 37 01 00 00 D3 00 00 00 00 00 00 00 C9 FF FF FF 00 00 00 00 C0 03 00 00 67 BE 00 00 00 00 00 00 00 CD CD CD 00 00 00 00 04 CD CD CD 00 00 00 00 CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD 37 01 D3 00 00 00 00 00 
                        0000BE67: 00 00 00 00 00 00 00 00 00 00 00 00 08 00 02 00 2B C7 00 00 3C 01 00 00 DD 00 00 00 FC FF FF FF C4 FF FF FF 00 00 00 00 C7 BE 00 00 F2 85 01 00 60 03 00 00 00 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 3C 01 DD 00 00 00 00 00 
    icon0               00000360: 00 00 00 00 00 00 00 00 00 00 00 00 08 CD 01 00 2F 01 00 00 0D 00 00 00 0D 00 00 00 11 00 00 00 F9 FF FF FF 00 00 00 00 C0 03 00 00 EF 04 00 00 00 00 00 00 00 CD CD CD 00 00 00 00 04 CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD 0D 00 0D 00 00 00 00 00 
    icon1               00000360: 00 00 00 00 00 00 00 00 00 00 00 00 08 CD 01 00 2F 01 00 00 0D 00 00 00 0D 00 00 00 11 00 00 00 F9 FF FF FF 00 00 00 00 C0 03 00 00 EF 04 00 00 00 00 00 00 00 CD CD CD 00 00 00 00 04 CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD 0D 00 0D 00 00 00 00 00 
    icon2               00000360: 00 00 00 00 00 00 00 00 00 00 00 00 08 CD 01 00 2D 01 00 00 0D 00 00 00 0D 00 00 00 11 00 00 00 F9 FF FF FF 00 00 00 00 C0 03 00 00 ED 04 00 00 00 00 00 00 00 CD CD CD 00 00 00 00 04 CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD 0D 00 0D 00 00 00 00 00 
    icon3               00000360: 00 00 00 00 00 00 00 00 00 00 00 00 08 CD 01 00 26 07 00 00 26 00 00 00 26 00 00 00 05 00 00 00 ED FF FF FF 00 00 00 00 C0 03 00 00 E6 0A 00 00 00 00 00 00 00 CD CD CD 00 00 00 00 04 CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD 26 00 26 00 00 00 00 00 
    back                00000360: 00 00 00 00 00 00 00 00 00 00 00 00 08 CD 01 00 58 99 07 00 20 03 00 00 58 02 00 00 08 00 00 00 B4 FD FF FF 00 00 00 00 C0 03 00 00 18 9D 07 00 00 00 00 00 00 CD CD CD 00 00 00 00 04 CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD 20 03 58 02 00 00 00 00 
    

    revealing the structure better. I found out some things from this like frame index and the ImageData align is to 0x0360 instead of 0x0370 I assumed before this. Also 0xCD pattern is present only for first frames and not at all positions. The file-format is like this:

    Offset              DataType                Meaning
    --------------------------------------------------------
    Header (0x0000):
    0x0000              char[4]                 `DREK` Signature
    0x0008              DWORD                   whole file size
    0x000E              WORD                    number of frames
    0x0010              DWORD                   ImageData offset from start of file 0x0360
    0x0054              DWORD                   Palette offset from start of file 0x0060
    0x0058              DWORD                   used_colors from Palette (rest are set to (CD,CD,CD))
    --------------------------------------------------------
    Palette: (0x060)
    +0x0000             BYTE[768]               RGB palette
    --------------------------------------------------------
    ImageData (0x0360)
    +0x000E             WORD                    frame 1,2,3,4,...
    +0x0010             DWORD                   sz (ImageData size - 0x60)
    +0x0014             DWORD                   xs
    +0x0018             DWORD                   ys
    +0x0060             WORD                    xs
    +0x0064             WORD                    ys
    +0x0068             DWORD[ys]               ScanLine offsets relative to end of this table (0x0068 + ys*4)
    +0x0068+(ys*4)      BYTE[sz-68-(ys*4)]      ScanLineData
    +0x0060+sz                                  Next frame ImageData if frame<frames
    

Now finally The decoder

The code was written in Borlands BDS2006 C++ using VCL TBitmap and AnsiString So you need to rewrite it to your kind of gfx access and strings your programming environment supports. If you need help with the meaning of the gfx code see:

Also File access functions may have a bit different name. I commented the code a bit but it should be obvious anyway. At least for those that already did things like this.

The defines just switch between debug and normal decoding. If not present then you do not need the debug image class img at all. To keep this simple and also in 30KB limit I removed the code for the debug draw class (those who want/need it look in the edit history). That means ignoring all the _debug_draw defines ...

Now the icons defined pixels are decoded correctly. But I did a lot of assumptions and may not work on all images. I would need more examples with ScreenShots to make it more robust. The codes itself should encode alpha channel and may be also position/offset.

I changed few codes and now I am temporarily ignoring the 0x02 end of ScanLine which make mess when not properly decoded codes are present. Also I added few special cases I found out. The x-offset for mode1 is also present.

In the code find line:

// image data (decoded)

That is where main decoding is. The decoding variables are:

  • _pixel - next BYTE is pixel
  • _sequence - sequence of pixels follows (ending by 0x01 or 0x03 if more then 5 pixels rendered)
  • _stop - end of scanline marked by 0x02
  • seq - just counter of how many pixels in sequence has been processed

In comments some of the codes are marked // OK those are not conflicting and straightforward. The rest of comments means what codes are following:

  • pXX means pixel 0xXX
  • cXX means code 0xXX
  • ?XX means not sure if pixel or code
  • ?pXX means most likely pixel 0xXX

The Transparent ScanLine data starts after first BYTE so do not skip 2 BYTEs just one !!!

Here some previews:

new previews

And the Dragon frames:

dragon frames

The C++ source for the decoder:

#ifdef _debug_save
AnsiString dbg_hdr="file                          K  E  R  D              file_size        frames image_ofs                                                                                                                                                                                                   palette_ofs used_colors\r\n";
AnsiString dbg_img="                                                                        frame sz          xs          ys                                                                                                                                                                                                                      xs    ys                \r\n";
#endif
void load_drk(Graphics::TBitmap *bmp,AnsiString name)
    {
    AnsiString s,fnam;
    fnam=name.SubString(1,name.Length()-4);

    // variables
    BYTE *dat;
    int hnd,adr,adr0,siz;
    int i,x,y,xs,ys,sz,mode,frame,frames;
    DWORD *p,*dir,pal[256],r,g,b,a;
    // allow direct pixel access
    bmp->HandleType=bmDIB;
    bmp->PixelFormat=pf32bit;
    // read file into memory
    hnd=FileOpen(name,fmOpenRead);
    if (hnd<0) return;
    siz=FileSeek(hnd,0,2);
        FileSeek(hnd,0,0);
    dat=new BYTE[siz];
    if (dat==NULL) { FileClose(hnd); return; }
    FileRead(hnd,dat,siz);
    FileClose(hnd);
    // decode
    for (;;)
        {
        #ifdef _debug_save
        // save frames to bmp
        s=fnam+"                    ";
        s=s.SubString(1,20);
        dbg_hdr+=s;
        dbg_img+=s;
        dbg_hdr+="00000000: "; for (i=0;i<0x60;i++) { s=s.sprintf("%X ",dat[i]); while (s.Length()<3) s="0"+s; dbg_hdr+=s; }
        dbg_hdr+="\r\n";
        #endif

        // signature
        if (siz<0x3C8) break;
        if (((DWORD*)(dat))[ 0]!='DREK') break;
        if (((DWORD*)(dat))[ 2]!=   siz) break;
        // palette
        for (adr=0x060,i=0;i<256;i++)
            {
            b=dat[adr]; adr++;
            g=dat[adr]; adr++;
            r=dat[adr]; adr++;
            pal[i]=(r<<16)|(g<<8)|(b);
            }
        // frames
        frames=((WORD*)(dat+0x0E))[0];
        adr0  =((DWORD*)(dat))[4];
        for (frame=0;frame<frames;adr0+=sz+0x60,frame++)
            {
            adr=adr0;
            // resolution
            if (adr0+0x60>siz) break;
            sz=((DWORD*)(dat+adr))[4];
            xs=((DWORD*)(dat+adr))[5];
            ys=((DWORD*)(dat+adr))[6];
            if (adr0+0x60+sz>siz) break;
            bmp->SetSize(xs,ys);

            #ifdef _debug_save
            // save frames to bmp
            if (frame) dbg_img+="                    ";
            s=s.sprintf("%X: ",adr0); while (s.Length()<10) s="0"+s; dbg_img+=s;
            for (i=adr0;i<adr0+0x68;i++) { s=s.sprintf("%X ",dat[i]); while (s.Length()<3) s="0"+s; dbg_img+=s; }
            dbg_img+="\r\n";
            #endif

            // scanline table
            dir=new DWORD[ys+1];
            if (dir==NULL) break;
            adr+=0x68;
            for (y=0;y<ys;y++)
             dir[y]=adr+(ys<<2)+((DWORD*)(dat+adr))[y];
            dir[ys]=adr0+sz+0x68;

            #ifdef _debug_draw
            // set size and palette of debug image
            img.resize(xs,ys);
            for (i=0;i<256;i++) img.rgb_pal[i]=pal[i];
            // copy reference data to debug image
            for (y=0;y<ys;y++) img.p[y].ref="";
            if ((png->Height)&&(png->Width))
                {
                Graphics::TBitmap *qqq=new Graphics::TBitmap;
                qqq->Assign(png);
                qqq->HandleType=bmDIB;
                qqq->PixelFormat=pf32bit;
                for (y=0;(y<qqq->Height)&&(y<ys);y++)
                    {
                    img.p[y].ref="";
                    img.p[y].cmp="";
                    p=(DWORD*)qqq->ScanLine[y];
                    for (x=0;(x<qqq->Width)&&(x<xs);x++)
                        {
                        for (a=0,b=256,g=768;a<256;a++)
                            {
                            r=rgb_absdistance(p[x],pal[a]);
                            if (g>r) { g=r; b=a; }
                            } a=b;
                        if (g) g='1'; else g='0';
                        img.p[y].ref+=char(BYTE(a));
                        img.p[y].cmp+=char(BYTE(g));
                        }
                    }
                delete qqq;
                }
            // copy encoded data to debug image
            for (y=0;y<ys;y++)
                {
                adr=dir[y];
                img.p[y].adr=adr;
                img.p[y].enc="";
                img.p[y].dec="";
                img.p[y].spc="";
                for (;adr<dir[y+1];adr++)
                 img.p[y].enc+=char(BYTE(dat[adr]));
                }
            #endif

            // image data (decoded)
            a=dat[dir[0]];
            mode=0; if (a!=0xFC) mode=1;
            for (y=0;y<ys;y++)
                {
                adr=dir[y];                     // get actual ScanLine offset in file
                p=(DWORD*)bmp->ScanLine[y];     // get actual ScanLine pointer in bmp
                int _sequence=0;
                int _pixel=0;
                int _stop=0;
                int seq=0;
                for (x=0;x<xs;x++) p[x]=0; x=0;

                if (mode==1)
                    {
                    // 1st ScanLine BYTE (x-offset)
                    a=dat[adr]; adr++;
                    if (BYTE(a&1)==1)
                        {
                        x=(a>>1)+1;
                        #ifdef _debug_draw
                        for (i=0;i<x;i++) img.p[y].spc+='\0';
                        #endif
                        }
                    // 2nd ScanLine BYTE special cases
                    a=dat[adr];
                    if (a==0x0D) adr++; 
                    }
                for (a=0;adr<dir[y+1];)
                    {
                    if (mode==0)
                        {
                        a=dat[adr]; adr++;              // color index
                        if (int(x&31)==0)
                            {
                            r=a;                        // flag
                            a=dat[adr]; adr++;          // color index
                            }
                        }
                    if (mode==1) for (;;)
                        {
                        a=dat[adr]; adr++;              // color index
                        if (_pixel) { _pixel=false; break; }
                        if (_sequence) { seq++; if (seq<6) break; }
                        // commands
                        if (a==0x01)
                            {
                            a=dat[adr]; adr++;
                            _pixel=true; _sequence=false;
                            //if (a==0x07) { adr++; _pixel=false; }
                            continue;
                            }
                        if (a==0x03)
                            {
                            if (!_sequence)
                                {
                                if (dat[adr]==0x18) _pixel=1;
                                continue;
                                }
                            if (_sequence==0x2B) break;
                            a=dat[adr]; adr++;
                            _pixel=true; _sequence=false;
                            //if (a==0x07) { adr++; _pixel=false; }
                            continue;
                            }
                        if (_sequence) break;
                        // unused: 0A,14,1F,20,21,22,24,25,26,28,29,2A,2C+
                        // flag/color prefix
    //                  if((a==0x02)&&(adr>dir[y]+3)) { _stop=true; break; }    // end of ScanLine
                        if (a==0x04) { _pixel=1; continue; }    // ?? ?p18 p47 ?p47
                        if (a==0x05) { _pixel=1; continue; }    // ?? ?18 ?09
                        if (a==0x06) { _pixel=1; continue; }    // ?? ?p18 ?p47
                        if (a==0x07) { _pixel=1; continue; }    // ?? ?18 ?46 ?47
                        if (a==0x08) { _pixel=1; continue; }    // ?? ?09
                        if (a==0x09) { _pixel=0; continue; }    // ?? ?p02 !!!!!!!!!!!
                        if (a==0x0B) { _pixel=1; continue; }    // ?? ?p02
                        if (a==0x0C) { _pixel=1; continue; }    // OK p1D
                        if (a==0x0D)                            // ?? c04 p23 ?c06 p05 p12
                            {
                            _pixel=0;
                            if (dat[adr]==0x02) _pixel=1;
                            if (dat[adr]==0x05) _pixel=1;   // p47 c0D p05 | c0D c05 p09
                            if (dat[adr]==0x12) _pixel=1;
                            if (dat[adr]==0x23) _pixel=1;
                            continue;
                            }
                        if (a==0x0E) { _pixel=1; continue; }    // OK p24 p26 p16
                        if (a==0x0F) { _pixel=1; continue; }    // OK p0F
                        if (a==0x10) { _pixel=1; continue; }    // OK p04
                        if (a==0x11) { _pixel=1; continue; }    // ?? ?p02
                        if (a==0x12) { _pixel=1; continue; }    // OK p27
                        if (a==0x15) { _pixel=1; continue; }    // OK p16
                        if (a==0x16) { _pixel=1; continue; }    // OK p16
                        if (a==0x18) { _pixel=1; continue; }    // OK p00 p02
                        if (a==0x19)                            // ?? p1B
                            {
                            _pixel=1;
                            if (dat[adr]==0x01) _pixel=0;
                            continue;
                            }
                        if (a==0x1A) { _pixel=1; continue; }    // OK p05 p04
                        if (a==0x1B) { _pixel=1; continue; }    // OK p1F
                        if (a==0x1C) { _pixel=1; continue; }    // OK p10
                        if (a==0x1D) { _pixel=1; continue; }    // OK p33 p1E p14
                        if (a==0x1E) { _pixel=1; continue; }    // OK p34 p50 p13

                        // test from dragon and mountain
                        if (a==0x49) { _pixel=1; continue; }    // ?? ?p02
                        if (a==0xE3) { _pixel=1; continue; }    // ?? p18
                        if (a==0xD7) { _pixel=1; continue; }    // ?? pBC
                        if (a==0xDA) { _pixel=1; continue; }    // ?? ?p02

                        // sequence start
                        if (a==0x13) { seq=0; _sequence=a; } // 03
                        if (a==0x17) { seq=0; _sequence=a; } // 01
                        if (a==0x23) { seq=0; _sequence=a; } // 01 03
                        if (a==0x27) { seq=0; _sequence=a; } // 01
                        if (a==0x2B) { seq=0; _sequence=a; } // 01 !03 03
                        if (_sequence)
                            {
                            if (adr==dir[y]+3) { _sequence=false; break; }
                            continue;
                            }
                        break;
                        }
                    if (_stop) break;
                    if (x>xs) break;
                    if ((mode==1)&&((a==0xFE)||(a==0xFF))) { a=dat[adr]; adr++; }   // shadows?

                    if ((x>=0)&&(x<xs)) p[x]=pal[a]; x++;
                    #ifdef _debug_draw
                    // copy decoded data to debug image
                    img.p[y].dec+=char(BYTE(a));
                    #endif
                    }
                }
            delete[] dir; dir=NULL;
            #ifdef _debug_save
            // save frames to bmp
            s=frame;
            while (s.Length()<3) s="0"+s;
            png->Assign(bmp);
            png->SaveToFile("decoded_"+fnam+"_"+s+".png");
            #endif
            }
        img.compute();
        break;
        }
    delete[] dat;
    }

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.