mirror of
https://github.com/0ldsk00l/nestopia.git
synced 2025-04-02 10:31:51 -04:00
355 lines
9.1 KiB
C++
355 lines
9.1 KiB
C++
////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Nestopia - NES/Famicom emulator written in C++
|
|
//
|
|
// Copyright (C) 2003-2008 Martin Freij
|
|
//
|
|
// This file is part of Nestopia.
|
|
//
|
|
// Nestopia 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.
|
|
//
|
|
// Nestopia 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 Nestopia; if not, write to the Free Software
|
|
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
#include <new>
|
|
#include "../NstMachine.hpp"
|
|
#include "../NstCheats.hpp"
|
|
#include "NstApiCheats.hpp"
|
|
#include "NstApiMachine.hpp"
|
|
|
|
namespace Nes
|
|
{
|
|
namespace Api
|
|
{
|
|
#ifdef NST_MSVC_OPTIMIZE
|
|
#pragma optimize("s", on)
|
|
#endif
|
|
|
|
Result NST_CALL Cheats::GameGenieEncode(const Code& code,char (&characters)[9]) throw()
|
|
{
|
|
if (code.address < 0x8000)
|
|
return RESULT_ERR_INVALID_PARAM;
|
|
|
|
const byte codes[8] =
|
|
{
|
|
static_cast<byte>((code.value >> 0 & 0x7U) | (code.value >> 4 & 0x8U)),
|
|
static_cast<byte>((code.value >> 4 & 0x7U) | (code.address >> 4 & 0x8U)),
|
|
static_cast<byte>((code.address >> 4 & 0x7U) | (code.useCompare ? 0x8U : 0x0U)),
|
|
static_cast<byte>((code.address >> 12 & 0x7U) | (code.address >> 0 & 0x8U)),
|
|
static_cast<byte>((code.address >> 0 & 0x7U) | (code.address >> 8 & 0x8U)),
|
|
static_cast<byte>((code.address >> 8 & 0x7U) | ((code.useCompare ? code.compare : code.value) & 0x8U)),
|
|
static_cast<byte>((code.useCompare ? ((code.compare >> 0 & 0x7U) | (code.compare >> 4 & 0x8U)) : 0)),
|
|
static_cast<byte>((code.useCompare ? ((code.compare >> 4 & 0x7U) | (code.value >> 0 & 0x8U)) : 0))
|
|
};
|
|
|
|
uint i = (code.useCompare ? 8 : 6);
|
|
|
|
characters[i--] = '\0';
|
|
|
|
do
|
|
{
|
|
static const char lut[] =
|
|
{
|
|
'A','P','Z','L','G','I','T','Y',
|
|
'E','O','X','U','K','S','V','N'
|
|
};
|
|
|
|
characters[i] = lut[codes[i]];
|
|
}
|
|
while (i--);
|
|
|
|
return RESULT_OK;
|
|
}
|
|
|
|
Result NST_CALL Cheats::GameGenieDecode(const char* const characters,Code& code) throw()
|
|
{
|
|
if (characters == NULL)
|
|
return RESULT_ERR_INVALID_PARAM;
|
|
|
|
byte codes[8];
|
|
|
|
uint length = 6;
|
|
|
|
for (uint i=0; i < length; ++i)
|
|
{
|
|
switch (characters[i])
|
|
{
|
|
case 'A': case 'a': codes[i] = 0x0; break;
|
|
case 'P': case 'p': codes[i] = 0x1; break;
|
|
case 'Z': case 'z': codes[i] = 0x2; break;
|
|
case 'L': case 'l': codes[i] = 0x3; break;
|
|
case 'G': case 'g': codes[i] = 0x4; break;
|
|
case 'I': case 'i': codes[i] = 0x5; break;
|
|
case 'T': case 't': codes[i] = 0x6; break;
|
|
case 'Y': case 'y': codes[i] = 0x7; break;
|
|
case 'E': case 'e': codes[i] = 0x8; break;
|
|
case 'O': case 'o': codes[i] = 0x9; break;
|
|
case 'X': case 'x': codes[i] = 0xA; break;
|
|
case 'U': case 'u': codes[i] = 0xB; break;
|
|
case 'K': case 'k': codes[i] = 0xC; break;
|
|
case 'S': case 's': codes[i] = 0xD; break;
|
|
case 'V': case 'v': codes[i] = 0xE; break;
|
|
case 'N': case 'n': codes[i] = 0xF; break;
|
|
|
|
default: return RESULT_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
if (i == 2 && codes[2] & 0x8U)
|
|
length = 8;
|
|
}
|
|
|
|
code.address = 0x8000 |
|
|
(
|
|
( codes[4] & 0x1U ) << 0x0 |
|
|
( codes[4] & 0x2U ) << 0x0 |
|
|
( codes[4] & 0x4U ) << 0x0 |
|
|
( codes[3] & 0x8U ) << 0x0 |
|
|
( codes[2] & 0x1U ) << 0x4 |
|
|
( codes[2] & 0x2U ) << 0x4 |
|
|
( codes[2] & 0x4U ) << 0x4 |
|
|
( codes[1] & 0x8U ) << 0x4 |
|
|
( codes[5] & 0x1U ) << 0x8 |
|
|
( codes[5] & 0x2U ) << 0x8 |
|
|
( codes[5] & 0x4U ) << 0x8 |
|
|
( codes[4] & 0x8U ) << 0x8 |
|
|
( codes[3] & 0x1U ) << 0xC |
|
|
( codes[3] & 0x2U ) << 0xC |
|
|
( codes[3] & 0x4U ) << 0xC
|
|
);
|
|
|
|
code.value =
|
|
(
|
|
( codes[0] & 0x1U ) << 0x0 |
|
|
( codes[0] & 0x2U ) << 0x0 |
|
|
( codes[0] & 0x4U ) << 0x0 |
|
|
( codes[1] & 0x1U ) << 0x4 |
|
|
( codes[1] & 0x2U ) << 0x4 |
|
|
( codes[1] & 0x4U ) << 0x4 |
|
|
( codes[0] & 0x8U ) << 0x4
|
|
);
|
|
|
|
if (length == 8)
|
|
{
|
|
code.useCompare = true;
|
|
code.value |= codes[7] & 0x8U;
|
|
code.compare =
|
|
(
|
|
( codes[6] & 0x1U ) << 0x0 |
|
|
( codes[6] & 0x2U ) << 0x0 |
|
|
( codes[6] & 0x4U ) << 0x0 |
|
|
( codes[5] & 0x8U ) << 0x0 |
|
|
( codes[7] & 0x1U ) << 0x4 |
|
|
( codes[7] & 0x2U ) << 0x4 |
|
|
( codes[7] & 0x4U ) << 0x4 |
|
|
( codes[6] & 0x8U ) << 0x4
|
|
);
|
|
}
|
|
else
|
|
{
|
|
code.useCompare = false;
|
|
code.value |= codes[5] & 0x8U;
|
|
code.compare = 0x00;
|
|
}
|
|
|
|
return RESULT_OK;
|
|
}
|
|
|
|
struct Cheats::Lut
|
|
{
|
|
static const byte rocky[];
|
|
};
|
|
|
|
const byte Cheats::Lut::rocky[] =
|
|
{
|
|
3, 13, 14, 1, 6, 9, 5, 0,
|
|
12, 7, 2, 8, 10, 11, 4, 19,
|
|
21, 23, 22, 20, 17, 16, 18, 29,
|
|
31, 24, 26, 25, 30, 27, 28
|
|
};
|
|
|
|
Result NST_CALL Cheats::ProActionRockyEncode(const Code& code,char (&characters)[9]) throw()
|
|
{
|
|
if (code.address < 0x8000 || !code.useCompare)
|
|
return RESULT_ERR_INVALID_PARAM;
|
|
|
|
const dword input = (code.address & 0x7FFFU) | dword(code.compare) << 16 | dword(code.value) << 24;
|
|
dword output = 0;
|
|
|
|
for (dword i=31, key=0xFCBDD274; i--; key = key << 1 & 0xFFFFFFFF)
|
|
{
|
|
const uint ctrl = input >> Lut::rocky[i] & 0x1;
|
|
output |= (key >> 31 ^ ctrl) << (i+1);
|
|
|
|
if (ctrl)
|
|
key ^= 0xB8309722;
|
|
}
|
|
|
|
characters[8] = '\0';
|
|
|
|
for (uint i=0; i < 8; ++i)
|
|
{
|
|
const int value = (output >> (i * 4)) & 0xF;
|
|
characters[i ^ 7] = (value >= 0xA) ? (value - 0xA + 'A') : (value + '0');
|
|
}
|
|
|
|
return RESULT_OK;
|
|
}
|
|
|
|
Result NST_CALL Cheats::ProActionRockyDecode(const char* const characters,Code& code) throw()
|
|
{
|
|
if (characters == NULL)
|
|
return RESULT_ERR_INVALID_PARAM;
|
|
|
|
dword input=0, output=0;
|
|
|
|
for (uint i=0; i < 8; ++i)
|
|
{
|
|
dword num;
|
|
const int character = characters[i ^ 7];
|
|
|
|
if (character >= '0' && character <= '9')
|
|
{
|
|
num = character - '0';
|
|
}
|
|
else if (character >= 'A' && character <= 'F')
|
|
{
|
|
num = character - 'A' + 0xA;
|
|
}
|
|
else if (character >= 'a' && character <= 'f')
|
|
{
|
|
num = character - 'a' + 0xA;
|
|
}
|
|
else
|
|
{
|
|
return RESULT_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
input |= num << (i * 4);
|
|
}
|
|
|
|
for (dword i=31, key=0xFCBDD274; i--; input <<= 1, key <<= 1)
|
|
{
|
|
if ((key ^ input) & 0x80000000)
|
|
{
|
|
output |= 1UL << Lut::rocky[i];
|
|
key ^= 0xB8309722;
|
|
}
|
|
}
|
|
|
|
code.address = (output & 0x7FFF) | 0x8000;
|
|
code.compare = output >> 16 & 0xFF;
|
|
code.value = output >> 24 & 0xFF;
|
|
code.useCompare = true;
|
|
|
|
return RESULT_OK;
|
|
}
|
|
|
|
Result Cheats::SetCode(const Code& code) throw()
|
|
{
|
|
if (emulator.tracker.IsLocked( true ))
|
|
return RESULT_ERR_NOT_READY;
|
|
|
|
try
|
|
{
|
|
if (emulator.cheats == NULL)
|
|
emulator.cheats = new Core::Cheats( emulator.cpu );
|
|
|
|
return emulator.tracker.TryResync
|
|
(
|
|
emulator.cheats->SetCode
|
|
(
|
|
code.address,
|
|
code.value,
|
|
code.compare,
|
|
code.useCompare,
|
|
emulator.Is(Machine::GAME)
|
|
),
|
|
true
|
|
);
|
|
}
|
|
catch (const std::bad_alloc&)
|
|
{
|
|
return RESULT_ERR_OUT_OF_MEMORY;
|
|
}
|
|
catch (...)
|
|
{
|
|
return RESULT_ERR_GENERIC;
|
|
}
|
|
}
|
|
|
|
Result Cheats::DeleteCode(const ulong index) throw()
|
|
{
|
|
if (emulator.tracker.IsLocked( true ))
|
|
return RESULT_ERR_NOT_READY;
|
|
|
|
if (!emulator.cheats)
|
|
return RESULT_ERR_INVALID_PARAM;
|
|
|
|
const Result result = emulator.tracker.TryResync( emulator.cheats->DeleteCode( index ), true );
|
|
|
|
if (!emulator.cheats->NumCodes())
|
|
{
|
|
delete emulator.cheats;
|
|
emulator.cheats = NULL;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
ulong Cheats::NumCodes() const throw()
|
|
{
|
|
return emulator.cheats ? emulator.cheats->NumCodes() : 0;
|
|
}
|
|
|
|
Result Cheats::GetCode(ulong index,ushort* address,uchar* value,uchar* compare,bool* useCompare) const throw()
|
|
{
|
|
if (emulator.cheats)
|
|
return emulator.cheats->GetCode( index, address, value, compare, useCompare );
|
|
else
|
|
return RESULT_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
Result Cheats::GetCode(ulong index,Code& code) const throw()
|
|
{
|
|
return GetCode( index, &code.address, &code.value, &code.compare, &code.useCompare );
|
|
}
|
|
|
|
Result Cheats::ClearCodes() throw()
|
|
{
|
|
if (emulator.tracker.IsLocked( true ))
|
|
return RESULT_ERR_NOT_READY;
|
|
|
|
if (!emulator.cheats)
|
|
return RESULT_NOP;
|
|
|
|
if (emulator.cheats->NumCodes())
|
|
emulator.tracker.Resync( true );
|
|
|
|
delete emulator.cheats;
|
|
emulator.cheats = NULL;
|
|
|
|
return RESULT_OK;
|
|
}
|
|
|
|
Cheats::Ram Cheats::GetRam() const throw()
|
|
{
|
|
return emulator.cpu.GetRam();
|
|
}
|
|
|
|
#ifdef NST_MSVC_OPTIMIZE
|
|
#pragma optimize("", on)
|
|
#endif
|
|
}
|
|
}
|
|
|