diff --git a/embedded8266/Makefile b/embedded8266/Makefile index cd99278..30eceb9 100644 --- a/embedded8266/Makefile +++ b/embedded8266/Makefile @@ -1,8 +1,7 @@ -all : image.elf FW_FILE_1:=0x00000.bin FW_FILE_2:=0x40000.bin - TARGET_OUT:=image.elf +all : $(TARGET_OUT) $(FW_FILE_1) $(FW_FILE_2) SRCS:=driver/uart.c \ @@ -24,7 +23,7 @@ SRCS:=driver/uart.c \ GCC_FOLDER:=~/esp8266/esp-open-sdk/xtensa-lx106-elf ESPTOOL_PY:=~/esp8266/esptool/esptool.py FW_TOOL:=~/esp8266/other/esptool/esptool -SDK:=/home/cnlohr/esp8266/esp_iot_sdk_v1.2.0 +SDK:=/home/cnlohr/esp8266/esp_iot_sdk_v1.3.0 PORT:=/dev/ttyUSB0 #PORT:=/dev/ttyACM0 diff --git a/embedded8266/common/commonservices.c b/embedded8266/common/commonservices.c index a370080..fb51781 100644 --- a/embedded8266/common/commonservices.c +++ b/embedded8266/common/commonservices.c @@ -128,7 +128,7 @@ int ICACHE_FLASH_ATTR issue_command(char * buffer, int retsize, char *pusrdata, buffend += ets_sprintf( buffend, "!FM%d\r\n", r ); break; } - case 'w': case 'W': //Flash Write (FW#\n) <- # = byte pos. + case 'w': case 'W': //Flash Write (FW#\n) <- # = byte pos. Reads until end-of-packet. if( colon ) { colon++; @@ -149,6 +149,48 @@ int ICACHE_FLASH_ATTR issue_command(char * buffer, int retsize, char *pusrdata, } buffend += ets_sprintf(buffend, "!FW\r\n" ); break; + case 'x': case 'X': //Flash Write Hex (FX#\t#\tDATTAAAAA) <- a = byte pos. b = length (in hex-pairs). Generally used for web-browser. + if( colon ) + { + int i; + int siz = 0; + colon++; + char * colon2 = (char *) ets_strstr( (char*)colon, "\t" ); + if( colon2 ) + { + *colon2 = 0; + siz = my_atoi( colon ); + } + //nr = place to write. + //siz = size to write. + //colon2 = data start. + if( colon2 && nr >= 524288) + { + colon2++; + int datlen = ((int)len - (colon2 - pusrdata))/2; + if( datlen > siz ) datlen = siz; + + for( i = 0; i < datlen; i++ ) + { + int8_t r1 = fromhex1( *(colon2++) ); + int8_t r2 = fromhex1( *(colon2++) ); + if( r1 == -1 || r2 == -1 ) goto failfx; + buffend[i] = (r1 << 4) | r2; + } + + //ets_memcpy( buffer, colon2, datlen ); + + EnterCritical(); + spi_flash_write( nr, (uint32*)buffend, (datlen/4)*4 ); + ExitCritical(); + + buffend += ets_sprintf(buffend, "FX%d\t%d\r\n", nr, siz ); + break; + } + } +failfx: + buffend += ets_sprintf(buffend, "!FX\r\n" ); + break; case 'r': case 'R': //Flash Read (FR#\n) <- # = sector. if( colon ) { diff --git a/embedded8266/common/esp8266_rom.h b/embedded8266/common/esp8266_rom.h index 96d839b..3ac9fdf 100644 --- a/embedded8266/common/esp8266_rom.h +++ b/embedded8266/common/esp8266_rom.h @@ -45,7 +45,7 @@ void SHA1Transform(uint32 state[5], const uint8 buffer[64]); //SPI_FLASH_SEC_SIZE 4096 -void SPIEraseSector(uint16 sec); //Doesn't work? +void SPIEraseSector(uint16 sec); void SPIEraseArea(uint32 start,uint32 len); //Doesn't work? void SPIEraseBlock(uint16 blk); void SPIWrite(uint32 des_addr, uint32_t *src_addr, uint32_t size); diff --git a/embedded8266/common/flash_rewriter.c b/embedded8266/common/flash_rewriter.c index 3967138..e0ff666 100644 --- a/embedded8266/common/flash_rewriter.c +++ b/embedded8266/common/flash_rewriter.c @@ -7,7 +7,7 @@ #include #define SRCSIZE 4096 - +#define BLKSIZE 65536 static const char * key = ""; static int keylen = 0; @@ -211,30 +211,39 @@ static int MyRewriteFlash( char * command, int commandlen ) //Disable all interrupts. ets_intr_lock(); - ipl = (size1/SRCSIZE)+1; - p = to1/SRCSIZE; + uart_tx_one_char( 'A' ); + + int j; + ipl = (size1/BLKSIZE)+1; + p = to1/BLKSIZE; for( i = 0; i < ipl; i++ ) { - SPIEraseSector( p++ ); - SPIWrite( to1, (uint32_t*)(0x40200000 + from1), SRCSIZE ); - to1 += SRCSIZE; - from1 += SRCSIZE; + SPIEraseBlock( p++ ); + + for( j = 0; j < BLKSIZE/SRCSIZE; j++ ) + { + SPIWrite( to1, (uint32_t*)(0x40200000 + from1), SRCSIZE ); + to1 += SRCSIZE; + from1 += SRCSIZE; + } } uart_tx_one_char( 'B' ); - ipl = (size2/SRCSIZE)+1; - p = to2/SRCSIZE; + ipl = (size2/BLKSIZE)+1; + p = to2/BLKSIZE; for( i = 0; i < ipl; i++ ) { - SPIEraseSector( p++ ); - SPIWrite( to2, (uint32_t*)(0x40200000 + from2), SRCSIZE ); - to2 += SRCSIZE; - from2 += SRCSIZE; - + SPIEraseBlock( p++ ); + for( j = 0; j < BLKSIZE/SRCSIZE; j++ ) + { + SPIWrite( to2, (uint32_t*)(0x40200000 + from2), SRCSIZE ); + to2 += SRCSIZE; + from2 += SRCSIZE; + } } - uart_tx_one_char( 'R' ); + uart_tx_one_char( 'C' ); void(*rebootme)() = (void(*)())0x40000080; diff --git a/embedded8266/common/http.c b/embedded8266/common/http.c index 603b1af..59b1856 100644 --- a/embedded8266/common/http.c +++ b/embedded8266/common/http.c @@ -515,12 +515,14 @@ void ICACHE_FLASH_ATTR WebSocketGotData( uint8_t c ) break; case 5: //Established connection. { + //XXX TODO: Seems to malfunction on large-ish packets. I know it has problems with 140-byte payloads. + if( curlen < 5 ) //Can't interpret packet. break; uint8_t fin = c & 1; uint8_t opcode = c << 4; - uint32_t payloadlen = *(curdata++); + uint16_t payloadlen = *(curdata++); curlen--; if( !(payloadlen & 0x80) ) { @@ -529,6 +531,8 @@ void ICACHE_FLASH_ATTR WebSocketGotData( uint8_t c ) break; } + payloadlen &= 0x7f; + if( payloadlen == 127 ) { //Very long payload. @@ -543,10 +547,6 @@ void ICACHE_FLASH_ATTR WebSocketGotData( uint8_t c ) curdata += 2; curlen -= 2; } - else - { - payloadlen &= 0x7f; - } wsmask[0] = curdata[0]; wsmask[1] = curdata[1]; @@ -556,10 +556,21 @@ void ICACHE_FLASH_ATTR WebSocketGotData( uint8_t c ) curlen -= 4; wsmaskplace = 0; + //XXX Warning: When packets get larger, they may split the + //websockets packets into multiple parts. We could handle this + //but at the cost of prescious RAM. I am chosing to just drop those + //packets on the floor, and restarting the connection. + if( curlen < payloadlen ) + { + HTDEBUG( "Websocket Fragmented. %d %d\n", curlen, payloadlen ); + curhttp->state = HTTP_WAIT_CLOSE; + return; + } + WebSocketData( payloadlen ); - curlen -= payloadlen; + curlen -= payloadlen; curdata += payloadlen; - + break; } default: diff --git a/embedded8266/common/mystuff.c b/embedded8266/common/mystuff.c index b9cf874..d12ad8d 100644 --- a/embedded8266/common/mystuff.c +++ b/embedded8266/common/mystuff.c @@ -68,11 +68,26 @@ char tohex1( uint8_t i ) return (i<10)?('0'+i):('a'-10+i); } +int8_t fromhex1( char c ) +{ + if( c >= '0' && c <= '9' ) + return c - '0'; + else if( c >= 'a' && c <= 'f' ) + return c - 'a' + 10; + else if( c >= 'A' && c <= 'F' ) + return c - 'A' + 10; + else + return -1; +} + + void ICACHE_FLASH_ATTR EndTCPWrite( struct espconn * conn ) { if(generic_ptr!=generic_buffer) - espconn_sent(conn,generic_buffer,generic_ptr-generic_buffer); + { + int r = espconn_sent(conn,generic_buffer,generic_ptr-generic_buffer); + } } @@ -93,6 +108,9 @@ void PushBlob( const uint8 * buffer, int len ) int8_t TCPCanSend( struct espconn * conn, int size ) { +#ifdef SAFESEND + return TCPDoneSend( conn ); +#else struct espconn_packet infoarg; sint8 r = espconn_get_packet_info(conn, &infoarg); @@ -100,6 +118,7 @@ int8_t TCPCanSend( struct espconn * conn, int size ) return 1; else return 0; +#endif } int8_t ICACHE_FLASH_ATTR TCPDoneSend( struct espconn * conn ) diff --git a/embedded8266/common/mystuff.h b/embedded8266/common/mystuff.h index 6fb3d6f..24cc6af 100644 --- a/embedded8266/common/mystuff.h +++ b/embedded8266/common/mystuff.h @@ -5,6 +5,7 @@ #ifndef _MYSTUFF_H #define _MYSTUFF_H + #include #include #include @@ -12,6 +13,14 @@ #include #include + +//XXX WARNING As of 1.3.0, "cansend" doesn't work. +//the SDK seems to misbehave when trying to send without a full +//response packet. + +#define SAFESEND + + extern char generic_print_buffer[384]; extern const char * enctypes[6];// = { "open", "wep", "wpa", "wpa2", "wpa_wpa2", 0 }; @@ -19,6 +28,7 @@ extern const char * enctypes[6];// = { "open", "wep", "wpa", "wpa2", "wpa_wpa2", #define printf( ... ) ets_sprintf( generic_print_buffer, __VA_ARGS__ ); uart0_sendStr( generic_print_buffer ); char tohex1( uint8_t i ); +int8_t fromhex1( char c ); //returns -1 if not hex char. int32 my_atoi( const char * in ); void Uint32To10Str( char * out, uint32 dat ); diff --git a/embedded8266/driver/uart.c b/embedded8266/driver/uart.c index 96c1422..5ae3356 100644 --- a/embedded8266/driver/uart.c +++ b/embedded8266/driver/uart.c @@ -10,6 +10,7 @@ * 2014/3/12, v1.0 create this file. * [various] Not released, Modified by Charles Lohr to change the RX behavior *******************************************************************************/ +#include #include #include #include diff --git a/embedded8266/image.elf b/embedded8266/image.elf index 19c96fe..08656c6 100755 Binary files a/embedded8266/image.elf and b/embedded8266/image.elf differ diff --git a/embedded8266/web/Makefile b/embedded8266/web/Makefile index 1c65988..575d7ed 100644 --- a/embedded8266/web/Makefile +++ b/embedded8266/web/Makefile @@ -1,13 +1,13 @@ -all : execute_reflash page.dat push +all : execute_reflash page.mpfs push IP?=192.168.4.1 mfsmaker : mfsmaker.c gcc -o $@ $^ -page.dat : mfsmaker page +page.mpfs : mfsmaker page # cat to_compress/*.js | gzip -9 > page/compressed.js.gz - ./mfsmaker page page.dat + ./mfsmaker page page.mpfs pushtodev : pushtodev.c gcc -o $@ $^ @@ -15,8 +15,8 @@ pushtodev : pushtodev.c execute_reflash : execute_reflash.c md5.c gcc -o $@ $^ -push : pushtodev page.dat - ./pushtodev $(IP) 1048576 page.dat +push : pushtodev page.mpfs + ./pushtodev $(IP) 1048576 page.mpfs clean : - rm -rf mfsmaker page.dat pushtodev execute_reflash + rm -rf mfsmaker page.mpfs pushtodev execute_reflash diff --git a/embedded8266/web/mfsmaker.c b/embedded8266/web/mfsmaker.c index ad67c7c..cd7d027 100644 --- a/embedded8266/web/mfsmaker.c +++ b/embedded8266/web/mfsmaker.c @@ -107,7 +107,7 @@ int main( int argc, char ** argv ) int rs = buf.st_size; rs = (rs+1+MFS_SECTOR)&(~(MFS_SECTOR-1)); datapointer += rs; - printf( "%s: %d (%d)\n", thisfile, rs, datapointer ); + printf( "%s: %d (%ld)\n", thisfile, rs, datapointer ); } } } @@ -121,7 +121,7 @@ int main( int argc, char ** argv ) mfsfat[i].start = ENDIAN(mfsfat[i].start + rs ); } - printf( "%d %d\n", rs, datapointer ); + printf( "%d %ld\n", rs, datapointer ); FILE * f = fopen( argv[2], "w" ); diff --git a/embedded8266/web/page/index.html b/embedded8266/web/page/index.html index b0be515..57a621e 100644 --- a/embedded8266/web/page/index.html +++ b/embedded8266/web/page/index.html @@ -13,6 +13,7 @@ td { vertical-align: top; } .collapsible { display:none; } .inbutton { background-color:blue; } #SystemMessage { position: fixed; top: 5px; background-color: DarkSlateBlue; color: #ffffff; left: 5px; display:none; } +.dragandrophandler { border:2px dotted #0B85A1; color:#92AAB0;vertical-align:middle;padding:10px 10px 10 10px;margin-bottom:10px;font-size:200%;} diff --git a/embedded8266/web/page/menuinterface.js b/embedded8266/web/page/menuinterface.js index dfc76b8..d43d79f 100644 --- a/embedded8266/web/page/menuinterface.js +++ b/embedded8266/web/page/menuinterface.js @@ -6,6 +6,10 @@ var output; var websocket; var commsup = 0; +var mpfs_start_at = 1048576; +var flash_scratchpad_at = 524288; +var flash_blocksize = 65536; +var flash_sendsize = 256; //Push objects that have: // .request // .callback = function( ref (this object), data ); @@ -93,20 +97,35 @@ function init() 13 \ 14 \ 15 \ - "); + \ +\ + \ + \ +
\ +
\ +
Drop or browse for system (0x000.. 0x400...) or web (.mpfs) reflash files.
\ +
\ +"); - + MakeDragDrop( "InnerSystemReflash", DragDropSystemFiles ); + $("#dragndropersystem").change(function() { DragDropSystemFiles(this.files ); }); $( ".collapsible" ).each(function( index ) { if( localStorage["sh" + this.id] > 0.5 ) { $( this ).show().toggleClass( 'opened' ); +// console.log( "OPEN: " + this.id ); } }); $("#custom_command_response").val( "" ); + //Preclude drag and drop on rest of document in event user misses firmware boxes. + donothing = function(e) {e.stopPropagation();e.preventDefault();}; + $(document).on('drop', donothing ); + $(document).on('dragover', donothing ); + $(document).on('dragenter', donothing ); output = document.getElementById("output"); Ticker(); @@ -146,7 +165,7 @@ function onClose(evt) var msg = 0; var tickmessage = 0; var lasthz = 0; - +var time_since_hz = 0; function Ticker() { setTimeout( Ticker, 1000 ); @@ -155,14 +174,21 @@ function Ticker() tickmessage = msg; if( lasthz == 0 ) { - $('#SystemStatusClicker').css("color", "red" ); - $('#SystemStatusClicker').prop( "value", "System Offline" ); - if( commsup != 0 && !is_waiting_on_stations ) IssueSystemMessage( "Comms Lost." ); - commsup = 0; - StartWebSocket(); + time_since_hz++; + if( time_since_hz > 3 ) + { + $('#SystemStatusClicker').css("color", "red" ); + $('#SystemStatusClicker').prop( "value", "System Offline" ); + if( commsup != 0 && !is_waiting_on_stations ) IssueSystemMessage( "Comms Lost." ); + commsup = 0; + StartWebSocket(); + } + else + $('#SystemStatusClicker').prop( "value", "System " + 0 + "Hz" ); } else { + time_since_hz = 0; $('#SystemStatusClicker').prop( "value", "System " + lasthz + "Hz" ); } } @@ -250,6 +276,52 @@ function IssueCustomCommand() + + + +function MakeDragDrop( divname, callback ) +{ + var obj = $("#" + divname); + obj.on('dragenter', function (e) + { + e.stopPropagation(); + e.preventDefault(); + $(this).css('border', '2px solid #0B85A1'); + }); + + obj.on('dragover', function (e) + { + e.stopPropagation(); + e.preventDefault(); + }); + + obj.on('dragend', function (e) + { + e.stopPropagation(); + e.preventDefault(); + $(this).css('border', '2px dotted #0B85A1'); + }); + + obj.on('drop', function (e) + { + $(this).css('border', '2px dotted #0B85A1'); + e.preventDefault(); + var files = e.originalEvent.dataTransfer.files; + + //We need to send dropped files to Server + callback(files); + }); +} + + + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////// +///Below here are mostly just events... + did_wifi_get_config = false; is_data_ticker_running = false; is_waiting_on_stations = false; @@ -396,7 +468,7 @@ function GPIOInput( gp ) } function GPIOUpdate(req,data) { - var secs = data.split( ":" ); + var secs = data.split( "\t" ); var op = 0; var n = Number(secs[2]); var m = Number(secs[1]); @@ -436,11 +508,11 @@ function GPIOUpdate(req,data) { b.attr( "value", "in" ); } } - - } if( IsTabOpen('GPIOs') ) + { QueueOperation( "GS", GPIOUpdate ); + } } function GPIODataTicker() @@ -462,6 +534,147 @@ function GPIODataTickerStart() +function SystemPushImageProgress( is_ok, comment, pushop ) +{ + if( !is_ok ) + { + $("#innersystemflashtext").html( "Failed: " + comment ); + return; + } + + $("#innersystemflashtext").html( comment ); + + if( pushop.place == pushop.padlen ) + { + if( pushop.ctx.current_state == 0 ) //File 1 is completing. + { + pushop.ctx.current_state = 1; + pushop.ctx.file1wassize = pushop.padlen; + pushop.ctx.file1md5 = faultylabs.MD5( pushop.paddata ).toLowerCase(); + var reader = new FileReader(); + + reader.onload = function(e) { + $("#innersystemflashtext").html( "Pusing second half..." ); + PushImageTo( e.target.result, flash_scratchpad_at + 0x40000, SystemPushImageProgress, pushop.ctx ); + } + + reader.readAsArrayBuffer( pushop.ctx.file2 ); + } + else if( pushop.ctx.current_state == 1 ) + { + var f1s = pushop.ctx.file1wassize; + var f1m = pushop.ctx.file1md5; + var f2s = pushop.padlen; + var f2m = faultylabs.MD5( pushop.paddata ).toLowerCase(); + + $("#innersystemflashtext").html( "Issuing reflash. Do not expect a response." ); + + var stf = "FM" + flash_scratchpad_at + "\t0\t" + f1s + "\t" + f1m + "\t" + (flash_scratchpad_at+0x40000) + "\t" + 0x40000 + "\t" + f2s + "\t" + f2m + "\n"; + var fun = function( fsrd, flashresponse ) { $("#innerflashtext").html( (flashresponse[0] == '!')?"Flashing failed.":"Flash success." ) }; + QueueOperation( stf, fun); + } + + return false; + } + + return true; +} + + +function WebPagePushImageFunction( ok, comment, pushop ) +{ + if( pushop.place == pushop.padlen ) + { + $("#innersystemflashtext").html("Push complete. Reload page."); + } + else + { + $("#innersystemflashtext").html(comment); + } + + return true; +} + +function DragDropSystemFiles( file ) +{ + if( file.length == 1 ) + { + //webpage ".mpfs" file. + var fn = file[0].name; + if( fn.substr( fn.length - 5 ) != ".mpfs" ) + { + $("#innersystemflashtext").html( "Web files are .mfps files." ); + return; + } + + $("#innersystemflashtext").html( "Opening " + fn ); + + var reader = new FileReader(); + + reader.onload = function(e) { + PushImageTo( e.target.result, mpfs_start_at, WebPagePushImageFunction ); + } + + reader.readAsArrayBuffer( file[0] ); + } + else if( file.length == 2 ) + { + var file1 = null; + var file2 = null; + + for( var i = 0; i < file.length; i++ ) + { + if( file[i].name.substr( 0, 7 ) == "0x00000" ) file1 = file[i]; + if( file[i].name.substr( 0, 7 ) == "0x40000" ) file2 = file[i]; + } + + if( !file1 ) + { + $("#innersystemflashtext").html( "Could not find a 0x00000... file." ); return; + } + + if( !file2 ) + { + $("#innersystemflashtext").html( "Could not find a 0x40000... file." ); return; + } + + if( file1.size > 65536 ) + { + $("#innersystemflashtext").html( "0x00000 needs to fit in IRAM. Too big." ); return; + } + + if( file2.size > 262144 ) + { + $("#innersystemflashtext").html( "0x40000 needs to fit in 256kB. Too big." ); return; + } + + //Files check out. Start pushing. + + $("#innersystemflashtext").html( "Starting." ); + + var reader = new FileReader(); + + reader.onload = function(e) { + var ctx = new Object(); + ctx.file1 = file1; + ctx.file2 = file2; + ctx.current_state = 0; + PushImageTo( e.target.result, flash_scratchpad_at, SystemPushImageProgress, ctx ); + } + + reader.readAsArrayBuffer( file[0] ); + return; + } + else + { + $("#innersystemflashtext").html( "Cannot accept anything other than 1 or 2 files." ); + } +} + + + + + @@ -471,3 +684,75 @@ function tohex8( c ) var hex = c.toString(16); return hex.length == 1 ? "0" + hex : hex; } + + +function ContinueSystemFlash( fsrd, flashresponse, pushop ) +{ + if( flashresponse[0] == '!' ) + { + pushop.status_callback( 0, flashresponse, pushop ); + console.log( flashresponse ); + return; + } + + var cont = pushop.status_callback( 1, flashresponse, pushop ); + + if( !cont ) return; + if( pushop.place >= pushop.padlen ) return; + + //If we are coming from a write, and now we need to erase the next block, do so. + + if( ( pushop.place % flash_blocksize ) == 0 && flashresponse[1] != 'B' ) + { + QueueOperation( "FB" + ((pushop.place+pushop.base_address)/flash_blocksize), function( x, y ) { ContinueSystemFlash( x, y, pushop ); } ); + } + else //Done erasing the next block, or coming off a write we don't need to erase? + { + var addy = pushop.place + pushop.base_address; + var sendstr = "FX" + addy + "\t" + flash_sendsize + "\t"; + for( var i = 0; i < flash_sendsize; i++ ) + { + sendstr += tohex8( pushop.paddata[pushop.place++] ); + } + QueueOperation( sendstr, function( x, y ) { ContinueSystemFlash( x, y, pushop ); } ); + } +} + +//The signature for status callback is: function AVRStatusCallback( is_ok, comment, pushop ) +//If pushop.place == pushop.padlen, no further callbacks will continue, even if true is returned. +//you must return "true." Returning false will cease further pushing. +//This function returns an object with all properties about the transfer. +//WARNING: "location" must be block (65536) aligned. +function PushImageTo( arraydata, location, status_callback, ctx ) +{ + if( location & 0xffff != 0 ) + { + console.log( "Error: to address not 65,536 aligned." ); + return null; + } + + var pushop = Object(); + pushop.padlen = Math.floor(((arraydata.byteLength-1)/flash_sendsize)+1)*flash_sendsize; + pushop.paddata = new Uint8Array( pushop.padlen, 0 ); + pushop.paddata.set( new Uint8Array( arraydata ), 0 ); + pushop.status_callback = status_callback; + pushop.place = 0; + pushop.base_address = location; + pushop.ctx = ctx; + + ContinueSystemFlash( null, "Starting", pushop ); + + return pushop; +} + + + + + +/* MD5 implementation minified from: http://blog.faultylabs.com/files/md5.js + Javascript MD5 library - version 0.4 Coded (2011) by Luigi Galli - LG@4e71.org - http://faultylabs.com + Thanks to: Roberto Viola The below code is PUBLIC DOMAIN - NO WARRANTY! + */ +"undefined"==typeof faultylabs&&(faultylabs={}),faultylabs.MD5=function(n){function r(n){var r=(n>>>0).toString(16);return"00000000".substr(0,8-r.length)+r}function t(n){for(var r=[],t=0;tt;t++)r.push(255&n),n>>>=8;return r}function o(n,r){return n<>>32-r}function a(n,r,t){return n&r|~n&t}function f(n,r,t){return t&n|~t&r}function u(n,r,t){return n^r^t}function i(n,r,t){return r^(n|~t)}function c(n,r){return n[r+3]<<24|n[r+2]<<16|n[r+1]<<8|n[r]}function s(n){for(var r=[],t=0;t=0;o--)e=arguments[o],t=255&e,e>>>=8,t<<=8,t|=255&e,e>>>=8,t<<=8,t|=255&e,e>>>=8,t<<=8,t|=e,n+=r(t);return n}function y(n){for(var r=new Array(n.length),t=0;t56){for(var s=0;64-t>s;s++)A.push(0);t=A.length%64}for(s=0;56-t>s;s++)A.push(0);A=A.concat(e(8*r));var y=1732584193,p=4023233417,g=2562383102,v=271733878,b=0,d=0,U=0,m=0;for(s=0;s