From e2db2c24fcb9c5290f0fe6bb0f0325df66e6ed6c Mon Sep 17 00:00:00 2001 From: Tim Allen Date: Thu, 7 Oct 2010 20:15:29 +1100 Subject: [PATCH] Update to v070r10 release. byuu says: - added workaround to phoenix/Windows to prevent horizontal scrollbar always being visible on single-column ListBoxes - phoenix gains Window::geometry() - added code to save and restore window positions, as in bsnes/Qt. Positions are saved to bsnes-phoenix-geometry.cfg this time - resizing the main window will keep its position onscreen now There's one issue with GTK+, if you close a window and then call gtk_window_get_position(), it returns the previously set position rather than where you actually placed the window. My easy fix of calling gtk_window_get_position right before actually closing the window didn't work, so for now you'll have to live with it. --- bsnes/nall/filemap.hpp | 2 +- bsnes/nall/ups.hpp | 355 ++++++++++++---------- bsnes/phoenix/gtk/gtk.hpp | 10 +- bsnes/phoenix/gtk/progressbar.cpp | 6 +- bsnes/phoenix/gtk/window.cpp | 7 + bsnes/phoenix/qt/progressbar.cpp | 4 +- bsnes/phoenix/qt/qt.hpp | 10 +- bsnes/phoenix/qt/qt.moc | 2 +- bsnes/phoenix/qt/window.cpp | 4 + bsnes/phoenix/windows/listbox.cpp | 6 + bsnes/phoenix/windows/progressbar.cpp | 6 +- bsnes/phoenix/windows/window.cpp | 12 + bsnes/phoenix/windows/windows.hpp | 12 +- bsnes/snes/snes.hpp | 2 +- bsnes/ui-phoenix/base.hpp | 14 +- bsnes/ui-phoenix/general/file-browser.cpp | 5 +- bsnes/ui-phoenix/general/file-browser.hpp | 2 +- bsnes/ui-phoenix/general/main-window.cpp | 5 +- bsnes/ui-phoenix/general/main-window.hpp | 2 +- bsnes/ui-phoenix/general/slot-loader.cpp | 10 +- bsnes/ui-phoenix/general/slot-loader.hpp | 4 +- bsnes/ui-phoenix/main.cpp | 31 ++ bsnes/ui-phoenix/settings/advanced.cpp | 5 +- bsnes/ui-phoenix/settings/advanced.hpp | 2 +- bsnes/ui-phoenix/settings/audio.cpp | 5 +- bsnes/ui-phoenix/settings/audio.hpp | 2 +- bsnes/ui-phoenix/settings/input.cpp | 5 +- bsnes/ui-phoenix/settings/input.hpp | 2 +- bsnes/ui-phoenix/settings/video.cpp | 5 +- bsnes/ui-phoenix/settings/video.hpp | 2 +- bsnes/ui-phoenix/tools/cheat-editor.cpp | 10 +- bsnes/ui-phoenix/tools/cheat-editor.hpp | 4 +- bsnes/ui-phoenix/tools/state-manager.cpp | 5 +- bsnes/ui-phoenix/tools/state-manager.hpp | 2 +- bsnes/ui-phoenix/utility/utility.cpp | 3 +- 35 files changed, 340 insertions(+), 223 deletions(-) diff --git a/bsnes/nall/filemap.hpp b/bsnes/nall/filemap.hpp index a05f0eb7..a99d162b 100755 --- a/bsnes/nall/filemap.hpp +++ b/bsnes/nall/filemap.hpp @@ -145,7 +145,7 @@ namespace nall { break; } - p_fd = ::open(filename, open_flags); + p_fd = ::open(filename, open_flags, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); if(p_fd < 0) return false; struct stat p_stat; diff --git a/bsnes/nall/ups.hpp b/bsnes/nall/ups.hpp index f255ecb3..9835f4ab 100755 --- a/bsnes/nall/ups.hpp +++ b/bsnes/nall/ups.hpp @@ -1,190 +1,221 @@ #ifndef NALL_UPS_HPP #define NALL_UPS_HPP -#include - -#include #include #include +#include #include namespace nall { - class ups { - public: - enum result { - ok, - patch_unreadable, - patch_unwritable, - patch_invalid, - input_invalid, - output_invalid, - patch_crc32_invalid, - input_crc32_invalid, - output_crc32_invalid, - }; - ups::result create(const char *patch_fn, const uint8_t *x_data, unsigned x_size, const uint8_t *y_data, unsigned y_size) { - if(!fp.open(patch_fn, file::mode_write)) return patch_unwritable; +struct ups { + enum class result_t : unsigned { + unknown, + success, + patch_unwritable, + patch_invalid, + source_invalid, + target_invalid, + target_too_small, + patch_checksum_invalid, + source_checksum_invalid, + target_checksum_invalid, + }; - crc32 = ~0; - uint32_t x_crc32 = crc32_calculate(x_data, x_size); - uint32_t y_crc32 = crc32_calculate(y_data, y_size); + function progress; - //header - write('U'); - write('P'); - write('S'); - write('1'); - encptr(x_size); - encptr(y_size); + result_t create( + const uint8_t *source_data_, unsigned source_length_, + const uint8_t *target_data_, unsigned target_length_, + const char *patch_filename + ) { + source_data = (uint8_t*)source_data_, target_data = (uint8_t*)target_data_; + source_length = source_length_, target_length = target_length_; + source_offset = target_offset = 0; + source_checksum = target_checksum = patch_checksum = ~0; - //body - unsigned max_size = max(x_size, y_size); - unsigned relative = 0; - for(unsigned i = 0; i < max_size;) { - uint8_t x = i < x_size ? x_data[i] : 0x00; - uint8_t y = i < y_size ? y_data[i] : 0x00; + if(patch_file.open(patch_filename, file::mode_write) == false) return result_t::patch_unwritable; - if(x == y) { - i++; - continue; - } + patch_write('U'); + patch_write('P'); + patch_write('S'); + patch_write('1'); + encode(source_length); + encode(target_length); - encptr(i++ - relative); - write(x ^ y); + unsigned output_length = source_length > target_length ? source_length : target_length; + unsigned relative = 0; + for(unsigned offset = 0; offset < output_length;) { + uint8_t x = source_read(); + uint8_t y = target_read(); - while(true) { - if(i >= max_size) { - write(0x00); - break; - } - - x = i < x_size ? x_data[i] : 0x00; - y = i < y_size ? y_data[i] : 0x00; - i++; - write(x ^ y); - if(x == y) break; - } - - relative = i; + if(x == y) { + offset++; + continue; } - //footer - for(unsigned i = 0; i < 4; i++) write(x_crc32 >> (i << 3)); - for(unsigned i = 0; i < 4; i++) write(y_crc32 >> (i << 3)); - uint32_t p_crc32 = ~crc32; - for(unsigned i = 0; i < 4; i++) write(p_crc32 >> (i << 3)); + encode(offset++ - relative); + patch_write(x ^ y); - fp.close(); - return ok; - } - - ups::result apply(const uint8_t *p_data, unsigned p_size, const uint8_t *x_data, unsigned x_size, uint8_t *&y_data, unsigned &y_size) { - if(p_size < 18) return patch_invalid; - p_buffer = p_data; - - crc32 = ~0; - - //header - if(read() != 'U') return patch_invalid; - if(read() != 'P') return patch_invalid; - if(read() != 'S') return patch_invalid; - if(read() != '1') return patch_invalid; - - unsigned px_size = decptr(); - unsigned py_size = decptr(); - - //mirror - if(x_size != px_size && x_size != py_size) return input_invalid; - y_size = (x_size == px_size) ? py_size : px_size; - y_data = new uint8_t[y_size](); - - for(unsigned i = 0; i < x_size && i < y_size; i++) y_data[i] = x_data[i]; - for(unsigned i = x_size; i < y_size; i++) y_data[i] = 0x00; - - //body - unsigned relative = 0; - while(p_buffer < p_data + p_size - 12) { - relative += decptr(); - - while(true) { - uint8_t x = read(); - if(x && relative < y_size) { - uint8_t y = relative < x_size ? x_data[relative] : 0x00; - y_data[relative] = x ^ y; - } - relative++; - if(!x) break; - } - } - - //footer - unsigned px_crc32 = 0, py_crc32 = 0, pp_crc32 = 0; - for(unsigned i = 0; i < 4; i++) px_crc32 |= read() << (i << 3); - for(unsigned i = 0; i < 4; i++) py_crc32 |= read() << (i << 3); - uint32_t p_crc32 = ~crc32; - for(unsigned i = 0; i < 4; i++) pp_crc32 |= read() << (i << 3); - - uint32_t x_crc32 = crc32_calculate(x_data, x_size); - uint32_t y_crc32 = crc32_calculate(y_data, y_size); - - if(px_size != py_size) { - if(x_size == px_size && x_crc32 != px_crc32) return input_crc32_invalid; - if(x_size == py_size && x_crc32 != py_crc32) return input_crc32_invalid; - if(y_size == px_size && y_crc32 != px_crc32) return output_crc32_invalid; - if(y_size == py_size && y_crc32 != py_crc32) return output_crc32_invalid; - } else { - if(x_crc32 != px_crc32 && x_crc32 != py_crc32) return input_crc32_invalid; - if(y_crc32 != px_crc32 && y_crc32 != py_crc32) return output_crc32_invalid; - if(x_crc32 == y_crc32 && px_crc32 != py_crc32) return output_crc32_invalid; - if(x_crc32 != y_crc32 && px_crc32 == py_crc32) return output_crc32_invalid; - } - - if(p_crc32 != pp_crc32) return patch_crc32_invalid; - return ok; - } - - private: - file fp; - uint32_t crc32; - const uint8_t *p_buffer; - - uint8_t read() { - uint8_t n = *p_buffer++; - crc32 = crc32_adjust(crc32, n); - return n; - } - - void write(uint8_t n) { - fp.write(n); - crc32 = crc32_adjust(crc32, n); - } - - void encptr(uint64_t offset) { while(true) { - uint64_t x = offset & 0x7f; - offset >>= 7; - if(offset == 0) { - write(0x80 | x); + if(offset >= output_length) { + patch_write(0x00); break; } - write(x); - offset--; + + x = source_read(); + y = target_read(); + offset++; + patch_write(x ^ y); + if(x == y) break; + } + + relative = offset; + } + + source_checksum = ~source_checksum; + target_checksum = ~target_checksum; + for(unsigned i = 0; i < 4; i++) patch_write(source_checksum >> (i * 8)); + for(unsigned i = 0; i < 4; i++) patch_write(target_checksum >> (i * 8)); + uint32_t patch_result_checksum = ~patch_checksum; + for(unsigned i = 0; i < 4; i++) patch_write(patch_result_checksum >> (i * 8)); + + patch_file.close(); + return result_t::success; + } + + result_t apply( + const uint8_t *patch_data_, unsigned patch_length_, + const uint8_t *source_data_, unsigned source_length_, + uint8_t *target_data_, unsigned &target_length_ + ) { + patch_data = (uint8_t*)patch_data_, source_data = (uint8_t*)source_data_, target_data = target_data_; + patch_length = patch_length_, source_length = source_length_, target_length = target_length_; + patch_offset = source_offset = target_offset = 0; + patch_checksum = source_checksum = target_checksum = ~0; + + if(patch_length < 18) return result_t::patch_invalid; + if(patch_read() != 'U') return result_t::patch_invalid; + if(patch_read() != 'P') return result_t::patch_invalid; + if(patch_read() != 'S') return result_t::patch_invalid; + if(patch_read() != '1') return result_t::patch_invalid; + + unsigned source_read_length = decode(); + unsigned target_read_length = decode(); + + if(source_length != source_read_length && source_length != target_read_length) return result_t::source_invalid; + target_length_ = (source_length == source_read_length ? target_read_length : source_read_length); + if(target_length < target_length_) return result_t::target_too_small; + target_length = target_length_; + + while(patch_offset < patch_length - 12) { + unsigned length = decode(); + while(length--) target_write(source_read()); + while(true) { + uint8_t patch_xor = patch_read(); + target_write(patch_xor ^ source_read()); + if(patch_xor == 0) break; } } - uint64_t decptr() { - uint64_t offset = 0, shift = 1; - while(true) { - uint8_t x = read(); - offset += (x & 0x7f) * shift; - if(x & 0x80) break; - shift <<= 7; - offset += shift; - } - return offset; + uint32_t patch_read_checksum = 0, source_read_checksum = 0, target_read_checksum = 0; + for(unsigned i = 0; i < 4; i++) source_read_checksum |= patch_read() << (i * 8); + for(unsigned i = 0; i < 4; i++) target_read_checksum |= patch_read() << (i * 8); + uint32_t patch_result_checksum = ~patch_checksum; + source_checksum = ~source_checksum; + target_checksum = ~target_checksum; + for(unsigned i = 0; i < 4; i++) patch_read_checksum |= patch_read() << (i * 8); + + if(patch_result_checksum != patch_read_checksum) return result_t::patch_invalid; + if(source_checksum == source_read_checksum && source_length == source_read_length) { + if(target_checksum == target_read_checksum && target_length == target_read_length) return result_t::success; + return result_t::target_invalid; + } else if(source_checksum == target_read_checksum && source_length == target_read_length) { + if(target_checksum == source_read_checksum && target_length == source_read_length) return result_t::success; + return result_t::target_invalid; + } else { + return result_t::source_invalid; } - }; + } + +private: + uint8_t *patch_data, *source_data, *target_data; + unsigned patch_length, source_length, target_length; + unsigned patch_offset, source_offset, target_offset; + unsigned patch_checksum, source_checksum, target_checksum; + file patch_file; + + uint8_t patch_read() { + if(patch_offset < patch_length) { + uint8_t n = patch_data[patch_offset++]; + patch_checksum = crc32_adjust(patch_checksum, n); + return n; + } + return 0x00; + } + + uint8_t source_read() { + if(source_offset < source_length) { + uint8_t n = source_data[source_offset++]; + source_checksum = crc32_adjust(source_checksum, n); + return n; + } + return 0x00; + } + + uint8_t target_read() { + uint8_t result = 0x00; + if(target_offset < target_length) { + result = target_data[target_offset]; + target_checksum = crc32_adjust(target_checksum, result); + } + if(((target_offset++ & 255) == 0) && progress) { + progress(target_offset, source_length > target_length ? source_length : target_length); + } + return result; + } + + void patch_write(uint8_t n) { + patch_file.write(n); + patch_checksum = crc32_adjust(patch_checksum, n); + } + + void target_write(uint8_t n) { + if(target_offset < target_length) { + target_data[target_offset] = n; + target_checksum = crc32_adjust(target_checksum, n); + } + if(((target_offset++ & 255) == 0) && progress) { + progress(target_offset, source_length > target_length ? source_length : target_length); + } + } + + void encode(uint64_t offset) { + while(true) { + uint64_t x = offset & 0x7f; + offset >>= 7; + if(offset == 0) { + patch_write(0x80 | x); + break; + } + patch_write(x); + offset--; + } + } + + uint64_t decode() { + uint64_t offset = 0, shift = 1; + while(true) { + uint8_t x = patch_read(); + offset += (x & 0x7f) * shift; + if(x & 0x80) break; + shift <<= 7; + offset += shift; + } + return offset; + } +}; + } #endif diff --git a/bsnes/phoenix/gtk/gtk.hpp b/bsnes/phoenix/gtk/gtk.hpp index 389708bc..352d6265 100755 --- a/bsnes/phoenix/gtk/gtk.hpp +++ b/bsnes/phoenix/gtk/gtk.hpp @@ -12,6 +12,13 @@ struct Object { Data *object; }; +struct Geometry { + unsigned x, y; + unsigned width, height; + inline Geometry() : x(0), y(0), width(0), height(0) {} + inline Geometry(unsigned x, unsigned y, unsigned width, unsigned height) : x(x), y(y), width(width), height(height) {} +}; + struct Font : Object { enum class Style : unsigned { None = 0, @@ -91,6 +98,7 @@ struct Window : Widget { void create(unsigned x, unsigned y, unsigned width, unsigned height, const char *text = ""); bool focused(); void setFocused(); + Geometry geometry(); void setGeometry(unsigned x, unsigned y, unsigned width, unsigned height); void setDefaultFont(Font &font); void setFont(Font &font); @@ -188,7 +196,7 @@ struct ListBox : Widget { struct ProgressBar : Widget { void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height); - void setProgress(unsigned progress); + void setPosition(unsigned position); }; struct RadioBox : Widget { diff --git a/bsnes/phoenix/gtk/progressbar.cpp b/bsnes/phoenix/gtk/progressbar.cpp index 28af161e..193e924d 100755 --- a/bsnes/phoenix/gtk/progressbar.cpp +++ b/bsnes/phoenix/gtk/progressbar.cpp @@ -6,7 +6,7 @@ void ProgressBar::create(Window &parent, unsigned x, unsigned y, unsigned width, gtk_widget_show(object->widget); } -void ProgressBar::setProgress(unsigned progress) { - progress = progress <= 100 ? progress : 0; - gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(object->widget), (double)progress / 100.0); +void ProgressBar::setPosition(unsigned position) { + position = position <= 100 ? position : 0; + gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(object->widget), (double)position / 100.0); } diff --git a/bsnes/phoenix/gtk/window.cpp b/bsnes/phoenix/gtk/window.cpp index e8fc64da..5ba26233 100755 --- a/bsnes/phoenix/gtk/window.cpp +++ b/bsnes/phoenix/gtk/window.cpp @@ -47,6 +47,13 @@ void Window::setFocused() { gtk_window_present(GTK_WINDOW(object->widget)); } +Geometry Window::geometry() { + gint x, y, width, height; + gtk_window_get_position(GTK_WINDOW(object->widget), &x, &y); + gtk_widget_get_size_request(object->formContainer, &width, &height); + return Geometry(x, y, width, height); +} + void Window::setGeometry(unsigned x, unsigned y, unsigned width, unsigned height) { gtk_window_move(GTK_WINDOW(object->widget), x, y); gtk_widget_set_size_request(object->formContainer, width, height); diff --git a/bsnes/phoenix/qt/progressbar.cpp b/bsnes/phoenix/qt/progressbar.cpp index 9d41bd79..ab21f882 100755 --- a/bsnes/phoenix/qt/progressbar.cpp +++ b/bsnes/phoenix/qt/progressbar.cpp @@ -6,8 +6,8 @@ void ProgressBar::create(Window &parent, unsigned x, unsigned y, unsigned width, progressBar->show(); } -void ProgressBar::setProgress(unsigned progress) { - progressBar->setValue(progress); +void ProgressBar::setPosition(unsigned position) { + progressBar->setValue(position); } ProgressBar::ProgressBar() { diff --git a/bsnes/phoenix/qt/qt.hpp b/bsnes/phoenix/qt/qt.hpp index 2a571585..43820955 100755 --- a/bsnes/phoenix/qt/qt.hpp +++ b/bsnes/phoenix/qt/qt.hpp @@ -12,6 +12,13 @@ struct Object { Data *object; }; +struct Geometry { + unsigned x, y; + unsigned width, height; + inline Geometry() : x(0), y(0), width(0), height(0) {} + inline Geometry(unsigned x, unsigned y, unsigned width, unsigned height) : x(x), y(y), width(width), height(height) {} +}; + struct Font : Object { enum class Style : unsigned { None = 0, @@ -123,6 +130,7 @@ struct Widget : Object { struct Window : Widget { nall::function onClose; void create(unsigned x, unsigned y, unsigned width, unsigned height, const char *text = ""); + Geometry geometry(); void setGeometry(unsigned x, unsigned y, unsigned width, unsigned height); void setDefaultFont(Font &font); void setFont(Font &font); @@ -240,7 +248,7 @@ struct ListBox : Widget { struct ProgressBar : Widget { void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height); - void setProgress(unsigned progress); + void setPosition(unsigned position); ProgressBar(); //private: struct Data; diff --git a/bsnes/phoenix/qt/qt.moc b/bsnes/phoenix/qt/qt.moc index 744c9485..21007c13 100755 --- a/bsnes/phoenix/qt/qt.moc +++ b/bsnes/phoenix/qt/qt.moc @@ -1,7 +1,7 @@ /**************************************************************************** ** Meta object code from reading C++ file 'qt.moc.hpp' ** -** Created: Mon Oct 4 00:53:54 2010 +** Created: Wed Oct 6 18:50:06 2010 ** by: The Qt Meta Object Compiler version 62 (Qt 4.6.2) ** ** WARNING! All changes made in this file will be lost! diff --git a/bsnes/phoenix/qt/window.cpp b/bsnes/phoenix/qt/window.cpp index 88887eee..4e45b45e 100755 --- a/bsnes/phoenix/qt/window.cpp +++ b/bsnes/phoenix/qt/window.cpp @@ -23,6 +23,10 @@ void Window::create(unsigned x, unsigned y, unsigned width, unsigned height, con window->layout->addWidget(window->statusBar); } +Geometry Window::geometry() { + return Geometry(window->x(), window->y(), window->container->width(), window->container->height()); +} + void Window::setGeometry(unsigned x, unsigned y, unsigned width, unsigned height) { window->container->setFixedSize(width, height); window->move(x, y); diff --git a/bsnes/phoenix/windows/listbox.cpp b/bsnes/phoenix/windows/listbox.cpp index f2bf4ba1..6ec92839 100755 --- a/bsnes/phoenix/windows/listbox.cpp +++ b/bsnes/phoenix/windows/listbox.cpp @@ -65,6 +65,9 @@ void ListBox::addItem(const char *text) { utf16_t wtext(list[i]); ListView_SetItemText(widget->window, row, i, wtext); } + + //workaround: when there is only one column, the horizontal scrollbar will always appear without this + if(listBox->columns == 1) ListView_SetColumnWidth(widget->window, 0, LVSCW_AUTOSIZE_USEHEADER); } void ListBox::setItem(unsigned row, const char *text) { @@ -74,6 +77,9 @@ void ListBox::setItem(unsigned row, const char *text) { utf16_t wtext(list[i]); ListView_SetItemText(widget->window, row, i, wtext); } + + //workaround: when there is only one column, the horizontal scrollbar will always appear without this + if(listBox->columns == 1) ListView_SetColumnWidth(widget->window, 0, LVSCW_AUTOSIZE_USEHEADER); } optional ListBox::selection() { diff --git a/bsnes/phoenix/windows/progressbar.cpp b/bsnes/phoenix/windows/progressbar.cpp index 6ab26239..230f12b4 100755 --- a/bsnes/phoenix/windows/progressbar.cpp +++ b/bsnes/phoenix/windows/progressbar.cpp @@ -9,10 +9,10 @@ void ProgressBar::create(Window &parent, unsigned x, unsigned y, unsigned width, SendMessage(widget->window, PBM_SETSTEP, MAKEWPARAM(1, 0), 0); } -unsigned ProgressBar::progress() { +unsigned ProgressBar::position() { return SendMessage(widget->window, PBM_GETPOS, 0, 0); } -void ProgressBar::setProgress(unsigned progress) { - SendMessage(widget->window, PBM_SETPOS, (WPARAM)progress, 0); +void ProgressBar::setPosition(unsigned position) { + SendMessage(widget->window, PBM_SETPOS, (WPARAM)position, 0); } diff --git a/bsnes/phoenix/windows/window.cpp b/bsnes/phoenix/windows/window.cpp index 1d952bbd..d27b0925 100755 --- a/bsnes/phoenix/windows/window.cpp +++ b/bsnes/phoenix/windows/window.cpp @@ -26,6 +26,18 @@ void Window::setFont(Font &font) { SendMessage(window->status, WM_SETFONT, (WPARAM)font.font->font, 0); } +Geometry Window::geometry() { + RECT position, size; + GetWindowRect(widget->window, &position); + GetClientRect(widget->window, &size); + if(GetWindowLongPtr(window->status, GWL_STYLE) & WS_VISIBLE) { + RECT status; + GetClientRect(window->status, &status); + size.bottom -= status.bottom - status.top; + } + return Geometry(position.left, position.top, size.right, size.bottom); +} + void Window::setGeometry(unsigned x, unsigned y, unsigned width, unsigned height) { bool isVisible = visible(); if(isVisible) setVisible(false); diff --git a/bsnes/phoenix/windows/windows.hpp b/bsnes/phoenix/windows/windows.hpp index 763e9750..e1608302 100755 --- a/bsnes/phoenix/windows/windows.hpp +++ b/bsnes/phoenix/windows/windows.hpp @@ -11,6 +11,13 @@ private: virtual void unused(); }; +struct Geometry { + unsigned x, y; + unsigned width, height; + inline Geometry() : x(0), y(0), width(0), height(0) {} + inline Geometry(unsigned x, unsigned y, unsigned width, unsigned height) : x(x), y(y), width(width), height(height) {} +}; + struct Font : Object { enum class Style : unsigned { None = 0, @@ -96,6 +103,7 @@ struct Window : Widget { void create(unsigned x, unsigned y, unsigned width, unsigned height, const char *text = ""); void setDefaultFont(Font &font); void setFont(Font &font); + Geometry geometry(); void setGeometry(unsigned x, unsigned y, unsigned width, unsigned height); void setBackgroundColor(uint8_t red, uint8_t green, uint8_t blue); void setTitle(const char *text); @@ -198,8 +206,8 @@ struct ListBox : Widget { struct ProgressBar : Widget { void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height); - unsigned progress(); - void setProgress(unsigned progress); + unsigned position(); + void setPosition(unsigned position); }; struct RadioBox : Widget { diff --git a/bsnes/snes/snes.hpp b/bsnes/snes/snes.hpp index cbb3401d..5219c51e 100755 --- a/bsnes/snes/snes.hpp +++ b/bsnes/snes/snes.hpp @@ -1,7 +1,7 @@ namespace SNES { namespace Info { static const char Name[] = "bsnes"; - static const char Version[] = "070.09"; + static const char Version[] = "070.10"; static const unsigned SerializerVersion = 13; } } diff --git a/bsnes/ui-phoenix/base.hpp b/bsnes/ui-phoenix/base.hpp index 2385b143..ce450efe 100755 --- a/bsnes/ui-phoenix/base.hpp +++ b/bsnes/ui-phoenix/base.hpp @@ -14,6 +14,11 @@ using namespace ruby; #include using namespace phoenix; +struct TopLevelWindow : Window { + string name; + string position; +}; + #include "interface.hpp" #include "config.hpp" #include "general/general.hpp" @@ -24,13 +29,20 @@ using namespace phoenix; #include "cartridge/cartridge.hpp" struct Application { - array windows; Font proportionalFont; Font proportionalFontBold; Font monospaceFont; bool quit; void main(int argc, char **argv); + + void addWindow(TopLevelWindow *window, const string &name, const string &position); + +private: + array windows; + configuration geometryConfig; + void loadGeometry(); + void saveGeometry(); }; extern Application application; diff --git a/bsnes/ui-phoenix/general/file-browser.cpp b/bsnes/ui-phoenix/general/file-browser.cpp index 0ca5f7e8..61827cd7 100755 --- a/bsnes/ui-phoenix/general/file-browser.cpp +++ b/bsnes/ui-phoenix/general/file-browser.cpp @@ -1,9 +1,8 @@ FileBrowser fileBrowser; void FileBrowser::create() { - application.windows.append(this); Window::create(0, 0, 256, 256); - setDefaultFont(application.proportionalFont); + application.addWindow(this, "FileBrowser", "160,160"); unsigned x = 5, y = 5, height = Style::TextBoxHeight; @@ -13,7 +12,7 @@ void FileBrowser::create() { contentsBox.create(*this, x, y, 630, 350); y += 350 + 5; - setGeometry(160, 160, 640, y); + setGeometry(0, 0, 640, y); pathBox.onActivate = []() { fileBrowser.setFolder(fileBrowser.pathBox.text()); }; browseButton.onTick = { &FileBrowser::folderBrowse, this }; diff --git a/bsnes/ui-phoenix/general/file-browser.hpp b/bsnes/ui-phoenix/general/file-browser.hpp index 0f406838..4565abf1 100755 --- a/bsnes/ui-phoenix/general/file-browser.hpp +++ b/bsnes/ui-phoenix/general/file-browser.hpp @@ -1,4 +1,4 @@ -struct FileBrowser : Window { +struct FileBrowser : TopLevelWindow { TextBox pathBox; Button browseButton; Button upButton; diff --git a/bsnes/ui-phoenix/general/main-window.cpp b/bsnes/ui-phoenix/general/main-window.cpp index 045339ac..76fdb44f 100755 --- a/bsnes/ui-phoenix/general/main-window.cpp +++ b/bsnes/ui-phoenix/general/main-window.cpp @@ -1,9 +1,8 @@ MainWindow mainWindow; void MainWindow::create() { - application.windows.append(this); - Window::create(128, 128, 595, 448, string(SNES::Info::Name, " v", SNES::Info::Version)); - setDefaultFont(application.proportionalFont); + Window::create(0, 0, 595, 448, string(SNES::Info::Name, " v", SNES::Info::Version)); + application.addWindow(this, "MainWindow", "128,128"); setFont(application.proportionalFontBold); setBackgroundColor(0, 0, 0); diff --git a/bsnes/ui-phoenix/general/main-window.hpp b/bsnes/ui-phoenix/general/main-window.hpp index 871b6b95..4fe85092 100755 --- a/bsnes/ui-phoenix/general/main-window.hpp +++ b/bsnes/ui-phoenix/general/main-window.hpp @@ -1,4 +1,4 @@ -struct MainWindow : Window { +struct MainWindow : TopLevelWindow { Menu system; MenuItem systemLoadCartridge; Menu systemLoadCartridgeSpecial; diff --git a/bsnes/ui-phoenix/general/slot-loader.cpp b/bsnes/ui-phoenix/general/slot-loader.cpp index 225bc28c..cc22d42b 100755 --- a/bsnes/ui-phoenix/general/slot-loader.cpp +++ b/bsnes/ui-phoenix/general/slot-loader.cpp @@ -2,9 +2,8 @@ SingleSlotLoader singleSlotLoader; DoubleSlotLoader doubleSlotLoader; void SingleSlotLoader::create() { - application.windows.append(this); Window::create(0, 0, 256, 256); - setDefaultFont(application.proportionalFont); + application.addWindow(this, "SingleSlotLoader", "160,160"); unsigned x = 5, y = 5, height = Style::TextBoxHeight, width = 365 + height; @@ -18,7 +17,7 @@ void SingleSlotLoader::create() { okButton.create(*this, x + width - 90, y, 80, Style::ButtonHeight, "Ok"); y += Style::ButtonHeight + 5; - setGeometry(160, 160, width, y); + setGeometry(0, 0, width, y); baseBrowse.onTick = []() { fileBrowser.fileOpen(FileBrowser::Mode::Cartridge, [](string filename) { @@ -88,9 +87,8 @@ void SingleSlotLoader::load() { // void DoubleSlotLoader::create() { - application.windows.append(this); Window::create(0, 0, 256, 256); - setDefaultFont(application.proportionalFont); + application.addWindow(this, "DoubleSlotLoader", "160,160"); unsigned x = 5, y = 5, height = Style::TextBoxHeight, width = 365 + height; @@ -108,7 +106,7 @@ void DoubleSlotLoader::create() { okButton.create(*this, x + width - 90, y, 80, Style::ButtonHeight, "Ok"); y += Style::ButtonHeight + 5; - setGeometry(160, 160, width, y); + setGeometry(0, 0, width, y); baseBrowse.onTick = []() { fileBrowser.fileOpen(FileBrowser::Mode::Cartridge, [](string filename) { diff --git a/bsnes/ui-phoenix/general/slot-loader.hpp b/bsnes/ui-phoenix/general/slot-loader.hpp index 8a327050..e324c5ba 100755 --- a/bsnes/ui-phoenix/general/slot-loader.hpp +++ b/bsnes/ui-phoenix/general/slot-loader.hpp @@ -1,4 +1,4 @@ -struct SingleSlotLoader : Window { +struct SingleSlotLoader : TopLevelWindow { Label baseLabel; TextBox basePath; Button baseBrowse; @@ -16,7 +16,7 @@ struct SingleSlotLoader : Window { void load(); }; -struct DoubleSlotLoader : Window { +struct DoubleSlotLoader : TopLevelWindow { Label baseLabel; TextBox basePath; Button baseBrowse; diff --git a/bsnes/ui-phoenix/main.cpp b/bsnes/ui-phoenix/main.cpp index 48b4ffbb..dbe45b2f 100755 --- a/bsnes/ui-phoenix/main.cpp +++ b/bsnes/ui-phoenix/main.cpp @@ -40,6 +40,7 @@ void Application::main(int argc, char **argv) { if(config.input.driver == "") config.input.driver = input.default_driver(); palette.update(); + mainWindow.create(); fileBrowser.create(); singleSlotLoader.create(); @@ -50,6 +51,9 @@ void Application::main(int argc, char **argv) { advancedSettings.create(); cheatEditor.create(); stateManager.create(); + loadGeometry(); + saveGeometry(); + utility.setScale(config.video.scale); mainWindow.setVisible(); OS::run(); @@ -111,6 +115,7 @@ void Application::main(int argc, char **argv) { } cartridge.unload(); + saveGeometry(); foreach(window, windows) window->setVisible(false); OS::run(); SNES::system.term(); @@ -121,7 +126,33 @@ void Application::main(int argc, char **argv) { input.term(); } +void Application::addWindow(TopLevelWindow *window, const string &name, const string &position) { + windows.append(window); + window->setDefaultFont(proportionalFont); + window->name = name; + window->position = position; + geometryConfig.attach(window->position, window->name); +} + int main(int argc, char **argv) { application.main(argc, argv); return 0; } + +void Application::loadGeometry() { + geometryConfig.load(string(config.path.user, "bsnes-phoenix-geometry.cfg")); + foreach(window, windows) { + lstring position; + position.split(",", window->position); + Geometry geom = window->geometry(); + window->setGeometry(strunsigned(position[0]), strunsigned(position[1]), geom.width, geom.height); + } +} + +void Application::saveGeometry() { + foreach(window, windows) { + Geometry geom = window->geometry(); + window->position = string(geom.x, ",", geom.y); + } + geometryConfig.save(string(config.path.user, "bsnes-phoenix-geometry.cfg")); +} diff --git a/bsnes/ui-phoenix/settings/advanced.cpp b/bsnes/ui-phoenix/settings/advanced.cpp index 6175d81d..2b214365 100755 --- a/bsnes/ui-phoenix/settings/advanced.cpp +++ b/bsnes/ui-phoenix/settings/advanced.cpp @@ -1,9 +1,8 @@ AdvancedSettings advancedSettings; void AdvancedSettings::create() { - application.windows.append(this); Window::create(0, 0, 256, 256, "Advanced Settings"); - setDefaultFont(application.proportionalFont); + application.addWindow(this, "AdvancedSettings", "160,160"); unsigned x = 5, y = 5; @@ -27,7 +26,7 @@ void AdvancedSettings::create() { if(config.settings.focusPolicy == 1) focusPolicyIgnore.setChecked(); if(config.settings.focusPolicy == 2) focusPolicyAllow.setChecked(); - setGeometry(160, 160, 605, y); + setGeometry(0, 0, 605, y); lstring list; diff --git a/bsnes/ui-phoenix/settings/advanced.hpp b/bsnes/ui-phoenix/settings/advanced.hpp index 23975b1a..834b8cb2 100755 --- a/bsnes/ui-phoenix/settings/advanced.hpp +++ b/bsnes/ui-phoenix/settings/advanced.hpp @@ -1,4 +1,4 @@ -struct AdvancedSettings : Window { +struct AdvancedSettings : TopLevelWindow { Label driverSelectionLabel; Label videoDriverLabel; ComboBox videoDriverBox; diff --git a/bsnes/ui-phoenix/settings/audio.cpp b/bsnes/ui-phoenix/settings/audio.cpp index 87864467..b1db895a 100755 --- a/bsnes/ui-phoenix/settings/audio.cpp +++ b/bsnes/ui-phoenix/settings/audio.cpp @@ -1,9 +1,8 @@ AudioSettings audioSettings; void AudioSettings::create() { - application.windows.append(this); Window::create(0, 0, 256, 256, "Audio Settings"); - setDefaultFont(application.proportionalFont); + application.addWindow(this, "AudioSettings", "160,160"); unsigned x = 5, y = 5; @@ -29,5 +28,5 @@ void AudioSettings::create() { audioSettings.frequencyValue.setText(string(config.audio.inputFrequency, "hz")); }; - setGeometry(160, 160, 440, y); + setGeometry(0, 0, 440, y); } diff --git a/bsnes/ui-phoenix/settings/audio.hpp b/bsnes/ui-phoenix/settings/audio.hpp index 6bf0e63a..c6687c5c 100755 --- a/bsnes/ui-phoenix/settings/audio.hpp +++ b/bsnes/ui-phoenix/settings/audio.hpp @@ -1,4 +1,4 @@ -struct AudioSettings : Window { +struct AudioSettings : TopLevelWindow { Label volumeLabel; Label volumeValue; HorizontalSlider volumeSlider; diff --git a/bsnes/ui-phoenix/settings/input.cpp b/bsnes/ui-phoenix/settings/input.cpp index 9f6dcc1d..68aad9ad 100755 --- a/bsnes/ui-phoenix/settings/input.cpp +++ b/bsnes/ui-phoenix/settings/input.cpp @@ -2,9 +2,8 @@ InputSettings inputSettings; static InputMapper::AbstractInput *activeInput = 0; void InputSettings::create() { - application.windows.append(this); Window::create(0, 0, 256, 256, "Input Settings"); - setDefaultFont(application.proportionalFont); + application.addWindow(this, "InputSettings", "160,160"); setFont(application.proportionalFontBold); setStatusVisible(); @@ -38,7 +37,7 @@ void InputSettings::create() { clearButton.create(*this, 515 - 85, y, 80, height, "Clear"); y += height + 5; - setGeometry(160, 160, 515, y); + setGeometry(0, 0, 515, y); refreshDevices(); portBox.onChange = { &InputSettings::refreshDevices, this }; diff --git a/bsnes/ui-phoenix/settings/input.hpp b/bsnes/ui-phoenix/settings/input.hpp index e6a316dc..1f688fb2 100755 --- a/bsnes/ui-phoenix/settings/input.hpp +++ b/bsnes/ui-phoenix/settings/input.hpp @@ -1,4 +1,4 @@ -struct InputSettings : Window { +struct InputSettings : TopLevelWindow { Label portLabel; ComboBox portBox; Label deviceLabel; diff --git a/bsnes/ui-phoenix/settings/video.cpp b/bsnes/ui-phoenix/settings/video.cpp index 478c7401..e3ebcd0e 100755 --- a/bsnes/ui-phoenix/settings/video.cpp +++ b/bsnes/ui-phoenix/settings/video.cpp @@ -1,9 +1,8 @@ VideoSettings videoSettings; void VideoSettings::create() { - application.windows.append(this); Window::create(0, 0, 256, 256, "Video Settings"); - setDefaultFont(application.proportionalFont); + application.addWindow(this, "VideoSettings", "160,160"); unsigned x = 5, y = 5, height = Style::TextBoxHeight; @@ -33,7 +32,7 @@ void VideoSettings::create() { shaderClear.create(*this, x + 430 - height - height - 5, y, height, height, ""); shaderSelect.create(*this, x + 430 - height, y, height, height, "..."); y += height + 5; - setGeometry(160, 160, 440, y); + setGeometry(0, 0, 440, y); brightnessSlider.setPosition(config.video.brightness); contrastSlider.setPosition(config.video.contrast); diff --git a/bsnes/ui-phoenix/settings/video.hpp b/bsnes/ui-phoenix/settings/video.hpp index 084dbca9..baf2f9d6 100755 --- a/bsnes/ui-phoenix/settings/video.hpp +++ b/bsnes/ui-phoenix/settings/video.hpp @@ -1,4 +1,4 @@ -struct VideoSettings : Window { +struct VideoSettings : TopLevelWindow { Label colorAdjustmentLabel; Label brightnessLabel; Label brightnessValue; diff --git a/bsnes/ui-phoenix/tools/cheat-editor.cpp b/bsnes/ui-phoenix/tools/cheat-editor.cpp index 788231a8..8b55ee02 100755 --- a/bsnes/ui-phoenix/tools/cheat-editor.cpp +++ b/bsnes/ui-phoenix/tools/cheat-editor.cpp @@ -79,9 +79,8 @@ void CheatEditor::save(string filename) { } void CheatEditor::create() { - application.windows.append(this); Window::create(0, 0, 256, 256, "Cheat Editor"); - setDefaultFont(application.proportionalFont); + application.addWindow(this, "CheatEditor", "160,160"); unsigned x = 5, y = 5, height = Style::ButtonHeight; @@ -99,7 +98,7 @@ void CheatEditor::create() { clearAllButton.create(*this, x + 505 - 85 - 85, y, 80, height, "Clear All"); clearButton.create(*this, x + 505 - 85, y, 80, height, "Clear"); y += height + 5; - setGeometry(160, 160, 510, y); + setGeometry(0, 0, 510, y); synchronize(); cheatList.onChange = { &CheatEditor::synchronize, this }; @@ -115,9 +114,8 @@ void CheatEditor::create() { }; //databaseWindow - application.windows.append(&databaseWindow); databaseWindow.create(0, 0, 256, 256); - databaseWindow.setDefaultFont(application.proportionalFont); + application.addWindow(&databaseWindow, "CheatDatabase", "192,192"); x = 5, y = 5; @@ -128,7 +126,7 @@ void CheatEditor::create() { databaseUnselectAll.create(databaseWindow, x + 105, y, 100, height, "Unselect All"); databaseOk.create(databaseWindow, 605 - 80, y, 80, height, "Ok"); y += height + 5; - databaseWindow.setGeometry(192, 192, 610, y); + databaseWindow.setGeometry(0, 0, 610, y); databaseSelectAll.onTick = []() { for(unsigned i = 0; i < cheatEditor.databaseCode.size(); i++) { diff --git a/bsnes/ui-phoenix/tools/cheat-editor.hpp b/bsnes/ui-phoenix/tools/cheat-editor.hpp index 6b29bbdf..e2e60942 100755 --- a/bsnes/ui-phoenix/tools/cheat-editor.hpp +++ b/bsnes/ui-phoenix/tools/cheat-editor.hpp @@ -1,4 +1,4 @@ -struct CheatEditor : Window { +struct CheatEditor : TopLevelWindow { ListBox cheatList; Label codeLabel; TextBox codeEdit; @@ -8,7 +8,7 @@ struct CheatEditor : Window { Button clearAllButton; Button clearButton; - Window databaseWindow; + TopLevelWindow databaseWindow; ListBox databaseList; lstring databaseCode; Button databaseSelectAll; diff --git a/bsnes/ui-phoenix/tools/state-manager.cpp b/bsnes/ui-phoenix/tools/state-manager.cpp index 23e54d99..4850a604 100755 --- a/bsnes/ui-phoenix/tools/state-manager.cpp +++ b/bsnes/ui-phoenix/tools/state-manager.cpp @@ -1,9 +1,8 @@ StateManager stateManager; void StateManager::create() { - application.windows.append(this); Window::create(0, 0, 256, 256, "State Manager"); - setDefaultFont(application.proportionalFont); + application.addWindow(this, "StateManager", "160,160"); unsigned x = 5, y = 5; @@ -17,7 +16,7 @@ void StateManager::create() { saveButton.create(*this, x + 505 - 85 - 85, y, 80, Style::ButtonHeight, "Save"); eraseButton.create(*this, x + 505 - 85, y, 80, Style::ButtonHeight, "Erase"); y += Style::ButtonHeight + 5; - setGeometry(160, 160, 510, y); + setGeometry(0, 0, 510, y); synchronize(); stateList.onActivate = { &StateManager::slotLoad, this }; diff --git a/bsnes/ui-phoenix/tools/state-manager.hpp b/bsnes/ui-phoenix/tools/state-manager.hpp index 84103172..274c9298 100755 --- a/bsnes/ui-phoenix/tools/state-manager.hpp +++ b/bsnes/ui-phoenix/tools/state-manager.hpp @@ -1,4 +1,4 @@ -struct StateManager : Window { +struct StateManager : TopLevelWindow { ListBox stateList; Label descLabel; TextBox descEdit; diff --git a/bsnes/ui-phoenix/utility/utility.cpp b/bsnes/ui-phoenix/utility/utility.cpp index 48ebab49..d3be01c7 100755 --- a/bsnes/ui-phoenix/utility/utility.cpp +++ b/bsnes/ui-phoenix/utility/utility.cpp @@ -66,7 +66,8 @@ void Utility::setScale(unsigned scale) { if(config.video.aspectRatioCorrection) width *= 32.0 / 23.0; } mainWindow.viewport.setGeometry(0, 0, width, height); - mainWindow.setGeometry(128, 128, width, height); + Geometry geom = mainWindow.geometry(); + mainWindow.setGeometry(geom.x, geom.y, width, height); } void Utility::setShader() {