#include <vstypes.h>
#ifdef __VSDSP__
#else
#include <inttypes.h>
typedef uint32_t u_int32;
typedef uint16_t u_int16;
#define __mem_y
#define memcpyYX memcpy
#define memcpyXY memcpy
#endif
#if 0
#include <vo_stdio.h>
#else
#include <stdio.h>
#endif
#include <string.h>
#include <swap.h>
#include "chacha20.h"

__mem_y u_int32 chachaState[16];
u_int32 chachaWork[16];
#if 1
#define NO_VS1005_MUTEX_PROTECTION
#endif

#ifndef NO_VS1005_MUTEX_PROTECTION
/* Without this protection, there is a danger situation when multiple
   processes want to access multiple encryted files at the same time. */
#  include <mutex.h>
u_int16 vs1005MutexVar = 1;
#endif


const u_int32 __mem_x * __mem_y chachaWorkP[32] = {
  chachaWork +  0, chachaWork +  4, chachaWork +  8, chachaWork + 12,
  chachaWork +  1, chachaWork +  5, chachaWork +  9, chachaWork + 13,
  chachaWork +  2, chachaWork +  6, chachaWork + 10, chachaWork + 14,
  chachaWork +  3, chachaWork +  7, chachaWork + 11, chachaWork + 15,
  chachaWork +  0, chachaWork +  5, chachaWork + 10, chachaWork + 15,
  chachaWork +  1, chachaWork +  6, chachaWork + 11, chachaWork + 12,
  chachaWork +  2, chachaWork +  7, chachaWork +  8, chachaWork + 13,
  chachaWork +  3, chachaWork +  4, chachaWork +  9, chachaWork + 14,
};

#if 0
#ifdef __VSDSP__
void Chacha20QuarterRoll8(void);
#else
void Chacha20QuarterRoll8(void) {
  u_int32 **cp = chachaWorkP;
  int i;
  for (i=0; i<8; i++) {
    u_int32 *a, *b, *c, *d;
    a = *cp++; b = *cp++; c = *cp++; d = *cp++;
#ifdef __VSDSP__
    printf(""); /* Dummy function call prevents LCC 1.59 bug. */
#endif
    *a += *b; *d ^= *a; *d = (*d << 16)|(*d >> (32 - 16));
    *c += *d; *b ^= *c; *b = (*b << 12)|(*b >> (32 - 12));
    *a += *b; *d ^= *a; *d = (*d <<  8)|(*d >> (32 -  8));
    *c += *d; *b ^= *c; *b = (*b <<  7)|(*b >> (32 -  7));
  }
}
#endif

#else

#ifdef __VSDSP__
void Chacha20QuarterRoll8N(register __c0 s_int16 n);
#else
void Chacha20QuarterRoll8N(register __c0 s_int16 n) {
  int j;
  for (j=0; j<n; j++) {
    u_int32 **cp = chachaWorkP;
    int i;
    for (i=0; i<8; i++) {
      u_int32 *a, *b, *c, *d;
      a = *cp++; b = *cp++; c = *cp++; d = *cp++;
#ifdef __VSDSP__
      printf(""); /* Dummy function call prevents LCC 1.59 bug. */
#endif
      *a += *b; *d ^= *a; *d = (*d << 16)|(*d >> (32 - 16));
      *c += *d; *b ^= *c; *b = (*b << 12)|(*b >> (32 - 12));
      *a += *b; *d ^= *a; *d = (*d <<  8)|(*d >> (32 -  8));
      *c += *d; *b ^= *c; *b = (*b <<  7)|(*b >> (32 -  7));
    }
  }
}
#endif

#endif


#if 0
void QuarterRoll(register __i0 u_int32 *a,
                 register __i1 u_int32 *b,
                 register __i2 u_int32 *c,
                 register __i3 u_int32 *d) {
  *a += *b; *d ^= *a; *d = (*d << 16)|(*d >> (32 - 16));
  *c += *d; *b ^= *c; *b = (*b << 12)|(*b >> (32 - 12));
  *a += *b; *d ^= *a; *d = (*d <<  8)|(*d >> (32 -  8));
  *c += *d; *b ^= *c; *b = (*b <<  7)|(*b >> (32 -  7));
#if 0
  printf("%2d %04x %08lx %08lx %08lx %08lx\n",
	 a-chachaWork, a, *a, *b, *c, *d);
#endif
}
#endif

void Chacha20SetKey(register u_int32 *key,
		    register u_int32 counter,
		    register u_int32 *nonce,
		    register u_int32 *state) {
  static const u_int32 keyConsts[] = {
    0x61707865, 0x3320646e, 0x79622d32, 0x6b206574
  };
  memcpy(state+ 0, keyConsts, 4*sizeof(chachaState[0]));
  memcpy(state+ 4, key,       8*sizeof(chachaState[0]));
  state[12] = counter;
  memcpy(state+13, nonce,     3*sizeof(chachaState[0]));
}


#ifdef __VSDSP__
void Chacha20Sum32NXY(register __i0 u_int32 *d, register __i2 u_int32 __mem_y *s, register __a0 s_int16 n);
#else
void Chacha20Sum32NXY(register __i0 u_int32 *d, register __i2 u_int32 __mem_y *s, register __a0 s_int16 n) {
  u_int32 __mem_y *st = chachaState;
  u_int32 *w = chachaWork;
  u_int16 i;

  for (i=0; i<n; i++) {
#ifdef __VSDSP__
    *w = Swap32Mix(*w + *st++); /* 0x11223344 -> 0x22114433 */
    w++;
#else
    *w++ += *st++;
#endif
  }
}
#endif


void Chacha20Block(void) {
  memcpyYX(chachaWork, chachaState, sizeof(chachaState));
  Chacha20QuarterRoll8N(10);
  Chacha20Sum32NXY(chachaWork, chachaState, 16);
}


#ifdef __VSDSP__
void Chacha20XOR16(register __i0 u_int16 *d, register __i2 u_int32 *s);
#else
void Chacha20XOR16(register __i0 u_int16 *d, register __i2 u_int32 *s) {
  int i;
  for (i=0; i<16; i++) {
    u_int16 t = (u_int16)(*s);
    *d++ ^= t;
    t = (u_int16)(*s >> 16);
    *d++ ^= t;
    s++;
  }
}
#endif


void Chacha20Encrypt(register u_int16 *msg, register u_int32 *state,
		     register u_int16 len) {
  u_int16 i;
  len /= 32;

#if 0
  printf("Chacha20Encrypt(msg=%x, state=%x, len=%x\n", msg, state, len);
#endif
#ifndef NO_VS1005_MUTEX_PROTECTION
  ObtainMutex(&vs1005MutexVar);
#endif

  memcpyXY(chachaState, state, sizeof(chachaState));
  for (i=0; i<len; i++) {
    Chacha20Block();
    Chacha20XOR16(msg, chachaWork);
    msg += 32;
    /* Udpate counter */
    chachaState[12]++;
  }
  memcpyYX(state, chachaState, sizeof(chachaState));

  /* Clear memory of our secrets. */
  memsetY(chachaState, 0, sizeof(chachaState));
  memset(chachaWork, 0, sizeof(chachaWork));

#ifndef NO_VS1005_MUTEX_PROTECTION
  ReleaseMutex(&vs1005MutexVar);
#endif
 
  return;
}
