/* Metroid password generator / decoder
 * Copyright (C) 1992,2004 Joel Yliluoma (http://iki.fi/bisqwit/)
 * Written for http://tasvideos.org/PasswordGenerators.html
 * Permission to copy and modify is granted under the following terms:
 *   The copyright notice is kept unmodified
 *   No attempts are made to prevent anyone downloading the source code
 *
 * Reverse engineering by unknown - algorithm copied from SnowBro's doc
 */
function metroinput(name)
{
  this.gel = function(id)   { return document.getElementById(name+id) }
  this.id  = function(id)   { return this.gel(id).selectedIndex }
  this.ids = function(id,v) { this.gel(id).selectedIndex=v }
  this.ch  = function(id)   { return this.gel(id).checked?1:0 }
  this.chs = function(id,v) { this.gel(id).checked = v>0 }
  this.tx  = function(id)   { return this.gel(id).value }
  this.txs = function(id,v) { this.gel(id).value = v }
  
  /* 0..255 values */
  this.bytes = [0,0,0,0,0,0, 0,0,0,0,0,0, 0,0,0,0,0,0]

  /* 0..63 encoded values */
  this.chars = [0,0,0,0,0,0, 0,0,0,0,0,0, 0,0,0,0,0,0, 0,0,0,0,0,0]
  
  /* 0..255 */
  this.roll = 0
  
  this.rotate = function(roll)
  {
    result = [0,0,0,0,0,0, 0,0,0,0,0,0, 0,0,0,0,0,0]
    result[16] = this.bytes[16]
    result[17] = this.bytes[17]
    
    /* handle bit by bit */
    for(n=0; n<128; ++n)
    {
      m = (n + roll) & 127
      bit = 1 & (this.bytes[m >> 3] >> (7-(m&7)))
      result[n >> 3] |= bit << (7-(n&7))
    }
    this.bytes = result
  }
  this.convert_bits = function(input, ib, ob)
  {
    cache=0; cachelen=0
    result = []
    nm = input.length
    obm = (1 << ob) - 1
    for(n=0; n<nm; ++n)
    {
      /* populate low bits */
      cache = (cache << ib) + input[n]
      for(cachelen += ib; cachelen >= ob; )
      {
        cachelen -= ob
        /* eat high bits */ result.push((cache >> cachelen) & obm)
        /* keep low bits */ cache &= (1 << cachelen)-1;
      }
    }
    return result
  }
  this.updatedots = function()
  {
    t = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz?- '
    s = ''
    for(n=0; n<24; ++n)
      s += t.charAt(this.chars[n])
    
    this.txs('l1', s.substr(0, 6)+ ' ' + s.substr( 6,6))
    this.txs('l2', s.substr(12,6)+ ' ' + s.substr(18,6))
  }
  this.decode = function()
  {
    error   = 0
    message = ''
    
    t = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz?- '
    
    l1 = this.tx('l1') + '0000000000000'
    l2 = this.tx('l2') + '0000000000000'

    for(n=0; n<6; ++n)
    {
      c=t.indexOf(l1.charAt(n  ));c=(c==64)?255:(c&63);this.chars[n+0 ] = c
      c=t.indexOf(l1.charAt(n+7));c=(c==64)?255:(c&63);this.chars[n+6 ] = c
      c=t.indexOf(l2.charAt(n  ));c=(c==64)?255:(c&63);this.chars[n+12] = c
      c=t.indexOf(l2.charAt(n+7));c=(c==64)?255:(c&63);this.chars[n+18] = c
    }
    
    /* convert 6-bit to 8-bit */
    this.bytes = this.convert_bits(this.chars, 6, 8)
    
    this.roll = this.bytes[16]
    sum       = this.bytes[17]
    
    this.rotate(this.roll) /* this is correct */

    sum2=0
    for(n=0; n<17; ++n)sum2 += this.bytes[n]
    sum2 &= 255
    
    for(n=0; n<128; ++n)
    {
      b = n >> 3;
      if(!(b == 10
       ||  (n >= 64 && n <= 67)
        ))
        this.chs('b' + n, this.bytes[b] & (1 << (n&7)))
    }
    this.ids('misl',  this.bytes[10])
    this.ids('begin', this.bytes[8] & 15)
    
    if(sum != sum2)
    {
      error=1
      message += '(chsum '+sum+' should be '+sum2+')'
    }
    
    if(error) message = '(error)' + message
    this.txs('msg', message)
  }
  this.modified = function()
  {
    this.decode()
    /* Re-encode the password. This fixes the checksum.
     * It does not hurt. */
    this.setup()
  }
  this.setup = function()
  {
    /* Clear the password */
    for(n=0; n<16; ++n) this.bytes[n] = 0
    /* Add each input bit to it */
    for(n=0; n<128; ++n)
    {
      b = n >> 3;
      /* Ignore bits which are given as numbers (such as missile counts) */
      if(!(b == 10
       ||  (n >= 64 && n <= 67)
        ))
        this.bytes[b] |= this.ch('b' + n) << (n&7)
    }
    /* Store the input numbers (this.id() fetches the selected index of a selectbox) */
    this.bytes[8] |= this.id('begin')
    this.bytes[10] = this.id('misl')
    this.bytes[16] = this.roll & 255
    
    /* Calculate, store checksum */
    sum=0
    for(n=0; n<17; ++n)sum += this.bytes[n]
    this.bytes[17] = sum       & 255
    
    this.rotate(-this.roll) /* this is correct */
    
    /* convert 8-bit to 6-bit */
    this.chars = this.convert_bits(this.bytes, 8, 6)

    this.updatedots()
    this.decode()
  }
}
