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