/*
Pirate-Loader for Bootloader v4
Version : 1.0.2
Changelog:
+2010-06-28 - Made HEX parser case-insensative
+ 2010-02-04 - Changed polling interval to 10ms on Windows select wrapper, suggested by Michal (robots)
+ 2010-02-04 - Added sleep(0) between write instructions, patch submitted by kbulgrien
+ 2010-01-22 - Added loader version number to the console output and source code
+ 2010-01-19 - Fixed BigEndian incompatibility
- Added programming simulate switch ( --simulate ) for data verification
+ 2010-01-18 - Initial release
Building:
UNIX family systems:
gcc pirate-loader.c -o pirate-loader
WINDOWS:
cl pirate-loader.c /DWIN32=1
Usage:
Run ./pirate-loader --help for more information on usage and possible switches
*/
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#include <fcntl.h>
#include <errno.h>
#define PIRATE_LOADER_VERSION "1.0.2"
#define STR_EXPAND(tok) #tok
#define OS_NAME(tok) STR_EXPAND(tok)
#ifdef WIN32
#include <windows.h>
#include <time.h>
#define O_NOCTTY 0
#define O_NDELAY 0
#define B115200 115200
#define OS WINDOWS
int write(int fd, const void* buf, int len)
{
HANDLE hCom = (HANDLE)fd;
int res = 0;
unsigned long bwritten = 0;
res = WriteFile(hCom, buf, len, &bwritten, NULL);
if( res == FALSE ) {
return -1;
} else {
return bwritten;
}
}
int read(int fd, void* buf, int len)
{
HANDLE hCom = (HANDLE)fd;
int res = 0;
unsigned long bread = 0;
res = ReadFile(hCom, buf, len, &bread, NULL);
if( res == FALSE ) {
return -1;
} else {
return bread;
}
}
int close(int fd)
{
HANDLE hCom = (HANDLE)fd;
CloseHandle(hCom);
return 0;
}
int open(const char* path, unsigned long flags)
{
static char full_path[32] = {0};
HANDLE hCom = NULL;
if( path[0] != '\\' ) {
_snprintf(full_path, sizeof(full_path) - 1, "\\\\.\\%s", path);
path = full_path;
}
hCom = CreateFileA(path, GENERIC_WRITE | GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if( !hCom || hCom == INVALID_HANDLE_VALUE ) {
return -1;
} else {
return (int)hCom;
}
}
int __stdcall select(int nfds, fd_set* readfds, fd_set* writefds, fd_set* exceptfs, const struct timeval* timeout)
{
time_t maxtc
= time(0) + (timeout
->tv_sec
); COMSTAT cs = {0};
unsigned long dwErrors = 0;
if( readfds->fd_count != 1 ) {
return -1;
}
while( time(0) <= maxtc
) { //only one file supported
if( ClearCommError( (HANDLE)readfds->fd_array[0], 0, &cs) != TRUE ){
return -1;
}
if( cs.cbInQue > 0 ) {
return 1;
}
Sleep(10);
}
return 0;
}
unsigned int sleep(unsigned int sec)
{
Sleep(sec * 1000);
return 0;
}
#else
#include <unistd.h>
#include <termios.h>
#include <sys/select.h>
#include <sys/types.h>
#include <sys/time.h>
#endif
/* macro definitions */
#if !defined OS
#define OS UNKNOWN
#endif
#define BOOTLOADER_HELLO_STR "\xC1"
#define BOOTLOADER_OK 0x4B
#define BOOTLOADER_PLACEMENT 1
#define PIC_FLASHSIZE 0xAC00
#define PIC_NUM_PAGES 512
#define PIC_NUM_ROWS_IN_PAGE 8
#define PIC_NUM_WORDS_IN_ROW 64
#define PIC_WORD_SIZE (3)
#define PIC_ROW_SIZE (PIC_NUM_WORDS_IN_ROW * PIC_WORD_SIZE)
#define PIC_PAGE_SIZE (PIC_NUM_ROWS_IN_PAGE * PIC_ROW_SIZE)
#define PIC_ROW_ADDR(p,r) (((p) * PIC_PAGE_SIZE) + ((r) * PIC_ROW_SIZE))
#define PIC_WORD_ADDR(p,r,w) (PIC_ROW_ADDR(p,r) + ((w) * PIC_WORD_SIZE))
#define PIC_PAGE_ADDR(p) (PIC_PAGE_SIZE * (p))
#define PAYLOAD_OFFSET 5
#define HEADER_LENGTH PAYLOAD_OFFSET
#define LENGTH_OFFSET 4
#define COMMAND_OFFSET 3
/* type definitions */
typedef unsigned char uint8;
typedef unsigned short uint16;
typedef unsigned long uint32;
/* global settings, command line arguments */
uint8 g_verbose = 0;
uint8 g_hello_only = 0;
uint8 g_simulate = 0;
const char* g_device_path = NULL;
const char* g_hexfile_path = NULL;
/* functions */
int readWithTimeout(int fd, uint8* out, int length, int timeout)
{
fd_set fds;
struct timeval tv = {timeout, 0};
int res = -1;
int got = 0;
do {
FD_ZERO(&fds);
FD_SET(fd, &fds);
res = select(fd + 1, &fds, NULL, NULL, &tv);
if( res > 0 ) {
res = read(fd, out, length);
if( res > 0 ) {
length -= res;
got += res;
out += res;
} else {
break;
}
} else {
return res;
}
} while( length > 0);
return got;
}
unsigned char hexdec(const char* pc)
{ unsigned char temp;
if(pc[0]>='a'){
temp=pc[0]-'a'+10;
}else if(pc[0] >= 'A'){
temp=pc[0]-'A'+10;
}else{
temp=pc[0] - '0';
}
temp=temp<<4;
if(pc[1]>='a'){
temp|=pc[1]-'a'+10;
}else if(pc[1] >= 'A'){
temp|=pc[1]-'A'+10;
}else{
temp|=pc[1] - '0';
}
return(temp & 0x0FF);
//return (((pc[0] >= 'A') ? ( pc[0] - 'A' + 10 ) : ( pc[0] - '0' ) ) << 4 |
// ((pc[1] >= 'A') ? ( pc[1] - 'A' + 10 ) : ( pc[1] - '0' ) )) & 0x0FF;
}
void dumpHex(uint8* buf, uint32 len)
{
uint32 i=0;
for(i=0; i<len; i++){
}
}
int readHEX(const char* file, uint8* bout, unsigned long max_length, uint8* pages_used)
{
static const uint32 HEX_DATA_OFFSET = 4;
uint8 linebin[256] = {0};
uint8* data = (linebin + HEX_DATA_OFFSET);
uint8 hex_crc, hex_type, hex_len;
uint32 hex_addr;
uint32 hex_base_addr = 0;
uint32 hex_words = 0;
uint32 f_addr = 0;
uint32 o_addr = 0;
uint32 num_words = 0;
char line[512] = {0};
char *pc;
char *pline = line + 1;
int res = 0;
int binlen = 0;
int line_no = 0;
int i = 0;
FILE
* fp
= fopen(file
, "rb");
if( !fp ) {
return -1;
}
while( !feof(fp
) && fgets(line
, sizeof(line
) - 1, fp
) ) {
line_no++;
if( line[0] != ':' ) {
break;
}
pc = pline + res - 1;
while( pc > pline && *pc <= ' ' ) {
*pc-- = 0;
res--;
}
if( res & 0x01 || res > 512 || res < 10) {
fprintf(stderr
, "Incorrect number of characters on line %d:%d\n", line_no
, res
); return -1;
}
hex_crc = 0;
for( pc = pline, i = 0; i<res; i+=2, pc+=2 ) {
linebin[i >> 1] = hexdec(pc);
hex_crc += linebin[i >> 1];
}
binlen = res / 2;
if( hex_crc != 0 ) {
fprintf(stderr
, "Checksum does not match, line %d\n", line_no
); return -1;
}
hex_addr = (linebin[1] << 8) | linebin[2];
hex_len = linebin[0];
hex_type = linebin[3];
if( binlen - (1 + 2 + 1 + hex_len + 1) != 0 ) {
fprintf(stderr
, "Incorrect number of bytes, line %d\n", line_no
); return -1;
}
if( hex_type == 0x00 )
{
f_addr = (hex_base_addr | (hex_addr)) / 2; //PCU
if( hex_len % 4 ) {
fprintf(stderr
, "Misaligned data, line %d\n", line_no
); return -1;
} else if( f_addr >= PIC_FLASHSIZE ) {
fprintf(stderr
, "Current record address is higher than maximum allowed, line %d\n", line_no
); return -1;
}
hex_words = hex_len / 4;
o_addr = (f_addr / 2) * PIC_WORD_SIZE; //BYTES
for( i=0; i<hex_words; i++)
{
bout[o_addr + 0] = data[(i*4) + 2];
bout[o_addr + 1] = data[(i*4) + 0];
bout[o_addr + 2] = data[(i*4) + 1];
pages_used[ (o_addr / PIC_PAGE_SIZE) ] = 1;
o_addr += PIC_WORD_SIZE;
num_words ++;
}
} else if ( hex_type == 0x04 && hex_len == 2) {
hex_base_addr = (linebin[4] << 24) | (linebin[5] << 16);
} else if ( hex_type == 0x01 ) {
break; //EOF
} else {
fprintf(stderr
, "Unsupported record type %02x, line %d\n", hex_type
, line_no
); return -1;
}
}
return num_words;
}
uint8 makeCrc(uint8* buf, uint32 len)
{
uint8 crc = 0, i = 0;
for(i=0; i<len; i++){
crc -= *buf++;
}
return crc;
}
int sendCommandAndWaitForResponse(int fd, uint8 *command)
{
uint8 response[4] = {0};
int res = 0;
res = write(fd, command, HEADER_LENGTH + command[LENGTH_OFFSET]);
if( res <= 0 ) {
return -1;
}
res = readWithTimeout(fd, response, 1, 5);
if( res != 1 ) {
return -1;
} else if ( response[0] != BOOTLOADER_OK ) {
printf("ERROR [%02x]\n", response
[0]); return -1;
} else {
return 0;
}
}
int sendFirmware(int fd, uint8* data, uint8* pages_used)
{
uint32 u_addr;
uint32 page = 0;
uint32 done = 0;
uint32 row = 0;
uint8 command[256] = {0};
for( page=0; page<PIC_NUM_PAGES; page++)
{
u_addr = page * ( PIC_NUM_WORDS_IN_ROW * 2 * PIC_NUM_ROWS_IN_PAGE );
if( pages_used[page] != 1 ) {
if( g_verbose && u_addr < PIC_FLASHSIZE) {
fprintf(stdout
, "Skipping page %ld [ %06lx ], not used\n", page
, u_addr
); }
continue;
}
if( u_addr >= PIC_FLASHSIZE ) {
fprintf(stderr
, "Address out of flash\n"); return -1;
}
//erase page
command[0] = (u_addr & 0x00FF0000) >> 16;
command[1] = (u_addr & 0x0000FF00) >> 8;
command[2] = (u_addr & 0x000000FF) >> 0;
command[COMMAND_OFFSET] = 0x01; //erase command
command[LENGTH_OFFSET ] = 0x01; //1 byte, CRC
command[PAYLOAD_OFFSET] = makeCrc(command, 5);
if( g_verbose ) {
dumpHex(command, HEADER_LENGTH + command[LENGTH_OFFSET]);
}
printf("Erasing page %ld, %04lx...", page
, u_addr
);
if( g_simulate == 0 && sendCommandAndWaitForResponse(fd, command) < 0 ) {
return -1;
}
//write 8 rows
for( row = 0; row < PIC_NUM_ROWS_IN_PAGE; row ++, u_addr += (PIC_NUM_WORDS_IN_ROW * 2))
{
command[0] = (u_addr & 0x00FF0000) >> 16;
command[1] = (u_addr & 0x0000FF00) >> 8;
command[2] = (u_addr & 0x000000FF) >> 0;
command[COMMAND_OFFSET] = 0x02; //write command
command[LENGTH_OFFSET ] = PIC_ROW_SIZE + 0x01; //DATA_LENGTH + CRC
memcpy(&command
[PAYLOAD_OFFSET
], &data
[PIC_ROW_ADDR
(page
, row
)], PIC_ROW_SIZE
);
command[PAYLOAD_OFFSET + PIC_ROW_SIZE] = makeCrc(command, HEADER_LENGTH + PIC_ROW_SIZE);
printf("Writing page %ld row %ld, %04lx...", page
, row
+ page
*PIC_NUM_ROWS_IN_PAGE
, u_addr
);
if( g_simulate == 0 && sendCommandAndWaitForResponse(fd, command) < 0 ) {
return -1;
}
sleep(0);
if( g_verbose ) {
dumpHex(command, HEADER_LENGTH + command[LENGTH_OFFSET]);
}
done += PIC_ROW_SIZE;
}
}
return done;
}
void fixJumps(uint8* bin_buff, uint8* pages_used)
{
uint32 iGotoUserAppAdress = 0;
uint32 iGotoUserAppAdressB3 = 0, iIter = 0;
uint32 iBLAddress = 0;
iBLAddress = ( PIC_FLASHSIZE - (BOOTLOADER_PLACEMENT * PIC_NUM_ROWS_IN_PAGE * PIC_NUM_WORDS_IN_ROW * 2)); //PCU
iGotoUserAppAdress = iBLAddress - 4;
iGotoUserAppAdressB3 = (iGotoUserAppAdress / 2) * 3;
for ( iIter = 0; iIter < 6; iIter++ ) {
bin_buff[ iGotoUserAppAdressB3 + iIter ] = bin_buff[ iIter ];
}
pages_used[ (iGotoUserAppAdressB3 / PIC_PAGE_SIZE) ] = 1;
bin_buff[0] = 0x04;
bin_buff[1] = ( (iBLAddress & 0x0000FE) );
bin_buff[2] = ( (iBLAddress & 0x00FF00) >> 8 );
bin_buff[3] = 0x00;
bin_buff[4] = ( (iBLAddress & 0x7F0000) >> 16 );
bin_buff[5] = 0x00;
}
/* non-firmware functions */
int configurePort(int fd, unsigned long baudrate)
{
#ifdef WIN32
DCB dcb = {0};
HANDLE hCom = (HANDLE)fd;
dcb.DCBlength = sizeof(dcb);
dcb.BaudRate = baudrate;
dcb.ByteSize = 8;
dcb.Parity = NOPARITY;
dcb.StopBits = ONESTOPBIT;
if( !SetCommState(hCom, &dcb) ){
return -1;
}
return (int)hCom;
#else
struct termios g_new_tio;
memset(&g_new_tio
, 0x00 , sizeof(g_new_tio
)); cfmakeraw(&g_new_tio);
g_new_tio.c_cflag |= (CS8 | CLOCAL | CREAD);
g_new_tio.c_cflag &= ~(PARENB | CSTOPB | CSIZE);
g_new_tio.c_oflag = 0;
g_new_tio.c_lflag = 0;
g_new_tio.c_cc[VTIME] = 0;
g_new_tio.c_cc[VMIN] = 1;
cfsetispeed (&g_new_tio, baudrate);
cfsetospeed (&g_new_tio, baudrate);
tcflush(fd, TCIOFLUSH);
return tcsetattr(fd, TCSANOW, &g_new_tio);
#endif
}
int openPort(const char* dev, unsigned long flags)
{
return open(dev, O_RDWR | O_NOCTTY | O_NDELAY | flags);
}
int parseCommandLine(int argc, const char** argv)
{
int i = 0;
for(i=1; i<argc; i++)
{
if( !strncmp(argv
[i
], "--hex=", 6) ) { g_hexfile_path = argv[i] + 6;
} else if ( !strncmp(argv
[i
], "--dev=", 6) ) { g_device_path = argv[i] + 6;
} else if ( !strcmp(argv
[i
], "--verbose") ) { g_verbose = 1;
} else if ( !strcmp(argv
[i
], "--hello") ) { g_hello_only = 1;
} else if ( !strcmp(argv
[i
], "--simulate") ) { g_simulate = 1;
} else if ( !strcmp(argv
[i
], "--help") ) { argc = 1; //that's not pretty, but it works :)
break;
} else {
fprintf(stderr
, "Unknown parameter %s, please use pirate-loader --help for usage\n", argv
[i
]); return -1;
}
}
if( argc == 1 )
{
//print usage
puts("pirate-loader usage:\n"); puts(" ./pirate-loader --dev=/path/to/device --hello"); puts(" ./pirate-loader --dev=/path/to/device --hex=/path/to/hexfile.hex [ --verbose"); puts(" ./pirate-loader --simulate --hex=/path/to/hexfile.hex [ --verbose");
return 0;
}
return 1;
}
/* entry point */
int main (int argc, const char** argv)
{
int dev_fd = -1, res = -1;
uint8 buffer[256] = {0};
uint8 pages_used[PIC_NUM_PAGES] = {0};
uint8* bin_buff = NULL;
puts("+++++++++++++++++++++++++++++++++++++++++++"); puts(" Pirate-Loader for BP with Bootloader v4+ "); puts(" Loader version: " PIRATE_LOADER_VERSION
" OS: " OS_NAME
(OS
)); puts("+++++++++++++++++++++++++++++++++++++++++++\n");
if( (res = parseCommandLine(argc, argv)) < 0 ) {
return -1;
} else if( res == 0 ) {
return 0;
}
if( !g_hello_only ) {
if( !g_hexfile_path ) {
fprintf(stderr
, "Please specify hexfile path --hex=/path/to/hexfile.hex\n"); return -1;
}
bin_buff
= (uint8*)malloc(256 << 10); //256kB if( !bin_buff ) {
fprintf(stderr
, "Could not allocate 256kB buffer\n"); goto Error;
}
//fill the buffer with 0xFF
memset(bin_buff
, 0xFFFFFFFF, (256 << 10));
printf("Parsing HEX file [%s]\n", g_hexfile_path
);
res = readHEX(g_hexfile_path, bin_buff, (256 << 8), pages_used);
if( res <= 0 || res > PIC_FLASHSIZE ) {
fprintf(stderr
, "Could not load HEX file, result=%d\n", res
); goto Error;
}
printf("Found %d words (%d bytes)\n", res
, res
* 3);
printf("Fixing bootloader/userprogram jumps\n"); fixJumps(bin_buff, pages_used);
}
if( g_simulate ) {
sendFirmware(dev_fd, bin_buff, pages_used);
goto Finished;
}
if( !g_device_path ) {
fprintf(stderr
, "Please specify serial device path --dev=/dev/...\n"); return -1;
}
printf("Opening serial device %s...", g_device_path
);
dev_fd = openPort(g_device_path, 0);
if( dev_fd < 0 ) {
fprintf(stderr
, "Could not open %s\n", g_device_path
); goto Error;
}
printf("Configuring serial port settings...");
if( configurePort(dev_fd, B115200) < 0 ) {
fprintf(stderr
, "Could not configure device, errno=%d\n", errno
); goto Error;
}
printf("Sending Hello to the Bootloader...");
//send HELLO
res = write(dev_fd, BOOTLOADER_HELLO_STR, 1);
res = readWithTimeout(dev_fd, buffer, 4, 3);
if( res != 4 || buffer[3] != BOOTLOADER_OK ) {
fprintf(stderr
, "No reply from the bootloader, or invalid reply received: %d\n", res
); fprintf(stderr
, "Please make sure that PGND and PGC are connected, replug the devide and try again\n"); goto Error;
}
puts("OK\n"); //extra LF for spacing
printf("Device ID: %s [%02x]\n", (buffer
[0] == 0xD4) ? "PIC24FJ64GA002" : "UNKNOWN", buffer
[0]); printf("Bootloader version: %d,%02d\n", buffer
[1], buffer
[2]);
if( buffer[0] != 0xD4 ) {
fprintf(stderr
, "Unsupported device (%02x:UNKNOWN), only 0xD4 PIC24FJ64GA002 is supported\n", buffer
[0]); goto Error;
}
if( !g_hello_only ) {
res = sendFirmware(dev_fd, bin_buff, pages_used);
if( res > 0 ) {
puts("\nFirmware updated successfully :)!"); printf("Use screen %s 115200 to verify\n", g_device_path
); } else {
puts("\nError updating firmware :("); goto Error;
}
}
Finished:
if( bin_buff ) {
}
close(dev_fd);
return 0;
Error:
if( bin_buff ) {
}
if( dev_fd >= 0 ) {
close(dev_fd);
}
return -1;
}
LyoKIAogUGlyYXRlLUxvYWRlciBmb3IgQm9vdGxvYWRlciB2NAogCiBWZXJzaW9uICA6IDEuMC4yCiAKIENoYW5nZWxvZzoKICsyMDEwLTA2LTI4IC0gTWFkZSBIRVggcGFyc2VyIGNhc2UtaW5zZW5zYXRpdmUKIAogICsgMjAxMC0wMi0wNCAtIENoYW5nZWQgcG9sbGluZyBpbnRlcnZhbCB0byAxMG1zIG9uIFdpbmRvd3Mgc2VsZWN0IHdyYXBwZXIsIHN1Z2dlc3RlZCBieSBNaWNoYWwgKHJvYm90cykKICAKICArIDIwMTAtMDItMDQgLSBBZGRlZCBzbGVlcCgwKSBiZXR3ZWVuIHdyaXRlIGluc3RydWN0aW9ucywgcGF0Y2ggc3VibWl0dGVkIGJ5IGtidWxncmllbgogCiAgKyAyMDEwLTAxLTIyIC0gQWRkZWQgbG9hZGVyIHZlcnNpb24gbnVtYmVyIHRvIHRoZSBjb25zb2xlIG91dHB1dCBhbmQgc291cmNlIGNvZGUKIAogICsgMjAxMC0wMS0xOSAtIEZpeGVkIEJpZ0VuZGlhbiBpbmNvbXBhdGliaWxpdHkKICAgICAgICAgICAgLSBBZGRlZCBwcm9ncmFtbWluZyBzaW11bGF0ZSBzd2l0Y2ggKCAtLXNpbXVsYXRlICkgZm9yIGRhdGEgdmVyaWZpY2F0aW9uIAogCiAgKyAyMDEwLTAxLTE4IC0gSW5pdGlhbCByZWxlYXNlCiAKIAogQnVpbGRpbmc6CiAKICBVTklYIGZhbWlseSBzeXN0ZW1zOgogICAKICAgZ2NjIHBpcmF0ZS1sb2FkZXIuYyAtbyBwaXJhdGUtbG9hZGVyCiAKICBXSU5ET1dTOgogICAgCiAgIGNsIHBpcmF0ZS1sb2FkZXIuYyAvRFdJTjMyPTEKIAogCiBVc2FnZToKICAgCiAgIFJ1biAuL3BpcmF0ZS1sb2FkZXIgLS1oZWxwIGZvciBtb3JlIGluZm9ybWF0aW9uIG9uIHVzYWdlIGFuZCBwb3NzaWJsZSBzd2l0Y2hlcwogCiAqLwoKI2luY2x1ZGUgPHN0ZGlvLmg+CiNpbmNsdWRlIDxzdGRsaWIuaD4KI2luY2x1ZGUgPG1lbW9yeS5oPgojaW5jbHVkZSA8ZmNudGwuaD4KI2luY2x1ZGUgPGVycm5vLmg+CgojZGVmaW5lIFBJUkFURV9MT0FERVJfVkVSU0lPTiAiMS4wLjIiCgojZGVmaW5lIFNUUl9FWFBBTkQodG9rKSAjdG9rCiNkZWZpbmUgT1NfTkFNRSh0b2spIFNUUl9FWFBBTkQodG9rKQoKCiNpZmRlZiBXSU4zMgogICAjaW5jbHVkZSA8d2luZG93cy5oPgogICAjaW5jbHVkZSA8dGltZS5oPgoKICAgI2RlZmluZSBPX05PQ1RUWSAwCiAgICNkZWZpbmUgT19OREVMQVkgMAogICAjZGVmaW5lIEIxMTUyMDAgMTE1MjAwCgogICAjZGVmaW5lIE9TIFdJTkRPV1MKICAgCiAgIGludCB3cml0ZShpbnQgZmQsIGNvbnN0IHZvaWQqIGJ1ZiwgaW50IGxlbikKICAgewogICAgICBIQU5ETEUgaENvbSA9IChIQU5ETEUpZmQ7CiAgICAgIGludCByZXMgPSAwOwogICAgICB1bnNpZ25lZCBsb25nIGJ3cml0dGVuID0gMDsKCgogICAgICByZXMgPSBXcml0ZUZpbGUoaENvbSwgYnVmLCBsZW4sICZid3JpdHRlbiwgTlVMTCk7CgogICAgICBpZiggcmVzID09IEZBTFNFICkgewogICAgICAgICByZXR1cm4gLTE7CiAgICAgIH0gZWxzZSB7CiAgICAgICAgIHJldHVybiBid3JpdHRlbjsKICAgICAgfQogICB9CgogICBpbnQgcmVhZChpbnQgZmQsIHZvaWQqIGJ1ZiwgaW50IGxlbikKICAgewogICAgICBIQU5ETEUgaENvbSA9IChIQU5ETEUpZmQ7CiAgICAgIGludCByZXMgPSAwOwogICAgICB1bnNpZ25lZCBsb25nIGJyZWFkID0gMDsKCiAgICAgIHJlcyA9IFJlYWRGaWxlKGhDb20sIGJ1ZiwgbGVuLCAmYnJlYWQsIE5VTEwpOwoKICAgICAgaWYoIHJlcyA9PSBGQUxTRSApIHsKICAgICAgICAgcmV0dXJuIC0xOwogICAgICB9IGVsc2UgewogICAgICAgICByZXR1cm4gYnJlYWQ7CiAgICAgIH0KICAgfQoKICAgaW50IGNsb3NlKGludCBmZCkKICAgewogICAgICBIQU5ETEUgaENvbSA9IChIQU5ETEUpZmQ7CgogICAgICBDbG9zZUhhbmRsZShoQ29tKTsKICAgICAgcmV0dXJuIDA7CiAgIH0KCiAgIGludCBvcGVuKGNvbnN0IGNoYXIqIHBhdGgsIHVuc2lnbmVkIGxvbmcgZmxhZ3MpCiAgIHsKICAgICAgc3RhdGljIGNoYXIgZnVsbF9wYXRoWzMyXSA9IHswfTsKCiAgICAgIEhBTkRMRSBoQ29tID0gTlVMTDsKICAgICAgCiAgICAgIGlmKCBwYXRoWzBdICE9ICdcXCcgKSB7CiAgICAgICAgIF9zbnByaW50ZihmdWxsX3BhdGgsIHNpemVvZihmdWxsX3BhdGgpIC0gMSwgIlxcXFwuXFwlcyIsIHBhdGgpOwogICAgICAgICBwYXRoID0gZnVsbF9wYXRoOwogICAgICB9CgogICAgICBoQ29tID0gQ3JlYXRlRmlsZUEocGF0aCwgR0VORVJJQ19XUklURSB8IEdFTkVSSUNfUkVBRCwgMCwgTlVMTCwgT1BFTl9FWElTVElORywgRklMRV9BVFRSSUJVVEVfTk9STUFMLCBOVUxMKTsKCiAgICAgIGlmKCAhaENvbSB8fCBoQ29tID09IElOVkFMSURfSEFORExFX1ZBTFVFICkgewogICAgICAgICByZXR1cm4gLTE7CiAgICAgIH0gZWxzZSB7CiAgICAgICAgIHJldHVybiAoaW50KWhDb207CiAgICAgIH0KICAgfQoKICAgaW50IF9fc3RkY2FsbCBzZWxlY3QoaW50IG5mZHMsIGZkX3NldCogcmVhZGZkcywgZmRfc2V0KiB3cml0ZWZkcywgZmRfc2V0KiBleGNlcHRmcywgY29uc3Qgc3RydWN0IHRpbWV2YWwqIHRpbWVvdXQpCiAgIHsKICAgICAgdGltZV90IG1heHRjID0gdGltZSgwKSArICh0aW1lb3V0LT50dl9zZWMpOwogICAgICBDT01TVEFUIGNzID0gezB9OwogICAgICB1bnNpZ25lZCBsb25nIGR3RXJyb3JzID0gMDsKCiAgICAgIGlmKCByZWFkZmRzLT5mZF9jb3VudCAhPSAxICkgewogICAgICAgICByZXR1cm4gLTE7CiAgICAgIH0KCiAgICAgIHdoaWxlKCB0aW1lKDApIDw9IG1heHRjICkKICAgICAgeyAvL29ubHkgb25lIGZpbGUgc3VwcG9ydGVkCiAgICAgICAgIGlmKCBDbGVhckNvbW1FcnJvciggKEhBTkRMRSlyZWFkZmRzLT5mZF9hcnJheVswXSwgMCwgJmNzKSAhPSBUUlVFICl7CiAgICAgICAgICAgIHJldHVybiAtMTsKICAgICAgICAgfQoKICAgICAgICAgaWYoIGNzLmNiSW5RdWUgPiAwICkgewogICAgICAgICAgICByZXR1cm4gMTsKICAgICAgICAgfQoKICAgICAgICAgU2xlZXAoMTApOwogICAgICB9CiAgICAgIHJldHVybiAwOwogICB9CgogICB1bnNpZ25lZCBpbnQgc2xlZXAodW5zaWduZWQgaW50IHNlYykKICAgewogICAgICBTbGVlcChzZWMgKiAxMDAwKTsKICAgICAgCiAgICAgIHJldHVybiAwOwogICB9CgojZWxzZQogICAjaW5jbHVkZSA8dW5pc3RkLmg+CiAgICNpbmNsdWRlIDx0ZXJtaW9zLmg+CiAgICNpbmNsdWRlIDxzeXMvc2VsZWN0Lmg+CiAgICNpbmNsdWRlIDxzeXMvdHlwZXMuaD4KICAgI2luY2x1ZGUgPHN5cy90aW1lLmg+CiNlbmRpZgoKLyogbWFjcm8gZGVmaW5pdGlvbnMgKi8KCiNpZiAhZGVmaW5lZCBPUwojZGVmaW5lIE9TIFVOS05PV04KI2VuZGlmCgojZGVmaW5lIEJPT1RMT0FERVJfSEVMTE9fU1RSICJceEMxIgojZGVmaW5lIEJPT1RMT0FERVJfT0sgMHg0QgojZGVmaW5lIEJPT1RMT0FERVJfUExBQ0VNRU5UIDEKCiNkZWZpbmUgUElDX0ZMQVNIU0laRSAweEFDMDAKCiNkZWZpbmUgUElDX05VTV9QQUdFUyA1MTIKI2RlZmluZSBQSUNfTlVNX1JPV1NfSU5fUEFHRSAgOAojZGVmaW5lIFBJQ19OVU1fV09SRFNfSU5fUk9XIDY0CgojZGVmaW5lIFBJQ19XT1JEX1NJWkUgICgzKQojZGVmaW5lIFBJQ19ST1dfU0laRSAgKFBJQ19OVU1fV09SRFNfSU5fUk9XICogUElDX1dPUkRfU0laRSkKI2RlZmluZSBQSUNfUEFHRV9TSVpFIChQSUNfTlVNX1JPV1NfSU5fUEFHRSAgKiBQSUNfUk9XX1NJWkUpCgoKI2RlZmluZSBQSUNfUk9XX0FERFIocCxyKSAgICAgICgoKHApICogUElDX1BBR0VfU0laRSkgKyAoKHIpICogUElDX1JPV19TSVpFKSkKI2RlZmluZSBQSUNfV09SRF9BRERSKHAscix3KSAgIChQSUNfUk9XX0FERFIocCxyKSArICgodykgKiBQSUNfV09SRF9TSVpFKSkKI2RlZmluZSBQSUNfUEFHRV9BRERSKHApICAgICAgKFBJQ19QQUdFX1NJWkUgKiAocCkpCgojZGVmaW5lIFBBWUxPQURfT0ZGU0VUIDUKI2RlZmluZSBIRUFERVJfTEVOR1RIIFBBWUxPQURfT0ZGU0VUCiNkZWZpbmUgTEVOR1RIX09GRlNFVCA0CiNkZWZpbmUgQ09NTUFORF9PRkZTRVQgMwoKLyogdHlwZSBkZWZpbml0aW9ucyAqLwoKdHlwZWRlZiB1bnNpZ25lZCBjaGFyICB1aW50ODsKdHlwZWRlZiB1bnNpZ25lZCBzaG9ydCB1aW50MTY7CnR5cGVkZWYgdW5zaWduZWQgbG9uZyAgdWludDMyOwoKLyogZ2xvYmFsIHNldHRpbmdzLCBjb21tYW5kIGxpbmUgYXJndW1lbnRzICovCgp1aW50OCAgICAgIGdfdmVyYm9zZSA9IDA7CnVpbnQ4ICAgICAgZ19oZWxsb19vbmx5ID0gMDsKdWludDggICAgICBnX3NpbXVsYXRlID0gMDsKY29uc3QgY2hhciogZ19kZXZpY2VfcGF0aCAgPSBOVUxMOwpjb25zdCBjaGFyKiBnX2hleGZpbGVfcGF0aCA9IE5VTEw7CgovKiBmdW5jdGlvbnMgKi8KCmludCByZWFkV2l0aFRpbWVvdXQoaW50IGZkLCB1aW50OCogb3V0LCBpbnQgbGVuZ3RoLCBpbnQgdGltZW91dCkKewogICBmZF9zZXQgZmRzOwogICBzdHJ1Y3QgdGltZXZhbCB0diA9IHt0aW1lb3V0LCAwfTsKICAgaW50IHJlcyA9IC0xOwogICBpbnQgZ290ID0gMDsKICAgCiAgIGRvIHsKICAgCiAgICAgIEZEX1pFUk8oJmZkcyk7CiAgICAgIEZEX1NFVChmZCwgJmZkcyk7CiAgICAgIAogICAgICByZXMgPSBzZWxlY3QoZmQgKyAxLCAmZmRzLCBOVUxMLCBOVUxMLCAmdHYpOwogICAgICAKICAgICAgaWYoIHJlcyA+IDAgKSB7CiAgICAgICAgIHJlcyA9IHJlYWQoZmQsIG91dCwgbGVuZ3RoKTsKICAgICAgICAgaWYoIHJlcyA+IDAgKSB7CiAgICAgICAgICAgIGxlbmd0aCAtPSByZXM7CiAgICAgICAgICAgIGdvdCAgICArPSByZXM7CiAgICAgICAgICAgIG91dCAgICArPSByZXM7CiAgICAgICAgIH0gZWxzZSB7CiAgICAgICAgICAgIGJyZWFrOwogICAgICAgICB9CiAgICAgIH0gZWxzZSB7IAogICAgICAgICByZXR1cm4gcmVzOwogICAgICB9CiAgIH0gd2hpbGUoIGxlbmd0aCA+IDApOwogICAKICAgcmV0dXJuIGdvdDsKfQoKdW5zaWduZWQgY2hhciBoZXhkZWMoY29uc3QgY2hhciogcGMpCnsgICB1bnNpZ25lZCBjaGFyIHRlbXA7CgogICBpZihwY1swXT49J2EnKXsKICAgICAgdGVtcD1wY1swXS0nYScrMTA7CiAgIH1lbHNlIGlmKHBjWzBdID49ICdBJyl7CiAgICAgIHRlbXA9cGNbMF0tJ0EnKzEwOyAgICAgIAogICB9ZWxzZXsKICAgICAgdGVtcD1wY1swXSAtICcwJzsKICAgfQogICB0ZW1wPXRlbXA8PDQ7CiAgIAogICBpZihwY1sxXT49J2EnKXsKICAgICAgdGVtcHw9cGNbMV0tJ2EnKzEwOwogICB9ZWxzZSBpZihwY1sxXSA+PSAnQScpewogICAgICB0ZW1wfD1wY1sxXS0nQScrMTA7ICAgICAgCiAgIH1lbHNlewogICAgICB0ZW1wfD1wY1sxXSAtICcwJzsKICAgfQogICAKICAgcmV0dXJuKHRlbXAgJiAweDBGRik7CgogICAKICAgLy9yZXR1cm4gKCgocGNbMF0gPj0gJ0EnKSA/ICggcGNbMF0gLSAnQScgKyAxMCApIDogKCBwY1swXSAtICcwJyApICkgPDwgNCB8IAogICAvLyAgICAgICgocGNbMV0gPj0gJ0EnKSA/ICggcGNbMV0gLSAnQScgKyAxMCApIDogKCBwY1sxXSAtICcwJyApICkpICYgMHgwRkY7CiAgIAp9Cgp2b2lkIGR1bXBIZXgodWludDgqIGJ1ZiwgdWludDMyIGxlbikKewogICB1aW50MzIgaT0wOwogICAKICAgZm9yKGk9MDsgaTxsZW47IGkrKyl7CiAgICAgIHByaW50ZigiJTAyWCAiLCBidWZbaV0pOwogICB9CiAgIHB1dGNoYXIoJ1xuJyk7Cn0KCmludCByZWFkSEVYKGNvbnN0IGNoYXIqIGZpbGUsIHVpbnQ4KiBib3V0LCB1bnNpZ25lZCBsb25nIG1heF9sZW5ndGgsIHVpbnQ4KiBwYWdlc191c2VkKQp7CiAgIHN0YXRpYyBjb25zdCB1aW50MzIgSEVYX0RBVEFfT0ZGU0VUID0gNDsKICAgdWludDggIGxpbmViaW5bMjU2XSA9IHswfTsKICAgdWludDgqIGRhdGEgPSAobGluZWJpbiArIEhFWF9EQVRBX09GRlNFVCk7CiAgIHVpbnQ4ICBoZXhfY3JjLCBoZXhfdHlwZSwgaGV4X2xlbjsKICAgdWludDMyIGhleF9hZGRyOwogICB1aW50MzIgaGV4X2Jhc2VfYWRkciA9IDA7CiAgIHVpbnQzMiBoZXhfd29yZHMgICAgID0gMDsKICAgCiAgIHVpbnQzMiBmX2FkZHIgPSAwOwogICB1aW50MzIgb19hZGRyID0gMDsKICAgCiAgIHVpbnQzMiBudW1fd29yZHMgPSAwOwogICAKICAgY2hhciAgbGluZVs1MTJdID0gezB9OwogICBjaGFyICAqcGM7CiAgIGNoYXIgICpwbGluZSA9IGxpbmUgKyAxOwogICBpbnQgICByZXMgPSAwOwogICBpbnQgICAgIGJpbmxlbiA9IDA7CiAgIGludCAgIGxpbmVfbm8gPSAwOwogICBpbnQgICBpID0gMDsKICAgCiAgIEZJTEUqIGZwID0gZm9wZW4oZmlsZSwgInJiIik7CiAgIAogICBpZiggIWZwICkgewogICAgICByZXR1cm4gLTE7CiAgIH0KICAgCiAgIHdoaWxlKCAhZmVvZihmcCkgJiYgZmdldHMobGluZSwgc2l6ZW9mKGxpbmUpIC0gMSwgZnApICkKICAgewogICAgICBsaW5lX25vKys7CiAgICAgIAogICAgICBpZiggbGluZVswXSAhPSAnOicgKSB7CiAgICAgICAgIGJyZWFrOwogICAgICB9CiAgICAgIAogICAgICByZXMgPSBzdHJsZW4ocGxpbmUpOwogICAgICBwYyAgPSBwbGluZSArIHJlcyAtIDE7CiAgICAgIAogICAgICB3aGlsZSggcGMgPiBwbGluZSAmJiAqcGMgPD0gJyAnICkgewogICAgICAgICAqcGMtLSA9IDA7CiAgICAgICAgIHJlcy0tOwogICAgICB9CiAgICAgIAogICAgICBpZiggcmVzICYgMHgwMSB8fCByZXMgPiA1MTIgfHwgcmVzIDwgMTApIHsKICAgICAgICAgZnByaW50ZihzdGRlcnIsICJJbmNvcnJlY3QgbnVtYmVyIG9mIGNoYXJhY3RlcnMgb24gbGluZSAlZDolZFxuIiwgbGluZV9ubywgcmVzKTsKICAgICAgICAgcmV0dXJuIC0xOwogICAgICB9CiAgICAgIAogICAgICBoZXhfY3JjID0gMDsKICAgICAgCiAgICAgIGZvciggcGMgPSBwbGluZSwgaSA9IDA7IGk8cmVzOyBpKz0yLCBwYys9MiApIHsKICAgICAgICAgbGluZWJpbltpID4+IDFdID0gaGV4ZGVjKHBjKTsKICAgICAgICAgaGV4X2NyYyArPSBsaW5lYmluW2kgPj4gMV07CiAgICAgIH0KICAgICAgCiAgICAgIGJpbmxlbiA9IHJlcyAvIDI7CiAgICAgIAogICAgICBpZiggaGV4X2NyYyAhPSAwICkgewogICAgICAgICBmcHJpbnRmKHN0ZGVyciwgIkNoZWNrc3VtIGRvZXMgbm90IG1hdGNoLCBsaW5lICVkXG4iLCBsaW5lX25vKTsKICAgICAgICAgcmV0dXJuIC0xOwogICAgICB9CiAgICAgIAogICAgICBoZXhfYWRkciA9IChsaW5lYmluWzFdIDw8IDgpIHwgbGluZWJpblsyXTsKICAgICAgaGV4X2xlbiAgPSBsaW5lYmluWzBdOwogICAgICBoZXhfdHlwZSA9IGxpbmViaW5bM107CiAgICAgIAogICAgICBpZiggYmlubGVuIC0gKDEgKyAyICsgMSArIGhleF9sZW4gKyAxKSAhPSAwICkgewogICAgICAgICBmcHJpbnRmKHN0ZGVyciwgIkluY29ycmVjdCBudW1iZXIgb2YgYnl0ZXMsIGxpbmUgJWRcbiIsIGxpbmVfbm8pOwogICAgICAgICByZXR1cm4gLTE7CiAgICAgIH0KICAgICAgCiAgICAgIGlmKCBoZXhfdHlwZSA9PSAweDAwICkKICAgICAgewogICAgICAgICBmX2FkZHIgID0gKGhleF9iYXNlX2FkZHIgfCAoaGV4X2FkZHIpKSAvIDI7IC8vUENVCiAgICAgICAgIAogICAgICAgICBpZiggaGV4X2xlbiAlIDQgKSB7CiAgICAgICAgICAgIGZwcmludGYoc3RkZXJyLCAiTWlzYWxpZ25lZCBkYXRhLCBsaW5lICVkXG4iLCBsaW5lX25vKTsKICAgICAgICAgICAgcmV0dXJuIC0xOwogICAgICAgICB9IGVsc2UgaWYoIGZfYWRkciA+PSBQSUNfRkxBU0hTSVpFICkgewogICAgICAgICAgICBmcHJpbnRmKHN0ZGVyciwgIkN1cnJlbnQgcmVjb3JkIGFkZHJlc3MgaXMgaGlnaGVyIHRoYW4gbWF4aW11bSBhbGxvd2VkLCBsaW5lICVkXG4iLCBsaW5lX25vKTsKICAgICAgICAgICAgcmV0dXJuIC0xOwogICAgICAgICB9CiAgICAgICAgIAogICAgICAgICBoZXhfd29yZHMgPSBoZXhfbGVuICAvIDQ7CiAgICAgICAgIG9fYWRkciAgPSAoZl9hZGRyIC8gMikgKiBQSUNfV09SRF9TSVpFOyAvL0JZVEVTCiAgICAgICAgIAogICAgICAgICBmb3IoIGk9MDsgaTxoZXhfd29yZHM7IGkrKykKICAgICAgICAgewogICAgICAgICAgICBib3V0W29fYWRkciArIDBdID0gZGF0YVsoaSo0KSArIDJdOwogICAgICAgICAgICBib3V0W29fYWRkciArIDFdID0gZGF0YVsoaSo0KSArIDBdOwogICAgICAgICAgICBib3V0W29fYWRkciArIDJdID0gZGF0YVsoaSo0KSArIDFdOwogICAgICAgICAgICAKICAgICAgICAgICAgcGFnZXNfdXNlZFsgKG9fYWRkciAvIFBJQ19QQUdFX1NJWkUpIF0gPSAxOwogICAgICAgICAgICAKICAgICAgICAgICAgb19hZGRyICAgICs9IFBJQ19XT1JEX1NJWkU7CiAgICAgICAgICAgIG51bV93b3JkcyArKzsKICAgICAgICAgfQoKICAgICAgfSBlbHNlIGlmICggaGV4X3R5cGUgPT0gMHgwNCAmJiBoZXhfbGVuID09IDIpIHsKICAgICAgICAgaGV4X2Jhc2VfYWRkciA9IChsaW5lYmluWzRdIDw8IDI0KSB8IChsaW5lYmluWzVdIDw8IDE2KTsKICAgICAgfSBlbHNlIGlmICggaGV4X3R5cGUgPT0gMHgwMSApIHsKICAgICAgICAgYnJlYWs7IC8vRU9GCiAgICAgIH0gZWxzZSB7CiAgICAgICAgIGZwcmludGYoc3RkZXJyLCAiVW5zdXBwb3J0ZWQgcmVjb3JkIHR5cGUgJTAyeCwgbGluZSAlZFxuIiwgaGV4X3R5cGUsIGxpbmVfbm8pOwogICAgICAgICByZXR1cm4gLTE7CiAgICAgIH0KICAgICAgCiAgIH0KICAgCiAgIGZjbG9zZShmcCk7CiAgIHJldHVybiBudW1fd29yZHM7Cn0KCnVpbnQ4IG1ha2VDcmModWludDgqIGJ1ZiwgdWludDMyIGxlbikKewogICB1aW50OCBjcmMgPSAwLCBpID0gMDsKCiAgIGZvcihpPTA7IGk8bGVuOyBpKyspewogICAgICBjcmMgLT0gKmJ1ZisrOwogICB9CiAgIAogICByZXR1cm4gY3JjOwp9CgppbnQgc2VuZENvbW1hbmRBbmRXYWl0Rm9yUmVzcG9uc2UoaW50IGZkLCB1aW50OCAqY29tbWFuZCkKewogICB1aW50OCAgcmVzcG9uc2VbNF0gPSB7MH07CiAgIGludCAgICByZXMgPSAwOwogICAKICAgcmVzID0gd3JpdGUoZmQsIGNvbW1hbmQsIEhFQURFUl9MRU5HVEggKyBjb21tYW5kW0xFTkdUSF9PRkZTRVRdKTsKICAgCiAgIGlmKCByZXMgPD0gMCApIHsKICAgICAgcHV0cygiRVJST1IiKTsKICAgICAgcmV0dXJuIC0xOwogICB9CiAgIAogICByZXMgPSByZWFkV2l0aFRpbWVvdXQoZmQsIHJlc3BvbnNlLCAxLCA1KTsKICAgaWYoIHJlcyAhPSAxICkgewogICAgICBwdXRzKCJFUlJPUiIpOwogICAgICByZXR1cm4gLTE7CiAgIH0gZWxzZSBpZiAoIHJlc3BvbnNlWzBdICE9IEJPT1RMT0FERVJfT0sgKSB7CiAgICAgIHByaW50ZigiRVJST1IgWyUwMnhdXG4iLCByZXNwb25zZVswXSk7CiAgICAgIHJldHVybiAtMTsKICAgfSBlbHNlIHsKICAgICAgcmV0dXJuIDA7CiAgIH0KfQoKCmludCBzZW5kRmlybXdhcmUoaW50IGZkLCB1aW50OCogZGF0YSwgdWludDgqIHBhZ2VzX3VzZWQpCnsKICAgdWludDMyIHVfYWRkcjsKICAgCiAgIAogICB1aW50MzIgcGFnZSAgPSAwOwogICB1aW50MzIgZG9uZSAgPSAwOwogICB1aW50MzIgcm93ICAgPSAwOwogICB1aW50OCAgY29tbWFuZFsyNTZdID0gezB9OwogICAKICAgCiAgIGZvciggcGFnZT0wOyBwYWdlPFBJQ19OVU1fUEFHRVM7IHBhZ2UrKykKICAgewogICAgICAKICAgICAgdV9hZGRyID0gcGFnZSAqICggUElDX05VTV9XT1JEU19JTl9ST1cgKiAyICogUElDX05VTV9ST1dTX0lOX1BBR0UgKTsKICAgICAgCiAgICAgIGlmKCBwYWdlc191c2VkW3BhZ2VdICE9IDEgKSB7CiAgICAgICAgIGlmKCBnX3ZlcmJvc2UgJiYgdV9hZGRyIDwgUElDX0ZMQVNIU0laRSkgewogICAgICAgICAgICBmcHJpbnRmKHN0ZG91dCwgIlNraXBwaW5nIHBhZ2UgJWxkIFsgJTA2bHggXSwgbm90IHVzZWRcbiIsIHBhZ2UsIHVfYWRkcik7CiAgICAgICAgIH0KICAgICAgICAgY29udGludWU7CiAgICAgIH0KICAgICAgCiAgICAgIGlmKCB1X2FkZHIgPj0gUElDX0ZMQVNIU0laRSApIHsKICAgICAgICAgZnByaW50ZihzdGRlcnIsICJBZGRyZXNzIG91dCBvZiBmbGFzaFxuIik7CiAgICAgICAgIHJldHVybiAtMTsKICAgICAgfQogICAgICAKICAgICAgLy9lcmFzZSBwYWdlCiAgICAgIGNvbW1hbmRbMF0gPSAodV9hZGRyICYgMHgwMEZGMDAwMCkgPj4gMTY7CiAgICAgIGNvbW1hbmRbMV0gPSAodV9hZGRyICYgMHgwMDAwRkYwMCkgPj4gIDg7CiAgICAgIGNvbW1hbmRbMl0gPSAodV9hZGRyICYgMHgwMDAwMDBGRikgPj4gIDA7CiAgICAgIGNvbW1hbmRbQ09NTUFORF9PRkZTRVRdID0gMHgwMTsgLy9lcmFzZSBjb21tYW5kCiAgICAgIGNvbW1hbmRbTEVOR1RIX09GRlNFVCBdID0gMHgwMTsgLy8xIGJ5dGUsIENSQwogICAgICBjb21tYW5kW1BBWUxPQURfT0ZGU0VUXSA9IG1ha2VDcmMoY29tbWFuZCwgNSk7CiAgICAgIAogICAgICBpZiggZ192ZXJib3NlICkgewogICAgICAgICBkdW1wSGV4KGNvbW1hbmQsIEhFQURFUl9MRU5HVEggKyBjb21tYW5kW0xFTkdUSF9PRkZTRVRdKTsKICAgICAgfQogICAgICAKICAgICAgcHJpbnRmKCJFcmFzaW5nIHBhZ2UgJWxkLCAlMDRseC4uLiIsIHBhZ2UsIHVfYWRkcik7CiAgICAgIAogICAgICBpZiggZ19zaW11bGF0ZSA9PSAwICYmIHNlbmRDb21tYW5kQW5kV2FpdEZvclJlc3BvbnNlKGZkLCBjb21tYW5kKSA8IDAgKSB7CiAgICAgICAgIHJldHVybiAtMTsKICAgICAgfQogICAgICAKICAgICAgcHV0cygiT0siKTsKICAgICAgCiAgICAgIC8vd3JpdGUgOCByb3dzCiAgICAgIGZvciggcm93ID0gMDsgcm93IDwgUElDX05VTV9ST1dTX0lOX1BBR0U7IHJvdyArKywgdV9hZGRyICs9IChQSUNfTlVNX1dPUkRTX0lOX1JPVyAqIDIpKQogICAgICB7CiAgICAgICAgIGNvbW1hbmRbMF0gPSAodV9hZGRyICYgMHgwMEZGMDAwMCkgPj4gMTY7CiAgICAgICAgIGNvbW1hbmRbMV0gPSAodV9hZGRyICYgMHgwMDAwRkYwMCkgPj4gIDg7CiAgICAgICAgIGNvbW1hbmRbMl0gPSAodV9hZGRyICYgMHgwMDAwMDBGRikgPj4gIDA7CiAgICAgICAgIGNvbW1hbmRbQ09NTUFORF9PRkZTRVRdID0gMHgwMjsgLy93cml0ZSBjb21tYW5kCiAgICAgICAgIGNvbW1hbmRbTEVOR1RIX09GRlNFVCBdID0gUElDX1JPV19TSVpFICsgMHgwMTsgLy9EQVRBX0xFTkdUSCArIENSQwogICAgICAgICAKICAgICAgICAgbWVtY3B5KCZjb21tYW5kW1BBWUxPQURfT0ZGU0VUXSwgJmRhdGFbUElDX1JPV19BRERSKHBhZ2UsIHJvdyldLCBQSUNfUk9XX1NJWkUpOwogICAgICAgICAKICAgICAgICAgY29tbWFuZFtQQVlMT0FEX09GRlNFVCArIFBJQ19ST1dfU0laRV0gPSBtYWtlQ3JjKGNvbW1hbmQsIEhFQURFUl9MRU5HVEggKyBQSUNfUk9XX1NJWkUpOwogICAgICAgICAKICAgICAgICAgcHJpbnRmKCJXcml0aW5nIHBhZ2UgJWxkIHJvdyAlbGQsICUwNGx4Li4uIiwgcGFnZSwgcm93ICsgcGFnZSpQSUNfTlVNX1JPV1NfSU5fUEFHRSwgdV9hZGRyKTsKICAgICAgICAgCiAgICAgICAgIGlmKCBnX3NpbXVsYXRlID09IDAgJiYgc2VuZENvbW1hbmRBbmRXYWl0Rm9yUmVzcG9uc2UoZmQsIGNvbW1hbmQpIDwgMCApIHsKICAgICAgICAgICAgcmV0dXJuIC0xOwogICAgICAgICB9CiAgICAgICAgIAogICAgICAgICBwdXRzKCJPSyIpOwogICAgICAgICAKICAgICAgICAgc2xlZXAoMCk7CiAgICAgICAgIAogICAgICAgICBpZiggZ192ZXJib3NlICkgewogICAgICAgICAgICBkdW1wSGV4KGNvbW1hbmQsIEhFQURFUl9MRU5HVEggKyBjb21tYW5kW0xFTkdUSF9PRkZTRVRdKTsKICAgICAgICAgfQogICAgICAgICBkb25lICs9IFBJQ19ST1dfU0laRTsKICAgICAgfQogICB9CiAgIAogICByZXR1cm4gZG9uZTsKfQoKdm9pZCBmaXhKdW1wcyh1aW50OCogYmluX2J1ZmYsIHVpbnQ4KiBwYWdlc191c2VkKQp7CiAgIHVpbnQzMiBpR290b1VzZXJBcHBBZHJlc3MgPSAwOwogICB1aW50MzIgaUdvdG9Vc2VyQXBwQWRyZXNzQjMgPSAwLCBpSXRlciA9IDA7CiAgIHVpbnQzMiBpQkxBZGRyZXNzID0gMDsKICAgCiAgIGlCTEFkZHJlc3MgPSAoIFBJQ19GTEFTSFNJWkUgLSAoQk9PVExPQURFUl9QTEFDRU1FTlQgKiBQSUNfTlVNX1JPV1NfSU5fUEFHRSAqIFBJQ19OVU1fV09SRFNfSU5fUk9XICogMikpOyAvL1BDVQogICBpR290b1VzZXJBcHBBZHJlc3MgPSBpQkxBZGRyZXNzICAtIDQ7IAogICBpR290b1VzZXJBcHBBZHJlc3NCMyA9IChpR290b1VzZXJBcHBBZHJlc3MgLyAyKSAqIDM7CiAgIAogICBmb3IgKCBpSXRlciA9IDA7IGlJdGVyIDwgNjsgaUl0ZXIrKyApIHsKICAgICAgYmluX2J1ZmZbIGlHb3RvVXNlckFwcEFkcmVzc0IzICsgaUl0ZXIgXSA9IGJpbl9idWZmWyBpSXRlciBdOwogICB9CiAgIAogICBwYWdlc191c2VkWyAoaUdvdG9Vc2VyQXBwQWRyZXNzQjMgLyBQSUNfUEFHRV9TSVpFKSBdID0gMTsKICAgCiAgIGJpbl9idWZmWzBdID0gMHgwNDsKICAgYmluX2J1ZmZbMV0gPSAoIChpQkxBZGRyZXNzICYgMHgwMDAwRkUpICk7ICAgICAgICAgCiAgIGJpbl9idWZmWzJdID0gKCAoaUJMQWRkcmVzcyAmIDB4MDBGRjAwKSA+PiA4ICk7ICAgICAgICAgCiAgIGJpbl9idWZmWzNdID0gMHgwMDsgICAKICAgYmluX2J1ZmZbNF0gPSAoIChpQkxBZGRyZXNzICYgMHg3RjAwMDApID4+IDE2ICk7CiAgIGJpbl9idWZmWzVdID0gMHgwMDsKfQoKLyogbm9uLWZpcm13YXJlIGZ1bmN0aW9ucyAqLwoKaW50IGNvbmZpZ3VyZVBvcnQoaW50IGZkLCB1bnNpZ25lZCBsb25nIGJhdWRyYXRlKQp7CiNpZmRlZiBXSU4zMgogICBEQ0IgZGNiID0gezB9OwogICBIQU5ETEUgaENvbSA9IChIQU5ETEUpZmQ7CgogICBkY2IuRENCbGVuZ3RoID0gc2l6ZW9mKGRjYik7CgogICBkY2IuQmF1ZFJhdGUgPSBiYXVkcmF0ZTsKICAgZGNiLkJ5dGVTaXplID0gODsKICAgZGNiLlBhcml0eSA9IE5PUEFSSVRZOwogICBkY2IuU3RvcEJpdHMgPSBPTkVTVE9QQklUOwoKICAgaWYoICFTZXRDb21tU3RhdGUoaENvbSwgJmRjYikgKXsKICAgICAgcmV0dXJuIC0xOwogICB9CgogICByZXR1cm4gKGludCloQ29tOwojZWxzZQogICBzdHJ1Y3QgdGVybWlvcyBnX25ld190aW87CiAgIAogICBtZW1zZXQoJmdfbmV3X3RpbywgMHgwMCAsIHNpemVvZihnX25ld190aW8pKTsKICAgY2ZtYWtlcmF3KCZnX25ld190aW8pOwogICAKICAgZ19uZXdfdGlvLmNfY2ZsYWcgfD0gIChDUzggfCBDTE9DQUwgfCBDUkVBRCk7CiAgIGdfbmV3X3Rpby5jX2NmbGFnICY9IH4oUEFSRU5CIHwgQ1NUT1BCIHwgQ1NJWkUpOwogICBnX25ld190aW8uY19vZmxhZyA9IDA7CiAgIGdfbmV3X3Rpby5jX2xmbGFnID0gMDsKICAgCiAgIAogICBnX25ld190aW8uY19jY1tWVElNRV0gPSAwOwogICBnX25ld190aW8uY19jY1tWTUlOXSA9IDE7CiAgIAogICBjZnNldGlzcGVlZCAoJmdfbmV3X3RpbywgYmF1ZHJhdGUpOwogICBjZnNldG9zcGVlZCAoJmdfbmV3X3RpbywgYmF1ZHJhdGUpOwogICAKICAgdGNmbHVzaChmZCwgVENJT0ZMVVNIKTsKICAgCiAgIHJldHVybiB0Y3NldGF0dHIoZmQsIFRDU0FOT1csICZnX25ld190aW8pOwojZW5kaWYKfQoKaW50IG9wZW5Qb3J0KGNvbnN0IGNoYXIqIGRldiwgdW5zaWduZWQgbG9uZyBmbGFncykKewogICByZXR1cm4gb3BlbihkZXYsIE9fUkRXUiB8IE9fTk9DVFRZIHwgT19OREVMQVkgfCBmbGFncyk7Cn0KCmludCBwYXJzZUNvbW1hbmRMaW5lKGludCBhcmdjLCBjb25zdCBjaGFyKiogYXJndikKewogICBpbnQgaSA9IDA7CiAgIAogICBmb3IoaT0xOyBpPGFyZ2M7IGkrKykKICAgewogICAgICAKICAgICAgaWYoICFzdHJuY21wKGFyZ3ZbaV0sICItLWhleD0iLCA2KSApIHsKICAgICAgICAgZ19oZXhmaWxlX3BhdGggPSBhcmd2W2ldICsgNjsKICAgICAgfSBlbHNlIGlmICggIXN0cm5jbXAoYXJndltpXSwgIi0tZGV2PSIsIDYpICkgewogICAgICAgICBnX2RldmljZV9wYXRoID0gYXJndltpXSArIDY7CiAgICAgIH0gZWxzZSBpZiAoICFzdHJjbXAoYXJndltpXSwgIi0tdmVyYm9zZSIpICkgewogICAgICAgICBnX3ZlcmJvc2UgPSAxOwogICAgICB9IGVsc2UgaWYgKCAhc3RyY21wKGFyZ3ZbaV0sICItLWhlbGxvIikgKSB7CiAgICAgICAgIGdfaGVsbG9fb25seSA9IDE7CiAgICAgIH0gZWxzZSBpZiAoICFzdHJjbXAoYXJndltpXSwgIi0tc2ltdWxhdGUiKSApIHsKICAgICAgICAgZ19zaW11bGF0ZSA9IDE7CiAgICAgIH0gZWxzZSBpZiAoICFzdHJjbXAoYXJndltpXSwgIi0taGVscCIpICkgewogICAgICAgICBhcmdjID0gMTsgLy90aGF0J3Mgbm90IHByZXR0eSwgYnV0IGl0IHdvcmtzIDopCiAgICAgICAgIGJyZWFrOwogICAgICB9IGVsc2UgewogICAgICAgICBmcHJpbnRmKHN0ZGVyciwgIlVua25vd24gcGFyYW1ldGVyICVzLCBwbGVhc2UgdXNlIHBpcmF0ZS1sb2FkZXIgLS1oZWxwIGZvciB1c2FnZVxuIiwgYXJndltpXSk7CiAgICAgICAgIHJldHVybiAtMTsKICAgICAgfQogICB9CiAgIAogICBpZiggYXJnYyA9PSAxICkKICAgewogICAgICAvL3ByaW50IHVzYWdlCiAgICAgIHB1dHMoInBpcmF0ZS1sb2FkZXIgdXNhZ2U6XG4iKTsKICAgICAgcHV0cygiIC4vcGlyYXRlLWxvYWRlciAtLWRldj0vcGF0aC90by9kZXZpY2UgLS1oZWxsbyIpOwogICAgICBwdXRzKCIgLi9waXJhdGUtbG9hZGVyIC0tZGV2PS9wYXRoL3RvL2RldmljZSAtLWhleD0vcGF0aC90by9oZXhmaWxlLmhleCBbIC0tdmVyYm9zZSIpOwogICAgICBwdXRzKCIgLi9waXJhdGUtbG9hZGVyIC0tc2ltdWxhdGUgLS1oZXg9L3BhdGgvdG8vaGV4ZmlsZS5oZXggWyAtLXZlcmJvc2UiKTsKICAgICAgcHV0cygiIik7CiAgICAgIAogICAgICByZXR1cm4gMDsKICAgfQogICAKICAgcmV0dXJuIDE7Cn0KCi8qIGVudHJ5IHBvaW50ICovCgppbnQgbWFpbiAoaW50IGFyZ2MsIGNvbnN0IGNoYXIqKiBhcmd2KQp7CiAgIGludCAgICAgIGRldl9mZCA9IC0xLCByZXMgPSAtMTsKICAgdWludDggICBidWZmZXJbMjU2XSA9IHswfTsKICAgdWludDggICBwYWdlc191c2VkW1BJQ19OVU1fUEFHRVNdID0gezB9OwogICB1aW50OCogICBiaW5fYnVmZiA9IE5VTEw7CiAgIAogICAKICAgcHV0cygiKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKyIpOwogICBwdXRzKCIgIFBpcmF0ZS1Mb2FkZXIgZm9yIEJQIHdpdGggQm9vdGxvYWRlciB2NCsgICIpOwogICBwdXRzKCIgIExvYWRlciB2ZXJzaW9uOiAiIFBJUkFURV9MT0FERVJfVkVSU0lPTiAiICBPUzogIiBPU19OQU1FKE9TKSk7CiAgIHB1dHMoIisrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKytcbiIpOwogICAKICAgaWYoIChyZXMgPSBwYXJzZUNvbW1hbmRMaW5lKGFyZ2MsIGFyZ3YpKSA8IDAgKSB7CiAgICAgIHJldHVybiAtMTsKICAgfSBlbHNlIGlmKCByZXMgPT0gMCApIHsKICAgICAgcmV0dXJuIDA7CiAgIH0KICAgCiAgIGlmKCAhZ19oZWxsb19vbmx5ICkgewogICAgICAKICAgICAgaWYoICFnX2hleGZpbGVfcGF0aCApIHsKICAgICAgICAgZnByaW50ZihzdGRlcnIsICJQbGVhc2Ugc3BlY2lmeSBoZXhmaWxlIHBhdGggLS1oZXg9L3BhdGgvdG8vaGV4ZmlsZS5oZXhcbiIpOwogICAgICAgICByZXR1cm4gLTE7CiAgICAgIH0KICAgCiAgICAgIGJpbl9idWZmID0gKHVpbnQ4KiltYWxsb2MoMjU2IDw8IDEwKTsgLy8yNTZrQgogICAgICBpZiggIWJpbl9idWZmICkgewogICAgICAgICBmcHJpbnRmKHN0ZGVyciwgIkNvdWxkIG5vdCBhbGxvY2F0ZSAyNTZrQiBidWZmZXJcbiIpOwogICAgICAgICBnb3RvIEVycm9yOwogICAgICB9CiAgICAgIAogICAgICAvL2ZpbGwgdGhlIGJ1ZmZlciB3aXRoIDB4RkYKICAgICAgbWVtc2V0KGJpbl9idWZmLCAweEZGRkZGRkZGLCAoMjU2IDw8IDEwKSk7CiAgICAgIAogICAgICBwcmludGYoIlBhcnNpbmcgSEVYIGZpbGUgWyVzXVxuIiwgZ19oZXhmaWxlX3BhdGgpOwogICAgICAKICAgICAgcmVzID0gcmVhZEhFWChnX2hleGZpbGVfcGF0aCwgYmluX2J1ZmYsICgyNTYgPDwgOCksIHBhZ2VzX3VzZWQpOwogICAgICBpZiggcmVzIDw9IDAgfHwgcmVzID4gUElDX0ZMQVNIU0laRSApIHsKICAgICAgICAgZnByaW50ZihzdGRlcnIsICJDb3VsZCBub3QgbG9hZCBIRVggZmlsZSwgcmVzdWx0PSVkXG4iLCByZXMpOwogICAgICAgICBnb3RvIEVycm9yOwogICAgICB9CiAgICAgIAogICAgICBwcmludGYoIkZvdW5kICVkIHdvcmRzICglZCBieXRlcylcbiIsIHJlcywgcmVzICogMyk7CiAgICAgIAogICAgICBwcmludGYoIkZpeGluZyBib290bG9hZGVyL3VzZXJwcm9ncmFtIGp1bXBzXG4iKTsKICAgICAgZml4SnVtcHMoYmluX2J1ZmYsIHBhZ2VzX3VzZWQpOwogICB9CiAgIAogICBpZiggZ19zaW11bGF0ZSApIHsKICAgICAgc2VuZEZpcm13YXJlKGRldl9mZCwgYmluX2J1ZmYsIHBhZ2VzX3VzZWQpOwogICAgICBnb3RvIEZpbmlzaGVkOwogICB9CiAgICAgIAogICBpZiggIWdfZGV2aWNlX3BhdGggKSB7CiAgICAgIGZwcmludGYoc3RkZXJyLCAiUGxlYXNlIHNwZWNpZnkgc2VyaWFsIGRldmljZSBwYXRoIC0tZGV2PS9kZXYvLi4uXG4iKTsKICAgICAgcmV0dXJuIC0xOwogICB9CiAgIAogICBwcmludGYoIk9wZW5pbmcgc2VyaWFsIGRldmljZSAlcy4uLiIsIGdfZGV2aWNlX3BhdGgpOwogICAKICAgZGV2X2ZkID0gb3BlblBvcnQoZ19kZXZpY2VfcGF0aCwgMCk7CiAgIAogICBpZiggZGV2X2ZkIDwgMCApIHsKICAgICAgcHV0cygiRVJST1IiKTsKICAgICAgZnByaW50ZihzdGRlcnIsICJDb3VsZCBub3Qgb3BlbiAlc1xuIiwgZ19kZXZpY2VfcGF0aCk7CiAgICAgIGdvdG8gRXJyb3I7CiAgIH0KICAgcHV0cygiT0siKTsKICAgCiAgIHByaW50ZigiQ29uZmlndXJpbmcgc2VyaWFsIHBvcnQgc2V0dGluZ3MuLi4iKTsKICAgCiAgIGlmKCBjb25maWd1cmVQb3J0KGRldl9mZCwgQjExNTIwMCkgPCAwICkgewogICAgICBwdXRzKCJFUlJPUiIpOwogICAgICBmcHJpbnRmKHN0ZGVyciwgIkNvdWxkIG5vdCBjb25maWd1cmUgZGV2aWNlLCBlcnJubz0lZFxuIiwgZXJybm8pOwogICAgICBnb3RvIEVycm9yOwogICB9CiAgIHB1dHMoIk9LIik7CiAgIAogICBwcmludGYoIlNlbmRpbmcgSGVsbG8gdG8gdGhlIEJvb3Rsb2FkZXIuLi4iKTsKICAgCiAgIC8vc2VuZCBIRUxMTwogICByZXMgPSB3cml0ZShkZXZfZmQsIEJPT1RMT0FERVJfSEVMTE9fU1RSLCAxKTsKICAgCiAgIHJlcyA9IHJlYWRXaXRoVGltZW91dChkZXZfZmQsIGJ1ZmZlciwgNCwgMyk7CiAgIAogICBpZiggcmVzICE9IDQgfHwgYnVmZmVyWzNdICE9IEJPT1RMT0FERVJfT0sgKSB7CiAgICAgIHB1dHMoIkVSUk9SIik7CiAgICAgIGZwcmludGYoc3RkZXJyLCAiTm8gcmVwbHkgZnJvbSB0aGUgYm9vdGxvYWRlciwgb3IgaW52YWxpZCByZXBseSByZWNlaXZlZDogJWRcbiIsIHJlcyk7CiAgICAgIGZwcmludGYoc3RkZXJyLCAiUGxlYXNlIG1ha2Ugc3VyZSB0aGF0IFBHTkQgYW5kIFBHQyBhcmUgY29ubmVjdGVkLCByZXBsdWcgdGhlIGRldmlkZSBhbmQgdHJ5IGFnYWluXG4iKTsKICAgICAgZ290byBFcnJvcjsKICAgfQogICBwdXRzKCJPS1xuIik7IC8vZXh0cmEgTEYgZm9yIHNwYWNpbmcKICAgCiAgIHByaW50ZigiRGV2aWNlIElEOiAlcyBbJTAyeF1cbiIsIChidWZmZXJbMF0gPT0gMHhENCkgPyAiUElDMjRGSjY0R0EwMDIiIDogIlVOS05PV04iLCBidWZmZXJbMF0pOwogICBwcmludGYoIkJvb3Rsb2FkZXIgdmVyc2lvbjogJWQsJTAyZFxuIiwgYnVmZmVyWzFdLCBidWZmZXJbMl0pOwogICAKICAgaWYoIGJ1ZmZlclswXSAhPSAweEQ0ICkgewogICAgICBmcHJpbnRmKHN0ZGVyciwgIlVuc3VwcG9ydGVkIGRldmljZSAoJTAyeDpVTktOT1dOKSwgb25seSAweEQ0IFBJQzI0Rko2NEdBMDAyIGlzIHN1cHBvcnRlZFxuIiwgYnVmZmVyWzBdKTsKICAgICAgZ290byBFcnJvcjsKICAgfQogICAKICAgaWYoICFnX2hlbGxvX29ubHkgKSB7CiAgIAogICAgICByZXMgPSBzZW5kRmlybXdhcmUoZGV2X2ZkLCBiaW5fYnVmZiwgcGFnZXNfdXNlZCk7CiAgICAgIAogICAgICBpZiggcmVzID4gMCApIHsKICAgICAgICAgcHV0cygiXG5GaXJtd2FyZSB1cGRhdGVkIHN1Y2Nlc3NmdWxseSA6KSEiKTsKICAgICAgICAgcHJpbnRmKCJVc2Ugc2NyZWVuICVzIDExNTIwMCB0byB2ZXJpZnlcbiIsIGdfZGV2aWNlX3BhdGgpOwogICAgICB9IGVsc2UgewogICAgICAgICBwdXRzKCJcbkVycm9yIHVwZGF0aW5nIGZpcm13YXJlIDooIik7CiAgICAgICAgIGdvdG8gRXJyb3I7CiAgICAgIH0KICAgICAgCiAgIH0KICAgCkZpbmlzaGVkOgogICBpZiggYmluX2J1ZmYgKSB7IAogICAgICBmcmVlKCBiaW5fYnVmZiApOwogICB9CiAgIGNsb3NlKGRldl9mZCk7CiAgICByZXR1cm4gMDsKICAgCkVycm9yOgogICBpZiggYmluX2J1ZmYgKSB7CiAgICAgIGZyZWUoIGJpbl9idWZmICk7CiAgIH0KICAgaWYoIGRldl9mZCA+PSAwICkgewogICAgICBjbG9zZShkZXZfZmQpOwogICB9CiAgIHJldHVybiAtMTsKfQ==