mirror of https://github.com/bsnes-emu/bsnes.git
Update to v093r06 release.
byuu says: Changelog: - Windows port should compile out-of-the-box - InputManager::scancode[] initialized at startup - Library menu shows item for each bootable media type (notably Game Boy Color) - Display Emulation menu selection fix - LibraryManager load button works now - Added hotkey to show library manager (defaults to L) - Added color emulation to video settings (missing on GBA for now) - SFC loading SGB without GB cartridge no longer segfaults - GB/GBC system.load() after cartridge.load() - GB/GBC BG-over-OAM fix - GB/GBC disallow up+down and left+right
This commit is contained in:
parent
ed4e87f65e
commit
35f1605829
|
@ -6,14 +6,14 @@ flags := $(flags) -O3 -fomit-frame-pointer -I..
|
|||
|
||||
all:
|
||||
$(compiler) $(cppflags) $(flags) -fPIC -o obj/ananke.o -c ananke.cpp
|
||||
ifeq ($(platform),x)
|
||||
$(compiler) $(link) -shared -Wl,-soname,libananke.so.1 -o libananke.so obj/ananke.o
|
||||
else ifeq ($(platform),osx)
|
||||
$(compiler) $(link) -shared -dynamiclib -undefined suppress -flat_namespace -o libananke.dylib obj/ananke.o
|
||||
else ifeq ($(platform),win)
|
||||
ifeq ($(platform),windows)
|
||||
$(compiler) $(phoenixflags) -fPIC -o obj/phoenix.o -c ../phoenix/phoenix.cpp
|
||||
$(compiler) $(link) -shared -o phoenix.dll obj/phoenix.o $(phoenixlink)
|
||||
$(compiler) $(link) -shared -o ananke.dll obj/ananke.o -L. -lphoenix
|
||||
else ifeq ($(platform),macosx)
|
||||
$(compiler) $(link) -shared -dynamiclib -undefined suppress -flat_namespace -o libananke.dylib obj/ananke.o
|
||||
else
|
||||
$(compiler) $(link) -shared -Wl,-soname,libananke.so.1 -o libananke.so obj/ananke.o
|
||||
endif
|
||||
|
||||
resource: force
|
||||
|
@ -24,23 +24,25 @@ clean:
|
|||
-@$(call delete,*.so)
|
||||
|
||||
install: uninstall
|
||||
ifeq ($(platform),x)
|
||||
if [ ! -d ~/.config/ananke ]; then mkdir ~/.config/ananke; fi
|
||||
sudo cp libananke.so $(path)/libananke.so.1
|
||||
sudo ln -s $(path)/libananke.so.1 $(path)/libananke.so
|
||||
else ifeq ($(platform),osx)
|
||||
ifeq ($(platform),windows)
|
||||
else ifeq ($(platform),macosx)
|
||||
if [ ! -d ~/Library/Application\ Support/ananke ]; then mkdir ~/Library/Application\ Support/ananke; fi
|
||||
sudo cp libananke.dylib $(path)/libananke.1.dylib
|
||||
sudo ln -s $(path)/libananke.1.dylib $(path)/libananke.dylib
|
||||
else
|
||||
if [ ! -d ~/.config/ananke ]; then mkdir ~/.config/ananke; fi
|
||||
sudo cp libananke.so $(path)/libananke.so.1
|
||||
sudo ln -s $(path)/libananke.so.1 $(path)/libananke.so
|
||||
endif
|
||||
|
||||
uninstall:
|
||||
ifeq ($(platform),x)
|
||||
if [ -f $(path)/libananke.so ]; then sudo rm $(path)/libananke.so; fi
|
||||
if [ -f $(path)/libananke.so.1 ]; then sudo rm $(path)/libananke.so.1; fi
|
||||
else ifeq ($(platform),osx)
|
||||
ifeq ($(platform),windows)
|
||||
else ifeq ($(platform),macosx)
|
||||
if [ -f $(path)/libananke.dylib ]; then sudo rm $(path)/libananke.dylib; fi
|
||||
if [ -f $(path)/libananke.1.dylib ]; then sudo rm $(path)/libananke.1.dylib; fi
|
||||
else
|
||||
if [ -f $(path)/libananke.so ]; then sudo rm $(path)/libananke.so; fi
|
||||
if [ -f $(path)/libananke.so.1 ]; then sudo rm $(path)/libananke.so.1; fi
|
||||
endif
|
||||
|
||||
force:
|
||||
|
|
|
@ -95,7 +95,7 @@ FileDialog *fileDialog = nullptr;
|
|||
Ananke::Ananke() {
|
||||
libraryPath = string::read({configpath(), "higan/library.bml"}).strip().ltrim<1>("Path: ").replace("\\", "/");
|
||||
if(libraryPath.empty()) libraryPath = {userpath(), "Emulation/"};
|
||||
if(libraryPath.endswith("/") == false) libraryPath.append("/");
|
||||
if(libraryPath.endsWith("/") == false) libraryPath.append("/");
|
||||
}
|
||||
|
||||
bool Ananke::supported(const string &filename) {
|
||||
|
@ -133,7 +133,7 @@ string Ananke::open(string filename) {
|
|||
config.path = information.path; //remember last used directory
|
||||
|
||||
vector<uint8_t> buffer;
|
||||
if(filename.endswith(".zip")) {
|
||||
if(filename.endsWith(".zip")) {
|
||||
information.archive = filename;
|
||||
buffer = extractROM();
|
||||
} else {
|
||||
|
@ -143,23 +143,23 @@ string Ananke::open(string filename) {
|
|||
|
||||
applyBeatPatch(buffer);
|
||||
|
||||
if(information.name.endswith(".fc") || information.name.endswith(".nes")) return openFamicom(buffer);
|
||||
if(information.name.endswith(".sfc") || information.name.endswith(".smc")) return openSuperFamicom(buffer);
|
||||
if(information.name.endswith(".st")) return openSufamiTurbo(buffer);
|
||||
if(information.name.endswith(".bs")) return openBsxSatellaview(buffer);
|
||||
if(information.name.endswith(".gb") || information.name.endswith(".gbc")) return openGameBoy(buffer);
|
||||
if(information.name.endswith(".gba")) return openGameBoyAdvance(buffer);
|
||||
if(information.name.endsWith(".fc") || information.name.endsWith(".nes")) return openFamicom(buffer);
|
||||
if(information.name.endsWith(".sfc") || information.name.endsWith(".smc")) return openSuperFamicom(buffer);
|
||||
if(information.name.endsWith(".st")) return openSufamiTurbo(buffer);
|
||||
if(information.name.endsWith(".bs")) return openBsxSatellaview(buffer);
|
||||
if(information.name.endsWith(".gb") || information.name.endsWith(".gbc")) return openGameBoy(buffer);
|
||||
if(information.name.endsWith(".gba")) return openGameBoyAdvance(buffer);
|
||||
return "";
|
||||
}
|
||||
|
||||
string Ananke::sync(string pathname) {
|
||||
if(pathname.endswith(".fc/")) return syncFamicom(pathname);
|
||||
if(pathname.endswith(".sfc/")) return syncSuperFamicom(pathname);
|
||||
if(pathname.endswith(".st/")) return syncSufamiTurbo(pathname);
|
||||
if(pathname.endswith(".bs/")) return syncBsxSatellaview(pathname);
|
||||
if(pathname.endswith(".gb/")) return syncGameBoy(pathname);
|
||||
if(pathname.endswith(".gbc/")) return syncGameBoy(pathname);
|
||||
if(pathname.endswith(".gba/")) return syncGameBoyAdvance(pathname);
|
||||
if(pathname.endsWith(".fc/")) return syncFamicom(pathname);
|
||||
if(pathname.endsWith(".sfc/")) return syncSuperFamicom(pathname);
|
||||
if(pathname.endsWith(".st/")) return syncSufamiTurbo(pathname);
|
||||
if(pathname.endsWith(".bs/")) return syncBsxSatellaview(pathname);
|
||||
if(pathname.endsWith(".gb/")) return syncGameBoy(pathname);
|
||||
if(pathname.endsWith(".gbc/")) return syncGameBoy(pathname);
|
||||
if(pathname.endsWith(".gba/")) return syncGameBoyAdvance(pathname);
|
||||
return "";
|
||||
}
|
||||
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
vector<uint8_t> Ananke::extractROM() {
|
||||
unzip archive;
|
||||
if(archive.open(information.archive)) {
|
||||
for(auto &file : archive.file) {
|
||||
if(file.name.endswith(".fc") || file.name.endswith(".nes")
|
||||
|| file.name.endswith(".sfc") || file.name.endswith(".smc")
|
||||
|| file.name.endswith(".st") || file.name.endswith(".bs")
|
||||
|| file.name.endswith(".gb") || file.name.endswith(".gbc")
|
||||
|| file.name.endswith(".gba")
|
||||
for(auto& file : archive.file) {
|
||||
if(file.name.endsWith(".fc") || file.name.endsWith(".nes")
|
||||
|| file.name.endsWith(".sfc") || file.name.endsWith(".smc")
|
||||
|| file.name.endsWith(".st") || file.name.endsWith(".bs")
|
||||
|| file.name.endsWith(".gb") || file.name.endsWith(".gbc")
|
||||
|| file.name.endsWith(".gba")
|
||||
) {
|
||||
information.name = notdir(file.name);
|
||||
return archive.extract(file);
|
||||
|
@ -16,10 +16,10 @@ vector<uint8_t> Ananke::extractROM() {
|
|||
return vector<uint8_t>();
|
||||
}
|
||||
|
||||
vector<uint8_t> Ananke::extractFile(const string &filename) {
|
||||
vector<uint8_t> Ananke::extractFile(const string& filename) {
|
||||
unzip archive;
|
||||
if(archive.open(information.archive)) {
|
||||
for(auto &file : archive.file) {
|
||||
for(auto& file : archive.file) {
|
||||
if(notdir(file.name) == filename) {
|
||||
return archive.extract(file);
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ struct FileDialog : Window {
|
|||
void setPath(const string &path) {
|
||||
pathname = string{path}.transform("\\", "/");
|
||||
if(pathname.empty()) pathname = userpath();
|
||||
if(pathname.endswith("/") == false) pathname.append("/");
|
||||
if(pathname.endsWith("/") == false) pathname.append("/");
|
||||
pathEdit.setText(pathname);
|
||||
|
||||
fileList.reset();
|
||||
|
@ -88,7 +88,7 @@ struct FileDialog : Window {
|
|||
string name = filenameList(fileList.selection());
|
||||
if(name.empty()) return;
|
||||
|
||||
if(name.endswith("/")) return setPath(name);
|
||||
if(name.endsWith("/")) return setPath(name);
|
||||
filename = name;
|
||||
onClose();
|
||||
};
|
||||
|
|
|
@ -38,23 +38,23 @@ GameBoyAdvanceCartridge::GameBoyAdvanceCartridge(const uint8_t *data, unsigned s
|
|||
char text[16];
|
||||
memcpy(text, data + n, id.size + 3);
|
||||
text[id.size + 3] = 0;
|
||||
list.appendonce(text);
|
||||
list.appendOnce(text);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
identifiers = list.concatenate(",");
|
||||
identifiers = list.merge(",");
|
||||
|
||||
markup = "";
|
||||
markup.append("cartridge\n");
|
||||
markup.append(" rom name=program.rom size=0x", hex(size), "\n");
|
||||
if(0);
|
||||
else if(identifiers.beginswith("SRAM_V" )) markup.append(" ram name=save.ram type=SRAM size=0x8000\n");
|
||||
else if(identifiers.beginswith("SRAM_F_V" )) markup.append(" ram name=save.ram type=FRAM size=0x8000\n");
|
||||
else if(identifiers.beginswith("EEPROM_V" )) markup.append(" ram name=save.ram type=EEPROM size=0x0\n");
|
||||
else if(identifiers.beginswith("FLASH_V" )) markup.append(" ram name=save.ram type=FlashROM size=0x10000\n");
|
||||
else if(identifiers.beginswith("FLASH512_V")) markup.append(" ram name=save.ram type=FlashROM size=0x10000\n");
|
||||
else if(identifiers.beginswith("FLASH1M_V" )) markup.append(" ram name=save.ram type=FlashROM size=0x20000\n");
|
||||
else if(identifiers.beginsWith("SRAM_V" )) markup.append(" ram name=save.ram type=SRAM size=0x8000\n");
|
||||
else if(identifiers.beginsWith("SRAM_F_V" )) markup.append(" ram name=save.ram type=FRAM size=0x8000\n");
|
||||
else if(identifiers.beginsWith("EEPROM_V" )) markup.append(" ram name=save.ram type=EEPROM size=0x0\n");
|
||||
else if(identifiers.beginsWith("FLASH_V" )) markup.append(" ram name=save.ram type=FlashROM size=0x10000\n");
|
||||
else if(identifiers.beginsWith("FLASH512_V")) markup.append(" ram name=save.ram type=FlashROM size=0x10000\n");
|
||||
else if(identifiers.beginsWith("FLASH1M_V" )) markup.append(" ram name=save.ram type=FlashROM size=0x20000\n");
|
||||
//if(identifiers.empty() == false) markup.append(" #detected: ", identifiers, "\n");
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
namespace Emulator {
|
||||
static const char Name[] = "higan";
|
||||
static const char Version[] = "093.05";
|
||||
static const char Version[] = "093.06";
|
||||
static const char Author[] = "byuu";
|
||||
static const char License[] = "GPLv3";
|
||||
static const char Website[] = "http://byuu.org/";
|
||||
|
|
|
@ -107,7 +107,7 @@ struct Interface {
|
|||
virtual void cheatSet(const lstring& = lstring{}) {}
|
||||
|
||||
//utility functions
|
||||
virtual void paletteUpdate() {}
|
||||
virtual void paletteUpdate(bool colorEmulation) {}
|
||||
|
||||
//debugger functions
|
||||
virtual bool tracerEnable(bool) { return false; }
|
||||
|
|
|
@ -115,8 +115,8 @@ void Interface::cheatSet(const lstring& list) {
|
|||
cheat.synchronize();
|
||||
}
|
||||
|
||||
void Interface::paletteUpdate() {
|
||||
video.generate_palette();
|
||||
void Interface::paletteUpdate(bool colorEmulation) {
|
||||
video.generate_palette(colorEmulation);
|
||||
}
|
||||
|
||||
Interface::Interface() {
|
||||
|
|
|
@ -45,7 +45,7 @@ struct Interface : Emulator::Interface {
|
|||
|
||||
void cheatSet(const lstring&);
|
||||
|
||||
void paletteUpdate();
|
||||
void paletteUpdate(bool colorEmulation);
|
||||
|
||||
Interface();
|
||||
|
||||
|
|
|
@ -6,12 +6,12 @@ namespace Famicom {
|
|||
|
||||
Video video;
|
||||
|
||||
void Video::generate_palette() {
|
||||
for(unsigned n = 0; n < (1 << 9); n++) palette[n] = generate_color(n, 2.0, 0.0, 1.0, 1.0, 1.8);
|
||||
void Video::generate_palette(bool color_emulation) {
|
||||
for(unsigned n = 0; n < (1 << 9); n++) palette[n] = generate_color(n, 2.0, 0.0, 1.0, 1.0, color_emulation ? 1.8 : 2.2);
|
||||
}
|
||||
|
||||
Video::Video() {
|
||||
palette = new unsigned[1 << 9];
|
||||
palette = new unsigned[1 << 9]();
|
||||
}
|
||||
|
||||
Video::~Video() {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
struct Video {
|
||||
unsigned* palette = nullptr;
|
||||
void generate_palette();
|
||||
void generate_palette(bool color_emulation);
|
||||
|
||||
Video();
|
||||
~Video();
|
||||
|
|
|
@ -18,8 +18,22 @@ string Cartridge::title() {
|
|||
return information.title;
|
||||
}
|
||||
|
||||
void Cartridge::load(System::Revision revision) {
|
||||
//intended for use with Super Game Boy for when no Game Boy cartridge is inserted
|
||||
void Cartridge::load_empty(System::Revision revision) {
|
||||
unload();
|
||||
romsize = 32768;
|
||||
romdata = allocate<uint8>(romsize, 0xff);
|
||||
ramsize = 0;
|
||||
mapper = &mbc0;
|
||||
sha256 = nall::sha256(romdata, romsize);
|
||||
loaded = true;
|
||||
system.load(revision);
|
||||
}
|
||||
|
||||
void Cartridge::load(System::Revision revision) {
|
||||
unload();
|
||||
|
||||
system.revision = revision; //needed for ID::Manifest to return correct group ID
|
||||
if(revision != System::Revision::SuperGameBoy) {
|
||||
interface->loadRequest(ID::Manifest, "manifest.bml");
|
||||
}
|
||||
|
@ -80,15 +94,14 @@ void Cartridge::load(System::Revision revision) {
|
|||
case Mapper::HuC3: mapper = &huc3; break;
|
||||
}
|
||||
|
||||
loaded = true;
|
||||
sha256 = nall::sha256(romdata, romsize);
|
||||
loaded = true;
|
||||
system.load(revision);
|
||||
}
|
||||
|
||||
void Cartridge::unload() {
|
||||
if(loaded == false) return;
|
||||
|
||||
if(romdata) { delete[] romdata; romdata = nullptr; }
|
||||
if(ramdata) { delete[] ramdata; ramdata = nullptr; }
|
||||
if(romdata) { delete[] romdata; romdata = nullptr; romsize = 0; }
|
||||
if(ramdata) { delete[] ramdata; ramdata = nullptr; ramsize = 0; }
|
||||
loaded = false;
|
||||
}
|
||||
|
||||
|
@ -119,7 +132,7 @@ uint8 Cartridge::mmio_read(uint16 addr) {
|
|||
|
||||
if(bootrom_enable) {
|
||||
const uint8* data = nullptr;
|
||||
switch(system.revision()) { default:
|
||||
switch(system.revision) { default:
|
||||
case System::Revision::GameBoy: data = system.bootROM.dmg; break;
|
||||
case System::Revision::SuperGameBoy: data = system.bootROM.sgb; break;
|
||||
case System::Revision::GameBoyColor: data = system.bootROM.cgb; break;
|
||||
|
@ -159,8 +172,7 @@ void Cartridge::power() {
|
|||
|
||||
Cartridge::Cartridge() {
|
||||
loaded = false;
|
||||
romdata = nullptr;
|
||||
ramdata = nullptr;
|
||||
sha256 = "";
|
||||
}
|
||||
|
||||
Cartridge::~Cartridge() {
|
||||
|
|
|
@ -45,15 +45,16 @@ struct Cartridge : MMIO, property<Cartridge> {
|
|||
readonly<bool> loaded;
|
||||
readonly<string> sha256;
|
||||
|
||||
uint8_t* romdata;
|
||||
unsigned romsize;
|
||||
uint8_t* romdata = nullptr;
|
||||
unsigned romsize = 0;
|
||||
|
||||
uint8_t* ramdata;
|
||||
unsigned ramsize;
|
||||
uint8_t* ramdata = nullptr;
|
||||
unsigned ramsize = 0;
|
||||
|
||||
MMIO* mapper;
|
||||
bool bootrom_enable;
|
||||
MMIO* mapper = nullptr;
|
||||
bool bootrom_enable = true;
|
||||
|
||||
void load_empty(System::Revision revision);
|
||||
void load(System::Revision revision);
|
||||
void unload();
|
||||
|
||||
|
|
|
@ -20,6 +20,10 @@ void CPU::mmio_joyp_poll() {
|
|||
dpad |= interface->inputPoll(0, 0, (unsigned)Input::Left) << 1;
|
||||
dpad |= interface->inputPoll(0, 0, (unsigned)Input::Right) << 0;
|
||||
|
||||
//D-pad pivot makes it impossible to press opposing directions at the same time
|
||||
if(dpad & 4) dpad &= ~8; //disallow up+down
|
||||
if(dpad & 2) dpad &= ~1; //disallow left+right
|
||||
|
||||
status.joyp = 0x0f;
|
||||
if(status.p15 == 1 && status.p14 == 1) status.joyp -= status.mlt_req;
|
||||
if(status.p15 == 0) status.joyp &= button ^ 0x0f;
|
||||
|
@ -32,6 +36,7 @@ uint8 CPU::mmio_read(uint16 addr) {
|
|||
if(addr >= 0xff80 && addr <= 0xfffe) return hram[addr & 0x7f];
|
||||
|
||||
if(addr == 0xff00) { //JOYP
|
||||
mmio_joyp_poll();
|
||||
return (status.p15 << 5)
|
||||
| (status.p14 << 4)
|
||||
| (status.joyp << 0);
|
||||
|
@ -134,7 +139,6 @@ void CPU::mmio_write(uint16 addr, uint8 data) {
|
|||
status.p15 = data & 0x20;
|
||||
status.p14 = data & 0x10;
|
||||
interface->joypWrite(status.p15, status.p14);
|
||||
mmio_joyp_poll();
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -41,12 +41,13 @@ unsigned Interface::group(unsigned id) {
|
|||
case ID::Manifest:
|
||||
case ID::ROM:
|
||||
case ID::RAM:
|
||||
if(system.revision() == System::Revision::GameBoy) return ID::GameBoy;
|
||||
if(system.revision() == System::Revision::SuperGameBoy) return ID::SuperGameBoy;
|
||||
if(system.revision() == System::Revision::GameBoyColor) return ID::GameBoyColor;
|
||||
switch(system.revision) {
|
||||
case System::Revision::GameBoy: return ID::GameBoy;
|
||||
case System::Revision::SuperGameBoy: return ID::SuperGameBoy;
|
||||
case System::Revision::GameBoyColor: return ID::GameBoyColor;
|
||||
}
|
||||
throw;
|
||||
}
|
||||
|
||||
throw;
|
||||
}
|
||||
|
||||
|
@ -130,8 +131,8 @@ void Interface::cheatSet(const lstring& list) {
|
|||
cheat.synchronize();
|
||||
}
|
||||
|
||||
void Interface::paletteUpdate() {
|
||||
video.generate_palette();
|
||||
void Interface::paletteUpdate(bool colorEmulation) {
|
||||
video.generate_palette(colorEmulation);
|
||||
}
|
||||
|
||||
Interface::Interface() {
|
||||
|
|
|
@ -58,7 +58,7 @@ struct Interface : Emulator::Interface {
|
|||
|
||||
void cheatSet(const lstring&);
|
||||
|
||||
void paletteUpdate();
|
||||
void paletteUpdate(bool colorEmulation);
|
||||
|
||||
Interface();
|
||||
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
#ifdef PPU_CPP
|
||||
|
||||
void PPU::cgb_render() {
|
||||
for(unsigned n = 0; n < 160; n++) {
|
||||
line[n] = 0x7fff;
|
||||
origin[n] = Origin::None;
|
||||
for(auto& pixel : pixels) {
|
||||
pixel.color = 0x7fff;
|
||||
pixel.palette = 0;
|
||||
pixel.origin = Pixel::Origin::None;
|
||||
}
|
||||
|
||||
if(status.display_enable) {
|
||||
|
@ -13,7 +14,7 @@ void PPU::cgb_render() {
|
|||
}
|
||||
|
||||
uint32* output = screen + status.ly * 160;
|
||||
for(unsigned n = 0; n < 160; n++) output[n] = video.palette[line[n]];
|
||||
for(unsigned n = 0; n < 160; n++) output[n] = video.palette[pixels[n].color];
|
||||
interface->lcdScanline();
|
||||
}
|
||||
|
||||
|
@ -56,14 +57,15 @@ void PPU::cgb_render_bg() {
|
|||
for(unsigned ox = 0; ox < 160; ox++) {
|
||||
unsigned index = ((data & (0x0080 >> tx)) ? 1 : 0)
|
||||
| ((data & (0x8000 >> tx)) ? 2 : 0);
|
||||
unsigned palette_index = ((attr & 0x07) << 3) + (index << 1);
|
||||
unsigned palette = 0;
|
||||
palette |= bgpd[palette_index++] << 0;
|
||||
palette |= bgpd[palette_index++] << 8;
|
||||
palette &= 0x7fff;
|
||||
unsigned palette = ((attr & 0x07) << 2) + index;
|
||||
unsigned color = 0;
|
||||
color |= bgpd[(palette << 1) + 0] << 0;
|
||||
color |= bgpd[(palette << 1) + 1] << 8;
|
||||
color &= 0x7fff;
|
||||
|
||||
line[ox] = palette;
|
||||
origin[ox] = (attr & 0x80 ? Origin::BGP : Origin::BG);
|
||||
pixels[ox].color = color;
|
||||
pixels[ox].palette = index;
|
||||
pixels[ox].origin = (attr & 0x80 ? Pixel::Origin::BGP : Pixel::Origin::BG);
|
||||
|
||||
ix = (ix + 1) & 255;
|
||||
tx = (tx + 1) & 7;
|
||||
|
@ -83,15 +85,16 @@ void PPU::cgb_render_window() {
|
|||
for(unsigned ox = 0; ox < 160; ox++) {
|
||||
unsigned index = ((data & (0x0080 >> tx)) ? 1 : 0)
|
||||
| ((data & (0x8000 >> tx)) ? 2 : 0);
|
||||
unsigned palette_index = ((attr & 0x07) << 3) + (index << 1);
|
||||
unsigned palette = 0;
|
||||
palette |= bgpd[palette_index++] << 0;
|
||||
palette |= bgpd[palette_index++] << 8;
|
||||
palette &= 0x7fff;
|
||||
unsigned palette = ((attr & 0x07) << 2) + index;
|
||||
unsigned color = 0;
|
||||
color |= bgpd[(palette << 1) + 0] << 0;
|
||||
color |= bgpd[(palette << 1) + 1] << 8;
|
||||
color &= 0x7fff;
|
||||
|
||||
if(ox - (status.wx - 7) < 160u) {
|
||||
line[ox] = palette;
|
||||
origin[ox] = (attr & 0x80 ? Origin::BGP : Origin::BG);
|
||||
pixels[ox].color = color;
|
||||
pixels[ox].palette = index;
|
||||
pixels[ox].origin = (attr & 0x80 ? Pixel::Origin::BGP : Pixel::Origin::BG);
|
||||
}
|
||||
|
||||
ix = (ix + 1) & 255;
|
||||
|
@ -157,26 +160,26 @@ void PPU::cgb_render_ob() {
|
|||
| ((data & (0x8000 >> tx)) ? 2 : 0);
|
||||
if(index == 0) continue;
|
||||
|
||||
unsigned palette_index = ((attr & 0x07) << 3) + (index << 1);
|
||||
unsigned palette = 0;
|
||||
palette |= obpd[palette_index++] << 0;
|
||||
palette |= obpd[palette_index++] << 8;
|
||||
palette &= 0x7fff;
|
||||
unsigned palette = ((attr & 0x07) << 2) + index;
|
||||
unsigned color = 0;
|
||||
color |= obpd[(palette << 1) + 0] << 0;
|
||||
color |= obpd[(palette << 1) + 1] << 8;
|
||||
color &= 0x7fff;
|
||||
|
||||
unsigned ox = sx + tx;
|
||||
|
||||
if(ox < 160) {
|
||||
//When LCDC.D0 (BG enable) is off, OB is always rendered above BG+Window
|
||||
if(status.bg_enable) {
|
||||
if(origin[ox] == Origin::BGP) continue;
|
||||
if(pixels[ox].origin == Pixel::Origin::BGP) continue;
|
||||
if(attr & 0x80) {
|
||||
if(origin[ox] == Origin::BG || origin[ox] == Origin::BGP) {
|
||||
if(line[ox] > 0) continue;
|
||||
if(pixels[ox].origin == Pixel::Origin::BG) {
|
||||
if(pixels[ox].palette > 0) continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
line[ox] = palette;
|
||||
origin[ox] = Origin::OB;
|
||||
pixels[ox].color = color;
|
||||
pixels[ox].palette = index;
|
||||
pixels[ox].origin = Pixel::Origin::OB;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
#ifdef PPU_CPP
|
||||
|
||||
void PPU::dmg_render() {
|
||||
for(unsigned n = 0; n < 160; n++) {
|
||||
line[n] = 0x00;
|
||||
origin[n] = Origin::None;
|
||||
for(auto& pixel : pixels) {
|
||||
pixel.color = 0;
|
||||
pixel.palette = 0;
|
||||
pixel.origin = Pixel::Origin::None;
|
||||
}
|
||||
|
||||
if(status.display_enable) {
|
||||
|
@ -13,7 +14,7 @@ void PPU::dmg_render() {
|
|||
}
|
||||
|
||||
uint32* output = screen + status.ly * 160;
|
||||
for(unsigned n = 0; n < 160; n++) output[n] = video.palette[line[n]];
|
||||
for(unsigned n = 0; n < 160; n++) output[n] = video.palette[pixels[n].color];
|
||||
interface->lcdScanline();
|
||||
}
|
||||
|
||||
|
@ -38,8 +39,9 @@ void PPU::dmg_render_bg() {
|
|||
uint8 palette = ((data & (0x0080 >> tx)) ? 1 : 0)
|
||||
| ((data & (0x8000 >> tx)) ? 2 : 0);
|
||||
|
||||
line[ox] = bgp[palette];
|
||||
origin[ox] = Origin::BG;
|
||||
pixels[ox].color = bgp[palette];
|
||||
pixels[ox].palette = palette;
|
||||
pixels[ox].origin = Pixel::Origin::BG;
|
||||
|
||||
ix = (ix + 1) & 255;
|
||||
tx = (tx + 1) & 7;
|
||||
|
@ -58,9 +60,11 @@ void PPU::dmg_render_window() {
|
|||
for(unsigned ox = 0; ox < 160; ox++) {
|
||||
uint8 palette = ((data & (0x0080 >> tx)) ? 1 : 0)
|
||||
| ((data & (0x8000 >> tx)) ? 2 : 0);
|
||||
|
||||
if(ox - (status.wx - 7) < 160u) {
|
||||
line[ox] = bgp[palette];
|
||||
origin[ox] = Origin::BG;
|
||||
pixels[ox].color = bgp[palette];
|
||||
pixels[ox].palette = palette;
|
||||
pixels[ox].origin = Pixel::Origin::BG;
|
||||
}
|
||||
|
||||
ix = (ix + 1) & 255;
|
||||
|
@ -126,17 +130,16 @@ void PPU::dmg_render_ob() {
|
|||
| ((data & (0x8000 >> tx)) ? 2 : 0);
|
||||
if(palette == 0) continue;
|
||||
|
||||
palette = obp[(bool)(attr & 0x10)][palette];
|
||||
unsigned ox = sx + tx;
|
||||
|
||||
if(ox < 160) {
|
||||
if(attr & 0x80) {
|
||||
if(origin[ox] == Origin::BG) {
|
||||
if(line[ox] > 0) continue;
|
||||
if(pixels[ox].origin == Pixel::Origin::BG) {
|
||||
if(pixels[ox].palette > 0) continue;
|
||||
}
|
||||
}
|
||||
line[ox] = palette;
|
||||
origin[ox] = Origin::OB;
|
||||
pixels[ox].color = obp[(bool)(attr & 0x10)][palette];
|
||||
pixels[ox].palette = palette;
|
||||
pixels[ox].origin = Pixel::Origin::OB;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -107,8 +107,11 @@ void PPU::power() {
|
|||
}
|
||||
|
||||
for(auto& n : screen) n = 0x0000;
|
||||
for(auto& n : line) n = 0x0000;
|
||||
for(auto& n : origin) n = Origin::None;
|
||||
for(auto& n : pixels) {
|
||||
n.color = 0;
|
||||
n.palette = 0;
|
||||
n.origin = Pixel::Origin::None;
|
||||
}
|
||||
|
||||
for(auto& n : vram) n = 0x00;
|
||||
for(auto& n : oam) n = 0x00;
|
||||
|
|
|
@ -50,9 +50,12 @@ struct PPU : Thread, MMIO {
|
|||
} status;
|
||||
|
||||
uint32 screen[160 * 144];
|
||||
uint16 line[160];
|
||||
struct Origin { enum : unsigned { None, BG, BGP, OB }; };
|
||||
uint8 origin[160];
|
||||
struct Pixel {
|
||||
enum class Origin : unsigned { None, BG, BGP, OB };
|
||||
uint16 color;
|
||||
uint8 palette;
|
||||
Origin origin;
|
||||
} pixels[160];
|
||||
|
||||
uint8 vram[16384]; //GB = 8192, GBC = 16384
|
||||
uint8 oam[160];
|
||||
|
|
|
@ -4,8 +4,11 @@ void PPU::serialize(serializer& s) {
|
|||
Thread::serialize(s);
|
||||
|
||||
s.array(screen);
|
||||
s.array(line);
|
||||
s.array(origin);
|
||||
for(auto& pixel : pixels) {
|
||||
s.integer(pixel.color);
|
||||
s.integer(pixel.palette);
|
||||
s.integer((unsigned&)pixel.origin);
|
||||
}
|
||||
|
||||
s.array(vram);
|
||||
s.array(oam);
|
||||
|
|
|
@ -4,13 +4,13 @@ enum class Input : unsigned {
|
|||
Up, Down, Left, Right, B, A, Select, Start,
|
||||
};
|
||||
|
||||
struct System : property<System> {
|
||||
struct System {
|
||||
enum class Revision : unsigned {
|
||||
GameBoy,
|
||||
SuperGameBoy,
|
||||
GameBoyColor,
|
||||
};
|
||||
readonly<Revision> revision;
|
||||
} revision;
|
||||
|
||||
inline bool dmg() const { return revision == Revision::GameBoy; }
|
||||
inline bool sgb() const { return revision == Revision::SuperGameBoy; }
|
||||
inline bool cgb() const { return revision == Revision::GameBoyColor; }
|
||||
|
|
|
@ -5,7 +5,8 @@ namespace GameBoy {
|
|||
|
||||
Video video;
|
||||
|
||||
void Video::generate_palette() {
|
||||
void Video::generate_palette(bool color_emulation) {
|
||||
this->color_emulation = color_emulation;
|
||||
if(system.dmg()) for(unsigned n = 0; n < 4; n++) palette[n] = palette_dmg(n);
|
||||
if(system.sgb()) for(unsigned n = 0; n < 4; n++) palette[n] = palette_sgb(n);
|
||||
if(system.cgb()) for(unsigned n = 0; n < (1 << 15); n++) palette[n] = palette_cgb(n);
|
||||
|
@ -20,6 +21,11 @@ Video::~Video() {
|
|||
}
|
||||
|
||||
unsigned Video::palette_dmg(unsigned color) const {
|
||||
if(color_emulation == false) {
|
||||
unsigned L = (3 - color) * 21845;
|
||||
return interface->videoColor(color, L, L, L);
|
||||
}
|
||||
|
||||
unsigned R = monochrome[color][0] * 65535.0;
|
||||
unsigned G = monochrome[color][1] * 65535.0;
|
||||
unsigned B = monochrome[color][2] * 65535.0;
|
||||
|
@ -40,6 +46,13 @@ unsigned Video::palette_cgb(unsigned color) const {
|
|||
unsigned g = (color >> 5) & 31;
|
||||
unsigned b = (color >> 10) & 31;
|
||||
|
||||
if(color_emulation == false) {
|
||||
unsigned R = (r << 11) | (r << 6) | (r << 1) | (r >> 4);
|
||||
unsigned G = (g << 11) | (g << 6) | (g << 1) | (g >> 4);
|
||||
unsigned B = (b << 11) | (b << 6) | (b << 1) | (b >> 4);
|
||||
return interface->videoColor(color, R, G, B);
|
||||
}
|
||||
|
||||
unsigned R = (r * 26 + g * 4 + b * 2);
|
||||
unsigned G = ( g * 24 + b * 8);
|
||||
unsigned B = (r * 6 + g * 4 + b * 22);
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
struct Video {
|
||||
uint32_t* palette;
|
||||
void generate_palette();
|
||||
void generate_palette(bool color_emulation);
|
||||
|
||||
Video();
|
||||
~Video();
|
||||
|
||||
private:
|
||||
bool color_emulation;
|
||||
static const double monochrome[4][3];
|
||||
uint32_t palette_dmg(unsigned color) const;
|
||||
uint32_t palette_sgb(unsigned color) const;
|
||||
|
|
|
@ -109,8 +109,8 @@ bool Interface::unserialize(serializer& s) {
|
|||
return system.unserialize(s);
|
||||
}
|
||||
|
||||
void Interface::paletteUpdate() {
|
||||
video.generate_palette();
|
||||
void Interface::paletteUpdate(bool colorEmulation) {
|
||||
video.generate_palette(colorEmulation);
|
||||
}
|
||||
|
||||
Interface::Interface() {
|
||||
|
|
|
@ -43,7 +43,7 @@ struct Interface : Emulator::Interface {
|
|||
serializer serialize();
|
||||
bool unserialize(serializer&);
|
||||
|
||||
void paletteUpdate();
|
||||
void paletteUpdate(bool colorEmulation);
|
||||
|
||||
Interface();
|
||||
|
||||
|
|
|
@ -4,7 +4,8 @@ namespace GameBoyAdvance {
|
|||
|
||||
Video video;
|
||||
|
||||
void Video::generate_palette() {
|
||||
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;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
struct Video {
|
||||
unsigned* palette;
|
||||
void generate_palette();
|
||||
void generate_palette(bool color_emulation);
|
||||
|
||||
Video();
|
||||
~Video();
|
||||
|
|
|
@ -85,7 +85,7 @@ private:
|
|||
inline bool directory::remove(const string& pathname) {
|
||||
lstring list = directory::contents(pathname);
|
||||
for(auto& name : list) {
|
||||
if(name.endswith("/")) directory::remove({pathname, name});
|
||||
if(name.endsWith("/")) directory::remove({pathname, name});
|
||||
else file::remove({pathname, name});
|
||||
}
|
||||
return _wrmdir(utf16_t(pathname)) == 0;
|
||||
|
|
|
@ -86,7 +86,7 @@ inline void library::close() {
|
|||
#elif defined(PLATFORM_WINDOWS)
|
||||
inline bool library::open(const string& name, const string& path) {
|
||||
if(handle) close();
|
||||
string filepath(path, !path.empty() && !path.endswith("/") && !path.endsWith("\\") ? "/" : "", name, ".dll");
|
||||
string filepath(path, !path.empty() && !path.endsWith("/") && !path.endsWith("\\") ? "/" : "", name, ".dll");
|
||||
handle = (uintptr_t)LoadLibraryW(utf16_t(filepath));
|
||||
return handle;
|
||||
}
|
||||
|
|
|
@ -17,9 +17,9 @@ struct image {
|
|||
unsigned pitch = 0;
|
||||
unsigned size = 0;
|
||||
|
||||
bool endian = 0; //0 = lsb, 1 = msb
|
||||
bool endian = 0; //0 = lsb, 1 = msb
|
||||
unsigned depth = 32;
|
||||
unsigned stride = 4;
|
||||
unsigned stride = 4;
|
||||
|
||||
struct Channel {
|
||||
uint64_t mask;
|
||||
|
@ -511,7 +511,7 @@ void image::scaleLinearWidth(unsigned outputWidth) {
|
|||
sp += stride;
|
||||
|
||||
unsigned x = 0;
|
||||
while(x < outputWidth) {
|
||||
while(true) {
|
||||
while(xfraction < 0x100000000 && x++ < outputWidth) {
|
||||
uint64_t A = interpolate1D((a & alpha.mask) >> alpha.shift, (b & alpha.mask) >> alpha.shift, xfraction);
|
||||
uint64_t R = interpolate1D((a & red.mask ) >> red.shift , (b & red.mask ) >> red.shift, xfraction);
|
||||
|
@ -522,6 +522,7 @@ void image::scaleLinearWidth(unsigned outputWidth) {
|
|||
dp += stride;
|
||||
xfraction += xstride;
|
||||
}
|
||||
if(x >= outputWidth) break;
|
||||
|
||||
sp += stride;
|
||||
a = b;
|
||||
|
@ -553,7 +554,7 @@ void image::scaleLinearHeight(unsigned outputHeight) {
|
|||
sp += pitch;
|
||||
|
||||
unsigned y = 0;
|
||||
while(y < outputHeight) {
|
||||
while(true) {
|
||||
while(yfraction < 0x100000000 && y++ < outputHeight) {
|
||||
uint64_t A = interpolate1D((a & alpha.mask) >> alpha.shift, (b & alpha.mask) >> alpha.shift, yfraction);
|
||||
uint64_t R = interpolate1D((a & red.mask ) >> red.shift, (b & red.mask ) >> red.shift, yfraction);
|
||||
|
@ -564,6 +565,7 @@ void image::scaleLinearHeight(unsigned outputHeight) {
|
|||
dp += pitch;
|
||||
yfraction += ystride;
|
||||
}
|
||||
if(y >= outputHeight) break;
|
||||
|
||||
sp += pitch;
|
||||
a = b;
|
||||
|
@ -600,7 +602,7 @@ void image::scaleLinear(unsigned outputWidth, unsigned outputHeight) {
|
|||
sp += stride;
|
||||
|
||||
unsigned x = 0;
|
||||
while(x < outputWidth) {
|
||||
while(true) {
|
||||
while(xfraction < 0x100000000 && x++ < outputWidth) {
|
||||
uint64_t A = interpolate2D((a & alpha.mask) >> alpha.shift, (b & alpha.mask) >> alpha.shift, (c & alpha.mask) >> alpha.shift, (d & alpha.mask) >> alpha.shift, xfraction, yfraction);
|
||||
uint64_t R = interpolate2D((a & red.mask ) >> red.shift, (b & red.mask ) >> red.shift, (c & red.mask ) >> red.shift, (d & red.mask ) >> red.shift, xfraction, yfraction);
|
||||
|
@ -611,6 +613,7 @@ void image::scaleLinear(unsigned outputWidth, unsigned outputHeight) {
|
|||
dp += stride;
|
||||
xfraction += xstride;
|
||||
}
|
||||
if(x >= outputWidth) break;
|
||||
|
||||
sp += stride;
|
||||
a = b;
|
||||
|
@ -647,12 +650,13 @@ void image::scaleNearest(unsigned outputWidth, unsigned outputHeight) {
|
|||
uint64_t a = read(sp);
|
||||
|
||||
unsigned x = 0;
|
||||
while(x < outputWidth) {
|
||||
while(true) {
|
||||
while(xfraction < 0x100000000 && x++ < outputWidth) {
|
||||
write(dp, a);
|
||||
dp += stride;
|
||||
xfraction += xstride;
|
||||
}
|
||||
if(x >= outputWidth) break;
|
||||
|
||||
sp += stride;
|
||||
a = read(sp);
|
||||
|
|
|
@ -65,11 +65,15 @@ void pTabFrame::setGeometry(Geometry geometry) {
|
|||
}
|
||||
|
||||
void pTabFrame::setImage(unsigned selection, const image& image) {
|
||||
nall::image copy = image;
|
||||
unsigned size = pFont::size(widget.state.font, " ").height;
|
||||
copy.scale(size, size);
|
||||
GdkPixbuf* pixbuf = CreatePixbuf(copy);
|
||||
gtk_image_set_from_pixbuf(GTK_IMAGE(tabs[selection].image), pixbuf);
|
||||
if(image.empty() == false) {
|
||||
nall::image copy = image;
|
||||
unsigned size = pFont::size(widget.state.font, " ").height;
|
||||
copy.scale(size, size);
|
||||
GdkPixbuf* pixbuf = CreatePixbuf(copy);
|
||||
gtk_image_set_from_pixbuf(GTK_IMAGE(tabs[selection].image), pixbuf);
|
||||
} else {
|
||||
gtk_image_clear(GTK_IMAGE(tabs[selection].image));
|
||||
}
|
||||
}
|
||||
|
||||
void pTabFrame::setSelection(unsigned selection) {
|
||||
|
|
|
@ -178,7 +178,7 @@ void pWindow::setMenuVisible(bool visible) {
|
|||
|
||||
void pWindow::setModal(bool modality) {
|
||||
if(modality == true) {
|
||||
modal.appendonce(this);
|
||||
modal.appendOnce(this);
|
||||
updateModality();
|
||||
while(window.state.modal) {
|
||||
Application::processEvents();
|
||||
|
|
|
@ -81,6 +81,7 @@ void Cartridge::parse_markup_icd2(Markup::Node root) {
|
|||
has_gb_slot = true;
|
||||
icd2.revision = max(1, numeral(root["revision"].data));
|
||||
|
||||
GameBoy::cartridge.load_empty(GameBoy::System::Revision::SuperGameBoy);
|
||||
interface->loadRequest(ID::SuperGameBoy, "Game Boy", "gb");
|
||||
|
||||
string bootROMName = root["rom"]["name"].data;
|
||||
|
|
|
@ -73,7 +73,7 @@ void ICD2::reset() {
|
|||
joyp14lock = 0;
|
||||
pulselock = true;
|
||||
|
||||
GameBoy::video.generate_palette();
|
||||
GameBoy::video.generate_palette(false);
|
||||
GameBoy::system.init();
|
||||
GameBoy::system.power();
|
||||
}
|
||||
|
|
|
@ -338,8 +338,8 @@ void Interface::cheatSet(const lstring& list) {
|
|||
cheat.synchronize();
|
||||
}
|
||||
|
||||
void Interface::paletteUpdate() {
|
||||
video.generate_palette();
|
||||
void Interface::paletteUpdate(bool colorEmulation) {
|
||||
video.generate_palette(colorEmulation);
|
||||
}
|
||||
|
||||
bool Interface::tracerEnable(bool trace) {
|
||||
|
|
|
@ -115,7 +115,7 @@ struct Interface : Emulator::Interface {
|
|||
|
||||
void cheatSet(const lstring&);
|
||||
|
||||
void paletteUpdate();
|
||||
void paletteUpdate(bool colorEmulation);
|
||||
|
||||
//debugger functions
|
||||
bool tracerEnable(bool);
|
||||
|
|
|
@ -2,18 +2,28 @@
|
|||
|
||||
Video video;
|
||||
|
||||
void Video::generate_palette() {
|
||||
void Video::generate_palette(bool color_emulation) {
|
||||
for(unsigned color = 0; color < (1 << 19); color++) {
|
||||
unsigned l = (color >> 15) & 15;
|
||||
unsigned b = (color >> 10) & 31;
|
||||
unsigned g = (color >> 5) & 31;
|
||||
unsigned r = (color >> 0) & 31;
|
||||
|
||||
if(color_emulation == true) {
|
||||
r = gamma_ramp[r];
|
||||
g = gamma_ramp[g];
|
||||
b = gamma_ramp[b];
|
||||
} else {
|
||||
r = (r << 3) | (r >> 2);
|
||||
g = (g << 3) | (g >> 2);
|
||||
b = (b << 3) | (b >> 2);
|
||||
}
|
||||
|
||||
double L = (1.0 + l) / 16.0;
|
||||
if(l == 0) L *= 0.5;
|
||||
unsigned R = L * (r << 11 | r << 6 | r << 1 | r >> 4);
|
||||
unsigned G = L * (g << 11 | g << 6 | g << 1 | g >> 4);
|
||||
unsigned B = L * (b << 11 | b << 6 | b << 1 | b >> 4);
|
||||
unsigned R = L * (r << 8 | r << 0);
|
||||
unsigned G = L * (g << 8 | g << 0);
|
||||
unsigned B = L * (b << 8 | b << 0);
|
||||
|
||||
palette[color] = interface->videoColor(color, R, G, B);
|
||||
}
|
||||
|
@ -29,6 +39,13 @@ Video::~Video() {
|
|||
|
||||
//internal
|
||||
|
||||
const uint8_t Video::gamma_ramp[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,
|
||||
};
|
||||
|
||||
const uint8_t Video::cursor[15 * 15] = {
|
||||
0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,
|
||||
0,0,0,0,1,1,2,2,2,1,1,0,0,0,0,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
struct Video {
|
||||
unsigned* palette;
|
||||
void generate_palette();
|
||||
void generate_palette(bool color_emulation);
|
||||
Video();
|
||||
~Video();
|
||||
|
||||
|
@ -12,6 +12,7 @@ private:
|
|||
void scanline();
|
||||
void init();
|
||||
|
||||
static const uint8_t gamma_ramp[32];
|
||||
static const uint8_t cursor[15 * 15];
|
||||
void draw_cursor(uint16_t color, int x, int y);
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ ConfigurationSettings::ConfigurationSettings() {
|
|||
video.append(video.shader = "Blur", "Shader");
|
||||
video.append(video.scaleMode = 0, "ScaleMode");
|
||||
video.append(video.aspectCorrection = true, "AspectCorrection");
|
||||
video.append(video.colorEmulation = true, "ColorEmulation");
|
||||
video.maskOverscan.assign(video.maskOverscan.enable = false);
|
||||
video.maskOverscan.append(video.maskOverscan.horizontal = 8, "Horizontal");
|
||||
video.maskOverscan.append(video.maskOverscan.vertical = 8, "Vertical");
|
||||
|
|
|
@ -5,6 +5,7 @@ struct ConfigurationSettings : Configuration::Document {
|
|||
string shader;
|
||||
unsigned scaleMode;
|
||||
bool aspectCorrection;
|
||||
bool colorEmulation;
|
||||
struct MaskOverscan : Configuration::Node {
|
||||
bool enable;
|
||||
unsigned horizontal;
|
||||
|
|
|
@ -150,6 +150,7 @@ LibraryManager::LibraryManager() {
|
|||
};
|
||||
|
||||
libraryFrame.onChange = {&LibraryManager::onChange, this};
|
||||
loadButton.onActivate = {&LibraryManager::onLoad, this};
|
||||
|
||||
//initial config value of -1 defaults to import tab on first launch of higan
|
||||
if(config->library.selection < 0) config->library.selection = browsers.size();
|
||||
|
@ -202,6 +203,11 @@ void LibraryManager::onChange() {
|
|||
}
|
||||
}
|
||||
|
||||
void LibraryManager::onLoad() {
|
||||
unsigned selection = libraryFrame.selection();
|
||||
if(selection < browsers.size()) browsers[selection]->onActivate();
|
||||
}
|
||||
|
||||
void LibraryManager::setInformation(bool load) {
|
||||
string text;
|
||||
if(loaded.size() == 0) {
|
||||
|
@ -235,6 +241,24 @@ void LibraryManager::show() {
|
|||
onChange();
|
||||
}
|
||||
|
||||
//set library to show a specific media type, and then show the library
|
||||
void LibraryManager::show(const string& type) {
|
||||
unsigned selection = 0;
|
||||
for(auto& browser : browsers) {
|
||||
unsigned mode = 0;
|
||||
for(auto& media : browser->emulator.media) {
|
||||
if(media.bootable && media.type == type) {
|
||||
libraryFrame.setSelection(selection);
|
||||
browser->mediaMode.setSelection(mode);
|
||||
browser->setMode();
|
||||
return show();
|
||||
}
|
||||
mode++;
|
||||
}
|
||||
selection++;
|
||||
}
|
||||
}
|
||||
|
||||
void LibraryManager::synchronize() {
|
||||
if(libraryFrame.selection() < browsers.size()) {
|
||||
auto& emulator = browsers[libraryFrame.selection()]->emulator;
|
||||
|
|
|
@ -40,8 +40,10 @@ struct LibraryManager : Window {
|
|||
void bootstrap();
|
||||
string load(const string& type);
|
||||
void onChange();
|
||||
void onLoad();
|
||||
void setInformation(bool load);
|
||||
void show();
|
||||
void show(const string& type);
|
||||
void synchronize();
|
||||
|
||||
lstring loaded;
|
||||
|
|
|
@ -12,7 +12,7 @@ void Presentation::synchronize() {
|
|||
shaderNone.setChecked();
|
||||
if(config->video.shader == "None") shaderNone.setChecked();
|
||||
if(config->video.shader == "Blur") shaderBlur.setChecked();
|
||||
if(config->video.shader == "Emulation") shaderEmulation.setChecked();
|
||||
if(config->video.shader == "Display Emulation") shaderEmulation.setChecked();
|
||||
for(auto& shader : shaderList) {
|
||||
string name = notdir(config->video.shader.split<1>(".shader/")(0));
|
||||
if(name == shader->text()) shader->setChecked();
|
||||
|
@ -62,7 +62,6 @@ Presentation::Presentation() {
|
|||
viewport.setDroppable();
|
||||
|
||||
loadMenu.setText("Library");
|
||||
loadGame.setText("Load Game ...");
|
||||
settingsMenu.setText("Settings");
|
||||
videoMenu.setText("Video");
|
||||
centerVideo.setText("Center");
|
||||
|
@ -90,7 +89,7 @@ Presentation::Presentation() {
|
|||
synchronizeTime.setText("Synchronize Time");
|
||||
|
||||
append(loadMenu);
|
||||
loadMenu.append(loadGame);
|
||||
for(auto& item : loadBootableMedia) loadMenu.append(*item);
|
||||
for(auto& systemItem : emulatorList) append(systemItem->menu);
|
||||
append(settingsMenu);
|
||||
settingsMenu.append(videoMenu);
|
||||
|
@ -150,7 +149,6 @@ Presentation::Presentation() {
|
|||
}
|
||||
};
|
||||
|
||||
loadGame.onActivate = [&] { libraryManager->show(); };
|
||||
shaderNone.onActivate = [&] { config->video.shader = "None"; utility->updateShader(); };
|
||||
shaderBlur.onActivate = [&] { config->video.shader = "Blur"; utility->updateShader(); };
|
||||
shaderEmulation.onActivate = [&] { config->video.shader = "Display Emulation"; utility->updateShader(); };
|
||||
|
@ -174,6 +172,16 @@ Presentation::Presentation() {
|
|||
}
|
||||
|
||||
void Presentation::bootstrap() {
|
||||
for(auto& emulator : program->emulator) {
|
||||
for(auto& media : emulator->media) {
|
||||
if(media.bootable == false) continue;
|
||||
Item* item = new Item;
|
||||
item->setText({media.name, " ..."});
|
||||
item->onActivate = [=] { libraryManager->show(media.type); };
|
||||
loadBootableMedia.append(item);
|
||||
}
|
||||
}
|
||||
|
||||
for(auto& emulator : program->emulator) {
|
||||
auto iEmulator = new Emulator;
|
||||
iEmulator->interface = emulator;
|
||||
|
|
|
@ -22,7 +22,7 @@ struct Presentation : Window {
|
|||
Emulator* active = nullptr;
|
||||
|
||||
Menu loadMenu;
|
||||
Item loadGame;
|
||||
vector<Item*> loadBootableMedia;
|
||||
Menu settingsMenu;
|
||||
Menu videoMenu;
|
||||
RadioItem centerVideo;
|
||||
|
|
|
@ -19,6 +19,16 @@ void InputManager::appendHotkeys() {
|
|||
};
|
||||
}
|
||||
|
||||
{
|
||||
auto hotkey = new HotkeyInput;
|
||||
hotkey->name = "Show Library";
|
||||
hotkey->mapping = "KB0::L";
|
||||
|
||||
hotkey->press = [] {
|
||||
libraryManager->show();
|
||||
};
|
||||
}
|
||||
|
||||
{
|
||||
auto hotkey = new HotkeyInput;
|
||||
hotkey->name = "Pause Emulation";
|
||||
|
|
|
@ -250,10 +250,17 @@ void InputManager::saveConfiguration() {
|
|||
|
||||
InputManager::InputManager() {
|
||||
inputManager = this;
|
||||
scancode[0] = new int16_t[Scancode::Limit]();
|
||||
scancode[1] = new int16_t[Scancode::Limit]();
|
||||
activeScancode = 0;
|
||||
bootstrap();
|
||||
}
|
||||
|
||||
InputManager::~InputManager() {
|
||||
delete[] scancode[0];
|
||||
delete[] scancode[1];
|
||||
}
|
||||
|
||||
void InputManager::bootstrap() {
|
||||
unsigned guid = 0;
|
||||
for(auto& emulator : program->emulator) {
|
||||
|
|
|
@ -44,7 +44,7 @@ struct HotkeyInput : DigitalInput {
|
|||
struct InputManager {
|
||||
vector<AbstractInput*> inputMap;
|
||||
vector<HotkeyInput*> hotkeyMap;
|
||||
int16_t scancode[2][Scancode::Limit];
|
||||
int16_t* scancode[2];
|
||||
bool activeScancode;
|
||||
|
||||
void bind();
|
||||
|
@ -53,6 +53,7 @@ struct InputManager {
|
|||
void saveConfiguration();
|
||||
void bootstrap();
|
||||
InputManager();
|
||||
~InputManager();
|
||||
|
||||
//hotkeys.cpp
|
||||
void appendHotkeys();
|
||||
|
|
|
@ -15,6 +15,7 @@ VideoSettings::VideoSettings() {
|
|||
gamma.slider.setLength(101);
|
||||
luminance.name.setText("Luminance:");
|
||||
luminance.slider.setLength(101);
|
||||
colorEmulation.setText("Color emulation");
|
||||
overscanAdjustment.setFont(program->boldFont);
|
||||
overscanAdjustment.setText("Overscan mask:");
|
||||
overscanHorizontal.name.setText("Horizontal:");
|
||||
|
@ -25,11 +26,13 @@ VideoSettings::VideoSettings() {
|
|||
append(colorAdjustment, {~0, 0});
|
||||
append(saturation, {~0, 0});
|
||||
append(gamma, {~0, 0});
|
||||
append(luminance, {~0, 0}, 5);
|
||||
append(luminance, {~0, 0});
|
||||
append(colorEmulation, {~0, 0}, 5);
|
||||
append(overscanAdjustment, {~0, 0});
|
||||
append(overscanHorizontal, {~0, 0});
|
||||
append(overscanVertical, {~0, 0}, 5);
|
||||
|
||||
colorEmulation.setChecked(config->video.colorEmulation);
|
||||
saturation.slider.setPosition(config->video.saturation);
|
||||
gamma.slider.setPosition(config->video.gamma - 100);
|
||||
luminance.slider.setPosition(config->video.luminance);
|
||||
|
@ -38,7 +41,7 @@ VideoSettings::VideoSettings() {
|
|||
|
||||
synchronize();
|
||||
|
||||
saturation.slider.onChange = gamma.slider.onChange = luminance.slider.onChange =
|
||||
saturation.slider.onChange = gamma.slider.onChange = luminance.slider.onChange = colorEmulation.onToggle =
|
||||
overscanHorizontal.slider.onChange = overscanVertical.slider.onChange =
|
||||
{&VideoSettings::synchronize, this};
|
||||
}
|
||||
|
@ -47,6 +50,7 @@ void VideoSettings::synchronize() {
|
|||
config->video.saturation = saturation.slider.position();
|
||||
config->video.gamma = 100 + gamma.slider.position();
|
||||
config->video.luminance = luminance.slider.position();
|
||||
config->video.colorEmulation = colorEmulation.checked();
|
||||
config->video.maskOverscan.horizontal = overscanHorizontal.slider.position();
|
||||
config->video.maskOverscan.vertical = overscanVertical.slider.position();
|
||||
|
||||
|
@ -56,5 +60,5 @@ void VideoSettings::synchronize() {
|
|||
overscanHorizontal.value.setText({config->video.maskOverscan.horizontal, "px"});
|
||||
overscanVertical.value.setText({config->video.maskOverscan.vertical, "px"});
|
||||
|
||||
if(program->active) system().paletteUpdate();
|
||||
if(program->active) system().paletteUpdate(config->video.colorEmulation);
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ struct VideoSettings : SettingsLayout {
|
|||
VideoSlider saturation;
|
||||
VideoSlider gamma;
|
||||
VideoSlider luminance;
|
||||
CheckLabel colorEmulation;
|
||||
Label overscanAdjustment;
|
||||
VideoSlider overscanHorizontal;
|
||||
VideoSlider overscanVertical;
|
||||
|
|
|
@ -91,7 +91,7 @@ void Utility::load() {
|
|||
cheatEditor->load({pathname[0], "cheats.bml"});
|
||||
stateManager->load({pathname[0], "bsnes/states.bsa"}, 1);
|
||||
|
||||
system().paletteUpdate();
|
||||
system().paletteUpdate(config->video.colorEmulation);
|
||||
synchronizeDSP();
|
||||
|
||||
resize();
|
||||
|
|
Loading…
Reference in New Issue