mirror of
https://github.com/pj64team/Project64-Legacy.git
synced 2026-04-03 13:14:52 +00:00
* Switch hex editor to full CUSTOMDRAW - This will allow us to draw with more precision. Especially for selection ranges and editing. * Add basic hex selection * Add ASCII column text selection * Ctrl+C to copy selected memory * Fix moving the window * Fix minor bugs in memory viewer with watchpoints - The colors and underlines were only showing when a watchpoint was disabled. - If the last hex byte in a row was underlined, the entire ASCII section would also be underlined on that row. - Now colors always show when a watchpoint exists. And the byte is underlined only when the watchpoint is enabled. * Fix copying the last line of memory with some selection sizes - This bug prevented the last line of the memory view from being copied to the clipboard when the start of the selection was at a higher column number than the end of the selection. - E.g. when address range 0x8000000c-0x80000023 was selected, it would only copy 0x8000000c-0x8000001f, truncating the last line and missing four selected bytes. * Add memory editor bookmarks * Add missing session files to VS project filters
205 lines
5.8 KiB
C++
205 lines
5.8 KiB
C++
/*
|
|
* 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 <windows.h>
|
|
#include <cstdint>
|
|
#include <string>
|
|
#include <vector>
|
|
#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<uint8_t> &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<uint8_t> &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 <typename InputIterator>
|
|
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 <typename Buffer>
|
|
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<uint8_t>();
|
|
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<uint8_t>();
|
|
|
|
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;
|
|
}
|
|
} |