In contrast to fonts on these phones, extracting the bitmaps used throughout the system for menus and indicators is quite a bit simpler. Each graphic is separate from one another, and its metadata is easy to read.

Data Structure

As shown in Extract Nokia DCT3 Fonts, pixel data for graphics are organized into 8 pixel-high columns, each represented by a byte. The following bitmap is 22×26px, and is shown split into said 8-pixel columns.

This bitmap requires 22 bytes for its width, and 4 bytes for its height, totaling 88 bytes. The two bottom rows of pixels are still split into columns of 8 pixels, even though only 2 pixels are used, so the remaining 6 non-existent pixels are assumed to be 0. Within the firmware, the bitmap’s bytes are ordered from left to right, top to bottom.

This is how it’s represented:

00 00 00 F8 04 04 04 04 04 04 04 04 04 04 04 04 08 10 E0 00 00 00
00 00 00 FF 00 FE 01 01 01 3D 25 3D 25 3D 01 FE 00 00 FF 00 00 00
00 00 00 7F 80 9F A0 AF A9 AF A9 AF A9 AF A0 9F 80 80 7F 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

Locating the bitmaps

NokiX has the ability to modify and insert system graphics on DCT3 phones, and does so by looking for a certain pattern of bytes within the firmware. This can be seen in the LOCATE_AUTO.rx macro script, on line 386.

else if func="GET_BITMAP" then do
        patt=x2c(420CD1071C011C4804000C00)
        mask=x2c(FFF0FFF0FF00FFF0F0F0F0F0)
        searchback=1
end

This searches the firmwares for a string of bytes whose bits are equal to the bits in patt where mask is F. Why not use NokiX then? Well, NokiX hasn’t seen active development in close to a decade, and this code doesn’t actually work on all firmwares of phones from the DCT3 generation. If a Nokia 5210, 5510, 6210, 6250, or 7110 firmware is used, NokiX can’t find a string of bytes that satisfy the above conditions.

Here I use the Nokia 3310’s v4.18 firmware as an example. The string that fits the criteria set out by NokiX is 42 03 D1 07 1C 20 1C 41 04 09 0C 0C , but what does this mean? These bytes represent THUMB machine code for the ARM7TDMI processor present in the Nokia 3310, so if we pass the firmware through a disassembler such as objdump, we can see that these are part of a subroutine:

30e6e:  b510    PUSH    {R4, LR}
30e70:  49ef    LDR     R1, [PC, #956] ;(0x31230)
30e72:  0040    LSL     R0, R0, #1
30e74:  180a    ADD     R2, R1, R0
30e76:  8814    LDRH    R4, [R2, #0]
30e78:  48f0    LDR     R0, [PC, #960] ;(0x3123c)
30e7a:  7803    LDRB    R3, [R0, #0]
30e7c:  48ed    LDR     R0, [PC, #948] ;(0x31234)
30e7e:  5d00    LDRB    R0, [R0, R4]
30e80:  4203    TST     R3, R0
30e82:  d107    BNE     0x30e94
30e84:  1c20    ADD     R0, R4, #0
30e86:  1c41    ADD     R1, R0, #1
30e88:  0409    LSL     R1, R1, #16
30e8a:  0c0c    LSR     R4, R1, #16
30e8c:  8851    LDRH    R1, [R2, #2]
30e8e:  4288    CMP     R0, R1
30e90:  d1f4    BNE     0x30e7c
30e92:  2400    MOV     R4, #0
30e94:  200c    MOV     R0, #12
30e96:  4360    MUL     R0, R4
30e98:  49e7    LDR     R1, [PC, #924] ;(0x31238)
30e9a:  1808    ADD     R0, R1, R0
30e9c:  bd10    POP     {R4, PC}

Of interest here are the PC-relative LDRs, which point to memory addresses relative to the program counter, shown below:

31230:  002e fc54
31234:  002e faf8
31238:  002e eaa8
3123c:  0011 fc95

From what I can tell, for DCT-3 phones, the memory range 0x00000000 to 0x001fffff points to RAM, and the memory range 0x00200000 and beyond points to the flash ROM. Any memory addresses referenced within the machine code are hence offset by 0x00200000 compared to the addresses shown on the left by the disassembler. With this knowledge, the fourth LDR can be ignored, as its value is 0x0011fc95, which is in RAM.

The three remaining memory addresses pointed to by the LDRs lead to data on the bitmaps.

0x002efc54 — List Block

The first LDR points to a list of what I believe to be 2-byte IDs for each bitmap in the system.

0x002efaf8 — FF Block

The second LDR points to a block of data containing mostly FF. In some firmwares, instead of FF, the block is filled with mostly 00 FF.

efaf8:  FF FF FF FF FF FF FF FF FF FF FF FF
efb04:  FF FF FF FF FF FF FF FF FF FF FF FF
efb10:  FF FF FF FF FF FF FF FF FF FF FF FF
efb1c:  FF FF FF FF FF FF FF FF FF FF FF FF
efb28:  FF FF FF FF FF FF 56 FF FF FF FF FF
efb34:  FF FF FF FF FF FF FF FF FF FF FF FF
efb40:  FF FF FF FF FF FF FF FF FF FF FF FF
efb4c:  FF FF FF FF FF FF FF FF FF FF FF FF
efb58:  FF FF FF FF FF FF FF FF FF FF FF FF
efb64:  FF FF FF FF FF FF FF FF FF FF FF FF
efb70:  FF FF FF FF FF FF FF FF FF FF FF FF
efb7c:  FF FF FF FF FF FF FF FF FF FF FF FF
efb88:  FF FF FF FF FF FF FF FF FF FF FF FF
efb94:  FF FF FF FF FF FF FF FF FF FF FF FF
efba0:  FF FF FF FF FF FF FF FF FF FF FF FF
efbac:  FF FF FF FF FF FF FF FF FF FF FF FF
efbb8:  FF FF FF FF FF FF FF FF FF FF FF FF
efbc4:  FF FF FF FF FF FF FF FF FF FF FF FF
efbd0:  FF FF FF FF FF FF FF FF FF FF FF FF
efbdc:  FF FF FF FF FF FF FF FF FF FF FF FF
efbe8:  FF FF FF FF FF FF FF FF FF FF FF FF
efbf4:  FF FF FF FF FF FF FF FF FF FF FF FF
efc00:  FF FF FF FF FF FF FF FF FF FF FF FF
efc0c:  FF FF FF FF FF FF FF FF FF FF FF FF
efc18:  FF FF FF FF FF FF FF FF FF FF FF FF
efc24:  FF FF FF FF FF FF FF FF FF FF FF FF
efc30:  FF FF FF FF FF FF FF FF FF FF FF FF
efc3c:  FF FF FF FF FF FF FF FF FF FF FF FF
efc48:  FF FF FF FF FF FF FF FF FF FF FF FF

0x002eeaa8 — Meta Block

The last LDR points to the most important block of data, where the memory address and resolution for each bitmap is located. It is split into 12-byte strings, most of which represent a bitmap, in the following format:

...
00 2E 2D BC  00 2E 10 80  11 06  00 00
00 2E 2D D0  00 2E 10 80  12 07  00 00
00 2E 2D E4  00 2E 10 80  09 07  00 00
00 2E 2D F0  00 2E 10 80  0B 07  00 00
00 2E 2D FC  00 2E 10 80  0A 07  00 00
00 2E 2E 08  00 2E 10 80  0C 07  00 00
00 2E 2E 14  00 2E 10 80  15 07  00 00
00 2E 2E 2C  00 2E 10 80  19 07  00 00
00 2E 2E 48  00 2E 10 80  1E 07  00 00
00 2E 2E 68  00 2E 10 80  03 05  00 00
00 2E 2E 6C  00 2E 10 80  03 05  00 00
00 2E 2E 70  00 2E 10 80  13 07  00 00
...

The first column is the memory address at which the pixel data for that particular bitmap is located at, unless it is 00 00 00 00, in which case the 12 bytes can be ignored.

The second column is only useful in some cases — in certain firmwares, there isn’t an LDR in the subroutine that points to a memory address with the address to the meta block with pixel location and resolution. In those cases, this column points to a memory address at which the following 4 bytes from it correspond to the memory address of the beginning of the meta block. In this firmware, that LDR is present, so the contents of 0x002e1080 don’t seem to relate to bitmaps.

The third column contains the width and height of the bitmap. The first byte is the width, the second byte is the height.

The fourth column seems to always remain at 00 00.

Firmware differences

In some firmwares, such as the 3410’s, some encoding is done in an attempt to save some space. This affects the data structure outlined at the beginning, as it is no longer simply pixel values.

In these firmwares, two 8-pixel columns are paired up with a third byte which describes how many of these 8-pixel columns are repeated in a row.

When looking at the top row of the same image shown earlier, we can see that the first 3 columns are repeated, and the fourth one does not. With this new encoding, instead of describing the 4 first columns with 00 00 00 F8, it is now written as 31 00 F8, saving 1 byte.

With this encoding scheme, the same image can be written like this:

31 00 F8 C1 04 08 11 10 E0 31 00 FF 11 00 FE
31 01 3D 11 25 3D 11 25 3D 11 01 FE 21 00 FF
30 00 00 31 00 7F 11 80 9F 11 AF A9 11 AF A9
11 AF A9 11 AF A0 12 9F 80 30 00 00 F7 00 00

This takes up a total of 60 bytes, compared to the un-encoded version’s 88 bytes.

Producing bitmaps

Just like with extracting DCT-3 fonts, a simple program can be made to automate the process of extracting these bitmaps. Such program can be found on my GitHub, which has been tested on every firmware of every DCT-3 phone that I could get my hands on. For a full list of those tested, refer to this list.