mirror of https://github.com/PCSX2/pcsx2.git
Debugger: Revise file format for UI layouts
This commit is contained in:
parent
c359c0e747
commit
76fd6e33ce
|
@ -14,6 +14,7 @@
|
|||
|
||||
DebuggerWidget::DebuggerWidget(const DebuggerWidgetParameters& parameters, u32 flags)
|
||||
: QWidget(parameters.parent)
|
||||
, m_id(parameters.id)
|
||||
, m_unique_name(parameters.unique_name)
|
||||
, m_cpu(parameters.cpu)
|
||||
, m_cpu_override(parameters.cpu_override)
|
||||
|
@ -36,6 +37,11 @@ QString DebuggerWidget::uniqueName() const
|
|||
return m_unique_name;
|
||||
}
|
||||
|
||||
u64 DebuggerWidget::id() const
|
||||
{
|
||||
return m_id;
|
||||
}
|
||||
|
||||
QString DebuggerWidget::displayName() const
|
||||
{
|
||||
QString name = displayNameWithoutSuffix();
|
||||
|
|
|
@ -16,6 +16,7 @@ class JsonValueWrapper;
|
|||
struct DebuggerWidgetParameters
|
||||
{
|
||||
QString unique_name;
|
||||
u64 id = 0;
|
||||
DebugInterface* cpu = nullptr;
|
||||
std::optional<BreakPointCpu> cpu_override;
|
||||
QWidget* parent = nullptr;
|
||||
|
@ -28,6 +29,7 @@ class DebuggerWidget : public QWidget
|
|||
|
||||
public:
|
||||
QString uniqueName() const;
|
||||
u64 id() const;
|
||||
|
||||
// Get the translated name that should be displayed for this widget.
|
||||
QString displayName() const;
|
||||
|
@ -177,6 +179,12 @@ private:
|
|||
const char* action_prefix,
|
||||
std::function<const DebuggerEvents::Event*()> event_func);
|
||||
|
||||
// Used for sorting debugger widgets that have the same display name. Unique
|
||||
// within a single layout.
|
||||
u64 m_id;
|
||||
|
||||
// Identifier for the dock widget used by KDDockWidgets. Unique within a
|
||||
// single layout.
|
||||
QString m_unique_name;
|
||||
|
||||
// A user-defined name, or an empty string if no name was specified so that
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
const char* DEBUGGER_LAYOUT_FILE_FORMAT = "PCSX2 Debugger User Interface Layout";
|
||||
|
||||
// Increment this whenever there is a breaking change to the JSON format.
|
||||
const u32 DEBUGGER_LAYOUT_FILE_VERSION_MAJOR = 1;
|
||||
const u32 DEBUGGER_LAYOUT_FILE_VERSION_MAJOR = 2;
|
||||
|
||||
// Increment this whenever there is a non-breaking change to the JSON format.
|
||||
const u32 DEBUGGER_LAYOUT_FILE_VERSION_MINOR = 0;
|
||||
|
@ -70,7 +70,7 @@ DockLayout::DockLayout(
|
|||
: m_name(name)
|
||||
, m_cpu(cpu)
|
||||
, m_is_default(is_default)
|
||||
, m_next_unique_name(layout_to_clone.m_next_unique_name)
|
||||
, m_next_id(layout_to_clone.m_next_id)
|
||||
, m_base_layout(layout_to_clone.m_base_layout)
|
||||
, m_toolbars(layout_to_clone.m_toolbars)
|
||||
, m_geometry(layout_to_clone.m_geometry)
|
||||
|
@ -83,6 +83,7 @@ DockLayout::DockLayout(
|
|||
|
||||
DebuggerWidgetParameters parameters;
|
||||
parameters.unique_name = unique_name;
|
||||
parameters.id = widget_to_clone->id();
|
||||
parameters.cpu = &DebugInterface::get(cpu);
|
||||
parameters.cpu_override = widget_to_clone->cpuOverride();
|
||||
|
||||
|
@ -262,7 +263,7 @@ void DockLayout::reset()
|
|||
delete widget;
|
||||
}
|
||||
|
||||
m_next_unique_name = 0;
|
||||
m_next_id = 0;
|
||||
m_toolbars.clear();
|
||||
m_widgets.clear();
|
||||
m_geometry.clear();
|
||||
|
@ -278,7 +279,8 @@ void DockLayout::reset()
|
|||
const DockTables::DebuggerWidgetDescription& dock_description = iterator->second;
|
||||
|
||||
DebuggerWidgetParameters parameters;
|
||||
parameters.unique_name = generateNewUniqueName(base_layout->widgets[i].type.c_str());
|
||||
std::tie(parameters.unique_name, parameters.id) =
|
||||
generateNewUniqueName(base_layout->widgets[i].type.c_str());
|
||||
parameters.cpu = &DebugInterface::get(m_cpu);
|
||||
|
||||
if (parameters.unique_name.isEmpty())
|
||||
|
@ -330,7 +332,7 @@ void DockLayout::updateDockWidgetTitles()
|
|||
{
|
||||
std::sort(widgets.begin(), widgets.end(),
|
||||
[&](const DebuggerWidget* lhs, const DebuggerWidget* rhs) {
|
||||
return lhs->uniqueName() < rhs->uniqueName();
|
||||
return lhs->id() < rhs->id();
|
||||
});
|
||||
|
||||
for (size_t i = 0; i < widgets.size(); i++)
|
||||
|
@ -389,7 +391,7 @@ void DockLayout::createDebuggerWidget(const std::string& type)
|
|||
const DockTables::DebuggerWidgetDescription& description = description_iterator->second;
|
||||
|
||||
DebuggerWidgetParameters parameters;
|
||||
parameters.unique_name = generateNewUniqueName(type.c_str());
|
||||
std::tie(parameters.unique_name, parameters.id) = generateNewUniqueName(type.c_str());
|
||||
parameters.cpu = &DebugInterface::get(m_cpu);
|
||||
|
||||
if (parameters.unique_name.isEmpty())
|
||||
|
@ -428,6 +430,7 @@ void DockLayout::recreateDebuggerWidget(const QString& unique_name)
|
|||
|
||||
DebuggerWidgetParameters parameters;
|
||||
parameters.unique_name = old_debugger_widget->uniqueName();
|
||||
parameters.id = old_debugger_widget->id();
|
||||
parameters.cpu = &DebugInterface::get(m_cpu);
|
||||
parameters.cpu_override = old_debugger_widget->cpuOverride();
|
||||
|
||||
|
@ -545,24 +548,22 @@ bool DockLayout::save(DockLayout::Index layout_index)
|
|||
rapidjson::Document geometry;
|
||||
|
||||
const char* cpu_name = DebugInterface::cpuName(m_cpu);
|
||||
const std::string& default_layouts_hash = DockTables::hashDefaultLayouts();
|
||||
u32 default_layout_hash = DockTables::hashDefaultLayouts();
|
||||
|
||||
rapidjson::Value format;
|
||||
format.SetString(DEBUGGER_LAYOUT_FILE_FORMAT, strlen(DEBUGGER_LAYOUT_FILE_FORMAT));
|
||||
json.AddMember("format", format, json.GetAllocator());
|
||||
|
||||
json.AddMember("version_major", DEBUGGER_LAYOUT_FILE_VERSION_MAJOR, json.GetAllocator());
|
||||
json.AddMember("version_minor", DEBUGGER_LAYOUT_FILE_VERSION_MINOR, json.GetAllocator());
|
||||
rapidjson::Value version_hash;
|
||||
version_hash.SetString(default_layouts_hash.c_str(), default_layouts_hash.size());
|
||||
json.AddMember("version_hash", version_hash, json.GetAllocator());
|
||||
json.AddMember("versionMajor", DEBUGGER_LAYOUT_FILE_VERSION_MAJOR, json.GetAllocator());
|
||||
json.AddMember("versionMinor", DEBUGGER_LAYOUT_FILE_VERSION_MINOR, json.GetAllocator());
|
||||
json.AddMember("defaultLayoutHash", default_layout_hash, json.GetAllocator());
|
||||
|
||||
std::string name_str = m_name.toStdString();
|
||||
json.AddMember("name", rapidjson::Value().SetString(name_str.c_str(), name_str.size()), json.GetAllocator());
|
||||
json.AddMember("target", rapidjson::Value().SetString(cpu_name, strlen(cpu_name)), json.GetAllocator());
|
||||
json.AddMember("index", static_cast<int>(layout_index), json.GetAllocator());
|
||||
json.AddMember("isDefault", m_is_default, json.GetAllocator());
|
||||
json.AddMember("nextUniqueName", m_next_unique_name, json.GetAllocator());
|
||||
json.AddMember("nextId", m_next_id, json.GetAllocator());
|
||||
|
||||
if (!m_base_layout.empty())
|
||||
{
|
||||
|
@ -590,6 +591,7 @@ bool DockLayout::save(DockLayout::Index layout_index)
|
|||
rapidjson::Value name;
|
||||
name.SetString(name_str.c_str(), name_str.size(), json.GetAllocator());
|
||||
object.AddMember("uniqueName", name, json.GetAllocator());
|
||||
object.AddMember("id", widget->id(), json.GetAllocator());
|
||||
|
||||
const char* type_str = widget->metaObject()->className();
|
||||
rapidjson::Value type;
|
||||
|
@ -687,11 +689,11 @@ void DockLayout::load(
|
|||
return;
|
||||
}
|
||||
|
||||
auto version_major = json.FindMember("version_major");
|
||||
auto version_major = json.FindMember("versionMajor");
|
||||
if (version_major == json.MemberEnd() || !version_major->value.IsInt())
|
||||
{
|
||||
Console.Error("Debugger: Layout file '%s' has missing or invalid 'version_major' property.", path.c_str());
|
||||
result = INVALID_FORMAT;
|
||||
Console.Error("Debugger: Layout file '%s' has missing or invalid 'versionMajor' property.", path.c_str());
|
||||
result = MAJOR_VERSION_MISMATCH;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -701,23 +703,23 @@ void DockLayout::load(
|
|||
return;
|
||||
}
|
||||
|
||||
auto version_minor = json.FindMember("version_minor");
|
||||
auto version_minor = json.FindMember("versionMinor");
|
||||
if (version_minor == json.MemberEnd() || !version_minor->value.IsInt())
|
||||
{
|
||||
Console.Error("Debugger: Layout file '%s' has missing or invalid 'version_minor' property.", path.c_str());
|
||||
result = INVALID_FORMAT;
|
||||
Console.Error("Debugger: Layout file '%s' has missing or invalid 'versionMinor' property.", path.c_str());
|
||||
result = MAJOR_VERSION_MISMATCH;
|
||||
return;
|
||||
}
|
||||
|
||||
auto version_hash = json.FindMember("version_hash");
|
||||
if (version_hash == json.MemberEnd() || !version_hash->value.IsString())
|
||||
auto default_layout_hash = json.FindMember("defaultLayoutHash");
|
||||
if (default_layout_hash == json.MemberEnd() || !default_layout_hash->value.IsUint())
|
||||
{
|
||||
Console.Error("Debugger: Layout file '%s' has missing or invalid 'version_hash' property.", path.c_str());
|
||||
result = INVALID_FORMAT;
|
||||
Console.Error("Debugger: Layout file '%s' has missing or invalid 'defaultLayoutHash' property.", path.c_str());
|
||||
result = MAJOR_VERSION_MISMATCH;
|
||||
return;
|
||||
}
|
||||
|
||||
if (strcmp(version_hash->value.GetString(), DockTables::hashDefaultLayouts().c_str()) != 0)
|
||||
if (default_layout_hash->value.GetUint() != DockTables::hashDefaultLayouts())
|
||||
result = DEFAULT_LAYOUT_HASH_MISMATCH;
|
||||
|
||||
auto name = json.FindMember("name");
|
||||
|
@ -745,9 +747,9 @@ void DockLayout::load(
|
|||
if (is_default != json.MemberEnd() && is_default->value.IsBool())
|
||||
m_is_default = is_default->value.GetBool();
|
||||
|
||||
auto next_unique_name = json.FindMember("nextUniqueName");
|
||||
if (next_unique_name != json.MemberBegin() && next_unique_name->value.IsInt())
|
||||
m_next_unique_name = next_unique_name->value.GetInt();
|
||||
auto next_id = json.FindMember("nextId");
|
||||
if (next_id != json.MemberBegin() && next_id->value.IsUint64())
|
||||
m_next_id = next_id->value.GetUint64();
|
||||
|
||||
auto base_layout = json.FindMember("baseLayout");
|
||||
if (base_layout != json.MemberEnd() && base_layout->value.IsString())
|
||||
|
@ -766,6 +768,10 @@ void DockLayout::load(
|
|||
if (unique_name == object.MemberEnd() || !unique_name->value.IsString())
|
||||
continue;
|
||||
|
||||
auto id = object.FindMember("id");
|
||||
if (id == object.MemberEnd() || !id->value.IsUint64())
|
||||
continue;
|
||||
|
||||
auto widgets_iterator = m_widgets.find(unique_name->value.GetString());
|
||||
if (widgets_iterator != m_widgets.end())
|
||||
continue;
|
||||
|
@ -790,6 +796,7 @@ void DockLayout::load(
|
|||
|
||||
DebuggerWidgetParameters parameters;
|
||||
parameters.unique_name = unique_name->value.GetString();
|
||||
parameters.id = id->value.GetUint64();
|
||||
parameters.cpu = &DebugInterface::get(m_cpu);
|
||||
parameters.cpu_override = cpu_override;
|
||||
|
||||
|
@ -900,19 +907,20 @@ void DockLayout::setupDefaultLayout()
|
|||
group->setCurrentTabIndex(0);
|
||||
}
|
||||
|
||||
QString DockLayout::generateNewUniqueName(const char* type)
|
||||
std::pair<QString, u64> DockLayout::generateNewUniqueName(const char* type)
|
||||
{
|
||||
QString name;
|
||||
u64 id;
|
||||
|
||||
do
|
||||
{
|
||||
if (m_next_unique_name == INT_MAX)
|
||||
return QString();
|
||||
if (m_next_id == INT_MAX)
|
||||
return {QString(), 0};
|
||||
|
||||
// Produce unique names that will lexicographically sort in the order
|
||||
// they were allocated. This ensures the #1, #2, etc suffixes for dock
|
||||
// widgets with conflicting names will be assigned in the correct order.
|
||||
name = QStringLiteral("%1-%2").arg(m_next_unique_name, 16, 10, QLatin1Char('0')).arg(type);
|
||||
m_next_unique_name++;
|
||||
id = m_next_id;
|
||||
name = QStringLiteral("%1-%2").arg(type).arg(static_cast<qulonglong>(m_next_id));
|
||||
m_next_id++;
|
||||
} while (hasDebuggerWidget(name));
|
||||
return name;
|
||||
|
||||
return {name, id};
|
||||
}
|
||||
|
|
|
@ -120,7 +120,7 @@ private:
|
|||
|
||||
void setupDefaultLayout();
|
||||
|
||||
QString generateNewUniqueName(const char* type);
|
||||
std::pair<QString, u64> generateNewUniqueName(const char* type);
|
||||
|
||||
// The name displayed in the user interface. Also used to determine the
|
||||
// file name for the layout file.
|
||||
|
@ -134,7 +134,7 @@ private:
|
|||
bool m_is_default = false;
|
||||
|
||||
// A counter used to generate new unique names for dock widgets.
|
||||
int m_next_unique_name = 0;
|
||||
u64 m_next_id = 0;
|
||||
|
||||
// The name of the default layout which this layout was based on. This will
|
||||
// be used if the m_geometry variable above is empty.
|
||||
|
|
|
@ -14,12 +14,14 @@
|
|||
#include "Debugger/Memory/SavedAddressesWidget.h"
|
||||
#include "Debugger/SymbolTree/SymbolTreeWidgets.h"
|
||||
|
||||
#include "common/MD5Digest.h"
|
||||
|
||||
#include "fmt/format.h"
|
||||
|
||||
using namespace DockUtils;
|
||||
|
||||
static void hashDefaultLayout(const DockTables::DefaultDockLayout& layout, u32& hash);
|
||||
static void hashDefaultGroup(const DockTables::DefaultDockGroupDescription& group, u32& hash);
|
||||
static void hashDefaultDockWidget(const DockTables::DefaultDockWidgetDescription& widget, u32& hash);
|
||||
static void hashNumber(u32 number, u32& hash);
|
||||
static void hashString(const char* string, u32& hash);
|
||||
|
||||
#define DEBUGGER_WIDGET(type, display_name, preferred_location) \
|
||||
{ \
|
||||
#type, \
|
||||
|
@ -123,67 +125,43 @@ const DockTables::DefaultDockLayout* DockTables::defaultLayout(const std::string
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
const std::string& DockTables::hashDefaultLayouts()
|
||||
u32 DockTables::hashDefaultLayouts()
|
||||
{
|
||||
static std::string hash;
|
||||
if (!hash.empty())
|
||||
return hash;
|
||||
static std::optional<u32> hash;
|
||||
if (hash.has_value())
|
||||
return *hash;
|
||||
|
||||
MD5Digest md5;
|
||||
hash.emplace(0);
|
||||
|
||||
u32 hash_version = 1;
|
||||
md5.Update(&hash_version, sizeof(hash_version));
|
||||
|
||||
u32 layout_count = static_cast<u32>(DEFAULT_DOCK_LAYOUTS.size());
|
||||
md5.Update(&layout_count, sizeof(layout_count));
|
||||
u32 hash_version = 2;
|
||||
hashNumber(hash_version, *hash);
|
||||
|
||||
hashNumber(static_cast<u32>(DEFAULT_DOCK_LAYOUTS.size()), *hash);
|
||||
for (const DefaultDockLayout& layout : DEFAULT_DOCK_LAYOUTS)
|
||||
hashDefaultLayout(layout, md5);
|
||||
hashDefaultLayout(layout, *hash);
|
||||
|
||||
u8 digest[16];
|
||||
md5.Final(digest);
|
||||
hash = fmt::format(
|
||||
"{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}",
|
||||
digest[0], digest[1], digest[2], digest[3], digest[4], digest[5], digest[6], digest[7],
|
||||
digest[8], digest[9], digest[10], digest[11], digest[12], digest[13], digest[14], digest[15]);
|
||||
|
||||
return hash;
|
||||
return *hash;
|
||||
}
|
||||
|
||||
void DockTables::hashDefaultLayout(const DefaultDockLayout& layout, MD5Digest& md5)
|
||||
static void hashDefaultLayout(const DockTables::DefaultDockLayout& layout, u32& hash)
|
||||
{
|
||||
u32 layout_name_size = static_cast<u32>(layout.name.size());
|
||||
md5.Update(&layout_name_size, sizeof(layout_name_size));
|
||||
md5.Update(layout.name.data(), layout_name_size);
|
||||
hashString(layout.name.c_str(), hash);
|
||||
hashString(DebugInterface::cpuName(layout.cpu), hash);
|
||||
|
||||
const char* cpu_name = DebugInterface::cpuName(layout.cpu);
|
||||
u32 cpu_name_size = static_cast<u32>(strlen(cpu_name));
|
||||
md5.Update(&cpu_name_size, sizeof(cpu_name_size));
|
||||
md5.Update(cpu_name, cpu_name_size);
|
||||
hashNumber(static_cast<u32>(layout.groups.size()), hash);
|
||||
for (const DockTables::DefaultDockGroupDescription& group : layout.groups)
|
||||
hashDefaultGroup(group, hash);
|
||||
|
||||
u32 group_count = static_cast<u32>(layout.groups.size());
|
||||
md5.Update(&group_count, sizeof(group_count));
|
||||
hashNumber(static_cast<u32>(layout.widgets.size()), hash);
|
||||
for (const DockTables::DefaultDockWidgetDescription& widget : layout.widgets)
|
||||
hashDefaultDockWidget(widget, hash);
|
||||
|
||||
for (const DefaultDockGroupDescription& group : layout.groups)
|
||||
hashDefaultGroup(group, md5);
|
||||
|
||||
u32 widget_count = static_cast<u32>(layout.widgets.size());
|
||||
md5.Update(&widget_count, sizeof(widget_count));
|
||||
|
||||
for (const DefaultDockWidgetDescription& widget : layout.widgets)
|
||||
hashDefaultDockWidget(widget, md5);
|
||||
|
||||
u32 toolbar_count = static_cast<u32>(layout.toolbars.size());
|
||||
md5.Update(&toolbar_count, sizeof(toolbar_count));
|
||||
hashNumber(static_cast<u32>(layout.toolbars.size()), hash);
|
||||
for (const std::string& toolbar : layout.toolbars)
|
||||
{
|
||||
u32 toolbar_size = toolbar.size();
|
||||
md5.Update(&toolbar_size, sizeof(toolbar_size));
|
||||
md5.Update(toolbar.data(), toolbar.size());
|
||||
}
|
||||
hashString(toolbar.c_str(), hash);
|
||||
}
|
||||
|
||||
void DockTables::hashDefaultGroup(const DefaultDockGroupDescription& group, MD5Digest& md5)
|
||||
static void hashDefaultGroup(const DockTables::DefaultDockGroupDescription& group, u32& hash)
|
||||
{
|
||||
// This is inline here so that it's obvious that changing it will affect the
|
||||
// result of the hash.
|
||||
|
@ -207,20 +185,25 @@ void DockTables::hashDefaultGroup(const DefaultDockGroupDescription& group, MD5D
|
|||
break;
|
||||
}
|
||||
|
||||
u32 location_size = static_cast<u32>(strlen(location));
|
||||
md5.Update(&location_size, sizeof(location_size));
|
||||
md5.Update(location, location_size);
|
||||
|
||||
u32 parent = static_cast<u32>(group.parent);
|
||||
md5.Update(&parent, sizeof(parent));
|
||||
hashString(location, hash);
|
||||
hashNumber(static_cast<u32>(group.parent), hash);
|
||||
}
|
||||
|
||||
void DockTables::hashDefaultDockWidget(const DefaultDockWidgetDescription& widget, MD5Digest& md5)
|
||||
static void hashDefaultDockWidget(const DockTables::DefaultDockWidgetDescription& widget, u32& hash)
|
||||
{
|
||||
u32 type_size = static_cast<u32>(widget.type.size());
|
||||
md5.Update(&type_size, sizeof(type_size));
|
||||
md5.Update(widget.type.data(), type_size);
|
||||
|
||||
u32 group = static_cast<u32>(widget.group);
|
||||
md5.Update(&group, sizeof(group));
|
||||
hashString(widget.type.c_str(), hash);
|
||||
hashNumber(static_cast<u32>(widget.group), hash);
|
||||
}
|
||||
|
||||
static void hashNumber(u32 number, u32& hash)
|
||||
{
|
||||
hash = hash * 31 + number;
|
||||
}
|
||||
|
||||
static void hashString(const char* string, u32& hash)
|
||||
{
|
||||
u32 size = static_cast<u32>(strlen(string));
|
||||
hash = hash * 31 + size;
|
||||
for (u32 i = 0; i < size; i++)
|
||||
hash = hash * 31 + string[i];
|
||||
}
|
||||
|
|
|
@ -67,9 +67,5 @@ namespace DockTables
|
|||
|
||||
// This is used to determine if the user has updated and we need to recreate
|
||||
// the default layouts.
|
||||
const std::string& hashDefaultLayouts();
|
||||
|
||||
void hashDefaultLayout(const DefaultDockLayout& layout, MD5Digest& md5);
|
||||
void hashDefaultGroup(const DefaultDockGroupDescription& group, MD5Digest& md5);
|
||||
void hashDefaultDockWidget(const DefaultDockWidgetDescription& widget, MD5Digest& md5);
|
||||
u32 hashDefaultLayouts();
|
||||
} // namespace DockTables
|
||||
|
|
Loading…
Reference in New Issue