diff options
Diffstat (limited to 'src/emu/cheat.c')
-rw-r--r-- | src/emu/cheat.c | 11890 |
1 files changed, 11890 insertions, 0 deletions
diff --git a/src/emu/cheat.c b/src/emu/cheat.c new file mode 100644 index 00000000000..9a423bbf480 --- /dev/null +++ b/src/emu/cheat.c @@ -0,0 +1,11890 @@ +/********************************************************************* + + cheat.c + + MAME cheat system. + + Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team. + Visit http://mamedev.org for licensing and usage restrictions. + +*********************************************************************/ + +#include "driver.h" +#include "ui.h" +#include "uimenu.h" +#include "uitext.h" +#include "machine/eeprom.h" +#include "cheat.h" +#include <ctype.h> +#include <math.h> + +#ifdef MESS +#include "cheatms.h" +#endif + +#define OSD_READKEY_KLUDGE 1 + +/*************************************************************************** + MACROS +***************************************************************************/ + +/* easy bitfield extraction and setting, uses *_Shift, *_ShiftedMask, and *_Mask enums */ +#define EXTRACT_FIELD(data, name) (((data) >> k##name##_Shift) & k##name##_ShiftedMask) +#define SET_FIELD(data, name, in) (data = (data & ~(k##name##_ShiftedMask << k##name##_Shift)) | (((in) & k##name##_ShiftedMask) << k##name##_Shift)) +#define TEST_FIELD(data, name) ((data) & k##name##_Mask) +#define SET_MASK_FIELD(data, name) ((data) |= k##name##_Mask) +#define CLEAR_MASK_FIELD(data, name) ((data) &= ~(k##name##_Mask)) +#define TOGGLE_MASK_FIELD(data, name) ((data) ^= k##name##_Mask) + +#define DEFINE_BITFIELD_ENUM(name, end, start) k##name##_Shift = (int)(end), \ + k##name##_ShiftedMask = (int)(0xFFFFFFFF >> (32 - (start - end + 1))), \ + k##name##_Mask = (int)(k##name##_ShiftedMask << k##name##_Shift) + +#define kRegionListLength (REGION_MAX - REGION_INVALID) + +/*************************************************************************** + CONSTANTS +***************************************************************************/ + +#define CHEAT_FILENAME_MAX_LEN 255 + +/********** BIT FIELD **********/ + +enum +{ + /* cheat code */ + DEFINE_BITFIELD_ENUM(OneShot, 0, 0), + DEFINE_BITFIELD_ENUM(Type, 1, 2), + DEFINE_BITFIELD_ENUM(Operation, 3, 4), + DEFINE_BITFIELD_ENUM(TypeParameter, 5, 7), + DEFINE_BITFIELD_ENUM(UserSelectEnable, 8, 8), + DEFINE_BITFIELD_ENUM(UserSelectMinimumDisplay, 9, 9), + DEFINE_BITFIELD_ENUM(UserSelectMinimum, 10, 10), + DEFINE_BITFIELD_ENUM(UserSelectBCD, 11, 11), + DEFINE_BITFIELD_ENUM(Prefill, 12, 13), + DEFINE_BITFIELD_ENUM(RemoveFromList, 14, 14), + DEFINE_BITFIELD_ENUM(LinkExtension, 15, 15), + DEFINE_BITFIELD_ENUM(LinkEnable, 16, 16), + DEFINE_BITFIELD_ENUM(LinkCopyPreviousValue, 17, 17), + DEFINE_BITFIELD_ENUM(OperationParameter, 18, 18), + DEFINE_BITFIELD_ENUM(OperationExtend, 19, 19), + DEFINE_BITFIELD_ENUM(BytesUsed, 20, 21), + DEFINE_BITFIELD_ENUM(Endianness, 22, 22), + DEFINE_BITFIELD_ENUM(RestorePreviousValue, 23, 23), + DEFINE_BITFIELD_ENUM(LocationParameter, 24, 28), + DEFINE_BITFIELD_ENUM(LocationType, 29, 31), + + /* watch (unused ?) */ +// DEFINE_BITFIELD_ENUM(Watch_AddValue, 0, 15), +// DEFINE_BITFIELD_ENUM(Watch_Label, 16, 17), +// DEFINE_BITFIELD_ENUM(Watch_DisplayType, 18, 19), + + /* command */ + DEFINE_BITFIELD_ENUM(AutoSaveEnabled, 0, 0), + DEFINE_BITFIELD_ENUM(SearchBox, 1, 2), // 0 = Minimum, 1 = Classic, 2 = Advanced + DEFINE_BITFIELD_ENUM(DontPrintNewLabels, 3, 3), // in options menu, it is reverted. + DEFINE_BITFIELD_ENUM(ActivationKeyMessage, 4, 4), + DEFINE_BITFIELD_ENUM(LoadOldFormat, 5, 5), + DEFINE_BITFIELD_ENUM(Debug, 6, 6) +}; + +/********** OPERATION **********/ + +enum // types +{ + kType_NormalOrDelay = 0, + kType_WaitForModification, + kType_IgnoreIfDecrementing, + kType_Watch +}; + +enum // operations +{ + kOperation_WriteMask = 0, + kOperation_AddSubtract, + kOperation_ForceRange, + kOperation_SetOrClearBits, + kOperation_WriteMatch, + // kOperation_Unused5, + // kOperation_Unused6, + kOperation_None = 7 +}; + +//enum // prefills (unused?) +//{ +// kPrefill_Disable = 0, +// kPrefill_UseFF, +// kPrefill_Use00, +// kPrefill_Use01 +//}; + +enum // location types +{ + kLocation_Standard = 0, + kLocation_MemoryRegion, + kLocation_HandlerMemory, + kLocation_Custom, + kLocation_IndirectIndexed, + kLocation_ProgramSpace +}; + +enum // location parameters +{ + kCustomLocation_Comment = 0, + kCustomLocation_EEPROM, + kCustomLocation_Select, + kCustomLocation_AssignActivationKey, + kCustomLocation_Enable, + kCustomLocation_Overclock, + kCustomLocation_RefreshRate +}; + +enum // flags for action +{ + /* set for wait for modification or ignore if decrementing cheats when + the targeted value has changed + cleared after the operation is performed */ + kActionFlag_WasModified = 1 << 0, + + /* set for one shot cheats after the operation is performed */ + kActionFlag_OperationDone = 1 << 1, + + /* set if the extendData field is being used by something other than a mask value */ + kActionFlag_IgnoreMask = 1 << 2, + + /* set if the lastValue field contains valid data and can be restored if needed */ + kActionFlag_LastValueGood = 1 << 3, + + /* set after value changes from prefill value */ + kActionFlag_PrefillDone = 1 << 4, + + /* set after prefill value written */ + kActionFlag_PrefillWritten = 1 << 5, + + /* masks */ + kActionFlag_StateMask = kActionFlag_OperationDone | + kActionFlag_LastValueGood | + kActionFlag_PrefillDone | + kActionFlag_PrefillWritten, + kActionFlag_InfoMask = kActionFlag_WasModified | + kActionFlag_IgnoreMask, + kActionFlag_PersistentMask = kActionFlag_LastValueGood +}; + +enum // flags for entry +{ + /* true when the cheat is active */ + kCheatFlag_Active = 1 << 0, + + /* true if the cheat is entirely one shot */ + kCheatFlag_OneShot = 1 << 1, + + /* true if the cheat will work with one shot in special case */ + kCheatFlag_DoOneShot = 1 << 2, + + /* true if the cheat is entirely null (ex. a comment) */ + kCheatFlag_Null = 1 << 3, + + /* true if the cheat contains a user-select element */ + kCheatFlag_UserSelect = 1 << 4, + + /* true if the cheat is a select cheat */ + kCheatFlag_Select = 1 << 5, + + /* true if the cheat has been assigned an 1st activation key */ + kCheatFlag_HasActivationKey1 = 1 << 6, + + /* true if the cheat has been assigned an 2nd activation key */ + kCheatFlag_HasActivationKey2 = 1 << 7, + + /* true if the activation key is being pressed */ + kCheatFlag_ActivationKeyPressed = 1 << 8, + + /* true if the cheat has been edited or is a new cheat */ + kCheatFlag_Dirty = 1 << 9, + + /* masks */ + kCheatFlag_StateMask = kCheatFlag_Active | + kCheatFlag_DoOneShot, + kCheatFlag_InfoMask = kCheatFlag_OneShot | + kCheatFlag_DoOneShot | + kCheatFlag_Null | + kCheatFlag_UserSelect | + kCheatFlag_Select | + kCheatFlag_HasActivationKey1 | + kCheatFlag_HasActivationKey2 | + kCheatFlag_ActivationKeyPressed, + kCheatFlag_PersistentMask = kCheatFlag_Active | + kCheatFlag_DoOneShot | + kCheatFlag_HasActivationKey1 | + kCheatFlag_HasActivationKey2 | + kCheatFlag_ActivationKeyPressed | + kCheatFlag_Dirty +}; + +/********** WATCH **********/ + +enum // types +{ + kWatchLabel_None = 0, + kWatchLabel_Address, + kWatchLabel_String, + + kWatchLabel_MaxPlusOne +}; + +enum // display types +{ + kWatchDisplayType_Hex = 0, + kWatchDisplayType_Decimal, + kWatchDisplayType_Binary, + kWatchDisplayType_ASCII, + + kWatchDisplayType_MaxPlusOne +}; + +/********** REGION **********/ + +enum // flags +{ + /* true if enabled for search */ + kRegionFlag_Enabled = 1 << 0 + + /* true if the memory region has no mapped memory and uses a memory handler (unused?) */ +// kRegionFlag_UsesHandler = 1 << 1 +}; + +enum // types +{ + kRegionType_CPU = 0, + kRegionType_Memory +}; + +enum // default search regions +{ + kSearchSpeed_Fast = 0, // RAM + some banks + kSearchSpeed_Medium, // RAM + BANKx + kSearchSpeed_Slow, // all memory areas except ROM, NOP, and custom handlers + kSearchSpeed_VerySlow, // all memory areas except ROM and NOP + kSearchSpeed_AllMemory, // entire CPU address space + + kSearchSpeed_Max = kSearchSpeed_AllMemory +}; + +/********** SEARCH **********/ + +enum // operands +{ + kSearchOperand_Current = 0, + kSearchOperand_Previous, + kSearchOperand_First, + kSearchOperand_Value, + + kSearchOperand_Max = kSearchOperand_Value +}; + +enum // bits +{ + kSearchSize_8Bit = 0, + kSearchSize_16Bit, + kSearchSize_24Bit, + kSearchSize_32Bit, + kSearchSize_1Bit, + + kSearchSize_Max = kSearchSize_1Bit +}; + +enum // comparisons +{ + kSearchComparison_LessThan = 0, + kSearchComparison_GreaterThan, + kSearchComparison_EqualTo, + kSearchComparison_LessThanOrEqualTo, + kSearchComparison_GreaterThanOrEqualTo, + kSearchComparison_NotEqual, + kSearchComparison_IncreasedBy, + kSearchComparison_NearTo, + + kSearchComparison_Max = kSearchComparison_NearTo +}; + +/********** OTHERS **********/ + +enum +{ + kVerticalKeyRepeatRate = 8, + kHorizontalFastKeyRepeatRate = 5, + kHorizontalSlowKeyRepeatRate = 8 +}; + +enum +{ + kSearchBox_Minimum = 0, + kSearchBox_Classic, + kSearchBox_Advanced +}; + +/*************************************************************************** + TYPE DEFINITIONS +***************************************************************************/ + +/********** ACTION **********/ + +struct CheatAction +{ + UINT32 type; + UINT32 address; + UINT32 data; + UINT32 extendData; + UINT32 originalDataField; + + INT32 frameTimer; + UINT32 lastValue; + + UINT32 flags; + + UINT8 ** cachedPointer; + UINT32 cachedOffset; + + char * optionalName; +}; + +typedef struct CheatAction CheatAction; + +/********** ENTRY **********/ + +struct CheatEntry +{ + char * name; + char * comment; + + INT32 actionListLength; + CheatAction * actionList; + + int activationKey1; + int activationKey2; + + UINT32 flags; + int selection; +}; + +typedef struct CheatEntry CheatEntry; + +/********** WATCH **********/ + +struct WatchInfo +{ + UINT32 address; + UINT8 cpu; + UINT8 numElements; + UINT8 elementBytes; + UINT8 labelType; + UINT8 displayType; + UINT8 skip; + UINT8 elementsPerLine; + INT8 addValue; + INT8 addressShift; + INT8 dataShift; + UINT32 xor; + + UINT8 locationType; + + float x, y; + + CheatEntry * linkedCheat; + + char label[256]; +}; + +typedef struct WatchInfo WatchInfo; + +/********** REGION **********/ + +struct SearchRegion +{ + UINT32 address; + UINT32 length; + + UINT8 targetType; + UINT8 targetIdx; + + UINT8 flags; + + UINT8 * cachedPointer; + const address_map + * writeHandler; + + UINT8 * first; + UINT8 * last; + + UINT8 * status; + + UINT8 * backupLast; + UINT8 * backupStatus; + + char name[32]; // 12345678 - 12345678 BANK31 + + UINT32 numResults; + UINT32 oldNumResults; +}; + +typedef struct SearchRegion SearchRegion; + +/********** SEARCH **********/ + +struct OldSearchOptions +{ + UINT8 status; + UINT8 energy; + UINT8 operand; + UINT32 value; + UINT32 delta; +}; + +typedef struct OldSearchOptions OldSearchOptions; + +struct SearchInfo +{ + INT32 regionListLength; + SearchRegion * regionList; + + char * name; + + INT8 bytes; // 0 = 1, 1 = 2, 2 = 3, 3 = 4, 4 = bit + UINT8 swap; + UINT8 sign; + INT8 lhs; + INT8 rhs; + INT8 comparison; + + UINT8 targetType; // cpu/region + UINT8 targetIdx; // cpu or region index + + UINT32 value; + + UINT8 searchSpeed; + + UINT32 numResults; + UINT32 oldNumResults; + + INT32 currentRegionIdx; + INT32 currentResultsPage; + + UINT8 backupValid; + + OldSearchOptions oldOptions; +}; + +typedef struct SearchInfo SearchInfo; + +/********** CPU **********/ + +struct CPUInfo +{ + UINT8 type; + UINT8 dataBits; + UINT8 addressBits; + UINT8 addressCharsNeeded; + UINT32 addressMask; + UINT8 endianness; + UINT8 addressShift; +}; + +typedef struct CPUInfo CPUInfo; + +/********** MENU **********/ + +struct MenuStringList +{ + const char ** mainList; // editable menu item lists + const char ** subList; + char * flagList; + + char ** mainStrings; // lists of useable strings + char ** subStrings; + + char * buf; // string storage + + UINT32 length; // number of menu items supported + UINT32 numStrings; // number of strings supported + UINT32 mainStringLength; // max length of main string + UINT32 subStringLength; // max length of sub string +}; + +typedef struct MenuStringList MenuStringList; + +struct MenuItemInfoStruct +{ + UINT32 subcheat; + UINT32 fieldType; + UINT32 extraData; +}; + +typedef struct MenuItemInfoStruct MenuItemInfoStruct; + +/*************************************************************************** + EXPORTED VARIABLES +***************************************************************************/ + +static const char * cheatfile = NULL; + +/*************************************************************************** + GLOBAL VARIABLES +***************************************************************************/ + +static emu_timer * periodic_timer; + +static CheatEntry * cheatList = NULL; +static INT32 cheatListLength = 0; + +static WatchInfo * watchList = NULL; +static INT32 watchListLength = 0; + +static SearchInfo * searchList = NULL; +static INT32 searchListLength = 0; +static INT32 currentSearchIdx = 0; + +static CPUInfo cpuInfoList[MAX_CPU]; +static CPUInfo regionInfoList[kRegionListLength]; + +//static int cheatEngineWasActive = 0; +static int foundCheatDatabase = 0; +static int cheatsDisabled = 0; +static int watchesDisabled = 0; + +static int fullMenuPageHeight = 0; + +static char mainDatabaseName[CHEAT_FILENAME_MAX_LEN + 1]; + +static MenuStringList menuStrings; + +static MenuItemInfoStruct * menuItemInfo; +static INT32 menuItemInfoLength = 0; + +static UINT32 cheatOptions = 2; + +static const char * kCheatNameTemplates[] = +{ + "Infinite Credits", + "Infinite Time", + "Infinite Lives", + "Infinite Energy", + "Invincibility", + "Infinite Time PL1", + "Infinite Lives PL1", + "Infinite Energy PL1", + "Drain all Energy Now! PL1", + "Invincibility PL1", + "Infinite Time PL2", + "Infinite Lives PL2", + "Infinite Energy PL2", + "Drain all Energy Now! PL2", + "Invincibility PL2", + "Infinite Ammo", + "Infinite Bombs", + "Select starting level", + "Rapid Fire", + "Infinite Ammo PL1", + "Infinite Bombs PL1", + "Select Score PL1", + "Rapid Fire PL1", + "Infinite Ammo PL2", + "Infinite Bombs PL2", + "Select Score PL2", + "Rapid Fire PL2", + "Display the Correct Answer", + "Infinite ", + "Always have ", + "Get ", + "Lose ", + "Finish this ", + "---> <ENTER> To Edit <---", + "\0" +}; + +static CPUInfo rawCPUInfo = +{ + 0, // type + 8, // dataBits + 8, // addressBits + 1, // addressCharsNeeded + CPU_IS_BE // endianness +}; + +static const int kSearchByteIncrementTable[] = +{ + 1, + 2, + 3, + 4, + 1 +}; + +static const int kSearchByteStep[] = +{ + 1, + 2, + 1, + 4, + 1 +}; + +static const int kSearchByteDigitsTable[] = +{ + 2, + 4, + 6, + 8, + 1 +}; + +static const int kSearchByteDecDigitsTable[] = +{ + 3, + 5, + 8, + 10, + 1 +}; + +static const UINT32 kSearchByteMaskTable[] = +{ + 0x000000FF, + 0x0000FFFF, + 0x00FFFFFF, + 0xFFFFFFFF, + 0x00000001 +}; + +static const UINT32 kSearchByteSignBitTable[] = +{ + 0x00000080, + 0x00008000, + 0x00800000, + 0x80000000, + 0x00000000 +}; + +static const UINT32 kSearchByteUnsignedMaskTable[] = +{ + 0x0000007F, + 0x00007FFF, + 0x007FFFFF, + 0x7FFFFFFF, + 0x00000001 +}; + +static const UINT32 kCheatSizeMaskTable[] = +{ + 0x000000FF, + 0x0000FFFF, + 0x00FFFFFF, + 0xFFFFFFFF +}; + +static const UINT32 kCheatSizeDigitsTable[] = +{ + 2, + 4, + 6, + 8 +}; + +static const int kByteConversionTable[] = +{ + kSearchSize_8Bit, + kSearchSize_16Bit, + kSearchSize_24Bit, + kSearchSize_32Bit, + kSearchSize_32Bit +}; + +static const int kWatchSizeConversionTable[] = +{ + kSearchSize_8Bit, + kSearchSize_16Bit, + kSearchSize_24Bit, + kSearchSize_32Bit, + kSearchSize_8Bit +}; + +static const int kSearchOperandNeedsInit[] = +{ + 0, + 1, + 1, + 0 +}; + +static const UINT32 kPrefillValueTable[] = +{ + 0x00, + 0xFF, + 0x00, + 0x01 +}; + +static const char * kWatchLabelStringList[] = +{ + "None", + "Address", + "String" +}; + +static const char * kWatchDisplayTypeStringList[] = +{ + "Hex", + "Decimal", + "Binary", + "ASCII" +}; + +/*************************************************************************** + FUNCTION PROTOTYPES +***************************************************************************/ + +/********** MAIN **********/ +static TIMER_CALLBACK( cheat_periodic ); +static void cheat_exit(running_machine *machine); + +/********** SPECIAL KEY HANDLING **********/ +static int ShiftKeyPressed(void); +static int ControlKeyPressed(void); +static int AltKeyPressed(void); + +static int UIPressedRepeatThrottle(int code, int baseSpeed); + +/********** DIRECT KEY INPUT **********/ +static int ReadHexInput(void); + +static char * DoDynamicEditTextField(char * buf); +static void DoStaticEditTextField(char * buf, int size); +static UINT32 DoEditHexField(UINT32 data); +static UINT32 DoEditHexFieldSigned(UINT32 data, UINT32 mask); +static INT32 DoEditDecField(INT32 data, INT32 min, INT32 max); + +/********** VALUE SHIFT OR CONVERTION **********/ +static UINT32 DoShift(UINT32 input, INT8 shift); + +static UINT32 BCDToDecimal(UINT32 value); +static UINT32 DecimalToBCD(UINT32 value); + +/********** STRINGS **********/ +static void RebuildStringTables(void); +static void RequestStrings(UINT32 length, UINT32 numStrings, UINT32 mainStringLength, UINT32 subStringLength); +static void InitStringTable(void); +static void FreeStringTable(void); + +static char * CreateStringCopy(char * buf); + +/********** ADDITIONAL MENU FOR CHEAT **********/ +static INT32 UserSelectValueMenu(int selection, CheatEntry * entry); + +/********** CHEAT MENU **********/ +static int EnableDisableCheatMenu(int selection, int firstTime); +static int AddEditCheatMenu(int selection); +static int EditCheatMenu(CheatEntry * entry, int index, int selection); + +static int DoSearchMenuMinimum(int selection); // minimum mode +static int DoSearchMenuClassic(int selection); // classic mode +static int DoSearchMenu(int selection); // advanced mode +static int SelectSearchRegions(int selection, SearchInfo * search); +static int ViewSearchResults(int selection, int firstTime); + +static int ChooseWatch(int selection); +static int EditWatch(WatchInfo * entry, int selection); + +static int SelectOptions(int selection); +static int SelectSearch(int selection); + +/********** ENTRY LIST **********/ +static void ResizeCheatList(UINT32 newLength); +static void ResizeCheatListNoDispose(UINT32 newLength); + +static void AddCheatBefore(UINT32 idx); +static void DeleteCheatAt(UINT32 idx); + +static void DisposeCheat(CheatEntry * entry); +static CheatEntry * GetNewCheat(void); + +/********** ACTION LIST **********/ +static void ResizeCheatActionList(CheatEntry * entry, UINT32 newLength); +static void ResizeCheatActionListNoDispose(CheatEntry * entry, UINT32 newLength); + +static void AddActionBefore(CheatEntry * entry, UINT32 idx); +static void DeleteActionAt(CheatEntry * entry, UINT32 idx); +static void DisposeAction(CheatAction * action); + +/********** WATCH LIST **********/ +static void InitWatch(WatchInfo * info, UINT32 idx); +static void ResizeWatchList(UINT32 newLength); +static void ResizeWatchListNoDispose(UINT32 newLength); + +static void AddWatchBefore(UINT32 idx); +static void DeleteWatchAt(UINT32 idx); +static void DisposeWatch(WatchInfo * watch); +static WatchInfo * GetUnusedWatch(void); + +static void AddCheatFromWatch(WatchInfo * watch); +static void SetupCheatFromWatchAsWatch(CheatEntry * entry, WatchInfo * watch); + +/********** SEARCH LIST **********/ +static void ResizeSearchList(UINT32 newLength); +static void ResizeSearchListNoDispose(UINT32 newLength); + +static void AddSearchBefore(UINT32 idx); +static void DeleteSearchAt(UINT32 idx); + +static void InitSearch(SearchInfo * info); + +static void DisposeSearchRegions(SearchInfo * info); +static void DisposeSearch(UINT32 idx); +static SearchInfo * GetCurrentSearch(void); + +static void FillBufferFromRegion(SearchRegion * region, UINT8 * buf); +static UINT32 ReadRegionData(SearchRegion * region, UINT32 offset, UINT8 size, UINT8 swap); + +static void BackupSearch(SearchInfo * info); +static void RestoreSearchBackup(SearchInfo * info); +static void BackupRegion(SearchRegion * region); +static void RestoreRegionBackup(SearchRegion * region); +static void SetSearchRegionDefaultName(SearchRegion * region); +static void AllocateSearchRegions(SearchInfo * info); +static void BuildSearchRegions(SearchInfo * info); + +/********** CODE LOADER **********/ +static int ConvertOldCode(int code, int cpu, int * data, int * extendData); +static void HandleLocalCommandCheat(UINT32 type, UINT32 address, UINT32 data, UINT32 extendData, char * name, char * description); + +static void LoadCheatFile(char * fileName); +static void LoadCheatDatabase(void); + +static void DisposeCheatDatabase(void); +static void ReloadCheatDatabase(void); + +static void SaveCheat(CheatEntry * entry, int selection, int saveCode); +static void DoAutoSaveCheats(void); + +/********** CODE ADDITION **********/ +static void AddCheatFromResult(SearchInfo * search, SearchRegion * region, UINT32 address); +static void AddCheatFromFirstResult(SearchInfo * search); +static void AddWatchFromResult(SearchInfo * search, SearchRegion * region, UINT32 address); + +/********** SEARCH **********/ +static UINT32 SearchSignExtend(SearchInfo * search, UINT32 value); +static UINT32 ReadSearchOperand(UINT8 type, SearchInfo * search, SearchRegion * region, UINT32 address); +static UINT32 ReadSearchOperandBit(UINT8 type, SearchInfo * search, SearchRegion * region, UINT32 address); +static UINT8 DoSearchComparison(SearchInfo * search, UINT32 lhs, UINT32 rhs); +static UINT32 DoSearchComparisonBit(SearchInfo * search, UINT32 lhs, UINT32 rhs); +//static UINT8 IsRegionOffsetValid(SearchInfo * search, SearchRegion * region, UINT32 offset); + +#define IsRegionOffsetValid IsRegionOffsetValidBit + +static UINT8 IsRegionOffsetValidBit(SearchInfo * search, SearchRegion * region, UINT32 offset); +static void InvalidateRegionOffset(SearchInfo * search, SearchRegion * region, UINT32 offset); +static void InvalidateRegionOffsetBit(SearchInfo * search, SearchRegion * region, UINT32 offset, UINT32 invalidate); +static void InvalidateEntireRegion(SearchInfo * search, SearchRegion * region); + +static void InitializeNewSearch(SearchInfo * search); +static void UpdateSearch(SearchInfo * search); + +static void DoSearch(SearchInfo * search); + +/********** MEMORY ACCESSORS **********/ +static UINT8 ** LookupHandlerMemory(UINT8 cpu, UINT32 address, UINT32 * outRelativeAddress); + +static UINT32 DoCPURead(UINT8 cpu, UINT32 address, UINT8 bytes, UINT8 swap); +static UINT32 DoMemoryRead(UINT8 * buf, UINT32 address, UINT8 bytes, UINT8 swap, CPUInfo * info); +static void DoCPUWrite(UINT32 data, UINT8 cpu, UINT32 address, UINT8 bytes, UINT8 swap); +static void DoMemoryWrite(UINT32 data, UINT8 * buf, UINT32 address, UINT8 bytes, UINT8 swap, CPUInfo * info); + +/********** CPU **********/ +static UINT8 CPUNeedsSwap(UINT8 cpu); +static UINT8 RegionNeedsSwap(UINT8 region); + +static CPUInfo * GetCPUInfo(UINT8 cpu); +static CPUInfo * GetRegionCPUInfo(UINT8 region); + +static UINT32 SwapAddress(UINT32 address, UINT8 dataSize, CPUInfo * info); + +static UINT32 ReadData(CheatAction * action); +static void WriteData(CheatAction * action, UINT32 data); + +/********** WATCH **********/ +static void WatchCheatEntry(CheatEntry * entry, UINT8 associate); +static void AddActionWatch(CheatAction * action, CheatEntry * entry); +static void RemoveAssociatedWatches(CheatEntry * entry); + +static void ResetAction(CheatAction * action); +static void ActivateCheat(CheatEntry * entry); +static void DeactivateCheat(CheatEntry * entry); +static void TempDeactivateCheat(CheatEntry * entry); + +static void cheat_periodicOperation(CheatAction * action); +static void cheat_periodicAction(CheatAction * action); +static void cheat_periodicEntry(CheatEntry * entry); + +static void UpdateAllCheatInfo(void); +static void UpdateCheatInfo(CheatEntry * entry, UINT8 isLoadTime); + +static int IsAddressInRange(CheatAction * action, UINT32 length); + +static void BuildCPUInfoList(void); + +/*-------------------------------------------------------------- + special key handler - check pressing shift, ctrl or alt key +--------------------------------------------------------------*/ + +static int ShiftKeyPressed(void) +{ + return (input_code_pressed(KEYCODE_LSHIFT) || input_code_pressed(KEYCODE_RSHIFT)); +} + +static int ControlKeyPressed(void) +{ + return (input_code_pressed(KEYCODE_LCONTROL) || input_code_pressed(KEYCODE_RCONTROL)); +} + +static int AltKeyPressed(void) +{ + return (input_code_pressed(KEYCODE_LALT) || input_code_pressed(KEYCODE_RALT)); +} + +/*--------------------------------------------------------------------------------------------------------------------- + ReadKeyAsync - dirty hack until osd_readkey_unicode is supported in MAMEW re-implementation of osd_readkey_unicode +---------------------------------------------------------------------------------------------------------------------*/ + +#if 1 + +#if OSD_READKEY_KLUDGE + +static int ReadKeyAsync(int flush) +{ + int code; + + if(flush) // check key input + { + while(input_code_poll_switches(TRUE) != INPUT_CODE_INVALID) ; + + return 0; + } + + while(1) // check pressed key + { + code = input_code_poll_switches(FALSE); + + if(code == INPUT_CODE_INVALID) + { + return 0; + } + else if((code >= KEYCODE_A) && (code <= KEYCODE_Z)) + { + if(ShiftKeyPressed()) + { + return 'A' + (code - KEYCODE_A); + } + else + { + return 'a' + (code - KEYCODE_A); + } + } + else if((code >= KEYCODE_0) && (code <= KEYCODE_9)) + { + if(ShiftKeyPressed()) + { + return ")!@#$%^&*("[code - KEYCODE_0]; + } + else + { + return '0' + (code - KEYCODE_0); + } + } + else if((code >= KEYCODE_0_PAD) && (code <= KEYCODE_9_PAD)) + { + return '0' + (code - KEYCODE_0_PAD); + } + else if(code == KEYCODE_TILDE) + { + if(ShiftKeyPressed()) + { + return '~'; + } + else + { + return '`'; + } + } + else if(code == KEYCODE_MINUS) + { + if(ShiftKeyPressed()) + { + return '_'; + } + else + { + return '-'; + } + } + else if(code == KEYCODE_EQUALS) + { + if(ShiftKeyPressed()) + { + return '+'; + } + else + { + return '='; + } + } + else if(code == KEYCODE_BACKSPACE) + { + return 0x08; + } + else if(code == KEYCODE_OPENBRACE) + { + if(ShiftKeyPressed()) + { + return '{'; + } + else + { + return '['; + } + } + else if(code == KEYCODE_CLOSEBRACE) + { + if(ShiftKeyPressed()) + { + return '}'; + } + else + { + return ']'; + } + } + else if(code == KEYCODE_COLON) + { + if(ShiftKeyPressed()) + { + return ':'; + } + else + { + return ';'; + } + } + else if(code == KEYCODE_QUOTE) + { + if(ShiftKeyPressed()) + { + return '\"'; + } + else + { + return '\''; + } + } + else if(code == KEYCODE_BACKSLASH) + { + if(ShiftKeyPressed()) + { + return '|'; + } + else + { + return '\\'; + } + } + else if(code == KEYCODE_COMMA) + { + if(ShiftKeyPressed()) + { + return '<'; + } + else + { + return ','; + } + } + else if(code == KEYCODE_STOP) + { + if(ShiftKeyPressed()) + { + return '>'; + } + else + { + return '.'; + } + } + else if(code == KEYCODE_SLASH) + { + if(ShiftKeyPressed()) + { + return '?'; + } + else + { + return '/'; + } + } + else if(code == KEYCODE_SLASH_PAD) + { + return '/'; + } + else if(code == KEYCODE_ASTERISK) + { + return '*'; + } + else if(code == KEYCODE_MINUS_PAD) + { + return '-'; + } + else if(code == KEYCODE_PLUS_PAD) + { + return '+'; + } + else if(code == KEYCODE_SPACE) + { + return ' '; + } + } +} + +#define osd_readkey_unicode ReadKeyAsync + +#endif + +#endif + +/*------------------------------------- + old_style_menu - export menu items +-------------------------------------*/ + +static void old_style_menu(const char **items, const char **subitems, char *flag, int selected, int arrowize_subitem) +{ + static ui_menu_item item_list[1000]; + int menu_items; + + for (menu_items = 0; items[menu_items]; menu_items++) + { + item_list[menu_items].text = items[menu_items]; + item_list[menu_items].subtext = subitems ? subitems[menu_items] : NULL; + item_list[menu_items].flags = 0; + if (flag && flag[menu_items]) + item_list[menu_items].flags |= MENU_FLAG_INVERT; + if (menu_items == selected) + { + if (arrowize_subitem & 1) + item_list[menu_items].flags |= MENU_FLAG_LEFT_ARROW; + if (arrowize_subitem & 2) + item_list[menu_items].flags |= MENU_FLAG_RIGHT_ARROW; + } + } + ui_menu_draw(item_list, menu_items, selected, NULL); +} + +/*------------------------------------------------- + UIPressedRepoeatThrottle - key repeat handling +-------------------------------------------------*/ + +static int UIPressedRepeatThrottle(int code, int baseSpeed) +{ + static int lastCode = -1; + static int lastSpeed = -1; + static int incrementTimer = 0; + int pressed = 0; + + const int kDelayRampTimer = 10; + + if(input_port_type_pressed(code, 0)) + { + if(lastCode != code) // different key pressed + { + lastCode = code; + lastSpeed = baseSpeed; + incrementTimer = kDelayRampTimer * lastSpeed; + } + else // same key pressed + { + incrementTimer--; + + if(incrementTimer <= 0) + { + incrementTimer = kDelayRampTimer * lastSpeed; + + lastSpeed /= 2; + + if(lastSpeed < 1) + lastSpeed = 1; + + pressed = 1; + } + } + } + else + { + if(lastCode == code) + lastCode = -1; + } + + return input_ui_pressed_repeat(code, lastSpeed); +} + +/*--------------------------------- + ReadHexInput - check hex input +---------------------------------*/ + +static int ReadHexInput(void) +{ + int i; + + for(i = 0; i < 10; i++) + { + if(input_code_pressed_once(KEYCODE_0 + i)) + return i; + } + + for(i = 0; i < 10; i++) + { + if(input_code_pressed_once(KEYCODE_0_PAD + i)) + return i; + } + + for(i = 0; i < 6; i++) + { + if(input_code_pressed_once(KEYCODE_A + i)) + return i + 10; + } + + return -1; +} + +/*----------------------------------------------------------------- + DoDynamicEditTextField - edit text field with direct key input +-----------------------------------------------------------------*/ + +static char * DoDynamicEditTextField(char * buf) +{ + char code = osd_readkey_unicode(0) & 0xFF; + + if(code == 0x08) + { + /* ----- backspace ----- */ + if(buf) + { + size_t length = strlen(buf); + + if(length > 0) + { + buf[length - 1] = 0; + + if(length > 1) + buf = realloc(buf, length); + else + { + free(buf); + + buf = NULL; + } + } + } + } + else + { + if(isprint(code)) + { + if(buf) + { + size_t length = strlen(buf); + + buf = realloc(buf, length + 2); + + buf[length] = code; + buf[length + 1] = 0; + } + else + { + buf = malloc(2); + + buf[0] = code; + buf[1] = 0; + } + } + } + + return buf; +} + +/*------------------------ + DoStaticEditTextField +------------------------*/ + +static void DoStaticEditTextField(char * buf, int size) +{ + char code = osd_readkey_unicode(0) & 0xFF; + size_t length; + + if(!buf) + return; + + length = strlen(buf); + + if(code == 0x08) + { + if(length > 0) + { + buf[length - 1] = 0; + } + } + else if(isprint(code)) + { + if(length + 1 < size) + { + buf[length] = code; + buf[length + 1] = 0; + } + } +} + +/*------------------------------------------------------------------ + DoEditHexField - edit hex field with direct key input (unsigned) +------------------------------------------------------------------*/ + +static UINT32 DoEditHexField(UINT32 data) +{ + INT8 key; + + if(input_code_pressed_once(KEYCODE_BACKSPACE)) + { + /* ----- backspace ----- */ + data >>= 4; + + return data; + } + else + { + if(input_ui_pressed(IPT_UI_CLEAR)) + { + data = 0; + + return data; + } + } + + key = ReadHexInput(); + + if(key != -1) + { + data <<= 4; + data |= key; + } + + return data; +} + +/*----------------------------------------------------------------------- + DoEditHexFieldSigned - edit hex field with direct key input (signed) +-----------------------------------------------------------------------*/ + +static UINT32 DoEditHexFieldSigned(UINT32 data, UINT32 mask) +{ + INT8 key; + UINT32 isNegative = data & mask; + + if(isNegative) + data |= mask; + + key = ReadHexInput(); + + if(key != -1) + { + if(isNegative) + data = (~data) + 1; + + data <<= 4; + data |= key; + + if(isNegative) + data = (~data) + 1; + } + else + { + if(input_code_pressed_once(KEYCODE_MINUS)) + data = (~data) + 1; + } + + return data; +} + +/*-------------------------------------------------------- + DoEditDecField - edit dec field with direct key input +--------------------------------------------------------*/ + +static INT32 DoEditDecField(INT32 data, INT32 min, INT32 max) +{ + char code = osd_readkey_unicode(0) & 0xFF; + + if((code >= '0') && (code <= '9')) + { + data *= 10; + data += (code - '0'); + } + else + { + if(code == '-') + data = -data; + else + { + /* ----- backspace ----- */ + if(code == 0x08) + data /= 10; + } + } + + /* ----- adjust value ----- */ + if(data < min) + data = min; + if(data > max) + data = max; + + return data; +} + +/*************************************************************************** + INITIALIZATION +***************************************************************************/ + +/*--------------------------------------- + cheat_init - initialize cheat system +---------------------------------------*/ + +void cheat_init(running_machine *machine) +{ + /* ----- initialize lists ----- */ + cheatList = NULL; + cheatListLength = 0; + + watchList = NULL; + watchListLength = 0; + + searchList = NULL; + searchListLength = 0; + +#ifdef MESS + InitMessCheats(); +#endif + + /* ----- initialize flags ----- */ + currentSearchIdx = 0; + foundCheatDatabase = 0; + cheatsDisabled = 0; + watchesDisabled = 0; + cheatOptions = 2; + + fullMenuPageHeight = floor(1.0f / ui_get_line_height()) - 1; + + /* ----- initialize CPU info for cheat system ----- */ + BuildCPUInfoList(); + + /* ----- load cheat database ----- */ + LoadCheatDatabase(); + + ResizeSearchList(1); + ResizeWatchList(20); + + /* ----- initialize search regions ----- */ + BuildSearchRegions(GetCurrentSearch()); + AllocateSearchRegions(GetCurrentSearch()); + + /* ----- initialize string table ----- */ + InitStringTable(); + + periodic_timer = timer_alloc(cheat_periodic); + timer_adjust(periodic_timer, attotime_make(0, Machine->screen[0].refresh), 0, attotime_make(0, Machine->screen[0].refresh)); + + add_exit_callback(machine, cheat_exit); +} + +/*--------------------------------- + cheat_exit - free cheat system +---------------------------------*/ + +static void cheat_exit(running_machine *machine) +{ + int i; + + /* ----- save all cheats automatically if needed ----- */ + if(TEST_FIELD(cheatOptions, AutoSaveEnabled)) + DoAutoSaveCheats(); + + /* ----- free database ----- */ + DisposeCheatDatabase(); + + /* ----- free watch lists ----- */ + if(watchList) + { + for(i = 0; i < watchListLength; i++) + DisposeWatch(&watchList[i]); + + free(watchList); + + watchList = NULL; + } + + /* ----- free search lists ----- */ + if(searchList) + { + for(i = 0; i < searchListLength; i++) + DisposeSearch(i); + + free(searchList); + + searchList = NULL; + } + + /* ----- free string table ----- */ + FreeStringTable(); + + free(menuItemInfo); + menuItemInfo = NULL; + +#ifdef MESS + StopMessCheats(); +#endif + + /* ----- free flags ----- */ + cheatListLength = 0; + watchListLength = 0; + searchListLength = 0; + currentSearchIdx = 0; +// cheatEngineWasActive = 0; + foundCheatDatabase = 0; + cheatsDisabled = 0; + watchesDisabled = 0; + mainDatabaseName[0] = 0; + menuItemInfoLength = 0; + cheatOptions = 0; +} + +/*----------------------------------------------------- + cheat_menu - management for the cheat general menu +-----------------------------------------------------*/ + +int cheat_menu(int selection) +{ + enum + { + kMenu_EnableDisable = 0, + kMenu_AddEdit, + kMenu_Search, + kMenu_ChooseWatch, + kMenu_ReloadDatabase, + kMenu_Options, + kMenu_Return, + + kMenu_Max + }; + + ui_menu_item menu_item[kMenu_Max]; + + INT32 sel; + UINT8 total = 0; + + static INT32 submenu_choice = 0; + static int firstEntry = 0; + static int lastPstn = 0; + + /* ----- allocate memory for item list ----- */ + memset(menu_item, 0, sizeof(menu_item)); + +// cheatEngineWasActive = 1; + + sel = lastPstn; + + /********** SUB MENU **********/ + if(submenu_choice) + { + switch(sel) + { + case kMenu_EnableDisable: + submenu_choice = EnableDisableCheatMenu(submenu_choice, firstEntry); + break; + + case kMenu_AddEdit: + submenu_choice = AddEditCheatMenu(submenu_choice); + break; + + case kMenu_Search: + switch(EXTRACT_FIELD(cheatOptions, SearchBox)) + { + case kSearchBox_Minimum: + submenu_choice = DoSearchMenuMinimum(submenu_choice); + break; + + case kSearchBox_Classic: + submenu_choice = DoSearchMenuClassic(submenu_choice); + break; + + case kSearchBox_Advanced: + submenu_choice = DoSearchMenu(submenu_choice); + break; + } + break; + + case kMenu_ChooseWatch: + submenu_choice = ChooseWatch(submenu_choice); + break; + + case kMenu_Options: + submenu_choice = SelectOptions(submenu_choice); + break; + } + + firstEntry = 0; + + /* ----- quick menu switching ------ */ + if(submenu_choice < 0) + { + switch(submenu_choice) + { + case -1: + lastPstn = kMenu_EnableDisable; + break; + + case -2: + lastPstn = kMenu_AddEdit; + break; + + case -3: + lastPstn = kMenu_Search; + break; + + case -4: + lastPstn = kMenu_ChooseWatch; + break; + + default: + ui_popup_time(1, "undefined menu switching : %x", submenu_choice); + } + + submenu_choice = 1; + } + + return sel + 1; + } + + /********** MENU CONSTRUCION **********/ + menu_item[total++].text = ui_getstring(UI_enablecheat); // Enable/Disable a Cheat + + menu_item[total++].text = ui_getstring(UI_addeditcheat); // Add/Edit a Cheat + + switch(EXTRACT_FIELD(cheatOptions, SearchBox)) // Search a Cheat + { + case kSearchBox_Minimum: + menu_item[total++].text = "Search a Cheat (Minimum Mode)"; + break; + + case kSearchBox_Classic: + menu_item[total++].text = "Search a Cheat (Classic Mode)"; + break; + + case kSearchBox_Advanced: + menu_item[total++].text = "Search a Cheat (Advanced Mode)"; + break; + } + + menu_item[total++].text = ui_getstring(UI_memorywatch); // Configure Watchpoints + + menu_item[total++].text = ui_getstring(UI_reloaddatabase); // Reload Cheat Database + + menu_item[total++].text = ui_getstring(UI_options); // Options + + menu_item[total++].text = ui_getstring(UI_returntomain); // return to the MAME general menu + + menu_item[total].text = NULL; // terminate array + + /* ----- adjust cursor postion ----- */ + if(sel < 0) + sel = 0; + if(sel > (total - 1)) + sel = total - 1; + + /* ----- print it ----- */ + ui_menu_draw(menu_item, total, sel, NULL); + + /********** KEY HANDLING **********/ + if(UIPressedRepeatThrottle(IPT_UI_DOWN, kVerticalKeyRepeatRate)) + { + if(sel < (total - 1)) + sel++; + else + sel = 0; + } + + if(UIPressedRepeatThrottle(IPT_UI_UP, kVerticalKeyRepeatRate)) + { + if(sel > 0) + sel--; + else + sel = total - 1; + } + + if(input_ui_pressed(IPT_UI_SELECT)) + { + switch(sel) + { + case kMenu_Return: + submenu_choice = 0; + sel = -1; + lastPstn = 0; // if cursor is now on the return item, adjust cursor keeper + break; + + case kMenu_ReloadDatabase: + ReloadCheatDatabase(); + break; + + default: + firstEntry = 1; + submenu_choice = 1; + break; + } + } + + if(input_ui_pressed(IPT_UI_RELOAD_CHEAT)) + ReloadCheatDatabase(); + + if(input_ui_pressed(IPT_UI_CANCEL)) + { + if(sel == kMenu_Return) + lastPstn = 0; // if cursor is now on the return item, adjust cursor keeper + + sel = -1; + } + + /* ----- keep current cursor position ----- */ + if(sel >= 0) + lastPstn = sel; + + return sel + 1; +} + +/*---------- + DoShift +----------*/ + +static UINT32 DoShift(UINT32 input, INT8 shift) +{ + if(shift > 0) + return input >> shift; + else + return input << -shift; +} + +/*---------------------------------------- + BCDToDecimal - convert a value to hex +----------------------------------------*/ + +static UINT32 BCDToDecimal(UINT32 value) +{ + UINT32 accumulator = 0; + UINT32 multiplier = 1; + int i; + + for(i = 0; i < 8; i++) + { + accumulator += (value & 0xF) * multiplier; + + multiplier *= 10; + value >>= 4; + } + + return accumulator; +} + +/*---------------------------------------- + DecimalToBCD - convert a value to dec +----------------------------------------*/ + +static UINT32 DecimalToBCD(UINT32 value) +{ + UINT32 accumulator = 0; + UINT32 divisor = 10; + int i; + + for(i = 0; i < 8; i++) + { + UINT32 temp; + + temp = value % divisor; + value -= temp; + temp /= divisor / 10; + + accumulator += temp << (i * 4); + + divisor *= 10; + } + + return accumulator; +} + +/*---------------------- + RebuildStringTables +----------------------*/ + +static void RebuildStringTables(void) +{ + UINT32 storageNeeded, i; + char * traverse; + + storageNeeded = (menuStrings.mainStringLength + menuStrings.subStringLength) * menuStrings.numStrings; + + menuStrings.mainList = (const char **) realloc((char *) menuStrings.mainList, sizeof(char *) * menuStrings.length); + menuStrings.subList = (const char **) realloc((char *) menuStrings.subList, sizeof(char *) * menuStrings.length); + menuStrings.flagList = realloc( menuStrings.flagList, sizeof(char) * menuStrings.length); + menuStrings.mainStrings = realloc( menuStrings.mainStrings, sizeof(char *) * menuStrings.numStrings); + menuStrings.subStrings = realloc( menuStrings.subStrings, sizeof(char *) * menuStrings.numStrings); + menuStrings.buf = realloc( menuStrings.buf, sizeof(char) * storageNeeded); + + if( (!menuStrings.mainList && menuStrings.length) || + (!menuStrings.subList && menuStrings.length) || + (!menuStrings.flagList && menuStrings.length) || + (!menuStrings.mainStrings && menuStrings.numStrings) || + (!menuStrings.subStrings && menuStrings.numStrings) || + (!menuStrings.buf && storageNeeded)) + { + fatalerror( "cheat: memory allocation error\n" + " length = %.8X\n" + " numStrings = %.8X\n" + " mainStringLength = %.8X\n" + " subStringLength = %.8X\n" + "%p %p %p %p %p %p\n", + menuStrings.length, + menuStrings.numStrings, + menuStrings.mainStringLength, + menuStrings.subStringLength, + + menuStrings.mainList, + menuStrings.subList, + menuStrings.flagList, + menuStrings.mainStrings, + menuStrings.subStrings, + menuStrings.buf); + } + + traverse = menuStrings.buf; + + for(i = 0; i < menuStrings.numStrings; i++) + { + menuStrings.mainStrings[i] = traverse; + traverse += menuStrings.mainStringLength; + + menuStrings.subStrings[i] = traverse; + traverse += menuStrings.subStringLength; + } +} + +/*----------------------------------------------------------- + RequestStrings - check list length and rebuild if needed +-----------------------------------------------------------*/ + +static void RequestStrings(UINT32 length, UINT32 numStrings, UINT32 mainStringLength, UINT32 subStringLength) +{ + UINT8 changed = 0; + + if(menuStrings.length < length) + { + menuStrings.length = length; + + changed = 1; + } + + if(menuStrings.numStrings < numStrings) + { + menuStrings.numStrings = numStrings; + + changed = 1; + } + + if(menuStrings.mainStringLength < mainStringLength) + { + menuStrings.mainStringLength = mainStringLength; + + changed = 1; + } + + if(menuStrings.subStringLength < subStringLength) + { + menuStrings.subStringLength = subStringLength; + + changed = 1; + } + + if(changed) + RebuildStringTables(); +} + +/*------------------ + InitStringTable +------------------*/ + +static void InitStringTable(void) +{ + memset(&menuStrings, 0, sizeof(MenuStringList)); +} + +/*------------------ + FreeStringTable +------------------*/ + +static void FreeStringTable(void) +{ + free((char *)menuStrings.mainList); + free((char *)menuStrings.subList); + free(menuStrings.flagList); + free(menuStrings.mainStrings); + free(menuStrings.subStrings); + free(menuStrings.buf); + + memset(&menuStrings, 0, sizeof(MenuStringList)); +} + +/*--------------------------------------------------------------------------- + CreateStringCopy - copy stirng. It is only called on LoadCheatFile() now +---------------------------------------------------------------------------*/ + +static char * CreateStringCopy(char * buf) +{ + char * temp = NULL; + + if(buf) + { + size_t length = strlen(buf) + 1; + + temp = malloc(length); + + if(temp) + memcpy(temp, buf, length); + } + + return temp; +} + +/*------------------------------------------------------------ + UserSelectValueMenu - management for value-selection menu +------------------------------------------------------------*/ + +static INT32 UserSelectValueMenu(int selection, CheatEntry * entry) +{ + char buf[2048]; + int sel; + CheatAction * action; + static INT32 value = -1; + static int firstTime = 1; + int delta = 0; + int displayValue; + int keyValue; + int forceUpdate = 0; + + sel = selection - 1; + + action = &entry->actionList[0]; + + /* ----- if we're just entering, save the value ----- */ + if(firstTime) + { + UINT32 min = EXTRACT_FIELD(action->type, UserSelectMinimum); + UINT32 max = action->originalDataField + min; + + value = ReadData(action); + + /* ----- and check for valid BCD values ----- */ + if(TEST_FIELD(action->type, UserSelectBCD)) + { + value = BCDToDecimal(value); + value = DecimalToBCD(value); + } + + if(value < min) + value = max; + if(value > max) + value = min; + + action->data = value; + firstTime = 0; + } + + displayValue = value; + + /* ----- if the minimum display value is one, add one to the display value ----- */ + if(TEST_FIELD(action->type, UserSelectMinimumDisplay)) + { + if(TEST_FIELD(action->type, UserSelectBCD)) + displayValue = BCDToDecimal(displayValue); // bcd -> dec + + displayValue++; + + if(TEST_FIELD(action->type, UserSelectBCD)) + displayValue = DecimalToBCD(displayValue); // dec -> bcd + } + + /* ----- print it ----- */ + if(TEST_FIELD(action->type, UserSelectBCD)) + sprintf(buf, "\t%s\n\t%.2X\n", ui_getstring(UI_search_select_value), displayValue); + else + sprintf(buf, "\t%s\n\t%.2X (%d)\n", ui_getstring(UI_search_select_value), displayValue, displayValue); + + /* ----- create fake menu strings ----- */ + strcat(buf, "\t"); + strcat(buf, ui_getstring(UI_lefthilight)); + strcat(buf, " "); + strcat(buf, ui_getstring(UI_OK)); + strcat(buf, " "); + strcat(buf, ui_getstring(UI_righthilight)); + + /* ----- print it ----- */ + ui_draw_message_window(buf); + + /********** KEY HANDLING **********/ + if(UIPressedRepeatThrottle(IPT_UI_LEFT, kHorizontalFastKeyRepeatRate)) + delta = -1; + + if(UIPressedRepeatThrottle(IPT_UI_RIGHT, kHorizontalFastKeyRepeatRate)) + delta = 1; + + if(input_ui_pressed(IPT_UI_SELECT)) + { + /* ### redundant?? probably can be removed ### */ + if(!firstTime) + { + int i; + + /* ----- copy data field if copy previous value ----- */ + for(i = 0; i < entry->actionListLength; i++) + { + CheatAction * traverse = &entry->actionList[i]; + + INT32 copyValue = value; + + if(TEST_FIELD(traverse->type, LinkEnable) && TEST_FIELD(traverse->type, LinkCopyPreviousValue)) + { + INT32 newData = traverse->originalDataField; + + if(TEST_FIELD(traverse->type, UserSelectBCD)) + { + copyValue = BCDToDecimal(value); + copyValue = DecimalToBCD(value); + + newData = BCDToDecimal(newData); + newData = DecimalToBCD(newData); + } + + copyValue += newData; + + if(TEST_FIELD(traverse->type, UserSelectBCD)) + { + copyValue = BCDToDecimal(copyValue); + copyValue = DecimalToBCD(copyValue); + } + } + + if(!i || TEST_FIELD(traverse->type, LinkCopyPreviousValue)) + traverse->data = copyValue; + } + + ActivateCheat(entry); + } + + firstTime = 1; + sel = -1; + } + + if(input_ui_pressed(IPT_UI_CANCEL)) + { + firstTime = 1; + sel = -1; + } + + /* ----- get other key inputs ----- */ + keyValue = ReadHexInput(); + + /* ----- BCD check for input key ----- */ + if((keyValue != -1) && TEST_FIELD(action->type, UserSelectBCD) && ((keyValue < 0 ) || (keyValue > 9))) + keyValue = -1; + + /* ----- if we got a key ----- */ + if(keyValue != -1) + { + /* ----- add it ----- */ + value <<= 4; + + switch(EXTRACT_FIELD(action->type, BytesUsed)) + { + case 0: // 8 bit + value &= 0xF0; + break; + case 1: // 16 bit + value &= 0xFFF0; + break; + case 2: // 24 bit + value &= 0xFFFFF0; + break; + case 3: // 32 bit + value &= 0xFFFFFFF0; + break; + } + + value |= keyValue & 0x0F; + + delta = 0; + forceUpdate = 1; + } + + /* ----- wrap-around with BCD stuff. ### this is a really bad way to do this ----- */ + if(delta || forceUpdate) + { + INT32 min = EXTRACT_FIELD(action->type, UserSelectMinimum); + INT32 max = action->originalDataField + min; + + if(TEST_FIELD(action->type, UserSelectBCD)) + value = BCDToDecimal(value); + + value += delta; + + if(TEST_FIELD(action->type, UserSelectBCD)) + value = DecimalToBCD(value); + + if(value < min) + value = max; + if(value > max) + value = min; + } + + return sel + 1; +} + +/*------------------------------------------------- + CommentMenu - management comment for code menu +-------------------------------------------------*/ + +static INT32 CommentMenu(int selection, CheatEntry * entry) +{ + char buf[2048]; + int sel; + const char * comment; + + if(!entry) + return 0; + + sel = selection - 1; + + /********** MENU CONSTRUCTION **********/ + if(entry->comment && entry->comment[0]) + comment = entry->comment; + else + comment = "(none)"; + + sprintf(buf, "%s\n\t%s %s %s", comment, ui_getstring(UI_lefthilight), ui_getstring(UI_OK), ui_getstring(UI_righthilight)); + + /* ----- print it ----- */ + ui_draw_message_window(buf); + + /********** KEY HANDLING **********/ + if(input_ui_pressed(IPT_UI_SELECT) || input_ui_pressed(IPT_UI_CANCEL)) + sel = -1; + + return sel + 1; +} + +/*-------------------------------------------------------------- + EnableDisableCheatMenu - management for Enable/Disable menu +--------------------------------------------------------------*/ + +static int EnableDisableCheatMenu(int selection, int firstTime) +{ + INT32 sel; + static INT32 submenu_choice = 0; + static INT32 submenu_id = 0; // 0 = none, 1 = comment, 2 = value-selection, 3 = editCheat + const char ** menu_item; + const char ** menu_subitem; + char * flagBuf; + INT32 i; + INT32 total = 0; + + CheatEntry * entry; + + RequestStrings(cheatListLength + 5, 0, 0, 0); + + menu_item = menuStrings.mainList; + menu_subitem = menuStrings.subList; + flagBuf = menuStrings.flagList; + + sel = selection - 1; + + /********** SUB MENU **********/ + if(submenu_choice) + { + switch(submenu_id) + { + case 1: + submenu_choice = CommentMenu(submenu_choice, &cheatList[sel]); + break; + + case 2: + submenu_choice = UserSelectValueMenu(submenu_choice, &cheatList[sel]); + break; + + case 3: + submenu_choice = EditCheatMenu(&cheatList[sel], sel, submenu_choice); + break; + + default: + submenu_choice = 0; + } + + /* ----- meaningless ? because no longer return with sel = -1 (pressed UI_CONFIG in submenu) ----- */ +// if(submenu_choice == -1) +// { +// submenu_choice = 0; +// sel = -2; +// } + + return sel + 1; + } + + /********** MENU CONSTRUCTION **********/ + for(i = 0; i < cheatListLength; i++) + { + CheatEntry * traverse = &cheatList[i]; + + /* ----- get code title ----- */ + if(traverse->name) + menu_item[total] = traverse->name; + else + menu_item[total] = "null name"; + + menu_subitem[total] = NULL; + + /* ----- get subitem ----- */ + if(traverse->flags & kCheatFlag_Select) // label-selection gets sub-lebel name or "OFF" + { + /* ----- no "OFF" item if one shot ----- */ + if((traverse->flags & kCheatFlag_OneShot) && !traverse->selection) + traverse->selection = 1; // adjust to 1st linked-code + + /* ----- set sub-label name or "OFF" to subitem ----- */ + if(traverse->selection && (traverse->selection < traverse->actionListLength)) + menu_subitem[total] = traverse->actionList[traverse->selection].optionalName; + else + menu_subitem[total] = ui_getstring(UI_off); + } + else // others get "ON" or "OFF" + { + /* ----- add subitems for all cheats that are not comments ----- */ + if(!(traverse->flags & kCheatFlag_Null)) + { + if(traverse->flags & kCheatFlag_OneShot) + menu_subitem[total] = ui_getstring(UI_set); + else + { + if(traverse->flags & kCheatFlag_Active) + menu_subitem[total] = ui_getstring(UI_on); + else + menu_subitem[total] = ui_getstring(UI_off); + } + } + } + + /* ----- get comment flag ----- */ + if(traverse->comment && traverse->comment[0]) + flagBuf[total++] = 1; + else + flagBuf[total++] = 0; + } + + /* ----- if no code, set special message ----- */ + if(cheatListLength == 0) + { + if(foundCheatDatabase) // the database found but no code + { + menu_item[total] = "there are no cheats for this game"; + menu_subitem[total] = NULL; + flagBuf[total++] = 0; + } + else // the database itself not found + { + menu_item[total] = "cheat database not found"; + menu_subitem[total] = NULL; + flagBuf[total++] = 0; + + menu_item[total] = "unzip it and place it in the MAME directory"; + menu_subitem[total] = NULL; + flagBuf[total++] = 0; + } + } + + /* ----- set return item ----- */ + menu_item[total] = ui_getstring(UI_returntoprior); + menu_subitem[total] = NULL; + flagBuf[total++] = 0; + + menu_item[total] = 0; // terminate array + menu_subitem[total] = 0; + flagBuf[total] = 0; + + /* ----- adjust cursor position ----- */ + if(sel < 0) + sel = 0; + if(sel > (total - 1)) + sel = total - 1; + + /* ----- if cursor is on comment code, skip it ----- */ + if(cheatListLength && firstTime) + { + while((sel < total - 1) && (cheatList[sel].flags & kCheatFlag_Null)) + sel++; + } + + /* ----- draw it ----- */ + old_style_menu(menu_item, menu_subitem, flagBuf, sel, 0); + + /********** KEY HANDLING **********/ + if(UIPressedRepeatThrottle(IPT_UI_DOWN, kVerticalKeyRepeatRate)) + { + sel++; + + if(sel >= total) + sel = 0; + + if(cheatListLength) + { + /* ----- if cursor is on comment code, skip it ----- */ + for(i = 0; (i < fullMenuPageHeight / 2) && (sel < total - 1) && (cheatList[sel].flags & kCheatFlag_Null); i++) + sel++; + } + } + + if(UIPressedRepeatThrottle(IPT_UI_UP, kVerticalKeyRepeatRate)) + { + sel--; + + if(sel < 0) + sel = total - 1; + else + { + if(cheatListLength) + { + /* ----- if cursor is on comment code, skip it ----- */ + for(i = 0; (i < fullMenuPageHeight / 2) && (sel != total - 1) && (cheatList[sel].flags & kCheatFlag_Null); i++) + { + sel--; + + if(sel < 0) + sel = total - 1; + } + } + } + } + + if(UIPressedRepeatThrottle(IPT_UI_PAGE_UP, kVerticalKeyRepeatRate)) + { + sel -= fullMenuPageHeight; + + if(sel < 0) + sel = 0; + } + + if(UIPressedRepeatThrottle(IPT_UI_PAGE_DOWN, kVerticalKeyRepeatRate)) + { + sel += fullMenuPageHeight; + + if(sel >= total) + sel = total - 1; + } + + if((sel >= 0) && (sel < cheatListLength)) + entry = &cheatList[sel]; + else + entry = NULL; + + if(UIPressedRepeatThrottle(IPT_UI_LEFT, kHorizontalSlowKeyRepeatRate)) + { + if((sel < (total - 1)) && entry) + { + if(entry->flags & kCheatFlag_Select) // label-selection + { + /* ----- move selection to unLinkExtension code or last linked-code ----- */ + do{ + entry->selection--; + + if(entry->flags & kCheatFlag_OneShot) + { + if(entry->selection <= 0) + entry->selection = entry->actionListLength - 1; // first -> last + + /* ----- no action when press arrow key ----- */ + entry->flags &= ~kCheatFlag_DoOneShot; + } + else + { + if(entry->selection < 0) + entry->selection = entry->actionListLength - 1; // OFF -> last + + if(entry->selection == 0) + DeactivateCheat(entry); // OFF + else + { + if(!TEST_FIELD(entry->actionList[entry->selection].type, LinkExtension)) + ActivateCheat(entry); // ON + } + } + }while(entry->selection && TEST_FIELD(entry->actionList[entry->selection].type, LinkExtension)); + } + else // others + { + if(!(entry->flags & kCheatFlag_Null) && !(entry->flags & kCheatFlag_OneShot)) + { + int active = entry->flags & kCheatFlag_Active; + + active ^= 0x01; + + /* ----- get the user's selected value if needed ----- */ + if((entry->flags & kCheatFlag_UserSelect) && active) + { + submenu_id = 2; + submenu_choice = 1; + } + else + { + if(active) + ActivateCheat(entry); // ON + else + DeactivateCheat(entry); // OFF + } + } + } + } + } + + if(UIPressedRepeatThrottle(IPT_UI_RIGHT, kHorizontalSlowKeyRepeatRate)) + { + if((sel < (total - 1)) && entry) + { + if(entry->flags & kCheatFlag_Select) // label-selection + { + /* ----- move selection to unLinkExtension code or 1st code ----- */ + do{ + entry->selection++; + + if(entry->flags & kCheatFlag_OneShot) + { + if(entry->selection >= entry->actionListLength) + { + entry->selection = 1; // last -> first + + if(entry->selection >= entry->actionListLength) + entry->selection = 0; + } + /* ----- no action when press arrow key ----- */ + entry->flags &= ~kCheatFlag_DoOneShot; + } + else // others + { + if(entry->selection >= entry->actionListLength) + { + entry->selection = 0; // last -> OFF + + DeactivateCheat(entry); // OFF + } + else + { + if(!TEST_FIELD(entry->actionList[entry->selection].type, LinkExtension)) + ActivateCheat(entry); // ON + } + } + }while(entry->selection && TEST_FIELD(entry->actionList[entry->selection].type, LinkExtension)); + } + else + { + if(!(entry->flags & kCheatFlag_Null) && !(entry->flags & kCheatFlag_OneShot)) + { + int active = entry->flags & kCheatFlag_Active; + + active ^= 0x01; // toggle On and OFF + + /* ----- get the user's selected value if needed ----- */ + if((entry->flags & kCheatFlag_UserSelect) && active) + { + submenu_id = 2; + submenu_choice = 1; + } + else + { + if(active) + ActivateCheat(entry); // ON + else + DeactivateCheat(entry); // OFF + } + } + } + } + } + + if(input_ui_pressed(IPT_UI_SELECT)) + { + if(sel == (total - 1)) + { + /* ----- return to prior menu ----- */ + submenu_choice = 0; + sel = -1; + } + else + { + if((sel < (total - 1)) && entry) + { + if(ShiftKeyPressed()) + { + if(entry->comment && entry->comment[0]) + { + /* ----- display comment ------ */ + submenu_id = 1; + submenu_choice = 1; + } + else + goto doActivate; + } + else + { + doActivate: + + if(entry->flags & kCheatFlag_UserSelect) + { + /* ----- value-selection menu ----- */ + submenu_id = 2; + submenu_choice = 1; + } + else + { + ActivateCheat(entry); // ON + + /* ----- set do one shot flag if label-selection ----- */ + if((entry->flags & kCheatFlag_Select) && (entry->flags & kCheatFlag_OneShot)) + entry->flags |= kCheatFlag_DoOneShot; + + /* ----- display one shot message if one shot ----- */ + if(entry->flags & kCheatFlag_OneShot) + ui_popup_time(1, "%s activated", entry->name); + } + } + } + } + } + + if(input_ui_pressed(IPT_UI_WATCH_VALUE)) + WatchCheatEntry(entry, 0); + + if(ShiftKeyPressed()) + { + if(input_ui_pressed(IPT_UI_SAVE_CHEAT)) // shift + save = save all codes + { + for(i = 0; i < cheatListLength; i++) + SaveCheat(&cheatList[i], 0, 0); + + ui_popup_time(1, "%d cheats saved", cheatListLength); + } + + if(input_ui_pressed(IPT_UI_ADD_CHEAT)) // shift + add = add new emypty code + AddCheatBefore(sel); + + if(input_ui_pressed(IPT_UI_DELETE_CHEAT)) // shift + delete = delete selected code + DeleteCheatAt(sel); + } + else + { + if(ControlKeyPressed()) + { + if(input_ui_pressed(IPT_UI_SAVE_CHEAT)) // ctrl + save = save activation key + { + if((entry->flags & kCheatFlag_HasActivationKey1) || (entry->flags & kCheatFlag_HasActivationKey2)) + { + SaveCheat(entry, sel, 1); + + ui_popup_time(1, "activation key saved"); + } + else + ui_popup_time(1, "no activation key"); + } + } + else + { + if(input_ui_pressed(IPT_UI_SAVE_CHEAT)) + SaveCheat(entry, 0, 0); // save current entry + } + } + + if(input_ui_pressed(IPT_UI_EDIT_CHEAT) && entry) // edit selected code + { + submenu_id = 3; + submenu_choice = 1; + } + + if(input_ui_pressed(IPT_UI_RELOAD_CHEAT)) + ReloadCheatDatabase(); + + if(UIPressedRepeatThrottle(IPT_UI_ZOOM_IN, kVerticalKeyRepeatRate)) + /* ----- quick menu switch : enable/disable -> add/edit ----- */ + sel = -3; + + if(UIPressedRepeatThrottle(IPT_UI_ZOOM_OUT, kVerticalKeyRepeatRate)) + /* ----- quick menu switch : enable/disable -> watchpoint list ----- */ + sel = -5; + + if(input_ui_pressed(IPT_UI_CANCEL)) + sel = -1; + + return sel + 1; +} + +/*-------------------------------------------------------- + AddEditCheatMenu - management for Add/Edit cheat menu +--------------------------------------------------------*/ + +static int AddEditCheatMenu(int selection) +{ + INT32 sel; + static INT32 submenuChoice = 0; + static INT32 submenuCheat = 0; + const char ** menu_item; + INT32 i; + INT32 total = 0; + CheatEntry * entry; + + sel = selection - 1; + + RequestStrings(cheatListLength + 2, 0, 0, 0); + + menu_item = menuStrings.mainList; + + /********** SUB MENU **********/ + if(submenuChoice) + { + submenuChoice = EditCheatMenu(&cheatList[submenuCheat], submenuCheat, submenuChoice); + + /* ----- meaningless ? because no longer return with sel = -1 (pressed UI_CONFIG in submenu) ----- */ +// if(submenuChoice == -1) +// { +// submenuChoice = 0; +// sel = -2; +// } + + return sel + 1; + } + + /********** MENU CONSTRUCTION **********/ + for(i = 0; i < cheatListLength; i++) + { + CheatEntry * traverse = &cheatList[i]; + + if(traverse->name) + menu_item[total++] = traverse->name; // code title + else + menu_item[total++] = "(none)"; + } + + menu_item[total++] = ui_getstring(UI_returntoprior); // return + + menu_item[total] = NULL; // terminate array + + /* ----- adjust current cursor position ----- */ + if(sel < 0) + sel = 0; + if(sel >= total) + sel = total - 1; + + /* ----- draw it ----- */ + old_style_menu(menu_item, NULL, NULL, sel, 0); + + /********** KEY HANDLING **********/ + if(UIPressedRepeatThrottle(IPT_UI_DOWN, kVerticalKeyRepeatRate)) + { + sel++; + + if(sel >= total) + sel = 0; + } + + if(UIPressedRepeatThrottle(IPT_UI_UP, kVerticalKeyRepeatRate)) + { + sel--; + + if(sel < 0) + sel = total - 1; + } + + if(UIPressedRepeatThrottle(IPT_UI_PAGE_UP, kVerticalKeyRepeatRate)) + { + sel -= fullMenuPageHeight; + + if(sel < 0) + sel = 0; + } + + if(UIPressedRepeatThrottle(IPT_UI_PAGE_DOWN, kVerticalKeyRepeatRate)) + { + sel += fullMenuPageHeight; + + if(sel >= total) + sel = total - 1; + } + + if(sel < (total - 1)) + entry = &cheatList[sel]; + else + entry = NULL; + + if(input_ui_pressed(IPT_UI_SAVE_CHEAT)) + { + if(ShiftKeyPressed()) + { + for(i = 0; i < cheatListLength; i++) // shift + save = save all codes + SaveCheat(&cheatList[i], 0, 0); + + ui_popup_time(1, "%d cheats saved", cheatListLength); + } + else + { + if(ControlKeyPressed()) + { + if((entry->flags & kCheatFlag_HasActivationKey1) || (entry->flags & kCheatFlag_HasActivationKey2)) + { + SaveCheat(entry, sel, 1); // ctrl + save = save activation key + + ui_popup_time(1, "activation key saved"); + } + else + ui_popup_time(1, "no activation key"); + } + else + SaveCheat(entry, 0, 0); // save current entry + } + } + + if(input_ui_pressed(IPT_UI_ADD_CHEAT)) + AddCheatBefore(sel); // insert empty code + + if(input_ui_pressed(IPT_UI_DELETE_CHEAT)) + DeleteCheatAt(sel); // delete selected code + + if(input_ui_pressed(IPT_UI_WATCH_VALUE)) + WatchCheatEntry(entry, 0); // watch selected code + + if(input_ui_pressed(IPT_UI_EDIT_CHEAT)) // edit selected code + { + /* ----- if selected item is not return, go to edit menu ----- */ + if(sel < (total - 1)) + { + submenuCheat = sel; + submenuChoice = 1; + } + } + + if(input_ui_pressed(IPT_UI_SELECT)) + { + /* ----- if selected item is return, return previous menu ----- */ + if(sel >= (total - 1)) + { + submenuChoice = 0; + sel = -1; + } + /* ----- otherwise, go to edit menu (same as pressing edit key) ----- */ + else + { + submenuCheat = sel; + submenuChoice = 1; + } + } + + if(input_ui_pressed(IPT_UI_RELOAD_CHEAT)) + ReloadCheatDatabase(); + + if(UIPressedRepeatThrottle(IPT_UI_ZOOM_IN, kVerticalKeyRepeatRate)) + /* ----- quick menu switch : add/edit -> search ----- */ + sel = -4; + + if(UIPressedRepeatThrottle(IPT_UI_ZOOM_OUT, kVerticalKeyRepeatRate)) + /* ----- quick menu switch : add/edit -> enable/disable ----- */ + sel = -2; + + if(input_ui_pressed(IPT_UI_CANCEL)) + sel = -1; + + return sel + 1; +} + +/*------------------------------------------------ + EditCheatMenu - management for edit code menu +------------------------------------------------*/ + +static int EditCheatMenu(CheatEntry * entry, int index, int selection) +{ + static const char * kTypeNames[] = + { + "Normal/Delay", + "Wait", + "Ignore Decrement", + "Watch", + "Comment", + "Select" + }; + + static const char * kNumbersTable[] = + { + "0", "1", "2", "3", "4", "5", "6", "7", + "8", "9", "10", "11", "12", "13", "14", "15", + "16", "17", "18", "19", "20", "21", "22", "23", + "24", "25", "26", "27", "28", "29", "30", "31" + }; + + static const char * kOperationNames[] = + { + "Write", + "Add/Subtract", + "Force Range", + "Set/Clear Bits", + "Write If Match", + "Unused (5)", + "Unused (6)", + "Null" + }; + + static const char * kAddSubtractNames[] = + { + "Add", + "Subtract" + }; + + static const char * kSetClearNames[] = + { + "Set", + "Clear" + }; + + static const char * kPrefillNames[] = + { + "None", + "FF", + "00", + "01" + }; + + static const char * kEndiannessNames[] = + { + "Normal", + "Swap" + }; + + static const char * kRegionNames[] = + { + "CPU1", "CPU2", "CPU3", "CPU4", "CPU5", "CPU6", "CPU7", "CPU8", + "GFX1", "GFX2", "GFX3", "GFX4", "GFX5", "GFX6", "GFX7", "GFX8", + "PROMS", + "SOUND1", "SOUND2", "SOUND3", "SOUND4", "SOUND5", "SOUND6", "SOUND7", "SOUND8", + "USER1", "USER2", "USER3", "USER4", "USER5", "USER6", "USER7", "USER8" + }; + + static const char * kLocationNames[] = + { + "Normal", + "Region", + "Mapped Memory", + "Custom", + "Relative Address", + "Program Space", + "Unused (6)", + "Unused (7)" + }; + + static const char * kCustomLocationNames[] = + { + "Comment", + "EEPROM", + "Select", + "Unused (3)", "Unused (4)", "Unused (5)", "Unused (6)", "Unused (7)", + "Unused (8)", "Unused (9)", "Unused (10)", "Unused (11)", "Unused (12)", + "Unused (13)", "Unused (14)", "Unused (15)", "Unused (16)", "Unused (17)", + "Unused (18)", "Unused (19)", "Unused (20)", "Unused (21)", "Unused (22)", + "Unused (23)", "Unused (24)", "Unused (25)", "Unused (26)", "Unused (27)", + "Unused (28)", "Unused (29)", "Unused (30)", "Unused (31)" + }; + + static const char * kSizeNames[] = + { + "8 Bit", + "16 Bit", + "24 Bit", + "32 Bit" + }; + + enum + { + kType_Name = 0, // text name + // NOTE: read from base cheat (for idx == 0) + kType_ExtendName, // text extraName + // NOTE: read from subcheat for (idx > 0) && (cheat[0].type == Select) + kType_Comment, // text comment + // NOTE: read from base cheat (for idx == 0) + kType_ActivationKey1, // key 1st activationKey + // NOTE: read from base cheat (for idx == 0) + kType_ActivationKey2, // key 2nd activationKey + // NOTE: read from base cheat (for idx == 0) + // if(idx > 0 && (cheat[0].type == Select)) + kType_LinkExtension, // select LinkExtension Off - On + kType_Type, // select Type Normal/Delay - Wait - Ignore Decrement - Watch - + // Comment - Select + // NOTE: also uses location type field for comment and select + // if((Type != Comment) && (Type != Select)) + // if(Type != Watch) + kType_OneShot, // select OneShot Off - On + kType_RestorePreviousValue, + // select RestorePreviousValue + // Off - On + // if((Type == Normal/Delay) || (Type == Wait)) + kType_Delay, // value TypeParameter 0 - 7 + // if(Type == Ignore Decrement) + kType_IgnoreDecrementBy,// value TypeParameter 0 - 7 + // if(Type == Watch) + kType_WatchSize, // value Data 0x01 - 0xFF (stored as 0x00 - 0xFE) + // NOTE: value is packed in to 0x000000FF + kType_WatchSkip, // value Data 0x00 - 0xFF + // NOTE: value is packed in to 0x0000FF00 + kType_WatchPerLine, // value Data 0x00 - 0xFF + // NOTE: value is packed in to 0x00FF0000 + kType_WatchAddValue, // value Data -0x80 - 0x7F + // NOTE: value is packed in to 0xFF000000 + kType_WatchFormat, // select TypeParameter Hex - Decimal - Binary - ASCII + // NOTE: value is packed in to 0x03 + kType_WatchLabel, // select TypeParameter Off - On + // NOTE: value is packed in to 0x04 + // and set operation to null + // else + kType_Operation, // select Operation Write - Add/Subtract - Force Range - Set/Clear Bits - Match to Write - + // Null + // if((Operation == Write) && (LocationType != Relative Address)) + kType_WriteMask, // value extendData 0x00000000 - 0xFFFFFFFF + // if(Operation == Add/Subtract) + kType_AddSubtract, // select OperationParameter Add - Subtract + // if(LocationType != Relative Address) + // if(OperationParameter == Add) + kType_AddMaximum, + // value extendData 0x00000000 - 0xFFFFFFFF + // else + kType_SubtractMinimum, + // value extendData 0x00000000 - 0xFFFFFFFF + // if((Operation == Force Range) && (LocationType != Relative Address)) + kType_RangeMinimum, // value extendData 0x00 - 0xFF / 0xFFFF + // NOTE: value is packed in to upper byte of extendData (as a word) + kType_RangeMaximum, // value extendData 0x00 - 0xFF / 0xFFFF + // NOTE: value is packed in to lower byte of extendData (as a word) + // if(Operation == Set/Clear) + kType_SetClear, // select OperationParameter Set - Clear + // if(Operation == Match to Write) + kType_WriteMatch, // value extendData 0x00000000 - 0xFFFFFFFF + // if((Operation != Null) || (Type == Watch)) + // if(Type != Watch) + kType_Data, + kType_UserSelect, // select UserSelectEnable Off - On + // if(UserSelect == On) + kType_UserSelectMinimumDisp, + // value UserSelectMinimumDisplay + // 0 - 1 + kType_UserSelectMinimum, + // value UserSelectMinimum 0 - 1 + kType_UserSelectBCD,// select UserSelectBCD Off - On + kType_Prefill, // select UserSelectPrefill None - FF - 00 - 01 + // if(idx > 0) + kType_CopyPrevious, // select LinkCopyPreviousValue + // Off - On + kType_ByteLength, // value BytesUsed 1 - 4 + // if(bytesUsed > 0) + kType_Endianness, // select Endianness Normal - Swap + kType_LocationType, // select LocationType Normal - Region - Mapped Memory - EEPROM - + // Relative Address - Program Space + // NOTE: also uses LocationParameter for EEPROM type + // if((LocationType == Normal) || (LocationType == HandlerMemory) || (LocationType == ProgramSpace) + kType_CPU, // value LocationParameter 0 - 31 + // if(LocationType == Region) + kType_Region, // select LocationParameter CPU1 - CPU2 - CPU3 - CPU4 - CPU5 - CPU6 - CPU7 - + // CPU8 - GFX1 - GFX2 - GFX3 - GFX4 - GFX5 - GFX6 - + // GFX7 - GFX8 - PROMS - SOUND1 - SOUND2 - SOUND3 - + // SOUND4 - SOUND5 - SOUND6 - SOUND7 - SOUND8 - + // USER1 - USER2 - USER3 - USER4 - USER5 - USER6 - + // USER7 + // if(LocationType == RelativeAddress) + kType_PackedCPU, // value LocationParameter 0 - 7 + // NOTE: packed in to upper three bits of LocationParameter + kType_PackedSize, // value LocationParameter 1 - 4 + // NOTE: packed in to lower two bits of LocationParameter + kType_AddressIndex, // value extendData -0x80000000 - 0x7FFFFFFF + kType_Address, + + kType_Return, + kType_Divider, + + kType_Max + }; + + INT32 sel; + const char ** menuItem; + const char ** menuSubItem; + char * flagBuf; + char ** extendDataBuf; // FFFFFFFF (-80000000) + char ** addressBuf; // FFFFFFFF + char ** dataBuf; // 80000000 (-2147483648) + char ** watchSizeBuf; // FF + char ** watchSkipBuf; // FF + char ** watchPerLineBuf; // FF + char ** watchAddValueBuf; // FF + INT32 i; + INT32 total = 0; + MenuItemInfoStruct * info = NULL; + CheatAction * action = NULL; + UINT8 isSelect = 0; + static UINT8 editActive = 0; + UINT32 increment = 1; + UINT8 dirty = 0; + static INT32 currentNameTemplate = 0; + + astring * temp_string_1 = astring_alloc(); + astring * temp_string_2 = astring_alloc(); + + if(!entry) + return 0; + + /********** MENU CONSTRUCTION **********/ + if(menuItemInfoLength < (kType_Max * entry->actionListLength) + 2) + { + menuItemInfoLength = (kType_Max * entry->actionListLength) + 2; + + menuItemInfo = realloc(menuItemInfo, menuItemInfoLength * sizeof(MenuItemInfoStruct)); + } + + RequestStrings((kType_Max * entry->actionListLength) + 2, 7 * entry->actionListLength, 24, 0); + + menuItem = menuStrings.mainList; + menuSubItem = menuStrings.subList; + flagBuf = menuStrings.flagList; + extendDataBuf = &menuStrings.mainStrings[entry->actionListLength * 0]; + addressBuf = &menuStrings.mainStrings[entry->actionListLength * 1]; + dataBuf = &menuStrings.mainStrings[entry->actionListLength * 2]; + watchSizeBuf = &menuStrings.mainStrings[entry->actionListLength * 3]; // these fields are wasteful + watchSkipBuf = &menuStrings.mainStrings[entry->actionListLength * 4]; // but the alternative is even more ugly + watchPerLineBuf = &menuStrings.mainStrings[entry->actionListLength * 5]; + watchAddValueBuf = &menuStrings.mainStrings[entry->actionListLength * 6]; + + sel = selection - 1; + + memset(flagBuf, 0, (kType_Max * entry->actionListLength) + 2); + + /* ----- create menu items ----- */ + for(i = 0; i < entry->actionListLength; i++) + { + CheatAction * traverse = &entry->actionList[i]; + + UINT32 type = EXTRACT_FIELD(traverse->type, Type); + UINT32 typeParameter = EXTRACT_FIELD(traverse->type, TypeParameter); + UINT32 operation = EXTRACT_FIELD(traverse->type, Operation) | + EXTRACT_FIELD(traverse->type, OperationExtend) << 2; + UINT32 operationParameter = EXTRACT_FIELD(traverse->type, OperationParameter); + UINT32 locationType = EXTRACT_FIELD(traverse->type, LocationType); + UINT32 locationParameter = EXTRACT_FIELD(traverse->type, LocationParameter); + + UINT8 wasCommentOrSelect = 0; + + if(isSelect) + { + /* ----- do extend name field ----- */ + menuItemInfo[total].subcheat = i; + menuItemInfo[total].fieldType = kType_ExtendName; + menuItem[total] = "Name"; + + if(traverse->optionalName) + menuSubItem[total++] = traverse->optionalName; + else + menuSubItem[total++] = "(none)"; + } + + /* ----- create items if 1st code ----- */ + if(i == 0) + { + { + /* ----- do name field ----- */ + menuItemInfo[total].subcheat = i; + menuItemInfo[total].fieldType = kType_Name; + menuItem[total] = "Name"; + + if(entry->name) + menuSubItem[total++] = entry->name; + else + menuSubItem[total++] = "(none)"; + } + + { + /* ----- do comment field ----- */ + menuItemInfo[total].subcheat = i; + menuItemInfo[total].fieldType = kType_Comment; + menuItem[total] = "Comment"; + + if(entry->comment) + menuSubItem[total++] = entry->comment; + else + menuSubItem[total++] = "(none)"; + } + + { + /* ----- do 1st activation key field ----- */ + menuItemInfo[total].subcheat = i; + menuItemInfo[total].fieldType = kType_ActivationKey1; + menuItem[total] = "Activation Key (1st)"; + + if((entry->flags & kCheatFlag_HasActivationKey1)) + menuSubItem[total++] = astring_c(input_code_name(temp_string_1, entry->activationKey1)); + else + menuSubItem[total++] = "(none)"; + } + + { + /* ----- do 2nd activation key field ----- */ + menuItemInfo[total].subcheat = i; + menuItemInfo[total].fieldType = kType_ActivationKey2; + menuItem[total] = "Activation Key (2nd)"; + + if((entry->flags & kCheatFlag_HasActivationKey2)) + menuSubItem[total++] = astring_c(input_code_name(temp_string_2, entry->activationKey2)); + else + menuSubItem[total++] = "(none)"; + } + + /* ----- is this label-selection cheat ? ----- */ + if((locationType == kLocation_Custom) && (locationParameter == kCustomLocation_Select)) + isSelect = 1; + } + + /* ----- create item if linked code on label-selection ----- */ + if(i && isSelect) + { + /* ----- do link extension field ----- */ + menuItemInfo[total].subcheat = i; + menuItemInfo[total].fieldType = kType_LinkExtension; + menuItem[total] = "Link Extension"; + menuSubItem[total++] = ui_getstring(TEST_FIELD(traverse->type, LinkExtension) ? UI_on : UI_off); + } + + { + /* ----- do type field ----- */ + menuItemInfo[total].subcheat = i; + menuItemInfo[total].fieldType = kType_Type; + menuItem[total] = "Type"; + + if(locationType == kLocation_Custom) + { + if(locationParameter == kCustomLocation_Comment) + { + /* ----- comment ----- */ + wasCommentOrSelect = 1; + + menuSubItem[total++] = kTypeNames[4]; + } + else + { + if(locationParameter == kCustomLocation_Select) + { + /* ----- label-selection ----- */ + wasCommentOrSelect = 1; + + menuSubItem[total++] = kTypeNames[5]; + } + else + menuSubItem[total++] = kTypeNames[type & 3]; + } + } + else + menuSubItem[total++] = kTypeNames[type & 3]; + } + + /* ----- create items if not comment or label-selection ----- */ + if(!wasCommentOrSelect) + { + if(type != kType_Watch) + { + { + /* ----- do one shot field ----- */ + menuItemInfo[total].subcheat = i; + menuItemInfo[total].fieldType = kType_OneShot; + menuItem[total] = "One Shot"; + menuSubItem[total++] = ui_getstring(TEST_FIELD(traverse->type, OneShot) ? UI_on : UI_off); + } + + { + /* ----- do restore previous value field ----- */ + menuItemInfo[total].subcheat = i; + menuItemInfo[total].fieldType = kType_RestorePreviousValue; + menuItem[total] = "Restore Previous Value"; + menuSubItem[total++] = ui_getstring(TEST_FIELD(traverse->type, RestorePreviousValue) ? UI_on : UI_off); + } + } + + if((type == kType_NormalOrDelay) || (type == kType_WaitForModification)) + { + /* ----- do delay field ----- */ + menuItemInfo[total].subcheat = i; + menuItemInfo[total].fieldType = kType_Delay; + + if( TEST_FIELD(traverse->type, OneShot) && TEST_FIELD(traverse->type, RestorePreviousValue) && + EXTRACT_FIELD(traverse->type, TypeParameter)) + menuItem[total] = "Keep"; + else + menuItem[total] = "Delay"; + + menuSubItem[total++] = kNumbersTable[typeParameter]; + } + + if(type == kType_IgnoreIfDecrementing) + { + /* ----- do ignore decrement by field ----- */ + menuItemInfo[total].subcheat = i; + menuItemInfo[total].fieldType = kType_IgnoreDecrementBy; + menuItem[total] = "Ignore Decrement By"; + menuSubItem[total++] = kNumbersTable[typeParameter]; + } + + if(type == kType_Watch) + { + { + /* ----- do watch size field ----- */ + sprintf(watchSizeBuf[i], "%d", (traverse->originalDataField & 0xFF) + 1); + + menuItemInfo[total].subcheat = i; + menuItemInfo[total].fieldType = kType_WatchSize; + menuItem[total] = "Watch Size"; + menuSubItem[total++] = watchSizeBuf[i]; + } + + { + /* ----- do watch skip field ----- */ + sprintf(watchSkipBuf[i], "%d", (traverse->data >> 8) & 0xFF); + + menuItemInfo[total].subcheat = i; + menuItemInfo[total].fieldType = kType_WatchSkip; + menuItem[total] = "Watch Skip"; + menuSubItem[total++] = watchSkipBuf[i]; + } + + { + /* ----- do watch per line field ----- */ + sprintf(watchPerLineBuf[i], "%d", (traverse->data >> 16) & 0xFF); + + menuItemInfo[total].subcheat = i; + menuItemInfo[total].fieldType = kType_WatchPerLine; + menuItem[total] = "Watch Per Line"; + menuSubItem[total++] = watchPerLineBuf[i]; + } + + { + /* ----- do watch add value field ----- */ + { + INT8 temp = (traverse->data >> 24) & 0xFF; + + if(temp < 0) + sprintf(watchAddValueBuf[i], "-%.2X", -temp); + else + sprintf(watchAddValueBuf[i], "%.2X", temp); + } + + menuItemInfo[total].subcheat = i; + menuItemInfo[total].fieldType = kType_WatchAddValue; + menuItem[total] = "Watch Add Value"; + menuSubItem[total++] = watchAddValueBuf[i]; + } + + { + /* ----- do watch format field ----- */ + menuItemInfo[total].subcheat = i; + menuItemInfo[total].fieldType = kType_WatchFormat; + menuItem[total] = "Watch Format"; + menuSubItem[total++] = kWatchDisplayTypeStringList[(typeParameter >> 0) & 0x03]; + } + + { + /* ----- do watch label field ----- */ + menuItemInfo[total].subcheat = i; + menuItemInfo[total].fieldType = kType_WatchLabel; + menuItem[total] = "Watch Label"; + menuSubItem[total++] = ui_getstring(((typeParameter >> 2) & 0x01) ? UI_on : UI_off); + } + } + else + { + /* ----- do operation field ----- */ + menuItemInfo[total].subcheat = i; + menuItemInfo[total].fieldType = kType_Operation; + menuItem[total] = "Operation"; + + if((locationType != kLocation_IndirectIndexed) && (operation == kOperation_SetOrClearBits)) + { + /* ----- ignore Set/Clear Bit item if not relative address ----- */ + operation = kOperation_WriteMask; + + CLEAR_MASK_FIELD(traverse->type, OperationExtend); + SET_FIELD(traverse->type, Operation, operation); + } + + menuSubItem[total++] = kOperationNames[operation]; + } + + if((operation == kOperation_WriteMask) && (locationType != kLocation_IndirectIndexed)) + { + /* ----- do mask field ----- */ + int numChars; + + if(traverse->flags & kActionFlag_IgnoreMask) + { + menuItemInfo[total].extraData = 0xFFFFFFFF; + numChars = 8; + } + else + { + menuItemInfo[total].extraData = kCheatSizeMaskTable[EXTRACT_FIELD(traverse->type, BytesUsed)]; + numChars = kCheatSizeDigitsTable[EXTRACT_FIELD(traverse->type, BytesUsed)]; + } + + sprintf(extendDataBuf[i], "%.*X", numChars, traverse->extendData); + + menuItemInfo[total].subcheat = i; + menuItemInfo[total].fieldType = kType_WriteMask; + menuItem[total] = "Mask"; + menuSubItem[total++] = extendDataBuf[i]; + } + + if(operation == kOperation_AddSubtract) + { + { + /* ----- do add/subtract field ----- */ + menuItemInfo[total].subcheat = i; + menuItemInfo[total].fieldType = kType_AddSubtract; + menuItem[total] = "Add/Subtract"; + menuSubItem[total++] = kAddSubtractNames[operationParameter]; + } + + if(locationType != kLocation_IndirectIndexed) + { + if(operationParameter) + { + /* ----- do subtract minimum field ----- */ + sprintf(extendDataBuf[i], "%.8X", traverse->extendData); + + menuItemInfo[total].subcheat = i; + menuItemInfo[total].fieldType = kType_SubtractMinimum; + menuItem[total] = "Minimum Boundary"; + menuSubItem[total++] = extendDataBuf[i]; + } + else + { + /* ----- do add maximum field ----- */ + sprintf(extendDataBuf[i], "%.8X", traverse->extendData); + + menuItemInfo[total].subcheat = i; + menuItemInfo[total].fieldType = kType_AddMaximum; + menuItem[total] = "Maximum Boundary"; + menuSubItem[total++] = extendDataBuf[i]; + } + } + } + + if((operation == kOperation_ForceRange) && (locationType != kLocation_IndirectIndexed)) + { + /* ----- do range minimum field ----- */ + if(!EXTRACT_FIELD(traverse->type, BytesUsed)) + sprintf(extendDataBuf[i], "%.2X", (traverse->extendData >> 8) & 0xFF); + else + sprintf(extendDataBuf[i], "%.4X", (traverse->extendData >> 16) & 0xFFFF); + + menuItemInfo[total].subcheat = i; + menuItemInfo[total].fieldType = kType_RangeMinimum; + menuItem[total] = "Range Minimum"; + menuSubItem[total++] = extendDataBuf[i]; + + /* ----- do range maximum field ----- */ + if(!EXTRACT_FIELD(traverse->type, BytesUsed)) + { + sprintf(extendDataBuf[i] + 3, "%.2X", (traverse->extendData >> 0) & 0xFF); + menuSubItem[total] = extendDataBuf[i] + 3; + } + else + { + sprintf(extendDataBuf[i] + 7, "%.4X", (traverse->extendData >> 0) & 0xFFFF); + menuSubItem[total] = extendDataBuf[i] + 7; + } + + menuItemInfo[total].subcheat = i; + menuItemInfo[total].fieldType = kType_RangeMaximum; + menuItem[total++] = "Range Maximum"; + } + + if(operation == kOperation_SetOrClearBits) + { + /* ----- do set/clear field ----- */ + menuItemInfo[total].subcheat = i; + menuItemInfo[total].fieldType = kType_SetClear; + menuItem[total] = "Set/Clear"; + menuSubItem[total++] = kSetClearNames[operationParameter]; + } + + if(operation == kOperation_WriteMatch) + { + /* ----- do match value field ----- */ + menuItemInfo[total].subcheat = i; + menuItemInfo[total].fieldType = kType_WriteMatch; + menuItem[total] = "Match to Write"; + + { + int numChars; + + if(traverse->flags & kActionFlag_IgnoreMask) + { + menuItemInfo[total].extraData = 0xFFFFFFFF; + numChars = 8; + } + else + { + menuItemInfo[total].extraData = kCheatSizeMaskTable[EXTRACT_FIELD(traverse->type, BytesUsed)]; + numChars = kCheatSizeDigitsTable[EXTRACT_FIELD(traverse->type, BytesUsed)]; + } + + sprintf(extendDataBuf[i], "%.*X", numChars, traverse->extendData); + } + + menuSubItem[total++] = extendDataBuf[i]; + } + + if((operation != kOperation_None) || (type == kType_Watch)) + { + UINT32 userSelect = TEST_FIELD(entry->actionList[0].type, UserSelectEnable); + UINT32 bytesUsed = EXTRACT_FIELD(traverse->type, BytesUsed); + + if(type != kType_Watch) + { + { + /* ----- do data field ----- */ + + sprintf(dataBuf[i], "%.*X (%d)", (int)kCheatSizeDigitsTable[bytesUsed], traverse->originalDataField, traverse->originalDataField); + + menuItemInfo[total].subcheat = i; + menuItemInfo[total].fieldType = kType_Data; + menuItemInfo[total].extraData = kCheatSizeMaskTable[bytesUsed]; + menuItem[total] = "Data"; + menuSubItem[total++] = dataBuf[i]; + } + + if(!i) + { + /* ----- do user select field ----- */ + menuItemInfo[total].subcheat = i; + menuItemInfo[total].fieldType = kType_UserSelect; + menuItem[total] = "User Select"; + menuSubItem[total++] = ui_getstring(userSelect ? UI_on : UI_off); + } + + if(!i && userSelect) + { + { + /* ----- do user select minimum displayed value field ----- */ + menuItemInfo[total].subcheat = i; + menuItemInfo[total].fieldType = kType_UserSelectMinimumDisp; + menuItem[total] = "Minimum Displayed Value"; + menuSubItem[total++] = kNumbersTable[EXTRACT_FIELD(traverse->type, UserSelectMinimumDisplay)]; + } + + { + /* ----- do user select minimum value field ----- */ + menuItemInfo[total].subcheat = i; + menuItemInfo[total].fieldType = kType_UserSelectMinimum; + menuItem[total] = "Minimum Value"; + menuSubItem[total++] = kNumbersTable[EXTRACT_FIELD(traverse->type, UserSelectMinimum)]; + } + } + + if(userSelect) + { + /* ----- do user select BCD field ----- */ + menuItemInfo[total].subcheat = i; + menuItemInfo[total].fieldType = kType_UserSelectBCD; + menuItem[total] = "BCD"; + menuSubItem[total++] = ui_getstring(TEST_FIELD(traverse->type, UserSelectBCD) ? UI_on : UI_off); + } + + if(userSelect || isSelect) + { + /* ----- do prefill field ----- */ + menuItemInfo[total].subcheat = i; + menuItemInfo[total].fieldType = kType_Prefill; + menuItem[total] = "Prefill"; + menuSubItem[total++] = kPrefillNames[EXTRACT_FIELD(traverse->type, Prefill)]; + } + + if(i && userSelect) + { + /* ----- do copy previous value field ----- */ + menuItemInfo[total].subcheat = i; + menuItemInfo[total].fieldType = kType_CopyPrevious; + menuItem[total] = "Copy Previous Value"; + menuSubItem[total++] = ui_getstring(TEST_FIELD(traverse->type, LinkCopyPreviousValue) ? UI_on : UI_off); + } + } + + { + /* ----- do byte length field ----- */ + menuItemInfo[total].subcheat = i; + menuItemInfo[total].fieldType = kType_ByteLength; + menuItem[total] = "Byte Length"; + menuSubItem[total++] = kSizeNames[bytesUsed]; + } + + if(bytesUsed > 0) + { + /* ----- do endianness field ----- */ + menuItemInfo[total].subcheat = i; + menuItemInfo[total].fieldType = kType_Endianness; + menuItem[total] = "Endianness"; + menuSubItem[total++] = kEndiannessNames[EXTRACT_FIELD(traverse->type, Endianness)]; + } + + { + /* ----- do location type field ----- */ + menuItemInfo[total].subcheat = i; + menuItemInfo[total].fieldType = kType_LocationType; + menuItem[total] = "Location"; + + if(locationType == kLocation_Custom) + menuSubItem[total++] = kCustomLocationNames[locationParameter]; + else + menuSubItem[total++] = kLocationNames[locationType]; + } + + if( (locationType == kLocation_Standard) || (locationType == kLocation_HandlerMemory) || + (locationType == kLocation_ProgramSpace)) + { + /* ----- do cpu field ----- */ + menuItemInfo[total].subcheat = i; + menuItemInfo[total].fieldType = kType_CPU; + menuItem[total] = "CPU"; + menuSubItem[total++] = kNumbersTable[locationParameter]; + } + + if(locationType == kLocation_MemoryRegion) + { + /* ----- do region field ----- */ + menuItemInfo[total].subcheat = i; + menuItemInfo[total].fieldType = kType_Region; + menuItem[total] = "Region"; + menuSubItem[total++] = kRegionNames[locationParameter]; + } + + if(locationType == kLocation_IndirectIndexed) + { + { + /* ----- do packed CPU field ----- */ + menuItemInfo[total].subcheat = i; + menuItemInfo[total].fieldType = kType_PackedCPU; + menuItem[total] = "CPU"; + menuSubItem[total++] = kNumbersTable[(locationParameter >> 2) & 7]; + } + + { + /* ----- do packed size field ----- */ + menuItemInfo[total].subcheat = i; + menuItemInfo[total].fieldType = kType_PackedSize; + menuItem[total] = "Address Size"; + menuSubItem[total++] = kNumbersTable[(locationParameter & 3) + 1]; + } + + { + /* ----- do address index field ----- */ + + /* ---- swap if negative ----- */ + if(traverse->extendData & 0x80000000) + { + int temp = traverse->extendData; + + temp = -temp; + + sprintf(extendDataBuf[i], "-%.8X", temp); + } + else + sprintf(extendDataBuf[i], "%.8X", traverse->extendData); + + menuItemInfo[total].subcheat = i; + menuItemInfo[total].fieldType = kType_AddressIndex; + menuItem[total] = "Address Index"; + menuSubItem[total++] = extendDataBuf[i]; + } + } + + { + /* ----- do address field ----- */ + int charsToPrint = 8; + + switch(EXTRACT_FIELD(traverse->type, LocationType)) + { + case kLocation_Standard: + case kLocation_HandlerMemory: + case kLocation_ProgramSpace: + { + CPUInfo * cpuInfo = &cpuInfoList[EXTRACT_FIELD(traverse->type, LocationParameter)]; + + charsToPrint = cpuInfo->addressCharsNeeded; + menuItemInfo[total].extraData = cpuInfo->addressMask; + } + break; + + case kLocation_IndirectIndexed: + { + CPUInfo * cpuInfo = &cpuInfoList[(EXTRACT_FIELD(traverse->type, LocationParameter) >> 2) & 7]; + + charsToPrint = cpuInfo->addressCharsNeeded; + menuItemInfo[total].extraData = cpuInfo->addressMask; + } + break; + + default: + menuItemInfo[total].extraData = 0xFFFFFFFF; + } + + sprintf(addressBuf[i], "%.*X", charsToPrint, traverse->address); + + menuItemInfo[total].subcheat = i; + menuItemInfo[total].fieldType = kType_Address; + menuItem[total] = "Address"; + menuSubItem[total++] = addressBuf[i]; + } + } + } + + if(i < (entry->actionListLength - 1)) + { + menuItemInfo[total].subcheat = i; + menuItemInfo[total].fieldType = kType_Divider; + menuItem[total] = "=========="; + menuSubItem[total++] = NULL; + } + } + + menuItemInfo[total].subcheat = 0; // return + menuItemInfo[total].fieldType = kType_Return; + menuItem[total] = ui_getstring(UI_returntoprior); + menuSubItem[total++] = NULL; + + menuItemInfo[total].subcheat = 0; // terminate arrey + menuItemInfo[total].fieldType = kType_Return; + menuItem[total] = NULL; + menuSubItem[total] = NULL; + + /* ----- adjuste cursor position ----- */ + if(sel < 0) + sel = 0; + if(sel >= total) + sel = total - 1; + + info = &menuItemInfo[sel]; + action = &entry->actionList[info->subcheat]; + + /* ----- higlighted sub-item if edit mode ----- */ + if(editActive) + flagBuf[sel] = 1; + + /* ---- draw it ----- */ + old_style_menu(menuItem, menuSubItem, flagBuf, sel, 0); + + /********** KEY HANDLING **********/ + if(AltKeyPressed()) + increment <<= 4; + if(ControlKeyPressed()) + increment <<= 8; + if(ShiftKeyPressed()) + increment <<= 16; + + if(UIPressedRepeatThrottle(IPT_UI_LEFT, kHorizontalSlowKeyRepeatRate)) + { + editActive = 0; + dirty = 1; + + switch(info->fieldType) + { + case kType_Name: + currentNameTemplate--; + + if(currentNameTemplate < 0) + { + currentNameTemplate = 0; + + while(kCheatNameTemplates[currentNameTemplate + 1][0]) + { + currentNameTemplate++; + } + } + + entry->name = realloc(entry->name, strlen(kCheatNameTemplates[currentNameTemplate]) + 1); + strcpy(entry->name, kCheatNameTemplates[currentNameTemplate]); + break; +/* + case kType_ActivationKey1: + case kType_ActivationKey2: + if(info->fieldType == kType_ActivationKey1) + { + entry->activationKey1--; + + if(entry->activationKey1 < 0) + entry->activationKey1 = __code_max - 1; + if(entry->activationKey1 >= __code_max) + entry->activationKey1 = 0; + + entry->flags |= kCheatFlag_HasActivationKey1; + } + else + { + entry->activationKey2--; + + if(entry->activationKey2 < 0) + entry->activationKey2 = __code_max - 1; + if(entry->activationKey2 >= __code_max) + entry->activationKey2 = 0; + + entry->flags |= kCheatFlag_HasActivationKey2; + } + break; +*/ + case kType_LinkExtension: + TOGGLE_MASK_FIELD(action->type, LinkExtension); + break; + + case kType_Type: + { + UINT8 handled = 0; + + CLEAR_MASK_FIELD(action->type, OperationExtend); + + if(EXTRACT_FIELD(action->type, LocationType) == kLocation_Custom) + { + UINT32 locationParameter = EXTRACT_FIELD(action->type, LocationParameter); + + if(locationParameter == kCustomLocation_Comment) + { + SET_FIELD(action->type, LocationParameter, 0); + SET_FIELD(action->type, LocationType, kLocation_Standard); + SET_FIELD(action->type, Type, kType_Watch); + SET_FIELD(action->type, Operation, kOperation_None); + SET_MASK_FIELD(action->type, OperationExtend); + + handled = 1; + } + else if(locationParameter == kCustomLocation_Select) + { + SET_FIELD(action->type, LocationParameter, kCustomLocation_Comment); + SET_FIELD(action->type, LocationType, kLocation_Custom); + SET_FIELD(action->type, Type, 0); + + handled = 1; + } + } + + if(!handled) + { + UINT32 type = EXTRACT_FIELD(action->type, Type); + + if(type == kType_NormalOrDelay) + { + SET_FIELD(action->type, LocationParameter, kCustomLocation_Select); + SET_FIELD(action->type, LocationType, kLocation_Custom); + SET_FIELD(action->type, Type, 0); + } + else + { + SET_FIELD(action->type, Type, type - 1); + } + } + } + break; + + case kType_OneShot: + TOGGLE_MASK_FIELD(action->type, OneShot); + break; + + case kType_RestorePreviousValue: + TOGGLE_MASK_FIELD(action->type, RestorePreviousValue); + break; + + case kType_Delay: + case kType_IgnoreDecrementBy: + { + UINT32 delay = (EXTRACT_FIELD(action->type, TypeParameter) - 1) & 7; + + SET_FIELD(action->type, TypeParameter, delay); + } + break; + + case kType_WatchSize: + action->originalDataField = (action->originalDataField & 0xFFFFFF00) | ((action->originalDataField - 0x00000001) & 0x000000FF); + action->data = action->originalDataField; + break; + + case kType_WatchSkip: + action->originalDataField = (action->originalDataField & 0xFFFF00FF) | ((action->originalDataField - 0x00000100) & 0x0000FF00); + action->data = action->originalDataField; + break; + + case kType_WatchPerLine: + action->originalDataField = (action->originalDataField & 0xFF00FFFF) | ((action->originalDataField - 0x00010000) & 0x00FF0000); + action->data = action->originalDataField; + break; + + case kType_WatchAddValue: + action->originalDataField = (action->originalDataField & 0x00FFFFFF) | ((action->originalDataField - 0x01000000) & 0xFF000000); + action->data = action->originalDataField; + break; + + case kType_WatchFormat: + { + UINT32 typeParameter = EXTRACT_FIELD(action->type, TypeParameter); + + typeParameter = (typeParameter & 0xFFFFFFFC) | ((typeParameter - 0x00000001) & 0x0000003); + SET_FIELD(action->type, TypeParameter, typeParameter); + } + break; + + case kType_WatchLabel: + SET_FIELD(action->type, TypeParameter, EXTRACT_FIELD(action->type, TypeParameter) ^ 0x00000004); + break; + + case kType_Operation: + { + UINT32 operation = ((EXTRACT_FIELD(action->type, Operation) | EXTRACT_FIELD(action->type, OperationExtend) << 2) - 1) & 7; + + if(operation == kOperation_None) + operation = kOperation_WriteMatch; + + if( (operation == kOperation_SetOrClearBits) && + !(EXTRACT_FIELD(action->type, LocationType) == kLocation_IndirectIndexed)) + operation = (operation - 1) & 7; + + if(operation >> 2) + SET_MASK_FIELD(action->type, OperationExtend); + else + CLEAR_MASK_FIELD(action->type, OperationExtend); + + SET_FIELD(action->type, Operation, operation); + } + break; + + case kType_WriteMask: + case kType_WriteMatch: + action->extendData -= increment; + action->extendData &= info->extraData; + break; + + case kType_RangeMinimum: + if(!EXTRACT_FIELD(action->type, BytesUsed)) + action->extendData = (action->extendData & 0xFFFF00FF) | ((action->extendData - 0x00000100) & 0x0000FF00); + else + action->extendData = (action->extendData & 0x0000FFFF) | ((action->extendData - 0x00010000) & 0xFFFF0000); + break; + + case kType_RangeMaximum: + if(!EXTRACT_FIELD(action->type, BytesUsed)) + action->extendData = (action->extendData & 0xFFFFFF00) | ((action->extendData - 0x00000001) & 0x000000FF); + else + action->extendData = (action->extendData & 0xFFFF0000) | ((action->extendData - 0x00000001) & 0x0000FFFF); + break; + + case kType_AddressIndex: + case kType_SubtractMinimum: + case kType_AddMaximum: + action->extendData -= increment; + break; + + case kType_AddSubtract: + case kType_SetClear: + TOGGLE_MASK_FIELD(action->type, OperationParameter); + break; + + case kType_Data: + action->originalDataField -= increment; + action->originalDataField &= info->extraData; + action->data = action->originalDataField; + break; + + case kType_UserSelect: + TOGGLE_MASK_FIELD(action->type, UserSelectEnable); + break; + + case kType_UserSelectMinimumDisp: + TOGGLE_MASK_FIELD(action->type, UserSelectMinimumDisplay); + break; + + case kType_UserSelectMinimum: + TOGGLE_MASK_FIELD(action->type, UserSelectMinimum); + break; + + case kType_UserSelectBCD: + TOGGLE_MASK_FIELD(action->type, UserSelectBCD); + break; + + case kType_Prefill: + { + UINT32 prefill = (EXTRACT_FIELD(action->type, Prefill) - 1) & 3; + + SET_FIELD(action->type, Prefill, prefill); + } + break; + + case kType_CopyPrevious: + TOGGLE_MASK_FIELD(action->type, LinkCopyPreviousValue); + break; + + case kType_ByteLength: + { + UINT32 length = (EXTRACT_FIELD(action->type, BytesUsed) - 1) & 3; + + SET_FIELD(action->type, BytesUsed, length); + } + break; + + case kType_Endianness: + TOGGLE_MASK_FIELD(action->type, Endianness); + break; + + case kType_LocationType: + { + UINT32 locationType = EXTRACT_FIELD(action->type, LocationType); + UINT32 locationParameter = EXTRACT_FIELD(action->type, LocationParameter); + + if(locationType == kLocation_Standard) + { + SET_FIELD(action->type, LocationType, kLocation_ProgramSpace); + SET_FIELD(action->type, LocationParameter, (locationParameter << 2) & 0x1C); + } + else if(locationType == kLocation_Custom) + { + SET_FIELD(action->type, LocationType, kLocation_HandlerMemory); + SET_FIELD(action->type, LocationParameter, 0); + } + else if(locationType == kLocation_IndirectIndexed) + { + SET_FIELD(action->type, LocationType, kLocation_Custom); + SET_FIELD(action->type, LocationParameter, kCustomLocation_EEPROM); + } + else + { + locationType--; + + SET_FIELD(action->type, LocationType, locationType); + } + } + break; + + case kType_CPU: + case kType_Region: + { + UINT32 locationParameter = EXTRACT_FIELD(action->type, LocationParameter); + + locationParameter = (locationParameter - 1) & 31; + + SET_FIELD(action->type, LocationParameter, locationParameter); + } + break; + + case kType_PackedCPU: + { + UINT32 locationParameter = EXTRACT_FIELD(action->type, LocationParameter); + + locationParameter = ((locationParameter - 0x04) & 0x1C) | (locationParameter & 0x03); + + SET_FIELD(action->type, LocationParameter, locationParameter); + } + break; + + case kType_PackedSize: + { + UINT32 locationParameter = EXTRACT_FIELD(action->type, LocationParameter); + + locationParameter = ((locationParameter - 0x01) & 0x03) | (locationParameter & 0x1C); + + SET_FIELD(action->type, LocationParameter, locationParameter); + } + break; + + case kType_Address: + action->address -= increment; + action->address &= info->extraData; + break; + + case kType_Return: + case kType_Divider: + break; + } + } + + if(UIPressedRepeatThrottle(IPT_UI_RIGHT, kHorizontalSlowKeyRepeatRate)) + { + editActive = 0; + dirty = 1; + + switch(info->fieldType) + { + case kType_Name: + currentNameTemplate++; + + if((currentNameTemplate < 0) || !kCheatNameTemplates[currentNameTemplate][0]) + { + currentNameTemplate = 0; + } + + entry->name = realloc(entry->name, strlen(kCheatNameTemplates[currentNameTemplate]) + 1); + strcpy(entry->name, kCheatNameTemplates[currentNameTemplate]); + break; +/* + case kType_ActivationKey1: + case kType_ActivationKey2: + if(info->fieldType == kType_ActivationKey1) + { + entry->activationKey1++; + + if(entry->activationKey1 < 0) + entry->activationKey1 = __code_max - 1; + if(entry->activationKey1 >= __code_max) + entry->activationKey1 = 0; + + entry->flags |= kCheatFlag_HasActivationKey1; + } + else + { + entry->activationKey2++; + + if(entry->activationKey2 < 0) + entry->activationKey2 = __code_max - 1; + if(entry->activationKey2 >= __code_max) + entry->activationKey2 = 0; + + entry->flags |= kCheatFlag_HasActivationKey2; + } + break; +*/ + case kType_LinkExtension: + TOGGLE_MASK_FIELD(action->type, LinkExtension); + break; + + case kType_Type: + { + UINT8 handled = 0; + + CLEAR_MASK_FIELD(action->type, OperationExtend); + + if(EXTRACT_FIELD(action->type, LocationType) == kLocation_Custom) + { + UINT32 locationParameter = EXTRACT_FIELD(action->type, LocationParameter); + + if(locationParameter == kCustomLocation_Comment) + { + SET_FIELD(action->type, LocationParameter, kCustomLocation_Select); + SET_FIELD(action->type, LocationType, kLocation_Custom); + SET_FIELD(action->type, Type, 0); + + handled = 1; + } + else if(locationParameter == kCustomLocation_Select) + { + SET_FIELD(action->type, LocationParameter, 0); + SET_FIELD(action->type, LocationType, kLocation_Standard); + SET_FIELD(action->type, Type, 0); + + handled = 1; + } + } + + if(!handled) + { + UINT32 type = EXTRACT_FIELD(action->type, Type); + + if(type == kType_Watch) + { + SET_FIELD(action->type, LocationParameter, kCustomLocation_Comment); + SET_FIELD(action->type, LocationType, kLocation_Custom); + SET_FIELD(action->type, Type, 0); + } + else + { + SET_FIELD(action->type, Type, type + 1); + + if((type + 1) == kType_Watch) + { + SET_FIELD(action->type, Operation, kOperation_None); + SET_MASK_FIELD(action->type, OperationExtend); + } + } + } + } + break; + + case kType_OneShot: + TOGGLE_MASK_FIELD(action->type, OneShot); + break; + + case kType_RestorePreviousValue: + TOGGLE_MASK_FIELD(action->type, RestorePreviousValue); + break; + + case kType_Delay: + case kType_IgnoreDecrementBy: + { + UINT32 delay = (EXTRACT_FIELD(action->type, TypeParameter) + 1) & 7; + + SET_FIELD(action->type, TypeParameter, delay); + } + break; + + case kType_WatchSize: + action->originalDataField = (action->originalDataField & 0xFFFFFF00) | ((action->originalDataField + 0x00000001) & 0x000000FF); + action->data = action->originalDataField; + break; + + case kType_WatchSkip: + action->originalDataField = (action->originalDataField & 0xFFFF00FF) | ((action->originalDataField + 0x00000100) & 0x0000FF00); + action->data = action->originalDataField; + break; + + case kType_WatchPerLine: + action->originalDataField = (action->originalDataField & 0xFF00FFFF) | ((action->originalDataField + 0x00010000) & 0x00FF0000); + action->data = action->originalDataField; + break; + + case kType_WatchAddValue: + action->originalDataField = (action->originalDataField & 0x00FFFFFF) | ((action->originalDataField + 0x01000000) & 0xFF000000); + action->data = action->originalDataField; + break; + + case kType_WatchFormat: + { + UINT32 typeParameter = EXTRACT_FIELD(action->type, TypeParameter); + + typeParameter = (typeParameter & 0xFFFFFFFC) | ((typeParameter + 0x00000001) & 0x0000003); + SET_FIELD(action->type, TypeParameter, typeParameter); + } + break; + + case kType_WatchLabel: + SET_FIELD(action->type, TypeParameter, EXTRACT_FIELD(action->type, TypeParameter) ^ 0x00000004); + break; + + case kType_Operation: + { + UINT32 operation = ((EXTRACT_FIELD(action->type, Operation) | EXTRACT_FIELD(action->type, OperationExtend) << 2) + 1) & 7; + + if(operation > kOperation_WriteMatch) + operation = kOperation_WriteMask; + + if( (operation == kOperation_SetOrClearBits) && + !(EXTRACT_FIELD(action->type, LocationType) == kLocation_IndirectIndexed)) + operation = (operation + 1) & 7; + + if(operation >> 2) + SET_MASK_FIELD(action->type, OperationExtend); + else + CLEAR_MASK_FIELD(action->type, OperationExtend); + + SET_FIELD(action->type, Operation, operation); + } + break; + + case kType_WriteMask: + case kType_WriteMatch: + action->extendData += increment; + action->extendData &= info->extraData; + break; + + case kType_RangeMinimum: + if(!EXTRACT_FIELD(action->type, BytesUsed)) + action->extendData = (action->extendData & 0xFFFF00FF) | ((action->extendData + 0x00000100) & 0x0000FF00); + else + action->extendData = (action->extendData & 0x0000FFFF) | ((action->extendData + 0x00010000) & 0xFFFF0000); + break; + + case kType_RangeMaximum: + if(!EXTRACT_FIELD(action->type, BytesUsed)) + action->extendData = (action->extendData & 0xFFFFFF00) | ((action->extendData + 0x00000001) & 0x000000FF); + else + action->extendData = (action->extendData & 0xFFFF0000) | ((action->extendData + 0x00000001) & 0x0000FFFF); + break; + + case kType_AddressIndex: + case kType_SubtractMinimum: + case kType_AddMaximum: + action->extendData += increment; + break; + + case kType_AddSubtract: + case kType_SetClear: + TOGGLE_MASK_FIELD(action->type, OperationParameter); + break; + + case kType_Data: + action->originalDataField += increment; + action->originalDataField &= info->extraData; + action->data = action->originalDataField; + break; + + case kType_UserSelect: + TOGGLE_MASK_FIELD(action->type, UserSelectEnable); + break; + + case kType_UserSelectMinimumDisp: + TOGGLE_MASK_FIELD(action->type, UserSelectMinimumDisplay); + break; + + case kType_UserSelectMinimum: + TOGGLE_MASK_FIELD(action->type, UserSelectMinimum); + break; + + case kType_UserSelectBCD: + TOGGLE_MASK_FIELD(action->type, UserSelectBCD); + break; + + case kType_Prefill: + { + UINT32 prefill = (EXTRACT_FIELD(action->type, Prefill) + 1) & 3; + + SET_FIELD(action->type, Prefill, prefill); + } + break; + + case kType_CopyPrevious: + TOGGLE_MASK_FIELD(action->type, LinkCopyPreviousValue); + break; + + case kType_ByteLength: + { + UINT32 length = (EXTRACT_FIELD(action->type, BytesUsed) + 1) & 3; + + SET_FIELD(action->type, BytesUsed, length); + } + break; + + case kType_Endianness: + TOGGLE_MASK_FIELD(action->type, Endianness); + break; + + case kType_LocationType: + { + UINT32 locationType = EXTRACT_FIELD(action->type, LocationType); + UINT32 locationParameter = EXTRACT_FIELD(action->type, LocationParameter); + + if(locationType == kLocation_ProgramSpace) + { + SET_FIELD(action->type, LocationType, kLocation_Standard); + SET_FIELD(action->type, LocationParameter, (locationParameter >> 2) & 7); + } + else if(locationType == kLocation_Custom) + { + SET_FIELD(action->type, LocationType, kLocation_IndirectIndexed); + SET_FIELD(action->type, LocationParameter, 0); + } + else if(locationType == kLocation_HandlerMemory) + { + SET_FIELD(action->type, LocationType, kLocation_Custom); + SET_FIELD(action->type, LocationParameter, kCustomLocation_EEPROM); + } + else + { + locationType++; + + SET_FIELD(action->type, LocationType, locationType); + } + } + break; + + case kType_CPU: + case kType_Region: + { + UINT32 locationParameter = EXTRACT_FIELD(action->type, LocationParameter); + + locationParameter = (locationParameter + 1) & 31; + + SET_FIELD(action->type, LocationParameter, locationParameter); + } + break; + + case kType_PackedCPU: + { + UINT32 locationParameter = EXTRACT_FIELD(action->type, LocationParameter); + + locationParameter = ((locationParameter + 0x04) & 0x1C) | (locationParameter & 0x03); + + SET_FIELD(action->type, LocationParameter, locationParameter); + } + break; + + case kType_PackedSize: + { + UINT32 locationParameter = EXTRACT_FIELD(action->type, LocationParameter); + + locationParameter = ((locationParameter + 0x01) & 0x03) | (locationParameter & 0x1C); + + SET_FIELD(action->type, LocationParameter, locationParameter); + } + break; + + case kType_Address: + action->address += increment; + action->address &= info->extraData; + break; + + case kType_Return: + case kType_Divider: + break; + } + } + + if(UIPressedRepeatThrottle(IPT_UI_DOWN, kVerticalKeyRepeatRate)) + { + sel++; + + if(sel >= total) + sel = 0; + + editActive = 0; + } + + if(UIPressedRepeatThrottle(IPT_UI_UP, kVerticalKeyRepeatRate)) + { + sel--; + + if(sel < 0) + sel = total - 1; + + editActive = 0; + } + + if(UIPressedRepeatThrottle(IPT_UI_PAGE_UP, kVerticalKeyRepeatRate)) + { + sel -= fullMenuPageHeight; + + if(sel < 0) + sel = 0; + + editActive = 0; + } + + if(UIPressedRepeatThrottle(IPT_UI_PAGE_DOWN, kVerticalKeyRepeatRate)) + { + sel += fullMenuPageHeight; + + if(sel >= total) + sel = total - 1; + + editActive = 0; + } + + if(input_ui_pressed(IPT_UI_SELECT)) + { + if(editActive) + { + editActive = 0; + } + else + { + switch(info->fieldType) + { + case kType_Name: + case kType_ExtendName: + case kType_Comment: + case kType_ActivationKey1: + case kType_ActivationKey2: + case kType_WatchSize: + case kType_WatchSkip: + case kType_WatchPerLine: + case kType_WatchAddValue: + case kType_WriteMask: + case kType_AddMaximum: + case kType_SubtractMinimum: + case kType_RangeMinimum: + case kType_RangeMaximum: + case kType_WriteMatch: + case kType_Data: + case kType_Address: + osd_readkey_unicode(1); + dirty = 1; + editActive = 1; + break; + + case kType_Return: + sel = -1; + break; + } + } + } + + /********** EDIT MODE **********/ + if(editActive) + { + /* ----- do edit text ----- */ + dirty = 1; + + switch(info->fieldType) + { + case kType_Name: + entry->name = DoDynamicEditTextField(entry->name); + break; + + case kType_ExtendName: + action->optionalName = DoDynamicEditTextField(action->optionalName); + break; + + case kType_Comment: + entry->comment = DoDynamicEditTextField(entry->comment); + break; + + case kType_ActivationKey1: + case kType_ActivationKey2: + { + if(input_ui_pressed(IPT_UI_CANCEL)) + { + if(info->fieldType == kType_ActivationKey1) + { + entry->activationKey1 = 0; + entry->flags &= ~kCheatFlag_HasActivationKey1; + } + else + { + entry->activationKey2 = 0; + entry->flags &= ~kCheatFlag_HasActivationKey2; + } + + editActive = 0; + } + else + { + int code = input_code_poll_switches(FALSE); + + if(code == KEYCODE_ESC) + { + if(info->fieldType == kType_ActivationKey1) + { + entry->activationKey1 = 0; + entry->flags &= ~kCheatFlag_HasActivationKey1; + } + else + { + entry->activationKey2 = 0; + entry->flags &= ~kCheatFlag_HasActivationKey2; + } + + editActive = 0; + } + else + { + if((code != INPUT_CODE_INVALID) && !input_ui_pressed(IPT_UI_SELECT)) + { + if(info->fieldType == kType_ActivationKey1) + { + entry->activationKey1 = code; + entry->flags |= kCheatFlag_HasActivationKey1; + } + else + { + entry->activationKey2 = code; + entry->flags |= kCheatFlag_HasActivationKey2; + } + + editActive = 0; + } + } + } + } + break; + + case kType_WatchSize: + { + UINT32 temp = (action->originalDataField >> 0) & 0xFF; + + temp++; + temp = DoEditHexField(temp) & 0xFF; + temp--; + + action->originalDataField = (action->originalDataField & 0xFFFFFF00) | ((temp << 0) & 0x000000FF); + action->data = action->originalDataField; + } + break; + + case kType_WatchSkip: + { + UINT32 temp = (action->originalDataField >> 8) & 0xFF; + + temp = DoEditHexField(temp) & 0xFF; + + action->originalDataField = (action->originalDataField & 0xFFFF00FF) | ((temp << 8) & 0x0000FF00); + action->data = action->originalDataField; + } + break; + + case kType_WatchPerLine: + { + UINT32 temp = (action->originalDataField >> 16) & 0xFF; + + temp = DoEditHexField(temp) & 0xFF; + + action->originalDataField = (action->originalDataField & 0xFF00FFFF) | ((temp << 16) & 0x00FF0000); + action->data = action->originalDataField; + } + break; + + case kType_WatchAddValue: + { + UINT32 temp = (action->originalDataField >> 24) & 0xFF; + + temp = DoEditHexFieldSigned(temp, 0xFFFFFF80) & 0xFF; + + action->originalDataField = (action->originalDataField & 0x00FFFFFF) | ((temp << 24) & 0xFF000000); + action->data = action->originalDataField; + } + break; + + case kType_WriteMask: + action->extendData = DoEditHexField(action->extendData); + action->extendData &= info->extraData; + break; + + case kType_AddMaximum: + case kType_SubtractMinimum: + case kType_WriteMatch: + action->extendData = DoEditHexField(action->extendData); + break; + + case kType_RangeMinimum: + { + UINT32 temp; + + if(!TEST_FIELD(action->type, BytesUsed)) + { + temp = (action->extendData >> 8) & 0xFF; + temp = DoEditHexField(temp) & 0xFF; + + action->extendData = (action->extendData & 0xFF) | ((temp << 8) & 0xFF00); + } + else + { + temp = (action->extendData >> 16) & 0xFFFF; + temp = DoEditHexField(temp) & 0xFFFF; + + action->extendData = (action->extendData & 0x0000FFFF) | ((temp << 16) & 0xFFFF0000); + } + } + break; + + case kType_RangeMaximum: + { + UINT32 temp; + + if(!TEST_FIELD(action->type, BytesUsed)) + { + temp = action->extendData & 0xFF; + temp = DoEditHexField(temp) & 0xFF; + + action->extendData = (action->extendData & 0xFF00) | (temp & 0x00FF); + } + else + { + temp = action->extendData & 0xFFFF; + temp = DoEditHexField(temp) & 0xFFFF; + + action->extendData = (action->extendData & 0xFFFF0000) | (temp & 0x0000FFFF); + } + } + break; + + case kType_Data: + action->originalDataField = DoEditHexField(action->originalDataField); + action->originalDataField &= info->extraData; + action->data = action->originalDataField; + break; + + case kType_Address: + action->address = DoEditHexField(action->address); + action->address &= info->extraData; + break; + } + + if(input_ui_pressed(IPT_UI_CANCEL)) + editActive = 0; + } + else + { + if(input_ui_pressed(IPT_UI_SAVE_CHEAT)) + { + if(ControlKeyPressed()) + if((entry->flags & kCheatFlag_HasActivationKey1) || (entry->flags & kCheatFlag_HasActivationKey2)) + { + SaveCheat(entry, index, 1); // save activation key + + ui_popup_time(1, "activation key saved"); + } + else + ui_popup_time(1, "no activation key"); + else + SaveCheat(entry, 0, 0); // save current entry + } + + if(input_ui_pressed(IPT_UI_WATCH_VALUE)) + WatchCheatEntry(entry, 0); + + if(input_ui_pressed(IPT_UI_ADD_CHEAT)) + AddActionBefore(entry, info->subcheat); + + if(input_ui_pressed(IPT_UI_DELETE_CHEAT)) + { + if(!info->subcheat) + { + /* ----- if selected entry is master, delete current entry then return to previous menu ----- */ + DeleteCheatAt(index); + + return 0; + } + else + DeleteActionAt(entry, info->subcheat); + } + } + + if(input_ui_pressed(IPT_UI_CANCEL)) + { + sel = -1; + editActive = 0; + } + + if(sel == -1) + { + editActive = 0; + dirty = 1; + } + + if(dirty) + { + UpdateCheatInfo(entry, 0); + + entry->flags |= kCheatFlag_Dirty; + } + + astring_free(temp_string_1); + astring_free(temp_string_2); + + return sel + 1; +} + +/*----------------------------------------------------------- + DoSearchMenuMinimum - management for minimum search menu +-----------------------------------------------------------*/ + +static int DoSearchMenuMinimum(int selection) +{ + /* main */ + enum + { + kMenu_CPU = 0, + kMenu_SearchItem, + + kMenu_Divider, + + kMenu_Memory, + kMenu_ViewResult, + kMenu_RestoreSearch, + + kMenu_Return, + }; + + /* search item */ + enum + { + kItem_Value, + kItem_Timer, + kItem_Equal, + kItem_NotEqual, + kItem_Less, + kItem_Greater, + + kItem_Max = kItem_Greater, + }; + + const char * menuItem[kMenu_Return + 2] = { 0 }; + const char * menuSubItem[kMenu_Return + 2] = { 0 }; + + char valueBuffer[60]; + char valueSignedBuffer[60]; + char cpuBuffer[20]; + char flagBuf[kMenu_Return + 2] = { 0 }; + + SearchInfo * search = GetCurrentSearch(); + + INT32 sel = selection - 1; + INT32 total = 0; + UINT8 doSearch = 0; + + static INT8 searchItem = 0; + static INT8 submenuChoice = 0; + static UINT8 doneSaveMemory = 0; + static UINT8 firstEntry = 0; + static UINT8 editActive = 0; + static UINT8 posnKeep = kMenu_SearchItem; + static UINT32 increment = 1; + + /********** SUB MENU **********/ + if(submenuChoice) + { + switch(sel) + { + case kMenu_CPU: + submenuChoice = SelectSearchRegions(submenuChoice, GetCurrentSearch()); + break; + + case kMenu_ViewResult: + submenuChoice = ViewSearchResults(submenuChoice, firstEntry); + break; + } + + firstEntry = 0; + + posnKeep = sel; + + /* ----- meaningless ? because no longer return with sel = -1 (pressed UI_CONFIG in submenu) ----- */ +// if(submenuChoice == -1) +// submenuChoice = 0; + + return sel + 1; + } + + sel = posnKeep; + + /* ----- unselectable comparison search item if no save ----- */ + if(!doneSaveMemory) + searchItem = kItem_Value; + + /********** MENU CONSTRUCTION **********/ + sprintf(cpuBuffer, "%d", search->targetIdx); // CPU + menuItem[total] = ui_getstring(UI_cpu); + menuSubItem[total++] = cpuBuffer; + + if(search->sign && (search->oldOptions.value & kSearchByteSignBitTable[search->bytes])) + { + UINT32 tempValue; + + tempValue = ~search->oldOptions.value + 1; + tempValue &= kSearchByteUnsignedMaskTable[search->bytes]; + + sprintf(valueBuffer, "-%.*X (-%d)", kSearchByteDigitsTable[search->bytes], tempValue, tempValue); + } + else + sprintf(valueBuffer, "%.*X (%d)", kSearchByteDigitsTable[search->bytes], + search->oldOptions.value & kSearchByteMaskTable[search->bytes], + search->oldOptions.value & kSearchByteMaskTable[search->bytes]); + + switch(searchItem) + { + case kItem_Value: // value + menuItem[total] = "Value"; + menuSubItem[total++] = valueBuffer; + break; + + case kItem_Timer: + menuItem[total] = "Timer (+ or -)"; // timer + + /* ----- if memory has been saved, set sub-item for timer ----- */ + if(doneSaveMemory) + { + if(search->oldOptions.delta & kSearchByteSignBitTable[search->bytes]) + { + UINT32 tempValue; + + tempValue = ~search->oldOptions.delta + 1; + tempValue &= kSearchByteUnsignedMaskTable[search->bytes]; + + sprintf(valueSignedBuffer, "-%.*X (-%d)", kSearchByteDigitsTable[search->bytes], tempValue, tempValue); + } + else + sprintf(valueSignedBuffer, "%.*X (%d)", kSearchByteDigitsTable[search->bytes], + search->oldOptions.delta & kSearchByteMaskTable[search->bytes], + search->oldOptions.delta & kSearchByteMaskTable[search->bytes]); + + menuSubItem[total++] = valueSignedBuffer; + } + break; + + case kItem_Equal: + menuItem[total] = "Comparison"; // equal + menuSubItem[total++] = "Equal"; + break; + + case kItem_NotEqual: + menuItem[total] = "Comparison"; // not equal + menuSubItem[total++] = "Not Equal"; + break; + + case kItem_Less: + menuItem[total] = "Comparison"; // less + menuSubItem[total++] = "Less"; + break; + + case kItem_Greater: + menuItem[total] = "Comparison"; // greater + menuSubItem[total++] = "Greater"; + break; + } + + menuItem[total] = "--------------------"; // divider + menuSubItem[total++] = NULL; + + if(doneSaveMemory) // memory + menuItem[total] = "Initialize Memory"; + else + menuItem[total] = "Save Memory"; + menuSubItem[total++] = NULL; + + menuItem[total] = ui_getstring(UI_viewresults); // view result + menuSubItem[total++] = NULL; + + menuItem[total] = ui_getstring(UI_restoreresults); // restore result + menuSubItem[total++] = NULL; + + menuItem[total] = ui_getstring(UI_returntoprior); // return + menuSubItem[total++] = NULL; + + menuItem[total] = NULL; // terminate array + menuSubItem[total] = NULL; + + /* ----- adjust current cursor position ----- */ + if((sel < 0) || (sel >= total)) + sel = kMenu_SearchItem; + + /* ----- higlighted sub-item if edit mode ----- */ + if(editActive) + flagBuf[sel] = 1; + + /* ----- draw it ----- */ + old_style_menu(menuItem, menuSubItem, flagBuf, sel, 0); + + /********** KEY HANDLING **********/ + if(UIPressedRepeatThrottle(IPT_UI_UP, kVerticalKeyRepeatRate)) + { + if(editActive) + { + increment <<= 4; + + switch(searchItem) + { + case kItem_Value: + search->oldOptions.value += increment; + search->oldOptions.value &= kSearchByteMaskTable[search->bytes]; + break; + + case kItem_Timer: + search->oldOptions.delta += increment; + search->oldOptions.delta &= kSearchByteMaskTable[search->bytes]; + break; + } + + increment >>= 4; + } + else + { + sel--; + + /* ----- if divider, skip it ----- */ + if(sel == kMenu_Divider) + sel--; + + if(sel < 0) + sel = total - 1; + } + } + + if(UIPressedRepeatThrottle(IPT_UI_DOWN, kVerticalKeyRepeatRate)) + { + if(editActive) + { + increment <<= 4; + + switch(searchItem) + { + case kItem_Value: + search->oldOptions.value -= increment; + search->oldOptions.value &= kSearchByteMaskTable[search->bytes]; + break; + + case kItem_Timer: + search->oldOptions.delta -= increment; + search->oldOptions.delta &= kSearchByteMaskTable[search->bytes]; + break; + } + + increment >>= 4; + } + else + { + sel++; + + /* ----- if divider, skip it ----- */ + if(sel == kMenu_Divider) + sel++; + + if(sel >= total) + sel = 0; + } + } + + if(UIPressedRepeatThrottle(IPT_UI_PAGE_UP, kVerticalKeyRepeatRate)) + { + sel -= fullMenuPageHeight; + + if(sel < 0) + sel = 0; + } + + if(UIPressedRepeatThrottle(IPT_UI_PAGE_DOWN, kVerticalKeyRepeatRate)) + { + sel += fullMenuPageHeight; + + if(sel >= total) + sel = total - 1; + } + + if(UIPressedRepeatThrottle(IPT_UI_LEFT, kHorizontalFastKeyRepeatRate)) + { + if(editActive) + { + switch(searchItem) + { + case kItem_Value: + search->oldOptions.value -= increment; + search->oldOptions.value &= kSearchByteMaskTable[search->bytes]; + break; + + case kItem_Timer: + search->oldOptions.delta -= increment; + search->oldOptions.delta &= kSearchByteMaskTable[search->bytes]; + break; + } + } + else + { + switch(sel) + { + case kMenu_CPU: + if(search->targetIdx > 0) + { + search->targetIdx--; + + BuildSearchRegions(search); + AllocateSearchRegions(search); + + doneSaveMemory = 0; + } + break; + + case kMenu_SearchItem: + if(doneSaveMemory) + searchItem--; + + if(searchItem < 0) + searchItem = kItem_Max; + break; + } + } + } + + if(UIPressedRepeatThrottle(IPT_UI_RIGHT, kHorizontalFastKeyRepeatRate)) + { + if(editActive) + { + switch(searchItem) + { + case kItem_Value: + search->oldOptions.value += increment; + search->oldOptions.value &= kSearchByteMaskTable[search->bytes]; + break; + + case kItem_Timer: + search->oldOptions.delta += increment; + search->oldOptions.delta &= kSearchByteMaskTable[search->bytes]; + break; + } + } + else + { + switch(sel) + { + case kMenu_CPU: + if(search->targetIdx < cpu_gettotalcpu() - 1) + { + search->targetIdx++; + + BuildSearchRegions(search); + AllocateSearchRegions(search); + + doneSaveMemory = 0; + } + break; + + case kMenu_SearchItem: + if(doneSaveMemory) + searchItem++; + + if(searchItem > kItem_Max) + searchItem = kItem_Value; + + break; + } + } + } + + if(input_ui_pressed(IPT_UI_SELECT)) + { + if(editActive) + { + switch(searchItem) + { + case kItem_Value: + search->bytes = kSearchSize_8Bit; + search->lhs = kSearchOperand_Current; + search->rhs = kSearchOperand_Value; + search->comparison = kSearchComparison_EqualTo; + search->value = search->oldOptions.value; + + doSearch = 1; + break; + + case kItem_Timer: + search->bytes = kSearchSize_8Bit; + search->lhs = kSearchOperand_Current; + search->rhs = kSearchOperand_Previous; + search->comparison = kSearchComparison_IncreasedBy; + search->value = search->oldOptions.delta; + + doSearch = 1; + break; + } + + /* ----- finish edit mode then do search ----- */ + editActive = 0; + } + else + { + switch(sel) + { + case kMenu_CPU: + submenuChoice = 1; // go to region selection menu + break; + + case kMenu_SearchItem: + { + + switch(searchItem) + { + case kItem_Value: + case kItem_Timer: + editActive = 1; + break; + + case kItem_Equal: + search->bytes = kSearchSize_8Bit; + search->lhs = kSearchOperand_Current; + search->rhs = kSearchOperand_Previous; + search->comparison = kSearchComparison_EqualTo; + + doSearch = 1; + break; + + case kItem_NotEqual: + search->bytes = kSearchSize_8Bit; + search->lhs = kSearchOperand_Current; + search->rhs = kSearchOperand_Previous; + search->comparison = kSearchComparison_NotEqual; + + doSearch = 1; + break; + + case kItem_Less: + search->bytes = kSearchSize_8Bit; + search->lhs = kSearchOperand_Current; + search->rhs = kSearchOperand_Previous; + search->comparison = kSearchComparison_LessThan; + + doSearch = 1; + break; + + case kItem_Greater: + search->bytes = kSearchSize_8Bit; + search->lhs = kSearchOperand_Current; + search->rhs = kSearchOperand_Previous; + search->comparison = kSearchComparison_GreaterThan; + + doSearch = 1; + break; + } + } + break; + + case kMenu_Memory: // save memory to search newly + doneSaveMemory = 0; + + doSearch = 1; + break; + + case kMenu_ViewResult: + if(search->regionListLength) + { + /* ----- go to result viewer ----- */ + firstEntry = 1; + submenuChoice = 1; + } + else + /* ----- if no search region (eg, sms.c in HazeMD), don't open result viewer to avoid the crash ----- */ + ui_popup_time(1, "no search regions"); + break; + + case kMenu_RestoreSearch: + RestoreSearchBackup(search); + break; + + case kMenu_Return: + posnKeep = kMenu_SearchItem; + sel = -1; + break; + } + } + } + + if(UIPressedRepeatThrottle(IPT_UI_ZOOM_IN, kVerticalKeyRepeatRate)) + /* ----- quick menu switch : search -> watchpoint list ----- */ + sel = -5; + + if(UIPressedRepeatThrottle(IPT_UI_ZOOM_OUT, kVerticalKeyRepeatRate)) + /* ----- quick menu switch : search -> add/edit ----- */ + sel = -3; + + /* ----- edit mode ON/OFF ----- */ + if(input_ui_pressed(IPT_UI_EDIT_CHEAT)) + { + if(editActive) + editActive = 0; + else + { + if((searchItem == kItem_Value) || (kItem_Timer)) + editActive = 1; + } + } + + /* ----- memory save ----- */ + if(input_ui_pressed(IPT_UI_CLEAR)) + { + doneSaveMemory = 0; + + doSearch = 1; + } + + if(input_ui_pressed(IPT_UI_CANCEL)) + { + if(editActive) + editActive = 0; + else + { + posnKeep = kMenu_Memory; + sel = -1; + } + } + + /********** SEARCH **********/ + if(doSearch) + { + if(!doneSaveMemory) + /* ----- initialize search ----- */ + InitializeNewSearch(search); + + if(((sel == kMenu_SearchItem) && (searchItem == kItem_Value)) || doneSaveMemory) + { + /* ----- do search ----- */ + BackupSearch(search); + + DoSearch(search); + } + + UpdateSearch(search); + + /* ----- display message ----- */ + if(((sel == kMenu_SearchItem) && (searchItem == kItem_Value)) || doneSaveMemory) + popmessage("%d results found", search->numResults); + else + popmessage("saved all memory regions"); + + doneSaveMemory = 1; + + if(search->numResults == 1) + { + AddCheatFromFirstResult(search); + + popmessage("1 result found, added to list"); + } + } + + /********** EDIT **********/ + if((sel == kMenu_SearchItem) && editActive) + { + if(searchItem == kItem_Value) + { + search->oldOptions.value = DoEditHexField(search->oldOptions.value); + + search->oldOptions.value &= kSearchByteMaskTable[search->bytes]; + } + else + { + if(searchItem == kItem_Timer) + { + search->oldOptions.delta = DoEditHexField(search->oldOptions.delta); + + search->oldOptions.delta &= kSearchByteMaskTable[search->bytes]; + } + } + } + + if(sel >= 0) + posnKeep = sel; + + return sel + 1; +} + +/*----------------------------------------------------------- + DoSearchMenuClassic - management for classic search menu +-----------------------------------------------------------*/ + +static int DoSearchMenuClassic(int selection) +{ + static const char * energyStrings[] = + { + "Less", + "Greater", + "Less or Equal", + "Greater or Equal", + }; + + static const char * equalOrNotStrings[] = + { + "Equal", + "Not Equal", + }; + + static const char * operandStrings[] = + { + "Previous Data", + "First Data", + }; + + /* conversion table */ + static const int kEnergyComparisonConversionTable[] = + { + kSearchComparison_LessThan, + kSearchComparison_GreaterThan, + kSearchComparison_LessThanOrEqualTo, + kSearchComparison_GreaterThanOrEqualTo + }; + + static const int kEqualOrNotConversionTable[] = + { + kSearchComparison_EqualTo, + kSearchComparison_NotEqual + }; + + static const int kOperandConversionTable[] = + { + kSearchOperand_Previous, + kSearchOperand_First + }; + + enum + { + kMenu_DoMemorySave = 0, + + kMenu_Divider, + + kMenu_CPU, + kMenu_ValueEqual, + kMenu_ValueNearTo, + kMenu_Time, + kMenu_Energy, + kMenu_Status, + kMenu_Operand, + + kMenu_Divider2, + + kMenu_ViewResult, + kMenu_RestoreSearch, + + kMenu_Return, + }; + + enum + { + kEnergy_Less = 0, + kEnergy_Greater, + kEnergy_LessOrEqual, + kEnergy_GreaterOrEqual, + }; + + const char * menuItem[kMenu_Return + 2] = { 0 }; + const char * menuSubItem[kMenu_Return + 2] = { 0 }; + + char valueBuffer[60]; + char valueSignedBuffer[60]; + char cpuBuffer[20]; + + SearchInfo * search = GetCurrentSearch(); + + INT32 sel = selection - 1; + INT32 total = 0; + UINT32 increment = 1; + UINT8 doSearch = 0; + + static INT8 submenuChoice = 0; + static UINT8 doneSaveMemory = 0; + static UINT8 firstEntry = 0; + static int lastPos = 0; + + sel = lastPos; + + /********** SUB MENU **********/ + if(submenuChoice) + { + switch(sel) + { + case kMenu_CPU: + submenuChoice = SelectSearchRegions(submenuChoice, GetCurrentSearch()); + break; + + case kMenu_ViewResult: + submenuChoice = ViewSearchResults(submenuChoice, firstEntry); + break; + } + + firstEntry = 0; + + /* ----- meaningless ? because no longer return with sel = -1 (pressed UI_CONFIG in submenu) ----- */ +// if(submenuChoice == -1) +// submenuChoice = 0; + + return sel + 1; + } + + /********** MENU CONSTRUCTION **********/ + if(!doneSaveMemory) + menuItem[total] = "Save Memory"; + else + menuItem[total] = "Initialize Memory"; + + menuSubItem[total++] = NULL; + + menuItem[total] = "--------------------"; + menuSubItem[total++] = NULL; + + sprintf(cpuBuffer, "%d", search->targetIdx); + menuItem[total] = ui_getstring(UI_cpu); + menuSubItem[total++] = cpuBuffer; + + if(search->sign && (search->oldOptions.value & kSearchByteSignBitTable[search->bytes])) + { + UINT32 tempValue; + + tempValue = ~search->oldOptions.value + 1; + tempValue &= kSearchByteUnsignedMaskTable[search->bytes]; + + sprintf(valueBuffer, "-%.*X (-%d)", kSearchByteDigitsTable[search->bytes], tempValue, tempValue); + } + else + sprintf(valueBuffer, "%.*X (%d)", kSearchByteDigitsTable[search->bytes], search->oldOptions.value & kSearchByteMaskTable[search->bytes], search->oldOptions.value & kSearchByteMaskTable[search->bytes]); + + menuItem[total] = "Value (Equal)"; + + if(sel >= kMenu_ValueNearTo) + menuSubItem[total++] = " "; + else + menuSubItem[total++] = valueBuffer; + + menuItem[total] = "Value (NearTo)"; + + if(sel >= kMenu_ValueNearTo) + menuSubItem[total++] = valueBuffer; + else + menuSubItem[total++] = " "; + + menuItem[total] = "Timer (+ or -)"; + menuSubItem[total++] = " "; + + menuItem[total] = "Energy (Less or Greater)"; + menuSubItem[total++] = " "; + + menuItem[total] = "Status (Same or Different)"; + menuSubItem[total++] = " "; + + menuItem[total] = "Comparison Option"; + menuSubItem[total++] = " "; + + menuItem[total] = "--------------------"; + menuSubItem[total++] = NULL; + + menuItem[total] = ui_getstring(UI_viewresults); // view result + menuSubItem[total++] = NULL; + + menuItem[total] = ui_getstring(UI_restoreresults); // restore result + menuSubItem[total++] = NULL; + + menuItem[total] = ui_getstring(UI_returntoprior); // return + menuSubItem[total++] = NULL; + + menuItem[total] = NULL; // terminate array + menuSubItem[total] = NULL; + + /* ----- if memory has been saved, set sub-menu items ----- */ + if(doneSaveMemory) + { + if(search->oldOptions.delta & kSearchByteSignBitTable[search->bytes]) + { + UINT32 tempValue; + + tempValue = ~search->oldOptions.delta + 1; + tempValue &= kSearchByteUnsignedMaskTable[search->bytes]; + + sprintf(valueSignedBuffer, "-%.*X (-%d)", kSearchByteDigitsTable[search->bytes], tempValue, tempValue); + } + else + sprintf(valueSignedBuffer, "%.*X (%d)", kSearchByteDigitsTable[search->bytes], search->oldOptions.delta & kSearchByteMaskTable[search->bytes], search->oldOptions.delta & kSearchByteMaskTable[search->bytes]); + + menuSubItem[kMenu_Time] = valueSignedBuffer; + menuSubItem[kMenu_Energy] = energyStrings[search->oldOptions.energy]; + menuSubItem[kMenu_Status] = equalOrNotStrings[search->oldOptions.status]; + menuSubItem[kMenu_Operand] = operandStrings[search->oldOptions.operand]; + } + + /* ----- adjust current cursor position ----- */ + if(sel < 0) + sel = 0; + if(sel >= total) + sel = total - 1; + + /* ----- draw it ----- */ + old_style_menu(menuItem, menuSubItem, 0, sel, 0); + + /********** KEY HANDLING **********/ + if(AltKeyPressed()) + increment <<= 4; + + if(UIPressedRepeatThrottle(IPT_UI_DOWN, kVerticalKeyRepeatRate)) + { + sel++; + + /* ----- if divider, skip it ----- */ + if((sel == kMenu_Divider) || (sel == kMenu_Divider2)) + sel++; + + if(sel >= total) + sel = 0; + } + + if(UIPressedRepeatThrottle(IPT_UI_UP, kVerticalKeyRepeatRate)) + { + sel--; + + /* ----- if divider, skip it ----- */ + if((sel == kMenu_Divider) || (sel == kMenu_Divider2)) + sel--; + + if(sel < 0) + sel = total - 1; + } + + if(UIPressedRepeatThrottle(IPT_UI_PAGE_UP, kVerticalKeyRepeatRate)) + { + sel -= fullMenuPageHeight; + + if(sel < 0) + sel = 0; + } + + if(UIPressedRepeatThrottle(IPT_UI_PAGE_DOWN, kVerticalKeyRepeatRate)) + { + sel += fullMenuPageHeight; + + if(sel >= total) + sel = total - 1; + } + + if(UIPressedRepeatThrottle(IPT_UI_LEFT, kHorizontalFastKeyRepeatRate)) + { + switch(sel) + { + case kMenu_CPU: + if(search->targetIdx > 0) + { + search->targetIdx--; + + BuildSearchRegions(search); + AllocateSearchRegions(search); + + doneSaveMemory = 0; + } + break; + + case kMenu_ValueEqual: + case kMenu_ValueNearTo: + search->oldOptions.value -= increment; + + search->oldOptions.value &= kSearchByteMaskTable[search->bytes]; + break; + + case kMenu_Time: + if(doneSaveMemory) + { + search->oldOptions.delta -= increment; + + search->oldOptions.delta &= kSearchByteMaskTable[search->bytes]; + } + break; + + case kMenu_Energy: + if(doneSaveMemory) + { + if(search->oldOptions.energy < kEnergy_GreaterOrEqual) + search->oldOptions.energy++; + else + search->oldOptions.energy = kEnergy_Less; + } + break; + + case kMenu_Status: + if(doneSaveMemory) + search->oldOptions.status ^= 1; + break; + + case kMenu_Operand: + if(doneSaveMemory) + search->oldOptions.operand ^= 1; + break; + } + } + + if(UIPressedRepeatThrottle(IPT_UI_RIGHT, kHorizontalFastKeyRepeatRate)) + { + switch(sel) + { + case kMenu_CPU: + if(search->targetIdx < cpu_gettotalcpu() - 1) + { + search->targetIdx++; + + BuildSearchRegions(search); + AllocateSearchRegions(search); + + doneSaveMemory = 0; + } + break; + + case kMenu_ValueEqual: + case kMenu_ValueNearTo: + search->oldOptions.value += increment; + + search->oldOptions.value &= kSearchByteMaskTable[search->bytes]; + break; + + case kMenu_Time: + if(doneSaveMemory) + { + search->oldOptions.delta += increment; + + search->oldOptions.delta &= kSearchByteMaskTable[search->bytes]; + } + break; + + case kMenu_Energy: + if(doneSaveMemory) + { + if(search->oldOptions.energy > kEnergy_Less) + search->oldOptions.energy--; + else + search->oldOptions.energy = kEnergy_GreaterOrEqual; + } + break; + + case kMenu_Status: + if(doneSaveMemory) + search->oldOptions.status ^= 1; + break; + + case kMenu_Operand: + if(doneSaveMemory) + search->oldOptions.operand ^= 1; + break; + } + } + + if(input_ui_pressed(IPT_UI_SELECT)) + { + switch(sel) + { + case kMenu_DoMemorySave: // save memory to search newly + doneSaveMemory = 0; + + doSearch = 1; + break; + + case kMenu_CPU: + submenuChoice = 1; // go to region selection menu + break; + + case kMenu_ValueEqual: + case kMenu_ValueNearTo: + search->bytes = kSearchSize_8Bit; + search->lhs = kSearchOperand_Current; + search->rhs = kSearchOperand_Value; + + if(sel == kMenu_ValueEqual) + search->comparison = kSearchComparison_EqualTo; // search requested value + else + search->comparison = kSearchComparison_NearTo; // search requested and requested -1 value + + search->value = search->oldOptions.value; + + doSearch = 1; + break; + + case kMenu_Time: + search->bytes = kSearchSize_8Bit; + search->lhs = kSearchOperand_Current; + search->rhs = kOperandConversionTable[search->oldOptions.operand]; + search->comparison = kSearchComparison_IncreasedBy; + search->value = search->oldOptions.delta; + + doSearch = 1; + break; + + case kMenu_Energy: + search->bytes = kSearchSize_8Bit; + search->lhs = kSearchOperand_Current; + search->rhs = kOperandConversionTable[search->oldOptions.operand]; + search->comparison = kEnergyComparisonConversionTable[search->oldOptions.energy]; + + doSearch = 1; + break; + + case kMenu_Status: + search->bytes = kSearchSize_8Bit; + search->lhs = kSearchOperand_Current; + search->rhs = kOperandConversionTable[search->oldOptions.operand]; + search->comparison = kEqualOrNotConversionTable[search->oldOptions.status]; + + doSearch = 1; + break; + + case kMenu_ViewResult: + if(search->regionListLength) + { + /* ----- go to result viewer ----- */ + firstEntry = 1; + submenuChoice = 1; + } + else + /* ----- if no search region (eg, sms.c in HazeMD), don't open result viewer to avoid the crash ----- */ + ui_popup_time(1, "no search regions"); + break; + + case kMenu_RestoreSearch: + /* ----- restore previous results ----- */ + RestoreSearchBackup(search); + break; + + case kMenu_Return: + sel = -1; + lastPos = 0; + break; + + default: + break; + } + } + + if(UIPressedRepeatThrottle(IPT_UI_ZOOM_IN, kVerticalKeyRepeatRate)) + /* ----- quick menu switch : search -> watchpoint list ----- */ + sel = -5; + + if(UIPressedRepeatThrottle(IPT_UI_ZOOM_OUT, kVerticalKeyRepeatRate)) + /* ----- quick menu switch : search -> add/edit ----- */ + sel = -3; + + + if(input_ui_pressed(IPT_UI_CANCEL)) + { + /* ----- if cursor is now on the return item, adjust cursor keeper ----- */ + if(sel == kMenu_Return) + lastPos = 0; + + sel = -1; + } + + if(doSearch) + { + if(!doneSaveMemory) + /* ----- initialize search ----- */ + InitializeNewSearch(search); + + if( (sel == kMenu_ValueEqual) || (sel == kMenu_ValueNearTo) || doneSaveMemory) + { + /* ----- do search ----- */ + BackupSearch(search); + + DoSearch(search); + } + + UpdateSearch(search); + + if( (sel == kMenu_ValueEqual) || (sel == kMenu_ValueNearTo) || doneSaveMemory) + popmessage("%d results found", search->numResults); + else + popmessage("saved all memory regions"); + + doneSaveMemory = 1; + + if(search->numResults == 1) + { + AddCheatFromFirstResult(search); + + popmessage("1 result found, added to list"); + } + } + + /* ----- edit a value if direct key input ----- */ + if((sel == kMenu_ValueEqual) || (sel == kMenu_ValueNearTo)) + { + search->oldOptions.value = DoEditHexField(search->oldOptions.value); + + search->oldOptions.value &= kSearchByteMaskTable[search->bytes]; + } + else + { + if(sel == kMenu_Time) + { + search->oldOptions.delta = DoEditHexField(search->oldOptions.delta); + + search->oldOptions.delta &= kSearchByteMaskTable[search->bytes]; + } + } + + /* ----- keep current cursor position ----- */ + if(sel >= 0) + lastPos = sel; + + return sel + 1; +} + +/*----------------------------------------------------- + DoSearchMenu - management for advanced search menu +-----------------------------------------------------*/ + +static int DoSearchMenu(int selection) +{ + /* menu stirngs */ + static const char * kOperandNameTable[] = + { + "Current Data", + "Previous Data", + "First Data", + "Value" + }; + + static const char * kComparisonNameTable[] = + { + "Less", + "Greater", + "Equal", + "Less Or Equal", + "Greater Or Equal", + "Not Equal", + "Increased By Value", + "Near To" + }; + + static const char * kSearchByteNameTable[] = + { + "1", + "2", + "3", + "4", + "Bit" + }; + + enum + { + kMenu_LHS, + kMenu_Comparison, + kMenu_RHS, + kMenu_Value, + + kMenu_Divider, + + kMenu_Size, + kMenu_Swap, + kMenu_Sign, + kMenu_CPU, + kMenu_Name, + + kMenu_Divider2, + + kMenu_DoSearch, + kMenu_SaveMemory, + + kMenu_Divider3, + + kMenu_ViewResult, + kMenu_RestoreSearch, + + kMenu_Return, + + kMenu_Max + }; + + const char * menu_item[kMenu_Max + 2] = { 0 }; + const char * menu_subitem[kMenu_Max + 2] = { 0 }; + + char valueBuffer[20]; + char cpuBuffer[20]; + char flagBuf[kMenu_Max + 2] = { 0 }; + + SearchInfo * search = GetCurrentSearch(); + + INT32 sel = selection - 1; + INT32 total = 0; + UINT32 increment = 1; + + static INT8 submenuChoice = 0; + static UINT8 doneMemorySave = 0; + static UINT8 editActive = 0; + static UINT8 firstEntry = 0; + static int lastSel = 0; + + sel = lastSel; + + /********** SUB MENU **********/ + if(submenuChoice) + { + switch(sel) + { + case kMenu_CPU: + submenuChoice = SelectSearchRegions(submenuChoice, GetCurrentSearch()); + break; + + case kMenu_ViewResult: + submenuChoice = ViewSearchResults(submenuChoice, firstEntry); + break; + } + + firstEntry = 0; + + /* ----- meaningless ? because no longer return with sel = -1 (pressed UI_CONFIG in submenu) ----- */ +// if(submenuChoice == -1) +// submenuChoice = 0; + + return sel + 1; + } + + /********** MENU CONSTRUCION **********/ + if((search->sign || search->comparison == kSearchComparison_IncreasedBy) && (search->value & kSearchByteSignBitTable[search->bytes])) + { + UINT32 tempValue; + + tempValue = ~search->value + 1; + tempValue &= kSearchByteUnsignedMaskTable[search->bytes]; + + sprintf(valueBuffer, "-%.*X", kSearchByteDigitsTable[search->bytes], tempValue); + } + else + sprintf(valueBuffer, "%.*X", kSearchByteDigitsTable[search->bytes], search->value & kSearchByteMaskTable[search->bytes]); + + if(TEST_FIELD(cheatOptions, DontPrintNewLabels)) + { + menu_item[total] = kOperandNameTable[search->lhs]; + menu_subitem[total++] = NULL; + + menu_item[total] = kComparisonNameTable[search->comparison]; + menu_subitem[total++] = NULL; + + menu_item[total] = kOperandNameTable[search->rhs]; + menu_subitem[total++] = NULL; + + menu_item[total] = valueBuffer; + menu_subitem[total++] = NULL; + } + else + { + menu_item[total] = "LHS"; + menu_subitem[total++] = kOperandNameTable[search->lhs]; + + menu_item[total] = "Comparison"; + menu_subitem[total++] = kComparisonNameTable[search->comparison]; + + menu_item[total] = "RHS"; + menu_subitem[total++] = kOperandNameTable[search->rhs]; + + menu_item[total] = "Value"; + menu_subitem[total++] = valueBuffer; + } + + menu_item[total] = "---"; + menu_subitem[total++] = NULL; + + menu_item[total] = "Size"; + menu_subitem[total++] = kSearchByteNameTable[search->bytes]; + + menu_item[total] = "Swap"; + menu_subitem[total++] = ui_getstring(search->swap ? UI_on : UI_off); + + menu_item[total] = "Signed"; + menu_subitem[total++] = ui_getstring(search->sign ? UI_on : UI_off); + + sprintf(cpuBuffer, "%d", search->targetIdx); + menu_item[total] = "CPU"; + menu_subitem[total++] = cpuBuffer; + + menu_item[total] = "Name"; + if(search->name) + menu_subitem[total++] = search->name; + else + menu_subitem[total++] = "(none)"; + + menu_item[total] = "---"; + menu_subitem[total++] = NULL; + + menu_item[total] = "Do Search"; + menu_subitem[total++] = NULL; + + menu_item[total] = "Save Memory"; + menu_subitem[total++] = NULL; + + menu_item[total] = "---"; + menu_subitem[total++] = NULL; + + menu_item[total] = ui_getstring(UI_viewresults); // view result + menu_subitem[total++] = NULL; + + menu_item[total] = ui_getstring(UI_restoreresults); // restore result + menu_subitem[total++] = NULL; + + menu_item[total] = ui_getstring(UI_returntoprior); // return + menu_subitem[total++] = NULL; + + menu_item[total] = NULL; // terminate array + menu_subitem[total] = NULL; + + /* ----- adjust current cursor position ----- */ + if(sel < 0) + sel = 0; + if(sel >= total) + sel = total - 1; + + /* ----- higlighted sub-item if edit mode ----- */ + if(editActive) + flagBuf[sel] = 1; + + /* ----- draw it ----- */ + old_style_menu(menu_item, menu_subitem, flagBuf, sel, 0); + + /********** KEY HANDLING **********/ + if(AltKeyPressed()) + increment <<= 4; + if(ControlKeyPressed()) + increment <<= 8; + if(ShiftKeyPressed()) + increment <<= 16; + + if(UIPressedRepeatThrottle(IPT_UI_DOWN, kVerticalKeyRepeatRate)) + { + sel++; + + /* ----- if divider, skip it ----- */ + if((sel == kMenu_Divider) || (sel == kMenu_Divider2) || (sel == kMenu_Divider3)) + sel++; + + if(sel >= total) + sel = 0; + } + + if(UIPressedRepeatThrottle(IPT_UI_UP, kVerticalKeyRepeatRate)) + { + sel--; + + /* ----- if divider, skip it ----- */ + if((sel == kMenu_Divider) || (sel == kMenu_Divider2) || (sel == kMenu_Divider3)) + sel--; + + if(sel < 0) + sel = total - 1; + } + + if(UIPressedRepeatThrottle(IPT_UI_PAGE_UP, kVerticalKeyRepeatRate)) + { + sel -= fullMenuPageHeight; + + if(sel < 0) + sel = 0; + } + + if(UIPressedRepeatThrottle(IPT_UI_PAGE_DOWN, kVerticalKeyRepeatRate)) + { + sel += fullMenuPageHeight; + + if(sel >= total) + sel = total - 1; + } + + if(UIPressedRepeatThrottle(IPT_UI_LEFT, kHorizontalFastKeyRepeatRate)) + { + switch(sel) + { + case kMenu_Value: + search->value -= increment; + + search->value &= kSearchByteMaskTable[search->bytes]; + break; + + case kMenu_LHS: + search->lhs--; + + if(search->lhs < kSearchOperand_Current) + search->lhs = kSearchOperand_Max; + break; + + case kMenu_RHS: + search->rhs--; + + if(search->rhs < kSearchOperand_Current) + search->rhs = kSearchOperand_Max; + break; + + case kMenu_Comparison: + search->comparison--; + + if(search->comparison < kSearchComparison_LessThan) + search->comparison = kSearchComparison_Max; + break; + + case kMenu_Size: + search->bytes--; + + if(search->bytes < kSearchSize_8Bit) + search->bytes = kSearchSize_Max; + break; + + case kMenu_Swap: + search->swap ^= 1; + break; + + case kMenu_Sign: + search->sign ^= 1; + break; + + case kMenu_CPU: + if(search->targetIdx > 0) + { + search->targetIdx--; + + BuildSearchRegions(search); + AllocateSearchRegions(search); + + doneMemorySave = 0; + } + break; + } + } + + if(UIPressedRepeatThrottle(IPT_UI_RIGHT, kHorizontalFastKeyRepeatRate)) + { + switch(sel) + { + case kMenu_Value: + search->value += increment; + + search->value &= kSearchByteMaskTable[search->bytes]; + break; + + case kMenu_Size: + search->bytes++; + + if(search->bytes > kSearchSize_Max) + search->bytes = kSearchSize_8Bit; + break; + + case kMenu_LHS: + search->lhs++; + + if(search->lhs > kSearchOperand_Max) + search->lhs = kSearchOperand_Current; + break; + + case kMenu_RHS: + search->rhs++; + + if(search->rhs > kSearchOperand_Max) + search->rhs = kSearchOperand_Current; + break; + + case kMenu_Comparison: + search->comparison++; + + if(search->comparison > kSearchComparison_Max) + search->comparison = kSearchComparison_LessThan; + break; + + case kMenu_Swap: + search->swap ^= 1; + break; + + case kMenu_Sign: + search->sign ^= 1; + break; + + case kMenu_CPU: + if(search->targetIdx < cpu_gettotalcpu() - 1) + { + search->targetIdx++; + + BuildSearchRegions(search); + AllocateSearchRegions(search); + + doneMemorySave = 0; + } + break; + } + } + + if(input_ui_pressed(IPT_UI_SELECT)) + { + if(editActive) + editActive = 0; + else + { + switch(sel) + { + case kMenu_Value: // edit selected field + case kMenu_Name: + editActive = 1; + break; + + case kMenu_CPU: // go to region selection menu + submenuChoice = 1; + break; + + case kMenu_DoSearch: + if(!doneMemorySave) + /* ----- initialize search ----- */ + InitializeNewSearch(search); + + if((!kSearchOperandNeedsInit[search->lhs] && !kSearchOperandNeedsInit[search->rhs]) || doneMemorySave) + { + BackupSearch(search); + + DoSearch(search); + } + + UpdateSearch(search); + + if((!kSearchOperandNeedsInit[search->lhs] && !kSearchOperandNeedsInit[search->rhs]) || doneMemorySave) + popmessage("%d results found", search->numResults); + else + popmessage("saved all memory regions"); + + doneMemorySave = 1; + + if(search->numResults == 1) + { + AddCheatFromFirstResult(search); + + popmessage("1 result found, added to list"); + } + break; + + case kMenu_SaveMemory: + InitializeNewSearch(search); + + UpdateSearch(search); + + popmessage("saved all memory regions"); + + doneMemorySave = 1; + break; + + case kMenu_ViewResult: + if(search->regionListLength) + { + /* ----- go to result viewer ----- */ + firstEntry = 1; + submenuChoice = 1; + } + else + /* ----- if no search region (eg, sms.c in HazeMD), don't open result viewer to avoid the crash ----- */ + ui_popup_time(1, "no search regions"); + break; + + case kMenu_RestoreSearch: + /* ----- restore previous results ----- */ + RestoreSearchBackup(search); + break; + + case kMenu_Return: + submenuChoice = 0; + sel = -1; + break; + + default: + break; + } + } + } + + /* ----- edit selected field ----- */ + if(editActive) + { + switch(sel) + { + case kMenu_Value: + search->value = DoEditHexField(search->value); + + search->value &= kSearchByteMaskTable[search->bytes]; + break; + + case kMenu_Name: + search->name = DoDynamicEditTextField(search->name); + break; + } + } + + if(input_ui_pressed(IPT_UI_CANCEL)) + sel = -1; + + /* ----- keep current cursor position ----- */ + if(!(sel == -1)) + lastSel = sel; + + return sel + 1; +} + +/*--------------------------------------------------------------------- + SelectSearchRegions - management for search regions selection menu +---------------------------------------------------------------------*/ + +static int SelectSearchRegions(int selection, SearchInfo * search) +{ + static const char * kSearchSpeedList[] = + { + "Fast", + "Medium", + "Slow", + "Very Slow", + "All Memory" + }; + + INT32 sel; + const char ** menuItem; + const char ** menuSubItem; + INT32 i; + INT32 total = 0; + SearchRegion * region; + + if(!search) + return 0; + + sel = selection - 1; + + RequestStrings(search->regionListLength + 3, 0, 0, 0); + + /********** MENU CONSTRUCTION **********/ + menuItem = menuStrings.mainList; + menuSubItem = menuStrings.subList; + + for(i = 0; i < search->regionListLength; i++) + { + SearchRegion * traverse = &search->regionList[i]; + + menuItem[total] = traverse->name; + menuSubItem[total++] = ui_getstring((traverse->flags & kRegionFlag_Enabled) ? UI_on : UI_off); + } + + menuItem[total] = "Search Speed"; + menuSubItem[total++] = kSearchSpeedList[search->searchSpeed]; + + menuItem[total] = ui_getstring(UI_returntoprior); // return item + menuSubItem[total++] = NULL; + + menuItem[total] = NULL; // terminate array + menuSubItem[total] = NULL; + + /* ----- adjust current cursor positon ----- */ + if(sel < 0) + sel = 0; + if(sel > (total - 1)) + sel = total - 1; + + if(sel < search->regionListLength) + region = &search->regionList[sel]; + else + region = NULL; + + /* ----- draw it ----- */ + old_style_menu(menuItem, menuSubItem, NULL, sel, 0); + + /********** KEY HANDLING **********/ + if(UIPressedRepeatThrottle(IPT_UI_DOWN, kVerticalKeyRepeatRate)) + { + sel++; + + if(sel >= total) + sel = 0; + } + + if(UIPressedRepeatThrottle(IPT_UI_UP, kVerticalKeyRepeatRate)) + { + sel--; + + if(sel < 0) + sel = total - 1; + } + + if(UIPressedRepeatThrottle(IPT_UI_PAGE_UP, kVerticalKeyRepeatRate)) + { + sel -= fullMenuPageHeight; + + if(sel < 0) + sel = 0; + } + + if(UIPressedRepeatThrottle(IPT_UI_PAGE_DOWN, kVerticalKeyRepeatRate)) + { + sel += fullMenuPageHeight; + + if(sel >= total) + sel = total - 1; + } + + if(UIPressedRepeatThrottle(IPT_UI_LEFT, kHorizontalSlowKeyRepeatRate)) + { + if(sel < search->regionListLength) + { + search->regionList[sel].flags ^= kRegionFlag_Enabled; + + AllocateSearchRegions(search); + } + + if(sel == search->regionListLength) // set search speed + { + if(search->searchSpeed > kSearchSpeed_Fast) + search->searchSpeed--; + else + search->searchSpeed = kSearchSpeed_Max; + + BuildSearchRegions(search); + AllocateSearchRegions(search); + } + } + + if(UIPressedRepeatThrottle(IPT_UI_RIGHT, kHorizontalSlowKeyRepeatRate)) + { + if(sel < search->regionListLength) + { + search->regionList[sel].flags ^= kRegionFlag_Enabled; + + AllocateSearchRegions(search); + } + + if(sel == search->regionListLength) // set search speed + { + if(search->searchSpeed < kSearchSpeed_Max) + search->searchSpeed++; + else + search->searchSpeed = kSearchSpeed_Fast; + + BuildSearchRegions(search); + AllocateSearchRegions(search); + } + } + + if(input_ui_pressed(IPT_UI_DELETE_CHEAT)) + { + if(ShiftKeyPressed()) // shift + delete = invalid region + { + if(region && search) + { + InvalidateEntireRegion(search, region); + + ui_popup_time(1, "region invalidated - %d results remain", search->numResults); + } + } + } + + if(input_ui_pressed(IPT_UI_SELECT)) + { + if(sel >= total - 1) + sel = -1; + } + + if(input_ui_pressed(IPT_UI_CANCEL)) + sel = -1; + + return sel + 1; +} + +/*-------------------------------------------------------- + ViewSearchResults - management for search result menu +--------------------------------------------------------*/ + +static int ViewSearchResults(int selection, int firstTime) +{ + enum + { + kMenu_Header = 0, + kMenu_FirstResult, + + kMaxResultsPerPage = 100 + }; + + const char ** menu_item; + char ** buf; + char * header; + + SearchInfo * search = GetCurrentSearch(); + SearchRegion * region; + + INT32 sel; + INT32 total = 0; + INT32 numPages; + INT32 resultsPerPage; + INT32 i; + UINT32 traverse; + UINT8 hadResults = 0; + INT32 numToSkip; + UINT32 resultsFound = 0; + UINT32 selectedAddress = 0; + UINT32 selectedOffset = 0; + UINT8 selectedAddressGood = 0; + int goToNextPage = 0; + int goToPrevPage = 0; + + RequestStrings(kMaxResultsPerPage + 3, kMaxResultsPerPage + 3, 80, 0); + + menu_item = menuStrings.mainList; + buf = &menuStrings.mainStrings[1]; + header = menuStrings.mainStrings[0]; + + sel = selection - 1; + + /********** FIRST SETTING **********/ + if(firstTime) + { + search->currentRegionIdx = 0; + search->currentResultsPage = 0; + + /* ----- set current REGION for first display ----- */ + for(traverse = 0; traverse < search->regionListLength; traverse++) + { + region = &search->regionList[traverse]; + + if(region->numResults) + { + search->currentRegionIdx = traverse; + break; + } + } + } + + /* ----- adjust current REGION ----- */ + if(search->currentRegionIdx >= search->regionListLength) + search->currentRegionIdx = search->regionListLength - 1; + if(search->currentRegionIdx < 0) + search->currentRegionIdx = 0; + + region = &search->regionList[search->currentRegionIdx]; + + /* ----- set and adjust maximum displayable result in the PAGE ----- */ + resultsPerPage = fullMenuPageHeight - 3; + + if(resultsPerPage <= 0) + resultsPerPage = 1; + else if(resultsPerPage > kMaxResultsPerPage) + resultsPerPage = kMaxResultsPerPage; + + /* ----- get the number of total page ----- */ + if(region->flags & kRegionFlag_Enabled) + numPages = (region->numResults / kSearchByteStep[search->bytes] + resultsPerPage - 1) / resultsPerPage; + else + numPages = 0; + + /* ----- adjust current PAGE ----- */ + if(search->currentResultsPage >= numPages) + search->currentResultsPage = numPages - 1; + if(search->currentResultsPage < 0) + search->currentResultsPage = 0; + + /* ----- set the number of undisplay result ----- */ + numToSkip = resultsPerPage * search->currentResultsPage; + + /********** MENU CONSTRUCTION **********/ + /* ----- construct header item ----- */ + sprintf(header, "%s %d/%d", region->name, search->currentResultsPage + 1, numPages); + + menu_item[total++] = header; + + traverse = 0; + + if((region->length < kSearchByteIncrementTable[search->bytes]) || !(region->flags & kRegionFlag_Enabled)) + ; // no results... + else + { + /* ----- construct result item ----- */ + for(i = 0; (i < resultsPerPage) && (traverse < region->length) && (resultsFound < region->numResults);) + { + while(!IsRegionOffsetValid(search, region, traverse) && (traverse < region->length)) + traverse += kSearchByteStep[search->bytes]; + + if(traverse < region->length) + { + if(numToSkip > 0) + numToSkip--; + else + { + if(total == sel) + { + selectedAddress = region->address + traverse; + selectedOffset = traverse; + selectedAddressGood = 1; + } + + sprintf( buf[total], + "%.8X (%.*X %.*X %.*X)", + region->address + traverse, + kSearchByteDigitsTable[search->bytes], + ReadSearchOperand(kSearchOperand_First, search, region, region->address + traverse), + kSearchByteDigitsTable[search->bytes], + ReadSearchOperand(kSearchOperand_Previous, search, region, region->address + traverse), + kSearchByteDigitsTable[search->bytes], + ReadSearchOperand(kSearchOperand_Current, search, region, region->address + traverse)); + + menu_item[total] = buf[total]; + total++; + + i++; + } + + traverse += kSearchByteStep[search->bytes]; + resultsFound++; + hadResults = 1; + } + } + } + + /* ----- set special message if empty REGION ----- */ + if(!hadResults) + { + if(search->numResults) + menu_item[total++] = "no results for this region"; + else + menu_item[total++] = "no results found"; + } + + menu_item[total++] = ui_getstring(UI_returntoprior); // return + + menu_item[total] = NULL; // terminate array + + /* ----- adjust current cursor position ----- */ + if(sel <= kMenu_Header) + sel = kMenu_FirstResult; + if(sel > (total - 1) || !hadResults) + sel = total - 1; + + /* ----- draw it ----- */ + old_style_menu(menu_item, NULL, NULL, sel, 0); + + /********** KEY HANDLING **********/ + if(UIPressedRepeatThrottle(IPT_UI_DOWN, kVerticalKeyRepeatRate)) + { + sel++; + + if(sel >= total) + sel = kMenu_FirstResult; + } + + if(UIPressedRepeatThrottle(IPT_UI_UP, kVerticalKeyRepeatRate)) + { + sel--; + + if(sel < 1) + sel = total - 1; + } + + if(UIPressedRepeatThrottle(IPT_UI_RIGHT, kHorizontalFastKeyRepeatRate)) + { + if(ShiftKeyPressed()) + search->currentResultsPage = numPages - 1; // shift + right = go to last PAGE + else + { + if(ControlKeyPressed()) + { + search->currentRegionIdx++; // ctrl + right = go to next REGION + + if(search->currentRegionIdx >= search->regionListLength) + search->currentRegionIdx = 0; + } + else + goToNextPage = 1; // right = go to next PAGE + } + + sel = kMenu_FirstResult; + } + + if(UIPressedRepeatThrottle(IPT_UI_LEFT, kHorizontalFastKeyRepeatRate)) + { + if(ShiftKeyPressed()) + search->currentResultsPage = 0; // shift + left = go to first PAGE + else + { + if(ControlKeyPressed()) + { + search->currentRegionIdx--; // ctrl + left = go to previous REGION + + if(search->currentRegionIdx < 0) + search->currentRegionIdx = search->regionListLength - 1; + } + else + goToPrevPage = 1; // left = go to previous PAGE + } + + sel = kMenu_FirstResult; + } + + if(UIPressedRepeatThrottle(IPT_UI_PAGE_UP, kHorizontalFastKeyRepeatRate)) + { + sel -=fullMenuPageHeight; + + if(sel <= kMenu_Header) + sel = kMenu_FirstResult; + } + + if(UIPressedRepeatThrottle(IPT_UI_PAGE_DOWN, kHorizontalFastKeyRepeatRate)) + { + sel +=fullMenuPageHeight; + + if(sel >= total) + sel = total - 1; + } + + if(input_code_pressed_once(KEYCODE_END)) + search->currentResultsPage = numPages - 1; // go to last PAGE + + if(input_code_pressed_once(KEYCODE_HOME)) + search->currentResultsPage = 0; // go to first PAGE + + if(UIPressedRepeatThrottle(IPT_UI_PREV_GROUP, kVerticalKeyRepeatRate)) + { + search->currentRegionIdx--; // go to previous REGION + + if(search->currentRegionIdx < 0) + search->currentRegionIdx = search->regionListLength - 1; + + sel = kMenu_FirstResult; + } + + if(UIPressedRepeatThrottle(IPT_UI_NEXT_GROUP, kVerticalKeyRepeatRate)) + { + search->currentRegionIdx++; // go to next REGION + + if(search->currentRegionIdx >= search->regionListLength) + search->currentRegionIdx = 0; + + sel = kMenu_FirstResult; + } + + if(goToNextPage) + { + search->currentResultsPage++; + + /* ----- if current PAGE is the last, go to first PAGE in next REGION ----- */ + if(search->currentResultsPage >= numPages) + { + search->currentResultsPage = 0; + search->currentRegionIdx++; + + /* ----- if current REGION is the last, go to first REGION ----- */ + if(search->currentRegionIdx >= search->regionListLength) + search->currentRegionIdx = 0; + + /* ----- if next REGION is empty, search next "non-empty" REGION. + but incomplete because last REGION is displayed even if empty ----- */ + for(traverse = search->currentRegionIdx; traverse < search->regionListLength; traverse++) + { + search->currentRegionIdx = traverse; + + if(search->regionList[traverse].numResults) + break; + } + } + + sel = kMenu_FirstResult; + } + + if(goToPrevPage) + { + search->currentResultsPage--; + + /* ----- if current PAGE is first, go to previous REGION ----- */ + if(search->currentResultsPage < 0) + { + search->currentResultsPage = 0; + search->currentRegionIdx--; + + /* ----- if current REGION is first, go to last REGION ----- */ + if(search->currentRegionIdx < 0) + search->currentRegionIdx = search->regionListLength - 1; + + /* ----- if previous REGION is empty, search previous "non-empty" REGION. + but incomplete because first REGION is displayed even if empty ----- */ + for(i = search->currentRegionIdx; i >= 0; i--) + { + search->currentRegionIdx = i; + + if(search->regionList[i].numResults) + break; + } + + /* ----- go to last PAGE in previous REGION ----- */ + { + /* ----- get the number of total page for previous REGION ----- */ + SearchRegion * newRegion = &search->regionList[search->currentRegionIdx]; + UINT32 nextNumPages = (newRegion->numResults / kSearchByteStep[search->bytes] + resultsPerPage - 1) / resultsPerPage; + + if(nextNumPages <= 0) + nextNumPages = 1; + + search->currentResultsPage = nextNumPages - 1; + } + } + + sel = kMenu_FirstResult; + } + + if(selectedAddressGood) + { + if(input_ui_pressed(IPT_UI_WATCH_VALUE)) + AddWatchFromResult(search, region, selectedAddress); // watch selected result + + if(input_ui_pressed(IPT_UI_ADD_CHEAT)) + AddCheatFromResult(search, region, selectedAddress); // add selected result as new code to cheat list + + if(input_ui_pressed(IPT_UI_DELETE_CHEAT)) + { + InvalidateRegionOffset(search, region, selectedOffset); // delete selected result + search->numResults--; + region->numResults--; + } + } + + if(input_ui_pressed(IPT_UI_DELETE_CHEAT)) + { + if(ShiftKeyPressed()) + { + if(region && search) + { + InvalidateEntireRegion(search, region); // invalidate all results in current REGION + + ui_popup_time(1, "region invalidated - %d results remain", search->numResults); + } + } + } + + if((input_ui_pressed(IPT_UI_SELECT) && (sel == total - 1)) || input_ui_pressed(IPT_UI_CANCEL)) + sel = -1; + + return sel + 1; +} + +/*---------------------------------------------------- + ChooseWatch - management for watchpoint list menu +----------------------------------------------------*/ + +static int ChooseWatch(int selection) +{ + const char ** menuItem; + char ** buf; + + WatchInfo * watch; + + static INT32 submenuChoice = 0; + static INT32 submenuWatch = 0; + INT32 sel; + INT32 total = 0; + int i; + + RequestStrings(watchListLength + 2, watchListLength, 30, 0); + + menuItem = menuStrings.mainList; + buf = menuStrings.mainStrings; + + sel = selection - 1; + + /********** SUB MENU **********/ + if(submenuChoice) + { + submenuChoice = EditWatch(&watchList[submenuWatch], submenuChoice); + + /* ----- meaningless ? because no longer return with sel = -1 (pressed UI_CONFIG in submenu) ----- */ +// if(submenuChoice == -1) +// { +// submenuChoice = 0; +// sel = -2; +// } + + return sel + 1; + } + + /********** MENU CONSTRUCTION **********/ + for(i = 0; i < watchListLength; i++) + { + WatchInfo * traverse = &watchList[i]; + + sprintf(buf[i], "%d:%.*X (%d)", traverse->cpu, cpuInfoList[traverse->cpu].addressCharsNeeded, traverse->address, traverse->numElements); + + menuItem[total] = buf[i]; + total++; + } + + menuItem[total++] = ui_getstring(UI_returntoprior); // return + + menuItem[total] = NULL; // terminate array + + /* ----- adjust current cursor position ----- */ + if(sel < 0) + sel = 0; + if(sel >= total) + sel = total - 1; + + /* ----- draw it ----- */ + old_style_menu(menuItem, NULL, NULL, sel, 0); + + if(sel < watchListLength) + watch = &watchList[sel]; + else + watch = NULL; + + /********** KEY HANDLING **********/ + if(UIPressedRepeatThrottle(IPT_UI_DOWN, kVerticalKeyRepeatRate)) + { + sel++; + + if(sel >= total) + sel = 0; + } + + if(UIPressedRepeatThrottle(IPT_UI_UP, kVerticalKeyRepeatRate)) + { + sel--; + + if(sel < 0) + sel = total - 1; + } + + if(UIPressedRepeatThrottle(IPT_UI_PAGE_UP, kVerticalKeyRepeatRate)) + { + sel -= fullMenuPageHeight; + + if(sel < 0) + sel = 0; + } + + if(UIPressedRepeatThrottle(IPT_UI_PAGE_DOWN, kVerticalKeyRepeatRate)) + { + sel += fullMenuPageHeight; + + if(sel >= total) + sel = total - 1; + } + + if(ShiftKeyPressed()) + { + if(input_ui_pressed(IPT_UI_ADD_CHEAT)) // shift + add = add new watchpoint + AddWatchBefore(sel); + + if(input_ui_pressed(IPT_UI_DELETE_CHEAT)) // shift + delete = delete selected watchpoint + DeleteWatchAt(sel); + } + else + { + if(input_ui_pressed(IPT_UI_SAVE_CHEAT)) // save = save selected watchpoint as watchpoint code + { + if(watch) + { + CheatEntry entry; + + memset(&entry, 0, sizeof(CheatEntry)); + + SetupCheatFromWatchAsWatch(&entry, watch); + SaveCheat(&entry, 0, 0); + DisposeCheat(&entry); + } + } + + if(input_ui_pressed(IPT_UI_ADD_CHEAT)) + { + if(watch) + { + if(ControlKeyPressed()) // ctrl + add = add selected watchpoing as watchpoint code to cheat list + { + CheatEntry * entry = GetNewCheat(); + + DisposeCheat(entry); + SetupCheatFromWatchAsWatch(entry, watch); + } + else + AddCheatFromWatch(watch); // add = add selected watchpoint as cheat code to cheat list + } + } + + if(input_ui_pressed(IPT_UI_DELETE_CHEAT)) + { + if(ControlKeyPressed()) + { + for(i = 0; i < watchListLength; i++) // ctrl + delete = disable all watchpoints + watchList[i].numElements = 0; + } + else + { + if(watch) // delete = disable selected watchpoint + watch->numElements = 0; + } + } + } + + if(input_ui_pressed(IPT_UI_EDIT_CHEAT)) // edit = edit selected watchpoint + { + if(sel < (total - 1)) + { + submenuWatch = sel; + submenuChoice = 1; + } + } + + if(input_ui_pressed(IPT_UI_SELECT)) + { + if(sel == (total - 1)) // return previous menu + { + submenuChoice = 0; + sel = -1; + } + else + { + if(sel < (total - 1)) // edit selected watchpoint (same as edit key) + { + submenuWatch = sel; + submenuChoice = 1; + } + } + } + + if(input_ui_pressed(IPT_UI_CLEAR)) + { + /* ----- reset all watchpoints ----- */ + for(i = 0; i < watchListLength; i++) + { + if(watch) + { + watchList[i].address = 0; + watchList[i].cpu = 0; + watchList[i].numElements = 0; + watchList[i].elementBytes = kWatchSizeConversionTable[0]; + watchList[i].labelType = kWatchLabel_None; + watchList[i].displayType = kWatchDisplayType_Hex; + watchList[i].skip = 0; + watchList[i].elementsPerLine = 0; + watchList[i].addValue = 0; + + watchList[i].linkedCheat = NULL; + + watchList[i].label[0] = 0; + } + } + } + + if(UIPressedRepeatThrottle(IPT_UI_ZOOM_IN, kVerticalKeyRepeatRate)) + /* ----- quick menu switch : watchpoint list -> enable/disable ----- */ + sel = -2; + + if(UIPressedRepeatThrottle(IPT_UI_ZOOM_OUT, kVerticalKeyRepeatRate)) + /* ----- quick menu switch : watchpoint list -> search ----- */ + sel = -4; + + if(input_ui_pressed(IPT_UI_CANCEL)) + sel = -1; + + return sel + 1; +} + +/*-------------------------------------------------- + EditWatch - management for watchpoint edit menu +--------------------------------------------------*/ + +static int EditWatch(WatchInfo * entry, int selection) +{ + enum + { + kMenu_Address = 0, + kMenu_CPU, + kMenu_NumElements, + kMenu_ElementSize, + kMenu_LabelType, + kMenu_TextLabel, + kMenu_DisplayType, + kMenu_XPosition, + kMenu_YPosition, + kMenu_Skip, + kMenu_ElementsPerLine, + kMenu_AddValue, + kMenu_AddressShift, + kMenu_DataShift, + kMenu_XOR, + + kMenu_Return + }; + + static const char * kWatchSizeStringList[] = + { + "8 Bit", + "16 Bit", + "24 Bit", + "32 Bit" + }; + + const char ** menuItem; + const char ** menuSubItem; + char ** buf; + char * flagBuf; + + INT32 sel; + INT32 total = 0; + UINT32 increment = 1; + static UINT8 editActive = 0; + + /* ----- if no watch entry, direct return ----- */ + if(!entry) + return 0; + + RequestStrings(kMenu_Return + 2, kMenu_Return, 0, 20); + + menuItem = menuStrings.mainList; + menuSubItem = menuStrings.subList; + buf = menuStrings.subStrings; + flagBuf = menuStrings.flagList; + + memset(flagBuf, 0, kMenu_Return + 2); + + sel = selection - 1; + + /********** MENU CONSTRUCTION **********/ + sprintf(buf[total], "%.*X", cpuInfoList[entry->cpu].addressCharsNeeded, entry->address >> entry->addressShift); + menuItem[total] = "Address"; + menuSubItem[total] = buf[total]; + total++; + + sprintf(buf[total], "%d", entry->cpu); + menuItem[total] = "CPU"; + menuSubItem[total] = buf[total]; + total++; + + sprintf(buf[total], "%d", entry->numElements); + menuItem[total] = "Length"; + menuSubItem[total] = buf[total]; + total++; + + menuItem[total] = "Element Size"; + menuSubItem[total++] = kWatchSizeStringList[entry->elementBytes]; + + menuItem[total] = "Label Type"; + menuSubItem[total++] = kWatchLabelStringList[entry->labelType]; + + menuItem[total] = "Text Label"; + if(entry->label[0]) + menuSubItem[total++] = entry->label; + else + menuSubItem[total++] = "(none)"; + + menuItem[total] = "Display Type"; + menuSubItem[total++] = kWatchDisplayTypeStringList[entry->displayType]; + + sprintf(buf[total], "%f", entry->x); + menuItem[total] = "X"; + menuSubItem[total] = buf[total]; + total++; + + sprintf(buf[total], "%f", entry->y); + menuItem[total] = "Y"; + menuSubItem[total] = buf[total]; + total++; + + sprintf(buf[total], "%d", entry->skip); + menuItem[total] = "Skip Bytes"; + menuSubItem[total] = buf[total]; + total++; + + sprintf(buf[total], "%d", entry->elementsPerLine); + menuItem[total] = "Elements Per Line"; + menuSubItem[total] = buf[total]; + total++; + + if(entry->addValue < 0) + sprintf(buf[total], "-%.2X", -entry->addValue); + else + sprintf(buf[total], "%.2X", entry->addValue); + + menuItem[total] = "Add Value"; + menuSubItem[total] = buf[total]; + total++; + + sprintf(buf[total], "%d", entry->addressShift); + menuItem[total] = "Address Shift"; + menuSubItem[total] = buf[total]; + total++; + + sprintf(buf[total], "%d", entry->dataShift); + menuItem[total] = "Data Shift"; + menuSubItem[total] = buf[total]; + total++; + + sprintf(buf[total], "%.*X", kSearchByteDigitsTable[kWatchSizeConversionTable[entry->elementBytes]], entry->xor); + menuItem[total] = "XOR"; + menuSubItem[total] = buf[total]; + total++; + + menuItem[total] = ui_getstring(UI_returntoprior); // return + menuSubItem[total++] = NULL; + + menuItem[total] = NULL; // terminate array + menuSubItem[total] = NULL; + + /* ----- adjust current cursor position ----- */ + if(sel < 0) + sel = 0; + if(sel >= total) + sel = total - 1; + + /* ----- higlighted sub-item if edit mode ----- */ + if(editActive) + flagBuf[sel] = 1; + + /* ----- draw it ----- */ + old_style_menu(menuItem, menuSubItem, flagBuf, sel, 0); + + /********** KEY HANDLING **********/ + if(AltKeyPressed()) + increment <<= 4; + if(ControlKeyPressed()) + increment <<= 8; + if(ShiftKeyPressed()) + increment <<= 16; + + if(UIPressedRepeatThrottle(IPT_UI_LEFT, kHorizontalSlowKeyRepeatRate)) + { + editActive = 0; + + switch(sel) + { + case kMenu_Address: + entry->address = DoShift(entry->address, entry->addressShift); + entry->address -= increment; + entry->address = DoShift(entry->address, -entry->addressShift); + + if(entry->locationType != kLocation_MemoryRegion) + entry->address &= cpuInfoList[entry->cpu].addressMask; + + break; + + case kMenu_CPU: + entry->cpu--; + + if(entry->cpu >= cpu_gettotalcpu()) + entry->cpu = cpu_gettotalcpu() - 1; + + entry->address &= cpuInfoList[entry->cpu].addressMask; + break; + + case kMenu_NumElements: + if(entry->numElements > 0) + entry->numElements--; + else + entry->numElements = 0; + break; + + case kMenu_ElementSize: + if(entry->elementBytes > 0) + entry->elementBytes--; + else + entry->elementBytes = 0; + + entry->xor &= kSearchByteMaskTable[kWatchSizeConversionTable[entry->elementBytes]]; + break; + + case kMenu_LabelType: + if(entry->labelType > 0) + entry->labelType--; + else + entry->labelType = 0; + break; + + case kMenu_TextLabel: + break; + + case kMenu_DisplayType: + if(entry->displayType > 0) + entry->displayType--; + else + entry->displayType = 0; + break; + + case kMenu_XPosition: + entry->x -= 0.01; + break; + + case kMenu_YPosition: + entry->y -= 0.01; + break; + + case kMenu_Skip: + if(entry->skip > 0) + entry->skip--; + break; + + case kMenu_ElementsPerLine: + if(entry->elementsPerLine > 0) + entry->elementsPerLine--; + break; + + case kMenu_AddValue: + entry->addValue = (entry->addValue - 1) & 0xFF; + break; + + case kMenu_AddressShift: + if(entry->addressShift > -31) + entry->addressShift--; + else + entry->addressShift = 31; + break; + + case kMenu_DataShift: + if(entry->dataShift > -31) + entry->dataShift--; + else + entry->dataShift = 31; + break; + + case kMenu_XOR: + entry->xor -= increment; + entry->xor &= kSearchByteMaskTable[kWatchSizeConversionTable[entry->elementBytes]]; + break; + } + } + + if(UIPressedRepeatThrottle(IPT_UI_RIGHT, kHorizontalSlowKeyRepeatRate)) + { + editActive = 0; + + switch(sel) + { + case kMenu_Address: + entry->address = DoShift(entry->address, entry->addressShift); + entry->address += increment; + entry->address = DoShift(entry->address, -entry->addressShift); + entry->address &= cpuInfoList[entry->cpu].addressMask; + break; + + case kMenu_CPU: + entry->cpu++; + + if(entry->cpu >= cpu_gettotalcpu()) + entry->cpu = 0; + + entry->address &= cpuInfoList[entry->cpu].addressMask; + break; + + case kMenu_NumElements: + entry->numElements++; + break; + + case kMenu_ElementSize: + if(entry->elementBytes < kSearchSize_32Bit) + entry->elementBytes++; + else + entry->elementBytes = kSearchSize_32Bit; + + entry->xor &= kSearchByteMaskTable[kWatchSizeConversionTable[entry->elementBytes]]; + break; + + case kMenu_LabelType: + if(entry->labelType < kWatchLabel_MaxPlusOne - 1) + entry->labelType++; + else + entry->labelType = kWatchLabel_MaxPlusOne - 1; + break; + + case kMenu_TextLabel: + break; + + case kMenu_DisplayType: + if(entry->displayType < kWatchDisplayType_MaxPlusOne - 1) + entry->displayType++; + else + entry->displayType = kWatchDisplayType_MaxPlusOne - 1; + break; + + case kMenu_XPosition: + entry->x += 0.01; + break; + + case kMenu_YPosition: + entry->y += 0.01; + break; + + case kMenu_Skip: + entry->skip++; + break; + + case kMenu_ElementsPerLine: + entry->elementsPerLine++; + break; + + case kMenu_AddValue: + entry->addValue = (entry->addValue + 1) & 0xFF; + break; + + case kMenu_AddressShift: + if(entry->addressShift < 31) + entry->addressShift++; + else + entry->addressShift = -31; + break; + + case kMenu_DataShift: + if(entry->dataShift < 31) + entry->dataShift++; + else + entry->dataShift = -31; + break; + + case kMenu_XOR: + entry->xor += increment; + entry->xor &= kSearchByteMaskTable[kWatchSizeConversionTable[entry->elementBytes]]; + break; + } + } + + if(UIPressedRepeatThrottle(IPT_UI_DOWN, kVerticalKeyRepeatRate)) + { + sel++; + + if(sel >= total) + sel = 0; + + editActive = 0; + } + + if(UIPressedRepeatThrottle(IPT_UI_UP, kVerticalKeyRepeatRate)) + { + sel--; + + if(sel < 0) + sel = total - 1; + + editActive = 0; + } + + if(UIPressedRepeatThrottle(IPT_UI_PAGE_UP, kVerticalKeyRepeatRate)) + { + sel -= fullMenuPageHeight; + + if(sel < 0) + sel = 0; + + editActive = 0; + } + + if(UIPressedRepeatThrottle(IPT_UI_PAGE_DOWN, kVerticalKeyRepeatRate)) + { + sel += fullMenuPageHeight; + + if(sel >= total) + sel = total - 1; + + editActive = 0; + } + + if(input_ui_pressed(IPT_UI_SELECT)) + { + if(editActive) + editActive = 0; + else + { + switch(sel) + { + case kMenu_Return: + sel = -1; + break; + + case kMenu_Address: + case kMenu_CPU: + case kMenu_NumElements: + case kMenu_TextLabel: + case kMenu_XPosition: + case kMenu_YPosition: + case kMenu_AddValue: + case kMenu_AddressShift: + case kMenu_DataShift: + case kMenu_XOR: + osd_readkey_unicode(1); + editActive = 1; + } + } + } + + if(editActive) + { + switch(sel) + { + case kMenu_Address: + entry->address = DoShift(entry->address, entry->addressShift); + entry->address = DoEditHexField(entry->address); + entry->address = DoShift(entry->address, -entry->addressShift); + entry->address &= cpuInfoList[entry->cpu].addressMask; + break; + + case kMenu_CPU: + entry->cpu = DoEditDecField(entry->cpu, 0, cpu_gettotalcpu() - 1); + entry->address &= cpuInfoList[entry->cpu].addressMask; + break; + + case kMenu_NumElements: + entry->numElements = DoEditDecField(entry->numElements, 0, 99); + break; + + case kMenu_TextLabel: + DoStaticEditTextField(entry->label, 255); + break; + + case kMenu_XPosition: + entry->x = DoEditDecField(entry->x, -1000, 1000); + break; + + case kMenu_YPosition: + entry->y = DoEditDecField(entry->y, -1000, 1000); + break; + + case kMenu_AddValue: + entry->addValue = DoEditHexFieldSigned(entry->addValue, 0xFFFFFF80) & 0xFF; + break; + + case kMenu_AddressShift: + entry->addressShift = DoEditDecField(entry->addressShift, -31, 31); + break; + + case kMenu_DataShift: + entry->dataShift = DoEditDecField(entry->dataShift, -31, 31); + break; + + case kMenu_XOR: + entry->xor = DoEditHexField(entry->xor); + entry->xor &= kSearchByteMaskTable[kWatchSizeConversionTable[entry->elementBytes]]; + break; + } + + if(input_ui_pressed(IPT_UI_CANCEL)) + editActive = 0; + } + else + { + if(input_ui_pressed(IPT_UI_ADD_CHEAT)) + AddCheatFromWatch(entry); + + if(input_ui_pressed(IPT_UI_DELETE_CHEAT)) + entry->numElements = 0; + + if(input_ui_pressed(IPT_UI_SAVE_CHEAT)) + { + CheatEntry tempEntry; + + memset(&tempEntry, 0, sizeof(CheatEntry)); + + SetupCheatFromWatchAsWatch(&tempEntry, entry); + SaveCheat(&tempEntry, 0, 0); + DisposeCheat(&tempEntry); + } + } + + if(input_ui_pressed(IPT_UI_CANCEL)) + sel = -1; + + return sel + 1; +} + +/*---------------------------------------------- + SelectOptions - management for options menu +----------------------------------------------*/ + +static int SelectOptions(int selection) +{ + enum + { + kMenu_SelectSearch = 0, + kMenu_SearchDialogStyle, + kMenu_ShowSearchLabels, + kMenu_AutoSaveCheats, + kMenu_ShowActivationKeyMessage, + kMenu_LoadOldFormat, + kMenu_Debug, + kMenu_Return, + + kMenu_Max + }; + + INT32 sel; + static INT32 submenuChoice = 0; + const char * menuItem[kMenu_Max + 1]; + const char * menuSubItem[kMenu_Max + 1]; + int total = 0; + + sel = selection - 1; + + /********** SUB MENU **********/ + if(submenuChoice) + { + switch(sel) + { + case kMenu_SelectSearch: + submenuChoice = SelectSearch(submenuChoice); + break; + + default: + submenuChoice = 0; + sel = -1; + break; + } + + if(submenuChoice == -1) + submenuChoice = 0; + + return sel + 1; + } + + /********** MENU CONSTRUCTION **********/ + menuItem[total] = "Select Search"; + menuSubItem[total++] = NULL; + + menuItem[total] = "Search Dialog Style"; + switch(EXTRACT_FIELD(cheatOptions, SearchBox)) + { + case kSearchBox_Minimum: + menuSubItem[total++] = "Minimum"; + break; + + case kSearchBox_Classic: + menuSubItem[total++] = "Classic"; + break; + + case kSearchBox_Advanced: + menuSubItem[total++] = "Advanced"; + break; + } + + menuItem[total] = "Show Search Labels"; + menuSubItem[total++] = ui_getstring(TEST_FIELD(cheatOptions, DontPrintNewLabels) ? UI_off : UI_on); + + menuItem[total] = "Auto Save Cheats"; + menuSubItem[total++] = ui_getstring(TEST_FIELD(cheatOptions, AutoSaveEnabled) ? UI_on : UI_off); + + menuItem[total] = "Show Activation Key Message"; + menuSubItem[total++] = ui_getstring(TEST_FIELD(cheatOptions, ActivationKeyMessage) ? UI_on : UI_off); + + menuItem[total] = "Load Old Format Code"; + menuSubItem[total++] = ui_getstring(TEST_FIELD(cheatOptions, LoadOldFormat) ? UI_on : UI_off); + + menuItem[total] = "Debug"; + menuSubItem[total++] = ui_getstring(TEST_FIELD(cheatOptions, Debug) ? UI_on : UI_off); + + menuItem[total] = ui_getstring(UI_returntoprior); // return + menuSubItem[total++] = NULL; + + menuItem[total] = NULL; // terminate array + menuSubItem[total] = NULL; + + /* ----- draw it ----- */ + old_style_menu(menuItem, menuSubItem, NULL, sel, 0); + + /********** KEY HANDLING **********/ + if(UIPressedRepeatThrottle(IPT_UI_RIGHT, kHorizontalSlowKeyRepeatRate)) + { + switch(sel) + { + case kMenu_SearchDialogStyle: + { + UINT8 length = (EXTRACT_FIELD(cheatOptions, SearchBox) + 1) & 3; + + if(length > kSearchBox_Advanced) + length = kSearchBox_Minimum; + + SET_FIELD(cheatOptions, SearchBox, length); + } + break; + + case kMenu_ShowSearchLabels: + TOGGLE_MASK_FIELD(cheatOptions, DontPrintNewLabels); + break; + + case kMenu_AutoSaveCheats: + TOGGLE_MASK_FIELD(cheatOptions, AutoSaveEnabled); + break; + + case kMenu_ShowActivationKeyMessage: + TOGGLE_MASK_FIELD(cheatOptions, ActivationKeyMessage); + break; + + case kMenu_LoadOldFormat: + TOGGLE_MASK_FIELD(cheatOptions, LoadOldFormat); + break; + + case kMenu_Debug: + TOGGLE_MASK_FIELD(cheatOptions, Debug); + break; + } + } + + if(UIPressedRepeatThrottle(IPT_UI_LEFT, kHorizontalSlowKeyRepeatRate)) + { + switch(sel) + { + case kMenu_SearchDialogStyle: + { + UINT8 length = (EXTRACT_FIELD(cheatOptions, SearchBox) - 1) & 3; + + if(length > kSearchBox_Advanced) + length = kSearchBox_Advanced; + + SET_FIELD(cheatOptions, SearchBox, length); + } + break; + + case kMenu_ShowSearchLabels: + TOGGLE_MASK_FIELD(cheatOptions, DontPrintNewLabels); + break; + + case kMenu_AutoSaveCheats: + TOGGLE_MASK_FIELD(cheatOptions, AutoSaveEnabled); + break; + + case kMenu_ShowActivationKeyMessage: + TOGGLE_MASK_FIELD(cheatOptions, ActivationKeyMessage); + break; + + case kMenu_LoadOldFormat: + TOGGLE_MASK_FIELD(cheatOptions, LoadOldFormat); + break; + + case kMenu_Debug: + TOGGLE_MASK_FIELD(cheatOptions, Debug); + break; + } + } + + if(UIPressedRepeatThrottle(IPT_UI_DOWN, kVerticalKeyRepeatRate)) + { + if(sel < (kMenu_Max - 1)) + sel++; + else + sel = 0; + } + + if(UIPressedRepeatThrottle(IPT_UI_UP, kVerticalKeyRepeatRate)) + { + if(sel > 0) + sel--; + else + sel = kMenu_Max - 1; + } + + if(input_ui_pressed(IPT_UI_SELECT)) + { + switch(sel) + { + case kMenu_Return: + submenuChoice = 0; + sel = -1; + break; + + case kMenu_SelectSearch: + submenuChoice = 1; + break; + } + } + + if(input_ui_pressed(IPT_UI_SAVE_CHEAT)) + { + SaveCheat(NULL, 0, 2); + + ui_popup_time(1, "command code saved"); + } + + if(input_ui_pressed(IPT_UI_RELOAD_CHEAT)) + ReloadCheatDatabase(); + + if(input_ui_pressed(IPT_UI_CANCEL)) + sel = -1; + + return sel + 1; +} + +/*------------------------------------------------------ + SelectSearch - management for search selection menu +------------------------------------------------------*/ + +static int SelectSearch(int selection) +{ + INT32 sel; + const char ** menuItem; + char ** buf; + INT32 i; + INT32 total = 0; + + sel = selection - 1; + + RequestStrings(searchListLength + 2, searchListLength, 300, 0); + + menuItem = menuStrings.mainList; + buf = menuStrings.mainStrings; + + /********** MENU CONSTRUCTION **********/ + for(i = 0; i < searchListLength; i++) + { + SearchInfo * info = &searchList[i]; + + if(i == currentSearchIdx) + { + if(info->name) + sprintf(buf[total], "[#%d: %s]", i, info->name); + else + sprintf(buf[total], "[#%d]", i); + } + else + { + if(info->name) + sprintf(buf[total], "#%d: %s", i, info->name); + else + sprintf(buf[total], "#%d", i); + } + + menuItem[total] = buf[total]; + total++; + } + + menuItem[total++] = ui_getstring(UI_returntoprior); // return + + menuItem[total] = NULL; // terminate array + + /* ----- adjust current cursor position ----- */ + if(sel < 0) + sel = 0; + if(sel > (total - 1)) + sel = total - 1; + + /* ----- draw it ----- */ + old_style_menu(menuItem, NULL, NULL, sel, 0); + + /********** KEY HANDLING **********/ + if(UIPressedRepeatThrottle(IPT_UI_DOWN, kVerticalKeyRepeatRate)) + { + sel++; + + if(sel >= total) + sel = 0; + } + + if(UIPressedRepeatThrottle(IPT_UI_UP, kVerticalKeyRepeatRate)) + { + sel--; + + if(sel < 0) + sel = total - 1; + } + + if(UIPressedRepeatThrottle(IPT_UI_PAGE_UP, kVerticalKeyRepeatRate)) + { + sel -= fullMenuPageHeight; + + if(sel < 0) + sel = 0; + } + + if(UIPressedRepeatThrottle(IPT_UI_PAGE_DOWN, kVerticalKeyRepeatRate)) + { + sel += fullMenuPageHeight; + + if(sel >= total) + sel = total - 1; + } + + if(input_ui_pressed(IPT_UI_ADD_CHEAT)) + { + AddSearchBefore(sel); + + BuildSearchRegions(&searchList[sel]); + AllocateSearchRegions(&searchList[sel]); + } + + if(input_ui_pressed(IPT_UI_DELETE_CHEAT)) + { + if(searchListLength > 1) + DeleteSearchAt(sel); + } + + if(input_ui_pressed(IPT_UI_EDIT_CHEAT)) + { + if(sel < total - 1) + currentSearchIdx = sel; + } + + if(input_ui_pressed(IPT_UI_SELECT)) + { + if(sel >= total - 1) + sel = -1; + else + currentSearchIdx = sel; + } + + if(input_ui_pressed(IPT_UI_CANCEL)) + sel = -1; + + return sel + 1; +} + +/*------------------ + cheate_periodic +------------------*/ + +static TIMER_CALLBACK( cheat_periodic ) +{ + int i; + + if(input_ui_pressed(IPT_UI_TOGGLE_CHEAT)) + { + if(ShiftKeyPressed()) + { + /* ------ toggle watchpoint display if shift + toggle cheat ----- */ + watchesDisabled ^= 1; + + ui_popup_time(1, "%s %s", ui_getstring(UI_watchpoints), watchesDisabled ? ui_getstring (UI_off) : ui_getstring (UI_on)); + } + else + { + /* ------ toggle cheat ----- */ + cheatsDisabled ^= 1; + + ui_popup_time(1, "%s %s", ui_getstring(UI_cheats), cheatsDisabled ? ui_getstring (UI_off) : ui_getstring (UI_on)); + + if(cheatsDisabled) + { + for(i = 0; i < cheatListLength; i++) + TempDeactivateCheat(&cheatList[i]); + } + } + } + + if(cheatsDisabled) + return; + + for(i = 0; i < cheatListLength; i++) + cheat_periodicEntry(&cheatList[i]); +} + +/*-------------- + PrintBinary +--------------*/ + +static UINT32 PrintBinary(char * buf, UINT32 data, UINT32 mask) +{ + UINT32 traverse = 0x80000000; + UINT32 written = 0; + + while(traverse) + { + if(mask & traverse) + { + *buf++ = (data & traverse) ? '1' : '0'; + written++; + } + + traverse >>= 1; + } + + *buf++ = 0; + + return written; +} + +/*------------- + PrintASCII +-------------*/ + +static UINT32 PrintASCII(char * buf, UINT32 data, UINT8 size) +{ + switch(size) + { + case kSearchSize_8Bit: + case kSearchSize_1Bit: + default: + buf[0] = (data >> 0) & 0xFF; + buf[1] = 0; + + return 1; + + case kSearchSize_16Bit: + buf[0] = (data >> 8) & 0xFF; + buf[1] = (data >> 0) & 0xFF; + buf[2] = 0; + + return 2; + + case kSearchSize_24Bit: + buf[0] = (data >> 16)& 0xFF; + buf[1] = (data >> 8) & 0xFF; + buf[2] = (data >> 0) & 0xFF; + buf[3] = 0; + + return 3; + + case kSearchSize_32Bit: + buf[0] = (data >> 24) & 0xFF; + buf[1] = (data >> 16) & 0xFF; + buf[2] = (data >> 8) & 0xFF; + buf[3] = (data >> 0) & 0xFF; + buf[4] = 0; + + return 4; + } + + buf[0] = 0; + return 0; +} + +/*--------------------------------------------- + cheat_display_watches - display watchpoint +---------------------------------------------*/ + +void cheat_display_watches(void) +{ + int i; + + if(watchesDisabled) + return; + + for(i = 0; i < watchListLength; i++) + { + int j; + WatchInfo * info = &watchList[i]; + char buf[1024]; + UINT32 address = info->address; + int xOffset = 0, yOffset = 0; + int numChars; + int lineElements = 0; + + /* ----- if the length of the watchpoint is not 0, display it ----- */ + if(info->numElements) + { + /* ----- label display ----- */ + switch(info->labelType) + { + case kWatchLabel_Address: + numChars = sprintf(buf, "%.8X: ", info->address); + + ui_draw_text(buf, xOffset * ui_get_char_width('0') + info->x, yOffset * ui_get_line_height() + info->y); + xOffset += numChars; + break; + + case kWatchLabel_String: + numChars = sprintf(buf, "%s: ", info->label); + + ui_draw_text(buf, xOffset * ui_get_char_width('0') + info->x, yOffset * ui_get_line_height() + info->y); + xOffset += numChars; + break; + } + + /* ----- value display ----- */ + for(j = 0; j < info->numElements; j++) + { + UINT32 data = 0; + + switch(info->locationType) + { + case kLocation_Standard: + data = (DoCPURead(info->cpu, address, kSearchByteIncrementTable[info->elementBytes], CPUNeedsSwap(info->cpu)) + info->addValue) & + kSearchByteMaskTable[info->elementBytes]; + break; + + case kLocation_MemoryRegion: + { + int region = REGION_CPU1 + info->cpu; + UINT8 * buf = memory_region(region); + + data = DoMemoryRead(buf, address, kSearchByteIncrementTable[info->elementBytes], RegionNeedsSwap(region) + info->addValue, GetRegionCPUInfo(region)) & + kSearchByteMaskTable[info->elementBytes]; + } + break; + + case kLocation_IndirectIndexed: + return; // under construction... + + case kLocation_ProgramSpace: + { + UINT8 * buf; + + memory_set_opbase(address); + + buf = memory_get_op_ptr(info->cpu, address, 0); + + buf -= address; + + if(buf) + data = DoMemoryRead(buf, address, kSearchByteIncrementTable[info->elementBytes], CPUNeedsSwap(info->cpu) + info->addValue, GetCPUInfo(info->cpu)) & + kSearchByteMaskTable[info->elementBytes]; + } + break; + + default: + logerror("invalid location type : %x\n", info->locationType); + return; + + } + + data = DoShift(data, info->dataShift); + data ^= info->xor; + + if( (lineElements >= info->elementsPerLine) && info->elementsPerLine) + { + lineElements = 0; + + xOffset = 0; + yOffset++; + } + + switch(info->displayType) + { + case kWatchDisplayType_Hex: + numChars = sprintf(buf, "%.*X", kSearchByteDigitsTable[info->elementBytes], data); + + ui_draw_text(buf, xOffset * ui_get_char_width('0') + info->x, yOffset * ui_get_line_height() + info->y); + xOffset += numChars; + xOffset++; + break; + + case kWatchDisplayType_Decimal: + numChars = sprintf(buf, "%.*d", kSearchByteDecDigitsTable[info->elementBytes], data); + + ui_draw_text(buf, xOffset * ui_get_char_width('0') + info->x, yOffset * ui_get_line_height() + info->y); + xOffset += numChars; + xOffset++; + break; + + case kWatchDisplayType_Binary: + numChars = PrintBinary(buf, data, kSearchByteMaskTable[info->elementBytes]); + + ui_draw_text(buf, xOffset * ui_get_char_width('0') + info->x, yOffset * ui_get_line_height() + info->y); + xOffset += numChars; + xOffset++; + break; + + case kWatchDisplayType_ASCII: + numChars = PrintASCII(buf, data, info->elementBytes); + + ui_draw_text(buf, xOffset * ui_get_char_width('0') + info->x, yOffset * ui_get_line_height() + info->y); + xOffset += numChars; + break; + } + + address += kSearchByteIncrementTable[info->elementBytes] + info->skip; + lineElements++; + } + } + } +} + +/*------------------ + ResizeCheatList +------------------*/ + +static void ResizeCheatList(UINT32 newLength) +{ + if(newLength != cheatListLength) + { + if(newLength < cheatListLength) + { + int i; + + for(i = newLength; i < cheatListLength; i++) + DisposeCheat(&cheatList[i]); + } + + cheatList = realloc(cheatList, newLength * sizeof(CheatEntry)); + + if(!cheatList && (newLength != 0)) + { + logerror("ResizeCheatList: out of memory resizing cheat list\n"); + ui_popup_time(2, "out of memory while loading cheat database"); + + cheatListLength = 0; + + return; + } + + if(newLength > cheatListLength) + { + int i; + + memset(&cheatList[cheatListLength], 0, (newLength - cheatListLength) * sizeof(CheatEntry)); + + for(i = cheatListLength; i < newLength; i++) + cheatList[i].flags |= kCheatFlag_Dirty; + } + + cheatListLength = newLength; + } +} + +/*--------------------------- + ResizeCheatListNoDispose +---------------------------*/ + +static void ResizeCheatListNoDispose(UINT32 newLength) +{ + if(newLength != cheatListLength) + { + cheatList = realloc(cheatList, newLength * sizeof(CheatEntry)); + + if(!cheatList && (newLength != 0)) + { + logerror("ResizeCheatListNoDispose: out of memory resizing cheat list\n"); + ui_popup_time(2, "out of memory while loading cheat database"); + + cheatListLength = 0; + + return; + } + + if(newLength > cheatListLength) + { + int i; + + memset(&cheatList[cheatListLength], 0, (newLength - cheatListLength) * sizeof(CheatEntry)); + + for(i = cheatListLength; i < newLength; i++) + cheatList[i].flags |= kCheatFlag_Dirty; + } + + cheatListLength = newLength; + } +} + +/*--------------------------------------------------------------- + AddCheatBefore - insert new cheat entry before selected code +---------------------------------------------------------------*/ + +static void AddCheatBefore(UINT32 idx) +{ + /* ----- memory reallocation ----- */ + ResizeCheatList(cheatListLength + 1); + + if(idx < (cheatListLength - 1)) + memmove(&cheatList[idx + 1], &cheatList[idx], sizeof(CheatEntry) * (cheatListLength - 1 - idx)); + + if(idx >= cheatListLength) + idx = cheatListLength - 1; + + /* ----- insert new entry ----- */ + memset(&cheatList[idx], 0, sizeof(CheatEntry)); + + cheatList[idx].flags |= kCheatFlag_Dirty; + + ResizeCheatActionList(&cheatList[idx], 1); +} + +/*---------------- + DeleteCheatAt +----------------*/ + +static void DeleteCheatAt(UINT32 idx) +{ + if(idx >= cheatListLength) + return; + + DisposeCheat(&cheatList[idx]); + + if(idx < (cheatListLength - 1)) + { + memmove(&cheatList[idx], &cheatList[idx + 1], sizeof(CheatEntry) * (cheatListLength - 1 - idx)); + } + + ResizeCheatListNoDispose(cheatListLength - 1); +} + +/*------------------------------------------------ + DisposeCheat - free memory for selected entry +------------------------------------------------*/ + +static void DisposeCheat(CheatEntry * entry) +{ + if(entry) + { + int i; + + free(entry->name); + free(entry->comment); + + for(i = 0; i < entry->actionListLength; i++) + { + CheatAction * action = &entry->actionList[i]; + + DisposeAction(action); + } + + free(entry->actionList); + + memset(entry, 0, sizeof(CheatEntry)); + } +} + +/*------------------------------------ + GetNewCheat - get new cheat entry +------------------------------------*/ + +static CheatEntry * GetNewCheat(void) +{ + /* ----- insert new cheat entry ----- */ + AddCheatBefore(cheatListLength); + + /* ----- return inserted entry ----- */ + return &cheatList[cheatListLength - 1]; +} + +/*------------------------ + ResizeCheatActionList +------------------------*/ + +static void ResizeCheatActionList(CheatEntry * entry, UINT32 newLength) +{ + if(newLength != entry->actionListLength) + { + if(newLength < entry->actionListLength) + { + int i; + + for(i = newLength; i < entry->actionListLength; i++) + DisposeAction(&entry->actionList[i]); + } + + entry->actionList = realloc(entry->actionList, newLength * sizeof(CheatAction)); + if(!entry->actionList && (newLength != 0)) + { + logerror("ResizeCheatActionList: out of memory resizing cheat action list\n"); + ui_popup_time(2, "out of memory while loading cheat database"); + + entry->actionListLength = 0; + + return; + } + + if(newLength > entry->actionListLength) + { + memset(&entry->actionList[entry->actionListLength], 0, (newLength - entry->actionListLength) * sizeof(CheatAction)); + } + + entry->actionListLength = newLength; + } +} + +/*--------------------------------- + ResizeCheatActionListNoDispose +---------------------------------*/ + +static void ResizeCheatActionListNoDispose(CheatEntry * entry, UINT32 newLength) +{ + if(newLength != entry->actionListLength) + { + entry->actionList = realloc(entry->actionList, newLength * sizeof(CheatAction)); + if(!entry->actionList && (newLength != 0)) + { + logerror("ResizeCheatActionList: out of memory resizing cheat action list\n"); + ui_popup_time(2, "out of memory while loading cheat database"); + + entry->actionListLength = 0; + + return; + } + + if(newLength > entry->actionListLength) + { + memset(&entry->actionList[entry->actionListLength], 0, (newLength - entry->actionListLength) * sizeof(CheatAction)); + } + + entry->actionListLength = newLength; + } +} + +/*------------------ + AddActionBefore +------------------*/ + +static void AddActionBefore(CheatEntry * entry, UINT32 idx) +{ + ResizeCheatActionList(entry, entry->actionListLength + 1); + + if(idx < (entry->actionListLength - 1)) + memmove(&entry->actionList[idx + 1], &entry->actionList[idx], sizeof(CheatAction) * (entry->actionListLength - 1 - idx)); + + if(idx >= entry->actionListLength) + idx = entry->actionListLength - 1; + + memset(&entry->actionList[idx], 0, sizeof(CheatAction)); +} + +/*----------------- + DeleteActionAt +-----------------*/ + +static void DeleteActionAt(CheatEntry * entry, UINT32 idx) +{ + if(idx >= entry->actionListLength) + return; + + DisposeAction(&entry->actionList[idx]); + + if(idx < (entry->actionListLength - 1)) + memmove(&entry->actionList[idx], &entry->actionList[idx + 1], sizeof(CheatAction) * (entry->actionListLength - 1 - idx)); + + ResizeCheatActionListNoDispose(entry, entry->actionListLength - 1); +} + +/*---------------- + DisposeAction +----------------*/ + +static void DisposeAction(CheatAction * action) +{ + if(action) + { + free(action->optionalName); + + memset(action, 0, sizeof(CheatAction)); + } +} + +/*---------------------------------------------------- + InitWatch - initialize y-position for watchpoints +----------------------------------------------------*/ + +static void InitWatch(WatchInfo * info, UINT32 idx) +{ + if(idx > 0) + /* ----- set 2nd or later y-position ----- */ + info->y = watchList[idx - 1].y + ui_get_line_height(); + else + /* ----- set 1st y-position (always Y=0) ----- */ + info->y = 0; +} + +/*------------------ + ResizeWatchList +------------------*/ + +static void ResizeWatchList(UINT32 newLength) +{ + if(newLength != watchListLength) + { + if(newLength < watchListLength) + { + int i; + + for(i = newLength; i < watchListLength; i++) + DisposeWatch(&watchList[i]); + } + + watchList = realloc(watchList, newLength * sizeof(WatchInfo)); + if(!watchList && (newLength != 0)) + { + logerror("ResizeWatchList: out of memory resizing watch list\n"); + ui_popup_time(2, "out of memory while adding watch"); + + watchListLength = 0; + + return; + } + + if(newLength > watchListLength) + { + int i; + + memset(&watchList[watchListLength], 0, (newLength - watchListLength) * sizeof(WatchInfo)); + + for(i = watchListLength; i < newLength; i++) + InitWatch(&watchList[i], i); + } + + watchListLength = newLength; + } +} + +/*--------------------------- + ResizeWatchListNoDispose +---------------------------*/ + +static void ResizeWatchListNoDispose(UINT32 newLength) +{ + if(newLength != watchListLength) + { + watchList = realloc(watchList, newLength * sizeof(WatchInfo)); + if(!watchList && (newLength != 0)) + { + logerror("ResizeWatchList: out of memory resizing watch list\n"); + ui_popup_time(2, "out of memory while adding watch"); + + watchListLength = 0; + + return; + } + + if(newLength > watchListLength) + { + int i; + + memset(&watchList[watchListLength], 0, (newLength - watchListLength) * sizeof(WatchInfo)); + + for(i = watchListLength; i < newLength; i++) + InitWatch(&watchList[i], i); + } + + watchListLength = newLength; + } +} + +/*----------------- + AddWatchBefore +-----------------*/ + +static void AddWatchBefore(UINT32 idx) +{ + ResizeWatchList(watchListLength + 1); + + if(idx < (watchListLength - 1)) + memmove(&watchList[idx + 1], &watchList[idx], sizeof(WatchInfo) * (watchListLength - 1 - idx)); + + if(idx >= watchListLength) + idx = watchListLength - 1; + + memset(&watchList[idx], 0, sizeof(WatchInfo)); + + InitWatch(&watchList[idx], idx); +} + +/*---------------- + DeleteWatchAt +----------------*/ + +static void DeleteWatchAt(UINT32 idx) +{ + if(idx >= watchListLength) + return; + + DisposeWatch(&watchList[idx]); + + if(idx < (watchListLength - 1)) + memmove(&watchList[idx], &watchList[idx + 1], sizeof(WatchInfo) * (watchListLength - 1 - idx)); + + ResizeWatchListNoDispose(watchListLength - 1); +} + +/*--------------- + DisposeWatch +---------------*/ + +static void DisposeWatch(WatchInfo * watch) +{ + if(watch) + memset(watch, 0, sizeof(WatchInfo)); +} + +/*---------------------------------------------------------- + GetUnusedWatch - search unused watchpoint and return it +----------------------------------------------------------*/ + +static WatchInfo * GetUnusedWatch(void) +{ + int i; + WatchInfo * info; + WatchInfo * theWatch = NULL; + + /* ----- search unused watchpoint ----- */ + for(i = 0; i < watchListLength; i++) + { + info = &watchList[i]; + + if(info->numElements == 0) + { + theWatch = info; + break; + } + } + + /* ----- if unused watchpoint is not found, insert new watchpoint as unused ----- */ + if(!theWatch) + { + AddWatchBefore(watchListLength); + + theWatch = &watchList[watchListLength - 1]; + } + + /* ----- return unused watchpoint ----- */ + return theWatch; +} + +/*---------------------------------------------------------------------------- + AddCheatFromWatch - add new cheat code from watchpoint list to cheat list +----------------------------------------------------------------------------*/ + +static void AddCheatFromWatch(WatchInfo * watch) +{ + if(watch) + { + CheatEntry * entry = GetNewCheat(); + CheatAction * action = &entry->actionList[0]; + char tempString[1024]; + int tempStringLength; + UINT32 data = DoCPURead(watch->cpu, watch->address, kSearchByteIncrementTable[watch->elementBytes], 0); + + tempStringLength = sprintf(tempString, "%.8X (%d) = %.*X", watch->address, watch->cpu, kSearchByteDigitsTable[watch->elementBytes], data); + + entry->name = realloc(entry->name, tempStringLength + 1); + memcpy(entry->name, tempString, tempStringLength + 1); + + SET_FIELD(action->type, LocationParameter, watch->cpu); + SET_FIELD(action->type, BytesUsed, kSearchByteIncrementTable[watch->elementBytes] - 1); + action->address = watch->address; + action->data = data; + action->extendData = 0xFFFFFFFF; + action->originalDataField = data; + + UpdateCheatInfo(entry, 0); + } +} + +/*------------------------------------------------------------------------------------- + SetupCheatFromWatchAsWatch - add new watch code from watchpoint list to cheat list +-------------------------------------------------------------------------------------*/ + +static void SetupCheatFromWatchAsWatch(CheatEntry * entry, WatchInfo * watch) +{ + if(watch && entry && watch->numElements) + { + CheatAction * action; + char tempString[1024]; + size_t tempStringLength; + + DisposeCheat(entry); + ResizeCheatActionList(entry, 1); + + action = &entry->actionList[0]; + + tempStringLength = sprintf(tempString, "Watch %.8X (%d)", watch->address, watch->cpu); + + entry->name = realloc(entry->name, tempStringLength + 1); + memcpy(entry->name, tempString, tempStringLength + 1); + + action->type = 0; + SET_FIELD(action->type, LocationParameter, watch->cpu); + SET_FIELD(action->type, Type, kType_Watch); + SET_FIELD(action->type, BytesUsed, kSearchByteIncrementTable[watch->elementBytes] - 1); + SET_FIELD(action->type, TypeParameter, watch->displayType | ((watch->labelType == kWatchLabel_String) ? 0x04 : 0)); + + action->address = watch->address; + action->data = ((watch->numElements - 1) & 0xFF) | + ((watch->skip & 0xFF) << 8) | + ((watch->elementsPerLine & 0xFF) << 16) | + ((watch->addValue & 0xFF) << 24); + action->originalDataField = action->data; + action->extendData = 0xFFFFFFFF; + + tempStringLength = strlen(watch->label); + entry->comment = realloc(entry->comment, tempStringLength + 1); + memcpy(entry->comment, watch->label, tempStringLength + 1); + + UpdateCheatInfo(entry, 0); + } +} + +/*------------------- + ResizeSearchList +-------------------*/ + +static void ResizeSearchList(UINT32 newLength) +{ + if(newLength != searchListLength) + { + if(newLength < searchListLength) + { + int i; + + for(i = newLength; i < searchListLength; i++) + DisposeSearch(i); + } + + searchList = realloc(searchList, newLength * sizeof(SearchInfo)); + if(!searchList && (newLength != 0)) + { + logerror("ResizeSearchList: out of memory resizing search list\n"); + ui_popup_time(2, "out of memory while adding search"); + + searchListLength = 0; + + return; + } + + if(newLength > searchListLength) + { + int i; + + memset(&searchList[searchListLength], 0, (newLength - searchListLength) * sizeof(SearchInfo)); + + for(i = searchListLength; i < newLength; i++) + { + InitSearch(&searchList[i]); + } + } + + searchListLength = newLength; + } +} + +/*---------------------------- + ResizeSearchListNoDispose +----------------------------*/ + +static void ResizeSearchListNoDispose(UINT32 newLength) +{ + if(newLength != searchListLength) + { + searchList = realloc(searchList, newLength * sizeof(SearchInfo)); + if(!searchList && (newLength != 0)) + { + logerror("ResizeSearchList: out of memory resizing search list\n"); + ui_popup_time(2, "out of memory while adding search"); + + searchListLength = 0; + + return; + } + + if(newLength > searchListLength) + { + memset(&searchList[searchListLength], 0, (newLength - searchListLength) * sizeof(SearchInfo)); + } + + searchListLength = newLength; + } +} + +/*------------------ + AddSearchBefore +------------------*/ + +static void AddSearchBefore(UINT32 idx) +{ + ResizeSearchListNoDispose(searchListLength + 1); + + if(idx < (searchListLength - 1)) + memmove(&searchList[idx + 1], &searchList[idx], sizeof(SearchInfo) * (searchListLength - 1 - idx)); + + if(idx >= searchListLength) + idx = searchListLength - 1; + + memset(&searchList[idx], 0, sizeof(SearchInfo)); + InitSearch(&searchList[idx]); +} + +/*----------------- + DeleteSearchAt +-----------------*/ + +static void DeleteSearchAt(UINT32 idx) +{ + if(idx >= searchListLength) + return; + + DisposeSearch(idx); + + if(idx < (searchListLength - 1)) + { + memmove(&searchList[idx], &searchList[idx + 1], sizeof(SearchInfo) * (searchListLength - 1 - idx)); + } + + ResizeSearchListNoDispose(searchListLength - 1); +} + +/*-------------------------------------- + InitSearch - initialize search info +--------------------------------------*/ + +static void InitSearch(SearchInfo * info) +{ + if(info) + { + info->searchSpeed = kSearchSpeed_Medium; + } +} + +/*----------------------- + DisposeSearchRegions +-----------------------*/ + +static void DisposeSearchRegions(SearchInfo * info) +{ + if(info->regionList) + { + int i; + + for(i = 0; i < info->regionListLength; i++) + { + SearchRegion * region = &info->regionList[i]; + + free(region->first); + free(region->last); + free(region->status); + free(region->backupLast); + free(region->backupStatus); + } + + free(info->regionList); + + info->regionList = NULL; + } + + info->regionListLength = 0; +} + +/*------------------ + DisposeSearch + ----------------*/ + +static void DisposeSearch(UINT32 idx) +{ + SearchInfo * info; + + if(idx >= searchListLength) + return; + + info = &searchList[idx]; + + DisposeSearchRegions(info); + + free(info->name); + info->name = NULL; +} + +/*---------------------------------------- + GetCurrentSearch - return search info +----------------------------------------*/ + +static SearchInfo * GetCurrentSearch(void) +{ + if(currentSearchIdx >= searchListLength) + currentSearchIdx = searchListLength - 1; + if(currentSearchIdx < 0) + currentSearchIdx = 0; + + return &searchList[currentSearchIdx]; +} + +/*------------------------------------------------------- + FillBufferFromRegion - set a data into search region +-------------------------------------------------------*/ + +static void FillBufferFromRegion(SearchRegion * region, UINT8 * buf) +{ + UINT32 offset; + + /* ### optimize if needed ### */ + for(offset = 0; offset < region->length; offset++) + buf[offset] = ReadRegionData(region, offset, 1, 0); +} + +/*------------------------------------------------------- + ReadRegionData - read a data from region when search +-------------------------------------------------------*/ + +static UINT32 ReadRegionData(SearchRegion * region, UINT32 offset, UINT8 size, UINT8 swap) +{ + UINT32 address = region->address + offset; + + switch(region->targetType) + { + case kRegionType_CPU: + return DoCPURead(region->targetIdx, address, size, CPUNeedsSwap(region->targetIdx) ^ swap); + + case kRegionType_Memory: + if(region->cachedPointer) + return DoMemoryRead(region->cachedPointer, address, size, swap, &rawCPUInfo); + else + return 0; + } + + return 0; +} +/*----------------------------------------------- + BackupSearch - back up current search region +-----------------------------------------------*/ + +static void BackupSearch(SearchInfo * info) +{ + int i; + + for(i = 0; i < info->regionListLength; i++) + BackupRegion(&info->regionList[i]); + + info->oldNumResults = info->numResults; + info->backupValid = 1; +} + +/*------------------------------------------------------- + RestoreSearchBackup - restore previous search region +-------------------------------------------------------*/ + +static void RestoreSearchBackup(SearchInfo * info) +{ + int i; + + if(info && info->backupValid) + { + for(i = 0; i < info->regionListLength; i++) + RestoreRegionBackup(&info->regionList[i]); + + info->numResults = info->oldNumResults; + info->backupValid = 0; + + ui_popup_time(1, "values restored"); + } + else + ui_popup_time(1, "there are no old values"); +} + +/*------------------------------------------------ + BackupRegion - back up current search results +------------------------------------------------*/ + +static void BackupRegion(SearchRegion * region) +{ + if(region->flags & kRegionFlag_Enabled) + { + memcpy(region->backupLast, region->last, region->length); + memcpy(region->backupStatus, region->status, region->length); + region->oldNumResults = region->numResults; + } +} + +/*-------------------------------------------------------- + RestoreSearchBackup - restore previous search results +--------------------------------------------------------*/ + +static void RestoreRegionBackup(SearchRegion * region) +{ + if(region->flags & kRegionFlag_Enabled) + { + memcpy(region->last, region->backupLast, region->length); + memcpy(region->status, region->backupStatus, region->length); + region->numResults = region->oldNumResults; + } +} + +/*----------------------------------------------------------- + DefaultEnableRegion - get default regions you can search +-----------------------------------------------------------*/ + +static UINT8 DefaultEnableRegion(SearchRegion * region, SearchInfo * info) +{ + write8_handler handler = region->writeHandler->write.handler8; + FPTR handlerAddress = (FPTR)handler; + + switch(info->searchSpeed) + { + case kSearchSpeed_Fast: + +#if HAS_SH2 + if(Machine->drv->cpu[0].type == CPU_SH2) + { + if( (info->targetType == kRegionType_CPU) && (info->targetIdx == 0) && (region->address == 0x06000000)) + return 1; + + return 0; + } +#endif + + if( (handler == MWA8_RAM) && (!region->writeHandler->base)) + return 1; + +#ifndef MESS + + { + /* ----- for neogeo, search bank one ----- */ + if( (!strcmp(Machine->gamedrv->parent, "neogeo")) && (info->targetType == kRegionType_CPU) && + (info->targetIdx == 0) && (handler == MWA8_BANK1)) + return 1; + } + +#endif + +#if HAS_TMS34010 + + /* ----- for exterminator, search bank one ----- */ + if( (Machine->drv->cpu[1].type == CPU_TMS34010) && (info->targetType == kRegionType_CPU) && + (info->targetIdx == 1) && (handler == MWA8_BANK1)) + return 1; + + /* ----- for smashtv, search bank two ----- */ + if( (Machine->drv->cpu[0].type == CPU_TMS34010) && (info->targetType == kRegionType_CPU) && + (info->targetIdx == 0) && (handler == MWA8_BANK2)) + return 1; + +#endif + return 0; + + case kSearchSpeed_Medium: + if( (handlerAddress >= ((FPTR)MWA8_BANK1)) && (handlerAddress <= ((FPTR)MWA8_BANK24))) + return 1; + + if(handler == MWA8_RAM) + return 1; + + return 0; + + case kSearchSpeed_Slow: + if( (handler == MWA8_NOP) || (handler == MWA8_ROM)) + return 0; + + if( (handlerAddress > STATIC_COUNT) && (!region->writeHandler->base)) + return 0; + + return 1; + + case kSearchSpeed_VerySlow: + if( (handler == MWA8_NOP) || (handler == MWA8_ROM)) + return 0; + + return 1; + } + + return 0; +} + +/*----------------------------- + SetSearchRegionDefaultName +-----------------------------*/ + +static void SetSearchRegionDefaultName(SearchRegion * region) +{ + switch(region->targetType) + { + case kRegionType_CPU: + { + char desc[16]; + + if(region->writeHandler) + { + genf * handler = region->writeHandler->write.handler; + FPTR handlerAddress = (FPTR)handler; + + if( (handlerAddress >= ((FPTR)MWA8_BANK1)) && (handlerAddress <= ((FPTR)MWA8_BANK24))) + sprintf(desc, "BANK%.2d", (int)(handlerAddress - (FPTR)MWA8_BANK1) + 1); + else + { + switch(handlerAddress) + { + case (FPTR)MWA8_NOP: strcpy(desc, "NOP "); break; + case (FPTR)MWA8_RAM: strcpy(desc, "RAM "); break; + case (FPTR)MWA8_ROM: strcpy(desc, "ROM "); break; + default: strcpy(desc, "CUSTOM"); break; + } + } + } + else + sprintf(desc, "CPU%.2d ", region->targetIdx); + + sprintf(region->name, "%.*X-%.*X %s", + cpuInfoList[region->targetIdx].addressCharsNeeded, + region->address, + cpuInfoList[region->targetIdx].addressCharsNeeded, + region->address + region->length - 1, + desc); + } + break; + + case kRegionType_Memory: + sprintf(region->name, "%.8X-%.8X MEMORY", region->address, region->address + region->length - 1); + break; + + default: + sprintf(region->name, "UNKNOWN"); + break; + } +} + +/*----------------------------------------------------------------------------------- + AllocateSearchRegions - free memory for search region then realloc if searchable +-----------------------------------------------------------------------------------*/ + +static void AllocateSearchRegions(SearchInfo * info) +{ + int i; + + info->backupValid = 0; + info->numResults = 0; + + for(i = 0; i < info->regionListLength; i++) + { + SearchRegion * region; + + region = &info->regionList[i]; + + region->numResults = 0; + + free(region->first); + free(region->last); + free(region->status); + free(region->backupLast); + free(region->backupStatus); + + if(region->flags & kRegionFlag_Enabled) + { + region->first = malloc(region->length); + region->last = malloc(region->length); + region->status = malloc(region->length); + region->backupLast = malloc(region->length); + region->backupStatus = malloc(region->length); + + if(!region->first || !region->last || !region->status || !region->backupLast || !region->backupStatus) + { + free(region->first); + free(region->last); + free(region->status); + free(region->backupLast); + free(region->backupStatus); + + region->first = NULL; + region->last = NULL; + region->status = NULL; + region->backupLast = NULL; + region->backupStatus = NULL; + + region->flags &= ~kRegionFlag_Enabled; + } + } + else + { + region->first = NULL; + region->last = NULL; + region->status = NULL; + region->backupLast = NULL; + region->backupStatus = NULL; + } + } +} + +/*--------------------- + BuildSearchRegions +---------------------*/ + +static void BuildSearchRegions(SearchInfo * info) +{ + info->comparison = kSearchComparison_EqualTo; + + DisposeSearchRegions(info); + + switch(info->targetType) + { + case kRegionType_CPU: + { + if(info->searchSpeed == kSearchSpeed_AllMemory) + { + UINT32 length = cpuInfoList[info->targetIdx].addressMask + 1; + SearchRegion * region; + + info->regionList = calloc(sizeof(SearchRegion), 1); + info->regionListLength = 1; + region = info->regionList; + + region->address = 0; + region->length = length; + + region->targetIdx = info->targetIdx; + region->targetType = info->targetType; + region->writeHandler = NULL; + + region->first = NULL; + region->last = NULL; + region->status = NULL; + + region->backupLast = NULL; + region->backupStatus = NULL; + + region->flags = kRegionFlag_Enabled; + + SetSearchRegionDefaultName(region); + } + else + { + if(info->targetIdx < cpu_gettotalcpu()) + { + const address_map * map = NULL; + SearchRegion * traverse; + int count = 0; + + map = memory_get_map(info->targetIdx, ADDRESS_SPACE_PROGRAM); + + while(!IS_AMENTRY_END(map)) + { + if(!IS_AMENTRY_EXTENDED(map) && map->write.handler) + count++; + + map++; + } + + info->regionList = calloc(sizeof(SearchRegion), count); + info->regionListLength = count; + traverse = info->regionList; + + map = memory_get_map(info->targetIdx, ADDRESS_SPACE_PROGRAM); + + while(!IS_AMENTRY_END(map)) + { + if(!IS_AMENTRY_EXTENDED(map) && map->write.handler) + { + UINT32 length = (map->end - map->start) + 1; + + traverse->address = map->start; + traverse->length = length; + + traverse->targetIdx = info->targetIdx; + traverse->targetType = info->targetType; + traverse->writeHandler = map; + + traverse->first = NULL; + traverse->last = NULL; + traverse->status = NULL; + + traverse->backupLast = NULL; + traverse->backupStatus = NULL; + + traverse->flags = DefaultEnableRegion(traverse, info) ? kRegionFlag_Enabled : 0; + + SetSearchRegionDefaultName(traverse); + + traverse++; + } + + map++; + } + } + } + } + break; + + case kRegionType_Memory: + break; + } +} + +/*--------------------------------------------------------------------- + ConvertOldCode - convert old format code to new when load database +---------------------------------------------------------------------*/ + +static int ConvertOldCode(int code, int cpu, int * data, int * extendData) +{ + enum + { + kCustomField_None = 0, + kCustomField_DontApplyCPUField = 1 << 0, + kCustomField_SetBit = 1 << 1, + kCustomField_ClearBit = 1 << 2, + kCustomField_SubtractOne = 1 << 3, + + kCustomField_BitMask = kCustomField_SetBit | + kCustomField_ClearBit, + kCustomField_End = 0xFF + }; + + struct ConversionTable + { + int oldCode; + UINT32 newCode; + UINT8 customField; + }; + + static struct ConversionTable kConversionTable[] = + { + { 0, 0x00000000, kCustomField_None }, + { 1, 0x00000001, kCustomField_None }, + { 2, 0x00000020, kCustomField_None }, + { 3, 0x00000040, kCustomField_None }, + { 4, 0x000000A0, kCustomField_None }, + { 5, 0x00000022, kCustomField_None }, + { 6, 0x00000042, kCustomField_None }, + { 7, 0x000000A2, kCustomField_None }, + { 8, 0x00000024, kCustomField_None }, + { 9, 0x00000044, kCustomField_None }, + { 10, 0x00000064, kCustomField_None }, + { 11, 0x00000084, kCustomField_None }, + { 15, 0x00000023, kCustomField_None }, + { 16, 0x00000043, kCustomField_None }, + { 17, 0x000000A3, kCustomField_None }, + { 20, 0x00000000, kCustomField_SetBit }, + { 21, 0x00000001, kCustomField_SetBit }, + { 22, 0x00000020, kCustomField_SetBit }, + { 23, 0x00000040, kCustomField_SetBit }, + { 24, 0x000000A0, kCustomField_SetBit }, + { 40, 0x00000000, kCustomField_ClearBit }, + { 41, 0x00000001, kCustomField_ClearBit }, + { 42, 0x00000020, kCustomField_ClearBit }, + { 43, 0x00000040, kCustomField_ClearBit }, + { 44, 0x000000A0, kCustomField_ClearBit }, + { 60, 0x00000103, kCustomField_None }, + { 61, 0x00000303, kCustomField_None }, + { 62, 0x00000503, kCustomField_SubtractOne }, + { 63, 0x00000903, kCustomField_None }, + { 64, 0x00000B03, kCustomField_None }, + { 65, 0x00000D03, kCustomField_SubtractOne }, + { 70, 0x00000101, kCustomField_None }, + { 71, 0x00000301, kCustomField_None }, + { 72, 0x00000501, kCustomField_SubtractOne }, + { 73, 0x00000901, kCustomField_None }, + { 74, 0x00000B01, kCustomField_None }, + { 75, 0x00000D01, kCustomField_SubtractOne }, + { 80, 0x00000102, kCustomField_None }, + { 81, 0x00000302, kCustomField_None }, + { 82, 0x00000502, kCustomField_SubtractOne }, + { 83, 0x00000902, kCustomField_None }, + { 84, 0x00000B02, kCustomField_None }, + { 85, 0x00000D02, kCustomField_SubtractOne }, + { 90, 0x00000100, kCustomField_None }, + { 91, 0x00000300, kCustomField_None }, + { 92, 0x00000500, kCustomField_SubtractOne }, + { 93, 0x00000900, kCustomField_None }, + { 94, 0x00000B00, kCustomField_None }, + { 95, 0x00000D00, kCustomField_SubtractOne }, + { 100, 0x20800000, kCustomField_None }, + { 101, 0x20000001, kCustomField_None }, + { 102, 0x20800000, kCustomField_None }, + { 103, 0x20000001, kCustomField_None }, + { 110, 0x40800000, kCustomField_None }, + { 111, 0x40000001, kCustomField_None }, + { 112, 0x40800000, kCustomField_None }, + { 113, 0x40000001, kCustomField_None }, + { 120, 0x63000001, kCustomField_None }, + { 121, 0x63000001, kCustomField_DontApplyCPUField | kCustomField_SetBit }, + { 122, 0x63000001, kCustomField_DontApplyCPUField | kCustomField_ClearBit }, + { 998, 0x00000006, kCustomField_None }, + { 999, 0x60000000, kCustomField_DontApplyCPUField }, + { -1, 0x00000000, kCustomField_End } + }; + + struct ConversionTable * traverse = kConversionTable; + UINT32 newCode; + UINT8 linkCheat = 0; + + /* ----- convert link cheats ----- */ + if((code >= 500) && (code <= 699)) + { + linkCheat = 1; + code -= 500; + } + + /* ----- look up code ----- */ + while(traverse->oldCode >= 0) + { + if(code == traverse->oldCode) + goto foundCode; + traverse++; + } + + logerror("ConvertOldCode: %d not found\n", code); + + /* ----- not found ----- */ + *extendData = 0; + return 0; + + /* ----- found ----- */ + foundCode: + + newCode = traverse->newCode; + + /* ----- add in the CPU field ----- */ + if(!(traverse->customField & kCustomField_DontApplyCPUField)) + newCode = (newCode & ~0x1F000000) | ((cpu << 24) & 0x1F000000); + + /* ----- hack-ish, subtract one from data field for x5 user select ----- */ + if(traverse->customField & kCustomField_SubtractOne) + (*data)--; // yaay for C operator precedence + + /* ----- set up the extend data ----- */ + if(traverse->customField & kCustomField_BitMask) + *extendData = *data; + else + *extendData = 0xFFFFFFFF; + + if(traverse->customField & kCustomField_ClearBit) + *data = 0; + + if(linkCheat) + SET_MASK_FIELD(newCode, LinkEnable); + + return newCode; +} + +/*-------------------------------------------------------------------------------- + HandleLocalCommandCheat - get special code which is not added into cheat list +--------------------------------------------------------------------------------*/ + +static void HandleLocalCommandCheat(UINT32 type, UINT32 address, UINT32 data, UINT32 extendData, char * name, char * description) +{ + switch(EXTRACT_FIELD(type, LocationType)) + { + case kLocation_Custom: + switch(EXTRACT_FIELD(type, LocationParameter)) + { + /* ----- activation key ----- */ + case kCustomLocation_AssignActivationKey: + { + if(address < cheatListLength) + { + CheatEntry * entry = &cheatList[address]; + + if(!TEST_FIELD(type, OneShot)) + { + /* ----- 1st activation key ----- */ + entry->activationKey1 = data; + entry->flags |= kCheatFlag_HasActivationKey1; + } + else + { + /* ----- 2nd activation key ----- */ + entry->activationKey2 = data; + entry->flags |= kCheatFlag_HasActivationKey2; + } + } + } + break; + + /* ----- pre-enable ----- */ + case kCustomLocation_Enable: + { + if(address < cheatListLength) + { + CheatEntry * entry = &cheatList[address]; + + ActivateCheat(entry); + } + } + break; + + /* ----- over-clock ----- */ + case kCustomLocation_Overclock: + { + if(address < cpu_gettotalcpu()) + { + double overclock = data; + + overclock /= 65536.0; + + cpunum_set_clockscale(address, overclock); + } + } + break; + + /* ----- refresh rate ----- */ + case kCustomLocation_RefreshRate: + { + screen_state *state = &Machine->screen[0]; + double refresh = data; + + refresh /= 65536.0; + + video_screen_configure(0, state->width, state->height, &state->visarea, HZ_TO_ATTOSECONDS(refresh)); + } + break; + } + break; + } +} + +/*------------------------------------------------ + LoadCheatFile - load cheat code from database +------------------------------------------------*/ + +static void LoadCheatFile(char * fileName) +{ + mame_file * theFile; + file_error filerr; + char formatString[256]; + char oldFormatString[256]; + char buf[2048]; + int recordNames = 0; + + /* ----- open the database ----- */ + filerr = mame_fopen(SEARCHPATH_CHEAT, fileName, OPEN_FLAG_READ, &theFile); + + if(filerr != FILERR_NONE) + return; + + foundCheatDatabase = 1; + + /* ----- make the format strings ----- */ +#ifdef MESS + sprintf(formatString, ":%s:%s", Machine->gamedrv->name, "%x:%x:%x:%x:%x:%[^:\n\r]:%[^:\n\r]"); + sprintf(oldFormatString, "%s:%s", Machine->gamedrv->name, "%x:%d:%x:%x:%d:%[^:\n\r]:%[^:\n\r]"); +#else + sprintf(formatString, ":%s:%s", Machine->gamedrv->name, "%x:%x:%x:%x:%[^:\n\r]:%[^:\n\r]"); + sprintf(oldFormatString, "%s:%s", Machine->gamedrv->name, "%d:%x:%x:%d:%[^:\n\r]:%[^:\n\r]"); +#endif + + /* ----- get a line from database ----- */ + while(mame_fgets(buf, 2048, theFile)) + { + int type; + int address; + int data; + int extendData; + char name[256]; + char description[256]; +#ifdef MESS + int crc; +#endif + + int argumentsMatched; + UINT32 command; + + CheatEntry * entry; + CheatAction * action; + + /* ----- get command line ----- */ + argumentsMatched = sscanf(buf, ":_command:%8X", &command); + + if(argumentsMatched == 1) + { + /* ----- set cheatOptions from command line ----- */ + cheatOptions = command; + + continue; + } + + /* ----- get cheat code ----- */ + name[0] = 0; + description[0] = 0; + +#ifdef MESS + argumentsMatched = sscanf(buf, formatString, &crc, &type, &address, &data, &extendData, name, description); +#else + argumentsMatched = sscanf(buf, formatString, &type, &address, &data, &extendData, name, description); +#endif + +#ifdef MESS + if(argumentsMatched < 5) +#else + if(argumentsMatched < 4) +#endif + { + int oldCPU; + int oldCode; + +#ifdef MESS + argumentsMatched = sscanf(buf, oldFormatString, &crc, &oldCPU, &address, &data, &oldCode, name, description); + + if(argumentsMatched < 5) +#else + argumentsMatched = sscanf(buf, oldFormatString, &oldCPU, &address, &data, &oldCode, name, description); + + if(argumentsMatched < 4) +#endif + /* ----- if it is not a cheat code, continue ----- */ + continue; + else + { +#ifdef MESS + if(!MatchesCRCTable(crc)) + continue; +#endif + /* ----- convert the old code to the new format ----- */ + if(TEST_FIELD(cheatOptions, LoadOldFormat)) + type = ConvertOldCode(oldCode, oldCPU, &data, &extendData); +// continue; + } + } + else + { +#ifdef MESS + if(!MatchesCRCTable(crc)) + continue; +#endif + } + + //logerror("cheat: processing %s\n", buf); + + if(TEST_FIELD(type, RemoveFromList)) + { + //logerror("cheat: cheat line removed\n", buf); + + HandleLocalCommandCheat(type, address, data, extendData, name, description); + } + else + { + if(TEST_FIELD(type, LinkEnable)) + { + /* ----- if 1st code is the link code, it is invalid ----- */ + if(cheatListLength == 0) + { + logerror("LoadCheatFile: first cheat found was link cheat; bailing\n"); + + goto bail; + } + + //logerror("cheat: doing link cheat\n"); + + entry = &cheatList[cheatListLength - 1]; + } + else + { + /* ----- go to the next cheat entry ----- */ + ResizeCheatList(cheatListLength + 1); + + //logerror("cheat: doing normal cheat\n"); + + if(cheatListLength == 0) + { + logerror("LoadCheatFile: cheat list resize failed; bailing\n"); + + goto bail; + } + + entry = &cheatList[cheatListLength - 1]; + + /* ----- copy the code name ----- */ + entry->name = CreateStringCopy(name); + + /* ----- copy the description if we got it ----- */ + if(argumentsMatched == 6) + entry->comment = CreateStringCopy(description); + + recordNames = 0; + + /* ----- try to copy the label if label-selection ------ */ + if( (EXTRACT_FIELD(type, LocationType) == kLocation_Custom) && + (EXTRACT_FIELD(type, LocationParameter) == kCustomLocation_Select)) + recordNames = 1; + } + + ResizeCheatActionList(&cheatList[cheatListLength - 1], entry->actionListLength + 1); + + if(entry->actionListLength == 0) + { + logerror("LoadCheatFile: action list resize failed; bailing\n"); + + goto bail; + } + + action = &entry->actionList[entry->actionListLength - 1]; + + action->type = type; + action->address = address; + action->data = data; + action->originalDataField = data; + action->extendData = extendData; + + /* ----- copy the label if label-selection ------ */ + if(recordNames) + action->optionalName = CreateStringCopy(name); + } + } + + bail: + + /* ----- close the database ----- */ + mame_fclose(theFile); +} + +/*--------------------------------------------------------- + LoadCheatDatabase - get the database name then load it +---------------------------------------------------------*/ + +static void LoadCheatDatabase(void) +{ + char buf[4096]; + const char * inTraverse; + char * outTraverse; + char * mainTraverse; + int first = 1; + char data; + + cheatfile = options_get_string(mame_options(), OPTION_CHEAT_FILE); + + /* ----- set default database name as "cheat.dat" ----- */ + if (cheatfile[0] == 0) + cheatfile = "cheat.dat"; + + inTraverse = cheatfile; + outTraverse = buf; + mainTraverse = mainDatabaseName; + + buf[0] = 0; + + /* ----- get database name ----- */ + do + { + data = *inTraverse; + + /* ----- check separator or line end ----- */ + if((data == ';') || (data == 0)) + { + *outTraverse++ = 0; + + if(first) + *mainTraverse++ = 0; + + if(buf[0]) + { + /* ----- load database based on the name we gotten ----- */ + LoadCheatFile(buf); + + outTraverse =buf; + buf[0] = 0; + first = 0; + } + } + else + { + *outTraverse++ = data; + + if(first) + *mainTraverse++ = data; + } + + inTraverse++; + } + while(data); + + UpdateAllCheatInfo(); +} + +/*----------------------------------------------------------------------------------------- + DisposeCheatDatabase - deactivate all cheats when reload database or exit cheat system +-----------------------------------------------------------------------------------------*/ + +static void DisposeCheatDatabase(void) +{ + int i; + + /* ----- first, turn all cheats "OFF" ----- */ + for(i = 0; i < cheatListLength; i++) + TempDeactivateCheat(&cheatList[i]); + + /* ----- next, free memory for all cheat entries ----- */ + if(cheatList) + { + for(i = 0; i < cheatListLength; i++) + DisposeCheat(&cheatList[i]); + + free(cheatList); + + cheatList = NULL; + cheatListLength = 0; + } +} + +/*------------------------------------------------------------------------- + ReloadCheatDatabase - reload cheat database directly on the cheat menu +-------------------------------------------------------------------------*/ + +static void ReloadCheatDatabase(void) +{ + DisposeCheatDatabase(); + LoadCheatDatabase(); + + ui_popup_time(1, "Cheat Database reloaded"); +} + +/*--------------------------------------------------------------- + SaveCheat - save a code (normal code, activation key, option) +---------------------------------------------------------------*/ + +static void SaveCheat(CheatEntry * entry, int selection, int saveCode) +{ + enum{ + normalCode = 0, + activationKey, + command, + }; + + mame_file * theFile; + file_error filerr; + UINT32 i; + char buf[4096]; + + /* ----- check selected entry ----- */ + if((saveCode == normalCode) || (saveCode == activationKey)) + { + if(!entry || !entry->actionList) + return; + + if((saveCode == activationKey) && (entry->flags & kCheatFlag_Select) && !entry->actionListLength) + return; + } + + /* ----- open the database ----- */ + filerr = mame_fopen(SEARCHPATH_CHEAT, mainDatabaseName, OPEN_FLAG_WRITE, &theFile); + + /* ----- if fails to create, no save ----- */ + if(!theFile) + return; + if (filerr != FILERR_NONE) + { + filerr = mame_fopen(SEARCHPATH_CHEAT, mainDatabaseName, OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS, &theFile); + + /* ----- if fails to create, no save ----- */ + if(filerr != FILERR_NONE) + return; + } + + mame_fseek(theFile, 0, SEEK_END); + + switch(saveCode) + { + case normalCode: + for(i = 0; i < entry->actionListLength; i++) + { + CheatAction * action = &entry->actionList[i]; + char * name = entry->name; + UINT32 type = action->type; + char * bufTraverse = buf; + int addressLength = 8; + + /* ----- set link enable and label if label-selection ----- */ + if(i) + { + SET_MASK_FIELD(type, LinkEnable); + + if(entry->flags & kCheatFlag_Select) + name = action->optionalName; + } + + /* ----- get address length for current CPU ----- */ + switch(EXTRACT_FIELD(type, LocationType)) + { + case kLocation_Standard: + case kLocation_HandlerMemory: + case kLocation_ProgramSpace: + addressLength = cpuInfoList[EXTRACT_FIELD(type, LocationParameter)].addressCharsNeeded; + break; + + case kLocation_IndirectIndexed: + addressLength = cpuInfoList[(EXTRACT_FIELD(type, LocationParameter) >> 2) & 0x7].addressCharsNeeded; + break; + + case kLocation_MemoryRegion: + { + int idx = EXTRACT_FIELD(type, LocationParameter) + REGION_CPU1 - REGION_INVALID; + + if(idx < kRegionListLength) + addressLength = regionInfoList[idx].addressCharsNeeded; + } + break; + } +#ifdef MESS + bufTraverse += sprintf(bufTraverse, ":%s:%.8X:%.8X:%.*X:%.8X:%.8X", Machine->gamedrv->name, thisGameCRC, type, addressLength, action->address, action->originalDataField, action->extendData); +#else + bufTraverse += sprintf(bufTraverse, ":%s:%.8X:%.*X:%.8X:%.8X", Machine->gamedrv->name, type, addressLength, action->address, action->originalDataField, action->extendData); +#endif + /* ----- set description and comment ----- */ + if(name) + { + bufTraverse += sprintf(bufTraverse, ":%s", name); + + if(!i && (entry->comment)) + bufTraverse += sprintf(bufTraverse, ":%s", entry->comment); + } + else + { + if(!i && (entry->comment)) + bufTraverse += sprintf(bufTraverse, ":(none):%s", entry->comment); + } + + bufTraverse += sprintf(bufTraverse, "\n"); + + /* ----- write the normal cheat code ----- */ + mame_fwrite(theFile, buf, (UINT32)strlen(buf)); + } + break; + + case activationKey: + for(i = 0; i < 2; i++) + { + char * bufTraverse = buf; + int addressLength; + + /* ----- if not find 1st activation key at saving 1st key, try to save 2nd key ----- */ + if(!i && !(entry->flags & kCheatFlag_HasActivationKey1)) + continue; + + /* ----- if not find 2nd key after 1st key saved, finish ------ */ + if(i && !(entry->flags & kCheatFlag_HasActivationKey2)) + break; + + /* ----- get address length for current CPU ----- */ + if(!(entry->flags & kCheatFlag_Select)) + addressLength = cpuInfoList[EXTRACT_FIELD(entry->actionList[0].type, LocationParameter)].addressCharsNeeded; + else + addressLength = cpuInfoList[EXTRACT_FIELD(entry->actionList[1].type, LocationParameter)].addressCharsNeeded; +#ifdef MESS + if(!i) + bufTraverse += sprintf(bufTraverse, ":%s:%.8X:63004000:%.*X:%.8X:00000000", Machine->gamedrv->name, thisGameCRC, addressLength, selection, entry->activationKey1); + else + bufTraverse += sprintf(bufTraverse, ":%s:%.8X:63004001:%.*X:%.8X:00000000", Machine->gamedrv->name, thisGameCRC, addressLength, selection, entry->activationKey2); +#else + if(!i) + bufTraverse += sprintf(bufTraverse, ":%s:63004000:%.*X:%.8X:00000000", Machine->gamedrv->name, addressLength, selection, entry->activationKey1); + else + bufTraverse += sprintf(bufTraverse, ":%s:63004001:%.*X:%.8X:00000000", Machine->gamedrv->name, addressLength, selection, entry->activationKey2); +#endif + /* ----- set description and button index ----- */ + if(!i) + { + /* ----- 1st key ----- */ + astring *name = input_code_name(astring_alloc(), entry->activationKey1); + if (entry->name) + bufTraverse += sprintf(bufTraverse, ":1st Activation Key for %s (%s)\n", entry->name, astring_c(name)); + else + bufTraverse += sprintf(bufTraverse, ":1st Activation Key (%s)\n", astring_c(name)); + astring_free(name); + } + else + { + /* ----- 2nd key ----- */ + astring *name = input_code_name(astring_alloc(), entry->activationKey2); + if (entry->name) + bufTraverse += sprintf(bufTraverse, ":2nd Activation Key for %s (%s)\n", entry->name, astring_c(name)); + else + bufTraverse += sprintf(bufTraverse, ":2nd Activation Key (%s)\n", astring_c(name)); + astring_free(name); + } + + /* ----- write the activation key code ----- */ + mame_fwrite(theFile, buf, (UINT32)strlen(buf)); + } + break; + + case command: + sprintf(buf, ":_command:%.8X\n", cheatOptions); + + /* ----- write the command code ----- */ + mame_fwrite(theFile, buf, (UINT32)strlen(buf)); + break; + + default: + break; + } + + /* ----- close the database ----- */ + mame_fclose(theFile); + + /* ----- clear dirty flag ----- */ + if((saveCode == normalCode) || (saveCode == activationKey)) + entry->flags &= ~kCheatFlag_Dirty; +} + +/*-------------------------------------------------------------------------- + DoAutoSaveCheat - save normal code automatically when exit cheat system +--------------------------------------------------------------------------*/ + +static void DoAutoSaveCheats(void) +{ + int i; + + for(i = 0; i < cheatListLength; i++) + { + CheatEntry * entry = &cheatList[i]; + + if(entry->flags & kCheatFlag_Dirty) + SaveCheat(entry, 0, 0); + } +} + +/*------------------------------------------------------------------- + AddCheatFromResult - add a code from result viewer to cheat list +-------------------------------------------------------------------*/ + +static void AddCheatFromResult(SearchInfo * search, SearchRegion * region, UINT32 address) +{ + if(region->targetType == kRegionType_CPU) + { + CheatEntry * entry = GetNewCheat(); + CheatAction * action = &entry->actionList[0]; + char tempString[1024]; + int tempStringLength; + UINT32 data = ReadSearchOperand(kSearchOperand_First, search, region, address); + + tempStringLength = sprintf(tempString, "%.8X (%d) = %.*X", address, region->targetIdx, kSearchByteDigitsTable[search->bytes], data); + + entry->name = realloc(entry->name, tempStringLength + 1); + memcpy(entry->name, tempString, tempStringLength + 1); + + SET_FIELD(action->type, LocationParameter, region->targetIdx); + SET_FIELD(action->type, BytesUsed, kSearchByteIncrementTable[search->bytes] - 1); + action->address = address; + action->data = data; + action->extendData = 0xFFFFFFFF; + action->originalDataField = data; + + UpdateCheatInfo(entry, 0); + } +} + +/*-------------------------------------------------------------------------------------------- + AddCheatFromFirstResult - add a code from search box to cheat list if found result is one +--------------------------------------------------------------------------------------------*/ + +static void AddCheatFromFirstResult(SearchInfo * search) +{ + int i; + + for(i = 0; i < search->regionListLength; i++) + { + SearchRegion * region = &search->regionList[i]; + + if(region->numResults) + { + UINT32 traverse; + + for(traverse = 0; traverse < region->length; traverse++) + { + UINT32 address = region->address + traverse; + + if(IsRegionOffsetValid(search, region, traverse)) + { + AddCheatFromResult(search, region, address); + + return; + } + } + } + } +} + +/*------------------------------------------------------------------------- + AddWatchFromResult - add a watch code from result viewer to cheat list +-------------------------------------------------------------------------*/ + +static void AddWatchFromResult(SearchInfo * search, SearchRegion * region, UINT32 address) +{ + if(region->targetType == kRegionType_CPU) + { + WatchInfo * info = GetUnusedWatch(); + + info->address = address; + info->cpu = region->targetIdx; + info->numElements = 1; + info->elementBytes = kWatchSizeConversionTable[search->bytes]; + info->labelType = kWatchLabel_None; + info->displayType = kWatchDisplayType_Hex; + info->skip = 0; + info->elementsPerLine = 0; + info->addValue = 0; + + info->linkedCheat = NULL; + + info->label[0] = 0; + } +} + +/*-------------------- + SearchSignExtend +--------------------*/ + +static UINT32 SearchSignExtend(SearchInfo * search, UINT32 value) +{ + if(search->sign) + { + if(value & kSearchByteSignBitTable[search->bytes]) + value |= ~kSearchByteUnsignedMaskTable[search->bytes]; + } + + return value; +} + +/*------------------------------------------------------------------- + ReadSearchOperand - read a data from search region and return it +-------------------------------------------------------------------*/ + +static UINT32 ReadSearchOperand(UINT8 type, SearchInfo * search, SearchRegion * region, UINT32 address) +{ + UINT32 value = 0; + + switch(type) + { + case kSearchOperand_Current: + value = ReadRegionData(region, address - region->address, kSearchByteIncrementTable[search->bytes], search->swap); + break; + + case kSearchOperand_Previous: + value = DoMemoryRead(region->last, address - region->address, kSearchByteIncrementTable[search->bytes], search->swap, NULL); + break; + + case kSearchOperand_First: + value = DoMemoryRead(region->first, address - region->address, kSearchByteIncrementTable[search->bytes], search->swap, NULL); + break; + + case kSearchOperand_Value: + value = search->value; + break; + } + + value = SearchSignExtend(search, value); + + return value; +} + +/*------------------------------------------------------------------------------------------ + ReadSearchOperandBit - read a bit data from search region and return it when bit search +------------------------------------------------------------------------------------------*/ + +static UINT32 ReadSearchOperandBit(UINT8 type, SearchInfo * search, SearchRegion * region, UINT32 address) +{ + UINT32 value = 0; + + switch(type) + { + case kSearchOperand_Current: + value = ReadRegionData(region, address - region->address, kSearchByteIncrementTable[search->bytes], search->swap); + break; + + case kSearchOperand_Previous: + value = DoMemoryRead(region->last, address - region->address, kSearchByteIncrementTable[search->bytes], search->swap, NULL); + break; + + case kSearchOperand_First: + value = DoMemoryRead(region->first, address - region->address, kSearchByteIncrementTable[search->bytes], search->swap, NULL); + break; + + case kSearchOperand_Value: + if(search->value) + value = 0xFFFFFFFF; + else + value = 0x00000000; + break; + } + + value = SearchSignExtend(search, value); + + return value; +} + +/*---------------------------------------------------------- + DoSearchComparison - compare data and return it matched +----------------------------------------------------------*/ + +static UINT8 DoSearchComparison(SearchInfo * search, UINT32 lhs, UINT32 rhs) +{ + INT32 svalue; + + if(search->sign) + { + /* ----- signed ----- */ + INT32 slhs = lhs; + INT32 srhs = rhs; + + switch(search->comparison) + { + case kSearchComparison_LessThan: + return slhs < srhs; + + case kSearchComparison_GreaterThan: + return slhs > srhs; + + case kSearchComparison_EqualTo: + return slhs == srhs; + + case kSearchComparison_LessThanOrEqualTo: + return slhs <= srhs; + + case kSearchComparison_GreaterThanOrEqualTo: + return slhs >= srhs; + + case kSearchComparison_NotEqual: + return slhs != srhs; + + case kSearchComparison_IncreasedBy: + svalue = search->value; + if(search->value & kSearchByteSignBitTable[search->bytes]) + svalue |= ~kSearchByteUnsignedMaskTable[search->bytes]; + + return slhs == (srhs + svalue); + + case kSearchComparison_NearTo: + return (slhs == srhs) || ((slhs + 1) == srhs); + } + } + else + { + /* ----- unsigned ----- */ + switch(search->comparison) + { + case kSearchComparison_LessThan: + return lhs < rhs; + + case kSearchComparison_GreaterThan: + return lhs > rhs; + + case kSearchComparison_EqualTo: + return lhs == rhs; + + case kSearchComparison_LessThanOrEqualTo: + return lhs <= rhs; + + case kSearchComparison_GreaterThanOrEqualTo: + return lhs >= rhs; + + case kSearchComparison_NotEqual: + return lhs != rhs; + + case kSearchComparison_IncreasedBy: + svalue = search->value; + if(search->value & kSearchByteSignBitTable[search->bytes]) + svalue |= ~kSearchByteUnsignedMaskTable[search->bytes]; + + return lhs == (rhs + svalue); + + case kSearchComparison_NearTo: + return (lhs == rhs) || ((lhs + 1) == rhs); + } + } + + return 0; +} + +/*----------------------------------------------------------------- + DoSearchComparisonBit - compare bit data and return it matched +-----------------------------------------------------------------*/ + +static UINT32 DoSearchComparisonBit(SearchInfo * search, UINT32 lhs, UINT32 rhs) +{ + switch(search->comparison) + { + case kSearchComparison_LessThan: + case kSearchComparison_NotEqual: + case kSearchComparison_GreaterThan: + case kSearchComparison_LessThanOrEqualTo: + case kSearchComparison_GreaterThanOrEqualTo: + case kSearchComparison_IncreasedBy: + return lhs ^ rhs; + + case kSearchComparison_EqualTo: + case kSearchComparison_NearTo: + return ~(lhs ^ rhs); + } + + return 0; +} + +/*---------------------- + IsRegionOffsetValid +----------------------*/ + +/* +static UINT8 IsRegionOffsetValid(SearchInfo * search, SearchRegion * region, UINT32 offset) +{ + switch(kSearchByteIncrementTable[search->bytes]) + { + case 1: + return *((UINT8 *)®ion->status[offset]) == 0xFF; + break; + + case 2: + return *((UINT16 *)®ion->status[offset]) == 0xFFFF; + break; + + case 4: + return *((UINT32 *)®ion->status[offset]) == 0xFFFFFFFF; + break; + } + + return 0; +} +*/ + +/*---------------------------------------------------------- + IsRegionOffsetValidBit - check selected offset is valid +----------------------------------------------------------*/ + +static UINT8 IsRegionOffsetValidBit(SearchInfo * search, SearchRegion * region, UINT32 offset) +{ + switch(kSearchByteStep[search->bytes]) + { + case 1: + return *((UINT8 *)®ion->status[offset]) != 0; + break; + + case 2: + return *((UINT16 *)®ion->status[offset]) != 0; + break; + + case 3: + return ((*((UINT16 *)®ion->status[offset]) != 0) | (*((UINT8 *)®ion->status[offset+2]) != 0)); + break; + + case 4: + return *((UINT32 *)®ion->status[offset]) != 0; + break; + } + + return 0; +} + +/*---------------------------------------------------------------- + InvalidateRegionOffset - remove unmatched offset after search +----------------------------------------------------------------*/ + +static void InvalidateRegionOffset(SearchInfo * search, SearchRegion * region, UINT32 offset) +{ + switch(kSearchByteStep[search->bytes]) + { + case 1: + *((UINT8 *)®ion->status[offset]) = 0; + break; + + case 2: + *((UINT16 *)®ion->status[offset]) = 0; + break; + + case 3: + *((UINT16 *)®ion->status[offset]) = 0; + *((UINT8 *)®ion->status[offset+2]) = 0; + break; + + case 4: + *((UINT32 *)®ion->status[offset]) = 0; + break; + } +} + +/*----------------------------------------------------------------------- + InvalidateRegionOffsetBit - remove unmatched offset after bit search +-----------------------------------------------------------------------*/ + +static void InvalidateRegionOffsetBit(SearchInfo * search, SearchRegion * region, UINT32 offset, UINT32 invalidate) +{ + switch(kSearchByteStep[search->bytes]) + { + case 1: + *((UINT8 *)®ion->status[offset]) &= ~invalidate; + break; + + case 2: + *((UINT16 *)®ion->status[offset]) &= ~invalidate; + break; + + case 3: + *((UINT16 *)®ion->status[offset]) &= ~invalidate; + *((UINT8 *)®ion->status[offset+2]) &= ~invalidate; + break; + + case 4: + *((UINT32 *)®ion->status[offset]) &= ~invalidate; + break; + } +} + +/*-------------------------------------------------------------- + InvalidateEntireRegions - invalidate selected search region +--------------------------------------------------------------*/ + +static void InvalidateEntireRegion(SearchInfo * search, SearchRegion * region) +{ + memset(region->status, 0, region->length); + + search->numResults -= region->numResults; + region->numResults = 0; +} + +/*------------------------------------------------------------------- + InitializeNewSearch - initialize search region to start a search +-------------------------------------------------------------------*/ + +static void InitializeNewSearch(SearchInfo * search) +{ + int i; + + search->numResults = 0; + + for(i = 0; i < search->regionListLength; i++) + { + SearchRegion * region = &search->regionList[i]; + + if(region->flags & kRegionFlag_Enabled) + { + region->numResults = 0; + + memset(region->status, 0xFF, region->length); + + FillBufferFromRegion(region, region->first); + + memcpy(region->last, region->first, region->length); + } + } +} + +/*--------------------------------------------------------------------------- + UpdateSearch - updata a data in search region after initialize or search +---------------------------------------------------------------------------*/ + +static void UpdateSearch(SearchInfo * search) +{ + int i; + + for(i = 0; i < search->regionListLength; i++) + { + SearchRegion * region = &search->regionList[i]; + + if(region->flags & kRegionFlag_Enabled) + FillBufferFromRegion(region, region->last); + } +} + +/*------------------------------------- + DoSearch - management for a search +-------------------------------------*/ + +static void DoSearch(SearchInfo * search) +{ + int i, j; + + search->numResults = 0; + + if(search->bytes == kSearchSize_1Bit) + { + /* ----- bit search ----- */ + for(i = 0; i < search->regionListLength; i++) + { + SearchRegion * region = &search->regionList[i]; + UINT32 lastAddress = region->length - kSearchByteIncrementTable[search->bytes] + 1; + UINT32 increment = kSearchByteStep[search->bytes]; + + region->numResults = 0; + + if((region->length < kSearchByteIncrementTable[search->bytes]) || !region->flags & kRegionFlag_Enabled) + continue; + + for(j = 0; j < lastAddress; j += increment) + { + UINT32 address; + UINT32 lhs, rhs; + + address = region->address + j; + + if(IsRegionOffsetValidBit(search, region, j)) + { + UINT32 validBits; + + lhs = ReadSearchOperandBit(search->lhs, search, region, address); + rhs = ReadSearchOperandBit(search->rhs, search, region, address); + + /* ----- do search ----- */ + validBits = DoSearchComparisonBit(search, lhs, rhs); + + InvalidateRegionOffsetBit(search, region, j, ~validBits); + + if(IsRegionOffsetValidBit(search, region, j)) + { + search->numResults++; + region->numResults++; + } + } + } + } + } + else + { + /* ----- normal search ----- */ + for(i = 0; i < search->regionListLength; i++) + { + SearchRegion * region = &search->regionList[i]; + UINT32 lastAddress = region->length - kSearchByteIncrementTable[search->bytes] + 1; + UINT32 increment = kSearchByteStep[search->bytes]; + + region->numResults = 0; + + if((region->length < kSearchByteIncrementTable[search->bytes]) || !region->flags & kRegionFlag_Enabled) + continue; + + for(j = 0; j < lastAddress; j += increment) + { + UINT32 address; + UINT32 lhs, rhs; + + address = region->address + j; + + if(IsRegionOffsetValid(search, region, j)) + { + lhs = ReadSearchOperand(search->lhs, search, region, address); + rhs = ReadSearchOperand(search->rhs, search, region, address); + + /* ----- do search ----- */ + if(!DoSearchComparison(search, lhs, rhs)) + /* ----- unmatched ----- */ + InvalidateRegionOffset(search, region, j); + else + { + /* ----- matched ----- */ + search->numResults++; + region->numResults++; + } + } + } + } + } +} + +/*---------------------- + LookupHandlerMemory +----------------------*/ + +static UINT8 ** LookupHandlerMemory(UINT8 cpu, UINT32 address, UINT32 * outRelativeAddress) +{ + const address_map * map = memory_get_map(cpu, ADDRESS_SPACE_PROGRAM); + + while(!IS_AMENTRY_END(map)) + { + if(!IS_AMENTRY_EXTENDED(map) && map->write.handler) + { + if((address >= map->start) && (address <= map->end)) + { + if(outRelativeAddress) + *outRelativeAddress = address - map->start; + + return (UINT8 **)map->base; + } + } + + map++; + } + + return NULL; +} + +/*------------------------------------------ + DoCPURead - read a data from RAM region +------------------------------------------*/ + +static UINT32 DoCPURead(UINT8 cpu, UINT32 address, UINT8 bytes, UINT8 swap) +{ + switch(bytes) + { + case 1: + return (cpunum_read_byte(cpu, address + 0) << 0); + + case 2: + if(swap) + return (cpunum_read_byte(cpu, address + 0) << 0) | (cpunum_read_byte(cpu, address + 1) << 8); + else + return (cpunum_read_byte(cpu, address + 0) << 8) | (cpunum_read_byte(cpu, address + 1) << 0); + break; + + case 3: + if(swap) + return (cpunum_read_byte(cpu, address + 0) << 0) | (cpunum_read_byte(cpu, address + 1) << 8) | + (cpunum_read_byte(cpu, address + 2) << 16); + else + return (cpunum_read_byte(cpu, address + 0) << 16) | (cpunum_read_byte(cpu, address + 1) << 8) | + (cpunum_read_byte(cpu, address + 2) << 0); + break; + + case 4: + if(swap) + return (cpunum_read_byte(cpu, address + 0) << 0) | (cpunum_read_byte(cpu, address + 1) << 8) | + (cpunum_read_byte(cpu, address + 2) << 16) | (cpunum_read_byte(cpu, address + 3) << 24); + else + return (cpunum_read_byte(cpu, address + 0) << 24) | (cpunum_read_byte(cpu, address + 1) << 16) | + (cpunum_read_byte(cpu, address + 2) << 8) | (cpunum_read_byte(cpu, address + 3) << 0); + break; + } + + return 0; +} + +/*----------------------------------------------------- + DoMemoryRead - read a data from ROM or User region +-----------------------------------------------------*/ + +static UINT32 DoMemoryRead(UINT8 * buf, UINT32 address, UINT8 bytes, UINT8 swap, CPUInfo * info) +{ + UINT32 data = 0; + + if(!info) + { + switch(bytes) + { + case 1: + data = buf[address]; + break; + + case 2: + data = *((UINT16 *)&buf[address]); + + if(swap) + data = ((data >> 8) & 0x00FF) | ((data << 8) & 0xFF00); + break; + + case 4: + data = *((UINT32 *)&buf[address]); + + if(swap) + data = ((data >> 24) & 0x000000FF) | ((data >> 8) & 0x0000FF00) | + ((data << 8) & 0x00FF0000) | ((data << 24) & 0xFF000000); + break; + + default: + info = &rawCPUInfo; + goto generic; + } + + return data; + } + +generic: + + if(swap) + { + UINT32 i; + + for(i = 0; i < bytes; i++) + data |= buf[SwapAddress(address + i, bytes, info)] << (i * 8); + } + else + { + UINT32 i; + + for(i = 0; i < bytes; i++) + data |= buf[SwapAddress(address + i, bytes, info)] << ((bytes - i - 1) * 8); + } + + return data; +} + +/*-------------------------------------------- + DoCPUWrite - write a data into RAM region +--------------------------------------------*/ + +static void DoCPUWrite(UINT32 data, UINT8 cpu, UINT32 address, UINT8 bytes, UINT8 swap) +{ + switch(bytes) + { + case 1: + cpunum_write_byte(cpu, address + 0, data & 0xFF); + break; + + case 2: + if(swap) + { + cpunum_write_byte(cpu, address + 0, (data >> 0) & 0xFF); + cpunum_write_byte(cpu, address + 1, (data >> 8) & 0xFF); + } + else + { + cpunum_write_byte(cpu, address + 0, (data >> 8) & 0xFF); + cpunum_write_byte(cpu, address + 1, (data >> 0) & 0xFF); + } + break; + + case 3: + if(swap) + { + cpunum_write_byte(cpu, address + 0, (data >> 0) & 0xFF); + cpunum_write_byte(cpu, address + 1, (data >> 8) & 0xFF); + cpunum_write_byte(cpu, address + 2, (data >> 16) & 0xFF); + } + else + { + cpunum_write_byte(cpu, address + 0, (data >> 16) & 0xFF); + cpunum_write_byte(cpu, address + 1, (data >> 8) & 0xFF); + cpunum_write_byte(cpu, address + 2, (data >> 0) & 0xFF); + } + break; + + case 4: + if(swap) + { + cpunum_write_byte(cpu, address + 0, (data >> 0) & 0xFF); + cpunum_write_byte(cpu, address + 1, (data >> 8) & 0xFF); + cpunum_write_byte(cpu, address + 2, (data >> 16) & 0xFF); + cpunum_write_byte(cpu, address + 3, (data >> 24) & 0xFF); + } + else + { + cpunum_write_byte(cpu, address + 0, (data >> 24) & 0xFF); + cpunum_write_byte(cpu, address + 1, (data >> 16) & 0xFF); + cpunum_write_byte(cpu, address + 2, (data >> 8) & 0xFF); + cpunum_write_byte(cpu, address + 3, (data >> 0) & 0xFF); + } + break; + + default: + logerror("DoCPUWrite: bad size (%d)\n", bytes); + break; + } +} + +/*------------------------------------------------------- + DoMemoryWrite - write a data into ROM or User region +-------------------------------------------------------*/ + +static void DoMemoryWrite(UINT32 data, UINT8 * buf, UINT32 address, UINT8 bytes, UINT8 swap, CPUInfo * info) +{ + if(!info) + { + switch(bytes) + { + case 1: + buf[address] = data; + break; + + case 2: + if(swap) + data = ((data >> 8) & 0x00FF) | ((data << 8) & 0xFF00); + + *((UINT16 *)&buf[address]) = data; + break; + + case 4: + if(swap) + data = ((data >> 24) & 0x000000FF) | ((data >> 8) & 0x0000FF00) | + ((data << 8) & 0x00FF0000) | ((data << 24) & 0xFF000000); + + *((UINT32 *)&buf[address]) = data; + break; + + default: + info = &rawCPUInfo; + goto generic; + } + + return; + } + +generic: + + if(swap) + { + UINT32 i; + + for(i = 0; i < bytes; i++) + buf[SwapAddress(address + i, bytes, info)] = data >> (i * 8); + } + else + { + UINT32 i; + + for(i = 0; i < bytes; i++) + buf[SwapAddress(address + i, bytes, info)] = data >> ((bytes - i - 1) * 8); + } +} + +/*-------------------------------- + CPUNeedsSwap - endian checker +--------------------------------*/ + +static UINT8 CPUNeedsSwap(UINT8 cpu) +{ + return cpuInfoList[cpu].endianness ^ 1; +} + +/*----------------------------------- + RegionNeedsSwap - endian checker +-----------------------------------*/ + +static UINT8 RegionNeedsSwap(UINT8 region) +{ + CPUInfo * temp = GetRegionCPUInfo(region); + + if(temp) + return temp->endianness ^ 1; + + return 0; +} + +/*---------------------------------------------------- + GetCPUInfo - return CPU info for standard region +----------------------------------------------------*/ + +static CPUInfo * GetCPUInfo(UINT8 cpu) +{ + return &cpuInfoList[cpu]; +} + +/*------------------------------------------------- + GetRegionCPUInfo - return CPU info for regions +-------------------------------------------------*/ + +static CPUInfo * GetRegionCPUInfo(UINT8 region) +{ + if((region >= REGION_INVALID) && (region < REGION_MAX)) + return ®ionInfoList[region - REGION_INVALID]; + + return NULL; +} + +/*-------------- + SwapAddress +--------------*/ + +static UINT32 SwapAddress(UINT32 address, UINT8 dataSize, CPUInfo * info) +{ + switch(info->dataBits) + { + case 16: + if(info->endianness == CPU_IS_BE) + return BYTE_XOR_BE(address); + else + return BYTE_XOR_LE(address); + + case 32: + if(info->endianness == CPU_IS_BE) + return BYTE4_XOR_BE(address); + else + return BYTE4_XOR_LE(address); + } + + return address; +} + +/*------------------------------------ + ReadData - read a data per region +------------------------------------*/ + +static UINT32 ReadData(CheatAction * action) +{ + UINT8 parameter = EXTRACT_FIELD(action->type, LocationParameter); + UINT8 bytes = EXTRACT_FIELD(action->type, BytesUsed) + 1; + UINT8 swapBytes = EXTRACT_FIELD(action->type, Endianness); + + switch(EXTRACT_FIELD(action->type, LocationType)) + { + case kLocation_Standard: + return DoCPURead(parameter, action->address, bytes, CPUNeedsSwap(parameter) ^ swapBytes); + break; + + case kLocation_MemoryRegion: + { + int region = REGION_CPU1 + parameter; + UINT8 * buf = memory_region(region); + + if(buf) + { + if(TEST_FIELD(cheatOptions, Debug)) + { + if(ControlKeyPressed()) + ui_popup_time(1, "MR : %x : %p", region, buf); + } + + if(IsAddressInRange(action, memory_region_length(region))) + return DoMemoryRead(buf, action->address, bytes, RegionNeedsSwap(region) ^ swapBytes, GetRegionCPUInfo(region)); + } + } + break; + + case kLocation_HandlerMemory: + { + UINT32 relativeAddress; + UINT8 ** buf; + + if(!action->cachedPointer) + action->cachedPointer = LookupHandlerMemory(parameter, action->address, &action->cachedOffset); + + buf = action->cachedPointer; + relativeAddress = action->cachedOffset; + + if(buf && *buf) + return DoMemoryRead(*buf, relativeAddress, bytes, CPUNeedsSwap(parameter) ^ swapBytes, GetCPUInfo(parameter)); + } + break; + + case kLocation_IndirectIndexed: + { + UINT32 address; + INT32 offset = action->extendData; + UINT8 cpu = (parameter >> 2) & 0x7; + UINT8 addressBytes = (parameter & 0x3) + 1; + CPUInfo * info = GetCPUInfo(cpu); + + address = DoCPURead(cpu, action->address, addressBytes, CPUNeedsSwap(cpu) ^ swapBytes); + + if(info) + address = DoShift(address, info->addressShift); + + if(address) + { + address += offset; + + return DoCPURead(cpu, address, bytes, CPUNeedsSwap(cpu) ^ swapBytes); + } + } + break; + + case kLocation_ProgramSpace: + { + UINT8 * buf; + + memory_set_opbase(action->address); + + buf = memory_get_op_ptr(parameter, action->address, 0); + + buf -= action->address; + + if(buf) + { + if(TEST_FIELD(cheatOptions, Debug)) + { + if(ControlKeyPressed()) + ui_popup_time(1,"PG : %x : %p", parameter, buf); + } + + return DoMemoryRead(buf, action->address, bytes, CPUNeedsSwap(parameter) ^ swapBytes, GetCPUInfo(parameter)); + } + } + break; + + case kLocation_Custom: + { + switch(parameter) + { + case kCustomLocation_Comment: + break; + + case kCustomLocation_EEPROM: + { + int length; + UINT8 * buf; + + buf = EEPROM_get_data_pointer(&length); + + if(IsAddressInRange(action, length)) + return DoMemoryRead(buf, action->address, bytes, swapBytes, &rawCPUInfo); + } + break; + } + } + break; + + default: + break; + } + + return 0; +} + +/*-------------------------------------- + WriteData - write a data per region +--------------------------------------*/ + +static void WriteData(CheatAction * action, UINT32 data) +{ + UINT8 parameter = EXTRACT_FIELD(action->type, LocationParameter); + UINT8 bytes = EXTRACT_FIELD(action->type, BytesUsed) + 1; + UINT8 swapBytes = EXTRACT_FIELD(action->type, Endianness); + + switch(EXTRACT_FIELD(action->type, LocationType)) + { + case kLocation_Standard: + DoCPUWrite(data, parameter, action->address, bytes, CPUNeedsSwap(parameter) ^ swapBytes); + + break; + + case kLocation_MemoryRegion: + { + int region = REGION_CPU1 + parameter; + UINT8 * buf = memory_region(region); + + if(buf) + { + if(IsAddressInRange(action, memory_region_length(region))) + DoMemoryWrite(data, buf, action->address, bytes, RegionNeedsSwap(region) ^ swapBytes, GetRegionCPUInfo(region)); + } + } + break; + + case kLocation_HandlerMemory: + { + UINT32 relativeAddress; + UINT8 ** buf; + + if(!action->cachedPointer) + { + action->cachedPointer = LookupHandlerMemory(parameter, action->address, &action->cachedOffset); + } + + buf = action->cachedPointer; + relativeAddress = action->cachedOffset; + + if(buf && *buf) + DoMemoryWrite(data, *buf, relativeAddress, bytes, CPUNeedsSwap(parameter) ^ swapBytes, GetCPUInfo(parameter)); + } + break; + + case kLocation_IndirectIndexed: + { + UINT32 address; + INT32 offset = action->extendData; + UINT8 cpu = (parameter >> 2) & 0x7; + UINT8 addressBytes = (parameter & 0x3) + 1; + CPUInfo * info = GetCPUInfo(cpu); + + address = DoCPURead(cpu, action->address, addressBytes, CPUNeedsSwap(cpu) ^ swapBytes); + + if(info) + address = DoShift(address, info->addressShift); + + if(address) + { + address += offset; + + DoCPUWrite(data, cpu, address, bytes, CPUNeedsSwap(cpu) ^ swapBytes); + } + } + break; + + case kLocation_ProgramSpace: + { + UINT8 * buf; + + memory_set_opbase(action->address); + + buf = memory_get_op_ptr(parameter, action->address, 0); + + buf -= action->address; + + if(buf) + DoMemoryWrite(data, buf, action->address, bytes, CPUNeedsSwap(parameter) ^ swapBytes, GetCPUInfo(parameter)); + } + break; + + case kLocation_Custom: + { + switch(parameter) + { + case kCustomLocation_Comment: + break; + + case kCustomLocation_EEPROM: + { + int length; + UINT8 * buf; + + buf = EEPROM_get_data_pointer(&length); + + if(IsAddressInRange(action, length)) + DoMemoryWrite(data, buf, action->address, bytes, swapBytes, &rawCPUInfo); + } + break; + } + } + break; + + default: + break; + } +} + +/*----------------------------------------------------- + WatchCheatEntry - set watchpoint from action entry +-----------------------------------------------------*/ + +static void WatchCheatEntry(CheatEntry * entry, UINT8 associate) +{ + UINT32 i, j; + CheatEntry * associateEntry = NULL; + + if(associate) + associateEntry = entry; + + if(!entry) + return; + + for(i = 0; i < entry->actionListLength; i++) + { + if(!i) + /* ----- first action entry ----- */ + AddActionWatch(&entry->actionList[i], associateEntry); + else + { + /* ----- 2nd or later action entry ----- */ + UINT8 sameAddress = 0; + + for(j = 0; j < i; j++) + { + /* ----- if we find the same address action entry, skip it ----- */ + if(entry->actionList[j].address == entry->actionList[i].address) + sameAddress = 1; + } + + if(!sameAddress) + AddActionWatch(&entry->actionList[i], associateEntry); + } + } +} + +/*-------------------------------------------------- + AddActionWatch - add watchpoint into watch list +--------------------------------------------------*/ + +static void AddActionWatch(CheatAction * action, CheatEntry * entry) +{ + if( (EXTRACT_FIELD(action->type, LocationType) == kLocation_Standard) || + (EXTRACT_FIELD(action->type, LocationType) == kLocation_MemoryRegion) || + (EXTRACT_FIELD(action->type, LocationType) == kLocation_IndirectIndexed) || + (EXTRACT_FIELD(action->type, LocationType) == kLocation_ProgramSpace)) + + { + WatchInfo * info = GetUnusedWatch(); + + info->address = action->address; + info->cpu = EXTRACT_FIELD(action->type, LocationParameter); + info->displayType = kWatchDisplayType_Hex; + info->elementBytes = kByteConversionTable[EXTRACT_FIELD(action->type, BytesUsed)]; + info->label[0] = 0; + info->labelType = kWatchLabel_None; + info->linkedCheat = entry; + info->numElements = 1; + info->skip = 0; + + info->locationType = EXTRACT_FIELD(action->type, LocationType); + + if(EXTRACT_FIELD(action->type, Type) == kType_Watch) + { + /* ----- set watchpoint from watch code ----- */ + UINT32 typeParameter = EXTRACT_FIELD(action->type, TypeParameter); + + info->numElements = (action->data & 0xFF) + 1; + + info->skip = (action->data >> 8) & 0xFF; + info->elementsPerLine = (action->data >> 16) & 0xFF; + info->addValue = (action->data >> 24) & 0xFF; + + if(info->addValue & 0x80) + info->addValue |= ~0xFF; + + if(action->extendData != 0xFFFFFFFF) + { + /* ### fix me... ### */ + info->x = (float)((action->extendData >> 16) & 0xFFFF)/100; + info->y = (float)((action->extendData >> 0) & 0xFFFF)/100; + } + + /* ----- if label is needed, copy the description from comment field ----- */ + if((typeParameter & 0x04) && (entry->comment) && (strlen(entry->comment) < 256)) + { + info->labelType = kWatchLabel_String; + strcpy(info->label, entry->comment); + } + + info->displayType = typeParameter & 0x03; + } + } +} + +/*----------------------------------------------------------------- + RemoveAssociatedWatches - remove watchpoint when CODE is "OFF" +-----------------------------------------------------------------*/ + +static void RemoveAssociatedWatches(CheatEntry * entry) +{ + int i; + + for(i = watchListLength - 1; i >= 0; i--) + { + WatchInfo * info = &watchList[i]; + + if(info->linkedCheat == entry) + DeleteWatchAt(i); + } +} + +/*---------------------------------------------------------------------- + ResetAction - back up data and set action flags when turn CODE "ON" +----------------------------------------------------------------------*/ + +static void ResetAction(CheatAction * action) +{ + action->frameTimer = 0; + + /* ----- backup value ----- */ + if(!(action->flags & kActionFlag_LastValueGood)) + action->lastValue = ReadData(action); + + action->flags &= ~kActionFlag_StateMask; + action->flags |= kActionFlag_LastValueGood; +} + +/*------------------------------------------------------------------------------------- + ActivateCheat - reset action entry and set activate entry flag when turn CODE "ON" +-------------------------------------------------------------------------------------*/ + +static void ActivateCheat(CheatEntry * entry) +{ + int i; + + for(i = 0; i < entry->actionListLength; i++) + { + CheatAction * action = &entry->actionList[i]; + + ResetAction(action); + + /* ----- if watchpoint code, add watchpoint ----- */ + if(EXTRACT_FIELD(action->type, Type) == kType_Watch) + AddActionWatch(action, entry); + } + + entry->flags |= kCheatFlag_Active; +} + +/*-------------------------------------------------------------------------------------- + DeactivateCheat - restore previous value and remove watchpoint when turn CODE "OFF" +--------------------------------------------------------------------------------------*/ + +static void DeactivateCheat(CheatEntry * entry) +{ + int i; + + for(i = 0; i < entry->actionListLength; i++) + { + CheatAction * action = &entry->actionList[i]; + + /* ----- restore previous value and clear backup flag if needed ----- */ + if(EXTRACT_FIELD(action->type, RestorePreviousValue) && (action->flags & kActionFlag_LastValueGood)) + { + WriteData(action, action->lastValue); + + action->flags &= ~kActionFlag_LastValueGood; + } + } + + /* ----- remove watchpoint ----- */ + RemoveAssociatedWatches(entry); + + entry->flags &= ~kCheatFlag_StateMask; +} + +/*-------------------------------------------------------------------- + TempDeactivateCheat - restore previous value when turn CHEAT "OFF" +--------------------------------------------------------------------*/ + +static void TempDeactivateCheat(CheatEntry * entry) +{ + if(entry->flags & kCheatFlag_Active) + { + int i; + + for(i = 0; i < entry->actionListLength; i++) + { + CheatAction * action = &entry->actionList[i]; + + /* ----- restore previous value if needed ----- */ + if(EXTRACT_FIELD(action->type, RestorePreviousValue) && (action->flags & kActionFlag_LastValueGood)) + WriteData(action, action->lastValue); + } + } +} + +/*------------------------------------------------------------ + cheat_periodicOperation - management for cheat operations +------------------------------------------------------------*/ + +static void cheat_periodicOperation(CheatAction * action) +{ + UINT8 operation = EXTRACT_FIELD(action->type, Operation) | (EXTRACT_FIELD(action->type, OperationExtend) << 2); + + switch(operation) + { + case kOperation_WriteMask: + { + UINT32 temp; + + if(action->flags & kActionFlag_IgnoreMask) + WriteData(action, action->data); + else + { + temp = ReadData(action); + + temp = (action->data & action->extendData) | (temp & ~action->extendData); + + WriteData(action, temp); + } + } + break; + + case kOperation_AddSubtract: + { + INT32 temp, bound; + + /* ----- if extend data field is invalid, direct return ----- */ + //if(action->flags & kActionFlag_IgnoreMask) + return; + + temp = ReadData(action); + + /* ----- OperationParameter field stores add/subtract ----- */ + if(TEST_FIELD(action->type, OperationParameter)) + { + /* ----- subtract ----- */ + bound = action->extendData + action->data; + + if(temp > bound) + temp -= action->data; + } + else + { + /* ----- add ----- */ + bound = action->extendData - action->data; + + if(temp < bound) + temp += action->data; + } + + WriteData(action, temp); + } + break; + + case kOperation_ForceRange: + { + UINT32 temp; + + /* ----- if extend data field is invalid, direct return ----- */ + if(action->flags & kActionFlag_IgnoreMask) + return; + + temp = ReadData(action); + + if(!TEST_FIELD(action->type, BytesUsed)) + { + /* ----- 8 bit ----- */ + if( (temp < ((action->extendData >> 8) & 0xFF)) || (temp > ((action->extendData >> 0) & 0xFF))) + { + temp = action->data; + + WriteData(action, temp); + } + } + else + { + /* ----- 16 bit (or 24, 32 bit) ----- */ + if( (temp < ((action->extendData >> 16) & 0xFFFF)) || (temp > ((action->extendData >> 0) & 0xFFFF))) + { + temp = action->data; + + WriteData(action, temp); + } + } + } + break; + + case kOperation_SetOrClearBits: + { + UINT32 temp; + + temp = ReadData(action); + + if(TEST_FIELD(action->type, OperationParameter)) + /* ----- clear bit ----- */ + temp &= ~action->data; + else + /* ----- set bit ----- */ + temp |= action->data; + + WriteData(action, temp); + } + break; + + case kOperation_WriteMatch: + { + UINT32 temp; + + /* ----- if extend data field is invalid, direct return ----- */ + if(action->flags & kActionFlag_IgnoreMask) + return; + + temp = ReadData(action); + + /* ----- if current value matches appointed value, write ----- */ + if(temp == action->extendData) + { + temp = action->data; + + WriteData(action, temp); + } + } + break; + + case kOperation_None: + break; + + default: + break; + } +} + +/*------------------------------------------------------ + cheat_periodicAction - management for cheat actions +------------------------------------------------------*/ + +static void cheat_periodicAction(CheatAction * action) +{ + UINT8 parameter = EXTRACT_FIELD(action->type, TypeParameter); + + /* ----- if operation is done in case of one shot, no action ----- */ + if(action->flags & kActionFlag_OperationDone) + return; + + if(TEST_FIELD(action->type, Prefill) && (!(action->flags & kActionFlag_PrefillDone))) + { + /* ----- pre-fill ----- */ + UINT32 prefillValue = kPrefillValueTable[EXTRACT_FIELD(action->type, Prefill)]; + + if(!(action->flags & kActionFlag_PrefillWritten)) + { + /* ----- set pre-fill value ---- */ + WriteData(action, prefillValue); + + action->flags |= kActionFlag_PrefillWritten; + + return; + } + else + { + /* ----- if value is changed, try to write new value ----- */ + if(ReadData(action) == prefillValue) + return; + + action->flags |= kActionFlag_PrefillDone; + } + } + + switch(EXTRACT_FIELD(action->type, Type)) + { + case kType_NormalOrDelay: + { + if(TEST_FIELD(action->type, OneShot) && TEST_FIELD(action->type, RestorePreviousValue) && parameter) + { + /* ----- keep if one shot + restore prevous value + delay !=0 ----- */ + cheat_periodicOperation(action); + + if(action->frameTimer >= (parameter * ATTOSECONDS_TO_HZ(Machine->screen[0].refresh))) + { + action->frameTimer = 0; + + action->flags |= kActionFlag_OperationDone; + } + else + action->frameTimer++; + } + else + { + /* ----- otherwise, delay ----- */ + if(action->frameTimer >= (parameter * ATTOSECONDS_TO_HZ(Machine->screen[0].refresh))) + { + action->frameTimer = 0; + + cheat_periodicOperation(action); + + if(TEST_FIELD(action->type, OneShot)) + action->flags |= kActionFlag_OperationDone; + } + else + action->frameTimer++; + } + } + break; + + case kType_WaitForModification: + { + if(action->flags & kActionFlag_WasModified) + { + if(action->frameTimer <= 0) + { + cheat_periodicOperation(action); + + action->flags &= ~kActionFlag_WasModified; + + if(TEST_FIELD(action->type, OneShot)) + action->flags |= kActionFlag_OperationDone; + } + else + action->frameTimer--; + + action->lastValue = ReadData(action); + } + else + { + UINT32 currentValue = ReadData(action); + + if(currentValue != action->lastValue) + { + action->frameTimer = parameter * ATTOSECONDS_TO_HZ(Machine->screen[0].refresh); + + action->flags |= kActionFlag_WasModified; + } + + action->lastValue = currentValue; + } + } + break; + + case kType_IgnoreIfDecrementing: + { + UINT32 currentValue = ReadData(action); + + /* ### is "NotEqual" correct ? "LessOrEqual" seems to be good... ### */ + if(currentValue != (action->lastValue - parameter)) + { + cheat_periodicOperation(action); + + if(TEST_FIELD(action->type, OneShot)) + action->flags |= kActionFlag_OperationDone; + } + + action->lastValue = currentValue; + } + break; + + default: + break; + } +} + +/*----------------------------------------------------- + cheat_periodicEntry - management for cheat entries +-----------------------------------------------------*/ + +static void cheat_periodicEntry(CheatEntry * entry) +{ + int i; + + /* ----- special handling for label-selection ----- */ + if(entry->flags & kCheatFlag_Select) + { + /* ----- special handling for activation key ----- */ + if((entry->flags & kCheatFlag_HasActivationKey1) || (entry->flags & kCheatFlag_HasActivationKey2)) + { + if(input_code_pressed(entry->activationKey1) || input_code_pressed(entry->activationKey2)) + { + if(!(entry->flags & kCheatFlag_ActivationKeyPressed)) + { + /* ----- move current position until find OFF or no link extension code ----- */ + do{ + /* ----- 1st activation key pressed ----- */ + if(input_code_pressed(entry->activationKey1)) + { + entry->selection++; + + if(entry->flags & kCheatFlag_OneShot) + { + if(entry->selection >= entry->actionListLength) + { + entry->selection = 1; + + if(entry->selection >= entry->actionListLength) + entry->selection = 0; + } + + if(!TEST_FIELD(entry->actionList[entry->selection].type, LinkExtension)) + { + ActivateCheat(entry); + + entry->flags |= kCheatFlag_DoOneShot; + } + } + else + { + if(entry->selection >= entry->actionListLength) + { + entry->selection = 0; + + DeactivateCheat(entry); + } + else + { + if(!TEST_FIELD(entry->actionList[entry->selection].type, LinkExtension)) + ActivateCheat(entry); + } + } + } + /* ----- 2nd activation key pressed ----- */ + else + { + entry->selection--; + + if(entry->flags & kCheatFlag_OneShot) + { + if(entry->selection <= 0) + entry->selection = entry->actionListLength - 1; + + if(!TEST_FIELD(entry->actionList[entry->selection].type, LinkExtension)) + { + ActivateCheat(entry); + + entry->flags |= kCheatFlag_DoOneShot; + } + } + else + { + if(!entry->selection) + DeactivateCheat(entry); + else + { + if(entry->selection < 0) + entry->selection = entry->actionListLength - 1; + + if(!TEST_FIELD(entry->actionList[entry->selection].type, LinkExtension)) + ActivateCheat(entry); + } + } + } + }while(entry->selection && TEST_FIELD(entry->actionList[entry->selection].type, LinkExtension)); + + /* ----- display selected label name if needed ----- */ + if(TEST_FIELD(cheatOptions, ActivationKeyMessage)) + { + if(!entry->selection) + ui_popup_time(1,"%s disabled", entry->name); + else + ui_popup_time(1,"%s : %s selected", entry->name, entry->actionList[entry->selection].optionalName); + } + + entry->flags |= kCheatFlag_ActivationKeyPressed; // set flag + } + } + else + entry->flags &= ~kCheatFlag_ActivationKeyPressed; // clear flag + } + + /* ----- if a subcheat is selected, handle it ----- */ + if(entry->selection) + { + i = entry->selection; + + do{ + if(!(entry->flags & kCheatFlag_OneShot) || (entry->flags & kCheatFlag_DoOneShot)) + cheat_periodicAction(&entry->actionList[i]); + + i++; + + /* ----- if illegal index, finish ----- */ + if(i >= entry->actionListLength) + break; + + /* ----- if next code has link extension, handle it too ----- */ + }while(TEST_FIELD(entry->actionList[i].type, LinkExtension)); + } + } + else + { + /* ----- NOTE : activatinon key for value-selection is disabled -----*/ + if(((entry->flags & kCheatFlag_HasActivationKey1) || (entry->flags & kCheatFlag_HasActivationKey2)) && + !(entry->flags & kCheatFlag_UserSelect)) + { + /* ----- special handling for activation key ----- */ + if(input_code_pressed(entry->activationKey1) || input_code_pressed(entry->activationKey2)) + { + if(!(entry->flags & kCheatFlag_ActivationKeyPressed)) + { + if(entry->flags & kCheatFlag_OneShot) + { + ActivateCheat(entry); + + /* ----- display description for one shot if needed ----- */ + if(TEST_FIELD(cheatOptions, ActivationKeyMessage)) + ui_popup_time(1,"set %s", entry->name); + } + else + { + if(entry->flags & kCheatFlag_Active) + { + DeactivateCheat(entry); + + /* ----- display description for OFF if needed ----- */ + if(TEST_FIELD(cheatOptions, ActivationKeyMessage)) + ui_popup_time(1,"%s disabled", entry->name); + } + else + { + ActivateCheat(entry); + + /* ----- display description for ON if needed ----- */ + if(TEST_FIELD(cheatOptions, ActivationKeyMessage)) + ui_popup_time(1,"%s enabled", entry->name); + } + } + + entry->flags |= kCheatFlag_ActivationKeyPressed; + } + } + else + entry->flags &= ~kCheatFlag_ActivationKeyPressed; + } + + if(!(entry->flags & kCheatFlag_Active)) + return; + + /* ----- update all actions ----- */ + for(i = 0; i < entry->actionListLength; i++) + cheat_periodicAction(&entry->actionList[i]); + + /* ----- if all actions are done, deactivate the cheat if oneshot entry ----- */ + { + UINT8 done = 1; + + for(i = 0 ; (i < entry->actionListLength && done) ; i++) + if(!(entry->actionList[i].flags & kActionFlag_OperationDone)) + done = 0; + + if(done) + DeactivateCheat(entry); + } + } +} + +/*------------------------------------------------------------------ + UpdateAllCheatInfo - update all cheat info when database loaded +------------------------------------------------------------------*/ + +static void UpdateAllCheatInfo(void) +{ + int i; + + for(i = 0; i < cheatListLength; i++) + UpdateCheatInfo(&cheatList[i], 1); +} + +/*-------------------------------------------------- + UpdateCheatInfo - update entry, action and flag +--------------------------------------------------*/ + +static void UpdateCheatInfo(CheatEntry * entry, UINT8 isLoadTime) +{ + int isOneShot = 1; + int isNull = 1; + int flags = 0; + int i; + + flags = entry->flags & kCheatFlag_PersistentMask; + + /* ---- is it label-selection ? ----- */ + if( (EXTRACT_FIELD(entry->actionList[0].type, LocationType) == kLocation_Custom) && + (EXTRACT_FIELD(entry->actionList[0].type, LocationParameter) == kCustomLocation_Select)) + flags |= kCheatFlag_Select; + + for(i = 0; i < entry->actionListLength; i++) + { + CheatAction * action = &entry->actionList[i]; + + int isActionNull = 0; + UINT32 size; + UINT32 operation; + UINT32 actionFlags = action->flags & kActionFlag_PersistentMask; + + size = EXTRACT_FIELD(action->type, BytesUsed); + + operation = EXTRACT_FIELD(action->type, Operation) | EXTRACT_FIELD(action->type, OperationExtend) << 2; + + /* ---- is it comment ? ----- */ + if( (EXTRACT_FIELD(action->type, LocationType) == kLocation_Custom) && + (EXTRACT_FIELD(action->type, LocationParameter) == kCustomLocation_Comment)) + isActionNull = 1; + else + isNull = 0; + + /* ---- is it one shot ? ----- */ + if(!TEST_FIELD(action->type, OneShot)) + isOneShot = 0; + + /* ---- is it value-selection ? ----- */ + if(TEST_FIELD(action->type, UserSelectEnable)) + flags |= kCheatFlag_UserSelect; + + /* ---- is it relative address ? ----- */ + if(EXTRACT_FIELD(action->type, LocationType) == kLocation_IndirectIndexed) + actionFlags |= kActionFlag_IgnoreMask; + else + { + if(isLoadTime) + { + /* ----- check for mask == 0 and fix when database loaded ----- */ + if((operation == kOperation_WriteMask) && (action->extendData == 0)) + action->extendData = ~0; + } + } + + action->flags = actionFlags; + } + + /* ---- set one shot flag ----- */ + if(isOneShot) + flags |= kCheatFlag_OneShot; + + /* ---- set null flag ----- */ + if(isNull) + flags |= kCheatFlag_Null; + + entry->flags = (flags & kCheatFlag_InfoMask) | (entry->flags & ~kCheatFlag_InfoMask); + + if(isLoadTime) + entry->flags &= ~kCheatFlag_Dirty; +} + +/*-------------------------------------------------------------------------------- + IsAddressInRange - check an address is in range (1 = in range, 0 = out range) +--------------------------------------------------------------------------------*/ + +static int IsAddressInRange(CheatAction * action, UINT32 length) +{ + UINT8 bytes = EXTRACT_FIELD(action->type, BytesUsed) + 1; + + return ((action->address + bytes) <= length); +} + +/*--------------------------------------------------------------- + BuildCPUInfoList - get CPU info when initialize cheat system +---------------------------------------------------------------*/ + +static void BuildCPUInfoList(void) +{ + int i; + + /* ----- do regions ----- */ + { + const rom_entry * traverse = rom_first_region(Machine->gamedrv); + + memset(regionInfoList, 0, sizeof(CPUInfo) * kRegionListLength); + + while(traverse) + { + if(ROMENTRY_ISREGION(traverse)) + { + UINT8 regionType = ROMREGION_GETTYPE(traverse); + + /* ----- non-cpu region ? ----- */ + if((regionType >= REGION_GFX1) && (regionType <= REGION_USER8)) + { + CPUInfo * info = ®ionInfoList[regionType - REGION_INVALID]; + UINT32 length = memory_region_length(regionType); + int bitState = 0; + + info->type = regionType; + info->dataBits = ROMREGION_GETWIDTH(traverse); + + info->addressBits = 0; + info->addressMask = length; + + /* ----- build address mask ----- */ + for(i = 0; i < 32; i++) + { + UINT32 mask = 1 << (31 - i); + + if(bitState) + info->addressMask |= mask; + else + { + if(info->addressMask & mask) + { + info->addressBits = 32 - i; + bitState = 1; + } + } + } + + info->addressCharsNeeded = info->addressBits >> 2; + + if(info->addressBits & 3) + info->addressCharsNeeded++; + + info->endianness = ROMREGION_ISBIGENDIAN(traverse); + } + } + + traverse = rom_next_region(traverse); + } + } + + /* ----- do CPUs ----- */ + { + memset(cpuInfoList, 0, sizeof(CPUInfo) * MAX_CPU); + + for(i = 0; i < cpu_gettotalcpu(); i++) + { + CPUInfo * info = &cpuInfoList[i]; + CPUInfo * regionInfo = ®ionInfoList[REGION_CPU1 + i - REGION_INVALID]; + + cpu_type type = Machine->drv->cpu[i].type; + + info->type = type; + info->dataBits = cputype_databus_width(type, ADDRESS_SPACE_PROGRAM); + info->addressBits = cputype_addrbus_width(type, ADDRESS_SPACE_PROGRAM); + info->addressMask = 0xFFFFFFFF >> (32 - cputype_addrbus_width(type, ADDRESS_SPACE_PROGRAM)); + + info->addressCharsNeeded = info->addressBits >> 2; + if(info->addressBits & 0x3) + info->addressCharsNeeded++; + + info->endianness = (cputype_endianness(type) == CPU_IS_BE); + + switch(type) + { +#if HAS_TMS34010 + case CPU_TMS34010: + info->addressShift = 3; + break; +#endif +#if HAS_TMS34020 + case CPU_TMS34020: + info->addressShift = 3; + break; +#endif + default: + info->addressShift = 0; + break; + } + + /* ----- copy to region list ----- */ + memcpy(regionInfo, info, sizeof(CPUInfo)); + } + } +} + |