mame/src/osd/modules/debugger/win/consolewininfo.cpp

587 lines
18 KiB
C++

// license:BSD-3-Clause
// copyright-holders:Aaron Giles, Vas Crabb
//============================================================
//
// consolewininfo.c - Win32 debug window handling
//
//============================================================
#include "emu.h"
#include "consolewininfo.h"
#include "debugviewinfo.h"
#include "uimetrics.h"
#include "debugger.h"
#include "debug/debugcon.h"
#include "debug/debugcpu.h"
#include "imagedev/cassette.h"
#include "strconv.h"
#include "winutf8.h"
consolewin_info::consolewin_info(debugger_windows_interface &debugger) :
disasmbasewin_info(debugger, true, "Debug", nullptr),
m_devices_menu(nullptr)
{
if ((window() == nullptr) || (m_views[0] == nullptr))
goto cleanup;
// create the views
m_views[1].reset(global_alloc(debugview_info(debugger, *this, window(), DVT_STATE)));
if (!m_views[1]->is_valid())
goto cleanup;
m_views[2].reset(global_alloc(debugview_info(debugger, *this, window(), DVT_CONSOLE)));
if (!m_views[2]->is_valid())
goto cleanup;
{
// Add image menu only if image devices exist
image_interface_iterator iter(machine().root_device());
if (iter.first() != nullptr)
{
m_devices_menu = CreatePopupMenu();
for (device_image_interface &img : iter)
{
if (!img.user_loadable())
continue;
osd::text::tstring tc_buf = osd::text::to_tstring(string_format("%s : %s", img.device().name(), img.exists() ? img.filename() : "[no image]"));
AppendMenu(m_devices_menu, MF_ENABLED, 0, tc_buf.c_str());
}
AppendMenu(GetMenu(window()), MF_ENABLED | MF_POPUP, (UINT_PTR)m_devices_menu, TEXT("Images"));
}
// get the work bounds
RECT work_bounds, bounds;
SystemParametersInfo(SPI_GETWORKAREA, 0, &work_bounds, 0);
// adjust the min/max sizes for the window style
bounds.top = bounds.left = 0;
bounds.right = bounds.bottom = EDGE_WIDTH + m_views[1]->maxwidth() + (2 * EDGE_WIDTH) + 100 + EDGE_WIDTH;
AdjustWindowRectEx(&bounds, DEBUG_WINDOW_STYLE, FALSE, DEBUG_WINDOW_STYLE_EX);
set_minwidth(bounds.right - bounds.left);
bounds.top = bounds.left = 0;
bounds.right = bounds.bottom = EDGE_WIDTH + m_views[1]->maxwidth() + (2 * EDGE_WIDTH) + std::max(m_views[0]->maxwidth(), m_views[2]->maxwidth()) + EDGE_WIDTH;
AdjustWindowRectEx(&bounds, DEBUG_WINDOW_STYLE, FALSE, DEBUG_WINDOW_STYLE_EX);
set_maxwidth(bounds.right - bounds.left);
// position the window at the bottom-right
int const bestwidth = (std::min<uint32_t>)(maxwidth(), work_bounds.right - work_bounds.left);
int const bestheight = (std::min<uint32_t>)(500, work_bounds.bottom - work_bounds.top);
SetWindowPos(window(), HWND_TOP,
work_bounds.right - bestwidth, work_bounds.bottom - bestheight,
bestwidth, bestheight,
SWP_SHOWWINDOW);
}
// recompute the children
set_cpu(*machine().debugger().cpu().get_visible_cpu());
// mark the edit box as the default focus and set it
editwin_info::set_default_focus();
return;
cleanup:
m_views[2].reset();
m_views[1].reset();
m_views[0].reset();
}
consolewin_info::~consolewin_info()
{
}
void consolewin_info::set_cpu(device_t &device)
{
// first set all the views to the new cpu number
m_views[0]->set_source_for_device(device);
m_views[1]->set_source_for_device(device);
// then update the caption
std::string title = string_format("Debug: %s - %s '%s'", device.machine().system().name, device.name(), device.tag());
std::string curtitle = win_get_window_text_utf8(window());
if (title != curtitle)
win_set_window_text_utf8(window(), title.c_str());
// and recompute the children
recompute_children();
}
void consolewin_info::recompute_children()
{
// get the parent's dimensions
RECT parent;
GetClientRect(window(), &parent);
// registers always get their desired width, and span the entire height
RECT regrect;
regrect.top = parent.top + EDGE_WIDTH;
regrect.bottom = parent.bottom - EDGE_WIDTH;
regrect.left = parent.left + EDGE_WIDTH;
regrect.right = regrect.left + m_views[1]->maxwidth();
// edit box goes at the bottom of the remaining area
RECT editrect;
editrect.bottom = parent.bottom - EDGE_WIDTH;
editrect.top = editrect.bottom - metrics().debug_font_height() - 4;
editrect.left = regrect.right + (EDGE_WIDTH * 2);
editrect.right = parent.right - EDGE_WIDTH;
// console and disassembly split the difference
RECT disrect;
disrect.top = parent.top + EDGE_WIDTH;
disrect.bottom = ((editrect.top - parent.top) / 2) - EDGE_WIDTH;
disrect.left = regrect.right + (EDGE_WIDTH * 2);
disrect.right = parent.right - EDGE_WIDTH;
RECT conrect;
conrect.top = disrect.bottom + (EDGE_WIDTH * 2);
conrect.bottom = editrect.top - EDGE_WIDTH;
conrect.left = regrect.right + (EDGE_WIDTH * 2);
conrect.right = parent.right - EDGE_WIDTH;
// set the bounds of things
m_views[0]->set_bounds(disrect);
m_views[1]->set_bounds(regrect);
m_views[2]->set_bounds(conrect);
set_editwnd_bounds(editrect);
}
void consolewin_info::update_menu()
{
disasmbasewin_info::update_menu();
if (m_devices_menu != nullptr)
{
// create the image menu
uint32_t cnt = 0;
for (device_image_interface &img : image_interface_iterator(machine().root_device()))
{
if (!img.user_loadable())
continue;
HMENU const devicesubmenu = CreatePopupMenu();
UINT_PTR const new_item = ID_DEVICE_OPTIONS + (cnt * DEVOPTION_MAX);
UINT flags_for_exists = MF_ENABLED | MF_STRING;
if (!img.exists())
flags_for_exists |= MF_GRAYED;
UINT flags_for_writing = flags_for_exists;
if (img.is_readonly())
flags_for_writing |= MF_GRAYED;
// not working properly, removed for now until investigation can be done
//if (get_softlist_info(&img))
// AppendMenu(devicesubmenu, MF_STRING, new_item + DEVOPTION_ITEM, TEXT("Mount Item..."));
AppendMenu(devicesubmenu, MF_STRING, new_item + DEVOPTION_OPEN, TEXT("Mount File..."));
if (img.is_creatable())
AppendMenu(devicesubmenu, MF_STRING, new_item + DEVOPTION_CREATE, TEXT("Create..."));
AppendMenu(devicesubmenu, flags_for_exists, new_item + DEVOPTION_CLOSE, TEXT("Unmount"));
if (img.device().type() == CASSETTE)
{
cassette_state const state = (cassette_state)(img.exists() ? (downcast<cassette_image_device *>(&img.device())->get_state() & CASSETTE_MASK_UISTATE) : CASSETTE_STOPPED);
AppendMenu(devicesubmenu, MF_SEPARATOR, 0, nullptr);
AppendMenu(devicesubmenu, flags_for_exists | ((state == CASSETTE_STOPPED) ? MF_CHECKED : 0), new_item + DEVOPTION_CASSETTE_STOPPAUSE, TEXT("Pause/Stop"));
AppendMenu(devicesubmenu, flags_for_exists | ((state == CASSETTE_PLAY) ? MF_CHECKED : 0), new_item + DEVOPTION_CASSETTE_PLAY, TEXT("Play"));
AppendMenu(devicesubmenu, flags_for_writing | ((state == CASSETTE_RECORD) ? MF_CHECKED : 0), new_item + DEVOPTION_CASSETTE_RECORD, TEXT("Record"));
AppendMenu(devicesubmenu, flags_for_exists, new_item + DEVOPTION_CASSETTE_REWIND, TEXT("Rewind"));
AppendMenu(devicesubmenu, flags_for_exists, new_item + DEVOPTION_CASSETTE_FASTFORWARD, TEXT("Fast Forward"));
}
std::string filename;
if (img.basename())
{
filename.assign(img.basename());
// if the image has been loaded through softlist, also show the loaded part
if (img.loaded_through_softlist())
{
const software_part *tmp = img.part_entry();
if (!tmp->name().empty())
{
filename.append(" (");
filename.append(tmp->name());
// also check if this part has a specific part_id (e.g. "Map Disc", "Bonus Disc", etc.), and in case display it
if (img.get_feature("part_id") != nullptr)
{
filename.append(": ");
filename.append(img.get_feature("part_id"));
}
filename.append(")");
}
}
}
else
filename.assign("---");
// Get instance names like the File Manager
osd::text::tstring tc_buf = osd::text::to_tstring(string_format("%s (%s): %s", img.instance_name(), img.brief_instance_name(), filename.c_str()));
std::transform(tc_buf.begin(), tc_buf.begin()+1, tc_buf.begin(), ::toupper); // turn first char to uppercase
ModifyMenu(m_devices_menu, cnt, MF_BYPOSITION | MF_POPUP, (UINT_PTR)devicesubmenu, tc_buf.c_str());
cnt++;
}
}
}
bool consolewin_info::handle_command(WPARAM wparam, LPARAM lparam)
{
if ((HIWORD(wparam) == 0) && (LOWORD(wparam) >= ID_DEVICE_OPTIONS))
{
uint32_t const devid = (LOWORD(wparam) - ID_DEVICE_OPTIONS) / DEVOPTION_MAX;
image_interface_iterator iter(machine().root_device());
device_image_interface *const img = iter.byindex(devid);
if (img != nullptr)
{
switch ((LOWORD(wparam) - ID_DEVICE_OPTIONS) % DEVOPTION_MAX)
{
case DEVOPTION_ITEM :
{
std::string filter;
build_generic_filter(nullptr, false, filter);
{
osd::text::tstring t_filter = osd::text::to_tstring(filter);
// convert a pipe-char delimited string into a NUL delimited string
for (int i = 0; t_filter[i] != '\0'; i++)
{
if (t_filter[i] == '|')
t_filter[i] = '\0';
}
std::string opt_name = img->instance_name();
std::string as = slmap.find(opt_name)->second;
/* Make sure a folder was specified, and that it exists */
if ((!osd::directory::open(as.c_str())) || (as.find(':') == std::string::npos))
{
/* Default to emu directory */
osd_get_full_path(as, ".");
}
osd::text::tstring t_dir = osd::text::to_tstring(as);
// display the dialog
TCHAR selectedFilename[MAX_PATH];
selectedFilename[0] = '\0';
OPENFILENAME ofn;
memset(&ofn, 0, sizeof(ofn));
ofn.lStructSize = sizeof(ofn);
ofn.hwndOwner = nullptr;
ofn.lpstrFile = selectedFilename;
ofn.lpstrFile[0] = '\0';
ofn.nMaxFile = MAX_PATH;
ofn.lpstrFilter = t_filter.c_str();
ofn.nFilterIndex = 1;
ofn.lpstrFileTitle = nullptr;
ofn.nMaxFileTitle = 0;
ofn.lpstrInitialDir = t_dir.c_str();
ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
if (GetOpenFileName(&ofn))
{
std::string buf = std::string(osd::text::from_tstring(selectedFilename));
// Get the Item name out of the full path
size_t t1 = buf.find(".zip"); // get rid of zip name and anything after
if (t1 != std::string::npos)
buf.erase(t1);
t1 = buf.find(".7z"); // get rid of 7zip name and anything after
if (t1 != std::string::npos)
buf.erase(t1);
t1 = buf.find_last_of("\\"); // put the swlist name in
buf[t1] = ':';
t1 = buf.find_last_of("\\"); // get rid of path; we only want the item name
buf.erase(0, t1+1);
// load software
img->load_software( buf.c_str());
}
}
}
return true;
case DEVOPTION_OPEN :
{
std::string filter;
build_generic_filter(img, false, filter);
{
osd::text::tstring t_filter = osd::text::to_tstring(filter);
// convert a pipe-char delimited string into a NUL delimited string
for (int i = 0; t_filter[i] != '\0'; i++)
{
if (t_filter[i] == '|')
t_filter[i] = '\0';
}
char buf[400];
std::string as;
strcpy(buf, machine().options().emu_options::sw_path());
// This pulls out the first path from a multipath field
const char* t1 = strtok(buf, ";");
if (t1)
as = t1; // the first path of many
else
as = buf; // the only path
/* Make sure a folder was specified, and that it exists */
if ((!osd::directory::open(as.c_str())) || (as.find(':') == std::string::npos))
{
/* Default to emu directory */
osd_get_full_path(as, ".");
}
osd::text::tstring t_dir = osd::text::to_tstring(as);
TCHAR selectedFilename[MAX_PATH];
selectedFilename[0] = '\0';
OPENFILENAME ofn;
memset(&ofn, 0, sizeof(ofn));
ofn.lStructSize = sizeof(ofn);
ofn.hwndOwner = nullptr;
ofn.lpstrFile = selectedFilename;
ofn.lpstrFile[0] = '\0';
ofn.nMaxFile = MAX_PATH;
ofn.lpstrFilter = t_filter.c_str();
ofn.nFilterIndex = 1;
ofn.lpstrFileTitle = nullptr;
ofn.nMaxFileTitle = 0;
ofn.lpstrInitialDir = t_dir.c_str();
ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
if (GetOpenFileName(&ofn))
{
auto utf8_buf = osd::text::from_tstring(selectedFilename);
img->load(utf8_buf.c_str());
}
}
}
return true;
case DEVOPTION_CREATE:
{
std::string filter;
build_generic_filter(img, true, filter);
{
osd::text::tstring t_filter = osd::text::to_tstring(filter);
// convert a pipe-char delimited string into a NUL delimited string
for (int i = 0; t_filter[i] != '\0'; i++)
{
if (t_filter[i] == '|')
t_filter[i] = '\0';
}
char buf[400];
std::string as;
strcpy(buf, machine().options().emu_options::sw_path());
// This pulls out the first path from a multipath field
const char* t1 = strtok(buf, ";");
if (t1)
as = t1; // the first path of many
else
as = buf; // the only path
/* Make sure a folder was specified, and that it exists */
if ((!osd::directory::open(as.c_str())) || (as.find(':') == std::string::npos))
{
/* Default to emu directory */
osd_get_full_path(as, ".");
}
osd::text::tstring t_dir = osd::text::to_tstring(as);
TCHAR selectedFilename[MAX_PATH];
selectedFilename[0] = '\0';
OPENFILENAME ofn;
memset(&ofn, 0, sizeof(ofn));
ofn.lStructSize = sizeof(ofn);
ofn.hwndOwner = nullptr;
ofn.lpstrFile = selectedFilename;
ofn.lpstrFile[0] = '\0';
ofn.nMaxFile = MAX_PATH;
ofn.lpstrFilter = t_filter.c_str();
ofn.nFilterIndex = 1;
ofn.lpstrFileTitle = nullptr;
ofn.nMaxFileTitle = 0;
ofn.lpstrInitialDir = t_dir.c_str();
ofn.Flags = OFN_PATHMUSTEXIST;
if (GetSaveFileName(&ofn))
{
auto utf8_buf = osd::text::from_tstring(selectedFilename);
img->create(utf8_buf.c_str(), img->device_get_indexed_creatable_format(0), nullptr);
}
}
}
return true;
case DEVOPTION_CLOSE:
img->unload();
return true;
}
if (img->device().type() == CASSETTE)
{
cassette_image_device *const cassette = downcast<cassette_image_device *>(&img->device());
switch ((LOWORD(wparam) - ID_DEVICE_OPTIONS) % DEVOPTION_MAX)
{
case DEVOPTION_CASSETTE_STOPPAUSE:
cassette->change_state(CASSETTE_STOPPED, CASSETTE_MASK_UISTATE);
return true;
case DEVOPTION_CASSETTE_PLAY:
cassette->change_state(CASSETTE_PLAY, CASSETTE_MASK_UISTATE);
return true;
case DEVOPTION_CASSETTE_RECORD:
cassette->change_state(CASSETTE_RECORD, CASSETTE_MASK_UISTATE);
return true;
case DEVOPTION_CASSETTE_REWIND:
cassette->seek(-60.0, SEEK_CUR);
return true;
case DEVOPTION_CASSETTE_FASTFORWARD:
cassette->seek(+60.0, SEEK_CUR);
return true;
}
}
}
}
return disasmbasewin_info::handle_command(wparam, lparam);
}
void consolewin_info::process_string(std::string const &string)
{
if (string.empty()) // an empty string is a single step
machine().debugger().cpu().get_visible_cpu()->debug()->single_step();
else // otherwise, just process the command
machine().debugger().console().execute_command(string, true);
// clear the edit text box
set_editwnd_text("");
}
void consolewin_info::build_generic_filter(device_image_interface *img, bool is_save, std::string &filter)
{
std::string file_extension;
if (img)
file_extension = img->file_extensions();
if (!is_save)
file_extension.append(",zip,7z");
add_filter_entry(filter, "Common image types", file_extension.c_str());
filter.append("All files (*.*)|*.*|");
if (!is_save)
filter.append("Compressed Images (*.zip;*.7z)|*.zip;*.7z|");
}
void consolewin_info::add_filter_entry(std::string &dest, const char *description, const char *extensions)
{
// add the description
dest.append(description);
dest.append(" (");
// add the extensions to the description
copy_extension_list(dest, extensions);
// add the trailing rparen and '|' character
dest.append(")|");
// now add the extension list itself
copy_extension_list(dest, extensions);
// append a '|'
dest.append("|");
}
void consolewin_info::copy_extension_list(std::string &dest, const char *extensions)
{
// our extension lists are comma delimited; Win32 expects to see lists
// delimited by semicolons
char const *s = extensions;
while (*s)
{
// append a semicolon if not at the beginning
if (s != extensions)
dest.push_back(';');
// append ".*"
dest.append("*.");
// append the file extension
while (*s && (*s != ','))
dest.push_back(*s++);
// if we found a comma, advance
while(*s == ',')
s++;
}
}
//============================================================
// get_softlist_info
//============================================================
bool consolewin_info::get_softlist_info(device_image_interface *img)
{
bool has_software = false;
bool passes_tests = false;
std::string sl_dir, opt_name = img->instance_name();
// Get the path to suitable software
for (software_list_device &swlist : software_list_device_iterator(machine().root_device()))
{
for (const software_info &swinfo : swlist.get_info())
{
const software_part &part = swinfo.parts().front();
if (swlist.is_compatible(part) == SOFTWARE_IS_COMPATIBLE)
{
for (device_image_interface &image : image_interface_iterator(machine().root_device()))
{
if (!image.user_loadable())
continue;
if (!has_software && (opt_name == image.instance_name()))
{
const char *interface = image.image_interface();
if (interface && part.matches_interface(interface))
{
sl_dir = "\\" + swlist.list_name();
has_software = true;
}
}
}
}
}
}
if (has_software)
{
/* Get the media_path */
char rompath[400];
strcpy(rompath, machine().options().emu_options::media_path());
// Now, scan through the media_path looking for the required folder
char* sl_root = strtok(rompath, ";");
while (sl_root && !passes_tests)
{
std::string test_path = sl_root + sl_dir;
if (osd::directory::open(test_path.c_str()))
{
passes_tests = true;
slmap[opt_name] = test_path;
}
sl_root = strtok(NULL, ";");
}
}
return passes_tests;
}