LockToKey

From DCppWiki

Jump to: navigation, search

This page contains some code used to generate a $Key from a $Lock in a variety of languages.

Contents

[edit] C

[edit] Example One

#include <string.h>
#include <stdlib.h>

/* erealloc done by James N. Hart
   Abridged version.
   INPUT:
     void **mem
       - should be a pointer to the pointer that you want to allocate or
       reallocate memory to.  If *mem == NULL (0) then data will be allocated
       from scratch.  If != NULL then data will be realloc'ed.
     int size
       - should be the size of the memory you want allocated. *note this
       revision doesn't do any checking for size 0.  Results are undefined if
       size == 0..

    OUTPUT:
      void **mem
        - assigned with allocated memory.  Memory is initialized with all
        bits 0.

    PURPOSE:
      To give one interface for malloc and realloc, to initialized malloc'ed
      data, and to do error checking and handling.

    ABRIDGED NOTE:
      There are comments where you should add your own error handling code.

    EXAMPLE:
      Create a character array of 35 characters, then resize it to 25.
      char *string = 0;  erealloc((void **)&string, sizeof(char)*35);
      erealloc((void **)&string, sizeof(char)*25);
*/
void erealloc(void **mem, size_t size)
{
   int err = 0;
   if(!*mem)
     {
        if(!(*mem = malloc(size)))
          {
            /*You should handle a memory allocation error here */
            exit(1);
          }
        memset(*mem, 0, size);
        return;
     }
   if(! (*mem = realloc(*mem, size) ) )
     {
        /*You should handle a memory allocation error here */
        exit(1);
     }
}
/* generateKey done by James N. Hart
   INPUT:
     unsigned char *lock
       - A null terminated character array that contains the lock posted with
       $Lock (excluding $Lock= and ' PK=*')
     unsigned char **fkey
       - a pointer to a previous allocated char *array or a pointer to a
       char *array that has been initialized wiht NULL (0).

    OUTPUT:
      unsigned char **fkey 
        - assigned a allocated char *array, null terminated that contains
        the key.

    PURPOSE:
      To generate the key from given lock for as specified for the
      DC Protocol.

    BUGS:
      If you want to use this in a real program, you may want to add error
      handling to check that the lock is at least 2 characters long. (if not
      this code should crash)

    EXAMPLE:
      char *lock = "IEatHamsters", *key = 0;
      generateKey(lock, &key);
*/
void generateKey(unsigned char *lock, unsigned char **fkey)
{
   int count = 0, len = 0, offset = 0;
   char *key = 0, *tkey= 0;
   /* Get the length of the lock */
   len = strlen(lock);
   /* Initialize the key memory */
   erealloc((void **)&key, sizeof(char)*(len + 1) );

   /* assign key[1 .. len - 1] accoarding to lock to key specs */
   while(lock[++count])
        key[count] = lock[count] ^ lock[count - 1];
   /* assign key[0] with the special data */
   key[0] = lock[0] ^ lock[len - 1] ^ lock[len - 2] ^ 5;

   count = 0;
   /* Swap 4 bits of each result in Key at this point */
   while(key[count++])
     key[count - 1] = ((key[count - 1]  << 4)) | ((key[count - 1] >> 4));

   count = 0;
   /* tkey stands for Temp Key, it is needed to write out the encoded key */
   erealloc((void **)fkey, sizeof(char)*(len + 1));
   tkey = *fkey;
   while(key[count++])
     {
        /* If a byte has any of the following values in the case statement it needs
           to be encoded in the format /%DCNxxx%/ where xxx is the ascii value of
           the character */
        switch(key[count - 1])
          {
           case 0:
           case 5:
           case  36:
           case 96:
           case 124:
           case 126:
             erealloc((void **)&tkey, sizeof(char)*(len + offset + 11));
             sprintf(&tkey[count - 1 + offset], "/%%DCN%.3d%%/", key[count - 1]);
             /* offset let's us keep track of where we are in tkey in comparison to
                key.  Every time we encode a character we have a diffrence of 9 more
                characters offseted in tkey in relation to key
            */
             offset += 9;
             break;
             /* We don't need to encode this character */
           default:
             tkey[count - 1 + offset] = key[count - 1];
             break;
          }

     }
   /* Give our string its ending null */
   tkey[len + offset] = 0;
   /* Assign memory to fkey */
   *fkey = tkey;
   /* Free key */
   free(key);
}

[edit] Example Two

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

//From rapsys : http://rapsys.free.fr/ (Under GPL v2 or more license)

/**
 * Compute the access key from the $Lock string
 * @param lock the lock string
 * @return the computed key
 */
char *dc_compute_access_key(char *lock)
{
  int count, len, offset;
  char *key, *tkey;
  unsigned char *fkey;

  //Default value
  offset = 0;
  key = NULL;
  tkey = NULL;

  // Get the length of the lock
  len = strlen(lock);

  //Initialize the key memory
  key = realloc(key, sizeof(char)*(len + 1) );
  if (key == NULL)
  {
    fprintf(stderr,"Error while realloc key");
    return NULL;
  }

  //Assign key[1 .. len - 1] accoarding to lock to key specs
  count = 1;
  while(lock[count])
  {
    key[count] = lock[count] ^ lock[count - 1];
    count++;
  }
  //Assign key[0] with the special data
  key[0] = lock[0] ^ lock[len - 1] ^ lock[len - 2] ^ 5;

  count = 0;
  //Swap 4 bits of each result in Key at this point
  while(key[count])
  {
    count++;
    key[ ((key[count - 1|count - 1] = ((key[count - 1]  << 4)) |] >> 4));
  }

  count = 0;
  /* tkey stands for Temp Key, it is needed to write out the encoded key */
  tkey = realloc(tkey, sizeof(char)*(len + 1));
  if (tkey == NULL)
  {
    fprintf(stderr,"Error while realloc tkey");
    return NULL;
  }
  fkey = (unsigned char*)tkey;
  while(key[count++])
  {
    /* If a byte has any of the following values in the case statement it needs
     * to be encoded in the format /%DCNxxx%/ where xxx is the ascii value of
     * the character
     */
    switch(key[count - 1])
    {
      case   0:
      case   5:
      case  36:
      case  96:
      case 124:
      case 126:
        tkey = realloc(tkey, sizeof(char)*(len + offset + 11));
        if (tkey == NULL)
        {
          fprintf(stderr,"Error while realloc fkey");
          return NULL;
        }
        sprintf(&tkey[count - 1 + offset], "/%%DCN%.3d%%/", key[count - 1]);
        /* offset let's us keep track of where we are in tkey in comparison to
         * key.  Every time we encode a character we have a diffrence of 9 more
         * characters offseted in tkey in relation to key
         */
        offset += 9;
        break;
      default:
        // We don't need to encode this character
        tkey[count - 1 + offset] = key[count - 1];
    }
  }
  //Give our string its ending null
  tkey[len + offset] = 0;

  //Free key
  free(key);
  return tkey;
}

int main(int argc, char** argv)
{
  printf("%s",dc_compute_access_key("somelockstring"));

  return 0;
}

[edit] Example Three

#include <stdlib.h>
/* Taken from Dolda Connect -- Licensed under the GPL */

static int reservedchar(unsigned char c)
{
    return((c == 0) || (c == 5) || (c == 124) || (c == 96) || (c == 126) || (c == 36));
}

static char *dcmakekey(char *lock)
{
    int i, len, offset;
    char *buf, *key;
    char save;
    
    /* Step 1: Compute key */
    buf = malloc(strlen(lock));
    save = 5;
    len = 0;
    for(i = 0; lock[i]; i++) {
        buf[i] = lock[i] ^ save;
        buf[i] = ((buf[i] & 0x0F) << 4) | ((buf[i] & 0xF0) >> 4);
        save = lock[i];
        if((i != 0) && reservedchar(buf[i]))
            len += 10;
        else
            len++;
    }
    buf[0] ^= buf[i - 1];
    if(reservedchar(buf[0]))
        len += 10;
    else
        len++;
    
    /* Step 2: Quote reserved characters */
    key = malloc(len + 1);
    offset = 0;
    for(i = 0; lock[i] != 0; i++) {
        if(reservedchar(buf[i]))
            offset += sprintf(key + offset, "/%%DCN%03i%%/", buf[i]);
        else
            key[offset++] = buf[i];
    }
    key[offset] = 0;
    free(buf);
    
    /* Observe: The caller will have to free the memory */
    return(key);
}

[edit] Perl

sub lock2key
{
   my @lock = split( // , shift );
   my $i;
   my @key = ();
   # convert to ordinal
   foreach( @lock ) {
       $_ = ord;
   }
   # calc key[0] with some xor-ing magic
   push( @key , $lock[0] ^ 5 );
   # calc rest of key with some other xor-ing magic
   for( $i = 1 ; $i < @lock ; $i++ ) {
       push( @key , ( $lock[$i] ^ $lock[$i - 1] ) );
   }
   # nibble swapping
   for( $i = 0 ; $i < @key ; $i++ ) {
       $key[$i] = ( (($key[$i] << 4) & 240) | ( ($key[$i] >> 4) & 15 )) & 0xff;
   }
   #temp[0] = (u_int8_t)(temp[0] ^ temp[aLock.length()-1]);
   $key[0] = $key[0] ^ $key[ @key - 1 ];
   # escape some
   foreach( @key ) {
       if ( $_ == 0 || $_ == 5 || $_ == 36 || $_ == 96 || $_ == 124 || $_ == 126 ) {
           $_ = sprintf( '/%%DCN%03i%%/' , $_ );
       } else {
           $_ = chr;
       }
   }
   # done
   return join( "" , @key );
}

[edit] PHP

[edit] Example One (Not work!)

//--------------------------------
// Done by [RO]VeNoM
// razvan_stanga@yahoo.com
//--------------------------------


function lock2key($_LOCK) {

   $lockLength = strlen ($_LOCK);

   $h = ord($_LOCK[0]) ^ ord( $_LOCK[ $lockLength - 1] ) ^ ord( $_LOCK[ $lockLength - 2] ) ^ 5;

   echo $h . "<br>";

   while ($h > 255) {$h = $h - 256;}

   $h = (($h<<4) & 240) || (($h>>4) & 15);

   $a = $h;

   if ($a == '126' or // '~'
       $a == '124' or // '||'
       $a == '96' or  // '`'
       $a == '36' or  // '$'
       $a == '5' or   // '^E'
       $a == '0')     // NUL
   {
     $LockToKey = "/%DCN";

     if ($a < 100)
       $LockToKey .="0";
     if ($a < 10)
       $LockToKey .="0";
       $LockToKey .= $a; // As a string integer
       $LockToKey .= "%/";
   } else {
     $LockToKey = chr ($a);  // No transformation.
   }

   for ($j = 1; $j < strlen($_LOCK); $j++) {
     $h = ord($_LOCK[$j]) ^ ord($_LOCK[$j-1]);

     while ($h > 255) {$h = $h - 256;}

     $h = (($h<<4) & 240) || (($h>>4) & 15);

     echo $j . " - " . ($h*16) . "<br>";

     $a = $h;

     if ($a == '126' or // '~'
         $a == '124' or // '||'
         $a == '96' or  // '`'
         $a == '36' or  // '$'
         $a == '5' or   // '^E'
         $a == '0')     // NUL
     {
       $LockToKey .= "/%DCN";

       if ($a < 100)
         $LockToKey .="0";
       if ($a < 10)
         $LockToKey .="0";
         $LockToKey .= $a; // As a string integer
         $LockToKey .= "%/";
     } else {
       $LockToKey .= chr ($a);  // No transformation.
     }
   }

   return $LockToKey;
}

[edit] Example Two

//----------
// owiec at barbara . eu . org [PL]
//----------

function keygen($lock)
{
     $len = strlen($lock);
     $key = array();
     $key[0] = ord($lock[0]) ^ ord($lock[$len-1]) ^ ord($lock[$len-2]) ^ 5;
     for ($i = 1; $i < $len; $i++)
        $key[$i] = ord($lock[$i]) ^ ord($lock[$i-1]);
     for ($i = 0; $i < $len; $i++)
        $key[$i] = (($key[$i]<<4) & 240) | (($key[$i]>>4) & 15);
     $key = array_map('chr',$key);
     for($i = 0; $i<$len; $i++) 
     {
       if( $key[$i] == chr(0))
         $key[$i] = '/%DCN000%/';
       if( $key[$i] == chr(5))
         $key[$i] = '/%DCN005%/';
       if( $key[$i] == chr(36))
         $key[$i] = '/%DCN036%/';
       if( $key[$i] == chr(96))
         $key[$i] = '/%DCN096%/';
       if( $key[$i] == chr(124))
         $key[$i] = '/%DCN124%/';
       if( $key[$i] == chr(126))
         $key[$i] = '/%DCN126%/';
     }
     $key = implode('',$key);
     return $key;
}

[edit] Example Three

[edit] One

/**********************************
 *            lock2key            *
 *      -------------------       *
 *   Sunday, January 27, 2007     *
 *   (C) 2007 The Freeman Group   *
 *   emdfreeman [at] mail . ru    *
 *                                *
 **********************************/

function lock2key($lock){
    $len = strlen($lock);
    $key[0] = ord($lock[0]) ^ ord($lock[$len-1]) ^ ord($lock[$len-2]) ^ 5;
 
    for ($i = 1; $i < $len; $i++) $key[$i] = ord($lock[$i]) ^ ord($lock[$i-1]);
    for ($i = 0; $i < $len; $i++) $key[$i] = (($key[$i]<<4) & 240) | (($key[$i]>>4) & 15);
    
    $key = array_map('chr',$key);
    for($i = 0; $i<$len; $i++){

            switch($key[$i]){

                    case chr(0):
                         $key[$i] = '/%DCN000%/';
                    break; 

                    case chr(2):
                         $key[$i] = '/%DCN005%/';
                    break;

                    case chr(36):
                         $key[$i] = '/%DCN036%/';
                    break; 

                    case chr(96):
                         $key[$i] = '/%DCN096%/';
                    break;

                    case chr(124):
                         $key[$i] = '/%DCN124%/';
                    break;

                    case chr(126):
                         $key[$i] = '/%DCN126%/';
                    break; 

           }
    }
    $key = implode("",$key);
    return $key;
}

[edit] Two

/**********************************
 *            lock2key            *
 *      -------------------       *
 *   Sunday, January 27, 2007     *
 *   (C) 2007 The Freeman Group   *
 *   emdfreeman [at] mail . ru    *
 *                                *
 **********************************/

function lock2key($lock){
    $len = strlen($lock);
    $key[0] = ord($lock[0]) ^ ord($lock[$len-1]) ^ ord($lock[$len-2]) ^ 5;
 
    for ($i = 1; $i < $len; $i++) $key[$i] = ord($lock[$i]) ^ ord($lock[$i-1]);
    for ($i = 0; $i < $len; $i++) $key[$i] = (($key[$i]<<4) & 240) | (($key[$i]>>4) & 15);
    
    $key = array_map('chr',$key);
    for($i = 0; $i<$len; $i++){

            for($z = 0; $z<240; $z++){
                    if(strlen($z) === 1 ) if( $key[$i] == chr($z)) $key[$i] = '/%DCN00'.$z.'%/';
            }

    }
    $key = implode("",$key);
    return $key;
}

[edit] Visual BASIC

[edit] Example One

'--------------------------------
' Done by Ofca
' ofca@ofca.pl
'--------------------------------
' Wiki edit by bluebear: Old style VB6 LockToKey; would probably not work with the newer versions of hub software

Function Lock2Key(StrLock As String) As String
    Dim TLock2Key As String, TChar As Integer
    If Len(StrLock) < 3 Then
      Lock2Key = Left$("BROKENCLIENT", Len(StrLock))
      Exit Function
    End If
    TLock2Key = Chr$(Asc(Left$(StrLock, 1)) Xor Asc(Right$(StrLock, 1)) Xor Asc(Mid$(StrLock, Len(StrLock) - 1, 1)) Xor 5)
    For i = 2 To Len(StrLock)
        TLock2Key = TLock2Key & Chr$(Asc(Mid$(StrLock, i, 1)) Xor Asc(Mid$(StrLock, i - 1, 1)))
    Next i
    For i = 1 To Len(TLock2Key)
        TChar = Asc(Mid$(TLock2Key, i, 1))
        TChar = TChar * 16 + TChar  16 'Swap bits 11110000 -> 00001111
        TChar = TChar Mod 256
        If TChar = 0 Or TChar = 5 Or TChar = 36 Or TChar = 96 Or TChar = 124 Or TChar = 126 Then
            Lock2Key = Lock2Key & "/%DCN" & Right$("000" & TChar,3) & "%/"
        Else
            Lock2Key = Lock2Key & Chr$(TChar)
        End If

    Next i
End Function

[edit] Example Two

'------------------------------------------------
'Function made by Massimiliano (Neo) from Italy -
'------------------------------------------------

' Wiki edit by bluebear: This LockToKey was acutally posted as a vb.net converter wich it's not.
' I've havent tryed this one, but guess it will work for vb6.
Public Function LockToKey(Lck As String) As String
 ' Edited by •--Gàu®áv™--•, the old LockToKey wasn't working properly as it was made for vb.net  converter
 Dim h As Integer, j As Integer
 n = 5
 
 h = InStr(1, Lck, " ")
 If h Then Lck = Left$(Lck, h - 1)
 
 h = Asc(Lck) Xor Asc(Right$(Lck, 1)) Xor Asc(Right$(Lck, 2)) Xor n
 h = (h \ 16) Xor (h * 16)
 
 Do While h > 255
   h = h - 256
 Loop
 
 Select Case h
   Case 0, 5, 36, 96, 124, 126
     LockToKey = "/%DCN" & Right$("00" & CStr(h), 3) & "%/"
   Case Else
     LockToKey = Chr$(h)
 End Select
 
 For j = 2 To Len(Lck)
   h = Asc(Mid$(Lck, j, 1)) Xor Asc(Mid$(Lck, j - 1, 1))
   h = (h \ 16) Xor (h * 16)
   Do While h > 255
     h = h - 256
   Loop
   
   Select Case h
     Case 0, 5, 36, 96, 124, 126
       LockToKey = LockToKey & "/%DCN" & Right$("00" & CStr(h), 3) & "%/"
     Case Else
       LockToKey = LockToKey & Chr$(h)
   End Select
 Next

End Function

[edit] Visual BASIC .NET

   Public Function LocktoKey(ByVal Lck As String) As String
       '// This LockToKey is for VB.NET
       '// Written by bluebear - http://www.thewildplace.dk/
       '// It uses the .net framework string class instead of the old vb6 style
       '// If this don't work for you it's because you use the wrong encoding on inbound/outbound data
       '// Expects lock with "$Lock" and PK in string.
       '// 

       Lck = Lck.Replace("$Lock ", "")
       Dim sKey As String
       Dim sTmp As String
       Dim iLen As Byte
       Dim iChar As Integer
       Dim iPos As Integer = Lck.IndexOf(" ", 1)
       If CBool(iPos) Then Lck = Lck.Substring(0, iPos)
       iChar = Asc(Lck) Xor Asc(Lck.Substring(Lck.Length - 1)) Xor Asc(Lck.Substring(Lck.Length - 2, 1)) Xor 5
       iChar = (iChar \ 16) Xor (iChar * 16)
       Do While iChar > 255
           iChar = iChar - 256
       Loop
       Select Case iChar
           Case 0, 5, 36, 96, 124, 126
               sTmp = "00" & CStr(iChar)
               iLen = CByte(sTmp.Length)
               If iLen > 3 Then iLen -= CByte(3) Else iLen = 0
               sKey = "/%DCN" & sTmp.Substring(iLen) & "%/"
           Case Else
               sKey = Chr(iChar)
       End Select
       For iPos = 1 To Lck.Length - 1
           iChar = Asc(Lck.Substring(iPos, 1)) Xor Asc(Lck.Substring(iPos - 1, 1))
           iChar = (iChar \ 16) Xor (iChar * 16)
           Do While iChar > 255
               iChar = iChar - 256
           Loop
           Select Case iChar
               Case 0, 5, 36, 96, 124, 126
                   sTmp = "00" & CStr(iChar)
                   iLen = CByte(sTmp.Length)
                   If iLen > 3 Then iLen -= CByte(3) Else iLen = 0
                   sKey += "/%DCN" & sTmp.Substring(iLen) & "%/"
               Case Else
                   sKey += Chr(iChar)
           End Select
       Next
       Return sKey
   End Function

[edit] REALbasic 5.5

  // Add a function:  Protected Function Lock2Key(sLock As String) As String
  // REALBasic Lock To Key converter
  // Written in REALBasic 5.5 Pro by Bluebear
  // http://www.thewildplace.dk/
  // Rember to use the correct encoding for incoming/outgoing data
  Dim Lck As String, key As String
  Dim iChar As Integer, iPos As Integer

  Lck = Replace(sLock, "$Lock ", "")
  iPos = InStr(1, Lck, " ")
  If iPos > 0 Then Lck = Left(Lck, iPos - 1)

  // 2005  //iChar = Bitwise.BitXor(Asc(Lck), Asc(Right(Lck, 1)), Asc(Right(Lck, 2)), 5)
  iChar = Bitwise.BitXor(Bitwise.BitXor(Asc(Lck), Asc(Right(Lck, 1))),Bitwise.BitXor(Asc(Right(Lck, 2)), 5))


  iChar = Bitwise.BitXor((iChar / 16), (iChar * 16))
  While iChar > 255
    iChar = iChar - 256
  Wend
  Select Case iChar
  Case 0, 5, 36, 96, 124, 126
    key = "/%DCN" + Right("00" + CStr(iChar), 3) + "%/"
  Case Else
    key = Chr(iChar)
  End Select

  For iPos=2 To Len(Lck)

    iChar = Bitwise.BitXor(Asc(Mid(Lck, iPos, 1)), Asc(Mid(Lck, iPos - 1, 1)))
    iChar = Bitwise.BitXor((iChar / 16), (iChar * 16))
    While iChar > 255
      iChar = iChar - 256
    Wend
    Select Case iChar
    Case 0, 5, 36, 96, 124, 126
      key = key + "/%DCN" + Right("00" + CStr(iChar), 3) + "%/"
    Case Else
      key = key + Chr(iChar)
    End Select

  Next
  Return key

[edit] REALbasic 2005

  // Add as function:   Function Lock2Key(sLock As String) As String
  // REALBasic Lock To Key converter
  // Written in REALBasic 2005 Pro by Bluebear
  // http://www.thewildplace.dk/
  // Rember to use the correct encoding for incoming/outgoing data

  Dim Lck As String, key As String
  Dim iChar As Integer, iPos As Integer

  Lck = Replace(sLock, "$Lock ", "")
  iPos = InStr(1, Lck, " ")
  If iPos > 0 Then Lck = Left(Lck, iPos - 1)

  iChar = Bitwise.BitXor(Asc(Lck), Asc(Right(Lck, 1)), Asc(Right(Lck, 2)), 5)
  iChar = Bitwise.BitXor((iChar / 16), (iChar * 16))
  While iChar > 255
    iChar = iChar - 256
  Wend
  Select Case iChar
  Case 0, 5, 36, 96, 124, 126
    key = "/%DCN" + Right("00" + CStr(iChar), 3) + "%/"
  Case Else
    key = Chr(iChar)
  End Select

  For iPos=2 To Len(Lck)

    iChar = Bitwise.BitXor(Asc(Mid(Lck, iPos, 1)), Asc(Mid(Lck, iPos - 1, 1)))
    iChar = Bitwise.BitXor((iChar / 16), (iChar * 16))
    While iChar > 255
      iChar = iChar - 256
    Wend
    Select Case iChar
    Case 0, 5, 36, 96, 124, 126
      key = key + "/%DCN" + Right("00" + CStr(iChar), 3) + "%/"
    Case Else
      key = key + Chr(iChar)
    End Select

  Next
  Return key

[edit] Python

[edit] Example One

# by Benjamin Bruheim

def lock2key(lock):
    "Generates response to $Lock challenge from Direct Connect Servers"
    lock = [ord(c) for c in lock]
    key = [0]
    for n in range(1,len(lock)):
        key.append(lock[n]^lock[n-1])
    key[0] = lock[0] ^ lock[-1] ^ lock[-2] ^ 5
    for n in range(len(lock)):
        key[n] = ((key[n] << 4) | (key[n] >> 4)) & 255
    result = ""
    for c in key:
        if c in [0, 5, 36, 96, 124, 126]:
            result += "/%%DCN%.3i%%/" % c
        else:
            result += chr(c)
    return result

if __name__=="__main__":
    key = lock2key("T&AUreb/M_2Wtp_lZU)EA_yU_)2[2/_4u:,`L`3\\m:+ctsnyw9@")
    assert key=="\x82'vArqp\xd4&!\xd6V2@\xf23c\xf0\xc7\xc6@\xe1b\xc2\xa0g" \
           + "\xb1\x96\x96\xd1\x07\xb6\x14\xf4a\xc4\xc2\xc25\xf6\x13u\x11" \
           + "\x84qp\xd1q\xe0\xe4\x97"

[edit] Example Two

# by Benjamin Bruheim, optimized by Dody Suria Wijaya (25% faster)
import array
def lock2key2(lock):
    "Generates response to $Lock challenge from Direct Connect Servers"
    lock = array.array('B', lock)
    ll = len(lock)
    key = list('0'*ll)
    for n in xrange(1,ll):
        key[n] = lock[n]^lock[n-1]
    key[0] = lock[0] ^ lock[-1] ^ lock[-2] ^ 5
    for n in xrange(ll):
        key[n] = ((key[n] << 4) | (key[n] >> 4)) & 255
    result = ""
    for c in key:
        if c in (0, 5, 36, 96, 124, 126):
            result += "/%%DCN%.3i%%/" % c
        else:
            result += chr(c)
    return result

[edit] TCL

# by Cez

proc lock2key { lock } {
   set key ""
   set temp ""
   set length [string length $lock]

   append temp [asc [expr [chr [string index $lock 0]] ^ [chr [string index $lock [expr $length-1]]] ^ [chr [string index $lock [expr $length-2]]] ^ 5]]

   for { set x 1 } { $x<$length } { incr x } {
           append temp [asc [expr [chr [string index $lock $x]] ^ [chr [string index $lock [expr $x-1]]]]]
   }

   for { set x 0 } { $x<$length } { incr x } {
           append key [asc [expr ([chr [string index $temp $x]] << 4) & 240 | ([chr [string index $temp $x]] >> 4) & 15]]
        }

   regsub -all -- {\0} $key "/%DCN000%/" key
   regsub -all -- {\^E} $key "/%DCN005%/" key
   regsub -all -- {\$} $key "/%DCN036%/" key
   regsub -all -- {\`} $key "/%DCN096%/" key
   regsub -all -- {\|} $key "/%DCN124%/" key
   regsub -all -- {\~} $key "/%DCN126%/" key
   return $key
}

proc asc i {
    if { $i<0 || $i>255 } { error "asc:Integer out of range 0-255" }
    return [format %c $i ]
}

proc chr { c } {
    set v 0
    scan $c %c v
    return $v
}

[edit] Delphi

[edit] Example One

// By ParadoxHeart
// Ported from JavaDC's key generator

// This version requires you to cut off the $Lock and pk parts *before* passing the lock code to LockToKey
// I'll post a version that deals with those itself when I get around to it. :p

uses
  SysUtils, StrUtils;

function LockToKey(const LockCode: String): String;
var
  i, j: Integer;
begin
  if Length(LockCode) < 3 then
  begin
    Result:= 'ERR_INVALIDLOCK';
    Exit;
  end;
  for i:= 1 to Length(LockCode) do
  begin
    j:= Ord(LockCode[i]);
    if i = 1 then
      j:= j xor 5
    else
      j:= j xor Ord(LockCode[i - 1]);
    j:= j + ((j mod 17) * 15);
    while j > 255 do
      Dec(j, 255);
    Result:= Result + Chr(j);
  end;
  Result[1]:= Chr(Ord(Result[1]) xor Ord(Result[Length(Result)]));
  AnsiReplaceStr(Result, #0, '/%DCN000%/');
  AnsiReplaceStr(Result, #5, '/%DCN005%/');
  AnsiReplaceStr(Result, #36, '/%DCN036%/');
  AnsiReplaceStr(Result, #96, '/%DCN096%/');
  AnsiReplaceStr(Result, #124, '/%DCN124%/');
  AnsiReplaceStr(Result, #126, '/%DCN126%/');
end;

[edit] Example Two

//New Delphi Lock2Key ( working perfectly )
 public
   { Public declarations }
   function Key(iLock: String; magic: byte): String;
   function ns(eightbit: byte): byte;
//The above declarations must be placed under the public diclarations
function TF_Dark_Deeds.Key(iLock: String; magic: byte): String;
var tmp,Lock : string;
    i : integer;
    a,b : byte;
begin
 if not (pos(' Pk',ilock) = 0) then
  lock := copy(ilock,1,pos(' Pk',ilock)-1)
 else lock := ilock;
 
 a := ord(lock[length(lock)-1]);
 b := ord(lock[length(lock)]);
 tmp := chr(ns(ord(lock[1]) xor a xor b xor magic));
 
 for i := 2 to length(lock) do
 begin
  tmp := tmp + chr(ns(ord(lock[i]) xor ord(lock[i-1])));
 end;
 
 for i := 1 to length(tmp) do
 begin
  case ord(tmp[i]) of
   0    : result := concat(result,'/%DCN000%/');
   5    : result := concat(result,'/%DCN005%/');
   36   : result := concat(result,'/%DCN036%/');
   96   : result := concat(result,'/%DCN096%/');
   124  : result := concat(result,'/%DCN124%/');
   126  : result := concat(result,'/%DCN126%/');
   else   result := concat(result,tmp[i]);
  end;
 end;
end;
 
function TF_Dark_Deeds.ns(eightbit: byte): byte;
begin
 result := (eightbit shl 4) or (eightbit shr 4);
end;
//Use the function like this -> key:=LockToKey(Lock,5); // just put a 5 i don't know why
//by the way tnx to a friend of mine i posted this function

[edit] Delphi.NET

// By jazper jazper@zaboo.net

// Ported ParadoxHearts code(above)
// This function also assumes you have stripped $Lock and remove everything from Pk= onwards
uses
  System.Text;

type TBuffArray: Array of byte;

function TDCHub.LockToKey(Data: TBuffArray): &string;
var
  i,j,Len: Int32;
  Key: TBuffArray;
begin
  Len := System.Array(Data).Length;
  SetLength(Key,Len);
  if Len < 3 then
  begin
    Result:= 'ERR_INVALIDLOCK';
    Exit;
  end;
  for i:= 0 to Len - 1 do
  begin
    j:= data[i];
    if i = 0 then
      j:= j xor 5
    else
      j:= j xor data[i - 1];
    j:= j + ((j mod 17) * 15);
    while j > 255 do
      Dec(j, 255);
    Key[i] := j;
  end;
  Key[0]:= Key[0] xor Key[Len-1];
  //
  // Convert to string so we can replace some char's
  Result := Encoding.Default.GetString(Key);
  // protocol has unaccepted char's, we must replace them
  Result := Result.Replace(#0,'/%DCN000%/');
  Result := Result.Replace(#5,'/%DCN005%/');
  Result := Result.Replace(#36,'/%DCN036%/');
  Result := Result.Replace(#96,'/%DCN096%/');
  Result := Result.Replace(#124,'/%DCN124%/');
  Result := Result.Replace(#126,'/%DCN126%/');

end;

[edit] BcB

//* Powered By m4st3rB [Poland] m4st3b@o2.pl
// This version requires you to cut off the "$Lock" and "Pk=..." parts before decoding the lock code to Key


//key1
LockLen=Lock.Length();
char L1,Ln1,Ln2=Lock[LockLen-2];
L1=Lock[1];
Ln1=Lock[LockLen-1];
Key1=Char(L1)^Char(Ln1)^Char(Ln2)^5;
Key1=((Char(Key1)<<4) & 240 ) | ((Char(Key1)>>4) & 15 );   // nibble swap
switch (Key1){
case 0: Key="/%DCN000%/";
break;
case 5: Key="/%DCN005%/";
break;
case 36: Key="/%DCN036%/";
break;
case 96: Key="/%DCN096%/";
break;
case 124: Key="/%DCN124%/";
break;
case 126: Key="/%DCN126%/";
break;
default:  Key=Char(Key1);
}
// keyi
for (int i=2;i<LockLen;i++){
L1=Lock[i],Ln1=Lock[i-1];
Keyi=Char(L1)^Char(Ln1);
Keyi=((Char(Keyi)<<4) & 240 ) | ((Char(Keyi)>>4) & 15 ); // nibble swap
switch (Keyi){
case 0: Key=Key+"/%DCN000%/";
break;
case 5: Key=Key+"/%DCN005%/";
break;
case 36: Key=Key+"/%DCN036%/";
break;
case 96: Key=Key+"/%DCN096%/";
break;
case 124: Key=Key+"/%DCN124%/";
break;
case 126: Key=Key+"/%DCN126%/";
break;
default:  Key=Key+Char(Keyi);
}
}

[edit] Java

/**
 * Contributed by dCoy
 */
public static String generateKey(String lockString){
    int i = 0;
    byte[] lock = null;
    byte[] key = null;

    lockString = lockString.substring(0,lockString.indexOf(' '));
    lockString.trim();
    lock = lockString.getBytes();
    key = new byte[lock.length];

    for(i=1;i<lock.length;i++){
        key[i] = (byte)((lock[i] ^ lock[i-1]) & 0xFF);
    }

    key[0] = (byte)((((lock[0] ^ lock[lock.length-1]) ^ lock[lock.length-2]) ^ 5) & 0xFF);

    for(i=0;i<key.length;i++){
        key[i] = (byte)((((key[i]<<4) & 0xF0) | ((key[i]>>4) & 0x0F)) & 0xFF);
    }

    return(dcnEncode(new String(key)));
}

public static String dcnEncode(String string){
    char[] replacements = null;
    int i = 0;
    int index = 0;

    replacements = new char[]{0,5,36,96,124,126};

    for(i=0;i<replacements.length;i++){
        while((index = string.indexOf(replacements[i])) >=0 ){
            string = string.substring(0,index)
                + "/%DCN"+leadz(replacements[i])+"%/"
                + string.substring(index+1,string.length());
        }
    }

    return(string);
}

private static String leadz(int nr){
    if(nr < 100 && nr > 10){
        return("0"+nr);
    } else if(nr < 10){
        return("00"+nr);
    } else{
        return(""+nr);
    }
}

[edit] C#

Please note that the Strings class used in these two examples are contained inside Microsoft.VisualBasic and does therefore require that you add Microsoft.VisualBasic as a reference and that you add using Microsoft.VisualBasic; to the top of the sourcefile. (This is atleast needed for vs 2005 I can't confirm if it is needed for older version of the compiler aswell)

[edit] Example One

// By RaptoR franz@digital-wave.de
// This function assumes you have stripped $Lock and remove everything from
//   Pk= onwards
// This code also works with yhub's extended char locks!
// Modified by Andreas Brekken <andreas@abrekken.com> for performance,
//   readability, less code

public static string Decode( string aLock )
{
    char[] key = new char[aLock.Length];

    for (int i = 1; i < aLock.Length; i++)
    {
        key[i] = Strings.Chr(Strings.Asc(aLock[i])
            ^ Strings.Asc(aLock[i - 1]));
    }

    key[0] = Strings.Chr(Strings.Asc(aLock[0])
        ^ Strings.Asc(aLock[aLock.Length - 1])
        ^ Strings.Asc(aLock[aLock.Length - 2])
        ^ 5);

    for (int i = 0; i < aLock.Length; i++)
    {
        key[i] = Strings.Chr(
            ((Strings.Asc(key[i]) << 4) & 240)
            | ((Strings.Asc(key[i]) >> 4) & 15));
    }

    string keyString = "";
    for (int i = 0; i < key.Length; i++)
    {
        int j = Strings.Asc(key[i]);
        if (j != (int)key[i])
        {
            key[i] = (char)j;
        }
        keyString += key[i];
    }
    return escapeChars(keyString);
}

private static string escapeChars( string key )
{
    System.Text.StringBuilder builder =
        new System.Text.StringBuilder(key.Length);

    for (int index=0; index<key.Length; index++)
    {
        int code = (int)key[index];
        if (code == 0 || code == 5 || code == 36 || code == 96
                || code == 124 || code == 126)
            builder.AppendFormat("/%DCN{0:000}%/", code);
        else
            builder.Append(key[index]);
    }

    return builder.ToString();
}

[edit] Example Two

public string LockToKey(string Lck)
{
  /*
   * This LockToKey is a direct translation of bluebear's VB.NET LockToKey
   *   into CSharp by kepp
   * Originally written by bluebear - http://www.thewildplace.dk/
   * Csharp translation was broken and fixed by bluebear
   * If this don't work for you it's because you use the wrong encoding on
   *   inbound/outbound data (System.Text.Encoding.Default)
   */

  /*
   * Edited by Carraya (I hate bugs)
   */

  Lck = Lck.Replace("$Lock ","");
  string sKey; 
  string sTmp; 
  byte iLen; 
  int iChar; 
  int iPos = Lck.IndexOf(" Pk=",1);

  if (Convert.ToBoolean(iPos))
    Lck = Lck.Substring(0,iPos);
  iChar = (Strings.Asc(Lck) ^ Strings.Asc(Lck.Substring(Lck.Length - 1)) ^ Strings.Asc(Lck.Substring(Lck.Length - 2, 1)) ^ 5);
  iChar = (iChar / 16) ^ (iChar * 16);

  while(iChar > 255)
  {
    iChar = iChar - 256;
  } 

  if ((iChar == 0) || (iChar == 5) || (iChar == 36) || (iChar == 96) || (iChar == 124) || (iChar == 126))
  {
    sTmp = "00" + iChar.ToString(); 
    iLen = Convert.ToByte(sTmp.Length);
    if (iLen > 3)
      iLen -= 3;
    else
      iLen = 0;
    sKey = "/%DCN" + sTmp.Substring(iLen) + "%/";
  }
  else
  {
    sKey = Strings.Chr(iChar).ToString();
  }

  for(iPos = 1; iPos < Lck.Length; iPos++)
  {
    iChar = Strings.Asc(Lck.Substring(iPos, 1)) ^ Strings.Asc(Lck.Substring(iPos - 1, 1));
    iChar = (iChar / 16) ^ (iChar * 16); 

    while(iChar > 255)
    {
      iChar = iChar - 256;
    }  

    if ((iChar == 0) || (iChar == 5) || (iChar == 36) || (iChar == 96) || (iChar == 124) || (iChar == 126))
    {
      sTmp = "00" + iChar.ToString(); 
      iLen = Convert.ToByte(sTmp.Length);
      if (iLen > 3) 
        iLen -= 3;
      else
        iLen = 0;
      sKey += "/%DCN" + sTmp.Substring(iLen) + "%/";
    }
    else
    {
      sKey += Strings.Chr(iChar).ToString();
    }
  }

  return sKey;
}

[edit] Example Three

// In order to work properly, your client should be properly configured for the
// 1252-Windows encoding. Alternatively, you may have to change to byte[] for it
// to pass properly over a socket. Written by Emil Müller.

public string LockToKey(string lck)
{
    string Key = "";
    for (int i = 0, j; lck.Length > i; i++)
    {
        if (i == 0) j = lck[0] ^ 5;
        else j = lck[i] ^ lck[i - 1];
        for (j += ((j % 17) * 15); j > 255; j -= 255) ;
        switch (j)
        {
            case 0:
            case 5:
            case 36:
            case 96:
            case 124:
            case 126:
                Key += "/%DCN" + ((string)("00" + j.ToString())).Substring(j.ToString().Length - 1) + "%/";
                break;
            default:
                Key += (char)j;
                break;
        }
    }
    return (char)(Key[0] ^ Key[Key.Length - 1]) + Key.Substring(1);
}

[edit] Example Four

/*
 * This LockToKey does NOT use Microsoft.VisualBasic as a reference 
 * also strips $Lock and Pk=
 * Written by Gargol (gargol@gbot.nu)
 */
public string L2K(string lck)
{
  lck = lck.Replace("$Lock ", "");
  int iPos = lck.IndexOf(" Pk=", 1);
  if (iPos > 0) lck = lck.Substring(0, iPos);
  int[] arrChar = new int[lck.Length + 1];
  int[] arrRet = new int[lck.Length + 1];
  arrChar[1] = lck[0];
  for (int i = 2; i < lck.Length + 1; i++)
  {
    arrChar[i] = lck[i - 1];
    arrRet[i] = arrChar[i] ^ arrChar[i - 1];
  }
  arrRet[1] = arrChar[1] ^ arrChar[lck.Length] ^ arrChar[lck.Length - 1] ^ 5;
  string sKey = "";
  for (int n = 1; n < lck.Length + 1; n++)
  {
    arrRet[n] = ((arrRet[n] * 16) & 240) | ((arrRet[n] / 16) & 15);
    int j = arrRet[n];
    switch (j)
    {
      case 0:
      case 5:
      case 36:
      case 96:
      case 124:
      case 126:
        sKey += "/%DCN"
             + ((string)("00" + j.ToString())).Substring(j.ToString().Length - 1)
             + "%/";
        break;
      default:
        sKey += Chr(Convert.ToByte((char)j));
        break;
    }
  }
  return sKey;
}

public static char Chr(byte src)
{
  return (Encoding.Default.GetChars(new byte[] { src })[0]);
}

[edit] Visual FoxPro 6

* Coded By ZOseZEro @ Latvia

func LockToKey
        para pcLock
        priv Lock2Key, TLock2Key, TChar, i
        Lock2Key=""
        If Len(pcLock)<3
      retu(Left("BROKENCLIENT", Len(pcLock)))
    EndIf

    TLock2Key = Chr(lXor(lXor(lXor(Asc(Left(pcLock, 1)), ;
                                   Asc(Right(pcLock, 1))), ;
                              Asc(substr(pcLock, Len(pcLock) - 1, 1))), ;
                         5))
    For i = 2 To Len(pcLock)
        TLock2Key = TLock2Key + Chr(lXor(Asc(substr(pcLock, i, 1)), ;
                                                                         Asc(substr(pcLock, i - 1, 1))))
    Next
    For i = 1 To Len(TLock2Key)
        TChar = Asc(substr(TLock2Key, i, 1))
        TChar = TChar * 16 + int(TChar / 16) &&Swap bits 11110000 -> 00001111
        TChar = mod(TChar, 256)
        If TChar = 0 Or TChar = 5 Or TChar = 36 Or TChar = 96 Or TChar = 124 Or TChar = 126
            Lock2Key = Lock2Key + "/%DCN" + Right("000" + alltrim(str(TChar)),3) + "%/"
        Else
            Lock2Key = Lock2Key + Chr(TChar)
        EndIf
    Next
    retu(Lock2Key)
End func

func lXor
        para n1,n2
        priv x,ret
        ret=0
        for x=1 to 8
                if mod(n1,2) + mod(n2,2)=1
                        ret=ret+2^(x-1)
                endif
            n1=int(n1/2)
            n2=int(n2/2)
        next
        retu (ret)
end func

[edit] mIRC 5.91+

; Coded by Mardeg
alias lock2key {
 var %gl = $1, %gf = $base($xor($xor($xor($asc($left(%gl,1)),$asc($right(%gl,1))),$asc($left($right(%gl,2),1))),5),10,2,8)
 var %gt = $+(/,%,DCN005,%,/,./,%,DCN000,%,/,./,%,DCN036,%,/,./,%,DCN096,%,/,./,%,DCN124,%,/,./,%,DCN126,%,/)
 var %gb = 2, %ge = $len(%gl), %gn = 5.0.36.96.124.126, %gk
 %gf = $base($+($right(%gf,4),$left(%gf,4)),2,10)
 %gf = $iif($findtok(%gn,%gf,1,46),$gettok(%gt,$ifmatch,46),$chr(%gf))
 while (%gb <= %ge) {
   %gk = $base($xor($asc($mid(%gl,%gb,1)),$asc($mid(%gl,$calc(%gb - 1),1))),10,2,8)
   %gk = $base($+($right(%gk,4),$left(%gk,4)),2,10)
   %gf = $+(%gf,$iif($findtok(%gn,%gk,1,46),$gettok(%gt,$ifmatch,46),$chr(%gk)))
   inc %gb
 }
 return %gf
}

[edit] Lua 5.0+

-- Coded by Mardeg
function bODD(x)
       return x ~= math.floor(x / 2) * 2
end

function bitwise(x, y, bw)
       local c, p = 0, 1
       while x > 0 or y > 0 do
           if bw == "xor" then
               if (bODD(x) and not bODD(y)) or (bODD(y) and not bODD(x)) then
                       c = c + p
               end
           elseif bw == "and" then
               if bODD(x) and bODD(y) then
                       c = c + p
               end
           elseif bw == "or" then
               if bODD(x) or bODD(y) then
                       c = c + p
               end
           end
           x = math.floor(x / 2)
           y = math.floor(y / 2)
           p = p * 2
       end
       return c
end

function nibbleswap(bits)
 	return bitwise(bitwise(bits*(2^4),240,"and"),bitwise(math.floor(bits/(2^4)),15,"and"),"or")
end

function lock2key(lock)
	local key = {}
	table.insert(key,bitwise(bitwise(bitwise(string.byte(lock,1),string.byte(lock,-1),"xor"),string.byte(lock,-2),"xor"),5,"xor"))
	for i=2,string.len(lock),1 do
		table.insert(key,bitwise(string.byte(lock,i),string.byte(lock,i - 1),"xor"))
	end
	local g = {["5"]=1,["0"]=1,["36"]=1,["96"]=1,["124"]=1,["126"]=1}
	for i=1,table.getn(key),1 do
		local b = nibbleswap(rawget(key,i)) 
		rawset(key,i,(g[tostring(b)] and string.format("/%%DCN%03d%%/",b) or string.char(b)))
	end
	return table.concat(key)
end

[edit] JavaScript 1.5

// Coded by Mardeg
function nibbleswap(bits) {
	return ((bits << 4) & 240) | ((bits >>> 4) & 15);
}
function chr(b) {
	return (("..0.5.36.96.124.126.").indexOf("."+b+".")>0)?"/%DCN"+(0).toPrecision(4-b.toString().length).substr(2)+b+"%/":String.fromCharCode(b);
}
function lock2key(lock) {
	var key = chr(nibbleswap(lock.charCodeAt(0) ^ lock.charCodeAt(-1) ^ lock.charCodeAt(-2) ^ 5));
	for (var i=1; i<lock.length; i++) {
		key += chr(nibbleswap(lock.charCodeAt(i) ^ lock.charCodeAt(i - 1)));
	}
	return key;
}

[edit] ICI

// Coded by Mardeg, tidied up by Andy Newman
static lock2key(lock)
{
	f := [func (c)
	{
		g := [struct
			(0) = "/%DCN000%/",
			(5) = "/%DCN005%/",
			(36) = "/%DCN036%/",
			(96) = "/%DCN096%/",
			(124) = "/%DCN124%/",
			(126) = "/%DCN126%/"
		];
		c = ((c << 4) & 0xf0) | ((c >> 4) & 15);
		b := g[c];
		return b == NULL ? tochar(c) : b;
	}];

	len := nels(lock);
	if (len < 3) return "BROKENCLIENT";
	key := array(f(toint(lock[0]) ^ toint(lock[len-1]) ^ toint(lock[len-2]) ^ 5));
	for (i := 1; i < len; ++i)
	{
		push(key, f(toint(lock[i]) ^ toint(lock[i - 1])));
	}
	return implode(key);
}

[edit] Erlang

%% Translated from Python code by Ivan Dubrov
lock_to_key(Lock) ->
    [First | Tail] = Lock,
    {Key, _} = lists:mapfoldl(fun(Cur, Prev) -> {Cur bxor Prev, Cur} end, First, Tail),
    [Lock1, Lock2] = lists:nthtail(length(Lock) - 2, Lock), [Lock0 | _] = Lock,
    Key2 = [Lock0 bxor Lock1 bxor Lock2 bxor 5 | Key],
    Key3 = lists:map(fun(Val) -> <<Hi:4, Low:4>> = <<Val>>, <<Res>> = <<Low:4, Hi:4>>, Res end, Key2),
    Key4 = lists:foldl(fun(Val, AccIn) ->
        AccIn ++
        if
            Val ==   0 -> "/%DCN000%/";
            Val ==   5 -> "/%DCN005%/";
            Val ==  36 -> "/%DCN036%/";
            Val ==  96 -> "/%DCN096%/";
            Val == 124 -> "/%DCN124%/";
            Val == 126 -> "/%DCN126%/";
            true -> [Val]
       end
    end, [], Key3),
    Key4.

[edit] Ruby

 # By C Erler with suggestions by Robert Klemme.
 # Took the way of combining neighboring characters from the Python code.
 
 def key(lock)
   # We need a String of at least two bytes.
   raise TypeError, "cannot convert #{lock.class} into String", caller unless lock.respond_to? :to_str
   lock = lock.to_str
   return '' unless lock.length >= 2
   
   # Transform the input bytes.
   result = Array.new(lock.length) { |i| lock[i - 1] ^ lock[i] }
   result[0] ^= lock[-2] ^ 5
   
   result.map! do |value|
     # Rotate each byte by four bits.
     value = ((value << 4) | (value >> 4)) & 0b11111111
     
     # Put the output in the correct format.
     case value
       when 0, 5, 36, 96, 124, 126
         '/%%DCN%03d%%/' % value
       else
         value.chr
     end
   end
   
   # Combine the parts into the resultant string.
   result.join
 end