diff --git a/PJ64.rc b/PJ64.rc
index dafdc1d..a80359d 100644
--- a/PJ64.rc
+++ b/PJ64.rc
@@ -36,6 +36,59 @@ IDB_BITMAP3 BITMAP "PJ64_3.bmp"
IDR_ABOUT_TXT TEXT "About.txt"
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog
+//
+
+IDD_MEM_BOOKMARK_EDIT DIALOGEX 0, 0, 239, 105
+STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Edit Bookmark"
+FONT 8, "MS Shell Dlg", 400, 0, 0x1
+BEGIN
+ DEFPUSHBUTTON "OK",IDOK,127,84,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,182,84,50,14
+ EDITTEXT IDC_NAME,61,7,171,12,ES_MULTILINE | ES_AUTOHSCROLL
+ EDITTEXT IDC_START,62,23,170,12,ES_AUTOHSCROLL
+ EDITTEXT IDC_END,62,39,170,12,ES_AUTOHSCROLL
+ CONTROL "Virtual Address",IDC_BOOKMARK_VADDR,"Button",BS_AUTORADIOBUTTON,62,54,170,10
+ CONTROL "Physical Address",IDC_BOOKMARK_PADDR,"Button",BS_AUTORADIOBUTTON,62,66,170,10
+ LTEXT "Name:",IDC_STATIC,7,9,49,11,0,WS_EX_RIGHT
+ LTEXT "Start Address:",IDC_STATIC,7,24,49,11,0,WS_EX_RIGHT
+ LTEXT "End Address:",IDC_STATIC,7,40,49,11,0,WS_EX_RIGHT
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// DESIGNINFO
+//
+
+#ifdef APSTUDIO_INVOKED
+GUIDELINES DESIGNINFO
+BEGIN
+ IDD_MEM_BOOKMARK_EDIT, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 232
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 98
+ END
+END
+#endif // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// AFX_DIALOG_LAYOUT
+//
+
+IDD_MEM_BOOKMARK_EDIT AFX_DIALOG_LAYOUT
+BEGIN
+ 0
+END
+
#endif // English (United States) resources
/////////////////////////////////////////////////////////////////////////////
@@ -744,6 +797,57 @@ END
#endif // APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// AFX_DIALOG_LAYOUT
+//
+
+IDD_Settings_Rom AFX_DIALOG_LAYOUT
+BEGIN
+ 0
+END
+
+IDD_Settings_Options AFX_DIALOG_LAYOUT
+BEGIN
+ 0
+END
+
+IDD_Cheats_CodeEx AFX_DIALOG_LAYOUT
+BEGIN
+ 0
+END
+
+IDD_About AFX_DIALOG_LAYOUT
+BEGIN
+ 0
+END
+
+MEMORY AFX_DIALOG_LAYOUT
+BEGIN
+ 0
+END
+
+ROM_INFO_DIALOG AFX_DIALOG_LAYOUT
+BEGIN
+ 0
+END
+
+IDD_Debugger_MemDump AFX_DIALOG_LAYOUT
+BEGIN
+ 0
+END
+
+BLANK AFX_DIALOG_LAYOUT
+BEGIN
+ 0
+END
+
+IDD_TLB AFX_DIALOG_LAYOUT
+BEGIN
+ 0
+END
+
+
/////////////////////////////////////////////////////////////////////////////
//
// Icon
@@ -798,7 +902,7 @@ BEGIN
MENUITEM "&Save\tF5", ID_CPU_SAVE, GRAYED
MENUITEM "Save As...\tCtrl+S", ID_CPU_SAVEAS, GRAYED
MENUITEM "&Restore\tF7", ID_CPU_RESTORE, GRAYED
- MENUITEM "Restore From\tCtrl+L", ID_CPU_LOAD, GRAYED
+ MENUITEM "Restore From\tCtrl+L", ID_CPU_LOAD, GRAYED
MENUITEM SEPARATOR
POPUP "Current Save S&tate", GRAYED
BEGIN
@@ -813,8 +917,6 @@ BEGIN
MENUITEM "Slot 7\t7", ID_CURRENTSAVE_7
MENUITEM "Slot 8\t8", ID_CURRENTSAVE_8
MENUITEM "Slot 9\t9", ID_CURRENTSAVE_9
- // Removed this as we now have 0 to 9 - Gent
- // MENUITEM "Slot 10\t0", ID_CURRENTSAVE_0
END
MENUITEM SEPARATOR
MENUITEM "Cheats...\tCtrl+C", ID_OPTIONS_CHEATS, GRAYED
@@ -981,8 +1083,6 @@ BEGIN
VK_F1, ID_CPU_RESET, VIRTKEY, NOINVERT
VK_F7, ID_CPU_RESTORE, VIRTKEY, NOINVERT
VK_F5, ID_CPU_SAVE, VIRTKEY, NOINVERT
- // Removed this as we now have 0 to 9 - Gent
- // "0", ID_CURRENTSAVE_0, VIRTKEY, NOINVERT
"1", ID_CURRENTSAVE_1, VIRTKEY, NOINVERT
"2", ID_CURRENTSAVE_2, VIRTKEY, NOINVERT
"3", ID_CURRENTSAVE_3, VIRTKEY, NOINVERT
@@ -1002,57 +1102,6 @@ BEGIN
VK_F4, ID_SYSTEM_LIMITFPS, VIRTKEY, NOINVERT
END
-
-/////////////////////////////////////////////////////////////////////////////
-//
-// AFX_DIALOG_LAYOUT
-//
-
-IDD_Settings_Rom AFX_DIALOG_LAYOUT
-BEGIN
- 0
-END
-
-IDD_Settings_Options AFX_DIALOG_LAYOUT
-BEGIN
- 0
-END
-
-IDD_Cheats_CodeEx AFX_DIALOG_LAYOUT
-BEGIN
- 0
-END
-
-IDD_About AFX_DIALOG_LAYOUT
-BEGIN
- 0
-END
-
-MEMORY AFX_DIALOG_LAYOUT
-BEGIN
- 0
-END
-
-ROM_INFO_DIALOG AFX_DIALOG_LAYOUT
-BEGIN
- 0
-END
-
-IDD_Debugger_MemDump AFX_DIALOG_LAYOUT
-BEGIN
- 0
-END
-
-BLANK AFX_DIALOG_LAYOUT
-BEGIN
- 0
-END
-
-IDD_TLB AFX_DIALOG_LAYOUT
-BEGIN
- 0
-END
-
#endif // English (Australia) resources
/////////////////////////////////////////////////////////////////////////////
diff --git a/Project64.vcxproj b/Project64.vcxproj
index 0797062..cb845b6 100644
--- a/Project64.vcxproj
+++ b/Project64.vcxproj
@@ -102,7 +102,7 @@
.\Debug\
.\Debug\
EnableFastChecks
- false
+ Sync
Default
false
true
@@ -285,6 +285,7 @@
+
@@ -321,6 +322,8 @@
+
+
@@ -354,6 +357,8 @@
+
+
diff --git a/Project64.vcxproj.filters b/Project64.vcxproj.filters
index e37b26c..7933a25 100644
--- a/Project64.vcxproj.filters
+++ b/Project64.vcxproj.filters
@@ -70,6 +70,9 @@
{e8877f6e-d284-4c15-84e8-18cf809faa07}
+
+ {c59b946f-43e7-4676-8b2b-840d70c41937}
+
@@ -279,6 +282,9 @@
Source Files\Debugger Source
+
+ Source Files
+
@@ -491,6 +497,18 @@
Header Files\Debugger Headers
+
+ Header Files\cbor-lite Headers
+
+
+ Header Files\cbor-lite Headers
+
+
+ Header Files
+
+
+ Header Files
+
diff --git a/SessionMemBookmarks.h b/SessionMemBookmarks.h
new file mode 100644
index 0000000..67fe8fc
--- /dev/null
+++ b/SessionMemBookmarks.h
@@ -0,0 +1,40 @@
+/*
+ * Project 64 - A Nintendo 64 emulator.
+ *
+ * (c) Copyright 2023 parasyte (jay@kodewerx.org)
+ *
+ * pj64 homepage: www.pj64.net
+ *
+ * Permission to use, copy, modify and distribute Project64 in both binary and
+ * source form, for non-commercial purposes, is hereby granted without fee,
+ * providing that this license information and copyright notice appear with
+ * all copies and any derived work.
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty. In no event shall the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Project64 is freeware for PERSONAL USE only. Commercial users should
+ * seek permission of the copyright holders first. Commercial use includes
+ * charging money for Project64 or software derived from Project64.
+ *
+ * The copyright holders request that bug fixes and improvements to the code
+ * should be forwarded to them so if they want them.
+ *
+ */
+
+#include
+
+#pragma once
+
+#define MAX_MEM_BOOKMARKS 1000
+
+struct MEM_BOOKMARK {
+ char name[100];
+ unsigned int width_required;
+ DWORD selection_range[2];
+ BOOL is_virtual;
+};
+
+BOOL Session_Load_MemBookmarks(unsigned int *num_bookmarks, struct MEM_BOOKMARK *bookmarks, unsigned int max_bookmarks, char *filename);
+BOOL Session_Save_MemBookmarks(char *filename, struct MEM_BOOKMARK *bookmarks, unsigned int num_bookmarks);
\ No newline at end of file
diff --git a/Sessions.cpp b/Sessions.cpp
new file mode 100644
index 0000000..52ce954
--- /dev/null
+++ b/Sessions.cpp
@@ -0,0 +1,205 @@
+/*
+ * Project 64 - A Nintendo 64 emulator.
+ *
+ * (c) Copyright 2023 parasyte (jay@kodewerx.org)
+ *
+ * pj64 homepage: www.pj64.net
+ *
+ * Permission to use, copy, modify and distribute Project64 in both binary and
+ * source form, for non-commercial purposes, is hereby granted without fee,
+ * providing that this license information and copyright notice appear with
+ * all copies and any derived work.
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty. In no event shall the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Project64 is freeware for PERSONAL USE only. Commercial users should
+ * seek permission of the copyright holders first. Commercial use includes
+ * charging money for Project64 or software derived from Project64.
+ *
+ * The copyright holders request that bug fixes and improvements to the code
+ * should be forwarded to them so if they want them.
+ *
+ */
+
+#include
+#include
+#include
+#include
+#include "cbor-lite/codec.h"
+
+using namespace CborLite;
+
+extern "C" {
+ #include "Sessions.h"
+}
+
+// Constants
+
+constexpr uint64_t SESSION_MEM_BOOKMARKS_VERSION = 1;
+
+// Utility functions
+
+std::string as_path(char *filename) {
+ auto filepath = std::string(filename);
+
+ // Support long file paths.
+ // SEE: https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea#parameters
+ if (filepath.substr(0, 4) != std::string(R"(\\?\)")) {
+ filepath.insert(0, R"(\\?\)");
+ }
+
+ return filepath;
+}
+
+bool read_cbor(std::vector &buffer, char *filename) {
+ auto filepath = as_path(filename);
+
+ HANDLE file = CreateFile(filepath.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (file == INVALID_HANDLE_VALUE) {
+ return false;
+ }
+
+ DWORD len = GetFileSize(file, NULL);
+ DWORD bytes_read = 0;
+
+ buffer.clear();
+ buffer.resize(len);
+ BOOL read_success = (ReadFile(file, buffer.data(), len, &bytes_read, NULL) == TRUE);
+
+ CloseHandle(file);
+
+ return read_success && bytes_read == len;
+}
+
+bool write_cbor(char *filename, std::vector &buffer) {
+ auto filepath = as_path(filename);
+
+ HANDLE file = CreateFile(filepath.c_str(), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (file == INVALID_HANDLE_VALUE) {
+ return false;
+ }
+
+ DWORD bytes_written;
+ BOOL write_success = (WriteFile(file, buffer.data(), buffer.size(), &bytes_written, NULL) == TRUE);
+
+ CloseHandle(file);
+
+ return write_success && bytes_written == buffer.size();
+}
+
+// C++ implementations
+
+template
+size_t decode_mem_bookmark(InputIterator &pos, InputIterator &end, struct MEM_BOOKMARK &bookmark, Flags flags = Flag::none) {
+ uint64_t num_items = 0;
+ auto len = decodeArraySize(pos, end, num_items, flags);
+ if (num_items != 5) {
+ throw Exception("expected 4 items");
+ }
+
+ auto name = std::string();
+ len += decodeText(pos, end, name, flags);
+ // Truncate name to fit in the buffer.
+ strncpy(bookmark.name, name.c_str(), min(name.size(), sizeof(bookmark.name) - 1));
+ bookmark.name[sizeof(bookmark.name) - 1] = 0;
+
+ uint64_t width_required = 0;
+ len += decodeUnsigned(pos, end, width_required, flags);
+ // Truncate 64-bit to 32-bit.
+ bookmark.width_required = (unsigned int)width_required;
+
+ uint64_t selection_range = 0;
+ len += decodeUnsigned(pos, end, selection_range, flags);
+ // Truncate 64-bit to 32-bit.
+ bookmark.selection_range[0] = (DWORD)selection_range;
+
+ len += decodeUnsigned(pos, end, selection_range, flags);
+ // Truncate 64-bit to 32-bit.
+ bookmark.selection_range[1] = (DWORD)selection_range;
+
+ bool is_virtual = true;
+ len += decodeBool(pos, end, is_virtual, flags);
+ // Convert bool to int.
+ bookmark.is_virtual = is_virtual;
+
+ return len;
+}
+
+template
+size_t encode_mem_bookmark(Buffer &buffer, const struct MEM_BOOKMARK &bookmark) {
+ auto len = encodeArraySize(buffer, 5u);
+
+ auto name = std::string(bookmark.name);
+ len += encodeText(buffer, name);
+ len += encodeUnsigned(buffer, bookmark.width_required);
+ len += encodeUnsigned(buffer, bookmark.selection_range[0]);
+ len += encodeUnsigned(buffer, bookmark.selection_range[1]);
+
+ bool is_virtual = bookmark.is_virtual;
+ len += encodeBool(buffer, is_virtual);
+
+ return len;
+}
+
+// C interfaces
+
+BOOL Session_Load_MemBookmarks(unsigned int *num_bookmarks, struct MEM_BOOKMARK *bookmarks, unsigned int max_bookmarks, char *filename) {
+ try {
+ auto buffer = std::vector();
+ if (!read_cbor(buffer, filename)) {
+ return FALSE;
+ }
+
+ auto pos = buffer.begin();
+ auto end = buffer.end();
+
+ // The version 1 schema looks like this:
+ // [1, ["name", width_required, selection_range_start, selection_range_end, is_virtual], ...]
+ //
+ // Example:
+ // [1,
+ // ["Off : Debug [0x800015C0]", 138, 2147489216, 2147489226, true],
+ // ["Play Track Options [0x80001584]", 173, 2147489156, 2147489173, true]
+ // ]
+ uint64_t num_items = 0;
+ decodeArraySize(pos, end, num_items);
+ if (num_items < 1 || num_items - 1 > max_bookmarks) {
+ return FALSE;
+ }
+ *num_bookmarks = (unsigned int)(num_items - 1);
+
+ uint64_t version = 0;
+ decodeUnsigned(pos, end, version);
+ if (version != SESSION_MEM_BOOKMARKS_VERSION) {
+ // NOTICE: Only version 1 is supported today.
+ return FALSE;
+ }
+
+ for (unsigned int i = 0; i < *num_bookmarks; i++) {
+ decode_mem_bookmark(pos, end, bookmarks[i]);
+ }
+
+ return TRUE;
+ } catch (Exception) {
+ return FALSE;
+ }
+}
+
+BOOL Session_Save_MemBookmarks(char *filename, struct MEM_BOOKMARK *bookmarks, unsigned int num_bookmarks) {
+ try {
+ auto buffer = std::vector();
+
+ encodeArraySize(buffer, num_bookmarks + 1);
+ encodeUnsigned(buffer, SESSION_MEM_BOOKMARKS_VERSION);
+
+ for (unsigned int i = 0; i < num_bookmarks; i++) {
+ encode_mem_bookmark(buffer, bookmarks[i]);
+ }
+
+ return write_cbor(filename, buffer);
+ } catch (Exception) {
+ return FALSE;
+ }
+}
\ No newline at end of file
diff --git a/Sessions.h b/Sessions.h
new file mode 100644
index 0000000..cc96800
--- /dev/null
+++ b/Sessions.h
@@ -0,0 +1,28 @@
+/*
+ * Project 64 - A Nintendo 64 emulator.
+ *
+ * (c) Copyright 2023 parasyte (jay@kodewerx.org)
+ *
+ * pj64 homepage: www.pj64.net
+ *
+ * Permission to use, copy, modify and distribute Project64 in both binary and
+ * source form, for non-commercial purposes, is hereby granted without fee,
+ * providing that this license information and copyright notice appear with
+ * all copies and any derived work.
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty. In no event shall the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Project64 is freeware for PERSONAL USE only. Commercial users should
+ * seek permission of the copyright holders first. Commercial use includes
+ * charging money for Project64 or software derived from Project64.
+ *
+ * The copyright holders request that bug fixes and improvements to the code
+ * should be forwarded to them so if they want them.
+ *
+ */
+
+#pragma once
+
+#include "SessionMemBookmarks.h"
\ No newline at end of file
diff --git a/Settings Common Defines.h b/Settings Common Defines.h
index fe2853e..5ce6410 100644
--- a/Settings Common Defines.h
+++ b/Settings Common Defines.h
@@ -5,7 +5,6 @@
#define RDN_NAME "Project64.rdn"
#define RDI_NAME "Project64.rdi"
#define CDB_NAME "Project64.cdb"
-#define CHTDEV_NAME "Project64.chtdev"
#define LNG_NAME "Project64.lng"
#define ROC_NAME "Project64.roc"
#define APPS_NAME "Project64.apps"
diff --git a/cbor-lite/COPYRIGHT.md b/cbor-lite/COPYRIGHT.md
new file mode 100644
index 0000000..0236663
--- /dev/null
+++ b/cbor-lite/COPYRIGHT.md
@@ -0,0 +1,52 @@
+Copyright
+=========
+
+This work is copyright [Isode Limited](https://isode.com) and made available as follows:
+
+```
+Copyright 2018-2019 Isode Limited.
+All rights reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+```
+
+Contributors
+------------
+
+None (yet). See [Contributing Guidelines](./CONTRIBUTING.md).
+
+Acknowledgements
+----------------
+
+This work is an implementation of the [Concise Binary Object Representation](https://cbor.io) (CBOR) as specified in [RFC 7049](https://tools.ietf.org/html/rfc7049), authored by C. Bormann and P. Hoffman and is a product of the [IETF](https://ietf.org). This work may quote portions of this technical specification. It also incorporates test cases published in this technical specification. RFC 7049 is published with the following notice:
+
+```
+Copyright (c) 2013 IETF Trust and the persons identified as the
+document authors. All rights reserved.
+
+This document is subject to BCP 78 and the IETF Trust's Legal
+Provisions Relating to IETF Documents
+(http://trustee.ietf.org/license-info) in effect on the date of
+publication of this document. Please review these documents
+carefully, as they describe your rights and restrictions with respect
+to this document. Code Components extracted from this document must
+include Simplified BSD License text as described in Section 4.e of
+the Trust Legal Provisions and are provided without warranty as
+described in the Simplified BSD License.
+```
diff --git a/cbor-lite/codec-fp.h b/cbor-lite/codec-fp.h
new file mode 100644
index 0000000..bc6494b
--- /dev/null
+++ b/cbor-lite/codec-fp.h
@@ -0,0 +1,145 @@
+#pragma once
+// This file is part of CBOR-lite which is copyright Isode Limited
+// and others and released under a MIT license. For details, see the
+// COPYRIGHT.md file in the top-level folder of the CBOR-lite software
+// distribution.
+
+// This copy of CBOR-lite has been modified for 32-bit MSVC for Project 64 Legacy.
+
+#include "codec.h"
+
+#ifndef __BYTE_ORDER__
+#ifdef _MSC_VER
+// PJ64: Byte order hack for MSVC
+#define __ORDER_LITTLE_ENDIAN__ 0
+#define __ORDER_BIG_ENDIAN__ 1
+#define __BYTE_ORDER__ __ORDER_LITTLE_ENDIAN__
+#else
+#error __BYTE_ORDER__ not defined
+#endif
+#elif (__BYTE_ORDER__ != __ORDER_LITTLE_ENDIAN__) && (__BYTE_ORDER__ != __ORDER_BIG_ENDIAN__)
+#error __BYTE_ORDER__ neither __ORDER_BIG_ENDIAN__ nor __ORDER_LITTLE_ENDIAN__
+#endif
+
+namespace CborLite {
+
+template
+typename std::enable_if::value && std::is_floating_point::value, std::size_t>::type encodeSingleFloat(
+ Buffer& buffer, const Type& t) {
+ static_assert(sizeof(float) == 4, "sizeof(float) expected to be 4");
+ auto len = encodeTagAndAdditional(buffer, Major::floatingPoint, Minor::singleFloat);
+ const char* p;
+ float ft;
+ if (sizeof(t) == sizeof(ft)) {
+ p = reinterpret_cast(&t);
+ } else {
+ ft = static_cast(t);
+ p = reinterpret_cast(&ft);
+ }
+#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+ for (auto i = 0u; i < sizeof(ft); ++i) {
+ buffer.push_back(p[i]);
+ }
+#else
+ for (auto i = 1u; i <= sizeof(ft); ++i) {
+ buffer.push_back(p[sizeof(ft) - i]);
+ }
+#endif
+ return len + sizeof(ft);
+}
+
+template
+typename std::enable_if::value && std::is_floating_point::value, std::size_t>::type encodeDoubleFloat(
+ Buffer& buffer, const Type& t) {
+ static_assert(sizeof(double) == 8, "sizeof(double) expected to be 8");
+ auto len = encodeTagAndAdditional(buffer, Major::floatingPoint, Minor::doubleFloat);
+ const char* p;
+ double ft;
+ if (sizeof(t) == sizeof(ft)) {
+ p = reinterpret_cast(&t);
+ } else {
+ ft = t;
+ p = reinterpret_cast(&ft);
+ }
+#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+ for (auto i = 0u; i < sizeof(ft); ++i) {
+ buffer.push_back(p[i]);
+ }
+#else
+ for (auto i = 1u; i <= sizeof(ft); ++i) {
+ buffer.push_back(p[sizeof(ft) - i]);
+ }
+#endif
+ return len + sizeof(ft);
+}
+
+template
+typename std::enable_if::value && std::is_floating_point::value && !std::is_const::value,
+ std::size_t>::type
+decodeSingleFloat(InputIterator& pos, InputIterator end, Type& t, Flags flags = Flag::none) {
+ static_assert(sizeof(float) == 4, "sizeof(float) expected to be 4");
+ auto tag = undefined;
+ auto value = undefined;
+ auto len = decodeTagAndAdditional(pos, end, tag, value, flags);
+ if (tag != Major::floatingPoint) throw Exception("not floating-point");
+ if (value != Minor::singleFloat) throw Exception("not single-precision floating-point");
+ if (std::distance(pos, end) < static_cast(sizeof(float))) throw Exception("not enough input");
+
+ char* p;
+ float ft;
+ if (sizeof(t) == sizeof(ft)) {
+ p = reinterpret_cast(&t);
+ } else {
+ ft = static_cast(t);
+ p = reinterpret_cast(&ft);
+ }
+
+#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+ for (auto i = 0u; i < sizeof(ft); ++i) {
+ p[i] = *(pos++);
+ }
+#else
+ for (auto i = 1u; i <= sizeof(ft); ++i) {
+ p[sizeof(ft) - i] = *(pos++);
+ }
+#endif
+ if (sizeof(t) != sizeof(ft)) t = ft;
+ return len + sizeof(ft);
+}
+
+template
+typename std::enable_if::value && std::is_floating_point::value && !std::is_const::value,
+ std::size_t>::type
+decodeDoubleFloat(InputIterator& pos, InputIterator end, Type& t, Flags flags = Flag::none) {
+ static_assert(sizeof(double) == 8, "sizeof(double) expected to be 8");
+ auto tag = undefined;
+ auto value = undefined;
+ auto len = decodeTagAndAdditional(pos, end, tag, value, flags);
+ if (tag != Major::floatingPoint) throw Exception("not floating-point");
+ if (value != Minor::doubleFloat) throw Exception("not double-precision floating-point");
+ if (std::distance(pos, end) < static_cast(sizeof(double))) throw Exception("not enough input");
+
+ char* p;
+ double ft;
+ if (sizeof(t) == sizeof(ft)) {
+ p = reinterpret_cast(&t);
+ } else {
+ ft = t;
+ p = reinterpret_cast(&ft);
+ }
+
+#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+ for (auto i = 0u; i < sizeof(ft); ++i) {
+ p[i] = *(pos++);
+ }
+#else
+ for (auto i = 1u; i <= sizeof(ft); ++i) {
+ p[sizeof(ft) - i] = *(pos++);
+ }
+#endif
+
+ if (sizeof(t) != sizeof(ft)) t = ft;
+ return len + sizeof(ft);
+}
+
+} // namespace CborLite
diff --git a/cbor-lite/codec.h b/cbor-lite/codec.h
new file mode 100644
index 0000000..79780fc
--- /dev/null
+++ b/cbor-lite/codec.h
@@ -0,0 +1,498 @@
+#pragma once
+// This file is part of CBOR-lite which is copyright Isode Limited
+// and others and released under a MIT license. For details, see the
+// COPYRIGHT.md file in the top-level folder of the CBOR-lite software
+// distribution.
+
+#include
+#include
+#include
+#include
+
+namespace CborLite {
+
+ class Exception : public std::exception {
+ public:
+ Exception() noexcept {
+ }
+ virtual ~Exception() noexcept = default;
+
+ Exception(const char *d) noexcept {
+ what_ += std::string(": ") + d;
+ }
+
+ Exception(const std::string &d) noexcept {
+ what_ += ": " + d;
+ }
+
+ Exception(const Exception &e) noexcept : what_(e.what_) {
+ }
+
+ Exception(Exception &&e) noexcept : what_(std::move(e.what_)) {
+ // Note that e.what_ is not re-initialized to "CBOR Exception" as
+ // the moved-from object is not expected to ever be reused.
+ }
+
+ Exception &operator=(const Exception &) = delete;
+ Exception &operator=(Exception &&) = delete;
+
+ virtual const char *what() const noexcept {
+ return what_.c_str();
+ }
+
+ private:
+ std::string what_ = "CBOR Exception";
+ };
+
+ using Tag = std::uint_fast64_t;
+
+ namespace Major {
+ constexpr Tag unsignedInteger = 0u;
+ constexpr Tag negativeInteger = 1u << 5;
+ constexpr Tag byteString = 2u << 5;
+ constexpr Tag textString = 3u << 5;
+ constexpr Tag array = 4u << 5;
+ constexpr Tag map = 5u << 5;
+ constexpr Tag semantic = 6u << 5;
+ constexpr Tag floatingPoint = 7u << 5;
+ constexpr Tag simple = 7u << 5;
+ constexpr Tag mask = 0xe0u;
+ } // namespace Major
+
+ namespace Minor {
+ constexpr Tag length1 = 24u;
+ constexpr Tag length2 = 25u;
+ constexpr Tag length4 = 26u;
+ constexpr Tag length8 = 27u;
+
+ constexpr Tag False = 20u;
+ constexpr Tag True = 21u;
+ constexpr Tag null = 22u;
+ constexpr Tag undefined = 23u;
+ constexpr Tag halfFloat = 25u; // not implemented
+ constexpr Tag singleFloat = 26u;
+ constexpr Tag doubleFloat = 27u;
+
+ constexpr Tag dataTime = 0u;
+ constexpr Tag epochDataTime = 1u;
+ constexpr Tag positiveBignum = 2u;
+ constexpr Tag negativeBignum = 3u;
+ constexpr Tag decimalFraction = 4u;
+ constexpr Tag bigfloat = 5u;
+ constexpr Tag convertBase64Url = 21u;
+ constexpr Tag convertBase64 = 22u;
+ constexpr Tag convertBase16 = 23u;
+ constexpr Tag cborEncodedData = 24u;
+ constexpr Tag uri = 32u;
+ constexpr Tag base64Url = 33u;
+ constexpr Tag base64 = 34u;
+ constexpr Tag regex = 35u;
+ constexpr Tag mimeMessage = 36u;
+ constexpr Tag selfDescribeCbor = 55799u;
+
+ constexpr Tag mask = 0x1fu;
+ } // namespace Minor
+
+ constexpr Tag undefined = Major::semantic + Minor::undefined;
+
+ using Flags = unsigned;
+ namespace Flag {
+ constexpr Flags none = 0;
+ constexpr Flags requireMinimalEncoding = 1 << 0;
+ } // namespace Flag
+
+ template
+ typename std::enable_if::value, std::size_t>::type length(Type val) {
+ if (val < 24) return 0;
+ for (std::size_t i = 1; i <= ((sizeof val) >> 1); i <<= 1) {
+ if (!(val >> (i << 3))) return i;
+ }
+ return sizeof val;
+ }
+
+ template
+ typename std::enable_if::value, std::size_t>::type encodeTagAndAdditional(
+ Buffer &buffer, Tag tag, Tag additional) {
+ buffer.push_back(static_cast(tag + additional));
+ return 1;
+ }
+
+ template
+ typename std::enable_if::value, std::size_t>::type decodeTagAndAdditional(
+ InputIterator &pos, InputIterator end, Tag &tag, Tag &additional, Flags = Flag::none) {
+ if (pos == end) throw Exception("not enough input");
+ auto octet = *(pos++);
+ tag = octet & Major::mask;
+ additional = octet & Minor::mask;
+ return 1;
+ }
+
+ // PJ64: Added type specialization for 64-bit values
+ template
+ typename std::enable_if::value && std::is_unsigned::value && sizeof(Type) == 8, std::size_t>::type encodeTagAndValue(
+ Buffer &buffer, Tag tag, const Type t) {
+ auto len = length(t);
+ buffer.reserve(buffer.size() + len + 1);
+
+ switch (len) {
+ case 8:
+ encodeTagAndAdditional(buffer, tag, Minor::length8);
+ break;
+ case 4:
+ encodeTagAndAdditional(buffer, tag, Minor::length4);
+ break;
+ case 2:
+ encodeTagAndAdditional(buffer, tag, Minor::length2);
+ break;
+ case 1:
+ encodeTagAndAdditional(buffer, tag, Minor::length1);
+ break;
+ case 0:
+ return encodeTagAndAdditional(buffer, tag, t);
+ default:
+ throw Exception("too long");
+ }
+
+ switch (len) {
+ case 8:
+ buffer.push_back((t >> 56) & 0xffU);
+ buffer.push_back((t >> 48) & 0xffU);
+ buffer.push_back((t >> 40) & 0xffU);
+ buffer.push_back((t >> 32) & 0xffU);
+ case 4:
+ buffer.push_back((t >> 24) & 0xffU);
+ buffer.push_back((t >> 16) & 0xffU);
+ case 2:
+ buffer.push_back((t >> 8) & 0xffU);
+ case 1:
+ buffer.push_back(t & 0xffU);
+ }
+
+ return 1 + len;
+ }
+
+ // PJ64: Added type specialization for 32-bit values
+ template
+ typename std::enable_if::value && std::is_unsigned::value && sizeof(Type) == 4, std::size_t>::type encodeTagAndValue(
+ Buffer &buffer, Tag tag, const Type t) {
+ auto len = length(t);
+ buffer.reserve(buffer.size() + len + 1);
+
+ switch (len) {
+ case 4:
+ encodeTagAndAdditional(buffer, tag, Minor::length4);
+ break;
+ case 2:
+ encodeTagAndAdditional(buffer, tag, Minor::length2);
+ break;
+ case 1:
+ encodeTagAndAdditional(buffer, tag, Minor::length1);
+ break;
+ case 0:
+ return encodeTagAndAdditional(buffer, tag, t);
+ default:
+ throw Exception("too long");
+ }
+
+ switch (len) {
+ case 4:
+ buffer.push_back((t >> 24) & 0xffU);
+ buffer.push_back((t >> 16) & 0xffU);
+ case 2:
+ buffer.push_back((t >> 8) & 0xffU);
+ case 1:
+ buffer.push_back(t & 0xffU);
+ }
+
+ return 1 + len;
+ }
+
+ // PJ64: Added type specialization for 64-bit values
+ template
+ typename std::enable_if::value && std::is_unsigned::value && sizeof(Type) == 8, std::size_t>::type decodeTagAndValue(
+ InputIterator &pos, InputIterator end, Tag &tag, Type &t, Flags flags = Flag::none) {
+ if (pos == end) throw Exception("not enough input");
+ auto additional = Minor::undefined;
+ auto len = decodeTagAndAdditional(pos, end, tag, additional, flags);
+ if (additional < Minor::length1) {
+ t = additional;
+ return len;
+ }
+ t = 0u;
+ switch (additional) {
+ case Minor::length8:
+ if (std::distance(pos, end) < 8) throw Exception("not enough input");
+ t |= static_cast(reinterpret_cast(*(pos++))) << 56;
+ t |= static_cast(reinterpret_cast(*(pos++))) << 48;
+ t |= static_cast(reinterpret_cast(*(pos++))) << 40;
+ t |= static_cast(reinterpret_cast(*(pos++))) << 32;
+ len += 4;
+ if ((flags & Flag::requireMinimalEncoding) && !t) throw Exception("encoding not minimal");
+ case Minor::length4:
+ if (std::distance(pos, end) < 4) throw Exception("not enough input");
+ t |= static_cast(reinterpret_cast(*(pos++))) << 24;
+ t |= static_cast(reinterpret_cast(*(pos++))) << 16;
+ len += 2;
+ if ((flags & Flag::requireMinimalEncoding) && !t) throw Exception("encoding not minimal");
+ case Minor::length2:
+ if (std::distance(pos, end) < 2) throw Exception("not enough input");
+ t |= static_cast(reinterpret_cast(*(pos++))) << 8;
+ len++;
+ if ((flags & Flag::requireMinimalEncoding) && !t) throw Exception("encoding not minimal");
+ case Minor::length1:
+ if (std::distance(pos, end) < 1) throw Exception("not enough input");
+ t |= static_cast(reinterpret_cast(*(pos++)));
+ len++;
+ if ((flags & Flag::requireMinimalEncoding) && t < 24) throw Exception("encoding not minimal");
+ return len;
+ }
+ throw Exception("bad additional value");
+ }
+
+ // PJ64: Added type specialization for 32-bit values
+ template
+ typename std::enable_if::value && std::is_unsigned::value && sizeof(Type) == 4, std::size_t>::type decodeTagAndValue(
+ InputIterator &pos, InputIterator end, Tag &tag, Type &t, Flags flags = Flag::none) {
+ if (pos == end) throw Exception("not enough input");
+ auto additional = Minor::undefined;
+ auto len = decodeTagAndAdditional(pos, end, tag, additional, flags);
+ if (additional < Minor::length1) {
+ t = additional;
+ return len;
+ }
+ t = 0u;
+ switch (additional) {
+ case Minor::length4:
+ if (std::distance(pos, end) < 4) throw Exception("not enough input");
+ t |= static_cast(reinterpret_cast(*(pos++))) << 24;
+ t |= static_cast(reinterpret_cast(*(pos++))) << 16;
+ len += 2;
+ if ((flags & Flag::requireMinimalEncoding) && !t) throw Exception("encoding not minimal");
+ case Minor::length2:
+ if (std::distance(pos, end) < 2) throw Exception("not enough input");
+ t |= static_cast(reinterpret_cast(*(pos++))) << 8;
+ len++;
+ if ((flags & Flag::requireMinimalEncoding) && !t) throw Exception("encoding not minimal");
+ case Minor::length1:
+ if (std::distance(pos, end) < 1) throw Exception("not enough input");
+ t |= static_cast(reinterpret_cast(*(pos++)));
+ len++;
+ if ((flags & Flag::requireMinimalEncoding) && t < 24) throw Exception("encoding not minimal");
+ return len;
+ }
+ throw Exception("bad additional value");
+ }
+
+ template
+ typename std::enable_if::value, std::size_t>::type encodeUnsigned(Buffer &buffer, const Type &t) {
+ return encodeTagAndValue(buffer, Major::unsignedInteger, t);
+ }
+
+ template
+ typename std::enable_if::value &&std::is_unsigned::value && !std::is_const::value,
+ std::size_t>::type
+ decodeUnsigned(InputIterator &pos, InputIterator end, Type &t, Flags flags = Flag::none) {
+ auto tag = undefined;
+ auto len = decodeTagAndValue(pos, end, tag, t, flags);
+ if (tag != Major::unsignedInteger) throw Exception("not Unsigned");
+ return len;
+ }
+
+ template
+ typename std::enable_if::value, std::size_t>::type encodeNegative(Buffer &buffer, const Type &t) {
+ return encodeTagAndValue(buffer, Major::negativeInteger, t);
+ }
+
+ template
+ typename std::enable_if::value &&std::is_unsigned::value && !std::is_const::value,
+ std::size_t>::type
+ decodeNegative(InputIterator &pos, InputIterator end, Type &t, Flags flags = Flag::none) {
+ auto tag = undefined;
+ auto len = decodeTagAndValue(pos, end, tag, t, flags);
+ if (tag != Major::negativeInteger) throw Exception("not Unsigned");
+ return len;
+ }
+
+ template
+ typename std::enable_if::value, std::size_t>::type encodeInteger(Buffer &buffer, const Type &t) {
+ if (t >= 0) {
+ unsigned long long val = t;
+ return encodeUnsigned(buffer, val);
+ } else {
+ unsigned long long val = -t - 1;
+ return encodeNegative(buffer, val);
+ }
+ }
+
+ template
+ typename std::enable_if::value &&std::is_signed::value && !std::is_const::value,
+ std::size_t>::type
+ decodeInteger(InputIterator &pos, InputIterator end, Type &t, Flags flags = Flag::none) {
+ auto tag = undefined;
+ unsigned long long val;
+ auto len = decodeTagAndValue(pos, end, tag, val, flags);
+ switch (tag) {
+ case Major::unsignedInteger:
+ t = val;
+ break;
+ case Major::negativeInteger:
+ t = -1 - static_cast(val);
+ break;
+ default:
+ throw Exception("not integer");
+ }
+ return len;
+ }
+
+ template
+ typename std::enable_if::value &&std::is_same::value, std::size_t>::type encodeBool(
+ Buffer &buffer, const Type &t) {
+ return encodeTagAndAdditional(buffer, Major::simple, t ? Minor::True : Minor::False);
+ }
+
+ template
+ typename std::enable_if::value &&std::is_same::value && !std::is_const::value,
+ std::size_t>::type
+ decodeBool(InputIterator &pos, InputIterator end, Type &t, Flags flags = Flag::none) {
+ auto tag = undefined;
+ auto value = undefined;
+ auto len = decodeTagAndValue(pos, end, tag, value, flags);
+ if (tag == Major::simple) {
+ if (value == Minor::True) {
+ t = true;
+ return len;
+ } else if (value == Minor::False) {
+ t = false;
+ return len;
+ }
+ throw Exception("not Boolean");
+ }
+ throw Exception("not Simple");
+ }
+
+ template
+ typename std::enable_if::value, std::size_t>::type encodeBytes(Buffer &buffer, const Type &t) {
+ auto len = encodeTagAndValue(buffer, Major::byteString, t.size());
+ buffer.insert(std::end(buffer), std::begin(t), std::end(t));
+ return len + t.size();
+ }
+
+ template
+ typename std::enable_if::value && !std::is_const::value, std::size_t>::type decodeBytes(
+ InputIterator &pos, InputIterator end, Type &t, Flags flags = Flag::none) {
+ auto tag = undefined;
+ auto value = undefined;
+ auto len = decodeTagAndValue(pos, end, tag, value, flags);
+ if (tag != Major::byteString) throw Exception("not ByteString");
+
+ auto dist = std::distance(pos, end);
+ if (dist < static_cast(value)) throw Exception("not enough input");
+ t.insert(std::end(t), pos, pos + value);
+ std::advance(pos, value);
+ return len + value;
+ }
+
+ template
+ typename std::enable_if::value &&std::is_unsigned::value, std::size_t>::type encodeEncodedBytesPrefix(
+ Buffer &buffer, const Type &t) {
+ auto len = encodeTagAndValue(buffer, Major::semantic, Minor::cborEncodedData);
+ return len + encodeTagAndValue(buffer, Major::byteString, t);
+ }
+
+ template
+ typename std::enable_if::value && !std::is_const::value, std::size_t>::type
+ decodeEncodedBytesPrefix(InputIterator &pos, InputIterator end, Type &t, Flags flags = Flag::none) {
+ auto tag = undefined;
+ auto value = undefined;
+ auto len = decodeTagAndValue(pos, end, tag, value, flags);
+ if (tag != Major::semantic || value != Minor::cborEncodedData) {
+ throw Exception("not CBOR Encoded Data");
+ }
+ tag = undefined;
+ len += decodeTagAndValue(pos, end, tag, value, flags);
+ if (tag != Major::byteString) throw Exception("not ByteString");
+ t = value;
+ return len;
+ }
+
+ template
+ typename std::enable_if::value, std::size_t>::type encodeEncodedBytes(Buffer &buffer, const Type &t) {
+ auto len = encodeTagAndValue(buffer, Major::semantic, Minor::cborEncodedData);
+ return len + encodeBytes(buffer, t);
+ }
+
+ template
+ typename std::enable_if::value && !std::is_const::value, std::size_t>::type decodeEncodedBytes(
+ InputIterator &pos, InputIterator end, Type &t, Flags flags = Flag::none) {
+ auto tag = undefined;
+ auto value = undefined;
+ auto len = decodeTagAndValue(pos, end, tag, value, flags);
+ if (tag != Major::semantic || value != Minor::cborEncodedData) {
+ throw Exception("not CBOR Encoded Data");
+ }
+ return len + decodeBytes(pos, end, t, flags);
+ }
+
+ template
+ typename std::enable_if::value, std::size_t>::type encodeText(Buffer &buffer, const Type &t) {
+ auto len = encodeTagAndValue(buffer, Major::textString, t.size());
+ buffer.insert(std::end(buffer), std::begin(t), std::end(t));
+ return len + t.size();
+ }
+
+ template
+ typename std::enable_if::value && !std::is_const::value, std::size_t>::type decodeText(
+ InputIterator &pos, InputIterator end, Type &t, Flags flags = Flag::none) {
+ auto tag = undefined;
+ auto value = undefined;
+ auto len = decodeTagAndValue(pos, end, tag, value, flags);
+ if (tag != Major::textString) throw Exception("not TextString");
+
+ auto dist = std::distance(pos, end);
+ if (dist < static_cast(value)) throw Exception("not enough input");
+ // PJ64: 32-bit plaform support
+ if (sizeof(size_t) == 4 && value > UINT32_MAX) {
+ throw Exception("TextString too long for 32-bit platforms");
+ }
+ t.insert(std::end(t), pos, pos + (size_t)value);
+ std::advance(pos, (size_t)value);
+ return (size_t)(len + value);
+ }
+
+ template
+ typename std::enable_if::value &&std::is_unsigned::value, std::size_t>::type encodeArraySize(
+ Buffer &buffer, const Type &t) {
+ return encodeTagAndValue(buffer, Major::array, t);
+ }
+
+ template
+ typename std::enable_if::value && !std::is_const::value &&std::is_unsigned::value,
+ std::size_t>::type
+ decodeArraySize(InputIterator &pos, InputIterator end, Type &t, Flags flags = Flag::none) {
+ auto tag = undefined;
+ auto value = undefined;
+ auto len = decodeTagAndValue(pos, end, tag, value, flags);
+ if (tag != Major::array) throw Exception("not Array");
+ t = value;
+ return len;
+ }
+
+ template
+ typename std::enable_if::value &&std::is_unsigned::value, std::size_t>::type encodeMapSize(
+ Buffer &buffer, const Type &t) {
+ return encodeTagAndValue(buffer, Major::map, t);
+ }
+
+ template
+ typename std::enable_if::value && !std::is_const::value &&std::is_unsigned::value,
+ std::size_t>::type
+ decodeMapSize(InputIterator &pos, InputIterator end, Type &t, Flags flags = Flag::none) {
+ auto tag = undefined;
+ auto value = undefined;
+ auto len = decodeTagAndValue(pos, end, tag, value, flags);
+ if (tag != Major::map) throw Exception("not Map");
+ t = value;
+ return len;
+ }
+
+} // namespace CborLite
\ No newline at end of file
diff --git a/r4300i Memory.c b/r4300i Memory.c
index a19bbeb..4b3ac07 100644
--- a/r4300i Memory.c
+++ b/r4300i Memory.c
@@ -25,62 +25,133 @@
*/
#include
+#include
+#include
#include
#include
#include "main.h"
#include "CPU.h"
#include "debugger.h"
#include "resource.h"
+#include "rom.h"
+#include "SessionMemBookmarks.h"
#define IDC_VADDR 0x100
#define IDC_PADDR 0x101
#define IDC_LIST_VIEW 0x102
#define IDC_SCRL_BAR 0x103
#define IDC_REFRESH 0x104
+#define IDC_BOOKMARKS 0x105
+#define IDC_BOOKMARK_ADD 0x106
+#define IDC_BOOKMARK_UPDATE 0x107
+#define IDC_BOOKMARK_REMOVE 0x108
+
+// TODO: Figure out if these can be queried instead of hardcoded
+#define PADDING 6
+#define CHAR_WIDTH 8
void Setup_Memory_Window (HWND hDlg);
void Start_Auto_Refresh_Thread(void);
void Scroll_Memory_View(int lines);
void Refresh_Memory_With_Diff(BOOL ShowDiff);
-
-LRESULT CALLBACK Memory_Window_Proc (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
+void Clear_Selection(void);
+void Copy_Selection(void);
+int Get_Ascii_Index(POINTS pt);
+LRESULT Change_Bookmark_Selection(int next);
+unsigned int Get_Bookmark_Name_Width(char *name);
+void Update_Bookmark_Width(void);
+void Add_Bookmark(void);
+void Edit_Bookmark(void);
+void Update_Bookmark(unsigned int item);
+void Remove_Bookmark(unsigned int item);
+void Load_Bookmark(unsigned int item);
+
+LRESULT CALLBACK Memory_Window_Proc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
struct MEMORY_VIEW_ROW {
unsigned char OldData[16];
COLORREF TextColors[16];
HFONT Fonts[16];
+ DWORD Location;
char LocationStr[11];
char HexStr[16][3];
char AsciiStr[17];
};
+struct SELECTION {
+ BOOL enabled;
+ BOOL dragging;
+ BOOL column_hex;
+ DWORD anchor;
+ DWORD range[2];
+ DWORD range_cmp[2];
+};
+
const COLORREF TC_INC = RGB(0, 180, 0); // Value increased
const COLORREF TC_DEC = RGB(180, 0, 0); // Value decreased
const COLORREF TC_READ = RGB(0, 0, 255); // Address has read watchpoint
const COLORREF TC_WRITE = RGB(255, 0, 255); // Address has write watchpoint
const COLORREF TC_READ_WRITE = RGB(128, 0, 255); // Address has read/write watchpoint
-const COLORREF TC_DEFAULT = RGB(0, 0, 0); // Default text color
const COLORREF BG_EVEN = RGB(240, 240, 240);
const COLORREF BG_ODD = RGB(230, 230, 230);
-const COLORREF BG_DEFAULT = RGB(255, 255, 255);
static HWND Memory_Win_hDlg, hAddrEdit, hVAddr, hPAddr, hRefresh, hList, hScrlBar;
-static HFONT hWatchFont;
+static HBRUSH hBkEven, hBkOdd;
+static HFONT hWatchFont, hDefaultFont;
static HANDLE hRefreshThread = NULL;
static HANDLE hRefreshMutex = NULL;
static int InMemoryWindow = FALSE;
static int wheel = 0;
static int thumb = -1;
static struct MEMORY_VIEW_ROW MemoryViewRows[16];
+static struct SELECTION selection = { 0 };
+static struct MEM_BOOKMARK bookmarks[MAX_MEM_BOOKMARKS] = { 0 };
+static unsigned int num_bookmarks = 0;
+static char bookmarks_cbor[MAX_PATH + 1] = { 0 };
void __cdecl Create_Memory_Window ( int Child ) {
DWORD ThreadID;
if ( Child ) {
+ hBkEven = CreateSolidBrush(BG_EVEN);
+ hBkOdd = CreateSolidBrush(BG_ODD);
+
+ // Create a font for the watchpoints within the hex editor
+ LOGFONT lf;
+ GetObject(GetStockObject(ANSI_FIXED_FONT), sizeof(lf), &lf);
+ lf.lfUnderline = TRUE;
+ hWatchFont = CreateFontIndirect(&lf);
+
+ // Create a default font that matches the system theme
+ NONCLIENTMETRICS metrics = { 0 };
+ metrics.cbSize = sizeof(NONCLIENTMETRICS);
+ if (!IsWindowsVistaOrGreater()) {
+ // NOTE: This is for compatibility with Windows XP.
+ metrics.cbSize -= sizeof(metrics.iPaddedBorderWidth);
+ }
+ SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICS), &metrics, 0);
+ hDefaultFont = CreateFontIndirect(&metrics.lfMessageFont);
+
+ // Load session bookmarks
+ char save_directory[MAX_PATH + 1] = { 0 };
+ Settings_GetDirectory(AutoSaveDir, save_directory, sizeof(save_directory));
+ if (strlen(save_directory) + strlen(RomFullName) + 27 <= sizeof(bookmarks_cbor)) {
+ sprintf(bookmarks_cbor, "%s%s.session.membookmarks.cbor", save_directory, RomFullName);
+ if (!Session_Load_MemBookmarks(&num_bookmarks, bookmarks, MAX_MEM_BOOKMARKS, bookmarks_cbor)) {
+ num_bookmarks = 0;
+ }
+ } else {
+ num_bookmarks = 0;
+ bookmarks_cbor[0] = 0;
+ }
+
InMemoryWindow = TRUE;
DialogBox( hInst, "MEMORY", NULL,(DLGPROC) Memory_Window_Proc );
- DeleteObject(hWatchFont);
InMemoryWindow = FALSE;
+
+ DeleteObject(hWatchFont);
+ DeleteObject(hBkOdd);
+ DeleteObject(hBkEven);
} else {
if (!InMemoryWindow) {
CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)Create_Memory_Window,
@@ -105,7 +176,7 @@ void Update_Data_Column(struct MEMORY_VIEW_ROW* row, MIPS_WORD word, int index,
} else if (ShowDiff && word.UB[3 - i] < row->OldData[index]) {
row->TextColors[index] = TC_DEC;
} else {
- row->TextColors[index] = TC_DEFAULT;
+ row->TextColors[index] = GetSysColor(COLOR_WINDOWTEXT);
}
row->OldData[index] = word.UB[3 - i];
@@ -114,17 +185,21 @@ void Update_Data_Column(struct MEMORY_VIEW_ROW* row, MIPS_WORD word, int index,
void Update_Data_Column_With_WatchPoint(struct MEMORY_VIEW_ROW* row, DWORD location, MIPS_WORD word, int index, int i, BOOL ShowDiff) {
sprintf(row->HexStr[index], "%02X", word.UB[3 - i]);
- switch (HasWatchPoint(location + i)) {
- case WP_READ:
+ int has_watch = (int)HasWatchPoint(location + i);
+ if (has_watch & WP_ENABLED) {
row->Fonts[index] = hWatchFont;
+ } else {
+ row->Fonts[index] = GetStockObject(ANSI_FIXED_FONT);
+ }
+
+ switch (has_watch & ~WP_ENABLED) {
+ case WP_READ:
row->TextColors[index] = TC_READ;
break;
case WP_WRITE:
- row->Fonts[index] = hWatchFont;
row->TextColors[index] = TC_WRITE;
break;
case WP_READ_WRITE:
- row->Fonts[index] = hWatchFont;
row->TextColors[index] = TC_READ_WRITE;
break;
default:
@@ -135,7 +210,7 @@ void Update_Data_Column_With_WatchPoint(struct MEMORY_VIEW_ROW* row, DWORD locat
} else if (ShowDiff && word.UB[3 - i] < row->OldData[index]) {
row->TextColors[index] = TC_DEC;
} else {
- row->TextColors[index] = TC_DEFAULT;
+ row->TextColors[index] = GetSysColor(COLOR_WINDOWTEXT);
}
break;
}
@@ -149,6 +224,7 @@ void Insert_MemoryLineDump (unsigned int location, int InsertPos, BOOL ShowDiff)
location <<= 4;
+ row->Location = location;
sprintf(row->LocationStr, "0x%08X", location);
__try {
@@ -163,8 +239,7 @@ void Insert_MemoryLineDump (unsigned int location, int InsertPos, BOOL ShowDiff)
word.UB[2] < ' ' || word.UB[2] > '~' ? '.' : word.UB[2],
word.UB[1] < ' ' || word.UB[1] > '~' ? '.' : word.UB[1],
word.UB[0] < ' ' || word.UB[0] > '~' ? '.' : word.UB[0]);
- }
- else {
+ } else {
for (int i = 0; i < 4; i++) {
int index = count * 4 + i;
@@ -189,8 +264,7 @@ void Insert_MemoryLineDump (unsigned int location, int InsertPos, BOOL ShowDiff)
word.UB[2] < ' ' || word.UB[2] > '~' ? '.' : word.UB[2],
word.UB[1] < ' ' || word.UB[1] > '~' ? '.' : word.UB[1],
word.UB[0] < ' ' || word.UB[0] > '~' ? '.' : word.UB[0]);
- }
- else {
+ } else {
for (int i = 0; i < 4; i++) {
int index = count * 4 + i;
@@ -210,7 +284,86 @@ void Insert_MemoryLineDump (unsigned int location, int InsertPos, BOOL ShowDiff)
}
}
-LRESULT CALLBACK Memory_Window_Proc (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
+void Write_MemoryLineDump(char *output, unsigned int location) {
+ MIPS_WORD word;
+
+ sprintf(output, "0x%08X ", location);
+ output += 12;
+
+ char bytes[49] = { 0 };
+ char ascii[17] = { 0 };
+ char *b = bytes;
+ char *a = ascii;
+ __try {
+ if (SendMessage(hVAddr, BM_GETSTATE, 0, 0) & BST_CHECKED) {
+ for (int count = 0; count < 4; count++) {
+ if (r4300i_LW_VAddr_NonCPU(location, &word.UW)) {
+ for (int i = 0; i < 4; i++) {
+ if (selection.enabled && (selection.range[0] > location + i || selection.range[1] < location + i)) {
+ strcpy(b, " ");
+ strcpy(a, " ");
+ } else {
+ sprintf(b, "%02X ", word.UB[3 - i]);
+ sprintf(a, "%c", (word.UB[3 - i] < ' ' || word.UB[3 - i] > '~') ? '.' : word.UB[3 - i]);
+ }
+ b += 3;
+ a++;
+ }
+ } else {
+ for (int i = 0; i < 4; i++) {
+ if (selection.enabled && (selection.range[0] > location + i || selection.range[1] < location + i)) {
+ strcpy(b, " ");
+ strcpy(a, " ");
+ } else {
+ strcpy(b, "** ");
+ strcpy(a, "*");
+ }
+ b += 3;
+ a++;
+ }
+ }
+ location += 4;
+ }
+ } else {
+ for (int count = 0; count < 4; count++) {
+ if (location < 0x1FFFFFFC) {
+ r4300i_LW_PAddr(location, &word.UW);
+ for (int i = 0; i < 4; i++) {
+ if (selection.enabled && (selection.range[0] > location + i || selection.range[1] < location + i)) {
+ strcpy(b, " ");
+ strcpy(a, " ");
+ } else {
+ sprintf(b, "%02X ", word.UB[3 - i]);
+ sprintf(a, "%c", (word.UB[3 - i] < ' ' || word.UB[3 - i] > '~') ? '.' : word.UB[3 - i]);
+ }
+ b += 3;
+ a++;
+ }
+ } else {
+ for (int i = 0; i < 4; i++) {
+ if (selection.enabled && (selection.range[0] > location + i || selection.range[1] < location + i)) {
+ strcpy(b, " ");
+ strcpy(a, " ");
+ } else {
+ strcpy(b, "** ");
+ strcpy(a, "*");
+ }
+ b += 3;
+ a++;
+ }
+ }
+ location += 4;
+ }
+ }
+ } __except( r4300i_Command_MemoryFilter( GetExceptionCode(), GetExceptionInformation()) ) {
+ DisplayError(GS(MSG_UNKNOWN_MEM_ACTION));
+ PostQuitMessage(0);
+ }
+
+ sprintf(output, "%s %s\r\n", bytes, ascii);
+}
+
+LRESULT CALLBACK Memory_Window_Proc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
PAINTSTRUCT ps;
RECT rcBox;
@@ -224,21 +377,28 @@ LRESULT CALLBACK Memory_Window_Proc (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM
StoreCurrentWinPos("Memory",hDlg);
break;
case WM_PAINT:
- BeginPaint( hDlg, &ps );
+ BeginPaint(hDlg, &ps);
+ SetBkMode(ps.hdc, TRANSPARENT);
+
+ SelectObject(ps.hdc, hDefaultFont);
+ TextOut(ps.hdc, 731, 15, "Bookmarks:", 10);
+
SelectObject(ps.hdc, GetStockObject(ANSI_FIXED_FONT));
- SetBkMode( ps.hdc, TRANSPARENT );
- TextOut(ps.hdc,25,17,"Address:",8);
+ TextOut(ps.hdc, 25, 17, "Address:", 8);
+
rcBox.left = 5;
rcBox.top = 5;
- rcBox.right = 721;
+ rcBox.right = 719;
+ rcBox.bottom = 348;
+ DrawEdge(ps.hdc, &rcBox, EDGE_ETCHED, BF_RECT);
+
+ rcBox.left = 724;
+ rcBox.top = 5;
+ rcBox.right = 891;
rcBox.bottom = 348;
- DrawEdge( ps.hdc, &rcBox, EDGE_RAISED, BF_RECT );
- rcBox.left = 8;
- rcBox.top = 8;
- rcBox.right = 718;
- rcBox.bottom = 345;
- DrawEdge( ps.hdc, &rcBox, EDGE_ETCHED, BF_RECT );
- EndPaint( hDlg, &ps );
+ DrawEdge(ps.hdc, &rcBox, EDGE_ETCHED, BF_RECT);
+
+ EndPaint(hDlg, &ps);
return TRUE;
case WM_COMMAND:
switch (LOWORD(wParam)) {
@@ -248,20 +408,60 @@ LRESULT CALLBACK Memory_Window_Proc (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM
}
break;
case IDC_VADDR:
- case IDC_PADDR:
+ case IDC_PADDR: {
+ char Value[20];
+ GetWindowText(hAddrEdit, Value, sizeof(Value));
+ DWORD address = AsciiToHex(Value);
+ if (LOWORD(wParam) == IDC_VADDR && address < 0x80000000 || address >= 0x80800000) {
+ SetWindowText(hAddrEdit, "80000000");
+ } else if (LOWORD(wParam) == IDC_PADDR && address >= 0x20000000) {
+ SetWindowText(hAddrEdit, "00000000");
+ }
+
+ Clear_Selection();
+ ListBox_SetCurSel(GetDlgItem(hDlg, IDC_BOOKMARKS), -1);
Refresh_Memory_With_Diff(FALSE);
break;
+ }
case IDC_REFRESH:
Start_Auto_Refresh_Thread();
Refresh_Memory_With_Diff(FALSE);
break;
case IDCANCEL:
- EndDialog( hDlg, IDCANCEL );
+ Clear_Selection();
+ break;
+ case IDC_BOOKMARKS:
+ if (HIWORD(wParam) == LBN_DBLCLK) {
+ Edit_Bookmark();
+ }
+ break;
+ case IDC_BOOKMARK_ADD:
+ Add_Bookmark();
+ break;
+ case IDC_BOOKMARK_UPDATE: {
+ int item = ListBox_GetCurSel(GetDlgItem(hDlg, IDC_BOOKMARKS));
+ if (item != LB_ERR) {
+ Update_Bookmark(item);
+ }
break;
+ }
+ case IDC_BOOKMARK_REMOVE: {
+ int item = ListBox_GetCurSel(GetDlgItem(hDlg, IDC_BOOKMARKS));
+ if (item != LB_ERR) {
+ Remove_Bookmark(item);
+ }
+ break;
+ }
default:
break;
}
return FALSE;
+ case WM_SYSCOMMAND:
+ if (wParam == SC_CLOSE) {
+ EndDialog(hDlg, TRUE);
+ return TRUE;
+ }
+ return FALSE;
case WM_NOTIFY:
switch (((LPNMHDR)lParam)->code) {
case HDN_BEGINTRACK:
@@ -296,6 +496,7 @@ LRESULT CALLBACK Memory_Window_Proc (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM
}
case NM_CUSTOMDRAW: {
LPNMLVCUSTOMDRAW lplvcd = (LPNMLVCUSTOMDRAW)lParam;
+
switch (lplvcd->nmcd.dwDrawStage) {
case CDDS_PREPAINT:
SetWindowLong(hDlg, DWL_MSGRESULT, CDRF_NOTIFYITEMDRAW);
@@ -303,38 +504,138 @@ LRESULT CALLBACK Memory_Window_Proc (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM
case CDDS_ITEMPREPAINT:
SetWindowLong(hDlg, DWL_MSGRESULT, CDRF_NOTIFYSUBITEMDRAW);
return TRUE;
- case CDDS_ITEMPREPAINT | CDDS_SUBITEM:
- if (lplvcd->iSubItem >= 1 && lplvcd->iSubItem < 17) {
+ case CDDS_ITEMPREPAINT | CDDS_SUBITEM: {
+ struct MEMORY_VIEW_ROW* row = &MemoryViewRows[lplvcd->nmcd.dwItemSpec];
+
+ switch (lplvcd->iSubItem) {
+ case 0:
+ // Address column
+ SetWindowLong(hDlg, DWL_MSGRESULT, CDRF_DODEFAULT);
+ break;
+ case 17: {
+ // ASCII column
+ SelectObject(lplvcd->nmcd.hdc, GetStockObject(ANSI_FIXED_FONT));
+ SetTextColor(lplvcd->nmcd.hdc, GetSysColor(COLOR_WINDOWTEXT));
+
+ DWORD location_start = row->Location;
+ DWORD location_end = row->Location + 15;
+ if (selection.enabled && location_start <= selection.range[1] && location_end >= selection.range[0]) {
+ RECT rc = lplvcd->nmcd.rc;
+ rc.left += PADDING;
+ rc.right -= PADDING;
+
+ // Text has selection
+ //
+ // There are four possible configurations:
+ // 1. The entire line is selected (one DrawText call)
+ // 2. Only the beginning of the line is selected (two DrawText calls)
+ // 3. Only the end of the line is selected (two DrawText calls)
+ // 4. Only the middle of the line is selected (three DrawText calls)
+ if (location_start >= selection.range[0] && location_end <= selection.range[1]) {
+ // Entire line selected
+ SetTextColor(lplvcd->nmcd.hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
+ FillRect(lplvcd->nmcd.hdc, &rc, GetSysColorBrush(COLOR_HIGHLIGHT));
+ DrawText(lplvcd->nmcd.hdc, row->AsciiStr, strlen(row->AsciiStr), &lplvcd->nmcd.rc, DT_SINGLELINE | DT_CENTER | DT_VCENTER);
+ } else if (location_start >= selection.range[0]) {
+ // Only the beginning selected
+ int unselected_count = location_end - selection.range[1];
+ int selected_count = 16 - unselected_count;
+
+ // Draw right side (unselected)
+ RECT right_rc = rc;
+ right_rc.left += selected_count * CHAR_WIDTH;
+ DrawText(lplvcd->nmcd.hdc, &row->AsciiStr[selected_count], unselected_count, &right_rc, DT_SINGLELINE | DT_CENTER | DT_VCENTER);
+
+ // Draw left side (selected)
+ RECT left_rc = rc;
+ left_rc.right = right_rc.left;
+ SetTextColor(lplvcd->nmcd.hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
+ FillRect(lplvcd->nmcd.hdc, &left_rc, GetSysColorBrush(COLOR_HIGHLIGHT));
+ DrawText(lplvcd->nmcd.hdc, row->AsciiStr, selected_count, &left_rc, DT_SINGLELINE | DT_CENTER | DT_VCENTER);
+ } else if (location_end <= selection.range[1]) {
+ // Only the end selected
+ int selected_count = location_end - selection.range[0] + 1;
+ int unselected_count = 16 - selected_count;
+
+ // Draw left side (unselected)
+ RECT left_rc = rc;
+ left_rc.right = left_rc.left + unselected_count * CHAR_WIDTH;
+ DrawText(lplvcd->nmcd.hdc, row->AsciiStr, unselected_count, &left_rc, DT_SINGLELINE | DT_CENTER | DT_VCENTER);
+
+ // Draw right side (selected)
+ RECT right_rc = rc;
+ right_rc.left = left_rc.right;
+ SetTextColor(lplvcd->nmcd.hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
+ FillRect(lplvcd->nmcd.hdc, &right_rc, GetSysColorBrush(COLOR_HIGHLIGHT));
+ DrawText(lplvcd->nmcd.hdc, &row->AsciiStr[unselected_count], selected_count, &right_rc, DT_SINGLELINE | DT_CENTER | DT_VCENTER);
+ } else {
+ // Only the middle selected
+ int left_count = selection.range[0] - location_start;
+ int right_count = location_end - selection.range[1];
+ int selected_count = 16 - (left_count + right_count);
+
+ // Draw left side (unselected)
+ RECT left_rc = rc;
+ left_rc.right = left_rc.left + left_count * CHAR_WIDTH;
+ DrawText(lplvcd->nmcd.hdc, row->AsciiStr, left_count, &left_rc, DT_SINGLELINE | DT_CENTER | DT_VCENTER);
+
+ // Draw right side (unselected)
+ RECT right_rc = rc;
+ right_rc.left += (left_count + selected_count) * CHAR_WIDTH;
+ DrawText(lplvcd->nmcd.hdc, &row->AsciiStr[left_count + selected_count], right_count, &right_rc, DT_SINGLELINE | DT_CENTER | DT_VCENTER);
+
+ // Draw middle (selected)
+ RECT mid_rc = rc;
+ mid_rc.left = left_rc.right;
+ mid_rc.right = right_rc.left;
+ SetTextColor(lplvcd->nmcd.hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
+ FillRect(lplvcd->nmcd.hdc, &mid_rc, GetSysColorBrush(COLOR_HIGHLIGHT));
+ DrawText(lplvcd->nmcd.hdc, &row->AsciiStr[left_count], selected_count, &mid_rc, DT_SINGLELINE | DT_CENTER | DT_VCENTER);
+ }
+ } else {
+ // Unselected text
+ DrawText(lplvcd->nmcd.hdc, row->AsciiStr, strlen(row->AsciiStr), &lplvcd->nmcd.rc, DT_SINGLELINE | DT_CENTER | DT_VCENTER);
+ }
+
+ SetWindowLong(hDlg, DWL_MSGRESULT, CDRF_SKIPDEFAULT);
+ break;
+ }
+ default: {
+ // Hex columns
int index = lplvcd->iSubItem - 1;
- struct MEMORY_VIEW_ROW* row = &MemoryViewRows[lplvcd->nmcd.dwItemSpec];
+ DWORD location = row->Location + index;
SelectObject(lplvcd->nmcd.hdc, row->Fonts[index]);
- lplvcd->clrText = row->TextColors[index];
-
- if (index & 1) {
- lplvcd->clrTextBk = BG_ODD;
+ if (selection.enabled && location >= selection.range[0] && location <= selection.range[1]) {
+ SetTextColor(lplvcd->nmcd.hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
+ FillRect(lplvcd->nmcd.hdc, &lplvcd->nmcd.rc, GetSysColorBrush(COLOR_HIGHLIGHT));
} else {
- lplvcd->clrTextBk = BG_EVEN;
+ SetTextColor(lplvcd->nmcd.hdc, row->TextColors[index]);
+
+ if (index & 1) {
+ FillRect(lplvcd->nmcd.hdc, &lplvcd->nmcd.rc, hBkOdd);
+ } else {
+ FillRect(lplvcd->nmcd.hdc, &lplvcd->nmcd.rc, hBkEven);
+ }
}
- } else {
- SelectObject(lplvcd->nmcd.hdc, GetStockObject(ANSI_FIXED_FONT));
- lplvcd->clrText = TC_DEFAULT;
- lplvcd->clrTextBk = BG_DEFAULT;
+
+ char* str = row->HexStr[index];
+ DrawText(lplvcd->nmcd.hdc, str, strlen(str), &lplvcd->nmcd.rc, DT_SINGLELINE | DT_CENTER | DT_VCENTER);
+
+ if (index % 4 == 3 && index < 15 && (!selection.enabled || location < selection.range[0] || location > selection.range[1])) {
+ // Draw vertical separators on word boundaries only when the byte is not selected
+ rcBox.left = lplvcd->nmcd.rc.right - 1;
+ rcBox.top = lplvcd->nmcd.rc.top;
+ rcBox.right = lplvcd->nmcd.rc.right;
+ rcBox.bottom = lplvcd->nmcd.rc.bottom;
+ FillRect(lplvcd->nmcd.hdc, &rcBox, (HBRUSH)GetStockObject(GRAY_BRUSH));
+ }
+ SetWindowLong(hDlg, DWL_MSGRESULT, CDRF_SKIPDEFAULT);
+ break;
}
- SetWindowLong(hDlg, DWL_MSGRESULT, CDRF_NEWFONT | CDRF_NOTIFYPOSTPAINT);
- return TRUE;
- case CDDS_ITEMPOSTPAINT | CDDS_SUBITEM:
- if (lplvcd->iSubItem >= 1 && lplvcd->iSubItem < 13 && (lplvcd->iSubItem - 1) % 4 == 3) {
- int x = (lplvcd->iSubItem - 1) * 28 + 117;
- int y = lplvcd->nmcd.dwItemSpec * 17 + 24;
-
- rcBox.left = x;
- rcBox.top = y;
- rcBox.right = x + 1;
- rcBox.bottom = y + 17;
- FillRect(lplvcd->nmcd.hdc, &rcBox, (HBRUSH)GetStockObject(GRAY_BRUSH));
}
return TRUE;
+ }
default:
SetWindowLong(hDlg, DWL_MSGRESULT, CDRF_DODEFAULT);
return TRUE;
@@ -414,9 +715,121 @@ LRESULT CALLBACK Memory_ListViewKeys_Proc(HWND hWnd, UINT uMsg, WPARAM wParam, L
case VK_NEXT:
Scroll_Memory_View(16);
return FALSE;
+ case 'C':
+ if (GetKeyState(VK_CONTROL) & 0x8000) {
+ Copy_Selection();
+ }
+ return FALSE;
default:
break;
}
+ break;
+ default:
+ break;
+ }
+
+ return DefSubclassProc(hWnd, uMsg, wParam, lParam);
+}
+
+LRESULT CALLBACK Memory_ListViewDrag_Proc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData) {
+ switch (uMsg) {
+ case WM_LBUTTONDOWN: {
+ SetFocus(hWnd);
+
+ POINT pt = { LOWORD(lParam), HIWORD(lParam) };
+ LVHITTESTINFO hit_test;
+ hit_test.pt = pt;
+ ListView_SubItemHitTest(hWnd, &hit_test);
+
+ if (hit_test.iSubItem == 0) {
+ // Clicking in address column
+ break;
+ }
+
+ struct MEMORY_VIEW_ROW* row = &MemoryViewRows[hit_test.iItem];
+
+ selection.enabled = TRUE;
+ selection.dragging = TRUE;
+
+ int index;
+ if (hit_test.iSubItem == 17) {
+ // Clicking in ASCII column
+ selection.column_hex = FALSE;
+ index = Get_Ascii_Index(MAKEPOINTS(lParam));
+ } else {
+ // Clicking in hex columns
+ selection.column_hex = TRUE;
+ index = hit_test.iSubItem - 1;
+ }
+
+ DWORD location = row->Location + index;
+ if (wParam & MK_SHIFT) {
+ // Shift-Click
+ selection.range[0] = min(location, selection.anchor);
+ selection.range[1] = max(location, selection.anchor);
+ memcpy(selection.range_cmp, selection.range, sizeof(selection.range_cmp));
+ }
+ else {
+ selection.anchor = location;
+ selection.range[0] = location;
+ selection.range[1] = location;
+ selection.range_cmp[0] = location;
+ selection.range_cmp[1] = location;
+ }
+
+ EnableWindow(GetDlgItem(Memory_Win_hDlg, IDC_BOOKMARK_ADD), TRUE);
+
+ return FALSE;
+ }
+ case WM_LBUTTONUP:
+ selection.dragging = FALSE;
+ break;
+ case WM_MOUSEMOVE:
+ if (selection.dragging) {
+ POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
+ LVHITTESTINFO hit_test;
+ hit_test.pt = pt;
+ ListView_SubItemHitTest(hWnd, &hit_test);
+ struct MEMORY_VIEW_ROW* row = &MemoryViewRows[hit_test.iItem];
+
+ int index;
+ if (hit_test.iSubItem == 17 && selection.column_hex == FALSE) {
+ // Dragging in ASCII column
+ index = Get_Ascii_Index(MAKEPOINTS(lParam));
+ } else if (hit_test.iSubItem > 0 && selection.column_hex == TRUE) {
+ // Dragging in hex columns
+ index = hit_test.iSubItem - 1;
+ } else {
+ // Dragging in address column or drag started in a different column
+ break;
+ }
+
+ DWORD location = row->Location + index;
+
+ selection.range[0] = min(location, selection.anchor);
+ selection.range[1] = max(location, selection.anchor);
+
+ if (memcmp(selection.range, selection.range_cmp, sizeof(selection.range)) != 0) {
+ // TODO: Invalidate only the changed rect
+ InvalidateRect(hWnd, NULL, FALSE);
+
+ memcpy(selection.range_cmp, selection.range, sizeof(selection.range_cmp));
+ return FALSE;
+ }
+ }
+ break;
+ case WM_ACTIVATE:
+ if (selection.dragging && wParam == WA_INACTIVE) {
+ Clear_Selection();
+ return FALSE;
+ }
+ break;
+ case WM_KILLFOCUS:
+ case WM_RBUTTONDOWN:
+ if (selection.dragging) {
+ Clear_Selection();
+ return FALSE;
+ }
default:
break;
}
@@ -424,6 +837,231 @@ LRESULT CALLBACK Memory_ListViewKeys_Proc(HWND hWnd, UINT uMsg, WPARAM wParam, L
return DefSubclassProc(hWnd, uMsg, wParam, lParam);
}
+LRESULT CALLBACK Bookmarks_ListBox_Proc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData) {
+ (void)uIdSubclass;
+ (void)dwRefData;
+
+ static BOOL mouse_held = FALSE;
+
+ switch (uMsg) {
+ case WM_LBUTTONDOWN: {
+ mouse_held = TRUE;
+
+ int height = ListBox_GetItemHeight(hWnd, 0);
+ POINT pt = { LOWORD(lParam), HIWORD(lParam) };
+ int item = (pt.y / height) + ListBox_GetTopIndex(hWnd);
+
+ if (item >= 0 && (unsigned int)item < num_bookmarks) {
+ BOOL is_virtual = (Button_GetCheck(hVAddr) == BST_CHECKED);
+ if (bookmarks[item].is_virtual != is_virtual) {
+ // Deselect the item if the virtual addressing setting doesn't match
+ // TODO: Color these items so they look disabled (needs OWNER DRAW?)
+ return FALSE;
+ }
+
+ Load_Bookmark(item);
+ }
+
+ break;
+ }
+ case WM_LBUTTONUP:
+ mouse_held = FALSE;
+ break;
+ case WM_MOUSEMOVE:
+ if (mouse_held) {
+ // Disable dragging
+ // TODO: Use drag to reorder bookmarks
+ return FALSE;
+ }
+ break;
+ case WM_KEYDOWN:
+ switch (wParam) {
+ case VK_UP:
+ if (Change_Bookmark_Selection(-1)) {
+ return TRUE;
+ }
+ break;
+ case VK_DOWN:
+ if (Change_Bookmark_Selection(1)) {
+ return TRUE;
+ }
+ break;
+ case VK_F2: {
+ int item = ListBox_GetCurSel(hWnd);
+ if (item != LB_ERR) {
+ Edit_Bookmark();
+ return TRUE;
+ }
+ break;
+ }
+ case VK_DELETE: {
+ int item = ListBox_GetCurSel(hWnd);
+ if (item != LB_ERR) {
+ Remove_Bookmark(item);
+ return TRUE;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ break;
+ }
+
+ return DefSubclassProc(hWnd, uMsg, wParam, lParam);
+}
+
+INT_PTR CALLBACK Edit_Bookmark_Proc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
+ (void)lParam;
+
+ HWND hBookmarks = GetDlgItem(Memory_Win_hDlg, IDC_BOOKMARKS);
+ int item = ListBox_GetCurSel(hBookmarks);
+
+ switch (uMsg) {
+ case WM_INITDIALOG: {
+ char address[9] = { 0 };
+
+ // TODO: Support language translations
+ HWND hName = GetDlgItem(hDlg, IDC_NAME);
+ Edit_SetText(hName, bookmarks[item].name);
+ Edit_LimitText(hName, sizeof(bookmarks->name) - 1);
+
+ HWND hStart = GetDlgItem(hDlg, IDC_START);
+ sprintf(address, "%08X", bookmarks[item].selection_range[0]);
+ Edit_SetText(hStart, address);
+ Edit_LimitText(hStart, 8);
+
+ HWND hEnd = GetDlgItem(hDlg, IDC_END);
+ sprintf(address, "%08X", bookmarks[item].selection_range[1]);
+ Edit_SetText(hEnd, address);
+ Edit_LimitText(hEnd, 8);
+
+ CheckRadioButton(hDlg, IDC_BOOKMARK_VADDR, IDC_BOOKMARK_PADDR, bookmarks[item].is_virtual ? IDC_BOOKMARK_VADDR : IDC_BOOKMARK_PADDR);
+
+ return TRUE;
+ }
+ case WM_COMMAND:
+ switch (LOWORD(wParam)) {
+ case IDOK: {
+ char name[sizeof(bookmarks->name)] = { 0 };
+ char temp[9] = { 0 };
+ char *end = NULL;
+ DWORD start_address = 0;
+ DWORD end_address = 0;
+
+ // Validate inputs
+ Edit_GetText(GetDlgItem(hDlg, IDC_NAME), name, sizeof(name));
+ if (name[0] == 0) {
+ DisplayError("Name is required");
+ return FALSE;
+ }
+
+ Edit_GetText(GetDlgItem(hDlg, IDC_START), temp, sizeof(temp));
+ start_address = strtoul(temp, &end, 16);
+ if (end == temp || *end != 0) {
+ DisplayError("Start address is invalid");
+ return FALSE;
+ }
+
+ Edit_GetText(GetDlgItem(hDlg, IDC_END), temp, sizeof(temp));
+ end_address = strtoul(temp, &end, 16);
+ if (end == temp || *end != 0) {
+ DisplayError("End address is invalid");
+ return FALSE;
+ }
+ if (end_address < start_address) {
+ DisplayError("Invalid address range: End must be greater or equal to start");
+ return FALSE;
+ }
+
+ // Update bookmark "atomically"
+ strcpy(bookmarks[item].name, name);
+ bookmarks[item].width_required = Get_Bookmark_Name_Width(name);
+ bookmarks[item].selection_range[0] = start_address;
+ bookmarks[item].selection_range[1] = end_address;
+ bookmarks[item].is_virtual = (Button_GetCheck(GetDlgItem(hDlg, IDC_BOOKMARK_VADDR)) == BST_CHECKED);
+
+ // Update parent control
+ BOOL is_virtual = (Button_GetCheck(hVAddr) == BST_CHECKED);
+ if (bookmarks[item].is_virtual == is_virtual) {
+ ListBox_DeleteString(hBookmarks, item);
+ ListBox_InsertString(hBookmarks, item, name);
+ ListBox_SetCurSel(hBookmarks, item);
+ Load_Bookmark(item);
+ } else {
+ ListBox_SetCurSel(hBookmarks, -1);
+ }
+
+ EndDialog(hDlg, 1);
+ return TRUE;
+ }
+ case IDCANCEL:
+ EndDialog(hDlg, 0);
+ return TRUE;
+ default:
+ return FALSE;
+ }
+ default:
+ return FALSE;
+ }
+}
+
+void Clear_Selection(void) {
+ selection.enabled = FALSE;
+ selection.dragging = FALSE;
+ selection.column_hex = FALSE;
+ selection.anchor = 0;
+ selection.range[0] = 0;
+ selection.range[1] = 0;
+ selection.range_cmp[0] = 0;
+ selection.range_cmp[1] = 0;
+
+ EnableWindow(GetDlgItem(Memory_Win_hDlg, IDC_BOOKMARK_ADD), FALSE);
+}
+
+void Copy_Selection(void) {
+ if (!selection.enabled || !OpenClipboard(NULL)) {
+ return;
+ }
+ EmptyClipboard();
+
+ DWORD location = selection.range[0] & ~15;
+ int lines = (selection.range[1] - location) / 16 + 1;
+
+ // Each line is exactly 79 bytes, plus the null terminator.
+ HGLOBAL hMemory = GlobalAlloc(GMEM_MOVEABLE, lines * 79 + 1);
+ if (!hMemory) {
+ CloseClipboard();
+ return;
+ }
+
+ char *memory = GlobalLock(hMemory);
+
+ char *output = memory;
+ for (int i = 0; i < lines; i++) {
+ Write_MemoryLineDump(output, location);
+ output += 79;
+ location += 16;
+ }
+
+ GlobalUnlock(hMemory);
+ // TODO: Also copy the binary data
+ SetClipboardData(CF_TEXT, memory);
+ CloseClipboard();
+}
+
+int Get_Ascii_Index(POINTS pt) {
+ RECT rc;
+ // The row doesn't matter because we ignore the Y coordinate
+ ListView_GetSubItemRect(hList, 0, 17, LVIR_BOUNDS, &rc);
+
+ // Compute the character index
+ int x = pt.x - rc.left;
+ int index = (x - PADDING) / CHAR_WIDTH;
+
+ return min(max(index, 0), 15);
+}
+
void Scroll_Memory_View(int lines) {
char value[20];
@@ -443,7 +1081,7 @@ void Scroll_Memory_View(int lines) {
location = UINT_MAX - 256 + 1;
}
} else {
- if (location >= -lines * 16) {
+ if (location >= (unsigned int)(-lines * 16)) {
location += lines * 16;
} else {
location = 0;
@@ -456,6 +1094,24 @@ void Scroll_Memory_View(int lines) {
ReleaseMutex(hRefreshMutex);
}
+LRESULT Change_Bookmark_Selection(int next) {
+ BOOL result = FALSE;
+ BOOL is_virtual = (Button_GetCheck(hVAddr) == BST_CHECKED);
+ HWND hBookmarks = GetDlgItem(Memory_Win_hDlg, IDC_BOOKMARKS);
+ int item = ListBox_GetCurSel(hBookmarks) + next;
+ while (item >= 0 && (unsigned int)item < num_bookmarks) {
+ result = TRUE;
+ if (bookmarks[item].is_virtual == is_virtual) {
+ ListBox_SetCurSel(hBookmarks, item);
+ Load_Bookmark(item);
+ return TRUE;
+ }
+
+ item += next;
+ }
+ return result;
+}
+
void __cdecl Refresh_Memory(void) {
Refresh_Memory_With_Diff(TRUE);
}
@@ -505,8 +1161,9 @@ void Start_Auto_Refresh_Thread(void) {
}
void Setup_Memory_Window (HWND hDlg) {
-#define WindowWidth 742
+#define WindowWidth 912
#define WindowHeight 392
+ HWND hBookmarks, hBookmarkAdd, hBookmarkEdit, hBookmarkRemove;
DWORD X, Y;
if (hRefreshMutex == NULL) {
@@ -517,52 +1174,58 @@ void Setup_Memory_Window (HWND hDlg) {
hVAddr = CreateWindowEx(0,"BUTTON", "Virtual Addressing", WS_CHILD | WS_VISIBLE |
BS_AUTORADIOBUTTON, 215,13,150,21,hDlg,(HMENU)IDC_VADDR,hInst,NULL );
SendMessage(hVAddr,BM_SETCHECK, BST_CHECKED,0);
-
- hPAddr = CreateWindowEx(0,"BUTTON", "Physical Addressing", WS_CHILD | WS_VISIBLE |
- BS_AUTORADIOBUTTON, 375,13,155,21,hDlg,(HMENU)IDC_PADDR,hInst,NULL );
+ SendMessage(hVAddr, WM_SETFONT, (WPARAM)hDefaultFont, 0);
+
+ hPAddr = CreateWindowEx(0, "BUTTON", "Physical Addressing", WS_CHILD | WS_VISIBLE |
+ BS_AUTORADIOBUTTON, 375, 13, 155, 21, hDlg, (HMENU)IDC_PADDR, hInst, NULL);
+ SendMessage(hPAddr, WM_SETFONT, (WPARAM)hDefaultFont, 0);
- hRefresh = CreateWindowEx(0,"BUTTON", "Auto Refresh", WS_CHILD | WS_VISIBLE |
- BS_AUTOCHECKBOX, 595,13,100,21,hDlg,(HMENU)IDC_REFRESH,hInst,NULL );
+ hRefresh = CreateWindowEx(0, "BUTTON", "Auto Refresh", WS_CHILD | WS_VISIBLE |
+ BS_AUTOCHECKBOX, 595, 13, 100, 21, hDlg, (HMENU)IDC_REFRESH, hInst, NULL);
SendMessage(hRefresh, BM_SETCHECK, BST_CHECKED, 0);
Start_Auto_Refresh_Thread();
+ SendMessage(hRefresh, WM_SETFONT, (WPARAM)hDefaultFont, 0);
hList = CreateWindowEx(WS_EX_CLIENTEDGE, WC_LISTVIEW, "", WS_CHILD | WS_VISIBLE |
- LVS_OWNERDATA | LVS_REPORT | LVS_NOSORTHEADER, 14,39,682,300,hDlg,
- (HMENU)IDC_LIST_VIEW,hInst,NULL );
+ LVS_OWNERDATA | LVS_REPORT | LVS_NOSORTHEADER | LVS_SINGLESEL, 12, 39, 682, 300, hDlg,
+ (HMENU)IDC_LIST_VIEW, hInst, NULL);
if (hList) {
+ SendMessage(hList, WM_SETFONT, (WPARAM)hDefaultFont, 0);
ListView_SetExtendedListViewStyle(hList, LVS_EX_DOUBLEBUFFER);
LV_COLUMN col;
int count;
col.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
- col.fmt = LVCFMT_LEFT;
+ col.fmt = LVCFMT_LEFT;
- col.pszText = "Address";
- col.cx = 90;
+ col.pszText = "Address";
+ col.cx = 90;
col.iSubItem = 0;
- ListView_InsertColumn ( hList, 0, &col);
+ ListView_InsertColumn(hList, 0, &col);
char ColumnName[3] = { 0 };
- col.pszText = ColumnName;
- col.cx = 28;
+ col.pszText = ColumnName;
+ col.cx = 28;
for (int i = 0; i < 16; i++) {
sprintf(ColumnName, " %X", i);
col.iSubItem = i + 1;
ListView_InsertColumn(hList, i + 1, &col);
}
- col.pszText = "Memory Ascii";
- col.cx = 140;
+ col.pszText = "Memory Ascii";
+ col.cx = 140;
col.iSubItem = 17;
- ListView_InsertColumn ( hList, 17, &col);
- ListView_SetItemCount ( hList, 16);
- SendMessage(hList,WM_SETFONT, (WPARAM)GetStockObject(ANSI_FIXED_FONT),0);
- for (count = 0 ; count < 16;count ++ ){
+ ListView_InsertColumn(hList, 17, &col);
+ ListView_SetItemCount(hList, 16);
+ SendMessage(hList, WM_SETFONT, (WPARAM)GetStockObject(ANSI_FIXED_FONT), 0);
+ for (count = 0; count < 16; count++) {
Insert_MemoryLineDump(count, count, FALSE);
}
SetWindowSubclass(hList, Memory_ListViewScroll_Proc, 0, 0);
SetWindowSubclass(hList, Memory_ListViewKeys_Proc, 0, 0);
+ SetWindowSubclass(hList, Memory_ListViewDrag_Proc, 0, 0);
+ Clear_Selection();
}
hAddrEdit = GetDlgItem(hDlg, IDC_ADDR_EDIT);
@@ -573,35 +1236,181 @@ void Setup_Memory_Window (HWND hDlg) {
}
ReleaseMutex(hRefreshMutex);
- SendMessage(hAddrEdit,EM_SETLIMITTEXT,(WPARAM)8,(LPARAM)0);
- SetWindowPos(hAddrEdit,NULL, 100,13,100,21, SWP_NOZORDER | SWP_SHOWWINDOW);
- SendMessage(hAddrEdit,WM_SETFONT, (WPARAM)GetStockObject(ANSI_FIXED_FONT),0);
+ SendMessage(hAddrEdit, EM_SETLIMITTEXT, (WPARAM)8, (LPARAM)0);
+ SetWindowPos(hAddrEdit, NULL, 100, 13, 100, 21, SWP_NOZORDER | SWP_SHOWWINDOW);
+ SendMessage(hAddrEdit, WM_SETFONT, (WPARAM)GetStockObject(ANSI_FIXED_FONT), 0);
hScrlBar = CreateWindowEx(0, "SCROLLBAR", "", WS_CHILD | WS_VISIBLE |
- WS_TABSTOP | SBS_VERT, 696,39,20,300, hDlg, (HMENU)IDC_SCRL_BAR, hInst, NULL );
+ WS_TABSTOP | SBS_VERT, 694, 39, 20, 300, hDlg, (HMENU)IDC_SCRL_BAR, hInst, NULL);
if (hScrlBar) {
SCROLLINFO si;
si.cbSize = sizeof(si);
- si.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
- si.nMin = 0;
- si.nMax = 300;
- si.nPos = 145;
- si.nPage = 10;
- SetScrollInfo(hScrlBar,SB_CTL,&si,TRUE);
+ si.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
+ si.nMin = 0;
+ si.nMax = 300;
+ si.nPos = 145;
+ si.nPage = 10;
+ SetScrollInfo(hScrlBar, SB_CTL, &si, TRUE);
SetWindowSubclass(hScrlBar, Memory_ListViewScroll_Proc, 0, 0);
- }
-
- LOGFONT lf;
- GetObject(GetStockObject(ANSI_FIXED_FONT), sizeof(lf), &lf);
- lf.lfUnderline = TRUE;
- hWatchFont = CreateFontIndirect(&lf);
+ }
+
+ hBookmarks = CreateWindowEx(WS_EX_CLIENTEDGE, WC_LISTBOX, "", WS_CHILD | WS_VISIBLE | WS_VSCROLL | WS_HSCROLL | LBS_NOTIFY,
+ 731, 39, 152, 280, hDlg, (HMENU)IDC_BOOKMARKS, hInst, NULL);
+ SendMessage(hBookmarks, WM_SETFONT, (WPARAM)hDefaultFont, 0);
+ SetWindowSubclass(hBookmarks, Bookmarks_ListBox_Proc, 0, 0);
+
+ for (unsigned int i = 0; i < num_bookmarks; i++) {
+ ListBox_AddString(hBookmarks, bookmarks[i].name);
+ }
+ Update_Bookmark_Width();
+
+ hBookmarkAdd = CreateWindowEx(WS_EX_WINDOWEDGE, WC_BUTTON, "Add", WS_CHILD | WS_VISIBLE,
+ 731, 318, 36, 22, hDlg, (HMENU)IDC_BOOKMARK_ADD, hInst, NULL);
+ SendMessage(hBookmarkAdd, WM_SETFONT, (WPARAM)hDefaultFont, 0);
+ EnableWindow(hBookmarkAdd, FALSE);
+
+ hBookmarkEdit = CreateWindowEx(WS_EX_WINDOWEDGE, WC_BUTTON, "Update", WS_CHILD | WS_VISIBLE,
+ 769, 318, 56, 22, hDlg, (HMENU)IDC_BOOKMARK_UPDATE, hInst, NULL);
+ SendMessage(hBookmarkEdit, WM_SETFONT, (WPARAM)hDefaultFont, 0);
+ EnableWindow(hBookmarkEdit, FALSE);
+
+ hBookmarkRemove = CreateWindowEx(WS_EX_WINDOWEDGE, WC_BUTTON, "Remove", WS_CHILD | WS_VISIBLE,
+ 827, 318, 56, 22, hDlg, (HMENU)IDC_BOOKMARK_REMOVE, hInst, NULL);
+ SendMessage(hBookmarkRemove, WM_SETFONT, (WPARAM)hDefaultFont, 0);
+ EnableWindow(hBookmarkRemove, FALSE);
if ( !GetStoredWinPos( "Memory", &X, &Y ) ) {
X = (GetSystemMetrics( SM_CXSCREEN ) - WindowWidth) / 2;
Y = (GetSystemMetrics( SM_CYSCREEN ) - WindowHeight) / 2;
}
- SetWindowPos(hDlg,NULL,X,Y,WindowWidth,WindowHeight, SWP_NOZORDER | SWP_SHOWWINDOW);
-
+ SetWindowPos(hDlg,NULL,X,Y,WindowWidth,WindowHeight, SWP_NOZORDER | SWP_SHOWWINDOW);
+}
+
+unsigned int Get_Bookmark_Name_Width(char *name) {
+ HWND hBookmark = GetDlgItem(Memory_Win_hDlg, IDC_BOOKMARKS);
+ HDC hDcBookmarks = GetDC(hBookmark);
+ HDC hdc = CreateCompatibleDC(hDcBookmarks);
+ HGDIOBJ hOldFont = SelectObject(hdc, hDefaultFont);
+
+ SIZE size = { 0 };
+ GetTextExtentPoint32(hdc, name, strlen(name), &size);
+
+ SelectObject(hdc, hOldFont);
+ DeleteDC(hdc);
+ ReleaseDC(hBookmark, hDcBookmarks);
+
+ return size.cx + GetSystemMetrics(SM_CXEDGE) * 2;
+}
+
+void Update_Bookmark_Width(void) {
+ unsigned int width_required = 0;
+ for (unsigned int i = 0; i < num_bookmarks; i++) {
+ width_required = max(width_required, bookmarks[i].width_required);
+ }
+
+ HWND hBookmarks = GetDlgItem(Memory_Win_hDlg, IDC_BOOKMARKS);
+ if (width_required != ListBox_GetHorizontalExtent(hBookmarks)) {
+ ListBox_SetHorizontalExtent(hBookmarks, width_required);
+ }
+}
+
+void Create_Bookmark_Name(char *name, BOOL is_virtual) {
+ DWORD address = selection.range[0];
+ DWORD len = min(selection.range[1] - address + 1, sizeof(bookmarks->name) - (8 + 6));
+ BYTE value = 0;
+
+ if (is_virtual) {
+ unsigned int i = 0;
+ while (i < len) {
+ if (r4300i_LB_VAddr_NonCPU(address + i, &value) && isprint(value)) {
+ name[i] = value;
+ } else {
+ sprintf(name, "[0x%08X]", address);
+ return;
+ }
+
+ i++;
+ }
+ sprintf(&name[i], " [0x%08X]", address);
+ } else {
+ sprintf(name, "PHYS [0x%08X]", address);
+ }
+}
+
+void Add_Bookmark(void) {
+ if (num_bookmarks < MAX_MEM_BOOKMARKS && selection.enabled) {
+ bookmarks[num_bookmarks].selection_range[0] = selection.range[0];
+ bookmarks[num_bookmarks].selection_range[1] = selection.range[1];
+ bookmarks[num_bookmarks].is_virtual = (Button_GetCheck(hVAddr) == BST_CHECKED);
+
+ Create_Bookmark_Name(bookmarks[num_bookmarks].name, bookmarks[num_bookmarks].is_virtual);
+ bookmarks[num_bookmarks].width_required = Get_Bookmark_Name_Width(bookmarks[num_bookmarks].name);
+
+ ListBox_AddString(GetDlgItem(Memory_Win_hDlg, IDC_BOOKMARKS), bookmarks[num_bookmarks].name);
+
+ num_bookmarks += 1;
+
+ Update_Bookmark_Width();
+
+ Session_Save_MemBookmarks(bookmarks_cbor, bookmarks, num_bookmarks);
+ }
+}
+
+void Edit_Bookmark(void) {
+ if (DialogBox(NULL, MAKEINTRESOURCE(IDD_MEM_BOOKMARK_EDIT), Memory_Win_hDlg, Edit_Bookmark_Proc)) {
+ Update_Bookmark_Width();
+
+ Session_Save_MemBookmarks(bookmarks_cbor, bookmarks, num_bookmarks);
+ }
+}
+
+void Update_Bookmark(unsigned int item) {
+ bookmarks[item].selection_range[0] = selection.range[0];
+ bookmarks[item].selection_range[1] = selection.range[1];
+
+ Session_Save_MemBookmarks(bookmarks_cbor, bookmarks, num_bookmarks);
+}
+
+void Remove_Bookmark(unsigned int item) {
+ if (item < num_bookmarks - 1) {
+ memmove(&bookmarks[item], &bookmarks[item + 1], sizeof(struct MEM_BOOKMARK) * (num_bookmarks - item - 1));
+ }
+
+ HWND hBookmarks = GetDlgItem(Memory_Win_hDlg, IDC_BOOKMARKS);
+ ListBox_DeleteString(hBookmarks, item);
+
+ num_bookmarks -= 1;
+
+ BOOL is_virtual = (Button_GetCheck(hVAddr) == BST_CHECKED);
+ if (item < num_bookmarks && bookmarks[item].is_virtual == is_virtual) {
+ ListBox_SetCurSel(hBookmarks, item);
+ } else {
+ EnableWindow(GetDlgItem(Memory_Win_hDlg, IDC_BOOKMARK_UPDATE), FALSE);
+ EnableWindow(GetDlgItem(Memory_Win_hDlg, IDC_BOOKMARK_REMOVE), FALSE);
+ }
+
+ Update_Bookmark_Width();
+
+ Session_Save_MemBookmarks(bookmarks_cbor, bookmarks, num_bookmarks);
+}
+
+void Load_Bookmark(unsigned int item) {
+ selection.enabled = TRUE;
+ selection.dragging = FALSE;
+ selection.column_hex = TRUE;
+ selection.anchor = bookmarks[item].selection_range[0];
+ selection.range[0] = bookmarks[item].selection_range[0];
+ selection.range[1] = bookmarks[item].selection_range[1];
+ selection.range_cmp[0] = bookmarks[item].selection_range[0];
+ selection.range_cmp[1] = bookmarks[item].selection_range[1];
+
+ char address[10] = { 0 };
+ sprintf(address, "%08X", bookmarks[item].selection_range[0]);
+ SetWindowText(hAddrEdit, address);
+
+ EnableWindow(GetDlgItem(Memory_Win_hDlg, IDC_BOOKMARK_ADD), TRUE);
+ EnableWindow(GetDlgItem(Memory_Win_hDlg, IDC_BOOKMARK_UPDATE), TRUE);
+ EnableWindow(GetDlgItem(Memory_Win_hDlg, IDC_BOOKMARK_REMOVE), TRUE);
}
\ No newline at end of file
diff --git a/resource.h b/resource.h
index 834aaab..86d6eac 100644
--- a/resource.h
+++ b/resource.h
@@ -39,6 +39,7 @@
#define IDD_Debugger_MemDump 178
#define IDB_BITMAP2 182
#define IDB_BITMAP3 183
+#define IDD_MEM_BOOKMARK_EDIT 184
#define IDC_CLOSE_BUTTON 1001
#define RSP_LIST 1004
#define RSP_ABOUT 1006
@@ -265,6 +266,10 @@
#define IDC_CLEAR_MEMORY 1258
#define IDC_CHECK1 1259
#define IDC_USEDEBUGGER 1259
+#define IDC_END 1260
+#define IDC_BOOKMARK_VADDR 1261
+#define IDC_BOOKMARK_PADDR 1262
+#define IDC_START 1263
#define ID_FILE_OPEN_ROM 40001
#define ID_FILE_ROM_INFO 40002
#define ID_FILE_EXIT 40005
@@ -346,9 +351,9 @@
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
-#define _APS_NEXT_RESOURCE_VALUE 183
+#define _APS_NEXT_RESOURCE_VALUE 185
#define _APS_NEXT_COMMAND_VALUE 40175
-#define _APS_NEXT_CONTROL_VALUE 1260
+#define _APS_NEXT_CONTROL_VALUE 1264
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif