mirror of
https://github.com/xemu-project/xemu.git
synced 2024-11-26 21:10:42 +00:00
ui: Create SnapshotManager, check snapshot disc image path
This commit is contained in:
parent
756e423eac
commit
0155721cfe
@ -31,6 +31,7 @@
|
||||
#include "migration/qemu-file.h"
|
||||
#include "migration/snapshot.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qapi/qapi-commands-block.h"
|
||||
#include "sysemu/runstate.h"
|
||||
#include "qemu-common.h"
|
||||
|
||||
@ -53,6 +54,7 @@ static void xemu_snapshots_load_data(BlockDriverState *bs_ro,
|
||||
QEMUSnapshotInfo *info,
|
||||
XemuSnapshotData *data, Error **err)
|
||||
{
|
||||
data->disc_path = NULL;
|
||||
data->xbe_title_name = NULL;
|
||||
data->gl_thumbnail = 0;
|
||||
|
||||
@ -82,11 +84,28 @@ static void xemu_snapshots_load_data(BlockDriverState *bs_ro,
|
||||
return;
|
||||
}
|
||||
|
||||
const size_t xbe_title_name_size = buf[0];
|
||||
offset = 1;
|
||||
assert(size >= 9);
|
||||
|
||||
offset = 0;
|
||||
|
||||
const size_t disc_path_size = be32_to_cpu(*(uint32_t *)&buf[offset]);
|
||||
offset += 4;
|
||||
|
||||
if (disc_path_size) {
|
||||
data->disc_path = (char *)g_malloc(disc_path_size + 1);
|
||||
assert(size >= (offset + disc_path_size));
|
||||
memcpy(data->disc_path, &buf[offset], disc_path_size);
|
||||
data->disc_path[disc_path_size] = 0;
|
||||
offset += disc_path_size;
|
||||
}
|
||||
|
||||
assert(size >= (offset + 4));
|
||||
const size_t xbe_title_name_size = buf[offset];
|
||||
offset += 1;
|
||||
|
||||
if (xbe_title_name_size) {
|
||||
data->xbe_title_name = (char *)g_malloc(xbe_title_name_size + 1);
|
||||
assert(size >= (offset + xbe_title_name_size));
|
||||
memcpy(data->xbe_title_name, &buf[offset], xbe_title_name_size);
|
||||
data->xbe_title_name[xbe_title_name_size] = 0;
|
||||
offset += xbe_title_name_size;
|
||||
@ -98,6 +117,7 @@ static void xemu_snapshots_load_data(BlockDriverState *bs_ro,
|
||||
if (thumbnail_size) {
|
||||
GLuint thumbnail;
|
||||
glGenTextures(1, &thumbnail);
|
||||
assert(size >= (offset + thumbnail_size));
|
||||
if (xemu_snapshots_load_png_to_texture(thumbnail, &buf[offset],
|
||||
thumbnail_size)) {
|
||||
data->gl_thumbnail = thumbnail;
|
||||
@ -202,6 +222,30 @@ done:
|
||||
return xemu_snapshots_len;
|
||||
}
|
||||
|
||||
char *xemu_get_currently_loaded_disc_path(void)
|
||||
{
|
||||
char *file = NULL;
|
||||
BlockInfoList *block_list, *info;
|
||||
|
||||
block_list = qmp_query_block(NULL);
|
||||
|
||||
for (info = block_list; info; info = info->next) {
|
||||
if (strcmp("ide0-cd1", info->value->device)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (info->value->has_inserted) {
|
||||
BlockDeviceInfo *inserted = info->value->inserted;
|
||||
if (inserted->has_node_name) {
|
||||
file = g_strdup(inserted->file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
qapi_free_BlockInfoList(block_list);
|
||||
return file;
|
||||
}
|
||||
|
||||
void xemu_snapshots_load(const char *vm_name, Error **err)
|
||||
{
|
||||
bool vm_running = runstate_is_running();
|
||||
@ -223,6 +267,9 @@ void xemu_snapshots_delete(const char *vm_name, Error **err)
|
||||
|
||||
void xemu_snapshots_save_extra_data(QEMUFile *f)
|
||||
{
|
||||
char *path = xemu_get_currently_loaded_disc_path();
|
||||
size_t path_size = path ? strlen(path) : 0;
|
||||
|
||||
size_t xbe_title_name_size = 0;
|
||||
char *xbe_title_name = NULL;
|
||||
struct xbe *xbe_data = xemu_get_xbe_info();
|
||||
@ -239,7 +286,13 @@ void xemu_snapshots_save_extra_data(QEMUFile *f)
|
||||
|
||||
qemu_put_be32(f, XEMU_SNAPSHOT_DATA_MAGIC);
|
||||
qemu_put_be32(f, XEMU_SNAPSHOT_DATA_VERSION);
|
||||
qemu_put_be32(f, 1 + xbe_title_name_size + 4 + thumbnail_size);
|
||||
qemu_put_be32(f, 4 + path_size + 1 + xbe_title_name_size + 4 + thumbnail_size);
|
||||
|
||||
qemu_put_be32(f, path_size);
|
||||
if (path_size) {
|
||||
qemu_put_buffer(f, (const uint8_t *)path, path_size);
|
||||
g_free(path);
|
||||
}
|
||||
|
||||
qemu_put_byte(f, xbe_title_name_size);
|
||||
if (xbe_title_name_size) {
|
||||
|
@ -37,11 +37,13 @@ extern "C" {
|
||||
extern const char **g_snapshot_shortcut_index_key_map[];
|
||||
|
||||
typedef struct XemuSnapshotData {
|
||||
char *disc_path;
|
||||
char *xbe_title_name;
|
||||
GLuint gl_thumbnail;
|
||||
} XemuSnapshotData;
|
||||
|
||||
// Implemented in xemu-snapshots.c
|
||||
char *xemu_get_currently_loaded_disc_path(void);
|
||||
int xemu_snapshots_list(QEMUSnapshotInfo **info, XemuSnapshotData **extra_data,
|
||||
Error **err);
|
||||
void xemu_snapshots_load(const char *vm_name, Error **err);
|
||||
|
21
ui/xemu.c
21
ui/xemu.c
@ -55,6 +55,7 @@
|
||||
|
||||
#include "hw/xbox/smbus.h" // For eject, drive tray
|
||||
#include "hw/xbox/nv2a/nv2a.h"
|
||||
#include "ui/xemu-notifications.h"
|
||||
|
||||
#include <stb_image.h>
|
||||
|
||||
@ -1556,26 +1557,34 @@ int main(int argc, char **argv)
|
||||
// rcu_unregister_thread();
|
||||
}
|
||||
|
||||
void xemu_eject_disc(void)
|
||||
void xemu_eject_disc(Error **errp)
|
||||
{
|
||||
Error *error = NULL;
|
||||
|
||||
xbox_smc_eject_button();
|
||||
|
||||
// Xbox software may request that the drive open, but do it now anyway
|
||||
Error *err = NULL;
|
||||
qmp_eject(true, "ide0-cd1", false, NULL, true, false, &err);
|
||||
qmp_eject(true, "ide0-cd1", false, NULL, true, false, &error);
|
||||
if (error) {
|
||||
error_propagate(errp, error);
|
||||
}
|
||||
|
||||
xbox_smc_update_tray_state();
|
||||
}
|
||||
|
||||
void xemu_load_disc(const char *path)
|
||||
void xemu_load_disc(const char *path, Error **errp)
|
||||
{
|
||||
Error *error = NULL;
|
||||
|
||||
// Ensure an eject sequence is always triggered so Xbox software reloads
|
||||
xbox_smc_eject_button();
|
||||
|
||||
Error *err = NULL;
|
||||
qmp_blockdev_change_medium(true, "ide0-cd1", false, NULL, path,
|
||||
false, "", false, 0,
|
||||
&err);
|
||||
&error);
|
||||
if (error) {
|
||||
error_propagate(errp, error);
|
||||
}
|
||||
|
||||
xbox_smc_update_tray_state();
|
||||
}
|
||||
|
@ -17,19 +17,29 @@
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
#include "common.hh"
|
||||
#include "actions.hh"
|
||||
#include "misc.hh"
|
||||
#include "xemu-hud.h"
|
||||
#include "../xemu-snapshots.h"
|
||||
#include "../xemu-notifications.h"
|
||||
#include "snapshot-manager.hh"
|
||||
|
||||
void ActionEjectDisc(void)
|
||||
{
|
||||
xemu_settings_set_string(&g_config.sys.files.dvd_path, "");
|
||||
xemu_eject_disc();
|
||||
|
||||
Error *err = NULL;
|
||||
xemu_eject_disc(&err);
|
||||
if (err) {
|
||||
xemu_queue_error_message(error_get_pretty(err));
|
||||
error_free(err);
|
||||
}
|
||||
}
|
||||
|
||||
void ActionLoadDisc(void)
|
||||
{
|
||||
Error *err = NULL;
|
||||
|
||||
const char *iso_file_filters = ".iso Files\0*.iso\0All Files\0*.*\0";
|
||||
const char *new_disc_path =
|
||||
PausedFileOpen(NOC_FILE_DIALOG_OPEN, iso_file_filters,
|
||||
@ -39,7 +49,12 @@ void ActionLoadDisc(void)
|
||||
return;
|
||||
}
|
||||
xemu_settings_set_string(&g_config.sys.files.dvd_path, new_disc_path);
|
||||
xemu_load_disc(new_disc_path);
|
||||
|
||||
xemu_load_disc(new_disc_path, &err);
|
||||
if (err) {
|
||||
xemu_queue_error_message(error_get_pretty(err));
|
||||
error_free(err);
|
||||
}
|
||||
}
|
||||
|
||||
void ActionTogglePause(void)
|
||||
@ -81,7 +96,7 @@ void ActionActivateBoundSnapshot(int slot, bool save)
|
||||
if (save) {
|
||||
xemu_snapshots_save(snapshot_name, &err);
|
||||
} else {
|
||||
xemu_snapshots_load(snapshot_name, &err);
|
||||
ActionLoadSnapshotChecked(snapshot_name);
|
||||
}
|
||||
|
||||
if (err) {
|
||||
@ -89,3 +104,8 @@ void ActionActivateBoundSnapshot(int slot, bool save)
|
||||
error_free(err);
|
||||
}
|
||||
}
|
||||
|
||||
void ActionLoadSnapshotChecked(const char *name)
|
||||
{
|
||||
g_snapshot_mgr.LoadSnapshotChecked(name);
|
||||
}
|
||||
|
@ -25,3 +25,4 @@ void ActionReset();
|
||||
void ActionShutdown();
|
||||
void ActionScreenshot();
|
||||
void ActionActivateBoundSnapshot(int slot, bool save);
|
||||
void ActionLoadSnapshotChecked(const char *name);
|
||||
|
@ -22,12 +22,14 @@
|
||||
#include "main-menu.hh"
|
||||
#include "font-manager.hh"
|
||||
#include "input-manager.hh"
|
||||
#include "snapshot-manager.hh"
|
||||
#include "viewport-manager.hh"
|
||||
#include "xemu-hud.h"
|
||||
#include "misc.hh"
|
||||
#include "gl-helpers.hh"
|
||||
#include "reporting.hh"
|
||||
#include "qapi/error.h"
|
||||
#include "actions.hh"
|
||||
|
||||
#include "../xemu-input.h"
|
||||
#include "../xemu-notifications.h"
|
||||
@ -35,7 +37,6 @@
|
||||
#include "../xemu-monitor.h"
|
||||
#include "../xemu-version.h"
|
||||
#include "../xemu-net.h"
|
||||
#include "../xemu-snapshots.h"
|
||||
#include "../xemu-os-utils.h"
|
||||
#include "../xemu-xbe.h"
|
||||
|
||||
@ -641,63 +642,24 @@ void MainMenuNetworkView::DrawUdpOptions(bool appearing)
|
||||
ImGui::PopFont();
|
||||
}
|
||||
|
||||
void MainMenuSnapshotsView::Load()
|
||||
{
|
||||
Error *err = NULL;
|
||||
|
||||
if (!m_load_failed) {
|
||||
m_snapshots_len = xemu_snapshots_list(&m_snapshots, &m_extra_data, &err);
|
||||
}
|
||||
|
||||
if (err) {
|
||||
m_load_failed = true;
|
||||
xemu_queue_error_message(error_get_pretty(err));
|
||||
error_free(err);
|
||||
m_snapshots_len = 0;
|
||||
}
|
||||
|
||||
struct xbe *xbe = xemu_get_xbe_info();
|
||||
if (xbe && xbe->cert->m_titleid != m_current_title_id) {
|
||||
g_free(m_current_title_name);
|
||||
m_current_title_name = g_utf16_to_utf8(xbe->cert->m_title_name, 40, NULL, NULL, NULL);
|
||||
m_current_title_id = xbe->cert->m_titleid;
|
||||
}
|
||||
}
|
||||
|
||||
MainMenuSnapshotsView::MainMenuSnapshotsView(): MainMenuTabView()
|
||||
MainMenuSnapshotsView::MainMenuSnapshotsView() : MainMenuTabView()
|
||||
{
|
||||
xemu_snapshots_mark_dirty();
|
||||
m_load_failed = false;
|
||||
|
||||
m_search_regex = NULL;
|
||||
m_current_title_name = NULL;
|
||||
m_current_title_id = 0;
|
||||
|
||||
}
|
||||
|
||||
MainMenuSnapshotsView::~MainMenuSnapshotsView()
|
||||
{
|
||||
g_free(m_snapshots);
|
||||
g_free(m_extra_data);
|
||||
xemu_snapshots_mark_dirty();
|
||||
|
||||
g_free(m_current_title_name);
|
||||
g_free(m_search_regex);
|
||||
}
|
||||
|
||||
void MainMenuSnapshotsView::SnapshotBigButton(QEMUSnapshotInfo *snapshot, const char *title_name, GLuint thumbnail)
|
||||
bool MainMenuSnapshotsView::BigSnapshotButton(QEMUSnapshotInfo *snapshot, XemuSnapshotData *data, int current_snapshot_binding)
|
||||
{
|
||||
Error *err = NULL;
|
||||
int current_snapshot_binding = -1;
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
if (g_strcmp0(*(g_snapshot_shortcut_index_key_map[i]), snapshot->name) == 0) {
|
||||
assert(current_snapshot_binding == -1);
|
||||
current_snapshot_binding = i;
|
||||
}
|
||||
}
|
||||
|
||||
ImGuiStyle &style = ImGui::GetStyle();
|
||||
ImVec2 pos = ImGui::GetCursorPos();
|
||||
ImDrawList *draw_list = ImGui::GetWindowDrawList();
|
||||
|
||||
ImGui::PushFont(g_font_mgr.m_menu_font_small);
|
||||
@ -706,6 +668,7 @@ void MainMenuSnapshotsView::SnapshotBigButton(QEMUSnapshotInfo *snapshot, const
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ButtonTextAlign, ImVec2(0, 0));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, g_viewport_mgr.Scale(ImVec2(5, 5)));
|
||||
|
||||
ImGui::PushFont(g_font_mgr.m_menu_font_medium);
|
||||
|
||||
ImVec2 ts_title = ImGui::CalcTextSize(snapshot->name);
|
||||
@ -715,54 +678,36 @@ void MainMenuSnapshotsView::SnapshotBigButton(QEMUSnapshotInfo *snapshot, const
|
||||
ImVec2 title_pos(name_pos.x, name_pos.y + ts_title.y + style.FramePadding.x);
|
||||
ImVec2 date_pos(name_pos.x, title_pos.y + ts_title.y + style.FramePadding.x);
|
||||
ImVec2 binding_pos(name_pos.x, date_pos.y + ts_title.y + style.FramePadding.x);
|
||||
ImVec2 button_size(-FLT_MIN, fmax(thumbnail_size.y + style.FramePadding.y * 2, ts_title.y + ts_sub.y + style.FramePadding.y * 3));
|
||||
|
||||
bool load = ImGui::Button("###button", ImVec2(-FLT_MIN, fmax(thumbnail_size.y + style.FramePadding.y * 2, ts_title.y + ts_sub.y + style.FramePadding.y * 3)));
|
||||
if (load) {
|
||||
xemu_snapshots_load(snapshot->name, &err);
|
||||
if (err) {
|
||||
xemu_queue_error_message(error_get_pretty(err));
|
||||
error_free(err);
|
||||
}
|
||||
}
|
||||
bool options_key_pressed = ImGui::IsKeyPressed(ImGuiKey_GamepadFaceLeft);
|
||||
bool show_snapshot_context_menu = ImGui::IsItemHovered() &&
|
||||
(ImGui::IsMouseReleased(ImGuiMouseButton_Right) ||
|
||||
options_key_pressed);
|
||||
bool load = ImGui::Button("###button", button_size);
|
||||
|
||||
ImVec2 next_pos = ImGui::GetCursorPos();
|
||||
const ImVec2 p0 = ImGui::GetItemRectMin();
|
||||
const ImVec2 p1 = ImGui::GetItemRectMax();
|
||||
ImGui::PopFont();
|
||||
|
||||
const ImVec2 p0 = ImGui::GetItemRectMin();
|
||||
const ImVec2 p1 = ImGui::GetItemRectMax();
|
||||
draw_list->PushClipRect(p0, p1, true);
|
||||
|
||||
// Snapshot thumbnail
|
||||
ImGui::SetItemAllowOverlap();
|
||||
ImGui::SetCursorPos(ImVec2(pos.x + thumbnail_pos.x, pos.y + thumbnail_pos.y));
|
||||
|
||||
if (!thumbnail) {
|
||||
thumbnail = g_icon_tex;
|
||||
}
|
||||
|
||||
GLuint thumbnail = data->gl_thumbnail ? data->gl_thumbnail : g_icon_tex;
|
||||
int thumbnail_width, thumbnail_height;
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, thumbnail);
|
||||
int thumbnail_width, thumbnail_height;
|
||||
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &thumbnail_width);
|
||||
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &thumbnail_height);
|
||||
|
||||
// Draw black background behind thumbnail
|
||||
draw_list->AddRectFilled(ImVec2(p0.x + thumbnail_pos.x, p0.y + thumbnail_pos.y),
|
||||
ImVec2(p0.x + thumbnail_pos.x + thumbnail_size.x, p0.y + thumbnail_pos.y + thumbnail_size.y),
|
||||
IM_COL32_BLACK);
|
||||
ImVec2 thumbnail_min(p0.x + thumbnail_pos.x, p0.y + thumbnail_pos.y);
|
||||
ImVec2 thumbnail_max(thumbnail_min.x + thumbnail_size.x, thumbnail_min.y + thumbnail_size.y);
|
||||
draw_list->AddRectFilled(thumbnail_min, thumbnail_max, IM_COL32_BLACK);
|
||||
|
||||
// Draw centered thumbnail
|
||||
// Draw centered thumbnail image
|
||||
int scaled_width, scaled_height;
|
||||
ScaleDimensions(thumbnail_width, thumbnail_height, thumbnail_size.x, thumbnail_size.y, &scaled_width, &scaled_height);
|
||||
ImVec2 img_pos = ImGui::GetCursorPos();
|
||||
img_pos.x += (thumbnail_size.x - scaled_width) / 2;
|
||||
img_pos.y += (thumbnail_size.y - scaled_height) / 2;
|
||||
ImGui::SetCursorPos(img_pos);
|
||||
ImGui::Image((ImTextureID)(uint64_t)thumbnail, ImVec2(scaled_width, scaled_height));
|
||||
ImVec2 img_min = ImVec2(thumbnail_min.x + (thumbnail_size.x - scaled_width) / 2,
|
||||
thumbnail_min.y + (thumbnail_size.y - scaled_height) / 2);
|
||||
ImVec2 img_max = ImVec2(img_min.x + scaled_width, img_min.y + scaled_height);
|
||||
draw_list->AddImage((ImTextureID)(uint64_t)thumbnail, img_min, img_max);
|
||||
|
||||
// Snapshot title
|
||||
ImGui::PushFont(g_font_mgr.m_menu_font_medium);
|
||||
@ -771,6 +716,7 @@ void MainMenuSnapshotsView::SnapshotBigButton(QEMUSnapshotInfo *snapshot, const
|
||||
|
||||
// Snapshot XBE title name
|
||||
ImGui::PushFont(g_font_mgr.m_menu_font_small);
|
||||
const char *title_name = data->xbe_title_name ? data->xbe_title_name : "(Unknown XBE Title Name)";
|
||||
draw_list->AddText(ImVec2(p0.x + title_pos.x, p0.y + title_pos.y), IM_COL32(255, 255, 255, 200), title_name);
|
||||
|
||||
// Snapshot date
|
||||
@ -788,71 +734,9 @@ void MainMenuSnapshotsView::SnapshotBigButton(QEMUSnapshotInfo *snapshot, const
|
||||
|
||||
ImGui::PopFont();
|
||||
draw_list->PopClipRect();
|
||||
|
||||
if (show_snapshot_context_menu) {
|
||||
if (options_key_pressed) {
|
||||
ImGui::SetNextWindowPos(p0);
|
||||
}
|
||||
ImGui::OpenPopup("Snapshot Options");
|
||||
}
|
||||
|
||||
if (ImGui::BeginPopupContextItem("Snapshot Options")) {
|
||||
if (ImGui::MenuItem("Load")) {
|
||||
xemu_snapshots_load(snapshot->name, &err);
|
||||
if (err) {
|
||||
xemu_queue_error_message(error_get_pretty(err));
|
||||
error_free(err);
|
||||
}
|
||||
}
|
||||
|
||||
if (ImGui::BeginMenu("Keybinding")) {
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
char *item_name = g_strdup_printf("Bind to F%d", i + 5);
|
||||
|
||||
if (ImGui::MenuItem(item_name)) {
|
||||
if (current_snapshot_binding >= 0) {
|
||||
xemu_settings_set_string(g_snapshot_shortcut_index_key_map[current_snapshot_binding], "");
|
||||
}
|
||||
xemu_settings_set_string(g_snapshot_shortcut_index_key_map[i], snapshot->name);
|
||||
current_snapshot_binding = i;
|
||||
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
|
||||
g_free(item_name);
|
||||
}
|
||||
|
||||
if (current_snapshot_binding >= 0) {
|
||||
if (ImGui::MenuItem("Unbind")) {
|
||||
xemu_settings_set_string(g_snapshot_shortcut_index_key_map[current_snapshot_binding], "");
|
||||
current_snapshot_binding = -1;
|
||||
}
|
||||
}
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
if (ImGui::MenuItem("Replace")) {
|
||||
xemu_snapshots_save(snapshot->name, &err);
|
||||
if (err) {
|
||||
xemu_queue_error_message(error_get_pretty(err));
|
||||
error_free(err);
|
||||
}
|
||||
}
|
||||
if (ImGui::MenuItem("Delete")) {
|
||||
xemu_snapshots_delete(snapshot->name, &err);
|
||||
if (err) {
|
||||
xemu_queue_error_message(error_get_pretty(err));
|
||||
error_free(err);
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
ImGui::PopStyleVar(2);
|
||||
ImGui::SetCursorPos(next_pos);
|
||||
|
||||
return load;
|
||||
}
|
||||
|
||||
void MainMenuSnapshotsView::ClearSearch()
|
||||
@ -892,18 +776,26 @@ static int MainMenuSnapshotsViewUpdateSearchBox(ImGuiInputTextCallbackData *data
|
||||
|
||||
void MainMenuSnapshotsView::Draw()
|
||||
{
|
||||
Load();
|
||||
SectionTitle("Snapshots");
|
||||
g_snapshot_mgr.Refresh();
|
||||
|
||||
Toggle("Filter by current title", &g_config.general.snapshots.filter_current_game);
|
||||
struct xbe *xbe = xemu_get_xbe_info();
|
||||
if (xbe && xbe->cert->m_titleid != m_current_title_id) {
|
||||
g_free(m_current_title_name);
|
||||
m_current_title_name = g_utf16_to_utf8(xbe->cert->m_title_name, 40, NULL, NULL, NULL);
|
||||
m_current_title_id = xbe->cert->m_titleid;
|
||||
}
|
||||
|
||||
SectionTitle("Snapshots");
|
||||
Toggle("Filter by current title", &g_config.general.snapshots.filter_current_game,
|
||||
"Only display snapshots created while running the currently running XBE");
|
||||
|
||||
ImGui::SetNextItemWidth(ImGui::GetColumnWidth() * 0.8);
|
||||
ImGui::InputTextWithHint("##search", "Search...", &m_search_buf, ImGuiInputTextFlags_CallbackEdit,
|
||||
&MainMenuSnapshotsViewUpdateSearchBox, this);
|
||||
|
||||
bool snapshot_with_create_name_exists = false;
|
||||
for (int i = 0; i < m_snapshots_len; ++i) {
|
||||
if (g_strcmp0(m_search_buf.c_str(), m_snapshots[i].name) == 0) {
|
||||
for (int i = 0; i < g_snapshot_mgr.m_snapshots_len; ++i) {
|
||||
if (g_strcmp0(m_search_buf.c_str(), g_snapshot_mgr.m_snapshots[i].name) == 0) {
|
||||
snapshot_with_create_name_exists = true;
|
||||
break;
|
||||
}
|
||||
@ -919,9 +811,9 @@ void MainMenuSnapshotsView::Draw()
|
||||
ImGui::SetTooltip("A snapshot with the name \"%s\" already exists. This button will overwrite the existing snapshot.", m_search_buf.c_str());
|
||||
}
|
||||
|
||||
for (int i = m_snapshots_len - 1; i >= 0; i--) {
|
||||
if (g_config.general.snapshots.filter_current_game && m_extra_data[i].xbe_title_name &&
|
||||
(strcmp(m_current_title_name, m_extra_data[i].xbe_title_name) != 0)) {
|
||||
for (int i = g_snapshot_mgr.m_snapshots_len - 1; i >= 0; i--) {
|
||||
if (g_config.general.snapshots.filter_current_game && g_snapshot_mgr.m_extra_data[i].xbe_title_name &&
|
||||
(strcmp(m_current_title_name, g_snapshot_mgr.m_extra_data[i].xbe_title_name) != 0)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -929,12 +821,12 @@ void MainMenuSnapshotsView::Draw()
|
||||
GMatchInfo *match;
|
||||
bool keep_entry = false;
|
||||
|
||||
g_regex_match(m_search_regex, m_snapshots[i].name, (GRegexMatchFlags)0, &match);
|
||||
g_regex_match(m_search_regex, g_snapshot_mgr.m_snapshots[i].name, (GRegexMatchFlags)0, &match);
|
||||
keep_entry |= g_match_info_matches(match);
|
||||
g_match_info_free(match);
|
||||
|
||||
if (m_extra_data[i].xbe_title_name) {
|
||||
g_regex_match(m_search_regex, m_extra_data[i].xbe_title_name, (GRegexMatchFlags)0, &match);
|
||||
if (g_snapshot_mgr.m_extra_data[i].xbe_title_name) {
|
||||
g_regex_match(m_search_regex, g_snapshot_mgr.m_extra_data[i].xbe_title_name, (GRegexMatchFlags)0, &match);
|
||||
keep_entry |= g_match_info_matches(match);
|
||||
g_free(match);
|
||||
}
|
||||
@ -944,16 +836,94 @@ void MainMenuSnapshotsView::Draw()
|
||||
}
|
||||
}
|
||||
|
||||
QEMUSnapshotInfo *snapshot = &g_snapshot_mgr.m_snapshots[i];
|
||||
XemuSnapshotData *data = &g_snapshot_mgr.m_extra_data[i];
|
||||
|
||||
int current_snapshot_binding = -1;
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
if (g_strcmp0(*(g_snapshot_shortcut_index_key_map[i]), snapshot->name) == 0) {
|
||||
assert(current_snapshot_binding == -1);
|
||||
current_snapshot_binding = i;
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::PushID(i);
|
||||
SnapshotBigButton(
|
||||
m_snapshots + i,
|
||||
m_extra_data[i].xbe_title_name ? m_extra_data[i].xbe_title_name : "Unknown",
|
||||
m_extra_data[i].gl_thumbnail
|
||||
);
|
||||
|
||||
ImVec2 pos = ImGui::GetCursorScreenPos();
|
||||
bool load = BigSnapshotButton(snapshot, data, current_snapshot_binding);
|
||||
|
||||
// FIXME: Provide context menu control annotation
|
||||
if (ImGui::IsItemHovered() && ImGui::IsKeyPressed(ImGuiKey_GamepadFaceLeft)) {
|
||||
ImGui::SetNextWindowPos(pos);
|
||||
ImGui::OpenPopup("Snapshot Options");
|
||||
}
|
||||
|
||||
DrawSnapshotContextMenu(snapshot, data, current_snapshot_binding);
|
||||
|
||||
ImGui::PopID();
|
||||
|
||||
if (load) {
|
||||
ActionLoadSnapshotChecked(snapshot->name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MainMenuSnapshotsView::DrawSnapshotContextMenu(QEMUSnapshotInfo *snapshot, XemuSnapshotData *data, int current_snapshot_binding)
|
||||
{
|
||||
if (!ImGui::BeginPopupContextItem("Snapshot Options")) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ImGui::MenuItem("Load")) {
|
||||
ActionLoadSnapshotChecked(snapshot->name);
|
||||
}
|
||||
|
||||
if (ImGui::BeginMenu("Keybinding")) {
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
char *item_name = g_strdup_printf("Bind to F%d", i + 5);
|
||||
|
||||
if (ImGui::MenuItem(item_name)) {
|
||||
if (current_snapshot_binding >= 0) {
|
||||
xemu_settings_set_string(g_snapshot_shortcut_index_key_map[current_snapshot_binding], "");
|
||||
}
|
||||
xemu_settings_set_string(g_snapshot_shortcut_index_key_map[i], snapshot->name);
|
||||
current_snapshot_binding = i;
|
||||
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
|
||||
g_free(item_name);
|
||||
}
|
||||
|
||||
if (current_snapshot_binding >= 0) {
|
||||
if (ImGui::MenuItem("Unbind")) {
|
||||
xemu_settings_set_string(g_snapshot_shortcut_index_key_map[current_snapshot_binding], "");
|
||||
current_snapshot_binding = -1;
|
||||
}
|
||||
}
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
Error *err = NULL;
|
||||
|
||||
if (ImGui::MenuItem("Replace")) {
|
||||
xemu_snapshots_save(snapshot->name, &err);
|
||||
}
|
||||
|
||||
if (ImGui::MenuItem("Delete")) {
|
||||
xemu_snapshots_delete(snapshot->name, &err);
|
||||
}
|
||||
|
||||
if (err) {
|
||||
xemu_queue_error_message(error_get_pretty(err));
|
||||
error_free(err);
|
||||
}
|
||||
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
MainMenuSystemView::MainMenuSystemView() : m_dirty(false)
|
||||
{
|
||||
}
|
||||
|
@ -104,24 +104,19 @@ public:
|
||||
class MainMenuSnapshotsView : public virtual MainMenuTabView
|
||||
{
|
||||
protected:
|
||||
QEMUSnapshotInfo *m_snapshots;
|
||||
XemuSnapshotData *m_extra_data;
|
||||
int m_snapshots_len;
|
||||
uint32_t m_current_title_id;
|
||||
char *m_current_title_name;
|
||||
std::string m_search_buf;
|
||||
bool m_load_failed;
|
||||
|
||||
private:
|
||||
void Load();
|
||||
void ClearSearch();
|
||||
void DrawSnapshotContextMenu(QEMUSnapshotInfo *snapshot, XemuSnapshotData *data, int current_snapshot_binding);
|
||||
|
||||
public:
|
||||
GRegex *m_search_regex;
|
||||
MainMenuSnapshotsView();
|
||||
~MainMenuSnapshotsView();
|
||||
void SnapshotBigButton(QEMUSnapshotInfo *snapshot, const char *title_name,
|
||||
GLuint screenshot);
|
||||
bool BigSnapshotButton(QEMUSnapshotInfo *snapshot, XemuSnapshotData *data, int current_snapshot_binding);
|
||||
void Draw() override;
|
||||
};
|
||||
|
||||
|
@ -37,6 +37,7 @@
|
||||
#include "misc.hh"
|
||||
#include "gl-helpers.hh"
|
||||
#include "input-manager.hh"
|
||||
#include "snapshot-manager.hh"
|
||||
#include "viewport-manager.hh"
|
||||
#include "font-manager.hh"
|
||||
#include "scene.hh"
|
||||
@ -54,6 +55,8 @@
|
||||
#endif
|
||||
|
||||
bool g_screenshot_pending;
|
||||
const char *g_snapshot_pending_load_name;
|
||||
|
||||
float g_main_menu_height;
|
||||
|
||||
static ImGuiStyle g_base_style;
|
||||
@ -298,6 +301,7 @@ void xemu_hud_render(void)
|
||||
#endif
|
||||
g_scene_mgr.Draw();
|
||||
if (!first_boot_window.is_open) notification_manager.Draw();
|
||||
g_snapshot_mgr.Draw();
|
||||
|
||||
// static bool show_demo = true;
|
||||
// if (show_demo) ImGui::ShowDemoWindow(&show_demo);
|
||||
|
@ -16,6 +16,7 @@ xemu_ss.add(files(
|
||||
'scene-components.cc',
|
||||
'scene-manager.cc',
|
||||
'scene.cc',
|
||||
'snapshot-manager.cc',
|
||||
'viewport-manager.cc',
|
||||
'welcome.cc',
|
||||
'widgets.cc',
|
||||
|
163
ui/xui/snapshot-manager.cc
Normal file
163
ui/xui/snapshot-manager.cc
Normal file
@ -0,0 +1,163 @@
|
||||
//
|
||||
// xemu User Interface
|
||||
//
|
||||
// Copyright (C) 2020-2022 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/>.
|
||||
//
|
||||
|
||||
#include "common.hh"
|
||||
#include "notifications.hh"
|
||||
#include "snapshot-manager.hh"
|
||||
#include "xemu-hud.h"
|
||||
|
||||
SnapshotManager g_snapshot_mgr;
|
||||
|
||||
SnapshotManager::SnapshotManager()
|
||||
{
|
||||
m_snapshots = NULL;
|
||||
m_extra_data = NULL;
|
||||
m_load_failed = false;
|
||||
m_open_pending = false;
|
||||
m_snapshots_len = 0;
|
||||
}
|
||||
|
||||
SnapshotManager::~SnapshotManager()
|
||||
{
|
||||
g_free(m_snapshots);
|
||||
g_free(m_extra_data);
|
||||
xemu_snapshots_mark_dirty();
|
||||
}
|
||||
|
||||
void SnapshotManager::Refresh()
|
||||
{
|
||||
Error *err = NULL;
|
||||
|
||||
if (!m_load_failed) {
|
||||
m_snapshots_len = xemu_snapshots_list(&m_snapshots, &m_extra_data, &err);
|
||||
}
|
||||
|
||||
if (err) {
|
||||
m_load_failed = true;
|
||||
xemu_queue_error_message(error_get_pretty(err));
|
||||
error_free(err);
|
||||
m_snapshots_len = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void SnapshotManager::LoadSnapshotChecked(const char *name)
|
||||
{
|
||||
Refresh();
|
||||
|
||||
XemuSnapshotData *data = NULL;
|
||||
for (int i = 0; i < m_snapshots_len; i++) {
|
||||
if (!strcmp(m_snapshots[i].name, name)) {
|
||||
data = &m_extra_data[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (data == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
char *current_disc_path = xemu_get_currently_loaded_disc_path();
|
||||
if (data->disc_path && (!current_disc_path || strcmp(current_disc_path, data->disc_path))) {
|
||||
if (current_disc_path) {
|
||||
m_current_disc_path = current_disc_path;
|
||||
} else {
|
||||
m_current_disc_path.clear();
|
||||
}
|
||||
m_target_disc_path = data->disc_path;
|
||||
m_pending_load_name = name;
|
||||
m_open_pending = true;
|
||||
} else {
|
||||
if (!data->disc_path) {
|
||||
xemu_eject_disc(NULL);
|
||||
}
|
||||
LoadSnapshot(name);
|
||||
}
|
||||
|
||||
if (current_disc_path) {
|
||||
g_free(current_disc_path);
|
||||
}
|
||||
}
|
||||
|
||||
void SnapshotManager::LoadSnapshot(const char *name)
|
||||
{
|
||||
Error *err = NULL;
|
||||
|
||||
xemu_snapshots_load(name, &err);
|
||||
|
||||
if (err) {
|
||||
xemu_queue_error_message(error_get_pretty(err));
|
||||
error_free(err);
|
||||
}
|
||||
}
|
||||
|
||||
void SnapshotManager::Draw()
|
||||
{
|
||||
DrawSnapshotDiscLoadDialog();
|
||||
}
|
||||
|
||||
void SnapshotManager::DrawSnapshotDiscLoadDialog()
|
||||
{
|
||||
if (m_open_pending) {
|
||||
ImGui::OpenPopup("DVD Drive Image");
|
||||
m_open_pending = false;
|
||||
}
|
||||
|
||||
if (!ImGui::BeginPopupModal("DVD Drive Image", NULL, ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ImGui::Text("The DVD drive disc image mounted when the snapshot was created does not appear to be loaded:");
|
||||
ImGui::Spacing();
|
||||
ImGui::Indent();
|
||||
ImGui::Text("Current Image: %s", m_current_disc_path.length() ? m_current_disc_path.c_str() : "(None)");
|
||||
ImGui::Text("Expected Image: %s", m_target_disc_path.length() ? m_target_disc_path.c_str() : "(None)");
|
||||
ImGui::Unindent();
|
||||
ImGui::Spacing();
|
||||
ImGui::Text("Would you like to load it now?");
|
||||
|
||||
ImGui::Dummy(ImVec2(0,16));
|
||||
|
||||
if (ImGui::Button("Yes", ImVec2(120, 0))) {
|
||||
xemu_eject_disc(NULL);
|
||||
|
||||
Error *err = NULL;
|
||||
xemu_load_disc(m_target_disc_path.c_str(), &err);
|
||||
if (err) {
|
||||
xemu_queue_error_message(error_get_pretty(err));
|
||||
error_free(err);
|
||||
} else {
|
||||
LoadSnapshot(m_pending_load_name.c_str());
|
||||
}
|
||||
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("No", ImVec2(120, 0))) {
|
||||
LoadSnapshot(m_pending_load_name.c_str());
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Cancel", ImVec2(120, 0))) {
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
|
||||
ImGui::EndPopup();
|
||||
}
|
47
ui/xui/snapshot-manager.hh
Normal file
47
ui/xui/snapshot-manager.hh
Normal file
@ -0,0 +1,47 @@
|
||||
//
|
||||
// xemu User Interface
|
||||
//
|
||||
// Copyright (C) 2020-2022 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/>.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include "../xemu-snapshots.h"
|
||||
|
||||
class SnapshotManager
|
||||
{
|
||||
public:
|
||||
QEMUSnapshotInfo *m_snapshots;
|
||||
XemuSnapshotData *m_extra_data;
|
||||
bool m_load_failed;
|
||||
bool m_open_pending;
|
||||
int m_snapshots_len;
|
||||
|
||||
std::string m_pending_load_name;
|
||||
std::string m_current_disc_path;
|
||||
std::string m_target_disc_path;
|
||||
|
||||
SnapshotManager();
|
||||
~SnapshotManager();
|
||||
void Refresh();
|
||||
void LoadSnapshot(const char *name);
|
||||
void LoadSnapshotChecked(const char *name);
|
||||
|
||||
void Draw();
|
||||
void DrawSnapshotDiscLoadDialog();
|
||||
};
|
||||
|
||||
extern SnapshotManager g_snapshot_mgr;
|
@ -33,8 +33,8 @@ extern "C" {
|
||||
int xemu_is_fullscreen(void);
|
||||
void xemu_monitor_init(void);
|
||||
void xemu_toggle_fullscreen(void);
|
||||
void xemu_eject_disc(void);
|
||||
void xemu_load_disc(const char *path);
|
||||
void xemu_eject_disc(Error **errp);
|
||||
void xemu_load_disc(const char *path, Error **errp);
|
||||
|
||||
// Implemented in xemu_hud.cc
|
||||
void xemu_hud_init(SDL_Window *window, void *sdl_gl_context);
|
||||
|
Loading…
Reference in New Issue
Block a user