mirror of https://github.com/xemu-project/xemu.git
ui: Add compatibility report dialog
This commit is contained in:
parent
d31e10cc59
commit
f8ac9ec658
|
@ -105,6 +105,10 @@ all: $(PROGS) stap
|
|||
|
||||
obj-y += trace/
|
||||
|
||||
#########################################################
|
||||
# xemu
|
||||
obj-y += xemu-xbe.o
|
||||
|
||||
#########################################################
|
||||
# cpu emulator library
|
||||
obj-y += exec.o exec-vary.o
|
||||
|
|
|
@ -45,15 +45,15 @@ ui/xemu-shaders.o: ui/shader/xemu-logo-frag.h
|
|||
|
||||
ifeq ($(CONFIG_WIN32),y)
|
||||
IMGUI_FLAGS = -DWIN32 -DMINGW32
|
||||
sdl.mo-objs := $(sdl.mo-objs) noc_file_dialog_win32.o
|
||||
sdl.mo-objs := $(sdl.mo-objs) noc_file_dialog_win32.o xemu-os-utils-win32.o
|
||||
endif
|
||||
ifeq ($(CONFIG_LINUX),y)
|
||||
IMGUI_FLAGS = -DLINUX
|
||||
sdl.mo-objs := $(sdl.mo-objs) noc_file_dialog_gtk.o
|
||||
sdl.mo-objs := $(sdl.mo-objs) noc_file_dialog_gtk.o xemu-os-utils-linux.o
|
||||
endif
|
||||
ifeq ($(CONFIG_DARWIN),y)
|
||||
IMGUI_FLAGS = -DAPPLE
|
||||
sdl.mo-objs := $(sdl.mo-objs) noc_file_dialog_macos.o
|
||||
sdl.mo-objs := $(sdl.mo-objs) noc_file_dialog_macos.o xemu-os-utils-macos.o
|
||||
endif
|
||||
|
||||
sdl.mo-cflags := $(SDL_CFLAGS) -DIMGUI_IMPL_OPENGL_LOADER_CUSTOM="\"epoxy/gl.h\"" -Iui/imgui $(IMGUI_FLAGS)
|
||||
|
|
174
ui/xemu-hud.cc
174
ui/xemu-hud.cc
|
@ -57,6 +57,7 @@ extern "C" {
|
|||
#include "qapi/qmp/qdict.h"
|
||||
#include "qemu/option.h"
|
||||
#include "qemu/config-file.h"
|
||||
|
||||
#undef typename
|
||||
#undef atomic_fetch_add
|
||||
#undef atomic_fetch_and
|
||||
|
@ -99,6 +100,9 @@ static void ShowAboutWindow(bool* p_open);
|
|||
bool show_network_window = false;
|
||||
static void ShowNetworkWindow(bool* p_open);
|
||||
|
||||
bool show_compatibility_reporter_window = true;
|
||||
static void ShowCompatibilityReporter(bool* p_open);
|
||||
|
||||
bool show_demo_window = false;
|
||||
|
||||
float ui_scale = 1.0;
|
||||
|
@ -304,6 +308,8 @@ static void ShowMainMenu()
|
|||
}
|
||||
if (ImGui::BeginMenu("Help"))
|
||||
{
|
||||
ImGui::MenuItem("Report Compatibility", NULL, &show_compatibility_reporter_window);
|
||||
ImGui::Separator();
|
||||
ImGui::MenuItem("About", NULL, &show_about_window);
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
@ -532,6 +538,7 @@ void xemu_hud_render(SDL_Window *window)
|
|||
if (show_monitor_window) ShowMonitorConsole(&show_monitor_window);
|
||||
if (show_about_window) ShowAboutWindow(&show_about_window);
|
||||
if (show_network_window) ShowNetworkWindow(&show_network_window);
|
||||
if (show_compatibility_reporter_window) ShowCompatibilityReporter(&show_compatibility_reporter_window);
|
||||
if (show_demo_window) ImGui::ShowDemoWindow(&show_demo_window);
|
||||
|
||||
if (notification.active) {
|
||||
|
@ -1318,7 +1325,7 @@ struct NetworkWindow
|
|||
bool is_enabled = xemu_net_is_enabled();
|
||||
|
||||
ImGui::TextWrapped(
|
||||
"xemu socket networking works by sending and recieving packets over "
|
||||
"xemu socket networking works by sending and receiving packets over "
|
||||
"UDP which encapsulate the network traffic that the machine would "
|
||||
"send or recieve when connected to a Local Area Network (LAN)."
|
||||
);
|
||||
|
@ -1385,3 +1392,168 @@ static void ShowNetworkWindow(bool* p_open)
|
|||
static NetworkWindow console;
|
||||
console.Draw("Network", p_open);
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
// https://stackoverflow.com/a/2513561
|
||||
#include <windows.h>
|
||||
unsigned long long getTotalSystemMemory()
|
||||
{
|
||||
MEMORYSTATUSEX status;
|
||||
status.dwLength = sizeof(status);
|
||||
GlobalMemoryStatusEx(&status);
|
||||
return status.ullTotalPhys / (1024 * 1024);
|
||||
}
|
||||
#else
|
||||
#include <unistd.h>
|
||||
unsigned long long getTotalSystemMemory()
|
||||
{
|
||||
long pages = sysconf(_SC_PHYS_PAGES);
|
||||
long page_size = sysconf(_SC_PAGE_SIZE);
|
||||
return pages * page_size / (1024 * 1024);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_CPUID_H
|
||||
#include <cpuid.h>
|
||||
#endif
|
||||
|
||||
const char *get_cpu_info(void)
|
||||
{
|
||||
const char *cpu_info = "";
|
||||
#ifdef CONFIG_CPUID_H
|
||||
static uint32_t brand[12];
|
||||
if (__get_cpuid_max(0x80000004, NULL)) {
|
||||
__get_cpuid(0x80000002, brand+0x0, brand+0x1, brand+0x2, brand+0x3);
|
||||
__get_cpuid(0x80000003, brand+0x4, brand+0x5, brand+0x6, brand+0x7);
|
||||
__get_cpuid(0x80000004, brand+0x8, brand+0x9, brand+0xa, brand+0xb);
|
||||
}
|
||||
cpu_info = (const char *)brand;
|
||||
#endif
|
||||
// FIXME: Support other architectures (e.g. ARM)
|
||||
return cpu_info;
|
||||
}
|
||||
|
||||
#include "xemu-os-utils.h"
|
||||
#include "xemu-xbe.h"
|
||||
|
||||
struct CompatibilityReporter
|
||||
{
|
||||
CompatibilityReporter()
|
||||
{
|
||||
}
|
||||
|
||||
~CompatibilityReporter()
|
||||
{
|
||||
}
|
||||
|
||||
void Draw(const char* title, bool* p_open)
|
||||
{
|
||||
ImVec2 size(450*ui_scale, 475*ui_scale);
|
||||
|
||||
ImGui::SetNextWindowSize(size, ImGuiCond_Appearing);
|
||||
if (!ImGui::Begin(title, p_open, ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse)) {
|
||||
ImGui::End();
|
||||
return;
|
||||
}
|
||||
|
||||
static char gpu_info[1024];
|
||||
static char xbe_info[512];
|
||||
static char report_info[4096];
|
||||
static int report_info_initialized = 0;
|
||||
|
||||
if (ImGui::IsWindowAppearing()) {
|
||||
// Refresh whenever the window is re-opened
|
||||
report_info_initialized = 0;
|
||||
}
|
||||
|
||||
if (!report_info_initialized) {
|
||||
struct xbe_info *xbe = xemu_get_xbe_info();
|
||||
snprintf(
|
||||
gpu_info,
|
||||
sizeof(gpu_info),
|
||||
"%s, %s, %s, %s",
|
||||
glGetString(GL_VENDOR),
|
||||
glGetString(GL_RENDERER),
|
||||
glGetString(GL_VERSION),
|
||||
glGetString(GL_SHADING_LANGUAGE_VERSION)
|
||||
);
|
||||
|
||||
if (xbe) {
|
||||
snprintf(xbe_info, sizeof(xbe_info),
|
||||
"%s v1.%02d", xbe->cert_title_id_str, xbe->cert_version);
|
||||
} else {
|
||||
xbe_info[0] = '\x00';
|
||||
}
|
||||
|
||||
snprintf(
|
||||
report_info,
|
||||
sizeof(report_info),
|
||||
"xemu: %s [branch %s on %s]\n"
|
||||
"OS: %s\n"
|
||||
"CPU: %s\n"
|
||||
"GPU: %s\n"
|
||||
"Memory: %lld M\n"
|
||||
"XBE: %s",
|
||||
xemu_version,
|
||||
xemu_branch,
|
||||
xemu_date,
|
||||
xemu_get_os_info(),
|
||||
get_cpu_info(),
|
||||
gpu_info,
|
||||
getTotalSystemMemory(),
|
||||
xbe_info
|
||||
);
|
||||
|
||||
report_info_initialized = 1;
|
||||
}
|
||||
|
||||
ImGui::TextWrapped(
|
||||
"If you would like to submit a compatibility report for this "
|
||||
"title, including some basic information about your system listed "
|
||||
"below, please select an appropriate playability level, enter a "
|
||||
"brief description of your experience, then click 'Send.' Note: "
|
||||
"this information may be made publicly available.");
|
||||
|
||||
ImGui::Dummy(ImVec2(0, 5*ui_scale));
|
||||
ImGui::Separator();
|
||||
ImGui::Dummy(ImVec2(0, 5*ui_scale));
|
||||
|
||||
static int playability;
|
||||
ImGui::Combo("Playability Rating", &playability,
|
||||
"Unknown\0" "Broken\0" "Intro/Menus\0" "Starts\0" "Playable\0" "Perfect\0");
|
||||
|
||||
char buf[64];
|
||||
buf[0] = '\x00';
|
||||
ImGui::InputText("Contributor Token", buf, sizeof(buf), 0);
|
||||
ImGui::SameLine();
|
||||
HelpMarker("Optional. This is a unique token that trusted users may "
|
||||
"provide in order to expedite publication of their compatibility "
|
||||
"reports.");
|
||||
|
||||
char description[255] = {0};
|
||||
ImGui::Text("Description");
|
||||
ImGui::InputTextMultiline("###desc", description, sizeof(description), ImVec2(-FLT_MIN, ImGui::GetTextLineHeight() * 6), 0);
|
||||
|
||||
ImGui::Text("Additional Information");
|
||||
ImGui::PushFont(fixed_width_font);
|
||||
ImGui::InputTextMultiline("##build_info", report_info, IM_ARRAYSIZE(report_info), ImVec2(-FLT_MIN, ImGui::GetTextLineHeight() * 7), ImGuiInputTextFlags_ReadOnly);
|
||||
ImGui::PopFont();
|
||||
|
||||
ImGui::Columns(1);
|
||||
|
||||
ImGui::SetCursorPosY(ImGui::GetWindowHeight()-(10+25)*ui_scale);
|
||||
ImGui::SetCursorPosX(ImGui::GetWindowWidth()-(120+10)*ui_scale);
|
||||
|
||||
ImGui::SetItemDefaultFocus();
|
||||
if (ImGui::Button("Send", ImVec2(120*ui_scale, 0))) {
|
||||
}
|
||||
|
||||
ImGui::End();
|
||||
}
|
||||
};
|
||||
|
||||
static void ShowCompatibilityReporter(bool* p_open)
|
||||
{
|
||||
static CompatibilityReporter console;
|
||||
console.Draw("Report Compatibility", p_open);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
#include "xemu-os-utils.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <glib.h>
|
||||
#include <glib/gprintf.h>
|
||||
|
||||
static char *read_file_if_possible(const char *path)
|
||||
{
|
||||
FILE *fd = fopen(path, "rb");
|
||||
if (fd == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
fseek(fd, 0, SEEK_END);
|
||||
size_t size = ftell(fd);
|
||||
fseek(fd, 0, SEEK_SET);
|
||||
|
||||
char *buf = malloc(size+1);
|
||||
int status = fread(buf, 1, size, fd);
|
||||
if (status != size) {
|
||||
free(buf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
buf[size] = '\x00';
|
||||
return buf;
|
||||
}
|
||||
|
||||
const char *xemu_get_os_info(void)
|
||||
{
|
||||
static const char *os_info = NULL;
|
||||
static int attempted_init = 0;
|
||||
|
||||
if (!attempted_init) {
|
||||
char *os_release = NULL;
|
||||
|
||||
// Try to get the Linux distro "pretty name" from /etc/os-release
|
||||
char *os_release_file = read_file_if_possible("/etc/os-release");
|
||||
if (os_release_file != NULL) {
|
||||
char *pretty_name = strstr(os_release_file, "PRETTY_NAME=\"");
|
||||
if (pretty_name != NULL) {
|
||||
pretty_name = pretty_name + 13;
|
||||
char *pretty_name_end = strchr(pretty_name, '"');
|
||||
if (pretty_name_end != NULL) {
|
||||
size_t len = pretty_name_end-pretty_name;
|
||||
os_release = malloc(len+1);
|
||||
assert(os_release != NULL);
|
||||
memcpy(os_release, pretty_name, len);
|
||||
os_release[len] = '\x00';
|
||||
}
|
||||
}
|
||||
free(os_release_file);
|
||||
}
|
||||
|
||||
os_info = g_strdup_printf("%s",
|
||||
os_release ? os_release : "Unknown Distro"
|
||||
);
|
||||
if (os_release) {
|
||||
free(os_release);
|
||||
}
|
||||
|
||||
attempted_init = 1;
|
||||
}
|
||||
|
||||
return os_info;
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
#import <Foundation/Foundation.h>
|
||||
#include "xemu-os-utils.h"
|
||||
|
||||
const char *xemu_get_os_info(void)
|
||||
{
|
||||
return [[[NSProcessInfo processInfo] operatingSystemVersionString] UTF8String];
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
#include "xemu-os-utils.h"
|
||||
|
||||
const char *xemu_get_os_info(void)
|
||||
{
|
||||
return "Windows";
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
#ifndef XEMU_OS_UTILS_H
|
||||
#define XEMU_OS_UTILS_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
const char *xemu_get_os_info(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -0,0 +1,149 @@
|
|||
#include "xemu-xbe.h"
|
||||
#include "qemu/osdep.h"
|
||||
#include "hw/hw.h"
|
||||
#include "hw/i386/pc.h"
|
||||
#include "hw/pci/pci.h"
|
||||
#include "monitor/hmp-target.h"
|
||||
#include "sysemu/hw_accel.h"
|
||||
|
||||
#if 0
|
||||
// http://www.caustik.com/cxbx/download/xbe.htm
|
||||
struct xbe_header
|
||||
{
|
||||
uint32_t m_magic; // magic number [should be "XBEH"]
|
||||
uint8_t m_digsig[256]; // digital signature
|
||||
uint32_t m_base; // base address
|
||||
uint32_t m_sizeof_headers; // size of headers
|
||||
uint32_t m_sizeof_image; // size of image
|
||||
uint32_t m_sizeof_image_header; // size of image header
|
||||
uint32_t m_timedate; // timedate stamp
|
||||
uint32_t m_certificate_addr; // certificate address
|
||||
uint32_t m_sections; // number of sections
|
||||
uint32_t m_section_headers_addr; // section headers address
|
||||
|
||||
struct init_flags
|
||||
{
|
||||
uint32_t m_mount_utility_drive : 1; // mount utility drive flag
|
||||
uint32_t m_format_utility_drive : 1; // format utility drive flag
|
||||
uint32_t m_limit_64mb : 1; // limit development kit run time memory to 64mb flag
|
||||
uint32_t m_dont_setup_harddisk : 1; // don't setup hard disk flag
|
||||
uint32_t m_unused : 4; // unused (or unknown)
|
||||
uint32_t m_unused_b1 : 8; // unused (or unknown)
|
||||
uint32_t m_unused_b2 : 8; // unused (or unknown)
|
||||
uint32_t m_unused_b3 : 8; // unused (or unknown)
|
||||
} m_init_flags;
|
||||
|
||||
uint32_t m_entry; // entry point address
|
||||
uint32_t m_tls_addr; // thread local storage directory address
|
||||
uint32_t m_pe_stack_commit; // size of stack commit
|
||||
uint32_t m_pe_heap_reserve; // size of heap reserve
|
||||
uint32_t m_pe_heap_commit; // size of heap commit
|
||||
uint32_t m_pe_base_addr; // original base address
|
||||
uint32_t m_pe_sizeof_image; // size of original image
|
||||
uint32_t m_pe_checksum; // original checksum
|
||||
uint32_t m_pe_timedate; // original timedate stamp
|
||||
uint32_t m_debug_pathname_addr; // debug pathname address
|
||||
uint32_t m_debug_filename_addr; // debug filename address
|
||||
uint32_t m_debug_unicode_filename_addr; // debug unicode filename address
|
||||
uint32_t m_kernel_image_thunk_addr; // kernel image thunk address
|
||||
uint32_t m_nonkernel_import_dir_addr; // non kernel import directory address
|
||||
uint32_t m_library_versions; // number of library versions
|
||||
uint32_t m_library_versions_addr; // library versions address
|
||||
uint32_t m_kernel_library_version_addr; // kernel library version address
|
||||
uint32_t m_xapi_library_version_addr; // xapi library version address
|
||||
uint32_t m_logo_bitmap_addr; // logo bitmap address
|
||||
uint32_t m_logo_bitmap_size; // logo bitmap size
|
||||
};
|
||||
|
||||
struct xbe_certificate
|
||||
{
|
||||
uint32_t m_size; // size of certificate
|
||||
uint32_t m_timedate; // timedate stamp
|
||||
uint32_t m_titleid; // title id
|
||||
uint16_t m_title_name[40]; // title name (unicode)
|
||||
uint32_t m_alt_title_id[0x10]; // alternate title ids
|
||||
uint32_t m_allowed_media; // allowed media types
|
||||
uint32_t m_game_region; // game region
|
||||
uint32_t m_game_ratings; // game ratings
|
||||
uint32_t m_disk_number; // disk number
|
||||
uint32_t m_version; // version
|
||||
uint8_t m_lan_key[16]; // lan key
|
||||
uint8_t m_sig_key[16]; // signature key
|
||||
uint8_t m_title_alt_sig_key[16][16]; // alternate signature keys
|
||||
};
|
||||
#endif
|
||||
|
||||
static int virt_to_phys(target_ulong virt_addr, hwaddr *phys_addr)
|
||||
{
|
||||
MemTxAttrs attrs;
|
||||
CPUState *cs;
|
||||
hwaddr gpa;
|
||||
|
||||
cs = qemu_get_cpu(0);
|
||||
if (!cs) {
|
||||
return 1; // No cpu
|
||||
}
|
||||
|
||||
cpu_synchronize_state(cs);
|
||||
|
||||
gpa = cpu_get_phys_page_attrs_debug(cs, virt_addr & TARGET_PAGE_MASK, &attrs);
|
||||
if (gpa == -1) {
|
||||
return 1; // Unmapped
|
||||
} else {
|
||||
*phys_addr = gpa + (virt_addr & ~TARGET_PAGE_MASK);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Get current XBE info
|
||||
struct xbe_info *xemu_get_xbe_info(void)
|
||||
{
|
||||
static struct xbe_info xbe_info;
|
||||
hwaddr hdr_addr;
|
||||
|
||||
// Get physical page offset of headers
|
||||
if (virt_to_phys(0x10000, &hdr_addr) != 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Check signature
|
||||
uint32_t sig = ldl_le_phys(&address_space_memory, hdr_addr);
|
||||
if (sig != 0x48454258) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
xbe_info.timedate = ldl_le_phys(&address_space_memory, hdr_addr+0x114);
|
||||
|
||||
// Find certificate (likely on same page, but be safe and map it)
|
||||
uint32_t cert_addr_virt = ldl_le_phys(&address_space_memory, hdr_addr+0x118);
|
||||
if (cert_addr_virt == 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
hwaddr cert_addr;
|
||||
if (virt_to_phys(cert_addr_virt, &cert_addr) != 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Extract title info from certificate
|
||||
xbe_info.cert_timedate = ldl_le_phys(&address_space_memory, cert_addr+0x04);
|
||||
xbe_info.cert_title_id = ldl_le_phys(&address_space_memory, cert_addr+0x08);
|
||||
xbe_info.cert_version = ldl_le_phys(&address_space_memory, cert_addr+0xac);
|
||||
|
||||
// Generate friendly name for title id
|
||||
uint8_t pub_hi = xbe_info.cert_title_id >> 24;
|
||||
uint8_t pub_lo = xbe_info.cert_title_id >> 16;
|
||||
|
||||
if ((65 > pub_hi) || (pub_hi > 90) || (65 > pub_lo) || (pub_lo > 90)) {
|
||||
// Non-printable publisher id
|
||||
snprintf(xbe_info.cert_title_id_str, sizeof(xbe_info.cert_title_id_str),
|
||||
"0x%08x", xbe_info.cert_title_id);
|
||||
} else {
|
||||
// Printable publisher id
|
||||
snprintf(xbe_info.cert_title_id_str, sizeof(xbe_info.cert_title_id_str),
|
||||
"%c%c-%03u", pub_hi, pub_lo, xbe_info.cert_title_id & 0xffff);
|
||||
}
|
||||
|
||||
return &xbe_info;
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* xemu XBE accessing
|
||||
*
|
||||
* Helper functions to get details about the currently running executable.
|
||||
*
|
||||
* Copyright (C) 2020 Matt Borgerson
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef XEMU_XBE_H
|
||||
#define XEMU_XBE_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
struct xbe_info {
|
||||
uint32_t timedate;
|
||||
uint32_t cert_timedate;
|
||||
uint32_t cert_title_id;
|
||||
char cert_title_id_str[12];
|
||||
uint32_t cert_version;
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// Get current XBE info
|
||||
struct xbe_info *xemu_get_xbe_info(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue