[Android Software/Hacking General [Developers Only]] makers_mark:
Nexus 6 with MotoRun

The other day Google released the factory image for the Nexus 6, and a couple of things have changed in the way of run length encoding formats. They brought out a new one that I wasn't sure if I'd be able to figure it out, but I finally got it. I will post the images in the next post. I made the decoder but haven't made an encoder yet, but I will. This new format is setup like a file format, it has a header that is 12 bytes long. The first 7 bytes is "MotoRun" followed by a "0" then a 16-bit unsigned integer representing the width of the image, then another 16-bit value representing the height.

Then it goes right into the data part. Parts of this resembles the three byte rle method, and the image is actually encoded in the bgr24 pixel format (in the Nexus 6 at least). Here is how the run length encoding works. You have two count bytes and three color bytes. If the last bit is set in the first count byte, you drop it and essentially have a 15 bit unsigned integer. It is flagged as having a run multiplier, or actually using all the bits (minus the flag bit). (You can look at it that way, or you can simply multiply the value of the first count byte by 256, after dropping the flag bit of course, and add the second byte to it.) That is how you count, then your next three bytes are the color bytes that are going to be repeated how ever many times the count calls for.

If the flag bit is not set, then the count is not going to be a run of the following three bytes. You have to get the count from the second byte, but this count is the number of pixels you are getting ready to copy in a row. If your count bytes is (0,5) then the next 15 bytes will be read directly. It is 15 in this format because we are dealing with a 3 byte pixel format, and 5*3=15.

The file is complete when 255 (aka 11111111) goes into the count.

The height and width encoded in the header is for a purpose. No runs shall carry over to the next line down! This means the height and width is just for encoding the image.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>

#define BIT_SET 0x80
#define IS_BIT_SET(x) (x & BIT_SET)

unsigned int _CRT_fmode = _O_BINARY;

void motoDecode(char *inputFile, char *outputfile)
{
FILE *outStream;
FILE *inStream;
unsigned short repeats;
unsigned char data[5];
unsigned char color[3];
unsigned short counttotal = 0;

if ((inStream = fopen(inputFile, "rb")) == NULL){
fclose(inStream);
fprintf(stderr, "\n%s\nCould not be opened.", inputFile);
return;
}

if ((outStream = fopen(outputfile, "wb")) == NULL){
fclose(outStream);
fclose(inStream);
fprintf(stderr, "\n%s\nCould not be opened.", outputfile);
return;
}
int inNumber = fileno(inStream);
int outNumber = fileno(outStream);

while(read(inNumber, data, 5) == 5){
counttotal = 0;
color[0] = data[2];
color[1] = data[3];
color[2] = data[4];

if (IS_BIT_SET(data[0])){
data[0] &= ~0x80;
counttotal = (((data[0] * 256)) + data[1]);
for (repeats = 0;repeats < counttotal; repeats++){
write(outNumber, &color, 3);
}
continue;
} else {
counttotal = (data[1]);
write(outNumber, &color, 3);
for (repeats = 0; repeats < counttotal -1; repeats++){
read(inNumber, color, 3);
write(outNumber, &color, 3);
}
continue;
}

}
}

int main(int argc, char **argv)
{
char *inputFile;
char *outputfile;

int c;

while ((c = getopt (argc, argv, "i:o:")) != -1)
switch(c)
{

case 'i':
inputFile = optarg;
break;
case 'o':
outputfile = optarg;
break;

}

motoDecode(inputFile, outputfile);

return(1);
}