/******************************************************************************/
/*                                                                            */
/*          BitSwap Protected ROMS decoder (c)1998 Antiriad/Raine             */
/*          -----------------------------------------------------             */
/*                                                                            */
/* Used for decoding the protected 68000 roms on Jaleco games. The games have */
/* the bits switched around in each word. This code checks all the possible   */
/* bitpatterns (which is 16 factorial, or to be precise 2,004,189,184). To    */
/* narrow down the search, you have to determine matching encoded and decoded */
/* words from the rom. This is done mainly by recognising the encoded words   */
/* from their frequency/position/distribution in the encoded rom. Good things */
/* to look for are RTS, NOP, ORI.W #$2700,SR and data in the vector table.    */
/* The more words you work out, the smaller the number of results, eventually */
/* it will narrow down to a few results and then just (the 100% correct) one. */
/*                                                                            */
/******************************************************************************/

#include "raine.h"
#include "decode.h"
#include "loadroms.h"

static DEF_INLINE UINT8 decode_byte(UINT8 src, UINT8 *bitp)
{
   UINT8 ret;
   UINT8 ta;

   ret=0;
   for(ta=0;ta<8;ta++)
      ret |= (((src>>bitp[ta])&1) << (7-ta));

   return ret;
}

void decode_ssmissin(void)
{
  /* Like Thunder Dragon Bootleg without the Program Rom Swapping */
  unsigned char *rom;
  int A;

  /* Graphic Roms Could Also Do With Rearranging to make things simpler */
  static unsigned char decode_data_tdragonbgfx[1][8] =
    {
      {0x7,0x6,0x5,0x3,0x4,0x2,0x1,0x0},
    };

  rom = load_region[REGION_GFX2];
  for (A = 0;A < get_region_size(REGION_GFX2);A++)
    {
      rom[A] = decode_byte( rom[A], decode_data_tdragonbgfx[0]);
    }

  rom = load_region[REGION_GFX3];
  for (A = 0;A < get_region_size(REGION_GFX3);A++)
    {
      rom[A] = decode_byte( rom[A], decode_data_tdragonbgfx[0]);
    }
}

static DEF_INLINE UINT8 encode_byte(UINT8 src, UINT8 *bitp)
{
   UINT8 ret;
   UINT8 ta;

   ret=0;
   for(ta=0;ta<8;ta++)
      ret |= (((src>>(7-ta))&1) << bitp[ta]);

   return ret;
}

static DEF_INLINE UINT16 decode_word(UINT16 src, UINT8 *bitp)
{
   UINT16 ret;
   UINT8 ta;

   ret=0;
   for(ta=0;ta<16;ta++)
      ret |= (((src>>bitp[ta])&1) << (15-ta));

   return ret;
}

static DEF_INLINE UINT16 encode_word(UINT16 src, UINT8 *bitp)
{
   UINT16 ret;
   UINT8 ta;

   ret=0;
   for(ta=0;ta<16;ta++)
      ret |= (((src>>(15-ta))&1) << bitp[ta]);

   return ret;
}

static UINT32 ms1_address_map(UINT32 addr)
{
   int ta;

   if(addr>=0x20000) return 3;
   if(addr>=0x18000) return 1;

   ta=1;

   if((addr&8)==0)ta=0;
   addr=addr>>4;
   if((addr&4)==0)ta=0;
   addr=addr>>4;
   if((addr&2)==0)ta=0;
   addr=addr>>4;
   if((addr&8)!=0)ta+=2;

   return ta;
}

static UINT32 bjt_address_map_obj(UINT32 addr)
{
   return ((addr&0x00010)>> 4) | ((addr&0x20000)>>16) | ((addr&0x100000)>>18);
}

static UINT32 bjt_address_map_bg0(UINT32 addr)
{
   return ((addr&0x00004)>> 2) | ((addr&0x00800)>> 10) | ((addr&0x40000)>> 16);
}

void DecodeStDragon(UINT8 *src)
{
   static UINT8 decode_data[4][16]={
   {0xD,0xE,0xF,0x0,0x1,0x8,0x9,0xA,0xB,0xC,0x5,0x6,0x7,0x2,0x3,0x4},
   {0xF,0xD,0xB,0x9,0x7,0x5,0x3,0x1,0xE,0xC,0xA,0x8,0x6,0x4,0x2,0x0},
   {0x0,0x1,0x2,0x3,0x4,0x5,0x6,0x7,0xB,0xA,0x9,0x8,0xF,0xE,0xD,0xC},
   {0x0,0x1,0x2,0x3,0x4,0x5,0x6,0x7,0xB,0xA,0x9,0x8,0xF,0xE,0xD,0xC},
   };

   int ta;

   for(ta=0;ta<0x40000;ta+=2){
      WriteWord68k(&src[ta],decode_word(ReadWord68k(&src[ta]),&decode_data[ms1_address_map(ta)][0]));
   }
}

void DecodePlusAlpha(UINT8 *src)
{
   static UINT8 decode_data[4][16]={
   {0xD,0xE,0xF,0x0,0xA,0x9,0x8,0x1,0x6,0x5,0xC,0xB,0x7,0x2,0x3,0x4},
   {0xF,0xD,0xB,0x9,0x7,0x5,0x3,0x1,0x8,0xA,0xC,0xE,0x0,0x2,0x4,0x6},
   {0x4,0x5,0x6,0x7,0x0,0x1,0x2,0x3,0xB,0xA,0x9,0x8,0xF,0xE,0xD,0xC},
   {0x4,0x5,0x6,0x7,0x0,0x1,0x2,0x3,0xB,0xA,0x9,0x8,0xF,0xE,0xD,0xC},
   };

   int ta;

   for(ta=0;ta<0x40000;ta+=2){
      WriteWord68k(&src[ta],decode_word(ReadWord68k(&src[ta]),&decode_data[ms1_address_map(ta)][0]));
   }
}

void DecodeRodlandE(UINT8 *src)
{
   static UINT8 decode_data[4][16]={
   {0xD,0x0,0xA,0x9,0x6,0xE,0xB,0xF,0x5,0xC,0x7,0x2,0x3,0x8,0x1,0x4},
   {0x4,0x5,0x6,0x7,0x0,0x1,0x2,0x3,0xB,0xA,0x9,0x8,0xF,0xE,0xD,0xC},
   {0xF,0xD,0xB,0x9,0xC,0xE,0x0,0x7,0x5,0x3,0x1,0x8,0xA,0x2,0x4,0x6},
   {0x4,0x5,0x1,0x2,0xE,0xD,0x3,0xB,0xA,0x9,0x6,0x7,0x0,0x8,0xF,0xC},
   };

   int ta;

   for(ta=0;ta<0x40000;ta+=2){
      WriteWord68k(&src[ta],decode_word(ReadWord68k(&src[ta]),&decode_data[ms1_address_map(ta)][0]));
   }
}

void DecodePipiBibi(UINT8 *src)
{
   static UINT8 decode_data[4][16]={
   {0x9,0xD,0xE,0xF,0x0,0xA,0x8,0x1,0x6,0x5,0xC,0xB,0x7,0x2,0x3,0x4},
   {0xD,0xB,0x9,0x7,0x5,0x3,0x1,0x8,0xA,0xC,0xE,0x0,0x2,0x4,0x6,0xF},
   {0x4,0x5,0x6,0x7,0x0,0x1,0x2,0x3,0xB,0xA,0x9,0x8,0xF,0xE,0xD,0xC},
   {0x0,0x1,0x2,0x3,0x4,0x5,0x6,0x7,0xB,0xA,0x9,0x8,0xF,0xE,0xD,0xC},
   };

   int ta;

   for(ta=0;ta<0x40000;ta+=2){
      WriteWord68k(&src[ta],decode_word(ReadWord68k(&src[ta]),&decode_data[(ta>>1)&3][0]));
   }
}

void DecodePlotting(UINT8 *src)
{
   static UINT8 decode_data[1][8]={
   {0,1,2,3,4,5,6,7},
   };

   int ta;

   for(ta=0;ta<0x10000;ta++){
      WriteByte68k(&src[ta],decode_byte(ReadByte68k(&src[ta]),&decode_data[0][0]));
   }
}

void DecodeBombJackTwin_OBJ(UINT8 *src, UINT32 size)
{
   static UINT8 decode_data[8][16]={
   {0x0,0xD,0x2,0xC,0xE,0x6,0xF,0xA,0x9,0x3,0x4,0x5,0x7,0x1,0xB,0x8},  /* Antiriad */
   {0x8,0x5,0xE,0x6,0xD,0x2,0x7,0x9,0x1,0x3,0xC,0x4,0x0,0xF,0xB,0xA},  /* Antiriad */
   {0x7,0x6,0x5,0x4,0x3,0x2,0x1,0x0,0xF,0xE,0xD,0xC,0xB,0xA,0x9,0x8},  /* Antiriad */
   {0x9,0x2,0x3,0x4,0x5,0xD,0x1,0x0,0xF,0xE,0xC,0x6,0xA,0xB,0x7,0x8},  /* Antiriad */
   /* The Following are used by Macross Only, and might not be 100% accurate (I'd say around 95%) */
   {0xa,0x3,0xd,0xe,    0xc,0x4,0x0,0x8,    0x1,0x6,0x2,0x5,    0xf,0x7,0xb,0x9},  /* Haze 20/07/00 */
   {0x9,0x6,0xc,0x2,    0x3,0x4,0x8,0xf,    0x7,0x5,0xd,0xe,    0xb,0xa,0x0,0x1},  /* Haze 20/07/00 */
   {0x1,0xD,0x2,0xE,    0x4,0xC,0x8,0xF,    0x0,0x5,0x6,0x3,    0x9,0xB,0xA,0x7},  /* Antiriad, Modified by Haze 20/07/00 */
   {0xA,0xD,0x3,0x6,    0x5,0xE,0x1,0x7,    0x9,0xC,0x4,0x2,    0xF,0x0,0xB,0x8},  /* Antiriad, Modified by Haze 20/07/00 */
   };

   int ta;

   for(ta=0;(UINT32)ta<size;ta+=2){
      WriteWord68k(&src[ta],decode_word(ReadWord68k(&src[ta]),&decode_data[bjt_address_map_obj(ta)][0]));
   }
}

void DecodeBombJackTwin_BG0(UINT8 *src, UINT32 size)
{
   static UINT8 decode_data[8][8]={
   {0x3,0x0,0x7,0x2,0x5,0x1,0x4,0x6},
   {0x1,0x2,0x6,0x5,0x4,0x0,0x3,0x7},
   {0x7,0x6,0x5,0x4,0x3,0x2,0x1,0x0},
   {0x7,0x6,0x5,0x0,0x1,0x4,0x3,0x2},
   {0x2,0x0,0x1,0x4,0x3,0x5,0x7,0x6},
   {0x5,0x3,0x7,0x0,0x4,0x6,0x2,0x1},
   {0x2,0x7,0x0,0x6,0x5,0x3,0x1,0x4},
   {0x3,0x4,0x7,0x6,0x2,0x0,0x5,0x1},
   };

   int ta;

   for(ta=0;(UINT32)ta<size;ta++){
      WriteByte68k(&src[ta],decode_byte(ReadByte68k(&src[ta]),&decode_data[bjt_address_map_bg0(ta)][0]));
   }
}

void DecodeThunderDragon_OBJ(UINT8 *src)
{
   static UINT8 decode_data[1][8]={
   {7,6,5,3,4,2,1,0},
   };

   int ta;

   for(ta=0;ta<0x100000;ta++){
      WriteByte68k(&src[ta],decode_byte(ReadByte68k(&src[ta]),&decode_data[0][0]));
   }
}

void DecodeThunderDragon_ROM(UINT8 *src)
{
   static UINT8 decode_data[1][16]={
   {0xE,0xC,0xA,0x8,0x7,0x5,0x3,0x1,0xF,0xD,0xB,0x9,0x6,0x4,0x2,0x0},
   };

   int ta;

   for(ta=0;ta<0x40000;ta+=2){
      WriteWord68k(&src[ta],decode_word(ReadWord68k(&src[ta]),&decode_data[0][0]));
   }
}

// Deocding stuff for qsound... Thanks mame team !

static int bitswap1(int src,int key,int select)
{
  if (select & (1 << ((key >> 0) & 7)))
    src = (src & 0xfc) | ((src & 0x01) << 1) | ((src & 0x02) >> 1);
  if (select & (1 << ((key >> 4) & 7)))
    src = (src & 0xf3) | ((src & 0x04) << 1) | ((src & 0x08) >> 1);
  if (select & (1 << ((key >> 8) & 7)))
    src = (src & 0xcf) | ((src & 0x10) << 1) | ((src & 0x20) >> 1);
  if (select & (1 << ((key >>12) & 7)))
    src = (src & 0x3f) | ((src & 0x40) << 1) | ((src & 0x80) >> 1);

  return src;
}

static int bitswap2(int src,int key,int select)
{
  if (select & (1 << ((key >>12) & 7)))
    src = (src & 0xfc) | ((src & 0x01) << 1) | ((src & 0x02) >> 1);
  if (select & (1 << ((key >> 8) & 7)))
    src = (src & 0xf3) | ((src & 0x04) << 1) | ((src & 0x08) >> 1);
  if (select & (1 << ((key >> 4) & 7)))
    src = (src & 0xcf) | ((src & 0x10) << 1) | ((src & 0x20) >> 1);
  if (select & (1 << ((key >> 0) & 7)))
    src = (src & 0x3f) | ((src & 0x40) << 1) | ((src & 0x80) >> 1);

  return src;
}

static int bytedecode(int src,int swap_key1,int swap_key2,int xor_key,int select)
{
  src = bitswap1(src,swap_key1 & 0xffff,select & 0xff);
  src = ((src & 0x7f) << 1) | ((src & 0x80) >> 7);
  src = bitswap2(src,swap_key1 >> 16,select & 0xff);
  src ^= xor_key;
  src = ((src & 0x7f) << 1) | ((src & 0x80) >> 7);
  src = bitswap2(src,swap_key2 & 0xffff,select >> 8);
  src = ((src & 0x7f) << 1) | ((src & 0x80) >> 7);
  src = bitswap1(src,swap_key2 >> 16,select >> 8);
  return src;
}

void kabuki_decode(unsigned char *src,unsigned char *dest_op,unsigned char *dest_data,
		int base_addr,int length,int swap_key1,int swap_key2,int addr_key,int xor_key)
{
  int A;
  int select;

  for (A = 0;A < length;A++)
    {
      /* decode opcodes */
      select = (A + base_addr) + addr_key;
      dest_op[A] = bytedecode(src[A],swap_key1,swap_key2,xor_key,select);

      /* decode data */
      select = ((A + base_addr) ^ 0x1fc0) + addr_key + 1;
      dest_data[A] = bytedecode(src[A],swap_key1,swap_key2,xor_key,select);
    }
}
