391 lines
		
	
	
	
		
			8.8 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			391 lines
		
	
	
	
		
			8.8 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/* gameplaySP
 | 
						|
 *
 | 
						|
 * Copyright (C) 2006 Exophase <exophase@gmail.com>
 | 
						|
 *
 | 
						|
 * This program is free software; you can redistribute it and/or
 | 
						|
 * modify it under the terms of the GNU General Public License as
 | 
						|
 * published by the Free Software Foundation; either version 2 of
 | 
						|
 * the License, or (at your option) any later version.
 | 
						|
 *
 | 
						|
 * This program is distributed in the hope that it will be useful,
 | 
						|
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
						|
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | 
						|
 * General Public License for more details.
 | 
						|
 *
 | 
						|
 * You should have received a copy of the GNU General Public License
 | 
						|
 * along with this program; if not, write to the Free Software
 | 
						|
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 | 
						|
 */
 | 
						|
 | 
						|
#include "common.h"
 | 
						|
 | 
						|
cheat_type cheats[MAX_CHEATS];
 | 
						|
u32 num_cheats;
 | 
						|
 | 
						|
void decrypt_gsa_code(u32 *address_ptr, u32 *value_ptr, cheat_variant_enum
 | 
						|
 cheat_variant)
 | 
						|
{
 | 
						|
  u32 i;
 | 
						|
  u32 address = *address_ptr;
 | 
						|
  u32 value = *value_ptr;
 | 
						|
  u32 r = 0xc6ef3720;
 | 
						|
 | 
						|
  u32 seeds_v1[4] =
 | 
						|
  {
 | 
						|
    0x09f4fbbd, 0x9681884a, 0x352027e9, 0xf3dee5a7
 | 
						|
  };
 | 
						|
  u32 seeds_v3[4] =
 | 
						|
  {
 | 
						|
    0x7aa9648f, 0x7fae6994, 0xc0efaad5, 0x42712c57
 | 
						|
  };
 | 
						|
  u32 *seeds;
 | 
						|
 | 
						|
  if(cheat_variant == CHEAT_TYPE_GAMESHARK_V1)
 | 
						|
    seeds = seeds_v1;
 | 
						|
  else
 | 
						|
    seeds = seeds_v3;
 | 
						|
 | 
						|
  for(i = 0; i < 32; i++)
 | 
						|
  {
 | 
						|
    value -= ((address << 4) + seeds[2]) ^ (address + r) ^
 | 
						|
     ((address >> 5) + seeds[3]);
 | 
						|
    address -= ((value << 4) + seeds[0]) ^ (value + r) ^
 | 
						|
     ((value >> 5) + seeds[1]);
 | 
						|
    r -= 0x9e3779b9;
 | 
						|
  }
 | 
						|
 | 
						|
  *address_ptr = address;
 | 
						|
  *value_ptr = value;
 | 
						|
}
 | 
						|
 | 
						|
void add_cheats(char *cheats_filename)
 | 
						|
{
 | 
						|
  FILE *cheats_file;
 | 
						|
  char current_line[256];
 | 
						|
  char *name_ptr;
 | 
						|
  u32 *cheat_code_ptr;
 | 
						|
  u32 address, value;
 | 
						|
  u32 num_cheat_lines;
 | 
						|
  u32 cheat_name_length;
 | 
						|
  cheat_variant_enum current_cheat_variant;
 | 
						|
 | 
						|
  num_cheats = 0;
 | 
						|
 | 
						|
  cheats_file = fopen(cheats_filename, "rb");
 | 
						|
 | 
						|
  if(cheats_file)
 | 
						|
  {
 | 
						|
    while(fgets(current_line, 256, cheats_file))
 | 
						|
    {
 | 
						|
      // Get the header line first
 | 
						|
      name_ptr = strchr(current_line, ' ');
 | 
						|
      if(name_ptr)
 | 
						|
      {
 | 
						|
        *name_ptr = 0;
 | 
						|
        name_ptr++;
 | 
						|
      }
 | 
						|
 | 
						|
      if(!strcasecmp(current_line, "gameshark_v1") ||
 | 
						|
       !strcasecmp(current_line, "gameshark_v2") ||
 | 
						|
       !strcasecmp(current_line, "PAR_v1") ||
 | 
						|
       !strcasecmp(current_line, "PAR_v2"))
 | 
						|
      {
 | 
						|
        current_cheat_variant = CHEAT_TYPE_GAMESHARK_V1;
 | 
						|
      }
 | 
						|
      else
 | 
						|
 | 
						|
      if(!strcasecmp(current_line, "gameshark_v3") ||
 | 
						|
       !strcasecmp(current_line, "PAR_v3"))
 | 
						|
      {
 | 
						|
        current_cheat_variant = CHEAT_TYPE_GAMESHARK_V3;
 | 
						|
      }
 | 
						|
      else
 | 
						|
      {
 | 
						|
        current_cheat_variant = CHEAT_TYPE_INVALID;
 | 
						|
      }
 | 
						|
 | 
						|
      if(current_cheat_variant != CHEAT_TYPE_INVALID)
 | 
						|
      {
 | 
						|
        strncpy(cheats[num_cheats].cheat_name, name_ptr, CHEAT_NAME_LENGTH - 1);
 | 
						|
        cheats[num_cheats].cheat_name[CHEAT_NAME_LENGTH - 1] = 0;
 | 
						|
        cheat_name_length = strlen(cheats[num_cheats].cheat_name);
 | 
						|
        if(cheat_name_length &&
 | 
						|
         ((cheats[num_cheats].cheat_name[cheat_name_length - 1] == '\n') ||
 | 
						|
          (cheats[num_cheats].cheat_name[cheat_name_length - 1] == '\r')))
 | 
						|
        {
 | 
						|
          cheats[num_cheats].cheat_name[cheat_name_length - 1] = 0;
 | 
						|
          cheat_name_length--;
 | 
						|
        }
 | 
						|
 | 
						|
        if(cheat_name_length &&
 | 
						|
         cheats[num_cheats].cheat_name[cheat_name_length - 1] == '\r')
 | 
						|
        {
 | 
						|
          cheats[num_cheats].cheat_name[cheat_name_length - 1] = 0;
 | 
						|
        }
 | 
						|
 | 
						|
        cheats[num_cheats].cheat_variant = current_cheat_variant;
 | 
						|
        cheat_code_ptr = cheats[num_cheats].cheat_codes;
 | 
						|
        num_cheat_lines = 0;
 | 
						|
 | 
						|
        while(fgets(current_line, 256, cheats_file))
 | 
						|
        {
 | 
						|
          if(strlen(current_line) < 3)
 | 
						|
            break;
 | 
						|
 | 
						|
          sscanf(current_line, "%08x %08x", &address, &value);
 | 
						|
 | 
						|
          decrypt_gsa_code(&address, &value, current_cheat_variant);
 | 
						|
 | 
						|
          cheat_code_ptr[0] = address;
 | 
						|
          cheat_code_ptr[1] = value;
 | 
						|
 | 
						|
          cheat_code_ptr += 2;
 | 
						|
          num_cheat_lines++;
 | 
						|
        }
 | 
						|
 | 
						|
        cheats[num_cheats].num_cheat_lines = num_cheat_lines;
 | 
						|
 | 
						|
        num_cheats++;
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    fclose(cheats_file);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void process_cheat_gs1(cheat_type *cheat)
 | 
						|
{
 | 
						|
  u32 cheat_opcode;
 | 
						|
  u32 *code_ptr = cheat->cheat_codes;
 | 
						|
  u32 address, value;
 | 
						|
  u32 i;
 | 
						|
 | 
						|
  for(i = 0; i < cheat->num_cheat_lines; i++)
 | 
						|
  {
 | 
						|
    address = code_ptr[0];
 | 
						|
    value = code_ptr[1];
 | 
						|
 | 
						|
    code_ptr += 2;
 | 
						|
 | 
						|
    cheat_opcode = address >> 28;
 | 
						|
    address &= 0xFFFFFFF;
 | 
						|
 | 
						|
    switch(cheat_opcode)
 | 
						|
    {
 | 
						|
      case 0x0:
 | 
						|
        write_memory8(address, value);
 | 
						|
        break;
 | 
						|
 | 
						|
      case 0x1:
 | 
						|
        write_memory16(address, value);
 | 
						|
        break;
 | 
						|
 | 
						|
      case 0x2:
 | 
						|
        write_memory32(address, value);
 | 
						|
        break;
 | 
						|
 | 
						|
      case 0x3:
 | 
						|
      {
 | 
						|
        u32 num_addresses = address & 0xFFFF;
 | 
						|
        u32 address1, address2;
 | 
						|
        u32 i2;
 | 
						|
 | 
						|
        for(i2 = 0; i2 < num_addresses; i2++)
 | 
						|
        {
 | 
						|
          address1 = code_ptr[0];
 | 
						|
          address2 = code_ptr[1];
 | 
						|
          code_ptr += 2;
 | 
						|
          i++;
 | 
						|
 | 
						|
          write_memory32(address1, value);
 | 
						|
          if(address2 != 0)
 | 
						|
            write_memory32(address2, value);
 | 
						|
        }
 | 
						|
        break;
 | 
						|
      }
 | 
						|
 | 
						|
      // ROM patch not supported yet
 | 
						|
      case 0x6:
 | 
						|
        break;
 | 
						|
 | 
						|
      // GS button down not supported yet
 | 
						|
      case 0x8:
 | 
						|
        break;
 | 
						|
 | 
						|
      // Reencryption (DEADFACE) not supported yet
 | 
						|
      case 0xD:
 | 
						|
        if(read_memory16(address) != (value & 0xFFFF))
 | 
						|
        {
 | 
						|
          code_ptr += 2;
 | 
						|
          i++;
 | 
						|
        }
 | 
						|
        break;
 | 
						|
 | 
						|
      case 0xE:
 | 
						|
        if(read_memory16(value & 0xFFFFFFF) != (address & 0xFFFF))
 | 
						|
        {
 | 
						|
          u32 skip = ((address >> 16) & 0x03);
 | 
						|
          code_ptr += skip * 2;
 | 
						|
          i += skip;
 | 
						|
        }
 | 
						|
        break;
 | 
						|
 | 
						|
      // Hook routine not supported yet (not important??)
 | 
						|
      case 0x0F:
 | 
						|
        break;
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
// These are especially incomplete.
 | 
						|
 | 
						|
void process_cheat_gs3(cheat_type *cheat)
 | 
						|
{
 | 
						|
  u32 cheat_opcode;
 | 
						|
  u32 *code_ptr = cheat->cheat_codes;
 | 
						|
  u32 address, value;
 | 
						|
  u32 i;
 | 
						|
 | 
						|
  for(i = 0; i < cheat->num_cheat_lines; i++)
 | 
						|
  {
 | 
						|
    address = code_ptr[0];
 | 
						|
    value = code_ptr[1];
 | 
						|
 | 
						|
    code_ptr += 2;
 | 
						|
 | 
						|
    cheat_opcode = address >> 28;
 | 
						|
    address &= 0xFFFFFFF;
 | 
						|
 | 
						|
    switch(cheat_opcode)
 | 
						|
    {
 | 
						|
      case 0x0:
 | 
						|
        cheat_opcode = address >> 24;
 | 
						|
        address = (address & 0xFFFFF) + ((address << 4) & 0xF000000);
 | 
						|
 | 
						|
        switch(cheat_opcode)
 | 
						|
        {
 | 
						|
          case 0x0:
 | 
						|
          {
 | 
						|
            u32 iterations = value >> 24;
 | 
						|
            u32 i2;
 | 
						|
 | 
						|
            value &= 0xFF;
 | 
						|
 | 
						|
            for(i2 = 0; i2 <= iterations; i2++, address++)
 | 
						|
            {
 | 
						|
              write_memory8(address, value);
 | 
						|
            }
 | 
						|
            break;
 | 
						|
          }
 | 
						|
 | 
						|
          case 0x2:
 | 
						|
          {
 | 
						|
            u32 iterations = value >> 16;
 | 
						|
            u32 i2;
 | 
						|
 | 
						|
            value &= 0xFFFF;
 | 
						|
 | 
						|
            for(i2 = 0; i2 <= iterations; i2++, address += 2)
 | 
						|
            {
 | 
						|
              write_memory16(address, value);
 | 
						|
            }
 | 
						|
            break;
 | 
						|
          }
 | 
						|
 | 
						|
          case 0x4:
 | 
						|
            write_memory32(address, value);
 | 
						|
            break;
 | 
						|
        }
 | 
						|
        break;
 | 
						|
 | 
						|
      case 0x4:
 | 
						|
        cheat_opcode = address >> 24;
 | 
						|
        address = (address & 0xFFFFF) + ((address << 4) & 0xF000000);
 | 
						|
 | 
						|
        switch(cheat_opcode)
 | 
						|
        {
 | 
						|
          case 0x0:
 | 
						|
            address = read_memory32(address) + (value >> 24);
 | 
						|
            write_memory8(address, value & 0xFF);
 | 
						|
            break;
 | 
						|
 | 
						|
          case 0x2:
 | 
						|
            address = read_memory32(address) + ((value >> 16) * 2);
 | 
						|
            write_memory16(address, value & 0xFFFF);
 | 
						|
            break;
 | 
						|
 | 
						|
          case 0x4:
 | 
						|
            address = read_memory32(address);
 | 
						|
            write_memory32(address, value);
 | 
						|
            break;
 | 
						|
 | 
						|
        }
 | 
						|
        break;
 | 
						|
 | 
						|
      case 0x8:
 | 
						|
        cheat_opcode = address >> 24;
 | 
						|
        address = (address & 0xFFFFF) + ((address << 4) & 0xF000000);
 | 
						|
 | 
						|
        switch(cheat_opcode)
 | 
						|
        {
 | 
						|
          case 0x0:
 | 
						|
            value = (value & 0xFF) + read_memory8(address);
 | 
						|
            write_memory8(address, value);
 | 
						|
            break;
 | 
						|
 | 
						|
          case 0x2:
 | 
						|
            value = (value & 0xFFFF) + read_memory16(address);
 | 
						|
            write_memory16(address, value);
 | 
						|
            break;
 | 
						|
 | 
						|
          case 0x4:
 | 
						|
            value = value + read_memory32(address);
 | 
						|
            write_memory32(address, value);
 | 
						|
            break;
 | 
						|
        }
 | 
						|
        break;
 | 
						|
 | 
						|
      case 0xC:
 | 
						|
        cheat_opcode = address >> 24;
 | 
						|
        address = (address & 0xFFFFFF) + 0x4000000;
 | 
						|
 | 
						|
        switch(cheat_opcode)
 | 
						|
        {
 | 
						|
          case 0x6:
 | 
						|
            write_memory16(address, value);
 | 
						|
            break;
 | 
						|
 | 
						|
          case 0x7:
 | 
						|
            write_memory32(address, value);
 | 
						|
            break;
 | 
						|
        }
 | 
						|
        break;
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void process_cheats(void)
 | 
						|
{
 | 
						|
  u32 i;
 | 
						|
 | 
						|
  for(i = 0; i < num_cheats; i++)
 | 
						|
  {
 | 
						|
    if(cheats[i].cheat_active)
 | 
						|
    {
 | 
						|
      switch(cheats[i].cheat_variant)
 | 
						|
      {
 | 
						|
        case CHEAT_TYPE_GAMESHARK_V1:
 | 
						|
          process_cheat_gs1(cheats + i);
 | 
						|
          break;
 | 
						|
 | 
						|
        case CHEAT_TYPE_GAMESHARK_V3:
 | 
						|
          process_cheat_gs3(cheats + i);
 | 
						|
          break;
 | 
						|
 | 
						|
        default:
 | 
						|
          break;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 |