diff --git a/bsnes/gameboy/apu/master/master.cpp b/bsnes/gameboy/apu/master/master.cpp index 02df2dc1..54667357 100755 --- a/bsnes/gameboy/apu/master/master.cpp +++ b/bsnes/gameboy/apu/master/master.cpp @@ -1,6 +1,11 @@ #ifdef APU_CPP void APU::Master::run() { + static int16_t volume[] = { + -16384, -14336, -12288, -10240, -8192, -6144, -4096, -2048, + +2048, +4096, +6144, +8192, +10240, +12288, +14336, +16384, + }; + if(enable == false) { center = 0; left = 0; @@ -14,7 +19,7 @@ void APU::Master::run() { sample += apu.wave.output; sample += apu.noise.output; sample >>= 2; - center = sclamp<16>(sample); + center = volume[sample]; sample = 0; channels = 0; @@ -23,7 +28,7 @@ void APU::Master::run() { if(channel3_left_enable) { sample += apu.wave.output; channels++; } if(channel4_left_enable) { sample += apu.noise.output; channels++; } if(channels) sample /= channels; - left = sclamp<16>(sample); + left = volume[sample]; switch(left_volume) { case 0: left >>= 3; break; // 12.5% @@ -43,7 +48,7 @@ void APU::Master::run() { if(channel3_right_enable) { sample += apu.wave.output; channels++; } if(channel4_right_enable) { sample += apu.noise.output; channels++; } if(channels) sample /= channels; - right = sclamp<16>(sample); + right = volume[sample]; switch(right_volume) { case 0: right >>= 3; break; // 12.5% diff --git a/bsnes/gameboy/apu/noise/noise.cpp b/bsnes/gameboy/apu/noise/noise.cpp index 7a016fae..c0def117 100755 --- a/bsnes/gameboy/apu/noise/noise.cpp +++ b/bsnes/gameboy/apu/noise/noise.cpp @@ -16,7 +16,7 @@ void APU::Noise::run() { uint4 sample = (lfsr & 1) ? (uint4)0 : volume; if(enable == false) sample = 0; - output = (sample * 4369) - 32768; + output = sample; } void APU::Noise::clock_length() { diff --git a/bsnes/gameboy/apu/square1/square1.cpp b/bsnes/gameboy/apu/square1/square1.cpp index e0fb5d20..0a9e0132 100755 --- a/bsnes/gameboy/apu/square1/square1.cpp +++ b/bsnes/gameboy/apu/square1/square1.cpp @@ -19,7 +19,7 @@ void APU::Square1::run() { uint4 sample = (duty_output ? volume : (uint4)0); if(enable == false) sample = 0; - output = (sample * 4369) - 32768; + output = sample; } void APU::Square1::sweep(bool update) { diff --git a/bsnes/gameboy/apu/square2/square2.cpp b/bsnes/gameboy/apu/square2/square2.cpp index e75c6d27..09e50678 100755 --- a/bsnes/gameboy/apu/square2/square2.cpp +++ b/bsnes/gameboy/apu/square2/square2.cpp @@ -19,7 +19,7 @@ void APU::Square2::run() { uint4 sample = (duty_output ? volume : (uint4)0); if(enable == false) sample = 0; - output = (sample * 4369) - 32768; + output = sample; } void APU::Square2::clock_length() { diff --git a/bsnes/gameboy/apu/wave/wave.cpp b/bsnes/gameboy/apu/wave/wave.cpp index 8525d0db..3db04eea 100755 --- a/bsnes/gameboy/apu/wave/wave.cpp +++ b/bsnes/gameboy/apu/wave/wave.cpp @@ -6,11 +6,10 @@ void APU::Wave::run() { pattern_sample = pattern[++pattern_offset]; } - uint4 sample = pattern_sample; + uint4 sample = pattern_sample >> volume_shift; if(enable == false) sample = 0; - output = (sample * 4369) - 32768; - output >>= volume_shift; + output = sample; } void APU::Wave::clock_length() { @@ -31,10 +30,10 @@ void APU::Wave::write(unsigned r, uint8 data) { if(r == 2) { //$ff1c NR32 switch((data >> 5) & 3) { - case 0: volume_shift = 16; break; // 0% - case 1: volume_shift = 0; break; //100% - case 2: volume_shift = 1; break; // 50% - case 3: volume_shift = 2; break; // 25% + case 0: volume_shift = 4; break; // 0% + case 1: volume_shift = 0; break; //100% + case 2: volume_shift = 1; break; // 50% + case 3: volume_shift = 2; break; // 25% } } diff --git a/bsnes/gameboy/gameboy.hpp b/bsnes/gameboy/gameboy.hpp index df2d742b..f32c2547 100755 --- a/bsnes/gameboy/gameboy.hpp +++ b/bsnes/gameboy/gameboy.hpp @@ -12,7 +12,7 @@ namespace GameBoy { /* bgameboy - Game Boy emulator author: byuu - license: GPLv2 + license: GPLv3 project started: 2010-12-27 */ diff --git a/bsnes/gameboy/interface/interface.cpp b/bsnes/gameboy/interface/interface.cpp index 5ab8fc51..333b569e 100755 --- a/bsnes/gameboy/interface/interface.cpp +++ b/bsnes/gameboy/interface/interface.cpp @@ -38,6 +38,16 @@ void Interface::unloadCartridge() { cartridge.unload(); } +unsigned Interface::memorySize(Memory memory) { + if(memory == Memory::RAM) return cartridge.ramsize; + return 0u; +} + +uint8_t* Interface::memoryData(Memory memory) { + if(memory == Memory::RAM) return cartridge.ramdata; + return 0u; +} + void Interface::power() { system.power(); } diff --git a/bsnes/gameboy/interface/interface.hpp b/bsnes/gameboy/interface/interface.hpp index 56dea02e..0d79aa5a 100755 --- a/bsnes/gameboy/interface/interface.hpp +++ b/bsnes/gameboy/interface/interface.hpp @@ -13,6 +13,13 @@ public: virtual void loadCartridge(const string &xml, const uint8_t *data, unsigned size); virtual void unloadCartridge(); + enum class Memory : unsigned { + RAM, + }; + + virtual unsigned memorySize(Memory); + virtual uint8_t* memoryData(Memory); + virtual void power(); virtual void run(); diff --git a/bsnes/gameboy/lcd/lcd.cpp b/bsnes/gameboy/lcd/lcd.cpp index 2e1da7af..5c0a5c72 100755 --- a/bsnes/gameboy/lcd/lcd.cpp +++ b/bsnes/gameboy/lcd/lcd.cpp @@ -78,7 +78,7 @@ void LCD::render() { } uint8_t *output = screen + status.ly * 160; - for(unsigned n = 0; n < 160; n++) output[n] = (3 - line[n]) * 0x55; + for(unsigned n = 0; n < 160; n++) output[n] = line[n]; interface->lcdScanline(); } diff --git a/bsnes/nes/cartridge/cartridge.cpp b/bsnes/nes/cartridge/cartridge.cpp index 256af6b0..82599183 100755 --- a/bsnes/nes/cartridge/cartridge.cpp +++ b/bsnes/nes/cartridge/cartridge.cpp @@ -49,6 +49,14 @@ void Cartridge::unload() { loaded = false; } +unsigned Cartridge::ram_size() { + return mapper->ram_size(); +} + +uint8* Cartridge::ram_data() { + return mapper->ram_data(); +} + void Cartridge::power() { mapper->power(); } diff --git a/bsnes/nes/cartridge/cartridge.hpp b/bsnes/nes/cartridge/cartridge.hpp index b3bb1df6..5aff5514 100755 --- a/bsnes/nes/cartridge/cartridge.hpp +++ b/bsnes/nes/cartridge/cartridge.hpp @@ -2,6 +2,9 @@ struct Cartridge : property { void load(const string &xml, const uint8_t *data, unsigned size); void unload(); + unsigned ram_size(); + uint8 *ram_data(); + void power(); void reset(); diff --git a/bsnes/nes/interface/interface.cpp b/bsnes/nes/interface/interface.cpp index f52d3cb0..16011da6 100755 --- a/bsnes/nes/interface/interface.cpp +++ b/bsnes/nes/interface/interface.cpp @@ -36,6 +36,16 @@ void Interface::unloadCartridge() { cartridge.unload(); } +unsigned Interface::memorySize(Memory memory) { + if(memory == Memory::RAM) return cartridge.ram_size(); + return 0u; +} + +uint8_t* Interface::memoryData(Memory memory) { + if(memory == Memory::RAM) return cartridge.ram_data(); + return 0u; +} + void Interface::power() { system.power(); } diff --git a/bsnes/nes/interface/interface.hpp b/bsnes/nes/interface/interface.hpp index 274f5317..2787f57b 100755 --- a/bsnes/nes/interface/interface.hpp +++ b/bsnes/nes/interface/interface.hpp @@ -11,6 +11,13 @@ struct Interface { virtual void loadCartridge(const string &xml, const uint8_t *data, unsigned size); virtual void unloadCartridge(); + enum class Memory : unsigned { + RAM, + }; + + virtual unsigned memorySize(Memory); + virtual uint8_t* memoryData(Memory); + virtual void power(); virtual void reset(); virtual void run(); diff --git a/bsnes/nes/mapper/mapper.cpp b/bsnes/nes/mapper/mapper.cpp index fe41b5d9..40eaf9fd 100755 --- a/bsnes/nes/mapper/mapper.cpp +++ b/bsnes/nes/mapper/mapper.cpp @@ -21,6 +21,14 @@ namespace Mapper { return base; } + unsigned Mapper::ram_size() { + return 0u; + } + + uint8* Mapper::ram_data() { + return 0; + } + #include "none/none.cpp" #include "aorom/aorom.cpp" #include "bandai-fcg/bandai-fcg.cpp" diff --git a/bsnes/nes/mapper/mapper.hpp b/bsnes/nes/mapper/mapper.hpp index be3ff05f..41748798 100755 --- a/bsnes/nes/mapper/mapper.hpp +++ b/bsnes/nes/mapper/mapper.hpp @@ -11,6 +11,9 @@ namespace Mapper { virtual uint8 ciram_read(uint13 addr) = 0; virtual void ciram_write(uint13 addr, uint8 data) = 0; + virtual unsigned ram_size(); + virtual uint8* ram_data(); + virtual void power() = 0; virtual void reset() = 0; }; diff --git a/bsnes/nes/mapper/mmc1/mmc1.cpp b/bsnes/nes/mapper/mmc1/mmc1.cpp index b2cc578f..84ae7c86 100755 --- a/bsnes/nes/mapper/mmc1/mmc1.cpp +++ b/bsnes/nes/mapper/mmc1/mmc1.cpp @@ -170,6 +170,16 @@ void MMC1::ciram_write(uint13 addr, uint8 data) { // +unsigned MMC1::ram_size() { + return 8192u; +} + +uint8* MMC1::ram_data() { + return prg_ram; +} + +// + void MMC1::power() { reset(); } diff --git a/bsnes/nes/mapper/mmc1/mmc1.hpp b/bsnes/nes/mapper/mmc1/mmc1.hpp index b8b4bee3..3351d616 100755 --- a/bsnes/nes/mapper/mmc1/mmc1.hpp +++ b/bsnes/nes/mapper/mmc1/mmc1.hpp @@ -8,6 +8,9 @@ struct MMC1 : Mapper { uint8 ciram_read(uint13 addr); void ciram_write(uint13 addr, uint8 data); + unsigned ram_size(); + uint8 *ram_data(); + void power(); void reset(); diff --git a/bsnes/nes/nes.hpp b/bsnes/nes/nes.hpp index 40280caa..0b7aa956 100755 --- a/bsnes/nes/nes.hpp +++ b/bsnes/nes/nes.hpp @@ -4,14 +4,14 @@ namespace NES { namespace Info { static const char Name[] = "bnes"; - static const char Version[] = "000.13"; + static const char Version[] = "000.14"; } } /* bnes - NES emulator authors: byuu, Ryphecha - license: GPLv2 + license: GPLv3 project started: 2011-09-05 */ diff --git a/bsnes/phoenix/core/core.cpp b/bsnes/phoenix/core/core.cpp index 204996c3..16a3dc7d 100755 --- a/bsnes/phoenix/core/core.cpp +++ b/bsnes/phoenix/core/core.cpp @@ -782,6 +782,10 @@ HexEdit::~HexEdit() { //HorizontalScrollBar //=================== +unsigned HorizontalScrollBar::length() { + return state.length; +} + unsigned HorizontalScrollBar::position() { return p.position(); } @@ -812,6 +816,10 @@ HorizontalScrollBar::~HorizontalScrollBar() { //HorizontalSlider //================ +unsigned HorizontalSlider::length() { + return state.length; +} + unsigned HorizontalSlider::position() { return p.position(); } @@ -1073,6 +1081,10 @@ TextEdit::~TextEdit() { //VerticalScrollBar //================= +unsigned VerticalScrollBar::length() { + return state.length; +} + unsigned VerticalScrollBar::position() { return p.position(); } @@ -1103,6 +1115,10 @@ VerticalScrollBar::~VerticalScrollBar() { //VerticalSlider //============== +unsigned VerticalSlider::length() { + return state.length; +} + unsigned VerticalSlider::position() { return p.position(); } diff --git a/bsnes/phoenix/core/core.hpp b/bsnes/phoenix/core/core.hpp index 14087c87..a87ad95f 100755 --- a/bsnes/phoenix/core/core.hpp +++ b/bsnes/phoenix/core/core.hpp @@ -358,6 +358,7 @@ struct HexEdit : private nall::base_from_member, Widget { struct HorizontalScrollBar : private nall::base_from_member, Widget { nall::function onChange; + unsigned length(); unsigned position(); void setLength(unsigned length); void setPosition(unsigned position); @@ -372,6 +373,7 @@ struct HorizontalScrollBar : private nall::base_from_member, Widget { nall::function onChange; + unsigned length(); unsigned position(); void setLength(unsigned length); void setPosition(unsigned position); @@ -485,6 +487,7 @@ struct TextEdit : private nall::base_from_member, Widget { struct VerticalScrollBar : private nall::base_from_member, Widget { nall::function onChange; + unsigned length(); unsigned position(); void setLength(unsigned length); void setPosition(unsigned position); @@ -499,6 +502,7 @@ struct VerticalScrollBar : private nall::base_from_member, struct VerticalSlider : private nall::base_from_member, Widget { nall::function onChange; + unsigned length(); unsigned position(); void setLength(unsigned length); void setPosition(unsigned position); diff --git a/bsnes/phoenix/core/layout/horizontal-layout.cpp b/bsnes/phoenix/core/layout/horizontal-layout.cpp index 2d0c008e..9f848224 100755 --- a/bsnes/phoenix/core/layout/horizontal-layout.cpp +++ b/bsnes/phoenix/core/layout/horizontal-layout.cpp @@ -41,6 +41,10 @@ Geometry HorizontalLayout::minimumGeometry() { void HorizontalLayout::remove(Sizable &sizable) { for(unsigned n = 0; n < children.size(); n++) { if(children[n].sizable == &sizable) { + if(dynamic_cast(children[n].sizable)) { + Layout *layout = (Layout*)children[n].sizable; + layout->reset(); + } children.remove(n); Layout::remove(sizable); break; @@ -50,6 +54,7 @@ void HorizontalLayout::remove(Sizable &sizable) { void HorizontalLayout::reset() { foreach(child, children) { + if(window() && dynamic_cast(child.sizable)) ((Layout*)child.sizable)->reset(); if(window() && dynamic_cast(child.sizable)) window()->remove((Widget&)*child.sizable); } } diff --git a/bsnes/phoenix/core/layout/vertical-layout.cpp b/bsnes/phoenix/core/layout/vertical-layout.cpp index 20e884b3..e34abeca 100755 --- a/bsnes/phoenix/core/layout/vertical-layout.cpp +++ b/bsnes/phoenix/core/layout/vertical-layout.cpp @@ -54,6 +54,7 @@ void VerticalLayout::remove(Sizable &sizable) { void VerticalLayout::reset() { foreach(child, children) { + if(window() && dynamic_cast(child.sizable)) ((Layout*)child.sizable)->reset(); if(window() && dynamic_cast(child.sizable)) window()->remove((Widget&)*child.sizable); } } diff --git a/bsnes/phoenix/gtk/widget/canvas.cpp b/bsnes/phoenix/gtk/widget/canvas.cpp index 9991f93f..a16b2a2d 100755 --- a/bsnes/phoenix/gtk/widget/canvas.cpp +++ b/bsnes/phoenix/gtk/widget/canvas.cpp @@ -11,11 +11,12 @@ uint32_t* pCanvas::buffer() { } void pCanvas::setGeometry(const Geometry &geometry) { - if(geometry.width == cairo_image_surface_get_width(surface) - && geometry.height == cairo_image_surface_get_height(surface)) return; - - cairo_surface_destroy(surface); - surface = cairo_image_surface_create(CAIRO_FORMAT_RGB24, geometry.width, geometry.height); + if(geometry.width != cairo_image_surface_get_width (surface) + || geometry.height != cairo_image_surface_get_height(surface) + ) { + cairo_surface_destroy(surface); + surface = cairo_image_surface_create(CAIRO_FORMAT_RGB24, geometry.width, geometry.height); + } pWidget::setGeometry(geometry); update(); diff --git a/bsnes/phoenix/qt/font.cpp b/bsnes/phoenix/qt/font.cpp index 3d9addfd..2a303835 100755 --- a/bsnes/phoenix/qt/font.cpp +++ b/bsnes/phoenix/qt/font.cpp @@ -13,8 +13,8 @@ QFont pFont::create(const string &description) { bool italic = part[2].position("Italic"); QFont qtFont; - qtFont.setFamily(part[0]); - qtFont.setPointSize(decimal(part[1])); + qtFont.setFamily(name); + qtFont.setPointSize(size); if(bold) qtFont.setBold(true); if(italic) qtFont.setItalic(true); return qtFont; diff --git a/bsnes/phoenix/qt/platform.moc b/bsnes/phoenix/qt/platform.moc index dd9ed3e2..4b05f4ef 100755 --- a/bsnes/phoenix/qt/platform.moc +++ b/bsnes/phoenix/qt/platform.moc @@ -1,7 +1,7 @@ /**************************************************************************** ** Meta object code from reading C++ file 'platform.moc.hpp' ** -** Created: Fri Sep 16 21:12:56 2011 +** Created: Mon Sep 19 18:14:48 2011 ** by: The Qt Meta Object Compiler version 62 (Qt 4.7.0) ** ** WARNING! All changes made in this file will be lost! diff --git a/bsnes/phoenix/qt/platform.moc.hpp b/bsnes/phoenix/qt/platform.moc.hpp index d1ecee36..74b629b0 100755 --- a/bsnes/phoenix/qt/platform.moc.hpp +++ b/bsnes/phoenix/qt/platform.moc.hpp @@ -243,6 +243,7 @@ struct pWidget : public pSizable { pWidget(Widget &widget) : pSizable(widget), widget(widget) {} void constructor(); + void synchronizeState(); void destructor(); virtual void orphan(); }; diff --git a/bsnes/phoenix/qt/widget/button.cpp b/bsnes/phoenix/qt/widget/button.cpp index 2be213d5..9f5d5a6a 100755 --- a/bsnes/phoenix/qt/widget/button.cpp +++ b/bsnes/phoenix/qt/widget/button.cpp @@ -10,6 +10,8 @@ void pButton::setText(const string &text) { void pButton::constructor() { qtWidget = qtButton = new QPushButton; connect(qtButton, SIGNAL(released()), SLOT(onTick())); + + pWidget::synchronizeState(); setText(button.state.text); } diff --git a/bsnes/phoenix/qt/widget/canvas.cpp b/bsnes/phoenix/qt/widget/canvas.cpp index cc79fd04..abcae404 100755 --- a/bsnes/phoenix/qt/widget/canvas.cpp +++ b/bsnes/phoenix/qt/widget/canvas.cpp @@ -17,6 +17,7 @@ void pCanvas::constructor() { qtWidget = qtCanvas = new QtCanvas(*this); qtImage = new QImage(256, 256, QImage::Format_RGB32); + pWidget::synchronizeState(); update(); } diff --git a/bsnes/phoenix/qt/widget/check-box.cpp b/bsnes/phoenix/qt/widget/check-box.cpp index f23e9d3b..b62e4384 100755 --- a/bsnes/phoenix/qt/widget/check-box.cpp +++ b/bsnes/phoenix/qt/widget/check-box.cpp @@ -21,6 +21,7 @@ void pCheckBox::constructor() { qtWidget = qtCheckBox = new QCheckBox; connect(qtCheckBox, SIGNAL(stateChanged(int)), SLOT(onTick())); + pWidget::synchronizeState(); setChecked(checkBox.state.checked); setText(checkBox.state.text); } diff --git a/bsnes/phoenix/qt/widget/combo-box.cpp b/bsnes/phoenix/qt/widget/combo-box.cpp index b5e57a93..ddabe344 100755 --- a/bsnes/phoenix/qt/widget/combo-box.cpp +++ b/bsnes/phoenix/qt/widget/combo-box.cpp @@ -32,10 +32,12 @@ void pComboBox::constructor() { qtWidget = qtComboBox = new QComboBox; connect(qtComboBox, SIGNAL(currentIndexChanged(int)), SLOT(onChange())); + pWidget::synchronizeState(); + unsigned selection = comboBox.state.selection; locked = true; foreach(text, comboBox.state.text) append(text); locked = false; - setSelection(comboBox.state.selection); + setSelection(selection); } void pComboBox::destructor() { diff --git a/bsnes/phoenix/qt/widget/hex-edit.cpp b/bsnes/phoenix/qt/widget/hex-edit.cpp index a2f61203..fdeebe4a 100755 --- a/bsnes/phoenix/qt/widget/hex-edit.cpp +++ b/bsnes/phoenix/qt/widget/hex-edit.cpp @@ -81,6 +81,7 @@ void pHexEdit::constructor() { connect(qtScroll, SIGNAL(actionTriggered(int)), SLOT(onScroll())); + pWidget::synchronizeState(); setColumns(hexEdit.state.columns); setRows(hexEdit.state.rows); setLength(hexEdit.state.length); diff --git a/bsnes/phoenix/qt/widget/horizontal-scroll-bar.cpp b/bsnes/phoenix/qt/widget/horizontal-scroll-bar.cpp index 5d770c68..6127c301 100755 --- a/bsnes/phoenix/qt/widget/horizontal-scroll-bar.cpp +++ b/bsnes/phoenix/qt/widget/horizontal-scroll-bar.cpp @@ -22,6 +22,7 @@ void pHorizontalScrollBar::constructor() { qtScrollBar->setPageStep(101 >> 3); connect(qtScrollBar, SIGNAL(valueChanged(int)), SLOT(onChange())); + pWidget::synchronizeState(); setLength(horizontalScrollBar.state.length); setPosition(horizontalScrollBar.state.position); } diff --git a/bsnes/phoenix/qt/widget/horizontal-slider.cpp b/bsnes/phoenix/qt/widget/horizontal-slider.cpp index 4cfa7144..5401aae1 100755 --- a/bsnes/phoenix/qt/widget/horizontal-slider.cpp +++ b/bsnes/phoenix/qt/widget/horizontal-slider.cpp @@ -22,6 +22,7 @@ void pHorizontalSlider::constructor() { qtSlider->setPageStep(101 >> 3); connect(qtSlider, SIGNAL(valueChanged(int)), SLOT(onChange())); + pWidget::synchronizeState(); setLength(horizontalSlider.state.length); setPosition(horizontalSlider.state.position); } diff --git a/bsnes/phoenix/qt/widget/label.cpp b/bsnes/phoenix/qt/widget/label.cpp index 98adf71f..8dd86eb6 100755 --- a/bsnes/phoenix/qt/widget/label.cpp +++ b/bsnes/phoenix/qt/widget/label.cpp @@ -9,6 +9,8 @@ void pLabel::setText(const string &text) { void pLabel::constructor() { qtWidget = qtLabel = new QLabel; + + pWidget::synchronizeState(); setText(label.state.text); } diff --git a/bsnes/phoenix/qt/widget/line-edit.cpp b/bsnes/phoenix/qt/widget/line-edit.cpp index 6f992028..a13f3b81 100755 --- a/bsnes/phoenix/qt/widget/line-edit.cpp +++ b/bsnes/phoenix/qt/widget/line-edit.cpp @@ -19,6 +19,8 @@ void pLineEdit::constructor() { qtWidget = qtLineEdit = new QLineEdit; connect(qtLineEdit, SIGNAL(returnPressed()), SLOT(onActivate())); connect(qtLineEdit, SIGNAL(textEdited(const QString&)), SLOT(onChange())); + + pWidget::synchronizeState(); setEditable(lineEdit.state.editable); setText(lineEdit.state.text); } diff --git a/bsnes/phoenix/qt/widget/list-view.cpp b/bsnes/phoenix/qt/widget/list-view.cpp index ae169c7d..9d1c7126 100755 --- a/bsnes/phoenix/qt/widget/list-view.cpp +++ b/bsnes/phoenix/qt/widget/list-view.cpp @@ -102,6 +102,7 @@ void pListView::constructor() { connect(qtListView, SIGNAL(currentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*)), SLOT(onChange(QTreeWidgetItem*))); connect(qtListView, SIGNAL(itemChanged(QTreeWidgetItem*, int)), SLOT(onTick(QTreeWidgetItem*))); + pWidget::synchronizeState(); setCheckable(listView.state.checkable); setHeaderText(listView.state.headerText.size() ? listView.state.headerText : lstring{ " " }); setHeaderVisible(listView.state.headerVisible); @@ -113,6 +114,7 @@ void pListView::constructor() { } setSelected(listView.state.selected); if(listView.state.selected) setSelection(listView.state.selection); + autoSizeColumns(); } void pListView::destructor() { diff --git a/bsnes/phoenix/qt/widget/progress-bar.cpp b/bsnes/phoenix/qt/widget/progress-bar.cpp index a132f5bd..8178bb66 100755 --- a/bsnes/phoenix/qt/widget/progress-bar.cpp +++ b/bsnes/phoenix/qt/widget/progress-bar.cpp @@ -11,6 +11,7 @@ void pProgressBar::constructor() { qtProgressBar->setRange(0, 100); qtProgressBar->setTextVisible(false); + pWidget::synchronizeState(); setPosition(progressBar.state.position); } diff --git a/bsnes/phoenix/qt/widget/radio-box.cpp b/bsnes/phoenix/qt/widget/radio-box.cpp index f02ae64a..f8e7ae40 100755 --- a/bsnes/phoenix/qt/widget/radio-box.cpp +++ b/bsnes/phoenix/qt/widget/radio-box.cpp @@ -23,7 +23,7 @@ void pRadioBox::setGroup(const reference_array &group) { delete qtGroup; qtGroup = 0; } - if(qtRadioBox == group[0].p.qtRadioBox) { + if(group.size() > 0 && qtRadioBox == group[0].p.qtRadioBox) { qtGroup = new QButtonGroup; foreach(item, group) qtGroup->addButton(item.p.qtRadioBox); setChecked(); @@ -42,6 +42,8 @@ void pRadioBox::constructor() { qtRadioBox->setChecked(true); connect(qtRadioBox, SIGNAL(toggled(bool)), SLOT(onTick())); + pWidget::synchronizeState(); + setGroup(radioBox.state.group); setText(radioBox.state.text); } diff --git a/bsnes/phoenix/qt/widget/text-edit.cpp b/bsnes/phoenix/qt/widget/text-edit.cpp index 67b8578f..ed168866 100755 --- a/bsnes/phoenix/qt/widget/text-edit.cpp +++ b/bsnes/phoenix/qt/widget/text-edit.cpp @@ -24,6 +24,8 @@ string pTextEdit::text() { void pTextEdit::constructor() { qtWidget = qtTextEdit = new QTextEdit; connect(qtTextEdit, SIGNAL(textChanged()), SLOT(onChange())); + + pWidget::synchronizeState(); setEditable(textEdit.state.editable); setText(textEdit.state.text); setWordWrap(textEdit.state.wordWrap); diff --git a/bsnes/phoenix/qt/widget/vertical-scroll-bar.cpp b/bsnes/phoenix/qt/widget/vertical-scroll-bar.cpp index 226a0274..74d68ca6 100755 --- a/bsnes/phoenix/qt/widget/vertical-scroll-bar.cpp +++ b/bsnes/phoenix/qt/widget/vertical-scroll-bar.cpp @@ -22,6 +22,7 @@ void pVerticalScrollBar::constructor() { qtScrollBar->setPageStep(101 >> 3); connect(qtScrollBar, SIGNAL(valueChanged(int)), SLOT(onChange())); + pWidget::synchronizeState(); setLength(verticalScrollBar.state.length); setPosition(verticalScrollBar.state.position); } diff --git a/bsnes/phoenix/qt/widget/vertical-slider.cpp b/bsnes/phoenix/qt/widget/vertical-slider.cpp index a42c4cb8..500adb07 100755 --- a/bsnes/phoenix/qt/widget/vertical-slider.cpp +++ b/bsnes/phoenix/qt/widget/vertical-slider.cpp @@ -22,6 +22,7 @@ void pVerticalSlider::constructor() { qtSlider->setPageStep(101 >> 3); connect(qtSlider, SIGNAL(valueChanged(int)), SLOT(onChange())); + pWidget::synchronizeState(); setLength(verticalSlider.state.length); setPosition(verticalSlider.state.position); } diff --git a/bsnes/phoenix/qt/widget/viewport.cpp b/bsnes/phoenix/qt/widget/viewport.cpp index ed03c566..3a6254f9 100755 --- a/bsnes/phoenix/qt/widget/viewport.cpp +++ b/bsnes/phoenix/qt/widget/viewport.cpp @@ -6,6 +6,8 @@ void pViewport::constructor() { qtWidget = new QWidget; qtWidget->setAttribute(Qt::WA_PaintOnScreen, true); qtWidget->setStyleSheet("background: #000000"); + + pWidget::synchronizeState(); } void pViewport::destructor() { diff --git a/bsnes/phoenix/qt/widget/widget.cpp b/bsnes/phoenix/qt/widget/widget.cpp index e05f70a9..0bc4901c 100755 --- a/bsnes/phoenix/qt/widget/widget.cpp +++ b/bsnes/phoenix/qt/widget/widget.cpp @@ -31,6 +31,14 @@ void pWidget::constructor() { if(widget.state.abstract) qtWidget = new QWidget; } +//pWidget::constructor() called before p{Derived}::constructor(); ergo qtWidget is not yet valid +//pWidget::synchronizeState() is called to finish construction of p{Derived}::constructor() +void pWidget::synchronizeState() { + setEnabled(widget.state.enabled); + setFont(widget.state.font); +//setVisible(widget.state.visible); +} + void pWidget::destructor() { if(widget.state.abstract) { delete qtWidget; diff --git a/bsnes/snes/chip/icd2/icd2.cpp b/bsnes/snes/chip/icd2/icd2.cpp index 45208c6b..0d515b5e 100755 --- a/bsnes/snes/chip/icd2/icd2.cpp +++ b/bsnes/snes/chip/icd2/icd2.cpp @@ -59,8 +59,8 @@ void ICD2::reset() { r7800 = 0x0000; mlt_req = 0; - foreach(byte, lcd.buffer) byte = 0xff; - foreach(byte, lcd.output) byte = 0xff; + foreach(byte, lcd.buffer) byte = 0; + foreach(byte, lcd.output) byte = 0; lcd.row = 0; packetsize = 0; diff --git a/bsnes/snes/chip/icd2/mmio/mmio.cpp b/bsnes/snes/chip/icd2/mmio/mmio.cpp index 0ee16ef5..0f6f64e2 100755 --- a/bsnes/snes/chip/icd2/mmio/mmio.cpp +++ b/bsnes/snes/chip/icd2/mmio/mmio.cpp @@ -6,9 +6,7 @@ void ICD2::render(const uint8 *source) { for(unsigned y = 0; y < 8; y++) { for(unsigned x = 0; x < 160; x++) { - unsigned pixel = *source++ / 0x55; - pixel ^= 3; - + unsigned pixel = *source++; unsigned addr = y * 2 + (x / 8 * 16); lcd.output[addr + 0] |= ((pixel & 1) >> 0) << (7 - (x & 7)); lcd.output[addr + 1] |= ((pixel & 2) >> 1) << (7 - (x & 7)); diff --git a/bsnes/snes/interface/interface.cpp b/bsnes/snes/interface/interface.cpp index 5f6fba95..54fa945d 100755 --- a/bsnes/snes/interface/interface.cpp +++ b/bsnes/snes/interface/interface.cpp @@ -66,6 +66,14 @@ void Interface::unloadCartridge() { cartridge.unload(); } +Cartridge::Information& Interface::information() { + return cartridge.information; +} + +linear_vector& Interface::memory() { + return cartridge.nvram; +} + void Interface::power() { system.power(); } diff --git a/bsnes/snes/interface/interface.hpp b/bsnes/snes/interface/interface.hpp index c91c98a6..7f7acb45 100755 --- a/bsnes/snes/interface/interface.hpp +++ b/bsnes/snes/interface/interface.hpp @@ -22,6 +22,9 @@ public: virtual void loadSuperGameBoyCartridge(const CartridgeData &base, const CartridgeData &slot); virtual void unloadCartridge(); + Cartridge::Information& information(); + linear_vector& memory(); + virtual void power(); virtual void reset(); virtual void run(); diff --git a/bsnes/snes/ppu/screen/screen.cpp b/bsnes/snes/ppu/screen/screen.cpp index 04beab96..5de6c33c 100755 --- a/bsnes/snes/ppu/screen/screen.cpp +++ b/bsnes/snes/ppu/screen/screen.cpp @@ -214,7 +214,7 @@ PPU::Screen::Screen(PPU &self) : self(self) { unsigned ar = (luma * r + 0.5); unsigned ag = (luma * g + 0.5); unsigned ab = (luma * b + 0.5); - light_table[l][(r << 10) + (g << 5) + b] = (ab << 10) + (ag << 5) + ar; + light_table[l][(b << 10) + (g << 5) + r] = (ab << 10) + (ag << 5) + ar; } } } diff --git a/bsnes/snes/snes.hpp b/bsnes/snes/snes.hpp index 31871ed9..3e4e6144 100755 --- a/bsnes/snes/snes.hpp +++ b/bsnes/snes/snes.hpp @@ -4,7 +4,7 @@ namespace SNES { namespace Info { static const char Name[] = "bsnes"; - static const char Version[] = "082.17"; + static const char Version[] = "082.18"; static const unsigned SerializerVersion = 22; } } @@ -12,7 +12,7 @@ namespace SNES { /* bsnes - SNES emulator author: byuu - license: GPLv2 + license: GPLv3 project started: 2004-10-14 */ diff --git a/bsnes/ui/config/config.cpp b/bsnes/ui/config/config.cpp index b1b09412..6d3cd7be 100755 --- a/bsnes/ui/config/config.cpp +++ b/bsnes/ui/config/config.cpp @@ -5,11 +5,24 @@ Config::Config() { attach(video.driver = "", "Video::Driver"); attach(video.shader = "", "Video::Shader"); attach(video.synchronize = true, "Video::Synchronize"); - attach(video.smooth = false, "Video::Smooth"); + attach(video.enableOverscan = false, "Video::EnableOverscan"); + attach(video.correctAspectRatio = true, "Video::CorrectAspectRatio"); + attach(video.smooth = true, "Video::Smooth"); + + attach(video.brightness = 100, "Video::Brightness"); + attach(video.contrast = 100, "Video::Contrast"); + attach(video.gamma = 100, "Video::Gamma"); + attach(video.gammaRamp = true, "Video::GammaRamp"); + attach(video.fullScreenMode = 0, "Video::FullScreenMode"); attach(audio.driver = "", "Audio::Driver"); attach(audio.synchronize = true, "Audio::Synchronize"); attach(audio.mute = false, "Audio::Mute"); + attach(audio.volume = 100, "Audio::Volume"); + + attach(audio.frequencyNES = 1789772, "Audio::Frequency::NES"); + attach(audio.frequencySNES = 32000, "Audio::Frequency::SNES"); + attach(audio.frequencyGameBoy = 4194304, "Audio::Frequency::GameBoy"); attach(input.driver = "", "Input::Driver"); attach(input.focusPolicy = 1, "Input::FocusPolicy"); diff --git a/bsnes/ui/config/config.hpp b/bsnes/ui/config/config.hpp index 3bacf588..2d757174 100755 --- a/bsnes/ui/config/config.hpp +++ b/bsnes/ui/config/config.hpp @@ -3,13 +3,26 @@ struct Config : public configuration { string driver; string shader; bool synchronize; + bool enableOverscan; + bool correctAspectRatio; bool smooth; + + unsigned brightness; + unsigned contrast; + unsigned gamma; + bool gammaRamp; + unsigned fullScreenMode; } video; struct Audio { string driver; bool synchronize; bool mute; + unsigned volume; + + unsigned frequencyNES; + unsigned frequencySNES; + unsigned frequencyGameBoy; } audio; struct Input { diff --git a/bsnes/ui/general/dip-switches.cpp b/bsnes/ui/general/dip-switches.cpp new file mode 100755 index 00000000..8920ffbd --- /dev/null +++ b/bsnes/ui/general/dip-switches.cpp @@ -0,0 +1,69 @@ +DipSwitches *dipSwitches = 0; + +DipSwitch::DipSwitch() { + append(name, ~0, 0, 5); + append(value, ~0, 0); +} + +DipSwitches::DipSwitches() { + setTitle("DIP Switches"); + + layout.setMargin(5); + acceptButton.setText("Accept"); + + append(layout); + for(unsigned n = 0; n < 8; n++) + layout.append(dip[n], ~0, 0, 5); + layout.append(controlLayout, ~0, 0, 5); + controlLayout.append(spacer, ~0, 0); + controlLayout.append(acceptButton, 0, 0); + + setGeometry({ 128, 128, 400, layout.minimumGeometry().height }); + windowManager->append(this, "DipSwitches"); + + acceptButton.onTick = { &DipSwitches::accept, this }; +} + +void DipSwitches::load() { + if(interface->mode() != Interface::Mode::SNES || SNES::cartridge.has_nss_dip() == false) return; + application->pause = true; + + auto info = interface->snes.information().nss; + unsigned count = info.setting.size(); + + for(unsigned n = 0; n < min(8, count); n++) { + dip[n].setEnabled(true); + dip[n].name.setText(info.setting[n]); + dip[n].value.reset(); + for(unsigned z = 0; z < min(16, info.option[n].size()); z++) { + lstring part; + part.split<1>(":", info.option[n][z]); + values[n][z] = hex(part[0]); + dip[n].value.append(part[1]); + } + } + + for(unsigned n = count; n < 8; n++) { + dip[n].setEnabled(false); + dip[n].name.setText("(unused)"); + dip[n].value.reset(); + dip[n].value.append("(unused)"); + } + + acceptButton.setFocused(); + setVisible(); +} + +void DipSwitches::accept() { + auto info = interface->snes.information().nss; + unsigned count = info.setting.size(); + + unsigned result = 0x0000; + for(unsigned n = 0; n < min(8, count); n++) { + result |= values[n][dip[n].value.selection()]; + } + + setVisible(false); + SNES::nss.set_dip(result); + application->pause = false; +} diff --git a/bsnes/ui/general/dip-switches.hpp b/bsnes/ui/general/dip-switches.hpp new file mode 100755 index 00000000..2b023738 --- /dev/null +++ b/bsnes/ui/general/dip-switches.hpp @@ -0,0 +1,23 @@ +struct DipSwitch : HorizontalLayout { + Label name; + ComboBox value; + + DipSwitch(); +}; + +struct DipSwitches : Window { + VerticalLayout layout; + DipSwitch dip[8]; + HorizontalLayout controlLayout; + Widget spacer; + Button acceptButton; + + void load(); + void accept(); + DipSwitches(); + +private: + unsigned values[8][16]; +}; + +extern DipSwitches *dipSwitches; diff --git a/bsnes/ui/general/general.cpp b/bsnes/ui/general/general.cpp index 50e7f351..b8c55a30 100755 --- a/bsnes/ui/general/general.cpp +++ b/bsnes/ui/general/general.cpp @@ -2,3 +2,4 @@ #include "main-window.cpp" #include "file-browser.cpp" #include "slot-loader.cpp" +#include "dip-switches.cpp" diff --git a/bsnes/ui/general/general.hpp b/bsnes/ui/general/general.hpp index a0f43444..ab6a06af 100755 --- a/bsnes/ui/general/general.hpp +++ b/bsnes/ui/general/general.hpp @@ -1,3 +1,4 @@ #include "main-window.hpp" #include "file-browser.hpp" #include "slot-loader.hpp" +#include "dip-switches.hpp" diff --git a/bsnes/ui/general/main-window.cpp b/bsnes/ui/general/main-window.cpp index 54010bcb..f4b82fcd 100755 --- a/bsnes/ui/general/main-window.cpp +++ b/bsnes/ui/general/main-window.cpp @@ -2,7 +2,7 @@ MainWindow *mainWindow = 0; MainWindow::MainWindow() { setTitle(application->title); - setGeometry({ 256, 256, 512, 480 }); + setGeometry({ 256, 256, 640, 480 }); setBackgroundColor({ 0, 0, 0 }); windowManager->append(this, "MainWindow"); @@ -65,6 +65,10 @@ MainWindow::MainWindow() { settingsSynchronizeVideo.setChecked(config->video.synchronize); settingsSynchronizeAudio.setText("Synchronize Audio"); settingsSynchronizeAudio.setChecked(config->audio.synchronize); + settingsEnableOverscan.setText("Enable Overscan"); + settingsEnableOverscan.setChecked(config->video.enableOverscan); + settingsCorrectAspectRatio.setText("Correct Aspect Ratio"); + settingsCorrectAspectRatio.setChecked(config->video.correctAspectRatio); settingsSmoothVideo.setText("Smooth Video Output"); settingsSmoothVideo.setChecked(config->video.smooth); settingsMuteAudio.setText("Mute Audio"); @@ -146,9 +150,12 @@ MainWindow::MainWindow() { settingsMenu.append(settingsSeparator1); settingsMenu.append(settingsSynchronizeVideo); settingsMenu.append(settingsSynchronizeAudio); + settingsMenu.append(settingsSeparator2); + settingsMenu.append(settingsEnableOverscan); + settingsMenu.append(settingsCorrectAspectRatio); settingsMenu.append(settingsSmoothVideo); settingsMenu.append(settingsMuteAudio); - settingsMenu.append(settingsSeparator2); + settingsMenu.append(settingsSeparator3); settingsMenu.append(settingsConfiguration); append(toolsMenu); @@ -250,6 +257,16 @@ MainWindow::MainWindow() { audio.set(Audio::Synchronize, config->audio.synchronize); }; + settingsEnableOverscan.onTick = [&] { + config->video.enableOverscan = settingsEnableOverscan.checked(); + utility->resizeMainWindow(); + }; + + settingsCorrectAspectRatio.onTick = [&] { + config->video.correctAspectRatio = settingsCorrectAspectRatio.checked(); + utility->resizeMainWindow(); + }; + settingsSmoothVideo.onTick = [&] { config->video.smooth = settingsSmoothVideo.checked(); video.set(Video::Filter, config->video.smooth == false ? 0u : 1u); @@ -262,17 +279,17 @@ MainWindow::MainWindow() { settingsConfiguration.onTick = [&] { settingsWindow->setVisible(); }; - toolsStateSave1.onTick = [&] { interface->saveState({ interface->baseName, "-1.bst" }); }; - toolsStateSave2.onTick = [&] { interface->saveState({ interface->baseName, "-2.bst" }); }; - toolsStateSave3.onTick = [&] { interface->saveState({ interface->baseName, "-3.bst" }); }; - toolsStateSave4.onTick = [&] { interface->saveState({ interface->baseName, "-4.bst" }); }; - toolsStateSave5.onTick = [&] { interface->saveState({ interface->baseName, "-5.bst" }); }; + toolsStateSave1.onTick = [&] { interface->saveState(1); }; + toolsStateSave2.onTick = [&] { interface->saveState(2); }; + toolsStateSave3.onTick = [&] { interface->saveState(3); }; + toolsStateSave4.onTick = [&] { interface->saveState(4); }; + toolsStateSave5.onTick = [&] { interface->saveState(5); }; - toolsStateLoad1.onTick = [&] { interface->loadState({ interface->baseName, "-1.bst" }); }; - toolsStateLoad2.onTick = [&] { interface->loadState({ interface->baseName, "-2.bst" }); }; - toolsStateLoad3.onTick = [&] { interface->loadState({ interface->baseName, "-3.bst" }); }; - toolsStateLoad4.onTick = [&] { interface->loadState({ interface->baseName, "-4.bst" }); }; - toolsStateLoad5.onTick = [&] { interface->loadState({ interface->baseName, "-5.bst" }); }; + toolsStateLoad1.onTick = [&] { interface->loadState(1); }; + toolsStateLoad2.onTick = [&] { interface->loadState(2); }; + toolsStateLoad3.onTick = [&] { interface->loadState(3); }; + toolsStateLoad4.onTick = [&] { interface->loadState(4); }; + toolsStateLoad5.onTick = [&] { interface->loadState(5); }; toolsCaptureMouse.onTick = [&] { input.acquire(); }; toolsShrinkWindow.onTick = [&] { utility->resizeMainWindow(true); }; diff --git a/bsnes/ui/general/main-window.hpp b/bsnes/ui/general/main-window.hpp index 5ed765d3..ce6bbf2a 100755 --- a/bsnes/ui/general/main-window.hpp +++ b/bsnes/ui/general/main-window.hpp @@ -45,9 +45,12 @@ struct MainWindow : Window { Separator settingsSeparator1; CheckItem settingsSynchronizeVideo; CheckItem settingsSynchronizeAudio; + Separator settingsSeparator2; + CheckItem settingsEnableOverscan; + CheckItem settingsCorrectAspectRatio; CheckItem settingsSmoothVideo; CheckItem settingsMuteAudio; - Separator settingsSeparator2; + Separator settingsSeparator3; Item settingsConfiguration; Menu toolsMenu; diff --git a/bsnes/ui/input/input.cpp b/bsnes/ui/input/input.cpp index 8f62f022..02724ff1 100755 --- a/bsnes/ui/input/input.cpp +++ b/bsnes/ui/input/input.cpp @@ -177,10 +177,6 @@ void InputManager::scan() { if(mainWindow->focused()) userInterface.inputEvent(n, scancode[activeScancode][n]); } } - - if(scancode[activeScancode][keyboard(0)[Keyboard::Escape]]) { - if(mainWindow->fullScreen() == false && input.acquired()) input.unacquire(); - } } InputManager::InputManager() { diff --git a/bsnes/ui/input/user-interface.cpp b/bsnes/ui/input/user-interface.cpp index 02cc6796..2363cca3 100755 --- a/bsnes/ui/input/user-interface.cpp +++ b/bsnes/ui/input/user-interface.cpp @@ -1,4 +1,28 @@ void HotkeyGeneral::inputEvent(int16_t scancode, int16_t value) { + if(scancode == saveState.scancode && value) { + interface->saveState(activeSlot); + } + + if(scancode == loadState.scancode && value) { + interface->loadState(activeSlot); + } + + if(scancode == decrementSlot.scancode && value) { + if(--activeSlot == 0) activeSlot = 5; + utility->showMessage({ "Selected slot ", activeSlot }); + } + + if(scancode == incrementSlot.scancode && value) { + if(++activeSlot == 6) activeSlot = 1; + utility->showMessage({ "Selected slot ", activeSlot }); + } + + if(scancode == toggleMouseCapture.scancode && value) { + if(mainWindow->fullScreen() == false) { + input.acquired() ? input.unacquire() : input.acquire(); + } + } + if(scancode == toggleFullScreen.scancode && value) { utility->toggleFullScreen(); } @@ -19,22 +43,57 @@ void HotkeyGeneral::inputEvent(int16_t scancode, int16_t value) { audio.set(Audio::Synchronize, Async); } } + + if(scancode == power.scancode && value) { + interface->power(); + } + + if(scancode == reset.scancode && value) { + interface->reset(); + } + + if(scancode == quit.scancode && value) { + application->quit = true; + } } HotkeyGeneral::HotkeyGeneral() { name = "General"; + saveState.name = "Save state"; + loadState.name = "Load state"; + decrementSlot.name = "Decrement state slot"; + incrementSlot.name = "Increment state slot"; + toggleMouseCapture.name = "Toggle mouse capture"; toggleFullScreen.name = "Toggle fullscreen"; pause.name = "Pause emulation"; turboMode.name = "Turbo mode"; + power.name = "Power cycle"; + reset.name = "Reset"; + quit.name = "Close emulator"; + saveState.mapping = "KB0::F5"; + loadState.mapping = "KB0::F7"; + decrementSlot.mapping = "KB0::F6"; + incrementSlot.mapping = "KB0::F8"; + toggleMouseCapture.mapping = "KB0::F12"; toggleFullScreen.mapping = "KB0::F11"; pause.mapping = "KB0::P"; turboMode.mapping = "KB0::Tilde"; + append(saveState); + append(loadState); + append(decrementSlot); + append(incrementSlot); + append(toggleMouseCapture); append(toggleFullScreen); append(pause); append(turboMode); + append(power); + append(reset); + append(quit); + + activeSlot = 1; } // diff --git a/bsnes/ui/input/user-interface.hpp b/bsnes/ui/input/user-interface.hpp index 328b26b9..62134c2d 100755 --- a/bsnes/ui/input/user-interface.hpp +++ b/bsnes/ui/input/user-interface.hpp @@ -1,10 +1,21 @@ struct HotkeyGeneral : TertiaryInput { + DigitalInput saveState; + DigitalInput loadState; + DigitalInput decrementSlot; + DigitalInput incrementSlot; + DigitalInput toggleMouseCapture; DigitalInput toggleFullScreen; DigitalInput pause; DigitalInput turboMode; + DigitalInput power; + DigitalInput reset; + DigitalInput quit; void inputEvent(int16_t scancode, int16_t value); HotkeyGeneral(); + +private: + unsigned activeSlot; }; struct HotkeyInput : SecondaryInput { diff --git a/bsnes/ui/interface/gameboy.cpp b/bsnes/ui/interface/gameboy.cpp index c65377ce..a8bf1bbe 100755 --- a/bsnes/ui/interface/gameboy.cpp +++ b/bsnes/ui/interface/gameboy.cpp @@ -10,11 +10,27 @@ bool InterfaceGameBoy::loadCartridge(const string &filename) { GameBoy::Interface::loadCartridge(info.xml, data, size); delete[] data; + if(GameBoy::Interface::memorySize(GameBoy::Interface::Memory::RAM) > 0) { + filemap fp; + if(fp.open(string{ interface->baseName, ".sav" }, filemap::mode::read)) { + memcpy(GameBoy::Interface::memoryData(GameBoy::Interface::Memory::RAM), fp.data(), + min(GameBoy::Interface::memorySize(GameBoy::Interface::Memory::RAM), fp.size()) + ); + } + } + interface->loadCartridge(::Interface::Mode::GameBoy); return true; } void InterfaceGameBoy::unloadCartridge() { + if(GameBoy::Interface::memorySize(GameBoy::Interface::Memory::RAM) > 0) { + file::write({ interface->baseName, ".sav" }, + GameBoy::Interface::memoryData(GameBoy::Interface::Memory::RAM), + GameBoy::Interface::memorySize(GameBoy::Interface::Memory::RAM) + ); + } + GameBoy::Interface::unloadCartridge(); interface->baseName = ""; } @@ -36,23 +52,21 @@ bool InterfaceGameBoy::loadState(const string &filename) { // void InterfaceGameBoy::videoRefresh(const uint8_t *data) { - interface->videoRefresh(); + static uint16_t output[160 * 144]; + static uint32_t palette[] = { + 0x9bbc0f, 0x8bac0f, 0x306230, 0x0f380f + }; - uint32_t *output; - unsigned outpitch; - if(video.lock(output, outpitch, 160, 144)) { - for(unsigned y = 0; y < 144; y++) { - const uint8_t *sp = data + y * 160; - uint32_t *dp = output + y * (outpitch >> 2); - for(unsigned x = 0; x < 160; x++) { - uint32_t color = *sp++; - *dp++ = (color << 16) | (color << 8) | (color << 0); - } + for(unsigned y = 0; y < 144; y++) { + const uint8_t *sp = data + y * 160; + uint16_t *dp = output + y * 160; + for(unsigned x = 0; x < 160; x++) { + uint32_t color = palette[*sp++]; + *dp++ = ((color & 0xf80000) >> 9) | ((color & 0x00f800) >> 6) | ((color & 0x0000f8) >> 3); } - - video.unlock(); - video.refresh(); } + + interface->videoRefresh(output, 160 * 2, 160, 144); } void InterfaceGameBoy::audioSample(int16_t csample, int16_t lsample, int16_t rsample) { diff --git a/bsnes/ui/interface/interface.cpp b/bsnes/ui/interface/interface.cpp index b36bfee4..d64ba9a5 100755 --- a/bsnes/ui/interface/interface.cpp +++ b/bsnes/ui/interface/interface.cpp @@ -1,4 +1,5 @@ #include "../base.hpp" +#include "palette.cpp" #include "nes.cpp" #include "snes.cpp" #include "gameboy.cpp" @@ -25,6 +26,15 @@ void Interface::setController(unsigned port, unsigned device) { } } +void Interface::updateDSP() { + dspaudio.setVolume((double)config->audio.volume / 100.0); + switch(mode()) { + case Mode::NES: return dspaudio.setFrequency(config->audio.frequencyNES); + case Mode::SNES: return dspaudio.setFrequency(config->audio.frequencySNES); + case Mode::GameBoy: return dspaudio.setFrequency(config->audio.frequencyGameBoy); + } +} + bool Interface::cartridgeLoaded() { switch(mode()) { case Mode::NES: return nes.cartridgeLoaded(); @@ -39,6 +49,7 @@ void Interface::loadCartridge(Mode mode) { bindControllers(); cheatEditor->load({ baseName, ".cht" }); stateManager->load({ baseName, ".bsa" }, 0u); + dipSwitches->load(); utility->showMessage({ "Loaded ", notdir(baseName) }); } @@ -63,23 +74,27 @@ void Interface::unloadCartridge() { case Mode::GameBoy: gameBoy.unloadCartridge(); break; } + interface->baseName = ""; + interface->slotName.reset(); utility->setMode(mode = Mode::None); } void Interface::power() { switch(mode()) { - case Mode::NES: return nes.power(); - case Mode::SNES: return snes.power(); - case Mode::GameBoy: return gameBoy.power(); + case Mode::NES: nes.power(); break; + case Mode::SNES: snes.power(); break; + case Mode::GameBoy: gameBoy.power(); break; } + utility->showMessage("System power was cycled"); } void Interface::reset() { switch(mode()) { - case Mode::NES: return nes.reset(); - case Mode::SNES: return snes.reset(); - case Mode::GameBoy: return gameBoy.power(); //Game Boy lacks reset button + case Mode::NES: nes.reset(); break; + case Mode::SNES: snes.reset(); break; + case Mode::GameBoy: gameBoy.power(); break; //Game Boy lacks reset button } + utility->showMessage("System was reset"); } void Interface::run() { @@ -106,23 +121,25 @@ bool Interface::unserialize(serializer &s) { return false; } -bool Interface::saveState(const string &filename) { +bool Interface::saveState(unsigned slot) { + string filename = { baseName, "-", slot, ".bst" }; bool result = false; switch(mode()) { case Mode::SNES: result = snes.saveState(filename); break; case Mode::GameBoy: result = gameBoy.saveState(filename); break; } - utility->showMessage(result == true ? "Saved state" : "Failed to save state"); + utility->showMessage(result == true ? string{ "Saved state ", slot } : "Failed to save state"); return result; } -bool Interface::loadState(const string &filename) { +bool Interface::loadState(unsigned slot) { + string filename = { baseName, "-", slot, ".bst" }; bool result = false; switch(mode()) { case Mode::SNES: result = snes.loadState(filename); break; case Mode::GameBoy: result = gameBoy.loadState(filename); break; } - utility->showMessage(result == true ? "Loaded state" : "Failed to load state"); + utility->showMessage(result == true ? string{ "Loaded state ", slot } : "Failed to load state"); return result; } @@ -136,6 +153,7 @@ void Interface::setCheatCodes(const lstring &list) { Interface::Interface() { mode = Mode::None; + palette.update(); nes.initialize(&nes); snes.initialize(&snes); gameBoy.initialize(&gameBoy); @@ -143,7 +161,26 @@ Interface::Interface() { //internal -void Interface::videoRefresh() { +//RGB555 input +void Interface::videoRefresh(const uint16_t *input, unsigned inputPitch, unsigned width, unsigned height) { + uint32_t *output; + unsigned outputPitch; + + if(video.lock(output, outputPitch, width, height)) { + inputPitch >>= 1, outputPitch >>= 2; + + for(unsigned y = 0; y < height; y++) { + const uint16_t *sp = input + y * inputPitch; + uint32_t *dp = output + y * outputPitch; + for(unsigned x = 0; x < width; x++) { + *dp++ = palette[*sp++]; + } + } + + video.unlock(); + video.refresh(); + } + static unsigned frameCounter = 0; static time_t previous, current; frameCounter++; diff --git a/bsnes/ui/interface/interface.hpp b/bsnes/ui/interface/interface.hpp index 95c1b258..1b693ad7 100755 --- a/bsnes/ui/interface/interface.hpp +++ b/bsnes/ui/interface/interface.hpp @@ -1,3 +1,5 @@ +#include "palette.hpp" + #include "nes.hpp" #include "snes.hpp" #include "gameboy.hpp" @@ -8,6 +10,7 @@ struct Interface : property { void bindControllers(); void setController(unsigned port, unsigned device); + void updateDSP(); bool cartridgeLoaded(); void loadCartridge(Mode mode); @@ -21,15 +24,16 @@ struct Interface : property { serializer serialize(); bool unserialize(serializer&); - bool saveState(const string &filename); - bool loadState(const string &filename); + bool saveState(unsigned slot); + bool loadState(unsigned slot); void setCheatCodes(const lstring &list = lstring{}); Interface(); - void videoRefresh(); + void videoRefresh(const uint16_t *input, unsigned inputPitch, unsigned width, unsigned height); string baseName; // = "/path/to/cartridge" (no extension) + lstring slotName; InterfaceNES nes; InterfaceSNES snes; diff --git a/bsnes/ui/interface/nes.cpp b/bsnes/ui/interface/nes.cpp index 6487fd4e..68a8c038 100755 --- a/bsnes/ui/interface/nes.cpp +++ b/bsnes/ui/interface/nes.cpp @@ -23,11 +23,26 @@ bool InterfaceNES::loadCartridge(const string &filename) { NES::Interface::loadCartridge("", fp.data(), fp.size()); fp.close(); + if(NES::Interface::memorySize(NES::Interface::Memory::RAM) > 0) { + if(fp.open(string{ interface->baseName, ".sav" }, filemap::mode::read)) { + memcpy(NES::Interface::memoryData(NES::Interface::Memory::RAM), fp.data(), + min(NES::Interface::memorySize(NES::Interface::Memory::RAM), fp.size()) + ); + } + } + interface->loadCartridge(::Interface::Mode::NES); return true; } void InterfaceNES::unloadCartridge() { + if(NES::Interface::memorySize(NES::Interface::Memory::RAM) > 0) { + file::write({ interface->baseName, ".sav" }, + NES::Interface::memoryData(NES::Interface::Memory::RAM), + NES::Interface::memorySize(NES::Interface::Memory::RAM) + ); + } + NES::Interface::unloadCartridge(); interface->baseName = ""; } @@ -35,22 +50,24 @@ void InterfaceNES::unloadCartridge() { // void InterfaceNES::videoRefresh(const uint16_t *data) { - interface->videoRefresh(); + static uint16_t output[256 * 240]; - uint32_t *output; - unsigned outpitch; - if(video.lock(output, outpitch, 256, 240)) { - for(unsigned y = 0; y < 240; y++) { - const uint16_t *sp = data + y * 256; - uint32_t *dp = output + y * (outpitch >> 2); - for(unsigned x = 0; x < 256; x++) { - *dp++ = palette[*sp++]; - } - } - - video.unlock(); - video.refresh(); + unsigned height = 240; + if(config->video.enableOverscan == false) { + height = 224; + data += 8 * 256; } + + for(unsigned y = 0; y < height; y++) { + const uint16_t *sp = data + y * 256; + uint16_t *dp = output + y * 256; + for(unsigned x = 0; x < 256; x++) { + uint32_t color = palette[*sp++]; + *dp++ = ((color & 0xf80000) >> 9) | ((color & 0x00f800) >> 6) | ((color & 0x0000f8) >> 3);; + } + } + + interface->videoRefresh(output, 256 * 2, 256, height); } void InterfaceNES::audioSample(int16_t sample) { diff --git a/bsnes/ui/interface/palette.cpp b/bsnes/ui/interface/palette.cpp new file mode 100755 index 00000000..bbb7789b --- /dev/null +++ b/bsnes/ui/interface/palette.cpp @@ -0,0 +1,67 @@ +Palette palette; + +uint32_t Palette::operator[](unsigned n) { + return color[n]; +} + +const uint8_t Palette::gammaRamp[32] = { + 0x00, 0x01, 0x03, 0x06, 0x0a, 0x0f, 0x15, 0x1c, + 0x24, 0x2d, 0x37, 0x42, 0x4e, 0x5b, 0x69, 0x78, + 0x88, 0x90, 0x98, 0xa0, 0xa8, 0xb0, 0xb8, 0xc0, + 0xc8, 0xd0, 0xd8, 0xe0, 0xe8, 0xf0, 0xf8, 0xff, +}; + +uint8_t Palette::contrastAdjust(uint8_t input) { + signed contrast = config->video.contrast - 100; + signed result = input - contrast + (2 * contrast * input + 127) / 255; + return max(0, min(255, result)); +} + +uint8_t Palette::brightnessAdjust(uint8_t input) { + signed brightness = config->video.brightness - 100; + signed result = input + brightness; + return max(0, min(255, result)); +} + +uint8_t Palette::gammaAdjust(uint8_t input) { + signed result = (signed)(pow(((double)input / 255.0), (double)config->video.gamma / 100.0) * 255.0 + 0.5); + return max(0, min(255, result)); +} + +void Palette::update() { + for(unsigned i = 0; i < 32768; i++) { + unsigned r = (i >> 10) & 31; + unsigned g = (i >> 5) & 31; + unsigned b = (i >> 0) & 31; + + r = (r << 3) | (r >> 2); + g = (g << 3) | (g >> 2); + b = (b << 3) | (b >> 2); + + if(config->video.gammaRamp) { + r = gammaRamp[r >> 3]; + g = gammaRamp[g >> 3]; + b = gammaRamp[b >> 3]; + } + + if(config->video.contrast != 100) { + r = contrastAdjust(r); + g = contrastAdjust(g); + b = contrastAdjust(b); + } + + if(config->video.brightness != 100) { + r = brightnessAdjust(r); + g = brightnessAdjust(g); + b = brightnessAdjust(b); + } + + if(config->video.gamma != 100) { + r = gammaAdjust(r); + g = gammaAdjust(g); + b = gammaAdjust(b); + } + + color[i] = (r << 16) | (g << 8) | (b << 0); + } +} diff --git a/bsnes/ui/interface/palette.hpp b/bsnes/ui/interface/palette.hpp new file mode 100755 index 00000000..0579ae4d --- /dev/null +++ b/bsnes/ui/interface/palette.hpp @@ -0,0 +1,14 @@ +struct Palette { + alwaysinline uint32_t operator[](unsigned n); + + uint8_t contrastAdjust(uint8_t); + uint8_t brightnessAdjust(uint8_t); + uint8_t gammaAdjust(uint8_t); + void update(); + +private: + static const uint8_t gammaRamp[32]; + uint32_t color[32768]; +}; + +extern Palette palette; diff --git a/bsnes/ui/interface/snes.cpp b/bsnes/ui/interface/snes.cpp index e554b71d..5354487f 100755 --- a/bsnes/ui/interface/snes.cpp +++ b/bsnes/ui/interface/snes.cpp @@ -21,18 +21,24 @@ void InterfaceSNES::setController(bool port, unsigned device) { } } -bool InterfaceSNES::loadCartridge(const string &filename) { +bool InterfaceSNES::loadCartridge(const string &basename) { uint8_t *data; unsigned size; - if(file::read(filename, data, size) == false) return false; + if(file::read(basename, data, size) == false) return false; interface->unloadCartridge(); - interface->baseName = nall::basename(filename); + interface->baseName = nall::basename(basename); + + string xml; + xml.readfile({ interface->baseName, ".xml" }); + if(xml == "") xml = SNESCartridge(data, size).xmlMemoryMap; - string xml = SNESCartridge(data, size).xmlMemoryMap; SNES::Interface::loadCartridge({ xml, data, size }); delete[] data; + interface->slotName = { nall::basename(basename) }; + + loadMemory(); interface->loadCartridge(::Interface::Mode::SNES); return true; } @@ -47,11 +53,17 @@ bool InterfaceSNES::loadSatellaviewSlottedCartridge(const string &basename, cons interface->baseName = nall::basename(basename); if(data[1]) interface->baseName.append("+", nall::basename(notdir(slotname))); - string xml = SNESCartridge(data[0], size[0]).xmlMemoryMap; + string xml; + xml.readfile({ interface->baseName, ".xml" }); + if(xml == "") xml = SNESCartridge(data[0], size[0]).xmlMemoryMap; + SNES::Interface::loadSatellaviewSlottedCartridge({ xml, data[0], size[0] }, { "", data[1], size[1] }); delete[] data[0]; if(data[1]) delete[] data[1]; + interface->slotName = { nall::basename(basename), nall::basename(slotname) }; + + loadMemory(); interface->loadCartridge(::Interface::Mode::SNES); return true; } @@ -66,11 +78,17 @@ bool InterfaceSNES::loadSatellaviewCartridge(const string &basename, const strin interface->baseName = nall::basename(basename); if(data[1]) interface->baseName.append("+", nall::basename(notdir(slotname))); - string xml = SNESCartridge(data[0], size[0]).xmlMemoryMap; + string xml; + xml.readfile({ interface->baseName, ".xml" }); + if(xml == "") xml = SNESCartridge(data[0], size[0]).xmlMemoryMap; + SNES::Interface::loadSatellaviewCartridge({ xml, data[0], size[0] }, { "", data[1], size[1] }); delete[] data[0]; if(data[1]) delete[] data[1]; + interface->slotName = { nall::basename(basename), nall::basename(slotname) }; + + loadMemory(); interface->loadCartridge(::Interface::Mode::SNES); return true; } @@ -88,12 +106,18 @@ bool InterfaceSNES::loadSufamiTurboCartridge(const string &basename, const strin else if(data[1]) interface->baseName = nall::basename(slotAname); else if(data[2]) interface->baseName = nall::basename(slotBname); - string xml = SNESCartridge(data[0], size[0]).xmlMemoryMap; + string xml; + xml.readfile({ interface->baseName, ".xml" }); + if(xml == "") xml = SNESCartridge(data[0], size[0]).xmlMemoryMap; + SNES::Interface::loadSufamiTurboCartridge({ xml, data[0], size[0] }, { "", data[1], size[1] }, { "", data[2], size[2] }); delete[] data[0]; if(data[1]) delete[] data[1]; if(data[2]) delete[] data[2]; + interface->slotName = { nall::basename(basename), nall::basename(slotAname), nall::basename(slotBname) }; + + loadMemory(); interface->loadCartridge(::Interface::Mode::SNES); return true; } @@ -108,21 +132,56 @@ bool InterfaceSNES::loadSuperGameBoyCartridge(const string &basename, const stri interface->baseName = nall::basename(basename); if(data[1]) interface->baseName = nall::basename(slotname); - string xml = SNESCartridge(data[0], size[0]).xmlMemoryMap; - string gbXml = GameBoyCartridge(data[1], size[1]).xml; + string xml; + xml.readfile({ interface->baseName, ".xml" }); + if(xml == "") xml = SNESCartridge(data[0], size[0]).xmlMemoryMap; + + string gbXml; + gbXml.readfile({ nall::basename(slotname), ".xml" }); + if(gbXml == "") gbXml = GameBoyCartridge(data[1], size[1]).xml; SNES::Interface::loadSuperGameBoyCartridge({ xml, data[0], size[0] }, { gbXml, data[1], size[1] }); delete[] data[0]; if(data[1]) delete[] data[1]; + interface->slotName = { nall::basename(basename), nall::basename(slotname) }; + + loadMemory(); interface->loadCartridge(::Interface::Mode::SNES); return true; } void InterfaceSNES::unloadCartridge() { + saveMemory(); SNES::Interface::unloadCartridge(); interface->baseName = ""; } +//slot[] array = Cartridge::Slot to slot# conversion: +//{ Base, Bsx, SufamiTurbo, SufamiTurboA, SufamiTurboB, GameBoy } + +void InterfaceSNES::loadMemory() { + static unsigned slot[] = { 0, 0, 0, 1, 2, 1 }; + foreach(memory, SNES::Interface::memory()) { + if(memory.size == 0) continue; + string filename = { interface->slotName[slot[(unsigned)memory.slot]], memory.id }; + uint8_t *data; + unsigned size; + if(file::read(filename, data, size)) { + memcpy(memory.data, data, min(memory.size, size)); + delete[] data; + } + } +} + +void InterfaceSNES::saveMemory() { + static unsigned slot[] = { 0, 0, 0, 1, 2, 1 }; + foreach(memory, SNES::Interface::memory()) { + if(memory.size == 0) continue; + string filename = { interface->slotName[slot[(unsigned)memory.slot]], memory.id }; + file::write(filename, memory.data, memory.size); + } +} + bool InterfaceSNES::saveState(const string &filename) { serializer s = serialize(); return file::write(filename, s.data(), s.size()); @@ -140,39 +199,32 @@ bool InterfaceSNES::loadState(const string &filename) { // void InterfaceSNES::videoRefresh(const uint16_t *data, bool hires, bool interlace, bool overscan) { - interface->videoRefresh(); + static uint16_t output[512 * 478]; - unsigned width = hires ? 512 : 256; - unsigned height = 0 ? 224 : 239; - if(interlace) height <<= 1; - unsigned inpitch = interlace ? 1024 : 2048; + unsigned width = 256 << hires; + unsigned height = (config->video.enableOverscan ? 240 : 224) << interlace; + unsigned pitch = 1024 >> interlace; - if(0) { //NTSC + //data[] = scanline { 8 (blank) + 240 (video) + 8 (blank) } + //first line of video data is not rendered (effectively blank as well) + if(config->video.enableOverscan) { + if(overscan == false) data += 1 * 1024; // 8 + 224 + 8 + if(overscan == true ) data += 9 * 1024; // 0 + 240 + 0 + } else { if(overscan == false) data += 9 * 1024; // 0 + 224 + 0 - if(overscan == true ) data += 16 * 1024; //-7 + 224 + -7 + if(overscan == true ) data += 16 * 1024; //-8 + 224 + -8 } - if(1) { //PAL - if(overscan == false) data += 1 * 1024; // 8 + 224 + 7 - if(overscan == true ) data += 9 * 1024; // 0 + 239 + 0 - } - - uint32_t *output; - unsigned outpitch; - if(video.lock(output, outpitch, width, height)) { - for(unsigned y = 0; y < height; y++) { - const uint16_t *sp = data + y * (inpitch >> 1); - uint32_t *dp = output + y * (outpitch >> 2); - for(unsigned x = 0; x < width; x++) { - uint32_t color = *sp++; - color = ((color & 0x7c00) << 9) | ((color & 0x03e0) << 6) | ((color & 0x001f) << 3); - *dp++ = color | ((color >> 3) & 0x070707); - } + for(unsigned y = 0; y < height; y++) { + const uint16_t *sp = data + y * pitch; + uint16_t *dp = output + y * 512; + for(unsigned x = 0; x < width; x++) { + uint16_t color = *sp++; + *dp++ = ((color & 0x001f) << 10) | (color & 0x03e0) | ((color & 0x7c00) >> 10); } - - video.unlock(); - video.refresh(); } + + interface->videoRefresh(output, 512 * 2, width, height); } void InterfaceSNES::audioSample(int16_t lsample, int16_t rsample) { diff --git a/bsnes/ui/interface/snes.hpp b/bsnes/ui/interface/snes.hpp index 429e2af3..31e9ab3d 100755 --- a/bsnes/ui/interface/snes.hpp +++ b/bsnes/ui/interface/snes.hpp @@ -8,6 +8,9 @@ struct InterfaceSNES : SNES::Interface { bool loadSuperGameBoyCartridge(const string &basename, const string &slotname); void unloadCartridge(); + void loadMemory(); + void saveMemory(); + bool saveState(const string &filename); bool loadState(const string &filename); diff --git a/bsnes/ui/main.cpp b/bsnes/ui/main.cpp index 09bcd265..69e73ef5 100755 --- a/bsnes/ui/main.cpp +++ b/bsnes/ui/main.cpp @@ -57,6 +57,7 @@ Application::Application(int argc, char **argv) { mainWindow = new MainWindow; fileBrowser = new FileBrowser; slotLoader = new SlotLoader; + dipSwitches = new DipSwitches; settingsWindow = new SettingsWindow; cheatEditor = new CheatEditor; stateManager = new StateManager; @@ -104,6 +105,7 @@ Application::~Application() { delete stateManager; delete cheatEditor; delete settingsWindow; + delete dipSwitches; delete slotLoader; delete fileBrowser; delete mainWindow; diff --git a/bsnes/ui/settings/advanced.hpp b/bsnes/ui/settings/advanced.hpp index 036e4148..860527d8 100755 --- a/bsnes/ui/settings/advanced.hpp +++ b/bsnes/ui/settings/advanced.hpp @@ -1,4 +1,4 @@ -struct AdvancedSettings : VerticalLayout { +struct AdvancedSettings : SettingsLayout { Label title; Label driverLabel; HorizontalLayout driverLayout; diff --git a/bsnes/ui/settings/audio.cpp b/bsnes/ui/settings/audio.cpp new file mode 100755 index 00000000..f934c5ea --- /dev/null +++ b/bsnes/ui/settings/audio.cpp @@ -0,0 +1,84 @@ +AudioSettings *audioSettings = 0; + +AudioSlider::AudioSlider() { + append(name, 75, 0); + append(value, 75, 0); + append(slider, ~0, 0); +} + +unsigned AudioSlider::position() { + unsigned value = slider.position(), center = slider.length() >> 1; + if(value > center) return base + (value - center) * step; + if(value < center) return base - (center - value) * step; + return base; +} + +void AudioSlider::setPosition(unsigned position) { + signed value = position - base, center = slider.length() >> 1; + if(value < 0) return slider.setPosition(center - (abs(value) / step)); + if(value > 0) return slider.setPosition((abs(value) / step) + center); + return slider.setPosition(center); +} + +AudioSettings::AudioSettings() { + title.setFont(application->titleFont); + title.setText("Audio Settings"); + frequencyAdjustmentLabel.setFont(application->boldFont); + frequencyAdjustmentLabel.setText("Frequency: (lower to reduce audio crackling; raise to reduce video tearing)"); + + nes.name.setText("NES:"); + nes.slider.setLength(2001); + nes.base = 1789772; + nes.step = 56; + + snes.name.setText("SNES:"); + snes.slider.setLength(2001); + snes.base = 32000; + snes.step = 1; + + gameBoy.name.setText("Game Boy:"); + gameBoy.slider.setLength(2001); + gameBoy.base = 4194304; + gameBoy.step = 131; + + outputAdjustmentLabel.setFont(application->boldFont); + outputAdjustmentLabel.setText("Output:"); + + volume.name.setText("Volume:"); + volume.slider.setLength(201); + volume.base = 100; + volume.step = 1; + + append(title, ~0, 0, 5); + append(frequencyAdjustmentLabel, ~0, 0); + append(nes, ~0, 0); + append(snes, ~0, 0); + append(gameBoy, ~0, 0, 5); + append(outputAdjustmentLabel, ~0, 0); + append(volume, ~0, 0); + + nes.setPosition(config->audio.frequencyNES); + snes.setPosition(config->audio.frequencySNES); + gameBoy.setPosition(config->audio.frequencyGameBoy); + volume.setPosition(config->audio.volume); + + nes.slider.onChange = snes.slider.onChange = gameBoy.slider.onChange = + volume.slider.onChange = + { &AudioSettings::synchronize, this }; + + synchronize(); +} + +void AudioSettings::synchronize() { + config->audio.frequencyNES = nes.position(); + config->audio.frequencySNES = snes.position(); + config->audio.frequencyGameBoy = gameBoy.position(); + config->audio.volume = volume.position(); + + nes.value.setText({ nes.position(), "hz" }); + snes.value.setText({ snes.position(), "hz" }); + gameBoy.value.setText({ gameBoy.position(), "hz" }); + volume.value.setText({ volume.position(), "%" }); + + interface->updateDSP(); +} diff --git a/bsnes/ui/settings/audio.hpp b/bsnes/ui/settings/audio.hpp new file mode 100755 index 00000000..78abc331 --- /dev/null +++ b/bsnes/ui/settings/audio.hpp @@ -0,0 +1,27 @@ +struct AudioSlider : HorizontalLayout { + Label name; + Label value; + HorizontalSlider slider; + + unsigned base; + unsigned step; + + unsigned position(); + void setPosition(unsigned position); + AudioSlider(); +}; + +struct AudioSettings : SettingsLayout { + Label title; + Label frequencyAdjustmentLabel; + AudioSlider nes; + AudioSlider snes; + AudioSlider gameBoy; + Label outputAdjustmentLabel; + AudioSlider volume; + + void synchronize(); + AudioSettings(); +}; + +extern AudioSettings *audioSettings; diff --git a/bsnes/ui/settings/input.cpp b/bsnes/ui/settings/input.cpp index 866e0a12..96704c9c 100755 --- a/bsnes/ui/settings/input.cpp +++ b/bsnes/ui/settings/input.cpp @@ -52,6 +52,7 @@ void InputSettings::primaryChange() { for(unsigned n = 0; n < input.size(); n++) { secondary.append(input[n].name); } + secondary.setEnabled(input.size() > 1); secondaryChange(); } @@ -62,6 +63,7 @@ void InputSettings::secondaryChange() { for(unsigned n = 0; n < input.size(); n++) { tertiary.append(input[n].name); } + tertiary.setEnabled(input.size() > 1); tertiaryChange(); } diff --git a/bsnes/ui/settings/input.hpp b/bsnes/ui/settings/input.hpp index ff35b69f..34d8c11f 100755 --- a/bsnes/ui/settings/input.hpp +++ b/bsnes/ui/settings/input.hpp @@ -1,4 +1,4 @@ -struct InputSettings : VerticalLayout { +struct InputSettings : SettingsLayout { Label title; HorizontalLayout selectionLayout; ComboBox primary; diff --git a/bsnes/ui/settings/settings.cpp b/bsnes/ui/settings/settings.cpp index d4c50ca2..3baa3529 100755 --- a/bsnes/ui/settings/settings.cpp +++ b/bsnes/ui/settings/settings.cpp @@ -1,51 +1,69 @@ #include "../base.hpp" +#include "video.cpp" +#include "audio.cpp" #include "input.cpp" #include "advanced.cpp" SettingsWindow *settingsWindow = 0; +void SettingsLayout::append(Sizable &sizable, unsigned width, unsigned height, unsigned spacing) { + layout.append(sizable, width, height, spacing); +} + +SettingsLayout::SettingsLayout() { + setMargin(5); + HorizontalLayout::append(spacer, 120, ~0, 5); + HorizontalLayout::append(layout, ~0, ~0); +} + SettingsWindow::SettingsWindow() { setTitle("Configuration Settings"); setGeometry({ 128, 128, 640, 360 }); setStatusVisible(); windowManager->append(this, "SettingsWindow"); + layout.setMargin(5); + panelList.setFont(application->boldFont); + panelList.append("Video"); + panelList.append("Audio"); panelList.append("Input"); panelList.append("Advanced"); + videoSettings = new VideoSettings; + audioSettings = new AudioSettings; inputSettings = new InputSettings; advancedSettings = new AdvancedSettings; append(layout); - layout.setMargin(5); - layout.append(panelList, 120, ~0, 5); + layout.append(panelList, 120, ~0, 5); + append(*videoSettings); + append(*audioSettings); + append(*inputSettings); + append(*advancedSettings); panelList.onChange = [&] { setPanel(panelList.selection()); }; - setPanel(0); + setPanel(2); } SettingsWindow::~SettingsWindow() { delete advancedSettings; delete inputSettings; + delete audioSettings; + delete videoSettings; } void SettingsWindow::setPanel(unsigned n) { - //TODO: removing layouts isn't working right, so for now we are hiding them on toggle - - layout.remove(*inputSettings); - layout.remove(*advancedSettings); + panelList.setSelection(n); + videoSettings->setVisible(false); + audioSettings->setVisible(false); inputSettings->setVisible(false); advancedSettings->setVisible(false); switch(n) { - case 0: - layout.append(*inputSettings, ~0, ~0); - inputSettings->setVisible(); - break; - case 1: - layout.append(*advancedSettings, ~0, ~0); - advancedSettings->setVisible(); - break; + case 0: return videoSettings->setVisible(); + case 1: return audioSettings->setVisible(); + case 2: return inputSettings->setVisible(); + case 3: return advancedSettings->setVisible(); } } diff --git a/bsnes/ui/settings/settings.hpp b/bsnes/ui/settings/settings.hpp index bd18e182..5cc3c458 100755 --- a/bsnes/ui/settings/settings.hpp +++ b/bsnes/ui/settings/settings.hpp @@ -1,3 +1,13 @@ +struct SettingsLayout : HorizontalLayout { + Widget spacer; + VerticalLayout layout; + + void append(Sizable &widget, unsigned width, unsigned height, unsigned spacing = 0); + SettingsLayout(); +}; + +#include "video.hpp" +#include "audio.hpp" #include "input.hpp" #include "advanced.hpp" diff --git a/bsnes/ui/settings/video.cpp b/bsnes/ui/settings/video.cpp new file mode 100755 index 00000000..5e50f067 --- /dev/null +++ b/bsnes/ui/settings/video.cpp @@ -0,0 +1,66 @@ +VideoSettings *videoSettings = 0; + +VideoSlider::VideoSlider() { + slider.setLength(201); + + append(name, 75, 0); + append(value, 75, 0); + append(slider, ~0, 0); +} + +VideoSettings::VideoSettings() { + title.setFont(application->titleFont); + title.setText("Video Settings"); + colorAdjustment.setFont(application->boldFont); + colorAdjustment.setText("Color adjustment:"); + brightness.name.setText("Brightness:"); + contrast.name.setText("Contrast:"); + gamma.name.setText("Gamma:"); + gammaRamp.setText("Enable gamma ramp simulation"); + fullScreenMode.setFont(application->boldFont); + fullScreenMode.setText("Fullscreen mode:"); + fullScreen[0].setText("Center"); + fullScreen[1].setText("Scale"); + fullScreen[2].setText("Stretch"); + RadioBox::group(fullScreen[0], fullScreen[1], fullScreen[2]); + + append(title, ~0, 0, 5); + append(colorAdjustment, ~0, 0); + append(brightness, ~0, 0); + append(contrast, ~0, 0); + append(gamma, ~0, 0); + append(gammaRamp, ~0, 0, 5); + append(fullScreenMode, ~0, 0); + append(fullScreenLayout, ~0, 0); + fullScreenLayout.append(fullScreen[0], ~0, 0, 5); + fullScreenLayout.append(fullScreen[1], ~0, 0, 5); + fullScreenLayout.append(fullScreen[2], ~0, 0); + + brightness.slider.setPosition(config->video.brightness); + contrast.slider.setPosition(config->video.contrast); + gamma.slider.setPosition(config->video.gamma); + gammaRamp.setChecked(config->video.gammaRamp); + fullScreen[config->video.fullScreenMode].setChecked(); + + synchronize(); + + brightness.slider.onChange = contrast.slider.onChange = gamma.slider.onChange = + gammaRamp.onTick = fullScreen[0].onTick = fullScreen[1].onTick = fullScreen[2].onTick = + { &VideoSettings::synchronize, this }; +} + +void VideoSettings::synchronize() { + config->video.brightness = brightness.slider.position(); + config->video.contrast = contrast.slider.position(); + config->video.gamma = gamma.slider.position(); + config->video.gammaRamp = gammaRamp.checked(); + if(fullScreen[0].checked()) config->video.fullScreenMode = 0; + if(fullScreen[1].checked()) config->video.fullScreenMode = 1; + if(fullScreen[2].checked()) config->video.fullScreenMode = 2; + + brightness.value.setText({ config->video.brightness, "%" }); + contrast.value.setText({ config->video.contrast, "%" }); + gamma.value.setText({ config->video.gamma, "%" }); + + palette.update(); +} diff --git a/bsnes/ui/settings/video.hpp b/bsnes/ui/settings/video.hpp new file mode 100755 index 00000000..55a04d9d --- /dev/null +++ b/bsnes/ui/settings/video.hpp @@ -0,0 +1,24 @@ +struct VideoSlider : HorizontalLayout { + Label name; + Label value; + HorizontalSlider slider; + + VideoSlider(); +}; + +struct VideoSettings : SettingsLayout { + Label title; + Label colorAdjustment; + VideoSlider brightness; + VideoSlider contrast; + VideoSlider gamma; + CheckBox gammaRamp; + Label fullScreenMode; + HorizontalLayout fullScreenLayout; + RadioBox fullScreen[3]; + + void synchronize(); + VideoSettings(); +}; + +extern VideoSettings *videoSettings; diff --git a/bsnes/ui/utility/utility.cpp b/bsnes/ui/utility/utility.cpp index 130828fd..72ad5761 100755 --- a/bsnes/ui/utility/utility.cpp +++ b/bsnes/ui/utility/utility.cpp @@ -19,23 +19,21 @@ void Utility::setMode(Interface::Mode mode) { mainWindow->setTitle({ notdir(interface->baseName), " - ", NES::Info::Name, " v", NES::Info::Version }); mainWindow->nesMenu.setVisible(true); dspaudio.setChannels(1); - dspaudio.setFrequency(315.0 / 88.0 * 6000000.0 / 12.0); } else if(mode == Interface::Mode::SNES) { mainWindow->setTitle({ notdir(interface->baseName), " - ", SNES::Info::Name, " v", SNES::Info::Version }); mainWindow->snesMenu.setVisible(true); dspaudio.setChannels(2); - dspaudio.setFrequency(32040.0); } else if(mode == Interface::Mode::GameBoy) { mainWindow->setTitle({ notdir(interface->baseName), " - ", GameBoy::Info::Name, " v", GameBoy::Info::Version }); mainWindow->gameBoyMenu.setVisible(true); dspaudio.setChannels(2); - dspaudio.setFrequency(4194304.0); } + interface->updateDSP(); mainWindow->synchronize(); resizeMainWindow(); } @@ -45,11 +43,17 @@ void Utility::resizeMainWindow(bool shrink) { unsigned width = geometry.width, height = geometry.height; switch(interface->mode()) { - case Interface::Mode::NES: width = 256, height = 240; break; - case Interface::Mode::SNES: width = 256, height = 239; break; + case Interface::Mode::NES: width = 256, height = config->video.enableOverscan ? 240 : 224; break; + case Interface::Mode::SNES: width = 256, height = config->video.enableOverscan ? 240 : 224; break; case Interface::Mode::GameBoy: width = 160, height = 144; break; } + if(config->video.correctAspectRatio) { + if(interface->mode() != Interface::Mode::GameBoy) { + width = (double)width * (config->video.enableOverscan ? 1.225 : 1.149); + } + } + unsigned maxW = geometry.width / width; unsigned maxH = geometry.height / height; unsigned maxM = max(1u, min(maxW, maxH)); @@ -57,6 +61,18 @@ void Utility::resizeMainWindow(bool shrink) { width = width * maxM; height = height * maxM; + if(mainWindow->fullScreen() == true) { + if(config->video.fullScreenMode == 1) { //scale + width = (double)width * ((double)geometry.height / height); + height = geometry.height; + } + + if(config->video.fullScreenMode == 2) { //stretch + width = geometry.width; + height = geometry.height; + } + } + if(shrink == false) { if(geometry.width < width ) width = geometry.width; if(geometry.height < height) height = geometry.height;