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.
Leave a Reply
You must be logged in to post a comment.