mirror of https://github.com/bsnes-emu/bsnes.git
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:
parent
35f1605829
commit
0f78acffd7
|
@ -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/";
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ double Interface::videoFrequency() {
|
|||
}
|
||||
|
||||
double Interface::audioFrequency() {
|
||||
return 4194304.0;
|
||||
return 4194304.0 / 2.0;
|
||||
}
|
||||
|
||||
bool Interface::loaded() {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -128,7 +128,7 @@ struct Sequencer {
|
|||
uint1 enable[4];
|
||||
uint1 masterenable;
|
||||
|
||||
uint13 base;
|
||||
uint12 base;
|
||||
uint3 step;
|
||||
int16 lsample;
|
||||
int16 rsample;
|
||||
|
|
|
@ -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; //_____-_
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -4,6 +4,9 @@ struct Video {
|
|||
|
||||
Video();
|
||||
~Video();
|
||||
|
||||
private:
|
||||
static const uint8 curve[32];
|
||||
};
|
||||
|
||||
extern Video video;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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() {}
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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;
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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");
|
||||
|
||||
|
|
|
@ -48,6 +48,7 @@ struct ConfigurationSettings : Configuration::Document {
|
|||
|
||||
struct Library : Configuration::Node {
|
||||
signed selection;
|
||||
unsigned mediaMode;
|
||||
bool showOnStartup;
|
||||
} library;
|
||||
|
||||
|
|
|
@ -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() {
|
||||
|
|
Loading…
Reference in New Issue