diff --git a/src/drivers/win/fceu_x86.manifest b/src/drivers/win/fceu_x86.manifest
new file mode 100644
index 00000000..7d518c99
--- /dev/null
+++ b/src/drivers/win/fceu_x86.manifest
@@ -0,0 +1,23 @@
+
+
+
+FCEUX
+
+
+
+
+
+
diff --git a/src/drivers/win/ram_search.cpp b/src/drivers/win/ram_search.cpp
new file mode 100644
index 00000000..1a7a0d58
--- /dev/null
+++ b/src/drivers/win/ram_search.cpp
@@ -0,0 +1,2089 @@
+// 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
+#include "common.h"
+#include "fceu.h"
+#include "../../debug.h"
+
+#include "resource.h"
+#include "ram_search.h"
+#include "ramwatch.h"
+#include
+#include
+#include
+#include
+#ifdef WIN32
+ #include "BaseTsd.h"
+ typedef INT_PTR intptr_t;
+#else
+ #include "stdint.h"
+#endif
+
+// TODO: cleanups :/
+static inline uint8* HardwareToSoftwareAddress(HWAddressType address)
+{
+ if (!GameInfo || address < 0x0000 || address > 0xffff)
+ return NULL;
+
+ // for some reasons, it looks not so easy to return direct pointer.
+ // so we copy a part of RAM to a temporary buffer and return it.
+ static uint8 tempBuf[0x10000];
+ for (int i = 0; i <= 4 && (address+i) <= 0xffff; i++) {
+ tempBuf[address+i] = GetMem(address+i);
+ }
+ return &tempBuf[address];
+}
+
+
+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 hAppWnd
+#define hInst fceu_hInstance
+static char 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 HardwareToSoftwareAddress 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 = HardwareToSoftwareAddress(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;
+ }
+ }
+
+ // TODO: don't include ROM in our RAM search (it's too huge)
+
+ // 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]+sizeof(compareType) <= i+sizeof(compareType)) // 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+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 == 'b' \
+ ? (isSigned \
+ ? functionName() \
+ : functionName()) \
+ : sizeTypeID == 'w' \
+ ? (isSigned \
+ ? (requiresAligned \
+ ? functionName() \
+ : functionName()) \
+ : (requiresAligned \
+ ? functionName() \
+ : functionName())) \
+ : sizeTypeID == 'd' \
+ ? (isSigned \
+ ? (requiresAligned \
+ ? functionName() \
+ : functionName()) \
+ : (requiresAligned \
+ ? functionName() \
+ : functionName())) \
+ : functionName())
+
+#define CALL_WITH_T_SIZE_TYPES_1(functionName, sizeTypeID, isSigned, requiresAligned, p0) \
+ (sizeTypeID == 'b' \
+ ? (isSigned \
+ ? functionName(p0) \
+ : functionName(p0)) \
+ : sizeTypeID == 'w' \
+ ? (isSigned \
+ ? (requiresAligned \
+ ? functionName(p0) \
+ : functionName(p0)) \
+ : (requiresAligned \
+ ? functionName(p0) \
+ : functionName(p0))) \
+ : sizeTypeID == '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 == 'b' \
+ ? (isSigned \
+ ? functionName(p0, p1, p2) \
+ : functionName(p0, p1, p2)) \
+ : sizeTypeID == 'w' \
+ ? (isSigned \
+ ? (requiresAligned \
+ ? functionName(p0, p1, p2) \
+ : functionName(p0, p1, p2)) \
+ : (requiresAligned \
+ ? functionName(p0, p1, p2) \
+ : functionName(p0, p1, p2))) \
+ : sizeTypeID == '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 == 'b' \
+ ? (isSigned \
+ ? functionName(p0, p1, p2, p3) \
+ : functionName(p0, p1, p2, p3)) \
+ : sizeTypeID == '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 == '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 == 'b' \
+ ? functionName(p0, p1, p2) \
+ : sizeTypeID == 'w' \
+ ? (requiresAligned \
+ ? functionName(p0, p1, p2) \
+ : functionName(p0, p1, p2)) \
+ : sizeTypeID == '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 == 'b' \
+ ? functionName(p0, p1, p2, p3) \
+ : sizeTypeID == 'w' \
+ ? (requiresAligned \
+ ? functionName(p0, p1, p2, p3) \
+ : functionName(p0, p1, p2, p3)) \
+ : sizeTypeID == '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='s';
+char rs_o='=';
+char rs_t='s';
+int rs_param=0, rs_val=0, rs_val_valid=0;
+char rs_type_size = '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 '<': DO_SEARCH_2(LessCmp,sf); break; \
+ case '>': DO_SEARCH_2(MoreCmp,sf); break; \
+ case '=': DO_SEARCH_2(EqualCmp,sf); break; \
+ case '!': DO_SEARCH_2(UnequalCmp,sf); break; \
+ case 'l': DO_SEARCH_2(LessEqualCmp,sf); break; \
+ case 'm': DO_SEARCH_2(MoreEqualCmp,sf); break; \
+ case 'd': DO_SEARCH_2(DiffByCmp,sf); break; \
+ case '%': 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 'r': DO_SEARCH(SearchRelative); break;
+ case '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 '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 '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 == 's'));
+ }
+
+ if(!ok)
+ {
+ if(GetDlgItemText(RamSearchHWnd,controlID,Str_Tmp,16))
+ {
+ for(int i = 0; Str_Tmp[i]; i++) {if(toupper(Str_Tmp[i]) == 'O') Str_Tmp[i] = '0';}
+ const char* strPtr = Str_Tmp;
+ bool negate = false;
+ while(strPtr[0] == '-')
+ strPtr++, negate = !negate;
+ if(strPtr[0] == '+')
+ strPtr++;
+ if(strPtr[0] == '0' && tolower(strPtr[1]) == 'x')
+ strPtr += 2, forceHex = true;
+ if(strPtr[0] == '$')
+ strPtr++, forceHex = true;
+ if(!forceHex)
+ {
+ const char* strSearchPtr = strPtr;
+ while(*strSearchPtr)
+ {
+ int c = tolower(*strSearchPtr++);
+ if(c >= 'a' && c <= 'f')
+ forceHex = true;
+ }
+ }
+ const char* formatString = forceHex ? "%X" : ((rs_t=='s') ? "%d" : "%u");
+ if(sscanf(strPtr, formatString, &rv) > 0)
+ ok = true;
+ if(negate)
+ rv = -rv;
+ }
+ }
+
+ success = ok;
+ return rv;
+}
+
+
+bool Set_RS_Val()
+{
+ BOOL success;
+
+ // update rs_val
+ switch(rs_c)
+ {
+ case 'r':
+ default:
+ rs_val = 0;
+ break;
+ case 's':
+ rs_val = ReadControlInt(IDC_EDIT_COMPAREVALUE, rs_t == 'h', success);
+ if(!success)
+ return false;
+ if((rs_type_size == 'b' && rs_t == 's' && (rs_val < -128 || rs_val > 127)) ||
+ (rs_type_size == 'b' && rs_t != 's' && (rs_val < 0 || rs_val > 255)) ||
+ (rs_type_size == 'w' && rs_t == 's' && (rs_val < -32768 || rs_val > 32767)) ||
+ (rs_type_size == 'w' && rs_t != 's' && (rs_val < 0 || rs_val > 65535)))
+ return false;
+ break;
+ case 'a':
+ rs_val = ReadControlInt(IDC_EDIT_COMPAREADDRESS, true, success);
+ if(!success || rs_val < 0 || rs_val > 0x06040000)
+ return false;
+ break;
+ case '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 'd':
+ rs_param = ReadControlInt(IDC_EDIT_DIFFBY, false, success);
+ if(!success)
+ return false;
+ if(rs_param < 0)
+ rs_param = -rs_param;
+ break;
+ case '%':
+ 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 == 'n')
+ appliedSize = 'w', appliedSign = 'u';
+ if(rs_c == 'a')
+ appliedSize = 'd', appliedSign = 'u';
+ if((appliedSize == 'b' && appliedSize == 's' && (rs_param < -128 || rs_param > 127)) ||
+ (appliedSize == 'b' && appliedSize != 's' && (rs_param < 0 || rs_param > 255)) ||
+ (appliedSize == 'w' && appliedSize == 's' && (rs_param < -32768 || rs_param > 32767)) ||
+ (appliedSize == 'w' && appliedSize != '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=='s'),noMisalign, CmpFun,itemIndex,rs_val,rs_param);
+ case 'r': DO_SEARCH(CompareRelativeAtItem); break;
+ case '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 '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 'n': DO_SEARCH(CompareChangesAtItem); break;
+ }
+ return false;
+}
+
+
+
+unsigned int ReadValueAtSoftwareAddress(const unsigned char* address, unsigned int size, int byteSwapped = false)
+{
+ unsigned int value = 0;
+ if(!byteSwapped)
+ {
+ // assumes we're little-endian
+ memcpy(&value, address, size);
+ }
+ else
+ {
+ // byte-swap and convert to current endianness at the same time
+ for(unsigned int i = 0; i < size; i++)
+ {
+ value <<= 8;
+ value |= *((unsigned char*)((intptr_t)address++^1));
+ }
+ }
+ return value;
+}
+void WriteValueAtSoftwareAddress(unsigned char* address, unsigned int value, unsigned int size, int byteSwapped = false)
+{
+ if(!byteSwapped)
+ {
+ // assumes we're little-endian
+ memcpy(address, &value, size);
+ }
+ else
+ {
+ // write as big endian
+ for(int i = size-1; i >= 0; i--)
+ {
+ address[i] = value & 0xFF;
+ value >>= 8;
+ }
+ }
+}
+unsigned int ReadValueAtHardwareAddress(HWAddressType address, unsigned int size)
+{
+ return ReadValueAtSoftwareAddress(HardwareToSoftwareAddress(address), size);
+}
+bool WriteValueAtHardwareAddress(HWAddressType address, unsigned int value, unsigned int size)
+{
+ // FIXME: doesn't work for now. see HardwareToSoftwareAddress to know why.
+ WriteValueAtSoftwareAddress(HardwareToSoftwareAddress(address), value, size);
+ return true;
+}
+bool IsHardwareAddressValid(HWAddressType address)
+{
+ return HardwareToSoftwareAddress(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=='b' || !noMisalign) ? 1 : 2;
+ int prevResultCount = ResultCount;
+
+ CalculateItemIndices(size);
+ ResultCount = CALL_WITH_T_SIZE_TYPES_0(CountRegionItemsT, rs_type_size,rs_t=='s',noMisalign);
+
+ UpdatePossibilities(ResultCount, (int)s_activeMemoryRegions.size());
+
+ if(ResultCount != prevResultCount)
+ ListView_SetItemCount(GetDlgItem(RamSearchHWnd,IDC_RAMLIST),ResultCount);
+}
+
+void soft_reset_address_info ()
+{
+ ResetMemoryRegions();
+ 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=='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);
+ 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=='b' || !rs_last_no_misalign) ? 1 : 2;
+ int newSize = (rs_type_size=='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=='s',rs_last_no_misalign, oldTopIndex);
+ unsigned int oldSelectionAddr = CALL_WITH_T_SIZE_TYPES_1(GetHardwareAddressFromItemIndex, rs_last_type_size,rs_t=='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=='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=='s',rs_last_no_misalign, watchIndex);
+ if(!selHardwareAddrs.empty() && addr == selHardwareAddrs.back().End())
+ selHardwareAddrs.back().size += size;
+ else
+ 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=='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=='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=='s',noMisalign, endAddr);
+ if(selRangeBottom == -1)
+ selRangeBottom = selRangeTop;
+ if(selRangeTop == -1)
+ continue;
+
+ // select the entire range at once without deselecting the other ranges
+ // looks hacky but it works, and the only documentation I found on how to do this was blatantly false and equally hacky anyway
+ POINT pos;
+ ListView_EnsureVisible(lv, selRangeTop, 0);
+ ListView_GetItemPosition(lv, selRangeTop, &pos);
+ SendMessage(lv, WM_LBUTTONDOWN, MK_LBUTTON|MK_CONTROL, MAKELONG(pos.x,pos.y));
+ ListView_EnsureVisible(lv, selRangeBottom, 0);
+ ListView_GetItemPosition(lv, selRangeBottom, &pos);
+ SendMessage(lv, WM_LBUTTONDOWN, MK_LBUTTON|MK_CONTROL|MK_SHIFT, MAKELONG(pos.x,pos.y));
+ }
+
+ // 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 != 'b');
+ }
+ else
+ {
+ ListView_Update(lv, -1);
+ }
+}
+
+
+
+
+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;
+
+ if (AutoSearch && !ResultCount)
+ {
+ if(!AutoSearchAutoRetry)
+ {
+// Clear_Sound_Buffer();
+ int answer = MessageBox(RamSearchHWnd,"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.","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();
+ }
+
+ int 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=='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=='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 '<':
+ SendDlgItemMessage(hDlg, IDC_LESSTHAN, BM_SETCHECK, BST_CHECKED, 0);
+ break;
+ case '>':
+ SendDlgItemMessage(hDlg, IDC_MORETHAN, BM_SETCHECK, BST_CHECKED, 0);
+ break;
+ case 'l':
+ SendDlgItemMessage(hDlg, IDC_NOMORETHAN, BM_SETCHECK, BST_CHECKED, 0);
+ break;
+ case 'm':
+ SendDlgItemMessage(hDlg, IDC_NOLESSTHAN, BM_SETCHECK, BST_CHECKED, 0);
+ break;
+ case '=':
+ SendDlgItemMessage(hDlg, IDC_EQUALTO, BM_SETCHECK, BST_CHECKED, 0);
+ break;
+ case '!':
+ SendDlgItemMessage(hDlg, IDC_DIFFERENTFROM, BM_SETCHECK, BST_CHECKED, 0);
+ break;
+ case 'd':
+ SendDlgItemMessage(hDlg, IDC_DIFFERENTBY, BM_SETCHECK, BST_CHECKED, 0);
+ EnableWindow(GetDlgItem(hDlg,IDC_EDIT_DIFFBY),true);
+ break;
+ case '%':
+ SendDlgItemMessage(hDlg, IDC_MODULO, BM_SETCHECK, BST_CHECKED, 0);
+ EnableWindow(GetDlgItem(hDlg,IDC_EDIT_MODBY),true);
+ break;
+ }
+ switch (rs_c)
+ {
+ case 'r':
+ SendDlgItemMessage(hDlg, IDC_PREVIOUSVALUE, BM_SETCHECK, BST_CHECKED, 0);
+ break;
+ case 's':
+ SendDlgItemMessage(hDlg, IDC_SPECIFICVALUE, BM_SETCHECK, BST_CHECKED, 0);
+ EnableWindow(GetDlgItem(hDlg,IDC_EDIT_COMPAREVALUE),true);
+ break;
+ case 'a':
+ SendDlgItemMessage(hDlg, IDC_SPECIFICADDRESS, BM_SETCHECK, BST_CHECKED, 0);
+ EnableWindow(GetDlgItem(hDlg,IDC_EDIT_COMPAREADDRESS),true);
+ break;
+ case 'n':
+ SendDlgItemMessage(hDlg, IDC_NUMBEROFCHANGES, BM_SETCHECK, BST_CHECKED, 0);
+ EnableWindow(GetDlgItem(hDlg,IDC_EDIT_COMPARECHANGES),true);
+ break;
+ }
+ switch (rs_t)
+ {
+ case 's':
+ SendDlgItemMessage(hDlg, IDC_SIGNED, BM_SETCHECK, BST_CHECKED, 0);
+ break;
+ case 'u':
+ SendDlgItemMessage(hDlg, IDC_UNSIGNED, BM_SETCHECK, BST_CHECKED, 0);
+ break;
+ case 'h':
+ SendDlgItemMessage(hDlg, IDC_HEX, BM_SETCHECK, BST_CHECKED, 0);
+ break;
+ }
+ switch (rs_type_size)
+ {
+ case 'b':
+ SendDlgItemMessage(hDlg, IDC_1_BYTE, BM_SETCHECK, BST_CHECKED, 0);
+ break;
+ case 'w':
+ SendDlgItemMessage(hDlg, IDC_2_BYTES, BM_SETCHECK, BST_CHECKED, 0);
+ break;
+ case '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 char* names[5] = {"Address","Value","Previous","Changes","Notes"};
+ //int widths[5] = {62,64,64,55,55};
+ const char* names[] = {"Address","Value","Previous","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 char* 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)
+ {
+ // 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;
+ Item->item.mask = LVIF_TEXT;
+ Item->item.state = 0;
+ Item->item.iImage = 0;
+ const unsigned int iNum = Item->item.iItem;
+ static char num[11];
+ switch (Item->item.iSubItem)
+ {
+ case 0:
+ {
+ int addr = CALL_WITH_T_SIZE_TYPES_1(GetHardwareAddressFromItemIndex, rs_type_size,rs_t=='s',noMisalign, iNum);
+ sprintf(num,"%04X",addr);
+ Item->item.pszText = num;
+ } return true;
+ case 1:
+ {
+ int i = CALL_WITH_T_SIZE_TYPES_1(GetCurValueFromItemIndex, rs_type_size,rs_t=='s',noMisalign, iNum);
+ const char* formatString = ((rs_t=='s') ? "%d" : (rs_t=='u') ? "%u" : (rs_type_size=='d' ? "%08X" : rs_type_size=='w' ? "%04X" : "%02X"));
+ switch (rs_type_size)
+ {
+ case 'b':
+ default: sprintf(num, formatString, rs_t=='s' ? (char)(i&0xff) : (unsigned char)(i&0xff)); break;
+ case 'w': sprintf(num, formatString, rs_t=='s' ? (short)(i&0xffff) : (unsigned short)(i&0xffff)); break;
+ case 'd': sprintf(num, formatString, rs_t=='s' ? (long)(i&0xffffffff) : (unsigned long)(i&0xffffffff)); break;
+ }
+ Item->item.pszText = num;
+ } return true;
+ case 2:
+ {
+ int i = CALL_WITH_T_SIZE_TYPES_1(GetPrevValueFromItemIndex, rs_type_size,rs_t=='s',noMisalign, iNum);
+ const char* formatString = ((rs_t=='s') ? "%d" : (rs_t=='u') ? "%u" : (rs_type_size=='d' ? "%08X" : rs_type_size=='w' ? "%04X" : "%02X"));
+ switch (rs_type_size)
+ {
+ case 'b':
+ default: sprintf(num, formatString, rs_t=='s' ? (char)(i&0xff) : (unsigned char)(i&0xff)); break;
+ case 'w': sprintf(num, formatString, rs_t=='s' ? (short)(i&0xffff) : (unsigned short)(i&0xffff)); break;
+ case 'd': sprintf(num, formatString, rs_t=='s' ? (long)(i&0xffffffff) : (unsigned long)(i&0xffffffff)); break;
+ }
+ Item->item.pszText = num;
+ } return true;
+ case 3:
+ {
+ int i = CALL_WITH_T_SIZE_TYPES_1(GetNumChangesFromItemIndex, rs_type_size,rs_t=='s',noMisalign, iNum);
+ sprintf(num,"%d",i);
+
+ Item->item.pszText = num;
+ } return true;
+ //case 4:
+ // Item->item.pszText = rsaddrs[rsresults[iNum].Index].comment ? rsaddrs[rsresults[iNum].Index].comment : "";
+ // return true;
+ default:
+ return false;
+ }
+ }
+
+ case NM_CUSTOMDRAW:
+ {
+ SetWindowLong(hDlg, DWL_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='s';
+ signal_new_size();
+ {rv = true; break;}
+ case IDC_UNSIGNED:
+ rs_t='u';
+ signal_new_size();
+ {rv = true; break;}
+ case IDC_HEX:
+ rs_t='h';
+ signal_new_size();
+ {rv = true; break;}
+ case IDC_1_BYTE:
+ rs_type_size = 'b';
+ signal_new_size();
+ {rv = true; break;}
+ case IDC_2_BYTES:
+ rs_type_size = 'w';
+ signal_new_size();
+ {rv = true; break;}
+ case IDC_4_BYTES:
+ rs_type_size = '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 = '<';
+ {rv = true; break;}
+ case IDC_MORETHAN:
+ EnableWindow(GetDlgItem(hDlg,IDC_EDIT_DIFFBY),false);
+ EnableWindow(GetDlgItem(hDlg,IDC_EDIT_MODBY),false);
+ rs_o = '>';
+ {rv = true; break;}
+ case IDC_NOMORETHAN:
+ EnableWindow(GetDlgItem(hDlg,IDC_EDIT_DIFFBY),false);
+ EnableWindow(GetDlgItem(hDlg,IDC_EDIT_MODBY),false);
+ rs_o = 'l';
+ {rv = true; break;}
+ case IDC_NOLESSTHAN:
+ EnableWindow(GetDlgItem(hDlg,IDC_EDIT_DIFFBY),false);
+ EnableWindow(GetDlgItem(hDlg,IDC_EDIT_MODBY),false);
+ rs_o = 'm';
+ {rv = true; break;}
+ case IDC_EQUALTO:
+ EnableWindow(GetDlgItem(hDlg,IDC_EDIT_DIFFBY),false);
+ EnableWindow(GetDlgItem(hDlg,IDC_EDIT_MODBY),false);
+ rs_o = '=';
+ {rv = true; break;}
+ case IDC_DIFFERENTFROM:
+ EnableWindow(GetDlgItem(hDlg,IDC_EDIT_DIFFBY),false);
+ EnableWindow(GetDlgItem(hDlg,IDC_EDIT_MODBY),false);
+ rs_o = '!';
+ {rv = true; break;}
+ case IDC_DIFFERENTBY:
+ {
+ rs_o = '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 = '%';
+ 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='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 = '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 = '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 = '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:
+ {
+ int watchItemIndex = ListView_GetSelectionMark(GetDlgItem(hDlg,IDC_RAMLIST));
+ if(watchItemIndex >= 0)
+ {
+ unsigned long address = CALL_WITH_T_SIZE_TYPES_1(GetHardwareAddressFromItemIndex, rs_type_size,rs_t=='s',noMisalign, watchItemIndex);
+
+ int sizeType = -1;
+ if(rs_type_size == 'b')
+ sizeType = 0;
+ else if(rs_type_size == 'w')
+ sizeType = 1;
+ else if(rs_type_size == 'd')
+ sizeType = 2;
+
+ int numberType = -1;
+ if(rs_t == 's')
+ numberType = 0;
+ else if(rs_t == 'u')
+ numberType = 1;
+ else if(rs_t == 'h')
+ numberType = 2;
+
+ // TODO: open add-cheat dialog
+ }
+ } {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=='s',rs_val,rs_param);
+
+ RefreshRamListSelectedCountControlStatus(hDlg);
+ }
+
+ if(!ResultCount)
+ {
+
+ MessageBox(RamSearchHWnd,"Resetting search.","Out of results.",MB_OK|MB_ICONINFORMATION);
+ soft_reset_address_info();
+ }
+
+ {rv = true; break;}
+
+invalid_field:
+ MessageBox(RamSearchHWnd,"Invalid or out-of-bound entered value.","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:
+ {
+ int watchItemIndex = ListView_GetSelectionMark(GetDlgItem(hDlg,IDC_RAMLIST));
+ if(watchItemIndex >= 0)
+ {
+ AddressWatcher tempWatch;
+ tempWatch.Address = CALL_WITH_T_SIZE_TYPES_1(GetHardwareAddressFromItemIndex, rs_type_size,rs_t=='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;
+
+ bool inserted = InsertWatch(tempWatch, hDlg);
+ //ListView_Update(GetDlgItem(hDlg,IDC_RAMLIST), -1);
+
+ // 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=='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=='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 " RAM Search - "
+#define PROGRESS_STR " %d%% ... "
+#define STATUS_STR "%d Possibilit%s (%d Region%s)"
+
+ int poss = last_rs_possible;
+ int regions = last_rs_regions;
+ if(poss <= 0)
+ strcpy(Str_Tmp," RAM Search");
+ else if(percent <= 0)
+ sprintf(Str_Tmp, HEADER_STR STATUS_STR, poss, poss==1?"y":"ies", regions, regions==1?"":"s");
+ else
+ sprintf(Str_Tmp, PROGRESS_STR STATUS_STR, percent, poss, poss==1?"y":"ies", regions, regions==1?"":"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) ? "Redo" : "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 char* 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 = (LPSTR)(Strs[i]);
+ Col.cx = columnWidths[i];
+ ListView_InsertColumn(Box,i,&Col);
+ }
+
+ ListView_SetExtendedListViewStyle(Box, LVS_EX_FULLROWSELECT);
+}
diff --git a/src/drivers/win/ram_search.h b/src/drivers/win/ram_search.h
new file mode 100644
index 00000000..e7f9b924
--- /dev/null
+++ b/src/drivers/win/ram_search.h
@@ -0,0 +1,32 @@
+#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 IsHardwareAddressValid(HWAddressType address);
+extern int curr_ram_size;
+extern bool noMisalign;
+
+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
+
+extern HWND RamSearchHWnd;
+extern LRESULT CALLBACK RamSearchProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
+
+#endif
diff --git a/src/drivers/win/ramwatch.cpp b/src/drivers/win/ramwatch.cpp
new file mode 100644
index 00000000..cc0263c8
--- /dev/null
+++ b/src/drivers/win/ramwatch.cpp
@@ -0,0 +1,1178 @@
+
+#include
+#include "common.h"
+#include "main.h"
+using namespace std;
+
+#include "resource.h"
+#include "ram_search.h"
+#include "ramwatch.h"
+#include
+#include
+#include
+#include
+
+/*
+#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;
+char rw_recent_files[MAX_RECENT_WATCHES][1024];
+//char Watch_Dir[1024]="";
+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
+char currentWatch[1024];
+int ramw_x, ramw_y; //Used to store ramwatch dialog window positions
+AddressWatcher rswatches[MAX_WATCH_COUNT];
+int WatchCount=0;
+
+char applicationPath[2048];
+struct InitRamWatch
+{
+ InitRamWatch()
+ {
+ GetModuleFileName(NULL, applicationPath, 2048);
+ }
+} initRamWatch;
+
+HWND RamWatchHWnd;
+#define gamefilename GetRomName()
+#define hWnd hAppWnd
+#define hInst fceu_hInstance
+static char Str_Tmp [1024];
+
+void init_list_box(HWND Box, const char* Strs[], int numColumns, int *columnWidths); //initializes the ram search and/or ram watch listbox
+
+#define MESSAGEBOXPARENT (RamWatchHWnd ? RamWatchHWnd : hWnd)
+
+bool QuickSaveWatches();
+bool ResetWatches();
+
+unsigned int GetCurrentValue(AddressWatcher& watch)
+{
+ return ReadValueAtHardwareAddress(watch.Address, watch.Size == 'd' ? 4 : watch.Size == '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, char *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 = (char *) malloc(strlen(Comment)+2);
+ NewWatch.CurValue = GetCurrentValue(NewWatch);
+ strcpy(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);
+ strcpy(Str_Tmp,"Enter a name for this RAM address.");
+ SendDlgItemMessage(hDlg,IDC_PROMPT_TEXT,WM_SETTEXT,0,(LPARAM)Str_Tmp);
+ strcpy(Str_Tmp,"");
+ 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, "Save Changes?", "Ram Watch", MB_YESNOCANCEL);
+ if(answer == IDYES)
+ if(!QuickSaveWatches())
+ return false;
+ return (answer != IDCANCEL);
+ }
+ return true;
+}
+
+void WriteRecentRWFiles()
+{
+ char str[2048];
+ for (int i = 0; i < MAX_RECENT_WATCHES; i++)
+ {
+ sprintf(str, "recentWatch%d", i+1);
+ //regSetStringValue(str, &rw_recent_files[i][0]); TODO
+ }
+}
+
+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 = strlen(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--)
+ {
+ char tmp[128 + 5];
+
+ // Skip empty strings
+ if(!strlen(rw_recent_files[x]))
+ {
+ continue;
+ }
+
+ moo.cbSize = sizeof(moo);
+ moo.fMask = MIIM_DATA | MIIM_ID | MIIM_TYPE;
+
+ // Fill in the menu text.
+ if(strlen(rw_recent_files[x]) < 128)
+ {
+ sprintf(tmp, "&%d. %s", ( x + 1 ) % 10, rw_recent_files[x]);
+ }
+ else
+ {
+ sprintf(tmp, "&%d. %s", ( x + 1 ) % 10, rw_recent_files[x] + strlen( rw_recent_files[x] ) - 127);
+ }
+
+ // Insert the menu item
+ moo.cch = strlen(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 char* 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(strlen(rw_recent_files[x]))
+ {
+ if(!strncmp(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;
+ char tmp[len];
+
+ // Save pointer.
+ strncpy(tmp, rw_recent_files[x], len);
+
+ for(y = x; y; y--)
+ {
+ // Move items down.
+ strncpy(rw_recent_files[y],rw_recent_files[y - 1], len);
+ }
+
+ // Put item on top.
+ strncpy(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--)
+ {
+ strncpy(rw_recent_files[x],rw_recent_files[x - 1], len);
+ }
+
+ // Add the new item.
+ strncpy(rw_recent_files[0], addString, len);
+
+ // Update the recent files menu
+ UpdateRW_RMenu(menu, menuItem, baseId);
+}
+
+void RWAddRecentFile(const char *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
+
+ char* 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;
+ }
+ }
+
+ strcpy(currentWatch,x);
+ strcpy(Str_Tmp,currentWatch);
+
+ //loadwatches here
+ FILE *WatchFile = fopen(Str_Tmp,"rb");
+ if (!WatchFile)
+ {
+ int answer = MessageBox(MESSAGEBOXPARENT,"Error opening file.","ERROR",MB_OKCANCEL);
+ if (answer == IDOK)
+ {
+ rw_recent_files[rnum][0] = '\0'; //Clear file from list
+ if (rnum) //Update the ramwatch list
+ RWAddRecentFile(rw_recent_files[0]);
+ else
+ RWAddRecentFile(rw_recent_files[1]);
+ }
+ return;
+ }
+ const char DELIM = '\t';
+ AddressWatcher Temp;
+ char mode;
+ fgets(Str_Tmp,1024,WatchFile);
+ sscanf(Str_Tmp,"%c%*s",&mode);
+ //if ((mode == '1' && !(SegaCD_Started)) || (mode == '2' && !(_32X_Started)))
+ //{
+ // char Device[8];
+ // strcpy(Device,(mode > '1')?"32X":"SegaCD");
+ // sprintf(Str_Tmp,"Warning: %s not started. \nWatches for %s addresses will be ignored.",Device,Device);
+ // MessageBox(MESSAGEBOXPARENT,Str_Tmp,"Possible Device Mismatch",MB_OK);
+ //}
+ int WatchAdd;
+ fgets(Str_Tmp,1024,WatchFile);
+ sscanf(Str_Tmp,"%d%*s",&WatchAdd);
+ WatchAdd+=WatchCount;
+ for (int i = WatchCount; i < WatchAdd; i++)
+ {
+ while (i < 0)
+ i++;
+ do {
+ fgets(Str_Tmp,1024,WatchFile);
+ } while (Str_Tmp[0] == '\n');
+ sscanf(Str_Tmp,"%*05X%*c%04X%*c%c%*c%c%*c%d",&(Temp.Address),&(Temp.Size),&(Temp.Type),&(Temp.WrongEndian));
+ Temp.WrongEndian = 0;
+ char *Comment = strrchr(Str_Tmp,DELIM) + 1;
+ *strrchr(Comment,'\n') = '\0';
+ InsertWatch(Temp,Comment);
+ }
+
+ fclose(WatchFile);
+ if (RamWatchHWnd)
+ ListView_SetItemCount(GetDlgItem(RamWatchHWnd,IDC_WATCHLIST),WatchCount);
+ RWfileChanged=false;
+ return;
+}
+
+int Change_File_L(char *Dest, char *Dir, char *Titre, char *Filter, char *Ext, HWND hwnd)
+{
+ OPENFILENAME ofn;
+
+ SetCurrentDirectory(applicationPath);
+
+ if (!strcmp(Dest, ""))
+ {
+ strcpy(Dest, "default.");
+ strcat(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(char *Dest, char *Dir, char *Titre, char *Filter, char *Ext, HWND hwnd)
+{
+ OPENFILENAME ofn;
+
+ SetCurrentDirectory(applicationPath);
+
+ if (!strcmp(Dest, ""))
+ {
+ strcpy(Dest, "default.");
+ strcat(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 char* slash = max(strrchr(gamefilename, '|'), max(strrchr(gamefilename, '\\'), strrchr(gamefilename, '/')));
+ strcpy(Str_Tmp,slash ? slash+1 : gamefilename);
+ char* dot = strrchr(Str_Tmp, '.');
+ if(dot) *dot = 0;
+ strcat(Str_Tmp,".wch");
+ if(Change_File_S(Str_Tmp, applicationPath, "Save Watches", "Watchlist\0*.wch\0All Files\0*.*\0\0", "wch", RamWatchHWnd))
+ {
+ FILE *WatchFile = fopen(Str_Tmp,"r+b");
+ if (!WatchFile) WatchFile = fopen(Str_Tmp,"w+b");
+ fputc('\n',WatchFile);
+ strcpy(currentWatch,Str_Tmp);
+ RWAddRecentFile(currentWatch);
+ sprintf(Str_Tmp,"%d\n",WatchCount);
+ fputs(Str_Tmp,WatchFile);
+ const char DELIM = '\t';
+ for (int i = 0; i < WatchCount; i++)
+ {
+ sprintf(Str_Tmp,"%05X%c%04X%c%c%c%c%c%d%c%s\n",i,DELIM,rswatches[i].Address,DELIM,rswatches[i].Size,DELIM,rswatches[i].Type,DELIM,rswatches[i].WrongEndian,DELIM,rswatches[i].comment);
+ fputs(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();
+ }
+
+ strcpy(Str_Tmp,currentWatch);
+ FILE *WatchFile = fopen(Str_Tmp,"r+b");
+ if (!WatchFile) WatchFile = fopen(Str_Tmp,"w+b");
+ fputc('\n',WatchFile);
+ sprintf(Str_Tmp,"%d\n",WatchCount);
+ fputs(Str_Tmp,WatchFile);
+ const char DELIM = '\t';
+ for (int i = 0; i < WatchCount; i++)
+ {
+ sprintf(Str_Tmp,"%05X%c%04X%c%c%c%c%c%d%c%s\n",i,DELIM,rswatches[i].Address,DELIM,rswatches[i].Size,DELIM,rswatches[i].Type,DELIM,rswatches[i].WrongEndian,DELIM,rswatches[i].comment);
+ fputs(Str_Tmp,WatchFile);
+ }
+ fclose(WatchFile);
+ RWfileChanged=false;
+ return true;
+}
+
+bool Load_Watches(bool clear, const char* filename)
+{
+ const char DELIM = '\t';
+ FILE* WatchFile = fopen(filename,"rb");
+ if (!WatchFile)
+ {
+ MessageBox(MESSAGEBOXPARENT,"Error opening file.","ERROR",MB_OK);
+ return false;
+ }
+ if(clear)
+ {
+ if(!ResetWatches())
+ {
+ fclose(WatchFile);
+ return false;
+ }
+ }
+ strcpy(currentWatch,filename);
+ RWAddRecentFile(currentWatch);
+ AddressWatcher Temp;
+ char mode;
+ fgets(Str_Tmp,1024,WatchFile);
+ sscanf(Str_Tmp,"%c%*s",&mode);
+ int WatchAdd;
+ fgets(Str_Tmp,1024,WatchFile);
+ sscanf(Str_Tmp,"%d%*s",&WatchAdd);
+ WatchAdd+=WatchCount;
+ for (int i = WatchCount; i < WatchAdd; i++)
+ {
+ while (i < 0)
+ i++;
+ do {
+ fgets(Str_Tmp,1024,WatchFile);
+ } while (Str_Tmp[0] == '\n');
+ sscanf(Str_Tmp,"%*05X%*c%04X%*c%c%*c%c%*c%d",&(Temp.Address),&(Temp.Size),&(Temp.Type),&(Temp.WrongEndian));
+ Temp.WrongEndian = 0;
+ char *Comment = strrchr(Str_Tmp,DELIM) + 1;
+ *strrchr(Comment,'\n') = '\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 char* slash = max(strrchr(gamefilename, '|'), max(strrchr(gamefilename, '\\'), strrchr(gamefilename, '/')));
+ strcpy(Str_Tmp,slash ? slash+1 : gamefilename);
+ char* dot = strrchr(Str_Tmp, '.');
+ if(dot) *dot = 0;
+ strcat(Str_Tmp,".wch");
+ if(Change_File_L(Str_Tmp, applicationPath, "Load Watches", "Watchlist\0*.wch\0All Files\0*.*\0\0", "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);
+ 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;
+ sprintf(Str_Tmp,"%04X",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 'b':
+ SendDlgItemMessage(hDlg, IDC_1_BYTE, BM_SETCHECK, BST_CHECKED, 0);
+ break;
+ case 'w':
+ SendDlgItemMessage(hDlg, IDC_2_BYTES, BM_SETCHECK, BST_CHECKED, 0);
+ break;
+ case 'd':
+ SendDlgItemMessage(hDlg, IDC_4_BYTES, BM_SETCHECK, BST_CHECKED, 0);
+ break;
+ default:
+ s = 0;
+ break;
+ }
+ switch (t)
+ {
+ case 's':
+ SendDlgItemMessage(hDlg, IDC_SIGNED, BM_SETCHECK, BST_CHECKED, 0);
+ break;
+ case 'u':
+ SendDlgItemMessage(hDlg, IDC_UNSIGNED, BM_SETCHECK, BST_CHECKED, 0);
+ break;
+ case '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='s';
+ return true;
+ case IDC_UNSIGNED:
+ t='u';
+ return true;
+ case IDC_HEX:
+ t='h';
+ return true;
+ case IDC_1_BYTE:
+ s = 'b';
+ return true;
+ case IDC_2_BYTES:
+ s = 'w';
+ return true;
+ case IDC_4_BYTES:
+ s = '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);
+ char *addrstr = Str_Tmp;
+ if (strlen(Str_Tmp) > 8) addrstr = &(Str_Tmp[strlen(Str_Tmp) - 9]);
+ for(int i = 0; addrstr[i]; i++) {if(toupper(addrstr[i]) == 'O') addrstr[i] = '0';}
+ sscanf(addrstr,"%04X",&(Temp.Address));
+
+ if((Temp.Address & ~0xFFFFFF) == ~0xFFFFFF)
+ Temp.Address &= 0xFFFFFF;
+
+ if(IsHardwareAddressValid(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,"Invalid Address","ERROR",MB_OK);
+ }
+ }
+ else
+ {
+ strcpy(Str_Tmp,"Error:");
+ if (!s)
+ strcat(Str_Tmp," Size must be specified.");
+ if (!t)
+ strcat(Str_Tmp," Type must be specified.");
+ MessageBox(hDlg,Str_Tmp,"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;
+}
+
+
+
+
+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;
+ //regSetDwordValue(RAMWX, ramw_x); TODO
+ //regSetDwordValue(RAMWY, ramw_y); TODO
+ } 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 char* names[3] = {"Address","Value","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);
+
+ 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)
+ {
+ // disable buttons that we don't have the right number of selected items for
+ RefreshWatchListSelectedItemControlStatus(hDlg);
+ }
+ } break;
+
+ case LVN_GETDISPINFO:
+ {
+ LV_DISPINFO *Item = (LV_DISPINFO *)lParam;
+ Item->item.mask = LVIF_TEXT;
+ Item->item.state = 0;
+ Item->item.iImage = 0;
+ const unsigned int iNum = Item->item.iItem;
+ static char num[11];
+ switch (Item->item.iSubItem)
+ {
+ case 0:
+ sprintf(num,"%04X",rswatches[iNum].Address);
+ Item->item.pszText = num;
+ return true;
+ case 1: {
+ int i = rswatches[iNum].CurValue;
+ int t = rswatches[iNum].Type;
+ int size = rswatches[iNum].Size;
+ const char* formatString = ((t=='s') ? "%d" : (t=='u') ? "%u" : (size=='d' ? "%08X" : size=='w' ? "%04X" : "%02X"));
+ switch (size)
+ {
+ case 'b':
+ default: sprintf(num, formatString, t=='s' ? (char)(i&0xff) : (unsigned char)(i&0xff)); break;
+ case 'w': sprintf(num, formatString, t=='s' ? (short)(i&0xffff) : (unsigned short)(i&0xffff)); break;
+ case 'd': sprintf(num, formatString, t=='s' ? (long)(i&0xffffffff) : (unsigned long)(i&0xffffffff)); break;
+ }
+
+ Item->item.pszText = num;
+ } return true;
+ case 2:
+ Item->item.pszText = rswatches[iNum].comment ? rswatches[iNum].comment : "";
+ return true;
+
+ default:
+ return false;
+ }
+ }
+ case LVN_ODFINDITEM:
+ {
+ // disable search by keyboard typing,
+ // because it interferes with some of the accelerators
+ // and it isn't very useful here anyway
+ SetWindowLong(hDlg, DWL_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:
+ watchIndex = ListView_GetSelectionMark(GetDlgItem(hDlg,IDC_WATCHLIST));
+ if(watchIndex != -1)
+ {
+ RemoveWatch(watchIndex);
+ ListView_SetItemCount(GetDlgItem(hDlg,IDC_WATCHLIST),WatchCount);
+ 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 = 'b';
+ rswatches[WatchCount].Type = '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_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_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 == 'b')
+ sizeType = 0;
+ else if(rswatches[watchIndex].Size == 'w')
+ sizeType = 1;
+ else if(rswatches[watchIndex].Size == 'd')
+ sizeType = 2;
+
+ int numberType = -1;
+ if(rswatches[watchIndex].Type == 's')
+ numberType = 0;
+ else if(rswatches[watchIndex].Type == 'u')
+ numberType = 1;
+ else if(rswatches[watchIndex].Type == 'h')
+ numberType = 2;
+
+ // TODO: open add-cheat dialog
+ }
+ }
+ 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;
+
+#if 0
+ // this message is never received
+ 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;
+#endif
+
+// 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/src/drivers/win/ramwatch.h b/src/drivers/win/ramwatch.h
new file mode 100644
index 00000000..c7985ba8
--- /dev/null
+++ b/src/drivers/win/ramwatch.h
@@ -0,0 +1,48 @@
+#ifndef RAMWATCH_H
+#define RAMWATCH_H
+bool ResetWatches();
+void OpenRWRecentFile(int memwRFileNumber);
+extern bool AutoRWLoad;
+extern bool RWSaveWindowPos;
+#define MAX_RECENT_WATCHES 5
+extern char 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
+ char Size;
+ char Type;
+ char* 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 char Watch_Dir[1024];
+
+extern HWND RamWatchHWnd;
+extern HACCEL RamWatchAccels;
+
+bool InsertWatch(const AddressWatcher& Watch, char *Comment);
+bool InsertWatch(const AddressWatcher& Watch, HWND parent=NULL); // asks user for comment
+void Update_RAM_Watch();
+bool Load_Watches(bool clear, const char* filename);
+void RWAddRecentFile(const char *filename);
+
+LRESULT CALLBACK RamWatchProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
+extern HWND RamWatchHWnd;
+
+#endif
diff --git a/src/drivers/win/res.rc b/src/drivers/win/res.rc
index e0269f2c..c0f0c66d 100644
--- a/src/drivers/win/res.rc
+++ b/src/drivers/win/res.rc
@@ -7,7 +7,8 @@
//
// Generated from the TEXTINCLUDE 2 resource.
//
-#include "afxres.h"
+#include "afxres.h"
+
/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS
@@ -152,6 +153,8 @@ BEGIN
POPUP "&Tools"
BEGIN
MENUITEM "&Cheats...", MENU_CHEATS
+ MENUITEM "RAM Watch...", ID_RAM_WATCH
+ MENUITEM "RAM Search...", ID_RAM_SEARCH
MENUITEM "&Memory Watch...", MENU_MEMORY_WATCH
MENUITEM "&RAM Filter...", MENU_RAMFILTER
MENUITEM "&TAS Edit...", MENU_TASEDIT, INACTIVE
@@ -388,6 +391,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
+
/////////////////////////////////////////////////////////////////////////////
//
@@ -1497,6 +1528,98 @@ BEGIN
GROUPBOX "Emulation",65430,196,236,178,32,WS_GROUP
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, 298
+STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU
+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,279,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_STATIC,222,11,42,125
+ 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
+
/////////////////////////////////////////////////////////////////////////////
//
@@ -1774,6 +1897,20 @@ BEGIN
"W", ACCEL_CTRL_W, VIRTKEY, CONTROL, 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
+
/////////////////////////////////////////////////////////////////////////////
//
@@ -1791,7 +1928,8 @@ IDB_TE_ARROW BITMAP "res/te_arrow.bmp"
//
// Generated from the TEXTINCLUDE 3 resource.
//
-
+
+
/////////////////////////////////////////////////////////////////////////////
#endif // not APSTUDIO_INVOKED
diff --git a/src/drivers/win/resource.h b/src/drivers/win/resource.h
index cd29e033..5e37291f 100644
--- a/src/drivers/win/resource.h
+++ b/src/drivers/win/resource.h
@@ -148,6 +148,11 @@
#define MENU_RECORD_AVI 151
#define MENU_STOP_AVI 152
#define MENU_EXIT 153
+#define IDD_RAMSEARCH 154
+#define IDD_RAMWATCH 155
+#define IDD_EDITWATCH 156
+#define IDD_PROMPT 157
+#define IDR_RWACCELERATOR 158
#define MENU_RESET 200
#define BUTTON_ROMS 200
#define TXT_PAD1 200
@@ -408,6 +413,49 @@
#define IDC_CHECK1 1203
#define CB_ENABLECONTEXTMENU 1203
#define IDC_VOLUMEGROUP 1204
+#define IDC_RAMLIST 1205
+#define IDC_C_SEARCH 1206
+#define IDC_C_ADDCHEAT 1207
+#define IDC_C_WATCH 1208
+#define IDC_C_RESET 1209
+#define IDC_C_ELIMINATE 1210
+#define IDC_LESSTHAN 1211
+#define IDC_MORETHAN 1212
+#define IDC_NOMORETHAN 1213
+#define IDC_NOLESSTHAN 1214
+#define IDC_EQUALTO 1215
+#define IDC_DIFFERENTFROM 1216
+#define IDC_DIFFERENTBY 1217
+#define IDC_MODULO 1218
+#define IDC_EDIT_DIFFBY 1219
+#define IDC_EDIT_MODBY 1220
+#define IDC_PREVIOUSVALUE 1221
+#define IDC_SPECIFICVALUE 1222
+#define IDC_SPECIFICADDRESS 1223
+#define IDC_NUMBEROFCHANGES 1224
+#define IDC_EDIT_COMPAREVALUE 1225
+#define IDC_EDIT_COMPAREADDRESS 1226
+#define IDC_EDIT_COMPARECHANGES 1227
+#define IDC_SIGNED 1228
+#define IDC_UNSIGNED 1229
+#define IDC_HEX 1230
+#define IDC_C_AUTOSEARCH 1231
+#define IDC_1_BYTE 1232
+#define IDC_2_BYTES 1233
+#define IDC_4_BYTES 1234
+#define IDC_MISALIGN 1235
+#define IDC_C_RESET_CHANGES 1236
+#define IDC_C_UNDO 1237
+#define IDC_WATCHLIST 1238
+#define IDC_C_WATCH_EDIT 1239
+#define IDC_C_WATCH_REMOVE 1240
+#define IDC_C_WATCH_DUPLICATE 1241
+#define ID_WATCHES_UPDOWN 1242
+#define IDC_C_WATCH_UP 1243
+#define IDC_C_WATCH_DOWN 1244
+#define IDC_PROMPT_TEXT 1245
+#define IDC_PROMPT_TEXT2 1246
+#define IDC_PROMPT_EDIT 1247
#define MENU_NETWORK 40040
#define MENU_PALETTE 40041
#define MENU_SOUND 40042
@@ -696,6 +744,18 @@
#define ID_NEWPPU 40365
#define ID_OLDPPU 40366
#define ID_CONFIG_SAVECONFIGFILE 40367
+#define RAMMENU_FILE_AUTOLOAD 40368
+#define RAMMENU_FILE_SAVEWINDOW 40369
+#define RAMMENU_FILE_SAVE 40370
+#define RAMMENU_FILE_SAVEAS 40371
+#define RAMMENU_FILE_OPEN 40372
+#define RAMMENU_FILE_APPEND 40373
+#define RAMMENU_FILE_NEW 40374
+#define RAMMENU_FILE_RECENT 40375
+#define ID_RAM_SEARCH 40376
+#define ID_RAM_WATCH 40377
+#define RW_MENU_FIRST_RECENT_FILE 40380
+#define RW_MENU_LAST_RECENT_FILE 40400
#define IDC_DEBUGGER_ICONTRAY 55535
#define MW_ValueLabel2 65423
#define MW_ValueLabel1 65426
@@ -704,9 +764,9 @@
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
-#define _APS_NEXT_RESOURCE_VALUE 125
-#define _APS_NEXT_COMMAND_VALUE 40368
-#define _APS_NEXT_CONTROL_VALUE 1205
+#define _APS_NEXT_RESOURCE_VALUE 159
+#define _APS_NEXT_COMMAND_VALUE 40401
+#define _APS_NEXT_CONTROL_VALUE 1248
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif
diff --git a/src/drivers/win/window.cpp b/src/drivers/win/window.cpp
index 9a62b944..d4ef61fb 100644
--- a/src/drivers/win/window.cpp
+++ b/src/drivers/win/window.cpp
@@ -35,6 +35,8 @@
#include "input.h"
#include "fceu.h"
+#include "ram_search.h"
+#include "ramwatch.h"
#include "memwatch.h"
#include "ppuview.h"
#include "debugger.h"
@@ -332,6 +334,8 @@ void updateGameDependentMenus(unsigned int enable)
MENU_CDLOGGER,
MENU_GAMEGENIEDECODER,
MENU_CHEATS,
+ ID_RAM_SEARCH,
+ ID_RAM_WATCH,
ID_TOOLS_TEXTHOOKER
};
@@ -1337,6 +1341,15 @@ LRESULT FAR PASCAL AppWndProc(HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam)
else if (!(fileDropped.find(".lua") == string::npos) && (fileDropped.find(".lua") == fileDropped.length()-4))
FCEU_LoadLuaCode(ftmp);
//-------------------------------------------------------
+ //Check if memory watchlist file
+ //-------------------------------------------------------
+ else if (!(fileDropped.find(".wch") == string::npos) && (fileDropped.find(".wch") == fileDropped.length()-4)) {
+ if (GameInfo) {
+ SendMessage(hWnd, WM_COMMAND, (WPARAM)ID_RAM_WATCH,(LPARAM)(NULL));
+ Load_Watches(true, fileDropped.c_str());
+ }
+ }
+ //-------------------------------------------------------
//If not a movie, Load it as a ROM file
//-------------------------------------------------------
else
@@ -1778,6 +1791,25 @@ LRESULT FAR PASCAL AppWndProc(HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam)
DoTextHooker();
break;
+ case ID_RAM_SEARCH:
+ if(!RamSearchHWnd)
+ {
+ reset_address_info();
+ RamSearchHWnd = CreateDialog(fceu_hInstance, MAKEINTRESOURCE(IDD_RAMSEARCH), hWnd, (DLGPROC) RamSearchProc);
+ }
+ else
+ SetForegroundWindow(RamSearchHWnd);
+ break;
+
+ case ID_RAM_WATCH:
+ if(!RamWatchHWnd)
+ {
+ RamWatchHWnd = CreateDialog(fceu_hInstance, MAKEINTRESOURCE(IDD_RAMWATCH), hWnd, (DLGPROC) RamWatchProc);
+ }
+ else
+ SetForegroundWindow(RamWatchHWnd);
+ break;
+
//Debug Menu-------------------------------------------------------------
case MENU_DEBUGGER:
DoDebug(0);
diff --git a/src/fceu.cpp b/src/fceu.cpp
index b09c803e..4576b778 100644
--- a/src/fceu.cpp
+++ b/src/fceu.cpp
@@ -66,6 +66,8 @@
#include "drivers/win/main.h"
#include "drivers/win/cheat.h"
#include "drivers/win/texthook.h"
+#include "drivers/win/ram_search.h"
+#include "drivers/win/ramwatch.h"
#include "drivers/win/memwatch.h"
#include "drivers/win/tracer.h"
#else
@@ -647,6 +649,7 @@ void FCEUI_Emulate(uint8 **pXBuf, int32 **SoundBuf, int32 *SoundBufSize, int ski
//These Windows only dialogs need to be updated only once per frame so they are included here
UpdateCheatList();
UpdateTextHooker();
+ Update_RAM_Search(); // Update_RAM_Watch() is also called.
RamChange();
UpdateLogWindow();
//FCEUI_AviVideoUpdate(XBuf);
diff --git a/vc/vc8_fceux.vcproj b/vc/vc8_fceux.vcproj
index 27660cc6..ed31ed3f 100644
--- a/vc/vc8_fceux.vcproj
+++ b/vc/vc8_fceux.vcproj
@@ -1232,6 +1232,22 @@
RelativePath="..\src\drivers\win\pref.h"
>
+
+
+
+
+
+
+
+
diff --git a/vc/vc9_fceux.vcproj b/vc/vc9_fceux.vcproj
index f111cf1a..d72fede4 100644
--- a/vc/vc9_fceux.vcproj
+++ b/vc/vc9_fceux.vcproj
@@ -84,6 +84,7 @@
/>
+
+
+
+
+
+
+
+