From 2e11f0197105a80dfb43323602aed2cbbabaecf7 Mon Sep 17 00:00:00 2001 From: gocha Date: Sun, 15 Jul 2012 18:41:23 +0900 Subject: [PATCH] win32: add tasvideos ramwatch & ramsearch tools. Note: - Traditional Cheat Search has been removed from the menu, but its implementation still remains in the source code, to make importing the changes in snes9xgit to snes9x-rr easier. - It can be compiled in x64 Unicode. At the moment, Unicode version exports wch file in UTF-16 (without BOM). I will probably change the charset to UTF-8 or something 8-bit friendly code for better compatibility. - LVN_GETDISPINFO handlers of both ramwatch and ramsearch are modified, since it apparently was harmful and caused a crash with Unicode version. Probbably, this change is applicable to all tasvideos emulators. --- win32/ram_search.cpp | 2118 +++++++++++++++++++++++++++++++++++++++++ win32/ram_search.h | 120 +++ win32/ramwatch.cpp | 1282 +++++++++++++++++++++++++ win32/ramwatch.h | 52 + win32/rsrc/resource.h | 57 ++ win32/rsrc/snes9x.rc | 188 +++- win32/snes9xw.vcproj | 16 + win32/wsnes9x.cpp | 54 +- win32/wsnes9x.h | 13 + 9 files changed, 3878 insertions(+), 22 deletions(-) create mode 100644 win32/ram_search.cpp create mode 100644 win32/ram_search.h create mode 100644 win32/ramwatch.cpp create mode 100644 win32/ramwatch.h diff --git a/win32/ram_search.cpp b/win32/ram_search.cpp new file mode 100644 index 00000000..d987f5d1 --- /dev/null +++ b/win32/ram_search.cpp @@ -0,0 +1,2118 @@ +// A few notes about this implementation of a RAM search window: +// +// Speed of update was one of the highest priories. +// This is because I wanted the RAM search window to be able to +// update every single value in RAM every single frame, and +// keep track of the exact number of frames across which each value has changed, +// without causing the emulation to run noticeably slower than normal. +// +// The data representation was changed from one entry per valid address +// to one entry per contiguous range of uneliminated addresses +// which references uniform pools of per-address properties. +// - This saves time when there are many items because +// it minimizes the amount of data that needs to be stored and processed per address. +// - It also saves time when there are few items because +// it ensures that no time is wasted in iterating through +// addresses that have already been eliminated from the search. +// +// The worst-case scenario is when every other item has been +// eliminated from the search, maximizing the number of regions. +// This implementation manages to handle even that pathological case +// acceptably well. In fact, it still updates faster than the previous implementation. +// The time spent setting up or clearing such a large number of regions +// is somewhat horrendous, but it seems reasonable to have poor worst-case speed +// during these sporadic "setup" steps to achieve an all-around faster per-update speed. +// (You can test this case by performing the search: Modulo 2 Is Specific Address 0) + + +#include "rsrc/resource.h" + +#include "../port.h" +#include "../snes9x.h" +#include "../memmap.h" +#include "../cheats.h" +#include "wsnes9x.h" + +#include "ram_search.h" +#include +#include +#include "ramwatch.h" +#include +#include +#ifdef _WIN32 + #include "BaseTsd.h" + typedef INT_PTR intptr_t; +#else + #include "stdint.h" +#endif + +extern SCheatData Cheat; + +struct MemoryRegion +{ + HWAddressType hardwareAddress; // hardware address of the start of this region + unsigned int size; // number of bytes to the end of this region + unsigned char* softwareAddress; // pointer to the start of the live emulator source values for this region + + unsigned int virtualIndex; // index into s_prevValues, s_curValues, and s_numChanges, valid after being initialized in ResetMemoryRegions() + unsigned int itemIndex; // index into listbox items, valid when s_itemIndicesInvalid is false +}; + +int MAX_RAM_SIZE = 0; +static unsigned char* s_prevValues = 0; // values at last search or reset +static unsigned char* s_curValues = 0; // values at last frame update +static unsigned short* s_numChanges = 0; // number of changes of the item starting at this virtual index address +static MemoryRegion** s_itemIndexToRegionPointer = 0; // used for random access into the memory list (trading memory size to get speed here, too bad it's so much memory), only valid when s_itemIndicesInvalid is false +static BOOL s_itemIndicesInvalid = true; // if true, the link from listbox items to memory regions (s_itemIndexToRegionPointer) and the link from memory regions to list box items (MemoryRegion::itemIndex) both need to be recalculated +static BOOL s_prevValuesNeedUpdate = true; // if true, the "prev" values should be updated using the "cur" values on the next frame update signaled +static unsigned int s_maxItemIndex = 0; // max currently valid item index, the listbox sometimes tries to update things past the end of the list so we need to know this to ignore those attempts + +HWND RamSearchHWnd; +#define hWnd GUI.hWnd +#define hInst GUI.hInstance +static TCHAR Str_Tmp [1024]; + +int disableRamSearchUpdate = false; + + +// list of contiguous uneliminated memory regions +typedef std::list MemoryList; +static MemoryList s_activeMemoryRegions; +static CRITICAL_SECTION s_activeMemoryRegionsCS; + +// for undo support (could be better, but this way was really easy) +static MemoryList s_activeMemoryRegionsBackup; +static int s_undoType = 0; // 0 means can't undo, 1 means can undo, 2 means can redo + +void RamSearchSaveUndoStateIfNotTooBig(HWND hDlg); +static const int tooManyRegionsForUndo = 10000; + +void ResetMemoryRegions() +{ +// Clear_Sound_Buffer(); + EnterCriticalSection(&s_activeMemoryRegionsCS); + + s_activeMemoryRegions.clear(); + + // use RWInternalToSoftwareAddress to figure out what all the possible memory regions are, + // split up wherever there's a discontinuity in the address in our software RAM. + static const int regionSearchGranularity = 0x100; // if this is too small, we'll waste time (in this function only), but if any region in RAM isn't evenly divisible by this, we might crash. + HWAddressType hwRegionStart = 0; + uint8* regionStart = NULL; + uint8* regionEnd = NULL; + for(HWAddressType addr = 0; addr != 0x10000000+regionSearchGranularity; addr += regionSearchGranularity) + { + uint8* swAddr = RWInternalToSoftwareAddress(addr); + if(regionEnd && swAddr != regionEnd+regionSearchGranularity) + { + // hit end of region + // check to see if it mirrors an existing one (in which case we discard it) + bool discard = false; + for(MemoryList::iterator iter = s_activeMemoryRegions.begin(); iter != s_activeMemoryRegions.end(); ++iter) + { + MemoryRegion& region = *iter; + if(region.softwareAddress == regionStart) + { + unsigned int size = regionSearchGranularity + (regionEnd - regionStart); + if(size <= region.size) + { + discard = true; + } + else + { + hwRegionStart += region.size; + regionStart += region.size; + } + break; + } + } + + // don't include ROM in our RAM search (it's too huge) + if(regionStart == Memory.ROM) + discard = true; + + // create the region + if(!discard) + { + MemoryRegion region = { hwRegionStart, regionSearchGranularity + (regionEnd - regionStart), regionStart }; + s_activeMemoryRegions.push_back(region); + } + + hwRegionStart = 0; + regionStart = NULL; + regionEnd = NULL; + } + if(swAddr) + { + if(regionStart) + { + // continue region + regionEnd = swAddr; + } + else + { + // start new region + hwRegionStart = addr; + regionStart = swAddr; + regionEnd = swAddr; + } + } + } + + + int nextVirtualIndex = 0; + for(MemoryList::iterator iter = s_activeMemoryRegions.begin(); iter != s_activeMemoryRegions.end(); ++iter) + { + MemoryRegion& region = *iter; + region.virtualIndex = nextVirtualIndex; + assert(((intptr_t)region.softwareAddress & 1) == 0 && "somebody needs to reimplement ReadValueAtSoftwareAddress()"); + nextVirtualIndex = region.virtualIndex + region.size; + } + //assert(nextVirtualIndex <= MAX_RAM_SIZE); + + if(nextVirtualIndex > MAX_RAM_SIZE) + { + s_prevValues = (unsigned char*)realloc(s_prevValues, sizeof(char)*(nextVirtualIndex+4)); + memset(s_prevValues, 0, sizeof(char)*(nextVirtualIndex+4)); + + s_curValues = (unsigned char*)realloc(s_curValues, sizeof(char)*(nextVirtualIndex+4)); + memset(s_curValues, 0, sizeof(char)*(nextVirtualIndex+4)); + + s_numChanges = (unsigned short*)realloc(s_numChanges, sizeof(short)*(nextVirtualIndex+4)); + memset(s_numChanges, 0, sizeof(short)*(nextVirtualIndex+4)); + + s_itemIndexToRegionPointer = (MemoryRegion**)realloc(s_itemIndexToRegionPointer, sizeof(MemoryRegion*)*(nextVirtualIndex+4)); + memset(s_itemIndexToRegionPointer, 0, sizeof(MemoryRegion*)*(nextVirtualIndex+4)); + + MAX_RAM_SIZE = nextVirtualIndex; + } + LeaveCriticalSection(&s_activeMemoryRegionsCS); +} + +// eliminates a range of hardware addresses from the search results +// returns 2 if it changed the region and moved the iterator to another region +// returns 1 if it changed the region but didn't move the iterator +// returns 0 if it had no effect +// warning: don't call anything that takes an itemIndex in a loop that calls DeactivateRegion... +// doing so would be tremendously slow because DeactivateRegion invalidates the index cache +int DeactivateRegion(MemoryRegion& region, MemoryList::iterator& iter, HWAddressType hardwareAddress, unsigned int size) +{ + if(hardwareAddress + size <= region.hardwareAddress || hardwareAddress >= region.hardwareAddress + region.size) + { + // region is unaffected + return 0; + } + else if(hardwareAddress > region.hardwareAddress && hardwareAddress + size >= region.hardwareAddress + region.size) + { + // erase end of region + region.size = hardwareAddress - region.hardwareAddress; + return 1; + } + else if(hardwareAddress <= region.hardwareAddress && hardwareAddress + size < region.hardwareAddress + region.size) + { + // erase start of region + int eraseSize = (hardwareAddress + size) - region.hardwareAddress; + region.hardwareAddress += eraseSize; + region.size -= eraseSize; + region.softwareAddress += eraseSize; + region.virtualIndex += eraseSize; + return 1; + } + else if(hardwareAddress <= region.hardwareAddress && hardwareAddress + size >= region.hardwareAddress + region.size) + { + // erase entire region + iter = s_activeMemoryRegions.erase(iter); + s_itemIndicesInvalid = TRUE; + return 2; + } + else //if(hardwareAddress > region.hardwareAddress && hardwareAddress + size < region.hardwareAddress + region.size) + { + // split region + int eraseSize = (hardwareAddress + size) - region.hardwareAddress; + MemoryRegion region2 = {region.hardwareAddress + eraseSize, region.size - eraseSize, region.softwareAddress + eraseSize, region.virtualIndex + eraseSize}; + region.size = hardwareAddress - region.hardwareAddress; + iter = s_activeMemoryRegions.insert(++iter, region2); + s_itemIndicesInvalid = TRUE; + return 2; + } +} + +/* +// eliminates a range of hardware addresses from the search results +// this is a simpler but usually slower interface for the above function +void DeactivateRegion(HWAddressType hardwareAddress, unsigned int size) +{ + for(MemoryList::iterator iter = s_activeMemoryRegions.begin(); iter != s_activeMemoryRegions.end(); ) + { + MemoryRegion& region = *iter; + if(2 != DeactivateRegion(region, iter, hardwareAddress, size)) + ++iter; + } +} +*/ + +struct AutoCritSect +{ + AutoCritSect(CRITICAL_SECTION* cs) : m_cs(cs) { EnterCriticalSection(m_cs); } + ~AutoCritSect() { LeaveCriticalSection(m_cs); } + CRITICAL_SECTION* m_cs; +}; + +// warning: can be slow +void CalculateItemIndices(int itemSize) +{ + AutoCritSect cs(&s_activeMemoryRegionsCS); + unsigned int itemIndex = 0; + for(MemoryList::iterator iter = s_activeMemoryRegions.begin(); iter != s_activeMemoryRegions.end(); ++iter) + { + MemoryRegion& region = *iter; + region.itemIndex = itemIndex; + int startSkipSize = ((unsigned int)(itemSize - (unsigned int)region.hardwareAddress)) % itemSize; // FIXME: is this still ok? + unsigned int start = startSkipSize; + unsigned int end = region.size; + for(unsigned int i = start; i < end; i += itemSize) + s_itemIndexToRegionPointer[itemIndex++] = ®ion; + } + s_maxItemIndex = itemIndex; + s_itemIndicesInvalid = FALSE; +} + +template +void UpdateRegionT(const MemoryRegion& region, const MemoryRegion* nextRegionPtr) +{ + //if(GetAsyncKeyState(VK_SHIFT) & 0x8000) // speed hack + // return; + + if(s_prevValuesNeedUpdate) + memcpy(s_prevValues + region.virtualIndex, s_curValues + region.virtualIndex, region.size + sizeof(compareType) - sizeof(stepType)); + + unsigned int startSkipSize = ((unsigned int)(sizeof(stepType) - region.hardwareAddress)) % sizeof(stepType); + + + unsigned char* sourceAddr = region.softwareAddress - region.virtualIndex; + + unsigned int indexStart = region.virtualIndex + startSkipSize; + unsigned int indexEnd = region.virtualIndex + region.size; + + if(sizeof(compareType) == 1) + { + for(unsigned int i = indexStart; i < indexEnd; i++) + { + if(s_curValues[i] != sourceAddr[i]) // if value changed + { + s_curValues[i] = sourceAddr[i]; // update value + //if(s_numChanges[i] != 0xFFFF) + s_numChanges[i]++; // increase change count + } + } + } + else // it's more complicated for non-byte sizes because: + { // - more than one byte can affect a given change count entry + // - when more than one of those bytes changes simultaneously the entry's change count should only increase by 1 + // - a few of those bytes can be outside the region + + unsigned int endSkipSize = ((unsigned int)(startSkipSize - region.size)) % sizeof(stepType); + unsigned int lastIndexToRead = indexEnd + endSkipSize + sizeof(compareType) - sizeof(stepType); + unsigned int lastIndexToCopy = lastIndexToRead; + if(nextRegionPtr) + { + const MemoryRegion& nextRegion = *nextRegionPtr; + int nextStartSkipSize = ((unsigned int)(sizeof(stepType) - nextRegion.hardwareAddress)) % sizeof(stepType); + unsigned int nextIndexStart = nextRegion.virtualIndex + nextStartSkipSize; + if(lastIndexToCopy > nextIndexStart) + lastIndexToCopy = nextIndexStart; + } + + unsigned int nextValidChange [sizeof(compareType)]; + for(unsigned int i = 0; i < sizeof(compareType); i++) + nextValidChange[i] = indexStart + i; + + for(unsigned int i = indexStart, j = 0; i < lastIndexToRead; i++, j++) + { + if(s_curValues[i] != sourceAddr[i]) // if value of this byte changed + { + if(i < lastIndexToCopy) + s_curValues[i] = sourceAddr[i]; // update value + for(int k = 0; k < sizeof(compareType); k++) // loop through the previous entries that contain this byte + { + if(i >= indexEnd+k) + continue; + int m = (j-k+sizeof(compareType)) & (sizeof(compareType)-1); + if(nextValidChange[m] <= i) // if we didn't already increase the change count for this entry + { + //if(s_numChanges[i-k] != 0xFFFF) + s_numChanges[i-k]++; // increase the change count for this entry + nextValidChange[m] = i-k+sizeof(compareType); // and remember not to increase it again + } + } + } + } + } +} + +template +void UpdateRegionsT() +{ + for(MemoryList::iterator iter = s_activeMemoryRegions.begin(); iter != s_activeMemoryRegions.end();) + { + const MemoryRegion& region = *iter; + ++iter; + const MemoryRegion* nextRegion = (iter == s_activeMemoryRegions.end()) ? NULL : &*iter; + + UpdateRegionT(region, nextRegion); + } + + s_prevValuesNeedUpdate = false; +} + +template +int CountRegionItemsT() +{ + AutoCritSect cs(&s_activeMemoryRegionsCS); + if(sizeof(stepType) == 1) + { + if(s_activeMemoryRegions.empty()) + return 0; + + if(s_itemIndicesInvalid) + CalculateItemIndices(sizeof(stepType)); + + MemoryRegion& lastRegion = s_activeMemoryRegions.back(); + return lastRegion.itemIndex + lastRegion.size; + } + else // the branch above is faster but won't work if the step size isn't 1 + { + int total = 0; + for(MemoryList::iterator iter = s_activeMemoryRegions.begin(); iter != s_activeMemoryRegions.end(); ++iter) + { + MemoryRegion& region = *iter; + int startSkipSize = ((unsigned int)(sizeof(stepType) - region.hardwareAddress)) % sizeof(stepType); + total += (region.size - startSkipSize + (sizeof(stepType)-1)) / sizeof(stepType); + } + return total; + } +} + +// returns information about the item in the form of a "fake" region +// that has the item in it and nothing else +template +void ItemIndexToVirtualRegion(unsigned int itemIndex, MemoryRegion& virtualRegion) +{ + if(s_itemIndicesInvalid) + CalculateItemIndices(sizeof(stepType)); + + if(itemIndex >= s_maxItemIndex) + { + memset(&virtualRegion, 0, sizeof(MemoryRegion)); + return; + } + + const MemoryRegion* regionPtr = s_itemIndexToRegionPointer[itemIndex]; + const MemoryRegion& region = *regionPtr; + + int bytesWithinRegion = (itemIndex - region.itemIndex) * sizeof(stepType); + int startSkipSize = ((unsigned int)(sizeof(stepType) - region.hardwareAddress)) % sizeof(stepType); + bytesWithinRegion += startSkipSize; + + virtualRegion.size = sizeof(compareType); + virtualRegion.hardwareAddress = region.hardwareAddress + bytesWithinRegion; + virtualRegion.softwareAddress = region.softwareAddress + bytesWithinRegion; + virtualRegion.virtualIndex = region.virtualIndex + bytesWithinRegion; + virtualRegion.itemIndex = itemIndex; + return; +} + +template +unsigned int ItemIndexToVirtualIndex(unsigned int itemIndex) +{ + MemoryRegion virtualRegion; + ItemIndexToVirtualRegion(itemIndex, virtualRegion); + return virtualRegion.virtualIndex; +} + +template +T ReadLocalValue(const unsigned char* data) +{ + return *(const T*)data; +} +//template<> signed char ReadLocalValue(const unsigned char* data) { return *data; } +//template<> unsigned char ReadLocalValue(const unsigned char* data) { return *data; } + + +template +compareType GetPrevValueFromVirtualIndex(unsigned int virtualIndex) +{ + return ReadLocalValue(s_prevValues + virtualIndex); + //return *(compareType*)(s_prevValues+virtualIndex); +} +template +compareType GetCurValueFromVirtualIndex(unsigned int virtualIndex) +{ + return ReadLocalValue(s_curValues + virtualIndex); +// return *(compareType*)(s_curValues+virtualIndex); +} +template +unsigned short GetNumChangesFromVirtualIndex(unsigned int virtualIndex) +{ + unsigned short num = s_numChanges[virtualIndex]; + //for(unsigned int i = 1; i < sizeof(stepType); i++) + // if(num < s_numChanges[virtualIndex+i]) + // num = s_numChanges[virtualIndex+i]; + return num; +} + +template +compareType GetPrevValueFromItemIndex(unsigned int itemIndex) +{ + int virtualIndex = ItemIndexToVirtualIndex(itemIndex); + return GetPrevValueFromVirtualIndex(virtualIndex); +} +template +compareType GetCurValueFromItemIndex(unsigned int itemIndex) +{ + int virtualIndex = ItemIndexToVirtualIndex(itemIndex); + return GetCurValueFromVirtualIndex(virtualIndex); +} +template +unsigned short GetNumChangesFromItemIndex(unsigned int itemIndex) +{ + int virtualIndex = ItemIndexToVirtualIndex(itemIndex); + return GetNumChangesFromVirtualIndex(virtualIndex); +} +template +unsigned int GetHardwareAddressFromItemIndex(unsigned int itemIndex) +{ + MemoryRegion virtualRegion; + ItemIndexToVirtualRegion(itemIndex, virtualRegion); + return virtualRegion.hardwareAddress; +} + +// this one might be unreliable, haven't used it much +template +unsigned int HardwareAddressToItemIndex(HWAddressType hardwareAddress) +{ + if(s_itemIndicesInvalid) + CalculateItemIndices(sizeof(stepType)); + + for(MemoryList::iterator iter = s_activeMemoryRegions.begin(); iter != s_activeMemoryRegions.end(); ++iter) + { + MemoryRegion& region = *iter; + if(hardwareAddress >= region.hardwareAddress && hardwareAddress < region.hardwareAddress + region.size) + { + int indexWithinRegion = (hardwareAddress - region.hardwareAddress) / sizeof(stepType); + return region.itemIndex + indexWithinRegion; + } + } + + return -1; +} + + + +// workaround for MSVC 7 that doesn't support varadic C99 macros +#define CALL_WITH_T_SIZE_TYPES_0(functionName, sizeTypeID, isSigned, requiresAligned) \ + (sizeTypeID == TEXT('b') \ + ? (isSigned \ + ? functionName() \ + : functionName()) \ + : sizeTypeID == TEXT('w') \ + ? (isSigned \ + ? (requiresAligned \ + ? functionName() \ + : functionName()) \ + : (requiresAligned \ + ? functionName() \ + : functionName())) \ + : sizeTypeID == TEXT('d') \ + ? (isSigned \ + ? (requiresAligned \ + ? functionName() \ + : functionName()) \ + : (requiresAligned \ + ? functionName() \ + : functionName())) \ + : functionName()) + +#define CALL_WITH_T_SIZE_TYPES_1(functionName, sizeTypeID, isSigned, requiresAligned, p0) \ + (sizeTypeID == TEXT('b') \ + ? (isSigned \ + ? functionName(p0) \ + : functionName(p0)) \ + : sizeTypeID == TEXT('w') \ + ? (isSigned \ + ? (requiresAligned \ + ? functionName(p0) \ + : functionName(p0)) \ + : (requiresAligned \ + ? functionName(p0) \ + : functionName(p0))) \ + : sizeTypeID == TEXT('d') \ + ? (isSigned \ + ? (requiresAligned \ + ? functionName(p0) \ + : functionName(p0)) \ + : (requiresAligned \ + ? functionName(p0) \ + : functionName(p0))) \ + : functionName(p0)) + +#define CALL_WITH_T_SIZE_TYPES_3(functionName, sizeTypeID, isSigned, requiresAligned, p0, p1, p2) \ + (sizeTypeID == TEXT('b') \ + ? (isSigned \ + ? functionName(p0, p1, p2) \ + : functionName(p0, p1, p2)) \ + : sizeTypeID == TEXT('w') \ + ? (isSigned \ + ? (requiresAligned \ + ? functionName(p0, p1, p2) \ + : functionName(p0, p1, p2)) \ + : (requiresAligned \ + ? functionName(p0, p1, p2) \ + : functionName(p0, p1, p2))) \ + : sizeTypeID == TEXT('d') \ + ? (isSigned \ + ? (requiresAligned \ + ? functionName(p0, p1, p2) \ + : functionName(p0, p1, p2)) \ + : (requiresAligned \ + ? functionName(p0, p1, p2) \ + : functionName(p0, p1, p2))) \ + : functionName(p0, p1, p2)) + +#define CALL_WITH_T_SIZE_TYPES_4(functionName, sizeTypeID, isSigned, requiresAligned, p0, p1, p2, p3) \ + (sizeTypeID == TEXT('b') \ + ? (isSigned \ + ? functionName(p0, p1, p2, p3) \ + : functionName(p0, p1, p2, p3)) \ + : sizeTypeID == TEXT('w') \ + ? (isSigned \ + ? (requiresAligned \ + ? functionName(p0, p1, p2, p3) \ + : functionName(p0, p1, p2, p3)) \ + : (requiresAligned \ + ? functionName(p0, p1, p2, p3) \ + : functionName(p0, p1, p2, p3))) \ + : sizeTypeID == TEXT('d') \ + ? (isSigned \ + ? (requiresAligned \ + ? functionName(p0, p1, p2, p3) \ + : functionName(p0, p1, p2, p3)) \ + : (requiresAligned \ + ? functionName(p0, p1, p2, p3) \ + : functionName(p0, p1, p2, p3))) \ + : functionName(p0, p1, p2, p3)) + +// version that takes a forced comparison type +#define CALL_WITH_T_STEP_3(functionName, sizeTypeID, type, requiresAligned, p0, p1, p2) \ + (sizeTypeID == TEXT('b') \ + ? functionName(p0, p1, p2) \ + : sizeTypeID == TEXT('w') \ + ? (requiresAligned \ + ? functionName(p0, p1, p2) \ + : functionName(p0, p1, p2)) \ + : sizeTypeID == TEXT('d') \ + ? (requiresAligned \ + ? functionName(p0, p1, p2) \ + : functionName(p0, p1, p2)) \ + : functionName(p0, p1, p2)) + +// version that takes a forced comparison type +#define CALL_WITH_T_STEP_4(functionName, sizeTypeID, type, requiresAligned, p0, p1, p2, p3) \ + (sizeTypeID == TEXT('b') \ + ? functionName(p0, p1, p2, p3) \ + : sizeTypeID == TEXT('w') \ + ? (requiresAligned \ + ? functionName(p0, p1, p2, p3) \ + : functionName(p0, p1, p2, p3)) \ + : sizeTypeID == TEXT('d') \ + ? (requiresAligned \ + ? functionName(p0, p1, p2, p3) \ + : functionName(p0, p1, p2, p3)) \ + : functionName(p0, p1, p2, p3)) + +// basic comparison functions: +template inline bool LessCmp (T x, T y, T i) { return x < y; } +template inline bool MoreCmp (T x, T y, T i) { return x > y; } +template inline bool LessEqualCmp (T x, T y, T i) { return x <= y; } +template inline bool MoreEqualCmp (T x, T y, T i) { return x >= y; } +template inline bool EqualCmp (T x, T y, T i) { return x == y; } +template inline bool UnequalCmp (T x, T y, T i) { return x != y; } +template inline bool DiffByCmp (T x, T y, T p) { return x - y == p || y - x == p; } +template inline bool ModIsCmp (T x, T y, T p) { return p && x % p == y; } + +// compare-to type functions: +template +void SearchRelative (bool(*cmpFun)(T,T,T), T ignored, T param) +{ + for(MemoryList::iterator iter = s_activeMemoryRegions.begin(); iter != s_activeMemoryRegions.end(); ) + { + MemoryRegion& region = *iter; + int startSkipSize = ((unsigned int)(sizeof(stepType) - region.hardwareAddress)) % sizeof(stepType); + unsigned int start = region.virtualIndex + startSkipSize; + unsigned int end = region.virtualIndex + region.size; + for(unsigned int i = start, hwaddr = region.hardwareAddress; i < end; i += sizeof(stepType), hwaddr += sizeof(stepType)) + if(!cmpFun(GetCurValueFromVirtualIndex(i), GetPrevValueFromVirtualIndex(i), param)) + if(2 == DeactivateRegion(region, iter, hwaddr, sizeof(stepType))) + goto outerContinue; + ++iter; +outerContinue: + continue; + } +} +template +void SearchSpecific (bool(*cmpFun)(T,T,T), T value, T param) +{ + for(MemoryList::iterator iter = s_activeMemoryRegions.begin(); iter != s_activeMemoryRegions.end(); ) + { + MemoryRegion& region = *iter; + int startSkipSize = ((unsigned int)(sizeof(stepType) - region.hardwareAddress)) % sizeof(stepType); + unsigned int start = region.virtualIndex + startSkipSize; + unsigned int end = region.virtualIndex + region.size; + for(unsigned int i = start, hwaddr = region.hardwareAddress; i < end; i += sizeof(stepType), hwaddr += sizeof(stepType)) + if(!cmpFun(GetCurValueFromVirtualIndex(i), value, param)) + if(2 == DeactivateRegion(region, iter, hwaddr, sizeof(stepType))) + goto outerContinue; + ++iter; +outerContinue: + continue; + } +} +template +void SearchAddress (bool(*cmpFun)(T,T,T), T address, T param) +{ + for(MemoryList::iterator iter = s_activeMemoryRegions.begin(); iter != s_activeMemoryRegions.end(); ) + { + MemoryRegion& region = *iter; + int startSkipSize = ((unsigned int)(sizeof(stepType) - region.hardwareAddress)) % sizeof(stepType); + unsigned int start = region.virtualIndex + startSkipSize; + unsigned int end = region.virtualIndex + region.size; + for(unsigned int i = start, hwaddr = region.hardwareAddress; i < end; i += sizeof(stepType), hwaddr += sizeof(stepType)) + if(!cmpFun(hwaddr, address, param)) + if(2 == DeactivateRegion(region, iter, hwaddr, sizeof(stepType))) + goto outerContinue; + ++iter; +outerContinue: + continue; + } +} +template +void SearchChanges (bool(*cmpFun)(T,T,T), T changes, T param) +{ + for(MemoryList::iterator iter = s_activeMemoryRegions.begin(); iter != s_activeMemoryRegions.end(); ) + { + MemoryRegion& region = *iter; + int startSkipSize = ((unsigned int)(sizeof(stepType) - region.hardwareAddress)) % sizeof(stepType); + unsigned int start = region.virtualIndex + startSkipSize; + unsigned int end = region.virtualIndex + region.size; + for(unsigned int i = start, hwaddr = region.hardwareAddress; i < end; i += sizeof(stepType), hwaddr += sizeof(stepType)) + if(!cmpFun(GetNumChangesFromVirtualIndex(i), changes, param)) + if(2 == DeactivateRegion(region, iter, hwaddr, sizeof(stepType))) + goto outerContinue; + ++iter; +outerContinue: + continue; + } +} + +char rs_c=TEXT('s'); +char rs_o=TEXT('='); +char rs_t=TEXT('s'); +int rs_param=0, rs_val=0, rs_val_valid=0; +char rs_type_size = TEXT('b'), rs_last_type_size = rs_type_size; +bool noMisalign = true, rs_last_no_misalign = noMisalign; +//bool littleEndian = false; +int last_rs_possible = -1; +int last_rs_regions = -1; + +void prune(char c,char o,char t,int v,int p) +{ + // repetition-reducing macros + #define DO_SEARCH(sf) \ + switch (o) \ + { \ + case TEXT('<'): DO_SEARCH_2(LessCmp,sf); break; \ + case TEXT('>'): DO_SEARCH_2(MoreCmp,sf); break; \ + case TEXT('='): DO_SEARCH_2(EqualCmp,sf); break; \ + case TEXT('!'): DO_SEARCH_2(UnequalCmp,sf); break; \ + case TEXT('l'): DO_SEARCH_2(LessEqualCmp,sf); break; \ + case TEXT('m'): DO_SEARCH_2(MoreEqualCmp,sf); break; \ + case TEXT('d'): DO_SEARCH_2(DiffByCmp,sf); break; \ + case TEXT('%'): DO_SEARCH_2(ModIsCmp,sf); break; \ + default: assert(!"Invalid operator for this search type."); break; \ + } + + // perform the search, eliminating nonmatching values + switch (c) + { + #define DO_SEARCH_2(CmpFun,sf) CALL_WITH_T_SIZE_TYPES_3(sf, rs_type_size, t, noMisalign, CmpFun,v,p) + case TEXT('r'): DO_SEARCH(SearchRelative); break; + case TEXT('s'): DO_SEARCH(SearchSpecific); break; + + #undef DO_SEARCH_2 + #define DO_SEARCH_2(CmpFun,sf) CALL_WITH_T_STEP_3(sf, rs_type_size, unsigned int, noMisalign, CmpFun,v,p) + case TEXT('a'): DO_SEARCH(SearchAddress); break; + + #undef DO_SEARCH_2 + #define DO_SEARCH_2(CmpFun,sf) CALL_WITH_T_STEP_3(sf, rs_type_size, unsigned short, noMisalign, CmpFun,v,p) + case TEXT('n'): DO_SEARCH(SearchChanges); break; + + default: assert(!"Invalid search comparison type."); break; + } + + s_prevValuesNeedUpdate = true; + + int prevNumItems = last_rs_possible; + + CompactAddrs(); + + if(prevNumItems == last_rs_possible) + { + SetRamSearchUndoType(RamSearchHWnd, 0); // nothing to undo + } +} + + + + +template +bool CompareRelativeAtItem (bool(*cmpFun)(T,T,T), int itemIndex, T ignored, T param) +{ + return cmpFun(GetCurValueFromItemIndex(itemIndex), GetPrevValueFromItemIndex(itemIndex), param); +} +template +bool CompareSpecificAtItem (bool(*cmpFun)(T,T,T), int itemIndex, T value, T param) +{ + return cmpFun(GetCurValueFromItemIndex(itemIndex), value, param); +} +template +bool CompareAddressAtItem (bool(*cmpFun)(T,T,T), int itemIndex, T address, T param) +{ + return cmpFun(GetHardwareAddressFromItemIndex(itemIndex), address, param); +} +template +bool CompareChangesAtItem (bool(*cmpFun)(T,T,T), int itemIndex, T changes, T param) +{ + return cmpFun(GetNumChangesFromItemIndex(itemIndex), changes, param); +} + +int ReadControlInt(int controlID, bool forceHex, BOOL& success) +{ + int rv = 0; + BOOL ok = false; + + if(!forceHex) + { + rv = GetDlgItemInt(RamSearchHWnd,controlID,&ok,(rs_t == TEXT('s'))); + } + + if(!ok) + { + if(GetDlgItemText(RamSearchHWnd,controlID,Str_Tmp,16)) + { + for(int i = 0; Str_Tmp[i]; i++) {if(toupper(Str_Tmp[i]) == TEXT('O')) Str_Tmp[i] = TEXT('0');} + const TCHAR* strPtr = Str_Tmp; + bool negate = false; + while(strPtr[0] == TEXT('-')) + strPtr++, negate = !negate; + if(strPtr[0] == TEXT('+')) + strPtr++; + if(strPtr[0] == TEXT('0') && tolower(strPtr[1]) == TEXT('x')) + strPtr += 2, forceHex = true; + if(strPtr[0] == TEXT('$')) + strPtr++, forceHex = true; + if(!forceHex) + { + const TCHAR* strSearchPtr = strPtr; + while(*strSearchPtr) + { + int c = _totlower(*strSearchPtr++); + if(c >= TEXT('a') && c <= TEXT('f')) + forceHex = true; + } + } + const TCHAR* formatString = forceHex ? TEXT("%X") : ((rs_t==TEXT('s')) ? TEXT("%d") : TEXT("%u")); + if(_stscanf(strPtr, formatString, &rv) > 0) + ok = true; + if(negate) + rv = -rv; + } + } + + success = ok; + return rv; +} + +int ReadControlAddr(int controlID, BOOL& success) +{ + int rv = 0; + BOOL ok = false; + TCHAR str[11]; + + GetDlgItemText(RamSearchHWnd,controlID,str,11); + rv = DisplayToRWInternalAddress(str); + ok = true; // TODO: validation + + success = ok; + return rv; +} + + +bool Set_RS_Val() +{ + BOOL success; + + // update rs_val + switch(rs_c) + { + case TEXT('r'): + default: + rs_val = 0; + break; + case TEXT('s'): + rs_val = ReadControlInt(IDC_EDIT_COMPAREVALUE, rs_t == TEXT('h'), success); + if(!success) + return false; + if((rs_type_size == TEXT('b') && rs_t == TEXT('s') && (rs_val < -128 || rs_val > 127)) || + (rs_type_size == TEXT('b') && rs_t != TEXT('s') && (rs_val < 0 || rs_val > 255)) || + (rs_type_size == TEXT('w') && rs_t == TEXT('s') && (rs_val < -32768 || rs_val > 32767)) || + (rs_type_size == TEXT('w') && rs_t != TEXT('s') && (rs_val < 0 || rs_val > 65535))) + return false; + break; + case TEXT('a'): + rs_val = ReadControlAddr(IDC_EDIT_COMPAREADDRESS, success); + if(!success || rs_val < 0 || rs_val > 0x06040000) + return false; + break; + case TEXT('n'): { + rs_val = ReadControlInt(IDC_EDIT_COMPARECHANGES, false, success); + if(!success || rs_val < 0 || rs_val > 0xFFFF) + return false; + } break; + } + + // also update rs_param + switch(rs_o) + { + default: + rs_param = 0; + break; + case TEXT('d'): + rs_param = ReadControlInt(IDC_EDIT_DIFFBY, false, success); + if(!success) + return false; + if(rs_param < 0) + rs_param = -rs_param; + break; + case TEXT('%'): + rs_param = ReadControlInt(IDC_EDIT_MODBY, false, success); + if(!success || rs_param == 0) + return false; + break; + } + + // validate that rs_param fits in the comparison data type + { + int appliedSize = rs_type_size; + int appliedSign = rs_t; + if(rs_c == TEXT('n')) + appliedSize = TEXT('w'), appliedSign = TEXT('u'); + if(rs_c == TEXT('a')) + appliedSize = TEXT('d'), appliedSign = TEXT('u'); + if((appliedSize == TEXT('b') && appliedSize == TEXT('s') && (rs_param < -128 || rs_param > 127)) || + (appliedSize == TEXT('b') && appliedSize != TEXT('s') && (rs_param < 0 || rs_param > 255)) || + (appliedSize == TEXT('w') && appliedSize == TEXT('s') && (rs_param < -32768 || rs_param > 32767)) || + (appliedSize == TEXT('w') && appliedSize != TEXT('s') && (rs_param < 0 || rs_param > 65535))) + return false; + } + + return true; +} + +bool IsSatisfied(int itemIndex) +{ + if(!rs_val_valid) + return true; + int o = rs_o; + switch (rs_c) + { + #undef DO_SEARCH_2 + #define DO_SEARCH_2(CmpFun,sf) return CALL_WITH_T_SIZE_TYPES_4(sf, rs_type_size,(rs_t==TEXT('s')),noMisalign, CmpFun,itemIndex,rs_val,rs_param); + case TEXT('r'): DO_SEARCH(CompareRelativeAtItem); break; + case TEXT('s'): DO_SEARCH(CompareSpecificAtItem); break; + + #undef DO_SEARCH_2 + #define DO_SEARCH_2(CmpFun,sf) return CALL_WITH_T_STEP_4(sf, rs_type_size, unsigned int, noMisalign, CmpFun,itemIndex,rs_val,rs_param); + case TEXT('a'): DO_SEARCH(CompareAddressAtItem); break; + + #undef DO_SEARCH_2 + #define DO_SEARCH_2(CmpFun,sf) return CALL_WITH_T_STEP_4(sf, rs_type_size, unsigned short, noMisalign, CmpFun,itemIndex,rs_val,rs_param); + case TEXT('n'): DO_SEARCH(CompareChangesAtItem); break; + } + return false; +} + + + +unsigned int ReadValueAtSoftwareAddress(const unsigned char* address, unsigned int size) +{ + unsigned int value = 0; + if(address) + { + // assumes we're little-endian + memcpy(&value, address, size); + } + return value; +} +void WriteValueAtSoftwareAddress(unsigned char* address, unsigned int value, unsigned int size) +{ + if(address) + { + // assumes we're little-endian + memcpy(address, &value, size); + } +} +unsigned int ReadValueAtHardwareAddress(HWAddressType address, unsigned int size) +{ + return ReadValueAtSoftwareAddress(RWInternalToSoftwareAddress(address), size); +} +bool WriteValueAtHardwareAddress(HWAddressType address, unsigned int value, unsigned int size) +{ + WriteValueAtSoftwareAddress(RWInternalToSoftwareAddress(address), value, size); + return true; +} +bool IsRAMWatchAddressValid(HWAddressType address) +{ + return RWInternalToSoftwareAddress(address) != NULL; +} + + + +int ResultCount=0; +bool AutoSearch=false; +bool AutoSearchAutoRetry=false; +LRESULT CALLBACK PromptWatchNameProc(HWND, UINT, WPARAM, LPARAM); +void UpdatePossibilities(int rs_possible, int regions); + + +void CompactAddrs() +{ + int size = (rs_type_size==TEXT('b') || !noMisalign) ? 1 : 2; + int prevResultCount = ResultCount; + + CalculateItemIndices(size); + ResultCount = CALL_WITH_T_SIZE_TYPES_0(CountRegionItemsT, rs_type_size,rs_t==TEXT('s'),noMisalign); + + UpdatePossibilities(ResultCount, (int)s_activeMemoryRegions.size()); + + if(ResultCount != prevResultCount) + ListView_SetItemCount(GetDlgItem(RamSearchHWnd,IDC_RAMLIST),ResultCount); +} + +void soft_reset_address_info () +{ + s_prevValuesNeedUpdate = false; + ResetMemoryRegions(); + if(!RamSearchHWnd) + { + EnterCriticalSection(&s_activeMemoryRegionsCS); + s_activeMemoryRegions.clear(); + LeaveCriticalSection(&s_activeMemoryRegionsCS); + ResultCount = 0; + } + else + { + // force s_prevValues to be valid + signal_new_frame(); + s_prevValuesNeedUpdate = true; + signal_new_frame(); + } + if(s_numChanges) + memset(s_numChanges, 0, (sizeof(*s_numChanges)*(MAX_RAM_SIZE))); + CompactAddrs(); +} +void reset_address_info () +{ + SetRamSearchUndoType(RamSearchHWnd, 0); + EnterCriticalSection(&s_activeMemoryRegionsCS); + s_activeMemoryRegionsBackup.clear(); // not necessary, but we'll take the time hit here instead of at the next thing that sets up an undo + LeaveCriticalSection(&s_activeMemoryRegionsCS); + if(s_prevValues) + memcpy(s_prevValues, s_curValues, (sizeof(*s_prevValues)*(MAX_RAM_SIZE))); + s_prevValuesNeedUpdate = false; + ResetMemoryRegions(); + if(!RamSearchHWnd) + { + EnterCriticalSection(&s_activeMemoryRegionsCS); + s_activeMemoryRegions.clear(); + LeaveCriticalSection(&s_activeMemoryRegionsCS); + ResultCount = 0; + } + else + { + // force s_prevValues to be valid + signal_new_frame(); + s_prevValuesNeedUpdate = true; + signal_new_frame(); + } + memset(s_numChanges, 0, (sizeof(*s_numChanges)*(MAX_RAM_SIZE))); + CompactAddrs(); +} + +void signal_new_frame () +{ + EnterCriticalSection(&s_activeMemoryRegionsCS); + CALL_WITH_T_SIZE_TYPES_0(UpdateRegionsT, rs_type_size, rs_t==TEXT('s'), noMisalign); + LeaveCriticalSection(&s_activeMemoryRegionsCS); +} + + + + + +bool RamSearchClosed = false; +bool RamWatchClosed = false; + +void ResetResults() +{ + reset_address_info(); + ResultCount = 0; + if (RamSearchHWnd) + ListView_SetItemCount(GetDlgItem(RamSearchHWnd,IDC_RAMLIST),ResultCount); +} +void CloseRamWindows() //Close the Ram Search & Watch windows when rom closes +{ + ResetWatches(); + ResetResults(); + if (RamSearchHWnd) + { + SendMessage(RamSearchHWnd,WM_CLOSE,NULL,NULL); + RamSearchClosed = true; + } + if (RamWatchHWnd) + { + SendMessage(RamWatchHWnd,WM_CLOSE,NULL,NULL); + RamWatchClosed = true; + } +} +void ReopenRamWindows() //Reopen them when a new Rom is loaded +{ + HWND hwnd = GetActiveWindow(); + + if (RamSearchClosed) + { + RamSearchClosed = false; + if(!RamSearchHWnd) + { + reset_address_info(); + LRESULT CALLBACK RamSearchProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); + RamSearchHWnd = CreateDialog(hInst, MAKEINTRESOURCE(IDD_RAMSEARCH), hWnd, (DLGPROC) RamSearchProc); + } + } + if (RamWatchClosed || AutoRWLoad) + { + RamWatchClosed = false; + if(!RamWatchHWnd) + { + if (AutoRWLoad) OpenRWRecentFile(0); + LRESULT CALLBACK RamWatchProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); + RamWatchHWnd = CreateDialog(hInst, MAKEINTRESOURCE(IDD_RAMWATCH), hWnd, (DLGPROC) RamWatchProc); + } + } + + if (hwnd == hWnd && hwnd != GetActiveWindow()) + SetActiveWindow(hWnd); // restore focus to the main window if it had it before +} + + + + + + +void RefreshRamListSelectedCountControlStatus(HWND hDlg) +{ + static int prevSelCount=-1; + int selCount = ListView_GetSelectedCount(GetDlgItem(hDlg,IDC_RAMLIST)); + if(selCount != prevSelCount) + { + if(selCount < 2 || prevSelCount < 2) + { + EnableWindow(GetDlgItem(hDlg, IDC_C_WATCH), (selCount >= 1 && WatchCount < MAX_WATCH_COUNT) ? TRUE : FALSE); + EnableWindow(GetDlgItem(hDlg, IDC_C_ADDCHEAT), (selCount >= 1) ? TRUE : FALSE); + EnableWindow(GetDlgItem(hDlg, IDC_C_ELIMINATE), (selCount >= 1) ? TRUE : FALSE); + } + prevSelCount = selCount; + } +} + + + + +struct AddrRange +{ + unsigned int addr; + unsigned int size; + unsigned int End() const { return addr + size; } + AddrRange(unsigned int a, unsigned int s) : addr(a),size(s){} +}; + +void signal_new_size () +{ + HWND lv = GetDlgItem(RamSearchHWnd,IDC_RAMLIST); + + int oldSize = (rs_last_type_size==TEXT('b') || !rs_last_no_misalign) ? 1 : 2; + int newSize = (rs_type_size==TEXT('b') || !noMisalign) ? 1 : 2; + bool numberOfItemsChanged = (oldSize != newSize); + + unsigned int itemsPerPage = ListView_GetCountPerPage(lv); + unsigned int oldTopIndex = ListView_GetTopIndex(lv); + unsigned int oldSelectionIndex = ListView_GetSelectionMark(lv); + unsigned int oldTopAddr = CALL_WITH_T_SIZE_TYPES_1(GetHardwareAddressFromItemIndex, rs_last_type_size,rs_t==TEXT('s'),rs_last_no_misalign, oldTopIndex); + unsigned int oldSelectionAddr = CALL_WITH_T_SIZE_TYPES_1(GetHardwareAddressFromItemIndex, rs_last_type_size,rs_t==TEXT('s'),rs_last_no_misalign, oldSelectionIndex); + + std::vector selHardwareAddrs; + if(numberOfItemsChanged) + { + // store selection ranges + // unfortunately this can take a while if the user has a huge range of items selected +// Clear_Sound_Buffer(); + int selCount = ListView_GetSelectedCount(lv); + int size = (rs_last_type_size==TEXT('b') || !rs_last_no_misalign) ? 1 : 2; + int watchIndex = -1; + for(int i = 0; i < selCount; ++i) + { + watchIndex = ListView_GetNextItem(lv, watchIndex, LVNI_SELECTED); + int addr = CALL_WITH_T_SIZE_TYPES_1(GetHardwareAddressFromItemIndex, rs_last_type_size,rs_t==TEXT('s'),rs_last_no_misalign, watchIndex); + if(!selHardwareAddrs.empty() && addr == selHardwareAddrs.back().End()) + selHardwareAddrs.back().size += size; + else if (!(noMisalign && oldSize < newSize && addr % newSize != 0)) + selHardwareAddrs.push_back(AddrRange(addr,size)); + } + } + + CompactAddrs(); + + rs_last_type_size = rs_type_size; + rs_last_no_misalign = noMisalign; + + if(numberOfItemsChanged) + { + // restore selection ranges + unsigned int newTopIndex = CALL_WITH_T_SIZE_TYPES_1(HardwareAddressToItemIndex, rs_type_size,rs_t==TEXT('s'),noMisalign, oldTopAddr); + unsigned int newBottomIndex = newTopIndex + itemsPerPage - 1; + SendMessage(lv, WM_SETREDRAW, FALSE, 0); + ListView_SetItemState(lv, -1, 0, LVIS_SELECTED|LVIS_FOCUSED); // deselect all + for(unsigned int i = 0; i < selHardwareAddrs.size(); i++) + { + // calculate index ranges of this selection + const AddrRange& range = selHardwareAddrs[i]; + int selRangeTop = CALL_WITH_T_SIZE_TYPES_1(HardwareAddressToItemIndex, rs_type_size,rs_t==TEXT('s'),noMisalign, range.addr); + int selRangeBottom = -1; + for(int endAddr = range.End()-1; endAddr >= selRangeTop && selRangeBottom == -1; endAddr--) + selRangeBottom = CALL_WITH_T_SIZE_TYPES_1(HardwareAddressToItemIndex, rs_type_size,rs_t==TEXT('s'),noMisalign, endAddr); + if(selRangeBottom == -1) + selRangeBottom = selRangeTop; + if(selRangeTop == -1) + continue; + + // select the entire range + for (unsigned int j = selRangeTop; j <= selRangeBottom; j++) + { + ListView_SetItemState(lv, j, LVIS_SELECTED|LVIS_FOCUSED, LVIS_SELECTED|LVIS_FOCUSED); + } + } + + // restore previous scroll position + if(newBottomIndex != -1) + ListView_EnsureVisible(lv, newBottomIndex, 0); + if(newTopIndex != -1) + ListView_EnsureVisible(lv, newTopIndex, 0); + + SendMessage(lv, WM_SETREDRAW, TRUE, 0); + + RefreshRamListSelectedCountControlStatus(RamSearchHWnd); + + EnableWindow(GetDlgItem(RamSearchHWnd,IDC_MISALIGN), rs_type_size != TEXT('b')); + } + else + { + ListView_Update(lv, -1); + } + InvalidateRect(lv, NULL, TRUE); + //SetFocus(lv); +} + + + + +LRESULT CustomDraw (LPARAM lParam) +{ + LPNMLVCUSTOMDRAW lplvcd = (LPNMLVCUSTOMDRAW)lParam; + + switch(lplvcd->nmcd.dwDrawStage) + { + case CDDS_PREPAINT : + return CDRF_NOTIFYITEMDRAW; + + case CDDS_ITEMPREPAINT: + { + int rv = CDRF_DODEFAULT; + + if(lplvcd->nmcd.dwItemSpec % 2) + { + // alternate the background color slightly + lplvcd->clrTextBk = RGB(248,248,255); + rv = CDRF_NEWFONT; + } + + if(!IsSatisfied(lplvcd->nmcd.dwItemSpec)) + { + // tint red any items that would be eliminated if a search were to run now + lplvcd->clrText = RGB(192,64,64); + rv = CDRF_NEWFONT; + } + + return rv; + } break; + } + return CDRF_DODEFAULT; +} + +void Update_RAM_Search() //keeps RAM values up to date in the search and watch windows +{ + if(disableRamSearchUpdate) + return; + + int prevValuesNeededUpdate; + if (AutoSearch && !ResultCount) + { + if(!AutoSearchAutoRetry) + { +// Clear_Sound_Buffer(); + int answer = MessageBox(RamSearchHWnd,TEXT("Choosing Retry will reset the search once and continue autosearching.\nChoose Ignore will reset the search whenever necessary and continue autosearching.\nChoosing Abort will reset the search once and stop autosearching."),TEXT("Autosearch - out of results."),MB_ABORTRETRYIGNORE|MB_DEFBUTTON2|MB_ICONINFORMATION); + if(answer == IDABORT) + { + SendDlgItemMessage(RamSearchHWnd, IDC_C_AUTOSEARCH, BM_SETCHECK, BST_UNCHECKED, 0); + SendMessage(RamSearchHWnd, WM_COMMAND, IDC_C_AUTOSEARCH, 0); + } + if(answer == IDIGNORE) + AutoSearchAutoRetry = true; + } + reset_address_info(); + prevValuesNeededUpdate = s_prevValuesNeedUpdate; + } + else + { + prevValuesNeededUpdate = s_prevValuesNeedUpdate; + if (RamSearchHWnd) + { + // update active RAM values + signal_new_frame(); + } + + if (AutoSearch && ResultCount) + { + //Clear_Sound_Buffer(); + if(!rs_val_valid) + rs_val_valid = Set_RS_Val(); + if(rs_val_valid) + prune(rs_c,rs_o,rs_t==TEXT('s'),rs_val,rs_param); + } + } + + if(RamSearchHWnd) + { + HWND lv = GetDlgItem(RamSearchHWnd,IDC_RAMLIST); + if(prevValuesNeededUpdate != s_prevValuesNeedUpdate) + { + // previous values got updated, refresh everything visible + ListView_Update(lv, -1); + } + else + { + // refresh any visible parts of the listview box that changed + static int changes[128]; + int top = ListView_GetTopIndex(lv); + int count = ListView_GetCountPerPage(lv); + int start = -1; + for(int i = top; i <= top+count; i++) + { + int changeNum = CALL_WITH_T_SIZE_TYPES_1(GetNumChangesFromItemIndex, rs_type_size,rs_t==TEXT('s'),noMisalign, i); //s_numChanges[i]; + int changed = changeNum != changes[i-top]; + if(changed) + changes[i-top] = changeNum; + + if(start == -1) + { + if(i != top+count && changed) + { + start = i; + //somethingChanged = true; + } + } + else + { + if(i == top+count || !changed) + { + ListView_RedrawItems(lv, start, i-1); + start = -1; + } + } + } + } + } + + if(RamWatchHWnd) + { + Update_RAM_Watch(); + } +} + +static int rs_lastPercent = -1; +inline void UpdateRamSearchProgressBar(int percent) +{ + if(rs_lastPercent != percent) + { + rs_lastPercent = percent; + UpdateRamSearchTitleBar(percent); + } +} + +static void SelectEditControl(int controlID) +{ + HWND hEdit = GetDlgItem(RamSearchHWnd,controlID); + SetFocus(hEdit); + SendMessage(hEdit, EM_SETSEL, 0, -1); +} + +static BOOL SelectingByKeyboard() +{ + int a = GetKeyState(VK_LEFT); + int b = GetKeyState(VK_RIGHT); + int c = GetKeyState(VK_UP); + int d = GetKeyState(VK_DOWN); // space and tab are intentionally omitted + return (a | b | c | d) & 0x80; +} + + +LRESULT CALLBACK RamSearchProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + RECT r; + RECT r2; + int dx1, dy1, dx2, dy2; + static int watchIndex=0; + + switch(uMsg) + { + case WM_INITDIALOG: { + RamSearchHWnd = hDlg; + + GetWindowRect(hWnd, &r); + dx1 = (r.right - r.left) / 2; + dy1 = (r.bottom - r.top) / 2; + + GetWindowRect(hDlg, &r2); + dx2 = (r2.right - r2.left) / 2; + dy2 = (r2.bottom - r2.top) / 2; + + // push it away from the main window if we can + const int width = (r.right-r.left); + const int width2 = (r2.right-r2.left); + if(r.left+width2 + width < GetSystemMetrics(SM_CXSCREEN)) + { + r.right += width; + r.left += width; + } + else if((int)r.left - (int)width2 > 0) + { + r.right -= width2; + r.left -= width2; + } + + SetWindowPos(hDlg, NULL, r.left, r.top, NULL, NULL, SWP_NOSIZE | SWP_NOZORDER | SWP_SHOWWINDOW); + switch(rs_o) + { + case TEXT('<'): + SendDlgItemMessage(hDlg, IDC_LESSTHAN, BM_SETCHECK, BST_CHECKED, 0); + break; + case TEXT('>'): + SendDlgItemMessage(hDlg, IDC_MORETHAN, BM_SETCHECK, BST_CHECKED, 0); + break; + case TEXT('l'): + SendDlgItemMessage(hDlg, IDC_NOMORETHAN, BM_SETCHECK, BST_CHECKED, 0); + break; + case TEXT('m'): + SendDlgItemMessage(hDlg, IDC_NOLESSTHAN, BM_SETCHECK, BST_CHECKED, 0); + break; + case TEXT('='): + SendDlgItemMessage(hDlg, IDC_EQUALTO, BM_SETCHECK, BST_CHECKED, 0); + break; + case TEXT('!'): + SendDlgItemMessage(hDlg, IDC_DIFFERENTFROM, BM_SETCHECK, BST_CHECKED, 0); + break; + case TEXT('d'): + SendDlgItemMessage(hDlg, IDC_DIFFERENTBY, BM_SETCHECK, BST_CHECKED, 0); + EnableWindow(GetDlgItem(hDlg,IDC_EDIT_DIFFBY),true); + break; + case TEXT('%'): + SendDlgItemMessage(hDlg, IDC_MODULO, BM_SETCHECK, BST_CHECKED, 0); + EnableWindow(GetDlgItem(hDlg,IDC_EDIT_MODBY),true); + break; + } + switch (rs_c) + { + case TEXT('r'): + SendDlgItemMessage(hDlg, IDC_PREVIOUSVALUE, BM_SETCHECK, BST_CHECKED, 0); + break; + case TEXT('s'): + SendDlgItemMessage(hDlg, IDC_SPECIFICVALUE, BM_SETCHECK, BST_CHECKED, 0); + EnableWindow(GetDlgItem(hDlg,IDC_EDIT_COMPAREVALUE),true); + break; + case TEXT('a'): + SendDlgItemMessage(hDlg, IDC_SPECIFICADDRESS, BM_SETCHECK, BST_CHECKED, 0); + EnableWindow(GetDlgItem(hDlg,IDC_EDIT_COMPAREADDRESS),true); + break; + case TEXT('n'): + SendDlgItemMessage(hDlg, IDC_NUMBEROFCHANGES, BM_SETCHECK, BST_CHECKED, 0); + EnableWindow(GetDlgItem(hDlg,IDC_EDIT_COMPARECHANGES),true); + break; + } + switch (rs_t) + { + case TEXT('s'): + SendDlgItemMessage(hDlg, IDC_SIGNED, BM_SETCHECK, BST_CHECKED, 0); + break; + case TEXT('u'): + SendDlgItemMessage(hDlg, IDC_UNSIGNED, BM_SETCHECK, BST_CHECKED, 0); + break; + case TEXT('h'): + SendDlgItemMessage(hDlg, IDC_HEX, BM_SETCHECK, BST_CHECKED, 0); + break; + } + switch (rs_type_size) + { + case TEXT('b'): + SendDlgItemMessage(hDlg, IDC_1_BYTE, BM_SETCHECK, BST_CHECKED, 0); + break; + case TEXT('w'): + SendDlgItemMessage(hDlg, IDC_2_BYTES, BM_SETCHECK, BST_CHECKED, 0); + break; + case TEXT('d'): + SendDlgItemMessage(hDlg, IDC_4_BYTES, BM_SETCHECK, BST_CHECKED, 0); + break; + } + + s_prevValuesNeedUpdate = true; + + SendDlgItemMessage(hDlg,IDC_C_AUTOSEARCH,BM_SETCHECK,AutoSearch?BST_CHECKED:BST_UNCHECKED,0); + //const TCHAR* names[5] = {TEXT("Address"),TEXT("Value"),TEXT("Previous"),TEXT("Changes"),TEXT("Notes")}; + //int widths[5] = {62,64,64,55,55}; + const TCHAR* names[] = {TEXT("Address"),TEXT("Value"),TEXT("Previous"),TEXT("Changes")}; + int widths[4] = {68,76,76,68}; + if (!ResultCount) + reset_address_info(); + else + { + signal_new_frame(); + CompactAddrs(); + } + void init_list_box(HWND Box, const TCHAR* Strs[], int numColumns, int *columnWidths); + init_list_box(GetDlgItem(hDlg,IDC_RAMLIST),names,4,widths); + //ListView_SetItemCount(GetDlgItem(hDlg,IDC_RAMLIST),ResultCount); + if (!noMisalign) SendDlgItemMessage(hDlg, IDC_MISALIGN, BM_SETCHECK, BST_CHECKED, 0); + //if (littleEndian) SendDlgItemMessage(hDlg, IDC_ENDIAN, BM_SETCHECK, BST_CHECKED, 0); + last_rs_possible = -1; + RefreshRamListSelectedCountControlStatus(hDlg); + + // force misalign checkbox to refresh + signal_new_size(); + + // force undo button to refresh + int undoType = s_undoType; + SetRamSearchUndoType(hDlg, -2); + SetRamSearchUndoType(hDlg, undoType); + + // force possibility count to refresh + last_rs_possible--; + UpdatePossibilities(ResultCount, (int)s_activeMemoryRegions.size()); + + rs_val_valid = Set_RS_Val(); + + ListView_SetCallbackMask(GetDlgItem(hDlg,IDC_RAMLIST), LVIS_FOCUSED|LVIS_SELECTED); + + return true; + } break; + + case WM_NOTIFY: + { + LPNMHDR lP = (LPNMHDR) lParam; + switch (lP->code) + { + case LVN_ITEMCHANGED: // selection changed event + { + NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)lP; + if(pNMListView->uNewState & LVIS_FOCUSED || + (pNMListView->uNewState ^ pNMListView->uOldState) & LVIS_SELECTED) + { + // disable buttons that we don't have the right number of selected items for + RefreshRamListSelectedCountControlStatus(hDlg); + } + } break; + + case LVN_GETDISPINFO: + { + LV_DISPINFO *Item = (LV_DISPINFO *)lParam; + + // ignore unnecessary requests + Item->item.mask &= LVIF_TEXT; + + // set content only when it's requested + if (Item->item.mask & LVIF_TEXT) + { + static TCHAR num[11]; + const unsigned int iNum = Item->item.iItem; + switch (Item->item.iSubItem) + { + case 0: + { + HWAddressType addr = CALL_WITH_T_SIZE_TYPES_1(GetHardwareAddressFromItemIndex, rs_type_size,rs_t==TEXT('s'),noMisalign, iNum); + lstrcpy(num, RWInternalToDisplayAddress(addr)); + Item->item.pszText = num; + } break; + + case 1: + { + int i = CALL_WITH_T_SIZE_TYPES_1(GetCurValueFromItemIndex, rs_type_size,rs_t==TEXT('s'),noMisalign, iNum); + const TCHAR* formatString = ((rs_t==TEXT('s')) ? TEXT("%d") : (rs_t==TEXT('u')) ? TEXT("%u") : (rs_type_size==TEXT('d') ? TEXT("%08X") : rs_type_size==TEXT('w') ? TEXT("%04X") : TEXT("%02X"))); + switch (rs_type_size) + { + case TEXT('b'): + default: _stprintf(num, formatString, rs_t==TEXT('s') ? (char)(i&0xff) : (unsigned char)(i&0xff)); break; + case TEXT('w'): _stprintf(num, formatString, rs_t==TEXT('s') ? (short)(i&0xffff) : (unsigned short)(i&0xffff)); break; + case TEXT('d'): _stprintf(num, formatString, rs_t==TEXT('s') ? (long)(i&0xffffffff) : (unsigned long)(i&0xffffffff)); break; + } + Item->item.pszText = num; + } break; + + case 2: + { + int i = CALL_WITH_T_SIZE_TYPES_1(GetPrevValueFromItemIndex, rs_type_size,rs_t==TEXT('s'),noMisalign, iNum); + const TCHAR* formatString = ((rs_t==TEXT('s')) ? TEXT("%d") : (rs_t==TEXT('u')) ? TEXT("%u") : (rs_type_size==TEXT('d') ? TEXT("%08X") : rs_type_size==TEXT('w') ? TEXT("%04X") : TEXT("%02X"))); + switch (rs_type_size) + { + case TEXT('b'): + default: _stprintf(num, formatString, rs_t==TEXT('s') ? (char)(i&0xff) : (unsigned char)(i&0xff)); break; + case TEXT('w'): _stprintf(num, formatString, rs_t==TEXT('s') ? (short)(i&0xffff) : (unsigned short)(i&0xffff)); break; + case TEXT('d'): _stprintf(num, formatString, rs_t==TEXT('s') ? (long)(i&0xffffffff) : (unsigned long)(i&0xffffffff)); break; + } + Item->item.pszText = num; + } break; + + case 3: + { + int i = CALL_WITH_T_SIZE_TYPES_1(GetNumChangesFromItemIndex, rs_type_size,rs_t==TEXT('s'),noMisalign, iNum); + _stprintf(num,TEXT("%d"),i); + + Item->item.pszText = num; + } break; + } + } + } break; + + case NM_CUSTOMDRAW: + { + SetWindowLongPtr(hDlg, DWLP_MSGRESULT, CustomDraw(lParam)); + return TRUE; + } break; + + //case LVN_ODCACHEHINT: //Copied this bit from the MSDN virtual listbox code sample. Eventually it should probably do something. + //{ + // LPNMLVCACHEHINT lpCacheHint = (LPNMLVCACHEHINT)lParam; + // return 0; + //} + //case LVN_ODFINDITEM: //Copied this bit from the MSDN virtual listbox code sample. Eventually it should probably do something. + //{ + // LPNMLVFINDITEM lpFindItem = (LPNMLVFINDITEM)lParam; + // return 0; + //} + } + } break; + + case WM_COMMAND: + { + int rv = false; + switch(LOWORD(wParam)) + { + case IDC_SIGNED: + rs_t=TEXT('s'); + signal_new_size(); + {rv = true; break;} + case IDC_UNSIGNED: + rs_t=TEXT('u'); + signal_new_size(); + {rv = true; break;} + case IDC_HEX: + rs_t=TEXT('h'); + signal_new_size(); + {rv = true; break;} + case IDC_1_BYTE: + rs_type_size = TEXT('b'); + signal_new_size(); + {rv = true; break;} + case IDC_2_BYTES: + rs_type_size = TEXT('w'); + signal_new_size(); + {rv = true; break;} + case IDC_4_BYTES: + rs_type_size = TEXT('d'); + signal_new_size(); + {rv = true; break;} + case IDC_MISALIGN: + noMisalign = !noMisalign; + //CompactAddrs(); + signal_new_size(); + {rv = true; break;} +// case IDC_ENDIAN: +//// littleEndian = !littleEndian; +//// signal_new_size(); +// {rv = true; break;} + case IDC_LESSTHAN: + EnableWindow(GetDlgItem(hDlg,IDC_EDIT_DIFFBY),false); + EnableWindow(GetDlgItem(hDlg,IDC_EDIT_MODBY),false); + rs_o = TEXT('<'); + {rv = true; break;} + case IDC_MORETHAN: + EnableWindow(GetDlgItem(hDlg,IDC_EDIT_DIFFBY),false); + EnableWindow(GetDlgItem(hDlg,IDC_EDIT_MODBY),false); + rs_o = TEXT('>'); + {rv = true; break;} + case IDC_NOMORETHAN: + EnableWindow(GetDlgItem(hDlg,IDC_EDIT_DIFFBY),false); + EnableWindow(GetDlgItem(hDlg,IDC_EDIT_MODBY),false); + rs_o = TEXT('l'); + {rv = true; break;} + case IDC_NOLESSTHAN: + EnableWindow(GetDlgItem(hDlg,IDC_EDIT_DIFFBY),false); + EnableWindow(GetDlgItem(hDlg,IDC_EDIT_MODBY),false); + rs_o = TEXT('m'); + {rv = true; break;} + case IDC_EQUALTO: + EnableWindow(GetDlgItem(hDlg,IDC_EDIT_DIFFBY),false); + EnableWindow(GetDlgItem(hDlg,IDC_EDIT_MODBY),false); + rs_o = TEXT('='); + {rv = true; break;} + case IDC_DIFFERENTFROM: + EnableWindow(GetDlgItem(hDlg,IDC_EDIT_DIFFBY),false); + EnableWindow(GetDlgItem(hDlg,IDC_EDIT_MODBY),false); + rs_o = TEXT('!'); + {rv = true; break;} + case IDC_DIFFERENTBY: + { + rs_o = TEXT('d'); + EnableWindow(GetDlgItem(hDlg,IDC_EDIT_DIFFBY),true); + EnableWindow(GetDlgItem(hDlg,IDC_EDIT_MODBY),false); + if(!SelectingByKeyboard()) + SelectEditControl(IDC_EDIT_DIFFBY); + } {rv = true; break;} + case IDC_MODULO: + { + rs_o = TEXT('%'); + EnableWindow(GetDlgItem(hDlg,IDC_EDIT_DIFFBY),false); + EnableWindow(GetDlgItem(hDlg,IDC_EDIT_MODBY),true); + if(!SelectingByKeyboard()) + SelectEditControl(IDC_EDIT_MODBY); + } {rv = true; break;} + case IDC_PREVIOUSVALUE: + rs_c=TEXT('r'); + EnableWindow(GetDlgItem(hDlg,IDC_EDIT_COMPAREVALUE),false); + EnableWindow(GetDlgItem(hDlg,IDC_EDIT_COMPAREADDRESS),false); + EnableWindow(GetDlgItem(hDlg,IDC_EDIT_COMPARECHANGES),false); + {rv = true; break;} + case IDC_SPECIFICVALUE: + { + rs_c = TEXT('s'); + EnableWindow(GetDlgItem(hDlg,IDC_EDIT_COMPAREVALUE),true); + EnableWindow(GetDlgItem(hDlg,IDC_EDIT_COMPAREADDRESS),false); + EnableWindow(GetDlgItem(hDlg,IDC_EDIT_COMPARECHANGES),false); + if(!SelectingByKeyboard()) + SelectEditControl(IDC_EDIT_COMPAREVALUE); + {rv = true; break;} + } + case IDC_SPECIFICADDRESS: + { + rs_c = TEXT('a'); + EnableWindow(GetDlgItem(hDlg,IDC_EDIT_COMPAREADDRESS),true); + EnableWindow(GetDlgItem(hDlg,IDC_EDIT_COMPAREVALUE),false); + EnableWindow(GetDlgItem(hDlg,IDC_EDIT_COMPARECHANGES),false); + if(!SelectingByKeyboard()) + SelectEditControl(IDC_EDIT_COMPAREADDRESS); + } {rv = true; break;} + case IDC_NUMBEROFCHANGES: + { + rs_c = TEXT('n'); + EnableWindow(GetDlgItem(hDlg,IDC_EDIT_COMPARECHANGES),true); + EnableWindow(GetDlgItem(hDlg,IDC_EDIT_COMPAREVALUE),false); + EnableWindow(GetDlgItem(hDlg,IDC_EDIT_COMPAREADDRESS),false); + if(!SelectingByKeyboard()) + SelectEditControl(IDC_EDIT_COMPARECHANGES); + } {rv = true; break;} + case IDC_C_ADDCHEAT: + { + HWND ramListControl = GetDlgItem(hDlg,IDC_RAMLIST); + int watchItemIndex = ListView_GetNextItem(ramListControl, -1, LVNI_SELECTED); + while (watchItemIndex >= 0) + { + unsigned long address = CALL_WITH_T_SIZE_TYPES_1(GetHardwareAddressFromItemIndex, rs_type_size,rs_t==TEXT('s'),noMisalign, watchItemIndex); + + int sizeType = -1; + if(rs_type_size == TEXT('b')) + sizeType = 0; + else if(rs_type_size == TEXT('w')) + sizeType = 1; + else if(rs_type_size == TEXT('d')) + sizeType = 2; + + int numberType = -1; + if(rs_t == TEXT('s')) + numberType = 0; + else if(rs_t == TEXT('u')) + numberType = 1; + else if(rs_t == TEXT('h')) + numberType = 2; + + if (sizeType == -1 || numberType == -1) + break; + + struct ICheat cht; + const int fmtTable[] = { 2, 1, 3 }; + ZeroMemory(&cht, sizeof(struct SCheat)); + cht.address = RWInternalToHardwareAddress(address); + cht.size = 1 << sizeType; + cht.format = fmtTable[numberType]; + cht.new_val = CALL_WITH_T_SIZE_TYPES_1(GetCurValueFromItemIndex, rs_type_size,rs_t==TEXT('s'),noMisalign, watchItemIndex); + cht.saved_val = CALL_WITH_T_SIZE_TYPES_1(GetPrevValueFromItemIndex, rs_type_size,rs_t==TEXT('s'),noMisalign, watchItemIndex); + extern INT_PTR CALLBACK DlgCheatSearchAdd(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam); + DialogBoxParam(hInst, MAKEINTRESOURCE(IDD_CHEAT_FROM_SEARCH), hDlg, DlgCheatSearchAdd, (LPARAM)&cht); + watchItemIndex = ListView_GetNextItem(ramListControl, watchItemIndex, LVNI_SELECTED); + } + } {rv = true; break;} + case IDC_C_RESET: + { + RamSearchSaveUndoStateIfNotTooBig(RamSearchHWnd); + int prevNumItems = last_rs_possible; + + soft_reset_address_info(); + + if(prevNumItems == last_rs_possible) + SetRamSearchUndoType(RamSearchHWnd, 0); // nothing to undo + + ListView_SetItemState(GetDlgItem(hDlg,IDC_RAMLIST), -1, 0, LVIS_SELECTED); // deselect all + //ListView_SetItemCount(GetDlgItem(hDlg,IDC_RAMLIST),ResultCount); + ListView_SetSelectionMark(GetDlgItem(hDlg,IDC_RAMLIST), 0); + RefreshRamListSelectedCountControlStatus(hDlg); + {rv = true; break;} + } + case IDC_C_RESET_CHANGES: + memset(s_numChanges, 0, (sizeof(*s_numChanges)*(MAX_RAM_SIZE))); + ListView_Update(GetDlgItem(hDlg,IDC_RAMLIST), -1); + //SetRamSearchUndoType(hDlg, 0); + {rv = true; break;} + case IDC_C_UNDO: + if(s_undoType>0) + { +// Clear_Sound_Buffer(); + EnterCriticalSection(&s_activeMemoryRegionsCS); + if(s_activeMemoryRegions.size() < tooManyRegionsForUndo) + { + MemoryList tempMemoryList = s_activeMemoryRegions; + s_activeMemoryRegions = s_activeMemoryRegionsBackup; + s_activeMemoryRegionsBackup = tempMemoryList; + LeaveCriticalSection(&s_activeMemoryRegionsCS); + SetRamSearchUndoType(hDlg, 3 - s_undoType); + } + else + { + s_activeMemoryRegions = s_activeMemoryRegionsBackup; + LeaveCriticalSection(&s_activeMemoryRegionsCS); + SetRamSearchUndoType(hDlg, -1); + } + CompactAddrs(); + ListView_SetItemState(GetDlgItem(hDlg,IDC_RAMLIST), -1, 0, LVIS_SELECTED); // deselect all + ListView_SetSelectionMark(GetDlgItem(hDlg,IDC_RAMLIST), 0); + RefreshRamListSelectedCountControlStatus(hDlg); + } + {rv = true; break;} + case IDC_C_AUTOSEARCH: + AutoSearch = SendDlgItemMessage(hDlg, IDC_C_AUTOSEARCH, BM_GETCHECK, 0, 0) != 0; + AutoSearchAutoRetry = false; + if (!AutoSearch) {rv = true; break;} + case IDC_C_SEARCH: + { +// Clear_Sound_Buffer(); + + if(!rs_val_valid && !(rs_val_valid = Set_RS_Val())) + goto invalid_field; + + if(ResultCount) + { + RamSearchSaveUndoStateIfNotTooBig(hDlg); + + prune(rs_c,rs_o,rs_t==TEXT('s'),rs_val,rs_param); + + RefreshRamListSelectedCountControlStatus(hDlg); + } + + if(!ResultCount) + { + + MessageBox(RamSearchHWnd,TEXT("Resetting search."),TEXT("Out of results."),MB_OK|MB_ICONINFORMATION); + soft_reset_address_info(); + } + + {rv = true; break;} + +invalid_field: + MessageBox(RamSearchHWnd,TEXT("Invalid or out-of-bound entered value."),TEXT("Error"),MB_OK|MB_ICONSTOP); + if(AutoSearch) // stop autosearch if it just started + { + SendDlgItemMessage(hDlg, IDC_C_AUTOSEARCH, BM_SETCHECK, BST_UNCHECKED, 0); + SendMessage(hDlg, WM_COMMAND, IDC_C_AUTOSEARCH, 0); + } + {rv = true; break;} + } + case IDC_C_WATCH: + { + HWND ramListControl = GetDlgItem(hDlg,IDC_RAMLIST); + int selCount = ListView_GetSelectedCount(ramListControl); + + bool inserted = false; + int watchItemIndex = ListView_GetNextItem(ramListControl, -1, LVNI_SELECTED); + while (watchItemIndex >= 0) + { + AddressWatcher tempWatch; + tempWatch.Address = CALL_WITH_T_SIZE_TYPES_1(GetHardwareAddressFromItemIndex, rs_type_size,rs_t==TEXT('s'),noMisalign, watchItemIndex); + tempWatch.Size = rs_type_size; + tempWatch.Type = rs_t; + tempWatch.WrongEndian = 0; //Replace when I get little endian working + tempWatch.comment = NULL; + + if (selCount == 1) + inserted |= InsertWatch(tempWatch, hDlg); + else + inserted |= InsertWatch(tempWatch, TEXT("")); + + watchItemIndex = ListView_GetNextItem(ramListControl, watchItemIndex, LVNI_SELECTED); + } + // bring up the ram watch window if it's not already showing so the user knows where the watch went + if(inserted && !RamWatchHWnd) + SendMessage(hWnd, WM_COMMAND, ID_RAM_WATCH, 0); + SetForegroundWindow(RamSearchHWnd); + {rv = true; break;} + } + + // eliminate all selected items + case IDC_C_ELIMINATE: + { + RamSearchSaveUndoStateIfNotTooBig(hDlg); + + HWND ramListControl = GetDlgItem(hDlg,IDC_RAMLIST); + int size = (rs_type_size==TEXT('b') || !noMisalign) ? 1 : 2; + int selCount = ListView_GetSelectedCount(ramListControl); + int watchIndex = -1; + + // time-saving trick #1: + // condense the selected items into an array of address ranges + std::vector selHardwareAddrs; + for(int i = 0, j = 1024; i < selCount; ++i, --j) + { + watchIndex = ListView_GetNextItem(ramListControl, watchIndex, LVNI_SELECTED); + int addr = CALL_WITH_T_SIZE_TYPES_1(GetHardwareAddressFromItemIndex, rs_type_size,rs_t==TEXT('s'),noMisalign, watchIndex); + if(!selHardwareAddrs.empty() && addr == selHardwareAddrs.back().End()) + selHardwareAddrs.back().size += size; + else + selHardwareAddrs.push_back(AddrRange(addr,size)); + + if(!j) UpdateRamSearchProgressBar(i * 50 / selCount), j = 1024; + } + + // now deactivate the ranges + + // time-saving trick #2: + // take advantage of the fact that the listbox items must be in the same order as the regions + MemoryList::iterator iter = s_activeMemoryRegions.begin(); + int numHardwareAddrRanges = selHardwareAddrs.size(); + for(int i = 0, j = 16; i < numHardwareAddrRanges; ++i, --j) + { + int addr = selHardwareAddrs[i].addr; + int size = selHardwareAddrs[i].size; + bool affected = false; + while(iter != s_activeMemoryRegions.end()) + { + MemoryRegion& region = *iter; + int affNow = DeactivateRegion(region, iter, addr, size); + if(affNow) + affected = true; + else if(affected) + break; + if(affNow != 2) + ++iter; + } + + if(!j) UpdateRamSearchProgressBar(50 + (i * 50 / selCount)), j = 16; + } + UpdateRamSearchTitleBar(); + + // careful -- if the above two time-saving tricks aren't working, + // the runtime can absolutely explode (seconds -> hours) when there are lots of regions + + ListView_SetItemState(ramListControl, -1, 0, LVIS_SELECTED); // deselect all + signal_new_size(); + {rv = true; break;} + } + //case IDOK: + case IDCANCEL: + RamSearchHWnd = NULL; +/* if (theApp.pauseDuringCheatSearch) + EndDialog(hDlg, true); // this should never be called on a modeless dialog + else +*/ + DestroyWindow(hDlg); + {rv = true; break;} + } + + // check refresh for comparison preview color update + // also, update rs_val if needed + bool needRefresh = false; + switch(LOWORD(wParam)) + { + case IDC_LESSTHAN: + case IDC_MORETHAN: + case IDC_NOMORETHAN: + case IDC_NOLESSTHAN: + case IDC_EQUALTO: + case IDC_DIFFERENTFROM: + case IDC_DIFFERENTBY: + case IDC_MODULO: + case IDC_PREVIOUSVALUE: + case IDC_SPECIFICVALUE: + case IDC_SPECIFICADDRESS: + case IDC_NUMBEROFCHANGES: + case IDC_SIGNED: + case IDC_UNSIGNED: + case IDC_HEX: + rs_val_valid = Set_RS_Val(); + needRefresh = true; + break; + case IDC_EDIT_COMPAREVALUE: + case IDC_EDIT_COMPAREADDRESS: + case IDC_EDIT_COMPARECHANGES: + case IDC_EDIT_DIFFBY: + case IDC_EDIT_MODBY: + if(HIWORD(wParam) == EN_CHANGE) + { + rs_val_valid = Set_RS_Val(); + needRefresh = true; + } + break; + } + if(needRefresh) + ListView_Update(GetDlgItem(hDlg,IDC_RAMLIST), -1); + + + return rv; + } break; + +// case WM_CLOSE: + case WM_DESTROY: + RamSearchHWnd = NULL; +// theApp.modelessCheatDialogIsOpen = false; +// return true; + break; + } + + return false; +} + +void UpdateRamSearchTitleBar(int percent) +{ +#define HEADER_STR TEXT(" RAM Search - ") +#define PROGRESS_STR TEXT(" %d%% ... ") +#define STATUS_STR TEXT("%d Possibilit%s (%d Region%s)") + + int poss = last_rs_possible; + int regions = last_rs_regions; + if(poss <= 0) + lstrcpy(Str_Tmp,TEXT(" RAM Search")); + else if(percent <= 0) + _stprintf(Str_Tmp, HEADER_STR STATUS_STR, poss, poss==1?TEXT("y"):TEXT("ies"), regions, regions==1?TEXT(""):TEXT("s")); + else + _stprintf(Str_Tmp, PROGRESS_STR STATUS_STR, percent, poss, poss==1?TEXT("y"):TEXT("ies"), regions, regions==1?TEXT(""):TEXT("s")); + SetWindowText(RamSearchHWnd, Str_Tmp); +} + +void UpdatePossibilities(int rs_possible, int regions) +{ + if(rs_possible != last_rs_possible) + { + last_rs_possible = rs_possible; + last_rs_regions = regions; + UpdateRamSearchTitleBar(); + } +} + +void SetRamSearchUndoType(HWND hDlg, int type) +{ + if(s_undoType != type) + { + if((s_undoType!=2 && s_undoType!=-1)!=(type!=2 && type!=-1)) + SendDlgItemMessage(hDlg,IDC_C_UNDO,WM_SETTEXT,0,(LPARAM)((type == 2 || type == -1) ? TEXT("Redo") : TEXT("Undo"))); + if((s_undoType>0)!=(type>0)) + EnableWindow(GetDlgItem(hDlg,IDC_C_UNDO),type>0); + s_undoType = type; + } +} + +void RamSearchSaveUndoStateIfNotTooBig(HWND hDlg) +{ + EnterCriticalSection(&s_activeMemoryRegionsCS); + if(s_activeMemoryRegions.size() < tooManyRegionsForUndo) + { + s_activeMemoryRegionsBackup = s_activeMemoryRegions; + LeaveCriticalSection(&s_activeMemoryRegionsCS); + SetRamSearchUndoType(hDlg, 1); + } + else + { + LeaveCriticalSection(&s_activeMemoryRegionsCS); + SetRamSearchUndoType(hDlg, 0); + } +} + +struct InitRamSearch +{ + InitRamSearch() + { + InitializeCriticalSection(&s_activeMemoryRegionsCS); + } + ~InitRamSearch() + { + DeleteCriticalSection(&s_activeMemoryRegionsCS); + } +} initRamSearch; + + +void init_list_box(HWND Box, const TCHAR* Strs[], int numColumns, int *columnWidths) //initializes the ram search and/or ram watch listbox +{ + LVCOLUMN Col; + Col.mask = LVCF_FMT | LVCF_ORDER | LVCF_SUBITEM | LVCF_TEXT | LVCF_WIDTH; + Col.fmt = LVCFMT_CENTER; + for (int i = 0; i < numColumns; i++) + { + Col.iOrder = i; + Col.iSubItem = i; + Col.pszText = (LPTSTR)(Strs[i]); + Col.cx = columnWidths[i]; + ListView_InsertColumn(Box,i,&Col); + } + + ListView_SetExtendedListViewStyle(Box, LVS_EX_FULLROWSELECT); +} diff --git a/win32/ram_search.h b/win32/ram_search.h new file mode 100644 index 00000000..4bf9693d --- /dev/null +++ b/win32/ram_search.h @@ -0,0 +1,120 @@ +//RamSearch dialog was copied and adapted from GENS11: http://code.google.com/p/gens-rerecording/ +//Authors: Upthorn, Nitsuja, adelikat + +#ifndef RAM_SEARCH_H +#define RAM_SEARCH_H + + +extern char rs_type_size; +extern int ResultCount; +typedef unsigned int HWAddressType; + +unsigned int sizeConv(unsigned int index,char size, char *prevSize = &rs_type_size, bool usePrev = false); +unsigned int GetRamValue(unsigned int Addr,char Size); +void prune(char Search, char Operater, char Type, int Value, int OperatorParameter); +void CompactAddrs(); +void reset_address_info(); +void signal_new_frame(); +void signal_new_size(); +void UpdateRamSearchTitleBar(int percent = 0); +void SetRamSearchUndoType(HWND hDlg, int type); +unsigned int ReadValueAtHardwareAddress(HWAddressType address, unsigned int size); +bool WriteValueAtHardwareAddress(HWAddressType address, unsigned int value, unsigned int size); +bool IsRAMWatchAddressValid(HWAddressType address); +extern int curr_ram_size; +extern bool noMisalign; +extern HWND RamSearchHWnd; + + +void ResetResults(); +void CloseRamWindows(); //Close the Ram Search & Watch windows when rom closes +void ReopenRamWindows(); //Reopen them when a new Rom is loaded +void Update_RAM_Search(); //keeps RAM values up to date in the search and watch windows + + +#define RW_VIRTUAL_ADDR_SHIFT 24 +#define RW_VIRTUAL_ADDR_MASK ((1<<(RW_VIRTUAL_ADDR_SHIFT))-1) +#define RW_VIRTUAL_ADDR_SRAM 1 +#define RW_VIRTUAL_ADDR_FILLRAM 2 + + +static inline uint8* RWInternalToSoftwareAddress(HWAddressType address) +{ + if(Settings.StopEmulation) + return NULL; + + HWAddressType base = address & RW_VIRTUAL_ADDR_MASK; + HWAddressType type = address >> RW_VIRTUAL_ADDR_SHIFT; + + switch(type) { + case RW_VIRTUAL_ADDR_SRAM: + if (Memory.SRAM != NULL && base < 0x20000) + return &Memory.SRAM[base]; + break; + case RW_VIRTUAL_ADDR_FILLRAM: + if (Memory.FillRAM != NULL && base < 0x8000) + return &Memory.FillRAM[base]; + break; + default: + if (Memory.RAM != NULL && address >= 0x7e0000 && address <= 0x7fffff) + return &Memory.RAM[address & 0x1ffff]; + } + return NULL; +} + +static inline HWAddressType DisplayToRWInternalAddress(const TCHAR* str) +{ + HWAddressType base, type = 0; + switch(str[0]) { + case 'S': + case 's': + base = _tcstoul(&str[1], NULL, 16); + type = RW_VIRTUAL_ADDR_SRAM; + break; + case 'I': + case 'i': + base = _tcstoul(&str[1], NULL, 16); + type = RW_VIRTUAL_ADDR_FILLRAM; + break; + default: + base = _tcstoul(str, NULL, 16); + } + return (type << RW_VIRTUAL_ADDR_SHIFT) | (base & RW_VIRTUAL_ADDR_MASK); +} + +static inline const TCHAR* RWInternalToDisplayAddress(HWAddressType address) +{ + static TCHAR str[11]; + HWAddressType base = address & RW_VIRTUAL_ADDR_MASK; + HWAddressType type = address >> RW_VIRTUAL_ADDR_SHIFT; + + // must return 6 characters + switch(type) { + case RW_VIRTUAL_ADDR_SRAM: + _stprintf(str, TEXT("s%05X"), base); + break; + case RW_VIRTUAL_ADDR_FILLRAM: + _stprintf(str, TEXT("i%05X"), base); + break; + default: + _stprintf(str, TEXT("%06X"), base); + } + return str; +} + +static inline HWAddressType RWInternalToHardwareAddress(HWAddressType address) +{ + HWAddressType base = address & RW_VIRTUAL_ADDR_MASK; + HWAddressType type = address >> RW_VIRTUAL_ADDR_SHIFT; + + switch(type) { + case RW_VIRTUAL_ADDR_SRAM: + return 0x700000 | (base & 0x1ffff); // FIXME: incorrect, but better than nothing + case RW_VIRTUAL_ADDR_FILLRAM: // FIXME + default: + return base; + } +} + + +#endif diff --git a/win32/ramwatch.cpp b/win32/ramwatch.cpp new file mode 100644 index 00000000..ae14a11e --- /dev/null +++ b/win32/ramwatch.cpp @@ -0,0 +1,1282 @@ +//RamWatch dialog was copied and adapted from GENS11: http://code.google.com/p/gens-rerecording/ +//Authors: Upthorn, Nitsuja, adelikat + +#include "../port.h" +#include "../snes9x.h" +#include "../display.h" +#include "../memmap.h" +#include "../cheats.h" +#include "wsnes9x.h" +#include "rsrc/resource.h" +#include "ramwatch.h" +#include "ram_search.h" +#include +#include +#include +#include + +#ifdef UNICODE +#define _tToChar WideToUtf8 +#define _tFromChar Utf8ToWide +#else +#define _tToChar +#define _tFromChar +#endif + +extern SCheatData Cheat; + +/* +#include +#pragma comment(lib, "comctl32.lib") +#include +#pragma comment(lib, "shell32.lib") +#include +#pragma comment(lib, "comdlg32.lib") +*/ + +static HMENU ramwatchmenu; +static HMENU rwrecentmenu; +/*static*/ HACCEL RamWatchAccels = NULL; +TCHAR rw_recent_files[MAX_RECENT_WATCHES][1024]; +//TCHAR Watch_Dir[1024]=TEXT(""); +bool RWfileChanged = false; //Keeps track of whether the current watch file has been changed, if so, ramwatch will prompt to save changes +bool AutoRWLoad = false; //Keeps track of whether Auto-load is checked +bool RWSaveWindowPos = false; //Keeps track of whether Save Window position is checked +TCHAR currentWatch[1024]; +int ramw_x, ramw_y; //Used to store ramwatch dialog window positions +AddressWatcher rswatches[MAX_WATCH_COUNT]; +int WatchCount=0; + +TCHAR applicationPath[2048]; +struct InitRamWatch +{ + InitRamWatch() + { + GetModuleFileName(NULL, applicationPath, 2048); + } +} initRamWatch; + +HWND RamWatchHWnd; +#define gamefilename _tFromChar(S9xBasename(S9xGetFilename("", DEFAULT_DIR))) +#define hWnd GUI.hWnd +#define hInst GUI.hInstance +static TCHAR Str_Tmp [1024]; + +void init_list_box(HWND Box, const TCHAR* Strs[], int numColumns, int *columnWidths); //initializes the ram search and/or ram watch listbox + +#define MESSAGEBOXPARENT (RamWatchHWnd ? RamWatchHWnd : hWnd) + +bool QuickSaveWatches(); +bool ResetWatches(); + +void RefreshWatchListSelectedCountControlStatus(HWND hDlg); + +unsigned int GetCurrentValue(AddressWatcher& watch) +{ + return ReadValueAtHardwareAddress(watch.Address, watch.Size == TEXT('d') ? 4 : watch.Size == TEXT('w') ? 2 : 1); +} + +bool IsSameWatch(const AddressWatcher& l, const AddressWatcher& r) +{ + return ((l.Address == r.Address) && (l.Size == r.Size) && (l.Type == r.Type)/* && (l.WrongEndian == r.WrongEndian)*/); +} + +bool VerifyWatchNotAlreadyAdded(const AddressWatcher& watch) +{ + for (int j = 0; j < WatchCount; j++) + { + if (IsSameWatch(rswatches[j], watch)) + { + if(RamWatchHWnd) + SetForegroundWindow(RamWatchHWnd); + return false; + } + } + return true; +} + + +bool InsertWatch(const AddressWatcher& Watch, TCHAR *Comment) +{ + if(!VerifyWatchNotAlreadyAdded(Watch)) + return false; + + if(WatchCount >= MAX_WATCH_COUNT) + return false; + + int i = WatchCount++; + AddressWatcher& NewWatch = rswatches[i]; + NewWatch = Watch; + //if (NewWatch.comment) free(NewWatch.comment); + NewWatch.comment = (TCHAR *) malloc((lstrlen(Comment)+2) * sizeof(TCHAR)); + NewWatch.CurValue = GetCurrentValue(NewWatch); + lstrcpy(NewWatch.comment, Comment); + ListView_SetItemCount(GetDlgItem(RamWatchHWnd,IDC_WATCHLIST),WatchCount); + RWfileChanged=true; + + return true; +} + +LRESULT CALLBACK PromptWatchNameProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) //Gets the description of a watched address +{ + RECT r; + RECT r2; + int dx1, dy1, dx2, dy2; + + switch(uMsg) + { + case WM_INITDIALOG: + //Clear_Sound_Buffer(); + + GetWindowRect(hWnd, &r); + dx1 = (r.right - r.left) / 2; + dy1 = (r.bottom - r.top) / 2; + + GetWindowRect(hDlg, &r2); + dx2 = (r2.right - r2.left) / 2; + dy2 = (r2.bottom - r2.top) / 2; + + //SetWindowPos(hDlg, NULL, max(0, r.left + (dx1 - dx2)), max(0, r.top + (dy1 - dy2)), NULL, NULL, SWP_NOSIZE | SWP_NOZORDER | SWP_SHOWWINDOW); + SetWindowPos(hDlg, NULL, r.left, r.top, NULL, NULL, SWP_NOSIZE | SWP_NOZORDER | SWP_SHOWWINDOW); + lstrcpy(Str_Tmp,TEXT("Enter a name for this RAM address.")); + SendDlgItemMessage(hDlg,IDC_PROMPT_TEXT,WM_SETTEXT,0,(LPARAM)Str_Tmp); + lstrcpy(Str_Tmp,TEXT("")); + SendDlgItemMessage(hDlg,IDC_PROMPT_TEXT2,WM_SETTEXT,0,(LPARAM)Str_Tmp); + return true; + break; + + case WM_COMMAND: + switch(LOWORD(wParam)) + { + case IDOK: + { + GetDlgItemText(hDlg,IDC_PROMPT_EDIT,Str_Tmp,80); + InsertWatch(rswatches[WatchCount],Str_Tmp); + EndDialog(hDlg, true); + return true; + break; + } + case IDCANCEL: + EndDialog(hDlg, false); + return false; + break; + } + break; + + case WM_CLOSE: + EndDialog(hDlg, false); + return false; + break; + } + + return false; +} + +bool InsertWatch(const AddressWatcher& Watch, HWND parent) +{ + if(!VerifyWatchNotAlreadyAdded(Watch)) + return false; + + if(!parent) + parent = RamWatchHWnd; + if(!parent) + parent = hWnd; + + int prevWatchCount = WatchCount; + + rswatches[WatchCount] = Watch; + rswatches[WatchCount].CurValue = GetCurrentValue(rswatches[WatchCount]); + DialogBox(hInst, MAKEINTRESOURCE(IDD_PROMPT), parent, (DLGPROC) PromptWatchNameProc); + + return WatchCount > prevWatchCount; +} + +void Update_RAM_Watch() +{ + BOOL watchChanged[MAX_WATCH_COUNT] = {0}; + + if(WatchCount) + { + // update cached values and detect changes to displayed listview items + + for(int i = 0; i < WatchCount; i++) + { + unsigned int prevCurValue = rswatches[i].CurValue; + unsigned int newCurValue = GetCurrentValue(rswatches[i]); + if(prevCurValue != newCurValue) + { + rswatches[i].CurValue = newCurValue; + watchChanged[i] = TRUE; + } + } + } + + // refresh any visible parts of the listview box that changed + HWND lv = GetDlgItem(RamWatchHWnd,IDC_WATCHLIST); + int top = ListView_GetTopIndex(lv); + int bottom = top + ListView_GetCountPerPage(lv) + 1; // +1 is so we will update a partially-displayed last item + if(top < 0) top = 0; + if(bottom > WatchCount) bottom = WatchCount; + int start = -1; + for(int i = top; i <= bottom; i++) + { + if(start == -1) + { + if(i != bottom && watchChanged[i]) + { + start = i; + //somethingChanged = true; + } + } + else + { + if(i == bottom || !watchChanged[i]) + { + ListView_RedrawItems(lv, start, i-1); + start = -1; + } + } + } +} + +bool AskSave() +{ + //This function asks to save changes if the watch file contents have changed + //returns false only if a save was attempted but failed or was cancelled + if (RWfileChanged) + { + int answer = MessageBox(MESSAGEBOXPARENT, TEXT("Save Changes?"), TEXT("Ram Watch"), MB_YESNOCANCEL); + if(answer == IDYES) + if(!QuickSaveWatches()) + return false; + return (answer != IDCANCEL); + } + return true; +} + +void WriteRecentRWFiles() +{ + // TODO + /* + TCHAR str[2048]; + for (int i = 0; i < MAX_RECENT_WATCHES; i++) + { + _stprintf(str, TEXT("recentWatch%d"), i+1); + regSetStringValue(str, &rw_recent_files[i][0]); + } + */ +} + +void UpdateRW_RMenu(HMENU menu, unsigned int mitem, unsigned int baseid) +{ + MENUITEMINFO moo; + int x; + + moo.cbSize = sizeof(moo); + moo.fMask = MIIM_SUBMENU | MIIM_STATE; + + GetMenuItemInfo(GetSubMenu(ramwatchmenu, 0), mitem, FALSE, &moo); + moo.hSubMenu = menu; + moo.fState = lstrlen(rw_recent_files[0]) ? MFS_ENABLED : MFS_GRAYED; + + SetMenuItemInfo(GetSubMenu(ramwatchmenu, 0), mitem, FALSE, &moo); + + // Remove all recent files submenus + for(x = 0; x < MAX_RECENT_WATCHES; x++) + { + RemoveMenu(menu, baseid + x, MF_BYCOMMAND); + } + + // Recreate the menus + for(x = MAX_RECENT_WATCHES - 1; x >= 0; x--) + { + TCHAR tmp[128 + 5]; + + // Skip empty strings + if(!lstrlen(rw_recent_files[x])) + { + continue; + } + + moo.cbSize = sizeof(moo); + moo.fMask = MIIM_DATA | MIIM_ID | MIIM_TYPE; + + // Fill in the menu text. + if(lstrlen(rw_recent_files[x]) < 128) + { + _stprintf(tmp, TEXT("&%d. %s"), ( x + 1 ) % 10, rw_recent_files[x]); + } + else + { + _stprintf(tmp, TEXT("&%d. %s"), ( x + 1 ) % 10, rw_recent_files[x] + lstrlen( rw_recent_files[x] ) - 127); + } + + // Insert the menu item + moo.cch = lstrlen(tmp); + moo.fType = 0; + moo.wID = baseid + x; + moo.dwTypeData = tmp; + InsertMenuItem(menu, 0, 1, &moo); + } + + // I don't think one function shall do so many things in a row +// WriteRecentRWFiles(); // write recent menu to ini +} + +void UpdateRWRecentArray(const TCHAR* addString, unsigned int arrayLen, HMENU menu, unsigned int menuItem, unsigned int baseId) +{ + const size_t len = 1024; // Avoid magic numbers + + // Try to find out if the filename is already in the recent files list. + for(unsigned int x = 0; x < arrayLen; x++) + { + if(lstrlen(rw_recent_files[x])) + { + if(_tcsnccmp(rw_recent_files[x], addString, 1024)) // Item is already in list. + { + // If the filename is in the file list don't add it again. + // Move it up in the list instead. + + int y; + TCHAR tmp[len]; + + // Save pointer. + lstrcpyn(tmp, rw_recent_files[x], len); + + for(y = x; y; y--) + { + // Move items down. + lstrcpyn(rw_recent_files[y],rw_recent_files[y - 1], len); + } + + // Put item on top. + lstrcpyn(rw_recent_files[0],tmp, len); + + // Update the recent files menu + UpdateRW_RMenu(menu, menuItem, baseId); + + return; + } + } + } + + // The filename wasn't found in the list. That means we need to add it. + + // Move the other items down. + for(unsigned int x = arrayLen - 1; x; x--) + { + lstrcpyn(rw_recent_files[x],rw_recent_files[x - 1], len); + } + + // Add the new item. + lstrcpyn(rw_recent_files[0], addString, len); + + // Update the recent files menu + UpdateRW_RMenu(menu, menuItem, baseId); +} + +void RWAddRecentFile(const TCHAR *filename) +{ + UpdateRWRecentArray(filename, MAX_RECENT_WATCHES, rwrecentmenu, RAMMENU_FILE_RECENT, RW_MENU_FIRST_RECENT_FILE); +} + +void OpenRWRecentFile(int memwRFileNumber) +{ + if(!ResetWatches()) + return; + + int rnum = memwRFileNumber; + if ((unsigned int)rnum >= MAX_RECENT_WATCHES) + return; //just in case + + TCHAR* x; + + while(true) + { + x = rw_recent_files[rnum]; + if (!*x) + return; //If no recent files exist just return. Useful for Load last file on startup (or if something goes screwy) + + if (rnum) //Change order of recent files if not most recent + { + RWAddRecentFile(x); + rnum = 0; + } + else + { + break; + } + } + + lstrcpy(currentWatch,x); + lstrcpy(Str_Tmp,currentWatch); + + //loadwatches here + FILE *WatchFile = _tfopen(Str_Tmp,TEXT("rb")); + if (!WatchFile) + { + int answer = MessageBox(MESSAGEBOXPARENT,TEXT("Error opening file."),TEXT("ERROR"),MB_OKCANCEL); + if (answer == IDOK) + { + rw_recent_files[rnum][0] = TEXT('\0'); //Clear file from list + if (rnum) //Update the ramwatch list + RWAddRecentFile(rw_recent_files[0]); + else + RWAddRecentFile(rw_recent_files[1]); + } + return; + } + const TCHAR DELIM = TEXT('\t'); + AddressWatcher Temp; + TCHAR mode; + _fgetts(Str_Tmp,1024,WatchFile); + _stscanf(Str_Tmp,TEXT("%c%*s"),&mode); + int WatchAdd; + _fgetts(Str_Tmp,1024,WatchFile); + _stscanf(Str_Tmp,TEXT("%d%*s"),&WatchAdd); + WatchAdd+=WatchCount; + for (int i = WatchCount; i < WatchAdd; i++) + { + TCHAR TempAddressStr[11]; + while (i < 0) + i++; + do { + _fgetts(Str_Tmp,1024,WatchFile); + } while (Str_Tmp[0] == TEXT('\n')); + _stscanf(Str_Tmp,TEXT("%*05X%*c%6s%*c%c%*c%c%*c%d"),TempAddressStr,&(Temp.Size),&(Temp.Type),&(Temp.WrongEndian)); + Temp.Address = DisplayToRWInternalAddress(TempAddressStr); + Temp.WrongEndian = 0; + TCHAR *Comment = _tcsrchr(Str_Tmp,DELIM) + 1; + *_tcsrchr(Comment,TEXT('\n')) = TEXT('\0'); + InsertWatch(Temp,Comment); + } + + fclose(WatchFile); + if (RamWatchHWnd) { + ListView_SetItemCount(GetDlgItem(RamWatchHWnd,IDC_WATCHLIST),WatchCount); + RefreshWatchListSelectedCountControlStatus(RamWatchHWnd); + } + RWfileChanged=false; + return; +} + +int Change_File_L(TCHAR *Dest, TCHAR *Dir, TCHAR *Titre, TCHAR *Filter, TCHAR *Ext, HWND hwnd) +{ + OPENFILENAME ofn; + + SetCurrentDirectory(applicationPath); + + if (!lstrcmp(Dest, TEXT(""))) + { + lstrcpy(Dest, TEXT("default.")); + lstrcat(Dest, Ext); + } + + memset(&ofn, 0, sizeof(OPENFILENAME)); + + ofn.lStructSize = sizeof(OPENFILENAME); + ofn.hwndOwner = hwnd; + ofn.hInstance = hInst; + ofn.lpstrFile = Dest; + ofn.nMaxFile = 2047; + ofn.lpstrFilter = Filter; + ofn.nFilterIndex = 1; + ofn.lpstrInitialDir = Dir; + ofn.lpstrTitle = Titre; + ofn.lpstrDefExt = Ext; + ofn.Flags = OFN_FILEMUSTEXIST | OFN_HIDEREADONLY; + + if (GetOpenFileName(&ofn)) return 1; + + return 0; +} + +int Change_File_S(TCHAR *Dest, TCHAR *Dir, TCHAR *Titre, TCHAR *Filter, TCHAR *Ext, HWND hwnd) +{ + OPENFILENAME ofn; + + SetCurrentDirectory(applicationPath); + + if (!lstrcmp(Dest, TEXT(""))) + { + lstrcpy(Dest, TEXT("default.")); + lstrcat(Dest, Ext); + } + + memset(&ofn, 0, sizeof(OPENFILENAME)); + + ofn.lStructSize = sizeof(OPENFILENAME); + ofn.hwndOwner = hwnd; + ofn.hInstance = hInst; + ofn.lpstrFile = Dest; + ofn.nMaxFile = 2047; + ofn.lpstrFilter = Filter; + ofn.nFilterIndex = 1; + ofn.lpstrInitialDir = Dir; + ofn.lpstrTitle = Titre; + ofn.lpstrDefExt = Ext; + ofn.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY; + + if (GetSaveFileName(&ofn)) return 1; + + return 0; +} + +bool Save_Watches() +{ + const TCHAR* slash = std::max(_tcsrchr(gamefilename, TEXT('|')), std::max(_tcsrchr(gamefilename, TEXT('\\')), _tcsrchr(gamefilename, TEXT('/')))); + lstrcpy(Str_Tmp,slash ? slash+1 : gamefilename); + TCHAR* dot = _tcsrchr(Str_Tmp, TEXT('.')); + if(dot) *dot = 0; + lstrcat(Str_Tmp,TEXT(".wch")); + if(Change_File_S(Str_Tmp, applicationPath, TEXT("Save Watches"), TEXT("Watchlist\0*.wch\0All Files\0*.*\0\0"), TEXT("wch"), RamWatchHWnd)) + { + FILE *WatchFile = _tfopen(Str_Tmp,TEXT("r+b")); + if (!WatchFile) WatchFile = _tfopen(Str_Tmp,TEXT("w+b")); + _fputtc(TEXT('\n'),WatchFile); + lstrcpy(currentWatch,Str_Tmp); + RWAddRecentFile(currentWatch); + _stprintf(Str_Tmp,TEXT("%d\n"),WatchCount); + _fputts(Str_Tmp,WatchFile); + const TCHAR DELIM = TEXT('\t'); + for (int i = 0; i < WatchCount; i++) + { + _stprintf(Str_Tmp,TEXT("%05X%c%-6s%c%c%c%c%c%d%c%s\n"),i,DELIM,RWInternalToDisplayAddress(rswatches[i].Address),DELIM,rswatches[i].Size,DELIM,rswatches[i].Type,DELIM,rswatches[i].WrongEndian,DELIM,rswatches[i].comment); + _fputts(Str_Tmp,WatchFile); + } + + fclose(WatchFile); + RWfileChanged=false; + //TODO: Add to recent list function call here + return true; + } + return false; +} + +bool QuickSaveWatches() +{ +if (RWfileChanged==false) return true; //If file has not changed, no need to save changes +if (currentWatch[0] == NULL) //If there is no currently loaded file, run to Save as and then return + { + return Save_Watches(); + } + + lstrcpy(Str_Tmp,currentWatch); + FILE *WatchFile = _tfopen(Str_Tmp,TEXT("r+b")); + if (!WatchFile) WatchFile = _tfopen(Str_Tmp,TEXT("w+b")); + _fputtc(TEXT('\n'),WatchFile); + _stprintf(Str_Tmp,TEXT("%d\n"),WatchCount); + _fputts(Str_Tmp,WatchFile); + const TCHAR DELIM = TEXT('\t'); + for (int i = 0; i < WatchCount; i++) + { + _stprintf(Str_Tmp,TEXT("%05X%c%-6s%c%c%c%c%c%d%c%s\n"),i,DELIM,RWInternalToDisplayAddress(rswatches[i].Address),DELIM,rswatches[i].Size,DELIM,rswatches[i].Type,DELIM,rswatches[i].WrongEndian,DELIM,rswatches[i].comment); + _fputts(Str_Tmp,WatchFile); + } + fclose(WatchFile); + RWfileChanged=false; + return true; +} + +bool Load_Watches(bool clear, const TCHAR* filename) +{ + const TCHAR DELIM = TEXT('\t'); + FILE* WatchFile = _tfopen(filename,TEXT("rb")); + if (!WatchFile) + { + MessageBox(MESSAGEBOXPARENT,TEXT("Error opening file."),TEXT("ERROR"),MB_OK); + return false; + } + if(clear) + { + if(!ResetWatches()) + { + fclose(WatchFile); + return false; + } + } + lstrcpy(currentWatch,filename); + RWAddRecentFile(currentWatch); + AddressWatcher Temp; + TCHAR mode; + _fgetts(Str_Tmp,1024,WatchFile); + _stscanf(Str_Tmp,TEXT("%c%*s"),&mode); + int WatchAdd; + _fgetts(Str_Tmp,1024,WatchFile); + _stscanf(Str_Tmp,TEXT("%d%*s"),&WatchAdd); + WatchAdd+=WatchCount; + for (int i = WatchCount; i < WatchAdd; i++) + { + TCHAR TempAddressStr[11]; + while (i < 0) + i++; + do { + _fgetts(Str_Tmp,1024,WatchFile); + } while (Str_Tmp[0] == TEXT('\n')); + _stscanf(Str_Tmp,TEXT("%*05X%*c%6s%*c%c%*c%c%*c%d"),TempAddressStr,&(Temp.Size),&(Temp.Type),&(Temp.WrongEndian)); + Temp.Address = DisplayToRWInternalAddress(TempAddressStr); + Temp.WrongEndian = 0; + TCHAR *Comment = _tcsrchr(Str_Tmp,DELIM) + 1; + *_tcsrchr(Comment,TEXT('\n')) = TEXT('\0'); + InsertWatch(Temp,Comment); + } + + fclose(WatchFile); + if (RamWatchHWnd) + ListView_SetItemCount(GetDlgItem(RamWatchHWnd,IDC_WATCHLIST),WatchCount); + RWfileChanged=false; + return true; +} + +bool Load_Watches(bool clear) +{ + const TCHAR* slash = std::max(_tcsrchr(gamefilename, TEXT('|')), std::max(_tcsrchr(gamefilename, TEXT('\\')), _tcsrchr(gamefilename, TEXT('/')))); + lstrcpy(Str_Tmp,slash ? slash+1 : gamefilename); + TCHAR* dot = _tcsrchr(Str_Tmp, TEXT('.')); + if(dot) *dot = 0; + lstrcat(Str_Tmp,TEXT(".wch")); + if(Change_File_L(Str_Tmp, applicationPath, TEXT("Load Watches"), TEXT("Watchlist\0*.wch\0All Files\0*.*\0\0"), TEXT("wch"), RamWatchHWnd)) + { + return Load_Watches(clear, Str_Tmp); + } + return false; +} + +bool ResetWatches() +{ + if(!AskSave()) + return false; + for (;WatchCount>=0;WatchCount--) + { + free(rswatches[WatchCount].comment); + rswatches[WatchCount].comment = NULL; + } + WatchCount++; + if (RamWatchHWnd) { + ListView_SetItemCount(GetDlgItem(RamWatchHWnd,IDC_WATCHLIST),WatchCount); + RefreshWatchListSelectedCountControlStatus(RamWatchHWnd); + } + RWfileChanged = false; + currentWatch[0] = NULL; + return true; +} + +void RemoveWatch(int watchIndex) +{ + free(rswatches[watchIndex].comment); + rswatches[watchIndex].comment = NULL; + for (int i = watchIndex; i <= WatchCount; i++) + rswatches[i] = rswatches[i+1]; + WatchCount--; +} + +void RefreshWatchListSelectedItemControlStatus(HWND hDlg) +{ + static int prevSelIndex=-1; + int selIndex = ListView_GetSelectionMark(GetDlgItem(hDlg,IDC_RAMLIST)); + if(selIndex != prevSelIndex) + { + if(selIndex == -1 || prevSelIndex == -1) + { + EnableWindow(GetDlgItem(hDlg, IDC_C_ADDCHEAT), (selIndex != -1) ? TRUE : FALSE); + } + prevSelIndex = selIndex; + } +} + +LRESULT CALLBACK EditWatchProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) //Gets info for a RAM Watch, and then inserts it into the Watch List +{ + RECT r; + RECT r2; + int dx1, dy1, dx2, dy2; + static int index; + static char s,t = s = 0; + + switch(uMsg) + { + case WM_INITDIALOG: + //Clear_Sound_Buffer(); + + + GetWindowRect(hWnd, &r); + dx1 = (r.right - r.left) / 2; + dy1 = (r.bottom - r.top) / 2; + + GetWindowRect(hDlg, &r2); + dx2 = (r2.right - r2.left) / 2; + dy2 = (r2.bottom - r2.top) / 2; + + //SetWindowPos(hDlg, NULL, max(0, r.left + (dx1 - dx2)), max(0, r.top + (dy1 - dy2)), NULL, NULL, SWP_NOSIZE | SWP_NOZORDER | SWP_SHOWWINDOW); + SetWindowPos(hDlg, NULL, r.left, r.top, NULL, NULL, SWP_NOSIZE | SWP_NOZORDER | SWP_SHOWWINDOW); + index = (int)lParam; + lstrcpy(Str_Tmp, RWInternalToDisplayAddress(rswatches[index].Address)); + SetDlgItemText(hDlg,IDC_EDIT_COMPAREADDRESS,Str_Tmp); + if (rswatches[index].comment != NULL) + SetDlgItemText(hDlg,IDC_PROMPT_EDIT,rswatches[index].comment); + s = rswatches[index].Size; + t = rswatches[index].Type; + switch (s) + { + case TEXT('b'): + SendDlgItemMessage(hDlg, IDC_1_BYTE, BM_SETCHECK, BST_CHECKED, 0); + break; + case TEXT('w'): + SendDlgItemMessage(hDlg, IDC_2_BYTES, BM_SETCHECK, BST_CHECKED, 0); + break; + case TEXT('d'): + SendDlgItemMessage(hDlg, IDC_4_BYTES, BM_SETCHECK, BST_CHECKED, 0); + break; + default: + s = 0; + break; + } + switch (t) + { + case TEXT('s'): + SendDlgItemMessage(hDlg, IDC_SIGNED, BM_SETCHECK, BST_CHECKED, 0); + break; + case TEXT('u'): + SendDlgItemMessage(hDlg, IDC_UNSIGNED, BM_SETCHECK, BST_CHECKED, 0); + break; + case TEXT('h'): + SendDlgItemMessage(hDlg, IDC_HEX, BM_SETCHECK, BST_CHECKED, 0); + break; + default: + t = 0; + break; + } + + return true; + break; + + case WM_COMMAND: + switch(LOWORD(wParam)) + { + case IDC_SIGNED: + t=TEXT('s'); + return true; + case IDC_UNSIGNED: + t=TEXT('u'); + return true; + case IDC_HEX: + t=TEXT('h'); + return true; + case IDC_1_BYTE: + s = TEXT('b'); + return true; + case IDC_2_BYTES: + s = TEXT('w'); + return true; + case IDC_4_BYTES: + s = TEXT('d'); + return true; + case IDOK: + { + if (s && t) + { + AddressWatcher Temp; + Temp.Size = s; + Temp.Type = t; + Temp.WrongEndian = false; //replace this when I get little endian working properly + GetDlgItemText(hDlg,IDC_EDIT_COMPAREADDRESS,Str_Tmp,1024); + TCHAR *addrstr = Str_Tmp; + if (lstrlen(Str_Tmp) > 8) addrstr = &(Str_Tmp[lstrlen(Str_Tmp) - 9]); + for(int i = 0; addrstr[i]; i++) {if(_totupper(addrstr[i]) == TEXT('O')) addrstr[i] = TEXT('0');} + Temp.Address = DisplayToRWInternalAddress(addrstr); + + if((Temp.Address & ~0xFFFFFF) == ~0xFFFFFF) + Temp.Address &= 0xFFFFFF; + + if(IsRAMWatchAddressValid(Temp.Address)) + { + GetDlgItemText(hDlg,IDC_PROMPT_EDIT,Str_Tmp,80); + if (index < WatchCount) RemoveWatch(index); + InsertWatch(Temp,Str_Tmp); + if(RamWatchHWnd) + { + ListView_SetItemCount(GetDlgItem(RamWatchHWnd,IDC_WATCHLIST),WatchCount); + } + EndDialog(hDlg, true); + } + else + { + MessageBox(hDlg,TEXT("Invalid Address"),TEXT("ERROR"),MB_OK); + } + } + else + { + lstrcpy(Str_Tmp,TEXT("Error:")); + if (!s) + lstrcat(Str_Tmp,TEXT(" Size must be specified.")); + if (!t) + lstrcat(Str_Tmp,TEXT(" Type must be specified.")); + MessageBox(hDlg,Str_Tmp,TEXT("ERROR"),MB_OK); + } + RWfileChanged=true; + return true; + break; + } + case IDCANCEL: + EndDialog(hDlg, false); + return false; + break; + } + break; + + case WM_CLOSE: + EndDialog(hDlg, false); + return false; + break; + } + + return false; +} + + + + +void RamWatchEnableCommand(HWND hDlg, HMENU hMenu, UINT uIDEnableItem, bool enable) +{ + EnableWindow(GetDlgItem(hDlg, uIDEnableItem), (enable?TRUE:FALSE)); + if (hMenu != NULL) { + if (uIDEnableItem == ID_WATCHES_UPDOWN) { + EnableMenuItem(hMenu, IDC_C_WATCH_UP, MF_BYCOMMAND | (enable?MF_ENABLED:MF_GRAYED)); + EnableMenuItem(hMenu, IDC_C_WATCH_DOWN, MF_BYCOMMAND | (enable?MF_ENABLED:MF_GRAYED)); + } + else + EnableMenuItem(hMenu, uIDEnableItem, MF_BYCOMMAND | (enable?MF_ENABLED:MF_GRAYED)); + } +} + +void RefreshWatchListSelectedCountControlStatus(HWND hDlg) +{ + static int prevSelCount=-1; + int selCount = ListView_GetSelectedCount(GetDlgItem(hDlg,IDC_WATCHLIST)); + if(selCount != prevSelCount) + { + if(selCount < 2 || prevSelCount < 2) + { + RamWatchEnableCommand(hDlg, ramwatchmenu, IDC_C_WATCH_EDIT, selCount == 1); + RamWatchEnableCommand(hDlg, ramwatchmenu, IDC_C_WATCH_REMOVE, selCount >= 1); + RamWatchEnableCommand(hDlg, ramwatchmenu, IDC_C_WATCH_DUPLICATE, selCount == 1); + RamWatchEnableCommand(hDlg, ramwatchmenu, IDC_C_ADDCHEAT, selCount == 1); + RamWatchEnableCommand(hDlg, ramwatchmenu, ID_WATCHES_UPDOWN, selCount == 1); + } + prevSelCount = selCount; + } +} + +LRESULT CALLBACK RamWatchProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + RECT r; + RECT r2; + int dx1, dy1, dx2, dy2; + static int watchIndex=0; + + switch(uMsg) + { + case WM_MOVE: { + RECT wrect; + GetWindowRect(hDlg,&wrect); + ramw_x = wrect.left; + ramw_y = wrect.top; + // TODO + //regSetDwordValue(RAMWX, ramw_x); + //regSetDwordValue(RAMWY, ramw_y); + } break; + + case WM_INITDIALOG: { + GetWindowRect(hWnd, &r); //Ramwatch window + dx1 = (r.right - r.left) / 2; + dy1 = (r.bottom - r.top) / 2; + + GetWindowRect(hDlg, &r2); // TASer window + dx2 = (r2.right - r2.left) / 2; + dy2 = (r2.bottom - r2.top) / 2; + + + // push it away from the main window if we can + const int width = (r.right-r.left); + const int height = (r.bottom - r.top); + const int width2 = (r2.right-r2.left); + if(r.left+width2 + width < GetSystemMetrics(SM_CXSCREEN)) + { + r.right += width; + r.left += width; + } + else if((int)r.left - (int)width2 > 0) + { + r.right -= width2; + r.left -= width2; + } + + //----------------------------------------------------------------------------------- + //If user has Save Window Pos selected, override default positioning + if (RWSaveWindowPos) + { + //If ramwindow is for some reason completely off screen, use default instead + if (ramw_x > (-width*2) || ramw_x < (width*2 + GetSystemMetrics(SM_CYSCREEN)) ) + r.left = ramw_x; //This also ignores cases of windows -32000 error codes + //If ramwindow is for some reason completely off screen, use default instead + if (ramw_y > (0-height*2) ||ramw_y < (height*2 + GetSystemMetrics(SM_CYSCREEN)) ) + r.top = ramw_y; //This also ignores cases of windows -32000 error codes + } + //------------------------------------------------------------------------------------- + SetWindowPos(hDlg, NULL, r.left, r.top, NULL, NULL, SWP_NOSIZE | SWP_NOZORDER | SWP_SHOWWINDOW); + + ramwatchmenu=GetMenu(hDlg); + rwrecentmenu=CreateMenu(); + UpdateRW_RMenu(rwrecentmenu, RAMMENU_FILE_RECENT, RW_MENU_FIRST_RECENT_FILE); + + const TCHAR* names[3] = {TEXT("Address"),TEXT("Value"),TEXT("Notes")}; + int widths[3] = {62,64,64+51+53}; + init_list_box(GetDlgItem(hDlg,IDC_WATCHLIST),names,3,widths); + if (!ResultCount) + reset_address_info(); + else + signal_new_frame(); + ListView_SetItemCount(GetDlgItem(hDlg,IDC_WATCHLIST),WatchCount); + if (!noMisalign) SendDlgItemMessage(hDlg, IDC_MISALIGN, BM_SETCHECK, BST_CHECKED, 0); + //if (littleEndian) SendDlgItemMessage(hDlg, IDC_ENDIAN, BM_SETCHECK, BST_CHECKED, 0); + + RamWatchAccels = LoadAccelerators(hInst, MAKEINTRESOURCE(IDR_RWACCELERATOR)); + + // due to some bug in windows, the arrow button width from the resource gets ignored, so we have to set it here + SetWindowPos(GetDlgItem(hDlg,ID_WATCHES_UPDOWN), 0,0,0, 30,60, SWP_NOMOVE); + + Update_RAM_Watch(); + + DragAcceptFiles(hDlg, TRUE); + + RefreshWatchListSelectedCountControlStatus(hDlg); + return false; + } break; + + case WM_INITMENU: + CheckMenuItem(ramwatchmenu, RAMMENU_FILE_AUTOLOAD, AutoRWLoad ? MF_CHECKED : MF_UNCHECKED); + CheckMenuItem(ramwatchmenu, RAMMENU_FILE_SAVEWINDOW, RWSaveWindowPos ? MF_CHECKED : MF_UNCHECKED); + break; + + case WM_MENUSELECT: + case WM_ENTERSIZEMOVE: + //Clear_Sound_Buffer(); + break; + + case WM_NOTIFY: + { + switch(wParam) + { + case ID_WATCHES_UPDOWN: + { + switch(((LPNMUPDOWN)lParam)->hdr.code) + { + case UDN_DELTAPOS: { + int delta = ((LPNMUPDOWN)lParam)->iDelta; + SendMessage(hDlg, WM_COMMAND, delta<0 ? IDC_C_WATCH_UP : IDC_C_WATCH_DOWN,0); + } break; + } + } break; + + default: + { + LPNMHDR lP = (LPNMHDR) lParam; + switch (lP->code) + { + case LVN_ITEMCHANGED: // selection changed event + { + NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)lP; + if(pNMListView->uNewState & LVIS_FOCUSED || + (pNMListView->uNewState ^ pNMListView->uOldState) & LVIS_SELECTED) + { + // disable buttons that we don't have the right number of selected items for + RefreshWatchListSelectedCountControlStatus(hDlg); + } + } break; + + case LVN_GETDISPINFO: + { + LV_DISPINFO *Item = (LV_DISPINFO *)lParam; + + // ignore unnecessary requests + Item->item.mask &= LVIF_TEXT; + + // set content only when it's requested + if (Item->item.mask & LVIF_TEXT) + { + const unsigned int iNum = Item->item.iItem; + static TCHAR num[11]; + switch (Item->item.iSubItem) + { + case 0: + lstrcpy(num, RWInternalToDisplayAddress(rswatches[iNum].Address)); + Item->item.pszText = num; + break; + case 1: { + int i = rswatches[iNum].CurValue; + int t = rswatches[iNum].Type; + int size = rswatches[iNum].Size; + const TCHAR* formatString = ((t==TEXT('s')) ? TEXT("%d") : (t==TEXT('u')) ? TEXT("%u") : (size==TEXT('d') ? TEXT("%08X") : size==TEXT('w') ? TEXT("%04X") : TEXT("%02X"))); + switch (size) + { + case TEXT('b'): + default: _stprintf(num, formatString, t==TEXT('s') ? (char)(i&0xff) : (unsigned char)(i&0xff)); break; + case TEXT('w'): _stprintf(num, formatString, t==TEXT('s') ? (short)(i&0xffff) : (unsigned short)(i&0xffff)); break; + case TEXT('d'): _stprintf(num, formatString, t==TEXT('s') ? (long)(i&0xffffffff) : (unsigned long)(i&0xffffffff)); break; + } + + Item->item.pszText = num; + } break; + case 2: + Item->item.pszText = rswatches[iNum].comment ? rswatches[iNum].comment : TEXT(""); + break; + } + } + } break; + case LVN_ODFINDITEM: + { + // disable search by keyboard typing, + // because it interferes with some of the accelerators + // and it isn't very useful here anyway + SetWindowLongPtr(hDlg, DWLP_MSGRESULT, ListView_GetSelectionMark(GetDlgItem(hDlg,IDC_WATCHLIST))); + return 1; + } + } + } + } + } break; + + case WM_COMMAND: + switch(LOWORD(wParam)) + { + case RAMMENU_FILE_SAVE: + QuickSaveWatches(); + break; + + case RAMMENU_FILE_SAVEAS: + //case IDC_C_SAVE: + return Save_Watches(); + case RAMMENU_FILE_OPEN: + return Load_Watches(true); + case RAMMENU_FILE_APPEND: + //case IDC_C_LOAD: + return Load_Watches(false); + case RAMMENU_FILE_NEW: + //case IDC_C_RESET: + ResetWatches(); + return true; + case IDC_C_WATCH_REMOVE: + { + HWND watchListControl = GetDlgItem(hDlg, IDC_WATCHLIST); + watchIndex = ListView_GetNextItem(watchListControl, -1, LVNI_ALL | LVNI_SELECTED); + while (watchIndex >= 0) + { + RemoveWatch(watchIndex); + ListView_DeleteItem(watchListControl, watchIndex); + watchIndex = ListView_GetNextItem(watchListControl, -1, LVNI_ALL | LVNI_SELECTED); + } + RWfileChanged=true; + SetFocus(GetDlgItem(hDlg,IDC_WATCHLIST)); + return true; + } + case IDC_C_WATCH_EDIT: + watchIndex = ListView_GetSelectionMark(GetDlgItem(hDlg,IDC_WATCHLIST)); + if(watchIndex != -1) + { + DialogBoxParam(hInst, MAKEINTRESOURCE(IDD_EDITWATCH), hDlg, (DLGPROC) EditWatchProc,(LPARAM) watchIndex); + SetFocus(GetDlgItem(hDlg,IDC_WATCHLIST)); + } + return true; + case IDC_C_WATCH: + rswatches[WatchCount].Address = rswatches[WatchCount].WrongEndian = 0; + rswatches[WatchCount].Size = TEXT('b'); + rswatches[WatchCount].Type = TEXT('s'); + DialogBoxParam(hInst, MAKEINTRESOURCE(IDD_EDITWATCH), hDlg, (DLGPROC) EditWatchProc,(LPARAM) WatchCount); + SetFocus(GetDlgItem(hDlg,IDC_WATCHLIST)); + return true; + case IDC_C_WATCH_DUPLICATE: + watchIndex = ListView_GetSelectionMark(GetDlgItem(hDlg,IDC_WATCHLIST)); + if(watchIndex != -1) + { + rswatches[WatchCount].Address = rswatches[watchIndex].Address; + rswatches[WatchCount].WrongEndian = rswatches[watchIndex].WrongEndian; + rswatches[WatchCount].Size = rswatches[watchIndex].Size; + rswatches[WatchCount].Type = rswatches[watchIndex].Type; + DialogBoxParam(hInst, MAKEINTRESOURCE(IDD_EDITWATCH), hDlg, (DLGPROC) EditWatchProc,(LPARAM) WatchCount); + SetFocus(GetDlgItem(hDlg,IDC_WATCHLIST)); + } + return true; + case IDC_C_WATCH_UP: + { + watchIndex = ListView_GetSelectionMark(GetDlgItem(hDlg,IDC_WATCHLIST)); + if (watchIndex == 0 || watchIndex == -1) + return true; + void *tmp = malloc(sizeof(AddressWatcher)); + memcpy(tmp,&(rswatches[watchIndex]),sizeof(AddressWatcher)); + memcpy(&(rswatches[watchIndex]),&(rswatches[watchIndex - 1]),sizeof(AddressWatcher)); + memcpy(&(rswatches[watchIndex - 1]),tmp,sizeof(AddressWatcher)); + free(tmp); + ListView_SetItemState(GetDlgItem(hDlg,IDC_WATCHLIST),watchIndex,0,LVIS_FOCUSED|LVIS_SELECTED); + ListView_SetSelectionMark(GetDlgItem(hDlg,IDC_WATCHLIST),watchIndex-1); + ListView_SetItemState(GetDlgItem(hDlg,IDC_WATCHLIST),watchIndex-1,LVIS_FOCUSED|LVIS_SELECTED,LVIS_FOCUSED|LVIS_SELECTED); + ListView_SetItemCount(GetDlgItem(hDlg,IDC_WATCHLIST),WatchCount); + RWfileChanged=true; + return true; + } + case IDC_C_WATCH_DOWN: + { + watchIndex = ListView_GetSelectionMark(GetDlgItem(hDlg,IDC_WATCHLIST)); + if (watchIndex >= WatchCount - 1 || watchIndex == -1) + return true; + void *tmp = malloc(sizeof(AddressWatcher)); + memcpy(tmp,&(rswatches[watchIndex]),sizeof(AddressWatcher)); + memcpy(&(rswatches[watchIndex]),&(rswatches[watchIndex + 1]),sizeof(AddressWatcher)); + memcpy(&(rswatches[watchIndex + 1]),tmp,sizeof(AddressWatcher)); + free(tmp); + ListView_SetItemState(GetDlgItem(hDlg,IDC_WATCHLIST),watchIndex,0,LVIS_FOCUSED|LVIS_SELECTED); + ListView_SetSelectionMark(GetDlgItem(hDlg,IDC_WATCHLIST),watchIndex+1); + ListView_SetItemState(GetDlgItem(hDlg,IDC_WATCHLIST),watchIndex+1,LVIS_FOCUSED|LVIS_SELECTED,LVIS_FOCUSED|LVIS_SELECTED); + ListView_SetItemCount(GetDlgItem(hDlg,IDC_WATCHLIST),WatchCount); + RWfileChanged=true; + return true; + } + case ID_WATCHES_UPDOWN: + { + int delta = ((LPNMUPDOWN)lParam)->iDelta; + SendMessage(hDlg, WM_COMMAND, delta<0 ? IDC_C_WATCH_UP : IDC_C_WATCH_DOWN,0); + break; + } + case RAMMENU_FILE_AUTOLOAD: + { + AutoRWLoad ^= 1; + CheckMenuItem(ramwatchmenu, RAMMENU_FILE_AUTOLOAD, AutoRWLoad ? MF_CHECKED : MF_UNCHECKED); + //regSetDwordValue(AUTORWLOAD, AutoRWLoad); TODO + break; + } + case RAMMENU_FILE_SAVEWINDOW: + { + RWSaveWindowPos ^=1; + CheckMenuItem(ramwatchmenu, RAMMENU_FILE_SAVEWINDOW, RWSaveWindowPos ? MF_CHECKED : MF_UNCHECKED); + //regSetDwordValue(RWSAVEPOS, RWSaveWindowPos); TODO + break; + } + case IDC_C_ADDCHEAT: + { + watchIndex = ListView_GetSelectionMark(GetDlgItem(hDlg,IDC_WATCHLIST)); + if(watchIndex >= 0) + { + unsigned int address = rswatches[watchIndex].Address; + + int sizeType = -1; + if(rswatches[watchIndex].Size == TEXT('b')) + sizeType = 0; + else if(rswatches[watchIndex].Size == TEXT('w')) + sizeType = 1; + else if(rswatches[watchIndex].Size == TEXT('d')) + sizeType = 2; + + int numberType = -1; + if(rswatches[watchIndex].Type == TEXT('s')) + numberType = 0; + else if(rswatches[watchIndex].Type == TEXT('u')) + numberType = 1; + else if(rswatches[watchIndex].Type == TEXT('h')) + numberType = 2; + + if (sizeType == -1 || numberType == -1) + break; + + struct ICheat cht; + const int fmtTable[] = { 2, 1, 3 }; + ZeroMemory(&cht, sizeof(struct SCheat)); + cht.address = RWInternalToHardwareAddress(address); + cht.size = 1 << sizeType; + cht.format = fmtTable[numberType]; + cht.new_val = rswatches[watchIndex].CurValue; + cht.saved_val = rswatches[watchIndex].CurValue; + extern INT_PTR CALLBACK DlgCheatSearchAdd(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam); + if(DialogBoxParam(hInst, MAKEINTRESOURCE(IDD_CHEAT_FROM_SEARCH), hDlg, DlgCheatSearchAdd, (LPARAM)&cht)) + { + int p; + for(p=0; p>(8*p))&0xFF)); + //add cheat + strcpy(Cheat.c[Cheat.num_cheats-1].name, cht.name); + } + } + } + } + break; + case IDOK: + case IDCANCEL: + RamWatchHWnd = NULL; + DragAcceptFiles(hDlg, FALSE); + EndDialog(hDlg, true); + return true; + default: + if (LOWORD(wParam) >= RW_MENU_FIRST_RECENT_FILE && LOWORD(wParam) < RW_MENU_FIRST_RECENT_FILE+MAX_RECENT_WATCHES && LOWORD(wParam) <= RW_MENU_LAST_RECENT_FILE) + OpenRWRecentFile(LOWORD(wParam) - RW_MENU_FIRST_RECENT_FILE); + } + break; + + case WM_KEYDOWN: // handle accelerator keys + { + SetFocus(GetDlgItem(hDlg,IDC_WATCHLIST)); + MSG msg; + msg.hwnd = hDlg; + msg.message = uMsg; + msg.wParam = wParam; + msg.lParam = lParam; + if(RamWatchAccels && TranslateAccelerator(hDlg, RamWatchAccels, &msg)) + return true; + } break; + + case WM_GETMINMAXINFO: + { + LPMINMAXINFO lpmm = (LPMINMAXINFO)lParam; + lpmm->ptMinTrackSize.x = 240; + lpmm->ptMinTrackSize.y = 320; + } break; + + case WM_SIZE: + { + UINT nType = (UINT) wParam; + int cx = (int) LOWORD(lParam); + int cy = (int) HIWORD(lParam); + + // more flexible resize routine is welcome + SetWindowPos(GetDlgItem(hDlg, IDC_WATCHES_GROUP), (HWND)NULL, cx-71, 18, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE); + SetWindowPos(GetDlgItem(hDlg, IDC_WATCHLIST), (HWND)NULL, 0, 0, cx-89, cy-31, SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE); + SetWindowPos(GetDlgItem(hDlg, IDC_C_WATCH_EDIT), (HWND)NULL, cx-65, 107, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE); + SetWindowPos(GetDlgItem(hDlg, IDC_C_WATCH_REMOVE), (HWND)NULL, cx-65, 135, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE); + SetWindowPos(GetDlgItem(hDlg, IDC_C_WATCH), (HWND)NULL, cx-65, 163, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE); + SetWindowPos(GetDlgItem(hDlg, IDC_C_WATCH_DUPLICATE), (HWND)NULL, cx-65, 190, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE); + SetWindowPos(GetDlgItem(hDlg, IDC_C_ADDCHEAT), (HWND)NULL, cx-71, 228, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE); + SetWindowPos(GetDlgItem(hDlg, ID_WATCHES_UPDOWN), (HWND)NULL, cx-56, 37, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE); + RedrawWindow(hDlg, NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW | RDW_ALLCHILDREN); // workaround for groupbox repainting + } break; + +// case WM_CLOSE: +// RamWatchHWnd = NULL; +// DragAcceptFiles(hDlg, FALSE); +// DestroyWindow(hDlg); +// return false; + + case WM_DESTROY: + // this is the correct place + RamWatchHWnd = NULL; + DragAcceptFiles(hDlg, FALSE); + WriteRecentRWFiles(); // write recent menu to ini + break; + + case WM_DROPFILES: + { + HDROP hDrop = (HDROP)wParam; + DragQueryFile(hDrop, 0, Str_Tmp, 1024); + DragFinish(hDrop); + return Load_Watches(true, Str_Tmp); + } break; + } + + return false; +} diff --git a/win32/ramwatch.h b/win32/ramwatch.h new file mode 100644 index 00000000..16da81bf --- /dev/null +++ b/win32/ramwatch.h @@ -0,0 +1,52 @@ +//RamWatch dialog was copied and adapted from GENS11: http://code.google.com/p/gens-rerecording/ +//Authors: Upthorn, Nitsuja, adelikat + +#ifndef RAMWATCH_H +#define RAMWATCH_H +#include +bool ResetWatches(); +void OpenRWRecentFile(int memwRFileNumber); +extern bool AutoRWLoad; +extern bool RWSaveWindowPos; +#define MAX_RECENT_WATCHES 5 +extern TCHAR rw_recent_files[MAX_RECENT_WATCHES][1024]; +extern bool AskSave(); +extern int ramw_x; +extern int ramw_y; +extern bool RWfileChanged; + +//Constants +//#define AUTORWLOAD "RamWatchAutoLoad" +//#define RWSAVEPOS "RamWatchSaveWindowPos" +//#define RAMWX "RamwX" +//#define RAMWY "RamwY" + +// AddressWatcher is self-contained now +struct AddressWatcher +{ + unsigned int Address; // hardware address + TCHAR Size; + TCHAR Type; + TCHAR* comment; // NULL means no comment, non-NULL means allocated comment + bool WrongEndian; + unsigned int CurValue; +}; +#define MAX_WATCH_COUNT 256 +extern AddressWatcher rswatches[MAX_WATCH_COUNT]; +extern int WatchCount; // number of valid items in rswatches + +extern TCHAR Watch_Dir[1024]; + +extern HWND RamWatchHWnd; +extern HACCEL RamWatchAccels; + +bool InsertWatch(const AddressWatcher& Watch, TCHAR *Comment); +bool InsertWatch(const AddressWatcher& Watch, HWND parent=NULL); // asks user for comment +void Update_RAM_Watch(); +bool Load_Watches(bool clear, const TCHAR* filename); +void RWAddRecentFile(const TCHAR *filename); + +LRESULT CALLBACK RamWatchProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); +extern HWND RamWatchHWnd; + +#endif diff --git a/win32/rsrc/resource.h b/win32/rsrc/resource.h index 04b0b0e8..e4485c77 100644 --- a/win32/rsrc/resource.h +++ b/win32/rsrc/resource.h @@ -37,6 +37,11 @@ #define IDB_LOCKEDFOLDER 148 #define IDB_HIDDENFOLDER 149 #define IDD_MULTICART 150 +#define IDD_RAMSEARCH 500 +#define IDD_RAMWATCH 501 +#define IDD_EDITWATCH 502 +#define IDD_PROMPT 503 +#define IDR_RWACCELERATOR 504 #define IDC_DRIVER 1001 #define IDC_BUFLEN 1002 #define IDC_RATE 1003 @@ -362,6 +367,46 @@ #define IDC_SHADER_HLSL_BROWSE 3016 #define IDC_SHADER_GROUP 3017 #define IDC_SHADER_GLSL_BROWSE 3018 +#define IDC_RAMLIST 5000 +//#define IDC_C_SEARCH 5001 +#define IDC_C_ADDCHEAT 5002 +//#define IDC_C_WATCH 5003 +//#define IDC_C_RESET 5004 +#define IDC_C_ELIMINATE 5005 +#define IDC_LESSTHAN 5006 +#define IDC_MORETHAN 5007 +#define IDC_NOMORETHAN 5008 +#define IDC_NOLESSTHAN 5009 +#define IDC_EQUALTO 5010 +#define IDC_DIFFERENTFROM 5011 +#define IDC_DIFFERENTBY 5012 +#define IDC_MODULO 5013 +#define IDC_EDIT_DIFFBY 5014 +#define IDC_EDIT_MODBY 5015 +#define IDC_PREVIOUSVALUE 5016 +#define IDC_SPECIFICVALUE 5017 +#define IDC_SPECIFICADDRESS 5018 +#define IDC_NUMBEROFCHANGES 5019 +#define IDC_EDIT_COMPAREVALUE 5020 +#define IDC_EDIT_COMPAREADDRESS 5021 +#define IDC_EDIT_COMPARECHANGES 5022 +#define IDC_C_AUTOSEARCH 5023 +#define IDC_2_BYTES 5024 +#define IDC_4_BYTES 5025 +#define IDC_MISALIGN 5026 +#define IDC_C_RESET_CHANGES 5027 +#define IDC_C_UNDO 5028 +#define IDC_WATCHLIST 5029 +#define IDC_C_WATCH_EDIT 5030 +#define IDC_C_WATCH_REMOVE 5031 +#define IDC_C_WATCH_DUPLICATE 5032 +#define ID_WATCHES_UPDOWN 5033 +#define IDC_C_WATCH_UP 5034 +#define IDC_C_WATCH_DOWN 5035 +#define IDC_PROMPT_TEXT 5036 +#define IDC_PROMPT_TEXT2 5037 +#define IDC_PROMPT_EDIT 5038 +#define IDC_WATCHES_GROUP 5039 #define ID_FILE_EXIT 40001 #define ID_WINDOW_HIDEMENUBAR 40004 #define ID_FILE_AVI_RECORDING 40005 @@ -495,6 +540,18 @@ #define ID_WINDOW_SIZE_4X 40172 #define ID_DEBUG_APU_TRACE 40173 #define ID_EMULATION_BACKGROUNDINPUT 40174 +#define RAMMENU_FILE_AUTOLOAD 45000 +#define RAMMENU_FILE_SAVEWINDOW 45001 +#define RAMMENU_FILE_SAVE 45002 +#define RAMMENU_FILE_SAVEAS 45003 +#define RAMMENU_FILE_OPEN 45004 +#define RAMMENU_FILE_APPEND 45005 +#define RAMMENU_FILE_NEW 45006 +#define RAMMENU_FILE_RECENT 45007 +#define ID_RAM_SEARCH 45008 +#define ID_RAM_WATCH 45009 +#define RW_MENU_FIRST_RECENT_FILE 45010 +#define RW_MENU_LAST_RECENT_FILE 45011 // Next default values for new objects // diff --git a/win32/rsrc/snes9x.rc b/win32/rsrc/snes9x.rc index 2aa4637f..fb83c951 100644 --- a/win32/rsrc/snes9x.rc +++ b/win32/rsrc/snes9x.rc @@ -35,7 +35,7 @@ IDC_CURSOR_SCOPE CURSOR "nodrop.cur" IDR_SNES9X_ACCELERATORS ACCELERATORS BEGIN "G", ID_CHEAT_ENTER, VIRTKEY, ALT, NOINVERT - "A", ID_CHEAT_SEARCH, VIRTKEY, ALT, NOINVERT + "A", ID_RAM_SEARCH, VIRTKEY, ALT, NOINVERT "O", ID_FILE_LOAD_GAME, VIRTKEY, CONTROL, NOINVERT VK_F5, ID_OPTIONS_DISPLAY, VIRTKEY, ALT, NOINVERT VK_F7, ID_OPTIONS_JOYPAD, VIRTKEY, ALT, NOINVERT @@ -49,6 +49,20 @@ BEGIN VK_BACK, ID_WINDOW_STRETCH, VIRTKEY, ALT, NOINVERT END +IDR_RWACCELERATOR ACCELERATORS +BEGIN + "N", RAMMENU_FILE_NEW, VIRTKEY, CONTROL + "O", RAMMENU_FILE_OPEN, VIRTKEY, CONTROL + "S", RAMMENU_FILE_SAVE, VIRTKEY, CONTROL + "S", RAMMENU_FILE_SAVEAS, VIRTKEY, SHIFT, CONTROL + "A", IDC_C_WATCH_DUPLICATE, VIRTKEY + "E", IDC_C_WATCH_EDIT, VIRTKEY + "D", IDC_C_WATCH_DOWN, VIRTKEY + "U", IDC_C_WATCH_UP, VIRTKEY + "N", IDC_C_WATCH, VIRTKEY + "R", IDC_C_WATCH_REMOVE, VIRTKEY +END + ///////////////////////////////////////////////////////////////////////////// // @@ -551,6 +565,138 @@ BEGIN COMBOBOX IDC_HOSTNAME,60,5,110,40,CBS_DROPDOWN | WS_VSCROLL | WS_TABSTOP END +IDD_RAM_SEARCH DIALOGEX 0, 0, 272, 275 +STYLE DS_SETFONT | DS_MODALFRAME | DS_SETFOREGROUND | DS_3DLOOK | DS_CENTER | WS_VISIBLE | WS_CLIPCHILDREN | WS_CAPTION | WS_SYSMENU +CAPTION "RAM Search" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + DEFPUSHBUTTON "&OK",IDOK,161,253,50,14 + PUSHBUTTON "&Cancel",IDCANCEL,218,253,50,14 + CONTROL "List1",IDC_ADDYS,"SysListView32",LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_OWNERDATA | WS_BORDER | WS_TABSTOP,7,7,201,152,WS_EX_CLIENTEDGE + PUSHBUTTON "&Search",IDC_C_SEARCH,215,7,52,16 + PUSHBUTTON "&Add Cheat",IDC_C_ADD,215,29,52,16,WS_DISABLED + PUSHBUTTON "&Reset",IDC_C_RESET,215,51,52,16 + CONTROL "1 byte",IDC_1_BYTE,"Button",BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,212,172,42,11 + CONTROL "2 bytes",IDC_2_BYTE,"Button",BS_AUTORADIOBUTTON,212,185,42,11 + CONTROL "3 bytes",IDC_3_BYTE,"Button",BS_AUTORADIOBUTTON,212,198,42,11 + CONTROL "4 bytes",IDC_4_BYTE,"Button",BS_AUTORADIOBUTTON,212,211,42,11 + GROUPBOX "Data Size",IDC_STATIC,205,162,54,66,0,WS_EX_TRANSPARENT + GROUPBOX "Comparison Type",IDC_STATIC,7,162,114,86,0,WS_EX_TRANSPARENT + CONTROL "< (Less Than)",IDC_LESS_THAN,"Button",BS_AUTORADIOBUTTON | WS_GROUP,13,170,90,11 + CONTROL "> (Greater Than)",IDC_GREATER_THAN,"Button",BS_AUTORADIOBUTTON,13,182,90,11 + CONTROL "<= (Less Than or Equal to)",IDC_LESS_THAN_EQUAL,"Button",BS_AUTORADIOBUTTON,13,194,106,11 + CONTROL ">= (Greater than or Equal To)",IDC_GREATER_THAN_EQUAL, + "Button",BS_AUTORADIOBUTTON,13,206,105,11 + CONTROL "= (Equal To)",IDC_EQUAL,"Button",BS_AUTORADIOBUTTON,13,218,90,11 + CONTROL "!= (Not Equal To)",IDC_NOT_EQUAL,"Button",BS_AUTORADIOBUTTON,13,230,90,11 + GROUPBOX "Data Type",IDC_STATIC,125,206,75,43,0,WS_EX_TRANSPARENT + GROUPBOX "Compare To",IDC_STATIC,125,162,75,42,0,WS_EX_TRANSPARENT + CONTROL "Previous Value",IDC_PREV,"Button",BS_AUTORADIOBUTTON | WS_GROUP,129,171,67,10 + CONTROL "Entered Value",IDC_ENTERED,"Button",BS_AUTORADIOBUTTON,129,181,67,10 + CONTROL "Entered Address",IDC_ENTEREDADDRESS,"Button",BS_AUTORADIOBUTTON,129,192,67,10 + CONTROL "Unsigned (>=0)",IDC_UNSIGNED,"Button",BS_AUTORADIOBUTTON | WS_GROUP,129,216,67,10 + CONTROL "Signed (+/-)",IDC_SIGNED,"Button",BS_AUTORADIOBUTTON,129,226,67,10 + CONTROL "Hexadecimal",IDC_HEX,"Button",BS_AUTORADIOBUTTON,129,236,67,10 + EDITTEXT IDC_VALUE_ENTER,72,253,83,12,ES_UPPERCASE | ES_AUTOHSCROLL | WS_DISABLED + RTEXT "Enter a Value:",IDC_ENTER_LABEL,7,253,54,12,SS_CENTERIMAGE | WS_DISABLED + PUSHBUTTON "&Watch",IDC_C_WATCH,215,91,52,16 + PUSHBUTTON "&Clear Watches",IDC_C_CLEARWATCH,215,108,52,16 + PUSHBUTTON "&Load Watches",IDC_C_LOADWATCH,215,125,52,16 + PUSHBUTTON "Sa&ve Watches",IDC_C_SAVEWATCH,215,142,52,16 +END + +IDD_RAMSEARCH DIALOGEX 0, 0, 287, 292 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION " RAM Search" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + CONTROL "",IDC_RAMLIST,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_OWNERDATA | WS_BORDER | WS_TABSTOP,9,9,214,151,WS_EX_CLIENTEDGE + PUSHBUTTON "&Search",IDC_C_SEARCH,226,9,52,16 + PUSHBUTTON "&Add Cheat",IDC_C_ADDCHEAT,226,145,52,16,WS_DISABLED + PUSHBUTTON "&Watch",IDC_C_WATCH,226,127,52,16 + PUSHBUTTON "&Reset",IDC_C_RESET,226,27,52,16 + PUSHBUTTON "&Eliminate",IDC_C_ELIMINATE,226,109,52,16 + GROUPBOX "Comparison Operator",IDC_STATIC,10,166,102,118,0,WS_EX_TRANSPARENT + CONTROL "Less Than",IDC_LESSTHAN,"Button",BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,14,178,95,11 + CONTROL "Greater Than",IDC_MORETHAN,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,14,191,95,11 + CONTROL "Less Than or Equal To",IDC_NOMORETHAN,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,14,204,95,11 + CONTROL "Greater Than or Equal To",IDC_NOLESSTHAN,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,14,217,95,11 + CONTROL "Equal To",IDC_EQUALTO,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,14,230,95,11 + CONTROL "Not Equal To",IDC_DIFFERENTFROM,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,14,243,95,11 + CONTROL "Different By:",IDC_DIFFERENTBY,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,14,256,52,11 + CONTROL "Modulo",IDC_MODULO,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,14,269,35,11 + EDITTEXT IDC_EDIT_DIFFBY,69,255,38,12,ES_UPPERCASE | ES_AUTOHSCROLL | WS_DISABLED + EDITTEXT IDC_EDIT_MODBY,51,267,38,12,ES_UPPERCASE | ES_AUTOHSCROLL | WS_DISABLED + GROUPBOX "Compare To / By",IDC_STATIC,118,166,153,58,0,WS_EX_TRANSPARENT + CONTROL "Previous Value",IDC_PREVIOUSVALUE,"Button",BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,121,176,67,10 + CONTROL "Specific Value:",IDC_SPECIFICVALUE,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,121,187,67,10 + CONTROL "Specific Address:",IDC_SPECIFICADDRESS,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,121,198,67,10 + CONTROL "Number of Changes:",IDC_NUMBEROFCHANGES,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,121,209,76,10 + EDITTEXT IDC_EDIT_COMPAREVALUE,203,183,63,12,ES_UPPERCASE | ES_AUTOHSCROLL | WS_DISABLED + EDITTEXT IDC_EDIT_COMPAREADDRESS,203,195,63,12,ES_UPPERCASE | ES_AUTOHSCROLL | WS_DISABLED + EDITTEXT IDC_EDIT_COMPARECHANGES,203,207,63,12,ES_UPPERCASE | ES_AUTOHSCROLL | WS_DISABLED + GROUPBOX "Data Type / Display",IDC_STATIC,196,227,75,44,0,WS_EX_TRANSPARENT + CONTROL "Signed",IDC_SIGNED,"Button",BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,200,237,67,9 + CONTROL "Unsigned",IDC_UNSIGNED,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,200,248,67,9 + CONTROL "Hexadecimal",IDC_HEX,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,200,259,67,9 + CONTROL "Autosearch",IDC_C_AUTOSEARCH,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,200,273,52,11 + GROUPBOX "Data Size",IDC_STATIC,117,227,73,57,0,WS_EX_TRANSPARENT + CONTROL "1 byte",IDC_1_BYTE,"Button",BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,121,237,61,11 + CONTROL "2 bytes",IDC_2_BYTES,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,121,248,61,11 + CONTROL "4 bytes",IDC_4_BYTES,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,121,259,61,11 + CONTROL "Check Misaligned",IDC_MISALIGN,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,121,272,65,8 + PUSHBUTTON "&Clear Change Counts",IDC_C_RESET_CHANGES,226,46,52,20,BS_MULTILINE + PUSHBUTTON "&Undo",IDC_C_UNDO,226,69,52,16,WS_DISABLED + LTEXT "Is",IDC_STATIC,92,270,12,8 +END + +IDD_RAMWATCH DIALOGEX 0, 0, 269, 274 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_MINIMIZEBOX | WS_POPUP | WS_VISIBLE | WS_CLIPCHILDREN | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME +CAPTION " RAM Watch" +MENU RAMWATCH_MENU +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + CONTROL "",IDC_WATCHLIST,"SysListView32",LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_OWNERDATA | WS_BORDER | WS_TABSTOP,9,9,210,255,WS_EX_CLIENTEDGE + PUSHBUTTON "Edit",IDC_C_WATCH_EDIT,226,66,34,14 + PUSHBUTTON "Remove",IDC_C_WATCH_REMOVE,226,83,34,14 + PUSHBUTTON "New",IDC_C_WATCH,226,100,34,14 + PUSHBUTTON "Duplicate",IDC_C_WATCH_DUPLICATE,226,117,34,14 + PUSHBUTTON "Add Cheat",IDC_C_ADDCHEAT,222,140,42,16,WS_DISABLED + GROUPBOX "Watches",IDC_WATCHES_GROUP,222,11,42,125,0,WS_EX_TRANSPARENT + CONTROL "",ID_WATCHES_UPDOWN,"msctls_updown32",WS_TABSTOP,232,23,19,36 +END + +IDD_EDITWATCH DIALOGEX 0, 0, 181, 95 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION " Edit Watch" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + CTEXT "Address:",IDC_SPECIFICADDRESS,18,12,35,10 + EDITTEXT IDC_EDIT_COMPAREADDRESS,55,10,65,12,ES_UPPERCASE | ES_AUTOHSCROLL + CTEXT "Notes:",IDC_PROMPT_TEXT,18,24,45,10 + EDITTEXT IDC_PROMPT_EDIT,55,22,65,12,ES_AUTOHSCROLL + GROUPBOX "Data Type",IDC_STATIC,14,37,75,42,0,WS_EX_TRANSPARENT + CONTROL "&Signed",IDC_SIGNED,"Button",BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,18,47,67,9 + CONTROL "&Unsigned",IDC_UNSIGNED,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,18,57,67,9 + CONTROL "&Hexadecimal",IDC_HEX,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,18,67,67,9 + GROUPBOX "Data Size",IDC_STATIC,94,37,73,42,0,WS_EX_TRANSPARENT + CONTROL "&1 byte",IDC_1_BYTE,"Button",BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,98,47,61,11 + CONTROL "&2 bytes",IDC_2_BYTES,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,98,57,61,11 + CONTROL "&4 bytes",IDC_4_BYTES,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,98,67,61,11 + DEFPUSHBUTTON "&OK",IDOK,66,80,50,14 + PUSHBUTTON "&Cancel",IDCANCEL,120,80,50,14 +END + +IDD_PROMPT DIALOG 0, 0, 186, 68 +STYLE DS_SETFONT | DS_MODALFRAME | DS_3DLOOK | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +CAPTION "Input Prompt" +FONT 8, "Ms Shell Dlg 2" +BEGIN + DEFPUSHBUTTON "OK",IDOK,100,43,50,14 + PUSHBUTTON "Cancel",IDCANCEL,37,42,50,14 + EDITTEXT IDC_PROMPT_EDIT,10,15,167,14,ES_AUTOHSCROLL +END + ///////////////////////////////////////////////////////////////////////////// // @@ -633,13 +779,6 @@ BEGIN HORZGUIDE, 122 END - IDD_CHEAT_SEARCH, DIALOG - BEGIN - LEFTMARGIN, 7 - TOPMARGIN, 7 - BOTTOMMARGIN, 268 - END - IDD_CHEAT_FROM_SEARCH, DIALOG BEGIN LEFTMARGIN, 7 @@ -934,9 +1073,10 @@ BEGIN POPUP "&Cheat" BEGIN MENUITEM "&Game Genie, Pro-Action Replay Codes\tAlt+G", ID_CHEAT_ENTER - MENUITEM "&Search for New Cheats", ID_CHEAT_SEARCH_MODAL - MENUITEM "Search for New Cheats (active)\tAlt+A", 40064 MENUITEM "&Apply Cheats", ID_CHEAT_APPLY, CHECKED + MENUITEM SEPARATOR + MENUITEM "RAM Watch...", ID_RAM_WATCH, GRAYED + MENUITEM "RAM &Search...\tAlt+A", ID_RAM_SEARCH, GRAYED END POPUP "&Netplay" BEGIN @@ -957,6 +1097,34 @@ BEGIN END END +RAMWATCH_MENU MENU +BEGIN + POPUP "File" + BEGIN + MENUITEM "&New list\tCtrl N", RAMMENU_FILE_NEW + MENUITEM "&Open...\tCtrl O", RAMMENU_FILE_OPEN + MENUITEM "&Save\tCtrl S", RAMMENU_FILE_SAVE + MENUITEM "Sa&ve As...\tCtrl Shift S", RAMMENU_FILE_SAVEAS + MENUITEM "&Append file...", RAMMENU_FILE_APPEND + MENUITEM "Recent", RAMMENU_FILE_RECENT + MENUITEM SEPARATOR + MENUITEM "Auto-&load", RAMMENU_FILE_AUTOLOAD + MENUITEM "Save Window Position", RAMMENU_FILE_SAVEWINDOW + MENUITEM SEPARATOR + MENUITEM "&Close\tAlt F4", IDCANCEL + END + POPUP "Watches" + BEGIN + MENUITEM "&New Watch\tN", IDC_C_WATCH + MENUITEM "&Edit Watch\tE", IDC_C_WATCH_EDIT + MENUITEM "&Remove Watch\tR", IDC_C_WATCH_REMOVE + MENUITEM "Duplicate Watch\tA", IDC_C_WATCH_DUPLICATE + MENUITEM SEPARATOR + MENUITEM "Move Up\tU", IDC_C_WATCH_UP + MENUITEM "Move Down\tD", IDC_C_WATCH_DOWN + END +END + #endif // English (U.S.) resources ///////////////////////////////////////////////////////////////////////////// diff --git a/win32/snes9xw.vcproj b/win32/snes9xw.vcproj index b94faa57..a0a95caa 100644 --- a/win32/snes9xw.vcproj +++ b/win32/snes9xw.vcproj @@ -3022,6 +3022,22 @@ RelativePath=".\InputCustom.h" > + + + + + + + + diff --git a/win32/wsnes9x.cpp b/win32/wsnes9x.cpp index 510c5914..694338f7 100644 --- a/win32/wsnes9x.cpp +++ b/win32/wsnes9x.cpp @@ -214,6 +214,8 @@ #include "../statemanager.h" #include "AVIOutput.h" #include "InputCustom.h" +#include "ram_search.h" +#include "ramwatch.h" #include #if (((defined(_MSC_VER) && _MSC_VER >= 1300)) || defined(__MINGW32__)) @@ -281,6 +283,9 @@ HRESULT CALLBACK EnumModesCallback( LPDDSURFACEDESC lpDDSurfaceDesc, LPVOID lpCo VOID CALLBACK HotkeyTimer( UINT idEvent, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2); +extern HWND RamSearchHWnd; +extern LRESULT CALLBACK RamSearchProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); + #define NOTKNOWN "Unknown Company " #define HEADER_SIZE 512 #define INFO_LEN (0xFF - 0xC0) @@ -2218,6 +2223,23 @@ LRESULT CALLBACK WinProc( S9xSaveCheatFile (S9xGetFilename (".cht", CHEAT_DIR)); RestoreSNESDisplay (); break; + case ID_RAM_SEARCH: + if(!RamSearchHWnd) + { + reset_address_info(); + RamSearchHWnd = CreateDialog(GUI.hInstance, MAKEINTRESOURCE(IDD_RAMSEARCH), hWnd, (DLGPROC) RamSearchProc); + } + else + SetForegroundWindow(RamSearchHWnd); + break; + case ID_RAM_WATCH: + if(!RamWatchHWnd) + { + RamWatchHWnd = CreateDialog(GUI.hInstance, MAKEINTRESOURCE(IDD_RAMWATCH), hWnd, (DLGPROC) RamWatchProc); + } + else + SetForegroundWindow(RamWatchHWnd); + break; case ID_CHEAT_APPLY: Settings.ApplyCheats = !Settings.ApplyCheats; if (!Settings.ApplyCheats){ @@ -3335,6 +3357,16 @@ int WINAPI WinMain( if (!GetMessage (&msg, NULL, 0, 0)) goto loop_exit; // got WM_QUIT + // do not process non-modal dialog messages + if (RamSearchHWnd && IsDialogMessage(RamSearchHWnd, &msg)) + continue; + + if (RamWatchHWnd && IsDialogMessage(RamWatchHWnd, &msg)) { + if(msg.message == WM_KEYDOWN) // send keydown messages to the dialog (for accelerators, and also needed for the Alt key to work) + SendMessage(RamWatchHWnd, msg.message, msg.wParam, msg.lParam); + continue; + } + if (!TranslateAccelerator (GUI.hWnd, GUI.Accelerators, &msg)) { TranslateMessage (&msg); @@ -3456,6 +3488,7 @@ int WINAPI WinMain( } S9xMainLoop(); + Update_RAM_Search(); // Update_RAM_Watch() is also called. GUI.FrameCount++; } @@ -3652,6 +3685,15 @@ static void CheckMenuStates () SetMenuItemInfo (GUI.hMenu, ID_CHEAT_SEARCH_MODAL, FALSE, &mii); SetMenuItemInfo (GUI.hMenu, IDM_ROM_INFO, FALSE, &mii); + mii.fState = RamWatchHWnd ? MFS_CHECKED : MFS_UNCHECKED; + if (Settings.StopEmulation || GUI.FullScreen) + mii.fState |= MFS_DISABLED; + SetMenuItemInfo (GUI.hMenu, ID_RAM_WATCH, FALSE, &mii); + mii.fState = RamSearchHWnd ? MFS_CHECKED : MFS_UNCHECKED; + if (Settings.StopEmulation || GUI.FullScreen) + mii.fState |= MFS_DISABLED; + SetMenuItemInfo (GUI.hMenu, ID_RAM_SEARCH, FALSE, &mii); + if (GUI.FullScreen) mii.fState |= MFS_DISABLED; SetMenuItemInfo (GUI.hMenu, ID_CHEAT_SEARCH, FALSE, &mii); @@ -8964,18 +9006,6 @@ static inline int CheatCount(int byteSub) } -struct ICheat -{ - uint32 address; - uint32 new_val; - uint32 saved_val; - int size; - bool8 enabled; - bool8 saved; - char name [22]; - int format; -}; - bool TestRange(int val_type, S9xCheatDataSize bytes, uint32 value) { if(val_type!=2) diff --git a/win32/wsnes9x.h b/win32/wsnes9x.h index fb7c81c6..11f97be9 100644 --- a/win32/wsnes9x.h +++ b/win32/wsnes9x.h @@ -205,6 +205,7 @@ #include #endif #include "rsrc/resource.h" +#include "../port.h" #define COUNT(a) (sizeof (a) / sizeof (a[0])) #define GUI_VERSION 1008 @@ -549,6 +550,18 @@ enum SNES_MAX_CONTROLLER_OPTIONS }; +struct ICheat +{ + uint32 address; + uint32 new_val; + uint32 saved_val; + int size; + bool8 enabled; + bool8 saved; + char name [22]; + int format; +}; + /*****************************************************************************/ void SetInfoDlgColor(unsigned char r, unsigned char g, unsigned char b);