Update to v093r07 release.

byuu says:

Changelog:
- importing a game won't show message box on success
- importing a game will select the game that was imported in the list
  - caveat: GTK+ port doesn't seem to be removing focus from item 0 even
    though the selection is on item 2
- Game Boy audio reduced in volume by 50%
- Game Boy Advance audio reduced in volume by 50%
- Game Boy internally mixes audio at 2MHz now
- Game Boy Advance's Game Boy audio hardware internally mixes audio at
  2MHz now
- Game Boy Color doesn't sort sprites by X-coordinate
- Game Boy Color allows transparency on BGpriority pixels
  - caveat: this seems to allow sprites to appear on top of windows
- Game Boy Color VRAM DMA transfers 16 bytes in 8 clocks (or 16 clocks
  in double speed mode)
- Game Boy Color VRAM DMA masks low 4-bits of source and destination
  address
- Game Boy Color VRAM DMA only allows reads from ROM or RAM
- Game Boy Color VRAM DMA only allows writes to VRAM
- fixed a bug in dereferencing a nullptr from pObject::find(), should
  fix crash when pressing enter key on blank windows
- fixed Windows RadioItem selection
- Game Boy Advance color emulation code added
This commit is contained in:
Tim Allen 2013-12-10 23:12:54 +11:00
parent 35f1605829
commit 0f78acffd7
37 changed files with 177 additions and 88 deletions

View File

@ -3,7 +3,7 @@
namespace Emulator {
static const char Name[] = "higan";
static const char Version[] = "093.06";
static const char Version[] = "093.07";
static const char Author[] = "byuu";
static const char License[] = "GPLv3";
static const char Website[] = "http://byuu.org/";

View File

@ -54,7 +54,7 @@ void APU::main() {
}
void APU::power() {
create(Main, 4 * 1024 * 1024);
create(Main, 2 * 1024 * 1024);
for(unsigned n = 0xff10; n <= 0xff3f; n++) bus.mmio[n] = this;
for(auto& n : mmio_data) n = 0x00;

View File

@ -6,7 +6,7 @@ struct APU : Thread, MMIO {
#include "master/master.hpp"
uint8 mmio_data[48];
uint13 sequencer_base;
uint12 sequencer_base;
uint3 sequencer_step;
Square1 square1;

View File

@ -21,8 +21,8 @@ void APU::Master::run() {
if(channel3_left_enable) sample += apu.wave.output;
if(channel4_left_enable) sample += apu.noise.output;
sample = (sample * 512) - 16384;
sample = (sample * (left_volume + 1)) / 8;
left = sample;
sample = (sample * (left_volume + 1)) / 8;
left = sample;
sample = 0;
if(channel1_right_enable) sample += apu.square1.output;
@ -32,6 +32,11 @@ void APU::Master::run() {
sample = (sample * 512) - 16384;
sample = (sample * (right_volume + 1)) / 8;
right = sample;
//reduce audio volume
center >>= 1;
left >>= 1;
right >>= 1;
}
void APU::Master::write(unsigned r, uint8 data) {

View File

@ -48,8 +48,8 @@ void APU::Noise::write(unsigned r, uint8 data) {
if(r == 3) { //$ff22 NR43
frequency = data >> 4;
narrow_lfsr = data & 0x08;
divisor = (data & 0x07) << 4;
if(divisor == 0) divisor = 8;
divisor = (data & 0x07) << 3;
if(divisor == 0) divisor = 4;
period = divisor << frequency;
}

View File

@ -6,7 +6,7 @@ bool APU::Square1::dac_enable() {
void APU::Square1::run() {
if(period && --period == 0) {
period = 4 * (2048 - frequency);
period = 2 * (2048 - frequency);
phase++;
switch(duty) {
case 0: duty_output = (phase == 6); break; //______-_
@ -34,7 +34,7 @@ void APU::Square1::sweep(bool update) {
} else if(sweep_shift && update) {
frequency_shadow = freq;
frequency = freq & 2047;
period = 4 * (2048 - frequency);
period = 2 * (2048 - frequency);
}
}
@ -91,7 +91,7 @@ void APU::Square1::write(unsigned r, uint8 data) {
if(initialize) {
enable = dac_enable();
period = 4 * (2048 - frequency);
period = 2 * (2048 - frequency);
envelope_period = envelope_frequency;
volume = envelope_volume;
frequency_shadow = frequency;

View File

@ -6,7 +6,7 @@ bool APU::Square2::dac_enable() {
void APU::Square2::run() {
if(period && --period == 0) {
period = 4 * (2048 - frequency);
period = 2 * (2048 - frequency);
phase++;
switch(duty) {
case 0: duty_output = (phase == 6); break; //______-_
@ -60,7 +60,7 @@ void APU::Square2::write(unsigned r, uint8 data) {
if(initialize) {
enable = dac_enable();
period = 4 * (2048 - frequency);
period = 2 * (2048 - frequency);
envelope_period = envelope_frequency;
volume = envelope_volume;
}

View File

@ -2,7 +2,7 @@
void APU::Wave::run() {
if(period && --period == 0) {
period = 2 * (2048 - frequency);
period = 1 * (2048 - frequency);
pattern_sample = pattern[++pattern_offset];
}
@ -48,7 +48,7 @@ void APU::Wave::write(unsigned r, uint8 data) {
if(initialize) {
enable = dac_enable;
period = 2 * (2048 - frequency);
period = 1 * (2048 - frequency);
pattern_offset = 0;
}
}

View File

@ -185,6 +185,7 @@ void CPU::power() {
status.dma_mode = 0;
status.dma_length = 0;
status.dma_completed = true;
status.ff6c = 0;
status.ff72 = 0;

View File

@ -57,6 +57,7 @@ struct CPU : Processor::LR35902, Thread, MMIO {
//$ff55 HDMA5
bool dma_mode;
uint16 dma_length;
bool dma_completed;
//$ff6c ???
uint8 ff6c;
@ -102,6 +103,8 @@ struct CPU : Processor::LR35902, Thread, MMIO {
uint8 op_read(uint16 addr);
void op_write(uint16 addr, uint8 data);
void cycle_edge();
uint8 dma_read(uint16 addr);
void dma_write(uint16 addr, uint8 data);
uint8 debugger_read(uint16 addr);
//timing.cpp

View File

@ -25,6 +25,20 @@ void CPU::cycle_edge() {
}
}
//VRAM DMA source can only be ROM or RAM
uint8 CPU::dma_read(uint16 addr) {
if(addr < 0x8000) return bus.read(addr); //0000-7fff
if(addr < 0xa000) return 0x00; //8000-9fff
if(addr < 0xe000) return bus.read(addr); //a000-dfff
return 0x00; //e000-ffff
}
//VRAM DMA target is always VRAM
void CPU::dma_write(uint16 addr, uint8 data) {
addr = 0x8000 | (addr & 0x1fff); //8000-9fff
return bus.write(addr, data);
}
uint8 CPU::debugger_read(uint16 addr) {
return bus.read(addr);
}

View File

@ -81,7 +81,8 @@ uint8 CPU::mmio_read(uint16 addr) {
}
if(addr == 0xff55) { //HDMA5
return (status.dma_length / 16) - 1;
return (status.dma_completed << 7)
| (((status.dma_length / 16) - 1) & 0x7f);
}
if(addr == 0xff56) { //RP
@ -203,7 +204,7 @@ void CPU::mmio_write(uint16 addr, uint8 data) {
}
if(addr == 0xff52) { //HDMA2
status.dma_source = (status.dma_source & 0xff00) | (data << 0);
status.dma_source = (status.dma_source & 0xff00) | (data & 0xf0);
return;
}
@ -213,18 +214,24 @@ void CPU::mmio_write(uint16 addr, uint8 data) {
}
if(addr == 0xff54) { //HDMA4
status.dma_target = (status.dma_target & 0xff00) | (data << 0);
status.dma_target = (status.dma_target & 0xff00) | (data & 0xf0);
return;
}
if(addr == 0xff55) { //HDMA5
status.dma_mode = data & 0x80;
status.dma_length = ((data & 0x7f) + 1) * 16;
status.dma_completed = !status.dma_mode;
if(status.dma_mode == 0) do {
bus.write(status.dma_target++, bus.read(status.dma_source++));
add_clocks(4 << status.speed_double);
} while(--status.dma_length);
if(status.dma_mode == 0) {
do {
for(unsigned n = 0; n < 16; n++) {
dma_write(status.dma_target++, dma_read(status.dma_source++));
}
add_clocks(8 << status.speed_double);
status.dma_length -= 16;
} while(status.dma_length);
}
return;
}

View File

@ -39,6 +39,7 @@ void CPU::serialize(serializer& s) {
s.integer(status.dma_target);
s.integer(status.dma_mode);
s.integer(status.dma_length);
s.integer(status.dma_completed);
s.integer(status.ff6c);

View File

@ -76,11 +76,11 @@ void CPU::timer_4096hz() {
}
void CPU::hblank() {
if(status.dma_mode == 1 && status.dma_length) {
if(status.dma_mode == 1 && status.dma_length && ppu.status.ly < 144) {
for(unsigned n = 0; n < 16; n++) {
bus.write(status.dma_target++, bus.read(status.dma_source++));
add_clocks(4);
dma_write(status.dma_target++, dma_read(status.dma_source++));
}
add_clocks(8 << status.speed_double);
status.dma_length -= 16;
}
}

View File

@ -21,7 +21,7 @@ double Interface::videoFrequency() {
}
double Interface::audioFrequency() {
return 4194304.0;
return 4194304.0 / 2.0;
}
bool Interface::loaded() {

View File

@ -125,19 +125,6 @@ void PPU::cgb_render_ob() {
if(sprites == 10) break;
}
//sort by X-coordinate, when equal, lower address comes first
for(unsigned x = 0; x < sprites; x++) {
for(unsigned y = x + 1; y < sprites; y++) {
signed sx = oam[(sprite[x] << 2) + 1] - 8;
signed sy = oam[(sprite[y] << 2) + 1] - 8;
if(sy < sx) {
sprite[x] ^= sprite[y];
sprite[y] ^= sprite[x];
sprite[x] ^= sprite[y];
}
}
}
//render backwards, so that first sprite has highest priority
for(signed s = sprites - 1; s >= 0; s--) {
unsigned n = sprite[s] << 2;
@ -170,9 +157,8 @@ void PPU::cgb_render_ob() {
if(ox < 160) {
//When LCDC.D0 (BG enable) is off, OB is always rendered above BG+Window
if(status.bg_enable) {
if(pixels[ox].origin == Pixel::Origin::BGP) continue;
if(attr & 0x80) {
if(pixels[ox].origin == Pixel::Origin::BG) {
if(pixels[ox].origin == Pixel::Origin::BG || pixels[ox].origin == Pixel::Origin::BGP) {
if(pixels[ox].palette > 0) continue;
}
}

View File

@ -95,16 +95,12 @@ void PPU::dmg_render_ob() {
if(sprites == 10) break;
}
//sort by X-coordinate, when equal, lower address comes first
//sort by X-coordinate; when equal, lower address comes first
for(unsigned x = 0; x < sprites; x++) {
for(unsigned y = x + 1; y < sprites; y++) {
signed sx = oam[(sprite[x] << 2) + 1] - 8;
signed sy = oam[(sprite[y] << 2) + 1] - 8;
if(sy < sx) {
sprite[x] ^= sprite[y];
sprite[y] ^= sprite[x];
sprite[x] ^= sprite[y];
}
if(sy < sx) std::swap(sprite[x], sprite[y]);
}
}

View File

@ -25,7 +25,7 @@ void APU::Enter() {
}
void APU::main() {
for(unsigned n = 0; n < 128; n++) {
for(unsigned n = 0; n < 64; n++) {
runsequencer();
}
@ -70,7 +70,7 @@ void APU::main() {
if(regs.bias.amplitude == 3) lsample &= ~15, rsample &= ~15;
if(cpu.regs.mode == CPU::Registers::Mode::Stop) lsample = 0, rsample = 0;
interface->audioSample(sclamp<16>(lsample << 7), sclamp<16>(rsample << 7)); //should be <<5, use <<7 for added volume
interface->audioSample(sclamp<16>(lsample << 6), sclamp<16>(rsample << 6)); //should be <<5, use <<6 for added volume
step(512);
}

View File

@ -1,6 +1,6 @@
unsigned APU::Noise::divider() const {
if(divisor == 0) return 8;
return divisor * 16;
if(divisor == 0) return 4;
return divisor * 8;
}
void APU::Noise::run() {

View File

@ -128,7 +128,7 @@ struct Sequencer {
uint1 enable[4];
uint1 masterenable;
uint13 base;
uint12 base;
uint3 step;
int16 lsample;
int16 rsample;

View File

@ -1,6 +1,6 @@
void APU::Square::run() {
if(period && --period == 0) {
period = 4 * (2048 - frequency);
period = 2 * (2048 - frequency);
phase++;
switch(duty) {
case 0: signal = (phase == 6); break; //_____-_

View File

@ -10,7 +10,7 @@ void APU::Square1::runsweep(bool update) {
} else if(sweep.shift && update) {
shadowfrequency = updatefrequency;
frequency = updatefrequency;
period = 4 * (2048 - frequency);
period = 2 * (2048 - frequency);
}
}
@ -64,7 +64,7 @@ void APU::Square1::write(unsigned addr, uint8 byte) {
if(initialize) {
enable = envelope.dacenable();
period = 4 * (2048 - frequency);
period = 2 * (2048 - frequency);
envelope.period = envelope.frequency;
volume = envelope.volume;
shadowfrequency = frequency;

View File

@ -32,7 +32,7 @@ void APU::Square2::write(unsigned addr, uint8 byte) {
if(initialize) {
enable = envelope.dacenable();
period = 4 * (2048 - frequency);
period = 2 * (2048 - frequency);
envelope.period = envelope.frequency;
volume = envelope.volume;
}

View File

@ -1,6 +1,6 @@
void APU::Wave::run() {
if(period && --period == 0) {
period = 2 * (2048 - frequency);
period = 1 * (2048 - frequency);
patternsample = pattern[patternbank * 16 + patternaddr++];
if(patternaddr == 0) patternbank ^= mode;
}
@ -55,7 +55,7 @@ void APU::Wave::write(unsigned addr, uint8 byte) {
if(initialize) {
enable = dacenable;
period = 2 * (2048 - frequency);
period = 1 * (2048 - frequency);
patternaddr = 0;
patternbank = mode ? (uint1)0 : bank;
}

View File

@ -7,13 +7,51 @@ Video video;
void Video::generate_palette(bool color_emulation) {
//todo: implement LCD color emulation
for(unsigned color = 0; color < (1 << 15); color++) {
uint5 b = color >> 10;
uint5 g = color >> 5;
uint5 r = color >> 0;
unsigned B = (color >> 10) & 31;
unsigned G = (color >> 5) & 31;
unsigned R = (color >> 0) & 31;
uint16 R = r << 11 | r << 6 | r << 1 | r >> 4;
uint16 G = g << 11 | g << 6 | g << 1 | g >> 4;
uint16 B = b << 11 | b << 6 | b << 1 | b >> 4;
if(color_emulation) {
R = curve[R];
G = curve[G];
B = curve[B];
unsigned Rr = R * 16;
unsigned Gr = R * 4;
unsigned Br = R * 4;
unsigned Rg = G * 8;
unsigned Gg = G * 16;
unsigned Bg = G * 8;
unsigned Rb = B * 0; //intentionally always zero
unsigned Gb = B * 8;
unsigned Bb = B * 16;
if(Rr < Rg) std::swap(Rr, Rg);
if(Rr < Rb) std::swap(Rr, Rb);
if(Rg < Rb) std::swap(Rg, Rb);
if(Gr < Gg) std::swap(Gr, Gg);
if(Gr < Gb) std::swap(Gr, Gb);
if(Gg < Gb) std::swap(Gg, Gb);
if(Br < Bg) std::swap(Br, Bg);
if(Br < Bb) std::swap(Br, Bb);
if(Bg < Bb) std::swap(Bg, Bb);
R = (((4 * Rr + 2 * Rg + Rb) * 160) >> 14) + 32;
G = (((4 * Gr + 2 * Gg + Gb) * 160) >> 14) + 32;
B = (((4 * Br + 2 * Bg + Bb) * 160) >> 14) + 32;
R = R << 8 | R;
G = G << 8 | G;
B = B << 8 | B;
} else {
R = R << 11 | R << 6 | R << 1 | R >> 4;
G = G << 11 | G << 6 | G << 1 | G >> 4;
B = B << 11 | B << 6 | B << 1 | B >> 4;
}
palette[color] = interface->videoColor(color, R, G, B);
}
@ -27,4 +65,11 @@ Video::~Video() {
delete[] palette;
}
const uint8 Video::curve[32] = {
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0e, 0x10, 0x12,
0x14, 0x16, 0x18, 0x1c, 0x20, 0x28, 0x38, 0x38,
0x40, 0x48, 0x50, 0x58, 0x60, 0x68, 0x70, 0x80,
0x88, 0x90, 0xa0, 0xb0, 0xc0, 0xd0, 0xe0, 0xf0,
};
}

View File

@ -4,6 +4,9 @@ struct Video {
Video();
~Video();
private:
static const uint8 curve[32];
};
extern Video video;

View File

@ -15,8 +15,8 @@ string activepath() {
string realpath(const string& name) {
string result;
char path[PATH_MAX] = "";
if(::realpath(name, path)) result = path;
if(result.empty()) result = {activepath(), name};
if(::realpath(name, path)) result = dir(path);
if(result.empty()) result = activepath();
result.transform("\\", "/");
if(result.endsWith("/") == false) result.append("/");
return result;
@ -25,15 +25,14 @@ string realpath(const string& name) {
// /home/username/
// c:/users/username/
string userpath() {
string result;
#if defined(PLATFORM_WINDOWS)
wchar_t path[PATH_MAX] = L"";
SHGetFolderPathW(nullptr, CSIDL_PROFILE | CSIDL_FLAG_CREATE, nullptr, 0, path);
result = (const char*)utf8_t(path);
string result = (const char*)utf8_t(path);
result.transform("\\", "/");
#else
struct passwd *userinfo = getpwuid(getuid());
result = userinfo->pw_dir;
struct passwd* userinfo = getpwuid(getuid());
string result = userinfo->pw_dir;
#endif
if(result.empty()) result = ".";
if(result.endsWith("/") == false) result.append("/");
@ -43,49 +42,55 @@ string userpath() {
// /home/username/.config/
// c:/users/username/appdata/roaming/
string configpath() {
string result;
#if defined(PLATFORM_WINDOWS)
wchar_t path[PATH_MAX] = L"";
SHGetFolderPathW(nullptr, CSIDL_APPDATA | CSIDL_FLAG_CREATE, nullptr, 0, path);
result = (const char*)utf8_t(path);
string result = (const char*)utf8_t(path);
result.transform("\\", "/");
#elif defined(PLATFORM_MACOSX)
result = {userpath(), "Library/Application Support/"};
string result = {userpath(), "Library/Application Support/"};
#else
result = {userpath(), ".config/"};
string result = {userpath(), ".config/"};
#endif
if(result.empty()) result = ".";
if(result.endsWith("/") == false) result.append("/");
return result;
}
// /usr/share
// /Library/Application Support/
// c:/ProgramData/
string sharedpath() {
string result;
#if defined(PLATFORM_WINDOWS)
wchar_t path[PATH_MAX] = L"";
SHGetFolderPathW(nullptr, CSIDL_COMMON_APPDATA | CSIDL_FLAG_CREATE, nullptr, 0, path);
result = (const char*)utf8_t(path);
string result = (const char*)utf8_t(path);
result.transform("\\", "/");
#elif defined(PLATFORM_MACOSX)
result = "/Library/Application Support/";
string result = "/Library/Application Support/";
#else
result = "/usr/share/";
string result = "/usr/share/";
#endif
if(result.empty()) result = ".";
if(result.endsWith("/") == false) result.append("/");
return result;
}
// /tmp
// c:/users/username/AppData/Local/Temp/
string temppath() {
#if defined(PLATFORM_WINDOWS)
wchar_t path[PATH_MAX] = L"";
GetTempPathW(PATH_MAX, path);
string result = (const char*)utf8_t(path);
result.transform("\\", "/");
return result;
#elif defined(P_tmpdir)
string result = P_tmpdir;
#else
return "/tmp/";
string result = "/tmp/";
#endif
if(result.endsWith("/") == false) result.append("/");
return result;
}
}

View File

@ -9,9 +9,9 @@ pObject::pObject(Object& object) : object(object) {
locked = false;
}
pObject* pObject::find(unsigned id) {
for(auto& item : objects) if(item->id == id) return item;
return 0;
Object* pObject::find(unsigned id) {
for(auto& item : objects) if(item->id == id) return &item->object;
return nullptr;
}
}

View File

@ -71,7 +71,7 @@ struct pObject {
bool locked;
pObject(Object& object);
static pObject* find(unsigned id);
static Object* find(unsigned id);
virtual ~pObject() {}
void constructor() {}

View File

@ -278,10 +278,11 @@ static LRESULT CALLBACK Shared_windowProc(WindowProc windowProc, HWND hwnd, UINT
case WM_COMMAND: {
unsigned id = LOWORD(wparam);
HWND control = GetDlgItem(hwnd, id);
Object* object = control ? (Object*)GetWindowLongPtr(control, GWLP_USERDATA) : (Object*)(&pObject::find(id)->object);
Object* object = control ? (Object*)GetWindowLongPtr(control, GWLP_USERDATA) : pObject::find(id);
if(object == nullptr) break;
if(dynamic_cast<Item*>(object)) { ((Item*)object)->p.onActivate(); return FALSE; }
if(dynamic_cast<CheckItem*>(object)) { ((CheckItem*)object)->p.onToggle(); return FALSE; }
if(dynamic_cast<RadioItem*>(object)) { ((RadioItem*)object)->p.onActivate(); return FALSE; }
if(dynamic_cast<Button*>(object)) { ((Button*)object)->p.onActivate(); return FALSE; }
if(dynamic_cast<CheckButton*>(object)) { ((CheckButton*)object)->p.onToggle(); return FALSE; }
if(dynamic_cast<CheckLabel*>(object)) { ((CheckLabel*)object)->p.onToggle(); return FALSE; }

View File

@ -87,7 +87,7 @@ void SuperFX::disassemble_alt0(char* output) {
case (0x9f): sprintf(t, "fmult"); break;
case16(0xa0): sprintf(t, "ibt r%u,#$%.2x", op0 & 15, op1); break;
case16(0xb0): sprintf(t, "from r%u", op0 & 15); break;
case (0xc0): sprintf(t, "hib");
case (0xc0): sprintf(t, "hib"); break;
case15(0xc1): sprintf(t, "or r%u", op0 & 15); break;
case15(0xd0): sprintf(t, "inc r%u", op0 & 15); break;
case (0xdf): sprintf(t, "getc"); break;

View File

@ -9,7 +9,7 @@ namespace SuperFamicom {
#include "memory/memory.cpp"
#include "mmio/mmio.cpp"
#include "timing/timing.cpp"
#include "disasm/disasm.cpp"
#include "disassembler/disassembler.cpp"
SuperFX superfx;

View File

@ -7,7 +7,7 @@ struct SuperFX : Processor::GSU, Coprocessor {
#include "memory/memory.hpp"
#include "mmio/mmio.hpp"
#include "timing/timing.hpp"
#include "disasm/disasm.hpp"
#include "disassembler/disassembler.hpp"
static void Enter();
void enter();

View File

@ -43,6 +43,7 @@ ConfigurationSettings::ConfigurationSettings() {
append(server, "Server");
library.append(library.selection = -1, "Selection");
library.append(library.mediaMode = 0, "MediaMode");
library.append(library.showOnStartup = true, "ShowOnStartup");
append(library, "Library");

View File

@ -48,6 +48,7 @@ struct ConfigurationSettings : Configuration::Document {
struct Library : Configuration::Node {
signed selection;
unsigned mediaMode;
bool showOnStartup;
} library;

View File

@ -68,6 +68,7 @@ void LibraryBrowser::refresh() {
}
void LibraryBrowser::setMode() {
config->library.mediaMode = mediaMode.selection();
auto& media = emulator.media[mediaMode.selection()];
pathname = {utility->libraryPath(), media.name, "/"};
@ -100,9 +101,10 @@ void LibraryImport::onImportActivate() {
}
function<string ()> browse = program->ananke.sym("ananke_browse");
if(!browse) return;
audio.clear(); //ananke's browser is modal
string pathname = browse();
pathname.rtrim<1>("/");
if(pathname.empty()) return;
MessageWindow().setText({"Successfully imported ", notdir(pathname.rtrim<1>("/"))}).information();
//after importing game, take user to the relevant game list to show the newly imported title
string type = extension(pathname);
@ -115,6 +117,17 @@ void LibraryImport::onImportActivate() {
browser->mediaMode.setSelection(mode);
libraryManager->libraryFrame.setSelection(selection);
libraryManager->onChange();
//find game in list and select it
string name = notdir(nall::basename(pathname));
for(unsigned n = 0; n < browser->folders.rows(); n++) {
if(browser->folders.text(n, 0) == name) {
browser->folders.setSelection(n);
browser->onChange();
break;
}
}
return;
}
mode++;
@ -125,6 +138,8 @@ void LibraryImport::onImportActivate() {
}
LibraryManager::LibraryManager() {
libraryManager = this;
setTitle("Game Library");
setGeometry({128, 128, 640, 680});
windowManager->append(this, "LibraryManager");
@ -155,6 +170,11 @@ LibraryManager::LibraryManager() {
//initial config value of -1 defaults to import tab on first launch of higan
if(config->library.selection < 0) config->library.selection = browsers.size();
libraryFrame.setSelection(config->library.selection);
if(libraryFrame.selection() < browsers.size()) {
browsers[libraryFrame.selection()]->mediaMode.setSelection(config->library.mediaMode);
browsers[libraryFrame.selection()]->setMode();
}
}
void LibraryManager::bootstrap() {