diff --git a/readme.txt b/readme.txt index 76b555ba..5d74c4f5 100644 --- a/readme.txt +++ b/readme.txt @@ -1,5 +1,5 @@ bsnes -Version: 0.028 +Version: 0.029 Author: byuu -------- @@ -17,7 +17,6 @@ Please see license.txt for important licensing information. Known Limitations: ------------------ S-CPU -- Invalid DMA / HDMA transfers not fully emulated - Multiply / Divide register delays not implemented S-PPU diff --git a/src/Makefile b/src/Makefile index 01347978..cba8839e 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,7 +1,5 @@ include lib/nall/Makefile.string - prefix = /usr/local -arch = ARCH_LSB ################ ### compiler ### @@ -37,12 +35,11 @@ endif ifeq ($(platform),x) # X11 ruby = video.glx video.xv video.sdl audio.openal audio.oss audio.ao input.sdl input.x - arch += PLATFORM_X link += `pkg-config --libs gtk+-2.0` + link += $(call mklib,Xtst) delete = rm -f $1 else ifeq ($(platform),win) # Windows ruby = video.direct3d video.directdraw video.gdi audio.directsound input.directinput - arch += PLATFORM_WIN link += $(if $(findstring mingw,$(compiler)),-mwindows) link += $(call mklib,uuid) link += $(call mklib,kernel32) @@ -61,13 +58,16 @@ endif ### ruby ### ############ +rubyflags = +rubyflags += $(if $(findstring .sdl,$(ruby)),`sdl-config --cflags`) + link += $(if $(findstring video.direct3d,$(ruby)),$(call mklib,d3d9)) link += $(if $(findstring video.directdraw,$(ruby)),$(call mklib,ddraw)) link += $(if $(findstring video.glx,$(ruby)),$(call mklib,GL)) link += $(if $(findstring video.xv,$(ruby)),$(call mklib,Xv)) link += $(if $(findstring audio.ao,$(ruby)),$(call mklib,ao)) link += $(if $(findstring audio.directsound,$(ruby)),$(call mklib,dsound)) -link += $(if $(findstring audio.openal,$(ruby)),$(call mklib,openal) $(call mklib,alut)) +link += $(if $(findstring audio.openal,$(ruby)),$(if $(call streq,$(platform),x),$(call mklib,openal),$(call mklib,openal32))) link += $(if $(findstring input.directinput,$(ruby)),$(call mklib,dinput8) $(call mklib,dxguid)) link += $(if $(findstring input.sdl,$(ruby)),`sdl-config --libs`) @@ -75,7 +75,7 @@ link += $(if $(findstring input.sdl,$(ruby)),`sdl-config --libs`) ### main target and dependencies ### #################################### -objects = main libco hiro ruby $(ruby) string reader cart cheat \ +objects = main libco hiro ruby libfilter string reader cart cheat \ memory smemory cpu scpu smp ssmp bdsp ppu bppu snes \ bsx srtc sdd1 cx4 dsp1 dsp2 dsp3 dsp4 obc1 st010 @@ -89,7 +89,6 @@ ifeq ($(enable_jma),true) flags += $(call mkdef,JMA_SUPPORT) endif -arch := $(patsubst %,$(call mkdef,%),$(arch)) objects := $(patsubst %,obj/%.$(obj),$(objects)) rubydef := $(foreach c,$(subst .,_,$(call strupper,$(ruby))),$(call mkdef,$c)) @@ -124,47 +123,22 @@ all: build; ### main ### ############ -obj/main.$(obj): ui/main.cpp config/* lib/nall/* lib/ruby/* ui/* ui/loader/* ui/settings/* - $(call compile,$(arch)) - -obj/bsnes.res : ui/bsnes.rc; rc /r /foobj/bsnes.res ui/bsnes.rc -obj/bsnesrc.$(obj): ui/bsnes.rc; windres -I data ui/bsnes.rc obj/bsnesrc.$(obj) - -############ -### ruby ### -############ - -obj/ruby.$(obj) : lib/ruby/ruby.cpp lib/ruby/* - $(call compile,$(rubydef)) -obj/video.direct3d.$(obj) : lib/ruby/video/direct3d.cpp lib/ruby/video/direct3d.* -obj/video.directdraw.$(obj) : lib/ruby/video/directdraw.cpp lib/ruby/video/directdraw.* -obj/video.gdi.$(obj) : lib/ruby/video/gdi.cpp lib/ruby/video/gdi.* -obj/video.glx.$(obj) : lib/ruby/video/glx.cpp lib/ruby/video/glx.* -obj/video.sdl.$(obj) : lib/ruby/video/sdl.cpp lib/ruby/video/sdl.* - $(call compile,`sdl-config --cflags`) -obj/video.xv.$(obj) : lib/ruby/video/xv.cpp lib/ruby/video/xv.* -obj/audio.ao.$(obj) : lib/ruby/audio/ao.cpp lib/ruby/audio/ao.* -obj/audio.directsound.$(obj): lib/ruby/audio/directsound.cpp lib/ruby/audio/directsound.* -obj/audio.openal.$(obj) : lib/ruby/audio/openal.cpp lib/ruby/audio/openal.* -obj/audio.oss.$(obj) : lib/ruby/audio/oss.cpp lib/ruby/audio/oss.* -obj/input.directinput.$(obj): lib/ruby/input/directinput.cpp lib/ruby/input/directinput.* -obj/input.sdl.$(obj) : lib/ruby/input/sdl.cpp lib/ruby/input/sdl.* - $(call compile,`sdl-config --cflags`) -obj/input.x.$(obj) : lib/ruby/input/x.cpp lib/ruby/input/x.* - -############ -### hiro ### -############ - -obj/hiro.$(obj): lib/hiro.cpp lib/hiro.* lib/hiro_gtk/* lib/hiro_win/* - $(call compile,$(if $(call streq,$(platform),x),`pkg-config --cflags gtk+-2.0`)) +obj/main.$(obj): ui/main.cpp ui/* ui/base/* ui/loader/* ui/settings/* +obj/bsnes.res: ui/bsnes.rc; rc /r /foobj/bsnes.res ui/bsnes.rc +obj/bsnesrc.$(obj): ui/bsnes.rc; windres ui/bsnes.rc obj/bsnesrc.$(obj) ################# ### libraries ### ################# -obj/libco.$(obj): lib/libco.c lib/libco.* lib/libco/* -obj/string.$(obj): lib/nall/string.cpp lib/nall/* +obj/ruby.$(obj): lib/ruby/ruby.cpp lib/ruby/* + $(call compile,$(rubydef) $(rubyflags)) +obj/hiro.$(obj): lib/hiro/hiro.cpp lib/hiro/* lib/hiro/gtk/* lib/hiro/win/* + $(call compile,$(if $(call streq,$(platform),x),`pkg-config --cflags gtk+-2.0`)) +obj/libco.$(obj): lib/libco/libco.c lib/libco/* + $(call compile,-static) +obj/libfilter.$(obj): lib/libfilter/libfilter.cpp lib/libfilter/* +obj/string.$(obj): lib/nall/string.cpp lib/nall/* ################# ### utilities ### @@ -179,7 +153,6 @@ obj/cheat.$(obj) : cheat/cheat.cpp cheat/* ############## obj/memory.$(obj) : memory/memory.cpp memory/* -obj/bmemory.$(obj): memory/bmemory/bmemory.cpp memory/bmemory/* memory/bmemory/mapper/* obj/smemory.$(obj): memory/smemory/smemory.cpp memory/smemory/* memory/smemory/mapper/* ########### @@ -270,8 +243,8 @@ build: $(objects) $(strip $(cpp) $(call mkbin,../bsnes) $(objects) $(link)) install: - install -D -m 755 ../bsnes $(prefix)/bin/bsnes - install -D -m 644 data/bsnes.png $(prefix)/share/icons/bsnes.png + install -D -m 755 ../bsnes $(DESTDIR)$(prefix)/bin/bsnes + install -D -m 644 data/bsnes.png $(DESTDIR)$(prefix)/share/icons/bsnes.png clean: -@$(call delete,obj/*.$(obj)) diff --git a/src/base.h b/src/base.h index ba31bd72..6966e20b 100644 --- a/src/base.h +++ b/src/base.h @@ -1,4 +1,4 @@ -#define BSNES_VERSION "0.028.01" +#define BSNES_VERSION "0.029" #define BSNES_TITLE "bsnes v" BSNES_VERSION #define BUSCORE sBus @@ -7,32 +7,28 @@ #define DSPCORE bDSP #define PPUCORE bPPU -//FAVOR_ACCURACY calculates RTO during frameskip, whereas FAVOR_SPEED does not +//FAST_FRAMESKIP disables calculation of RTO during frameskip //frameskip offers near-zero speedup if RTO is calculated //accuracy is not affected by this define when frameskipping is off - -//#define FAVOR_ACCURACY -#define FAVOR_SPEED +#define FAST_FRAMESKIP //game genie + pro action replay code support (~1-3% speed hit) #define CHEAT_SYSTEM -#if !defined(ARCH_LSB) && !defined(ARCH_MSB) - #define ARCH_LSB //guess -#endif - #include #include #include #include +#include #include #include #include +#include #include #include using namespace nall; -#include +#include #include //platform-specific global functions diff --git a/src/cart/cart.cpp b/src/cart/cart.cpp index 8ad84b62..c309ea7d 100644 --- a/src/cart/cart.cpp +++ b/src/cart/cart.cpp @@ -1,5 +1,6 @@ -#include "../base.h" - +#include "../base.h" +#define CART_CPP + #include "cart_normal.cpp" #include "cart_bsx.cpp" #include "cart_bsc.cpp" @@ -82,12 +83,9 @@ void Cartridge::load_end() { memory::stBrom.write_protect(true); memory::stBram.write_protect(false); -char fn[PATH_MAX]; - strcpy(fn, cart.fn); - modify_extension(fn, "cht"); - if(fexists(fn)) { + if(fexists(get_cheat_filename(cart.fn, "cht"))) { cheat.clear(); - cheat.load(fn); + cheat.load(cheatfn); } cart.loaded = true; @@ -114,11 +112,11 @@ bool Cartridge::unload() { safe_free(stB.rom); safe_free(stB.ram); -char fn[PATH_MAX]; + char fn[PATH_MAX]; strcpy(fn, cart.fn); modify_extension(fn, "cht"); - if(cheat.count() > 0 || fexists(fn)) { - cheat.save(fn); + if(cheat.count() > 0 || fexists(get_cheat_filename(cart.fn, "cht"))) { + cheat.save(cheatfn); cheat.clear(); } diff --git a/src/cart/cart.h b/src/cart/cart.h index 886526e2..9d580172 100644 --- a/src/cart/cart.h +++ b/src/cart/cart.h @@ -20,11 +20,11 @@ public: CKSUM = 0x1e, RESL = 0x3c, RESH = 0x3d, - }; - - enum Region { - NTSC, - PAL, + }; + + enum Region { + NTSC, + PAL, }; enum MemoryMapper { @@ -67,7 +67,8 @@ public: struct { CartridgeType type; - uint32 crc32; + uint32 crc32; + char filename[PATH_MAX * 4]; char name[128]; Region region; @@ -122,14 +123,18 @@ public: bool load_file(const char *fn, uint8 *&data, uint &size); bool save_file(const char *fn, uint8 *data, uint size); - char* modify_extension(char *filename, const char *extension); - char* get_save_filename(const char *source, const char *extension); + char* modify_extension(char *filename, const char *extension); + char* get_base_filename(char *filename); + char* get_path_filename(char *filename, const char *path, const char *source, const char *extension); + char* get_save_filename(const char *source, const char *extension); + char* get_cheat_filename(const char *source, const char *extension); Cartridge(); ~Cartridge(); private: - char savefn[PATH_MAX]; + char savefn[PATH_MAX]; + char cheatfn[PATH_MAX]; }; namespace memory { diff --git a/src/cart/cart_bsc.cpp b/src/cart/cart_bsc.cpp index 18e81267..704d0a22 100644 --- a/src/cart/cart_bsc.cpp +++ b/src/cart/cart_bsc.cpp @@ -1,3 +1,5 @@ +#ifdef CART_CPP + void Cartridge::load_cart_bsc(const char *base, const char *slot) { if(!base || !*base) return; @@ -5,8 +7,8 @@ void Cartridge::load_cart_bsc(const char *base, const char *slot) { strcpy(bs.fn, slot ? slot : ""); load_begin(CartridgeBSC); -uint8 *data; -uint size; + uint8_t *data = 0; + unsigned size; load_file(cart.fn, data, size); cart.rom = data, cart.rom_size = size; @@ -34,8 +36,21 @@ uint size; } load_end(); + + //set base filename + strcpy(info.filename, cart.fn); + get_base_filename(info.filename); + if(*bs.fn) { + char filenameBS[PATH_MAX]; + strcpy(filenameBS, bs.fn); + get_base_filename(filenameBS); + strcat(info.filename, " + "); + strcat(info.filename, filenameBS); + } } void Cartridge::unload_cart_bsc() { if(cart.ram) save_file(get_save_filename(cart.fn, "srm"), cart.ram, cart.ram_size); } + +#endif //ifdef CART_CPP diff --git a/src/cart/cart_bsx.cpp b/src/cart/cart_bsx.cpp index eaee15af..aaf1e5c0 100644 --- a/src/cart/cart_bsx.cpp +++ b/src/cart/cart_bsx.cpp @@ -1,3 +1,5 @@ +#ifdef CART_CPP + void Cartridge::load_cart_bsx(const char *base, const char *slot) { if(!base || !*base) return; @@ -10,8 +12,8 @@ void Cartridge::load_cart_bsx(const char *base, const char *slot) { info.mapper = BSXROM; info.region = NTSC; -uint8 *data; -uint size; + uint8_t *data = 0; + unsigned size; load_file(cart.fn, data, size); cart.rom = data, cart.rom_size = size; cart.ram = 0, cart.ram_size = 0; @@ -37,9 +39,14 @@ uint size; } load_end(); + + strcpy(info.filename, !*bs.fn ? cart.fn : bs.fn); + get_base_filename(info.filename); } void Cartridge::unload_cart_bsx() { save_file(get_save_filename(cart.fn, "srm"), bsxcart.sram.handle (), bsxcart.sram.size ()); save_file(get_save_filename(cart.fn, "psr"), bsxcart.psram.handle(), bsxcart.psram.size()); } + +#endif //ifdef CART_CPP diff --git a/src/cart/cart_file.cpp b/src/cart/cart_file.cpp index d2ed5499..6f71ae4a 100644 --- a/src/cart/cart_file.cpp +++ b/src/cart/cart_file.cpp @@ -1,3 +1,5 @@ +#ifdef CART_CPP + #include "../reader/filereader.h" #if defined(GZIP_SUPPORT) @@ -10,7 +12,7 @@ #endif char* Cartridge::modify_extension(char *filename, const char *extension) { -int i; + int i; for(i = strlen(filename); i >= 0; i--) { if(filename[i] == '.') break; if(filename[i] == '/') break; @@ -20,32 +22,65 @@ int i; strcat(filename, "."); strcat(filename, extension); return filename; +} + +//remove directory information and file extension ("/foo/bar.ext" -> "bar") +char* Cartridge::get_base_filename(char *filename) { + //remove extension + for(int i = strlen(filename) - 1; i >= 0; i--) { + if(filename[i] == '.') { + filename[i] = 0; + break; + } + } + + //remove directory information + for(int i = strlen(filename) - 1; i >= 0; i--) { + if(filename[i] == '/' || filename[i] == '\\') { + i++; + char *output = filename; + while(true) { + *output++ = filename[i]; + if(!filename[i]) break; + i++; + } + break; + } + } } -char* Cartridge::get_save_filename(const char *source, const char *extension) { - strcpy(savefn, source); - for(char *p = savefn; *p; p++) { if(*p == '\\') *p = '/'; } - modify_extension(savefn, extension); +char* Cartridge::get_path_filename(char *filename, const char *path, const char *source, const char *extension) { + strcpy(filename, source); + for(char *p = filename; *p; p++) { if(*p == '\\') *p = '/'; } + modify_extension(filename, extension); -//override path with user-specified folder, if one was defined - if(config::path.save != "") { - lstring part; - split(part, "/", savefn); - string fn = (const char*)config::path.save; + //override path with user-specified folder, if one was defined + if(path != "") { + lstring part; + split(part, "/", filename); + string fn = path; if(strend(fn, "/") == false) strcat(fn, "/"); strcat(fn, part[count(part) - 1]); - strcpy(savefn, fn); + strcpy(filename, fn); - //resolve relative path, if found + //resolve relative path, if found if(strbegin(fn, "./") == true) { ltrim(fn, "./"); - strcpy(savefn, config::path.base); - strcat(savefn, fn); + strcpy(filename, config::path.base); + strcat(filename, fn); } } - return savefn; + return filename; } + +char* Cartridge::get_save_filename(const char *source, const char *extension) { + return get_path_filename(savefn, config::path.save, source, extension); +} + +char* Cartridge::get_cheat_filename(const char *source, const char *extension) { + return get_path_filename(cheatfn, config::path.cheat, source, extension); +} bool Cartridge::load_file(const char *fn, uint8 *&data, uint &size) { dprintf("* Loading \"%s\"...", fn); @@ -54,8 +89,8 @@ bool Cartridge::load_file(const char *fn, uint8 *&data, uint &size) { switch(Reader::detect(fn)) { default: - case Reader::RF_NORMAL: { - FileReader ff(fn); + case Reader::Normal: { + FileReader ff(fn); if(!ff.ready()) { alert("Error loading image file (%s)!", fn); return false; @@ -64,9 +99,9 @@ bool Cartridge::load_file(const char *fn, uint8 *&data, uint &size) { data = ff.read(); } break; - #ifdef GZIP_SUPPORT - case Reader::RF_GZ: { - GZReader gf(fn); + #ifdef GZIP_SUPPORT + case Reader::GZIP: { + GZReader gf(fn); if(!gf.ready()) { alert("Error loading image file (%s)!", fn); return false; @@ -75,17 +110,17 @@ bool Cartridge::load_file(const char *fn, uint8 *&data, uint &size) { data = gf.read(); } break; - case Reader::RF_ZIP: { - ZipReader zf(fn); + case Reader::ZIP: { + ZipReader zf(fn); size = zf.size(); data = zf.read(); } break; - #endif + #endif - #ifdef JMA_SUPPORT - case Reader::RF_JMA: { + #ifdef JMA_SUPPORT + case Reader::JMA: { try { - JMAReader jf(fn); + JMAReader jf(fn); size = jf.size(); data = jf.read(); } catch(JMA::jma_errors jma_error) { @@ -93,15 +128,17 @@ bool Cartridge::load_file(const char *fn, uint8 *&data, uint &size) { return false; } } break; - #endif + #endif } return true; } bool Cartridge::save_file(const char *fn, uint8 *data, uint size) { -FileWriter ff(fn); + FileWriter ff(fn); if(!ff.ready())return false; ff.write(data, size); return true; } + +#endif //ifdef CART_CPP diff --git a/src/cart/cart_header.cpp b/src/cart/cart_header.cpp index e929c465..bd96e774 100644 --- a/src/cart/cart_header.cpp +++ b/src/cart/cart_header.cpp @@ -1,184 +1,189 @@ -void Cartridge::read_header() { -uint8 *rom = cart.rom; -uint index = info.header_index; -uint8 mapper = rom[index + MAPPER]; -uint8 rom_type = rom[index + ROM_TYPE]; -uint8 company = rom[index + COMPANY]; -uint8 region = rom[index + REGION] & 0x7f; - -//detect presence of BS-X flash cartridge connector (reads extended header information) -bool has_bsxflash = false; - if(rom[index - 14] == 'Z') { - if(rom[index - 11] == 'J') { - uint8 n13 = rom[index - 13]; - if((n13 >= 'A' && n13 <= 'Z') || (n13 >= '0' && n13 <= '9')) { - if(company == 0x33 || (rom[index - 10] == 0x00 && rom[index - 4] == 0x00)) { - has_bsxflash = true; - } - } - } - } - - if(has_bsxflash == true) { - info.mapper = index == 0x7fc0 ? BSCLoROM : BSCHiROM; - } else if(index == 0x7fc0 && cart.rom_size >= 0x401000) { - info.mapper = ExLoROM; - } else if(index == 0x7fc0 && mapper == 0x32) { - info.mapper = ExLoROM; - } else if(index == 0x7fc0) { - info.mapper = LoROM; - } else if(index == 0xffc0) { - info.mapper = HiROM; - } else { //index == 0x40ffc0 - info.mapper = ExHiROM; - } - - if(mapper == 0x20 && (rom_type == 0x13 || rom_type == 0x14 || rom_type == 0x15 || rom_type == 0x1a)) { - info.superfx = true; - } - - if(mapper == 0x23 && (rom_type == 0x34 || rom_type == 0x35)) { - info.sa1 = true; - } - - if(mapper == 0x35 && rom_type == 0x55) { - info.srtc = true; - } - - if(mapper == 0x32 && (rom_type == 0x43 || rom_type == 0x45)) { - info.sdd1 = true; - } - - if(mapper == 0x20 && rom_type == 0xf3) { - info.cx4 = true; - } - - if((mapper == 0x20 || mapper == 0x21) && rom_type == 0x03) { - info.dsp1 = true; - } - - if(mapper == 0x30 && rom_type == 0x05 && company != 0xb2) { - info.dsp1 = true; - } - - if(mapper == 0x31 && (rom_type == 0x03 || rom_type == 0x05)) { - info.dsp1 = true; - } - - if(info.dsp1 == true) { - if((mapper & 0x2f) == 0x20 && cart.rom_size <= 0x100000) { - info.dsp1_mapper = DSP1LoROM1MB; - } else if((mapper & 0x2f) == 0x20) { - info.dsp1_mapper = DSP1LoROM2MB; - } else if((mapper & 0x2f) == 0x21) { - info.dsp1_mapper = DSP1HiROM; - } - } - - if(mapper == 0x20 && rom_type == 0x05) { - info.dsp2 = true; - } - - if(mapper == 0x30 && rom_type == 0x05 && company == 0xb2) { - info.dsp3 = true; - } - - if(mapper == 0x30 && rom_type == 0x03) { - info.dsp4 = true; - } - - if(mapper == 0x30 && rom_type == 0x25) { - info.obc1 = true; - } - - if(mapper == 0x30 && rom_type == 0xf6) { - //TODO: both ST010 and ST011 share the same mapper + rom_type - //need way to determine which is which - //for now, default to supported ST010 - info.st010 = true; - } - - if(mapper == 0x30 && rom_type == 0xf5) { - info.st018 = true; - } - - if(rom[info.header_index + RAM_SIZE] & 7) { - info.ram_size = 1024 << (rom[info.header_index + RAM_SIZE] & 7); - } else { - info.ram_size = 0; - } - -//0, 1, 13 = NTSC; 2 - 12 = PAL - info.region = (region <= 1 || region >= 13) ? NTSC : PAL; - - memcpy(&info.name, &rom[info.header_index + CART_NAME], 21); - info.name[21] = 0; - - for(int i = 0; i < 22; i++) { - if(info.name[i] & 0x80) { - info.name[i] = '?'; - } - } -} - -void Cartridge::find_header() { -int32 score_lo = 0, - score_hi = 0, - score_ex = 0; -uint8 *rom = cart.rom; - - if(cart.rom_size < 0x010000) { - //cart too small to be anything but lorom - info.header_index = 0x007fc0; - return; - } - - if((rom[0x7fc0 + MAPPER] & ~0x10) == 0x20)score_lo++; - if((rom[0xffc0 + MAPPER] & ~0x10) == 0x21)score_hi++; - - if(rom[0x7fc0 + ROM_TYPE] < 0x08)score_lo++; - if(rom[0xffc0 + ROM_TYPE] < 0x08)score_hi++; - - if(rom[0x7fc0 + ROM_SIZE] < 0x10)score_lo++; - if(rom[0xffc0 + ROM_SIZE] < 0x10)score_hi++; - - if(rom[0x7fc0 + RAM_SIZE] < 0x08)score_lo++; - if(rom[0xffc0 + RAM_SIZE] < 0x08)score_hi++; - - if(rom[0x7fc0 + REGION] < 14)score_lo++; - if(rom[0xffc0 + REGION] < 14)score_hi++; - - if(rom[0x7fc0 + COMPANY] < 3)score_lo++; - if(rom[0xffc0 + COMPANY] < 3)score_hi++; - - if(rom[0x7fc0 + RESH] & 0x80)score_lo += 2; - if(rom[0xffc0 + RESH] & 0x80)score_hi += 2; - -uint16 cksum, icksum; - cksum = rom[0x7fc0 + CKSUM] | (rom[0x7fc0 + CKSUM + 1] << 8); - icksum = rom[0x7fc0 + ICKSUM] | (rom[0x7fc0 + ICKSUM + 1] << 8); - if((cksum + icksum) == 0xffff && (cksum != 0) && (icksum != 0)) { - score_lo += 8; - } - - cksum = rom[0xffc0 + CKSUM] | (rom[0xffc0 + CKSUM + 1] << 8); - icksum = rom[0xffc0 + ICKSUM] | (rom[0xffc0 + ICKSUM + 1] << 8); - if((cksum + icksum) == 0xffff && (cksum != 0) && (icksum != 0)) { - score_hi += 8; - } - - if(cart.rom_size < 0x401000) { - score_ex = 0; - } else { - if(rom[0x7fc0 + MAPPER] == 0x32)score_lo++; - else score_ex += 16; - } - - if(score_lo >= score_hi && score_lo >= score_ex) { - info.header_index = 0x007fc0; - } else if(score_hi >= score_ex) { - info.header_index = 0x00ffc0; - } else { - info.header_index = 0x40ffc0; - } -} +#ifdef CART_CPP + +void Cartridge::read_header() { + uint8 *rom = cart.rom; + uint index = info.header_index; + uint8 mapper = rom[index + MAPPER]; + uint8 rom_type = rom[index + ROM_TYPE]; + uint8 company = rom[index + COMPANY]; + uint8 region = rom[index + REGION] & 0x7f; + + //detect presence of BS-X flash cartridge connector (reads extended header information) + bool has_bsxflash = false; + if(rom[index - 14] == 'Z') { + if(rom[index - 11] == 'J') { + uint8 n13 = rom[index - 13]; + if((n13 >= 'A' && n13 <= 'Z') || (n13 >= '0' && n13 <= '9')) { + if(company == 0x33 || (rom[index - 10] == 0x00 && rom[index - 4] == 0x00)) { + has_bsxflash = true; + } + } + } + } + + if(has_bsxflash == true) { + info.mapper = index == 0x7fc0 ? BSCLoROM : BSCHiROM; + } else if(index == 0x7fc0 && cart.rom_size >= 0x401000) { + info.mapper = ExLoROM; + } else if(index == 0x7fc0 && mapper == 0x32) { + info.mapper = ExLoROM; + } else if(index == 0x7fc0) { + info.mapper = LoROM; + } else if(index == 0xffc0) { + info.mapper = HiROM; + } else { //index == 0x40ffc0 + info.mapper = ExHiROM; + } + + if(mapper == 0x20 && (rom_type == 0x13 || rom_type == 0x14 || rom_type == 0x15 || rom_type == 0x1a)) { + info.superfx = true; + } + + if(mapper == 0x23 && (rom_type == 0x34 || rom_type == 0x35)) { + info.sa1 = true; + } + + if(mapper == 0x35 && rom_type == 0x55) { + info.srtc = true; + } + + if(mapper == 0x32 && (rom_type == 0x43 || rom_type == 0x45)) { + info.sdd1 = true; + } + + if(mapper == 0x20 && rom_type == 0xf3) { + info.cx4 = true; + } + + if((mapper == 0x20 || mapper == 0x21) && rom_type == 0x03) { + info.dsp1 = true; + } + + if(mapper == 0x30 && rom_type == 0x05 && company != 0xb2) { + info.dsp1 = true; + } + + if(mapper == 0x31 && (rom_type == 0x03 || rom_type == 0x05)) { + info.dsp1 = true; + } + + if(info.dsp1 == true) { + if((mapper & 0x2f) == 0x20 && cart.rom_size <= 0x100000) { + info.dsp1_mapper = DSP1LoROM1MB; + } else if((mapper & 0x2f) == 0x20) { + info.dsp1_mapper = DSP1LoROM2MB; + } else if((mapper & 0x2f) == 0x21) { + info.dsp1_mapper = DSP1HiROM; + } + } + + if(mapper == 0x20 && rom_type == 0x05) { + info.dsp2 = true; + } + + if(mapper == 0x30 && rom_type == 0x05 && company == 0xb2) { + info.dsp3 = true; + } + + if(mapper == 0x30 && rom_type == 0x03) { + info.dsp4 = true; + } + + if(mapper == 0x30 && rom_type == 0x25) { + info.obc1 = true; + } + + if(mapper == 0x30 && rom_type == 0xf6) { + //TODO: both ST010 and ST011 share the same mapper + rom_type. + //need way to determine which is which. + //for now, default to supported ST010. + info.st010 = true; + } + + if(mapper == 0x30 && rom_type == 0xf5) { + info.st018 = true; + } + + if(rom[info.header_index + RAM_SIZE] & 7) { + info.ram_size = 1024 << (rom[info.header_index + RAM_SIZE] & 7); + } else { + info.ram_size = 0; + } + + //0, 1, 13 = NTSC; 2 - 12 = PAL + info.region = (region <= 1 || region >= 13) ? NTSC : PAL; + + memcpy(&info.name, &rom[info.header_index + CART_NAME], 21); + info.name[21] = 0; + trim(info.name); + + //convert undisplayable characters (half-width katakana, etc) to '?' characters + for(int i = 0; i < 21; i++) { + if(info.name[i] & 0x80) info.name[i] = '?'; + } + + //always display something + if(!info.name[0]) strcpy(info.name, "(untitled)"); +} + +void Cartridge::find_header() { + int32 score_lo = 0, score_hi = 0, score_ex = 0; + uint8_t *rom = cart.rom; + + if(cart.rom_size < 0x010000) { + //cart too small to be anything but lorom + info.header_index = 0x007fc0; + return; + } + + if((rom[0x7fc0 + MAPPER] & ~0x10) == 0x20) score_lo++; + if((rom[0xffc0 + MAPPER] & ~0x10) == 0x21) score_hi++; + + if(rom[0x7fc0 + ROM_TYPE] < 0x08) score_lo++; + if(rom[0xffc0 + ROM_TYPE] < 0x08) score_hi++; + + if(rom[0x7fc0 + ROM_SIZE] < 0x10) score_lo++; + if(rom[0xffc0 + ROM_SIZE] < 0x10) score_hi++; + + if(rom[0x7fc0 + RAM_SIZE] < 0x08) score_lo++; + if(rom[0xffc0 + RAM_SIZE] < 0x08) score_hi++; + + if(rom[0x7fc0 + REGION] < 14) score_lo++; + if(rom[0xffc0 + REGION] < 14) score_hi++; + + if(rom[0x7fc0 + COMPANY] < 3) score_lo++; + if(rom[0xffc0 + COMPANY] < 3) score_hi++; + + if(rom[0x7fc0 + RESH] & 0x80) score_lo += 2; + if(rom[0xffc0 + RESH] & 0x80) score_hi += 2; + + uint16 cksum, icksum; + cksum = rom[0x7fc0 + CKSUM] | (rom[0x7fc0 + CKSUM + 1] << 8); + icksum = rom[0x7fc0 + ICKSUM] | (rom[0x7fc0 + ICKSUM + 1] << 8); + if((cksum + icksum) == 0xffff && (cksum != 0) && (icksum != 0)) { + score_lo += 8; + } + + cksum = rom[0xffc0 + CKSUM] | (rom[0xffc0 + CKSUM + 1] << 8); + icksum = rom[0xffc0 + ICKSUM] | (rom[0xffc0 + ICKSUM + 1] << 8); + if((cksum + icksum) == 0xffff && (cksum != 0) && (icksum != 0)) { + score_hi += 8; + } + + if(cart.rom_size < 0x401000) { + score_ex = 0; + } else { + if(rom[0x7fc0 + MAPPER] == 0x32) score_lo++; + else score_ex += 16; + } + + if(score_lo >= score_hi && score_lo >= score_ex) { + info.header_index = 0x007fc0; + } else if(score_hi >= score_ex) { + info.header_index = 0x00ffc0; + } else { + info.header_index = 0x40ffc0; + } +} + +#endif //ifdef CART_CPP diff --git a/src/cart/cart_normal.cpp b/src/cart/cart_normal.cpp index cf227007..c809af75 100644 --- a/src/cart/cart_normal.cpp +++ b/src/cart/cart_normal.cpp @@ -1,14 +1,16 @@ +#ifdef CART_CPP + void Cartridge::load_cart_normal(const char *filename) { if(!filename || !*filename) return; -uint8 *data; -uint size; + uint8_t *data = 0; + unsigned size; if(load_file(filename, data, size) == false) return; strcpy(cart.fn, filename); load_begin(CartridgeNormal); -//load ROM data, ignore 512-byte header if detected + //load ROM data, ignore 512-byte header if detected if((size & 0x7fff) != 512) { cart.rom = (uint8*)malloc(cart.rom_size = size); memcpy(cart.rom, data, size); @@ -34,8 +36,14 @@ uint size; } load_end(); + + //set base filename + strcpy(info.filename, cart.fn); + get_base_filename(info.filename); } void Cartridge::unload_cart_normal() { if(cart.ram) save_file(get_save_filename(cart.fn, "srm"), cart.ram, cart.ram_size); } + +#endif //ifdef CART_CPP diff --git a/src/cart/cart_st.cpp b/src/cart/cart_st.cpp index 4eac85ed..1f4cb686 100644 --- a/src/cart/cart_st.cpp +++ b/src/cart/cart_st.cpp @@ -1,3 +1,5 @@ +#ifdef CART_CPP + void Cartridge::load_cart_st(const char *base, const char *slotA, const char *slotB) { if(!base || !*base) return; @@ -10,8 +12,8 @@ void Cartridge::load_cart_st(const char *base, const char *slotA, const char *sl info.mapper = STROM; info.region = NTSC; -uint8 *data; -uint size; + uint8_t *data = 0; + unsigned size; if(load_file(cart.fn, data, size) == true) { cart.rom = (uint8*)malloc(cart.rom_size = 0x040000); memcpy(cart.rom, data, min(size, cart.rom_size)); @@ -51,9 +53,32 @@ uint size; } load_end(); + + //set base filename + if(!*stA.fn && !*stB.fn) { + strcpy(info.filename, cart.fn); + get_base_filename(info.filename); + } else if(*stA.fn && !*stB.fn) { + strcpy(info.filename, stA.fn); + get_base_filename(info.filename); + } else if(!*stA.fn && *stB.fn) { + strcpy(info.filename, stB.fn); + get_base_filename(info.filename); + } else { + char filenameA[PATH_MAX], filenameB[PATH_MAX]; + strcpy(filenameA, stA.fn); + get_base_filename(filenameA); + strcpy(filenameB, stB.fn); + get_base_filename(filenameB); + strcpy(info.filename, filenameA); + strcat(info.filename, " + "); + strcat(info.filename, filenameB); + } } void Cartridge::unload_cart_st() { if(stA.ram) save_file(get_save_filename(stA.fn, "srm"), stA.ram, stA.ram_size); if(stB.ram) save_file(get_save_filename(stB.fn, "srm"), stB.ram, stB.ram_size); } + +#endif //ifdef CART_CPP diff --git a/src/cheat/cheat.cpp b/src/cheat/cheat.cpp index 73aa96f4..ba1a1a3d 100644 --- a/src/cheat/cheat.cpp +++ b/src/cheat/cheat.cpp @@ -10,24 +10,24 @@ Cheat cheat; *****/ bool Cheat::decode(char *str, uint32 &addr, uint8 &data, uint8 &type) { -string t, part; + string t, part; strcpy(t, str); strlower(t()); if(strlen(t) == 8 || (strlen(t) == 9 && t()[6] == ':')) { - type = CT_PRO_ACTION_REPLAY; + type = ProActionReplay; replace(t, ":", ""); - uint32 r = strhex((const char*)t); + uint32 r = strhex((const char*)t); addr = r >> 8; data = r & 0xff; return true; } else if(strlen(t) == 9 && t()[4] == '-') { - type = CT_GAME_GENIE; + type = GameGenie; replace(t, "-", ""); strtr(t, "df4709156bc8a23e", "0123456789abcdef"); - uint32 r = strhex((const char*)t); - //8421 8421 8421 8421 8421 8421 - //abcd efgh ijkl mnop qrst uvwx - //ijkl qrst opab cduv wxef ghmn + uint32 r = strhex((const char*)t); + //8421 8421 8421 8421 8421 8421 + //abcd efgh ijkl mnop qrst uvwx + //ijkl qrst opab cduv wxef ghmn addr = (!!(r & 0x002000) << 23) | (!!(r & 0x001000) << 22) | (!!(r & 0x000800) << 21) | (!!(r & 0x000400) << 20) | (!!(r & 0x000020) << 19) | (!!(r & 0x000010) << 18) | @@ -47,10 +47,10 @@ string t, part; } bool Cheat::encode(char *str, uint32 addr, uint8 data, uint8 type) { - if(type == CT_PRO_ACTION_REPLAY) { + if(type == ProActionReplay) { sprintf(str, "%0.6x:%0.2x", addr, data); return true; - } else if(type == CT_GAME_GENIE) { + } else if(type == GameGenie) { uint32 r = addr; addr = (!!(r & 0x008000) << 23) | (!!(r & 0x004000) << 22) | (!!(r & 0x002000) << 21) | (!!(r & 0x001000) << 20) | @@ -79,9 +79,9 @@ bool Cheat::encode(char *str, uint32 addr, uint8 data, uint8 type) { *****/ uint Cheat::mirror_address(uint addr) { - if((addr & 0x40e000) != 0x0000)return addr; -//8k WRAM mirror -//$[00-3f|80-bf]:[0000-1fff] -> $7e:[0000-1fff] + if((addr & 0x40e000) != 0x0000) return addr; + //8k WRAM mirror + //$[00-3f|80-bf]:[0000-1fff] -> $7e:[0000-1fff] return (0x7e0000 + (addr & 0x1fff)); } @@ -90,8 +90,8 @@ void Cheat::set(uint32 addr) { mask[addr >> 3] |= 1 << (addr & 7); if((addr & 0xffe000) == 0x7e0000) { - //mirror $7e:[0000-1fff] to $[00-3f|80-bf]:[0000-1fff] - uint mirror; + //mirror $7e:[0000-1fff] to $[00-3f|80-bf]:[0000-1fff] + uint mirror; for(int x = 0; x <= 0x3f; x++) { mirror = ((0x00 + x) << 16) + (addr & 0x1fff); mask[mirror >> 3] |= 1 << (mirror & 7); @@ -104,16 +104,16 @@ void Cheat::set(uint32 addr) { void Cheat::clear(uint32 addr) { addr = mirror_address(addr); -//is there more than one cheat code using the same address -//(and likely a different override value) that is enabled? -//if so, do not clear code lookup table entry for this address. -uint8 r; + //is there more than one cheat code using the same address + //(and likely a different override value) that is enabled? + //if so, do not clear code lookup table entry for this address. + uint8 r; if(read(addr, r) == true)return; mask[addr >> 3] &= ~(1 << (addr & 7)); if((addr & 0xffe000) == 0x7e0000) { - //mirror $7e:[0000-1fff] to $[00-3f|80-bf]:[0000-1fff] - uint mirror; + //mirror $7e:[0000-1fff] to $[00-3f|80-bf]:[0000-1fff] + uint mirror; for(int x = 0; x <= 0x3f; x++) { mirror = ((0x00 + x) << 16) + (addr & 0x1fff); mask[mirror >> 3] &= ~(1 << (mirror & 7)); @@ -133,13 +133,13 @@ uint8 r; bool Cheat::read(uint32 addr, uint8 &data) { addr = mirror_address(addr); for(int i = 0; i < cheat_count; i++) { - if(enabled(i) == false)continue; + if(enabled(i) == false) continue; if(addr == mirror_address(index[i].addr)) { data = index[i].data; return true; } } -//code not found, or code is disabled + //code not found, or code is disabled return false; } @@ -147,9 +147,10 @@ bool Cheat::read(uint32 addr, uint8 &data) { * update_cheat_status() will scan to see if any codes are * enabled. if any are, make sure the cheat system is on. * otherwise, turn cheat system off to speed up emulation. - *****/ + *****/ + void Cheat::update_cheat_status() { - for(int i = 0; i < cheat_count; i++) { + for(unsigned i = 0; i < cheat_count; i++) { if(index[i].enabled) { cheat_enabled = true; return; @@ -163,11 +164,11 @@ void Cheat::update_cheat_status() { *****/ bool Cheat::add(bool enable, char *code, char *desc) { - if(cheat_count >= CHEAT_LIMIT)return false; + if(cheat_count >= CheatLimit) return false; -uint32 addr, len; -uint8 data, type; - if(decode(code, addr, data, type) == false)return false; + uint32 addr, len; + uint8 data, type; + if(decode(code, addr, data, type) == false) return false; index[cheat_count].enabled = enable; index[cheat_count].addr = addr; @@ -188,17 +189,17 @@ uint8 data, type; } bool Cheat::edit(uint32 n, bool enable, char *code, char *desc) { - if(n >= cheat_count)return false; + if(n >= cheat_count) return false; -uint32 addr, len; -uint8 data, type; - if(decode(code, addr, data, type) == false)return false; + uint32 addr, len; + uint8 data, type; + if(decode(code, addr, data, type) == false) return false; -//disable current code and clear from code lookup table + //disable current code and clear from code lookup table index[n].enabled = false; clear(index[n].addr); -//update code and enable in code lookup table + //update code and enable in code lookup table index[n].enabled = enable; index[n].addr = addr; index[n].data = data; @@ -217,9 +218,9 @@ uint8 data, type; } bool Cheat::remove(uint32 n) { - if(n >= cheat_count)return false; + if(n >= cheat_count) return false; - for(int i = n; i < cheat_count; i++) { + for(unsigned i = n; i < cheat_count; i++) { index[i].enabled = index[i + 1].enabled; index[i].addr = index[i + 1].addr; index[i].data = index[i + 1].data; @@ -233,7 +234,7 @@ bool Cheat::remove(uint32 n) { } bool Cheat::get(uint32 n, bool &enable, uint32 &addr, uint8 &data, char *code, char *desc) { - if(n >= cheat_count)return false; + if(n >= cheat_count) return false; enable = index[n].enabled; addr = index[n].addr; data = index[n].data; @@ -247,19 +248,19 @@ bool Cheat::get(uint32 n, bool &enable, uint32 &addr, uint8 &data, char *code, c *****/ bool Cheat::enabled(uint32 n) { - if(n >= cheat_count)return false; + if(n >= cheat_count) return false; return index[n].enabled; } void Cheat::enable(uint32 n) { - if(n >= cheat_count)return; + if(n >= cheat_count) return; index[n].enabled = true; set(index[n].addr); update_cheat_status(); } void Cheat::disable(uint32 n) { - if(n >= cheat_count)return; + if(n >= cheat_count) return; index[n].enabled = false; clear(index[n].addr); update_cheat_status(); @@ -274,16 +275,16 @@ void Cheat::disable(uint32 n) { /* ... */ bool Cheat::load(const char *fn) { -string data; + string data; if(!fread(data, fn)) return false; replace(data, "\r\n", "\n"); qreplace(data, "=", ","); qreplace(data, " ", ""); -lstring line; + lstring line; split(line, "\n", data); - for(int i = 0; i < ::count(line); i++) { - lstring part; + for(unsigned i = 0; i < ::count(line); i++) { + lstring part; split(part, ",", line[i]); if(::count(part) != 3) continue; trim(part[2], "\""); @@ -294,9 +295,9 @@ lstring line; } bool Cheat::save(const char *fn) { -FILE *fp = fopen(fn, "wb"); + FILE *fp = fopen(fn, "wb"); if(!fp) return false; - for(int i = 0; i < cheat_count; i++) { + for(unsigned i = 0; i < cheat_count; i++) { fprintf(fp, "%9s = %8s, \"%s\"\r\n", index[i].code, index[i].enabled ? "enabled" : "disabled", @@ -314,7 +315,7 @@ void Cheat::clear() { cheat_enabled = false; cheat_count = 0; memset(mask, 0, 0x200000); - for(int i = 0; i <= CHEAT_LIMIT; i++) { + for(unsigned i = 0; i <= CheatLimit; i++) { index[i].enabled = false; index[i].addr = 0x000000; index[i].data = 0x00; diff --git a/src/cheat/cheat.h b/src/cheat/cheat.h index 58041537..74ead8ad 100644 --- a/src/cheat/cheat.h +++ b/src/cheat/cheat.h @@ -1,51 +1,51 @@ -#define CHEAT_LIMIT 1024 - class Cheat { -public: - enum { - CT_PRO_ACTION_REPLAY, - CT_GAME_GENIE +public: + enum { CheatLimit = 1024 }; + + enum Type { + ProActionReplay, + GameGenie, }; struct CheatIndex { - bool enabled; + bool enabled; uint32 addr; - uint8 data; - char code[ 16 + 1]; - char desc[128 + 1]; - } index[CHEAT_LIMIT + 1]; + uint8 data; + char code[ 16 + 1]; + char desc[128 + 1]; + } index[CheatLimit + 1]; - bool cheat_enabled; + bool cheat_enabled; uint32 cheat_count; - uint8 mask[0x200000]; + uint8 mask[0x200000]; inline bool enabled() { return cheat_enabled; } inline uint count() { return cheat_count; } inline bool exists(uint32 addr) { return bool(mask[addr >> 3] & 1 << (addr & 7)); } - bool decode(char *str, uint32 &addr, uint8 &data, uint8 &type); - bool encode(char *str, uint32 addr, uint8 data, uint8 type); + bool decode(char *str, uint32 &addr, uint8 &data, uint8 &type); + bool encode(char *str, uint32 addr, uint8 data, uint8 type); - bool read(uint32 addr, uint8 &data); + bool read(uint32 addr, uint8 &data); - void update_cheat_status(); - bool add(bool enable, char *code, char *desc); - bool edit(uint32 n, bool enable, char *code, char *desc); - bool get(uint32 n, bool &enable, uint32 &addr, uint8 &data, char *code, char *desc); - bool remove (uint32 n); - bool enabled(uint32 n); - void enable (uint32 n); - void disable(uint32 n); - bool load(const char *fn); - bool save(const char *fn); - void clear(); + void update_cheat_status(); + bool add(bool enable, char *code, char *desc); + bool edit(uint32 n, bool enable, char *code, char *desc); + bool get(uint32 n, bool &enable, uint32 &addr, uint8 &data, char *code, char *desc); + bool remove(uint32 n); + bool enabled(uint32 n); + void enable(uint32 n); + void disable(uint32 n); + bool load(const char *fn); + bool save(const char *fn); + void clear(); Cheat(); private: - uint mirror_address(uint addr); - void set(uint32 addr); - void clear(uint32 addr); + uint mirror_address(uint addr); + void set(uint32 addr); + void clear(uint32 addr); }; extern Cheat cheat; diff --git a/src/chip/bsx/bsx.cpp b/src/chip/bsx/bsx.cpp index e4f907fe..1b30ace5 100644 --- a/src/chip/bsx/bsx.cpp +++ b/src/chip/bsx/bsx.cpp @@ -1,4 +1,5 @@ -#include "../../base.h" +#include "../../base.h" +#define BSX_CPP #include "bsx_base.cpp" #include "bsx_cart.cpp" diff --git a/src/chip/bsx/bsx_base.cpp b/src/chip/bsx/bsx_base.cpp index 6c414935..090226b6 100644 --- a/src/chip/bsx/bsx_base.cpp +++ b/src/chip/bsx/bsx_base.cpp @@ -1,3 +1,5 @@ +#ifdef BSX_CPP + void BSXBase::init() { } @@ -131,3 +133,5 @@ void BSXBase::mmio_write(uint addr, uint8 data) { } break; } } + +#endif //ifdef BSX_CPP diff --git a/src/chip/bsx/bsx_cart.cpp b/src/chip/bsx/bsx_cart.cpp index e02ebd4f..60e84e3b 100644 --- a/src/chip/bsx/bsx_cart.cpp +++ b/src/chip/bsx/bsx_cart.cpp @@ -1,3 +1,5 @@ +#ifdef BSX_CPP + void BSXCart::init() { } @@ -93,3 +95,5 @@ BSXCart::~BSXCart() { safe_free(sram_data); safe_free(psram_data); } + +#endif //ifdef BSX_CPP diff --git a/src/chip/bsx/bsx_flash.cpp b/src/chip/bsx/bsx_flash.cpp index e89e0191..43f925fd 100644 --- a/src/chip/bsx/bsx_flash.cpp +++ b/src/chip/bsx/bsx_flash.cpp @@ -1,3 +1,5 @@ +#ifdef BSX_CPP + void BSXFlash::init() {} void BSXFlash::enable() {} @@ -107,3 +109,5 @@ void BSXFlash::write(uint addr, uint8 data) { } } } + +#endif //ifdef BSX_CPP diff --git a/src/chip/cx4/cx4.cpp b/src/chip/cx4/cx4.cpp index efe96f77..a0f32358 100644 --- a/src/chip/cx4/cx4.cpp +++ b/src/chip/cx4/cx4.cpp @@ -5,7 +5,8 @@ Portions (c) anomie, Overload, zsKnight, Nach, byuu */ -#include "../../base.h" +#include "../../base.h" +#define CX4_CPP #include "cx4data.cpp" #include "cx4fn.cpp" diff --git a/src/chip/cx4/cx4data.cpp b/src/chip/cx4/cx4data.cpp index 57bc48e7..0308c833 100644 --- a/src/chip/cx4/cx4data.cpp +++ b/src/chip/cx4/cx4data.cpp @@ -1,3 +1,5 @@ +#ifdef CX4_CPP + const uint8 Cx4::immediate_data[48] = { 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x80, 0xff, 0xff, 0x7f, @@ -181,3 +183,5 @@ const int16 Cx4::CosTable[512] = { 32138, 32214, 32285, 32351, 32413, 32469, 32521, 32568, 32610, 32647, 32679, 32706, 32728, 32745, 32758, 32765 }; + +#endif //ifdef CX4_CPP diff --git a/src/chip/cx4/cx4fn.cpp b/src/chip/cx4/cx4fn.cpp index ec358b51..ca0f8dd7 100644 --- a/src/chip/cx4/cx4fn.cpp +++ b/src/chip/cx4/cx4fn.cpp @@ -1,3 +1,5 @@ +#ifdef CX4_CPP + #include #define Tan(a) (CosTable[a] ? ((((int32)SinTable[a]) << 16) / CosTable[a]) : 0x80000000) #define sar(b, n) ((b) >> (n)) @@ -240,3 +242,5 @@ uint8 bit = 0x80; LineY += D; } } + +#endif //ifdef CX4_CPP diff --git a/src/chip/cx4/cx4oam.cpp b/src/chip/cx4/cx4oam.cpp index feb45b63..6173cb05 100644 --- a/src/chip/cx4/cx4oam.cpp +++ b/src/chip/cx4/cx4oam.cpp @@ -1,3 +1,5 @@ +#ifdef CX4_CPP + //Build OAM void Cx4::op00_00() { uint32 oamptr = ram[0x626] << 2; @@ -217,3 +219,5 @@ uint16 mask2 = 0x3f3f; destptr += 16; } } + +#endif //ifdef CX4_CPP diff --git a/src/chip/cx4/cx4ops.cpp b/src/chip/cx4/cx4ops.cpp index 6af839ee..8cc509ee 100644 --- a/src/chip/cx4/cx4ops.cpp +++ b/src/chip/cx4/cx4ops.cpp @@ -1,3 +1,5 @@ +#ifdef CX4_CPP + //Sprite Functions void Cx4::op00() { switch(reg[0x4d]) { @@ -220,3 +222,5 @@ void Cx4::op89() { str(0, 0x054336); str(1, 0xffffff); } + +#endif //ifdef CX4_CPP diff --git a/src/chip/dsp1/dsp1.cpp b/src/chip/dsp1/dsp1.cpp index da437768..aa375106 100644 --- a/src/chip/dsp1/dsp1.cpp +++ b/src/chip/dsp1/dsp1.cpp @@ -1,4 +1,5 @@ -#include "../../base.h" +#include "../../base.h" +#define DSP1_CPP #include "dsp1emu.cpp" diff --git a/src/chip/dsp1/dsp1emu.cpp b/src/chip/dsp1/dsp1emu.cpp index 61bd4428..58dd2dea 100644 --- a/src/chip/dsp1/dsp1emu.cpp +++ b/src/chip/dsp1/dsp1emu.cpp @@ -1,3 +1,5 @@ +#ifdef DSP1_CPP + // DSP-1's emulation code // // Based on research by Overload, The Dumper, Neviksti and Andreas Naive @@ -1620,3 +1622,4 @@ const int16 Dsp1::SinTable[256] = { ////////////////////////////////////////////////////////////////// +#endif //ifdef DSP1_CPP diff --git a/src/chip/dsp2/dsp2.cpp b/src/chip/dsp2/dsp2.cpp index e8bc6679..54c0a252 100644 --- a/src/chip/dsp2/dsp2.cpp +++ b/src/chip/dsp2/dsp2.cpp @@ -1,4 +1,5 @@ -#include "../../base.h" +#include "../../base.h" +#define DSP2_CPP #include "dsp2_op.cpp" diff --git a/src/chip/dsp2/dsp2_op.cpp b/src/chip/dsp2/dsp2_op.cpp index 15a51529..fec67bd6 100644 --- a/src/chip/dsp2/dsp2_op.cpp +++ b/src/chip/dsp2/dsp2_op.cpp @@ -1,3 +1,5 @@ +#ifdef DSP2_CPP + //convert bitmap to bitplane tile void DSP2::op01() { //op01 size is always 32 bytes input and output @@ -171,3 +173,5 @@ uint8 pixelarray[512]; status.output[i] = (pixelarray[i << 1] << 4) | pixelarray[(i << 1) + 1]; } } + +#endif //ifdef DSP2_CPP diff --git a/src/chip/dsp3/dsp3.cpp b/src/chip/dsp3/dsp3.cpp index 139b1b7d..28d7c0a9 100644 --- a/src/chip/dsp3/dsp3.cpp +++ b/src/chip/dsp3/dsp3.cpp @@ -1,4 +1,5 @@ -#include "../../base.h" +#include "../../base.h" +#define DSP3_CPP namespace DSP3i { #define bool8 uint8 diff --git a/src/chip/dsp3/dsp3emu.c b/src/chip/dsp3/dsp3emu.c index ab557718..7ce0d81e 100644 --- a/src/chip/dsp3/dsp3emu.c +++ b/src/chip/dsp3/dsp3emu.c @@ -1,3 +1,5 @@ +#ifdef DSP3_CPP + //DSP-3 emulator code //Copyright (c) 2003-2006 John Weidman, Kris Bleakley, Lancer, z80 gaiden @@ -1140,3 +1142,5 @@ void InitDSP3() { DSP3_Reset(); } + +#endif //ifdef DSP3_CPP diff --git a/src/chip/dsp4/dsp4.cpp b/src/chip/dsp4/dsp4.cpp index a27bf8d3..3f653571 100644 --- a/src/chip/dsp4/dsp4.cpp +++ b/src/chip/dsp4/dsp4.cpp @@ -1,4 +1,5 @@ -#include "../../base.h" +#include "../../base.h" +#define DSP4_CPP namespace DSP4i { inline uint16 READ_WORD(uint8 *addr) { diff --git a/src/chip/dsp4/dsp4emu.c b/src/chip/dsp4/dsp4emu.c index 668f5867..50785775 100644 --- a/src/chip/dsp4/dsp4emu.c +++ b/src/chip/dsp4/dsp4emu.c @@ -1,3 +1,5 @@ +#ifdef DSP4_CPP + //DSP-4 emulator code //Copyright (c) 2004-2006 Dreamer Nom, John Weidman, Kris Bleakley, Nach, z80 gaiden @@ -2144,3 +2146,5 @@ void DSP4GetByte() dsp4_byte = 0xff; } } + +#endif //ifdef DSP4_CPP diff --git a/src/chip/sdd1/sdd1.cpp b/src/chip/sdd1/sdd1.cpp index 3e345ea4..b69d022a 100644 --- a/src/chip/sdd1/sdd1.cpp +++ b/src/chip/sdd1/sdd1.cpp @@ -1,4 +1,5 @@ -#include "../../base.h" +#include "../../base.h" +#define SDD1_CPP #include "sdd1emu.cpp" diff --git a/src/chip/sdd1/sdd1emu.cpp b/src/chip/sdd1/sdd1emu.cpp index 5f53fa7a..30f821c9 100644 --- a/src/chip/sdd1/sdd1emu.cpp +++ b/src/chip/sdd1/sdd1emu.cpp @@ -1,3 +1,5 @@ +#ifdef SDD1_CPP + /************************************************************************ S-DD1'algorithm emulation code @@ -445,3 +447,5 @@ SDD1emu::SDD1emu() : } /////////////////////////////////////////////////////////// + +#endif //ifdef SDD1_CPP diff --git a/src/chip/st010/st010.cpp b/src/chip/st010/st010.cpp index d8b956de..38a5e1c7 100644 --- a/src/chip/st010/st010.cpp +++ b/src/chip/st010/st010.cpp @@ -1,4 +1,6 @@ -#include "../../base.h" +#include "../../base.h" +#define ST010_CPP + #include "st010_data.h" #include "st010_op.cpp" diff --git a/src/chip/st010/st010_op.cpp b/src/chip/st010/st010_op.cpp index 4219edb6..1eb9c55c 100644 --- a/src/chip/st010/st010_op.cpp +++ b/src/chip/st010/st010_op.cpp @@ -1,3 +1,5 @@ +#ifdef ST010_CPP + //ST-010 emulation code - Copyright (C) 2003 The Dumper, Matthew Kendora, Overload, Feather //bsnes port - Copyright (C) 2007 byuu @@ -255,3 +257,5 @@ int16 x1, y1; writew(0x0010, x1); writew(0x0012, y1); } + +#endif //ifdef ST010_CPP diff --git a/src/config/config.cpp b/src/config/config.cpp index a48a8445..4eb9b8e1 100644 --- a/src/config/config.cpp +++ b/src/config/config.cpp @@ -1,28 +1,36 @@ namespace config { configuration& config() { -static configuration config; + static configuration config; return config; -} +} + +integral_setting File::autodetect_type(config(), "file.autodetect_type", + "Auto-detect file type by inspecting file header, rather than by file extension.\n" + "In other words, if a .zip file is renamed to .smc, it will still be correctly\n" + "identified as a .zip file. However, there is an infinitesimal (1:~500,000,000)\n" + "chance of a false detection when loading an uncompressed image file, if this\n" + "option is enabled.", + integral_setting::boolean, false); string file_updatepath(const char *req_file, const char *req_path) { -string file(req_file); + string file(req_file); replace(file, "\\", "/"); if(!req_path || strlen(req_path) == 0) { return file; } -string path(req_path); + string path(req_path); replace(path, "\\", "/"); if(!strend(path, "/")) { strcat(path, "/"); } if(strbegin(path, "./")) { ltrim(path(), "./"); - string temp; + string temp; strcpy(temp, config::path.base); strcat(temp, path); strcpy(path, temp); } -lstring part; + lstring part; split(part, "/", file); strcat(path, part[count(part) - 1]); return path; @@ -32,35 +40,17 @@ string_setting Path::base("path.base", "Path that bsnes resides in", ""); string_setting Path::rom(config(), "path.rom", "Default path to look for ROM files in (\"\" = use default directory)", ""); -string_setting Path::save(config(), "path.save", - "Default path for all save RAM and cheat files (\"\" = use current directory)", ""); +string_setting Path::save(config(), "path.save", + "Default path for all save RAM files (\"\" = use current directory)", ""); +string_setting Path::cheat(config(), "path.cheat", + "Default path for all cheat files (\"\" = use current directory)", ""); string_setting Path::bsx(config(), "path.bsx", "", ""); string_setting Path::st(config(), "path.st", "", ""); -integral_setting SNES::gamma_ramp(config(), "snes.colorfilter.gamma_ramp", - "Use precalculated TV-style gamma ramp", integral_setting::boolean, true); -integral_setting SNES::sepia(config(), "snes.colorfilter.sepia", - "Convert color to sepia tone", integral_setting::boolean, false); -integral_setting SNES::grayscale(config(), "snes.colorfilter.grayscale", - "Convert color to grayscale tone", integral_setting::boolean, false); -integral_setting SNES::invert(config(), "snes.colorfilter.invert", - "Invert output image colors", integral_setting::boolean, false); -integral_setting SNES::contrast(config(), "snes.colorfilter.contrast", - "Contrast", integral_setting::decimal, 0); -integral_setting SNES::brightness(config(), "snes.colorfilter.brightness", - "Brightness", integral_setting::decimal, 0); -integral_setting SNES::gamma(config(), "snes.colorfilter.gamma", - "Gamma", integral_setting::decimal, 80); - -integral_setting SNES::ntsc_merge_fields(config(), "snes.ntsc_merge_fields", - "Merge fields in NTSC video filter\n" - "Set to true if using filter at any refresh rate other than 60hz\n" - "", integral_setting::boolean, true); - integral_setting SNES::controller_port0(config(), "snes.controller_port_1", - "Controller attached to SNES port 1", integral_setting::decimal, ::SNES::DEVICEID_JOYPAD1); + "Controller attached to SNES port 1", integral_setting::decimal, ::SNES::Input::DeviceIDJoypad1); integral_setting SNES::controller_port1(config(), "snes.controller_port_2", - "Controller attached to SNES port 2", integral_setting::decimal, ::SNES::DEVICEID_JOYPAD2); + "Controller attached to SNES port 2", integral_setting::decimal, ::SNES::Input::DeviceIDJoypad2); integral_setting CPU::ntsc_clock_rate(config(), "cpu.ntsc_clock_rate", "NTSC S-CPU clock rate (in hz)", integral_setting::decimal, 21477272); @@ -126,4 +116,4 @@ integral_setting PPU::oam_pri1_enable("ppu.oam_pri1_enable", "Enable OAM Priorit integral_setting PPU::oam_pri2_enable("ppu.oam_pri2_enable", "Enable OAM Priority 2", integral_setting::boolean, true); integral_setting PPU::oam_pri3_enable("ppu.oam_pri3_enable", "Enable OAM Priority 3", integral_setting::boolean, true); -}; +} //namespace config diff --git a/src/config/config.h b/src/config/config.h index 7e44de36..9cfcea63 100644 --- a/src/config/config.h +++ b/src/config/config.h @@ -2,16 +2,18 @@ namespace config { extern configuration& config(); -string file_updatepath(const char *, const char *); +string file_updatepath(const char*, const char*); + +extern struct File { + static integral_setting autodetect_type; +} file; extern struct Path { - static string_setting base, rom, save; + static string_setting base, rom, save, cheat; static string_setting bsx, st; } path; extern struct SNES { - static integral_setting gamma_ramp, sepia, grayscale, invert, contrast, brightness, gamma; - static integral_setting ntsc_merge_fields; static integral_setting controller_port0; static integral_setting controller_port1; } snes; diff --git a/src/cpu/cpu.cpp b/src/cpu/cpu.cpp index f1a85146..0122057a 100644 --- a/src/cpu/cpu.cpp +++ b/src/cpu/cpu.cpp @@ -1,4 +1,6 @@ -#include "../base.h" +#include "../base.h" +#define CPU_CPP + #include "dcpu.cpp" CPU::CPU() { diff --git a/src/cpu/cpu.h b/src/cpu/cpu.h index ae8fa24e..18b24640 100644 --- a/src/cpu/cpu.h +++ b/src/cpu/cpu.h @@ -4,81 +4,75 @@ class CPU : public MMIO { public: virtual void enter() = 0; -public: -//CPU version number -//* 1 and 2 are known -//* reported by $4210 -//* affects DRAM refresh behavior -uint8 cpu_version; + //CPU version number + //* 1 and 2 are known + //* reported by $4210 + //* affects DRAM refresh behavior + uint8 cpu_version; -//timing + //timing virtual uint16 vcounter() = 0; virtual uint16 hcounter() = 0; - virtual uint16 hclock() = 0; - virtual bool interlace() = 0; - virtual bool interlace_field() = 0; - virtual bool overscan() = 0; - virtual uint16 region_scanlines() = 0; - virtual void set_interlace(bool r) = 0; - virtual void set_overscan (bool r) = 0; - -CPURegs regs; - virtual uint8 port_read (uint8 port) = 0; - virtual void port_write(uint8 port, uint8 value) = 0; + virtual uint16 hdot() = 0; + virtual uint8 pio_status() = 0; + virtual uint8 port_read(uint8 port) = 0; + virtual void port_write(uint8 port, uint8 value) = 0; + + CPURegs regs; enum { FLAG_N = 0x80, FLAG_V = 0x40, FLAG_M = 0x20, FLAG_X = 0x10, FLAG_D = 0x08, FLAG_I = 0x04, FLAG_Z = 0x02, FLAG_C = 0x01 }; - virtual uint8 pio_status() = 0; - virtual void scanline() = 0; - virtual void frame() = 0; - virtual void power() = 0; - virtual void reset() = 0; + + virtual void scanline() = 0; + virtual void frame() = 0; + virtual void power() = 0; + virtual void reset() = 0; -/***** - * in opcode-based CPU emulators, the main emulation routine - * will only be able to call the disassemble_opcode() function - * on clean opcode edges. but with cycle-based CPU emulators, - * the CPU may be in the middle of executing an opcode when the - * emulator (e.g. debugger) wants to disassemble an opcode. this - * would mean that important registers may not reflect what they - * did at the start of the opcode (especially regs.pc), so in - * cycle-based emulators, this function should be overridden to - * reflect whether or not an opcode has only been partially - * executed. if not, the debugger should abort attempts to skip, - * disable, or disassemble the current opcode. - *****/ + /***** + * in opcode-based CPU emulators, the main emulation routine + * will only be able to call the disassemble_opcode() function + * on clean opcode edges. but with cycle-based CPU emulators, + * the CPU may be in the middle of executing an opcode when the + * emulator (e.g. debugger) wants to disassemble an opcode. this + * would mean that important registers may not reflect what they + * did at the start of the opcode (especially regs.pc), so in + * cycle-based emulators, this function should be overridden to + * reflect whether or not an opcode has only been partially + * executed. if not, the debugger should abort attempts to skip, + * disable, or disassemble the current opcode. + *****/ virtual bool in_opcode() { return false; } -/***** - * opcode disassembler - *****/ -enum { - OPTYPE_DP = 0, //dp - OPTYPE_DPX, //dp,x - OPTYPE_DPY, //dp,y - OPTYPE_IDP, //(dp) - OPTYPE_IDPX, //(dp,x) - OPTYPE_IDPY, //(dp),y - OPTYPE_ILDP, //[dp] - OPTYPE_ILDPY, //[dp],y - OPTYPE_ADDR, //addr - OPTYPE_ADDRX, //addr,x - OPTYPE_ADDRY, //addr,y - OPTYPE_IADDRX, //(addr,x) - OPTYPE_ILADDR, //[addr] - OPTYPE_LONG, //long - OPTYPE_LONGX, //long, x - OPTYPE_SR, //sr,s - OPTYPE_ISRY, //(sr,s),y - OPTYPE_ADDR_PC, //pbr:addr - OPTYPE_IADDR_PC, //pbr:(addr) - OPTYPE_RELB, //relb - OPTYPE_RELW, //relw -}; + /***** + * opcode disassembler + *****/ + enum { + OPTYPE_DP = 0, //dp + OPTYPE_DPX, //dp,x + OPTYPE_DPY, //dp,y + OPTYPE_IDP, //(dp) + OPTYPE_IDPX, //(dp,x) + OPTYPE_IDPY, //(dp),y + OPTYPE_ILDP, //[dp] + OPTYPE_ILDPY, //[dp],y + OPTYPE_ADDR, //addr + OPTYPE_ADDRX, //addr,x + OPTYPE_ADDRY, //addr,y + OPTYPE_IADDRX, //(addr,x) + OPTYPE_ILADDR, //[addr] + OPTYPE_LONG, //long + OPTYPE_LONGX, //long, x + OPTYPE_SR, //sr,s + OPTYPE_ISRY, //(sr,s),y + OPTYPE_ADDR_PC, //pbr:addr + OPTYPE_IADDR_PC, //pbr:(addr) + OPTYPE_RELB, //relb + OPTYPE_RELW, //relw + }; void disassemble_opcode(char *output); uint8 dreadb(uint32 addr); diff --git a/src/cpu/cpuregs.h b/src/cpu/cpuregs.h index 5b502044..4c6195ee 100644 --- a/src/cpu/cpuregs.h +++ b/src/cpu/cpuregs.h @@ -1,11 +1,11 @@ class CPURegFlags { public: -union { - uint8 data; - struct { - bool order_msb8(n:1, v:1, m:1, x:1, d:1, i:1, z:1, c:1); + union { + uint8 data; + struct { + bool order_msb8(n:1, v:1, m:1, x:1, d:1, i:1, z:1, c:1); + }; }; -}; inline operator unsigned() const { return data; } template inline unsigned operator = (const T i) { data = i; return data; } @@ -18,10 +18,10 @@ union { class CPUReg16 { public: -union { - uint16 w; - struct { uint8 order_lsb2(l, h); }; -}; + union { + uint16 w; + struct { uint8 order_lsb2(l, h); }; + }; inline operator unsigned() const { return w; } template inline unsigned operator = (const T i) { w = i; return w; } @@ -41,11 +41,11 @@ union { class CPUReg24 { public: -union { - uint32 d; - struct { uint16 order_lsb2(w, wh); }; - struct { uint8 order_lsb4(l, h, b, bh); }; -}; + union { + uint32 d; + struct { uint16 order_lsb2(w, wh); }; + struct { uint8 order_lsb4(l, h, b, bh); }; + }; inline operator unsigned() const { return d; } template inline unsigned operator = (const T i) { d = uclip<24>(i); return d; } @@ -65,11 +65,11 @@ union { class CPURegs { public: -CPUReg24 pc; -CPUReg16 a, x, y, s, d; -CPURegFlags p; -uint8 db; -uint8 mdr; -bool e; + CPUReg24 pc; + CPUReg16 a, x, y, s, d; + CPURegFlags p; + uint8 db; + uint8 mdr; + bool e; CPURegs() : db(0), mdr(0x00), e(false) {} }; diff --git a/src/cpu/dcpu.cpp b/src/cpu/dcpu.cpp index 8d781bbc..98c4b38c 100644 --- a/src/cpu/dcpu.cpp +++ b/src/cpu/dcpu.cpp @@ -1,3 +1,5 @@ +#ifdef CPU_CPP + uint8 CPU::dreadb(uint32 addr) { if((addr & 0x40ffff) >= 0x2000 && (addr & 0x40ffff) <= 0x5fff) { //$[00-3f|80-bf]:[2000-5fff] @@ -423,7 +425,7 @@ uint8 op2 = dreadb(pc.d); strcat(s, t); strcat(s, " "); - sprintf(t, "V:%3d H:%4d", vcounter(), hclock()); + sprintf(t, "V:%3d H:%4d", vcounter(), hcounter()); strcat(s, t); } @@ -473,3 +475,5 @@ static uint8 op_len_tbl[256] = { if(len == 6)return (regs.e || regs.p.x) ? 2 : 3; return len; } + +#endif //ifdef CPU_CPP diff --git a/src/cpu/scpu/core/core.cpp b/src/cpu/scpu/core/core.cpp index aea39dd1..bba72e9a 100644 --- a/src/cpu/scpu/core/core.cpp +++ b/src/cpu/scpu/core/core.cpp @@ -1,3 +1,5 @@ +#ifdef SCPU_CPP + #include "opfn.cpp" #include "op_read.cpp" @@ -43,8 +45,6 @@ void sCPU::op_irq() { regs.pc.w = rd.w; } -// - alwaysinline void sCPU::op_io_cond2() { if(regs.d.l != 0x00) { op_io(); @@ -62,3 +62,5 @@ alwaysinline void sCPU::op_io_cond6(uint16 addr) { op_io(); } } + +#endif //ifdef SCPU_CPP diff --git a/src/cpu/scpu/core/core.h b/src/cpu/scpu/core/core.h index 9097197f..1ab5f209 100644 --- a/src/cpu/scpu/core/core.h +++ b/src/cpu/scpu/core/core.h @@ -1,57 +1,57 @@ -void (sCPU::*optbl[256])(); + void (sCPU::*optbl[256])(); -CPUReg24 aa, rd; -uint8 dp, sp; + CPUReg24 aa, rd; + uint8_t dp, sp; - void op_irq(); + void op_irq(); inline bool in_opcode() { return status.in_opcode; } -//op_read - void op_adc_b(); - void op_adc_w(); - void op_and_b(); - void op_and_w(); - void op_bit_b(); - void op_bit_w(); - void op_cmp_b(); - void op_cmp_w(); - void op_cpx_b(); - void op_cpx_w(); - void op_cpy_b(); - void op_cpy_w(); - void op_eor_b(); - void op_eor_w(); - void op_lda_b(); - void op_lda_w(); - void op_ldx_b(); - void op_ldx_w(); - void op_ldy_b(); - void op_ldy_w(); - void op_ora_b(); - void op_ora_w(); - void op_sbc_b(); - void op_sbc_w(); -//op_rmw - void op_inc_b(); - void op_inc_w(); - void op_dec_b(); - void op_dec_w(); - void op_asl_b(); - void op_asl_w(); - void op_lsr_b(); - void op_lsr_w(); - void op_rol_b(); - void op_rol_w(); - void op_ror_b(); - void op_ror_w(); - void op_trb_b(); - void op_trb_w(); - void op_tsb_b(); - void op_tsb_w(); + //op_read + void op_adc_b(); + void op_adc_w(); + void op_and_b(); + void op_and_w(); + void op_bit_b(); + void op_bit_w(); + void op_cmp_b(); + void op_cmp_w(); + void op_cpx_b(); + void op_cpx_w(); + void op_cpy_b(); + void op_cpy_w(); + void op_eor_b(); + void op_eor_w(); + void op_lda_b(); + void op_lda_w(); + void op_ldx_b(); + void op_ldx_w(); + void op_ldy_b(); + void op_ldy_w(); + void op_ora_b(); + void op_ora_w(); + void op_sbc_b(); + void op_sbc_w(); + //op_rmw + void op_inc_b(); + void op_inc_w(); + void op_dec_b(); + void op_dec_w(); + void op_asl_b(); + void op_asl_w(); + void op_lsr_b(); + void op_lsr_w(); + void op_rol_b(); + void op_rol_w(); + void op_ror_b(); + void op_ror_w(); + void op_trb_b(); + void op_trb_w(); + void op_tsb_b(); + void op_tsb_w(); - void op_io_cond2(); - void op_io_cond4(uint16 x, uint16 y); - void op_io_cond6(uint16 addr); + void op_io_cond2(); + void op_io_cond4(uint16 x, uint16 y); + void op_io_cond6(uint16 addr); -#include "op.h" + #include "op.h" diff --git a/src/cpu/scpu/core/opfn.cpp b/src/cpu/scpu/core/opfn.cpp index 9527d983..23d9372f 100644 --- a/src/cpu/scpu/core/opfn.cpp +++ b/src/cpu/scpu/core/opfn.cpp @@ -1,3 +1,5 @@ +#ifdef SCPU_CPP + //op_read inline void sCPU::op_adc_b() { int32 r = regs.a.l + rd.l + regs.p.c; @@ -371,3 +373,5 @@ inline void sCPU::op_tsb_w() { regs.p.z = ((rd.w & regs.a.w) == 0); rd.w |= regs.a.w; } + +#endif //ifdef SCPU_CPP diff --git a/src/cpu/scpu/dma/dma.cpp b/src/cpu/scpu/dma/dma.cpp index f3cb27d6..c7697e12 100644 --- a/src/cpu/scpu/dma/dma.cpp +++ b/src/cpu/scpu/dma/dma.cpp @@ -1,273 +1,289 @@ -void sCPU::dma_add_clocks(uint clocks) { - status.dma_clocks += clocks; - add_clocks(clocks); -} - -/***** - * used by both DMA and HDMA - * - * DMA address bus A cannot read from or write to the following addresses : - * $[00-3f|80-bf]:43[00-7f] - * $[00-3f|80-bf]:420b - * $[00-3f|80-bf]:420c - * WRAM<>WRAM transfers via $2180 - *****/ -void sCPU::dma_transfer(bool direction, uint8 bbus, uint32 abus) { -uint8 r; - if(direction == 0) { //a->b - if((abus & 0x40ff00) == 0x2100 || (abus & 0x40ff80) == 0x4300 || - (abus & 0x40ffff) == 0x420b || (abus & 0x40ffff) == 0x420c) { - r = regs.mdr; - } else { - r = bus.read(abus); - } - bus.write(0x2100 | bbus, r); - } else { //b->a - if(bbus == 0x80 && ((abus & 0x7e0000) == 0x7e0000 || (abus & 0x40e000) == 0x0000)) { - //prevent WRAM->WRAM transfers - r = regs.mdr; - } else { - r = bus.read(0x2100 | bbus); - } - if((abus & 0x40ff00) == 0x2100 || (abus & 0x40ff80) == 0x4300 || - (abus & 0x40ffff) == 0x420b || (abus & 0x40ffff) == 0x420c)return; - bus.write(abus, r); - } - - dma_add_clocks(8); - cycle_edge(); -} - -/***** - * address calculation functions - *****/ - -uint8 sCPU::dma_bbus(uint8 i, uint8 index) { - switch(channel[i].xfermode) { - default: - case 0: return (channel[i].destaddr); break; //0 - case 1: return (channel[i].destaddr + (index & 1)); break; //0,1 - case 2: return (channel[i].destaddr); break; //0,0 - case 3: return (channel[i].destaddr + ((index >> 1) & 1)); break; //0,0,1,1 - case 4: return (channel[i].destaddr + (index & 3)); break; //0,1,2,3 - case 5: return (channel[i].destaddr + (index & 1)); break; //0,1,0,1 - case 6: return (channel[i].destaddr); break; //0,0 [2] - case 7: return (channel[i].destaddr + ((index >> 1) & 1)); break; //0,0,1,1 [3] - } -} - -inline uint32 sCPU::dma_addr(uint8 i) { -uint32 r = (channel[i].srcbank << 16) | (channel[i].srcaddr); - - if(channel[i].fixedxfer == false) { - if(channel[i].reversexfer == false) { - channel[i].srcaddr++; - } else { - channel[i].srcaddr--; - } - } - - return r; -} - -inline uint32 sCPU::hdma_addr(uint8 i) { - return (channel[i].srcbank << 16) | (channel[i].hdma_addr++); -} - -inline uint32 sCPU::hdma_iaddr(uint8 i) { - return (channel[i].hdma_ibank << 16) | (channel[i].hdma_iaddr++); -} - -/***** - * DMA functions - *****/ - -void sCPU::dma_transfertobusb(uint8 i, uint8 bbus) { - if(cartridge.info.sdd1 == true && sdd1.dma_active() == true) { - bus.write(0x2100 | bbus, sdd1.dma_read()); - } else { - dma_transfer(0, bbus, dma_addr(i)); - } - channel[i].xfersize--; -} - -void sCPU::dma_transfertobusa(uint8 i, uint8 bbus) { - dma_transfer(1, bbus, dma_addr(i)); - channel[i].xfersize--; -} - -inline void sCPU::dma_write(uint8 i, uint8 index) { -//cannot use dma_transfer() directly, due to current S-DD1 implementation - if(channel[i].direction == 0) { - dma_transfertobusb(i, index); - } else { - dma_transfertobusa(i, index); - } -} - -void sCPU::dma_run() { - for(int i = 0; i < 8; i++) { - if(channel[i].dma_enabled == false)continue; - dma_add_clocks(8); - - if(cartridge.info.sdd1 == true) { - sdd1.dma_begin(i, (channel[i].srcbank << 16) | (channel[i].srcaddr), channel[i].xfersize); - } - - if(tracer.enabled() == true && tracer.cpudma_enabled() == true) { - tprintf("[DMA] channel:%d direction:%s reverse:%c fixed:%c mode:%d b_addr:$21%0.2x " - "a_addr:$%0.2x%0.4x length:$%0.4x (%5d)", - i, channel[i].direction ? "b->a" : "a->b", channel[i].reversexfer ? '1' : '0', - channel[i].fixedxfer ? '1' : '0', channel[i].xfermode, channel[i].destaddr, - channel[i].srcbank, channel[i].srcaddr, - channel[i].xfersize, channel[i].xfersize ? channel[i].xfersize : 65536); - } - - uint index = 0; - do { - dma_write(i, dma_bbus(i, index++)); - } while(channel[i].dma_enabled && channel[i].xfersize); - - channel[i].dma_enabled = false; - } - - counter.set(counter.irq_delay, 2); -} - -/***** - * HDMA functions - *****/ - -inline bool sCPU::hdma_active(uint8 i) { - return (channel[i].hdma_enabled && !channel[i].hdma_completed); -} - -inline bool sCPU::hdma_active_after(uint8 i) { - for(int n = i + 1; n < 8; n++) { - if(hdma_active(n) == true) { return true; } - } - - return false; -} - -inline uint8 sCPU::hdma_enabled_channels() { -uint8 r = 0; - for(int i = 0; i < 8; i++) { - if(channel[i].hdma_enabled)r++; - } - return r; -} - -inline uint8 sCPU::hdma_active_channels() { -uint8 r = 0; - for(int i = 0; i < 8; i++) { - if(hdma_active(i) == true)r++; - } - return r; -} - -void sCPU::hdma_update(uint8 i) { - channel[i].hdma_line_counter = bus.read(hdma_addr(i)); - dma_add_clocks(8); - - channel[i].hdma_completed = (channel[i].hdma_line_counter == 0); - channel[i].hdma_do_transfer = !channel[i].hdma_completed; - - if(channel[i].hdma_indirect) { - channel[i].hdma_iaddr = bus.read(hdma_addr(i)) << 8; - dma_add_clocks(8); - - if(!channel[i].hdma_completed || hdma_active_after(i)) { - channel[i].hdma_iaddr >>= 8; - channel[i].hdma_iaddr |= bus.read(hdma_addr(i)) << 8; - dma_add_clocks(8); - } - } -} - -void sCPU::hdma_run() { -static uint8 hdma_xferlen[8] = { 1, 2, 2, 4, 4, 4, 2, 4 }; - for(int i = 0; i < 8; i++) { - if(hdma_active(i) == false)continue; - channel[i].dma_enabled = false; //HDMA run during DMA will stop DMA mid-transfer - dma_add_clocks(8); - - if(channel[i].hdma_do_transfer) { - int xferlen = hdma_xferlen[channel[i].xfermode]; - for(int index = 0; index < xferlen; index++) { - if(bool(config::cpu.hdma_enable) == true) { - dma_transfer(channel[i].direction, dma_bbus(i, index), - !channel[i].hdma_indirect ? hdma_addr(i) : hdma_iaddr(i)); - } else { - dma_add_clocks(8); - cycle_edge(); - } - } - } - - channel[i].hdma_line_counter--; - channel[i].hdma_do_transfer = bool(channel[i].hdma_line_counter & 0x80); - if((channel[i].hdma_line_counter & 0x7f) == 0) { - hdma_update(i); - } - } - - counter.set(counter.irq_delay, 2); -} - -void sCPU::hdma_init_reset() { - for(int i = 0; i < 8; i++) { - channel[i].hdma_completed = false; - channel[i].hdma_do_transfer = false; - } -} - -void sCPU::hdma_init() { - for(int i = 0; i < 8; i++) { - if(!channel[i].hdma_enabled)continue; - channel[i].dma_enabled = false; //HDMA init during DMA will stop DMA mid-transfer - - channel[i].hdma_addr = channel[i].srcaddr; - hdma_update(i); - } - - counter.set(counter.irq_delay, 2); -} - -/***** - * power / reset functions - *****/ - -void sCPU::dma_power() { - for(int i = 0; i < 8; i++) { - channel[i].dmap = 0xff; - channel[i].direction = 1; - channel[i].hdma_indirect = true; - channel[i].reversexfer = true; - channel[i].fixedxfer = true; - channel[i].xfermode = 7; - - channel[i].destaddr = 0xff; - - channel[i].srcaddr = 0xffff; - channel[i].srcbank = 0xff; - - channel[i].xfersize = 0xffff; - //channel[i].hdma_iaddr = 0xffff; //union with xfersize - channel[i].hdma_ibank = 0xff; - - channel[i].hdma_addr = 0xffff; - channel[i].hdma_line_counter = 0xff; - channel[i].unknown = 0xff; - } -} - -void sCPU::dma_reset() { - for(int i = 0; i < 8; i++) { - channel[i].dma_enabled = false; - channel[i].hdma_enabled = false; - - channel[i].hdma_completed = false; - channel[i].hdma_do_transfer = false; - } -} +#ifdef SCPU_CPP + +void sCPU::dma_add_clocks(uint clocks) { + status.dma_clocks += clocks; + add_clocks(clocks); +} + +/***** + * used by both DMA and HDMA + * + * DMA address bus A cannot read from or write to the following addresses : + * $[00-3f|80-bf]:43[00-7f] + * $[00-3f|80-bf]:420b + * $[00-3f|80-bf]:420c + * + * WRAM<>WRAM transfers via $2180 are also illegal + *****/ + +void sCPU::dma_transfer(bool direction, uint8 bbus, uint32 abus) { + if(direction == 0) { + //a->b transfer (to $21xx) + if(bbus == 0x80 && ((abus & 0xfe0000) == 0x7e0000 || (abus & 0x40e000) == 0x0000)) { + //illegal WRAM->WRAM transfer + //read most likely occurs; no write occurs + //read is irrelevant, as it has no observable effect on emulation + } else if((abus & 0x40ff00) == 0x2100 || (abus & 0x40ff80) == 0x4300 + || (abus & 0x40ffff) == 0x420b || (abus & 0x40ffff) == 0x420c) { + //illegal register access + bus.write(0x2100 | bbus, regs.mdr); //TODO: verify if MDR is written here + } else { + //valid transfer + bus.write(0x2100 | bbus, bus.read(abus)); + } + } else { + //b->a transfer (from $21xx) + if(bbus == 0x80 && ((abus & 0xfe0000) == 0x7e0000 || (abus & 0x40e000) == 0x0000)) { + //illegal WRAM->WRAM transfer + //no read occurs; write does occur + //does not write MDR as expected + //TODO: 0x00 was observed on hardware; verify if other values are possible + bus.write(abus, 0x00); + } else if((abus & 0x40ff00) == 0x2100 || (abus & 0x40ff80) == 0x4300 + || (abus & 0x40ffff) == 0x420b || (abus & 0x40ffff) == 0x420c) { + //illegal register access + bus.write(abus, regs.mdr); //TODO: verify if MDR is written here + } else { + //valid transfer + bus.write(abus, bus.read(0x2100 | bbus)); + } + } + + //each byte *always* consumes 8 clocks, even if transfer is invalid and no read and/or write occurs + dma_add_clocks(8); + cycle_edge(); +} + +/***** + * address calculation functions + *****/ + +uint8 sCPU::dma_bbus(uint8 i, uint8 index) { + switch(channel[i].xfermode) { default: + case 0: return (channel[i].destaddr); //0 + case 1: return (channel[i].destaddr + (index & 1)); //0,1 + case 2: return (channel[i].destaddr); //0,0 + case 3: return (channel[i].destaddr + ((index >> 1) & 1)); //0,0,1,1 + case 4: return (channel[i].destaddr + (index & 3)); //0,1,2,3 + case 5: return (channel[i].destaddr + (index & 1)); //0,1,0,1 + case 6: return (channel[i].destaddr); //0,0 [2] + case 7: return (channel[i].destaddr + ((index >> 1) & 1)); //0,0,1,1 [3] + } +} + +inline uint32 sCPU::dma_addr(uint8 i) { + uint32 r = (channel[i].srcbank << 16) | (channel[i].srcaddr); + + if(channel[i].fixedxfer == false) { + if(channel[i].reversexfer == false) { + channel[i].srcaddr++; + } else { + channel[i].srcaddr--; + } + } + + return r; +} + +inline uint32 sCPU::hdma_addr(uint8 i) { + return (channel[i].srcbank << 16) | (channel[i].hdma_addr++); +} + +inline uint32 sCPU::hdma_iaddr(uint8 i) { + return (channel[i].hdma_ibank << 16) | (channel[i].hdma_iaddr++); +} + +/***** + * DMA functions + *****/ + +void sCPU::dma_transfertobusb(uint8 i, uint8 bbus) { + if(cartridge.info.sdd1 == true && sdd1.dma_active() == true) { + bus.write(0x2100 | bbus, sdd1.dma_read()); + } else { + dma_transfer(0, bbus, dma_addr(i)); + } + channel[i].xfersize--; +} + +void sCPU::dma_transfertobusa(uint8 i, uint8 bbus) { + dma_transfer(1, bbus, dma_addr(i)); + channel[i].xfersize--; +} + +inline void sCPU::dma_write(uint8 i, uint8 index) { + //cannot use dma_transfer() directly, due to current S-DD1 implementation + if(channel[i].direction == 0) { + dma_transfertobusb(i, index); + } else { + dma_transfertobusa(i, index); + } +} + +void sCPU::dma_run() { + for(int i = 0; i < 8; i++) { + if(channel[i].dma_enabled == false) continue; + dma_add_clocks(8); + + if(cartridge.info.sdd1 == true) { + sdd1.dma_begin(i, (channel[i].srcbank << 16) | (channel[i].srcaddr), channel[i].xfersize); + } + + if(tracer.enabled() == true && tracer.cpudma_enabled() == true) { + tprintf("[DMA] channel:%d direction:%s reverse:%c fixed:%c mode:%d b_addr:$21%0.2x " + "a_addr:$%0.2x%0.4x length:$%0.4x (%5d)", + i, channel[i].direction ? "b->a" : "a->b", channel[i].reversexfer ? '1' : '0', + channel[i].fixedxfer ? '1' : '0', channel[i].xfermode, channel[i].destaddr, + channel[i].srcbank, channel[i].srcaddr, + channel[i].xfersize, channel[i].xfersize ? channel[i].xfersize : 65536); + } + + uint index = 0; + do { + dma_write(i, dma_bbus(i, index++)); + } while(channel[i].dma_enabled && channel[i].xfersize); + + channel[i].dma_enabled = false; + } + + counter.set(counter.irq_delay, 2); +} + +/***** + * HDMA functions + *****/ + +inline bool sCPU::hdma_active(uint8 i) { + return (channel[i].hdma_enabled && !channel[i].hdma_completed); +} + +inline bool sCPU::hdma_active_after(uint8 i) { + for(int n = i + 1; n < 8; n++) { + if(hdma_active(n) == true) return true; + } + return false; +} + +inline uint8 sCPU::hdma_enabled_channels() { + uint8 r = 0; + for(int i = 0; i < 8; i++) { + if(channel[i].hdma_enabled) r++; + } + return r; +} + +inline uint8 sCPU::hdma_active_channels() { + uint8 r = 0; + for(int i = 0; i < 8; i++) { + if(hdma_active(i) == true) r++; + } + return r; +} + +void sCPU::hdma_update(uint8 i) { + channel[i].hdma_line_counter = bus.read(hdma_addr(i)); + dma_add_clocks(8); + + channel[i].hdma_completed = (channel[i].hdma_line_counter == 0); + channel[i].hdma_do_transfer = !channel[i].hdma_completed; + + if(channel[i].hdma_indirect) { + channel[i].hdma_iaddr = bus.read(hdma_addr(i)) << 8; + dma_add_clocks(8); + + if(!channel[i].hdma_completed || hdma_active_after(i)) { + channel[i].hdma_iaddr >>= 8; + channel[i].hdma_iaddr |= bus.read(hdma_addr(i)) << 8; + dma_add_clocks(8); + } + } +} + +void sCPU::hdma_run() { + static uint8 hdma_xferlen[8] = { 1, 2, 2, 4, 4, 4, 2, 4 }; + for(int i = 0; i < 8; i++) { + if(hdma_active(i) == false) continue; + channel[i].dma_enabled = false; //HDMA run during DMA will stop DMA mid-transfer + dma_add_clocks(8); + + if(channel[i].hdma_do_transfer) { + int xferlen = hdma_xferlen[channel[i].xfermode]; + for(int index = 0; index < xferlen; index++) { + if(bool(config::cpu.hdma_enable) == true) { + dma_transfer(channel[i].direction, dma_bbus(i, index), + !channel[i].hdma_indirect ? hdma_addr(i) : hdma_iaddr(i)); + } else { + dma_add_clocks(8); + cycle_edge(); + } + } + } + + channel[i].hdma_line_counter--; + channel[i].hdma_do_transfer = bool(channel[i].hdma_line_counter & 0x80); + if((channel[i].hdma_line_counter & 0x7f) == 0) { + hdma_update(i); + } + } + + counter.set(counter.irq_delay, 2); +} + +void sCPU::hdma_init_reset() { + for(int i = 0; i < 8; i++) { + channel[i].hdma_completed = false; + channel[i].hdma_do_transfer = false; + } +} + +void sCPU::hdma_init() { + for(int i = 0; i < 8; i++) { + if(!channel[i].hdma_enabled)continue; + channel[i].dma_enabled = false; //HDMA init during DMA will stop DMA mid-transfer + + channel[i].hdma_addr = channel[i].srcaddr; + hdma_update(i); + } + + counter.set(counter.irq_delay, 2); +} + +/***** + * power / reset functions + *****/ + +void sCPU::dma_power() { + for(int i = 0; i < 8; i++) { + channel[i].dmap = 0xff; + channel[i].direction = 1; + channel[i].hdma_indirect = true; + channel[i].reversexfer = true; + channel[i].fixedxfer = true; + channel[i].xfermode = 7; + + channel[i].destaddr = 0xff; + + channel[i].srcaddr = 0xffff; + channel[i].srcbank = 0xff; + + channel[i].xfersize = 0xffff; + //channel[i].hdma_iaddr = 0xffff; //union with xfersize + channel[i].hdma_ibank = 0xff; + + channel[i].hdma_addr = 0xffff; + channel[i].hdma_line_counter = 0xff; + channel[i].unknown = 0xff; + } +} + +void sCPU::dma_reset() { + for(int i = 0; i < 8; i++) { + channel[i].dma_enabled = false; + channel[i].hdma_enabled = false; + + channel[i].hdma_completed = false; + channel[i].hdma_do_transfer = false; + } +} + +#endif //ifdef SCPU_CPP diff --git a/src/cpu/scpu/dma/dma.h b/src/cpu/scpu/dma/dma.h index 845e2f8d..3ad1651b 100644 --- a/src/cpu/scpu/dma/dma.h +++ b/src/cpu/scpu/dma/dma.h @@ -1,71 +1,71 @@ -struct { -//$420b - bool dma_enabled; + struct { + //$420b + bool dma_enabled; -//$420c - bool hdma_enabled; + //$420c + bool hdma_enabled; -//$43x0 - uint8 dmap; - bool direction; - bool hdma_indirect; - bool reversexfer; - bool fixedxfer; - uint8 xfermode; + //$43x0 + uint8 dmap; + bool direction; + bool hdma_indirect; + bool reversexfer; + bool fixedxfer; + uint8 xfermode; -//$43x1 - uint8 destaddr; + //$43x1 + uint8 destaddr; -//$43x2-$43x3 - uint16 srcaddr; + //$43x2-$43x3 + uint16 srcaddr; -//$43x4 - uint8 srcbank; + //$43x4 + uint8 srcbank; -//$43x5-$43x6 - union { - uint16 xfersize; - uint16 hdma_iaddr; - }; + //$43x5-$43x6 + union { + uint16 xfersize; + uint16 hdma_iaddr; + }; -//$43x7 - uint8 hdma_ibank; + //$43x7 + uint8 hdma_ibank; -//$43x8-$43x9 - uint16 hdma_addr; + //$43x8-$43x9 + uint16 hdma_addr; -//$43xa - uint8 hdma_line_counter; + //$43xa + uint8 hdma_line_counter; -//$43xb/$43xf - uint8 unknown; + //$43xb/$43xf + uint8 unknown; -//internal variables - bool hdma_completed; - bool hdma_do_transfer; -} channel[8]; + //internal variables + bool hdma_completed; + bool hdma_do_transfer; + } channel[8]; - void dma_add_clocks(uint clocks); - void dma_transfer(bool direction, uint8 bbus, uint32 abus); + void dma_add_clocks(uint clocks); + void dma_transfer(bool direction, uint8 bbus, uint32 abus); - uint8 dma_bbus(uint8 i, uint8 index); + uint8 dma_bbus(uint8 i, uint8 index); uint32 dma_addr(uint8 i); uint32 hdma_addr(uint8 i); uint32 hdma_iaddr(uint8 i); - void dma_transfertobusb(uint8 i, uint8 bbus); - void dma_transfertobusa(uint8 i, uint8 bbus); - void dma_write(uint8 i, uint8 index); - void dma_run(); + void dma_transfertobusb(uint8 i, uint8 bbus); + void dma_transfertobusa(uint8 i, uint8 bbus); + void dma_write(uint8 i, uint8 index); + void dma_run(); - bool hdma_active(uint8 i); - bool hdma_active_after(uint8 i); - uint8 hdma_enabled_channels(); - uint8 hdma_active_channels(); - void hdma_update(uint8 i); - void hdma_run(); - void hdma_init_reset(); - void hdma_init(); + bool hdma_active(uint8 i); + bool hdma_active_after(uint8 i); + uint8 hdma_enabled_channels(); + uint8 hdma_active_channels(); + void hdma_update(uint8 i); + void hdma_run(); + void hdma_init_reset(); + void hdma_init(); - void dma_power(); - void dma_reset(); + void dma_power(); + void dma_reset(); diff --git a/src/cpu/scpu/memory/memory.cpp b/src/cpu/scpu/memory/memory.cpp index 36cd1ec5..16faf779 100644 --- a/src/cpu/scpu/memory/memory.cpp +++ b/src/cpu/scpu/memory/memory.cpp @@ -1,3 +1,5 @@ +#ifdef SCPU_CPP + /***** * These 3 functions control bus timing for the CPU. * cpu_io is an I/O cycle, and always 6 clock cycles long. @@ -119,3 +121,5 @@ alwaysinline void sCPU::op_writedp(uint32 addr, uint8 data) { alwaysinline void sCPU::op_writesp(uint32 addr, uint8 data) { op_write((regs.s + (addr & 0xffff)) & 0xffff, data); } + +#endif //ifdef SCPU_CPP diff --git a/src/cpu/scpu/memory/memory.h b/src/cpu/scpu/memory/memory.h index 545c6c64..50c3b212 100644 --- a/src/cpu/scpu/memory/memory.h +++ b/src/cpu/scpu/memory/memory.h @@ -1,35 +1,35 @@ -/***** - * CPU<>APU communication ports - *****/ -uint8 apu_port[4]; - uint8 port_read (uint8 port) { return apu_port[port & 3]; } - void port_write(uint8 port, uint8 data) { apu_port[port & 3] = data; } + /***** + * CPU<>APU communication ports + *****/ + uint8 apu_port[4]; + uint8 port_read(uint8 port) { return apu_port[port & 3]; } + void port_write(uint8 port, uint8 data) { apu_port[port & 3] = data; } -/***** - * core CPU bus functions - *****/ - void op_io(); - uint8 op_read (uint32 addr); - void op_write(uint32 addr, uint8 data); + /***** + * core CPU bus functions + *****/ + void op_io(); + uint8 op_read(uint32 addr); + void op_write(uint32 addr, uint8 data); -/***** - * helper memory addressing functions used by CPU core - *****/ - uint8 op_readpc (); - uint8 op_readstack (); - uint8 op_readstackn(); - uint8 op_readaddr (uint32 addr); - uint8 op_readlong (uint32 addr); - uint8 op_readdbr (uint32 addr); - uint8 op_readpbr (uint32 addr); - uint8 op_readdp (uint32 addr); - uint8 op_readsp (uint32 addr); + /***** + * helper memory addressing functions used by CPU core + *****/ + uint8 op_readpc (); + uint8 op_readstack (); + uint8 op_readstackn(); + uint8 op_readaddr (uint32 addr); + uint8 op_readlong (uint32 addr); + uint8 op_readdbr (uint32 addr); + uint8 op_readpbr (uint32 addr); + uint8 op_readdp (uint32 addr); + uint8 op_readsp (uint32 addr); - void op_writestack (uint8 data); - void op_writestackn(uint8 data); - void op_writeaddr (uint32 addr, uint8 data); - void op_writelong (uint32 addr, uint8 data); - void op_writedbr (uint32 addr, uint8 data); - void op_writepbr (uint32 addr, uint8 data); - void op_writedp (uint32 addr, uint8 data); - void op_writesp (uint32 addr, uint8 data); + void op_writestack (uint8 data); + void op_writestackn(uint8 data); + void op_writeaddr (uint32 addr, uint8 data); + void op_writelong (uint32 addr, uint8 data); + void op_writedbr (uint32 addr, uint8 data); + void op_writepbr (uint32 addr, uint8 data); + void op_writedp (uint32 addr, uint8 data); + void op_writesp (uint32 addr, uint8 data); diff --git a/src/cpu/scpu/mmio/mmio.cpp b/src/cpu/scpu/mmio/mmio.cpp index 13e12f25..e511c109 100644 --- a/src/cpu/scpu/mmio/mmio.cpp +++ b/src/cpu/scpu/mmio/mmio.cpp @@ -1,10 +1,12 @@ +#ifdef SCPU_CPP + uint8 sCPU::pio_status() { return status.pio; } //WMDATA uint8 sCPU::mmio_r2180() { -uint8 r = bus.read(0x7e0000 | status.wram_addr); + uint8 r = bus.read(0x7e0000 | status.wram_addr); status.wram_addr = (status.wram_addr + 1) & 0x01ffff; return r; } @@ -41,7 +43,7 @@ void sCPU::mmio_w4016(uint8 data) { status.joypad_strobe_latch = !!(data & 1); if(status.joypad_strobe_latch == 1) { - snes.poll_input(); + snes.input.poll(); } } @@ -52,8 +54,8 @@ void sCPU::mmio_w4016(uint8 data) { //TODO: test whether strobe latch of zero returns //realtime or buffered status of joypadN.b uint8 sCPU::mmio_r4016() { -uint8 r = regs.mdr & 0xfc; - r |= (uint8)snes.port_read(0); + uint8 r = regs.mdr & 0xfc; + r |= (uint8)snes.input.port_read(0); return r; } @@ -62,8 +64,8 @@ uint8 r = regs.mdr & 0xfc; //4-2 = Always 1 (pins are connected to GND) //1-0 = Joypad serial data uint8 sCPU::mmio_r4017() { -uint8 r = (regs.mdr & 0xe0) | 0x1c; - r |= (uint8)snes.port_read(1); + uint8 r = (regs.mdr & 0xe0) | 0x1c; + r |= (uint8)snes.input.port_read(1); return r; } @@ -167,7 +169,7 @@ void sCPU::mmio_w420d(uint8 data) { //6-4 = MDR //3-0 = CPU (5a22) version uint8 sCPU::mmio_r4210() { -uint8 r = (regs.mdr & 0x70); + uint8 r = (regs.mdr & 0x70); r |= (uint8)(rdnmi()) << 7; r |= (cpu_version & 0x0f); return r; @@ -177,7 +179,7 @@ uint8 r = (regs.mdr & 0x70); //7 = IRQ acknowledge //6-0 = MDR uint8 sCPU::mmio_r4211() { -uint8 r = (regs.mdr & 0x7f); + uint8 r = (regs.mdr & 0x7f); r |= (uint8)(timeup()) << 7; return r; } @@ -188,16 +190,16 @@ uint8 r = (regs.mdr & 0x7f); //5-1 = MDR //0 = JOYPAD acknowledge uint8 sCPU::mmio_r4212() { -uint8 r = (regs.mdr & 0x3e); -uint16 vs = !overscan() ? 225 : 240; + uint8 r = (regs.mdr & 0x3e); + uint16 vs = ppu.overscan() == false ? 225 : 240; -//auto joypad polling + //auto joypad polling if(status.vcounter >= vs && status.vcounter <= (vs + 2))r |= 0x01; -//hblank - if(status.hclock <= 2 || status.hclock >= 1096)r |= 0x40; + //hblank + if(status.hcounter <= 2 || status.hcounter >= 1096)r |= 0x40; -//vblank + //vblank if(status.vcounter >= vs)r |= 0x80; return r; @@ -375,40 +377,40 @@ void sCPU::mmio_power() { } void sCPU::mmio_reset() { -//$2181-$2183 + //$2181-$2183 status.wram_addr = 0x000000; -//$4016-$4017 + //$4016-$4017 status.joypad_strobe_latch = 0; status.joypad1_bits = ~0; status.joypad2_bits = ~0; -//$4200 + //$4200 status.nmi_enabled = false; status.hirq_enabled = false; status.virq_enabled = false; status.auto_joypad_poll = false; -//$4201 + //$4201 status.pio = 0xff; -//$4202-$4203 + //$4202-$4203 status.mul_a = 0xff; status.mul_b = 0xff; -//$4204-$4206 + //$4204-$4206 status.div_a = 0xffff; status.div_b = 0xff; -//$4207-$420a + //$4207-$420a status.hirq_pos = 0x01ff; status.virq_pos = 0x01ff; -//$4214-$4217 + //$4214-$4217 status.r4214 = 0x0000; status.r4216 = 0x0000; -//$4218-$421f + //$4218-$421f status.joy1l = 0x00; status.joy1h = 0x00; status.joy2l = 0x00; @@ -422,55 +424,55 @@ void sCPU::mmio_reset() { uint8 sCPU::mmio_read(uint addr) { addr &= 0xffff; -//APU + //APU if((addr & 0xffc0) == 0x2140) { //$2140-$217f scheduler.sync_cpusmp(); return smp.port_read(addr & 3); } -//DMA + //DMA if((addr & 0xff80) == 0x4300) { //$4300-$437f - uint i = (addr >> 4) & 7; + uint i = (addr >> 4) & 7; switch(addr & 0xf) { - case 0x0: return mmio_r43x0(i); - case 0x1: return mmio_r43x1(i); - case 0x2: return mmio_r43x2(i); - case 0x3: return mmio_r43x3(i); - case 0x4: return mmio_r43x4(i); - case 0x5: return mmio_r43x5(i); - case 0x6: return mmio_r43x6(i); - case 0x7: return mmio_r43x7(i); - case 0x8: return mmio_r43x8(i); - case 0x9: return mmio_r43x9(i); - case 0xa: return mmio_r43xa(i); - case 0xb: return mmio_r43xb(i); - case 0xc: return regs.mdr; //unmapped - case 0xd: return regs.mdr; //unmapped - case 0xe: return regs.mdr; //unmapped - case 0xf: return mmio_r43xb(i); //mirror of $43xb + case 0x0: return mmio_r43x0(i); + case 0x1: return mmio_r43x1(i); + case 0x2: return mmio_r43x2(i); + case 0x3: return mmio_r43x3(i); + case 0x4: return mmio_r43x4(i); + case 0x5: return mmio_r43x5(i); + case 0x6: return mmio_r43x6(i); + case 0x7: return mmio_r43x7(i); + case 0x8: return mmio_r43x8(i); + case 0x9: return mmio_r43x9(i); + case 0xa: return mmio_r43xa(i); + case 0xb: return mmio_r43xb(i); + case 0xc: return regs.mdr; //unmapped + case 0xd: return regs.mdr; //unmapped + case 0xe: return regs.mdr; //unmapped + case 0xf: return mmio_r43xb(i); //mirror of $43xb } } switch(addr) { - case 0x2180: return mmio_r2180(); - case 0x4016: return mmio_r4016(); - case 0x4017: return mmio_r4017(); - case 0x4210: return mmio_r4210(); - case 0x4211: return mmio_r4211(); - case 0x4212: return mmio_r4212(); - case 0x4213: return mmio_r4213(); - case 0x4214: return mmio_r4214(); - case 0x4215: return mmio_r4215(); - case 0x4216: return mmio_r4216(); - case 0x4217: return mmio_r4217(); - case 0x4218: return mmio_r4218(); - case 0x4219: return mmio_r4219(); - case 0x421a: return mmio_r421a(); - case 0x421b: return mmio_r421b(); - case 0x421c: return mmio_r421c(); - case 0x421d: return mmio_r421d(); - case 0x421e: return mmio_r421e(); - case 0x421f: return mmio_r421f(); + case 0x2180: return mmio_r2180(); + case 0x4016: return mmio_r4016(); + case 0x4017: return mmio_r4017(); + case 0x4210: return mmio_r4210(); + case 0x4211: return mmio_r4211(); + case 0x4212: return mmio_r4212(); + case 0x4213: return mmio_r4213(); + case 0x4214: return mmio_r4214(); + case 0x4215: return mmio_r4215(); + case 0x4216: return mmio_r4216(); + case 0x4217: return mmio_r4217(); + case 0x4218: return mmio_r4218(); + case 0x4219: return mmio_r4219(); + case 0x421a: return mmio_r421a(); + case 0x421b: return mmio_r421b(); + case 0x421c: return mmio_r421c(); + case 0x421d: return mmio_r421d(); + case 0x421e: return mmio_r421e(); + case 0x421f: return mmio_r421f(); } return regs.mdr; @@ -479,56 +481,58 @@ uint8 sCPU::mmio_read(uint addr) { void sCPU::mmio_write(uint addr, uint8 data) { addr &= 0xffff; -//APU + //APU if((addr & 0xffc0) == 0x2140) { //$2140-$217f scheduler.sync_cpusmp(); port_write(addr & 3, data); return; } -//DMA + //DMA if((addr & 0xff80) == 0x4300) { //$4300-$437f uint i = (addr >> 4) & 7; switch(addr & 0xf) { - case 0x0: mmio_w43x0(i, data); return; - case 0x1: mmio_w43x1(i, data); return; - case 0x2: mmio_w43x2(i, data); return; - case 0x3: mmio_w43x3(i, data); return; - case 0x4: mmio_w43x4(i, data); return; - case 0x5: mmio_w43x5(i, data); return; - case 0x6: mmio_w43x6(i, data); return; - case 0x7: mmio_w43x7(i, data); return; - case 0x8: mmio_w43x8(i, data); return; - case 0x9: mmio_w43x9(i, data); return; - case 0xa: mmio_w43xa(i, data); return; - case 0xb: mmio_w43xb(i, data); return; - case 0xc: return; //unmapped - case 0xd: return; //unmapped - case 0xe: return; //unmapped - case 0xf: mmio_w43xb(i, data); return; //mirror of $43xb + case 0x0: mmio_w43x0(i, data); return; + case 0x1: mmio_w43x1(i, data); return; + case 0x2: mmio_w43x2(i, data); return; + case 0x3: mmio_w43x3(i, data); return; + case 0x4: mmio_w43x4(i, data); return; + case 0x5: mmio_w43x5(i, data); return; + case 0x6: mmio_w43x6(i, data); return; + case 0x7: mmio_w43x7(i, data); return; + case 0x8: mmio_w43x8(i, data); return; + case 0x9: mmio_w43x9(i, data); return; + case 0xa: mmio_w43xa(i, data); return; + case 0xb: mmio_w43xb(i, data); return; + case 0xc: return; //unmapped + case 0xd: return; //unmapped + case 0xe: return; //unmapped + case 0xf: mmio_w43xb(i, data); return; //mirror of $43xb } } switch(addr) { - case 0x2180: mmio_w2180(data); return; - case 0x2181: mmio_w2181(data); return; - case 0x2182: mmio_w2182(data); return; - case 0x2183: mmio_w2183(data); return; - case 0x4016: mmio_w4016(data); return; - case 0x4017: return; //unmapped - case 0x4200: mmio_w4200(data); return; - case 0x4201: mmio_w4201(data); return; - case 0x4202: mmio_w4202(data); return; - case 0x4203: mmio_w4203(data); return; - case 0x4204: mmio_w4204(data); return; - case 0x4205: mmio_w4205(data); return; - case 0x4206: mmio_w4206(data); return; - case 0x4207: mmio_w4207(data); return; - case 0x4208: mmio_w4208(data); return; - case 0x4209: mmio_w4209(data); return; - case 0x420a: mmio_w420a(data); return; - case 0x420b: mmio_w420b(data); return; - case 0x420c: mmio_w420c(data); return; - case 0x420d: mmio_w420d(data); return; + case 0x2180: mmio_w2180(data); return; + case 0x2181: mmio_w2181(data); return; + case 0x2182: mmio_w2182(data); return; + case 0x2183: mmio_w2183(data); return; + case 0x4016: mmio_w4016(data); return; + case 0x4017: return; //unmapped + case 0x4200: mmio_w4200(data); return; + case 0x4201: mmio_w4201(data); return; + case 0x4202: mmio_w4202(data); return; + case 0x4203: mmio_w4203(data); return; + case 0x4204: mmio_w4204(data); return; + case 0x4205: mmio_w4205(data); return; + case 0x4206: mmio_w4206(data); return; + case 0x4207: mmio_w4207(data); return; + case 0x4208: mmio_w4208(data); return; + case 0x4209: mmio_w4209(data); return; + case 0x420a: mmio_w420a(data); return; + case 0x420b: mmio_w420b(data); return; + case 0x420c: mmio_w420c(data); return; + case 0x420d: mmio_w420d(data); return; } } + +#endif //ifdef SCPU_CPP diff --git a/src/cpu/scpu/mmio/mmio.h b/src/cpu/scpu/mmio/mmio.h index 74cdcebf..33d92d12 100644 --- a/src/cpu/scpu/mmio/mmio.h +++ b/src/cpu/scpu/mmio/mmio.h @@ -1,70 +1,70 @@ - void mmio_power(); - void mmio_reset(); - uint8 mmio_read (uint addr); - void mmio_write(uint addr, uint8 data); + void mmio_power(); + void mmio_reset(); + uint8 mmio_read(uint addr); + void mmio_write(uint addr, uint8 data); - uint8 pio_status(); + uint8 pio_status(); - uint8 mmio_r2180(); - uint8 mmio_r4016(); - uint8 mmio_r4017(); - uint8 mmio_r4210(); - uint8 mmio_r4211(); - uint8 mmio_r4212(); - uint8 mmio_r4213(); - uint8 mmio_r4214(); - uint8 mmio_r4215(); - uint8 mmio_r4216(); - uint8 mmio_r4217(); - uint8 mmio_r4218(); - uint8 mmio_r4219(); - uint8 mmio_r421a(); - uint8 mmio_r421b(); - uint8 mmio_r421c(); - uint8 mmio_r421d(); - uint8 mmio_r421e(); - uint8 mmio_r421f(); - uint8 mmio_r43x0(uint8 i); - uint8 mmio_r43x1(uint8 i); - uint8 mmio_r43x2(uint8 i); - uint8 mmio_r43x3(uint8 i); - uint8 mmio_r43x4(uint8 i); - uint8 mmio_r43x5(uint8 i); - uint8 mmio_r43x6(uint8 i); - uint8 mmio_r43x7(uint8 i); - uint8 mmio_r43x8(uint8 i); - uint8 mmio_r43x9(uint8 i); - uint8 mmio_r43xa(uint8 i); - uint8 mmio_r43xb(uint8 i); + uint8 mmio_r2180(); + uint8 mmio_r4016(); + uint8 mmio_r4017(); + uint8 mmio_r4210(); + uint8 mmio_r4211(); + uint8 mmio_r4212(); + uint8 mmio_r4213(); + uint8 mmio_r4214(); + uint8 mmio_r4215(); + uint8 mmio_r4216(); + uint8 mmio_r4217(); + uint8 mmio_r4218(); + uint8 mmio_r4219(); + uint8 mmio_r421a(); + uint8 mmio_r421b(); + uint8 mmio_r421c(); + uint8 mmio_r421d(); + uint8 mmio_r421e(); + uint8 mmio_r421f(); + uint8 mmio_r43x0(uint8 i); + uint8 mmio_r43x1(uint8 i); + uint8 mmio_r43x2(uint8 i); + uint8 mmio_r43x3(uint8 i); + uint8 mmio_r43x4(uint8 i); + uint8 mmio_r43x5(uint8 i); + uint8 mmio_r43x6(uint8 i); + uint8 mmio_r43x7(uint8 i); + uint8 mmio_r43x8(uint8 i); + uint8 mmio_r43x9(uint8 i); + uint8 mmio_r43xa(uint8 i); + uint8 mmio_r43xb(uint8 i); - void mmio_w2180(uint8 data); - void mmio_w2181(uint8 data); - void mmio_w2182(uint8 data); - void mmio_w2183(uint8 data); - void mmio_w4016(uint8 data); - void mmio_w4200(uint8 data); - void mmio_w4201(uint8 data); - void mmio_w4202(uint8 data); - void mmio_w4203(uint8 data); - void mmio_w4204(uint8 data); - void mmio_w4205(uint8 data); - void mmio_w4206(uint8 data); - void mmio_w4207(uint8 data); - void mmio_w4208(uint8 data); - void mmio_w4209(uint8 data); - void mmio_w420a(uint8 data); - void mmio_w420b(uint8 data); - void mmio_w420c(uint8 data); - void mmio_w420d(uint8 data); - void mmio_w43x0(uint8 i, uint8 data); - void mmio_w43x1(uint8 i, uint8 data); - void mmio_w43x2(uint8 i, uint8 data); - void mmio_w43x3(uint8 i, uint8 data); - void mmio_w43x4(uint8 i, uint8 data); - void mmio_w43x5(uint8 i, uint8 data); - void mmio_w43x6(uint8 i, uint8 data); - void mmio_w43x7(uint8 i, uint8 data); - void mmio_w43x8(uint8 i, uint8 data); - void mmio_w43x9(uint8 i, uint8 data); - void mmio_w43xa(uint8 i, uint8 data); - void mmio_w43xb(uint8 i, uint8 data); + void mmio_w2180(uint8 data); + void mmio_w2181(uint8 data); + void mmio_w2182(uint8 data); + void mmio_w2183(uint8 data); + void mmio_w4016(uint8 data); + void mmio_w4200(uint8 data); + void mmio_w4201(uint8 data); + void mmio_w4202(uint8 data); + void mmio_w4203(uint8 data); + void mmio_w4204(uint8 data); + void mmio_w4205(uint8 data); + void mmio_w4206(uint8 data); + void mmio_w4207(uint8 data); + void mmio_w4208(uint8 data); + void mmio_w4209(uint8 data); + void mmio_w420a(uint8 data); + void mmio_w420b(uint8 data); + void mmio_w420c(uint8 data); + void mmio_w420d(uint8 data); + void mmio_w43x0(uint8 i, uint8 data); + void mmio_w43x1(uint8 i, uint8 data); + void mmio_w43x2(uint8 i, uint8 data); + void mmio_w43x3(uint8 i, uint8 data); + void mmio_w43x4(uint8 i, uint8 data); + void mmio_w43x5(uint8 i, uint8 data); + void mmio_w43x6(uint8 i, uint8 data); + void mmio_w43x7(uint8 i, uint8 data); + void mmio_w43x8(uint8 i, uint8 data); + void mmio_w43x9(uint8 i, uint8 data); + void mmio_w43xa(uint8 i, uint8 data); + void mmio_w43xb(uint8 i, uint8 data); diff --git a/src/cpu/scpu/scpu.cpp b/src/cpu/scpu/scpu.cpp index 924d3dc3..53f7415d 100644 --- a/src/cpu/scpu/scpu.cpp +++ b/src/cpu/scpu/scpu.cpp @@ -1,4 +1,5 @@ -#include "../../base.h" +#include "../../base.h" +#define SCPU_CPP #include "core/core.cpp" #include "dma/dma.cpp" @@ -7,8 +8,6 @@ #include "timing/timing.cpp" void sCPU::power() { - status.region = (bool)snes.region(); - regs.a = regs.x = regs.y = 0x0000; regs.s = 0x01ff; @@ -24,7 +23,7 @@ void sCPU::reset() { regs.pc.l = bus.read(0xfffc); regs.pc.h = bus.read(0xfffd); -//note: some registers are not fully reset by SNES + //note: some registers are not fully reset by SNES regs.x.h = 0x00; regs.y.h = 0x00; regs.s.h = 0x01; diff --git a/src/cpu/scpu/scpu.h b/src/cpu/scpu/scpu.h index dc90a0c5..6dedfcff 100644 --- a/src/cpu/scpu/scpu.h +++ b/src/cpu/scpu/scpu.h @@ -1,138 +1,132 @@ -class sCPU : public CPU { public: +class sCPU : public CPU { +public: void enter(); -#include "core/core.h" -#include "dma/dma.h" -#include "memory/memory.h" -#include "mmio/mmio.h" -#include "timing/timing.h" + #include "core/core.h" + #include "dma/dma.h" + #include "memory/memory.h" + #include "mmio/mmio.h" + #include "timing/timing.h" -struct { - bool wai; - bool irq; - uint16 irq_vector; -} event; + struct { + bool wai; + bool irq; + uint16 irq_vector; + } event; -struct { - uint nmi_hold; - uint irq_hold; + struct { + uint nmi_hold; + uint irq_hold; - uint nmi_fire; - uint irq_fire; - uint irq_delay; - uint hw_math; + uint nmi_fire; + uint irq_fire; + uint irq_delay; + uint hw_math; - alwaysinline void set(uint &ctr, uint clocks) { - if(clocks >= ctr) { ctr = clocks; } - } - - alwaysinline void sub(uint &ctr, uint clocks) { - if(ctr >= clocks) { - ctr -= clocks; - } else { - ctr = 0; + alwaysinline void set(uint &ctr, uint clocks) { + if(clocks >= ctr) { ctr = clocks; } } - } -} counter; -enum { - DMASTATE_INACTIVE, - DMASTATE_DMASYNC, - DMASTATE_RUN, - DMASTATE_CPUSYNC, -}; + alwaysinline void sub(uint &ctr, uint clocks) { + if(ctr >= clocks) { + ctr -= clocks; + } else { + ctr = 0; + } + } + } counter; -struct { -//core - uint8 opcode; - bool in_opcode; + enum { + DMASTATE_INACTIVE, + DMASTATE_DMASYNC, + DMASTATE_RUN, + DMASTATE_CPUSYNC, + }; - uint clock_count; + struct { + //core + uint8 opcode; + bool in_opcode; -//timing - bool region; - uint16 region_scanlines; - uint16 vcounter, hcounter, hclock; - bool interlace, interlace_field; - bool overscan; - uint16 field_lines, line_clocks; - uint16 prev_field_lines, prev_line_clocks; - uint16 vblstart; + uint clock_count; - bool line_rendered; - uint16 line_render_position; + //timing + uint16 vcounter, hcounter; + uint16 field_lines, line_clocks; - bool dram_refreshed; - uint16 dram_refresh_position; + bool line_rendered; + uint16 line_render_position; - bool hdmainit_triggered; - uint16 hdmainit_trigger_position; + bool dram_refreshed; + uint16 dram_refresh_position; - bool hdma_triggered; + bool hdmainit_triggered; + uint16 hdmainit_trigger_position; - uint16 irq_delay; + bool hdma_triggered; - uint16 vnmi_trigger_pos; - bool nmi_valid; - bool nmi_line; - bool nmi_transition; - bool nmi_pending; + uint16 irq_delay; - uint16 virq_trigger_pos, hirq_trigger_pos; - bool irq_valid; - bool irq_line; - bool irq_transition; - bool irq_pending; + bool nmi_valid; + bool nmi_line; + bool nmi_transition; + bool nmi_pending; -//dma - uint dma_counter; - uint dma_clocks; - uint dma_state; - bool dma_pending; - bool hdma_pending; - bool hdmainit_pending; + uint16 virq_trigger_pos, hirq_trigger_pos; + bool irq_valid; + bool irq_line; + bool irq_transition; + bool irq_pending; -//mmio + //dma + uint dma_counter; + uint dma_clocks; + uint dma_state; + bool dma_pending; + bool hdma_pending; + bool hdmainit_pending; -//$2181-$2183 - uint32 wram_addr; + //mmio -//$4016-$4017 - bool joypad_strobe_latch; - uint32 joypad1_bits; - uint32 joypad2_bits; + //$2181-$2183 + uint32 wram_addr; -//$4200 - bool nmi_enabled; - bool hirq_enabled, virq_enabled; - bool auto_joypad_poll; + //$4016-$4017 + bool joypad_strobe_latch; + uint32 joypad1_bits; + uint32 joypad2_bits; -//$4201 - uint8 pio; + //$4200 + bool nmi_enabled; + bool hirq_enabled, virq_enabled; + bool auto_joypad_poll; -//$4202-$4203 - uint8 mul_a, mul_b; + //$4201 + uint8 pio; -//$4204-$4206 - uint16 div_a; - uint8 div_b; + //$4202-$4203 + uint8 mul_a, mul_b; -//$4207-$420a - uint16 hirq_pos, virq_pos; + //$4204-$4206 + uint16 div_a; + uint8 div_b; -//$4214-$4217 - uint16 r4214; - uint16 r4216; + //$4207-$420a + uint16 hirq_pos, virq_pos; -//$4218-$421f - uint8 joy1l, joy1h; - uint8 joy2l, joy2h; - uint8 joy3l, joy3h; - uint8 joy4l, joy4h; -} status; + //$4214-$4217 + uint16 r4214; + uint16 r4216; - void power(); - void reset(); + //$4218-$421f + uint8 joy1l, joy1h; + uint8 joy2l, joy2h; + uint8 joy3l, joy3h; + uint8 joy4l, joy4h; + } status; + + void power(); + void reset(); sCPU(); ~sCPU(); diff --git a/src/cpu/scpu/timing/irq.cpp b/src/cpu/scpu/timing/irq.cpp index 0765ee93..5b151474 100644 --- a/src/cpu/scpu/timing/irq.cpp +++ b/src/cpu/scpu/timing/irq.cpp @@ -1,60 +1,137 @@ -#include "irqtiming.cpp" - -bool sCPU::irq_pos_valid() { -uint vpos = status.virq_pos; -uint hpos = (status.hirq_enabled) ? status.hirq_pos : 0; -uint vlimit = region_scanlines() >> 1; -//positions that can never be latched -//vlimit = 262/NTSC, 312/PAL -//PAL results are unverified on hardware - if(vpos == 240 && hpos == 339 && interlace() == false && interlace_field() == 1)return false; - if(vpos == (vlimit - 1) && hpos == 339 && interlace() == false)return false; - if(vpos == vlimit && interlace() == false)return false; - if(vpos == vlimit && hpos == 339)return false; - if(vpos > vlimit)return false; - if(hpos > 339)return false; - - return true; +#ifdef SCPU_CPP + +void sCPU::update_interrupts() { + if(irq_pos_valid() == true) { + status.virq_trigger_pos = status.virq_pos; + status.hirq_trigger_pos = 4 * ((status.hirq_enabled) ? (status.hirq_pos + 1) : 0); + } else { + status.virq_trigger_pos = IRQ_TRIGGER_NEVER; + status.hirq_trigger_pos = IRQ_TRIGGER_NEVER; + } } -alwaysinline -bool sCPU::nmi_test() { - if(status.nmi_transition == false) { return false; } - status.nmi_transition = false; +alwaysinline void sCPU::poll_interrupts() { + uint16_t vpos, hpos; - event.wai = false; - return true; -} - -alwaysinline -bool sCPU::irq_test() { - if(status.irq_transition == false) { return false; } - status.irq_transition = false; - - event.wai = false; - return (regs.p.i) ? false : true; -} - -/* - if(status.irq_transition == 1)goto irq_trigger; - - if(status.irq_read == 0) { - if(status.irq_line == 1 && irq_edge()) { - return false; + //NMI hold + if(counter.nmi_hold) { + counter.nmi_hold -= 2; + if(counter.nmi_hold == 0) { + if(status.nmi_enabled == true) status.nmi_transition = true; } - goto irq_trigger; } - if(status.irq_line == 0) { - status.irq_line = 1; - goto irq_trigger; + //NMI test + history.query(2, vpos, hpos); + bool nmi_valid = (vpos >= (!ppu.overscan() ? 225 : 240)); + if(status.nmi_valid == false && nmi_valid == true) { + //0->1 edge sensitive transition + status.nmi_line = true; + counter.nmi_hold = 4; + } else if(status.nmi_valid == true && nmi_valid == false) { + //1->0 edge sensitive transition + status.nmi_line = false; + } + status.nmi_valid = nmi_valid; + + //IRQ hold + if(counter.irq_hold) counter.irq_hold -= 2; + if(status.irq_line == true && counter.irq_hold == 0) { + if(status.virq_enabled == true || status.hirq_enabled == true) status.irq_transition = true; } - return false; - -irq_trigger: - status.irq_transition = 0; - event.wai = false; - return (regs.p.i) ? false : true; + //IRQ test + history.query(10, vpos, hpos); + bool irq_valid = (status.virq_enabled == true || status.hirq_enabled == true); + if(irq_valid == true) { + if(status.virq_enabled == true && vpos != status.virq_trigger_pos) irq_valid = false; + if(status.hirq_enabled == true && hpos != status.hirq_trigger_pos) irq_valid = false; + } + if(status.irq_valid == false && irq_valid == true) { + //0->1 edge sensitive transition + status.irq_line = true; + counter.irq_hold = 4; + } + status.irq_valid = irq_valid; } -*/ + +void sCPU::nmitimen_update(uint8 data) { + bool nmi_enabled = status.nmi_enabled; + bool virq_enabled = status.virq_enabled; + bool hirq_enabled = status.hirq_enabled; + status.nmi_enabled = !!(data & 0x80); + status.virq_enabled = !!(data & 0x20); + status.hirq_enabled = !!(data & 0x10); + + //0->1 edge sensitive transition + if(nmi_enabled == false && status.nmi_enabled == true && status.nmi_line == true) { + status.nmi_transition = true; + } + + //?->1 level sensitive transition + if(status.virq_enabled == true && status.hirq_enabled == false && status.irq_line == true) { + status.irq_transition = true; + } + + if(status.virq_enabled == false && status.hirq_enabled == false) { + status.irq_line = false; + status.irq_transition = false; + } + + update_interrupts(); + counter.set(counter.irq_delay, 2); +} + +void sCPU::hvtime_update(uint16 addr) { + update_interrupts(); +} + +bool sCPU::rdnmi() { + bool result = status.nmi_line; + if(counter.nmi_hold == 0) { + status.nmi_line = false; + } + return result; +} + +bool sCPU::timeup() { + bool result = status.irq_line; + if(counter.irq_hold == 0) { + status.irq_line = false; + status.irq_transition = false; + } + return result; +} + +bool sCPU::irq_pos_valid() { + uint vpos = status.virq_pos; + uint hpos = (status.hirq_enabled) ? status.hirq_pos : 0; + uint vlimit = (snes.region() == SNES::NTSC ? 525 : 625) >> 1; + //positions that can never be latched + //vlimit = 262/NTSC, 312/PAL + //PAL results are unverified on hardware + if(vpos == 240 && hpos == 339 && ppu.interlace() == false && ppu.field() == 1) return false; + if(vpos == (vlimit - 1) && hpos == 339 && ppu.interlace() == false) return false; + if(vpos == vlimit && ppu.interlace() == false) return false; + if(vpos == vlimit && hpos == 339) return false; + if(vpos > vlimit) return false; + if(hpos > 339) return false; + + return true; +} + +alwaysinline bool sCPU::nmi_test() { + if(status.nmi_transition == false) return false; + status.nmi_transition = false; + event.wai = false; + return true; +} + +alwaysinline bool sCPU::irq_test() { + if(status.irq_transition == false) return false; + status.irq_transition = false; + event.wai = false; + return regs.p.i ? false : true; +} + +#endif //ifdef SCPU_CPP diff --git a/src/cpu/scpu/timing/irqtiming.cpp b/src/cpu/scpu/timing/irqtiming.cpp deleted file mode 100644 index f29e6d5d..00000000 --- a/src/cpu/scpu/timing/irqtiming.cpp +++ /dev/null @@ -1,105 +0,0 @@ -void sCPU::update_interrupts() { - status.vnmi_trigger_pos = status.vblstart; - - if(irq_pos_valid() == true) { - status.virq_trigger_pos = status.virq_pos; - status.hirq_trigger_pos = 4 * ((status.hirq_enabled) ? (status.hirq_pos + 1) : 0); - } else { - status.virq_trigger_pos = IRQ_TRIGGER_NEVER; - status.hirq_trigger_pos = IRQ_TRIGGER_NEVER; - } -} - -alwaysinline -void sCPU::poll_interrupts() { -uint vpos = status.vcounter, hpos = status.hclock; - -//NMI test - timeshift_backward(2, vpos, hpos); -bool nmi_valid = (vpos >= status.vnmi_trigger_pos); - if(status.nmi_valid == false && nmi_valid == true) { - //0->1 edge sensitive transition - status.nmi_line = true; - counter.nmi_hold = 6; - } else if(status.nmi_valid == true && nmi_valid == false) { - //1->0 edge sensitive transition - status.nmi_line = false; - } - status.nmi_valid = nmi_valid; - -//NMI hold - if(counter.nmi_hold) { - counter.nmi_hold -= 2; - if(counter.nmi_hold == 0) { - if(status.nmi_enabled == true) { status.nmi_transition = true; } - } - } - -//IRQ test - timeshift_backward(8, vpos, hpos); -bool irq_valid = (status.virq_enabled == true || status.hirq_enabled == true); - if(irq_valid == true) { - if(status.virq_enabled == true && vpos != status.virq_trigger_pos) { irq_valid = false; } - if(status.hirq_enabled == true && hpos != status.hirq_trigger_pos) { irq_valid = false; } - } - if(status.irq_valid == false && irq_valid == true) { - //0->1 edge sensitive transition - status.irq_line = true; - counter.irq_hold = 6; - } - status.irq_valid = irq_valid; - -//IRQ hold - if(counter.irq_hold) { counter.irq_hold -= 2; } - if(status.irq_line == true && counter.irq_hold == 0) { - if(status.virq_enabled == true || status.hirq_enabled == true) { status.irq_transition = true; } - } -} - -void sCPU::nmitimen_update(uint8 data) { -bool nmi_enabled = status.nmi_enabled; -bool virq_enabled = status.virq_enabled; -bool hirq_enabled = status.hirq_enabled; - status.nmi_enabled = !!(data & 0x80); - status.virq_enabled = !!(data & 0x20); - status.hirq_enabled = !!(data & 0x10); - -//0->1 edge sensitive transition - if(nmi_enabled == false && status.nmi_enabled == true && status.nmi_line == true) { - status.nmi_transition = true; - } - -//?->1 level sensitive transition - if(status.virq_enabled == true && status.hirq_enabled == false && status.irq_line == true) { - status.irq_transition = true; - } - - if(status.virq_enabled == false && status.hirq_enabled == false) { - status.irq_line = false; - status.irq_transition = false; - } - - update_interrupts(); - counter.set(counter.irq_delay, 2); -} - -void sCPU::hvtime_update(uint16 addr) { - update_interrupts(); -} - -bool sCPU::rdnmi() { -bool result = status.nmi_line; - if(counter.nmi_hold == 0) { - status.nmi_line = false; - } - return result; -} - -bool sCPU::timeup() { -bool result = status.irq_line; - if(counter.irq_hold == 0) { - status.irq_line = false; - status.irq_transition = false; - } - return result; -} diff --git a/src/cpu/scpu/timing/joypad.cpp b/src/cpu/scpu/timing/joypad.cpp index 3cd1924a..50e1cce2 100644 --- a/src/cpu/scpu/timing/joypad.cpp +++ b/src/cpu/scpu/timing/joypad.cpp @@ -1,8 +1,10 @@ +#ifdef SCPU_CPP + void sCPU::run_auto_joypad_poll() { -uint16 joy1 = 0, joy2 = 0; - for(int i = 0; i < 16; i++) { - joy1 |= (uint16)snes.port_read(0) ? (0x8000 >> i) : 0; - joy2 |= (uint16)snes.port_read(1) ? (0x8000 >> i) : 0; + uint16_t joy1 = 0, joy2 = 0; + for(unsigned i = 0; i < 16; i++) { + joy1 |= (uint16_t)snes.input.port_read(0) ? (0x8000 >> i) : 0; + joy2 |= (uint16_t)snes.input.port_read(1) ? (0x8000 >> i) : 0; } status.joy1l = joy1; @@ -17,3 +19,5 @@ uint16 joy1 = 0, joy2 = 0; status.joy4l = 0x00; status.joy4h = 0x00; } + +#endif //ifdef SCPU_CPP diff --git a/src/cpu/scpu/timing/timeshift.cpp b/src/cpu/scpu/timing/timeshift.cpp deleted file mode 100644 index 1f1408fb..00000000 --- a/src/cpu/scpu/timing/timeshift.cpp +++ /dev/null @@ -1,22 +0,0 @@ -alwaysinline void sCPU::timeshift_forward(uint clocks, uint &vtime, uint &htime) { - htime += clocks; - if(htime >= status.line_clocks) { - htime -= status.line_clocks; - if(++vtime >= status.field_lines) { - vtime = 0; - } - } -} - -alwaysinline void sCPU::timeshift_backward(uint clocks, uint &vtime, uint &htime) { - if(htime >= clocks) { - htime -= clocks; - } else { - htime += status.prev_line_clocks - clocks; - if(vtime > 0) { - vtime--; - } else { - vtime = status.prev_field_lines - 1; - } - } -} diff --git a/src/cpu/scpu/timing/timing.cpp b/src/cpu/scpu/timing/timing.cpp index 3be22721..2453a906 100644 --- a/src/cpu/scpu/timing/timing.cpp +++ b/src/cpu/scpu/timing/timing.cpp @@ -1,31 +1,16 @@ -#define ntsc_color_burst_phase_shift_scanline() \ - (status.region == SNES::NTSC && status.vcounter == 240 && \ - status.interlace == false && status.interlace_field == 1) +#ifdef SCPU_CPP + +#define ntsc_color_burst_phase_shift_scanline() ( \ + snes.region() == SNES::NTSC && status.vcounter == 240 && \ + ppu.interlace() == false && ppu.field() == 1 \ +) -#include "timeshift.cpp" #include "irq.cpp" #include "joypad.cpp" -uint16 sCPU::vcounter() { return status.vcounter; } -uint16 sCPU::hclock() { return status.hclock; } - -bool sCPU::interlace() { return status.interlace; } -bool sCPU::interlace_field() { return status.interlace_field; } -bool sCPU::overscan() { return status.overscan; } -uint16 sCPU::region_scanlines() { return status.region_scanlines; } - -uint sCPU::dma_counter() { return (status.dma_counter + status.hclock) & 7; } - -void sCPU::set_interlace(bool r) { - status.interlace = r; - update_interrupts(); -} - -void sCPU::set_overscan (bool r) { - status.overscan = r; - status.vblstart = (status.overscan == false) ? 225 : 240; - update_interrupts(); -} +uint16 sCPU::vcounter() { return status.vcounter; } +uint16 sCPU::hcounter() { return status.hcounter; } +uint sCPU::dma_counter() { return (status.dma_counter + status.hcounter) & 7; } /***** * One PPU dot = 4 CPU clocks @@ -37,16 +22,14 @@ void sCPU::set_overscan (bool r) { * Dot 323 range = { 1292, 1294, 1296 } * Dot 327 range = { 1310, 1312, 1314 } *****/ -uint16 sCPU::hcounter() { - if(ntsc_color_burst_phase_shift_scanline() == true) { - return (status.hclock >> 2); - } - return (status.hclock - ((status.hclock > 1292) << 1) - ((status.hclock > 1310) << 1)) >> 2; +uint16 sCPU::hdot() { + if(ntsc_color_burst_phase_shift_scanline() == true) return (status.hcounter >> 2); + return (status.hcounter - ((status.hcounter > 1292) << 1) - ((status.hcounter > 1310) << 1)) >> 2; } void sCPU::add_clocks(uint clocks) { if(status.dram_refreshed == false) { - if(status.hclock + clocks >= status.dram_refresh_position) { + if(status.hcounter + clocks >= status.dram_refresh_position) { status.dram_refreshed = true; clocks += 40; } @@ -56,25 +39,21 @@ void sCPU::add_clocks(uint clocks) { scheduler.addclocks_cpu(clocks); clocks >>= 1; - while(clocks--) { - status.hclock += 2; - if(status.hclock >= status.line_clocks) { scanline(); } + while(clocks--) { + history.enqueue(status.vcounter, status.hcounter); + status.hcounter += 2; + if(status.hcounter >= status.line_clocks) scanline(); poll_interrupts(); } } void sCPU::scanline() { - status.hclock = 0; + status.hcounter = 0; status.dma_counter = (status.dma_counter + status.line_clocks) & 7; - - if(++status.vcounter >= status.field_lines) { - frame(); - } - - status.prev_line_clocks = status.line_clocks; + if(++status.vcounter >= status.field_lines) frame(); status.line_clocks = (ntsc_color_burst_phase_shift_scanline() == false) ? 1364 : 1360; -//dram refresh occurs once every scanline + //dram refresh occurs once every scanline status.dram_refreshed = false; if(cpu_version == 2) { if(ntsc_color_burst_phase_shift_scanline() == false) { @@ -86,29 +65,30 @@ void sCPU::scanline() { } } -//hdma triggers once every visible scanline + //hdma triggers once every visible scanline status.line_rendered = false; - status.hdma_triggered = (status.vcounter <= (!overscan() ? 224 : 239)) ? false : true; + status.hdma_triggered = (status.vcounter <= (ppu.overscan() == false ? 224 : 239)) ? false : true; ppu.scanline(); snes.scanline(); update_interrupts(); - if(status.auto_joypad_poll == true && status.vcounter == (!overscan() ? 227 : 242)) { - snes.poll_input(); + if(status.auto_joypad_poll == true && status.vcounter == (ppu.overscan() == false ? 227 : 242)) { + snes.input.poll(); run_auto_joypad_poll(); } } -void sCPU::frame() { +void sCPU::frame() { + ppu.frame(); + snes.frame(); + status.vcounter = 0; - status.interlace_field ^= 1; - status.prev_field_lines = status.field_lines; - status.field_lines = (status.region_scanlines >> 1); -//interlaced even fields have one extra scanline -//(263+262=525 NTSC, 313+312=625 PAL) - if(status.interlace == true && status.interlace_field == 0)status.field_lines++; + status.field_lines = (snes.region() == SNES::NTSC ? 525 : 625) >> 1; + //interlaced even fields have one extra scanline + //(263+262=525 NTSC, 313+312=625 PAL) + if(ppu.interlace() == true && ppu.field() == 0) status.field_lines++; status.hdmainit_triggered = false; if(cpu_version == 1) { @@ -116,9 +96,6 @@ void sCPU::frame() { } else { status.hdmainit_trigger_position = 12 + dma_counter(); } - - ppu.frame(); - snes.frame(); } /***** @@ -129,7 +106,7 @@ void sCPU::frame() { alwaysinline void sCPU::precycle_edge() { if(status.dma_state == DMASTATE_CPUSYNC) { status.dma_state = DMASTATE_INACTIVE; - uint n = status.clock_count - (status.dma_clocks % status.clock_count); + uint n = status.clock_count - (status.dma_clocks % status.clock_count); add_clocks(n ? n : status.clock_count); } } @@ -141,34 +118,33 @@ alwaysinline void sCPU::precycle_edge() { *****/ void sCPU::cycle_edge() { if(status.line_rendered == false) { - if(status.hclock >= status.line_render_position) { + if(status.hcounter >= status.line_render_position) { status.line_rendered = true; ppu.render_scanline(); } } switch(status.dma_state) { - case DMASTATE_INACTIVE: - break; + case DMASTATE_INACTIVE: break; - case DMASTATE_DMASYNC: - status.dma_state = DMASTATE_RUN; - break; + case DMASTATE_DMASYNC: { + status.dma_state = DMASTATE_RUN; + } break; - case DMASTATE_RUN: - status.dma_state = DMASTATE_CPUSYNC; - status.dma_clocks = 8 - dma_counter() + 8; - add_clocks(status.dma_clocks); + case DMASTATE_RUN: { + status.dma_state = DMASTATE_CPUSYNC; + status.dma_clocks = 8 - dma_counter() + 8; + add_clocks(status.dma_clocks); - if(status.hdmainit_pending) { hdma_init(); status.hdmainit_pending = false; } - if(status.hdma_pending) { hdma_run(); status.hdma_pending = false; } - if(status.dma_pending) { dma_run(); status.dma_pending = false; } + if(status.hdmainit_pending) { hdma_init(); status.hdmainit_pending = false; } + if(status.hdma_pending) { hdma_run(); status.hdma_pending = false; } + if(status.dma_pending) { dma_run(); status.dma_pending = false; } - break; + } break; } if(status.hdmainit_triggered == false) { - if(status.hclock >= status.hdmainit_trigger_position || status.vcounter) { + if(status.hcounter >= status.hdmainit_trigger_position || status.vcounter) { status.hdmainit_triggered = true; hdma_init_reset(); if(hdma_enabled_channels()) { @@ -185,7 +161,7 @@ void sCPU::cycle_edge() { } if(status.hdma_triggered == false) { - if(status.hclock >= 1106) { + if(status.hcounter >= 1106) { status.hdma_triggered = true; if(hdma_active_channels()) { add_clocks(18); @@ -211,7 +187,7 @@ void sCPU::cycle_edge() { * trigger during certain events (immediately after DMA, writes to $4200, etc) *****/ void sCPU::last_cycle() { - if(counter.irq_delay) { return; } + if(counter.irq_delay) return; status.nmi_pending |= nmi_test(); status.irq_pending |= irq_test(); @@ -235,27 +211,17 @@ void sCPU::timing_reset() { status.vcounter = 0; status.hcounter = 0; - status.hclock = 0; - status.interlace = 0; - status.interlace_field = 0; - status.overscan = false; - status.region_scanlines = (status.region == SNES::NTSC) ? 525 : 625; - status.vblstart = 225; - - status.field_lines = status.region_scanlines >> 1; + status.field_lines = (snes.region() == SNES::NTSC ? 525 : 625) >> 1; status.line_clocks = 1364; - status.prev_field_lines = status.region_scanlines >> 1; - status.prev_line_clocks = 1364; - - status.line_rendered = false; + status.line_rendered = false; status.line_render_position = min(1112U, (uint)config::ppu.hack.render_scanline_position); - status.dram_refreshed = false; + status.dram_refreshed = false; status.dram_refresh_position = (cpu_version == 1) ? 530 : 538; - status.hdmainit_triggered = false; + status.hdmainit_triggered = false; status.hdmainit_trigger_position = 0; status.hdma_triggered = false; @@ -278,12 +244,16 @@ void sCPU::timing_reset() { status.dma_state = DMASTATE_INACTIVE; status.dma_pending = false; status.hdma_pending = false; - status.hdmainit_pending = false; + status.hdmainit_pending = false; + + history.reset(); -//initial latch values for $213c/$213d -//[x]0035 : [y]0000 (53.0 -> 212) [lda $2137] -//[x]0038 : [y]0000 (56.5 -> 226) [nop : lda $2137] -//add_clocks(186); + //initial latch values for $213c/$213d + //[x]0035 : [y]0000 (53.0 -> 212) [lda $2137] + //[x]0038 : [y]0000 (56.5 -> 226) [nop : lda $2137] + add_clocks(186); } #undef ntsc_color_burst_phase_shift_scanline + +#endif //ifdef SCPU_CPP diff --git a/src/cpu/scpu/timing/timing.h b/src/cpu/scpu/timing/timing.h index 975aa1b5..12185d57 100644 --- a/src/cpu/scpu/timing/timing.h +++ b/src/cpu/scpu/timing/timing.h @@ -1,45 +1,57 @@ uint16 vcounter(); uint16 hcounter(); - uint16 hclock(); + uint16 hdot(); + uint dma_counter(); - bool interlace(); - bool interlace_field(); - bool overscan(); - uint16 region_scanlines(); + void add_clocks(uint clocks); + void scanline(); + void frame(); - void set_interlace(bool r); - void set_overscan(bool r); - - uint dma_counter(); - - void add_clocks(uint clocks); - void scanline(); - void frame(); - - void precycle_edge(); - void cycle_edge(); - void last_cycle(); + void precycle_edge(); + void cycle_edge(); + void last_cycle(); uint32 clocks_executed(); - void timing_power(); - void timing_reset(); + void timing_power(); + void timing_reset(); + + //timeshifting -- needed by NMI and IRQ timing + struct History { + struct Time { + uint16 vcounter; + uint16 hcounter; + } time[32]; + unsigned index; + alwaysinline void enqueue(uint16 vcounter, uint16 hcounter) { + Time &t = time[index++]; + index &= 31; + t.vcounter = vcounter; + t.hcounter = hcounter; + } + alwaysinline void query(unsigned offset, uint16 &vcounter, uint16 &hcounter) { + Time &t = time[(index - (offset >> 1)) & 31]; + vcounter = t.vcounter; + hcounter = t.hcounter; + } + void reset() { + index = 0; + for(unsigned i = 0; i < 32; i++) time[i].vcounter = time[i].hcounter = 0; + } + History() { reset(); } + } history; -//timeshift.cpp - void timeshift_forward (uint clocks, uint &v, uint &h); - void timeshift_backward(uint clocks, uint &v, uint &h); + //irq.cpp + enum { IRQ_TRIGGER_NEVER = 0x3fff }; + void update_interrupts(); + void poll_interrupts(); + void nmitimen_update(uint8 data); + void hvtime_update(uint16 addr); + bool rdnmi(); + bool timeup(); -//irq.cpp -enum { IRQ_TRIGGER_NEVER = 0x3fff }; - void update_interrupts(); - void poll_interrupts(); - void nmitimen_update(uint8 data); - void hvtime_update(uint16 addr); - bool rdnmi(); - bool timeup(); + bool irq_pos_valid(); + bool nmi_test(); + bool irq_test(); - bool irq_pos_valid(); - bool nmi_test(); - bool irq_test(); - -//joypad.cpp - void run_auto_joypad_poll(); + //joypad.cpp + void run_auto_joypad_poll(); diff --git a/src/data/controller.h b/src/data/controller.h new file mode 100644 index 00000000..6b7175e3 --- /dev/null +++ b/src/data/controller.h @@ -0,0 +1,1013 @@ +static char enc_controller[] = { + "_v8B8AHwAfAB8AHwAfAB8P8B8AHwAfAB8AHwAfAB8AHw_wHwAfAB8AHwAfAB8AHw" + "AfB_AfAB8AHwAfAB8AHwAeD9AP396OjomJiZAHBwcG5ubmpqAGpYWFhLS0tBAEFB" + "Ojo6MzMz8QYAy8vLOfAB8AHwAfD_AfAB8AHwAfAB8AHwAfAB8P8B8AHwAfAB8AHw" + "AfAB8AHw_wHwAfAB8AHwAfAB8AHwAfD_AfAB8AHwAfAB8AHwAfAB8P8B8AHwAfAB" + "8AHwAfAB8AHw_wHwAfAB8AHwAfAB8AHwAfD_AfAB8AHwAfAB8AHwAfABwAD8_PzU" + "1NR8fAB8cXFxb29vaQBpaVtbW05OTgBEREQ8PDw3N4A3UlJS3t7eOfD_AfAB8AHw" + "AfAB8AHwAfAB8P8B8AHwAfAB8AHwAfAB8AHw_wHwAfAB8AHwAfAB8AHwAfD_AfAB" + "8AHwAfAB8AHwAfAB8P8B8AHwAfAB8AHwAfAB8AHw_wHwAfAB8AHwAfAB8AHwAfD_" + "AfAB8AHwAfAB8AHwAfAB8AcB8AHwAcD-_v7FxCTFcwEAcnK7CGZmAGZZWVlPT09J" + "AEhIPz4-PT0-wHh4eO_v7znwAfD_AfAB8AHwAfAB8AHwAfAB8P8B8AHwAfAB8AHw" + "AfAB8AHw_wHwAfAB8AHwAfAB8AHwAfD_AfAB8AHwAfAB8AHwAfAB8P8B8AHwAfAB" + "8AHwAfAB8AHw_wHwAfAB8AHwAfAB8AHwAfD_AfAB8AHwAfAB8AHwAfAB8AMB8Fz0" + "r7Gwd3d3CHZ1drsIZWVmWQBaWVNSUUtLSghCQUIaDZ2dnPP88_M58AHwAfAB8AHw" + "AfD_AfAB8AHwAfAB8AHwAfAB8P8B8AHwAfAB8AHwAfAB8AHw_wHwAfAB8AHwAfAB" + "8AHwAfD_AfAB8AHwAfAB8AHwAfAB8P8B8AHwAfAB8AHwAfAB8AHw_wHwAfAB8AHw" + "AfAB8AHwAfA_AfAB8AHwAfAB8AHA-_wA_JiYmXt8e3oMeXm7CLgIW1tZUSBQTkpK" + "SFwERUXwRbGxsTbwAfAB8AHw_wHwAfAB8AHwAfAB8AHwAfD_AfAB8AHwAfAB8AHw" + "AfAB8P8B8AHwAfAB8AHwAfAB8AHw_wHwAfAB8AHwAfAB8AHwAfD_AfAB8AHwAfAB" + "8AHwAfAB8P8B8AHwAfAB8AHwAfAB8AHw_wHwAfAB8AHwAfAB8AHwAfAA9fb2hYaH" + "fX0AfHl3eHJycWcAZ2hbXFlPUlEAR0dJQUBASkrwSsLCwjbwAfAB8AHw_wHwAfAB" + "8AHwAfAB8AHwAfD_AfAB8AHwAfAB8AHwAfAB8P8B8AHwAfAB8AHwAfAB8AHw_wHw" + "AfAB8AHwAfAB8AHwAfD_AfAB8AHwAfAB8AHwAfAB8P8B8AHwAfAB8AHwAfAB8AHw" + "_wHwAfAB8AHwAfAB8AHwEf0A6erqgYCAfn4AfnZ3dnFycmkAaGlaWlhSUVAAR0ZG" + "QkJCTEzwTMrKyjnwAfAB8AHw_wHwAfAB8AHwAfAB8AHwAfD_AfAB8AHwAfAB8AHw" + "AfAB8P8B8AHwAfAB8AHwAfAB8AHw_wHwAfAB8AHwAfAB8AHwAfD_AfAB8AHwAfAB" + "8AHwAfAB8P8B8AHwAfAB8AHwAfAB8AHw_wHwAfAB8AHwAfAB8AHwAcAA_f392tra" + "gYAAgoCBgXh3eW8Ab29laGdYWFUATk1ORkVDREOAQ1JSUtbW1jnw_wHwAfAB8AHw" + "AfAB8AHwAfD_AfAB8AHwAfAB8AHwAfAB8P8B8AHwAfAB8AHwAfAB8AHw_wHwAfAB" + "8AHwAfAB8AHwAfD_AfAB8AHwAfAB8AHwAfAB8P8B8AHwAfAB8AHwAfAB8AHw_wHw" + "AfAB8AHwAfAB8AHwAfAHAfAB8Lj42NjYgYEAgYKCgnp6em8Abm5iZGJUVVUATUtO" + "RkZGRUSARFZWVuPj4znw_wHwAfAB8AHwAfAB8AHwAfD_AfAB8AHwAfAB8AHwAfAB" + "8P8B8AHwAfAB8AHwAfAB8AHw_wHwAfAB8AHwAfAB8AHwAfD_AfAB8AHwAfAB8AHw" + "AfAB8P8B8AHwAfAB8AHwAfAB8AHw_wHwAfAB8AHwAfAB8AHwAfAHAfAB8AHw09PT" + "g4NihAMAent-uAhcBFWAV1VNTEtHSAIAgEddXV3v7-828P8B8AHwAfAB8AHwAfAB" + "8AHw_wHwAfAB8AHwAfAB8AHwAfD_AfAB8AHwAfAB8AHwAfAB8P8B8AHwAfAB8AHw" + "AfAB8AHw_wHwAfAB8AHwAfAB8AHwAfD_AfAB8AHwAfAB8AHwAfAB8P8B8AHwAfAB" + "8AHwAfAB8AHwBwHwAfAB8MzMzIWFAIWEg4R9fHxyAHJxZGNjV1VVAE9OTUhJSEpL" + "gEtoaGj29vY28P8B8AHwAfAB8AHwAfAB8AHw_wHwAfAB8AHwAfAB8AHwAfD_AfAB" + "8AHwAfAB8AHwAfAB8P8B8AHwAfAB8AHwAfAB8AHw_wHwAfAB8AHwAfAB8AHwAfD_" + "AfAB8AHwAfAB8AHwAfAB8P8B8AHwAfAB8AHwAfAB8AHwBwHwAfAB8MrJyoWFAIaF" + "hIV-fX1yAHJzZWRlWVlXAFBOT0xMTE5PgE9ra2v4-Pg28P8B8AHwAfAB8AHwAfAB" + "8AHw_wHwAfAB8AHwAfAB8AHwAfD_AfAB8AHwAfAB8AHwAfAB8P8B8AHwAfAB8AHw" + "AfAB8AHw_wHwAfAB8AHwAfAB8AHwAfD_AfAB8AHwAfAB8AHwAfAB8P8B8AHwAfAB" + "8AHwAfAB8AHwBwHwAfAB8MbGxoeHAodfBH9-gHN0dABlZWZaWFdRUABQTk1NT05O" + "cuBycvn5-TbwAfAB8P8B8AHwAfAB8AHwAfAB8AHw_wHwAfAB8AHwAfAB8AHwAfD_" + "AfAB8AHwAfAB8AHwAfAB8P8B8AHwAfAB8AHwAfAB8AHw_wHwAfAB8AHwAfAB8AHw" + "AfD_AfAB8AHwAfAB8AHwAfAB8P8B8AHwAfAB8AHwAfAB8AHwAwHwXASJiImFhoYA" + "f39_dHR0Z2cAZ1pbWVJRUE4ATk5QUFB3d3f_XPQB8AHwAfAB8AHwAfAB8P8B8AHw" + "AfAB8AHwAfAB8AHw_wHwAfAB8AHwAfAB8AHwAfD_AfAB8AHwAfAB8AHwAfAB8P8B" + "8AHwAfAB8AHwAfAB8AHw_wHwAfAB8AHwAfAB8AHwAfD_AfAB8AHwAfAB8AHwAfAB" + "8B8B8AHwAfAB8Fw0jY2NAImJiYB_gHZ3CHZpaVwEWFJSUgBOT05QUVGAgPB--_v7" + "NvAB8AHwAfD_AfAB8AHwAfAB8AHwAfAB8P8B8AHwAfAB8AHwAfAB8AHw_wHwAfAB" + "8AHwAfAB8AHwAfD_AfAB8AHwAfAB8AHwAfAB8P8B8AHwAfAB8AHwAfAB8AHw_QFg" + "_gHwAfAB8AHwAfAB8P8BUIfwAfAB8AHwAfAB8AHwBwHwAfBcBI6OjomIAIiBgIB3" + "dndpAGpqWFtaVFJSCE9PT1wEgYCB9_z39zbwAfAB8AHwAfAB8P8B8AHwAfDg8QHw" + "AfAB8AHw_wHw3fEB8AHwAfAB8AHwAfD_AfAB8AHwAfAB8AHwAfAB8P8B8AHwAfAB" + "8AHwAfAB8AHwvwHwAfAB8AHwXvIBYP0BgP78AeCMBwHwAfAB8AHwAcD_acCEYJHy" + "AfAB8AHwAfAB8AcB8AHwAWDPz8-KigCKi4uLgYGBdwB2eGlra1xbXRhVVVQRDQEA" + "e3t7-Pb29jbwAfAB8AHwAfD_AfAB8AHw7JHm8ePxAfAB8P8B8AHw4PHjkeaR6fEB" + "8AHw_wHwAfAB8AHwAfAB8AHwAfD_AfAB8AHwAfAB8AHwAfAB8P8B8AHwAfAB8AHw" + "AfBhwlWS6voBUPkBIPgBgIkHAfD_ATBIAwHwAfABkELAZmB4MP-BAJRiZfQB8AHw" + "AfAB8AHwRwHwAfABMNjY2FkEjACMjIKDgnh4eQBrbWxfYF9YWQJYXDR1dXT19fX_" + "NvAB8AHwAfAB8AHwAfDy8f_yke9h6cHa8QHwAfDX8QHw_0yS5mEBMICU7_EB8AHw" + "AfD_AfAB8AHwAfAB8AHwAfAB8P8B8AHwAfAB8AHwAfAB8AHw_wHwAfAB8AHwW_Je" + "MkdkVWJXHwIVAwEA9AEg8wEg8sUBUPEB8PHx8AHwAfD_AeBCkF0waQB1MH4ApjJl" + "9P9w8gHwAfAB8AHwAfAB8FzECIiIiFwUgYJ5eSB6bW9tYVwEWFeIUlFTXARpaWnS" + "AP828AHwAfAB8AHwAfAB8P7B_1P072HsYebxufEB8NrxAfD_Q2JSMulh7GF9NHFk" + "a_T78f8B8AHwAfAB8AHwAfAB8AHw_wHwAfAB8AHwAfAB8AHwAfD_AfAB8AHwAfAB" + "8FPEUJRNZAdbYlgyTDLv7-_t7VTt7AEg6wEg6gEg6T0BUOcB8AHwAfAB8OfnfugB" + "IFQAYABpAHIAewDu_O7uozKWAGj0zZgB8AHwHwHwAfAB8AHwAcDj4-MohISEXBSC" + "XBRvbQBtYWJiWFhZUwBSU09QUFtcXP-9ADbwAfAB8AHwAfAB8AHw_6P4UJSfAO8x" + "bgHvMekxjAH_lTGt8QHwAfAB8E4w5jEBAP9SMl4y7DFwAu9hWpZrZGiU_2X0AfAB" + "8AHwAfAB8AHwAfD_AfAB8AHwAfAB8AHwAfAB8P8B8AHwAfAB8AHwqMZbwlCU_01k" + "SjRYYvgx8gF5MmY2ATD_7zEr-wHwAfAB8AHwAQBbAuOTMIcA5ubmmQAMAGWU_9vG" + "AfAB8AHwAfAB8AHwAZCDVgFcZICAgHp6XAQAbmVlZVpaWlMAU1RSUlJVVVX_wAA5" + "8AHwAfAB8AHwAfAB8P-YylOU9TGiAGgBYjF6AY_x_wHwAfAB8CP9MTK7mFA0eWL_" + "YGZ8Mms0KgBolFr2ZfQB8P8B8AHwAfAB8AHwAfAB8AHw_wHwAfAB8AHwAfAB8AHw" + "AfD_AfAB8Kb4o8igOLABqzbFAf_UMV4yvTN4_ymdL80B8AHwPwHwAfABYPdinACZ" + "AMrK_sr9AqIwhQK3AJcycJIB8H8B8AHwAfAB8AHwAcBcZIMghIN-gX5cBGhpAGhZ" + "WFhTUlRQAFBPVFRU3t7e_zbwAfAB8AHwAfAB8AHwS_bHz2NWAVwByMjICQBuAf_B" + "ZZXxAfAB8AHwAfA3Yr8x_330GwCfAJcynADnNl2WIQD_zfjK-AHwAfAB8AHwAfAB" + "8P8B8AHwAfAB8AHwAfAB8AHw_wHwAfAB8AHwVvSpmKZoU5T_WGK5AV8EFQCqMVNk" + "i2Ipnf8BYIHP9QEB8AHwAfAB8AEwB5MAnwCcAOXl5dDQANCpqanc3Nzg4ODg5OTk" + "WwsYANWW_wnzAfAB8AHwAfAB8AHwAQABXGSBgIF5eXlyAHJyZ2hnW1xb_7IIFw1c" + "NDbwAfAB8AHwAfD_AfAB8K9lTmbQPlABVgFcAfikpKRoAW4ByTOfPAHw_wHwAfAB" + "8AGQMTJAMgFgEGv_cWR3ZAkAYzauAGg0TGtllP9ilMf4AfAB8AHwAfAB8AHw_wHw" + "AfAB8AHwAfAB8AHwAfD_AfAB8AHwVvSp-CM0UzT-Ov8MBp8zngFQlJdoi2IZOAEA" + "_xswgWwJACSWAfAB8AHwAfD_AfAB8AHwAfAB8AHwAfAB8H8B8AHwAQBWAR0ByyUD" + "8P34_Pv8mgH6MjzwAfAB8P8B8AHwAfAB8AHwAfAB8AHw_wHwAfAB8AHwMQFMMgNv" + "wDb_gDQybYIyazQkAFYEvgIEBf9mOWA2wmrH-GL0AfAB8AHw_wHwAfAB8AHwAfAB" + "8AHwAfD_AfAB8AHwAfAB8Fb0VvSpOP-uNqY4lQEBAAwAGwAjnStr_2k_FQBFAGL6" + "AfAB8AHwAfD_AfAB8AHwAfAB8AHwAfAB8P8B8AHwAfABYPsHXAF6AYkx_VAU_dwS" + "ODFKAQYA8Ts88P8B8AHwAfAB8AHwAfAB8AHw_wHwAfAB8AHwAfAB8MTydJT_SjQT" + "O-g4awTWOAEADAB0Af96AWlpLz3BMmL0xPIB8AHw_wHwAfAB8AHwAfAB8AHwAfD_" + "AfAB8AHwAfAB8AHwVvT_nP8DNv46TQoJAHoBUzTQaBVm_1DEafAB8AHwAfAB8AHw" + "AfD_AfAB8AHwAfAB8AHwAfAB8AMB8AGQ2NjY3d3d53IH4UlbAfj5hAFTAesy_9w4" + "-gI88AHwAfAB8AHwAfD_AfAB8AHwAfAB8AHwAfAB8P8B8AHw2fKRApcCRAHNMgZv" + "j1s7cQEPAMEI4uLi4gL_kQshMHcxQTdi9AHwAfAB8P8B8AHwAfAB8AHwAfAB8AHw" + "_wHwAfAB8AHwAfCAYV4-PgH_rJhWZHcBdAGpyFM0qT4VAP968Uj2ZmABYLMBAfAB" + "8AHw_wHwAfAB8AHwAfAB8AHwAfCPAfAB8AHwAZDT09NfBA87B8QF3wIBEOvs6uv5" + "CCDr630BawEbAJIBPPD_AfAB8AHwAfAB8AHwAfAB8P8B8AHwAfAB8AHwAfD2MwEw" + "_2EyKPuFkicAwQJeC2WUzTjjFQBiAePj4-4CCQB0Af8pPYU75WJjMEjzAfAB8AHw" + "_wHwAfAB8AHwAfAB8AHwAfD_AfAB8AHwAfBZZOczNWR6Mf9WNC8KZQEYAFPEBgAS" + "AFOU_wbPPmQ4neiYNjbUMQHwAfD_AfAB8AHwAfAB8AHwAfAB8P8B8AHwAfAB8AEw" + "6AIB8AEA_yrwAfAB8AHwAfAB8AHwAfD_AfAB8AHwAfAB8AHwAfABwP95aAEAZDip" + "MmpooPKgki0A_z4EuwUObfw_xDgpPf0C4jj_XTDBmE7zAfAB8AHwAfAB8P8B8AHw" + "AfAB8AHwAfAB8AHw_wHwWfRZlK84SgHTAgI9dAH_UzERNN8FxzUjylP9U207NP85" + "AFcAAQDLAf0-ATBL8wHw_wHwAfAB8AHwAfAB8AHwAfD_AfAB8AHwAfAB8AHwAfAB" + "8P8B8AHwAfAB8AHwAfAB8AHw_wHwAfAB8AHwAfAB8AEATGL_WzKFOHc0Xj5zOLJi" + "tZKyYv8wACAKET1TB1cABgAPAMQI-Ofn53o0JAD2P6abVPP_AfAB8AHwAfAB8AHw" + "AfAB8P8B8AHwAfAB8AHwAfCL_g5k_48BmwGpBVkEuwUMAB00CQD_1jW4leScQmCD" + "NE00RGQQ8v8B8AHwAfAB8AHwAfAB8AHw_wHwAfAB8AHwAfAB8AHwAfD_AfAB8AHw" + "AfAB8AHwAfAB8P8B8AHwAfAB8AHwAfAB8AHw_wHwAfBxZGs02Tho9MSSJAAA5OTk" + "3Nzc4eGO4QkAWQQJAODg4MEI_3QETgBLACCdes0B8AHwAfD_AfAB8AHwAfAB8AHw" + "AfAB8P8B8AHwAfBc9FYxKQFkODsB_1k0rAWLPsE1VgFTAXCe7vj_UPQB8AHwAfAB" + "8AHwAfAB8P8B8AHwAfAB8AHwAfAB8AHw_wHwAfAB8AHwAfAB8AHwAfD_AfAB8AHw" + "AfAB8AHwAfAB8P8B8AHwAfAB8AHwaPTQOLsCD2XEDDNNB18Ezc3N0YEBIMzMzNra" + "2l80_0sAXzQ8kFrzAfAB8AHwAfD_AfAB8AHwAfAB8AHwAfAB8P8B8AHwXJQjAXA4" + "NQELPa8F_7UFTQG7CBcKGABKkabIU5T_1PEB8AHwAfAB8AHwAfAB8P8B8AHwAfAB" + "MNIAfwIBAMk5_w8AkQLkMIVlHz5YNTMw-T__AQCXNZc7_DkgwRswxztaMP8JMDmQ" + "VjE8MBIzATBvMCQw_7lhGzCHMBsAUTMzYOg-ATD_PwDYADkAbDAB8AHwAfAB8D8B" + "8AHwAfAB8AFgiALX1wLX2wO-vr63t7dCrgFQpqamngFQqwSrqxhgvLy8wcH-wRQE" + "GgQdNwHwFzFlZMr4AzJnJADKysq5ubkAqampvb2939_-3774KgAa_QHwAfAB8AHw" + "_wHwAfAB8AHwAfAB8AHwAfD_AfAB8FnERwEpAcEyOwFWBP9HAakFsgVcBP9pATAw" + "kKb4_wHwAfAB8AHwAfAB8AHwQ_IfmfAB8AHwnDZVBbOzsxi0tLQOAYUC3t7eOMDA" + "wBEBUwTBAtbWItYkANDQ0NACv7_-vw8AEQHQAiQAEgABAN8C8R0BycnJDAAVACoA" + "jWD_CQBFABsAOQCvAkIDUQBaABE_MN3d3TkAx8fH4x4wNQrGxsbwDzMD4gIRYDDL" + "y8tCAMXFxYEMALu7u9XV1VcAwMPDw8LCwjAAUQD_MwAJAAEAEgA2ABsAxgBfNPGo" + "ANTU1IoDrgN1AF_0_wHwAfAB8AHwAfAB8AHAfALjKQHPA62trT5kAfAB8AMB8AEA" + "oqKir6-v_wIBngEyBx_yZWQUAYuYATAHPjc4AUsArKys2Nj-2B0NFgJfNKA1MwCS" + "NB39_wHwAfAB8AHwAfAB8AHwAfD_AfAB8AHwAfAB8AHwCwEsNP9BAVwBZQGLAjsB" + "QQEYAwkA_zWaqcimAqn4AfAB8AHwAfD_AfAB8AHwE8i6ANc3gTYk8AcB8LKYcAJ3" + "d3c1NYA1MTExb29vPQJxYQIyMjIdAfwDCQCZRJmZFAFmZmYMADbgNjY_Pz8sBNID" + "DwDANzc3OTk5lQRIAAhDQ0NFADQ0NDo8OjoXBD4BYDBHBEhIEEgwMDA2ALa2tvEz" + "AOLi4gYDPwA5ACQA-EBAQHsAaQNUACoAeADHbAAnAGAweXl5hwN1AIF7ADg4OJqa" + "mgMDMWkAOzs7LwQ1BFZWRlakAbMEMzMzNjBd_F1dYAPUAWkDlgA2ABUA-ImJiY1p" + "AfAB8AHwAfCPAfAB8AGQCwHS0tIlBfinp6cg9AHwAfAB8AHw4wEAaATExMQJCZcF" + "x_gfLwHH-PNv5QXBBdnZ2f9fNOsFHTokY1_0AfAB8AHw_wHwAfAB8AHwAfAB8AHw" + "AfD_AfAB8Fn0tZjiO5cC4gI7Af9lAWJhVmSLO1b0AfAB8AHwfwHwAfAB8AHwjfbv" + "AZMDjwCPj3R0dHp6en-9BlQDLTwB8AHwAQDtAJ8En597A87Ozm5uEG5LS0tGAn9_" + "f_hCQkL_AAcIFATAAwgBODw8POcDIAQSAJCQPpANCCQAYQjBAioAuLj-uEUAUwSE" + "ACEAPANuAZM8B-EDFwT5A9PT05SU_pTtA2MAJzASAGwDWQT_A4EyBIqKilJSUvcF" + "gU0EkZGRSUlJ3gD_0gCLCMIEjwRQBx4A7Ac8AAg-Pj6WAJWVlWhEaGjjAY2NjWYA" + "z2DPz5OTk1owZgBbIFtbY2Nj7AGBgfCBUFBQPAB89QHwAfD_AfAB8AHwAWB4AJAA" + "WAUF9D8B8AHwAfAB8AHwATCkpH6kIDpi9GI0LgIA8xgA4fzh4V8EBgA-AcAALQAq" + "AP9wAtk7UQNi9AHwAfAB8AHw_wHwAfAB8AHwAfAB8AHwAfD_AfBZZLU4FAEaATUB" + "Dj1WBP9lAR06WTGs-Fb0AfAB8AHwPwHwAfAB8AHwH8I8A5ubjptjA0sDAWBgYGDt" + "Ax8bADPwAfDkAO0AZ2dnCFRUVHMFWVlZYYxhYQkAkwNlZWU9AvidnZ1UADQOyQaR" + "CGsEB_YDNAhuBIiIiF5ejl5wAmwAOwGAgICvCPEMALW1tTMAwwyEMFABOLq6ujMA" + "2wNHBHJyxnKUCFoAg4ODMACBADixsbEUBIEAAANkZPBkdnZ2hgEhAA4EKgDHswEM" + "AIkETExMEgZvAEcPAEgJjwROTk53BGr8amqKAOMBYgTBCAkALQA_UQB9B28AigBd" + "AwcChob-hs0IbwBc9AHwAfAB8AHwxwHwAQA7Ab6-vpMA9vN_AfAB8AHwAfAB8AHw" + "AQCq_KqqFwr3MmL0xJgGAIY3_1MEHg-DBx4_XzTrmF_0AfD_AfAB8AHwAfAB8AHw" + "AfAB8P8B8AHwAfAB8PkwtTgOPSAB_-QJXAQJAFlkTTHHAiEAWgP_VvQB8AHwAfAB" + "8AHwAfAB8McB8AEA5gHKysorCFmU_wEw8wM4NENiP_BcZEAC2AMfCw09AqgDhQIw" + "AD09Pcf8A0EBMgRBQUEUATIEI8UEOwG7u7ufDOPj4uMIBHt7e4Y9TQGMBIhYWFho" + "AZKSkoQAj_AGFQBjACEAoKCgRQDHKQSFCCQAhYWFkgHRBMcLBB4AZwhcXFwaBN8I" + "H0EEEgkeAJYADACEhISPxzLINBo0MwB-fn4zCccsBLoDjQClpaW8AbwE-HBwcDwA" + "4A3xCMsB1ATHigCxAB4AU1NTrjAO8T8B8AHwAfAB8AHAjgWtrf6t5PMB8AHwAfAB" + "8AHwAfD_AfABAJINLwRixOMBMgESw_8PAMUB7AEBAF80JwBfNB09_1_0AfAB8AHw" + "AfAB8AHwAfD_AfAB8AHwAfAB8Fn0ujkCAf8OnVABPW5WZDIBVvQB8AHw_wHwAfAB" + "8AHwAfAB8BCS4AGPBQ1Z9AEA1AfBwcHwAH_VAN4ACjLkANvw8ABnBUh8SEgODRQB" + "HQEGAMM8s_yzszYA0gNACE0BzAyuDANLAJ0CVVVV5-fnwE1NTZqamkEBRQDjdATP" + "A3V1dXQB2wNRAI-lP3IARQBiDXFxcR4AeMzMzBsAGwxmAAkAc4xzcws0wgRra2tf" + "DfHUBK-vr7ABewnGAK0NP8ECLQD1BMhkAQBWBGJi4mIQBcTExCwNBgDBCD90DZgE" + "kACzBLkEWgCcnP6cDgHZC24NTgCuME3xAfAfAfAB8AHwAWAwCampqf_V8wHwAfAB" + "8AHwAfAB8AHw_wHAeQWUCGL0-Twbk4ABXwH_sAoGACoAJMAgnYTzAfAB8P8B8AHw" + "AfAB8AHwAfAB8AHw_wHwr_hWNOQ5IzFBAUs8GjH_eDMFbR4wK_IB8AHwAfAB8B8B" + "8AHwAfAB8ApioaGhx0IMGwxf9KenpwIBdQOIkZGR_geMjIzSA48S_A8ADgHkAzs7" + "O4ED_wwAEQHwDGcITAIgBAIEJwMfSwAkAAEAXAE0CG1tbeOXCMcIycnJEgA2ADwA" + "j0IJoAgqAGUEbGxsbjEjXzSyCJaWlj8AubkQuUtLS64M4ODgR-0D0Q1vAF9fX3oN" + "aeBpabS0tCYNgAS2AR8qAMYAYgekAf4ET09PH9IAbADWAj8AlgCKioo_bgQVADMA" + "EgCKADAAsLD-sNUAGQXoCC0AIQCtBIQA45YMHgC4uLj4AU3xAfD_AfAB8AHwVjST" + "AMnzAfAB8P8B8AHwAfAB8AHwAfABYHQB_8sBVWIBACOdJGPOAUoKaAH_X2RFMyD9" + "YvQB8AHwAfAB8P8B8AHwAfAB8AHwAfAB8Fn0_1k0AgGUAjUBSzM1YQX9AfD_AfAB" + "8AHwAfAB8AHwAfAB8D8BkMU6QwjMA1zESAOxsfCxNDQ0FZBoBDAA6sDxGgHFxcUe" + "AEQEFzHvBwdgDCcA2AY8PDzDww7DKQG3DKsD1NTURgBGRpWVldHR0T9dAC8EuQSZ" + "DHMIQgBhYcZhEgB-DMjIyFcAvDT_nwASAI4IGwkNBXMIuwJXCfg3NzeKA6wCbgTO" + "BB4A8aYIQEBAfwu8AfcCUQB_QgmxAP0ChwChBKcNWgDdRN3dvwSysrKQAI38jY1o" + "BAgBYAAwACUOMwCPCQAmDfgBbABaWloJAIijo6MVANPT0yoAP0fxAfAB8AHwAcBm" + "DK6u_q698wHwAfAB8AHwAfAB8I8B8AHwAfB3AcLCwsEF_yP9g5c7Mc4BKjBOA38y" + "X_T_X_QB8AHwAfAB8AHwAfAB8P8B8AHwAfAB8FnEFzFZNLI4_1k0r8gr8gHwAfAB" + "8AHwAfD_AfAB8AHwasi9APgByz0GAMdhCEgDRGQ_Pz9sA5ADfyNtAQAMMAADvMdR" + "AE4AZOBkZGBgYBEBNQHDDOPJAzECwMDAnQiUCLQG4wIE3AhCQkIbAHsPYAB_GwDe" + "A88MDgTbM94MRA1X4FdX5ubmWQHhNnoEf_8DWAiaCIkEyQyeAe0DRwRHRyEA0NDQ" + "TEziTDIEcHBweAAaDd4AP6EBVwBgAMoCrgAeADg4AjiiCXx8fM_Pz_i_v78yBD0F" + "yAQ2AJAA__IBNwWvCKgApwSKALEJ2ADxCAF4eHgWApUEvQBOAD-3yQHwAfAB8AHA" + "dQC9vf69tPMB8AHwAfAB8AHwAfAfAfAB8AHwAZBvCdXV1f8Zm8HIX5QSBj8PTQEh" + "AIIC_yEwX_QB8AHwAfAB8AHwAfD_AfAB8AHwAfAB8Fn0WTR-M__oMsZpbjQXAa_4" + "AfAB8AHw_wHwAfAB8AHwAfABwLcAygL4y8vLWgNFAwYADADqA__8APMMPjSTAxsM" + "5wMp_QEA-FVVVUIAY8MRAR0EgATHTgBMAqcEhoaGVACrDPhNTU0jAXYCnANCAzUE" + "4w8AYgGCgoKmCGUBAQD_DwCEDHgASAAkAJEIUARWBDGxMLu7u24EQgBlZeJlXwSO" + "jo5aADsEPwCBvwStra1iYmI5CYhqamohAJqamk4A_58ApATsBAkAJAD3Ap4EDACP" + "1wHgAV8NUwS6urquCX_PAB0NUQD3CLYEHgBUCTMcMzPvBIQApw2qqqr_ogBsAHoN" + "E_4B8AHwAfABkPHYAKKioqvzAfAB8AHw_wHwAfAB8AHwAfAB8AHwAQD4tbW14wHB" + "mD5kAQDoO_8bAHMCAQBJAiQA6QHhNl_0_6LzAfAB8AHwAfAB8AHwAfD_AfAB8AHw" + "WfRZZCABZAVZZP8I_QHwAfAB8AHwAfAB8AHwHwHwAfABMPYDSQhWVlZ4NTU1LGQM" + "APgH3wLc_NzcVAMMACII9gB1A1n0gQEAOTk54ODgbTUftTgpAREBeAwUBKurq48S" + "APYMzwNTAX19ffAD41ABcwI9PT2xA1oAOwHxJwlFRUVfBPkDfzInAPh5eXkzAPkD" + "UA2_ZF0Ax6EEUwSrDGNjY1kNOQn_ngFQBF8EMADVDJEIiwgeAB-0AC4FMwC4AnMI" + "fn5-f68yuQTvBBo0EgBpABIAvny-vlwEHQ0UAcAAPACYxJiYUQB0dHQtACcAj9QE" + "EAXXDf4BQ0NDFQn_FQDy8QHwAfAB8AEAVwDQBf-f8wHwAfAB8AHwAfAB8AHw_wHw" + "AfAB8AGQUwqtASD9P5P__AC-CAEACgJf9F_0AfAB8P8B8AHwAfAB8AHwAfAB8AHw" + "_1n0WcT5AAs9GjGfAwwAWgP_WfQB8AHwAfAB8AHwAfAB8H8B8AHAwAB4A_cLL_QB" + "MKg8qKhmA2IEKgD7AczMgMysrKxoaGgnwI9vA0cEXDQFAeTk5E8CB4EDOAQbALm5" + "ueLifuJNDYEPRAECBE4MXwGZYJmZT09PSQJKNNfE19dLAJycnFw0SgTAU1NTm5ub" + "KgC3DP-lDCQAXzGMAYMEpATgBzoI-E5OTuEMfgBOAKMIMAAfWQRfBHgA5wydCNPT" + "0_GnAdvb2ygF5QI_AGAAeFhYWNsM2ABfNGAApfylpbgInwDIBLoA5QKtDQiysrI8" + "AFlZWYkEiYlaAJ-fn0pK_koLAUwFOAEzANIAWfEB8P8B8AHwaAGv-AHwAfAB8AHw" + "_wHwAfAB8AHwAfAB8AHwATD_VQ7XAV9kswGKP19kGABfZP_9PmYzX_Su8wHwAfAB" + "8AHw_wHwAfAB8AHwAfBZ9FmUvTn_C20BAFlkVvQB8AHwAfAB8P8B8AHwAfAB8AHw" + "1QDyAVP0_wFgDAwLAVg4AQAEAg4B8wB4kZGRJ5DhAzEyCTm8RLy8OQzd3d3zAzrE" + "OjoJAMHBwd4MkAOBlgNLS0umpqZXAP9iDbQMUjLmNEsAPABCAFAB8a0EQUFBDg11" + "AMwMUA1fdAS0b7UIhwOtBEcBIOX85eVdAL8EWAiuADAAjw0_GQWpCCkEkQtFAHM4" + "2Nj-2AwARAdsALkEAgHlAiEAeFFRUZwJ-A11AMg0zsTOzjAA1NTU6AJlBP9oDRAC" + "0QR9BDYAcwXSAKdtjwHwAfAB8AGQw8PDjQn_AfAB8AHwAfAB8AHwAfAB8P8B8AHw" + "AfAB8AFgmAF7BsFoH19kTjMNMo8BAQDq6ur_JAAhMF_0X_QB8AHwAfAB8P8B8AHw" + "AfAB8AHwWfRZlPkA__MATwIOMQYAhQJZ9AHwAfD_AfAB8AHwAfAB8AHwAfDdYYHP" + "AJCQkDQ0NFn0-G9vb3gD9gANAvUHSAP_JgQwABQB8wCNAypg3QcOAeNUMAIEl5eX" + "Rw1HAUQEH5YDLwHvN0cBQgA4ODgIxcXFMABbW1uHHIeHMjQGALIIMTEx_1UCegF1" + "AJgEMg11AOoGfAiPcAgUBBg53gxUVFSNAz_wA34ASACGAWgEmw3Z2cbZHgAwANDQ" + "0DsEHgD4ZGRk6zgPAIQAwABIAP-gCBsARwelADwAcQSzDXsAf7EAGABBBPsBaA2k" + "DS0A0fzR0SUOUQDvAQkJtAA2AP8hCTAJnAD78QHwAfABkLwE_2wAh_MB8AHwAfAB" + "8AHwAfD_AfAB8AHwAfAB8AHwywGiCf9flIMBUZOBBnMCX_Rf9Lrz_wHwAfAB8AHw" + "AfAB8AHwAfDHAfBZ9Fk06enp6gAaB_8FAc436wInPwHwAfAB8AHw_wHwAfAB8AHw" + "AfAB8AEAzwARzADHx8eLCHBwcAhra2sGAIqKirUMtbXbAMgHbGxsRBxERLcDATCo" + "DIuLi4HwDHp6ei8vL3UDf34MXjVRMCYE7Qx-DGIETTxNTXgMSgFcBC0AkpLwkmNj" + "Y7oMRwECBDsExxgAVDw2AH5-fiQA2AP4lJSUYQJ2CIgIXgJfBP8YAA4EqACxMBIA" + "PABoDVcJcRsAbW1tYzAkCe0Dz_zPz3AIOQDGAB4AIA05AIFgAHV1dVdXVz8AB9QB" + "GgQbAHFxcYyM_owqAC0JdwTjAVEwqAABAviOjo5zBTAJUAS_BHsA_5YAqgTAAKcE" + "dwRgAFoA_vGPAfAB8AFgRQOkpKSB8_8B8AHwAfAB8AHwAfAB8AHw_wHwAfAB8AHw" + "ATCtATsKAjH_MDZLA1djpAEiApcOX_RfxP-68wHwAfAB8AHwAfAB8AHw_wHwWfRZ" + "xPAACj4GAMM5WfT_AfAB8AHwAfAB8AHwAfAB8P8B8AHw3WHVAOoAwgEGP4oD_wIB" + "bgRpAwHwAQDYDE0NogP_lQRyAzYATmAMAKIMeAO3DB9CACg4KwKxA6cEhYWFf1sI" + "YABcBBIAMQi4NVMBscSxseoDgICAlQQVAI-XCOEGCQDkBpOTk1QA_3QB4Tb_DOUI" + "DwBrDTMAnwz_rgAgBNw4tQKcACcAUA0eAMfQCMgBDwCqqqoxBecAEX4Av7-_EwWC" + "goL4iIiIsQCUCB4AGABRAPEYANLS0v4BUQDGAJAAH-wEoQ2KAF0AGwB7e3v_ggVA" + "BTkAkzD_kAHwAfABYP-jBWYDe_MB8AHwAfAB8AHw_wHwAfAB8AHwAfAB8AHwAZD_" + "pAEGBl_0qgHIN28DPwZf9P9f9AHwAfAB8AHwAfAB8AHw_wHwAfBZ9Flk2ADqALo2" + "DwD_WfQB8AHwAfAB8AHwAfAB8P8B8AHwAfAB8CydDwDmB1b08QFgp6enLAERAQXB" + "DwBx-QzU1NR3BLQDUQzMYMzMxsbGDAABANcc19cPYFkE-QzKysqIzc3NFQDJyck2" + "AD96BAYAWQR5AiYEGDDV1f7VSDAwAC0AGwA_ABgA6gMPRQAYMAYAXWDT09PO_M7O" + "MwAnAC0AFQA2AAEA_w8AGAAJAGkwPAAeMCoAYwD_sQAbABgAJwAvDRsADAAPMOM_" + "MCoAy8vLdQAwAAYA_7oA3Q0VAEgJCvIB8AHArgD_YAMlBQHwAfAB8AHwAfAB8A8B" + "8AHwAfABwLCvr9Yj4CEPMLq5uQ8Av73-vSrwAfABwMUBX_SyNRIA_9UJ1gVfZB4w" + "X_TG8wHwAfD_AfAB8AHwAfAB8AHwWTRcNP-6AMYGqAZHNN4AbQtZZKs2_0_yAfAB" + "8AHwAfAB8AHwAfA_AfAB8AHwAfABMOQAmJjGmFP0ATBMTEyfAxoBIznwOQB5eXlO" + "DHR0cHRoaGgJAKsM8AxcfFxcUw1EDfUHxgwJADfENzcJAFJSUiQAYwxHfgx7DBgA" + "RkZGdw1VjFVVGABcDWFhYR4AjxgAIw2fDBUAODg4OQDAXV1dNjY2DwAPCY9dAPwM" + "VDCtDVhYWBgA_zAACD0MABgAhAA_MAEwLQCPOw2zDeQMDwA_Pz9-AI-zDSoAsQAY" + "AF9fXzlg_2wADACJDRgAIg54AHIAMADxewBAQEBGDn4GEPIB8P8BYJsEvAds8wHw" + "AfAB8AHwPwHwAfAB8AHwAfABMNHPAM_p5-e-vb2rBKqqCQDg3d2hof6hJ_AB8AHw" + "DAZfxKAIeDP_dQO7CF_0mQOfA6UDXDRf9P8B8AHwAfAB8AHwAfAB8AHw_1lkEAVZ" + "lNALwQs9AjYD9gD_EgDXN_kAAfAB8AHwAfAB8P8B8AHwAfAB8AHwAfAB8AEAAd48" + "urq6f39_UQxRUYcDqwNCQkJn4GdnoqKiqAz7MTbwASYBcnJyLi4urTytreQDCQA_" + "AKQHgYHGgQwAAQB3d3erAwkAcasDlpaWVwAMAMkDuIy4uF0ADABBQUHXBP_VAwwA" + "XATdB1wEDAB0BOcDAwkA_AO7u7t8fHyPDAAjBM8Mng2oqKgkMMdIAEIADABiYmJ_" + "COMEgXgwiYmJR0dHDDB_ATAPADkA1QAMAIcAsQBeBF5eDABKSkqwsHCwTU1NJzDI" + "DTkwVMRUVGUNMTExDABNDfigoKBsAHQE_g1cNBHx_wHwAQA8CWPzAfAB8AHwAfB_" + "AfAB8AHwAfAB8AGQSgTf_N3dVgQGMGIEJPAB8AHw_wEwDgpAYl0DXzTCN3sDAw_x" + "XzTr6-seMF9ku2hf9P8B8AHwAfAB8AHwAfAB8Fz0_1mU-A3eAM8A1QA3AllkXT__" + "MfIB8AHwAfAB8AHwAfAB8P8B8AHwAfAB8CaUAQCoORAO490BQgbj4-MLAZUKBQHH" + "M_BclFsIk5OT4weHA_8NCPkDlwirMyAEGDAxCKID40Q0EAilpaU1BAwA1QMfEWQD" + "CdgG5zMrCGpqahEJAEtLS1xkPj4-cRQEtra2QgAPAOcDn4yfnyEJDACKiooYAP-w" + "NH84qgRclH8IOARcNEgAPy0JGDCwBP0IbwAkMHt7hns5MF0Abm5uMAEg-IeHh3kF" + "YwAYAL4IyDr_EfEB8LANXfMB8AHwAfAB8H8B8AHwAfAB8AHwAfDTBenE5uYDAL68" + "vB7wAfD_AfABYMMJBwvVll80BgBWAfGBA97e3n8CiAJfNGQC_58DX8TS8wHwAfAB" + "8AHwAfD_AfAB8Fn0aQnODT8D2wDGAP9OA88AtThZNPs6NPIB8AHw_wHwAfAB8AHw" + "AfAB8AHwAfD_AfAB8K06CwEBMELzLcAmAQMCBM4Hg4ODMjIyH3sDlgyTA0YIujNl" + "ZWV_CTAHCLQMDDAxCCQADDBJ4ElJUFBQDDBlBCQAH4g4Jg1AOAYAlQRWVlbjDABR" + "CX19feoDCQDINP_FZDkAOwT0OIoAgWABYFcJx4gIKQRENGBgYBgwCQAxNQRDQ0MM" + "AKYIZGQeZBgwzg1xNFA0jo6O_4MEDAAhAFz0AfABAMAJWvP_AfAB8AHwAfAB8AHw" + "AfAB8B8B8AHwAQBTBFw00M_P_x7wAfAB8AGQngodAXBiXzSPdQN7A4ED2wbd3d2N" + "A_9fBCQAHgCfA18EqwOxA1_0_wHwAfAB8AHwAfAB8AHwDv3HnwBZlNUA5OTkkwnJ" + "AP_kAOcwWfQB8AHwAfAB8AHw_wHwAfAB8AHwAfAB8AHwAfAfAfAB8Pgx0gZZBJGR" + "kYECBIKCgoSEhOQDfwYADAAYAPwDCQABAAkAkPyQkBIAJGAYMC1gBjCVDf8JMBgA" + "LWABMC1gDDABAGUEf3gADwBpMFRgYJABYJ8AjfyNja0NVGAYYEUAGGBIAON4MBgA" + "j4-PDAABAHgw4xsAJwCmpqZZ9AHwfQf9QgObAfAB8AHwAfAB8AHwHwHwAfAB8AHw" + "AeCqqakA6Obm39zc0M7izgkAvby8JPAB8AHw4wGQZA7f399flHgzGj3_GwBBAZMz" + "IQCNA6UDqwNfNP8a_QHwAfAB8AHwAfAB8AHwH7X4Dj0GDDQFwwDl5eX_5ADGALJo" + "-QBGBQHwAfAB8B8B8AHwAfAB8AEw2traAaYOwcHBs7OzrMcBsKwLGADS0tKpDjnw" + "_wHwAfAB8AHwAfABYBI_FgL_Uz0EAgEwD2AJYCTADDABkP8VkAzAujY8wFTwP_AS" + "kC1g_5nwGPBd8BswPD8B8AGQjAH4oaGhUfMB8AHwAfAB8AcB8AHwHwirq6vExIDE" + "2NjY29vbYzz_sQMMAEwCSQKcDDbwAfAFBPje3NxTBGIEDJAq8AHwxwHwAZA8D-np" + "6ZYAaQPHvmjUN40D3NzcYgQJAP8kABIAZzIa_QHwAfAB8AHw_wHwAfAB8Fn0Dj2o" + "AK8CKwLjGQUNAurq6t6QWfQB8D8B8AHwAfAB8AHwdg7MzCLMAQKWlpY4B5SUAJSk" + "pKStra27iLu7wwGwtbW1GAD_SQK1C1kH_gFuBNAOVPAB8P8B8AHwAfAB8AHwAfAB" + "8AHw_wHwAfAB8AHwAfAB8AHwAfCPAfAB8AHwDAOnp6dL838B8AHwAfAB8AHwAcAw" + "CcLIwcHk4Q_q6p_wqJAxZAKioqJC8AGQvbuAu9TT07i3tw8wiKmoqA8AysnJKvDH" + "AfAB8AHAuLi4jTBfNP_aZ40DBzWZAwwDIQCiD6sD_7EDtwMa_QHwAfAB8AHwAfAf" + "AfAB8A79Dj3JAOLi4v-1aAEAxgBuBIzxAfAB8AHwBwHwAfDvkcnJyaOjEKOJiYkG" + "AL29vT-yCJkAAfAB8AEAkgHQ0MbQbgS2AYuLi9YI0Aj_ZvAB8AHwAfAB8AHwAfAB" + "8P8B8AHwAfAB8AHwAfAB8AHwHwHwAfAB8AHwAWC3t7f-mgHwAfAB8AHwAfAB8AGA" + "IfgBurm53VsI5-f9XgjprAhf9AGwsgvcCJwGf07wAfAB8AHwAfAB8AGQzfzNzYow" + "dQN7A4Qzu2gBAP-fA3UJgT8a_V_0AfAB8AHw_wHwAfAB8LX4Dm3GAFwBtTj_2AC9" + "AAYAsjgSAAHwAfAB8AcB8AHwAfDZ2dmwsPCwh4eH8wZuAYg-AfCPAfAB8AEw7gXF" + "xcXDBviTk5OkBOcAdfAB8AHw_wHwAfAB8AHwAfAB8AHwAfD_AfAB8AHwAfAB8AHw" + "AfAB8P8BwLwBP_MB8AHwAfAB8AHwAwGQhg3Z19fk4eGQ6ebm5wMA5eVfBADg1MvI" + "nn--hoBeuX1QtXNCCQBAxJRx2cW2t8DX_NfXwQhU8AHwAfAB8AHwxwHwAWDXBN3d" + "3YQAvjgfh2MSAJkDGAPBCOPj4__BAtYCsQMa_erzAfAB8AHw_wHwAfAB8LVoDj34" + "BA49wwDx2ADk5OQWNQEAsjg08n8B8AHwAfAB8AHw1wHVDJDgkJDBwcGgBSb0AfAP" + "AfAB8AFgtQ6urq6I_IiIgApxBIHwAfAB8AHw_wHwAfAB8AHwAfAB8AHwAfD_AfAB" + "8AHwAfAB8AHwAfABMP9MAgH4AfAB8AHwAfAB8AGQALGwsOXj4-fkBuQDMFYEz6yU" + "t3QAQtGlhObPvfU86-QGMwkADwAVALBmgC_Gmnnn5OK9YMDi4eGqqqr6CF3w_wHw" + "AfAB8AHwAfABMFAH_Db_vjiKM5MDmQO4C18ExALHCP8BOBo9uzjPA7v4AfAB8AHw" + "_wHwAfAB8LWYDs29ALU4hAn_DwAGANIANPIB8AHwAfAB8IEBwOHh4bKyslgI_5UK" + "VjEB8AHwAfAB8AHw-gUAtLS0ioqKu7v-u4rwAfAB8AHwAfAB8AHw_wHwAfAB8AHw" + "AfAB8AHwAfAfAfAB8AHwAfAeA6ampv6ZAfAB8AHwAfAB8AHwAVBAwL-_5uTkA2De" + "ANDGvH1P4MGpOPv17wPwA2AhALVvUDrVu6i9AOm-AOby5l8UqakfBWPwAfAB8I8B" + "8AHwAfABMMLCwkcxj4EDjWO7OH0H29vbLQP_Gv0abfDzAfAB8AHwAfAB8ONZ9A7N" + "8vLyqAC0AF8E99gDyQBZZOkB8AHwAfAB8CMB8AGAvLy86gO6uv66UDEB8AHwAfAB" + "8AHwAZADqgEAA6SkpMzMzP-T8AHwAfAB8AHwAfAB8AHw_wHwAfAB8AHwAfAB8AHw" + "AfDHAfAB8AGQwMDAM_MB8B8B8AHwAfAB8AEwn56eCNHPz1mU1r-vuQB1QfLfzvbn" + "2AcD8APAJwC2bjjJo8aGWQQ_MN3b2x0NafB_AfAB8AHwAfAB8AEwMAnc_NzcRwFf" + "ZAEAmQOfA7gI_18EFAoabb0DGv328wHwAfAfAfAB8AHwXPQOzeLi4v9sCa4wyQAG" + "AFn0AfAB8AHwBwHwAZDRBLCwsKWl_qV-AP_zAfAB8AHwAfAB8BEBYM7Ozi0Jtra2" + "_5zwAfAB8AHwAfAB8AHwAfD_AfAB8AHwAfAB8AHwAfAB8McB8AHwATDe3t7cCzPz" + "PwHwAfAB8AHwAfABAKmogKjd2trm4-MDMADn5eXczMCyZIAo7tK58NW9A_ADA_AD" + "AO3QtrFfIRjKpotKDUUww8PD_-oDb_AB8AHwAfAB8AHwATA_ug9NMYcDk2OfAycD" + "1dX-1WIEGgfVPxrNXPQB8AHwHwHwAfAB8Fn0tcjx8fH_YwOuAAoFET3GACUFXPQB" + "8B8B8AHwAfABYP8Mi4uL-L-_v_PzAfAB8AHwAfD_AfAB8KQB5Q7jBOkEpfAB8P8B" + "8AHwAfAB8AHwAfAB8AHw_wHwAfAB8AHwAfAB8AHwAfD9SgGYAfAB8AHwAfAB8AHw" + "AJiYt7a25OHhGOXj4wMwFATk3dkArVsbzpBe6sIenwPwA_ADYDMApUsFCNS9rEsw" + "5OLiqfypqV8EdfAB8AHwAfAB8OMB8AEw0dHRXDSNA1804wwApQPW1tZfBNkIEAj_" + "Gj3DA7v4_PMB8AHwAfAB8P8B8LX4tTi0AKUAVwNjA8Yw_wYA1QAlBQHwAfAB8AHw" + "AfABAQC5ubmWlpbY_NjYbwAB8AHwAfAB8AHwDwHwAfABMMcLjIyMyvzKyqvwAfAB" + "8AHwAfAB8P8B8AHwAfAB8AHwAfAB8AHwjwHwAfAB8AGQzc3NJ_M_AfAB8AHwAfAB" + "8PYDycgGyFZkWRTm5ujn5wDBjGKqTADTkvBa46-AA_AD8ANgMwAAqEsAq14g498C" + "3lEw0M7Onp6ef3vwAfAB8AHwAfAB8AEwo_yjozsEUwGNA5ljpQOrAxjS0tIaDSAK" + "5eXl_xA4u_j88wHwAfAB8AHwAfABtcj39_f09PTwjPDw-AThA-Hh4cYA_8g3sjg3" + "8gHwAfAB8AHwAQD_hAywAdQE5PMB8AHwAfAB8A8B8AHwAcAqBo6OjsX8xcWx8AHw" + "AfAB8AHwAfD_AfAB8AHwAfAB8AHwAfAB8A8B8AHwAfABMOTk5KD8oKAn8wHwAfAB" + "8AHwAcAApKSk2dfX5eIO4gMwuxgJAOPj3NAAxqxQBbBPAMfgdjLbm2AD8APwA2BA" + "x3czr04AXwTLxKiNVzDh4ODV8AHwHwHwAfAB8AHwAZDLy8s_XDSTA5wzDwCrA7gI" + "09Pw09ra2gwAuzjJA88D_9UDu_gB8AHwAfAB8AHwXPTxtWjy8vLyBJ8AEQ0WAn_A" + "MLX4AfAB8AHwAfABYLT8tLSUBSc5AfAB8AFgNgD9QQ24AfAB4CcALQBC8AHw4wHA" + "DwaSkpLECLfwAfD_AfAB8AHwAfAB8AHwAfAB8P8B8AHwAfAB8AHwAfAB8AEA8MPD" + "w5cB8AHwAfAB8McB8AGweg3i39-0DAPwAMORaLFRALVTAAC5WAbShD3VfIhCA_AD" + "8AMALQAzALUAUgCtTwCxajDjTjCtBKysrIHwAfAB8P8B8AHwAfABYD0Fu5j4N6sD" + "_7EDCAe7OA8AHgDJA88Du_j_AfAB8AHwAfAB8Fn0tTibDeO1aKIA3d3d6gPaPVk0" + "P9IAAfAB8AHwAfABwLW18LWoqKhjAG8AAfAB8IEBYFRUVH5-fnML_7UFAfABwCcA" + "LQAzAKAOOAofeAD9NQHwAfDKDpWVlf--CL3wAfAB8AHwAfAB8AHw_wHwAfAB8AHw" + "AfAB8AHwAfDHAfABwGsBnJycIfMB8B8B8AHwAfABYNEHwL-_B6YIA_ADMLdqKLdV" + "AAC7VwC8VwDAAF0IzXIiz3YnHwPwA8AnAC0AMwC7VgAAs1MAqE4A3dP-zAw5qwZ1" + "AIrwAfAB8AHwxwHwAfABYMzMzF-UoAL_qwOxA1wEu2gSALv4X_QB8P8B8AHwAfAB" + "8LX47ASWAIoD_1o5ujBZ9AHwAfAB8AHwAQDAurq6oqKiCQDS8wMB8AHATU1NWVlZ" + "fb4CxAHwAeAnAC0AMwDUxNTU8g3f39_qAF_0xwHwATBwBZGRkZIKw_D_AfAB8AHw" + "AfAB8AHwAfAB8P8B8AHwAfAB8AHwAfAB8AGQ8MHBwZYB8AHwAfAB8McB8AFQSw_P" + "zs5Q9ANgAOHa1rJTAL5ZIADCWwDDAzBcAQDHYgjKZw_KaOYQA_ADEGcPIQAnADAw" + "AMFaALlWAK5READQs5xUEOLiyvzJyecDjfAB8AHwAfAB8OMB8AFgp6enXzGPN6UD" + "AwwAFz3Pz8_W1tb_aAS7-Lv4AfAB8AHwAfAB8CNcNLWY8fHx7ATb2-LbSAPi4uLw" + "M6IACQB_hwMB8AHwAfAB8AGQaQydfJ2dbABmAAHwAfABwEogSkpSUlIyBGNj_GNb" + "AfABgCEAJwAtADMA-Ly8vGsE5QVfZAHwAfD_AQAzAB0NGAbJ8AHwAfAB8P8B8AHw" + "AfAB8AHwAfAB8AHw_wHwAfAB8AHwATAdCt0BG_MfAfAB8AHwAfBpY9vZ2Rjj4eED" + "8ANg2ci5ALdXAMJdAMhghADJA7DIXwDHAzAeXgMgDCAVACfAxl4AAL9bALFUAMii" + "_oBRAFwEzgRc9AHwAfAB8P8B8AHwAcBEBGIBfGsJAEUDgbcDzs7O0tLSu5j_1QPb" + "A-EDuzhc9AHwAfAB8A8B8LX4bwC1OPPz8-_87--rAFcJAwz4BHU5BgD_N_IB8AHw" + "AfABwAUBaQxsAI_G8wHwAfBcNFFRUTUE_030AfAnAC0AMwAqD4YNXwQfvgjfNQHw" + "AfABMMLCwvi0tLTM8AHwAfAB8AHw_wHwAfAB8AHwAfAB8AHwAfCPAfAB8AHwATDI" + "yMjgAT8B8AHwAfAB8AHwAQC3toC24d_f4-DgA_BBA5DWv6u7WlkUznBjANBkA_AD" + "8AOgzwBkAM1iAMNeAEC0VwDGnHdUEOHw4dLQ0OQDk_AB8AHw_wHwAfAB8AGQxAJl" + "MaVjygLHSwN0BLs43t7e3DLVA__bA7s4F_0B8AHwAfAB8Fz04xE91AT19fW1OP4E" + "XAT_0QdcNFk0gQYB8AHwAfAB8IEBkK-vr8rKymYwHwHwAfAB8Fz0AQBVVVUePQEg" + "CQBc9DMAo6Oj-Kurq74I3QTfBVfwAfD_AZDXAcgNz_AB8AHwAfAB8P8B8AHwAfAB" + "8AHwAfAB8AHwjwHwAfAB8AEApqamFfMPAfAB8AHwAfCYmJjI4MfH4uDgAzBiBAxg" + "AQPw18SzvFwAy4BkANNnANZpA_ADA_ADoNVpANBmAADFYQC0WQDGnuJ5VDDQ0NC4" + "CJnwAfD_AfAB8AHwAfABkNcEYAxlAY9fNFw0twO9A8zMzAYA_7s40Ai7OOED5wMX" + "_QHwAfCPAfAB8Fn0ET339_e1OP9dCVcJ9QRRObEADj038gHwxwHwAfABkLm5uT0I" + "uvMPAfAB8Fz0QZRAQEBNDwEgCQAbwFw0oaGhqoyqqoEAHwLg4OBX8I8B8AGQfwXr" + "C8vLy9Xw_wHwAfAB8AHwAfAB8AHwAfB_AfAB8AHwAfAB8AHwAcDZfNnZEvMB8AHw" + "AfAB8KHAoKDV09PiUAQDIDjg3t4MYAPwAwDc0ADHvF0AzWUA2MBqANxtAN0D8APw" + "AQOA22wA1GkAxgBiALNYAM2vk_FUMM3MzLj4AfAB8AHwxwHwAfAB8L29vVw0pQP_" + "BDi3A70DmgK7CMkDuwghAP_VA7s45wMXPV_0AfAB8AHwBwHwtcgRPfLy8u3t_u2i" + "ALAN8mS1-AHwAfAB8OMB8DEInp6eG2kB8AHwRwHwXPQBAExMTFYEWP8BIAkADwBc" + "9Fz0AfAB8AGQ_8AA-gjY8AHwAfAB8AHwAfD_AfAB8AHwAfAB8AHwAfAB8MMB8AHA" + "u7u7lAHwAfAHAfAB8AGwrq2t3tzm3KkIA0De3gxwA_ADUAC8aBXMZQDabcAA4HAA" + "43ED8APwAQNA4nEA328A1QBqAMViAK9XAPjZzsVRMDYJXASf8AHwHwHwAfAB8AHw" + "AcCgoKA_azEHaPYGvQO4CAEA1NT-1NkIEgAbANsD4QMXPbv4fwHwAfAB8AHwXJS1" + "OCoJ9AD09PDw8Ovr6_9pA28DkzAQNboAAgQB8AHwxwHwAfABYLKysl4FYAAfAfAB" + "8AHwAWBc9EhISH9WBBIAAQAJAA8AXPRcBKngqam4uLjtAEcKV_ADAfAB8LOzs7-_" + "v__b8AHwAfAB8AHwAfAB8AHw_wHwAfAB8AHwAfAB8AHwAZB_PAYP8wHwAfAB8AFg" + "zAO_HL29OPQD8APwxItUAMdkANltAORy4ADodQDpA_AD8AMgBOZ0jxTSagC_YJAA" + "tG0nrXS7u1z0fwHwAfAB8AHwAfAB8AEA1fzV1WsBpQNgPLcDvQPWAgDKysrR0dHY" + "2P7Y0gMYANsD4QPnA-0Duzj_XPQB8AHwAfAB8BFtyAQRPcDu7u7p6elRCZIH8UsJ" + "4-PjkwABADTyAfDHAfAB8AGQx8fHNwiu848B8AHwXPRclFRUVKwI_loBgA8AFQBc" + "9Fz0AfAB8OMBkDkAm5ubvAHh8AHw_wHwAfAB8AHwAfAB8AHwAfB_AfAB8AHwAfAB" + "8AEwlAuTHwHwAfAB8AHwAVCcnJx4z87OphgD8APwA-DUAL6qvmAA1GsAAONzAOt3" + "AO55HADvA_AD4CcA6HYAAN1wAMxnALZcMADCmXBOMIYNqqn-qd4DqPAB8AHwAfAB" + "8AHw8QHwurq6cTGuM7cDvQOBwwPIyMjNzc16BPjd3d0eANsD4QMXPfMDPxf9AfAB" + "8AHwAfARzfX18PXx8fE2CTADEwJLCf9cNLI4CQAB8AHwAfAB8AFg_9gDxQQOMQHw" + "AfAB8AFgXMT4VlZWWZQBMBUAXMRNCsCoqKi3t7fwAO0AH1fwAfAB8AEA9wu-vr7_" + "4fAB8AHwAfAB8AHwAfAB8P8B8AHwAfAB8AHwAfAB8AEw-LS0tAnzAfAB8AHwAQDh" + "wwOop6fahgfVIwPwBwPwA_ADAMCBQchmAADccADpdwDwwHoA83wA9APwA4ADIQBc" + "BOV0ANRsAAC_YQCuYhLd1-LUSzDZ19eZCavwAfD_AfAB8AHwAfAB8AEAJgdxMR--" + "OFw0wwO_BxgM09PT8SMN4eHh2wPhA-cD7QP_F_0B8AHwAfAB8BH9EW2fAMDJycnX" + "19eSB1z0HwHwAfAB8AHwrgmvr6_fqPMB8AHwAfBclFwBIAJt_wFgGGBc9Fz0AfAB" + "8AFgKQH_TQe8AefwAfAB8AHwAfAB8P8B8AHwAfAB8AHwAfAB8AHwfwHwAQAXAQnz" + "AfAB8AHwlwCXl7e2tt_d3R7gA_AD8APwA4DVw7MAuGEFzmoA4HMAAOx5APN9APco" + "fwD4A_B_GBD1fgAA8XwA6HcA2QBvAMVlAK5ZABjGpYVLYJMAlZWVP67wAfAB8AHw" + "AfABwMPBIsF1ANbU1Bvw2dn-2VyUlALDA8kDuwiXCxcN_xIAGwAX_Vz0AfAB8AHw" + "XPTjXDQRndzc3OwEmQC4OH-uAL8NAfAB8AHwAfABYK38ra1PBe0AAfAB8AHwAcAB" + "XJRkZGRfX19gfQEgYQEgDDAVABsAXJSfxJ-fXDTOzs41ClfwjwHwAfABMJUKw8PD" + "5_D_AfAB8AHwAfAB8AHwAfAB8H8B8AHwAfAB8AHwAfCLC5J_AfAB8AHwAbCvCKQE" + "VwPefNzcXAQD8APwA_ADwMgAp4W5XwDPawAG318Euwh9APiAABD6gQD7A4D5gAAE" + "9n9ZFOd3ANhwAADGZgCxWwC0BIBJRWDPzc2dnf6dsfAB8AHwAfAB8AHAXgURdQDV" + "09MGAKqqqvEewMTExHcxCji9A8MD8ckDxsbGF20YABedXPT_AfAB8AHwAfAR_RE9" + "SwmNAP8HBbg4AQA38gHwAfAB8AFg8WMAq6urovMB8AHwAfABXJRmZmZ0dHRpAGlp" + "ampqa2tr_mwBIAkADwAVABsAIQBc9P9c9AHwAfABkHQKSwDq8AHw_wHwAfAB8AHw" + "AfAB8AHwAfD_AfAB8AHwAfAB8CgOIfMB8McB8AGQkgeioaHGA1kUHwPwA_AD8APw" + "AyDCnncgtF0AyGg4FOV3AADtewDyfQD1B6wIUyQJAPF9AOp5AADgdADSbQDAAGMA" + "rVoAsXxGON3Z1zwAPQiDB5ubjpufALTwwMCjo6N7BnEcC9LS0vMDADMMAMJ8wsIj" + "BDoFNvAB8AEAuBy3t3sAbQsWDsvKyvEewLCwsHcxtDO9A8MD_8kDdAQkDG8AF_0X" + "_QHwAfD_AfAB8FxkEZ3iC2kDCAGKAPjg4OBc9AHwAfAB8AHA-Ly8vE8FovMB8AHw" + "AfABXJRycnJ6enpzAHNzdXV1dnZ2_ncBIAkADwAVABsAIQBc9I9c9AHwAfABwK6u" + "rjQC_-3wAfAB8AHwAfAB8AHwAfD_AfAB8AHwAfAB8AHwAcA5Ax6RAfAB8AHwAYCj" + "oqL419bWWQQD8APwA_AD8AkDENvbCTDHrpSrAF0KuWAAx2cAINNtANtxShTidQkD" + "EN5zdBTOawDCAGQAslwAoVMACLeQaEJg3Nrapgympq7wAZCWlpa8xLu7SAPi4eEd" + "8QFgYwUE8gqZmZlC8AHA2uDX19DOzgkAPwOEAIign58ekJ6enlyU_70DewDJA1wE" + "F53hAxedXPQfAfAB8AHwXPQRne_v78cwCVEJigDR0dFcNK4wf-8EAfAB8AHwAfAB" + "AFcAofyhocEFbAAB8AHwAfAB8AFcZF1dXX5-fonAiYmLi4uMAYAPAA8VABsAIQBc" + "lKenp7b8trbfBVwEV_AB8AHwAWD4x8fH9QHw8AHwAfAB8P8B8AHwAfAB8AHwAfAB" + "8AHw_wHwAfABwF0DIfMB8AHwAWDAt7a229jYCxQD8A8D8APwA_AD4NbNxrUAiVql" + "VwOuWgAAt18AvWIAv2MRAxC7YQDWCKpYAACeUgCnczvOvwKyM2DZ19esq6sHrvAB" + "YKwIwcDA2Nf-1zsd5Sj1AiDxAZD2A9wIx38ITvABMK6trXsA7whAysnJz87ODADB" + "xL-_JMDb29tLALQzR1w0yQNcBMXFxeIC1vzW1uUy5wPtAxf9AfAB8A8B8AHwXGQY" + "Cfb29vLE8vIqCejo6JwAigD409PTqABcNAjxAfAB8McB8AFgTAW1tbVeNQHw7wHw" + "AfAB8FxkWwHwAeBcZMfrBX4GjQDMzMyMBlfw_wHwAfABYB8C_go1AfPwAfD_AfAB" + "8AHwAfAB8AHwAfAB8L8B8AHwAfAB8AFg4Q-QAfDHAfAB8AFQube3_QsIdADc3MHS" + "wHi5dwBZrlhMqks_pQI-CQBrtGqsyqoPMvQD8APwYsS9oIOoAHZApWosmVEDAJhP" + "AKFkJKJtgDS0km_QxLonYMDd2trY1tYaB67wcQFgmpqatzBZBCRpwwDC1Xp6vFpa" + "sEBNTaxAQKcJAG1gbbevr881wQIEmAyYmFTwAQDU0dHZ4NbWysjIAzAPAFoA_2wA" + "JJDHAkgwXDTDA8kDzwPxGAnLy8vVMxsA5wPtA__zA_kD_wNc9AHwAfAB8AGQjxE9" + "XQB6DYAN7OzsMAn_7ASuDJ49LQNnOAHwAfAB8P8B8AEwhw_jB5zzAfAB8AHw_1z0" + "AfBc9Fz0XPQB8AHwAcD4sbGxOwHz8AHwAfAB8P8B8AHwAfAB8AHwAfAB8AHw_wHw" + "AfABYNMCIfMB8AHwATCAuLi429ra3c8DAZwjkcOQQalAhADJhL3iveTz5A_BMgkA" + "DwAVAC6iLnLgt3HV2NMwMAPwA_D_A2DeDBXwA_AqMJMMjQmu8A8BYAcLUZC6AJCO" + "wUAAQKmEhMm9veLI5OTzukDk8w8AFQAALi6jdHS52NhG3ThhXgibm5tXwKjEp6dO" + "ALu6uhLwJ5A_NwtFMAk5wwOOAs8DwMAQwMnJycQI2dnZ_x4A5wPtA_MD-QP_Axc9" + "IPT_AfAB8AHwtThcNHQNeg2ADfGGDeXl5W8GjQDjNFz0DwHwAfAB8AHAr6-v13zX" + "13n-AfAB8AHwATBVAFVVVFRUWlpaP0r0AfAnAC0AMwBcNLS0frTWBdM1AfAB8AHw" + "AWDC_MLCgwrz8AHwAfAB8AHw_wHwAfAB8AHwAfAB8AHwAfDDAfABYLe3t48B8AHw" + "OQHwj4-SAesFRQDd3ADcv9K-Ta9NqeDcqe_77wPwA2AhAMA5pzmfxZxbCAPw4wPw" + "A2Dc2dkDMBvwA8D4urm5wAOu8AFgXQ8wkAC_vtJNTa-pqQ7cr_ADkCEAOTmooSCg" + "yeDf3zsx1NQw1JSUlFqQSgS2tRC1mZmZb6C1tbLEsLASkK6urlyUuDjxzwO_v7_O" + "BzAMeAMYAP_nA-0D8wP5A_8DXDS7-AHwfwHwAfBcZG4NdA16DYAN6vzq6owNgQBc" + "NLg4qAAGAB8B8AHwAfAB8PAwq6ur_5bzAWBXDAsBtwYB8AHwATAR1AF5eXk1BG9v" + "b_9K9AHwJzAzADkATvAB8AFg_5kAnwC08AFgJwZUAPbwAfD_AfAB8AHwAfAB8AHw" + "AfAB8P8B8AHwAfAB8AFg5gF-8wHwIwHwIQPe3t5FMKjLAKhCrULQ79DaHPTaA_AD" + "wCcAOKk4OH-6fjoIA_AD8NvZHtkDABjwA_DkDLGwsBiRkZGu8AEwlZSUCMfFxTOQ" + "p6fLQQBBrc7O8NjY9QcD8APAJwA4OKqAf368-QD8MGcIyPQB8AHAohyiojgxtzNc" + "ZL29vcdHCnENFQnd3d3nA-0D__MD-QP_AwUEXPQB8AHwAfAfXGRoDVcAdA1jAO7u" + "7peGDdYLwgfIzg3U1Lg4H1z0AfAB8AHwAWDOzs44urq6n_8BME8Ifn7-fgUBPwkB" + "8AHwAWBSDkoK-Ht7e0r0AfAnAC0AMwD_SPAB8AGQmQCfAKUAbwJZB48HAiQ2AWAS" + "AKmpqRtj_wHwAfAB8AHwAfAB8AHwAfA_AfAB8AHwAfAB8AHwn5-On_3yAfABwJqa" + "mv0CAUIwuNG4KaUpvODrvMDtwAPwA_ADAAC56rkjoyOEu96DjmgD8AMwLwTbAyAb" + "8IEDwNXT06WkpFn0gQFgn56e09DQM3AA29u4uNEoKKbAubnrvb3tA_AD8AEDALa2" + "6iEhpISMg7yrCUAyrKysbPD_AfABkNkIXMTJA88DXATuAv_5Dw8A_D_tA_MD-QP_" + "AwUE_wsEXPQB8AHwAfBcZG4NXQAA8vLy7e3t5-f-5x4DPAkBABQ95DBMNQHwfwHw" + "AfAB8EAFAQBW9AEwTQBNTVlZWZycnD9aDAHwAfABME8O6A6Dg_CDY2NjSvQB8CcA" + "LQD_MwA5AE7wAfABYJkAnwClAHGnAdDQ0GIKCQNfxLL8srIeBvnwAfAB8AHwAfD_" + "AfAB8AHwAfAB8AHwAfAB8PcB8AEw1guOAfAB8AGwAwMBPzDO2M4bnRxg4MRho-Wj" + "A_AD8ANggTMABpQHpcaklzjjA_ADANnX11wUG_AD4MDFxMSWlpa3A6vwMQEwrays" + "TgAwYN3bANvOztgaGp5e4F7Fn5_lA_AD8ANggTMABQWWpaPGRQD_AgFGArsIcvAB" + "8AFgqAD6aAcGALg4zwO8vLzBwfDBysrKWAsXB6YC7QP_8wP5A_8DBQRc9AHwAfAB" + "8A8BMAYJaA1XAPX19fEA8fHs7Ozm5ub_JwNCA5AJXGQB8AHwAfAB8HEBYL6-vv4H" + "n_8BMErgSkpSUlLAAyb0AWBx2wNISEgkABvwJ8A_AD8_Nzc3NTU1ADw8PEZGRlhY" + "_lgn8AFgUQBXADYAG_CSZP8kAJ8ApQD5DAwGKQFlOsOQ-Lu7u1gO-fAB8AHwAfD_" + "AfAB8AHwAfAB8AHwAfAB8P8B8AHwAQDBAvryAfABwHcHA00BQjBfs2ACmQPAXslf" + "hNyFA_AD8AMDYDMAApgDIZsheNbW05o4A_AD8APw2jzX1wgNpfABYMoIv70GvTOQ" + "yxrd3V5etQAAAJxaWsqAgB7dA_AD8ANgMwAAAJtAHx6e19TXSzCx_LGxePAB8AGQ" + "8AaGYVw0j6YClALPD8EI0dHRWgX_pgLtA_MD-QP_AwUECwQRBB8m9AHwAfAB8Fw0" + "-Pj4APT09PDw8Ovr_utvAO8EyweEAOA06QEB8I8B8AHwAfABYLi4uPAAx0P7ATC3" + "A1FRUTj0L2QAQEBAPj4-SUlwSURERCHwATBEBEOIQ0M6AeA9PT1dAP8w8AEwUQBX" + "AF0AYwBi9IBkA0IApQClpaWurq6PDwZHBCkBw5DCwsJBAf_58AHwAfAB8AHwAfAB" + "8AHw_wHwAfAB8AHwAfAB8AEA4wE-jQHwAfABsLIFPzDB0wDBCJsJA54ENuC6N2XT" + "ZwPwA_ADYAA3uzgDnQQDl_AEib2InQgD8APwA_DA09HRoaCgovABYDEHBc7MzDBg" + "CTna2QDZv73QBQWeAAAAojIyvGBg1AcD8APwA2AzM70AACKhXwSIh74DGdzc_xwL" + "e_AB8AGQMwCGYZoCyQP_jgJcBNQHwQivAn0EpgLtA__zA_kD_wMFBFz0AfAB8AHw" + "AxE9XJTv7-_q6ur45eXl7ASPB1z0AfAB8AcB8AHwAQC1tbXY2A7YVvRc9AGQTExM" + "QcRBQfwDVlZWHgABAP8FBPzz_zNZBLIIAfABMF_0_wEwUQBaMGMAaQBvAHUAXfAx" + "XDSdnZ1fBMwAy8vGy8_AwgHGxsb58AHw_wHwAfAB8AHwAfAB8AHwAfD_AfAB8AHw" + "AfABAJsK9_IB8BEBwM7Ozj8waLdoAASfBQSjBQqogAtCyERIy0oD8B8D8AMALQAz" + "ADkABJwFeDGhMXwIA_AD8APAyBzGxtENn_ABYKalpRjX1dU58AMAZGO3AAAApQAA" + "qQYGgK09PclCQswD8D8D8AMALQAzADkAXwQuLo6lSwBfBL4ImJiYgfCPAfABYPkM" + "XPS5ubkIB_jFxcViAbE_7QPzA_kDP1yUF_0B8AHwXPRLAPr6gPr39_fz8_NjAMDp" + "6enk5OSQAIQAfzxp2wAB8AHwAfAB8AFgsxyzs279AWBcxFpaWsfhA1IILwRXV1f8" + "MwEwGEJCQiD0AQBOTk7GRQHwAYBHR0e89AEA_1EAYJBvAHUAewCBAIcAXMTAm5ub" + "o6OjswosAf-GmgEAbgS7CPnwAfAB8AHw_wHwAfAB8AHwAfAB8AHwAfDvAfAB8AEA" + "YQWMAfAB8AGACJCQkD9gK6YsBQCkBwWoBwWpBwANrg8pwSsuxH4wA_ADwCcALQAz" + "ADkQoYAHBJcGxdDDUgjHA_AD8ANgtbOzogOf8GMBMBsAtbS0NvADYCYAJqsAAKwA" + "ALAAAACxCAi1IiLwwicnxAPwA8AnAC0AhzMAOSBfBJ7Fw9FOAMDb2tqsrKyH8AHw" + "PwFgQQRHYcMDyQOjArq6Irq7CMTExPQF1dX-1ecD7QPzA_kD_wMFBAsEH1z0AfAB" + "8AHwRQD8_PwRXGTu7u5pAOPj4_-QAIcAGQJc9AHwAfAB8AHw-LS0tEweAfBc5N4D" + "QAg4UFBQ7TPhAwGQSEj-SCD0GADPDA4EAWAPABLA8QEAVFRUPPBRAGPAeDB_gQCH" + "AI0AXPRc9AEAGwbB_MHB-fAB8AHwAfAB8AHw_wHwAfAB8AHwAfAB8AHwAfB_AQDi" + "AvfyAfABkFMBPDDSANnSBZ8HBqkIIAatCAauAxAHrwAJELYSGLwaGfy9GwPwAwAb" + "ACEAJwAwMAAGrAgGpQgFm4AHmMGX2dbWA_ADA_ADANbT06OiogOZ8AFgk5OTxcPD" + "IznwA2DPzNMaJLQAIAC4AAC5AwABAQC5CAi8Dw--EH4QA_ADEBsAIQAnADAwAMYA" + "9whfBKWXlWkQaA3_cwiK8AHwXPRcZM8DRQDYAwDDw8PMzMzU1P7UGwDtA_MD-QNc" + "ZBEEXPQPAfAB8Fz0XDT29vbyBPLyYwDo6Oji4uLiUgi4uLjTC5MA6wv_NPIB8AHw" + "AfBcxFP0XPTJA_hPT0-fDAX0ATAeABXw0FhYWFIBIFMB8AEg8R4wVVVVPPBR8AFg" + "hwD_HjBc9Fz00w64-AHwAfAB8P8B8AHwAfAB8AHwAfAB8AHw7wHwAfABMAIBiwHw" + "AfABgANQATwwttC3BqQJAAeuCgezCge0gQOgCLYKCLcLAwA8CbgDEAwwFQAnwAex" + "AAoHqwkGngl9YLh82NbWA_AD8MxgysqXlpaW8AFgnGCcnNDOzjnwA5CyILDNAACv" + "IyS_AAQAwAOwvgAAvQDgALwAALsDIAkADwAXFQAnwBUAtqEEenm941EAuwi_v7-N" + "8AHwAWD4wMDAhmHDA8kDzwPVA8cBAJUBVQvT09PnA-0D__MD-QP_AwUEXPQB8AHw" + "AfARXGT5-flcNO3t7UDn5-fh4eGhDbf8t7eNACEDMwMPAIcAAfD_AfAB8AHwATBg" + "APYAov8BMAdclNIDPQhdXV1eXuBeX19fYAFQDwABAP9JOLj45Wge8I6YHvBRAFow" + "P2lgdQAPAIEANgC4-KKi_qJcBBUG8gbDkDsBIQb58P8B8AHwAfAB8AEARQ8oAkYC" + "_wYA5goh8AHwAfAB8GbwZvA_AfAB8AEAEvMB8AHAp6cCpzwwqcuqCKcKAAmzCwm5" + "Cwm6HwPwA_ADsDYAcQSuCggAoQp2tnXY1dUDA_ADwL68vI6OjgOT8AFgrKur1tTU" + "YznwA8Clo8m1KCMExjgAAMgD8APwA7DHAIQAxXokrXJxu1EA_1wETAKQ8AHwAWB6" + "AUcxvQM_wwOOAs8DdQNcBE8Lysr-ytcBGwDtA_MD-QNcNAsE_1z0AfAB8AHwXPRc" + "ZIQAVwn_2QszA1z0AfAB8AHwAZD4DQ-rCVb0XPTVA2hoaGnhASBqamprAVAMABUw" + "QyD0MwBhYWFiAVBlDGVlQgABAGZmZmP8Y2MVMCEANQQ88GnwaWD_QgBaAFz0XPQB" + "AIcDQQH58B8B8AHwAfABwCgCs7OzCKSkpKALh4eHj_yPj5MD8gqQBi3wAfAB8L8B" + "8GbwZvAB8AHAxwKKAfAPAfABgFABPDCxzrEJAKkLCbYMCr0NHArAA_AD8AOwvw0K" + "ALsNCbEMCKILwHi3eNfV1QPwA2AxCA2rqqqN8AFgjY2Qjb28vDNw1NRF8HEDYKyq" + "zNUsIATBCNCHA_AD8AOwzwAAypskMK90c71RALsIwcD-wJbwAfABYNhmvQOaAskD" + "j44CXAQBACcGycnJXPQfXPQB8AHwAfBc9PHx8QDs7Ozm5ubg4P7g9gOEAKgGmD0P" + "AIcAAfCPAfAB8AHwATC-vr5PBQ9G-wEwXJQsNG1tbXgAeHh6enp2dnYCdQEgc3Nz" + "cnJyiHd3dyD0XFxcFAQIcXFxLQB7e3t_CH9_ggEggYGBffx9fUgAOQBRAGIEPPAe" + "MP9dAGYwQgB1ABUAMABZNFyUiJqamgwDsLCwhwP41dXVw5A7ASkN-fAB8I8B8AHw" + "AZAcAqurq8MwMfMDlZWVTAL4BHx8_ny2AfkDM_AB8AHwZvBm8H9m8AHwAZBDAvfy" + "AfABkJkEmZk8MMTTxAmoAAwKtg0LwA4LcMQOC8UD8APwA4DDBA4LlQSxDQmgCziQ" + "vY8OFAPwAyDPzA7MJAaK8AFglpWVymP0DjBQ1dPTRfADkMAOvh0EXAR_CNIAANa4" + "AADXA_AD8AOA1V8kIsFcBI6MwlQwvbv-u5nwAfABYHoBRzG9A8MDA44CEgC1tbW2" + "trb_0whcBCYNGwDtA_MD-QP_Ax9c9AHwAfAB8FzE9fX1_1z0XgKYPbANEgAB8AHw" + "AfD_AfABMF0AjQPq9gEwXMQBYIEvBISEhIODgyMEfzUE9gMMACD0AQAXBBIDjACM" + "jJKSkpaWlmOyAgEAl5eXgweAB4n8iYlrBPAMOfBRAFcAXQD4fn5-DABvAHUAJMBc" + "ZIFUAKGhoa-vrxUG_8IBw5C_AT4B-fAB8AHwAfBHATCBBpQFqKiogQls_GxsXAp-" + "BiUCAQCEALkN-HBwcCsINzIB8AHwAfAfZvBm8GbwAfABMMTExD9rAQHwAfABYCII" + "P2AdqgAgC7YODMMPDHDJEAzLA_AD8ANQygAQDMcQC74PCgCwDgmdDL7MvAfMDAPw" + "AwDDwcGQkAaQh_ABYKKhodPRHtEwYFkERfAD8BQUuMPeLFkE3AAA3wPwA_ABA1De" + "AADbAADRYVwkrL27zk4Auwi1_LS0MgSf8AHwATBJC0Qx_70DwwOLAnIDhgpcNPYG" + "XPT_BQRc9AHwAfAB8Fz0XGSQAP9SAmECPDlc9AHwAfAB8AGQ8ekH0NDQVvRc9JT4" + "OAQ_NQQbDz4EIwQk8AEAZGQCZGsHnp6eo6OjgcoLqampqqqqMAPApaWloKCgqAaL" + "CD828AEAUQBXAF0AYwB0dP50IfBc9Fz0UTzhAPnwAfCPAfAB8AEADQK6urpvAOPb" + "ABYFpqamKwX4BA8AGG5ubkAFBgBUVFT_8AABCz_wAfAB8GbwZvBm8D8B8AFgQAL3" + "8gHwAcDKygLKPzBYtVoLsg8ADMEQDcsRDc84EQ3QA_AD8AMgzhEADMgQDLwPC6pw" + "Di2iL_MMA_ADALMMsrKx8wFgi4uLtByysjbwA_AD8FFPvQMULVwE4QAA5QAADuYD" + "8APwAyDjAADdIbgovSYlsFFgpKP-o6vwAfABYKkIXGTDA4kB_88DXAS7CAkAXASd" + "AhsAXPQfXPQB8AHwAfBFAPv7-8dc9HUAVw-8vLz-BIQA_8kGEgAB8AHwAfAB8AEw" + "SQWP_wBG-wEwXDRXV1fY8_8BYPkDWwhHBxvwATB0B7EM-LGxsVABhwkuBbUFXwH4" + "ra2t7vgBYFEAVwBW9P8BkJkAXGSqBFwEoQceBg_M_24E-fAB8AHwAfBvA8YA5wPx" + "8ANpaWnYAAEFWAgPAAMBMK4MampqU1NT__wDNzIB8AHwZvBm8GbwZvD3AfABMIAE" + "iAHwAfABsLMBET8wqMmoHQQMvRAADcoRDtISDtQ4Eg7VA_AD4CcAzxIADcURDLYP" + "C6IwDnGycW0IA8DT0DDQpKKie_ABYJGR8JHEw8M58APwA_ADAECjockAALx_KOAA" + "AADoAADrAAAe7APwA-AnAF8E2gAAgMoAALRsa7quYP9_C6XwAfABYBM4AQC9A6oB" + "_00EzwMMM1z0XJQRBFz0AfCPAfBc9Fz0XDS7u7vABv-PB4QAvQw38gHwAfAB8AFg" + "OM_Pz-EAVvRcZE9P_E9OAfAB8AGAMwD3ApKU-F9fX5UB4D3HCwYAuAj4b29vJpQz" + "AGzwAfBs8Mdc9MBg9T3X19f58AHwPwHwAcADCRYCSAn4BHl5PnlMCBwF85MBMO0A" + "UFD-UCQAePAB8AHwZvBm8Gbw_wHwAZBeBffyAfABwLgOXWAAR7BJDLMQDsUAEQ7R" + "Eg_XEw9w2RMP2gPwA4AhANUAEw7MEg2-EQwAqw8bnR3Lz8kI1dLSA5DQzc2fDJ6e" + "ePABYJ2cnM88zc02wAPwA_ADkD8-MLsAAMYpJFwE7gDgAPEAAPID8AOAIQADXwS4" + "CNMAAL4SERCsy8jPSzC-vb3_iwio8AHwAWBBYb0DwwPJAxHPA7CwsFzE39_f8fMD" + "6-vrXPQB8AHwAfDjXPRc9L29vekHkwB7Bv9c9AHwAfAB8HbCXQBG-wEwAkEBIERE" + "REBAQB4_AfAB8AEgMwBGRkbx-gJYWFhqyG4H2QvlyP8nAC0AMwA5AE7wAfABMJMA" + "I5kAojCYmJhcBK6u8K7Dw8MqBsBgMgGrAz_28AHwAfABwFoAYAB_fwB_XV1dhoaG" + "pACkpHt7e2NjYwfzwwEA0gBISEhnZ_5nkg1I8AHwZvBm8GbwZvDjAfABkI2Njf0I" + "AfAB8AcBkH8CPzCxy7IRpAAUDbgRDscTDwDTFBDZFRDcFQwQ3QPwGCDaFRDXABQP" + "zxQOwRINALARC5sPg7aDiNTS0gOQwL6-vAQHcvABMAgNrKur09Ee0TbAA_AD8APA" + "rKrJAAUFtwAAzQAAht-RKD4E9gAA9wPwoRgg9AAA8IYk2E0tEK1_fr1IMNHPz_6U" + "sfAB8AGAUAeAYYsCwwP_bAPPAwUHXPRc9Fz0AfAB8P9c9Fz0XDR3B4cA2Qu6DAgE" + "_1oJCgUB8AHwAfAB8AEw8AAHVwBZ9AEwNTU1NDQcNDEB8AHwAVA4ODj__ANVCE39" + "AfC1OC0AMwA5AP9O8AHwAWCZAJ8AWQRc9AEwf7kB9Q3z8AHwAfABYB0BvwC_v6mp" + "qXNzcyMWBaYLn5-frQ1iYo5i8_MUBCgFPj4-Swn43NzcTvAB8GbwZvBm8P9m8AHw" + "AZBiDf3yAfAe8xUDAUIwhbyHDKUPDkC4EQ_HExBfFBQAEd0VEd8VEeARA4DeFRFc" + "BNcUEAjOEw9ZFBAMng9wTaRO1CYEA1BaDK0crKzUB2_wATCQkJD4t7a2JwA2kAPw" + "A_AD8AEDMH99vwAAuQAMAM9fJJQI8wAA-EAAAPoAAPsDgPkbXCS1COdZJOsIsUZF" + "Pq-0YF4Cq_AB8AGQmpr-mt1hvQPDA8kDzwNLABT9HxT9XPQB8AHwXPT8_PzHXPR1" + "APkDs7OzdgKED382A1z0AfAB8AHwAZB-A9H80dHq9gEwdQBpDMYJvQOPygLJMwHw" + "AcBFRUW4-P8B8LiYMwBI8AHwAfABABQ9_6UAqwDibgEAzgHbAPPwAfAfAfABMBMC" + "xQpACGlpaRGHDJubm28Abm5ueGFhYfPzFATaDfMDTeBNTeHh4VIIUfAB8D9m8Gbw" + "ZvBm8AHwAZCioh6iCQkB8AHwAcCoqKgBRWB5tnsMoBANALIRDsATD8wULWIU16Yo" + "UATbrzgVEATQFH0EDrsSDasAEQyaD0qhS84cz8yCCAMw5AyenZ0PeANp8AEAcQSW" + "lpbD_MLCzAMzYAPwA_AD8APAhHJxGg20AADIOCTQ5QAA7cEo9a9YCQABVgTqAADg" + "AADSCAAAwLIIQ0KszgLMhxDSz8_CwcH_Dg2x8AHwAZD5D1xklwLDA__JA88DXPRc" + "9BEEXPQB8AHwj1z0XPRvAHoHycnJfg__mwqHMEUGqAAB8AHwAfAB8OMBMFcAzc3N" + "VvQ1bXsA-Le3t1MHwANNBwHwAfAASkpKUVFRWVn-WUr0AfAnAC0AMwAmNO4FwJ2d" + "nZ6enlfwAfB_ATCZAJ8ApQCrALEAvWDHfMfHMvEB8AHwATAXAbYctraNAD0IAQCh" + "oaGPUwfXDfDzAQBcXFwiBYg8PDxOCePj4-YK_1HwAfBm8GbwZvBm8AHwAcD4ra2t" + "_fIB8AHwAQCfAAFIYJO8lBWZFwwApRANsREOvBJID8MTSgQPyQMgxgITdAQOtxIN" + "rRGBrwgLjw5pqWomFHEDUMXDwwkJYPABYJjgl5fLysozkAPwA_ALA_ADkNIJUI2L" + "vgoECqrBKMcAANMAVADbSiTiAyDedCTOAAAAwgAAsgAAEKFjYrA2MNDOzji8urpZ" + "9AHwAfCysv6yNTGFAkoAnQLJA88DjAH_UwFoBBIA_AbnA1z0XPQB8EcB8AHwXGT2" + "9vZcxM_Ez8_mBLm5uZIHhQL409PTqAAB8AHwAfAB8P8BMD406vZTZygOzAkOASgO" + "5qwB8AHwrKxc9AHwXPSPuAtZBCwE8QWqqqpX8P8B8AEwmQCfAKUAqwCxALow_yoG" + "kwnw8AHwAfABADIKBwJjnAxJCHBwcJMAVgdnDGdn8PMhAFpaWkv8S0vUDU8IewC8" + "AVHwAfA_ZvBm8GbwZvAB8AHwu7ucu4UB8AHwAfCFhWcIAwkDS2DCzcJep18ADZMQ" + "DJsPDKNAEA2oEA2qAxAMBKYQ1ggLlw8LjYAOQZlDr8Sv9Rc_3iMDAOcMSABd8AFg" + "q6r-qtYIMGAD8APwA_AD8AOQAL68ylZWrwICAKUAAK4AALcA8AC9AAAvDQMA1jh6" + "BICeOTikqqnDMDAA0c_Pv729iYiOiK7wAfAB8MDAwDIx_7cDvQPDA8kDzwMBAFw0" + "Ogg_XPRc9AHwAfAB8Fz08vKA8u3t7efn5-8K-Nra2iED6ws5M-ILDwD_PAM68gHw" + "AfAB8AFgWgD_AA_t9nM1dQAaAcbGxsOYw8PBAfAB8MHBXGTxDwNEREQMAwHwAQAb" + "AD8hAFxkEAsPDGADOAq8vP68V_AB8AEwmQCfAKUAqwD_qWhoBOo5AfAB8AHABwKw" + "BAMMA4EMf39_pqamwICAgGRkZPPz8wOB1A1HR0c1NTUoBf-2ASgCODEB8GbwZvBm" + "8Gbw_2bwAfABwJEL_fIB8AHwATADvjhRkMLMwoOyhQBGmkgzljUMiAAPCocNK5Iu" + "OoCUO3CpcrbGKkADrAJRDNHOzqSjo9NU8AGQrayBDMwtAANAAYwEyMhysrJUqYCp" + "SKWlO6KiCQDAZa6uo8HBJHAD8AMD8APwz8--vMh-AHy0Pj2kKimiAAICmQAAmCMi" + "AJ8xMZ9qaa2yjK_EJ2CyCL27uxD1nwHwAfABMIICBUHS0mMD8aE3q6ur9QFkC3oE" + "XAT_GwDnA9EB8wP5A_8DBQRc9P8B8AHwAfBc9FzEmw1sA-Y0_1z0AfAB8AHwAcBX" + "ACEJWfR_X2QgMasDJwAB8AHwXGRI_EhIDAAJAAHwAQAbACEA_1yUGwzeDFkE7QBX" + "8AHwATD_mQAaYbdgtQgs8QHwAfDGCQFxAX19fV1dXYoAioqjo6N5eXkHrgzz8xQE" + "VlZWQ0MCQ6IMeHh45OTk_6UMUfAB8GbwZvBm8GbwAfD3AfABYGYAhAHwAfAB8AFQ" + "j3wOC_oB8FiSu7q64w3HS_ABkHoKz87OJQgDAAFlAoq8vECoqIQAycm94uLk8_MP" + "JDMJAA8AFQAtoaFs4LGxys3NMDAD8APwHwNgLwQS8BXwAwDPzMz4tbS0q_AB8AHw" + "ATD2APlhddLS3AXDA_IBkQtcNP8UBxUAXPRc9AHwAfAB8FyUCPr6-lw07u7u6ADo" + "6OLi4tvb2zFPCLq6uo0A1ArHx_7HCgUSAK4AAfAB8AHwAfD_AWBYBYrzAfAB8AHw" + "AQBcZPhQUFAVA50IAfABABsA_yEAXMTZBdMF9DUB8AHwAfB_AQBrCiEC5_AB8AHw" + "2gSMHIyMlgD5A0gJcXFxGGFhYfPzFARUVFRAQEBAMzMzzg3j_OPjsAFO8AHwZvBm" + "8Gbwj2bwAfAB8AGQnJycA_OPAfAB8AGQjwenp6cuMj8B8AFgFDfhAELwAcCoqAao" + "GwYzENDQtMnJAEqtrafb2-36DvoD8ANgIQA4pqaWPLy8EgkD8APwA2DQzY7NAzAb" + "8CfAoqGhq_DHAfAB8AFgoqKiCzGxAz_NAr0DwwPJA_AAigy4uP64xwipBR4A0QHt" + "A_MD-QN_XDQLBFz0AfAB8AHwXGT3IPf38_PzYwDp6eLpRgLc3NycAPEL2QX_jQAJ" + "APc7y_EB8AHwAfABkP-NAwEApfMB8AHwAfBcZMEL_7g4CQABwLg4CTBc9Fz0AfD_" + "AfABYFkEhfUB8AHwSwZUBghXV1fVA5OTk2yMbGzw8wEAXFxctAAAPT09Nzc3srL-" + "shwCNg9O8AHwZvBm8GbwD2bwAfAB8AHAra2tgw8B8AHwAfAB8IODlZX-lVEAIQOX" + "ApQyPwMMAPcLP8UERA0z8AHwgQPQYqDDAMM_rKzJ7e3THPLyA_ADwCcANqmp-Hiz" + "s3k4A_AD0FY0GPCBA-DMysqamZmr8P8B8AHwAZBbBWdltwMaAcMDA8kDlwuxsbG5" + "ubn_EgCjNecD7QPzA_kD_wMFBB9c9AHwAfAB8Fw0_f39MVxk7-_vaQD7Ct3djt0n" + "A4EAkAC0tLRUCf-UAgA8OQMB8AHwAfAB8AFgP-n3AfAB8AHwAQBclFVV_lV6BycD" + "AQBLAwEADDAVAP8bAFyUrwJcBDUK6gCPAVfwfwHwAfC_beHwAfAB8KsMmSCZmU5O" + "TooMc3Mec0z4AQC0ACEAOjo6iD8_PzAA4ODggw3_S_AB8GbwZvBm8GbwAfAB8DEB" + "8MXFxQPzAQCIiICIqaiopaOjEmCIm5qaEgCRkJAt8B8B8AHwAfAB8AEAjo6OA88G" + "nDCux8cmpKTAsejoterqA_AD8AEDAK_n5yCion0ctLSOaAPwAzDOzMw4z83NAwAb" + "8APAwsDwwI6NjavwAfAB8AHA_9UP2ACLMhE9wwPJA98CJwbxxAjExMTlAuED5wPt" + "A__zA_kDXGQRBFz0AfAB8Fz0AUsA-_v7-Pj49AT09GMA6urq5eXw5d7e3k8Cwgch" + "Bhc9-MbGxg8AkgG0AAHwAfD_AfAB8AFguzir8wHwAfBc9IdcZAEAKgNmZmZnAYD_" + "DwA_M1z0XPQB8AHwAZDOAT8j8QHwAfABAMoF9wtJSSJJBAViYmLww19fjl_LDasA" + "SQhNTU1GCP-kAc40AfAB8GbwZvBm8GbwXwHwAfAB8AEApQyCAfCCEIK3tbV7A5qa" + "mgMMACQAysjIxcPD-IeHhyrwAfAB8AHwAfAZdRC3t5YwjwsZnZ3AVsLCk-DgA_AD" + "8AMDYDMABZWVnL29xzs0A_ADAM7Ly1xkA_DxA5C0s7OVAajwAfAB8PEBwJCQkFRj" + "sQO3AxE9-KamppcLZQQSD-sFGwD_4QPdAe0D8wP5A_8DBQQLBD9c9AHwAfAB8AEA" + "XJTw8BDw6-vrbwDf39_419fXTwhoCpMAjAGDCv-XArs4AfAB8AHwAfBfxPD2DwHw" + "AfABwFzEZWVlb_hvb3ABgA8AFQBc9E8L8dwFvr6-5wBX8AHwAfA_AQBZ9AHwAfAB" + "YIcAQ0MOQ7EM5APzw1hYWEYARkY0NDRjY2P_yApyBpw2AfAB8GbwZvBm8B9m8AHw" + "AfAB8AEwoKCgPwnzAQBHBE0EUwQJALu68LrPzMweAyfwAfAB8EcB8AHwAQCNjY2n" + "YVkAsLAAnJxPxcV4cNbWA_AD8ANgMwAAAJubHZycysvLH7QAA_AD8APwAwDNy8v4" + "o6KipfAB8AHwAfABAPioqKjbMKsDygK3A70D_8MDXAQoCC8N4gJDDtsD4QP_5wPt" + "A_MD-QNclCb0AfAB8AMB8Fw0-fn59fX1APHx8ezs7ObmDuYHC7QGgwHAwMCn4Ken" + "s7OzkwBsCZMA_z_zAfAB8AHwAcCo8wHwAfCHAfBcxAEAenp6dwGA_w8AG2Bc9Fz0" + "AfAB8AFg2PAPAfAB8AEwHwjLy8s94D09SEhImQD5Y2EIA8INDgQzMzOBgYHA4uLi" + "29vbP_AB8P8B8GbwZvBm8AHwAfAB8AHAjy8HVwAB8AEAlZSU4Af4wL6-BgBtAiHw" + "AfAB8B8B8AHwATCeCpMwt8rKAAWengCioii4cLhOzMwD8APwA2ApILm5AKGhXwSA" + "th62twAD8APwA_DFxMT4kZGRovAB8AHwAfABMPjBwcGqN6sDsQPQAr0D41YBXASu" + "rq4rCJoL6A7_IQDhA-cD7QPzA_kD_wMFBB9c9AHwAfAB8Bdt_Pz8AVcA9vb28vLy" + "7QDt7efn5-Hh4Tja2tqlALANljC5uf65SwkwDPc7AfAB8AHwAfA_AZCf8wHwAfAB" + "wFz0dnZ6doUOfgEgCQAPAFz0lCCUlJubm1wEvb3-vXsDV_AB8LTzuDgB8AHwjwHw" + "sjVLBqIANjY25AMjmQDtA1RUVJ8AOjqAOjAwMJ6enpgB-NjY2DzwAfAB8GbwZvA_" + "ZvAB8AHwAfAB8DcCiooeigzzATD7AkIMurm5_xvwAfAB8AHwAfABYMsHkzAAYLS0" + "AKWlAKkAqQSsrCvBwS_8w8MD8APwAwAtADMAOQDxXwQso6P8HAPwA_ADsP_tAKsJ" + "n_AB8AHwAfABMFUOH25hqwOxA7cDvQOkpKSB0AiwsLC4uLh9BP_VAwcC4QPpAe0D" + "8wP5A_8DfwUECwQR_QHwAfAB8Fxk-gT6-lw07u7u6Oji6BAL3NzcMAPFB60N_5kA" + "TgmWMLs4AfAB8AHwAfB_98IEDl_0AfAB8Fz0QWRewF5ej4-PiAEgCQD_DwBc9Fz0" + "AfAB8FmUAQDS8B8B8AHwAZD_AC4Ic3NzAC4uLjExMTU14jVGCC0tLWQI0QpmBvjV" + "1dU28AHwAfBm8Gbwf2bwAfAB8AHwAfABMD4HgOMB8AEglJOTHgzGBhvwHwHwAfAB" + "8AHwATCEhIRhHWclqqoAIwQXDbEAsQSzsxO7uxX8vLwD8APAJwAtADMAORDBXwSe" + "nrrGxr0AA_D7A_ADYKifKZ_wAfAB8AHw_wFgqAPbMKUDqwOxA7cDFQAIoqKirgmy" + "srK64Lq6w8PD1QOvAuED_-cD7QPzA1w0BQRc9AHwAfAPAfBcZAYJVwD39_fzAPPz" + "7-_v6enpeOTk5GwPhQL-BM4KpSylpWIECQDGRwHNzf-6AKEBAfAB8AHwAfABkFkE" + "f3UAl_IB8AHwATBc9AEAZYhlZZMBIGZmZhiQ_1xkGwBcBIYB5ACVAVfwAfD_Csgt" + "NgHwAfAB8AGQSwB6AQHjBHV1dVNTU2fgZ2eZmZmPMRACMPD_AfAB8AHwZvBm8AHw" + "AfAB8GMB8AFgxMTED_MBMJPIkpLNIgulpRvwAfA_AfAB8AHwATBdBo0wxs4gzgCo" + "qAAmBLe3CAC4uAMwAbe3Ajy2tgPwA2AhAC1gALYAtgCvrwCkpI48u7u9AAPwA_AD" + "AMrH8MeXlpaZ8AHwAfAB8P8BwKkI2wCfA6UDqwPNArcD470DuAirq6sdDRIAfjz_" + "ZgPsAfIB7QPzA_kD_wMFBH8LBFz0AfAB8AHwXGRoDfgA-Pj09PTw8PAA6-vr5eXl" + "39_G34ICqADHx8dRBvgE-Le3t_UEuzg68gHwAfD_AfAB8DUHOPcB8AHwXPQB8P8B" + "wFz0XPQB8FnEPwwTBcnw_wHwAfAB8AEAsDqAAbMKAQD43t7eEQEn8AHwAfAB8H9m" + "8GbwAfAB8AHwAfAB8JBIkJB_AfB_f1wEzPzKygYDG_AB8AHwAfAB8BEBMJeXl4ow" + "q8jIARoUubkAv78AwALAA6C9vQC7uwD8urpERAkADwAVACfwXwTgqalztra9AAPw" + "A_DAwL6-i4qKlvAB8McB8AHwAcCVlZXbMLU4f6sDsQO3A7UIwAOSCsQIvvy-vs8D" + "IQDbA_sB5wPtA__zA_kD_wMFBFz0AfAB8AHwBwFgXDRuDfX19fHxgPHs7Ozm5uYc" + "C4jZ2dk_A8nJyaoNcZ8AsbGx6gacAPgEzvzOzqIAAfAB8AHwAfABwI9f9AHwAfBc" + "lFdXV0r0_wHwJwBc9Fz0AfABYFkE6Qf_w_AB8AHwo_UB8AHwAfAB8P8B8AHwAfAB" + "8AHwAfDNMgHw_wHwAQBJAhXzAfAB8AHwAfAPAfAB8OYBijCexcUAxLS0IxTGxgBo" + "BAPwYwPwA5DHxwBCAHQErYCtbLW1zMnJA_CBA8CysLCCgoKT8P8B8AHwAfAB8EoE" + "2ACZA58Dj6UDqwOxA7cDoKCg2QLjwQghAMDAwM8DuALbA__hA-cD7QPzA_kD_wNc" + "NCD0_wHwAfAB8LtoaA1dAHQNeg2I7e3thg3i4uKWD_GSB8vLy-8E_gRXCbs4_xIA" + "XPQB8AHwAfAB8HIztAkPnfsB8AHwKQRPT09OHwHwAfABIC0AXDSampr4p6enXAR9" + "AVfwAfABAP9ZBAQFwPAB8AHwAfAB8AHw_wHwAfAB8AHwAfAB8AHwAfAPAfAB8AGQ" + "9wKFhYV-fwHwAfAB8AHwAfAB8AGwljSWloowph0EdgjExMAAzMwAz88D8APwEwOg" + "Uw3JyfQYr69u4La2y8nJA_ADYAgN-J-eno3wAfAB8AHwAfDxAQCKioqTMwYAnwO1" + "OA-xA7cDAQC-CLKysrr8urq9M9UD2wPhA-cD7QP_8wP5A_8DBQS1-AHwAfAB8P9c" + "xG4NdA16DYANhg1-P-QDjxACpA2wCruYzMzMxgD_AfAB8AHwAfAB8F80pvsB8AUB" + "wEEBIEREREBA_EA_AfABgCEAJwAwMFz0_wHwWfSiALrwAfAB8AFgfvP_AfAB8AHw" + "AcBp8HKQePAB8P8B8AHwb_AB8AHwZQEb8wHwHwHwAfAB8AHwAcCNjY2BhzC4ysoA" + "t7d_GADS0gDW1gDX1ycD8APwA3DV1WIUwcGBXASGurrLyMgD8IEDMMPAwI-Pj4rw" + "HwHwAfAB8AHwATCurq5H2DCZA7WYnp6eugOs4KystLS03ALJA88D_7gC2wPhA-cD" + "7QPzA_kD_wP_XPQB8AHwAfBcxBc9dA16DT-ADYYNMAmSDUIDSAPIyPDIs7OzXQkB" + "Bbs4OQz_Gj0B8AHwAfAB8AHweAAWDg-BMNDyAfABYDU1NTR4NDQxAfAB4CcALQCS" + "EQEgmZmZjAG7u7vH7QBU8FfJ2traWfQB8P8B8AHwAfAB8AHwAfAB8KEB_wHwJ_AB" + "8AHwAfAB8AHwAfDDAQA6AoODg30B8AHwHwHwAfAB8AHwAYCBgYEdBWETuAhcBCYE" + "29sAHN7eA_AD8ANA3d0AINraANDQUx2srJKyDwPIyANAx8cDEPEPULe1tagJh_AB" + "8AHw_wHwAfABMGMA7DfbAF8BtThjqwOxA5-fn80FEQ224La2v7-_3AIXN9sD_-ED" + "5wPtA_MD-QMRPVz0AfB_AfAB8Fz0GAl6DYANKgnnIOfn4eHhRQPS0gLSsQDBwcGh" + "oaEYpKSkMw8WBcbGxv9dCbvyAfAB8AHwAfABYMMDP8AJX_QB8AFgeQiiAKmp_qld" + "D5EIOzQB8AGQJwAtAP8zADkAPwBU8AFgrA6QAM85_wHwAfAB8AHwgfMB8AHwAfD_" + "XPQB8BgwLfAB8AHwAfBp8P8B8AHw7gIh8wHwAfAB8AHwjwHwAcDhD4cwTbq6FB0B" + "XATg4ADk5ADlDuUD8APwAxDi4gDcgty4GLy8JK2tSmQYycfHAwAPYKempn9SCIHw" + "AfAB8AHwAfABYK38ra3SMJMDmQPEAqUDqwMPtQgRDdYCxAi5ubnC_MLCyQPPA9UD" + "2wPhA-cD_-0D8wP5A1z0AfAB8AHwAfARF83u7u4wCePj4_88CZkMFz14Ca4AEAUE" + "BRwF_4QDLgU68gHwAfAB8AHwX5Q_hwAUAerzAfABAGIBwMD-wKwFWQEyBJEIAfAB" + "wCcAPy0AMwA5AD8AVPABMNTU8NTe3t4RMQHwAfAB8B8B8PaTGQJvNgQCxMTE_wwA" + "iD59ARwCnDZ-BuMKJADHYwnpBAYAo6Oj8goKC-NkAgAGqKioDwAvATAA_wHwszEJ" + "MM8AywH5AJUKbwD_OQBCMH4DPwCLAnUACzchAP8eAC0AHQG3D5kAhDDG8AHwwwHw" + "OgKEhIR8AfAB8H8B8AHwAfAB8AGAJAyHMJ1BWQS8vADR0VwU6IDoAOvrAOzsA_Az" + "A9AnAObmuBhWDbS0iGa0tFM0ycbGCUAAx8fGxMSVlZU_e_AB8AHwAfAB8AGQiIj-" + "iOY30QFZAZkDpwGhAasDGJ2dnRH9vgLc3Nz_2wPhA-cDEf0B8AHwAfAB8ONc9Bed" + "5eXlF51zAgo1_4QDqzB2AtgAvvIB8AHwAfD_AfABYAYDigAXATX3AfAwAP-cAAMD" + "qAASA58DAfABwCcAPy0AMwA5AMMzAcBZBNnZ_tnxBc8Dn_AB8AHwAfDtkwjT09Md" + "BHV1dWkgaWk8PDxICZSUAJRERERtbW2OYI6OkJCQ-AqIPokAiYlVVVVlZWUBtwCX" + "l5dzc3NgIGBgWlpanwZjYxBjLCwsQgCRkZF_LARp8AEARQAGMNQBJwBqAGpqSEhI" + "W1tbAz0LRQA4ODhfX18_UwTmBBUAAQByAM4ESUkASVxcXFNTU4YAhoaamppNTU3A" + "Pj4-eXl5tQJRMP8B8AHwAZBZByfzAfAB8AHwRwHwAfABkIeHh7ViPGC5uQDGxikU" + "XATugO4A8fEA8vID8AcDcCEAXwTj4wDT0wAAvr4Rq6vAxcbFU2QDMLy6uiYBePD_" + "AfAB8AHwAfABwIMByQDSAP-JAb4CmQNWZ_0FET0jDRHN_9sDEZ1c9AHwAfAB8AHw" + "F_3_F836Ar0AyQYEBV8EwzYmDf-uAFz0AfAB8AHwAfABwBcB_94DwTj886PLaDFQ" + "AWsBMgT_AfABwCcALQAuNTP8OQmPAfjX19eW8AHwAfAB8AHwP3UALATeA2wAEAIx" + "AmhoHmj8A1UFEgC2DXJycuMJANoEWVlZnAYOAQEAH3oEGwAVAKIGugCysrL4V1dX" + "pAQ2AMA5AfBcZPHYAFBQUHkC7QZlB10A8QAJUlJS7AGNAOkBDABHwgTjAVcAVFRU" + "_wB34Hd3enp6XQD7BO4LvxIAUWAB8AHwAcC6AHsB8P8B8AHwAfAB8AHwAYALCoQw" + "AKbExAW3twDNMM0A39-RGD4E9vYYAPf3A_AYEPT0AATw8IYU2NgAxcWAAK2teLa2" + "ySEBIMfHyMbGAzCurfCtf39_cvAB8AHwAfD_AfABwHgGyTCJAYkAkwOZA_-fA6UD" + "XATRAcEIvwHiAr0D_yQA3QER_RH9AfAB8AHwAfBHXJS8BGkA9vb2F23kHOTkF23M" + "BoILnJyc_zcLPQveBho9vjgB8AHwAfD_AfAB8AGQHQHSCQEAUQNt-_8BYG4xnABp" + "AwHwAfABAC0Axyg1SJD5AODg4OsFigP_jfAB8AHwAfAB8M46agg0CBhFRUUlAm8A" + "SkpKfy4I3gNoAZ0IhAZVBYMBRgBGRlFRUYCAgMC1tbVWVlaGBEcExw4BMwBqAnFx" + "cavwAfDHAQCbBF8HOzs7iALhAPGEAIODgzsESwBpAHgAMVsIXl5ecgA2CTY2_jYM" + "AG4EAQIeACEJCgViMY_-8QHwAfC4Pn19fTDz_wHwAfAB8AHwAfABYPkAt2BmehcN" + "vgjPz18UlAjzAPMA-PgA-voAZPv7A3D5-VwUtQjnBudZFOsIsbFCrKwDU2QDAMbD" + "w5ybm_9s8AHwAfAB8AHwAfA-AUsM_981Dj0rAtY-XAqJCqQBDwDxKAK9vb2hANAy" + "zwPVA_8Rbe0D8wP5A1z0AfAB8AHwxwHwF_0XPebm5l0zcwL_wAAQAnwLEwXIDRAF" + "2wDsB_-WMwHwAfAB8AHwAfAB8CMBwN7e3tvb21cDWfT_AfAB8AHwAcDFBGMA5QWE" + "A_-B8AHwAfAB8AHwAWARBDk58YsIYmJiWQeBDHUGFQAPcgaQDHoBMQiZmZlw4HBw" + "uLi4GACJAWc4xyEAOQAUAWtra34PqgE_cDIB8AEwzjQ2MFYNNzfGN6EE5wBkZGRH" + "BFYBR7kE0wiaCD8_PwsHbwBvb4GBgY2NjceTAPgEOQBOTk6EAGUE_w8AXPQB8AHw" + "ATAGA84EAfD_AfAB8AHwAfAB8AFgIweHYABuuLgAtLQAyILIOBTl5QDt7cEYHPX1" + "r0gJAFYE6uoAAODgANLSAMDAAbIIQKiow8TEyAzFxQMAVgS8u7uK_IqKZvAB8AHw" + "AfAB8AHw_wEA-QzAMIEDywGNA5MDUAT_nwNcBB4AIgIhBqoBPQIkAP8ZAskDzwPV" + "AxH9WfQB8AHwjwHwAfAXzc4E8vLyNgnA6Ojo4uLicALZAv-RAlwHuzjtBvoCgQm3" + "APU3_8TyAfAB8AHwAfAB8AEwxwv44-PjxAjHO3b7AfAB8P8BwAJtVwA8A3XwAfAB" + "8AHw_wHw7cO2CsMDmw0dBDIEdQM4SEhIigZfATECT08GT2sBnACHh4dubo5ucQFM" + "AoMBXV1dCQDHdQ_rBa4AW1tbWwJwBUdj8AHAJAB0dHTIAbMcs7NpANUGhABNTU1H" + "cgC2BBsJeXl5LQM9_D09owIYAIQA7wSvCDkA_9Q0EDWKAF3wAfAB8AEwmwT_0gA2" + "8wHwAfAB8AHwAfABkAPBAopgh7q6CqqqAcEYx8cA09MA25LbShTi4gMQ3t50FADO" + "zgDCwgCyssAAoaFfrKyBAQMwwMTBwainp0MIYPD_AfAB8AHwAfAB8AEAOAF1Mx-d" + "OygCIgLSABYCmJiY_wYAEwLZDjYGVQK3A9wCwwP_yQPPA9UD2wPhA7U4Ef0B8B8B" + "8AHwAfAX_cgE9_f3ATAJ7-_v6urq5fzl5cIHXQ9YAg0CfAK0AONBBwADrq6uawSR" + "AhgA_1_0AfAB8AHwAfAB8JaTVAP_CAQBZfECMvQB8AHAtgfhAOMvCq0E2dnZn_MB" + "8AHwHwHwAfAB8AEwhABycnJxOghqamp1BssKwANg4GBghYWFewYxCAwAgVUIiYmJ" + "WVlZDwA4p6enjwS6DAUEdnb-dl4ChgHrC2cISwBUMJIBYwHwcmCLi4shCdEEgoyC" + "gr8BXwR4eHgIAf_aAbgICQBZAbsI2QiGBGAA-K-vryoAJwCSBAkABQf_sgIh-QHw" + "AfABkK8LHAU88_8B8AHwAfAB8AHwAWBXDFsFAYpgt8TEU62tAgCkpACurgC3t0AA" + "vb0Av78DELsGu9YYegSenjeiooikvr6sCMfExAMAwMG_v5aVlVrwAfD_AfAB8AHw" + "AfABMDIBOAHjMf-8AfsBhwONA5MDvwFAAgkAiKioqDoCt7e3sQP_JADTAuICyQPP" + "A9UD2wPhA__nA7U4XPQB8AHwAfAB8Lv4ASoJ9fX18fHx7ADs7Ofn5-Hh4Y8eDB8I" + "TALJMJ2dncYA_xwFzwbnAGcCvjgB8AHwAfD_AfAB8AHwAZCZAJdlrgAJAP__CScM" + "AwMBANaYZQEVMB4A_9UAUAQzAPg9qThd8AHwAfD_AfAB8AHw8GOZABcEYzxoAf_g" + "ClMBAQAJAL0AhwxRPx4A_6IGCQBuARgw2TVsCSoARgL_3gAlO8nwATBpAB4wPwBy" + "AP8BAPkAeDBLAFMEGADOMQkA_1EATgAPACEwCQAhADMACQB_QQEkAMg0ZvAB8AHw" + "AWCr_KurIgUB8AHwAfAB8AHwHwHwAWBfBH4AjZC2w8MAebGxO6KiKKEAoQKZmQCY" + "mCHAnp4vnZ1moS4nYMDFwsK6uLj6BVTw_wHwAfAB8AHwAfABYLRggwEfjAGBA4cD" + "jQOTA5aWlvG1CKSkpAEIOgX7N8UB_1UC7gIbA88D1QPbA-ED5wP_tfgB8AHwAfAB" + "8Fz0uzgXPQHaBO7u7unp6eT85OTyBMECuwLPAHMCJQvHxgDDBiILtbW18QLSBv--" + "OAHwAfAB8AHwAfAB8AHw_wEAFQNKAaYFYgQ1BAEAwAD_ZA4BMEo09gxrAV8EUASN" + "M_8B8AHwAfAB8AHwAfAB8AHw_wHwAfAB8AGQjwEB8CfwAfD_AfAB8AHwAfAB8AHw" + "ATBMAn9C8wHwAfAB8AHwAfABwJT8lJTTApbwAfDPM8cFeAz_QAhL8AHwAfAB8AHw" + "AfABYH8sAeBhdQN7A4EDhwONA5X8lZW1aGgKtTixALcDvQP_wwMJA88D1QPbAxE9" + "tfgB8H8B8AHwAfAB8GYAFz3UBPT89PS7OJMDuziKM7s4HAXjJTVGBbi4uOQA_A_J" + "AP_Q8gHwAfAB8AHwAfAB8AHw_wHwAfAB8AHwAfAB8AHwAfD_AfDB8gHwAfAB8Fz0" + "AfAYMP8t8AHwAfAB8GnwAfAB8AHw_30HBAiVDQHwAfAB8AHwAfDHAfABkCkEm5ub" + "xAKc8P8BANYCAQDfAogOwAMtAELwPwHwAfAB8AHwAfABkISE_oRHOpAwsQB7A4ED" + "hwONA_-QM8EItci3A7U4yQO1ONsD_-EDET1Z9AHwAfAB8AHwXPTjFz27OPLy8rto" + "Pwz-BHjOzs67OBYFhAmQD6L8oqIlBckwSQVf9AHwAfD_AfAB8AHwAfAB8AHwAfAB" + "8P8B8AHwAfAB8AHwAfAB8AHw_wHwAfAB8AHwAfAB8AHwAfA_AfAB8AHwAfABYNkL" + "fn7-fk7zAfAB8AHwAfAB8AHwEQEAioqKLzenp6cAra2trq6upqYQpqOjo-gCg4OD" + "_zcIM_AB8AHwAfAB8AHwAcD4fX19ZgNZNGkD4zd7A-OBA58Ak5OTHwUMALUI-LCw" + "sLVosQO3A70DtWj_1QO1OBH9AfAB8AHwAfAB8CMXndQE9_f34ATw8P7wu_jYMMMA" + "UAogCsQIJg34vLy8XzQ68gHwAfAB8P8B8AHwAfAB8AHwAfAB8AHw_wHwAfAB8AHw" + "AfAB8AHwAfD_AfAB8AHwAfAB8AHwAfAB8B8B8AHwAfABYIMHfHx8XncB8AHwAfAB" + "4IsmBJ3inQOQgYGBJ_AB8AHw_wHwAfAB8AHwAfAB8AHwATD_1wSLCCkxWTSOBXUD" + "pgiBA4iSkpLpB5-fn7U4-LS0tLX4tTjPA9UD2wP_tThc9AHwAfAB8AHwAfC7-P-7" + "-L8N4QCQOcwA2ADmDd8F__wA8AC-aAHwAfAB8AHwAfD_AfAB8AHwAfAB8AHwAfAB" + "8P8B8AHwAfAB8AHwAfAB8AHw_wHwAfAB8AHwAfAB8AHwAfD_AfAB8AEwLQNFA1rz" + "AfAB8IEBwJ2cnMTCwgOQwL-9vYaFhSrwAfD_AfAB8AHwAfAB8AHwAfAB8P-1CKAI" + "IzFZZG8DdQN7A4EDEaoNnZ2dYAmpqan4sbGxmQPgPasDtfi1-P8B8AHwAfAB8AHw" + "XPS7-PIECOfn57s409PTzIzMzPgNqQi1tbWKCceQOY0JDwC-vr5f9AHw_wHwAfAB" + "8AHwAfAB8AHwAfD_AfAB8AHwAfAB8AHwAfAB8P8B8AHwAfAB8AHwAfAB8AHwPwHw" + "AfAB8AHwAfABwKqq-qpOA3YB8AHwAfABgFwUIMHBiomJDzCUk_6TDwAVACrwAfAB" + "8AHwAfD_AfAB8AHwAfAB8EQEWWR4AxFjA7a2tkcEnJycx3sDXAR-A6CgoMgNxQ3_" + "hAOZAycApQOrA7U4vQO1-P9c9AHwAfAB8AHwAfC7-Ls4iPPz8_IE6urq_gQA3t7e" + "19fX0NAA0MnJycHBwbn8ubkoBTcF5ADqAPAAX2T4xcXFCwEB8AHwAfAB8P8B8AHw" + "AfAB8AHwAfAB8AHw_wHwAfAB8AHwAfAB8AHwAfD_AfAB8AHwAfAB8AHwAfAB8P8B" + "8AHwAfABMKUGX_QB8AHwAQGQnJubw8HBpgykpAkAAwC1s7O-4Ly8gICAKvAB8AHw" + "_wHwAfAB8AHwAfAB8AHA6gw_FzFRA1cDWWSpCHUDkJACkA4Nnp6epKSk-Kurq_kJ" + "kwOZA58DpQP_qwOxA7cDvQPDA8kDtfgB8D8B8AHwAfAB8AHwu8j4-ID49fX18fHx" + "uzgA4uLi29vb1NQw1M3NzfAA6gC3t_634w3kAOoA5ADhAPwAX_T_AfAB8AHwAfAB" + "8AHwAfAB8P8B8AHwAfAB8AHwAfAB8AHw_wHwAfAB8AHwAfAB8AHwAfD_AfAB8AHw" + "AfAB8AHwAfCAB58JDGnzAfAB8Fx0wMADkMCvrq6Eg4Mq8AHw_wHwAfAB8AHwAfAB" + "8AHwAZB4oqKiWcRvM2kDbwOP4I-PlpaW1AcnAG4E-LCwsB4AJAAwAJ8DpQP_qwOx" + "A7U4wwPJA88DtfgB8H8B8AHwAfAB8AHwu_jyBO8E7-_-BObm5t_fAN_Z2dnS0tLL" + "jMvL8ACBCbS0tJAJ_-oAXzT8AOQwX_QB8AHwAfD_AfAB8AHwAfAB8AHwAfAB8P8B" + "8AHwAfAB8AHwAfAB8AHw_wHwAfAB8AHwAfAB8AHwAfAPAfAB8AHwAcCzs7N7OHt7" + "dQHwAfAB8HV1gScMwsDAiYiIDzDjVgQPAKqoqCrwAfAB8P8B8AHwAfAB8AHwAfAB" + "MFkED8AJWfRdA8oIlJSUjoyOjgYADm2tra2HAz-NAyoAmQOfA6UDqwPl5f7ltwO9" + "A7X49vMB8AHwAfAPAfAB8Fz0u5jy8vLtAO3t6Ojo4-PjAN3d3dbW1s_PMM_IyMga" + "PY0Jo6MGo_Awvmi9vb3Cwv7C-w0dAQHwAfAB8AHwAfD_AfAB8AHwAfAB8AHwAfAB" + "8P8B8AHwAfAB8AHwAfAB8AHw_wHwAfAB8AHwAfAB8AHwAfCPAfAB8AEwSwN9fX14" + "8yMB8AHwm5qaTQSlo_6jCQADAAkADzAq8AHwAfD_AfAB8AHwAfAB8AHwAQATDv-6" + "CQUxPwNFA0sDUQNXA10DgVk0kpKSmpqaaAT4paWlDp2TA5kDnwOlA_-rA7EDtwO1" + "-Fz0AfAB8AHwDwHwAfAB8LuY9_f39GD09PDw8F0JuwjhROHhGp2_v78HArEAsbGh" + "oaGMjIz_HZ1f9AHwAfAB8AHwAfAB8P8B8AHwAfAB8AHwAfAB8AHw_wHwAfAB8AHw" + "AfAB8AHwAfD_AfAB8AHwAfAB8AHwAfAB8IEB8Lu7u4WFhX7zIwHwXPTCv78DkKmo" + "8Kh5eXkq8AHwAfAB8B8B8AHwAfAB8AHwenp6HykEWfRZNF0DYwOQkJBAmZmZnZ2d" + "0Qeq_KqqDv2fA6UDqwOxA7cD_70DwwO1-AHwAfAB8AHwAfD_AfBc9OYEkADyBFEJ" + "uwgaPQ9pCRqdAgH_ALW1ta_Er6_-DYuLi1UFCAH_9gAVAF_0AfAB8AHwAfAB8P8B" + "8AHwAfAB8AHwAfAB8AHwvwHwNGIBAL8KGgEBAMABgP-pBQHwAfAB8AHwAfAB8AHw" + "_wHwAfAB8AHwAfABYPCQAjHfCwEUMSjyHgBRA3QB8AHw_wHwAfAB8AHwAfAB8AHw" + "AfAHAfAB8AGwhoaGurp-ulnERQNLA1EDVwNdA48gj4-YmJhpA6KiPqI4DXUDZjMw" + "AA797u7-7rcDvQPDA8kDtfgB8AHw_wHwAfAB8AHwX_QanV0JGp0A19fX0NDQycl-" + "yQUBYwbyDcwJUgUCAZH8kZH_APwAOQ8bAEYyQ5L_AfAB8AHwAfAB8AHwAfAB8P8B" + "8AHwAfAB8AHwSmRNNLkKx2ADGw9GAri4uBkCIwH_9gl-BigCQQEB8AHwAfAB8P8B" + "8AHwAfAB8AHwAfAB8AHw_wFg5wDtAPMA-QD_AHQH0Av_EQF6ByhiK8JJAioMjfMB" + "8P8B8AHwAfAB8AHwAfAB8AHw_wHwAfABMDIE9jAoMgsBOQM_IwH8AEsDUQNXAw4N" + "l5cGlw8AJAynp6etrf6tdQMnADAAhwONA5MDDv3_Dv0B8AHwAfAB8AHwAfAB8Adc" + "NBr9Gj3n5-fi4gDi3Nzc1dXVzjzOzuYNMwMtAwsBrKxirAoOioqKugkJAKb8pqbk" + "CRsAUQNf9AHwAfD_AfAB8AHwAfAB8AHwAfAB8P8B8AHwVvRTZFAEJgFjA-YK8TcC" + "pKSkgQYrApkGAQD_6QoBAD0CAfAB8AHwAfAB8P8B8AHwAfAB8AHwAfABwN4w8eow" + "oKCg9gD_ACQDmAf_EQEXAWgEKgMpAS8BLsKABPCHh4dzAfAB8AHwAfD_AfAB8AHw" + "AfAB8AHwAfABsP-tDTYDzpoCATMDOQM_A0UDj0sDUQOyCFkEm5ubOwH_GAAzADIB" + "FwEsAYEDhwONA_-TA5kDnwMO_Vk0XPQB8AHwfwHwAfAB8AHwu_i7aF0J8wzz8xo9" + "zg3g4ODa_Nra4A3mDewNEQE5AxEB__YASQIOAWc1Fg46AjcCugn_mZYB8AHwAfAB" + "8AHwAfAB8P8B8AHwAfAB8AHwAZCBNpIH4ysC2DmpqalNBF0D4QnYlJSU6Qo6Ao0B" + "IJkG_wEAOAEB8AHwAfAB8AHwAfD_AfAB8AHwAfAB8AHwAQDkMPiMjIzwAC0DjwcF" + "AYyXHyMBKQEzAy5iMZKlpaX4eHh4n_MB8AHwAfAB8H8B8AHwAfAB8AHwAfABMIn8" + "iYkcAi6S-QAtAwgBOQOPGgFFAyQALAGWlpZZNP8FAZkGaQNvA3UDewOBA4cD_40D" + "kwOZA58DpQOrAw49tfj_AfAB8AHwAfAB8AHwAfBf9McabcINyA3p6enUDdoNgeAN" + "09PTzMzMJQ7_RQPwAO8K_AA_AxQBRgI9Av8RARIAwDkx8gHwAfAB8AHw_wHwAfAB" + "8AHwAfAB8AHwVvR_UQZWlGwGFAEuAuA6hgeEQISEgoKCfwEgfeEBIHx8fHsB8AHw" + "AfD_AfAB8AHwAfAB8AHwAfAB8B8B8AEg4QDqMPYwgYGB_wUBCwERAWQFPwMjAc0I" + "7jv_YgQpzQEAEgCRBaXzAfAB8P8B8AHwAfAB8AHwAfAB8AHA_wsBuQouYght-QAt" + "AzMDOQMxPwOIiIgaMVkEn59-n1k0YwOABAsBdQN7A9L80tKHA40DkwOZA58DpQP_" + "Dv3e8wHwAfAB8AHwAfAB8D8B8Br9aQnIDc4N1A3k5ADk3t7e2NjY0eDR0cvLy180" + "9gBfNP8LARoBrgYNAg0LSgFtBTEy__VqAfAB8AHwAfAB8AHwAfB_AfAB8AHwAfBW" + "9OQAKAK1_LW1VgSPBzMDKwKtB1M08VQDgICABgABAF8EEgD9ATCFAVAS8AHwAfAB" + "8AHw9wHwAfABkIYBILcAAQDOAf8BADMGAQCCmFwHWQcDAwEA_zoFATD1AQHwGAAk" + "AC0AMwDHGQJIABExg4ODBgAPAP9OACkBxwhiBMQ4nQUx8kkC_TgBcgHwAfAB8AHw" + "AfAB8A8B8AHwAfAB8HJydHT-dGMGcwgxwhsD2AAnAy0D_ywBIAEdATIBCz1ZxN8I" + "RA3_dQN7A4EDhwONA5MDmQOfA_8O_Vz0AfAB8AHwAfAB8AHw_wHwu8hfNGkJugDO" + "DdQN2g0A4-Pj3d3d19f-11808ADVOV806Qp_AiMBMUwCmJiY5wmTBra2_rZXbzQy" + "AfAB8AHwAfAB8P8B8AHwAfAB8AHwAfDMAAJt8eQAr6-vrAgoAoE2RTP_AQCrA5xj" + "n2MBAKhjATCBM_8B8AHwAfAB8AHwAfABwIEAjwHAPjRBNCw0k5OT3gD-lQFQXwcB" + "MA8DAfABMCRg_zMwPwBFAF0AzDnOBAYACwH_FQBiBE4DxDhTASY9MZJiAf_9CL3z" + "AfAB8AHwAfAB8AHw_wHwAfABkPsBcgYxYgEACQP_DwMxAuoAIQMnA30ByjViAXFZ" + "lKqqqlcDKgBZNMr8ysp1A3sDgQOHA40DkwP_mQMOPVk0tfgB8AHwAfAB8P8B8AHw" + "AfAB8BrNugDAANQNCOvr69IA4uLi3ADc3NbW1tDQ0P_wAF80AgFfNBQBIwFRAz0C" + "_0kCwgeaBRsAPg0u8gHwAfD_AfAB8AHwAfAB8AHwAfAB8OMlwlgFsLCw5AAQAkID" + "_6xoAjEPAKUzy2efY3VjnGP_q_MB8AHwAfAB8AHwAfDlAsUBkJkBsJqamvQCATD_" + "BQQBkE8FAcAhwDlgRQBOMP9gAOcACAEaAbAHKjlRPz8A_6MFRwRSAsQ4NMJuAQUK" + "xvP_AfAB8AHwAfAB8AHwAfAB8P8yBJcIisYDAwkDDwMxAt06_yUC_wAFAQwAWTQg" + "DbJoWTQ_7ghpA28DdQN7A4ED5ub-5o0DkwNZNA79AfAB8AHw_wHwAfAB8AHwAfAa" + "_V9kzg3H1A1fNNgA4eHhX5T8AP--mBwCPQJYO1oDwWjyamL0_wHwAfAB8AHwAfAB" + "8AHwAfDjAfBWxLy8vCUyXQauCf8WAn427guMBzEC-QDvB0o0_0gDrmOcM0IDWwIB" + "MJUEAQD_UgIB8AHwAfAB8AHwAfAB8PcBwNUAAZCiAeDUAQHwAfD_MGBFAFcA6gDz" + "AIA0CwERAf87AXMF4AeRBUEBjgUvAScAP5M2VQJrAfX6twOQA3Nz_HNxAfAB8AHw" + "AfAB8AHw_wHwAeBWBPsBFATg-v0CAwP_r2gnAPAAWQQZPhIAFAFZ9P9ZxHsDWWST" + "A5kDWcRc9AHw_wHwAfAB8AHwAfAB8AHwX2QDaQlflO7u7urq6uNf9B2drKysXwRD" + "AjEy_wQCPQI4ARUAVA8jCmI0MQL_ZTQB8AHwAfAB8AHwAfAB8P8B8AHwHzKxALcA" + "rDhWNAoC_1Y00QoCPVYEjAdTBJoIGwCPTTRKNLEzWAKkpKRAArEOAaenp0IDAQCp" + "AVD_VAAB8AHwAfAB8AHwAfAB8P8B8AEw7gIB8AHwAZA8YNsw_-cA8AD2APwAAgFu" + "NEQEHQH_IwFaAyUCRwGdBVQ_PgEyAT85AKkIVz-WNjTCPAB8fP585PMB8AHwAfAB" + "8AHwAfDHATCHAJkJubm5NPLxAv_3Av0C_wAJA-oAFQPdOoEG_ysCKAIIAREBIQA_" + "A1U1WcT429vbWTSBA4cDWZSlA_9Z9AHwAfAB8AHwAfAB8AHwHwHwGv0QBboAzg33" + "9_f_X_TwAF-URwHOB1QGnQVJAv9AAkcBkDbyCkQBIz0ukmL0_wHwAfAB8AHwAfAB" + "8AHwAfB_VpS3AL0ArDgQAkcEVjSC_IKC4QBWZCoDZwJGAvAA_1A0UQMRASoDdQMd" + "AWcC9wvfSwBGAgEAMgEBALMB8AHw_wHwAfAB8AHwAfAB8AHwAfD_AfABgNgwAQDk" + "AO0A8wD5AP__AHo0CgJLA2gEZTQvATgB_8IH-QliBFkB2gdiNC0APwAfbwNi9AEw" + "0gM5AHl5eX_z8wHwAfAB8AHwAfABMHb8dnb1AdsANPI0kgU9HAL_KAI2P18EOQCl" + "CSsCGwAtA_8iAh0BLwFFAzwAUQNZ9FmU_5MDmQOfAw79AfAB8AHwAfA_AfAB8AHw" + "AfAB8F_09PQw9PHx8b74AgHLy_DLxcXFUwHpAVgCRgL_SwM6AjQCzgeQ9mI00wWg" + "Bf9i9AHwAfAB8AHwAfAB8AHw_yKSnwClAFbEbwaWOVYEJQL__zxWNKk45AAuAlME" + "VQI2AP80An8CYwMaARkLTgAqA0MC_ywBXQABAIUCATADDAHwAfD_AfAB8AHwAfAB" + "8AHwAfAB8P8BwM9g3jDnAO0A8wD5AMIH_wIBQgNQAWg0ZTSCBTUBOwH_bAOTNgEA" + "YgSyBVYBdwFiNB80Mpb2ATAbAKgDgYGB8HR0dHAB8AHwAfAB8PkB8HBwYwDvAccC" + "sG008v80MiQA4Go2PwYA1QAIPS4C_1gFJwMFATMDHwIsAd4ASwP_UQNZ9HsDgQOH" + "A1n0wPMB8P8B8AHwAfAB8AHwAfAB8F_0R1_0X_QCAdHR0Q4BxvzGxl8BWz5OAxAC" + "YAMZAv-gBQIKxDhiZKkFUAEqAHkC_4gCZTQ0MgHwAfAB8AHwAfD_AfABwIQwjQCT" + "AJkAnwClAP-rACIyTQesOF4CBgCsmFZk_ycDLQNTNF4CMQJRA1oDFwH_eAMYM4gC" + "jQ_1CgEAogZMAv8BkEcBAfAB8AHwAfAB8AHw_wHwAfAB8AHwAQDJwNsAAQD_5ADt" + "APMAzjcFAQsBXAEXAf8dAWU0LwFlAcc4IARNAbUF_7sFYjTEaDTyNPIBMNsDMADx" + "cAJ6enod9AHwAfABkMBycnKAgIARBIEA_3gANPI08jRiVjQuNbgIrzj_CG0hA_wA" + "LQM2ABcBTgBZNP9RA1cDXQNZ9Fn0uvMB8AHw_wHwAfAB8AHwAfAB8AHwxT0DX_Rf" + "9NfX19LS0sDMzMzHx8dGAg46_6oHQA4gPUoBIgIuMmI0YgH_FQBcAb0GKgBqArUF" + "fmMu8v8B8AHwAfAB8AHwImL6MocA_40AkwCZAJ8ApQCrACJiwwD_KAXMAFb0GwPt" + "ACsCWwI_AH8uAso7dgJgAGMDNwIgAchcyMjvCkACLwHNAYDO_wHwAfAB8AHwAfAB" + "8AHwAfD_AfAB8AHwAVDYYOcA7QDzAPjJycn_AAUBCwElAjY8_2U0ygg1AXEBYpTE" + "CI0DYmT_ljY08jTyAfDFBEMIPAAPBse_ASkEATB1dXVBZIYE_xhgbQs_CdQBlgBj" + "AFHwNPL_NPI0kukKUQAxYghtIQM2AP8tAwsBFAEgAS8BSwNRA1n0_1n0ojO68wHw" + "AfAB8AHwAfB_AfAB8AHwAfBf9F_0_ADiAOLi3d3d2NjY-NPT00wCPAOoBlEDWgOP" + "IG1xAaoBVgGNjY31B_-iMzoCDAZDAkwCcG5oAaoB_10_lvYB8AHwAfAB8HKW5TL_" + "dQB7AIEAhwCNAJMAmQCfAP-lAKsAsQC3AFbEIQBWlFgC_-0AQgD0MlNk0QdXA2AD" + "HQE4z8_P7Ao9AiwB1NT81NUBIKIGAfABMGQCAfD_AfAB8AHwAfAB8AHwAfABwP-0" + "8AFg3jDnAO0A8wCMB_8A_240aDTWCCMBRWwuAsIHKT3_YjRfAeMHxAg0YkACNPI0" + "8v_s8QHwAfAB8AHwNPI08jSS_wVtGQUxYg8DFQMIPScDAgH_MwM5Ax0BLAFLA1ED" + "VwNZ9P9Z9LTzAfAB8AHwAfAB8AHwHwHwAfAB8F_0X8Ty8vIB9gDr6-vn5-fjAOPj" + "3t7e2dnZ_0kCQAKlBjcCgwEgPfgKkzb_MAxhDpA2dwFaPNMFfQHoBficnJzNlfs6" + "S5w08gHw_wHwHPKdy2kAY5mHAI0AkwD_mQD_PMkGIjJW9NIAVpRqAv9UACsChAah" + "By4CUwTFB1cDjzQCaQONAzoC2traSA__mQYBAEYCATBBAQHwAfAB8P8B8AHwAfAB" + "8AHwAfAB8AHw_wEA1WDkMO0A8wAfAv8ABQH_CwERARcBZTQpAVoDkgFixP_EOGUB" + "YpQ08jTyNPIB8AHw_wHwNPI08jTyNPIFPSQA_QL_MWIVAwg9WgAtAzMDOQM_A_8m" + "AUEBUQNXA10DYwNpA1n0_1n0AfAB8AHwAfAB8AHwAfAfAfAB8AHwX_RfNPX19RH2" + "AO_v71805OTk-N_f30MCPQKbBzcCsAf_YwOPAcFoMAyyBYwB8joECECDg4N_f3-M" + "AYX8hYU9AhIAvQMbAIYBWAL_wAa_AYYBPwANCDcyMAZUAP8BAMMD7wEBYAEyAWAb" + "kDAw_zkA6s9dAD8GwTJ1AHsAgQA_hwCNAJMAmQB7Bo4LkZH-kVb0JTICPVZkbAAn" + "A6k4f4QGCAFIA8IHUARgA0E04Pzg4OwKlgZAAgEAOAEBYP7lAfAB8AHwAfAB8AHw" + "AfDPAfAB8AHwAfDl5dJg5DD_7QDzAPkAIgIFAQsBQgMXAf8dASMBKQFaAzUBYvST" + "ZmI0_30BaQw08jQyQTrRATTyNDL_MfIx8gEwKgA08jTyNPI0wv9WNAVt_QIxMq84" + "YAAhA_kA_y0DCAE5Az8DIwE4AVk0XQP_YwNpA1n0WfQB8AHwAfAB8P8B8AHwAfAB" + "8AHwAfBf9F_0wOzs7Ojo6FUCQwL_qAafBgQLNAJjAxUMMTKRPv-bATIEZQFUP3cB" + "ngHuBes1fzEyDwBDApsBRwohAO41pvympjcycwJIAM8DPTIBAP-LAv4BAfAYAB4A" + "HgYtADMA_zkAGTJLAEI5XQBjAGkAbwD_dQAfMnI_fzskD84KIgInP_9W9AI9VpQn" + "A_kAmAeEBlNkf1cDTTQjAekKOgIvAQEA6f8BIPgKAfAB8AHwAfAB8AHw_wHwAfAB" + "8AHwAfABwN4wAQD_6gDzAPkAdzQLAREBZwUdAf8jAVQDxzhi9F8BZQFixIkB_5UB" + "NPI08jTyDQIxMtsDMfL_MTJHNDTyNPI08jSSVmTrAv_xAik3VAAxYhsDhDYtAzMD" + "_zkDPwMjAS8BUQNZ9Fn0AfD_AfAB8AHwAfAB8AHwAfAB8I8B8AHwX_TwAPj4-PwA" + "APPz8_Dw8O3t_u1GAkACOgIgnRUMwTgMBv9NOv01VG9iNIkB6wWeAfoF-H5-fmBv" + "EgbFARsANzj_4WM5AOwBswH1AQEATgABAP8EAgEwDzAbMCcAMwaXYkUA_0sAUQBX" + "AF0AGG91AAEAFAf4i4uLqWhGAiKSsQBWxP9yDy0_VpQtAzMDKwKEBi4C_04DUDRH" + "NDcCLAEBAO8KAQD_QwIBkEoBAfAB8AHwAfAB8P8B8AHwAfAB8AHwAWDPwOQw__Aw" + "-QCMB2s0EQFlNCMBKQH_LgI1AWMDYvRixDTyNPI08v8wAAECBwJxNAEASwAx8hsw" + "_1UCJwBmADTyNPI08jTyf2v_sz0zP-QAGwMInQ4BFwEgAf8sAUoBVwNdA2MDWfSc" + "8wHw_wHwAfAB8AHwAfAB8AHwAfBHAfBf9F-U-vr6_AD2xPb2EA7x8fFGAiCd_5YG" + "wZhVDrsOZQojPRI2xDj_9QESBpEOZQrIAacBwgGzAT-8MRIGDAAhBsEOGACMjP6M" + "OjIBACEGJDYBANQBAQD_PwABAAwwFQAeAEgGFjI5AP8_AEUASwBRAFcAYDAlAi4C" + "_x4AH2KpOI0AJD9WZN8CVsTxJQLFxcVWlCgCVjQrAv-EBlM0FwFNNDQCKQEBAJMG" + "_z0CATA-AQHwAfAB8AHwAfD_AfAB8AHwAfAB8AHwAcDhYP_tAAEA9gB9NGg0ZwXN" + "OM8J_8cI3gli9GI0awFxAWJklmb_NPI08jQyTwi6DOAxEwLjMf8lMgFgRjJSMl4y" + "NPI08l4C_zTymQACPVY06wIFPf0CAwP_CQOvmCcDCJ0lAikBPgFXA_9dA2MDWfQB" + "8AHwAfAB8AHw_wHwAfAB8AHwAfAB8AHwX_R_AgG3CQ4BIP0xYk8OVQ7I_MjICArc" + "DmLEdGoqBu4FPzFi8gETAr4OJwacBnx88Hx9fX3aAQwA5gEVAPiEhITjAQEA_gEn" + "AAGQ_xIAHgAQAicALQDbNj8ARQD_rm9vNsU6cjYfYuo25zYiYv_fAkcHVpQlMlaU" + "JwMtAzMD_wUBPwNIA444TTQmAY0GNwL_AQA1AQEwQQEB8AHwAfAB8P8B8AHwAfAB" + "8AHwAfAB8AHA_-Fg8DD5AHE0aDRlNE4Dxzj_YsRTAVkBxPiWljTyNPLyAf80Ytk4" + "EwIZAv4BAfAYAAEA_yQANPI08jTyNJKMbVZk9wL__QIDAwkDDwMxMvAACP0mAf8W" + "Mln0kPMB8AHwAfAB8AHw_wHwAfAB8AHwAfAB8GLE8AD_YjQCAbcJDgEgnZY2MTJy" + "AxGgBdPT0yM9x8fH42KUJj2tra1sDBwIxzj_MWL7Or8BVDY3Akg2ODRFNv0BMHoB" + "4BgwKzKABHACwAb4kJCQbDbAZl0AxTofYv91Nv88mQCfACIC2QInPwL9_wI9VpSp" + "OCsCRQPdOk00dTP_LwE1AQFgQAIB8AHwAfAB8P8B8AHwAfAB8AHwAfAB8AHA_-SQ" + "8wBFP2s0KzJIAy4C7zr_UT8xYmL0YsQ08jTyNMLJnPEyBHt7ey7yNPI0Ak00_zTy" + "NPI0ktkCVpT3AgU9CQP_DwMxYicDhDYIbSMBIjJWNP9ZNITzAfAB8AHwAfAB8AHw" + "_wHwAfAB8AHwAfAB8MH4OjIfIG3BODEyHgxPDtra2vjW1tbyOmLEhQ71Osdo_zEy" + "9z5iCvcONDIhBjcyxTr_6QEBAMg6AQD-AQHwGzAqMP8KOxkySwAcMl0A-TxvAB8y" + "_3VmkwAkPyIyVvRWNNQKVsT_qTimOFM0LjJ1MzIBATA6Mv8B8AHwAfAB8AHwAfAB" + "8AHw_wHwAfAB8AHwfmYBMHFkZZT_7zpi9GL0YmQ0YpsBNPI08v808jRiLvIxYjTy" + "NPI08jQy_80CNGJWZPcC_QIDAwkDDwP_FQPqAPAArzgInSABr_gB8P8B8AHwAfAB" + "8AHwAfAB8AHw_wHwAfAB8AHwX_Q3YsH4mgX_I21ixCMKfw6FDmYMkwbHOPixsbEx" + "MgMG-zpgDxgG_zQyJwY3YjoyPTIBYAcCCjL_AWAkMDAwGTJFABwyVwBdAP_5PG8A" + "HzKBAIcAqTiZACQ__yJiAp1W9FZkqThTZE2UODH_AWBZ8QHwAfAB8AHwAfAB8P8B" + "8AHwAfAB8AHw58BrlC6S_2L0Jp1iZJaWNJKnATTyNPL_NPI08jTyNPI08jSSwQI0" + "kv_fAlbEBW2v-C7CWfQB8AHw_wHwAfAB8AHwAfAB8AHwAfD_AfAB8AHwX8TB-DEy" + "wThiNDjk5ORilCY9fw7JyRDJxcXFkza8vLz4ubm5MTIDBvs6NGKcNv_aATAGOjKP" + "CgEA9QFDMgHAfxgwJDAWMjkAPwBFABxiq_yrq8U6pjh7AIEAhwCpOP-ZAJ8ApQCr" + "ACIyVjQC_Vb0_yiSK_IB8AHwAfAB8AHwAfD_AfAB8AHwAfAB8AHwAfAu8v-Q9mL0" + "YvSWNjTyNPLRAdcB_2hkNPI08jGSNPI0Mn8CNPL_NPI0kuUCVpQDA1Y0rzgxYv-v" + "yC7yAfAB8AHwAfAB8AHw_wHwAfAB8AHwAfAB8AHwNPJ_wfjBaPAJLgIjbWKUeQ7U" + "_NTU9TrHODGSAwYvPTRi-K6urmtkhgrpAXE0QAL_QzIBwBgwIQAWMjMAGWJLAD8c" + "YmMAcjYfknU2mQDQ0P7QpQCrACIyVvRW9Fb0KPL_AfAB8AHwAfAB8AHwAfAB8P8B" + "8AHwAfAB8AHwLvJi9GL0_1kBYsQ0kpUBNPI08jRiazT_NDIQMjTyMWI0Ml4yNDJz" + "Av808mkANPI0MtMCNJJWNAVt_w8DFQOv-K_4AfAB8AHwAfD_AfAB8AHwAfAB8AHw" + "AfAB8H8B8AHwMfLB-CNtYvR5Dtvg29vY2Nj1CuIFMcLjrA5cCr6-vjRiIQY3Mviz" + "s7OGCukBjwoBAPUB_0MyAfAYACQwLQAzABliSwD_HGJjAGkAbwBTxCYHmQCfAP-l" + "AKsAImJW9CXyVvQB8AHw_wHwAfAB8AHwAfAB8AHwAfD_AfAB8AHwAfAB8DHyYvRi" + "ZP808jQyoQE08jTya2Q08jHy_xgwNJJtAjTyNPI0MscCNPL_VpQFba_4MfIB8AHw" + "AfAB8P8B8AHwAfAB8AHwAfAB8AHw_wHwAfAx8sH4I51ilB0KeQ744uLik2Yxwvs6" + "YA80MhjBwcEhBjcyu7u7_-MBiQrsAXMI9QFDMgHAGDD_IQAnABYyOQAZMksAUQAc" + "Mo9jAMU6U8R1Nt_f36UA_yQ_twDrAiIyVpTkAFb0JfL_AfAB8AHwAfAB8AHwAfAB" + "8P8B8AHwAfAB8AHwAfAB8GL0_2L0Jp00kpUBNPI08jTyCjL_NPI08icAYQI08jTy" + "NPLTAv80klZkBc2v-CPxAfAB8AHw_wHwAfAB8AHwAfAB8AHwAfD_AfAB8AHwLvJi" + "9C7yYjR5Dn_1OpM2MZKmDi89tQ40MsoEyso3MsbGxsTE_sTjAYkK7AE_BvUBQzIB" + "wP8YMCEAJwAtADMAOQAZMmwG_1EAHDJjAMU6dQBTlKmYJD__IsJW9CXyAfAB8AHw" + "AfAB8P8B8AHwAfAB8AHwAfAB8AHw_wHwAfAB8GL0NDJZATTyNPL_NPI0wukBazQ0" + "8jTyFTA08v9zAk00NPI08jTyNv9W9DTy_wHwAfAB8AHwAfAB8AHwAfD_AfAB8AHw" + "AfAB8AHwAfAu8o9i9C4yFwpzDu_v7_Vqj5M2Wg8xYlYK2dnZNGLjGwY3Ms7Ozjoy" + "iQrsAf90NEMCAfABABsAEzItABYy_z8AGTJRABwyGA9pAG8AH2L_hwCpmKUAqwAk" + "PyLyVvQB8P8B8AHwAfAB8AHwAfAB8AHw_wHwAfAB8AHwAfAB8AHwAfD_NPI08jTy" + "NPI0MtEBNPI08v8u8jGSNPI0kpcCNPI08jTy_zTyNPIB8AHwAfAB8AHwAfD_AfAB" + "8AHwAfAB8AHwAfAB8P8B8AHwLvImzU4MdwGTZjGSx5Y2Vgo0Ytzc3BsGa2T_gArj" + "AdwOAQBAMgEAmwoBwP8YMCEAAQAqADMAOQBNZFEA_1cAHGJvAHI2H5J1ZiTPIvL_" + "IvIB8AHwAfAB8AHwAfAB8P8B8AHwAfAB8AHwAfAB8AHw_wHwAfA08jTyNPI08jTC" + "azT_NGIQMjTyNMJeMjRiTTQ08v808jTyNPI08gHwAfAB8AHw_wHwAfAB8AHwAfAB" + "8AHwAfD_AfAB8AHwAfAB8C7yLpKTxj8xkvtqNDKcNscO1AHe3v7eOjI2BuwBQDIB" + "MAECAZD_FTAkMC0AFjI_AEUASwBsNv9dAG8GxWofknXGIvIi8gHw_wHwAfAB8AHw" + "AfAB8AHwAfD_AfAB8AHwAfAB8AHwAfAB8P8B8DTyNPI08jSS0QE08jTy_zHySWI0" + "8jTCmjI08jTyNPL_NPIB8AHwAfAB8AHwAfAB8P8B8AHwAfAB8AHwAfAB8AHw_wHw" + "AfAu8i6SMcLiBTGS-zr_rw40Ypw2OpI2Bqg2QzIB8P8BMCEwKgBKlGw2HGJpAMU6" + "_x-SkwAfwiLy6vAB8AHwAfD_AfAB8AHwAfAB8AHwAfAB8P8B8AHwAfAB8AHwAfAB" + "8DTy_zTyNPI08jTyMfIB8DFiNPL_NPI08jTyNPIB8AHwAfAB8P8B8AHwAfAB8AHw" + "AfAB8AHw_wHwAfAB8AHwAfAB8C7yMfL_gwGW9vs6NGKcNjdigAoBAP89MgEw-zEB" + "8BAyEzI2MBmS_1cAHGLFOh_CdfbDYN7wAfD_AfAB8AHwAfAB8AHwAfAB8P8B8AHw" + "AfAB8AHwAfAB8AHw_wHwAfA08mJkZfQ08jTyMfL_MfI08jTyNPJTZDTyNPIB8P8B" + "8AHwAfAB8AHwAfAB8AHw_wHwAfAB8AHwAfAB8AHwAfD_AfAB8JP2ZfRlNDRiN8I6" + "Mv89MkAyAfABYCQwMDA8MBmS_xxixcofYnX2AfAB8AHwAfD_AfAB8AHwAfAB8AHw" + "AfAB8P8B8AHwAfAB8AHwAfAB8AHw_zTyNPI08jTyNPI08jTyNPL_NPI08jTyNPIB" + "8AHwAfAB8P8B8AHwAfAB8AHwAfAB8AHw_wHwAfAB8AHwAfAB8AHwLvL_MfL7mjRi" + "N2I6wgEw9QEB8P8BYCdgFpIZYqP4H_Ii8gHw_wHwAfAB8AHwAfAB8AHwAfD_AfAB" + "8AHwAfAB8AHwAfAB8P8B8AHwAfAB8DTyNPI08jTy_yjyNPI08jTyNPI08gHwAfD_" + "AfAB8AHwAfAB8AHwAfAB8P8B8AHwAfAB8AHwAfAB8AHw_wHwAfAB8DTyNPI3kjqS" + "AWD_-zEB8CSQFmIZkhzyHPIB8P8B8AHwAfAB8AHwAfAB8AHw_wHwAfAB8AHwAfAB" + "8AHwAfD_AfAB8AHwAfAB8DTyNPI08v808jTyNPI08jTyNPKo8AHw_wHwAfAB8AHw" + "AfAB8AHwAfABAcA" +}; diff --git a/src/data/icon48.h b/src/data/icon48.h new file mode 100644 index 00000000..b9cfaa8a --- /dev/null +++ b/src/data/icon48.h @@ -0,0 +1,40 @@ +static char enc_icon48[] = { + "_gAB8AHwAfAB8AHwAfAB8P8B8AHwAfAB8AHwAfAB8AHw_wHwAfAB8AHwAfAB8AHw" + "AfD_AfAB8AHwAfAB8AHwAfAB8P8B8AHwAfAB8AHwAfAB8AHw_wHwAfAB8AHwAfAB" + "8AHwAfD_AfAB8AHwAfAB8AHwAfAB8P8B8AHwAfAB8AHwAfAB8AHw_wHwAfAB8AHw" + "AfAB8AHwAfD_AfAB8AHwAfAB8AHwAfABgFD_oRYPBAA1BABWVQQAWwQAQQQAGgQA" + "Av8u8AHwAfAB8AHwAfAB8AHwVbSAAQQALQQAsAQA9FUEAP8EwPsEAM4EAFP9BAAE" + "QvAB8AHwAfAB8AHwqwHwvOAEBACFBAD5uPDVBOD-BAC9BAAUSvAB8L8B8AHwAfAB" + "8AHwvGADBACuoqjwBPAEwNsEABZS8H8B8AHwAfAB8AHwAfDAIHuvnPAE8ATwBCDH" + "BAAFVvC_AfAB8AHwAfAB8LzAIAQA3vaY8ATwBPAEYGZa8AHw3wHwAfAB8AHwwMCX" + "kPAE8OsE8ASg3wQABl7wAfAB8K8B8AHwAfC8QAYEAOeQ8PcE8ATw0OMvYvAB8AHw" + "AfDXAfAB8MBAHAQA_JDwBPALBPAE4GRi8AAAPz9U_wEEAA0EAB0EAB59BAAPBABE" + "9gHwAfDA8BVVBABxBAB3BAChUET9p5zwBPAE8BaGuMALBACqbAQAzgQA9AQA-wQA" + "qvwEAPYEANYEAHoEAL4SlvAB8AHwAfAEhjIEAF68FEKo8ATwBFCBcIFHtQQA5AQA" + "_wTwBKDuBAB-XDQQjvAB8AHwAfAQhmhHzPYE8ARgpQ5YwEBor2xBsPAE8AQQ_QQA" + "hFTyfwHwAfAB8MjA-Bgg8wTQowAQ-_-rACMuNrz_SrzwBPAE8MQgZ4LwbwHwAfAB" + "8BDJkbTwBMCnAAfLfV-0ETU6fP_mnPAE8ATwBCAkExxfhvAB8AHwAfDEgBEEAO0D" + "uPAEQKQO_f-tAIBCLDX_cTw-mPA3BPAE8ASAlYLwAUDNAKoBBAAFBAAUBAAiBAB-" + "JQhAEBAYEETz5PYE8KgABZxKRfIENju8_9eQ8ATwBPAEoOq0wKoDBAAsBACGBADM" + "BADq7AQA_AQA_gSAEBAYEOrKBACDBAAqBABMo6gAFkC88AQgrvgCBy83wP8VPj7_" + "-JDwBPCvBPAIs7yAuBDEoED_BPBXBPAEQDAQwAQAKcCAHRf8S7hQMBl_uFk4O_--" + "KYzwBPAE8ATwvEBhBABe95zwBPAE8ARg9gQAW1HAIJ8XFKwA8uhIkFUEAB-sxhkE" + "AH0EAJL1BADDBADznPAE8ATwwADozQBIBAD5kPAE8ATwAQTg-ADQACn_nrwYAshD" + "pPUB8NAQEQQA3nGgRqjwBPDAkOKI8ATw9wTwBPAEIM8UAwHwAfAB8HvIEPQYzKjw" + "BPAEQDQS0t-I8ATwBPAE8ARAeprwAfB3AfDEYLAZyqzwBPAEAMX1cAAvBADxiPAE" + "8ATwBOB68AQAGZbwAfAB8MSgHK0EAPGw8ASg_gQAScBDvj4URZTwBPAE8AQw6gQA" + "RjwN9mwJ__8IBAAT_QQAGgRADBAUEET2xHDgE6u48ARwuwQAA0CFGQQAup0EAPOc" + "8ATwBIDyBACgmADLABe0gAkEAKpFBACaBADSBADsBABa-QQA-wRADBDtBADTVQQA" + "nQQASAQACsDAGK0EAPW48AQg3QQAFfbwVcggEQQAUwQAoRxD6b0EAPRcJNwwEBAY" + "EJ8EAKpRBAAQuIAGBABuBAC66AQA_wTwBPAEgOoEAEpzBAAHSxBBOFgryF-88BQg" + "LHEB8NDQBgQAC10EAA0IQLj-vDAQBADFr5jwBPAE8ASgywQAFMRA6pm4gOcEAHAE" + "AOAgAfCvAfAB8AHwvEACBAC1jPAvBPAE8ATwBAC_VBBDMtT_TrAAeQQAPAQAYKLf" + "AfAB8AHwAfDAQBnYQozw1wTwBPAE8P0EAB1p8AHwrwHwAfAB8MDgGAQA-ojw7wTw" + "BPAE8AQA_AQA3PsB8N8B8AHwAfDA8CQVsYjwBPD3BPAE8AQAulz1AfAB8AHw1wHw" + "AfDEQA0EAL2M8ATw9wTwBKAEFBFh8AHwAfAB8FcB8AHwxIAFBABoBADjXQQA_pzw" + "BPAEgOUEAG3_hCUB8AHwAfAB8AHwAfAB8KvIEFwXPQQAkgQAz8BGXveYQwQQDBAU" + "ENAEAJT9BABB9CYB8AHwAfAB8AHwrwHwAfAB8NBwBAQADtgl_QQwDxRA5PIB8AHw" + "AfAB8P8B8AHwAfAB8AHwAfAB8AHw_wHwAfAB8AHwAfAB8AHwAfD_AfAB8AHwAfAB" + "8AHwAfAB8P8B8AHwAfAB8AHwAfAB8AHw_wHwAfAB8AHwAfAB8AHwAfD_AfAB8AHw" + "AfAB8AHwAfAB8P8B8AHwAfAB8AHwAfAB8AHwfwHwAfAB8AHwAfAB8AGA" +}; diff --git a/src/dsp/adsp/adsp.cpp b/src/dsp/adsp/adsp.cpp index 1a102662..5a3879d5 100644 --- a/src/dsp/adsp/adsp.cpp +++ b/src/dsp/adsp/adsp.cpp @@ -1,4 +1,6 @@ -#include "../../base.h" +#include "../../base.h" +#define ADSP_CPP + #include "adsp_tables.cpp" void aDSP::enter() { loop: @@ -579,7 +581,7 @@ int32 fir_samplel, fir_sampler; } snes.audio_update(msamplel, msampler); - scheduler.addclocks_dsp(32 * 3); + scheduler.addclocks_dsp(32 * 3 * 8); } aDSP::aDSP() {} diff --git a/src/dsp/adsp/adsp_tables.cpp b/src/dsp/adsp/adsp_tables.cpp index bb72e587..e9f1b7eb 100644 --- a/src/dsp/adsp/adsp_tables.cpp +++ b/src/dsp/adsp/adsp_tables.cpp @@ -1,3 +1,5 @@ +#ifdef ADSP_CPP + const uint16 aDSP::rate_table[32] = { 0x0000, 0x000F, 0x0014, 0x0018, 0x001E, 0x0028, 0x0030, 0x003C, 0x0050, 0x0060, 0x0078, 0x00A0, 0x00C0, 0x00F0, 0x0140, 0x0180, @@ -71,3 +73,5 @@ const int16 aDSP::gaussian_table[512] = { 0x513, 0x514, 0x514, 0x515, 0x516, 0x516, 0x517, 0x517, 0x517, 0x518, 0x518, 0x518, 0x518, 0x518, 0x519, 0x519 }; + +#endif //ifdef ADSP_CPP diff --git a/src/dsp/bdsp/bdsp.cpp b/src/dsp/bdsp/bdsp.cpp index bb3e3fd7..6e984dc9 100644 --- a/src/dsp/bdsp/bdsp.cpp +++ b/src/dsp/bdsp/bdsp.cpp @@ -469,7 +469,7 @@ void bDSP::enter() { // n is currently ignored #define NEXT_CLOCK( n ) \ - scheduler.addclocks_dsp( 3 ); + scheduler.addclocks_dsp( 3 * 8 ); // Execute clock for a particular voice #define V( clock, voice ) voice_##clock( &m.voices [voice] ); @@ -563,7 +563,7 @@ void bDSP::enter() } // Output sample to DAC - snes.audio_update( main_out_l, main_out_r ); + snes.audio.update( main_out_l, main_out_r ); m.t_main_out [0] = 0; m.t_main_out [1] = 0; diff --git a/src/dsp/dsp.h b/src/dsp/dsp.h index abb2ed56..1e36b51b 100644 --- a/src/dsp/dsp.h +++ b/src/dsp/dsp.h @@ -1,11 +1,12 @@ -class DSP { public: - virtual void enter() = 0; +class DSP { +public: + virtual void enter() = 0; - virtual uint8 read (uint8 addr) = 0; - virtual void write(uint8 addr, uint8 data) = 0; + virtual uint8 read(uint8 addr) = 0; + virtual void write(uint8 addr, uint8 data) = 0; - virtual void power() = 0; - virtual void reset() = 0; + virtual void power() = 0; + virtual void reset() = 0; DSP() {} virtual ~DSP() {} diff --git a/src/lib/hiro_gtk/button.cpp b/src/lib/hiro/gtk/button.cpp similarity index 100% rename from src/lib/hiro_gtk/button.cpp rename to src/lib/hiro/gtk/button.cpp diff --git a/src/lib/hiro_gtk/button.h b/src/lib/hiro/gtk/button.h similarity index 100% rename from src/lib/hiro_gtk/button.h rename to src/lib/hiro/gtk/button.h diff --git a/src/lib/hiro_gtk/canvas.cpp b/src/lib/hiro/gtk/canvas.cpp similarity index 94% rename from src/lib/hiro_gtk/canvas.cpp rename to src/lib/hiro/gtk/canvas.cpp index 5351bab4..e6f7e4bb 100644 --- a/src/lib/hiro_gtk/canvas.cpp +++ b/src/lib/hiro/gtk/canvas.cpp @@ -1,9 +1,9 @@ void hiro_pcanvas_expose(pCanvas *p) { -uint32_t *f = p->fbuffer; -uint32_t *r = p->rbuffer; + uint32_t *f = p->fbuffer; + uint32_t *r = p->rbuffer; for(uint y = p->canvas->allocation.height; y; y--) { for(uint x = p->canvas->allocation.width; x; x--) { - uint32_t p = *f++; + uint32_t p = *f++; *r++ = ((p << 16) & 0xff0000) + (p & 0x00ff00) + ((p >> 16) & 0x0000ff); } } @@ -28,7 +28,7 @@ void pCanvas::create(uint style, uint width, uint height) { void pCanvas::redraw() { if(!canvas || !canvas->window) return; -GdkRectangle rect; + GdkRectangle rect; rect.x = 0; rect.y = 0; rect.width = canvas->allocation.width; diff --git a/src/lib/hiro_gtk/canvas.h b/src/lib/hiro/gtk/canvas.h similarity index 100% rename from src/lib/hiro_gtk/canvas.h rename to src/lib/hiro/gtk/canvas.h diff --git a/src/lib/hiro_gtk/checkbox.cpp b/src/lib/hiro/gtk/checkbox.cpp similarity index 100% rename from src/lib/hiro_gtk/checkbox.cpp rename to src/lib/hiro/gtk/checkbox.cpp diff --git a/src/lib/hiro_gtk/checkbox.h b/src/lib/hiro/gtk/checkbox.h similarity index 100% rename from src/lib/hiro_gtk/checkbox.h rename to src/lib/hiro/gtk/checkbox.h diff --git a/src/lib/hiro_gtk/combobox.cpp b/src/lib/hiro/gtk/combobox.cpp similarity index 100% rename from src/lib/hiro_gtk/combobox.cpp rename to src/lib/hiro/gtk/combobox.cpp diff --git a/src/lib/hiro_gtk/combobox.h b/src/lib/hiro/gtk/combobox.h similarity index 100% rename from src/lib/hiro_gtk/combobox.h rename to src/lib/hiro/gtk/combobox.h diff --git a/src/lib/hiro_gtk/editbox.cpp b/src/lib/hiro/gtk/editbox.cpp similarity index 100% rename from src/lib/hiro_gtk/editbox.cpp rename to src/lib/hiro/gtk/editbox.cpp diff --git a/src/lib/hiro_gtk/editbox.h b/src/lib/hiro/gtk/editbox.h similarity index 100% rename from src/lib/hiro_gtk/editbox.h rename to src/lib/hiro/gtk/editbox.h diff --git a/src/lib/hiro_gtk/formcontrol.cpp b/src/lib/hiro/gtk/formcontrol.cpp similarity index 100% rename from src/lib/hiro_gtk/formcontrol.cpp rename to src/lib/hiro/gtk/formcontrol.cpp diff --git a/src/lib/hiro_gtk/formcontrol.h b/src/lib/hiro/gtk/formcontrol.h similarity index 100% rename from src/lib/hiro_gtk/formcontrol.h rename to src/lib/hiro/gtk/formcontrol.h diff --git a/src/lib/hiro_gtk/frame.cpp b/src/lib/hiro/gtk/frame.cpp similarity index 100% rename from src/lib/hiro_gtk/frame.cpp rename to src/lib/hiro/gtk/frame.cpp diff --git a/src/lib/hiro_gtk/frame.h b/src/lib/hiro/gtk/frame.h similarity index 100% rename from src/lib/hiro_gtk/frame.h rename to src/lib/hiro/gtk/frame.h diff --git a/src/lib/hiro_gtk/hiro.cpp b/src/lib/hiro/gtk/hiro.cpp similarity index 54% rename from src/lib/hiro_gtk/hiro.cpp rename to src/lib/hiro/gtk/hiro.cpp index 3b64799e..8f4fd280 100644 --- a/src/lib/hiro_gtk/hiro.cpp +++ b/src/lib/hiro/gtk/hiro.cpp @@ -38,12 +38,24 @@ void pHiro::init() { gtk_init(&argc, &argv); free(argv[0]); free(argv); + + is_composited = false; + screen = gdk_screen_get_default(); + if(gdk_screen_is_composited(screen)) { + colormap = gdk_screen_get_rgba_colormap(screen); + if(colormap) is_composited = true; + else colormap = gdk_screen_get_rgb_colormap(screen); //fallback + } else { + colormap = gdk_screen_get_rgb_colormap(screen); + } } void pHiro::term() { + enable_screensaver(); } bool pHiro::run() { + if(is_screensaver_enabled == false) screensaver_tick(); gtk_main_iteration_do(false); return pending(); } @@ -52,11 +64,34 @@ bool pHiro::pending() { return gtk_events_pending(); } -bool pHiro::file_load(Window *focus, char *filename, const char *filter, const char *path) { +bool pHiro::folder_select(Window *focus, char *filename, const char *path) { if(!filename) return false; strcpy(filename, ""); - GtkWidget *dialog = gtk_file_chooser_dialog_new("Load File", + GtkWidget *dialog = gtk_file_chooser_dialog_new("Select Folder", + focus ? GTK_WINDOW(focus->p.gtk_handle()) : (GtkWindow*)0, + GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, + (const gchar*)0); + + if(path && *path) gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), path); + + if(gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) { + char *fn = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)); + strcpy(filename, fn); + g_free(fn); + } + + gtk_widget_destroy(dialog); + return strcmp(filename, ""); //return true if filename exists +} + +bool pHiro::file_open(Window *focus, char *filename, const char *path, const char *filter) { + if(!filename) return false; + strcpy(filename, ""); + + GtkWidget *dialog = gtk_file_chooser_dialog_new("Open File", focus ? GTK_WINDOW(focus->p.gtk_handle()) : (GtkWindow*)0, GTK_FILE_CHOOSER_ACTION_OPEN, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, @@ -75,7 +110,7 @@ bool pHiro::file_load(Window *focus, char *filename, const char *filter, const c return strcmp(filename, ""); //return true if filename exists } -bool pHiro::file_save(Window *focus, char *filename, const char *filter, const char *path) { +bool pHiro::file_save(Window *focus, char *filename, const char *path, const char *filter) { if(!filename) return false; strcpy(filename, ""); @@ -107,15 +142,50 @@ uint pHiro::screen_height() { return gdk_screen_height(); } +void pHiro::enable_screensaver() { + if(is_screensaver_enabled == true) return; + is_screensaver_enabled = true; + DPMSDisable(GDK_DISPLAY()); +} + +void pHiro::disable_screensaver() { + if(is_screensaver_enabled == false) return; + is_screensaver_enabled = false; + DPMSEnable(GDK_DISPLAY()); +} + pHiro& pHiro::handle() { return hiro().p; } pHiro::pHiro(Hiro &self_) : self(self_) { + is_screensaver_enabled = true; } pHiro& phiro() { return pHiro::handle(); } +/* internal */ + +void pHiro::screensaver_tick() { + static clock_t delta_x = 0, delta_y = 0; + + delta_y = clock(); + if(delta_y - delta_x < CLOCKS_PER_SEC * 20) return; + + //XSetScreenSaver(timeout = 0) does not work + //XResetScreenSaver() does not work + //XScreenSaverSuspend() does not work + //DPMSDisable() does not work + //XSendEvent(KeyPressMask) does not work + //use XTest extension to send fake keypress every ~20 seconds. + //keycode of 255 does not map to any actual key, but it will block screensaver. + delta_x = delta_y; + XTestFakeKeyEvent(GDK_DISPLAY(), 255, True, 0); + XSync(GDK_DISPLAY(), False); + XTestFakeKeyEvent(GDK_DISPLAY(), 255, False, 0); + XSync(GDK_DISPLAY(), False); +} + } //namespace libhiro diff --git a/src/lib/hiro_gtk/hiro.h b/src/lib/hiro/gtk/hiro.h similarity index 63% rename from src/lib/hiro_gtk/hiro.h rename to src/lib/hiro/gtk/hiro.h index 79160626..6d2c6b5a 100644 --- a/src/lib/hiro_gtk/hiro.h +++ b/src/lib/hiro/gtk/hiro.h @@ -3,7 +3,10 @@ #include #include +#include #include +#include +#include namespace libhiro { @@ -36,16 +39,26 @@ public: bool run(); bool pending(); - bool file_load(Window *focus, char *filename, const char *filter, const char *path); - bool file_save(Window *focus, char *filename, const char *filter, const char *path); + bool folder_select(Window *focus, char *filename, const char *path = ""); + bool file_open(Window *focus, char *filename, const char *path = "", const char *filter = ""); + bool file_save(Window *focus, char *filename, const char *path = "", const char *filter = ""); uint screen_width(); uint screen_height(); + void enable_screensaver(); + void disable_screensaver(); + static pHiro& handle(); pHiro(Hiro&); /* internal */ + GdkScreen *screen; + GdkColormap *colormap; + bool is_composited; + + bool is_screensaver_enabled; + void screensaver_tick(); uint16_t translate_key(uint key); }; diff --git a/src/lib/hiro_gtk/keymap.cpp b/src/lib/hiro/gtk/keymap.cpp similarity index 100% rename from src/lib/hiro_gtk/keymap.cpp rename to src/lib/hiro/gtk/keymap.cpp diff --git a/src/lib/hiro_gtk/label.cpp b/src/lib/hiro/gtk/label.cpp similarity index 100% rename from src/lib/hiro_gtk/label.cpp rename to src/lib/hiro/gtk/label.cpp diff --git a/src/lib/hiro_gtk/label.h b/src/lib/hiro/gtk/label.h similarity index 100% rename from src/lib/hiro_gtk/label.h rename to src/lib/hiro/gtk/label.h diff --git a/src/lib/hiro_gtk/listbox.cpp b/src/lib/hiro/gtk/listbox.cpp similarity index 100% rename from src/lib/hiro_gtk/listbox.cpp rename to src/lib/hiro/gtk/listbox.cpp diff --git a/src/lib/hiro_gtk/listbox.h b/src/lib/hiro/gtk/listbox.h similarity index 100% rename from src/lib/hiro_gtk/listbox.h rename to src/lib/hiro/gtk/listbox.h diff --git a/src/lib/hiro_gtk/menucheckitem.cpp b/src/lib/hiro/gtk/menucheckitem.cpp similarity index 100% rename from src/lib/hiro_gtk/menucheckitem.cpp rename to src/lib/hiro/gtk/menucheckitem.cpp diff --git a/src/lib/hiro_gtk/menucheckitem.h b/src/lib/hiro/gtk/menucheckitem.h similarity index 100% rename from src/lib/hiro_gtk/menucheckitem.h rename to src/lib/hiro/gtk/menucheckitem.h diff --git a/src/lib/hiro_gtk/menucontrol.cpp b/src/lib/hiro/gtk/menucontrol.cpp similarity index 100% rename from src/lib/hiro_gtk/menucontrol.cpp rename to src/lib/hiro/gtk/menucontrol.cpp diff --git a/src/lib/hiro_gtk/menucontrol.h b/src/lib/hiro/gtk/menucontrol.h similarity index 100% rename from src/lib/hiro_gtk/menucontrol.h rename to src/lib/hiro/gtk/menucontrol.h diff --git a/src/lib/hiro_gtk/menugroup.cpp b/src/lib/hiro/gtk/menugroup.cpp similarity index 100% rename from src/lib/hiro_gtk/menugroup.cpp rename to src/lib/hiro/gtk/menugroup.cpp diff --git a/src/lib/hiro_gtk/menugroup.h b/src/lib/hiro/gtk/menugroup.h similarity index 100% rename from src/lib/hiro_gtk/menugroup.h rename to src/lib/hiro/gtk/menugroup.h diff --git a/src/lib/hiro_gtk/menuitem.cpp b/src/lib/hiro/gtk/menuitem.cpp similarity index 100% rename from src/lib/hiro_gtk/menuitem.cpp rename to src/lib/hiro/gtk/menuitem.cpp diff --git a/src/lib/hiro_gtk/menuitem.h b/src/lib/hiro/gtk/menuitem.h similarity index 100% rename from src/lib/hiro_gtk/menuitem.h rename to src/lib/hiro/gtk/menuitem.h diff --git a/src/lib/hiro_gtk/menuradioitem.cpp b/src/lib/hiro/gtk/menuradioitem.cpp similarity index 100% rename from src/lib/hiro_gtk/menuradioitem.cpp rename to src/lib/hiro/gtk/menuradioitem.cpp diff --git a/src/lib/hiro_gtk/menuradioitem.h b/src/lib/hiro/gtk/menuradioitem.h similarity index 100% rename from src/lib/hiro_gtk/menuradioitem.h rename to src/lib/hiro/gtk/menuradioitem.h diff --git a/src/lib/hiro_gtk/menuseparator.cpp b/src/lib/hiro/gtk/menuseparator.cpp similarity index 100% rename from src/lib/hiro_gtk/menuseparator.cpp rename to src/lib/hiro/gtk/menuseparator.cpp diff --git a/src/lib/hiro_gtk/menuseparator.h b/src/lib/hiro/gtk/menuseparator.h similarity index 100% rename from src/lib/hiro_gtk/menuseparator.h rename to src/lib/hiro/gtk/menuseparator.h diff --git a/src/lib/hiro_gtk/progressbar.cpp b/src/lib/hiro/gtk/progressbar.cpp similarity index 100% rename from src/lib/hiro_gtk/progressbar.cpp rename to src/lib/hiro/gtk/progressbar.cpp diff --git a/src/lib/hiro_gtk/progressbar.h b/src/lib/hiro/gtk/progressbar.h similarity index 100% rename from src/lib/hiro_gtk/progressbar.h rename to src/lib/hiro/gtk/progressbar.h diff --git a/src/lib/hiro_gtk/radiobox.cpp b/src/lib/hiro/gtk/radiobox.cpp similarity index 100% rename from src/lib/hiro_gtk/radiobox.cpp rename to src/lib/hiro/gtk/radiobox.cpp diff --git a/src/lib/hiro_gtk/radiobox.h b/src/lib/hiro/gtk/radiobox.h similarity index 100% rename from src/lib/hiro_gtk/radiobox.h rename to src/lib/hiro/gtk/radiobox.h diff --git a/src/lib/hiro_gtk/slider.cpp b/src/lib/hiro/gtk/slider.cpp similarity index 100% rename from src/lib/hiro_gtk/slider.cpp rename to src/lib/hiro/gtk/slider.cpp diff --git a/src/lib/hiro_gtk/slider.h b/src/lib/hiro/gtk/slider.h similarity index 100% rename from src/lib/hiro_gtk/slider.h rename to src/lib/hiro/gtk/slider.h diff --git a/src/lib/hiro_gtk/widget.cpp b/src/lib/hiro/gtk/widget.cpp similarity index 100% rename from src/lib/hiro_gtk/widget.cpp rename to src/lib/hiro/gtk/widget.cpp diff --git a/src/lib/hiro_gtk/widget.h b/src/lib/hiro/gtk/widget.h similarity index 100% rename from src/lib/hiro_gtk/widget.h rename to src/lib/hiro/gtk/widget.h diff --git a/src/lib/hiro_gtk/window.cpp b/src/lib/hiro/gtk/window.cpp similarity index 78% rename from src/lib/hiro_gtk/window.cpp rename to src/lib/hiro/gtk/window.cpp index 930e4586..a1e9bd31 100644 --- a/src/lib/hiro_gtk/window.cpp +++ b/src/lib/hiro/gtk/window.cpp @@ -1,24 +1,49 @@ -gint hiro_pwindow_close(pWindow *p) { -uintptr_t r = p->self.on_close ? p->self.on_close(Event(Event::Close, 0, &p->self)) : true; +static gint hiro_pwindow_close(pWindow *p) { + uintptr_t r = p->self.on_close ? p->self.on_close(Event(Event::Close, 0, &p->self)) : true; return !bool(r); } -gint hiro_pwindow_keydown(GtkWidget *w, GdkEventKey *key, pWindow *p) { +static gboolean hiro_pwindow_expose(pWindow *p) { + cairo_t *context = gdk_cairo_create(p->window->window); + GtkStyle *style = gtk_widget_get_style(p->window); + + double red = double(style->bg[GTK_STATE_NORMAL].red) / 65536.0; + double green = double(style->bg[GTK_STATE_NORMAL].green) / 65536.0; + double blue = double(style->bg[GTK_STATE_NORMAL].blue) / 65536.0; + double alpha = double(p->state.alpha) / 256.0; + + if(phiro().is_composited == true) { + cairo_set_source_rgba(context, red, green, blue, alpha); + } else { + cairo_set_source_rgb(context, red, green, blue); + } + + cairo_set_operator(context, CAIRO_OPERATOR_SOURCE); + cairo_paint(context); + cairo_destroy(context); + return FALSE; +} + +static gint hiro_pwindow_keydown(GtkWidget *w, GdkEventKey *key, pWindow *p) { if(p && p->self.on_keydown) p->self.on_keydown(Event(Event::KeyDown, phiro().translate_key(key->keyval), &p->self)); return FALSE; } -gint hiro_pwindow_keyup(GtkWidget *w, GdkEventKey *key, pWindow *p) { +static gint hiro_pwindow_keyup(GtkWidget *w, GdkEventKey *key, pWindow *p) { if(p && p->self.on_keyup) p->self.on_keyup(Event(Event::KeyUp, phiro().translate_key(key->keyval), &p->self)); return FALSE; } void pWindow::create(uint style, uint width, uint height, const char *text) { window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_widget_set_colormap(window, phiro().colormap); + gtk_window_set_title(GTK_WINDOW(window), text ? text : ""); gtk_window_set_resizable(GTK_WINDOW(window), false); + gtk_widget_set_app_paintable(window, true); if(style & Window::AutoCenter) gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER_ALWAYS); g_signal_connect_swapped(G_OBJECT(window), "delete_event", G_CALLBACK(hiro_pwindow_close), (gpointer)this); + g_signal_connect_swapped(G_OBJECT(window), "expose_event", G_CALLBACK(hiro_pwindow_expose), (gpointer)this); g_signal_connect(G_OBJECT(window), "key_press_event", G_CALLBACK(hiro_pwindow_keydown), (gpointer)this); g_signal_connect(G_OBJECT(window), "key_release_event", G_CALLBACK(hiro_pwindow_keyup), (gpointer)this); @@ -45,6 +70,7 @@ void pWindow::create(uint style, uint width, uint height, const char *text) { gtk_box_pack_start(GTK_BOX(menucontainer), statuscontainer, false, false, 0); gtk_widget_show(statuscontainer); + gtk_widget_realize(window); state.is_fullscreen = false; state.width = width; state.height = height; @@ -131,8 +157,13 @@ uint pWindow::get_height() { return height; } +void pWindow::set_opacity(uint8_t opacity) { + state.alpha = opacity; + if(window) gtk_widget_queue_draw(window); +} + void pWindow::set_background_color(uint8_t r, uint8_t g, uint8_t b) { -GdkColor color; + GdkColor color; color.pixel = (r << 16) | (g << 8) | b; color.red = (r << 8) | r; color.green = (g << 8) | g; @@ -140,6 +171,28 @@ GdkColor color; gtk_widget_modify_bg(window, GTK_STATE_NORMAL, &color); } +void pWindow::set_icon(unsigned width, unsigned height, const uint32_t *data) { + uint8_t *temp = (uint8_t*)malloc(width * height * 4); + memcpy(temp, data, width * height * 4); + for(unsigned i = 0; i < width * height; i++) { + //ABGR -> ARGB + uint8_t t = temp[i * 4 + 0]; + temp[i * 4 + 0] = temp[i * 4 + 2]; + temp[i * 4 + 2] = t; + } + + GdkPixbuf *pixbuf = gdk_pixbuf_new_from_data( + (const guchar*)temp, GDK_COLORSPACE_RGB, + /* has_alpha = */ TRUE, /* bits_per_sample = */ 8, + width, height, /* rowstride = */ width * 4, + /* destroy_fn = */ NULL, /* destroy_fn_data = */ NULL + ); + gtk_window_set_icon(GTK_WINDOW(window), pixbuf); + + g_object_unref(pixbuf); + free(temp); +} + void pWindow::set_text(const char *text) { gtk_window_set_title(GTK_WINDOW(window), text ? text : ""); } @@ -224,6 +277,7 @@ pWindow::pWindow(Window &self_) : pWidget(self_), self(self_), menu(*this), stat state.is_fullscreen = false; state.width = 0; state.height = 0; + state.alpha = 255; } /* internal */ diff --git a/src/lib/hiro_gtk/window.h b/src/lib/hiro/gtk/window.h similarity index 92% rename from src/lib/hiro_gtk/window.h rename to src/lib/hiro/gtk/window.h index d1e5663a..1a2d09ad 100644 --- a/src/lib/hiro_gtk/window.h +++ b/src/lib/hiro/gtk/window.h @@ -10,7 +10,9 @@ public: void unfullscreen(); uint get_width(); uint get_height(); + void set_opacity(uint8_t opacity); void set_background_color(uint8_t r, uint8_t g, uint8_t b); + void set_icon(unsigned width, unsigned height, const uint32_t *data); void set_text(const char *text = ""); void attach(Window &window, uint x, uint y); void attach(MenuGroup &menugroup); @@ -55,6 +57,7 @@ public: bool is_fullscreen; uint width; uint height; + uint alpha; } state; void menu_show(bool = true); diff --git a/src/lib/hiro.cpp b/src/lib/hiro/hiro.cpp similarity index 94% rename from src/lib/hiro.cpp rename to src/lib/hiro/hiro.cpp index 7dca8509..30d096bf 100644 --- a/src/lib/hiro.cpp +++ b/src/lib/hiro/hiro.cpp @@ -1,10 +1,10 @@ -#include +#include "hiro.h" using namespace nall; #if defined(_WIN32) - #include + #include "win/hiro.cpp" #else - #include + #include "gtk/hiro.cpp" #endif namespace libhiro { @@ -15,10 +15,13 @@ void Hiro::init() { p.init(); } void Hiro::term() { p.term(); } bool Hiro::run() { return p.run(); } bool Hiro::pending() { return p.pending(); } -bool Hiro::file_load(Window *focus, char *filename, const char *filter, const char *path) { return p.file_load(focus, filename, filter, path); } -bool Hiro::file_save(Window *focus, char *filename, const char *filter, const char *path) { return p.file_save(focus, filename, filter, path); } +bool Hiro::folder_select(Window *focus, char *filename, const char *path) { return p.folder_select(focus, filename, path); } +bool Hiro::file_open(Window *focus, char *filename, const char *path, const char *filter) { return p.file_open(focus, filename, path, filter); } +bool Hiro::file_save(Window *focus, char *filename, const char *path, const char *filter) { return p.file_save(focus, filename, path, filter); } uint Hiro::screen_width() { return p.screen_width(); } uint Hiro::screen_height() { return p.screen_height(); } +void Hiro::enable_screensaver() { p.enable_screensaver(); } +void Hiro::disable_screensaver() { p.disable_screensaver(); } Hiro& Hiro::handle() { static Hiro hiro; return hiro; } Hiro::Hiro() : p(*new pHiro(*this)) {} Hiro::~Hiro() { delete &p; } @@ -46,7 +49,9 @@ void Window::fullscreen() { p.fullscreen(); } void Window::unfullscreen() { p.unfullscreen(); } uint Window::get_width() { return p.get_width(); } uint Window::get_height() { return p.get_height(); } +void Window::set_opacity(uint8_t opacity) { p.set_opacity(opacity); } void Window::set_background_color(uint8_t r, uint8_t g, uint8_t b) { p.set_background_color(r, g, b); } +void Window::set_icon(unsigned width, unsigned height, const uint32_t *data) { p.set_icon(width, height, data); } void Window::set_text(const char *text) { p.set_text(text); } void Window::attach(Window &window, uint x, uint y) { p.attach(window, x, y); } void Window::attach(MenuGroup &menugroup) { p.attach(menugroup); } diff --git a/src/lib/hiro.h b/src/lib/hiro/hiro.h similarity index 95% rename from src/lib/hiro.h rename to src/lib/hiro/hiro.h index 069797fc..0d1962f8 100644 --- a/src/lib/hiro.h +++ b/src/lib/hiro/hiro.h @@ -1,6 +1,6 @@ /* hiro - version: 0.001 (2008-02-03) + version: 0.002 (2008-02-19) author: byuu license: public domain */ @@ -112,12 +112,16 @@ public: bool run(); bool pending(); - bool file_load(Window *focus, char *filename, const char *filter, const char *path); - bool file_save(Window *focus, char *filename, const char *filter, const char *path); + bool folder_select(Window *focus, char *filename, const char *path = ""); + bool file_open(Window *focus, char *filename, const char *path = "", const char *filter = ""); + bool file_save(Window *focus, char *filename, const char *path = "", const char *filter = ""); uint screen_width(); uint screen_height(); + void enable_screensaver(); + void disable_screensaver(); + static Hiro& handle(); Hiro(); ~Hiro(); @@ -187,7 +191,9 @@ public: void unfullscreen(); uint get_width(); uint get_height(); + void set_opacity(uint8_t opacity); void set_background_color(uint8_t r, uint8_t g, uint8_t b); + void set_icon(unsigned width, unsigned height, const uint32_t *data); void set_status_text(const char *text = ""); void set_text(const char *text = ""); void attach(Window &window, uint x, uint y); diff --git a/src/lib/hiro_win/button.cpp b/src/lib/hiro/win/button.cpp similarity index 95% rename from src/lib/hiro_win/button.cpp rename to src/lib/hiro/win/button.cpp index 8347f1b5..ac0cfb1b 100644 --- a/src/lib/hiro_win/button.cpp +++ b/src/lib/hiro/win/button.cpp @@ -1,5 +1,5 @@ void pButton::create(uint style, uint width, uint height, const char *text) { - hwnd = CreateWindow("BUTTON", text ? text : "", WS_CHILD | WS_VISIBLE, + hwnd = CreateWindow("BUTTON", text ? text : "", WS_CHILD | WS_TABSTOP | WS_VISIBLE, 0, 0, width, height, phiro().default_hwnd, (HMENU)instance, GetModuleHandle(0), 0); SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)this); diff --git a/src/lib/hiro_win/button.h b/src/lib/hiro/win/button.h similarity index 100% rename from src/lib/hiro_win/button.h rename to src/lib/hiro/win/button.h diff --git a/src/lib/hiro_win/canvas.cpp b/src/lib/hiro/win/canvas.cpp similarity index 98% rename from src/lib/hiro_win/canvas.cpp rename to src/lib/hiro/win/canvas.cpp index 0abf990f..19160414 100644 --- a/src/lib/hiro_win/canvas.cpp +++ b/src/lib/hiro/win/canvas.cpp @@ -31,7 +31,7 @@ pCanvas::~pCanvas() { /* internal */ void pCanvas::blit() { -PAINTSTRUCT ps; + PAINTSTRUCT ps; BeginPaint(hwnd, &ps); SetDIBitsToDevice(ps.hdc, 0, 0, iwidth, iheight, 0, 0, 0, iheight, (void*)ibuffer, &bmi, DIB_RGB_COLORS); EndPaint(hwnd, &ps); diff --git a/src/lib/hiro_win/canvas.h b/src/lib/hiro/win/canvas.h similarity index 100% rename from src/lib/hiro_win/canvas.h rename to src/lib/hiro/win/canvas.h diff --git a/src/lib/hiro_win/checkbox.cpp b/src/lib/hiro/win/checkbox.cpp similarity index 95% rename from src/lib/hiro_win/checkbox.cpp rename to src/lib/hiro/win/checkbox.cpp index 07d040b2..c11cfe87 100644 --- a/src/lib/hiro_win/checkbox.cpp +++ b/src/lib/hiro/win/checkbox.cpp @@ -1,5 +1,5 @@ void pCheckbox::create(uint style, uint width, uint height, const char *text) { - hwnd = CreateWindow("BUTTON", text ? text : "", WS_CHILD | WS_VISIBLE | BS_CHECKBOX, + hwnd = CreateWindow("BUTTON", text ? text : "", WS_CHILD | WS_TABSTOP | WS_VISIBLE | BS_CHECKBOX, 0, 0, width, height, phiro().default_hwnd, (HMENU)instance, GetModuleHandle(0), 0); SendMessage(hwnd, WM_SETFONT, (WPARAM)phiro().default_font, 0); diff --git a/src/lib/hiro_win/checkbox.h b/src/lib/hiro/win/checkbox.h similarity index 100% rename from src/lib/hiro_win/checkbox.h rename to src/lib/hiro/win/checkbox.h diff --git a/src/lib/hiro_win/combobox.cpp b/src/lib/hiro/win/combobox.cpp similarity index 55% rename from src/lib/hiro_win/combobox.cpp rename to src/lib/hiro/win/combobox.cpp index bcd3b020..acb3858f 100644 --- a/src/lib/hiro_win/combobox.cpp +++ b/src/lib/hiro/win/combobox.cpp @@ -1,9 +1,19 @@ void pCombobox::create(uint style, uint width, uint height, const char *text) { hwnd = CreateWindowEx(WS_EX_CLIENTEDGE, "COMBOBOX", "", - WS_CHILD | WS_VISIBLE | CBS_DROPDOWNLIST | CBS_HASSTRINGS, + WS_CHILD | WS_TABSTOP | WS_VISIBLE | CBS_DROPDOWNLIST | CBS_HASSTRINGS, 0, 0, width, 200, phiro().default_hwnd, (HMENU)instance, GetModuleHandle(0), 0); SendMessage(hwnd, WM_SETFONT, (WPARAM)phiro().default_font, 0); + + //fix combobox height + //CreateWindow height parameter represents dropdown window height; auto-sizes control based on current font setting + //GetWindowRect returns true height (~22px), CB_GETITEMHEIGHT returns text height (~16px) + //difference = WindowHeight - CB_GETITEMHEIGHT + //CB_SETITEMHEIGHT to height - difference, so that height matches requested height perfectly + RECT rc; + GetWindowRect(hwnd, &rc); + unsigned adjusted_height = height - ((rc.bottom - rc.top) - SendMessage(hwnd, CB_GETITEMHEIGHT, (WPARAM)-1, 0)); + SendMessage(hwnd, CB_SETITEMHEIGHT, (WPARAM)-1, adjusted_height); } void pCombobox::add_item(const char *text) { diff --git a/src/lib/hiro_win/combobox.h b/src/lib/hiro/win/combobox.h similarity index 100% rename from src/lib/hiro_win/combobox.h rename to src/lib/hiro/win/combobox.h diff --git a/src/lib/hiro_win/editbox.cpp b/src/lib/hiro/win/editbox.cpp similarity index 94% rename from src/lib/hiro_win/editbox.cpp rename to src/lib/hiro/win/editbox.cpp index 079e0761..d1b8b686 100644 --- a/src/lib/hiro_win/editbox.cpp +++ b/src/lib/hiro/win/editbox.cpp @@ -10,7 +10,7 @@ void pEditbox::create(uint style, uint width, uint height, const char *text) { hwnd = CreateWindowEx(WS_EX_CLIENTEDGE, "EDIT", "", WS_CHILD | WS_VISIBLE | vscroll | hscroll | - (multiline == true ? ES_MULTILINE : 0) | + (multiline == true ? ES_MULTILINE | ES_WANTRETURN : WS_TABSTOP) | (readonly == true ? ES_READONLY : 0), 0, 0, width, height, phiro().default_hwnd, (HMENU)instance, GetModuleHandle(0), 0); diff --git a/src/lib/hiro_win/editbox.h b/src/lib/hiro/win/editbox.h similarity index 100% rename from src/lib/hiro_win/editbox.h rename to src/lib/hiro/win/editbox.h diff --git a/src/lib/hiro_win/formcontrol.cpp b/src/lib/hiro/win/formcontrol.cpp similarity index 71% rename from src/lib/hiro_win/formcontrol.cpp rename to src/lib/hiro/win/formcontrol.cpp index 3f96e298..32eb0a32 100644 --- a/src/lib/hiro_win/formcontrol.cpp +++ b/src/lib/hiro/win/formcontrol.cpp @@ -29,3 +29,17 @@ uintptr_t pFormControl::handle() { pFormControl::pFormControl(FormControl &self_) : pWidget(self_), self(self_) { hwnd = 0; } + +/* internal */ + +void pFormControl::show(bool state) { + ShowWindow(hwnd, state ? SW_NORMAL : SW_HIDE); +} + +void pFormControl::hide() { + show(false); +} + +bool pFormControl::visible() { + return GetWindowLong(hwnd, GWL_STYLE) & WS_VISIBLE; +} diff --git a/src/lib/hiro_win/formcontrol.h b/src/lib/hiro/win/formcontrol.h similarity index 78% rename from src/lib/hiro_win/formcontrol.h rename to src/lib/hiro/win/formcontrol.h index e2faeaf6..c70e93b7 100644 --- a/src/lib/hiro_win/formcontrol.h +++ b/src/lib/hiro/win/formcontrol.h @@ -8,6 +8,10 @@ public: bool enabled(); uintptr_t handle(); + virtual void show(bool = true); + virtual void hide(); + virtual bool visible(); + FormControl &self; pFormControl(FormControl&); diff --git a/src/lib/hiro_win/frame.cpp b/src/lib/hiro/win/frame.cpp similarity index 100% rename from src/lib/hiro_win/frame.cpp rename to src/lib/hiro/win/frame.cpp diff --git a/src/lib/hiro_win/frame.h b/src/lib/hiro/win/frame.h similarity index 100% rename from src/lib/hiro_win/frame.h rename to src/lib/hiro/win/frame.h diff --git a/src/lib/hiro_win/hiro.cpp b/src/lib/hiro/win/hiro.cpp similarity index 84% rename from src/lib/hiro_win/hiro.cpp rename to src/lib/hiro/win/hiro.cpp index 0c677f4c..98d5e69a 100644 --- a/src/lib/hiro_win/hiro.cpp +++ b/src/lib/hiro/win/hiro.cpp @@ -31,10 +31,14 @@ LRESULT CALLBACK phiro_wndproc(HWND, UINT, WPARAM, LPARAM); #include "slider.cpp" void pHiro::init() { -WNDCLASS wc; + memset(&osversioninfo, 0, sizeof(OSVERSIONINFO)); + osversioninfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + GetVersionEx(&osversioninfo); + + WNDCLASS wc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; - wc.hbrBackground = (HBRUSH)(COLOR_WINDOW); + wc.hbrBackground = (HBRUSH)(COLOR_3DFACE + 1); wc.hCursor = LoadCursor(0, IDC_ARROW); wc.hIcon = LoadIcon(0, IDI_APPLICATION); wc.hInstance = GetModuleHandle(0); @@ -55,25 +59,53 @@ void pHiro::term() { } bool pHiro::run() { -MSG msg; + MSG msg; if(PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) { - TranslateMessage(&msg); - DispatchMessage(&msg); + if(!IsDialogMessage(GetParent(msg.hwnd) ? GetParent(msg.hwnd) : msg.hwnd, &msg)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } } return pending(); } bool pHiro::pending() { -MSG msg; + MSG msg; return PeekMessage(&msg, 0, 0, 0, PM_NOREMOVE); } -bool pHiro::file_load(Window *focus, char *filename, const char *filter, const char *path) { -string dir, f; +bool pHiro::folder_select(Window *focus, char *filename, const char *path) { + strcpy(filename, ""); + BROWSEINFO bi; + bi.hwndOwner = focus ? focus->p.hwnd : 0; + bi.pidlRoot = NULL; + bi.pszDisplayName = filename; + bi.lpszTitle = "Select Folder"; + bi.ulFlags = BIF_NEWDIALOGSTYLE | BIF_RETURNONLYFSDIRS; + bi.lpfn = NULL; + bi.lParam = 0; + bi.iImage = 0; + bool result = false; + LPITEMIDLIST pidl = SHBrowseForFolder(&bi); + if(pidl) { + if(SHGetPathFromIDList(pidl, filename)) { + result = true; + IMalloc *imalloc = 0; + if(SUCCEEDED(SHGetMalloc(&imalloc))) { + imalloc->Free(pidl); + imalloc->Release(); + } + } + } + return result; +} + +bool pHiro::file_open(Window *focus, char *filename, const char *path, const char *filter) { + string dir, f; strcpy(dir, path ? path : ""); replace(dir, "/", "\\"); -lstring type, part; + lstring type, part; strcpy(f, ""); split(type, "|", filter); for(int i = 0; i < count(type); i++) { @@ -89,12 +121,12 @@ lstring type, part; strcat(f, "|"); } -char *pf = f(); + char *pf = f(); for(int i = strlen(pf) - 1; i >= 0; i--) { if(pf[i] == '|') pf[i] = '\0'; } -OPENFILENAME ofn; + OPENFILENAME ofn; strcpy(filename, ""); memset(&ofn, 0, sizeof(ofn)); ofn.lStructSize = sizeof(ofn); @@ -109,12 +141,12 @@ OPENFILENAME ofn; return GetOpenFileName(&ofn); } -bool pHiro::file_save(Window *focus, char *filename, const char *filter, const char *path) { -string dir, f; +bool pHiro::file_save(Window *focus, char *filename, const char *path, const char *filter) { + string dir, f; strcpy(dir, path ? path : ""); replace(dir, "/", "\\"); -lstring type, part; + lstring type, part; strcpy(f, ""); split(type, "|", filter); for(int i = 0; i < count(type); i++) { @@ -130,12 +162,12 @@ lstring type, part; strcat(f, "|"); } -char *pf = f(); + char *pf = f(); for(int i = strlen(pf) - 1; i >= 0; i--) { if(pf[i] == '|') pf[i] = '\0'; } -OPENFILENAME ofn; + OPENFILENAME ofn; strcpy(filename, ""); memset(&ofn, 0, sizeof(ofn)); ofn.lStructSize = sizeof(ofn); @@ -158,11 +190,20 @@ uint pHiro::screen_height() { return GetSystemMetrics(SM_CYSCREEN); } +void pHiro::enable_screensaver() { + is_screensaver_enabled = true; +} + +void pHiro::disable_screensaver() { + is_screensaver_enabled = false; +} + pHiro& pHiro::handle() { return hiro().p; } pHiro::pHiro(Hiro &self_) : self(self_) { + is_screensaver_enabled = true; } pHiro& phiro() { @@ -199,11 +240,11 @@ LRESULT pHiro::wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { switch(msg) { case WM_SYSCOMMAND: { switch(wparam) { - //TODO: write hiro API to allow this to be toggled. - //for now, screensaver and monitorpower events are disabled always. case SC_SCREENSAVE: - case SC_MONITORPOWER: - return FALSE; + case SC_MONITORPOWER: { + if(is_screensaver_enabled == false) return FALSE; + //fallthrough to DefWindowProc() + } break; } } break; diff --git a/src/lib/hiro_win/hiro.h b/src/lib/hiro/win/hiro.h similarity index 77% rename from src/lib/hiro_win/hiro.h rename to src/lib/hiro/win/hiro.h index f16c0ab4..8969990d 100644 --- a/src/lib/hiro_win/hiro.h +++ b/src/lib/hiro/win/hiro.h @@ -13,6 +13,7 @@ #include #include +#include namespace libhiro { @@ -45,16 +46,22 @@ public: bool run(); bool pending(); - bool file_load(Window *focus, char *filename, const char *filter, const char *path); - bool file_save(Window *focus, char *filename, const char *filter, const char *path); + bool folder_select(Window *focus, char *filename, const char *path = ""); + bool file_open(Window *focus, char *filename, const char *path = "", const char *filter = ""); + bool file_save(Window *focus, char *filename, const char *path = "", const char *filter = ""); uint screen_width(); uint screen_height(); + void enable_screensaver(); + void disable_screensaver(); + static pHiro& handle(); pHiro(Hiro&); /* internal */ + OSVERSIONINFO osversioninfo; + bool is_screensaver_enabled; HWND default_hwnd; //default parent window for all windowless controls HFONT default_font; //default font for all controls HBRUSH black_brush; //used for Canvas background diff --git a/src/lib/hiro_win/keymap.cpp b/src/lib/hiro/win/keymap.cpp similarity index 100% rename from src/lib/hiro_win/keymap.cpp rename to src/lib/hiro/win/keymap.cpp diff --git a/src/lib/hiro_win/label.cpp b/src/lib/hiro/win/label.cpp similarity index 100% rename from src/lib/hiro_win/label.cpp rename to src/lib/hiro/win/label.cpp diff --git a/src/lib/hiro_win/label.h b/src/lib/hiro/win/label.h similarity index 100% rename from src/lib/hiro_win/label.h rename to src/lib/hiro/win/label.h diff --git a/src/lib/hiro_win/listbox.cpp b/src/lib/hiro/win/listbox.cpp similarity index 95% rename from src/lib/hiro_win/listbox.cpp rename to src/lib/hiro/win/listbox.cpp index 242f858b..f8d72ac4 100644 --- a/src/lib/hiro_win/listbox.cpp +++ b/src/lib/hiro/win/listbox.cpp @@ -7,7 +7,8 @@ uint vscroll = (style & Listbox::VerticalScrollAlways) ? WS_VSCROLL : (style & Listbox::VerticalScrollNever) ? 0 : 0; hwnd = CreateWindowEx(WS_EX_CLIENTEDGE, WC_LISTVIEW, "", - WS_CHILD | WS_VISIBLE | LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_NOSORTHEADER | vscroll | hscroll | + WS_CHILD | WS_TABSTOP | WS_VISIBLE | + LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_NOSORTHEADER | vscroll | hscroll | (header ? 0 : LVS_NOCOLUMNHEADER), 0, 0, width, height, phiro().default_hwnd, (HMENU)instance, GetModuleHandle(0), 0); diff --git a/src/lib/hiro_win/listbox.h b/src/lib/hiro/win/listbox.h similarity index 100% rename from src/lib/hiro_win/listbox.h rename to src/lib/hiro/win/listbox.h diff --git a/src/lib/hiro_win/menucheckitem.cpp b/src/lib/hiro/win/menucheckitem.cpp similarity index 100% rename from src/lib/hiro_win/menucheckitem.cpp rename to src/lib/hiro/win/menucheckitem.cpp diff --git a/src/lib/hiro_win/menucheckitem.h b/src/lib/hiro/win/menucheckitem.h similarity index 100% rename from src/lib/hiro_win/menucheckitem.h rename to src/lib/hiro/win/menucheckitem.h diff --git a/src/lib/hiro_win/menucontrol.cpp b/src/lib/hiro/win/menucontrol.cpp similarity index 100% rename from src/lib/hiro_win/menucontrol.cpp rename to src/lib/hiro/win/menucontrol.cpp diff --git a/src/lib/hiro_win/menucontrol.h b/src/lib/hiro/win/menucontrol.h similarity index 100% rename from src/lib/hiro_win/menucontrol.h rename to src/lib/hiro/win/menucontrol.h diff --git a/src/lib/hiro_win/menugroup.cpp b/src/lib/hiro/win/menugroup.cpp similarity index 100% rename from src/lib/hiro_win/menugroup.cpp rename to src/lib/hiro/win/menugroup.cpp diff --git a/src/lib/hiro_win/menugroup.h b/src/lib/hiro/win/menugroup.h similarity index 100% rename from src/lib/hiro_win/menugroup.h rename to src/lib/hiro/win/menugroup.h diff --git a/src/lib/hiro_win/menuitem.cpp b/src/lib/hiro/win/menuitem.cpp similarity index 100% rename from src/lib/hiro_win/menuitem.cpp rename to src/lib/hiro/win/menuitem.cpp diff --git a/src/lib/hiro_win/menuitem.h b/src/lib/hiro/win/menuitem.h similarity index 100% rename from src/lib/hiro_win/menuitem.h rename to src/lib/hiro/win/menuitem.h diff --git a/src/lib/hiro_win/menuradioitem.cpp b/src/lib/hiro/win/menuradioitem.cpp similarity index 100% rename from src/lib/hiro_win/menuradioitem.cpp rename to src/lib/hiro/win/menuradioitem.cpp diff --git a/src/lib/hiro_win/menuradioitem.h b/src/lib/hiro/win/menuradioitem.h similarity index 100% rename from src/lib/hiro_win/menuradioitem.h rename to src/lib/hiro/win/menuradioitem.h diff --git a/src/lib/hiro_win/menuseparator.cpp b/src/lib/hiro/win/menuseparator.cpp similarity index 100% rename from src/lib/hiro_win/menuseparator.cpp rename to src/lib/hiro/win/menuseparator.cpp diff --git a/src/lib/hiro_win/menuseparator.h b/src/lib/hiro/win/menuseparator.h similarity index 100% rename from src/lib/hiro_win/menuseparator.h rename to src/lib/hiro/win/menuseparator.h diff --git a/src/lib/hiro_win/progressbar.cpp b/src/lib/hiro/win/progressbar.cpp similarity index 100% rename from src/lib/hiro_win/progressbar.cpp rename to src/lib/hiro/win/progressbar.cpp diff --git a/src/lib/hiro_win/progressbar.h b/src/lib/hiro/win/progressbar.h similarity index 100% rename from src/lib/hiro_win/progressbar.h rename to src/lib/hiro/win/progressbar.h diff --git a/src/lib/hiro_win/radiobox.cpp b/src/lib/hiro/win/radiobox.cpp similarity index 95% rename from src/lib/hiro_win/radiobox.cpp rename to src/lib/hiro/win/radiobox.cpp index 66233dab..d776674d 100644 --- a/src/lib/hiro_win/radiobox.cpp +++ b/src/lib/hiro/win/radiobox.cpp @@ -1,6 +1,6 @@ void pRadiobox::create(RadioboxGroup &group_, uint style, uint width, uint height, const char *text) { group = group_; - hwnd = CreateWindow("BUTTON", text ? text : "", WS_CHILD | WS_VISIBLE | BS_RADIOBUTTON, + hwnd = CreateWindow("BUTTON", text ? text : "", WS_CHILD | WS_TABSTOP | WS_VISIBLE | BS_RADIOBUTTON, 0, 0, width, height, phiro().default_hwnd, (HMENU)instance, GetModuleHandle(0), 0); SendMessage(hwnd, WM_SETFONT, (WPARAM)phiro().default_font, 0); if(group[0] == &self) check(); diff --git a/src/lib/hiro_win/radiobox.h b/src/lib/hiro/win/radiobox.h similarity index 100% rename from src/lib/hiro_win/radiobox.h rename to src/lib/hiro/win/radiobox.h diff --git a/src/lib/hiro_win/slider.cpp b/src/lib/hiro/win/slider.cpp similarity index 92% rename from src/lib/hiro_win/slider.cpp rename to src/lib/hiro/win/slider.cpp index dca64aea..81e1eb95 100644 --- a/src/lib/hiro_win/slider.cpp +++ b/src/lib/hiro/win/slider.cpp @@ -2,7 +2,7 @@ void pSlider::create(uint style, uint width, uint height, uint length) { if(length < 1) length = 1; hwnd = CreateWindow(TRACKBAR_CLASS, "", - WS_CHILD | WS_VISIBLE | TBS_NOTICKS | TBS_BOTH | + WS_CHILD | WS_VISIBLE | WS_TABSTOP | TBS_NOTICKS | TBS_BOTH | (style & Slider::Vertical ? TBS_VERT : TBS_HORZ), 0, 0, width, height, phiro().default_hwnd, (HMENU)instance, GetModuleHandle(0), 0); diff --git a/src/lib/hiro_win/slider.h b/src/lib/hiro/win/slider.h similarity index 100% rename from src/lib/hiro_win/slider.h rename to src/lib/hiro/win/slider.h diff --git a/src/lib/hiro_win/widget.cpp b/src/lib/hiro/win/widget.cpp similarity index 100% rename from src/lib/hiro_win/widget.cpp rename to src/lib/hiro/win/widget.cpp diff --git a/src/lib/hiro_win/widget.h b/src/lib/hiro/win/widget.h similarity index 100% rename from src/lib/hiro_win/widget.h rename to src/lib/hiro/win/widget.h diff --git a/src/lib/hiro_win/window.cpp b/src/lib/hiro/win/window.cpp similarity index 59% rename from src/lib/hiro_win/window.cpp rename to src/lib/hiro/win/window.cpp index d6de842f..d7a70cc6 100644 --- a/src/lib/hiro_win/window.cpp +++ b/src/lib/hiro/win/window.cpp @@ -1,7 +1,7 @@ void pWindow::create(uint style, uint width_, uint height_, const char *text) { auto_center = style & Window::AutoCenter; -RECT rc; + RECT rc; SystemParametersInfo(SPI_GETWORKAREA, 0, &rc, 0); hwnd = CreateWindowEx(0, "hiro_window", text ? text : "", @@ -30,40 +30,51 @@ void pWindow::move(uint x, uint y) { } void pWindow::resize(uint width_, uint height_) { + int screen_width = GetSystemMetrics(SM_CXSCREEN); + int screen_height = GetSystemMetrics(SM_CYSCREEN); + if(is_fullscreen == true) { - width_ = GetSystemMetrics(SM_CXSCREEN); - height_ = GetSystemMetrics(SM_CYSCREEN); - SetWindowPos(hwnd, 0, 0, 0, width_, height_, SWP_NOZORDER | SWP_FRAMECHANGED); + SetWindowPos(hwnd, 0, 0, 0, screen_width, screen_height, SWP_NOZORDER | SWP_FRAMECHANGED); return; } width = width_; height = height_; -//set requested window size to hidden window, calculate the difference between -//requested and actual client size area, and then adjust width so that new -//width, height values will set requested client area size. -//AdjustWindowRect() does not properly calculate the height of multi-line menus, -//and thusly is not used. + //set requested window size to hidden window, calculate the difference between + //requested and actual client size area, and then adjust width so that new + //width, height values will set requested client area size. + //AdjustWindowRect() does not properly calculate the height of multi-line menus, + //and thusly is not used. SetWindowPos(hwndr, 0, 0, 0, width_, height_, SWP_NOMOVE | SWP_NOZORDER); -RECT rc; + RECT rc; GetClientRect(hwndr, &rc); width_ += width_ - (rc.right - rc.left); height_ += height_ - (rc.bottom - rc.top); if(status.visible()) { - //statusbar does not count as part of window client area width, height + //statusbar does not count as part of window client area width, height GetClientRect(hstatus, &rc); height_ += rc.bottom - rc.top; } -int x = (GetSystemMetrics(SM_CXSCREEN) - width_) / 2; -int y = (GetSystemMetrics(SM_CYSCREEN) - height_) / 2; + //if window is larger than the screen size, Windows will hide the window entirely. + //therefore, window must be constrained to fit within the current screen size. -//if window is larger than screen, force window to top-left corner - SystemParametersInfo(SPI_GETWORKAREA, 0, &rc, 0); - if(x < rc.left) x = rc.left; - if(y < rc.top) y = rc.top; + int x, y; + if(width_ <= screen_width) { + x = (screen_width - width_) >> 1; + } else { + x = 0; + width_ = screen_width; + } + + if(height_ <= screen_height) { + y = (screen_height - height_) >> 1; + } else { + y = 0; + height_ = screen_height; + } SetWindowPos(hwnd, 0, x, y, width_, height_, (auto_center ? 0 : SWP_NOMOVE) | SWP_NOZORDER | SWP_FRAMECHANGED); } @@ -124,11 +135,64 @@ uint pWindow::get_height() { return (rc.bottom - rc.top) - (src.bottom - src.top); } +void pWindow::set_opacity(uint8_t opacity_) { + opacity = opacity_; + if(!hwnd) return; + + if(opacity != 255) { + //enable translucency + SetWindowLong(hwnd, GWL_EXSTYLE, GetWindowLong(hwnd, GWL_EXSTYLE) | WS_EX_LAYERED); + SetLayeredWindowAttributes(hwnd, 0, opacity, LWA_ALPHA); + } else { + //disable transluceny + SetWindowLong(hwnd, GWL_EXSTYLE, GetWindowLong(hwnd, GWL_EXSTYLE) & ~WS_EX_LAYERED); + SetLayeredWindowAttributes(hwnd, 0, 0, 0); + } + + InvalidateRect(hwnd, 0, TRUE); +} + void pWindow::set_background_color(uint8_t r, uint8_t g, uint8_t b) { if(background) DeleteObject(background); background = CreateSolidBrush(RGB(r, g, b)); } +void pWindow::set_icon(unsigned width, unsigned height, const uint32_t *data) { + if(hicon) DestroyIcon(hicon); + + uint8_t *mask = (uint8_t*)malloc(width * height / 8); + memset(mask, 0, width * height / 8); + uint8_t *icon = (uint8_t*)malloc(width * height * 4); + memcpy(icon, data, width * height * 4); + + if((phiro().osversioninfo.dwMajorVersion < 5) + || (phiro().osversioninfo.dwMajorVersion == 5 && phiro().osversioninfo.dwMinorVersion < 1)) { + //5.1 and above represents Windows XP or later, which supports 32-bit icons. + //5.0 and below represents Windows 2000 or earlier, which does not support 32-bit icons. + //if running Win2k or prior, alpha channel will be ignored; + //so scale color intensity by alpha level, which gives icons a black background. + //this is because an alpha of 0 (fully transparent) results in a color of 0 (black). + //without this step and alpha ignored, icons lose appearance of anti-aliasing. + for(unsigned i = 0; i < width * height; i++) { + uint8_t a = icon[i * 4 + 3]; + uint8_t r = uint8_t(1.0 / 256.0 * a * icon[i * 4 + 2]); + uint8_t g = uint8_t(1.0 / 256.0 * a * icon[i * 4 + 1]); + uint8_t b = uint8_t(1.0 / 256.0 * a * icon[i * 4 + 0]); + icon[i * 4 + 3] = 0xff; //ignored anyway, but just to be safe ... + icon[i * 4 + 2] = max(0, min(255, r)); //clamp 0 <= color <= 255 ... + icon[i * 4 + 1] = max(0, min(255, g)); //not required, but again ... + icon[i * 4 + 0] = max(0, min(255, b)); //it's better to be safe. + } + } + + hicon = CreateIcon(GetModuleHandle(0), width, height, 1, 32, (const BYTE*)mask, (const BYTE*)icon); + SendMessage(hwnd, WM_SETICON, ICON_SMALL, (LPARAM)hicon); + SendMessage(hwnd, WM_SETICON, ICON_BIG, (LPARAM)hicon); + + free(mask); + free(icon); +} + void pWindow::set_text(const char *text) { SetWindowText(hwnd, text); } @@ -136,13 +200,14 @@ void pWindow::set_text(const char *text) { void pWindow::attach(Window &window, uint x, uint y) { if(!window.p.hwnd) return; -//toplevel window size is larger, because it includes window borders -//read size of window without window borders, and resize upon attach -RECT rc; + //toplevel window size is larger, because it includes window borders + //read size of window without window borders, and resize upon attach + RECT rc; GetClientRect(window.p.hwnd, &rc); ShowWindow(window.p.hwnd, SW_HIDE); SetWindowLong(window.p.hwnd, GWL_STYLE, WS_CHILD); + SetWindowLong(window.p.hwnd, GWL_EXSTYLE, WS_EX_CONTROLPARENT); SetParent(window.p.hwnd, hwnd); SetWindowPos(window.p.hwnd, 0, x, y, rc.right - rc.left, rc.bottom - rc.top, SWP_NOZORDER | SWP_FRAMECHANGED); ShowWindow(window.p.hwnd, SW_NORMAL); @@ -156,6 +221,10 @@ void pWindow::attach(MenuGroup &menugroup) { void pWindow::attach(FormControl &formcontrol, uint x, uint y) { SetWindowPos(formcontrol.p.hwnd, 0, x, y, 0, 0, SWP_NOSIZE | SWP_NOZORDER); SetParent(formcontrol.p.hwnd, hwnd); + //SetParent() sets Z-order to topmost ... + //this causes WS_TABSTOP property to run through controls "backward" + //by inverting this, the later a control is attached, the later the tab key moves to it + SetWindowPos(formcontrol.p.hwnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE); } void pWindow::move(Window &window, uint x, uint y) { @@ -207,8 +276,10 @@ pWindow::Statusbar::Statusbar(pWindow &p_) : p(p_) { pWindow::pWindow(Window &self_) : pWidget(self_), self(self_), menu(*this), status(*this) { hwnd = 0; hwndr = 0; + hicon = 0; hmenu = 0; background = 0; + opacity = 255; is_fullscreen = false; auto_center = false; width = 0; @@ -216,6 +287,7 @@ pWindow::pWindow(Window &self_) : pWidget(self_), self(self_), menu(*this), stat } pWindow::~pWindow() { + if(hicon) DestroyIcon(hicon); if(background) DeleteObject(background); } diff --git a/src/lib/hiro_win/window.h b/src/lib/hiro/win/window.h similarity index 91% rename from src/lib/hiro_win/window.h rename to src/lib/hiro/win/window.h index 71693fbe..9a816ef9 100644 --- a/src/lib/hiro_win/window.h +++ b/src/lib/hiro/win/window.h @@ -10,7 +10,9 @@ public: void unfullscreen(); uint get_width(); uint get_height(); + void set_opacity(uint8_t opacity); void set_background_color(uint8_t r, uint8_t g, uint8_t b); + void set_icon(unsigned width, unsigned height, const uint32_t *data); void set_text(const char *text = ""); void attach(Window &window, uint x, uint y); void attach(MenuGroup &menugroup); @@ -46,9 +48,11 @@ public: /* internal */ HWND hwnd; HWND hwndr; //hidden window, used as resize assistant + HICON hicon; HMENU hmenu; HWND hstatus; HBRUSH background; + uint8_t opacity; bool is_fullscreen; bool auto_center; uint width, height; diff --git a/src/lib/libco/fiber.c b/src/lib/libco/fiber.c index d1b39586..02ef5bc7 100644 --- a/src/lib/libco/fiber.c +++ b/src/lib/libco/fiber.c @@ -5,7 +5,7 @@ */ #define LIBCO_C -#include "../libco.h" +#include "libco.h" #define WINVER 0x0400 #define _WIN32_WINNT 0x0400 #define WIN32_LEAN_AND_MEAN diff --git a/src/lib/libco.c b/src/lib/libco/libco.c similarity index 71% rename from src/lib/libco.c rename to src/lib/libco/libco.c index 0e7d3761..604b37df 100644 --- a/src/lib/libco.c +++ b/src/lib/libco/libco.c @@ -5,17 +5,17 @@ */ #if defined(__GNUC__) && defined(__i386__) - #include "libco/x86.c" + #include "x86.c" #elif defined(__GNUC__) && defined(__amd64__) && !defined(__MINGW64__) - #include "libco/x86-64.c" + #include "x86-64.c" #elif defined(__MINGW64__) - #include "libco/fiber.c" + #include "fiber.c" #elif defined(__GNUC__) - #include "libco/sjlj.c" + #include "sjlj.c" #elif defined(_MSC_VER) && defined(_M_IX86) - #include "libco/x86.c" + #include "x86.c" #elif defined(_MSC_VER) && defined(_M_AMD64) - #include "libco/fiber.c" + #include "fiber.c" #else #error "libco: unsupported processor, compiler or operating system" #endif diff --git a/src/lib/libco.h b/src/lib/libco/libco.h similarity index 100% rename from src/lib/libco.h rename to src/lib/libco/libco.h diff --git a/src/lib/libco/sjlj.c b/src/lib/libco/sjlj.c index 86b2ea26..8b72b614 100644 --- a/src/lib/libco/sjlj.c +++ b/src/lib/libco/sjlj.c @@ -12,7 +12,7 @@ */ #define LIBCO_C -#include "../libco.h" +#include "libco.h" #include #include #include diff --git a/src/lib/libco/ucontext.c b/src/lib/libco/ucontext.c index 47766e64..17472f6b 100644 --- a/src/lib/libco/ucontext.c +++ b/src/lib/libco/ucontext.c @@ -17,7 +17,7 @@ */ #define LIBCO_C -#include "../libco.h" +#include "libco.h" #include #include diff --git a/src/lib/libco/x86-64.c b/src/lib/libco/x86-64.c index e1e8c7f3..2e2a1131 100644 --- a/src/lib/libco/x86-64.c +++ b/src/lib/libco/x86-64.c @@ -5,7 +5,7 @@ */ #define LIBCO_C -#include "../libco.h" +#include "libco.h" #include #include diff --git a/src/lib/libco/x86.c b/src/lib/libco/x86.c index 18af8ac4..3a5507ff 100644 --- a/src/lib/libco/x86.c +++ b/src/lib/libco/x86.c @@ -5,7 +5,7 @@ */ #define LIBCO_C -#include "../libco.h" +#include "libco.h" #include #include diff --git a/src/lib/libfilter/colortable.cpp b/src/lib/libfilter/colortable.cpp new file mode 100644 index 00000000..936ab495 --- /dev/null +++ b/src/lib/libfilter/colortable.cpp @@ -0,0 +1,123 @@ +Colortable colortable; + +void Colortable::set_format(Format format_) { format = format_; } +void Colortable::set_contrast(int32_t contrast_) { contrast = contrast_; } +void Colortable::set_brightness(int32_t brightness_) { brightness = brightness_; } +void Colortable::set_gamma(int32_t gamma_) { gamma = gamma_; } + +void Colortable::enable_gamma_ramp(bool value) { gamma_ramp = value; } +void Colortable::enable_sepia(bool value) { sepia = value; } +void Colortable::enable_grayscale(bool value) { grayscale = value; } +void Colortable::enable_invert(bool value) { invert = value; } + +void Colortable::update() { + int32_t l, r, g, b; + double kr = 0.2126, kb = 0.0722, kg = (1.0 - kr - kb); //luminance + uint32_t col; + for(unsigned i = 0; i < 32768; i++) { + //bgr555->rgb888 + col = ((i & 0x001f) << 19) | ((i & 0x001c) << 14) + | ((i & 0x03e0) << 6) | ((i & 0x0380) << 1) + | ((i & 0x7c00) >> 7) | ((i & 0x7000) >> 12); + + r = (col >> 16) & 0xff; + g = (col >> 8) & 0xff; + b = (col ) & 0xff; + + if(gamma_ramp == true) { + r = gamma_ramp_table[r >> 3]; + g = gamma_ramp_table[g >> 3]; + b = gamma_ramp_table[b >> 3]; + } + + contrast_adjust(r); brightness_adjust(r); gamma_adjust(r); + contrast_adjust(g); brightness_adjust(g); gamma_adjust(g); + contrast_adjust(b); brightness_adjust(b); gamma_adjust(b); + + if(sepia == true) { + l = (int32_t)((double)r * kr + (double)g * kg + (double)b * kb); + l = max(0, min(255, l)); + + r = (int32_t)((double)l * (1.0 + 0.300)); + g = (int32_t)((double)l * (1.0 - 0.055)); + b = (int32_t)((double)l * (1.0 - 0.225)); + + r = max(0, min(255, r)); + g = max(0, min(255, g)); + b = max(0, min(255, b)); + } + + if(grayscale == true) { + l = (int32_t)((double)r * kr + (double)g * kg + (double)b * kb); + l = max(0, min(255, l)); + r = g = b = l; + } + + if(invert == true) { + r ^= 0xff; + g ^= 0xff; + b ^= 0xff; + } + + switch(format) { + case RGB555: { + r >>= 3; + g >>= 3; + b >>= 3; + table[i] = (r << 10) | (g << 5) | (b); + } break; + + case RGB565: { + r >>= 3; + g >>= 2; + b >>= 3; + table[i] = (r << 11) | (g << 5) | (b); + } break; + + case RGB888: { + table[i] = (r << 16) | (g << 8) | (b); + } break; + + default: { + table[i] = -1U; + } break; + } + } +} + +Colortable::Colortable() { + table = new uint32_t[32768]; + contrast = 0; + brightness = 0; + gamma = 100; +} + +Colortable::~Colortable() { + delete[] table; +} + +/* internal */ + +const uint8_t Colortable::gamma_ramp_table[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, +}; + +void Colortable::contrast_adjust(int32_t &input) { + double lmin = 0.0 - (double)contrast; + double lmax = 255.0 + (double)contrast; + int32_t result = (int32_t)(lmin + (double)input * ((lmax - lmin) / 256.0)); + input = max(0, min(255, result)); +} + +void Colortable::brightness_adjust(int32_t &input) { + int32_t result = input + brightness; + input = max(0, min(255, result)); +} + +void Colortable::gamma_adjust(int32_t &input) { + int32_t result = (int32_t)(pow(((double)(input + 1) / 256.0), (double)gamma / 100.0) * 256.0); + input = max(0, min(255, result)); +} diff --git a/src/lib/libfilter/colortable.h b/src/lib/libfilter/colortable.h new file mode 100644 index 00000000..0fc42017 --- /dev/null +++ b/src/lib/libfilter/colortable.h @@ -0,0 +1,44 @@ +class Colortable { +public: + enum Format { + RGB555, + RGB565, + RGB888, + }; + + const inline uint32_t operator[](uint16_t index) const { return table[index]; } + + void set_format(Format); + void set_contrast(int32_t); + void set_brightness(int32_t); + void set_gamma(int32_t); + void enable_gamma_ramp(bool); + void enable_sepia(bool); + void enable_grayscale(bool); + void enable_invert(bool); + + void update(); + + Colortable(); + ~Colortable(); + +private: + uint32_t *table; + Format format; + + int32_t contrast; + int32_t brightness; + int32_t gamma; + + bool gamma_ramp; + bool sepia; + bool grayscale; + bool invert; + + static const uint8_t gamma_ramp_table[32]; + void contrast_adjust(int32_t &input); + void brightness_adjust(int32_t &input); + void gamma_adjust(int32_t &input); +}; + +extern Colortable colortable; diff --git a/src/lib/libfilter/direct.cpp b/src/lib/libfilter/direct.cpp new file mode 100644 index 00000000..6afe0e38 --- /dev/null +++ b/src/lib/libfilter/direct.cpp @@ -0,0 +1,30 @@ +DirectFilter filter_direct; + +void DirectFilter::render( + uint32_t *output, unsigned outpitch, unsigned &outwidth, unsigned &outheight, + uint16_t *input, unsigned pitch, unsigned *line, unsigned width, unsigned height +) { + pitch >>= 1; + outpitch >>= 2; + + for(unsigned y = 0; y < height; y++) { + if(width == 512 && line[y] == 256) { + for(unsigned x = 0; x < 256; x++) { + uint16_t p = *input++; + *output++ = colortable[p]; + *output++ = colortable[p]; + } + input += 256; + } else { + for(unsigned x = 0; x < width; x++) { + uint16_t p = *input++; + *output++ = colortable[p]; + } + } + input += pitch - width; + output += outpitch - width; + } + + outwidth = width; + outheight = height; +} diff --git a/src/lib/libfilter/direct.h b/src/lib/libfilter/direct.h new file mode 100644 index 00000000..0d339fae --- /dev/null +++ b/src/lib/libfilter/direct.h @@ -0,0 +1,6 @@ +class DirectFilter : public Filter { +public: + void render(uint32_t*, unsigned, unsigned&, unsigned&, uint16_t*, unsigned, unsigned*, unsigned, unsigned); +}; + +extern DirectFilter filter_direct; diff --git a/src/lib/libfilter/filter.cpp b/src/lib/libfilter/filter.cpp new file mode 100644 index 00000000..7025ba60 --- /dev/null +++ b/src/lib/libfilter/filter.cpp @@ -0,0 +1,34 @@ +FilterInterface filter; + +void FilterInterface::set(FilterInterface::FilterType type) { + active_filter = type; +} + +void FilterInterface::render( + uint32_t *output, unsigned outpitch, unsigned &outwidth, unsigned &outheight, + uint16_t *input, unsigned pitch, unsigned *line, unsigned width, unsigned height +) { + switch(active_filter) { default: + case Direct: { + filter_direct.render(output, outpitch, outwidth, outheight, input, pitch, line, width, height); + } break; + + case Scanline: { + filter_scanline.render(output, outpitch, outwidth, outheight, input, pitch, line, width, height); + } break; + + case Scale2x: { + filter_scale2x.render(output, outpitch, outwidth, outheight, input, pitch, line, width, height); + } break; + + case HQ2x: { + filter_hq2x.render(output, outpitch, outwidth, outheight, input, pitch, line, width, height); + } break; + + case NTSC: { + filter_ntsc.render(output, outpitch, outwidth, outheight, input, pitch, line, width, height); + } break; + } +} + +FilterInterface::FilterInterface() : active_filter(FilterInterface::Direct) {} diff --git a/src/lib/libfilter/filter.h b/src/lib/libfilter/filter.h new file mode 100644 index 00000000..7aaa36b0 --- /dev/null +++ b/src/lib/libfilter/filter.h @@ -0,0 +1,32 @@ +class Filter { +public: + virtual void render( + uint32_t *output, unsigned outpitch, unsigned &outwidth, unsigned &outheight, + uint16_t *input, unsigned pitch, unsigned *line, unsigned width, unsigned height + ) = 0; +}; + +class FilterInterface : public Filter { +public: + enum FilterType { + Direct, + Scanline, + Scale2x, + HQ2x, + NTSC, + }; + + void set(FilterType type); + + void render( + uint32_t *output, unsigned outpitch, unsigned &outwidth, unsigned &outheight, + uint16_t *input, unsigned pitch, unsigned *line, unsigned width, unsigned height + ); + + FilterInterface(); + +private: + FilterType active_filter; +}; + +extern FilterInterface filter; diff --git a/src/lib/libfilter/hq2x.cpp b/src/lib/libfilter/hq2x.cpp new file mode 100644 index 00000000..0418fdde --- /dev/null +++ b/src/lib/libfilter/hq2x.cpp @@ -0,0 +1,160 @@ +HQ2xFilter filter_hq2x; + +#define diff(x, y) ((yuvtable[x] - yuvtable[y] + diff_offset) & diff_mask) +#define hdiff(x, y) ((x - yuvtable[y]) & diff_mask) +#define expand_rgb(n) { n |= n << 16; n &= 0x03e07c1f; } +#define pack_rgb(n) { n &= 0x03e07c1f; n |= n >> 16; } + +static uint16_t blend1(uint32_t c1, uint32_t c2) { + expand_rgb(c1); + expand_rgb(c2); + c1 = (c1 * 3 + c2) >> 2; + pack_rgb(c1); + return c1; +} + +static uint16_t blend2(uint32_t c1, uint32_t c2, uint32_t c3) { +//c1 = (c1 * 2 + c2 + c3) >> 2; + c2 = (c2 + c3 - ((c2 ^ c3) & 0x0421)) >> 1; + c1 = (c1 + c2 - ((c1 ^ c2) & 0x0421)) >> 1; + return c1; +} + +static uint16_t blend6(uint32_t c1, uint32_t c2, uint32_t c3) { + expand_rgb(c1); + expand_rgb(c2); + expand_rgb(c3); + c1 = (c1 * 5 + c2 * 2 + c3) >> 3; + pack_rgb(c1); + return c1; +} + +static uint16_t blend7(uint32_t c1, uint32_t c2, uint32_t c3) { + expand_rgb(c1); + expand_rgb(c2); + expand_rgb(c3); + c1 = (c1 * 6 + c2 + c3) >> 3; + pack_rgb(c1); + return c1; +} + +static uint16_t blend9(uint32_t c1, uint32_t c2, uint32_t c3) { + expand_rgb(c1); + expand_rgb(c2); + expand_rgb(c3); + c1 = (c1 * 2 + (c2 + c3) * 3) >> 3; + pack_rgb(c1); + return c1; +} + +static uint16_t blend10(uint32_t c1, uint32_t c2, uint32_t c3) { + expand_rgb(c1); + expand_rgb(c2); + expand_rgb(c3); + c1 = (c1 * 14 + c2 + c3) >> 4; + pack_rgb(c1); + return c1; +} + +void HQ2xFilter::render( + uint32_t *output, unsigned outpitch, unsigned &outwidth, unsigned &outheight, + uint16_t *input, unsigned pitch, unsigned *line, unsigned width, unsigned height +) { + if(width > 256 || height > 240) { + filter_direct.render(output, outpitch, outwidth, outheight, input, pitch, line, width, height); + return; + } + + pitch >>= 1; + outpitch >>= 2; + + uint32_t *out0 = output; + uint32_t *out1 = output + outpitch; + + #define W1 input[-1 - pitch] + #define W2 input[ 0 - pitch] + #define W3 input[+1 - pitch] + #define W4 input[-1] + #define W5 input[ 0] + #define W6 input[+1] + #define W7 input[-1 + pitch] + #define W8 input[ 0 + pitch] + #define W9 input[+1 + pitch] + + input += pitch; + memset(out0, 0, 2048); out0 += outpitch << 1; + memset(out1, 0, 2048); out1 += outpitch << 1; + + for(int y = height - 2; y; --y) { + input++; + *out0++ = 0; *out0++ = 0; + *out1++ = 0; *out1++ = 0; + + int32_t pattern = diff(W5, W4) ? 0x10 : 0x00; + for(int x = 256 - 2; x; --x) { + uint32_t center = yuvtable[W5] + diff_offset; + + //W4 for pixel x+1 is the same as W6 for pixel x + pattern = (pattern & 0x10) >> 1; + pattern |= hdiff(center, W1) ? 0x01 : 0x00; + pattern |= hdiff(center, W2) ? 0x02 : 0x00; + pattern |= hdiff(center, W3) ? 0x04 : 0x00; + //pattern |= hdiff(center, W4) ? 0x08 : 0x00; + pattern |= hdiff(center, W6) ? 0x10 : 0x00; + pattern |= hdiff(center, W7) ? 0x20 : 0x00; + pattern |= hdiff(center, W8) ? 0x40 : 0x00; + pattern |= hdiff(center, W9) ? 0x80 : 0x00; + + switch(pattern) { + #include "hq2x_table.h" + } + + input++; + out0 += 2; + out1 += 2; + } + + input++; + *out0++ = 0; *out0++ = 0; + *out1++ = 0; *out1++ = 0; + + input += pitch - 256; + out0 += outpitch + outpitch - 512; + out1 += outpitch + outpitch - 512; + } + + memset(out0, 0, 2048); + memset(out1, 0, 2048); + + outwidth = width * 2; + outheight = height * 2; +} + +HQ2xFilter::HQ2xFilter() { + yuvtable = new uint32_t[32768]; + + for(int i = 0; i < 32768; i++) { + int ir = (i) & 0x1f; + int ig = (i >> 5) & 0x1f; + int ib = (i >> 10) & 0x1f; + + //bgr555->bgr888 + double r = (ir << 3) | (ir >> 2); + double g = (ig << 3) | (ig >> 2); + double b = (ib << 3) | (ib >> 2); + + //bgr888->yuv888 + double y = (r + g + b) * (0.25f * (63.5f / 48.0f)); + double u = ((r - b) * 0.25f + 128.0f) * (7.5f / 7.0f); + double v = ((g * 2.0f - r - b) * 0.125f + 128.0f) * (7.5f / 6.0f); + + yuvtable[i] = (int(y) << 21) + (int(u) << 11) + (int(v)); + } + + diff_offset = (0x440 << 21) + (0x207 << 11) + 0x407; + diff_mask = (0x380 << 21) + (0x1f0 << 11) + 0x3f0; +} + +HQ2xFilter::~HQ2xFilter() { + delete[] yuvtable; +} diff --git a/src/lib/libfilter/hq2x.h b/src/lib/libfilter/hq2x.h new file mode 100644 index 00000000..227a796c --- /dev/null +++ b/src/lib/libfilter/hq2x.h @@ -0,0 +1,14 @@ +class HQ2xFilter : public Filter { +public: + void render(uint32_t*, unsigned, unsigned&, unsigned&, uint16_t*, unsigned, unsigned*, unsigned, unsigned); + + HQ2xFilter(); + ~HQ2xFilter(); + +private: + uint32_t *yuvtable; + uint32_t diff_offset; + uint32_t diff_mask; +}; + +extern HQ2xFilter filter_hq2x; diff --git a/src/snes/video/filter_hq2x_lookuptbl.h b/src/lib/libfilter/hq2x_table.h similarity index 93% rename from src/snes/video/filter_hq2x_lookuptbl.h rename to src/lib/libfilter/hq2x_table.h index c737d134..3d93e947 100644 --- a/src/snes/video/filter_hq2x_lookuptbl.h +++ b/src/lib/libfilter/hq2x_table.h @@ -1,7 +1,14 @@ -#define p00(__color) *(out0 + 0) = (colortbl[__color]) -#define p01(__color) *(out0 + 1) = (colortbl[__color]) -#define p10(__color) *(out1 + 0) = (colortbl[__color]) -#define p11(__color) *(out1 + 1) = (colortbl[__color]) +/***** + * HQ2x Algorithm (C) 2003 Maxim Stepin + * License: LGPL + * + * Optimizations (C) 2006 Shay Green, byuu + *****/ + +#define p00(color) *(out0 + 0) = (colortable[color]) +#define p01(color) *(out0 + 1) = (colortable[color]) +#define p10(color) *(out1 + 0) = (colortable[color]) +#define p11(color) *(out1 + 1) = (colortable[color]) #define PIXEL00_0 p00( W5); #define PIXEL00_10 p00(blend1 (W5, W1)); diff --git a/src/lib/libfilter/libfilter.cpp b/src/lib/libfilter/libfilter.cpp new file mode 100644 index 00000000..0f984efa --- /dev/null +++ b/src/lib/libfilter/libfilter.cpp @@ -0,0 +1,14 @@ +#include "libfilter.h" +using nall::min; +using nall::max; + +namespace libfilter { + #include "colortable.cpp" + #include "filter.cpp" + + #include "direct.cpp" + #include "scanline.cpp" + #include "scale2x.cpp" + #include "hq2x.cpp" + #include "ntsc.cpp" +} //namespace libfilter diff --git a/src/lib/libfilter/libfilter.h b/src/lib/libfilter/libfilter.h new file mode 100644 index 00000000..e06a63d7 --- /dev/null +++ b/src/lib/libfilter/libfilter.h @@ -0,0 +1,22 @@ +#ifndef LIBFILTER_H +#define LIBFILTER_H + +#include +#include +#include +#include +#include +#include + +namespace libfilter { + #include "colortable.h" + #include "filter.h" + + #include "direct.h" + #include "scanline.h" + #include "scale2x.h" + #include "hq2x.h" + #include "ntsc.h" +} //namespace libfilter + +#endif //ifndef LIBFILTER_H diff --git a/src/lib/libfilter/ntsc.cpp b/src/lib/libfilter/ntsc.cpp new file mode 100644 index 00000000..14b8c37a --- /dev/null +++ b/src/lib/libfilter/ntsc.cpp @@ -0,0 +1,73 @@ +#include "snes_ntsc/snes_ntsc.c" + +NTSCFilter filter_ntsc; + +void NTSCFilter::render( + uint32_t *output, unsigned outpitch, unsigned &outwidth, unsigned &outheight, + uint16_t *input, unsigned pitch, unsigned *line, unsigned width, unsigned height +) { + if(!ntsc)return; + + int const out_width = outwidth = SNES_NTSC_OUT_WIDTH(256); + int const out_height = outheight = height; + burst ^= burst_toggle; + + //blit multiple scanlines of same width, rather than one at a time + int run_start = 0; + int run_width = line[0]; + int l = 0; + + while(1) { + if(run_width != line[l] || l >= out_height) { + uint16_t const *in = (uint16_t*)((uint8_t*)input + pitch * run_start); + uint16_t *out = (uint16_t*)((uint8_t*)output + outpitch * run_start); + int height = l - run_start; + int line_burst = (burst + run_start) % 3; + if(run_width == 256) { + snes_ntsc_blit(ntsc, in, (pitch >> 1), line_burst, out_width, height, out, outpitch); + } else { + snes_ntsc_blit_hires(ntsc, in, (pitch >> 1), line_burst, out_width, height, out, outpitch); + } + if(l >= out_height) break; + run_width = line[l]; + run_start = l; + } + l++; + } +} + +void NTSCFilter::adjust( + float hue, float saturation, float contrast, + float brightness, float sharpness, bool merge_fields +) { + static snes_ntsc_setup_t defaults; + snes_ntsc_setup_t setup = defaults; + setup.hue = hue; + setup.saturation = saturation; + setup.contrast = contrast; + setup.brightness = brightness; + setup.sharpness = sharpness; + setup.resolution = sharpness; + setup.merge_fields = merge_fields; + setup.bsnes_colortbl = 0; + + if(!ntsc) { + ntsc = (snes_ntsc_t*)malloc(sizeof *ntsc); + if(!ntsc) { + return; //to do: report out of memory error + } + } + + burst = 0; + burst_toggle = (merge_fields ? 0 : 1); // don't toggle burst when fields are merged + snes_ntsc_init(ntsc, &setup); +} + +NTSCFilter::NTSCFilter() { + ntsc = 0; + adjust(0, 0, 0, 0, 0, true); +} + +NTSCFilter::~NTSCFilter() { + if(ntsc) free(ntsc); +} diff --git a/src/lib/libfilter/ntsc.h b/src/lib/libfilter/ntsc.h new file mode 100644 index 00000000..953f4885 --- /dev/null +++ b/src/lib/libfilter/ntsc.h @@ -0,0 +1,17 @@ +#include "snes_ntsc/snes_ntsc.h" + +class NTSCFilter : public Filter { +public: + void render(uint32_t*, unsigned, unsigned&, unsigned&, uint16_t*, unsigned, unsigned*, unsigned, unsigned); + + NTSCFilter(); + ~NTSCFilter(); + +private: + struct snes_ntsc_t *ntsc; + int burst, burst_toggle; + + void adjust(float hue, float saturation, float contrast, float brightness, float sharpness, bool merge_fields); +}; + +extern NTSCFilter filter_ntsc; diff --git a/src/lib/libfilter/scale2x.cpp b/src/lib/libfilter/scale2x.cpp new file mode 100644 index 00000000..687a5683 --- /dev/null +++ b/src/lib/libfilter/scale2x.cpp @@ -0,0 +1,47 @@ +Scale2xFilter filter_scale2x; + +void Scale2xFilter::render( + uint32_t *output, unsigned outpitch, unsigned &outwidth, unsigned &outheight, + uint16_t *input, unsigned pitch, unsigned *line, unsigned width, unsigned height +) { + if(width > 256 || height > 240) { + filter_direct.render(output, outpitch, outwidth, outheight, input, pitch, line, width, height); + return; + } + + pitch >>= 1; + outpitch >>= 2; + + uint32_t A, B, C, D, P; + int prevline, nextline; + + for(int y = 0; y < height; y++) { + prevline = (y > 0) ? -pitch : 0; + nextline = (y < height - 1) ? pitch : 0; + for(int x = 0; x < 256; x++) { + A = *(input + prevline); + B = (x > 0) ? *(input - 1) : *input; + C = (x < 255) ? *(input + 1) : *input; + D = *(input + nextline); + P = colortable[*input]; + if(A != D && B != C) { + *(output) = A == B ? colortable[A] : P; + *(output + 1) = A == C ? colortable[A] : P; + *(output + outpitch) = D == B ? colortable[D] : P; + *(output + outpitch + 1) = D == C ? colortable[D] : P; + } else { + *(output) = P; + *(output + 1) = P; + *(output + outpitch) = P; + *(output + outpitch + 1) = P; + } + input++; + output += 2; + } + input += pitch - 256; + output += outpitch + outpitch - 512; + } + + outwidth = width * 2; + outheight = height * 2; +} diff --git a/src/lib/libfilter/scale2x.h b/src/lib/libfilter/scale2x.h new file mode 100644 index 00000000..00f52896 --- /dev/null +++ b/src/lib/libfilter/scale2x.h @@ -0,0 +1,6 @@ +class Scale2xFilter : public Filter { +public: + void render(uint32_t*, unsigned, unsigned&, unsigned&, uint16_t*, unsigned, unsigned*, unsigned, unsigned); +}; + +extern Scale2xFilter filter_scale2x; diff --git a/src/lib/libfilter/scanline.cpp b/src/lib/libfilter/scanline.cpp new file mode 100644 index 00000000..af402e7e --- /dev/null +++ b/src/lib/libfilter/scanline.cpp @@ -0,0 +1,40 @@ +ScanlineFilter filter_scanline; + +void ScanlineFilter::render( + uint32_t *output, unsigned outpitch, unsigned &outwidth, unsigned &outheight, + uint16_t *input, unsigned pitch, unsigned *line, unsigned width, unsigned height +) { + if(height > 240) { + filter_direct.render(output, outpitch, outwidth, outheight, input, pitch, line, width, height); + return; + } + + pitch >>= 1; + outpitch >>= 2; + + for(unsigned y = 0; y < height; y++) { + uint32_t *out0 = output; + uint32_t *out1 = output + pitch; + if(width == 512 && line[y] == 256) { + for(unsigned x = 0; x < 256; x++) { + uint16_t p = *input++; + *out0++ = colortable[p]; + *out0++ = colortable[p]; + *out1++ = (colortable[p] >> 1) & 0x7f7f7f; + *out1++ = (colortable[p] >> 1) & 0x7f7f7f; + } + input += 256; + } else { + for(unsigned x = 0; x < width; x++) { + uint16_t p = *input++; + *out0++ = colortable[p]; + *out1++ = (colortable[p] >> 1) & 0x7f7f7f; + } + } + input += pitch - width; + output += outpitch * 2; + } + + outwidth = width; + outheight = height * 2; +} diff --git a/src/lib/libfilter/scanline.h b/src/lib/libfilter/scanline.h new file mode 100644 index 00000000..c1b804cc --- /dev/null +++ b/src/lib/libfilter/scanline.h @@ -0,0 +1,6 @@ +class ScanlineFilter : public Filter { +public: + void render(uint32_t*, unsigned, unsigned&, unsigned&, uint16_t*, unsigned, unsigned*, unsigned, unsigned); +}; + +extern ScanlineFilter filter_scanline; diff --git a/src/snes/video/ntsc/snes_ntsc.c b/src/lib/libfilter/snes_ntsc/snes_ntsc.c similarity index 100% rename from src/snes/video/ntsc/snes_ntsc.c rename to src/lib/libfilter/snes_ntsc/snes_ntsc.c diff --git a/src/snes/video/ntsc/snes_ntsc.h b/src/lib/libfilter/snes_ntsc/snes_ntsc.h similarity index 96% rename from src/snes/video/ntsc/snes_ntsc.h rename to src/lib/libfilter/snes_ntsc/snes_ntsc.h index 5338d145..37b3c7bc 100644 --- a/src/snes/video/ntsc/snes_ntsc.h +++ b/src/lib/libfilter/snes_ntsc/snes_ntsc.h @@ -207,15 +207,15 @@ enum { snes_ntsc_burst_size = snes_ntsc_entry_size / snes_ntsc_burst_count }; if ( bits == 16 ) {\ rgb_out = (raw_>>(13-x)& 0xF800)|(raw_>>(8-x)&0x07E0)|(raw_>>(4-x)&0x001F);\ rgb_out = ((rgb_out&0xf800)>>11)|((rgb_out&0x07c0)>>1)|((rgb_out&0x001f)<<10);\ - rgb_out = snes.color_lookup_table[rgb_out];\ + rgb_out = colortable[rgb_out];\ } else if ( bits == 24 || bits == 32 ) {\ rgb_out = (raw_>>(5-x)&0xFF0000)|(raw_>>(3-x)&0xFF00)|(raw_>>(1-x)&0xFF);\ rgb_out = ((rgb_out&0xf80000)>>19)|((rgb_out&0x00f800)>>6)|((rgb_out&0x0000f8)<<7);\ - rgb_out = snes.color_lookup_table[rgb_out];\ + rgb_out = colortable[rgb_out];\ } else if ( bits == 15 ) {\ rgb_out = (raw_>>(14-x)& 0x7C00)|(raw_>>(9-x)&0x03E0)|(raw_>>(4-x)&0x001F);\ rgb_out = ((rgb_out&0x7c00)>>10)|((rgb_out&0x03e0))|((rgb_out&0x001f)<<10);\ - rgb_out = snes.color_lookup_table[rgb_out];\ + rgb_out = colortable[rgb_out];\ } else {\ rgb_out = raw_ << x;\ }\ diff --git a/src/snes/video/ntsc/snes_ntsc_config.h b/src/lib/libfilter/snes_ntsc/snes_ntsc_config.h similarity index 93% rename from src/snes/video/ntsc/snes_ntsc_config.h rename to src/lib/libfilter/snes_ntsc/snes_ntsc_config.h index 58cc92c1..5c85e26b 100644 --- a/src/snes/video/ntsc/snes_ntsc_config.h +++ b/src/lib/libfilter/snes_ntsc/snes_ntsc_config.h @@ -11,7 +11,7 @@ handle things however it wants. */ /* Bits per pixel of output. Can be 15, 16, 32, or 24 (same as 32). */ -#define SNES_NTSC_OUT_DEPTH 16 +#define SNES_NTSC_OUT_DEPTH 32 /* Type of input pixel values */ #define SNES_NTSC_IN_T unsigned short diff --git a/src/snes/video/ntsc/snes_ntsc_impl.h b/src/lib/libfilter/snes_ntsc/snes_ntsc_impl.h similarity index 100% rename from src/snes/video/ntsc/snes_ntsc_impl.h rename to src/lib/libfilter/snes_ntsc/snes_ntsc_impl.h diff --git a/src/lib/nall/base64.hpp b/src/lib/nall/base64.hpp new file mode 100644 index 00000000..a4f3d07e --- /dev/null +++ b/src/lib/nall/base64.hpp @@ -0,0 +1,94 @@ +#ifndef NALL_BASE64_HPP +#define NALL_BASE64_HPP + +#include +#include + +#include + +namespace nall { + +class base64 { +public: + static bool encode(char *&output, const uint8_t* input, unsigned inlength) { + output = new(zeromemory) char[inlength * 8 / 6 + 6]; + + unsigned i = 0, o = 0; + while(i < inlength) { + switch(i % 3) { + case 0: { + output[o++] = enc(input[i] >> 2); + output[o] = enc((input[i] & 3) << 4); + } break; + + case 1: { + uint8_t prev = dec(output[o]); + output[o++] = enc(prev + (input[i] >> 4)); + output[o] = enc((input[i] & 15) << 2); + } break; + + case 2: { + uint8_t prev = dec(output[o]); + output[o++] = enc(prev + (input[i] >> 6)); + output[o++] = enc(input[i] & 63); + } break; + } + + i++; + } + + return true; + } + + static bool decode(uint8_t *&output, unsigned &outlength, const char *input) { + unsigned inlength = strlen(input), infix = 0; + output = new(zeromemory) uint8_t[inlength]; + + unsigned i = 0, o = 0; + while(i < inlength) { + uint8_t x = dec(input[i]); + + switch(i++ & 3) { + case 0: { + output[o] = x << 2; + } break; + + case 1: { + output[o++] |= x >> 4; + output[o] = (x & 15) << 4; + } break; + + case 2: { + output[o++] |= x >> 2; + output[o] = (x & 3) << 6; + } break; + + case 3: { + output[o++] |= x; + } break; + } + } + + outlength = o; + return true; + } + +private: + static char enc(uint8_t n) { + static char lookup_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; + return lookup_table[n & 63]; + } + + static uint8_t dec(char n) { + if(n >= 'A' && n <= 'Z') return n - 'A'; + if(n >= 'a' && n <= 'z') return n - 'a' + 26; + if(n >= '0' && n <= '9') return n - '0' + 52; + if(n == '-') return 62; + if(n == '_') return 63; + return 0; + } +}; + +} //namespace nall + +#endif //ifndef NALL_BASE64_HPP diff --git a/src/lib/nall/detect.hpp b/src/lib/nall/detect.hpp new file mode 100644 index 00000000..c196237f --- /dev/null +++ b/src/lib/nall/detect.hpp @@ -0,0 +1,30 @@ +#ifndef NALL_DETECT_HPP +#define NALL_DETECT_HPP + +/* Compiler detection */ + +#if defined(__GNUC__) + #define COMPILER_GCC +#elif defined(_MSC_VER) + #define COMPILER_VISUALC +#endif + +/* Platform detection */ + +#if defined(_WIN32) + #define PLATFORM_WIN +#elif defined(__APPLE__) + #define PLATFORM_OSX +#elif defined(linux) || defined(__sun__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) + #define PLATFORM_X +#endif + +/* Endian detection */ + +#if defined(__i386__) || defined(__amd64__) || defined(_M_IX86) || defined(_M_AMD64) + #define ARCH_LSB +#elif defined(__powerpc__) || defined(_M_PPC) + #define ARCH_MSB +#endif + +#endif //ifndef NALL_DETECT_HPP diff --git a/src/lib/nall/lzss.hpp b/src/lib/nall/lzss.hpp new file mode 100644 index 00000000..0073460c --- /dev/null +++ b/src/lib/nall/lzss.hpp @@ -0,0 +1,83 @@ +#ifndef NALL_LZSS_HPP +#define NALL_LZSS_HPP + +#include +#include +#include + +namespace nall { + +class lzss { +public: + static bool encode(uint8_t *&output, unsigned &outlength, const uint8_t *input, unsigned inlength) { + output = new(zeromemory) uint8_t[inlength * 9 / 8 + 9]; + + unsigned i = 0, o = 0; + while(i < inlength) { + unsigned flagoffset = o++; + uint8_t flag = 0x00; + + for(unsigned b = 0; b < 8 && i < inlength; b++) { + unsigned longest = 0, pointer; + for(unsigned index = 1; index < 4096; index++) { + unsigned count = 0; + while(true) { + if(count >= 15 + 3) break; //verify pattern match is not longer than max length + if(i + count >= inlength) break; //verify pattern match does not read past end of input + if(i + count < index) break; //verify read is not before start of input + if(input[i + count] != input[i + count - index]) break; //verify pattern still matches + count++; + } + + if(count > longest) { + longest = count; + pointer = index; + } + } + + if(longest < 3) output[o++] = input[i++]; + else { + flag |= 1 << b; + uint16_t x = ((longest - 3) << 12) + pointer; + output[o++] = x; + output[o++] = x >> 8; + i += longest; + } + } + + output[flagoffset] = flag; + } + + outlength = o; + return true; + } + + static bool decode(uint8_t *&output, const uint8_t *input, unsigned length) { + output = new(zeromemory) uint8_t[length]; + + unsigned i = 0, o = 0; + while(o < length) { + uint8_t flag = input[i++]; + + for(unsigned b = 0; b < 8 && o < length; b++) { + if(!(flag & (1 << b))) output[o++] = input[i++]; + else { + uint16_t offset = input[i++]; + offset += input[i++] << 8; + uint16_t lookuplength = (offset >> 12) + 3; + offset &= 4095; + for(unsigned index = 0; index < lookuplength && o + index < length; index++) { + output[o + index] = output[o + index - offset]; + } + o += lookuplength; + } + } + } + + return true; + } +}; + +} //namespace nall + +#endif //ifndef NALL_LZSS_HPP diff --git a/src/lib/nall/string.cpp b/src/lib/nall/string.cpp index bdfd33a8..508371e1 100644 --- a/src/lib/nall/string.cpp +++ b/src/lib/nall/string.cpp @@ -1,8 +1,11 @@ #ifndef NALL_STRING_CPP #define NALL_STRING_CPP -#include +#include +#include +#include +#include #include #include #include diff --git a/src/lib/nall/string/class.cpp b/src/lib/nall/string/class.cpp index 000d8fc2..8c167c9c 100644 --- a/src/lib/nall/string/class.cpp +++ b/src/lib/nall/string/class.cpp @@ -1,5 +1,4 @@ -#ifndef NALL_STRING_CLASS_CPP -#define NALL_STRING_CLASS_CPP +#ifdef NALL_STRING_CPP size_t count(nall::lstring &str) { return str.size(); @@ -220,4 +219,4 @@ char *fdata = (char*)malloc(size + 1); return true; } -#endif //ifndef NALL_STRING_CLASS_CPP +#endif //ifdef NALL_STRING_CPP diff --git a/src/lib/nall/string/compare.cpp b/src/lib/nall/string/compare.cpp index f7155a3d..72a63667 100644 --- a/src/lib/nall/string/compare.cpp +++ b/src/lib/nall/string/compare.cpp @@ -1,5 +1,4 @@ -#ifndef NALL_STRING_COMPARE_CPP -#define NALL_STRING_COMPARE_CPP +#ifdef NALL_STRING_CPP char chrlower(char c) { return (c >= 'A' && c <= 'Z') ? c + ('a' - 'A') : c; @@ -97,4 +96,4 @@ bool striend(const char *str, const char *key) { return true; } -#endif //ifndef NALL_STRING_COMPARE_CPP +#endif //ifdef NALL_STRING_CPP diff --git a/src/lib/nall/string/convert.cpp b/src/lib/nall/string/convert.cpp index 94a1e175..70551c43 100644 --- a/src/lib/nall/string/convert.cpp +++ b/src/lib/nall/string/convert.cpp @@ -1,9 +1,4 @@ -#ifndef NALL_STRING_CONVERT_CPP -#define NALL_STRING_CONVERT_CPP - -#include -#include -#include +#ifdef NALL_STRING_CPP char* strlower(char *str) { if(!str) return 0; @@ -290,4 +285,4 @@ size_t strdouble(char *str, double value, size_t length /* = 0 */) { return nall::min(initial_length, digits + 1); } -#endif //ifndef NALL_STRING_CONVERT_CPP +#endif //ifdef NALL_STRING_CPP diff --git a/src/lib/nall/string/match.cpp b/src/lib/nall/string/match.cpp index 43df1e36..7cee520e 100644 --- a/src/lib/nall/string/match.cpp +++ b/src/lib/nall/string/match.cpp @@ -1,5 +1,4 @@ -#ifndef NALL_MATCH_CPP -#define NALL_MATCH_CPP +#ifdef NALL_STRING_CPP bool match(const char *p, const char *s) { const char *p_ = 0, *s_ = 0; @@ -69,4 +68,4 @@ bool match(const char *p, const char *s) { } } -#endif //ifndef NALL_MATCH_CPP +#endif //ifdef NALL_STRING_CPP diff --git a/src/lib/nall/string/math.cpp b/src/lib/nall/string/math.cpp index 7a7249db..812ebd0d 100644 --- a/src/lib/nall/string/math.cpp +++ b/src/lib/nall/string/math.cpp @@ -1,5 +1,4 @@ -#ifndef NALL_MATH_CPP -#define NALL_MATH_CPP +#ifdef NALL_STRING_CPP static int eval_integer(const char *&s) { if(!*s) throw "nall::bad_eval_integer"; @@ -147,4 +146,4 @@ bool strmath(const char *s, int &result) { } } -#endif //ifndef NALL_MATH_CPP +#endif //ifdef NALL_STRING_CPP diff --git a/src/lib/nall/string/replace.cpp b/src/lib/nall/string/replace.cpp index f6f28a1a..9474c8b0 100644 --- a/src/lib/nall/string/replace.cpp +++ b/src/lib/nall/string/replace.cpp @@ -1,5 +1,4 @@ -#ifndef NALL_STRING_REPLACE_CPP -#define NALL_STRING_REPLACE_CPP +#ifdef NALL_STRING_CPP nall::string &replace(nall::string &str, const char *key, const char *token) { int i, z, ksl = strlen(key), tsl = strlen(token), ssl = strlen(str); @@ -88,4 +87,4 @@ nall::string &qreplace(nall::string &str, const char *key, const char *token) { return str; } -#endif //ifndef NALL_STRING_REPLACE_CPP +#endif //ifdef NALL_STRING_CPP diff --git a/src/lib/nall/string/split.cpp b/src/lib/nall/string/split.cpp index 29dd246b..ebcd9b53 100644 --- a/src/lib/nall/string/split.cpp +++ b/src/lib/nall/string/split.cpp @@ -1,5 +1,4 @@ -#ifndef NALL_STRING_SPLIT_CPP -#define NALL_STRING_SPLIT_CPP +#ifdef NALL_STRING_CPP void split(nall::lstring &dest, const char *key, const char *src, size_t limit) { dest.reset(); @@ -49,4 +48,4 @@ void qsplit(nall::lstring &dest, const char *key, const char *src, size_t limit) strcpy(dest[split_count++], src + lp); } -#endif //ifndef NALL_STRING_SPLIT_CPP +#endif //ifdef NALL_STRING_CPP diff --git a/src/lib/nall/string/strl.cpp b/src/lib/nall/string/strl.cpp index d7efa354..5710b9f2 100644 --- a/src/lib/nall/string/strl.cpp +++ b/src/lib/nall/string/strl.cpp @@ -1,5 +1,4 @@ -#ifndef NALL_STRING_STRL_CPP -#define NALL_STRING_STRL_CPP +#ifdef NALL_STRING_CPP //strlcpy, strlcat based on OpenBSD implementation by Todd C. Miller @@ -45,4 +44,4 @@ size_t strlcat(char *dest, const char *src, size_t length) { return dlength + (s - src); //return length of resulting string, sans null terminator } -#endif //ifndef NALL_STRING_STRL_CPP +#endif //ifdef NALL_STRING_CPP diff --git a/src/lib/nall/string/trim.cpp b/src/lib/nall/string/trim.cpp index 087f4743..bef5d166 100644 --- a/src/lib/nall/string/trim.cpp +++ b/src/lib/nall/string/trim.cpp @@ -1,5 +1,4 @@ -#ifndef NALL_STRING_TRIM_CPP -#define NALL_STRING_TRIM_CPP +#ifdef NALL_STRING_CPP char* ltrim(char *str, const char *key) { if(!key || !*key) return str; @@ -33,4 +32,4 @@ char* trim_once(char *str, const char *key) { return ltrim_once(rtrim_once(str, key), key); } -#endif //ifndef NALL_STRING_TRIM_CPP +#endif //ifdef NALL_STRING_CPP diff --git a/src/lib/ruby/audio/openal.cpp b/src/lib/ruby/audio/openal.cpp index 4aad7c42..28067b44 100644 --- a/src/lib/ruby/audio/openal.cpp +++ b/src/lib/ruby/audio/openal.cpp @@ -1,5 +1,5 @@ #include -#include +#include #include @@ -172,13 +172,10 @@ public: settings.synchronize = true; settings.frequency = 22050; - - alutInit(0, NULL); } ~pAudioOpenAL() { term(); - alutExit(); } }; diff --git a/src/lib/ruby/input/directinput.cpp b/src/lib/ruby/input/directinput.cpp index 285f4e57..f655446b 100644 --- a/src/lib/ruby/input/directinput.cpp +++ b/src/lib/ruby/input/directinput.cpp @@ -1,5 +1,4 @@ #include -#define WIN32_LEAN_AND_MEAN #include #define DIRECTINPUT_VERSION 0x0800 diff --git a/src/lib/ruby/input/sdl.cpp b/src/lib/ruby/input/sdl.cpp index de22c515..387ba997 100644 --- a/src/lib/ruby/input/sdl.cpp +++ b/src/lib/ruby/input/sdl.cpp @@ -25,9 +25,6 @@ #include #include #include -#include -#include -#include #endif #include diff --git a/src/lib/ruby/input/x.cpp b/src/lib/ruby/input/x.cpp index e45f15ee..cd259b7a 100644 --- a/src/lib/ruby/input/x.cpp +++ b/src/lib/ruby/input/x.cpp @@ -3,9 +3,6 @@ #include #include #include -#include -#include -#include #include diff --git a/src/lib/ruby/ruby.cpp b/src/lib/ruby/ruby.cpp index ac0366e6..679003bd 100644 --- a/src/lib/ruby/ruby.cpp +++ b/src/lib/ruby/ruby.cpp @@ -1,9 +1,8 @@ #include +#include namespace ruby { -#include - VideoInterface video; AudioInterface audio; InputInterface input; @@ -73,7 +72,7 @@ void VideoInterface::term() { bool VideoInterface::cap(Video::Setting setting) { return p ? p->cap(setting) : false; } uintptr_t VideoInterface::get(Video::Setting setting) { return p ? p->get(setting) : false; } bool VideoInterface::set(Video::Setting setting, uintptr_t param) { return p ? p->set(setting, param) : false; } -bool VideoInterface::lock(uint16_t *&data, unsigned &pitch) { return p ? p->lock(data, pitch) : false; } +bool VideoInterface::lock(uint32_t *&data, unsigned &pitch) { return p ? p->lock(data, pitch) : false; } void VideoInterface::unlock() { if(p) p->unlock(); } void VideoInterface::clear() { if(p) p->clear(); } void VideoInterface::refresh(unsigned width, unsigned height) { if(p) p->refresh(width, height); } diff --git a/src/lib/ruby/ruby.h b/src/lib/ruby/ruby.h index ff401fcb..075ed44f 100644 --- a/src/lib/ruby/ruby.h +++ b/src/lib/ruby/ruby.h @@ -30,7 +30,7 @@ public: bool cap(Video::Setting setting); uintptr_t get(Video::Setting setting); bool set(Video::Setting setting, uintptr_t param); - bool lock(uint16_t *&data, unsigned &pitch); + bool lock(uint32_t *&data, unsigned &pitch); void unlock(); void clear(); void refresh(unsigned width, unsigned height); diff --git a/src/lib/ruby/ruby.impl.h b/src/lib/ruby/ruby.impl.h deleted file mode 100644 index e5370168..00000000 --- a/src/lib/ruby/ruby.impl.h +++ /dev/null @@ -1,57 +0,0 @@ -/* Video */ - -#ifdef VIDEO_DIRECT3D - #include -#endif - -#ifdef VIDEO_DIRECTDRAW - #include -#endif - -#ifdef VIDEO_GDI - #include -#endif - -#ifdef VIDEO_GLX - #include -#endif - -#ifdef VIDEO_SDL - #include -#endif - -#ifdef VIDEO_XV - #include -#endif - -/* Audio */ - -#ifdef AUDIO_AO - #include -#endif - -#ifdef AUDIO_DIRECTSOUND - #include -#endif - -#ifdef AUDIO_OPENAL - #include -#endif - -#ifdef AUDIO_OSS - #include -#endif - -/* Input */ - -#ifdef INPUT_DIRECTINPUT - #include -#endif - -#ifdef INPUT_SDL - #include -#endif - -#ifdef INPUT_X - #include -#endif diff --git a/src/lib/ruby/ruby_impl.cpp b/src/lib/ruby/ruby_impl.cpp new file mode 100644 index 00000000..aa272076 --- /dev/null +++ b/src/lib/ruby/ruby_impl.cpp @@ -0,0 +1,57 @@ +/* Video */ + +#ifdef VIDEO_DIRECT3D + #include +#endif + +#ifdef VIDEO_DIRECTDRAW + #include +#endif + +#ifdef VIDEO_GDI + #include +#endif + +#ifdef VIDEO_GLX + #include +#endif + +#ifdef VIDEO_SDL + #include +#endif + +#ifdef VIDEO_XV + #include +#endif + +/* Audio */ + +#ifdef AUDIO_AO + #include +#endif + +#ifdef AUDIO_DIRECTSOUND + #include +#endif + +#ifdef AUDIO_OPENAL + #include +#endif + +#ifdef AUDIO_OSS + #include +#endif + +/* Input */ + +#ifdef INPUT_DIRECTINPUT + #include +#endif + +#ifdef INPUT_SDL + #include +#endif + +#ifdef INPUT_X + #include +#endif diff --git a/src/lib/ruby/video.h b/src/lib/ruby/video.h index 091e3e20..c23a54bb 100644 --- a/src/lib/ruby/video.h +++ b/src/lib/ruby/video.h @@ -15,7 +15,7 @@ public: virtual uintptr_t get(Setting) { return false; } virtual bool set(Setting, uintptr_t) { return false; } - virtual bool lock(uint16_t *&data, unsigned &pitch) { return false; } + virtual bool lock(uint32_t *&data, unsigned &pitch) { return false; } virtual void unlock() {} virtual void clear() {} diff --git a/src/lib/ruby/video/direct3d.cpp b/src/lib/ruby/video/direct3d.cpp index a0656ecc..75d1e828 100644 --- a/src/lib/ruby/video/direct3d.cpp +++ b/src/lib/ruby/video/direct3d.cpp @@ -1,4 +1,3 @@ -#define WIN32_LEAN_AND_MEAN #include #include @@ -109,7 +108,7 @@ public: uint32_t px, uint32_t py, uint32_t pw, uint32_t ph, uint32_t tw, uint32_t th, uint32_t x, uint32_t y, uint32_t w, uint32_t h - ){ + ) { d3dvertex vertex[4]; vertex[0].x = vertex[2].x = (double)(x ) - 0.5; vertex[1].x = vertex[3].x = (double)(x + w) - 0.5; @@ -157,14 +156,14 @@ public: } } - bool lock(uint16_t *&data, unsigned &pitch) { + bool lock(uint32_t *&data, unsigned &pitch) { if(caps.stretchrect == false) { texture->GetLevelDesc(0, &d3dsd); texture->GetSurfaceLevel(0, &surface); } surface->LockRect(&d3dlr, 0, flags.lock); pitch = d3dlr.Pitch; - return data = (uint16_t*)d3dlr.pBits; + return data = (uint32_t*)d3dlr.pBits; } void unlock() { @@ -279,10 +278,10 @@ public: device->SetFVF(D3DVERTEX); if(caps.stretchrect == true) { - device->CreateOffscreenPlainSurface(1024, 1024, D3DFMT_R5G6B5, + device->CreateOffscreenPlainSurface(1024, 1024, D3DFMT_X8R8G8B8, D3DPOOL_DEFAULT, &surface, NULL); } else { - device->CreateTexture(1024, 1024, 1, flags.t_usage, D3DFMT_R5G6B5, + device->CreateTexture(1024, 1024, 1, flags.t_usage, D3DFMT_X8R8G8B8, static_cast(flags.t_pool), &texture, NULL); } @@ -318,7 +317,7 @@ public: bool VideoD3D::cap(Setting setting) { return p.cap(setting); } uintptr_t VideoD3D::get(Setting setting) { return p.get(setting); } bool VideoD3D::set(Setting setting, uintptr_t param) { return p.set(setting, param); } -bool VideoD3D::lock(uint16_t *&data, unsigned &pitch) { return p.lock(data, pitch); } +bool VideoD3D::lock(uint32_t *&data, unsigned &pitch) { return p.lock(data, pitch); } void VideoD3D::unlock() { p.unlock(); } void VideoD3D::clear() { p.clear(); } void VideoD3D::refresh(unsigned width, unsigned height) { p.refresh(width, height); } diff --git a/src/lib/ruby/video/direct3d.h b/src/lib/ruby/video/direct3d.h index 17c2d66a..9f2fd063 100644 --- a/src/lib/ruby/video/direct3d.h +++ b/src/lib/ruby/video/direct3d.h @@ -6,7 +6,7 @@ public: uintptr_t get(Setting); bool set(Setting, uintptr_t); - bool lock(uint16_t *&data, unsigned &pitch); + bool lock(uint32_t *&data, unsigned &pitch); void unlock(); void clear(); diff --git a/src/lib/ruby/video/directdraw.cpp b/src/lib/ruby/video/directdraw.cpp index 4d021d1e..d7d2f88f 100644 --- a/src/lib/ruby/video/directdraw.cpp +++ b/src/lib/ruby/video/directdraw.cpp @@ -1,4 +1,3 @@ -#define WIN32_LEAN_AND_MEAN #include #include @@ -49,17 +48,17 @@ public: } void clear() { - DDBLTFX fx; + DDBLTFX fx; fx.dwSize = sizeof(DDBLTFX); fx.dwFillColor = 0x00000000; screen->Blt(0, 0, 0, DDBLT_WAIT | DDBLT_COLORFILL, &fx); raster->Blt(0, 0, 0, DDBLT_WAIT | DDBLT_COLORFILL, &fx); } - bool lock(uint16_t *&data, unsigned &pitch) { + bool lock(uint32_t *&data, unsigned &pitch) { if(raster->Lock(0, &ddsd, DDLOCK_WAIT, 0) != DD_OK) return false; pitch = ddsd.lPitch; - return data = (uint16_t*)ddsd.lpSurface; + return data = (uint32_t*)ddsd.lpSurface; } void unlock() { @@ -69,18 +68,18 @@ public: void refresh(unsigned r_width, unsigned r_height) { if(settings.synchronize) { for(;;) { - BOOL in_vblank; + BOOL in_vblank; lpdd7->GetVerticalBlankStatus(&in_vblank); if(bool(in_vblank) == true) break; //Sleep(1); } } - HRESULT hr; - RECT rd, rs; + HRESULT hr; + RECT rd, rs; SetRect(&rs, 0, 0, r_width, r_height); - POINT p = { 0, 0 }; + POINT p = { 0, 0 }; ClientToScreen(settings.handle, &p); GetClientRect(settings.handle, &rd); OffsetRect(&rd, p.x, p.y); @@ -117,10 +116,9 @@ public: } void create_raster() { - int depth; screen->GetSurfaceDesc(&ddsd); - depth = ddsd.ddpfPixelFormat.dwRGBBitCount; - if(depth == 15 || depth == 16) goto try_native_surface; + int depth = ddsd.ddpfPixelFormat.dwRGBBitCount; + if(depth == 32) goto try_native_surface; memset(&ddsd, 0, sizeof(DDSURFACEDESC2)); ddsd.dwSize = sizeof(DDSURFACEDESC2); @@ -131,14 +129,14 @@ public: ddsd.ddpfPixelFormat.dwSize = sizeof(DDPIXELFORMAT); ddsd.ddpfPixelFormat.dwFlags = DDPF_RGB; - ddsd.ddpfPixelFormat.dwRGBBitCount = 16; - ddsd.ddpfPixelFormat.dwRBitMask = 0xf800; - ddsd.ddpfPixelFormat.dwGBitMask = 0x07e0; - ddsd.ddpfPixelFormat.dwBBitMask = 0x001f; + ddsd.ddpfPixelFormat.dwRGBBitCount = 32; + ddsd.ddpfPixelFormat.dwRBitMask = 0xff0000; + ddsd.ddpfPixelFormat.dwGBitMask = 0x00ff00; + ddsd.ddpfPixelFormat.dwBBitMask = 0x0000ff; if(lpdd7->CreateSurface(&ddsd, &raster, 0) == DD_OK) return; - try_native_surface: + try_native_surface: memset(&ddsd, 0, sizeof(DDSURFACEDESC2)); ddsd.dwSize = sizeof(DDSURFACEDESC2); ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT; @@ -146,7 +144,7 @@ public: ddsd.dwWidth = 1024; ddsd.dwHeight = 1024; - lpdd7->CreateSurface(&ddsd, &raster, 0); + if(lpdd7->CreateSurface(&ddsd, &raster, 0) == DD_OK) return; } void term() { @@ -171,7 +169,7 @@ public: bool VideoDD::cap(Setting setting) { return p.cap(setting); } uintptr_t VideoDD::get(Setting setting) { return p.get(setting); } bool VideoDD::set(Setting setting, uintptr_t param) { return p.set(setting, param); } -bool VideoDD::lock(uint16_t *&data, unsigned &pitch) { return p.lock(data, pitch); } +bool VideoDD::lock(uint32_t *&data, unsigned &pitch) { return p.lock(data, pitch); } void VideoDD::unlock() { p.unlock(); } void VideoDD::clear() { p.clear(); } void VideoDD::refresh(unsigned width, unsigned height) { p.refresh(width, height); } diff --git a/src/lib/ruby/video/directdraw.h b/src/lib/ruby/video/directdraw.h index f202121b..e4e8eb9d 100644 --- a/src/lib/ruby/video/directdraw.h +++ b/src/lib/ruby/video/directdraw.h @@ -6,7 +6,7 @@ public: uintptr_t get(Setting); bool set(Setting, uintptr_t); - bool lock(uint16_t *&data, unsigned &pitch); + bool lock(uint32_t *&data, unsigned &pitch); void unlock(); void clear(); diff --git a/src/lib/ruby/video/gdi.cpp b/src/lib/ruby/video/gdi.cpp index 4c827a27..7a29c9c1 100644 --- a/src/lib/ruby/video/gdi.cpp +++ b/src/lib/ruby/video/gdi.cpp @@ -1,5 +1,4 @@ #include -#define WIN32_LEAN_AND_MEAN #include #include @@ -12,7 +11,7 @@ class pVideoGDI { public: VideoGDI &self; - uint16_t *buffer; + uint32_t *buffer; HBITMAP bitmap; HDC bitmapdc; BITMAPINFO bmi; @@ -39,8 +38,8 @@ public: return false; } - bool lock(uint16_t *&data, unsigned &pitch) { - pitch = 2048; + bool lock(uint32_t *&data, unsigned &pitch) { + pitch = 1024 * 4; return data = buffer; } @@ -70,9 +69,9 @@ public: bmi.bmiHeader.biWidth = 1024; bmi.bmiHeader.biHeight = -1024; bmi.bmiHeader.biPlanes = 1; - bmi.bmiHeader.biBitCount = 16; //biBitCount of 15 is invalid, biBitCount of 16 is really RGB555 + bmi.bmiHeader.biBitCount = 32; //biBitCount of 15 is invalid, biBitCount of 16 is really RGB555 bmi.bmiHeader.biCompression = BI_RGB; - bmi.bmiHeader.biSizeImage = 1024 * 1024 * sizeof(uint16_t); + bmi.bmiHeader.biSizeImage = 1024 * 1024 * sizeof(uint32_t); return true; } @@ -83,7 +82,7 @@ public: } pVideoGDI(VideoGDI &self_) : self(self_) { - buffer = (uint16_t*)malloc(1024 * 1024 * sizeof(uint16_t)); + buffer = (uint32_t*)malloc(1024 * 1024 * sizeof(uint32_t)); settings.handle = 0; } @@ -95,7 +94,7 @@ public: bool VideoGDI::cap(Setting setting) { return p.cap(setting); } uintptr_t VideoGDI::get(Setting setting) { return p.get(setting); } bool VideoGDI::set(Setting setting, uintptr_t param) { return p.set(setting, param); } -bool VideoGDI::lock(uint16_t *&data, unsigned &pitch) { return p.lock(data, pitch); } +bool VideoGDI::lock(uint32_t *&data, unsigned &pitch) { return p.lock(data, pitch); } void VideoGDI::unlock() { p.unlock(); } void VideoGDI::refresh(unsigned width, unsigned height) { p.refresh(width, height); } bool VideoGDI::init() { return p.init(); } diff --git a/src/lib/ruby/video/gdi.h b/src/lib/ruby/video/gdi.h index 7a2c5735..e701f70a 100644 --- a/src/lib/ruby/video/gdi.h +++ b/src/lib/ruby/video/gdi.h @@ -6,7 +6,7 @@ public: uintptr_t get(Setting); bool set(Setting, uintptr_t); - bool lock(uint16_t *&data, unsigned &pitch); + bool lock(uint32_t *&data, unsigned &pitch); void unlock(); void refresh(unsigned width, unsigned height); diff --git a/src/lib/ruby/video/glx.cpp b/src/lib/ruby/video/glx.cpp index 85961eeb..9aea321b 100644 --- a/src/lib/ruby/video/glx.cpp +++ b/src/lib/ruby/video/glx.cpp @@ -40,7 +40,7 @@ static Bool x_wait_for_map_notify(Display *d, XEvent *e, char *arg) { class pVideoGLX { public: VideoGLX &self; - uint16_t *buffer; + uint32_t *buffer; Display *display; int screen; @@ -84,8 +84,8 @@ public: return false; } - bool lock(uint16_t *&data, unsigned &pitch) { - pitch = 1024 * 2; + bool lock(uint32_t *&data, unsigned &pitch) { + pitch = 1024 * 4; return data = buffer; } @@ -93,7 +93,7 @@ public: } void clear() { - memset(buffer, 0, 1024 * 1024 * sizeof(uint16_t)); + memset(buffer, 0, 1024 * 1024 * sizeof(uint32_t)); glClearColor(0.0, 0.0, 0.0, 1.0); glClear(GL_COLOR_BUFFER_BIT); glFlush(); @@ -129,7 +129,7 @@ public: glPixelStorei(GL_UNPACK_ROW_LENGTH, 1024); //length of buffer in pixels glTexSubImage2D(GL_TEXTURE_2D, /* mip-map level = */ 0, /* x = */ 0, /* y = */ 0, - width, height, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, buffer); + width, height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, buffer); //OpenGL projection sets 0,0 as *bottom-left* of screen. //therefore, below vertices flip image to support top-left source. @@ -157,7 +157,7 @@ public: //require GLX 1.2+ API if(glx.version_major < 1 || (glx.version_major == 1 && glx.version_minor < 2)) return false; - buffer = new(zeromemory) uint16_t[1024 * 1024 * sizeof(uint16_t)]; + buffer = new(zeromemory) uint32_t[1024 * 1024 * sizeof(uint32_t)]; XWindowAttributes wa; XGetWindowAttributes(display, settings.handle, &wa); @@ -166,7 +166,7 @@ public: int elements = 0; int attributelist[] = { GLX_RGBA, - GLX_DOUBLEBUFFER, True, + GLX_DOUBLEBUFFER, None }; XVisualInfo *vi = glXChooseVisual(display, screen, attributelist); @@ -216,7 +216,7 @@ public: glTexImage2D(GL_TEXTURE_2D, /* mip-map level = */ 0, /* internal format = */ GL_RGB, /* width = */ 1024, /* height = */ 1024, /* border = */ 0, - /* format = */ GL_RGB, GL_UNSIGNED_SHORT_5_6_5, buffer); + /* format = */ GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, buffer); return true; } @@ -252,7 +252,7 @@ public: bool VideoGLX::cap(Setting setting) { return p.cap(setting); } uintptr_t VideoGLX::get(Setting setting) { return p.get(setting); } bool VideoGLX::set(Setting setting, uintptr_t param) { return p.set(setting, param); } -bool VideoGLX::lock(uint16_t *&data, unsigned &pitch) { return p.lock(data, pitch); } +bool VideoGLX::lock(uint32_t *&data, unsigned &pitch) { return p.lock(data, pitch); } void VideoGLX::unlock() { p.unlock(); } void VideoGLX::clear() { p.clear(); } void VideoGLX::refresh(unsigned width, unsigned height) { p.refresh(width, height); } diff --git a/src/lib/ruby/video/glx.h b/src/lib/ruby/video/glx.h index 444f60e6..f80b20ff 100644 --- a/src/lib/ruby/video/glx.h +++ b/src/lib/ruby/video/glx.h @@ -6,7 +6,7 @@ public: uintptr_t get(Setting); bool set(Setting, uintptr_t); - bool lock(uint16_t *&data, unsigned &pitch); + bool lock(uint32_t *&data, unsigned &pitch); void unlock(); void clear(); diff --git a/src/lib/ruby/video/sdl.cpp b/src/lib/ruby/video/sdl.cpp index ab13c878..52734570 100644 --- a/src/lib/ruby/video/sdl.cpp +++ b/src/lib/ruby/video/sdl.cpp @@ -42,10 +42,10 @@ public: return false; } - bool lock(uint16_t *&data, unsigned &pitch) { + bool lock(uint32_t *&data, unsigned &pitch) { if(SDL_MUSTLOCK(buffer)) SDL_LockSurface(buffer); pitch = buffer->pitch; - return data = (uint16_t*)buffer->pixels; + return data = (uint32_t*)buffer->pixels; } void unlock() { @@ -81,10 +81,10 @@ public: sprintf(env, "SDL_WINDOWID=%ld", settings.handle); putenv(env); SDL_InitSubSystem(SDL_INIT_VIDEO); - screen = SDL_SetVideoMode(2560, 1600, 16, SDL_HWSURFACE); + screen = SDL_SetVideoMode(2560, 1600, 32, SDL_HWSURFACE); buffer = SDL_CreateRGBSurface(SDL_HWSURFACE, 1024, 1024, - 16, 0xf800, 0x07e0, 0x001f, 0x0000 + 32, 0x00ff0000, 0x0000ff00, 0x000000ff, 0x00000000 ); return true; } @@ -101,7 +101,7 @@ public: bool VideoSDL::cap(Setting setting) { return p.cap(setting); } uintptr_t VideoSDL::get(Setting setting) { return p.get(setting); } bool VideoSDL::set(Setting setting, uintptr_t param) { return p.set(setting, param); } -bool VideoSDL::lock(uint16_t *&data, unsigned &pitch) { return p.lock(data, pitch); } +bool VideoSDL::lock(uint32_t *&data, unsigned &pitch) { return p.lock(data, pitch); } void VideoSDL::unlock() { p.unlock(); } void VideoSDL::clear() { p.clear(); } void VideoSDL::refresh(unsigned width, unsigned height) { p.refresh(width, height); } diff --git a/src/lib/ruby/video/sdl.h b/src/lib/ruby/video/sdl.h index d3fb9f43..5dcf3506 100644 --- a/src/lib/ruby/video/sdl.h +++ b/src/lib/ruby/video/sdl.h @@ -6,7 +6,7 @@ public: uintptr_t get(Setting); bool set(Setting, uintptr_t); - bool lock(uint16_t *&data, unsigned &pitch); + bool lock(uint32_t *&data, unsigned &pitch); void unlock(); void clear(); diff --git a/src/lib/ruby/video/xv.cpp b/src/lib/ruby/video/xv.cpp index 79809717..83576a9f 100644 --- a/src/lib/ruby/video/xv.cpp +++ b/src/lib/ruby/video/xv.cpp @@ -18,7 +18,7 @@ namespace ruby { class pVideoXv { public: VideoXv &self; - uint16_t *buffer; + uint32_t *buffer; XvImage *xvimage; GC gc; Display *display; @@ -48,8 +48,8 @@ public: return false; } - bool lock(uint16_t *&data, unsigned &pitch) { - pitch = 1024 * 2; + bool lock(uint32_t *&data, unsigned &pitch) { + pitch = 1024 * 4; return data = buffer; } @@ -57,25 +57,24 @@ public: } void clear() { - memset(buffer, 0, 1024 * 1024 * sizeof(uint16_t)); + memset(buffer, 0, 1024 * 1024 * sizeof(uint32_t)); //clear twice in case video is double buffered ... refresh(1024, 1024); refresh(1024, 1024); } - void refresh(unsigned r_width, unsigned r_height) { - Window dw; - int d0, d1; - unsigned d2, d3; - unsigned width, height; - XGetGeometry(display, settings.handle, &dw, &d0, &d1, &width, &height, &d2, &d3); + void refresh(unsigned width, unsigned height) { + XWindowAttributes attributes; + XGetWindowAttributes(display, settings.handle, &attributes); - uint16_t *input = (uint16_t*)buffer; - uint16_t *output = (uint16_t*)xvimage->data; - for(int y = 0; y < r_height; y++) { - for(int x = 0; x < r_width >> 1; x++) { - uint16_t p0 = *input++; - uint16_t p1 = *input++; + uint32_t *input = (uint32_t*)buffer; + uint16_t *output = (uint16_t*)xvimage->data; + for(int y = 0; y < height; y++) { + for(int x = 0; x < width >> 1; x++) { + uint32_t p0 = *input++; + uint32_t p1 = *input++; + p0 = ((p0 >> 8) & 0xf800) + ((p0 >> 5) & 0x07e0) + ((p0 >> 3) & 0x001f); + p1 = ((p1 >> 8) & 0xf800) + ((p1 >> 5) & 0x07e0) + ((p1 >> 3) & 0x001f); uint8_t u = (utable[p0] + utable[p1]) >> 1; uint8_t v = (vtable[p0] + vtable[p1]) >> 1; @@ -83,35 +82,22 @@ public: *output++ = (u << 8) | ytable[p0]; *output++ = (v << 8) | ytable[p1]; } - input += 1024 - r_width; - output += 1024 - r_width; + input += 1024 - width; + output += 1024 - width; } XvShmPutImage(display, xv_port, settings.handle, gc, xvimage, - 0, 0, r_width, r_height, 0, 0, width, height, + 0, 0, attributes.width, attributes.height, true); } bool init() { - buffer = (uint16_t*)malloc(1024 * 1024 * sizeof(uint16_t)); + buffer = (uint32_t*)malloc(1024 * 1024 * sizeof(uint32_t)); display = XOpenDisplay(0); screen = DefaultScreen(display); gc = XCreateGC(display, settings.handle, 0, 0); - XVisualInfo visual_info; - if(XMatchVisualInfo(display, screen, 24, TrueColor, &visual_info)) { - } else if(XMatchVisualInfo(display, screen, 16, TrueColor, &visual_info)) { - } else if(XMatchVisualInfo(display, screen, 15, TrueColor, &visual_info)) { - } else if(XMatchVisualInfo(display, screen, 8, PseudoColor, &visual_info)) { - } else if(XMatchVisualInfo(display, screen, 8, GrayScale, &visual_info)) { - } else if(XMatchVisualInfo(display, screen, 8, StaticGray, &visual_info)) { - } else if(XMatchVisualInfo(display, screen, 1, StaticGray, &visual_info)) { - } else { - fprintf(stderr, "VideoXv: unable to find suitable video display.\n"); - return false; - } - if(!XShmQueryExtension(display)) { fprintf(stderr, "VideoXv: XShm extension not found.\n"); return false; @@ -138,8 +124,7 @@ public: const Atom atom = XInternAtom(display, "XV_AUTOPAINT_COLORKEY", true); if(atom != None) XvSetPortAttribute(display, xv_port, atom, 1); - //0x00000003 = 32-bit X8R8G8B8 [xRGB] (few drivers support this mode) - //0x32595559 = 16-bit Y8U8,Y8V8 [YUY2] (most drivers support this mode) + //0x32595559 = 16-bit Y8U8,Y8V8 (YUY2) xvimage = XvShmCreateImage(display, xv_port, 0x32595559, 0, 1024, 1024, &shminfo); if(!xvimage) { fprintf(stderr, "VideoXv: XShmCreateImage failed.\n"); @@ -202,7 +187,7 @@ public: bool VideoXv::cap(Setting setting) { return p.cap(setting); } uintptr_t VideoXv::get(Setting setting) { return p.get(setting); } bool VideoXv::set(Setting setting, uintptr_t param) { return p.set(setting, param); } -bool VideoXv::lock(uint16_t *&data, unsigned &pitch) { return p.lock(data, pitch); } +bool VideoXv::lock(uint32_t *&data, unsigned &pitch) { return p.lock(data, pitch); } void VideoXv::unlock() { p.unlock(); } void VideoXv::clear() { p.clear(); } void VideoXv::refresh(unsigned width, unsigned height) { p.refresh(width, height); } diff --git a/src/lib/ruby/video/xv.h b/src/lib/ruby/video/xv.h index f93b9728..258fe4e4 100644 --- a/src/lib/ruby/video/xv.h +++ b/src/lib/ruby/video/xv.h @@ -6,7 +6,7 @@ public: uintptr_t get(Setting); bool set(Setting, uintptr_t); - bool lock(uint16_t *&data, unsigned &pitch); + bool lock(uint32_t *&data, unsigned &pitch); void unlock(); void clear(); diff --git a/src/memory/memory.cpp b/src/memory/memory.cpp index cdc4e27a..4750d9a2 100644 --- a/src/memory/memory.cpp +++ b/src/memory/memory.cpp @@ -1,4 +1,6 @@ #include "../base.h" +#define MEMORY_CPP + #include "memory_rw.cpp" namespace memory { @@ -16,7 +18,7 @@ uint8 UnmappedMMIO::mmio_read(uint) { return cpu.regs.mdr; } void UnmappedMMIO::mmio_write(uint, uint8) {} void MMIOAccess::map(uint addr, MMIO &access) { -//MMIO: $[00-3f]:[2000-5fff] + //MMIO: $[00-3f]:[2000-5fff] mmio[(addr - 0x2000) & 0x3fff] = &access; } @@ -29,9 +31,9 @@ void MMIOAccess::write(uint addr, uint8 data) { } uint Bus::mirror(uint addr, uint size) { -uint base = 0; + uint base = 0; if(size) { - uint mask = 1 << 23; + uint mask = 1 << 23; while(addr >= size) { while(!(addr & mask)) mask >>= 1; addr -= mask; @@ -62,9 +64,9 @@ void Bus::map( if(access.size() == -1U) return; -uint8 page_lo = addr_lo >> 8; -uint8 page_hi = addr_hi >> 8; -uint index = 0; + uint8 page_lo = addr_lo >> 8; + uint8 page_hi = addr_hi >> 8; + uint index = 0; switch(mode) { case MapDirect: { diff --git a/src/memory/memory.h b/src/memory/memory.h index eb33ff44..9adaa324 100644 --- a/src/memory/memory.h +++ b/src/memory/memory.h @@ -3,7 +3,7 @@ struct Memory { virtual uint8 read(uint addr) = 0; virtual void write(uint addr, uint8 data) = 0; -enum { WRAP_NONE = 0, WRAP_BANK = 1, WRAP_PAGE = 2 }; + enum { WRAP_NONE = 0, WRAP_BANK = 1, WRAP_PAGE = 2 }; virtual uint16 read_word(uint addr, uint wrap = WRAP_NONE); virtual void write_word(uint addr, uint16 data, uint wrap = WRAP_NONE); virtual uint32 read_long(uint addr, uint wrap = WRAP_NONE); @@ -69,26 +69,26 @@ class Bus { public: uint mirror(uint addr, uint size); void map(uint addr, Memory &access, uint offset); -enum MapMode { MapDirect, MapLinear, MapShadow }; + enum MapMode { MapDirect, MapLinear, MapShadow }; void map(MapMode mode, uint8 bank_lo, uint8 bank_hi, uint16 addr_lo, uint16 addr_hi, Memory &access, uint offset = 0, uint size = 0); alwaysinline uint8 read(uint addr) { - #if defined(CHEAT_SYSTEM) + #if defined(CHEAT_SYSTEM) if(cheat.enabled() && cheat.exists(addr)) { - uint8 r; + uint8 r; if(cheat.read(addr, r)) return r; } - #endif + #endif - Page &p = page[addr >> 8]; + Page &p = page[addr >> 8]; return p.access->read(p.offset + addr); } alwaysinline void write(uint addr, uint8 data) { - Page &p = page[addr >> 8]; + Page &p = page[addr >> 8]; return p.access->write(p.offset + addr, data); } diff --git a/src/memory/memory_rw.cpp b/src/memory/memory_rw.cpp index fb132cc1..ad653b0c 100644 --- a/src/memory/memory_rw.cpp +++ b/src/memory/memory_rw.cpp @@ -1,77 +1,81 @@ +#ifdef MEMORY_CPP + uint16 Memory::read_word(uint addr, uint wrap) { -uint16 r; + uint16 r; switch(wrap) { - case WRAP_NONE: - r = read(addr); - r |= read(addr + 1) << 8; - break; - case WRAP_BANK: - r = read(addr); - r |= read((addr & 0xff0000) | ((addr + 1) & 0xffff)) << 8; - break; - case WRAP_PAGE: - r = read(addr); - r |= read((addr & 0xffff00) | ((addr + 1) & 0xff)) << 8; - break; + case WRAP_NONE: { + r = read(addr); + r |= read(addr + 1) << 8; + } break; + case WRAP_BANK: { + r = read(addr); + r |= read((addr & 0xff0000) | ((addr + 1) & 0xffff)) << 8; + } break; + case WRAP_PAGE: { + r = read(addr); + r |= read((addr & 0xffff00) | ((addr + 1) & 0xff)) << 8; + } break; } return r; } void Memory::write_word(uint addr, uint16 data, uint wrap) { switch(wrap) { - case WRAP_NONE: - write(addr, data); - write(addr + 1, data >> 8); - return; - case WRAP_BANK: - write(addr, data); - write((addr & 0xff0000) | ((addr + 1) & 0xffff), data >> 8); - return; - case WRAP_PAGE: - write(addr, data); - write((addr & 0xffff00) | ((addr + 1) & 0xff), data >> 8); - return; + case WRAP_NONE: { + write(addr, data); + write(addr + 1, data >> 8); + } return; + case WRAP_BANK: { + write(addr, data); + write((addr & 0xff0000) | ((addr + 1) & 0xffff), data >> 8); + } return; + case WRAP_PAGE: { + write(addr, data); + write((addr & 0xffff00) | ((addr + 1) & 0xff), data >> 8); + } return; } } uint32 Memory::read_long(uint addr, uint wrap) { -uint32 r; + uint32 r; switch(wrap) { - case WRAP_NONE: - r = read(addr); - r |= read(addr + 1) << 8; - r |= read(addr + 2) << 16; - break; - case WRAP_BANK: - r = read(addr); - r |= read((addr & 0xff0000) | ((addr + 1) & 0xffff)) << 8; - r |= read((addr & 0xff0000) | ((addr + 2) & 0xffff)) << 16; - break; - case WRAP_PAGE: - r = read(addr); - r |= read((addr & 0xffff00) | ((addr + 1) & 0xff)) << 8; - r |= read((addr & 0xffff00) | ((addr + 2) & 0xff)) << 16; - break; + case WRAP_NONE: { + r = read(addr); + r |= read(addr + 1) << 8; + r |= read(addr + 2) << 16; + } break; + case WRAP_BANK: { + r = read(addr); + r |= read((addr & 0xff0000) | ((addr + 1) & 0xffff)) << 8; + r |= read((addr & 0xff0000) | ((addr + 2) & 0xffff)) << 16; + } break; + case WRAP_PAGE: { + r = read(addr); + r |= read((addr & 0xffff00) | ((addr + 1) & 0xff)) << 8; + r |= read((addr & 0xffff00) | ((addr + 2) & 0xff)) << 16; + } break; } return r; } void Memory::write_long(uint addr, uint32 data, uint wrap) { switch(wrap) { - case WRAP_NONE: - write(addr, data); - write(addr + 1, data >> 8); - write(addr + 2, data >> 16); - return; - case WRAP_BANK: - write(addr, data); - write((addr & 0xff0000) | ((addr + 1) & 0xffff), data >> 8); - write((addr & 0xff0000) | ((addr + 2) & 0xffff), data >> 16); - return; - case WRAP_PAGE: - write(addr, data); - write((addr & 0xffff00) | ((addr + 1) & 0xff), data >> 8); - write((addr & 0xffff00) | ((addr + 2) & 0xff), data >> 16); - return; + case WRAP_NONE: { + write(addr, data); + write(addr + 1, data >> 8); + write(addr + 2, data >> 16); + } return; + case WRAP_BANK: { + write(addr, data); + write((addr & 0xff0000) | ((addr + 1) & 0xffff), data >> 8); + write((addr & 0xff0000) | ((addr + 2) & 0xffff), data >> 16); + } return; + case WRAP_PAGE: { + write(addr, data); + write((addr & 0xffff00) | ((addr + 1) & 0xff), data >> 8); + write((addr & 0xffff00) | ((addr + 2) & 0xff), data >> 16); + } return; } } + +#endif //ifdef MEMORY_CPP diff --git a/src/memory/smemory/mapper/chip.cpp b/src/memory/smemory/mapper/chip.cpp index d49eaf39..9c65cba7 100644 --- a/src/memory/smemory/mapper/chip.cpp +++ b/src/memory/smemory/mapper/chip.cpp @@ -1,3 +1,5 @@ +#ifdef SMEMORY_CPP + void sBus::map_cx4() { map(MapDirect, 0x00, 0x3f, 0x6000, 0x7fff, cx4); map(MapDirect, 0x80, 0xbf, 0x6000, 0x7fff, cx4); @@ -48,3 +50,5 @@ void sBus::map_st010() { map(MapDirect, 0x68, 0x6f, 0x0000, 0x0fff, st010); map(MapDirect, 0xe8, 0xef, 0x0000, 0x0fff, st010); } + +#endif //ifdef SMEMORY_CPP diff --git a/src/memory/smemory/mapper/generic.cpp b/src/memory/smemory/mapper/generic.cpp index ee0f2b15..30eb7665 100644 --- a/src/memory/smemory/mapper/generic.cpp +++ b/src/memory/smemory/mapper/generic.cpp @@ -1,3 +1,5 @@ +#ifdef SMEMORY_CPP + void sBus::map_generic() { switch(cartridge.mapper()) { case Cartridge::LoROM: { @@ -81,10 +83,12 @@ void sBus::map_generic_sram() { map(MapLinear, 0x20, 0x3f, 0x6000, 0x7fff, memory::cartram); map(MapLinear, 0xa0, 0xbf, 0x6000, 0x7fff, memory::cartram); -//research shows only games with very large ROM/RAM sizes require MAD-1 memory mapping of SRAM -//otherwise, default to safer, larger SRAM address window -uint16 addr_hi = (memory::cartrom.size() > 0x200000 || memory::cartram.size() > 32 * 1024) ? 0x7fff : 0xffff; + //research shows only games with very large ROM/RAM sizes require MAD-1 memory mapping of SRAM + //otherwise, default to safer, larger SRAM address window + uint16 addr_hi = (memory::cartrom.size() > 0x200000 || memory::cartram.size() > 32 * 1024) ? 0x7fff : 0xffff; map(MapLinear, 0x70, 0x7f, 0x0000, addr_hi, memory::cartram); if(cartridge.info.mapper != Cartridge::LoROM) return; map(MapLinear, 0xf0, 0xff, 0x0000, addr_hi, memory::cartram); } + +#endif //ifdef SMEMORY_CPP diff --git a/src/memory/smemory/mapper/system.cpp b/src/memory/smemory/mapper/system.cpp index e26bf865..ef089f9d 100644 --- a/src/memory/smemory/mapper/system.cpp +++ b/src/memory/smemory/mapper/system.cpp @@ -1,8 +1,10 @@ +#ifdef SMEMORY_CPP + void sBus::map_reset() { - for(uint i = 0x0000; i <= 0xffff; i++) + for(uint32_t i = 0x0000; i <= 0xffff; i++) map(i << 8, memory::memory_unmapped, 0); - for(uint i = 0x2000; i <= 0x5fff; i++) + for(uint16_t i = 0x2000; i <= 0x5fff; i++) memory::mmio.map(i, memory::mmio_unmapped); } @@ -15,3 +17,5 @@ void sBus::map_system() { map(MapLinear, 0x7e, 0x7f, 0x0000, 0xffff, memory::wram); } + +#endif //ifdef SMEMORY_CPP diff --git a/src/memory/smemory/smemory.cpp b/src/memory/smemory/smemory.cpp index 38ffd4b8..411e788a 100644 --- a/src/memory/smemory/smemory.cpp +++ b/src/memory/smemory/smemory.cpp @@ -1,4 +1,6 @@ #include "../../base.h" +#define SMEMORY_CPP + #include "mapper/system.cpp" #include "mapper/generic.cpp" #include "mapper/chip.cpp" @@ -28,7 +30,7 @@ void sBus::load_cart() { if(cartridge.info.obc1) map_obc1(); if(cartridge.info.st010) map_st010(); - snes.set_region(cartridge.region()); + snes.set_region(cartridge.region() == Cartridge::NTSC ? SNES::NTSC : SNES::PAL); is_cart_loaded = true; } diff --git a/src/ppu/bppu/bppu.cpp b/src/ppu/bppu/bppu.cpp index 135420ec..a864d66c 100644 --- a/src/ppu/bppu/bppu.cpp +++ b/src/ppu/bppu/bppu.cpp @@ -1,87 +1,69 @@ -#include "../../base.h" +#include "../../base.h" +#define BPPU_CPP + #include "bppu_mmio.cpp" #include "bppu_render.cpp" -void bPPU::run() {} - void bPPU::scanline() { - line.y = cpu.vcounter(); - line.interlace = cpu.interlace(); - line.interlace_field = cpu.interlace_field(); + line.y = cpu.vcounter(); if(line.y == 0) { - //RTO flag reset + //RTO flag reset regs.time_over = false; regs.range_over = false; } - if(line.y == 1) { - //mosaic reset - for(int bg = BG1; bg <= BG4; bg++) { - regs.bg_y[bg] = 1; - } - + if(line.y == 1) { + //mosaic reset + for(int bg = BG1; bg <= BG4; bg++) regs.bg_y[bg] = 1; regs.mosaic_countdown = regs.mosaic_size + 1; regs.mosaic_countdown--; } else { for(int bg = BG1; bg <= BG4; bg++) { - if(!regs.mosaic_enabled[bg] || !regs.mosaic_countdown) { - regs.bg_y[bg] = line.y; - } - } - - if(!regs.mosaic_countdown) { - regs.mosaic_countdown = regs.mosaic_size + 1; + if(!regs.mosaic_enabled[bg] || !regs.mosaic_countdown) regs.bg_y[bg] = line.y; } + if(!regs.mosaic_countdown) regs.mosaic_countdown = regs.mosaic_size + 1; regs.mosaic_countdown--; } -//note: this should actually occur at V=225,HC=10. -//this is a limitation of the scanline-based renderer. - if(line.y == (!cpu.overscan() ? 225 : 240)) { + //note: this should actually occur at V=225,HC=10. + //this is a limitation of the scanline-based renderer. + if(line.y == (!overscan() ? 225 : 240)) { if(regs.display_disabled == false) { - //OAM address reset + //OAM address reset regs.oam_addr = regs.oam_baseaddr << 1; regs.oam_firstsprite = (regs.oam_priority == false) ? 0 : (regs.oam_addr >> 2) & 127; } } - - if(line.y == 241 && line.interlace_field == 1) { - if(regs.interlace != cpu.interlace()) { - //clear entire frame so that odd scanlines are empty - //this should be handled better, but one blank frame looks - //better than one improperly interlaced frame... - memset(output, 0, 512 * 480 * sizeof(uint16)); - } - //cpu.set_overscan(regs.overscan); - cpu.set_interlace(regs.interlace); - regs.scanlines = (regs.overscan == false) ? 224 : 239; - } } void bPPU::render_scanline() { -#ifdef FAVOR_SPEED -//bypass RTO status flag calculations - if(status.render_output == false)return; -#endif + #ifdef FAST_FRAMESKIP + //bypass RTO status flag calculations + if(status.render_output == false) return; + #endif - if(line.y >= 0 && line.y < (cpu.overscan() ? 240 : 225)) { + if(line.y >= 0 && line.y < (!overscan() ? 225 : 240)) { if(config::ppu.hack.obj_cache == false) { if(line.y != 0) { render_line_oam_rto(); render_line(); } } else { - if(line.y != 0) { - render_line(); - } + if(line.y != 0) render_line(); render_line_oam_rto(); } } } void bPPU::frame() { - PPU::frame(); + PPU::frame(); + + display.field ^= 1; + if(display.field == 0) { + display.interlace = regs.interlace; + regs.scanlines = (regs.overscan == false) ? 224 : 239; + } } void bPPU::power() { @@ -93,25 +75,25 @@ void bPPU::power() { region = snes.region(); -//$2100 + //$2100 regs.display_disabled = 1; regs.display_brightness = 15; -//$2101 + //$2101 regs.oam_basesize = 0; regs.oam_nameselect = 0; regs.oam_tdaddr = 0x0000; -//$2102-$2103 + //$2102-$2103 regs.oam_baseaddr = 0x0000; regs.oam_addr = 0x0000; regs.oam_priority = false; regs.oam_firstsprite = 0; -//$2104 + //$2104 regs.oam_latchdata = 0x00; -//$2105 + //$2105 regs.bg_tilesize[BG1] = 0; regs.bg_tilesize[BG2] = 0; regs.bg_tilesize[BG3] = 0; @@ -119,7 +101,7 @@ void bPPU::power() { regs.bg3_priority = 0; regs.bg_mode = 0; -//$2106 + //$2106 regs.mosaic_size = 0; regs.mosaic_enabled[BG1] = false; regs.mosaic_enabled[BG2] = false; @@ -127,7 +109,7 @@ void bPPU::power() { regs.mosaic_enabled[BG4] = false; regs.mosaic_countdown = 0; -//$2107-$210a + //$2107-$210a regs.bg_scaddr[BG1] = 0x0000; regs.bg_scaddr[BG2] = 0x0000; regs.bg_scaddr[BG3] = 0x0000; @@ -137,13 +119,13 @@ void bPPU::power() { regs.bg_scsize[BG3] = SC_32x32; regs.bg_scsize[BG4] = SC_32x32; -//$210b-$210c + //$210b-$210c regs.bg_tdaddr[BG1] = 0x0000; regs.bg_tdaddr[BG2] = 0x0000; regs.bg_tdaddr[BG3] = 0x0000; regs.bg_tdaddr[BG4] = 0x0000; -//$210d-$2114 + //$210d-$2114 regs.bg_ofslatch = 0x00; regs.m7_hofs = regs.m7_vofs = 0x0000; regs.bg_hofs[BG1] = regs.bg_vofs[BG1] = 0x0000; @@ -151,20 +133,20 @@ void bPPU::power() { regs.bg_hofs[BG3] = regs.bg_vofs[BG3] = 0x0000; regs.bg_hofs[BG4] = regs.bg_vofs[BG4] = 0x0000; -//$2115 + //$2115 regs.vram_incmode = 1; regs.vram_mapping = 0; regs.vram_incsize = 1; -//$2116-$2117 + //$2116-$2117 regs.vram_addr = 0x0000; -//$211a + //$211a regs.mode7_repeat = 0; regs.mode7_vflip = false; regs.mode7_hflip = false; -//$211b-$2120 + //$211b-$2120 regs.m7_latch = 0x00; regs.m7a = 0x0000; regs.m7b = 0x0000; @@ -173,13 +155,13 @@ void bPPU::power() { regs.m7x = 0x0000; regs.m7y = 0x0000; -//$2121 + //$2121 regs.cgram_addr = 0x0000; -//$2122 + //$2122 regs.cgram_latchdata = 0x00; -//$2123-$2125 + //$2123-$2125 regs.window1_enabled[BG1] = false; regs.window1_enabled[BG2] = false; regs.window1_enabled[BG3] = false; @@ -208,13 +190,13 @@ void bPPU::power() { regs.window2_invert [OAM] = false; regs.window2_invert [COL] = false; -//$2126-$2129 + //$2126-$2129 regs.window1_left = 0x00; regs.window1_right = 0x00; regs.window2_left = 0x00; regs.window2_right = 0x00; -//$212a-$212b + //$212a-$212b regs.window_mask[BG1] = 0; regs.window_mask[BG2] = 0; regs.window_mask[BG3] = 0; @@ -222,7 +204,7 @@ void bPPU::power() { regs.window_mask[OAM] = 0; regs.window_mask[COL] = 0; -//$212c-$212d + //$212c-$212d regs.bg_enabled[BG1] = false; regs.bg_enabled[BG2] = false; regs.bg_enabled[BG3] = false; @@ -234,7 +216,7 @@ void bPPU::power() { regs.bgsub_enabled[BG4] = false; regs.bgsub_enabled[OAM] = false; -//$212e-$212f + //$212e-$212f regs.window_enabled[BG1] = false; regs.window_enabled[BG2] = false; regs.window_enabled[BG3] = false; @@ -246,13 +228,13 @@ void bPPU::power() { regs.sub_window_enabled[BG4] = false; regs.sub_window_enabled[OAM] = false; -//$2130 + //$2130 regs.color_mask = 0; regs.colorsub_mask = 0; regs.addsub_mode = false; regs.direct_color = false; -//$2131 + //$2131 regs.color_mode = 0; regs.color_halve = false; regs.color_enabled[BACK] = false; @@ -262,13 +244,13 @@ void bPPU::power() { regs.color_enabled[BG2] = false; regs.color_enabled[BG1] = false; -//$2132 + //$2132 regs.color_r = 0x00; regs.color_g = 0x00; regs.color_b = 0x00; regs.color_rgb = 0x0000; -//$2133 + //$2133 regs.mode7_extbg = false; regs.pseudo_hires = false; regs.overscan = false; @@ -276,17 +258,17 @@ void bPPU::power() { regs.oam_interlace = false; regs.interlace = false; -//$2137 + //$2137 regs.hcounter = 0; regs.vcounter = 0; regs.latch_hcounter = 0; regs.latch_vcounter = 0; regs.counters_latched = false; -//$2139-$213a + //$2139-$213a regs.vram_readbuffer = 0x0000; -//$213e + //$213e regs.time_over = false; regs.range_over = false; @@ -295,15 +277,19 @@ void bPPU::power() { void bPPU::reset() { PPU::reset(); - frame(); + frame(); + + display.field = 0; + display.interlace = false; + display.overscan = false; memset(sprite_list, 0, sizeof(sprite_list)); -//open bus support + //open bus support regs.ppu1_mdr = 0xff; regs.ppu2_mdr = 0xff; -//bg line counters + //bg line counters regs.bg_y[0] = 0; regs.bg_y[1] = 0; regs.bg_y[2] = 0; @@ -313,9 +299,7 @@ void bPPU::reset() { } uint8 bPPU::vram_read(uint16 addr) { -uint8 r; - r = vram[addr]; - return r; + return vram[addr]; } void bPPU::vram_write(uint16 addr, uint8 value) { @@ -323,10 +307,8 @@ void bPPU::vram_write(uint16 addr, uint8 value) { } uint8 bPPU::oam_read(uint16 addr) { -uint8 r; if(addr >= 0x0200) { addr = 0x0200 | (addr & 31); } - r = oam[addr]; - return r; + return oam[addr]; } void bPPU::oam_write(uint16 addr, uint8 value) { @@ -335,9 +317,8 @@ void bPPU::oam_write(uint16 addr, uint8 value) { } uint8 bPPU::cgram_read(uint16 addr) { -uint8 r; addr &= 511; - r = cgram[addr]; + uint8 r = cgram[addr]; if(addr & 1) { r &= 0x7f; } return r; } @@ -365,18 +346,20 @@ bPPU::bPPU() { } for(int l = 0; l < 16; l++) { - double m = (double)l / 15.0; + double m = (double)l / 15.0; for(int i = 0; i < 32 * 32; i++) { - int r = minmax<0, 31>((int)((double)((i) & 31) * m + 0.5)); - int g = minmax<0, 31>((int)((double)((i >> 5) & 31) * m + 0.5)); - if(i < 32)light_table_b[l][i] = (r << 10); + int r = (int)((double)((i) & 31) * m + 0.5); + int g = (int)((double)((i >> 5) & 31) * m + 0.5); + r = max(0, min(31, r)); + g = max(0, min(31, g)); + if(i < 32) light_table_b[l][i] = (r << 10); light_table_gr[l][i] = (g << 5) | (r); } } } bPPU::~bPPU() { - safe_free(vram); - safe_free(oam); - safe_free(cgram); + free(vram); + free(oam); + free(cgram); } diff --git a/src/ppu/bppu/bppu.h b/src/ppu/bppu/bppu.h index 5759ffc9..c611c4de 100644 --- a/src/ppu/bppu/bppu.h +++ b/src/ppu/bppu/bppu.h @@ -1,145 +1,155 @@ class bPPU : public PPU { public: -uint8 *vram, *oam, *cgram; -uint8 region; + uint8 *vram, *oam, *cgram; + uint8 region; -enum { NTSC = 0, PAL = 1 }; -enum { BG1 = 0, BG2 = 1, BG3 = 2, BG4 = 3, OAM = 4, BACK = 5, COL = 5 }; -enum { SC_32x32 = 0, SC_64x32 = 1, SC_32x64 = 2, SC_64x64 = 3 }; + enum { NTSC = 0, PAL = 1 }; + enum { BG1 = 0, BG2 = 1, BG3 = 2, BG4 = 3, OAM = 4, BACK = 5, COL = 5 }; + enum { SC_32x32 = 0, SC_64x32 = 1, SC_32x64 = 2, SC_64x64 = 3 }; -struct { - uint y; - bool interlace; - bool interlace_field; -} line; + struct { + uint y; + } line; + + struct { + bool field; + bool interlace; + bool overscan; + } display; -struct { -//open bus support - uint8 ppu1_mdr, ppu2_mdr; + struct { + //open bus support + uint8 ppu1_mdr, ppu2_mdr; -//bg line counters - uint16 bg_y[4]; + //bg line counters + uint16 bg_y[4]; -//$2100 - bool display_disabled; - uint8 display_brightness; + //$2100 + bool display_disabled; + uint8 display_brightness; -//$2101 - uint8 oam_basesize; - uint8 oam_nameselect; - uint16 oam_tdaddr; + //$2101 + uint8 oam_basesize; + uint8 oam_nameselect; + uint16 oam_tdaddr; -//$2102-$2103 - uint16 oam_baseaddr; - uint16 oam_addr; - bool oam_priority; - uint8 oam_firstsprite; + //$2102-$2103 + uint16 oam_baseaddr; + uint16 oam_addr; + bool oam_priority; + uint8 oam_firstsprite; -//$2104 - uint8 oam_latchdata; + //$2104 + uint8 oam_latchdata; -//$2105 - bool bg_tilesize[4]; - bool bg3_priority; - uint8 bg_mode; + //$2105 + bool bg_tilesize[4]; + bool bg3_priority; + uint8 bg_mode; -//$2106 - uint8 mosaic_size; - bool mosaic_enabled[4]; - uint16 mosaic_countdown; + //$2106 + uint8 mosaic_size; + bool mosaic_enabled[4]; + uint16 mosaic_countdown; -//$2107-$210a - uint16 bg_scaddr[4]; - uint8 bg_scsize[4]; + //$2107-$210a + uint16 bg_scaddr[4]; + uint8 bg_scsize[4]; -//$210b-$210c - uint16 bg_tdaddr[4]; + //$210b-$210c + uint16 bg_tdaddr[4]; -//$210d-$2114 - uint8 bg_ofslatch; - uint16 m7_hofs, m7_vofs; - uint16 bg_hofs[4]; - uint16 bg_vofs[4]; + //$210d-$2114 + uint8 bg_ofslatch; + uint16 m7_hofs, m7_vofs; + uint16 bg_hofs[4]; + uint16 bg_vofs[4]; -//$2115 - bool vram_incmode; - uint8 vram_mapping; - uint8 vram_incsize; + //$2115 + bool vram_incmode; + uint8 vram_mapping; + uint8 vram_incsize; -//$2116-$2117 - uint16 vram_addr; + //$2116-$2117 + uint16 vram_addr; -//$211a - uint8 mode7_repeat; - bool mode7_vflip; - bool mode7_hflip; + //$211a + uint8 mode7_repeat; + bool mode7_vflip; + bool mode7_hflip; -//$211b-$2120 - uint8 m7_latch; - uint16 m7a, m7b, m7c, m7d, m7x, m7y; + //$211b-$2120 + uint8 m7_latch; + uint16 m7a, m7b, m7c, m7d, m7x, m7y; -//$2121 - uint16 cgram_addr; + //$2121 + uint16 cgram_addr; -//$2122 - uint8 cgram_latchdata; + //$2122 + uint8 cgram_latchdata; -//$2123-$2125 - bool window1_enabled[6]; - bool window1_invert [6]; - bool window2_enabled[6]; - bool window2_invert [6]; + //$2123-$2125 + bool window1_enabled[6]; + bool window1_invert [6]; + bool window2_enabled[6]; + bool window2_invert [6]; -//$2126-$2129 - uint8 window1_left, window1_right; - uint8 window2_left, window2_right; + //$2126-$2129 + uint8 window1_left, window1_right; + uint8 window2_left, window2_right; -//$212a-$212b - uint8 window_mask[6]; + //$212a-$212b + uint8 window_mask[6]; -//$212c-$212d - bool bg_enabled[5], bgsub_enabled[5]; + //$212c-$212d + bool bg_enabled[5], bgsub_enabled[5]; -//$212e-$212f - bool window_enabled[5], sub_window_enabled[5]; + //$212e-$212f + bool window_enabled[5], sub_window_enabled[5]; -//$2130 - uint8 color_mask, colorsub_mask; - bool addsub_mode; - bool direct_color; + //$2130 + uint8 color_mask, colorsub_mask; + bool addsub_mode; + bool direct_color; -//$2131 - bool color_mode, color_halve; - bool color_enabled[6]; + //$2131 + bool color_mode, color_halve; + bool color_enabled[6]; -//$2132 - uint8 color_r, color_g, color_b; - uint16 color_rgb; + //$2132 + uint8 color_r, color_g, color_b; + uint16 color_rgb; -//$2133 -//overscan and interlace are checked once per frame to -//determine if entire frame should be interlaced/non-interlace -//and overscan adjusted. therefore, the variables act sort of -//like a buffer, but they do still affect internal rendering - bool mode7_extbg; - bool pseudo_hires; - bool overscan; - uint16 scanlines; - bool oam_interlace; - bool interlace; + //$2133 + //overscan and interlace are checked once per frame to + //determine if entire frame should be interlaced/non-interlace + //and overscan adjusted. therefore, the variables act sort of + //like a buffer, but they do still affect internal rendering + bool mode7_extbg; + bool pseudo_hires; + bool overscan; + uint16 scanlines; + bool oam_interlace; + bool interlace; -//$2137 - uint16 hcounter, vcounter; - bool latch_hcounter, latch_vcounter; - bool counters_latched; + //$2137 + uint16 hcounter, vcounter; + bool latch_hcounter, latch_vcounter; + bool counters_latched; -//$2139-$213a - uint16 vram_readbuffer; + //$2139-$213a + uint16 vram_readbuffer; + + //$213e + bool time_over, range_over; + uint16 oam_itemcount, oam_tilecount; + } regs; + + alwaysinline bool field() { return display.field; } + alwaysinline bool interlace() { return display.interlace; } + alwaysinline bool overscan() { return display.overscan; } + alwaysinline bool hires() { return (regs.pseudo_hires || regs.bg_mode == 5 || regs.bg_mode == 6); } -//$213e - bool time_over, range_over; - uint16 oam_itemcount, oam_tilecount; -} regs; uint8 vram_read (uint16 addr); void vram_write (uint16 addr, uint8 value); uint8 oam_read (uint16 addr); @@ -220,37 +230,34 @@ struct { uint8 mmio_r213e(); //STAT77 uint8 mmio_r213f(); //STAT78 - uint8 mmio_read (uint addr); - void mmio_write(uint addr, uint8 data); + uint8 mmio_read(uint addr); + void mmio_write(uint addr, uint8 data); - void latch_counters(); + void latch_counters(); -//PPU render functions + //PPU render functions + #include "bppu_render.h" -#include "bppu_render.h" + uint16 light_table_b[16][32]; + uint16 light_table_gr[16][32 * 32]; + uint16 mosaic_table[16][4096]; + void render_line(); -uint16 light_table_b[16][32]; -uint16 light_table_gr[16][32 * 32]; -uint16 mosaic_table[16][4096]; - void render_line(); - - void update_oam_status(); -//required functions - void run(); - void scanline(); - void render_scanline(); - void frame(); - void power(); - void reset(); - - bool scanline_is_hires() { return (regs.pseudo_hires || regs.bg_mode == 5 || regs.bg_mode == 6); } + void update_oam_status(); + //required functions + void run(); + void scanline(); + void render_scanline(); + void frame(); + void power(); + void reset(); inline uint16 read16(uint8 *addr, uint pos) { - #if defined(ARCH_LSB) + #if defined(ARCH_LSB) return *((uint16*)(addr + pos)); - #else + #else return (addr[pos]) | (addr[pos + 1] << 8); - #endif + #endif } bPPU(); diff --git a/src/ppu/bppu/bppu_mmio.cpp b/src/ppu/bppu/bppu_mmio.cpp index 17784636..a8c1af4e 100644 --- a/src/ppu/bppu/bppu_mmio.cpp +++ b/src/ppu/bppu/bppu_mmio.cpp @@ -1,5 +1,7 @@ +#ifdef BPPU_CPP + void bPPU::latch_counters() { - regs.hcounter = cpu.hcounter(); + regs.hcounter = cpu.hdot(); regs.vcounter = cpu.vcounter(); regs.counters_latched = true; } @@ -21,61 +23,36 @@ uint16 bPPU::get_vram_address() { //write occurs during the very last clock cycle of vblank. uint8 bPPU::vram_mmio_read(uint16 addr) { - if(regs.display_disabled == true) { - return vram_read(addr); - } + if(regs.display_disabled == true) return vram_read(addr); -uint16 v = cpu.vcounter(); -uint16 hc = cpu.hclock(); -uint16 ls = (cpu.region_scanlines() >> 1) - 1; - if(cpu.interlace() && !cpu.interlace_field())ls++; - - if(v == ls && hc == 1362) { + uint16 v = cpu.vcounter(); + uint16 h = cpu.hcounter(); + uint16 ls = ((snes.region() == SNES::NTSC ? 525 : 625) >> 1) - 1; + if(interlace() && !field()) ls++; + if(v == ls && h == 1362) return 0x00; + if(v < (!overscan() ? 224 : 239)) return 0x00; + if(v == (!overscan() ? 224 : 239)) { + if(h == 1362) return vram_read(addr); return 0x00; } - - if(v < (!cpu.overscan() ? 224 : 239)) { - return 0x00; - } - - if(v == (!cpu.overscan() ? 224 : 239)) { - if(hc == 1362) { - return vram_read(addr); - } - return 0x00; - } - return vram_read(addr); } void bPPU::vram_mmio_write(uint16 addr, uint8 data) { - if(regs.display_disabled == true) { - return vram_write(addr, data); - } + if(regs.display_disabled == true) return vram_write(addr, data); -uint16 v = cpu.vcounter(); -uint16 hc = cpu.hclock(); + uint16 v = cpu.vcounter(); + uint16 h = cpu.hcounter(); if(v == 0) { - if(hc <= 4) { - return vram_write(addr, data); - } - if(hc == 6) { - return vram_write(addr, cpu.regs.mdr); - } + if(h <= 4) return vram_write(addr, data); + if(h == 6) return vram_write(addr, cpu.regs.mdr); return; } - - if(v < (!cpu.overscan() ? 225 : 240)) { - return; - } - - if(v == (!cpu.overscan() ? 225 : 240)) { - if(hc <= 4) { - return; - } + if(v < (!overscan() ? 225 : 240)) return; + if(v == (!overscan() ? 225 : 240)) { + if(h <= 4) return; return vram_write(addr, data); } - vram_write(addr, data); } @@ -99,11 +76,8 @@ uint8 bPPU::oam_mmio_read(uint16 addr) { return oam_read(addr); } -uint16 v = cpu.vcounter(); - if(v < (!cpu.overscan() ? 225 : 240)) { - return oam_read(0x0218); - } - + uint16 v = cpu.vcounter(); + if(v < (!overscan() ? 225 : 240)) return oam_read(0x0218); return oam_read(addr); } @@ -112,11 +86,8 @@ void bPPU::oam_mmio_write(uint16 addr, uint8 data) { return oam_write(addr, data); } -uint16 v = cpu.vcounter(); - if(v < (!cpu.overscan() ? 225 : 240)) { - return oam_write(0x0218, data); - } - + uint16 v = cpu.vcounter(); + if(v < (!overscan() ? 225 : 240)) return oam_write(0x0218, data); oam_write(addr, data); } @@ -131,12 +102,11 @@ uint8 bPPU::cgram_mmio_read(uint16 addr) { return cgram_read(addr); } -uint16 v = cpu.vcounter(); -uint16 hc = cpu.hclock(); - if(v < (!cpu.overscan() ? 225 : 240) && hc >= 72 && hc < 1096) { + uint16 v = cpu.vcounter(); + uint16 h = cpu.hcounter(); + if(v < (!overscan() ? 225 : 240) && h >= 72 && h < 1096) { return cgram_read(0x01ff); } - return cgram_read(addr); } @@ -145,18 +115,17 @@ void bPPU::cgram_mmio_write(uint16 addr, uint8 data) { return cgram_write(addr, data); } -uint16 v = cpu.vcounter(); -uint16 hc = cpu.hclock(); - if(v < (!cpu.overscan() ? 225 : 240) && hc >= 72 && hc < 1096) { + uint16 v = cpu.vcounter(); + uint16 h = cpu.hcounter(); + if(v < (!overscan() ? 225 : 240) && h >= 72 && h < 1096) { return cgram_write(0x01ff, data); } - cgram_write(addr, data); } //INIDISP void bPPU::mmio_w2100(uint8 value) { - if(regs.display_disabled == true && cpu.vcounter() == (!cpu.overscan() ? 225 : 240)) { + if(regs.display_disabled == true && cpu.vcounter() == (!overscan() ? 225 : 240)) { regs.oam_addr = regs.oam_baseaddr << 1; regs.oam_firstsprite = (regs.oam_priority == false) ? 0 : (regs.oam_addr >> 2) & 127; } @@ -563,11 +532,13 @@ void bPPU::mmio_w2131(uint8 value) { //COLDATA void bPPU::mmio_w2132(uint8 value) { - if(value & 0x80)regs.color_b = value & 0x1f; - if(value & 0x40)regs.color_g = value & 0x1f; - if(value & 0x20)regs.color_r = value & 0x1f; + if(value & 0x80) regs.color_b = value & 0x1f; + if(value & 0x40) regs.color_g = value & 0x1f; + if(value & 0x20) regs.color_r = value & 0x1f; - regs.color_rgb = (regs.color_r) | (regs.color_g << 5) | (regs.color_b << 10); + regs.color_rgb = (regs.color_r) + | (regs.color_g << 5) + | (regs.color_b << 10); } //SETINI @@ -576,9 +547,9 @@ void bPPU::mmio_w2133(uint8 value) { regs.pseudo_hires = !!(value & 0x08); regs.overscan = !!(value & 0x04); regs.oam_interlace = !!(value & 0x02); - regs.interlace = !!(value & 0x01); - - cpu.set_overscan(regs.overscan); + regs.interlace = !!(value & 0x01); + + display.overscan = regs.overscan; } //MPYL @@ -707,7 +678,7 @@ uint8 r = 0x00; regs.latch_hcounter = 0; regs.latch_vcounter = 0; - r |= cpu.interlace_field() << 7; + r |= field() << 7; if(!(cpu.pio_status() & 0x80)) { r |= 0x40; } else if(regs.counters_latched == true) { @@ -816,3 +787,5 @@ void bPPU::mmio_write(uint addr, uint8 data) { case 0x2133: mmio_w2133(data); return; //SETINI } } + +#endif //ifdef BPPU_CPP diff --git a/src/ppu/bppu/bppu_render.cpp b/src/ppu/bppu/bppu_render.cpp index 834892a0..305390ee 100644 --- a/src/ppu/bppu/bppu_render.cpp +++ b/src/ppu/bppu/bppu_render.cpp @@ -1,3 +1,5 @@ +#ifdef BPPU_CPP + #include "bppu_render_cache.cpp" #include "bppu_render_windows.cpp" #include "bppu_render_bg.cpp" @@ -173,3 +175,5 @@ void bPPU::render_line() { render_line_output(); } + +#endif //ifdef BPPU_CPP diff --git a/src/ppu/bppu/bppu_render_addsub.cpp b/src/ppu/bppu/bppu_render_addsub.cpp index c54158e1..a6a3e334 100644 --- a/src/ppu/bppu/bppu_render_addsub.cpp +++ b/src/ppu/bppu/bppu_render_addsub.cpp @@ -1,13 +1,8 @@ +#ifdef BPPU_CPP + /***** * Color Addition / Subtraction * Thanks to blargg for the optimized algorithms - * - * clock() counts for 32768x32768 iterations of addsub(), - * taken on an Athlon 3500+/DDR266 system: - * add = 10594 - * half_add = 6516 - * sub = 10579 - * half_sub = 11860 *****/ inline uint16 bPPU::addsub(uint32 x, uint32 y, bool halve) { if(!regs.color_mode) { @@ -28,3 +23,5 @@ inline uint16 bPPU::addsub(uint32 x, uint32 y, bool halve) { } } } + +#endif //ifdef BPPU_CPP diff --git a/src/ppu/bppu/bppu_render_bg.cpp b/src/ppu/bppu/bppu_render_bg.cpp index b663b328..53639ce8 100644 --- a/src/ppu/bppu/bppu_render_bg.cpp +++ b/src/ppu/bppu/bppu_render_bg.cpp @@ -1,3 +1,5 @@ +#ifdef BPPU_CPP + //called once at the start of every rendered scanline void bPPU::update_bg_info() { uint hires = (regs.bg_mode == 5 || regs.bg_mode == 6); @@ -88,9 +90,7 @@ uint width = (!hires) ? 256 : 512; if(hires) { hscroll <<= 1; - if(regs.interlace) { - y = (y << 1) + line.interlace_field; - } + if(regs.interlace) y = (y << 1) + field(); } uint16 *mtable = mosaic_table[(regs.mosaic_enabled[bg]) ? regs.mosaic_size : 0]; @@ -212,3 +212,5 @@ uint16 prev_x = 0xffff, prev_y = 0xffff; #undef setpixel_main #undef setpixel_sub + +#endif //ifdef BPPU_CPP diff --git a/src/ppu/bppu/bppu_render_cache.cpp b/src/ppu/bppu/bppu_render_cache.cpp index 79113b46..d668b3ac 100644 --- a/src/ppu/bppu/bppu_render_cache.cpp +++ b/src/ppu/bppu/bppu_render_cache.cpp @@ -1,3 +1,5 @@ +#ifdef BPPU_CPP + #define render_bg_tile_line_2bpp(mask) \ col = !!(d0 & mask) << 0; \ col += !!(d1 & mask) << 1; \ @@ -132,3 +134,5 @@ void bPPU::clear_tiledata_cache() { memset(bg_tiledata_state[TILE_4BIT], 0, 2048); memset(bg_tiledata_state[TILE_8BIT], 0, 1024); } + +#endif //ifdef BPPU_CPP diff --git a/src/ppu/bppu/bppu_render_line.cpp b/src/ppu/bppu/bppu_render_line.cpp index 9204e7ea..c85a3bf2 100644 --- a/src/ppu/bppu/bppu_render_line.cpp +++ b/src/ppu/bppu/bppu_render_line.cpp @@ -1,3 +1,5 @@ +#ifdef BPPU_CPP + inline uint16 bPPU::get_palette(uint8 index) { return read16(cgram, index << 1); } @@ -83,7 +85,7 @@ uint8 bg_sub; inline void bPPU::render_line_output() { uint16 *ptr = (uint16*)output + (line.y * 1024) + - ((line.interlace && line.interlace_field) ? 512 : 0); + ((interlace() && field()) ? 512 : 0); uint16 *luma_b = light_table_b[regs.display_brightness]; uint16 *luma_gr = light_table_gr[regs.display_brightness]; uint16 curr, prev; @@ -128,7 +130,9 @@ uint16 curr, prev; inline void bPPU::render_line_clear() { uint16 *ptr = (uint16*)output + (line.y * 1024) + - ((line.interlace && line.interlace_field) ? 512 : 0); + ((interlace() && field()) ? 512 : 0); uint16 width = (!regs.pseudo_hires && regs.bg_mode != 5 && regs.bg_mode != 6) ? 256 : 512; memset(ptr, 0, width * 2 * sizeof(uint16)); } + +#endif //ifdef BPPU_CPP diff --git a/src/ppu/bppu/bppu_render_mode7.cpp b/src/ppu/bppu/bppu_render_mode7.cpp index 9d16a8be..c255e72b 100644 --- a/src/ppu/bppu/bppu_render_mode7.cpp +++ b/src/ppu/bppu/bppu_render_mode7.cpp @@ -1,3 +1,5 @@ +#ifdef BPPU_CPP + /***** * bsnes mode7 renderer * @@ -142,3 +144,5 @@ void bPPU::render_line_mode7(uint8 bg, uint8 pri0_pos, uint8 pri1_pos) { } #undef CLIP + +#endif //ifdef BPPU_CPP diff --git a/src/ppu/bppu/bppu_render_oam.cpp b/src/ppu/bppu/bppu_render_oam.cpp index 2cf3184e..ea671434 100644 --- a/src/ppu/bppu/bppu_render_oam.cpp +++ b/src/ppu/bppu/bppu_render_oam.cpp @@ -1,3 +1,5 @@ +#ifdef BPPU_CPP + void bPPU::build_sprite_list() { uint8 *tableA = oam, *tableB = oam + 512; uint8 y_offset = (config::ppu.hack.obj_cache == true) ? 0 : 1; @@ -78,7 +80,7 @@ int y = line.y - spr->y; } if(regs.oam_interlace == true) { - y = (spr->vflip == false) ? (y + line.interlace_field) : (y - line.interlace_field); + y = (spr->vflip == false) ? (y + field()) : (y - field()); } x &= 511; @@ -218,3 +220,5 @@ int pri_tbl[4] = { pri0_pos, pri1_pos, pri2_pos, pri3_pos }; #undef setpixel_main #undef setpixel_sub + +#endif //ifdef BPPU_CPP diff --git a/src/ppu/bppu/bppu_render_windows.cpp b/src/ppu/bppu/bppu_render_windows.cpp index 7bea657f..dd34dafb 100644 --- a/src/ppu/bppu/bppu_render_windows.cpp +++ b/src/ppu/bppu/bppu_render_windows.cpp @@ -1,3 +1,5 @@ +#ifdef BPPU_CPP + void bPPU::build_window_table(uint8 bg, bool mainscreen) { uint8 set = 1, clr = 0; uint8 *wtbl = (mainscreen == true) ? window[bg].main : window[bg].sub; @@ -84,3 +86,5 @@ void bPPU::build_window_tables(uint8 bg) { build_window_table(bg, true); build_window_table(bg, false); } + +#endif //ifdef BPPU_CPP diff --git a/src/ppu/ppu.cpp b/src/ppu/ppu.cpp index b3773bbf..c3be2bde 100644 --- a/src/ppu/ppu.cpp +++ b/src/ppu/ppu.cpp @@ -1,18 +1,13 @@ #include "../base.h" -void PPU::get_scanline_info(scanline_info *info) { - info->hires = scanline_is_hires(); - info->interlace = cpu.interlace(); -} - void PPU::enable_renderer(bool r) { status.render_output = r; } bool PPU::renderer_enabled() { return status.render_output; } void PPU::frame() { status.frame_executed = true; -static int32 fr = 0, fe = 0; -static time_t prev, curr; + static int32 fr = 0, fe = 0; + static time_t prev, curr; fe++; if(status.render_output)fr++; @@ -46,5 +41,5 @@ PPU::PPU() { } PPU::~PPU() { - safe_free(output); + free(output); } diff --git a/src/ppu/ppu.h b/src/ppu/ppu.h index 255af9d6..16010d16 100644 --- a/src/ppu/ppu.h +++ b/src/ppu/ppu.h @@ -1,34 +1,32 @@ class PPU : public MMIO { public: -uint16 *output; + uint16 *output; -//this struct should be read-only to -//functions outside of this class -struct { - bool render_output; + //this struct should be read-only to + //functions outside of this class + struct { + bool render_output; - bool frame_executed; - bool frames_updated; - uint32 frames_rendered; - uint32 frames_executed; -} status; + bool frame_executed; + bool frames_updated; + uint32 frames_rendered; + uint32 frames_executed; + } status; -//PPU1 version number -//* 1 is known -//* reported by $213e -uint8 ppu1_version; + //PPU1 version number + //* 1 is known + //* reported by $213e + uint8 ppu1_version; -//PPU2 version number -//* 1 and 3 are known -//* reported by $213f -uint8 ppu2_version; + //PPU2 version number + //* 1 and 3 are known + //* reported by $213f + uint8 ppu2_version; -struct scanline_info { - bool hires; - bool interlace; -}; - virtual bool scanline_is_hires() = 0; - virtual void get_scanline_info(scanline_info *info); + virtual bool field() = 0; + virtual bool interlace() = 0; + virtual bool overscan() = 0; + virtual bool hires() = 0; virtual uint8 vram_read (uint16 addr) = 0; virtual void vram_write (uint16 addr, uint8 value) = 0; @@ -39,7 +37,6 @@ struct scanline_info { virtual void latch_counters() = 0; - virtual void run() = 0; virtual void render_scanline() = 0; virtual void scanline() = 0; virtual void frame(); diff --git a/src/reader/filereader.cpp b/src/reader/filereader.cpp index b7cfe85a..62b19c72 100644 --- a/src/reader/filereader.cpp +++ b/src/reader/filereader.cpp @@ -1,27 +1,30 @@ +#ifdef READER_CPP + #include "filereader.h" -uint32 FileReader::size() { - return fsize; +unsigned FileReader::size() { + return filesize; } //This function will allocate memory even if open() fails. //This is needed so that when SRAM files do not exist, the //memory for the SRAM data will be allocated still. //The memory is flushed to 0x00 when no file is opened. -uint8 *FileReader::read(uint32 length) { -uint8 *data; +uint8_t* FileReader::read(unsigned length) { + uint8_t *data = 0; + if(length == 0) { - //read the entire file into RAM - data = (uint8*)malloc(fsize); - memset(data, 0, fsize); - if(fp)fread(data, 1, fsize, fp); - } else if(length > fsize) { - //read the entire file into RAM, pad the rest with 0x00s + //read the entire file into RAM + data = (uint8*)malloc(filesize); + memset(data, 0, filesize); + if(fp) fread(data, 1, filesize, fp); + } else if(length > filesize) { + //read the entire file into RAM, pad the rest with 0x00s data = (uint8*)malloc(length); memset(data, 0, length); - if(fp)fread(data, 1, fsize, fp); - } else { //fsize >= length - //read only what was requested + if(fp)fread(data, 1, filesize, fp); + } else { //filesize >= length + //read only what was requested data = (uint8*)malloc(length); memset(data, 0, length); if(fp)fread(data, 1, length, fp); @@ -35,14 +38,14 @@ bool FileReader::ready() { FileReader::FileReader(const char *fn) { fp = fopen(fn, "rb"); - if(!fp)return; + if(!fp) return; fseek(fp, 0, SEEK_END); - fsize = ftell(fp); - fseek(fp, 0, SEEK_SET); + filesize = ftell(fp); + rewind(fp); -//empty file? - if(fsize == 0) { + //empty file? + if(filesize == 0) { fclose(fp); fp = 0; } @@ -55,9 +58,8 @@ FileReader::~FileReader() { } } -void FileWriter::write(uint8 *buffer, uint32 length) { - if(!fp)return; - +void FileWriter::write(uint8_t *buffer, unsigned length) { + if(!fp) return; fwrite(buffer, 1, length, fp); } @@ -75,3 +77,5 @@ FileWriter::~FileWriter() { fp = 0; } } + +#endif //ifdef READER_CPP diff --git a/src/reader/filereader.h b/src/reader/filereader.h index 964744e5..a04f1385 100644 --- a/src/reader/filereader.h +++ b/src/reader/filereader.h @@ -1,12 +1,12 @@ class FileReader : public Reader { private: -FILE *fp; -uint32 fsize; + FILE *fp; + unsigned filesize; public: - uint32 size(); - uint8 *read(uint32 length = 0); - bool ready(); + unsigned size(); + uint8_t* read(unsigned length = 0); + bool ready(); FileReader(const char *fn); ~FileReader(); @@ -14,10 +14,10 @@ public: class FileWriter : public Writer { private: -FILE *fp; + FILE *fp; public: - void write(uint8 *buffer, uint32 length); + void write(uint8_t *buffer, unsigned length); bool ready(); FileWriter(const char *fn); diff --git a/src/reader/gzreader.cpp b/src/reader/gzreader.cpp index 00798166..cae1f5ee 100644 --- a/src/reader/gzreader.cpp +++ b/src/reader/gzreader.cpp @@ -1,31 +1,35 @@ +#ifdef READER_CPP + #include "gzreader.h" -uint32 GZReader::size() { - return fsize; +unsigned GZReader::size() { + return filesize; } //This function will allocate memory even if open() fails. //This is needed so that when SRAM files do not exist, the //memory for the SRAM data will be allocated still. //The memory is flushed to 0x00 when no file is opened. -uint8 *GZReader::read(uint32 length) { -uint8 *data; +uint8_t* GZReader::read(unsigned length) { + uint8_t *data = 0; + if(length == 0) { - //read the entire file into RAM - data = (uint8*)malloc(fsize); - memset(data, 0, fsize); - if(gp)gzread(gp, data, fsize); - } else if(length > fsize) { - //read the entire file into RAM, pad the rest with 0x00s + //read the entire file into RAM + data = (uint8*)malloc(filesize); + memset(data, 0, filesize); + if(gp)gzread(gp, data, filesize); + } else if(length > filesize) { + //read the entire file into RAM, pad the rest with 0x00s data = (uint8*)malloc(length); memset(data, 0, length); - if(gp)gzread(gp, data, fsize); - } else { //fsize >= length - //read only what was requested + if(gp)gzread(gp, data, filesize); + } else { //filesize >= length + //read only what was requested data = (uint8*)malloc(length); memset(data, 0, length); if(gp)gzread(gp, data, length); } + return data; } @@ -35,14 +39,13 @@ bool GZReader::ready() { GZReader::GZReader(const char *fn) { gp = 0; - -FILE *fp = fopen(fn, "rb"); + FILE *fp = fopen(fn, "rb"); if(!fp)return; fseek(fp, 0, SEEK_END); - fsize = ftell(fp); + filesize = ftell(fp); - if(fsize < 4) { + if(filesize < 4) { fclose(fp); fp = 0; return; @@ -62,11 +65,11 @@ uint32 gzsize; if(!gp)return; if(!gzdirect(gp)) { - fsize = gzsize; + filesize = gzsize; } -//empty file? - if(fsize == 0) { + //empty file? + if(filesize == 0) { gzclose(gp); gp = 0; return; @@ -79,3 +82,5 @@ GZReader::~GZReader() { gp = 0; } } + +#endif //ifdef READER_CPP diff --git a/src/reader/gzreader.h b/src/reader/gzreader.h index 53ccc074..a49682be 100644 --- a/src/reader/gzreader.h +++ b/src/reader/gzreader.h @@ -2,13 +2,13 @@ class GZReader : public Reader { private: -gzFile gp; -uint32 fsize; + gzFile gp; + uint32 filesize; public: - uint32 size(); - uint8 *read(uint32 length = 0); - bool ready(); + unsigned size(); + uint8_t* read(unsigned length = 0); + bool ready(); GZReader(const char *fn); ~GZReader(); diff --git a/src/reader/jmareader.cpp b/src/reader/jmareader.cpp index 15e93daa..13fff44e 100644 --- a/src/reader/jmareader.cpp +++ b/src/reader/jmareader.cpp @@ -1,44 +1,41 @@ -//created by Nach - +#ifdef READER_CPP + #include "jmareader.h" #include "jma/jma.h" -uint32 JMAReader::size() { - return fsize; +unsigned JMAReader::size() { + return filesize; } #define MAXROM 0x800000 -uint8 *JMAReader::read(uint32 length) -{ - uint8 *data; - if (!fsize) { return 0; } - if (length <= fsize) - { +uint8_t* JMAReader::read(unsigned length) { + uint8_t *data = 0; + if(!filesize) return 0; + + if(length <= filesize) { //read the entire file into RAM - data = (uint8*)malloc(fsize); + data = (uint8_t*)malloc(filesize); JMAFile.extract_file(cname, data); - } - else if (length > fsize) - { + } else if(length > filesize) { //read the entire file into RAM, pad the rest with 0x00s - data = (uint8*)malloc(length); + data = (uint8_t*)malloc(length); memset(data, 0, length); JMAFile.extract_file(cname, data); - } + } + return data; } -JMAReader::JMAReader(const char *fn) : JMAFile(fn), fsize(0) -{ +JMAReader::JMAReader(const char *fn) : JMAFile(fn), filesize(0) { std::vector file_info = JMAFile.get_files_info(); - for (std::vector::iterator i = file_info.begin(); i != file_info.end(); i++) - { + for(std::vector::iterator i = file_info.begin(); i != file_info.end(); i++) { //Check for valid ROM based on size - if ((i->size <= MAXROM+512) && (i->size > fsize)) - { + if((i->size <= MAXROM + 512) && (i->size > filesize)) { cname = i->name; - fsize = i->size; + filesize = i->size; } } } + +#endif //ifdef READER_CPP diff --git a/src/reader/jmareader.h b/src/reader/jmareader.h index 78eb19a5..bb3d6450 100644 --- a/src/reader/jmareader.h +++ b/src/reader/jmareader.h @@ -1,17 +1,14 @@ -//created by Nach - #include "jma/jma.h" class JMAReader : public Reader { -private: -JMA::jma_open JMAFile; -uint32 fsize; -std::string cname; - public: - uint32 size(); - uint8 *read(uint32 length = 0); + unsigned size(); + uint8_t* read(unsigned length = 0); - JMAReader(const char *fn); - ~JMAReader() { } + JMAReader(const char *fn); + +private: + JMA::jma_open JMAFile; + uint32 filesize; + std::string cname; }; diff --git a/src/reader/reader.cpp b/src/reader/reader.cpp index 1f96c1de..00095ab6 100644 --- a/src/reader/reader.cpp +++ b/src/reader/reader.cpp @@ -1,23 +1,36 @@ -#include "../base.h" +#include "../base.h" +#define READER_CPP #include "filereader.cpp" + #if defined(GZIP_SUPPORT) #include "gzreader.cpp" #include "zipreader.cpp" #endif + #if defined(JMA_SUPPORT) #include "jmareader.cpp" -#endif - -uint Reader::detect(const char *fn) { -int len = strlen(fn); - if(len >= 4 && !stricmp(fn + len - 3, ".gz")) { - return RF_GZ; - } else if(len >= 5 && !stricmp(fn + len - 4, ".zip")) { - return RF_ZIP; - } else if(len >= 5 && !stricmp(fn + len - 4, ".jma")) { - return RF_JMA; - } else { - return RF_NORMAL; - } +#endif + +Reader::Type Reader::detect(const char *fn) { + FILE *fp = fopen(fn, "rb"); + if(!fp) return Unknown; + + uint8_t p[8]; + memset(p, 0, sizeof p); + fread(p, 1, 8, fp); + fclose(fp); + + if(config::file.autodetect_type == true) { + //inspect file header to determine type + if(p[0] == 0x1f && p[1] == 0x8b && p[2] == 0x08 && p[3] <= 0x1f) return GZIP; + if(p[0] == 0x50 && p[1] == 0x4b && p[2] == 0x03 && p[3] == 0x04) return ZIP; + if(p[0] == 0x4a && p[1] == 0x4d && p[2] == 0x41 && p[3] == 0x00 && p[4] == 0x4e) return JMA; + } else { + //check file extension to determine type + if(striend(fn, ".gz")) return GZIP; + if(striend(fn, ".zip") || striend(fn, ".z")) return ZIP; + if(striend(fn, ".jma")) return JMA; + } + return Normal; } diff --git a/src/reader/reader.h b/src/reader/reader.h index 4c4e8e5b..97c8d46c 100644 --- a/src/reader/reader.h +++ b/src/reader/reader.h @@ -1,27 +1,21 @@ class Reader { public: -enum { - RF_NORMAL = 0, - RF_GZ = 1, - RF_ZIP = 2, - RF_JMA = 3 -}; + enum Type { + Unknown, + Normal, + GZIP, + ZIP, + JMA, + }; -//attemps to determine filetype by extension, -//RF_NORMAL is returned on failure as a failsafe - static uint detect(const char *fn); - virtual uint32 size() = 0; - -//return is 0 on failure, caller must deallocate memory manually - virtual uint8 *read(uint32 length = 0) = 0; - -//returns whether or not read() will work (e.g. for FileReader, -//if the file handle is open and ready to be read from) - virtual bool ready() { return true; } + static Type detect(const char *fn); + virtual unsigned size() = 0; + virtual uint8_t* read(unsigned length = 0) = 0; + virtual bool ready() { return true; } //can only call read() when ready() returns true }; class Writer { public: - virtual void write(uint8 *buffer, uint32 length) = 0; + virtual void write(uint8_t *buffer, uint32 length) = 0; virtual bool ready() { return true; } }; diff --git a/src/reader/zipreader.cpp b/src/reader/zipreader.cpp index 9ea5ca22..140ae1e0 100644 --- a/src/reader/zipreader.cpp +++ b/src/reader/zipreader.cpp @@ -1,56 +1,59 @@ -//created by Nach - +#ifdef READER_CPP + #include "zipreader.h" -uint32 ZipReader::size() { - return fsize; +unsigned ZipReader::size() { + return filesize; } #define MAXROM 0x800000 -uint8 *ZipReader::read(uint32 length) -{ - uint8 *data; - if (!fsize) { return 0; } - if (length <= fsize) - { +uint8_t* ZipReader::read(unsigned length) { + uint8_t *data = 0; + + if(!filesize) return 0; + + if(length <= filesize) { //read the entire file into RAM - data = (uint8*)malloc(fsize); - unzReadCurrentFile(zipfile, data, fsize); - } - else if (length > fsize) - { + data = (uint8_t*)malloc(filesize); + unzReadCurrentFile(zipfile, data, filesize); + } else if(length > filesize) { //read the entire file into RAM, pad the rest with 0x00s - data = (uint8*)malloc(length); + data = (uint8_t*)malloc(length); memset(data, 0, length); - unzReadCurrentFile(zipfile, data, fsize); - } + unzReadCurrentFile(zipfile, data, filesize); + } + return data; } -ZipReader::ZipReader(const char *fn) : fsize(0) -{ +ZipReader::ZipReader(const char *fn) : filesize(0) { unz_file_info cFileInfo; //Create variable to hold info for a compressed file char cFileName[sizeof(cname)]; - if (zipfile = unzOpen(fn)) //Open zip file - { - for (int cFile = unzGoToFirstFile(zipfile); cFile == UNZ_OK; cFile = unzGoToNextFile(zipfile)) - { + if(zipfile = unzOpen(fn)) { //Open zip file + for(int cFile = unzGoToFirstFile(zipfile); cFile == UNZ_OK; cFile = unzGoToNextFile(zipfile)) { //Gets info on current file, and places it in cFileInfo unzGetCurrentFileInfo(zipfile, &cFileInfo, cFileName, sizeof(cname), 0, 0, 0, 0); - if ((cFileInfo.uncompressed_size <= MAXROM+512) && (cFileInfo.uncompressed_size > fsize)) - { + if((cFileInfo.uncompressed_size <= MAXROM+512) && (cFileInfo.uncompressed_size > filesize)) { strcpy(cname, cFileName); - fsize = cFileInfo.uncompressed_size; + filesize = cFileInfo.uncompressed_size; } } - if (fsize) - { + if(filesize) { unzLocateFile(zipfile, cname, 1); unzOpenCurrentFile(zipfile); } } } + +ZipReader::~ZipReader() { + if(zipfile) { + unzCloseCurrentFile(zipfile); + unzClose(zipfile); + } +} + +#endif //ifdef READER_CPP diff --git a/src/reader/zipreader.h b/src/reader/zipreader.h index 05a90946..0db3bdf7 100644 --- a/src/reader/zipreader.h +++ b/src/reader/zipreader.h @@ -1,27 +1,18 @@ -//created by Nach - #include "zlib/unzip.h" //Could be up to 65536 #define ZIP_MAX_FILE_NAME 4096 class ZipReader : public Reader { -private: -unzFile zipfile; -uint32 fsize; -char cname[4096]; - public: - uint32 size(); - uint8 *read(uint32 length = 0); + unsigned size(); + uint8_t* read(unsigned length = 0); ZipReader(const char *fn); - ~ZipReader() - { - if (zipfile) - { - unzCloseCurrentFile(zipfile); - unzClose(zipfile); - } - } + ~ZipReader(); + +private: + unzFile zipfile; + uint32 filesize; + char cname[4096]; }; diff --git a/src/smp/dsmp.cpp b/src/smp/dsmp.cpp index 9ed0c587..e7644b5e 100644 --- a/src/smp/dsmp.cpp +++ b/src/smp/dsmp.cpp @@ -1,3 +1,5 @@ +#ifdef SMP_CPP + //virtual function, see src/cpu/dcpu.cpp //for explanation of this function bool SMP::in_opcode() { return false; } @@ -304,3 +306,5 @@ uint16 opw, opdp0, opdp1; (regs.p.c) ? 'C' : 'c'); strcat(s, t); } + +#endif //ifdef SMP_CPP diff --git a/src/smp/smp.cpp b/src/smp/smp.cpp index bbe6bab7..f55e0fd2 100644 --- a/src/smp/smp.cpp +++ b/src/smp/smp.cpp @@ -1,3 +1,5 @@ -#include "../base.h" +#include "../base.h" +#define SMP_CPP + #include "iplrom.h" #include "dsmp.cpp" diff --git a/src/smp/smp.h b/src/smp/smp.h index cf792492..77fd8ede 100644 --- a/src/smp/smp.h +++ b/src/smp/smp.h @@ -4,26 +4,25 @@ class SMP { public: virtual void enter() = 0; -public: -SMPRegs regs; -uint8 spcram[65536]; -static const uint8 iplrom[64]; + SMPRegs regs; + uint8 spcram[65536]; + static const uint8 iplrom[64]; enum { FLAG_N = 0x80, FLAG_V = 0x40, FLAG_P = 0x20, FLAG_B = 0x10, - FLAG_H = 0x08, FLAG_I = 0x04, FLAG_Z = 0x02, FLAG_C = 0x01 + FLAG_H = 0x08, FLAG_I = 0x04, FLAG_Z = 0x02, FLAG_C = 0x01, }; - virtual uint8 ram_read (uint16 addr) = 0; - virtual void ram_write(uint16 addr, uint8 value) = 0; -//$f4-$f7 - virtual uint8 port_read (uint8 port) = 0; - virtual void port_write(uint8 port, uint8 value) = 0; + virtual uint8 ram_read(uint16 addr) = 0; + virtual void ram_write(uint16 addr, uint8 value) = 0; + //$f4-$f7 + virtual uint8 port_read(uint8 port) = 0; + virtual void port_write(uint8 port, uint8 value) = 0; - virtual uint8 *get_spcram_handle() { return spcram; } - virtual void power() = 0; - virtual void reset() = 0; + virtual uint8* get_spcram_handle() { return spcram; } + virtual void power() = 0; + virtual void reset() = 0; -//debugging functions + //debugging functions virtual bool in_opcode(); void disassemble_opcode(char *output); inline uint16 __relb(int8 offset, int op_len); diff --git a/src/smp/smpregs.h b/src/smp/smpregs.h index 993f3a44..587cc35b 100644 --- a/src/smp/smpregs.h +++ b/src/smp/smpregs.h @@ -1,11 +1,11 @@ class SMPRegFlags { public: -union { - uint8 data; - struct { - bool order_msb8(n:1, v:1, p:1, b:1, h:1, i:1, z:1, c:1); + union { + uint8 data; + struct { + bool order_msb8(n:1, v:1, p:1, b:1, h:1, i:1, z:1, c:1); + }; }; -}; inline operator unsigned() const { return data; } template inline unsigned operator = (const T i) { data = i; return data; } @@ -18,11 +18,11 @@ union { class SMPRegs { public: -uint16 pc; -union { - uint16 ya; - struct { uint8 order_lsb2(a, y); }; -}; -uint8 x, sp; -SMPRegFlags p; + uint16 pc; + union { + uint16 ya; + struct { uint8 order_lsb2(a, y); }; + }; + uint8 x, sp; + SMPRegFlags p; }; diff --git a/src/smp/ssmp/core/core.cpp b/src/smp/ssmp/core/core.cpp index b06b1174..50c2ed0f 100644 --- a/src/smp/ssmp/core/core.cpp +++ b/src/smp/ssmp/core/core.cpp @@ -1,3 +1,5 @@ +#ifdef SSMP_CPP + #include "opfn.cpp" #include "op_mov.cpp" @@ -15,3 +17,5 @@ void sSMP::enter() { loop: goto loop; } + +#endif //ifdef SSMP_CPP diff --git a/src/smp/ssmp/core/opfn.cpp b/src/smp/ssmp/core/opfn.cpp index 3f2ca3e4..c6027901 100644 --- a/src/smp/ssmp/core/opfn.cpp +++ b/src/smp/ssmp/core/opfn.cpp @@ -1,3 +1,5 @@ +#ifdef SSMP_CPP + uint8 sSMP::op_adc(uint8 x, uint8 y) { int16 r = x + y + regs.p.c; regs.p.n = !!(r & 0x80); @@ -120,3 +122,5 @@ uint8 carry = (uint8)regs.p.c << 7; regs.p.z = (x == 0); return x; } + +#endif //ifdef SSMP_CPP diff --git a/src/smp/ssmp/memory/memory.cpp b/src/smp/ssmp/memory/memory.cpp index b8a2285b..5f17105b 100644 --- a/src/smp/ssmp/memory/memory.cpp +++ b/src/smp/ssmp/memory/memory.cpp @@ -1,13 +1,15 @@ +#ifdef SSMP_CPP + alwaysinline uint8 sSMP::ram_read(uint16 addr) { - if(addr < 0xffc0)return spcram[addr]; - if(status.iplrom_enabled == false)return spcram[addr]; + if(addr < 0xffc0) return spcram[addr]; + if(status.iplrom_enabled == false) return spcram[addr]; return iplrom[addr & 0x3f]; } alwaysinline void sSMP::ram_write(uint16 addr, uint8 data) { -//writes to $ffc0-$ffff always go to spcram, even if the iplrom is enabled + //writes to $ffc0-$ffff always go to spcram, even if the iplrom is enabled spcram[addr] = data; } @@ -27,65 +29,63 @@ void sSMP::port_write(uint8 port, uint8 data) { alwaysinline uint8 sSMP::op_busread(uint16 addr) { -uint8 r; + uint8 r; if((addr & 0xfff0) == 0x00f0) { - //addr >= 0x00f0 && addr <= 0x00ff + //addr >= 0x00f0 && addr <= 0x00ff switch(addr) { + case 0xf0: { //TEST -- write-only register + r = 0x00; + } break; - case 0xf0: { //TEST -- write-only register - r = 0x00; - } break; + case 0xf1: { //CONTROL -- write-only register + r = 0x00; + } break; - case 0xf1: { //CONTROL -- write-only register - r = 0x00; - } break; + case 0xf2: { //DSPADDR + r = status.dsp_addr; + } break; - case 0xf2: { //DSPADDR - r = status.dsp_addr; - } break; + case 0xf3: { //DSPDATA + //0x80-0xff are read-only mirrors of 0x00-0x7f + r = dsp.read(status.dsp_addr & 0x7f); + } break; - case 0xf3: { //DSPDATA - //0x80-0xff are read-only mirrors of 0x00-0x7f - r = dsp.read(status.dsp_addr & 0x7f); - } break; + case 0xf4: //CPUIO0 + case 0xf5: //CPUIO1 + case 0xf6: //CPUIO2 + case 0xf7: { //CPUIO3 + scheduler.sync_smpcpu(); + r = cpu.port_read(addr & 3); + } break; - case 0xf4: //CPUIO0 - case 0xf5: //CPUIO1 - case 0xf6: //CPUIO2 - case 0xf7: { //CPUIO3 - scheduler.sync_smpcpu(); - r = cpu.port_read(addr & 3); - } break; + case 0xf8: { //??? + r = status.smp_f8; + } break; - case 0xf8: { //??? - r = status.smp_f8; - } break; + case 0xf9: { //??? + r = status.smp_f9; + } break; - case 0xf9: { //??? - r = status.smp_f9; - } break; + case 0xfa: //T0TARGET + case 0xfb: //T1TARGET + case 0xfc: { //T2TARGET -- write-only registers + r = 0x00; + } break; - case 0xfa: //T0TARGET - case 0xfb: //T1TARGET - case 0xfc: { //T2TARGET -- write-only registers - r = 0x00; - } break; + case 0xfd: { //T0OUT -- 4-bit counter value + r = t0.stage3_ticks & 15; + t0.stage3_ticks = 0; + } break; - case 0xfd: { //T0OUT -- 4-bit counter value - r = t0.stage3_ticks & 15; - t0.stage3_ticks = 0; - } break; - - case 0xfe: { //T1OUT -- 4-bit counter value - r = t1.stage3_ticks & 15; - t1.stage3_ticks = 0; - } break; - - case 0xff: { //T2OUT -- 4-bit counter value - r = t2.stage3_ticks & 15; - t2.stage3_ticks = 0; - } break; + case 0xfe: { //T1OUT -- 4-bit counter value + r = t1.stage3_ticks & 15; + t1.stage3_ticks = 0; + } break; + case 0xff: { //T2OUT -- 4-bit counter value + r = t2.stage3_ticks & 15; + t2.stage3_ticks = 0; + } break; } } else if(addr < 0xffc0) { r = spcram[addr]; @@ -103,115 +103,112 @@ uint8 r; alwaysinline void sSMP::op_buswrite(uint16 addr, uint8 data) { if((addr & 0xfff0) == 0x00f0) { - //addr >= 0x00f0 && addr >= 0x00ff - if(status.mmio_disabled == true)return; + //addr >= 0x00f0 && addr >= 0x00ff + if(status.mmio_disabled == true) return; switch(addr) { + case 0xf0: { //TEST + if(regs.p.p) break; //writes only valid when P flag is clear - case 0xf0: { //TEST - if(regs.p.p)break; //writes only valid when P flag clear + //multiplier table may not be 100% accurate, some settings crash + //the processor due to S-SMP <> S-DSP bus access misalignment + static uint8 clock_speed_tbl[16] = + { 3, 5, 9, 17, 4, 6, 10, 18, 6, 8, 12, 20, 10, 12, 16, 24 }; - //multiplier table may not be 100% accurate, some settings crash - //the processor due to S-SMP <> S-DSP bus access misalignment - static uint8 clock_speed_tbl[16] = - { 3, 5, 9, 17, 4, 6, 10, 18, 6, 8, 12, 20, 10, 12, 16, 24 }; + status.clock_speed = 24 * clock_speed_tbl[data >> 4] / 3; + status.mmio_disabled = !!(data & 0x04); + status.ram_writable = !!(data & 0x02); - status.clock_speed = 24 * clock_speed_tbl[data >> 4] / 3; - status.mmio_disabled = !!(data & 0x04); - status.ram_writable = !!(data & 0x02); + if((data >> 4) != 0) { + dprintf(source::smp, "* S-SMP critical warning: $00f0 (TEST) clock speed control modified!"); + dprintf(source::smp, "* S-SMP may crash on hardware as a result!"); + } + } break; - if((data >> 4) != 0) { - dprintf(source::smp, "* S-SMP critical warning: $00f0 (TEST) clock speed control modified!"); - dprintf(source::smp, "* S-SMP may crash on hardware as a result!"); - } + case 0xf1: { //CONTROL + status.iplrom_enabled = !!(data & 0x80); - } break; + if(data & 0x30) { + //one-time clearing of APU port read registers, + //emulated by simulating CPU writes of 0x00 + scheduler.sync_smpcpu(); + if(data & 0x20) { + cpu.port_write(2, 0x00); + cpu.port_write(3, 0x00); + } + if(data & 0x10) { + cpu.port_write(0, 0x00); + cpu.port_write(1, 0x00); + } + } - case 0xf1: { //CONTROL - status.iplrom_enabled = !!(data & 0x80); + //0->1 transistion resets timers + if(t2.enabled == false && (data & 0x04)) { + t2.stage2_ticks = 0; + t2.stage3_ticks = 0; + } + t2.enabled = !!(data & 0x04); - if(data & 0x30) { - //one-time clearing of APU port read registers, - //emulated by simulating CPU writes of 0x00 + if(t1.enabled == false && (data & 0x02)) { + t1.stage2_ticks = 0; + t1.stage3_ticks = 0; + } + t1.enabled = !!(data & 0x02); + + if(t0.enabled == false && (data & 0x01)) { + t0.stage2_ticks = 0; + t0.stage3_ticks = 0; + } + t0.enabled = !!(data & 0x01); + } break; + + case 0xf2: { //DSPADDR + status.dsp_addr = data; + } break; + + case 0xf3: { //DSPDATA + //0x80-0xff is a read-only mirror of 0x00-0x7f + if(!(status.dsp_addr & 0x80)) { + dsp.write(status.dsp_addr & 0x7f, data); + } + } break; + + case 0xf4: //CPUIO0 + case 0xf5: //CPUIO1 + case 0xf6: //CPUIO2 + case 0xf7: { //CPUIO3 scheduler.sync_smpcpu(); - if(data & 0x20) { - cpu.port_write(2, 0x00); - cpu.port_write(3, 0x00); - } - if(data & 0x10) { - cpu.port_write(0, 0x00); - cpu.port_write(1, 0x00); - } - } + port_write(addr & 3, data); + } break; - //0->1 transistion resets timers - if(t2.enabled == false && (data & 0x04)) { - t2.stage2_ticks = 0; - t2.stage3_ticks = 0; - } - t2.enabled = !!(data & 0x04); + case 0xf8: { //??? + status.smp_f8 = data; + } break; - if(t1.enabled == false && (data & 0x02)) { - t1.stage2_ticks = 0; - t1.stage3_ticks = 0; - } - t1.enabled = !!(data & 0x02); + case 0xf9: { //??? + status.smp_f9 = data; + } break; - if(t0.enabled == false && (data & 0x01)) { - t0.stage2_ticks = 0; - t0.stage3_ticks = 0; - } - t0.enabled = !!(data & 0x01); - } break; + case 0xfa: { //T0TARGET + t0.target = data; + } break; - case 0xf2: { //DSPADDR - status.dsp_addr = data; - } break; + case 0xfb: { //T1TARGET + t1.target = data; + } break; - case 0xf3: { //DSPDATA - //0x80-0xff is a read-only mirror of 0x00-0x7f - if(!(status.dsp_addr & 0x80)) { - dsp.write(status.dsp_addr & 0x7f, data); - } - } break; - - case 0xf4: //CPUIO0 - case 0xf5: //CPUIO1 - case 0xf6: //CPUIO2 - case 0xf7: { //CPUIO3 - scheduler.sync_smpcpu(); - port_write(addr & 3, data); - } break; - - case 0xf8: { //??? - status.smp_f8 = data; - } break; - - case 0xf9: { //??? - status.smp_f9 = data; - } break; - - case 0xfa: { //T0TARGET - t0.target = data; - } break; - - case 0xfb: { //T1TARGET - t1.target = data; - } break; - - case 0xfc: { //T2TARGET - t2.target = data; - } break; - - case 0xfd: //T0OUT - case 0xfe: //T1OUT - case 0xff: { //T2OUT -- read-only registers - } break; + case 0xfc: { //T2TARGET + t2.target = data; + } break; + case 0xfd: //T0OUT + case 0xfe: //T1OUT + case 0xff: { //T2OUT -- read-only registers + } break; } } -//all writes, even to MMIO registers, appear on bus + //all writes, even to MMIO registers, appear on bus if(status.ram_writable == true) { ram_write(addr, data); } @@ -226,7 +223,7 @@ void sSMP::op_io() { uint8 sSMP::op_read(uint16 addr) { add_clocks(12); -uint8 r = op_busread(addr); + uint8 r = op_busread(addr); add_clocks(12); tick_timers(); return r; @@ -274,3 +271,5 @@ alwaysinline void sSMP::op_writedp(uint8 addr, uint8 data) { op_write((uint(regs.p.p) << 8) + addr, data); } + +#endif //ifdef SSMP_CPP diff --git a/src/smp/ssmp/memory/memory.h b/src/smp/ssmp/memory/memory.h index ca6e1f8d..39ba859d 100644 --- a/src/smp/ssmp/memory/memory.h +++ b/src/smp/ssmp/memory/memory.h @@ -1,29 +1,29 @@ - uint8 ram_read (uint16 addr); - void ram_write(uint16 addr, uint8 data); + uint8 ram_read(uint16 addr); + void ram_write(uint16 addr, uint8 data); - uint8 port_read (uint8 port); - void port_write(uint8 port, uint8 data); + uint8 port_read(uint8 port); + void port_write(uint8 port, uint8 data); /***** * core SMP bus functions *****/ - uint8 op_busread (uint16 addr); - void op_buswrite(uint16 addr, uint8 data); + uint8 op_busread(uint16 addr); + void op_buswrite(uint16 addr, uint8 data); - void op_io (); - uint8 op_read (uint16 addr); - void op_write(uint16 addr, uint8 data); + void op_io(); + uint8 op_read(uint16 addr); + void op_write(uint16 addr, uint8 data); /***** * helper memory addressing functions used by SMP core *****/ - uint8 op_readpc (); + uint8 op_readpc(); - uint8 op_readstack (); - void op_writestack(uint8 data); + uint8 op_readstack(); + void op_writestack(uint8 data); - uint8 op_readaddr (uint16 addr); - void op_writeaddr (uint16 addr, uint8 data); + uint8 op_readaddr(uint16 addr); + void op_writeaddr(uint16 addr, uint8 data); - uint8 op_readdp (uint8 addr); - void op_writedp (uint8 addr, uint8 data); + uint8 op_readdp(uint8 addr); + void op_writedp(uint8 addr, uint8 data); diff --git a/src/smp/ssmp/ssmp.cpp b/src/smp/ssmp/ssmp.cpp index c7c237c3..16b03b00 100644 --- a/src/smp/ssmp/ssmp.cpp +++ b/src/smp/ssmp/ssmp.cpp @@ -1,18 +1,20 @@ -#include "../../base.h" +#include "../../base.h" +#define SSMP_CPP #include "core/core.cpp" #include "memory/memory.cpp" #include "timing/timing.cpp" -void sSMP::power() { -//for(int i = 0; i < 65536; i += 64) { +void sSMP::power() { +//SNES hardware SPCRAM contains pseudo-random data upon power up +//for(unsigned i = 0; i < 65536; i += 64) { // memset(spcram + i, 0x00, 32); // memset(spcram + i + 32, 0xff, 32); //} memset(spcram, 0x00, 65536); -//targets not initialized/changed upon reset + //targets not initialized/changed upon reset t0.target = 0; t1.target = 0; t2.target = 0; @@ -31,18 +33,18 @@ void sSMP::reset() { status.clock_counter = 0; status.dsp_counter = 0; -//$00f0 + //$00f0 status.clock_speed = 24 * 3 / 3; status.mmio_disabled = false; status.ram_writable = true; -//$00f1 + //$00f1 status.iplrom_enabled = true; -//$00f2 + //$00f2 status.dsp_addr = 0x00; -//$00f8,$00f9 + //$00f8,$00f9 status.smp_f8 = 0x00; status.smp_f9 = 0x00; diff --git a/src/smp/ssmp/ssmp.h b/src/smp/ssmp/ssmp.h index db612227..e412520f 100644 --- a/src/smp/ssmp/ssmp.h +++ b/src/smp/ssmp/ssmp.h @@ -3,36 +3,36 @@ public: void enter(); public: -#include "core/core.h" -#include "memory/memory.h" -#include "timing/timing.h" + #include "core/core.h" + #include "memory/memory.h" + #include "timing/timing.h" -struct { - uint8 opcode; - bool in_opcode; + struct { + uint8 opcode; + bool in_opcode; -//timing - uint32 clock_counter; - uint32 dsp_counter; + //timing + uint32 clock_counter; + uint32 dsp_counter; -//$00f0 - uint8 clock_speed; - bool mmio_disabled; - bool ram_writable; + //$00f0 + uint8 clock_speed; + bool mmio_disabled; + bool ram_writable; -//$00f1 - bool iplrom_enabled; + //$00f1 + bool iplrom_enabled; -//$00f2 - uint8 dsp_addr; + //$00f2 + uint8 dsp_addr; -//$00f8,$00f9 - uint8 smp_f8, smp_f9; -} status; + //$00f8,$00f9 + uint8 smp_f8, smp_f9; + } status; -//ssmp.cpp - void power(); - void reset(); + //ssmp.cpp + void power(); + void reset(); sSMP(); ~sSMP(); diff --git a/src/smp/ssmp/timing/timing.cpp b/src/smp/ssmp/timing/timing.cpp index 22f1cc54..508a2d66 100644 --- a/src/smp/ssmp/timing/timing.cpp +++ b/src/smp/ssmp/timing/timing.cpp @@ -1,3 +1,5 @@ +#ifdef SSMP_CPP + alwaysinline void sSMP::add_clocks(uint clocks) { scheduler.addclocks_smp(clocks); @@ -9,3 +11,5 @@ void sSMP::tick_timers() { t1.tick(); t2.tick(); } + +#endif //ifdef SSMP_CPP diff --git a/src/smp/ssmp/timing/timing.h b/src/smp/ssmp/timing/timing.h index afb88d61..0f7376be 100644 --- a/src/smp/ssmp/timing/timing.h +++ b/src/smp/ssmp/timing/timing.h @@ -1,33 +1,34 @@ -template class sSMPTimer { +template +class sSMPTimer { public: -uint8 target; -uint8 stage1_ticks, stage2_ticks, stage3_ticks; -bool enabled; + uint8 target; + uint8 stage1_ticks, stage2_ticks, stage3_ticks; + bool enabled; void tick() { - //stage 1 increment + //stage 1 increment stage1_ticks++; - if(stage1_ticks < cycle_frequency)return; + if(stage1_ticks < cycle_frequency) return; stage1_ticks -= cycle_frequency; - if(enabled == false)return; + if(enabled == false) return; - //stage 2 increment + //stage 2 increment stage2_ticks++; - if(stage2_ticks != target)return; + if(stage2_ticks != target) return; - //stage 3 increment + //stage 3 increment stage2_ticks = 0; stage3_ticks++; stage3_ticks &= 15; } }; -sSMPTimer<128> t0; -sSMPTimer<128> t1; -sSMPTimer< 16> t2; + sSMPTimer<128> t0; + sSMPTimer<128> t1; + sSMPTimer< 16> t2; - void add_clocks(uint clocks); - void tick_timers(); + void add_clocks(uint clocks); + void tick_timers(); uint32 clocks_executed(); diff --git a/src/snes/audio/audio.cpp b/src/snes/audio/audio.cpp index e6469b28..30fa1204 100644 --- a/src/snes/audio/audio.cpp +++ b/src/snes/audio/audio.cpp @@ -1,6 +1,8 @@ +#ifdef SNES_CPP + //TODO: move audio logging code to SNESInterface class -void SNES::audio_update(uint16 l_sample, uint16 r_sample) { +void SNES::Audio::update(uint16 l_sample, uint16 r_sample) { if(pcmfp) { fput(pcmfp, l_sample, 2); fput(pcmfp, r_sample, 2); @@ -9,21 +11,21 @@ void SNES::audio_update(uint16 l_sample, uint16 r_sample) { snesinterface.audio_sample(l_sample, r_sample); } -void SNES::log_audio_enable(const char *fn) { - if(pcmfp) { log_audio_disable(); } +void SNES::Audio::log_enable(const char *fn) { + if(pcmfp) log_disable(); -char tfn[256]; + char tfn[256]; if(!fn) { int i = 0; while(i < 1000) { sprintf(tfn, "audio%0.3d.wav", i); pcmfp = fopen(tfn, "rb"); - if(!pcmfp)break; + if(!pcmfp) break; fclose(pcmfp); pcmfp = 0; i++; } - if(i >= 1000)return; + if(i >= 1000) return; } else { strcpy(tfn, fn); } @@ -31,41 +33,50 @@ char tfn[256]; pcmfp = fopen(tfn, "wb"); if(!pcmfp)return; -//header - fwrite("RIFF", 1, 4, pcmfp); -//file size + //header + fwrite("RIFF", 1, 4, pcmfp); + + //file size fputc(0, pcmfp); fputc(0, pcmfp); fputc(0, pcmfp); fputc(0, pcmfp); -//format + + //format fwrite("WAVE", 1, 4, pcmfp); fwrite("fmt ", 1, 4, pcmfp); -//fmt size + + //fmt size fputc(0x12, pcmfp); fputc(0x00, pcmfp); fputc(0x00, pcmfp); fputc(0x00, pcmfp); -//fmt type (PCM) + + //fmt type (PCM) fputc(1, pcmfp); fputc(0, pcmfp); -//channels + + //channels fputc(2, pcmfp); fputc(0, pcmfp); -//sample rate (32000hz) + + //sample rate (32000hz) fputc(0x00, pcmfp); fputc(0x7d, pcmfp); fputc(0x00, pcmfp); fputc(0x00, pcmfp); -//byte rate (32000 * 2 * (16 / 8) + + //byte rate (32000 * 2 * (16 / 8) fputc(0x00, pcmfp); fputc(0xf4, pcmfp); fputc(0x01, pcmfp); fputc(0x00, pcmfp); -//block align (bytes per sample) (4) + + //block align (bytes per sample) (4) fputc(4, pcmfp); fputc(0, pcmfp); -//??? + + //??? fputc(0x10, pcmfp); fputc(0x00, pcmfp); fputc(0x00, pcmfp); @@ -79,18 +90,20 @@ char tfn[256]; fputc(0xf4, pcmfp); fputc(0x01, pcmfp); fputc(0x00, pcmfp); -//data + + //data fwrite("data", 1, 4, pcmfp); -//data size + + //data size fputc(0, pcmfp); fputc(0, pcmfp); fputc(0, pcmfp); fputc(0, pcmfp); } -void SNES::log_audio_disable() { +void SNES::Audio::log_disable() { if(pcmfp) { - int fsize, t; + int fsize, t; fseek(pcmfp, 0, SEEK_END); fsize = ftell(pcmfp); fseek(pcmfp, 4, SEEK_SET); @@ -110,10 +123,12 @@ void SNES::log_audio_disable() { } } -void SNES::audio_init() { +void SNES::Audio::init() { pcmfp = 0; } -void SNES::audio_term() { - log_audio_disable(); +void SNES::Audio::term() { + log_disable(); } + +#endif //ifdef SNES_CPP diff --git a/src/snes/audio/audio.h b/src/snes/audio/audio.h index 7b51c6a5..fe51f3eb 100644 --- a/src/snes/audio/audio.h +++ b/src/snes/audio/audio.h @@ -1,10 +1,14 @@ -FILE *pcmfp; +class Audio { +public: + //if a filename is not specified, one will be generated + //automatically ("audio%0.3d.wav") + void log_enable(const char *fn = 0); + void log_disable(); -//if a filename is not specified, one will be generated -//automatically ("audio%0.3d.wav") - void log_audio_enable(const char *fn = 0); - void log_audio_disable(); - - void audio_update(uint16 l_sample, uint16 r_sample); - void audio_init(); - void audio_term(); + void update(uint16 l_sample, uint16 r_sample); + void init(); + void term(); + +private: + FILE *pcmfp; +} audio; diff --git a/src/snes/input/input.cpp b/src/snes/input/input.cpp index 123438ca..d1ef7fb8 100644 --- a/src/snes/input/input.cpp +++ b/src/snes/input/input.cpp @@ -1,42 +1,45 @@ -bool SNES::port_read(bool port) { +#ifdef SNES_CPP + +bool SNES::Input::port_read(bool port) { if(port == 0) { switch(input.port0_device) { - case DEVICE_NONE: - return false; + case DeviceNone: return false; - default: - if(input.port0_devicebitpos < input.port0_devicebits) { - return input.port0_bits[input.port0_devicebitpos++]; - } else { - return true; + default: { + if(input.port0_devicebitpos < input.port0_devicebits) { + return input.port0_bits[input.port0_devicebitpos++]; + } else { + return true; + } } } } else { switch(input.port1_device) { - case DEVICE_NONE: - return false; + case DeviceNone: return false; - default: - if(input.port1_devicebitpos < input.port1_devicebits) { - return input.port1_bits[input.port1_devicebitpos++]; - } else { - return true; + default: { + if(input.port1_devicebitpos < input.port1_devicebits) { + return input.port1_bits[input.port1_devicebitpos++]; + } else { + return true; + } } } } } -void SNES::port_set_deviceid(bool port, uint deviceid) { +void SNES::Input::port_set_deviceid(bool port, uint deviceid) { if(port == 0) { switch(deviceid) { - case DEVICEID_NONE: - input.port0_device = DEVICE_NONE; - break; - - case DEVICEID_JOYPAD1: - case DEVICEID_JOYPAD2: - input.port0_device = DEVICE_JOYPAD; - input.port0_devicebits = 16; + case DeviceIDNone: { + input.port0_device = DeviceNone; + } break; + + case DeviceIDJoypad1: + case DeviceIDJoypad2: { + input.port0_device = DeviceJoypad; + input.port0_devicebits = 16; + } break; } memset(input.port0_bits, 0, sizeof(input.port0_bits)); @@ -44,14 +47,15 @@ void SNES::port_set_deviceid(bool port, uint deviceid) { input.port0_deviceid = deviceid; } else { switch(deviceid) { - case DEVICEID_NONE: - input.port1_device = DEVICE_NONE; - break; + case DeviceIDNone: { + input.port1_device = DeviceNone; + } break; - case DEVICEID_JOYPAD1: - case DEVICEID_JOYPAD2: - input.port1_device = DEVICE_JOYPAD; - input.port1_devicebits = 16; + case DeviceIDJoypad1: + case DeviceIDJoypad2: { + input.port1_device = DeviceJoypad; + input.port1_devicebits = 16; + } break; } memset(input.port1_bits, 0, sizeof(input.port1_bits)); @@ -60,34 +64,39 @@ void SNES::port_set_deviceid(bool port, uint deviceid) { } } -void SNES::poll_input() { +void SNES::Input::poll() { snesinterface.input_poll(); -bool *p0 = input.port0_bits; -bool *p1 = input.port1_bits; + bool *p0 = input.port0_bits; + bool *p1 = input.port1_bits; + switch(input.port0_device) { - case DEVICE_NONE: - break; + case DeviceNone: break; - default: - for(int i = 0; i < input.port0_devicebits; i++) { *p0++ = snesinterface.input_poll(input.port0_deviceid, i); } - break; + default: { + for(int i = 0; i < input.port0_devicebits; i++) { + *p0++ = snesinterface.input_poll(input.port0_deviceid, i); + } + } break; } switch(input.port1_device) { - case DEVICE_NONE: - break; + case DeviceNone: break; - default: - for(int i = 0; i < input.port1_devicebits; i++) { *p1++ = snesinterface.input_poll(input.port1_deviceid, i); } - break; + default: { + for(int i = 0; i < input.port1_devicebits; i++) { + *p1++ = snesinterface.input_poll(input.port1_deviceid, i); + } + } break; } input.port0_devicebitpos = 0; input.port1_devicebitpos = 0; } -void SNES::input_init() { +void SNES::Input::init() { port_set_deviceid(0, config::snes.controller_port0); port_set_deviceid(1, config::snes.controller_port1); } + +#endif //ifdef SNES_CPP diff --git a/src/snes/input/input.h b/src/snes/input/input.h index e5a94a31..38effaf4 100644 --- a/src/snes/input/input.h +++ b/src/snes/input/input.h @@ -1,32 +1,35 @@ -enum { - DEVICE_NONE = 0, - DEVICE_JOYPAD, -}; +class Input { +public: + enum Device { + DeviceNone, + DeviceJoypad, + }; -enum { - DEVICEID_NONE = 0, - DEVICEID_JOYPAD1, - DEVICEID_JOYPAD2, -}; + enum { + DeviceIDNone, + DeviceIDJoypad1, + DeviceIDJoypad2, + }; -enum { - JOYPAD_B = 0, JOYPAD_Y = 1, - JOYPAD_SELECT = 2, JOYPAD_START = 3, - JOYPAD_UP = 4, JOYPAD_DOWN = 5, - JOYPAD_LEFT = 6, JOYPAD_RIGHT = 7, - JOYPAD_A = 8, JOYPAD_X = 9, - JOYPAD_L = 10, JOYPAD_R = 11, -}; + enum { + JoypadB = 0, JoypadY = 1, + JoypadSelect = 2, JoypadStart = 3, + JoypadUp = 4, JoypadDown = 5, + JoypadLeft = 6, JoypadRight = 7, + JoypadA = 8, JoypadX = 9, + JoypadL = 10, JoypadR = 11, + }; -struct { - uint port0_device, port0_devicebits, port0_devicebitpos, port0_deviceid; - uint port1_device, port1_devicebits, port1_devicebitpos, port1_deviceid; + struct { + uint port0_device, port0_devicebits, port0_devicebitpos, port0_deviceid; + uint port1_device, port1_devicebits, port1_devicebitpos, port1_deviceid; - bool port0_bits[256]; - bool port1_bits[256]; -} input; + bool port0_bits[256]; + bool port1_bits[256]; + } input; bool port_read(bool port); void port_set_deviceid(bool port, uint deviceid); - void input_init(); - void poll_input(); + void init(); + void poll(); +} input; diff --git a/src/snes/interface/interface.h b/src/snes/interface/interface.h index e10a2950..447efb11 100644 --- a/src/snes/interface/interface.h +++ b/src/snes/interface/interface.h @@ -6,15 +6,13 @@ *****/ class SNESInterface { public: - bool video_lock(uint16 *&data, uint &pitch); - void video_unlock(); - void video_refresh(); + void video_refresh(uint16_t *data, unsigned pitch, unsigned *line, unsigned width, unsigned height); - void audio_sample(uint16 l_sample, uint16 r_sample); + void audio_sample(uint16_t l_sample, uint16_t r_sample); -function input_ready; + function input_ready; void input_poll(); - bool input_poll(uint deviceid, uint button); + bool input_poll(unsigned deviceid, unsigned button); void init(); void term(); diff --git a/src/snes/scheduler/scheduler.cpp b/src/snes/scheduler/scheduler.cpp index 6120a23f..0bf4ea9c 100644 --- a/src/snes/scheduler/scheduler.cpp +++ b/src/snes/scheduler/scheduler.cpp @@ -1,14 +1,12 @@ +#ifdef SNES_CPP + Scheduler scheduler; -// - void threadentry_cpu() { cpu.enter(); } void threadentry_smp() { smp.enter(); } -void threadentry_ppu() { } //currently unused +void threadentry_ppu() { } void threadentry_dsp() { dsp.enter(); } -// - void Scheduler::enter() { switch(clock.active) { case THREAD_CPU: co_switch(thread_cpu); break; @@ -23,14 +21,12 @@ void Scheduler::exit() { } void Scheduler::init() { - clock.cpu_freq = snes.region() == SNES::NTSC ? - config::cpu.ntsc_clock_rate : - config::cpu.pal_clock_rate; - clock.smp_freq = snes.region() == SNES::NTSC ? - config::smp.ntsc_clock_rate : - config::smp.pal_clock_rate; - clock.smp_freq &= ~7; //smp_freq must be divisible by eight for dsp_freq - clock.dsp_freq = clock.smp_freq >> 3; + clock.cpu_freq = snes.region() == SNES::NTSC + ? config::cpu.ntsc_clock_rate + : config::cpu.pal_clock_rate; + clock.smp_freq = snes.region() == SNES::NTSC + ? config::smp.ntsc_clock_rate + : config::smp.pal_clock_rate; clock.active = THREAD_CPU; clock.cpusmp = 0; @@ -47,9 +43,7 @@ void Scheduler::init() { thread_ppu = co_create(sizeof(void*) * 64 * 1024, threadentry_ppu); thread_dsp = co_create(sizeof(void*) * 64 * 1024, threadentry_dsp); } - -// - + Scheduler::Scheduler() { thread_snes = 0; thread_cpu = 0; @@ -57,3 +51,5 @@ Scheduler::Scheduler() { thread_ppu = 0; thread_dsp = 0; } + +#endif //ifdef SNES_CPP diff --git a/src/snes/scheduler/scheduler.h b/src/snes/scheduler/scheduler.h index 5a89e974..d1fb5d28 100644 --- a/src/snes/scheduler/scheduler.h +++ b/src/snes/scheduler/scheduler.h @@ -1,31 +1,28 @@ class Scheduler { public: + cothread_t thread_snes; + cothread_t thread_cpu; + cothread_t thread_smp; + cothread_t thread_ppu; //currently unused + cothread_t thread_dsp; -cothread_t thread_snes; -cothread_t thread_cpu; -cothread_t thread_smp; -cothread_t thread_ppu; //currently unused -cothread_t thread_dsp; + enum ActiveThread { + THREAD_CPU, + THREAD_SMP, + THREAD_PPU, + THREAD_DSP, + }; -enum ActiveThread { - THREAD_CPU, - THREAD_SMP, - THREAD_PPU, - THREAD_DSP, -}; + struct { + uint cpu_freq; + uint smp_freq; -struct { - uint cpu_freq; - uint smp_freq; - uint ppu_freq; - uint dsp_freq; + ActiveThread active; + int64 cpusmp; + int64 smpdsp; + } clock; - ActiveThread active; - int64 cpusmp; - int64 smpdsp; -} clock; - -// + // CPU <> SMP alwaysinline void sync_cpusmp() { if(clock.cpusmp < 0) { @@ -41,7 +38,7 @@ struct { } } -// + // SMP <> DSP alwaysinline void sync_smpdsp() { if(clock.smpdsp < 0) { @@ -57,27 +54,25 @@ struct { } } -// + // Timing alwaysinline void addclocks_cpu(uint clocks) { clock.cpusmp -= clocks * (uint64)clock.smp_freq; - if(clock.cpusmp < -(250000 * (int64)20000000)) { sync_cpusmp(); } + if(clock.cpusmp < -(250000 * (int64)20000000)) sync_cpusmp(); } alwaysinline void addclocks_smp(uint clocks) { clock.cpusmp += clocks * (uint64)clock.cpu_freq; - if(clock.cpusmp > +(250000 * (int64)20000000)) { sync_smpcpu(); } - clock.smpdsp -= clocks * (uint64)clock.dsp_freq; + if(clock.cpusmp > +(250000 * (int64)20000000)) sync_smpcpu(); + clock.smpdsp -= clocks; sync_smpdsp(); } alwaysinline void addclocks_dsp(uint clocks) { - clock.smpdsp += clocks * (uint64)clock.smp_freq; + clock.smpdsp += clocks; sync_dspsmp(); } -// - void enter(); void exit(); void init(); diff --git a/src/snes/snes.cpp b/src/snes/snes.cpp index de5a1357..4cd7f5b8 100644 --- a/src/snes/snes.cpp +++ b/src/snes/snes.cpp @@ -1,7 +1,6 @@ #include "../base.h" +#define SNES_CPP -cothread_t co_active_ = 0; - SNES snes; BSXBase bsxbase; BSXCart bsxcart; @@ -43,14 +42,14 @@ void SNES::init() { obc1.init(); st010.init(); - video_init(); - audio_init(); - input_init(); + video.init(); + audio.init(); + input.init(); snesinterface.init(); } void SNES::term() { - audio_term(); + audio.term(); snesinterface.term(); } @@ -76,12 +75,12 @@ void SNES::power() { if(cartridge.info.obc1) obc1.power(); if(cartridge.info.st010) st010.power(); - for(int i = 0x2100; i <= 0x213f; i++) memory::mmio.map(i, ppu); - for(int i = 0x2140; i <= 0x217f; i++) memory::mmio.map(i, cpu); - for(int i = 0x2180; i <= 0x2183; i++) memory::mmio.map(i, cpu); - for(int i = 0x4016; i <= 0x4017; i++) memory::mmio.map(i, cpu); - for(int i = 0x4200; i <= 0x421f; i++) memory::mmio.map(i, cpu); - for(int i = 0x4300; i <= 0x437f; i++) memory::mmio.map(i, cpu); + for(uint16_t i = 0x2100; i <= 0x213f; i++) memory::mmio.map(i, ppu); + for(uint16_t i = 0x2140; i <= 0x217f; i++) memory::mmio.map(i, cpu); + for(uint16_t i = 0x2180; i <= 0x2183; i++) memory::mmio.map(i, cpu); + for(uint16_t i = 0x4016; i <= 0x4017; i++) memory::mmio.map(i, cpu); + for(uint16_t i = 0x4200; i <= 0x421f; i++) memory::mmio.map(i, cpu); + for(uint16_t i = 0x4300; i <= 0x437f; i++) memory::mmio.map(i, cpu); if(cartridge.info.bsxbase) bsxbase.enable(); if(cartridge.info.bsxcart) bsxcart.enable(); @@ -96,7 +95,7 @@ void SNES::power() { if(cartridge.info.obc1) obc1.enable(); if(cartridge.info.st010) st010.enable(); - video_update(); + video.update(); } void SNES::reset() { @@ -121,36 +120,19 @@ void SNES::reset() { if(cartridge.info.obc1) obc1.reset(); if(cartridge.info.st010) st010.reset(); - video_update(); + video.update(); } void SNES::scanline() { - video_scanline(); + video.scanline(); -//draw before the start of the next frame, to make the -//video output seem more responsive to controller input if(cpu.vcounter() == 241) { - video_update(); + video.update(); scheduler.exit(); } } void SNES::frame() {} - -/***** - * PAL/NTSC - *****/ - -void SNES::set_region(uint8 new_region) { - if(new_region == NTSC) { - snes_region = NTSC; - } else if(new_region == PAL) { - snes_region = PAL; - } else { - alert("Unsupported region : %0.2x", new_region); - } -} - -uint8 SNES::region() { return snes_region; } - +void SNES::set_region(Region region) { snes_region = region; } +SNES::Region SNES::region() { return snes_region; } SNES::SNES() {} diff --git a/src/snes/snes.h b/src/snes/snes.h index 65348e57..a52aaba6 100644 --- a/src/snes/snes.h +++ b/src/snes/snes.h @@ -5,13 +5,10 @@ class VideoFilter; class SNES { -protected: -uint8 snes_region; - public: -enum { NTSC = 0, PAL = 1 }; + enum Region { NTSC = 0, PAL = 1 }; -//system functions + //system functions virtual void run(); virtual void runtoframe(); @@ -23,18 +20,19 @@ enum { NTSC = 0, PAL = 1 }; virtual void frame(); virtual void scanline(); -//PAL/NTSC - uint8 region(); - void set_region(uint8 new_region); + //PAL/NTSC + Region region(); + void set_region(Region); -#include "video/video.h" -#include "audio/audio.h" -#include "input/input.h" + #include "video/video.h" + #include "audio/audio.h" + #include "input/input.h" SNES(); - virtual ~SNES() {} + virtual ~SNES() {} + +private: + Region snes_region; }; extern SNES snes; - -#include "video/filter.h" diff --git a/src/snes/tracer/tracer.cpp b/src/snes/tracer/tracer.cpp index a7e7c6f4..4f7f1bc9 100644 --- a/src/snes/tracer/tracer.cpp +++ b/src/snes/tracer/tracer.cpp @@ -1,3 +1,5 @@ +#ifdef SNES_CPP + Tracer tracer; void tprintf(const char *s, ...) { @@ -91,3 +93,5 @@ Tracer::Tracer() { Tracer::~Tracer() { } + +#endif //ifdef SNES_CPP diff --git a/src/snes/video/filter.cpp b/src/snes/video/filter.cpp deleted file mode 100644 index 80950ddb..00000000 --- a/src/snes/video/filter.cpp +++ /dev/null @@ -1,6 +0,0 @@ -#include "video_colortable.cpp" -#include "video_normalize.cpp" -#include "filter_direct.cpp" -#include "filter_ntsc.cpp" -#include "filter_hq2x.cpp" -#include "filter_scale2x.cpp" diff --git a/src/snes/video/filter.h b/src/snes/video/filter.h deleted file mode 100644 index a8f2c2ce..00000000 --- a/src/snes/video/filter.h +++ /dev/null @@ -1,35 +0,0 @@ -enum { - VIDEOFILTER_DIRECT, - VIDEOFILTER_NTSC, - VIDEOFILTER_HQ2X, - VIDEOFILTER_SCALE2X, -}; - -//all video filters must derive from this class -class VideoFilter { -public: -/*[run] - * uint32 *colortbl - Color lookup table to convert BGR555 to current pixel format - * uint16 *data - Input data buffer (always in system memory) - * uint32 width - Input width (always 256 or 512) - * uint32 height - Input height (<=240 = progressive, >240 = interlace) - * uint32 pitch - Input data buffer bytes per scanline (always 1024) - * uint16 *output - Output data buffer (sometimes in video memory, never read from here!) - * uint32 max_width - Output data buffer width limit (do not write past buffer) - * uint32 max_height - Output data buffer height limit (do not write past buffer) - * uint32 output_pitch - Output data buffer bytes per scanline - * uint32 req_width - Requested output width (can be ignored if filter resizes to fixed size) - * uint32 req_height - Requested output height (can be ignored if filter resizes to fixed size) - * uint32 &result_width - Actual rendered width by filter (input is undefined, write only) - * uint32 &result_height - Actual rendered height by filter (input is undefined, write only) - */ - virtual void run(uint32 *colortbl, uint16 *data, uint32 width, uint32 height, uint32 pitch, - uint16 *output, uint32 max_width, uint32 max_height, uint32 output_pitch, - uint32 req_width, uint32 req_height, uint &result_width, uint &result_height, uint16 *scanline_widths = 0) = 0; - virtual ~VideoFilter() {} -}; - -#include "filter_direct.h" -#include "filter_ntsc.h" -#include "filter_hq2x.h" -#include "filter_scale2x.h" diff --git a/src/snes/video/filter_direct.cpp b/src/snes/video/filter_direct.cpp deleted file mode 100644 index bf3f2d1e..00000000 --- a/src/snes/video/filter_direct.cpp +++ /dev/null @@ -1,27 +0,0 @@ -void DirectVideoFilter::run( -uint32 *colortbl, uint16 *data, uint32 width, uint32 height, uint32 pitch, -uint16 *output, uint32 max_width, uint32 max_height, uint32 output_pitch, -uint32 req_width, uint32 req_height, uint &result_width, uint &result_height, -uint16 *scanline_widths -) { - pitch >>= 1; - output_pitch >>= 1; - - for(int y = 0; y < height; y++) { - if(width == 512 && scanline_widths[y] == 256) { - for(int x = 0; x < 256; x++) { - *output++ = colortbl[*data]; - *output++ = colortbl[*data++]; - } - data += 256; - } else { - for(int x = 0; x < width; x++) { - *output++ = colortbl[*data++]; - } - } - data += pitch - width; - output += output_pitch - width; - } - result_width = width; - result_height = height; -} diff --git a/src/snes/video/filter_direct.h b/src/snes/video/filter_direct.h deleted file mode 100644 index 97c76299..00000000 --- a/src/snes/video/filter_direct.h +++ /dev/null @@ -1,6 +0,0 @@ -class DirectVideoFilter : public VideoFilter { -public: - void run(uint32 *colortbl, uint16 *data, uint32 width, uint32 height, uint32 pitch, - uint16 *output, uint32 max_width, uint32 max_height, uint32 output_pitch, - uint32 req_width, uint32 req_height, uint &result_width, uint &result_height, uint16 *scanline_widths = 0); -}; diff --git a/src/snes/video/filter_hq2x.cpp b/src/snes/video/filter_hq2x.cpp deleted file mode 100644 index 5688fc22..00000000 --- a/src/snes/video/filter_hq2x.cpp +++ /dev/null @@ -1,180 +0,0 @@ -/***** - * HQ2x Algorithm (C) 2003 Maxim Stepin - * License: LGPL - * - * Optimizations (C) 2006 Shay Green, byuu - *****/ - -#define diff(x, y) ((yuvtable[x] - yuvtable[y] + diff_offset) & diff_mask) -#define hdiff(x, y) ((x - yuvtable[y]) & diff_mask) -#define expand_rgb(n) { n |= n << 16; n &= 0x03e07c1f; } -#define pack_rgb(n) { n &= 0x03e07c1f; n |= n >> 16; } - -static uint16 blend1(uint32 c1, uint32 c2) { - expand_rgb(c1); - expand_rgb(c2); - c1 = (c1 * 3 + c2) >> 2; - pack_rgb(c1); - return c1; -} - -static uint16 blend2(uint32 c1, uint32 c2, uint32 c3) { -//c1 = (c1 * 2 + c2 + c3) >> 2; - c2 = (c2 + c3 - ((c2 ^ c3) & 0x0421)) >> 1; - c1 = (c1 + c2 - ((c1 ^ c2) & 0x0421)) >> 1; - return c1; -} - -static uint16 blend6(uint32 c1, uint32 c2, uint32 c3) { - expand_rgb(c1); - expand_rgb(c2); - expand_rgb(c3); - c1 = (c1 * 5 + c2 * 2 + c3) >> 3; - pack_rgb(c1); - return c1; -} - -static uint16 blend7(uint32 c1, uint32 c2, uint32 c3) { - expand_rgb(c1); - expand_rgb(c2); - expand_rgb(c3); - c1 = (c1 * 6 + c2 + c3) >> 3; - pack_rgb(c1); - return c1; -} - -static uint16 blend9(uint32 c1, uint32 c2, uint32 c3) { - expand_rgb(c1); - expand_rgb(c2); - expand_rgb(c3); - c1 = (c1 * 2 + (c2 + c3) * 3) >> 3; - pack_rgb(c1); - return c1; -} - -static uint16 blend10(uint32 c1, uint32 c2, uint32 c3) { - expand_rgb(c1); - expand_rgb(c2); - expand_rgb(c3); - c1 = (c1 * 14 + c2 + c3) >> 4; - pack_rgb(c1); - return c1; -} - -void HQ2xVideoFilter::run( -uint32 *colortbl, uint16 *data, uint32 width, uint32 height, uint32 pitch, -uint16 *output, uint32 max_width, uint32 max_height, uint32 output_pitch, -uint32 req_width, uint32 req_height, uint &result_width, uint &result_height, -uint16 *scanline_widths -) { - pitch >>= 1; - output_pitch >>= 1; - - if(width == 256 && height <= 240) { - lores_progressive(colortbl, data, height, pitch, output, output_pitch); - result_width = 512; - result_height = height * 2; - return; - } - - for(int y = 0; y < height; y++) { - if(width == 512 && scanline_widths[y] == 256) { - for(int x = 0; x < 256; x++) { - *output++ = colortbl[*data]; - *output++ = colortbl[*data++]; - } - data += 256; - } else { - for(int x = 0; x < width; x++) { - *output++ = colortbl[*data++]; - } - } - data += pitch - width; - output += output_pitch - width; - } - result_width = width; - result_height = height; -} - -void HQ2xVideoFilter::lores_progressive( -uint32 *colortbl, uint16 *data, uint32 height, uint32 pitch, -uint16 *output, uint32 output_pitch -) { -uint16 *out0 = (uint16*)(output); -uint16 *out1 = (uint16*)(output + output_pitch); -#define W1 data[-1 - (SNES::VIDEOPITCH_LORES >> 1)] -#define W2 data[ 0 - (SNES::VIDEOPITCH_LORES >> 1)] -#define W3 data[+1 - (SNES::VIDEOPITCH_LORES >> 1)] -#define W4 data[-1] -#define W5 data[ 0] -#define W6 data[+1] -#define W7 data[-1 + (SNES::VIDEOPITCH_LORES >> 1)] -#define W8 data[ 0 + (SNES::VIDEOPITCH_LORES >> 1)] -#define W9 data[+1 + (SNES::VIDEOPITCH_LORES >> 1)] - - data += pitch; - memset(out0, 0, 1024); out0 += output_pitch << 1; - memset(out1, 0, 1024); out1 += output_pitch << 1; - - for(int y = height - 2; y; --y) { - data++; - *(uint32*)out0 = 0; out0 += 2; - *(uint32*)out1 = 0; out1 += 2; - - int32 pattern = diff(W5, W4) ? 0x10 : 0x00; - for(int x = 256 - 2; x; --x) { - uint32 center = yuvtable[W5] + diff_offset; - //W4 for pixel x+1 is the same as W6 for pixel x - pattern = (pattern & 0x10) >> 1; - pattern |= hdiff(center, W1) ? 0x01 : 0x00; - pattern |= hdiff(center, W2) ? 0x02 : 0x00; - pattern |= hdiff(center, W3) ? 0x04 : 0x00; - //pattern |= hdiff(center, W4) ? 0x08 : 0x00; - pattern |= hdiff(center, W6) ? 0x10 : 0x00; - pattern |= hdiff(center, W7) ? 0x20 : 0x00; - pattern |= hdiff(center, W8) ? 0x40 : 0x00; - pattern |= hdiff(center, W9) ? 0x80 : 0x00; - - switch(pattern) { - #include "filter_hq2x_lookuptbl.h" - } - - data++; - out0 += 2; - out1 += 2; - } - - data++; - *(uint32*)out0 = 0; out0 += 2; - *(uint32*)out1 = 0; out1 += 2; - - data += pitch - 256; - out0 += output_pitch + output_pitch - 512; - out1 += output_pitch + output_pitch - 512; - } - - memset(out0, 0, 1024); - memset(out1, 0, 1024); -} - -HQ2xVideoFilter::HQ2xVideoFilter() { - for(int i = 0; i < 32768; i++) { - int ir = (i) & 0x1f; - int ig = (i >> 5) & 0x1f; - int ib = (i >> 10) & 0x1f; - - //bgr555->bgr888 - double r = (ir << 3) | (ir >> 2); - double g = (ig << 3) | (ig >> 2); - double b = (ib << 3) | (ib >> 2); - - //bgr888->yuv888 - double y = (r + g + b) * (0.25f * (63.5f / 48.0f)); - double u = ((r - b) * 0.25f + 128.0f) * (7.5f / 7.0f); - double v = ((g * 2.0f - r - b) * 0.125f + 128.0f) * (7.5f / 6.0f); - yuvtable[i] = (int(y) << 21) + (int(u) << 11) + (int(v)); - } - - diff_offset = (0x440 << 21) + (0x207 << 11) + 0x407; - diff_mask = (0x380 << 21) + (0x1f0 << 11) + 0x3f0; -} diff --git a/src/snes/video/filter_hq2x.h b/src/snes/video/filter_hq2x.h deleted file mode 100644 index 32419131..00000000 --- a/src/snes/video/filter_hq2x.h +++ /dev/null @@ -1,13 +0,0 @@ -class HQ2xVideoFilter : public VideoFilter { -public: -uint32 yuvtable[32768]; -uint32 diff_offset, diff_mask; - void run(uint32 *colortbl, uint16 *data, uint32 width, uint32 height, uint32 pitch, - uint16 *output, uint32 max_width, uint32 max_height, uint32 output_pitch, - uint32 req_width, uint32 req_height, uint &result_width, uint &result_height, uint16 *scanline_widths = 0); - - void lores_progressive(uint32 *colortbl, uint16 *data, uint32 height, uint32 pitch, - uint16 *output, uint32 output_pitch); - - HQ2xVideoFilter(); -}; diff --git a/src/snes/video/filter_ntsc.cpp b/src/snes/video/filter_ntsc.cpp deleted file mode 100644 index e2532d05..00000000 --- a/src/snes/video/filter_ntsc.cpp +++ /dev/null @@ -1,83 +0,0 @@ -#include "ntsc/snes_ntsc.c" - -NtscVideoFilter::NtscVideoFilter() { - ntsc = 0; - -//to do: defer initialization? - adjust(0, 0, 0, 0, 0, bool(config::snes.ntsc_merge_fields), 0); -} - -NtscVideoFilter::~NtscVideoFilter() { - safe_free(ntsc); -} - -void NtscVideoFilter::adjust(float hue, float saturation, float contrast, -float brightness, float sharpness, bool merge_fields, uint32 *colortbl) { -static snes_ntsc_setup_t defaults; -snes_ntsc_setup_t setup = defaults; - setup.hue = hue; - setup.saturation = saturation; - setup.contrast = contrast; - setup.brightness = brightness; - setup.sharpness = sharpness; - setup.resolution = sharpness; - setup.merge_fields = merge_fields; - setup.bsnes_colortbl = (unsigned long*)colortbl; - - if(!ntsc) { - ntsc = (snes_ntsc_t*)malloc(sizeof *ntsc); - if(!ntsc) { - return; //to do: report out of memory error - } - } - - burst = 0; - burst_toggle = (merge_fields ? 0 : 1); // don't toggle burst when fields are merged - snes_ntsc_init(ntsc, &setup); -} - -void NtscVideoFilter::run( -uint32 *colortbl, uint16 *data, uint32 width, uint32 height, uint32 pitch, -uint16 *output, uint32 max_width, uint32 max_height, uint32 output_pitch, -uint32 req_width, uint32 req_height, uint &result_width, uint &result_height, -uint16 *scanline_widths) -{ - if(!ntsc)return; - -int const out_width = SNES_NTSC_OUT_WIDTH(256); -int const out_height = height; - result_width = out_width; - result_height = out_height; - burst ^= burst_toggle; - - if(!scanline_widths) { - if(width == 256) { - snes_ntsc_blit(ntsc, data, (pitch >> 1), burst, out_width, out_height, output, output_pitch); - } else { - snes_ntsc_blit_hires(ntsc, data, (pitch >> 1), burst, out_width, out_height, output, output_pitch); - } - } else { - //blit multiple scanlines of same width, rather than one at a time - int run_start = 0; - int run_width = scanline_widths[0]; - int line = 0; - - while(1) { - if(run_width != scanline_widths[line] || line >= out_height) { - uint16 const *in = (uint16*)((uint8*)data + pitch * run_start); - uint16 *out = (uint16*)((uint8*)output + output_pitch * run_start); - int height = line - run_start; - int line_burst = (burst + run_start) % 3; - if(run_width == 256) { - snes_ntsc_blit(ntsc, in, (pitch >> 1), line_burst, out_width, height, out, output_pitch); - } else { - snes_ntsc_blit_hires(ntsc, in, (pitch >> 1), line_burst, out_width, height, out, output_pitch); - } - if(line >= out_height)break; - run_width = scanline_widths[line]; - run_start = line; - } - line++; - } - } -} diff --git a/src/snes/video/filter_ntsc.h b/src/snes/video/filter_ntsc.h deleted file mode 100644 index 7a79b43e..00000000 --- a/src/snes/video/filter_ntsc.h +++ /dev/null @@ -1,24 +0,0 @@ -#include "ntsc/snes_ntsc.h" - -class NtscVideoFilter : public VideoFilter { -private: -struct snes_ntsc_t *ntsc; -int burst, burst_toggle; - -public: -// - Use 0 for any parameter to use default value -// - Colortbl must map to 16-bit RGB - void adjust(float hue, float saturation, float contrast, float brightness, - float sharpness, bool merge_fields, uint32 *colortbl); - -// - Output is currently always 16-bit RGB -// - Scanline_widths [i] contains the number of source pixels for scanline i, -// where 0 < i < height - void run(uint32 *colortbl, uint16 *data, uint32 width, uint32 height, uint32 pitch, - uint16 *output, uint32 max_width, uint32 max_height, uint32 output_pitch, - uint32 req_width, uint32 req_height, uint &result_width, uint &result_height, - uint16 *scanline_widths = 0); - - NtscVideoFilter(); - ~NtscVideoFilter(); -}; diff --git a/src/snes/video/filter_scale2x.cpp b/src/snes/video/filter_scale2x.cpp deleted file mode 100644 index 47f9aa0b..00000000 --- a/src/snes/video/filter_scale2x.cpp +++ /dev/null @@ -1,64 +0,0 @@ -void Scale2xVideoFilter::run( -uint32 *colortbl, uint16 *data, uint32 width, uint32 height, uint32 pitch, -uint16 *output, uint32 max_width, uint32 max_height, uint32 output_pitch, -uint32 req_width, uint32 req_height, uint &result_width, uint &result_height, -uint16 *scanline_widths -) { - pitch >>= 1; - output_pitch >>= 1; - if(width == 256 && height <= 240) { - lores_progressive(colortbl, data, height, pitch, output, output_pitch); - result_width = 512; - result_height = height * 2; - } else { - int w = (width <= max_width) ? width : max_width; - int h = (height <= max_height) ? height : max_height; - for(int y = 0; y < h; y++) { - for(int x = 0; x < w; x++) { - *output++ = colortbl[*data++]; - } - data += pitch - w; - output += output_pitch - w; - } - result_width = width; - result_height = height; - } -} - -void Scale2xVideoFilter::lores_progressive( -uint32 *colortbl, uint16 *data, uint32 height, uint32 pitch, -uint16 *output, uint32 output_pitch -) { -uint16 A, B, C, D, P; -int32 prevline, nextline; -//.A. ->.AA. -//BpC ->BppC -//.D. ->BppC -// ->.DD. - for(int y = 0; y < height; y++) { - prevline = (y > 0) ? -pitch : 0; - nextline = (y < height - 1) ? pitch : 0; - for(int x = 0; x < 256; x++) { - A = *(data + prevline); - B = (x > 0) ? *(data - 1) : *data; - C = (x < 255) ? *(data + 1) : *data; - D = *(data + nextline); - P = colortbl[*(data)]; - if(A != D && B != C) { - *(output) = A == B ? colortbl[A] : P; - *(output + 1) = A == C ? colortbl[A] : P; - *(output + output_pitch) = D == B ? colortbl[D] : P; - *(output + output_pitch + 1) = D == C ? colortbl[D] : P; - } else { - *(output) = P; - *(output + 1) = P; - *(output + output_pitch) = P; - *(output + output_pitch + 1) = P; - } - data++; - output += 2; - } - data += pitch - 256; - output += output_pitch + output_pitch - 512; - } -} diff --git a/src/snes/video/filter_scale2x.h b/src/snes/video/filter_scale2x.h deleted file mode 100644 index dd338f54..00000000 --- a/src/snes/video/filter_scale2x.h +++ /dev/null @@ -1,9 +0,0 @@ -class Scale2xVideoFilter : public VideoFilter { -public: - void run(uint32 *colortbl, uint16 *data, uint32 width, uint32 height, uint32 pitch, - uint16 *output, uint32 max_width, uint32 max_height, uint32 output_pitch, - uint32 req_width, uint32 req_height, uint &result_width, uint &result_height, uint16 *scanline_widths = 0); - - void lores_progressive(uint32 *colortbl, uint16 *data, uint32 height, uint32 pitch, - uint16 *output, uint32 output_pitch); -}; diff --git a/src/snes/video/video.cpp b/src/snes/video/video.cpp index 212c4b6e..596fb648 100644 --- a/src/snes/video/video.cpp +++ b/src/snes/video/video.cpp @@ -1,118 +1,58 @@ -#include "filter.cpp" - -void SNES::set_video_filter(uint video_filter) { - video_format.filter = video_filter; - video_format.modified = true; +#ifdef SNES_CPP + +void SNES::Video::set_mode(Mode mode_) { + mode = mode_; } -void SNES::set_video_standard(uint video_standard) { - video_format.video_standard = video_standard; - video_format.modified = true; -} - -void SNES::set_video_pixel_format(uint pixel_format) { - video_format.pixel_format = pixel_format; - video_format.modified = true; -} - -/***** - * Internal function called at the start of each frame. - * Use SNES::set_video_format to modify these options. - *****/ -void SNES::update_video_format() { - if(video_format.modified == false)return; - video_format.modified = false; - - video.filter = video_format.filter; - safe_delete(video_filter); - switch(video.filter) { - default: - case VIDEOFILTER_DIRECT: video_filter = new DirectVideoFilter(); break; - case VIDEOFILTER_NTSC: video_filter = new NtscVideoFilter(); break; - case VIDEOFILTER_HQ2X: video_filter = new HQ2xVideoFilter(); break; - case VIDEOFILTER_SCALE2X: video_filter = new Scale2xVideoFilter(); break; - } - - video.video_standard = video_format.video_standard; - video.pixel_format = video_format.pixel_format; - update_color_lookup_table(); -} - -void SNES::get_video_info(video_info *info) { - info->filter = video.filter; - info->video_standard = video.video_standard; - info->pixel_format = video.pixel_format; - info->width = video.width; - info->height = video.height; -} - -void SNES::video_update() { - if(ppu.renderer_enabled()) { - update_video_format(); - - video.ppu_data = (uint16*)ppu.output; -// video_normalize(); - - switch(video.video_standard) { - default: - case VIDEOSTANDARD_NTSC: - video.raster_width = 256; - video.raster_height = 224; - video.ppu_data += (int(cpu.overscan()) << 13) + 1024; - break; - case VIDEOSTANDARD_PAL: - video.raster_width = 256; - video.raster_height = 239; - video.ppu_data += 1024; - break; - } - - if(video.frame_hires) { video.raster_width <<= 1; } - if(video.frame_interlace) { video.raster_height <<= 1; } - - if(snesinterface.video_lock(video.data, video.pitch) == true) { - video_filter->run(color_lookup_table, video.ppu_data, - video.raster_width, video.raster_height, - video.raster_height <= 240 ? 2048 : 1024, - video.data, 512, 480, video.pitch, - 512, 480, video.width, video.height, - video.raster_height <= 240 ? (pline_width + 1) : (iline_width + 2)); - snesinterface.video_unlock(); +void SNES::Video::update() { + uint16_t *data = (uint16_t*)ppu.output; + unsigned width, height; + + switch(mode) { default: + case ModeNTSC: { + width = 256; + height = 224; + data += ((int)ppu.overscan() << 13) + 1024; + } break; + case ModePAL: { + width = 256; + height = 239; + data += 1024; + break; } } - snesinterface.video_refresh(); + if(frame_hires) width <<= 1; + if(frame_interlace) height <<= 1; + + snesinterface.video_refresh( + data, + /* pitch = */ height <= 240 ? 2048 : 1024, + /* *line = */ height <= 240 ? (pline_width + 1) : (iline_width + 2), + width, height + ); - video.frame_hires = false; - video.frame_interlace = false; + frame_hires = false; + frame_interlace = false; } -void SNES::video_scanline() { -int y = cpu.vcounter(); -int o = (video.video_standard == VIDEOSTANDARD_NTSC) ? (int(cpu.overscan()) << 3) : 0; - if(y <= (0 + o) || y >= (225 + o))return; +void SNES::Video::scanline() { + int y = cpu.vcounter(); + int o = (mode == ModeNTSC) ? ((int)ppu.overscan() << 3) : 0; + if(y <= (0 + o) || y >= (225 + o)) return; y -= o; -PPU::scanline_info si; - ppu.get_scanline_info(&si); - - pline_width[y] = iline_width[y * 2 + int(cpu.interlace_field())] = - (si.hires == false) ? 256 : 512; - video.frame_hires |= si.hires; - video.frame_interlace |= si.interlace; + pline_width[y] = iline_width[y * 2 + (int)ppu.field()] = (ppu.hires() == false) ? 256 : 512; + frame_hires |= ppu.hires(); + frame_interlace |= ppu.interlace(); } -void SNES::video_init() { - for(int i = 0; i < 240; i++)pline_width[i] = 256; - for(int i = 0; i < 480; i++)iline_width[i] = 256; - video.frame_hires = false; - video.frame_interlace = false; - - video.raster_data = (uint16*)malloc(512 * 480 * sizeof(uint16)); - memset(video.raster_data, 0, 512 * 480 * sizeof(uint16)); - video_filter = 0; - set_video_filter(VIDEOFILTER_DIRECT); - set_video_standard(VIDEOSTANDARD_NTSC); - set_video_pixel_format(PIXELFORMAT_RGB565); - update_video_format(); +void SNES::Video::init() { + for(unsigned i = 0; i < 240; i++) pline_width[i] = 256; + for(unsigned i = 0; i < 480; i++) iline_width[i] = 256; + frame_hires = false; + frame_interlace = false; + set_mode(ModeNTSC); } + +#endif //ifdef SNES_CPP diff --git a/src/snes/video/video.h b/src/snes/video/video.h index 8dfb15d7..5766bcf0 100644 --- a/src/snes/video/video.h +++ b/src/snes/video/video.h @@ -1,62 +1,22 @@ -enum { - VIDEOSTANDARD_NTSC, - VIDEOSTANDARD_PAL, -}; - -enum { - PIXELFORMAT_RGB444, - PIXELFORMAT_RGB555, - PIXELFORMAT_RGB565, - PIXELFORMAT_RGB888, -}; - -enum { - VIDEOPITCH_LORES = 2048, - VIDEOPITCH_HIRES = 1024, -}; - -VideoFilter *video_filter; - -static const uint8 gamma_ramp_table[32]; -uint32 color_lookup_table[32768]; - -struct { - uint16 *data, *raster_data, *ppu_data; - uint raster_width, raster_height; - uint width, height; - uint filter, video_standard, pixel_format; - uint pitch; - - bool frame_hires, frame_interlace; -} video; - -struct { - bool modified; - uint filter, video_standard, pixel_format; -} video_format; - -uint16 pline_width[240], iline_width[480]; - -struct video_info { - uint filter, video_standard, pixel_format, width, height; -}; - - void contrast_adjust(int32 &input); - void brightness_adjust(int32 &input); - void gamma_adjust(int32 &input); -//public functions - void update_color_lookup_table(); - - void set_video_filter(uint video_filter); - void set_video_standard(uint video_standard); - void set_video_pixel_format(uint pixel_format); - void get_video_info(video_info *info); - -//private functions +class Video { +public: + enum Mode { + ModeNTSC, + ModePAL, + }; + void set_mode(Mode); + private: - void update_video_format(); - void video_normalize(); - void video_update(); - void video_scanline(); - void video_init(); -public: + Mode mode; + bool frame_hires; + bool frame_interlace; + + unsigned pline_width[240]; //progressive + unsigned iline_width[480]; //interlace + + void update(); + void scanline(); + void init(); + + friend class SNES; +} video; diff --git a/src/snes/video/video_colortable.cpp b/src/snes/video/video_colortable.cpp deleted file mode 100644 index 5f5ce24e..00000000 --- a/src/snes/video/video_colortable.cpp +++ /dev/null @@ -1,103 +0,0 @@ -//Overload's gamma curve adjustment table -const uint8 SNES::gamma_ramp_table[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 -}; - -void SNES::contrast_adjust(int32 &input) { -double lmin, lmax; - lmin = 0.0 - (double)((int32)config::snes.contrast); - lmax = 255.0 + (double)((int32)config::snes.contrast); -int32 result = (int32)(lmin + (double)input * ((lmax - lmin) / 256.0)); - input = minmax<0, 255>(result); -} - -void SNES::brightness_adjust(int32 &input) { -int32 result; - result = input + (int32)config::snes.brightness; - input = minmax<0, 255>(result); -} - -void SNES::gamma_adjust(int32 &input) { -int32 result; - result = (int32)(pow(((double)(input + 1) / 256.0), (double)config::snes.gamma / 100.0) * 256.0); - input = minmax<0, 255>(result); -} - -void SNES::update_color_lookup_table() { -int32 l, r, g, b; -double kr = 0.2126, kb = 0.0722, kg = (1.0 - kr - kb); //luminance -uint32 col; - for(int i = 0; i < 32768; i++) { - //bgr555->rgb888 - col = ((i & 0x001f) << 19) | ((i & 0x001c) << 14) | - ((i & 0x03e0) << 6) | ((i & 0x0380) << 1) | - ((i & 0x7c00) >> 7) | ((i & 0x7000) >> 12); - - r = (col >> 16) & 0xff; - g = (col >> 8) & 0xff; - b = (col ) & 0xff; - - if((bool)config::snes.gamma_ramp == true) { - r = gamma_ramp_table[r >> 3]; - g = gamma_ramp_table[g >> 3]; - b = gamma_ramp_table[b >> 3]; - } - - contrast_adjust(r); brightness_adjust(r); gamma_adjust(r); - contrast_adjust(g); brightness_adjust(g); gamma_adjust(g); - contrast_adjust(b); brightness_adjust(b); gamma_adjust(b); - - if((bool)config::snes.sepia == true) { - l = (int32)((double)r * kr + (double)g * kg + (double)b * kb); - l = (l > 255) ? 255 : (l < 0) ? 0 : l; - r = (int32)((double)l * (1.0 + 0.300)); - g = (int32)((double)l * (1.0 - 0.055)); - b = (int32)((double)l * (1.0 - 0.225)); - r = minmax<0, 255>(r); - g = minmax<0, 255>(g); - b = minmax<0, 255>(b); - } - - if((bool)config::snes.grayscale == true) { - l = (int32)((double)r * kr + (double)g * kg + (double)b * kb); - l = minmax<0, 255>(l); - r = g = b = l; - } - - if((bool)config::snes.invert == true) { - r ^= 0xff; - g ^= 0xff; - b ^= 0xff; - } - - switch(video.pixel_format) { - case PIXELFORMAT_RGB444: - r >>= 4; - g >>= 4; - b >>= 4; - color_lookup_table[i] = (r << 8) | (g << 4) | (b); - break; - case PIXELFORMAT_RGB555: - r >>= 3; - g >>= 3; - b >>= 3; - color_lookup_table[i] = (r << 10) | (g << 5) | (b); - break; - case PIXELFORMAT_RGB565: - r >>= 3; - g >>= 2; - b >>= 3; - color_lookup_table[i] = (r << 11) | (g << 5) | (b); - break; - case PIXELFORMAT_RGB888: - color_lookup_table[i] = (r << 16) | (g << 8) | (b); - break; - default: - color_lookup_table[i] = (uint)-1; - break; - } - } -} diff --git a/src/snes/video/video_normalize.cpp b/src/snes/video/video_normalize.cpp deleted file mode 100644 index df6ea2eb..00000000 --- a/src/snes/video/video_normalize.cpp +++ /dev/null @@ -1,150 +0,0 @@ -void SNES::video_normalize() { -} - -/* -void SNES::video_normalize_256x223() { -uint16 *src = video.ppu_data + 1024; -uint16 *dest = video.raster_data; - for(int y = 1; y < 224; y++) { - if(video_frame[y].hires == false) { - for(int x = 0; x < 256; x++) { - *dest++ = *src++; - } - src += 768; - } else { - for(int x = 0; x < 256; x++) { - *dest++ = *src; - src += 2; - } - } - dest += 256; - } -} - -void SNES::video_normalize_512x223() { -uint16 *src = video.ppu_data + 1024; -uint16 *dest = video.raster_data; - for(int y = 1; y < 224; y++) { - if(video_frame[y].hires == false) { - for(int x = 0; x < 256; x++) { - *dest++ = *src; - *dest++ = *src++; - } - src += 768; - } else { - for(int x = 0; x < 512; x++) { - *dest++ = *src++; - } - src += 512; - } - } -} - -void SNES::video_normalize_256x446() { -uint16 *src = video.ppu_data + 1024; -uint16 *dest = video.raster_data; -bool field = !r_cpu->interlace_field(); - for(int y = 1; y < 224; y++) { - if(video_frame[y].interlace == false) { - if(video_frame[y].hires == false) { - for(int x = 0; x < 256; x++) { - *dest++ = *(src + x); - } - dest += 256; - for(int x = 0; x < 256; x++) { - *dest++ = *(src + x); - } - dest += 256; - } else { - for(int x = 0; x < 256; x++) { - *dest++ = *(src + (x << 1)); - } - dest += 256; - for(int x = 0; x < 256; x++) { - *dest++ = *(src + (x << 1)); - } - dest += 256; - } - src += 1024; - } else { - if(field) { - dest += 512; - src += 512; - } - - if(video_frame[y].hires == false) { - for(int x = 0; x < 256; x++) { - *dest++ = *src++; - } - src += 256; - } else { - for(int x = 0; x < 256; x++) { - *dest++ = *src; - src += 2; - } - } - dest += 256; - - if(!field) { - dest += 512; - src += 512; - } - } - } -} - -void SNES::video_normalize_512x446() { -uint16 *src = video.ppu_data + 1024; -uint16 *dest = video.raster_data; -bool field = !r_cpu->interlace_field(); - for(int y = 1; y < 224; y++) { - if(video_frame[y].interlace == false) { - if(video_frame[y].hires == false) { - for(int x = 0; x < 256; x++) { - *dest++ = *(src + x); - *dest++ = *(src + x); - } - dest += 512; - for(int x = 0; x < 256; x++) { - *dest++ = *(src + x); - *dest++ = *(src + x); - } - dest += 512; - } else { - for(int x = 0; x < 512; x++) { - *dest++ = *(src + x); - } - dest += 512; - for(int x = 0; x < 512; x++) { - *dest++ = *(src + x); - } - dest += 512; - } - src += 1024; - } else { - if(field) { - dest += 512; - src += 512; - } - - if(video_frame[y].hires == false) { - for(int x = 0; x < 256; x++) { - *dest++ = *(src + x); - *dest++ = *(src + x); - } - src += 512; - } else { - for(int x = 0; x < 512; x++) { - *dest++ = *src++; - } - } - dest += 512; - - if(!field) { - dest += 512; - src += 512; - } - } - } -} -*/ diff --git a/src/ui/base/about.cpp b/src/ui/base/about.cpp new file mode 100644 index 00000000..ff8256f3 --- /dev/null +++ b/src/ui/base/about.cpp @@ -0,0 +1,40 @@ +const char AboutWindow::about_text[1024] = "" + "bsnes -- version " BSNES_VERSION "\n" + "Author: byuu\n" + "Project began: October 14th, 2004"; + +const char AboutWindow::contributors_text[1024] = + "Contributors:\n" + " anomie, blargg, DMV27, GIGO, kode54, Nach,\n" + " Overload, Richard Bannister, TRAC, zones"; + +uintptr_t AboutWindow::close(Event) { + hide(); + return false; +} + +void AboutWindow::setup() { + create(Window::AutoCenter, 350, 125, "About bsnes ..."); + set_icon(48, 48, (uint32_t*)resource::icon48); + + icon.create(0, 48, 48); + about.create(0, 287, 48, about_text); + contributors.create(0, 340, 48, contributors_text); + + attach(icon, 5, 5); + attach(about, 58, 5); + attach(contributors, 5, 58); + + on_close = bind(&AboutWindow::close, this); + + uint8_t *buffer = new uint8_t[48 * 48 * 4]; + memcpy(buffer, resource::icon48, 48 * 48 * 4); + for(unsigned i = 0; i < 48 * 48; i++) { + uint8_t alpha = buffer[i * 4 + 3]; + buffer[i * 4 + 2] = uint8_t(1.0 / 256.0 * alpha * buffer[i * 4 + 2]); + buffer[i * 4 + 1] = uint8_t(1.0 / 256.0 * alpha * buffer[i * 4 + 1]); + buffer[i * 4 + 0] = uint8_t(1.0 / 256.0 * alpha * buffer[i * 4 + 0]); + } + memcpy(icon.buffer(), buffer, 48 * 48 * 4); + delete[] buffer; +} diff --git a/src/ui/base/about.h b/src/ui/base/about.h new file mode 100644 index 00000000..c07de079 --- /dev/null +++ b/src/ui/base/about.h @@ -0,0 +1,11 @@ +class AboutWindow : public Window { +public: + Canvas icon; + Label about; + Label contributors; + static const char about_text[1024]; + static const char contributors_text[1024]; + + void setup(); + uintptr_t close(Event); +} window_about; diff --git a/src/ui/ui_main.cpp b/src/ui/base/main.cpp similarity index 84% rename from src/ui/ui_main.cpp rename to src/ui/base/main.cpp index f6221998..adbd057d 100644 --- a/src/ui/ui_main.cpp +++ b/src/ui/base/main.cpp @@ -4,341 +4,350 @@ bool MainWindow::input_ready() { //allow input if config set to never block input if(config::input.capture_mode == 0) return true; //block input - return false; -} - -uintptr_t MainWindow::close(Event) { - _term_ = true; - window_about.hide(); - window_message.hide(); - window_settings.hide(); - window_bsxloader.hide(); - window_stloader.hide(); - hide(); - return false; -} - -uintptr_t MainWindow::event(Event e) { - if(e.type == Event::Tick) { - if(e.widget == &menu_file_load) { - event::load_rom(); - } - - if(e.widget == &menu_file_load_bsx) { - window_bsxloader.mode = BSXLoaderWindow::ModeBSX; - window_bsxloader.set_text("Load BS-X Cartridge"); - window_bsxloader.tbase.set_text(config::path.bsx); - window_bsxloader.focus(); - } - - if(e.widget == &menu_file_load_bsc) { - window_bsxloader.mode = BSXLoaderWindow::ModeBSC; - window_bsxloader.set_text("Load BS-X Slotted Cartridge"); - window_bsxloader.tbase.set_text(""); - window_bsxloader.focus(); - } - - if(e.widget == &menu_file_load_st) { - window_stloader.tbase.set_text(config::path.st); - window_stloader.focus(); - } - - if(e.widget == &menu_file_unload) { - event::unload_rom(); - } - - if(e.widget == &menu_file_reset) { - event::reset(); - } - - if(e.widget == &menu_file_power) { - event::power(); - } - - if(e.widget == &menu_file_exit) { - event(Event(Event::Close)); - } - - if(locked == false) { - //set locked to true to update below menu item check statuses without triggering events - if(e.widget == &menu_settings_videomode_1x) { event::update_multiplier(1); } - if(e.widget == &menu_settings_videomode_2x) { event::update_multiplier(2); } - if(e.widget == &menu_settings_videomode_3x) { event::update_multiplier(3); } - if(e.widget == &menu_settings_videomode_4x) { event::update_multiplier(4); } - if(e.widget == &menu_settings_videomode_5x) { event::update_multiplier(5); } - - if(e.widget == &menu_settings_videomode_aspect_correction) { - event::update_aspect_correction(menu_settings_videomode_aspect_correction.checked()); - } - - if(e.widget == &menu_settings_videomode_ntsc) { event::update_region(0); } - if(e.widget == &menu_settings_videomode_pal) { event::update_region(1); } - - if(e.widget == &menu_settings_videofilter_hwpoint) { event::update_hardware_filter(0); } - if(e.widget == &menu_settings_videofilter_hwlinear) { event::update_hardware_filter(1); } - - if(e.widget == &menu_settings_videofilter_swnone) { event::update_software_filter(0); } - if(e.widget == &menu_settings_videofilter_swntsc) { event::update_software_filter(1); } - if(e.widget == &menu_settings_videofilter_swhq2x) { event::update_software_filter(2); } - if(e.widget == &menu_settings_videofilter_swscale2x) { event::update_software_filter(3); } - - if(e.widget == &menu_settings_videoframeskip_0) { config::video.frameskip = 0; } - if(e.widget == &menu_settings_videoframeskip_1) { config::video.frameskip = 1; } - if(e.widget == &menu_settings_videoframeskip_2) { config::video.frameskip = 2; } - if(e.widget == &menu_settings_videoframeskip_3) { config::video.frameskip = 3; } - if(e.widget == &menu_settings_videoframeskip_4) { config::video.frameskip = 4; } - if(e.widget == &menu_settings_videoframeskip_5) { config::video.frameskip = 5; } - if(e.widget == &menu_settings_videoframeskip_6) { config::video.frameskip = 6; } - if(e.widget == &menu_settings_videoframeskip_7) { config::video.frameskip = 7; } - if(e.widget == &menu_settings_videoframeskip_8) { config::video.frameskip = 8; } - if(e.widget == &menu_settings_videoframeskip_9) { config::video.frameskip = 9; } - - if(e.widget == &menu_settings_mute) { - config::audio.mute = menu_settings_mute.checked(); - } - - if(e.widget == &menu_settings_speedreg_enable) { - config::system.regulate_speed = menu_settings_speedreg_enable.checked(); - audio.set(Audio::Synchronize, (bool)config::system.regulate_speed); - } - - if(e.widget == &menu_settings_speedreg_slowest) { event::update_speed_regulation(1); } - if(e.widget == &menu_settings_speedreg_slow) { event::update_speed_regulation(2); } - if(e.widget == &menu_settings_speedreg_normal) { event::update_speed_regulation(3); } - if(e.widget == &menu_settings_speedreg_fast) { event::update_speed_regulation(4); } - if(e.widget == &menu_settings_speedreg_fastest) { event::update_speed_regulation(5); } - } - - if(e.widget == &menu_settings_config) { window_settings.show(); } - - if(e.widget == &menu_misc_logaudio) { - (menu_misc_logaudio.checked() == true) ? snes.log_audio_enable() : snes.log_audio_disable(); - } - - if(e.widget == &menu_misc_showstatus) { - status.show(config::misc.show_status = menu_misc_showstatus.checked()); - } - - if(e.widget == &menu_misc_about) { - window_about.focus(); - } - } - - return true; -} - -uintptr_t MainWindow::block(Event) { - audio.clear(); - return true; -} - -void MainWindow::setup() { - locked = true; - - create(Window::AutoCenter, 256, 224, BSNES_TITLE); - set_background_color(0, 0, 0); - -MenuRadioItemGroup group; - attach(menu_file.create("File")); - menu_file.attach(menu_file_load.create("Load Cartridge ...")); - menu_file.attach(menu_file_load_special.create("Load Special")); - menu_file_load_special.attach(menu_file_load_bsx.create("Load BS-X Cartridge ...")); - menu_file_load_special.attach(menu_file_load_bsc.create("Load BS-X Slotted Cartridge ...")); - menu_file_load_special.attach(menu_file_load_st.create("Load ST Cartridge ...")); - menu_file.attach(menu_file_unload.create("Unload Cartridge")); - menu_file.attach(menu_file_sep1.create()); - menu_file.attach(menu_file_reset.create("Reset System")); - menu_file.attach(menu_file_power.create("Power Cycle System")); - menu_file.attach(menu_file_sep2.create()); - menu_file.attach(menu_file_exit.create("Exit")); - - attach(menu_settings.create("Settings")); - menu_settings.attach(menu_settings_videomode.create("Video Mode")); - group.add(&menu_settings_videomode_1x); - group.add(&menu_settings_videomode_2x); - group.add(&menu_settings_videomode_3x); - group.add(&menu_settings_videomode_4x); - group.add(&menu_settings_videomode_5x); - menu_settings_videomode.attach(menu_settings_videomode_1x.create(group, "Scale 1x")); - menu_settings_videomode.attach(menu_settings_videomode_2x.create(group, "Scale 2x")); - menu_settings_videomode.attach(menu_settings_videomode_3x.create(group, "Scale 3x")); - menu_settings_videomode.attach(menu_settings_videomode_4x.create(group, "Scale 4x")); - menu_settings_videomode.attach(menu_settings_videomode_5x.create(group, "Scale 5x")); - group.reset(); - menu_settings_videomode.attach(menu_settings_videomode_sep1.create()); - menu_settings_videomode.attach(menu_settings_videomode_aspect_correction.create("Correct Aspect Ratio")); - menu_settings_videomode.attach(menu_settings_videomode_sep2.create()); - group.add(&menu_settings_videomode_ntsc); - group.add(&menu_settings_videomode_pal); - menu_settings_videomode.attach(menu_settings_videomode_ntsc.create(group, "NTSC")); - menu_settings_videomode.attach(menu_settings_videomode_pal.create(group, "PAL")); - group.reset(); - - menu_settings.attach(menu_settings_videofilter.create("Video Filter")); - group.add(&menu_settings_videofilter_hwpoint); - group.add(&menu_settings_videofilter_hwlinear); - menu_settings_videofilter.attach(menu_settings_videofilter_hwpoint.create(group, "Point")); - menu_settings_videofilter.attach(menu_settings_videofilter_hwlinear.create(group, "Linear")); - group.reset(); - menu_settings_videofilter.attach(menu_settings_videofilter_sep1.create()); - group.add(&menu_settings_videofilter_swnone); - group.add(&menu_settings_videofilter_swntsc); - group.add(&menu_settings_videofilter_swhq2x); - group.add(&menu_settings_videofilter_swscale2x); - menu_settings_videofilter.attach(menu_settings_videofilter_swnone.create(group, "None")); - menu_settings_videofilter.attach(menu_settings_videofilter_swntsc.create(group, "NTSC")); - menu_settings_videofilter.attach(menu_settings_videofilter_swhq2x.create(group, "HQ2x")); - menu_settings_videofilter.attach(menu_settings_videofilter_swscale2x.create(group, "Scale2x")); - group.reset(); - - menu_settings.attach(menu_settings_videoframeskip.create("Video Frameskip")); - group.add(&menu_settings_videoframeskip_0); - group.add(&menu_settings_videoframeskip_1); - group.add(&menu_settings_videoframeskip_2); - group.add(&menu_settings_videoframeskip_3); - group.add(&menu_settings_videoframeskip_4); - group.add(&menu_settings_videoframeskip_5); - group.add(&menu_settings_videoframeskip_6); - group.add(&menu_settings_videoframeskip_7); - group.add(&menu_settings_videoframeskip_8); - group.add(&menu_settings_videoframeskip_9); - menu_settings_videoframeskip.attach(menu_settings_videoframeskip_0.create(group, "0")); - menu_settings_videoframeskip.attach(menu_settings_videoframeskip_sep1.create()); - menu_settings_videoframeskip.attach(menu_settings_videoframeskip_1.create(group, "1")); - menu_settings_videoframeskip.attach(menu_settings_videoframeskip_2.create(group, "2")); - menu_settings_videoframeskip.attach(menu_settings_videoframeskip_3.create(group, "3")); - menu_settings_videoframeskip.attach(menu_settings_videoframeskip_4.create(group, "4")); - menu_settings_videoframeskip.attach(menu_settings_videoframeskip_5.create(group, "5")); - menu_settings_videoframeskip.attach(menu_settings_videoframeskip_6.create(group, "6")); - menu_settings_videoframeskip.attach(menu_settings_videoframeskip_7.create(group, "7")); - menu_settings_videoframeskip.attach(menu_settings_videoframeskip_8.create(group, "8")); - menu_settings_videoframeskip.attach(menu_settings_videoframeskip_9.create(group, "9")); - group.reset(); - - menu_settings.attach(menu_settings_sep1.create()); - menu_settings.attach(menu_settings_mute.create("Mute Sound Output")); - menu_settings.attach(menu_settings_sep2.create()); - - menu_settings.attach(menu_settings_speedreg.create("Speed Regulation")); - menu_settings_speedreg.attach(menu_settings_speedreg_enable.create("Enable")); - menu_settings_speedreg.attach(menu_settings_speedreg_sep1.create()); - group.add(&menu_settings_speedreg_slowest); - group.add(&menu_settings_speedreg_slow); - group.add(&menu_settings_speedreg_normal); - group.add(&menu_settings_speedreg_fast); - group.add(&menu_settings_speedreg_fastest); - menu_settings_speedreg.attach(menu_settings_speedreg_slowest.create(group, "Slowest")); - menu_settings_speedreg.attach(menu_settings_speedreg_slow.create(group, "Slow")); - menu_settings_speedreg.attach(menu_settings_speedreg_normal.create(group, "Normal")); - menu_settings_speedreg.attach(menu_settings_speedreg_fast.create(group, "Fast")); - menu_settings_speedreg.attach(menu_settings_speedreg_fastest.create(group, "Fastest")); - group.reset(); - - menu_settings.attach(menu_settings_sep3.create()); - menu_settings.attach(menu_settings_config.create("Configuration ...")); - - attach(menu_misc.create("Misc")); - menu_misc.attach(menu_misc_logaudio.create("Log Audio Data")); - menu_misc.attach(menu_misc_showstatus.create("Show Statusbar")); - menu_misc.attach(menu_misc_sep1.create()); - menu_misc.attach(menu_misc_about.create("About ...")); - - view.create(0, 256, 224); - attach(view, 0, 0); - - on_close = bind(&MainWindow::close, this); - on_block = bind(&MainWindow::block, this); - - menu_file_exit.on_tick = bind(&MainWindow::close, this); - - menu_file_load.on_tick = - menu_file_load_bsx.on_tick = - menu_file_load_bsc.on_tick = - menu_file_load_st.on_tick = - menu_file_unload.on_tick = - menu_file_reset.on_tick = - menu_file_power.on_tick = - - menu_settings_videomode_1x.on_tick = - menu_settings_videomode_2x.on_tick = - menu_settings_videomode_3x.on_tick = - menu_settings_videomode_4x.on_tick = - menu_settings_videomode_5x.on_tick = - menu_settings_videomode_aspect_correction.on_tick = - menu_settings_videomode_ntsc.on_tick = - menu_settings_videomode_pal.on_tick = - - menu_settings_videofilter_hwpoint.on_tick = - menu_settings_videofilter_hwlinear.on_tick = - menu_settings_videofilter_swnone.on_tick = - menu_settings_videofilter_swntsc.on_tick = - menu_settings_videofilter_swhq2x.on_tick = - menu_settings_videofilter_swscale2x.on_tick = - - menu_settings_videoframeskip_0.on_tick = - menu_settings_videoframeskip_1.on_tick = - menu_settings_videoframeskip_2.on_tick = - menu_settings_videoframeskip_3.on_tick = - menu_settings_videoframeskip_4.on_tick = - menu_settings_videoframeskip_5.on_tick = - menu_settings_videoframeskip_6.on_tick = - menu_settings_videoframeskip_7.on_tick = - menu_settings_videoframeskip_8.on_tick = - menu_settings_videoframeskip_9.on_tick = - - menu_settings_mute.on_tick = - menu_settings_speedreg_enable.on_tick = - menu_settings_speedreg_slowest.on_tick = - menu_settings_speedreg_slow.on_tick = - menu_settings_speedreg_normal.on_tick = - menu_settings_speedreg_fast.on_tick = - menu_settings_speedreg_fastest.on_tick = - menu_settings_config.on_tick = - - menu_misc_logaudio.on_tick = - menu_misc_showstatus.on_tick = - menu_misc_about.on_tick = - - bind(&MainWindow::event, this); -} - -void MainWindow::update_menu_settings() { - locked = true; - event::load_video_settings(); - - switch(event::video_settings.multiplier) { default: - case 1: menu_settings_videomode_1x.check(); break; - case 2: menu_settings_videomode_2x.check(); break; - case 3: menu_settings_videomode_3x.check(); break; - case 4: menu_settings_videomode_4x.check(); break; - case 5: menu_settings_videomode_5x.check(); break; - } - - menu_settings_videomode_aspect_correction.check(event::video_settings.aspect_correction); - - switch(event::video_settings.region) { default: - case 0: menu_settings_videomode_ntsc.check(); break; - case 1: menu_settings_videomode_pal.check(); break; - } - - switch(event::video_settings.hardware_filter) { default: - case 0: menu_settings_videofilter_hwpoint.check(); break; - case 1: menu_settings_videofilter_hwlinear.check(); break; - } - - switch(event::video_settings.software_filter) { default: - case 0: menu_settings_videofilter_swnone.check(); break; - case 1: menu_settings_videofilter_swntsc.check(); break; - case 2: menu_settings_videofilter_swhq2x.check(); break; - case 3: menu_settings_videofilter_swscale2x.check(); break; - } - - menu_settings_mute.check(config::audio.mute); - - menu_settings_speedreg_enable.check(config::system.regulate_speed); - menu_settings_speedreg_normal.check(); - - menu_misc_showstatus.check(config::misc.show_status); - - locked = false; -} + return false; +} + +uintptr_t MainWindow::close(Event) { + app.term = true; + window_about.hide(); + window_message.hide(); + window_settings.hide(); + window_bsxloader.hide(); + window_stloader.hide(); + hide(); + return false; +} + +uintptr_t MainWindow::event(Event e) { + if(e.type == Event::Tick) { + if(e.widget == &menu_file_load) { + event::load_rom(); + } + + if(e.widget == &menu_file_load_bsx) { + window_bsxloader.mode = BSXLoaderWindow::ModeBSX; + window_bsxloader.set_text("Load BS-X Cartridge"); + window_bsxloader.tbase.set_text(config::path.bsx); + window_bsxloader.focus(); + } + + if(e.widget == &menu_file_load_bsc) { + window_bsxloader.mode = BSXLoaderWindow::ModeBSC; + window_bsxloader.set_text("Load BS-X Slotted Cartridge"); + window_bsxloader.tbase.set_text(""); + window_bsxloader.focus(); + } + + if(e.widget == &menu_file_load_st) { + window_stloader.tbase.set_text(config::path.st); + window_stloader.focus(); + } + + if(e.widget == &menu_file_unload) { + event::unload_rom(); + } + + if(e.widget == &menu_file_reset) { + event::reset(); + } + + if(e.widget == &menu_file_power) { + event::power(); + } + + if(e.widget == &menu_file_exit) { + event(Event(Event::Close)); + } + + if(locked == false) { + //set locked to true to update below menu item check statuses without triggering events + if(e.widget == &menu_settings_videomode_1x) { event::update_multiplier(1); } + if(e.widget == &menu_settings_videomode_2x) { event::update_multiplier(2); } + if(e.widget == &menu_settings_videomode_3x) { event::update_multiplier(3); } + if(e.widget == &menu_settings_videomode_4x) { event::update_multiplier(4); } + if(e.widget == &menu_settings_videomode_5x) { event::update_multiplier(5); } + + if(e.widget == &menu_settings_videomode_aspect_correction) { + event::update_aspect_correction(menu_settings_videomode_aspect_correction.checked()); + } + + if(e.widget == &menu_settings_videomode_ntsc) { event::update_region(0); } + if(e.widget == &menu_settings_videomode_pal) { event::update_region(1); } + + if(e.widget == &menu_settings_videofilter_hwpoint) { event::update_hardware_filter(0); } + if(e.widget == &menu_settings_videofilter_hwlinear) { event::update_hardware_filter(1); } + + if(e.widget == &menu_settings_videofilter_swnone) { event::update_software_filter(0); } + if(e.widget == &menu_settings_videofilter_swscanline) { event::update_software_filter(1); } + if(e.widget == &menu_settings_videofilter_swscale2x) { event::update_software_filter(2); } + if(e.widget == &menu_settings_videofilter_swhq2x) { event::update_software_filter(3); } + if(e.widget == &menu_settings_videofilter_swntsc) { event::update_software_filter(4); } + + if(e.widget == &menu_settings_videoframeskip_0) { config::video.frameskip = 0; } + if(e.widget == &menu_settings_videoframeskip_1) { config::video.frameskip = 1; } + if(e.widget == &menu_settings_videoframeskip_2) { config::video.frameskip = 2; } + if(e.widget == &menu_settings_videoframeskip_3) { config::video.frameskip = 3; } + if(e.widget == &menu_settings_videoframeskip_4) { config::video.frameskip = 4; } + if(e.widget == &menu_settings_videoframeskip_5) { config::video.frameskip = 5; } + if(e.widget == &menu_settings_videoframeskip_6) { config::video.frameskip = 6; } + if(e.widget == &menu_settings_videoframeskip_7) { config::video.frameskip = 7; } + if(e.widget == &menu_settings_videoframeskip_8) { config::video.frameskip = 8; } + if(e.widget == &menu_settings_videoframeskip_9) { config::video.frameskip = 9; } + + if(e.widget == &menu_settings_mute) { + config::audio.mute = menu_settings_mute.checked(); + } + + if(e.widget == &menu_settings_speedreg_disabled) { event::update_speed_regulation(0); } + if(e.widget == &menu_settings_speedreg_slowest) { event::update_speed_regulation(1); } + if(e.widget == &menu_settings_speedreg_slow) { event::update_speed_regulation(2); } + if(e.widget == &menu_settings_speedreg_normal) { event::update_speed_regulation(3); } + if(e.widget == &menu_settings_speedreg_fast) { event::update_speed_regulation(4); } + if(e.widget == &menu_settings_speedreg_fastest) { event::update_speed_regulation(5); } + } + + if(e.widget == &menu_settings_config) { window_settings.show(); } + + if(e.widget == &menu_misc_logaudio) { + (menu_misc_logaudio.checked() == true) ? snes.audio.log_enable() : snes.audio.log_disable(); + } + + if(e.widget == &menu_misc_showstatus) { + status.show(config::misc.status_enable = menu_misc_showstatus.checked()); + } + + if(e.widget == &menu_misc_about) { + window_about.focus(); + } + } + + return true; +} + +uintptr_t MainWindow::block(Event) { + audio.clear(); + return true; +} + +void MainWindow::setup() { + locked = true; + + create(Window::AutoCenter, 256, 224, BSNES_TITLE); + set_background_color(0, 0, 0); + set_icon(48, 48, (uint32_t*)resource::icon48); + +MenuRadioItemGroup group; + attach(menu_file.create("File")); + menu_file.attach(menu_file_load.create("Load Cartridge ...")); + menu_file.attach(menu_file_load_special.create("Load Special")); + menu_file_load_special.attach(menu_file_load_bsx.create("Load BS-X Cartridge ...")); + menu_file_load_special.attach(menu_file_load_bsc.create("Load BS-X Slotted Cartridge ...")); + menu_file_load_special.attach(menu_file_load_st.create("Load ST Cartridge ...")); + menu_file.attach(menu_file_unload.create("Unload Cartridge")); + menu_file.attach(menu_file_sep1.create()); + menu_file.attach(menu_file_reset.create("Reset System")); + menu_file.attach(menu_file_power.create("Power Cycle System")); + menu_file.attach(menu_file_sep2.create()); + menu_file.attach(menu_file_exit.create("Exit")); + + attach(menu_settings.create("Settings")); + menu_settings.attach(menu_settings_videomode.create("Video Mode")); + group.add(&menu_settings_videomode_1x); + group.add(&menu_settings_videomode_2x); + group.add(&menu_settings_videomode_3x); + group.add(&menu_settings_videomode_4x); + group.add(&menu_settings_videomode_5x); + menu_settings_videomode.attach(menu_settings_videomode_1x.create(group, "Scale 1x")); + menu_settings_videomode.attach(menu_settings_videomode_2x.create(group, "Scale 2x")); + menu_settings_videomode.attach(menu_settings_videomode_3x.create(group, "Scale 3x")); + menu_settings_videomode.attach(menu_settings_videomode_4x.create(group, "Scale 4x")); + menu_settings_videomode.attach(menu_settings_videomode_5x.create(group, "Scale 5x")); + group.reset(); + menu_settings_videomode.attach(menu_settings_videomode_sep1.create()); + menu_settings_videomode.attach(menu_settings_videomode_aspect_correction.create("Correct Aspect Ratio")); + menu_settings_videomode.attach(menu_settings_videomode_sep2.create()); + group.add(&menu_settings_videomode_ntsc); + group.add(&menu_settings_videomode_pal); + menu_settings_videomode.attach(menu_settings_videomode_ntsc.create(group, "NTSC")); + menu_settings_videomode.attach(menu_settings_videomode_pal.create(group, "PAL")); + group.reset(); + + menu_settings.attach(menu_settings_videofilter.create("Video Filter")); + group.add(&menu_settings_videofilter_hwpoint); + group.add(&menu_settings_videofilter_hwlinear); + menu_settings_videofilter.attach(menu_settings_videofilter_hwpoint.create(group, "Point")); + menu_settings_videofilter.attach(menu_settings_videofilter_hwlinear.create(group, "Linear")); + group.reset(); + menu_settings_videofilter.attach(menu_settings_videofilter_sep1.create()); + group.add(&menu_settings_videofilter_swnone); + group.add(&menu_settings_videofilter_swscanline); + group.add(&menu_settings_videofilter_swscale2x); + group.add(&menu_settings_videofilter_swhq2x); + group.add(&menu_settings_videofilter_swntsc); + menu_settings_videofilter.attach(menu_settings_videofilter_swnone.create(group, "None")); + menu_settings_videofilter.attach(menu_settings_videofilter_swscanline.create(group, "Scanline")); + menu_settings_videofilter.attach(menu_settings_videofilter_swscale2x.create(group, "Scale2x")); + menu_settings_videofilter.attach(menu_settings_videofilter_swhq2x.create(group, "HQ2x")); + menu_settings_videofilter.attach(menu_settings_videofilter_swntsc.create(group, "NTSC")); + group.reset(); + + menu_settings.attach(menu_settings_videoframeskip.create("Video Frameskip")); + group.add(&menu_settings_videoframeskip_0); + group.add(&menu_settings_videoframeskip_1); + group.add(&menu_settings_videoframeskip_2); + group.add(&menu_settings_videoframeskip_3); + group.add(&menu_settings_videoframeskip_4); + group.add(&menu_settings_videoframeskip_5); + group.add(&menu_settings_videoframeskip_6); + group.add(&menu_settings_videoframeskip_7); + group.add(&menu_settings_videoframeskip_8); + group.add(&menu_settings_videoframeskip_9); + menu_settings_videoframeskip.attach(menu_settings_videoframeskip_0.create(group, "0")); + menu_settings_videoframeskip.attach(menu_settings_videoframeskip_sep1.create()); + menu_settings_videoframeskip.attach(menu_settings_videoframeskip_1.create(group, "1")); + menu_settings_videoframeskip.attach(menu_settings_videoframeskip_2.create(group, "2")); + menu_settings_videoframeskip.attach(menu_settings_videoframeskip_3.create(group, "3")); + menu_settings_videoframeskip.attach(menu_settings_videoframeskip_4.create(group, "4")); + menu_settings_videoframeskip.attach(menu_settings_videoframeskip_5.create(group, "5")); + menu_settings_videoframeskip.attach(menu_settings_videoframeskip_6.create(group, "6")); + menu_settings_videoframeskip.attach(menu_settings_videoframeskip_7.create(group, "7")); + menu_settings_videoframeskip.attach(menu_settings_videoframeskip_8.create(group, "8")); + menu_settings_videoframeskip.attach(menu_settings_videoframeskip_9.create(group, "9")); + group.reset(); + + menu_settings.attach(menu_settings_sep1.create()); + menu_settings.attach(menu_settings_mute.create("Mute Sound Output")); + menu_settings.attach(menu_settings_sep2.create()); + + menu_settings.attach(menu_settings_speedreg.create("Speed Regulation")); + group.add(&menu_settings_speedreg_disabled); + group.add(&menu_settings_speedreg_slowest); + group.add(&menu_settings_speedreg_slow); + group.add(&menu_settings_speedreg_normal); + group.add(&menu_settings_speedreg_fast); + group.add(&menu_settings_speedreg_fastest); + menu_settings_speedreg.attach(menu_settings_speedreg_disabled.create(group, "Disabled")); + menu_settings_speedreg.attach(menu_settings_speedreg_sep1.create()); + menu_settings_speedreg.attach(menu_settings_speedreg_slowest.create(group, "Slowest")); + menu_settings_speedreg.attach(menu_settings_speedreg_slow.create(group, "Slow")); + menu_settings_speedreg.attach(menu_settings_speedreg_normal.create(group, "Normal")); + menu_settings_speedreg.attach(menu_settings_speedreg_fast.create(group, "Fast")); + menu_settings_speedreg.attach(menu_settings_speedreg_fastest.create(group, "Fastest")); + group.reset(); + + menu_settings.attach(menu_settings_sep3.create()); + menu_settings.attach(menu_settings_config.create("Configuration ...")); + + attach(menu_misc.create("Misc")); + menu_misc.attach(menu_misc_logaudio.create("Log Audio Data")); + menu_misc.attach(menu_misc_showstatus.create("Show Statusbar")); + menu_misc.attach(menu_misc_sep1.create()); + menu_misc.attach(menu_misc_about.create("About ...")); + + view.create(0, 256, 224); + attach(view, 0, 0); + + on_close = bind(&MainWindow::close, this); + on_block = bind(&MainWindow::block, this); + + menu_file_exit.on_tick = bind(&MainWindow::close, this); + + menu_file_load.on_tick = + menu_file_load_bsx.on_tick = + menu_file_load_bsc.on_tick = + menu_file_load_st.on_tick = + menu_file_unload.on_tick = + menu_file_reset.on_tick = + menu_file_power.on_tick = + + menu_settings_videomode_1x.on_tick = + menu_settings_videomode_2x.on_tick = + menu_settings_videomode_3x.on_tick = + menu_settings_videomode_4x.on_tick = + menu_settings_videomode_5x.on_tick = + menu_settings_videomode_aspect_correction.on_tick = + menu_settings_videomode_ntsc.on_tick = + menu_settings_videomode_pal.on_tick = + + menu_settings_videofilter_hwpoint.on_tick = + menu_settings_videofilter_hwlinear.on_tick = + menu_settings_videofilter_swnone.on_tick = + menu_settings_videofilter_swscanline.on_tick = + menu_settings_videofilter_swscale2x.on_tick = + menu_settings_videofilter_swhq2x.on_tick = + menu_settings_videofilter_swntsc.on_tick = + + menu_settings_videoframeskip_0.on_tick = + menu_settings_videoframeskip_1.on_tick = + menu_settings_videoframeskip_2.on_tick = + menu_settings_videoframeskip_3.on_tick = + menu_settings_videoframeskip_4.on_tick = + menu_settings_videoframeskip_5.on_tick = + menu_settings_videoframeskip_6.on_tick = + menu_settings_videoframeskip_7.on_tick = + menu_settings_videoframeskip_8.on_tick = + menu_settings_videoframeskip_9.on_tick = + + menu_settings_mute.on_tick = + menu_settings_speedreg_disabled.on_tick = + menu_settings_speedreg_slowest.on_tick = + menu_settings_speedreg_slow.on_tick = + menu_settings_speedreg_normal.on_tick = + menu_settings_speedreg_fast.on_tick = + menu_settings_speedreg_fastest.on_tick = + menu_settings_config.on_tick = + + menu_misc_logaudio.on_tick = + menu_misc_showstatus.on_tick = + menu_misc_about.on_tick = + + bind(&MainWindow::event, this); +} + +void MainWindow::update_menu_settings() { + locked = true; + event::load_video_settings(); + + switch(event::video_settings.multiplier) { default: + case 1: menu_settings_videomode_1x.check(); break; + case 2: menu_settings_videomode_2x.check(); break; + case 3: menu_settings_videomode_3x.check(); break; + case 4: menu_settings_videomode_4x.check(); break; + case 5: menu_settings_videomode_5x.check(); break; + } + + menu_settings_videomode_aspect_correction.check(event::video_settings.aspect_correction); + + switch(event::video_settings.region) { default: + case 0: menu_settings_videomode_ntsc.check(); break; + case 1: menu_settings_videomode_pal.check(); break; + } + + switch(event::video_settings.hardware_filter) { default: + case 0: menu_settings_videofilter_hwpoint.check(); break; + case 1: menu_settings_videofilter_hwlinear.check(); break; + } + + switch(event::video_settings.software_filter) { default: + case 0: menu_settings_videofilter_swnone.check(); break; + case 1: menu_settings_videofilter_swscanline.check(); break; + case 2: menu_settings_videofilter_swscale2x.check(); break; + case 3: menu_settings_videofilter_swhq2x.check(); break; + case 4: menu_settings_videofilter_swntsc.check(); break; + } + + menu_settings_mute.check(config::audio.mute); + + switch(config::system.speed_regulation) { + case 0: menu_settings_speedreg_disabled.check(); break; + case 1: menu_settings_speedreg_slowest.check(); break; + case 2: menu_settings_speedreg_slow.check(); break; + case 3: menu_settings_speedreg_normal.check(); break; + case 4: menu_settings_speedreg_fast.check(); break; + case 5: menu_settings_speedreg_fastest.check(); break; + } + + menu_misc_showstatus.check(config::misc.status_enable); + + locked = false; +} diff --git a/src/ui/ui_main.h b/src/ui/base/main.h similarity index 93% rename from src/ui/ui_main.h rename to src/ui/base/main.h index 1a3dd7d2..412759a2 100644 --- a/src/ui/ui_main.h +++ b/src/ui/base/main.h @@ -30,9 +30,10 @@ public: MenuRadioItem menu_settings_videofilter_hwlinear; MenuSeparator menu_settings_videofilter_sep1; MenuRadioItem menu_settings_videofilter_swnone; - MenuRadioItem menu_settings_videofilter_swntsc; - MenuRadioItem menu_settings_videofilter_swhq2x; + MenuRadioItem menu_settings_videofilter_swscanline; MenuRadioItem menu_settings_videofilter_swscale2x; + MenuRadioItem menu_settings_videofilter_swhq2x; + MenuRadioItem menu_settings_videofilter_swntsc; MenuGroup menu_settings_videoframeskip; MenuRadioItem menu_settings_videoframeskip_0; MenuSeparator menu_settings_videoframeskip_sep1; @@ -49,7 +50,7 @@ public: MenuCheckItem menu_settings_mute; MenuSeparator menu_settings_sep2; MenuGroup menu_settings_speedreg; - MenuCheckItem menu_settings_speedreg_enable; + MenuRadioItem menu_settings_speedreg_disabled; MenuSeparator menu_settings_speedreg_sep1; MenuRadioItem menu_settings_speedreg_slowest; MenuRadioItem menu_settings_speedreg_slow; @@ -71,7 +72,7 @@ public: bool input_ready(); void setup(); - void update_menu_settings(); + void update_menu_settings(); uintptr_t close(Event); uintptr_t event(Event); uintptr_t block(Event); diff --git a/src/ui/ui_message.cpp b/src/ui/base/message.cpp similarity index 100% rename from src/ui/ui_message.cpp rename to src/ui/base/message.cpp diff --git a/src/ui/ui_message.h b/src/ui/base/message.h similarity index 100% rename from src/ui/ui_message.h rename to src/ui/base/message.h diff --git a/src/ui/bsnes.Manifest b/src/ui/bsnes.Manifest new file mode 100644 index 00000000..4602d4fe --- /dev/null +++ b/src/ui/bsnes.Manifest @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/src/ui/bsnes.rc b/src/ui/bsnes.rc index 9a76d707..c53b45aa 100644 --- a/src/ui/bsnes.rc +++ b/src/ui/bsnes.rc @@ -1,3 +1,4 @@ #define IDI_APP_ICON 100 -IDI_APP_ICON ICON DISCARDABLE "../data/bsnes.ico" +1 24 "ui/bsnes.Manifest" +IDI_APP_ICON ICON DISCARDABLE "data/bsnes.ico" diff --git a/src/ui/config.cpp b/src/ui/config.cpp index a82f40b9..2c3008b2 100644 --- a/src/ui/config.cpp +++ b/src/ui/config.cpp @@ -4,15 +4,38 @@ char filename[PATH_MAX + 16] = "bsnes.cfg"; struct System { static string_setting video, audio, input; - static integral_setting regulate_speed, speed; + static integral_setting speed_regulation; + static integral_setting gamma_ramp, sepia, grayscale, invert, contrast, brightness, gamma; } system; - + string_setting System::video(config(), "system.video", "Video hardware interface", ""); string_setting System::audio(config(), "system.audio", "Audio hardware interface", ""); string_setting System::input(config(), "system.input", "Input hardware interface", ""); -integral_setting System::regulate_speed(config(), "system.regulate_speed", "Regulate speed to 60hz (NTSC) / 50hz (PAL)", integral_setting::boolean, true); -integral_setting System::speed("system.speed", "Current speed regulation setting (1-5)", integral_setting::decimal, 3); +integral_setting System::speed_regulation(config(), "system.speed_regulation", + "Speed regulation setting\n" + "0 = Disabled\n" + "1 = Slowest\n" + "2 = Slow\n" + "3 = Normal\n" + "4 = Fast\n" + "5 = Fastest", + integral_setting::decimal, 3); + +integral_setting System::gamma_ramp(config(), "system.colorfilter.gamma_ramp", + "Use precalculated TV-style gamma ramp", integral_setting::boolean, true); +integral_setting System::sepia(config(), "system.colorfilter.sepia", + "Convert color to sepia tone", integral_setting::boolean, false); +integral_setting System::grayscale(config(), "system.colorfilter.grayscale", + "Convert color to grayscale tone", integral_setting::boolean, false); +integral_setting System::invert(config(), "system.colorfilter.invert", + "Invert output image colors", integral_setting::boolean, false); +integral_setting System::contrast(config(), "system.colorfilter.contrast", + "Contrast", integral_setting::decimal, 0); +integral_setting System::brightness(config(), "system.colorfilter.brightness", + "Brightness", integral_setting::decimal, 0); +integral_setting System::gamma(config(), "system.colorfilter.gamma", + "Gamma", integral_setting::decimal, 80); struct Video { static integral_setting mode; @@ -26,7 +49,6 @@ struct Video { } fullscreen; static integral_setting aspect_ntsc_x, aspect_ntsc_y, aspect_pal_x, aspect_pal_y; static integral_setting frameskip; - static integral_setting use_vram; } video; //0 = windowed, 1 = fullscreen, 2 = exclusive @@ -52,9 +74,10 @@ integral_setting Video::Windowed::hardware_filter(config(), "video.windowed.hard integral_setting::decimal, 1); integral_setting Video::Windowed::software_filter(config(), "video.windowed.software_filter", "Video software filter\n" "0 = None\n" - "1 = NTSC\n" - "2 = HQ2x\n" - "3 = Scale2x\n", + "1 = Scanline\n" + "2 = Scale2x\n" + "3 = HQ2x\n" + "4 = NTSC\n", integral_setting::decimal, 0); integral_setting Video::Fullscreen::synchronize (config(), "video.fullscreen.synchronize", "", integral_setting::boolean, false); @@ -70,20 +93,16 @@ integral_setting Video::aspect_pal_x (config(), "video.aspect_pal_x", "", integ integral_setting Video::aspect_pal_y (config(), "video.aspect_pal_y", "", integral_setting::decimal, 23); integral_setting Video::frameskip("video.frameskip", "Video frameskip", integral_setting::decimal, 0); -integral_setting Video::use_vram(config(), "video.use_vram", "Use Video RAM instead of System RAM when possible", integral_setting::boolean, true); struct Audio { static integral_setting synchronize; - static integral_setting frequency; static integral_setting mute; } audio; integral_setting Audio::synchronize(config(), "audio.synchronize", "Synchronize to audio sample rate", integral_setting::boolean, true); -integral_setting Audio::frequency(config(), "audio.frequency", "Default audio playback frequency", integral_setting::decimal, 32000); integral_setting Audio::mute(config(), "audio.mute", "Mute audio playback", integral_setting::boolean, false); -struct Input { +struct Input { static integral_setting capture_mode; - static integral_setting axis_resistance; static integral_setting allow_invalid_input; struct Joypad1 { @@ -92,41 +111,31 @@ struct Input { struct Joypad2 { static string_setting up, down, left, right, a, b, x, y, l, r, select, start; - } joypad2; - - struct GUI { - static string_setting load; - static string_setting pause; - static string_setting reset; - static string_setting power; - static string_setting toggle_fullscreen; - static string_setting toggle_menubar; - static string_setting toggle_statusbar; + } joypad2; + + struct GUI { + static string_setting load; + static string_setting pause; + static string_setting reset; + static string_setting power; + static string_setting toggle_fullscreen; + static string_setting toggle_menubar; + static string_setting toggle_statusbar; } gui; -} input; - -integral_setting Input::capture_mode(config(), "input.capture_mode", - "Capture method for input to main emulation window\n" - "When emulation window does not have focus:\n" - "0 = Allow input\n" - "1 = Ignore input\n" - "2 = Pause emulator", +} input; + +integral_setting Input::capture_mode(config(), "input.capture_mode", + "Capture method for input to main emulation window\n" + "When emulation window does not have focus:\n" + "0 = Allow input\n" + "1 = Ignore input\n" + "2 = Pause emulator", integral_setting::decimal, 2); -integral_setting Input::axis_resistance(config(), "input.axis_resistance", - "Axis resistance for all analog joypads\n" - "Affects responsiveness of analog stick movement by specifying what percentage\n" - "in any given direction the axis must be pressed to trigger a button press.\n" - "In other words, this determines how hard you have to press the analog stick to\n" - "simulate pressing e.g. left or right on a digital joypad.\n" - "Value is a percentage, from 0 (axis will trigger with virtually any axis movement)\n" - "up to 100 (axis must be pressed fully to given corner).\n" - "Value affects all four directions of the axis equally.\n" - "Note: Values below 10 or above 90 are not recommended and may not work at all.", - integral_setting::decimal, 75); - integral_setting Input::allow_invalid_input(config(), "input.allow_invalid_input", - "Allow up+down and left+right combinations (not recommended)", + "Allow up+down and left+right combinations (not recommended.)\n" + "This is not possible on an actual SNES controller, due to its design.\n" + "Enabling this option can trigger bugs in certain games.\n", integral_setting::boolean, false); string_setting Input::Joypad1::up (config(), "input.joypad1.up", "", "up"); @@ -153,20 +162,29 @@ string_setting Input::Joypad2::y (config(), "input.joypad2.y", "", "u") string_setting Input::Joypad2::l (config(), "input.joypad2.l", "", "o"); string_setting Input::Joypad2::r (config(), "input.joypad2.r", "", "l"); string_setting Input::Joypad2::select(config(), "input.joypad2.select", "", "lbracket"); -string_setting Input::Joypad2::start (config(), "input.joypad2.start", "", "rbracket"); - -string_setting Input::GUI::load (config(), "input.gui.load", "", "none"); -string_setting Input::GUI::pause (config(), "input.gui.pause", "", "f12"); -string_setting Input::GUI::reset (config(), "input.gui.reset", "", "none"); -string_setting Input::GUI::power (config(), "input.gui.power", "", "none"); -string_setting Input::GUI::toggle_fullscreen(config(), "input.gui.toggle_fullscreen", "", "f11"); -string_setting Input::GUI::toggle_menubar (config(), "input.gui.toggle_menubar", "", "escape"); +string_setting Input::Joypad2::start (config(), "input.joypad2.start", "", "rbracket"); + +string_setting Input::GUI::load (config(), "input.gui.load", "", "none"); +string_setting Input::GUI::pause (config(), "input.gui.pause", "", "f12"); +string_setting Input::GUI::reset (config(), "input.gui.reset", "", "none"); +string_setting Input::GUI::power (config(), "input.gui.power", "", "none"); +string_setting Input::GUI::toggle_fullscreen(config(), "input.gui.toggle_fullscreen", "", "f11"); +string_setting Input::GUI::toggle_menubar (config(), "input.gui.toggle_menubar", "", "escape"); string_setting Input::GUI::toggle_statusbar (config(), "input.gui.toggle_statusbar", "", "escape"); struct Misc { - static integral_setting show_status; + static integral_setting opacity; + static integral_setting status_enable; + static string_setting status_text; } misc; -integral_setting Misc::show_status(config(), "misc.show_status", "Display information statusbar", integral_setting::boolean, true); - +integral_setting Misc::opacity(config(), "misc.opacity", "Opacity of user interface windows", integral_setting::decimal, 100); +integral_setting Misc::status_enable(config(), "misc.status_enable", "Display information statusbar", integral_setting::boolean, true); +string_setting Misc::status_text(config(), "misc.status_text", + "Text to print inside statusbar\n" + "%n = cartridge file name\n" + "%t = internal cartridge header name\n" + "%f = executed frames per second\n" + "%m = maximum frames per second" + "", "%n : %f / %m"); }; diff --git a/src/ui/event.cpp b/src/ui/event.cpp index d51e1490..020f5230 100644 --- a/src/ui/event.cpp +++ b/src/ui/event.cpp @@ -4,10 +4,10 @@ void keydown(uint16_t key) { if(window_main.focused()) { if(key == input_manager.gui.load) load_rom(); if(key == input_manager.gui.pause) { - _pause_ = !_pause_; //toggle pause state - if(_pause_) { + app.pause = !app.pause; //toggle pause state + if(app.pause) { audio.clear(); - if(cartridge.loaded()) window_main.status.set_text("(Paused)"); + if(cartridge.loaded()) update_status(); } } if(key == input_manager.gui.reset) reset(); @@ -100,41 +100,59 @@ void update_software_filter(uint software_filter) { } void update_speed_regulation(uint speed) { - config::system.speed = max(1U, min(speed, 5U)); + config::system.speed_regulation = speed <= 5 ? speed : 0; + + //adjust audio frequency to match selected speed setting if(audio.cap(Audio::Frequency)) { - switch(config::system.speed) { - case 1: audio.set(Audio::Frequency, 16000); break; - case 2: audio.set(Audio::Frequency, 24000); break; - case 3: audio.set(Audio::Frequency, 32000); break; - case 4: audio.set(Audio::Frequency, 48000); break; - case 5: audio.set(Audio::Frequency, 64000); break; + switch(config::system.speed_regulation) { + case 0: audio.set(Audio::Frequency, 32000); break; //disabled + case 1: audio.set(Audio::Frequency, 16000); break; //slowest (50%) + case 2: audio.set(Audio::Frequency, 24000); break; //slow (75%) + case 3: audio.set(Audio::Frequency, 32000); break; //normal (100%) + case 4: audio.set(Audio::Frequency, 48000); break; //fast (150%) + case 5: audio.set(Audio::Frequency, 64000); break; //fastest (200%) } } + + //do not regulate speed when speed regulation is disabled + if(audio.cap(Audio::Synchronize)) { + audio.set(Audio::Synchronize, config::system.speed_regulation != 0); + } } -void update_frame_counter() { +void update_status() { if(!cartridge.loaded()) { window_main.status.set_text(""); - return; - } - - if(_pause_ || _autopause_) return; - - if(ppu.status.frames_updated) { + } else if(app.pause || app.autopause) { + window_main.status.set_text("(paused)"); + } else if(ppu.status.frames_updated) { ppu.status.frames_updated = false; - window_main.status.set_text(string() - << (int)ppu.status.frames_executed - << (snes.region() == SNES::NTSC ? " / 60 fps" : " / 50 fps") - ); + + unsigned max_framerate = snes.region() == SNES::NTSC ? 60 : 50; + switch(config::system.speed_regulation) { + case 0: max_framerate = 0; break; + case 1: max_framerate = unsigned(0.50 * max_framerate); break; + case 2: max_framerate = unsigned(0.75 * max_framerate); break; + case 3: break; + case 4: max_framerate = unsigned(1.50 * max_framerate); break; + case 5: max_framerate = unsigned(2.00 * max_framerate); break; + } + + string output = (const char*)config::misc.status_text; + replace(output, "%f", string() << (int)ppu.status.frames_executed); + replace(output, "%m", string() << (int)max_framerate); + replace(output, "%n", cartridge.info.filename); + replace(output, "%t", cartridge.info.name); + window_main.status.set_text(output); } } void update_video_settings() { load_video_settings(); -uint width = 256; -uint height = video_settings.region == 0 ? 224 : 239; -uint multiplier = minmax<1, 5>(video_settings.multiplier); + uint width = 256; + uint height = video_settings.region == 0 ? 224 : 239; + uint multiplier = minmax<1, 5>(video_settings.multiplier); width *= multiplier; height *= multiplier; if(video_settings.aspect_correction == true) { @@ -171,29 +189,45 @@ uint multiplier = minmax<1, 5>(video_settings.multiplier); } break; } -uint filter, standard; + libfilter::FilterInterface::FilterType filter; switch(video_settings.software_filter) { default: - case 0: filter = VIDEOFILTER_DIRECT; break; - case 1: filter = VIDEOFILTER_NTSC; break; - case 2: filter = VIDEOFILTER_HQ2X; break; - case 3: filter = VIDEOFILTER_SCALE2X; break; + case 0: filter = libfilter::FilterInterface::Direct; break; + case 1: filter = libfilter::FilterInterface::Scanline; break; + case 2: filter = libfilter::FilterInterface::Scale2x; break; + case 3: filter = libfilter::FilterInterface::HQ2x; break; + case 4: filter = libfilter::FilterInterface::NTSC; break; } + libfilter::filter.set(filter); + + SNES::Video::Mode mode; switch(video_settings.region) { default: - case 0: standard = SNES::VIDEOSTANDARD_NTSC; break; - case 1: standard = SNES::VIDEOSTANDARD_PAL; break; + case 0: mode = SNES::Video::ModeNTSC; break; + case 1: mode = SNES::Video::ModePAL; break; } - snes.set_video_filter(filter); - snes.set_video_standard(standard); + snes.video.set_mode(mode); video.set(Video::Synchronize, video_settings.synchronize); video.set(Video::Filter, video_settings.hardware_filter); -//update main window video mode checkbox settings + //update main window video mode checkbox settings window_main.update_menu_settings(); } +void update_opacity() { + //convert opacity from 50-100 (percentage) to 128-255 (8-bit alpha) + unsigned opacity = max(50, min(100, config::misc.opacity)); + opacity = unsigned(256.0 / 100.0 * opacity); + opacity = max(128, min(255, opacity)); + + window_settings.set_opacity(opacity); + window_input_capture.set_opacity(opacity); + window_bsxloader.set_opacity(opacity); + window_stloader.set_opacity(opacity); + window_about.set_opacity(opacity); +} + void toggle_fullscreen() { if(config::video.mode != 1) { //switch to fullscreen mode if not already in it config::video.mode = 1; @@ -224,7 +258,7 @@ bool load_rom(char *fn) { replace(dir[0], "\\", "/"); if(strlen(dir[0]) && !strend(dir[0], "/")) strcat(dir[0], "/"); -//append base path if rom path is relative + //append base path if rom path is relative if(strbegin(dir[0], "./")) { ltrim(dir[0], "./"); strcpy(dir[1], dir[0]); @@ -232,20 +266,21 @@ bool load_rom(char *fn) { strcat(dir[0], dir[1]); } - return hiro().file_load(0, fn, + return hiro().file_open(0, fn, + dir[0], "SNES images;*.smc,*.sfc,*.swc,*.fig,*.bs,*.st" - #if defined(GZIP_SUPPORT) + #if defined(GZIP_SUPPORT) ",*.gz,*.z,*.zip" - #endif - #if defined(JMA_SUPPORT) + #endif + #if defined(JMA_SUPPORT) ",*.jma" - #endif - "|All files;*.*", - dir[0]); + #endif + "|All files;*.*" + ); } void load_rom() { -char fn[PATH_MAX]; + char fn[PATH_MAX]; if(load_rom(fn) == false) return; load_cart_normal(fn); } @@ -262,7 +297,7 @@ void load_cart_normal(const char *filename) { if(cartridge.info.st011) alert("Warning: unsupported ST011 chip detected."); if(cartridge.info.st018) alert("Warning: unsupported ST018 chip detected."); - _pause_ = false; + app.pause = false; snes.power(); window_cheat_editor.refresh(); } @@ -273,7 +308,7 @@ void load_cart_bsx(const char *base, const char *slot) { if(cartridge.loaded() == true) cartridge.unload(); cartridge.load_cart_bsx(base, slot); - _pause_ = false; + app.pause = false; snes.power(); window_cheat_editor.refresh(); } @@ -284,7 +319,7 @@ void load_cart_bsc(const char *base, const char *slot) { if(cartridge.loaded() == true) cartridge.unload(); cartridge.load_cart_bsc(base, slot); - _pause_ = false; + app.pause = false; snes.power(); window_cheat_editor.refresh(); } @@ -295,7 +330,7 @@ void load_cart_st(const char *base, const char *slotA, const char *slotB) { if(cartridge.loaded() == true) cartridge.unload(); cartridge.load_cart_st(base, slotA, slotB); - _pause_ = false; + app.pause = false; snes.power(); window_cheat_editor.refresh(); } @@ -306,7 +341,7 @@ void unload_rom() { video.clear(); audio.clear(); } - window_main.status.set_text(""); + event::update_status(); window_cheat_editor.refresh(); } diff --git a/src/ui/event.h b/src/ui/event.h index 2e4b6e27..29b080bb 100644 --- a/src/ui/event.h +++ b/src/ui/event.h @@ -22,8 +22,9 @@ void update_software_filter(uint); void update_speed_regulation(uint); -void update_frame_counter(); +void update_status(); void update_video_settings(); +void update_opacity(); void toggle_fullscreen(); void toggle_menubar(); void toggle_statusbar(); diff --git a/src/ui/inputmgr.cpp b/src/ui/inputmanager.cpp similarity index 75% rename from src/ui/inputmgr.cpp rename to src/ui/inputmanager.cpp index 038b8cd1..f11be3ab 100644 --- a/src/ui/inputmgr.cpp +++ b/src/ui/inputmanager.cpp @@ -123,37 +123,37 @@ void InputManager::poll() { bool InputManager::get_status(uint device, uint button) { switch(device) { - case SNES::DEVICEID_JOYPAD1: { + case SNES::Input::DeviceIDJoypad1: { switch(button) { - case SNES::JOYPAD_UP: return joypad1.up.state; - case SNES::JOYPAD_DOWN: return joypad1.down.state; - case SNES::JOYPAD_LEFT: return joypad1.left.state; - case SNES::JOYPAD_RIGHT: return joypad1.right.state; - case SNES::JOYPAD_A: return joypad1.a.state; - case SNES::JOYPAD_B: return joypad1.b.state; - case SNES::JOYPAD_X: return joypad1.x.state; - case SNES::JOYPAD_Y: return joypad1.y.state; - case SNES::JOYPAD_L: return joypad1.l.state; - case SNES::JOYPAD_R: return joypad1.r.state; - case SNES::JOYPAD_SELECT: return joypad1.select.state; - case SNES::JOYPAD_START: return joypad1.start.state; + case SNES::Input::JoypadUp: return joypad1.up.state; + case SNES::Input::JoypadDown: return joypad1.down.state; + case SNES::Input::JoypadLeft: return joypad1.left.state; + case SNES::Input::JoypadRight: return joypad1.right.state; + case SNES::Input::JoypadA: return joypad1.a.state; + case SNES::Input::JoypadB: return joypad1.b.state; + case SNES::Input::JoypadX: return joypad1.x.state; + case SNES::Input::JoypadY: return joypad1.y.state; + case SNES::Input::JoypadL: return joypad1.l.state; + case SNES::Input::JoypadR: return joypad1.r.state; + case SNES::Input::JoypadSelect: return joypad1.select.state; + case SNES::Input::JoypadStart: return joypad1.start.state; } } break; - case SNES::DEVICEID_JOYPAD2: { + case SNES::Input::DeviceIDJoypad2: { switch(button) { - case SNES::JOYPAD_UP: return joypad2.up.state; - case SNES::JOYPAD_DOWN: return joypad2.down.state; - case SNES::JOYPAD_LEFT: return joypad2.left.state; - case SNES::JOYPAD_RIGHT: return joypad2.right.state; - case SNES::JOYPAD_A: return joypad2.a.state; - case SNES::JOYPAD_B: return joypad2.b.state; - case SNES::JOYPAD_X: return joypad2.x.state; - case SNES::JOYPAD_Y: return joypad2.y.state; - case SNES::JOYPAD_L: return joypad2.l.state; - case SNES::JOYPAD_R: return joypad2.r.state; - case SNES::JOYPAD_SELECT: return joypad2.select.state; - case SNES::JOYPAD_START: return joypad2.start.state; + case SNES::Input::JoypadUp: return joypad2.up.state; + case SNES::Input::JoypadDown: return joypad2.down.state; + case SNES::Input::JoypadLeft: return joypad2.left.state; + case SNES::Input::JoypadRight: return joypad2.right.state; + case SNES::Input::JoypadA: return joypad2.a.state; + case SNES::Input::JoypadB: return joypad2.b.state; + case SNES::Input::JoypadX: return joypad2.x.state; + case SNES::Input::JoypadY: return joypad2.y.state; + case SNES::Input::JoypadL: return joypad2.l.state; + case SNES::Input::JoypadR: return joypad2.r.state; + case SNES::Input::JoypadSelect: return joypad2.select.state; + case SNES::Input::JoypadStart: return joypad2.start.state; } } break; } diff --git a/src/ui/interface.cpp b/src/ui/interface.cpp index f7b06188..2b040526 100644 --- a/src/ui/interface.cpp +++ b/src/ui/interface.cpp @@ -9,21 +9,18 @@ SNESInterface snesinterface; //video -bool SNESInterface::video_lock(uint16 *&data, uint &pitch) { - return video.lock(data, pitch); -} - -void SNESInterface::video_unlock() { - video.unlock(); -} - static uint frameskip_counter = 0; -void SNESInterface::video_refresh() { +void SNESInterface::video_refresh(uint16_t *data, unsigned pitch, unsigned *line, unsigned width, unsigned height) { if(ppu.renderer_enabled() == true) { - SNES::video_info vi; - snes.get_video_info(&vi); - video.refresh(vi.width, vi.height); + uint32_t *output; + unsigned opitch; + if(video.lock(output, opitch) == true) { + unsigned owidth, oheight; + libfilter::filter.render(output, opitch, owidth, oheight, data, pitch, line, width, height); + video.unlock(); + video.refresh(owidth, oheight); + } } frameskip_counter++; @@ -33,10 +30,10 @@ void SNESInterface::video_refresh() { //audio -void SNESInterface::audio_sample(uint16 l_sample, uint16 r_sample) { - if(config::audio.mute == true) { - l_sample = 0; - r_sample = 0; +void SNESInterface::audio_sample(uint16 l_sample, uint16 r_sample) { + if(config::audio.mute == true) { + l_sample = 0; + r_sample = 0; } audio.sample(l_sample, r_sample); } @@ -60,6 +57,15 @@ bool SNESInterface::input_poll(uint deviceid, uint button) { void SNESInterface::init() { input_manager.bind(); + libfilter::colortable.set_format(libfilter::Colortable::RGB888); + libfilter::colortable.set_contrast(config::system.contrast); + libfilter::colortable.set_brightness(config::system.brightness); + libfilter::colortable.set_gamma(config::system.gamma); + libfilter::colortable.enable_gamma_ramp(config::system.gamma_ramp); + libfilter::colortable.enable_sepia(config::system.sepia); + libfilter::colortable.enable_grayscale(config::system.grayscale); + libfilter::colortable.enable_invert(config::system.invert); + libfilter::colortable.update(); } void SNESInterface::term() { diff --git a/src/ui/loader/ui_bsxloader.cpp b/src/ui/loader/bsxloader.cpp similarity index 97% rename from src/ui/loader/ui_bsxloader.cpp rename to src/ui/loader/bsxloader.cpp index 00853038..5cf85b14 100644 --- a/src/ui/loader/ui_bsxloader.cpp +++ b/src/ui/loader/bsxloader.cpp @@ -52,6 +52,7 @@ uintptr_t BSXLoaderWindow::cancel_tick(Event) { void BSXLoaderWindow::setup() { create(Window::AutoCenter, 640, 150, "Load BS-X Cartridge"); + set_icon(48, 48, (uint32_t*)resource::icon48); lbase.create(0, 630, 20, "Base cartridge:"); tbase.create(0, 420, 30); diff --git a/src/ui/loader/ui_bsxloader.h b/src/ui/loader/bsxloader.h similarity index 100% rename from src/ui/loader/ui_bsxloader.h rename to src/ui/loader/bsxloader.h diff --git a/src/ui/loader/ui_stloader.cpp b/src/ui/loader/stloader.cpp similarity index 98% rename from src/ui/loader/ui_stloader.cpp rename to src/ui/loader/stloader.cpp index a133b5ad..73bed7ff 100644 --- a/src/ui/loader/ui_stloader.cpp +++ b/src/ui/loader/stloader.cpp @@ -62,6 +62,7 @@ uintptr_t STLoaderWindow::cancel_tick(Event) { void STLoaderWindow::setup() { create(Window::AutoCenter, 640, 205, "Load Sufami Turbo Cartridge"); + set_icon(48, 48, (uint32_t*)resource::icon48); lbase.create(0, 630, 20, "Base cartridge:"); tbase.create(0, 420, 30); diff --git a/src/ui/loader/ui_stloader.h b/src/ui/loader/stloader.h similarity index 100% rename from src/ui/loader/ui_stloader.h rename to src/ui/loader/stloader.h diff --git a/src/ui/main.cpp b/src/ui/main.cpp index 239fec64..e5196f97 100644 --- a/src/ui/main.cpp +++ b/src/ui/main.cpp @@ -1,6 +1,8 @@ #define INTERFACE_MAIN #include "../base.h" + +#include "main.h" #include "config.cpp" void init_snes(); @@ -13,24 +15,22 @@ void term_snes(); #include using namespace ruby; -#include "inputmgr.cpp" +#include + +#include "inputmanager.cpp" #include "interface.cpp" /***** * platform abstraction layer *****/ -#include +#include using namespace libhiro; /***** * core *****/ -bool _term_ = false; -bool _pause_ = false; -bool _autopause_ = false; - #include "ui.h" #include "event.h" @@ -94,37 +94,37 @@ void get_base_path(const char *image) { } } - if(strend(t, "/") == false) { strcat(t, "/"); } + if(strend(t, "/") == false) strcat(t, "/"); config::path.base = t; } void run() { while(hiro().pending()) hiro().run(); input_manager.refresh(); - event::update_frame_counter(); if(config::input.capture_mode == 2) { bool inactive = window_main.focused() == false; - if(_autopause_ == false && inactive == true) { + if(app.autopause == false && inactive == true) { + app.autopause = true; audio.clear(); - if(cartridge.loaded()) window_main.status.set_text("(Paused)"); - _autopause_ = true; - } else if(_autopause_ == true && inactive == false) { - _autopause_ = false; + if(cartridge.loaded()) event::update_status(); + } else if(app.autopause == true && inactive == false) { + app.autopause = false; } } else { - _autopause_ = false; + app.autopause = false; } - if(cartridge.loaded() == false || _pause_ == true || _autopause_ == true) { + if(cartridge.loaded() == false || app.pause == true || app.autopause == true) { //prevent bsnes from consuming 100% CPU resources when idle #if defined(PLATFORM_WIN) Sleep(20); #elif defined(PLATFORM_X) - usleep(20); + usleep(20 * 1000); #endif } else { snes.runtoframe(); + event::update_status(); } } @@ -140,24 +140,22 @@ int main(int argc, char *argv[]) { */ set_config_filename(); get_base_path(argv[0]); - hiro().init(); config::config().load(config::filename); if(fexists(config::filename) == false) { - //in case program crashes on first run, save config file - //settings, so that they can be modified by hand ... + //in case program crashes on first run, save config file + //settings, so that they can be modified by hand ... config::config().save(config::filename); } - snes.init(); + hiro().init(); ui_init(); + snes.init(); if(argc >= 2) { cartridge.load_cart_normal(argv[1]); snes.power(); } - while(_term_ == false) { - run(); - } + while(app.term == false) run(); event::unload_rom(); diff --git a/src/ui/main.h b/src/ui/main.h new file mode 100644 index 00000000..a2ae7714 --- /dev/null +++ b/src/ui/main.h @@ -0,0 +1,7 @@ +struct Application { + bool term; + bool pause; + bool autopause; + + Application() : term(false), pause(false), autopause(false) {} +} app; diff --git a/src/ui/resource.cpp b/src/ui/resource.cpp new file mode 100644 index 00000000..d43743d4 --- /dev/null +++ b/src/ui/resource.cpp @@ -0,0 +1,36 @@ +namespace resource { + +#include "../data/icon48.h" +#include "../data/controller.h" + +static uint8_t *icon48; +static uint8_t *controller; + +//call once at program startup +void init() { + uint8_t *lzssdata; + uint8_t *rawdata; + unsigned length; + + base64::decode(lzssdata, length, enc_icon48); + lzss::decode(icon48, lzssdata, 48 * 48 * 4); + delete[] lzssdata; + + //controller data stored as 24-bit RGB888 + //expand to 32-bit ARGB8888 for direct use with hiro::Canvas + base64::decode(lzssdata, length, enc_controller); + lzss::decode(rawdata, lzssdata, 372 * 178 * 3); + delete[] lzssdata; + controller = new uint8_t[372 * 178 * 4]; + for(unsigned dp = 0, sp = 0, y = 0; y < 178; y++) { + for(unsigned x = 0; x < 372; x++) { + controller[dp++] = rawdata[sp++]; //blue + controller[dp++] = rawdata[sp++]; //green + controller[dp++] = rawdata[sp++]; //red + controller[dp++] = 255; //alpha + } + } + delete[] rawdata; +} + +} //namespace resource diff --git a/src/ui/settings/advanced.cpp b/src/ui/settings/advanced.cpp new file mode 100644 index 00000000..ff1aa7cb --- /dev/null +++ b/src/ui/settings/advanced.cpp @@ -0,0 +1,113 @@ +uintptr_t AdvancedWindow::list_change(Event) { + int pos = list.get_selection(); + edit_val.enable(pos >= 0); + set_val.enable(pos >= 0); + set_def.enable(pos >= 0); + if(pos >= 0) { + unsigned i = lookup[pos]; + string default_; + config::config().list[i]->get_default(default_); + desc.set_text(string() << "(default = " << default_ << ")\n" << config::config().list[i]->description); + string value_; + config::config().list[i]->get(value_); + edit_val.set_text(value_); + } + return true; +} + +uintptr_t AdvancedWindow::setval_tick(Event) { + char t[4096]; + edit_val.get_text(t, sizeof t); + update(list.get_selection(), t); + return true; +} + +uintptr_t AdvancedWindow::setdef_tick(Event) { + update(list.get_selection(), 0); + return true; +} + +void AdvancedWindow::update(uint pos, const char *data) { + unsigned i = lookup[pos]; + string default_; + config::config().list[i]->get_default(default_); + config::config().list[i]->set(data ? data : (const char*)default_); + string value_; + config::config().list[i]->get(value_); + edit_val.set_text(value_); + list.set_item(pos, string() + << config::config().list[i]->name + << (value_ == default_ ? "" : " (*)") + << "\t" + << (config::config().list[i]->type == setting::string_type ? "string" : "integral") + << "\t" + << value_ + ); + list.autosize_columns(); +} + +void AdvancedWindow::load() { + lookup.reset(); + + for(unsigned i = 0; i < config::config().list.size(); i++) { + string name = config::config().list[i]->name; + + //blacklist (omit/hide options that can be configured through the standard UI) + if(name == "file.autodetect_type") continue; + if(strbegin(name, "path.")) continue; + if(strbegin(name, "snes.controller_port_")) continue; + if(strpos(name, "colorfilter.") >= 0) continue; + if(name == "misc.status_enable") continue; + if(name == "system.regulate_speed") continue; + if(strbegin(name, "video.windowed.") && name != "video.windowed.synchronize") continue; + if(strbegin(name, "video.fullscreen.") && name != "video.fullscreen.synchronize") continue; + if(name == "audio.mute") continue; + if(name == "input.capture_mode") continue; + if(strbegin(name, "input.joypad")) continue; + if(strbegin(name, "input.gui")) continue; + + string value_, default_; + config::config().list[i]->get(value_); + config::config().list[i]->get_default(default_); + list.add_item(string() + << name + << (value_ == default_ ? "" : " (*)") + << "\t" + << (config::config().list[i]->type == setting::string_type ? "string" : "integral") + << "\t" + << value_ + ); + lookup.add(i); + } +} + +void AdvancedWindow::setup() { + create(0, 475, 355); + + list.create(Listbox::Header | Listbox::VerticalScrollAlways, 475, 235, "Name\tType\tValue"); + desc.create(Editbox::Multiline | Editbox::VerticalScrollAlways | Editbox::Readonly, 475, 80, + "Note: modification of certain variables will not take effect until\n" + "bsnes is restarted. (*) = modified" + ); + edit_val.create(0, 265, 30, ""); + set_val.create (0, 100, 30, "Set"); + set_def.create (0, 100, 30, "Default"); + + unsigned y = 0; + attach(list, 0, y); y += 235 + 5; + attach(desc, 0, y); y += 80 + 5; + attach(edit_val, 0, y); + attach(set_val, 270, y); + attach(set_def, 375, y); y += 30 + 5; + + load(); + list.autosize_columns(); + + edit_val.disable(); + set_val.disable(); + set_def.disable(); + + list.on_change = bind(&AdvancedWindow::list_change, this); + set_val.on_tick = bind(&AdvancedWindow::setval_tick, this); + set_def.on_tick = bind(&AdvancedWindow::setdef_tick, this); +} diff --git a/src/ui/settings/ui_advanced.h b/src/ui/settings/advanced.h similarity index 84% rename from src/ui/settings/ui_advanced.h rename to src/ui/settings/advanced.h index 561d0514..1068b77f 100644 --- a/src/ui/settings/ui_advanced.h +++ b/src/ui/settings/advanced.h @@ -5,9 +5,10 @@ public: Editbox edit_val; Button set_val; Button set_def; - - void read_config(uint pos, string &data); - void update(uint pos, const char *data); + + array lookup; + void update(uint pos, const char *data); + void load(); void setup(); uintptr_t list_change(Event); uintptr_t setval_tick(Event); diff --git a/src/ui/settings/ui_cheateditor.cpp b/src/ui/settings/cheateditor.cpp similarity index 81% rename from src/ui/settings/ui_cheateditor.cpp rename to src/ui/settings/cheateditor.cpp index 9594d09e..dee76f63 100644 --- a/src/ui/settings/ui_cheateditor.cpp +++ b/src/ui/settings/cheateditor.cpp @@ -8,7 +8,7 @@ void CheatEditorWindow::setup() { code.create(0, 155, 30, ""); desc.create(0, 315, 30, ""); -uint y = 0; + unsigned y = 0; attach(list, 0, y); y += 285 + 5; attach(add_code, 0, y); attach(toggle_code, 160, y); @@ -28,10 +28,10 @@ void CheatEditorWindow::refresh() { list.reset(); for(uint i = 0; i < cheat.count(); i++) { - bool enabled; - uint32 addr; - uint8 data; - char s_code[256], s_desc[256]; + bool enabled; + uint32 addr; + uint8 data; + char s_code[256], s_desc[256]; cheat.get(i, enabled, addr, data, s_code, s_desc); list.add_item(string() << (enabled ? "Enabled" : "Disabled") << "\t" @@ -41,21 +41,23 @@ void CheatEditorWindow::refresh() { list.autosize_columns(); -//enable controls only if cartridge is loaded -bool loaded = cartridge.loaded(); + //enable controls only if cartridge is loaded + bool loaded = cartridge.loaded(); + code.enable(loaded); + desc.enable(loaded); add_code.enable(loaded); toggle_code.enable(loaded); delete_code.enable(loaded); } uintptr_t CheatEditorWindow::toggle_event(Event) { -int index = list.get_selection(); + int index = list.get_selection(); if(index >= 0 && index < cheat.count()) { cheat.enabled(index) ? cheat.disable(index) : cheat.enable(index); - bool enabled; - uint32 addr; - uint8 data; - char s_code[256], s_desc[256]; + bool enabled; + uint32 addr; + uint8 data; + char s_code[256], s_desc[256]; cheat.get(index, enabled, addr, data, s_code, s_desc); list.set_item(index, string() << (enabled ? "Enabled" : "Disabled") << "\t" @@ -66,7 +68,7 @@ int index = list.get_selection(); } uintptr_t CheatEditorWindow::add_tick(Event) { -char s_code[256], s_desc[256]; + char s_code[256], s_desc[256]; code.get_text(s_code, sizeof s_code); desc.get_text(s_desc, sizeof s_desc); cheat.add(false, s_code, s_desc); //param 0 = false, meaning: new codes disabled by default @@ -75,7 +77,7 @@ char s_code[256], s_desc[256]; } uintptr_t CheatEditorWindow::delete_tick(Event) { -int index = list.get_selection(); + int index = list.get_selection(); if(index >= 0 && index < cheat.count()) { cheat.remove(index); refresh(); diff --git a/src/ui/settings/ui_cheateditor.h b/src/ui/settings/cheateditor.h similarity index 100% rename from src/ui/settings/ui_cheateditor.h rename to src/ui/settings/cheateditor.h diff --git a/src/ui/settings/ui_inputconfig.cpp b/src/ui/settings/inputconfig.cpp similarity index 91% rename from src/ui/settings/ui_inputconfig.cpp rename to src/ui/settings/inputconfig.cpp index ed3278e6..6473245d 100644 --- a/src/ui/settings/ui_inputconfig.cpp +++ b/src/ui/settings/inputconfig.cpp @@ -18,7 +18,7 @@ void InputConfigWindow::setup() { clrkey.create(0, 235, 30, "Unassign Key"); clrkey.disable(); -uint y = 0; + unsigned y = 0; attach(capture_mode, 0, y); y += 15 + 5; attach(capture_always, 0, y); attach(capture_focus, 160, y); @@ -39,13 +39,13 @@ uint y = 0; else if(config::input.capture_mode == 2) capture_pause.check(); else config::input.capture_mode = 0; //capture_always - for(uint i = 0; i < list_size; i++) list.add_item("???\t???"); + for(unsigned i = 0; i < list_size; i++) list.add_item("???\t???"); refresh_list(); window_input_capture.setup(); } void InputConfigWindow::refresh_list() { - for(uint i = 0; i < list_size; i++) { + for(unsigned i = 0; i < list_size; i++) { list.set_item(i, string() << list_index[i] << "\t" << input_find(get_value(i))); } list.autosize_columns(); @@ -59,23 +59,24 @@ uintptr_t InputConfigWindow::capture_change(Event e) { } uintptr_t InputConfigWindow::list_change(Event) { -int pos = list.get_selection(); + int pos = list.get_selection(); setkey.enable(pos >= 0); clrkey.enable(pos >= 0); return true; } uintptr_t InputConfigWindow::set_tick(Event) { -int pos = list.get_selection(); + int pos = list.get_selection(); if(pos < 0) return true; window_input_capture.index = pos; window_input_capture.label.set_text(string() << "Press a key to assign to " << list_index[pos] << " ..."); + window_input_capture.canvas.show(pos < 24); //only show joypad graphic if setting joypad 1/2 button window_input_capture.show(); return true; } uintptr_t InputConfigWindow::clr_tick(Event) { -int pos = list.get_selection(); + int pos = list.get_selection(); if(pos < 0) return true; set_value(pos, keyboard::none); refresh_list(); @@ -105,9 +106,12 @@ uintptr_t InputCaptureWindow::close(Event) { } void InputCaptureWindow::setup() { - create(Window::AutoCenter, 350, 100, "bsnes Key Capture"); - label.create(0, 340, 90); + create(Window::AutoCenter, 382, 208, "bsnes Key Capture"); + label.create(0, 340, 20); + canvas.create(0, 372, 178); + memcpy(canvas.buffer(), resource::controller, 372 * 178 * 4); attach(label, 5, 5); + attach(canvas, 5, 25); on_close = bind(&InputCaptureWindow::close, this); } diff --git a/src/ui/settings/ui_inputconfig.h b/src/ui/settings/inputconfig.h similarity index 94% rename from src/ui/settings/ui_inputconfig.h rename to src/ui/settings/inputconfig.h index 37060147..3efcca91 100644 --- a/src/ui/settings/ui_inputconfig.h +++ b/src/ui/settings/inputconfig.h @@ -24,7 +24,8 @@ public: class InputCaptureWindow : public Window { public: - Label label; + Label label; + Canvas canvas; bool waiting; bool locked; diff --git a/src/ui/settings/pathsettings.cpp b/src/ui/settings/pathsettings.cpp new file mode 100644 index 00000000..f515cb27 --- /dev/null +++ b/src/ui/settings/pathsettings.cpp @@ -0,0 +1,94 @@ +uintptr_t PathSettingsWindow::selectpath_rom(Event) { + char t[PATH_MAX]; + if(hiro().folder_select(&window_settings, t) == true) { + config::path.rom = t; + rompath.set_text(config::path.rom); + } + return true; +} + +uintptr_t PathSettingsWindow::defaultpath_rom(Event) { + config::path.rom = ""; + rompath.set_text(""); +} + +uintptr_t PathSettingsWindow::selectpath_save(Event) { + char t[PATH_MAX]; + if(hiro().folder_select(&window_settings, t) == true) { + config::path.save = t; + savepath.set_text(config::path.save); + } + return true; +} + +uintptr_t PathSettingsWindow::defaultpath_save(Event) { + config::path.save = ""; + savepath.set_text(""); +} + +uintptr_t PathSettingsWindow::selectpath_cheat(Event) { + char t[PATH_MAX]; + if(hiro().folder_select(&window_settings, t) == true) { + config::path.cheat = t; + cheatpath.set_text(config::path.cheat); + } + return true; +} + +uintptr_t PathSettingsWindow::defaultpath_cheat(Event) { + config::path.cheat = ""; + cheatpath.set_text(""); +} + +uintptr_t PathSettingsWindow::autodetect_tick(Event) { + config::file.autodetect_type = autodetect.checked(); +} + +void PathSettingsWindow::setup() { + create(0, 475, 355); + + lrompath.create(0, 475, 20, "Default game ROM path:"); + rompath.create(Editbox::Readonly, 265, 30); + romselect.create(0, 100, 30, "Select"); + romdefault.create(0, 100, 30, "Default"); + + lsavepath.create(0, 475, 20, "Default save RAM path:"); + savepath.create(Editbox::Readonly, 265, 30); + saveselect.create(0, 100, 30, "Select"); + savedefault.create(0, 100, 30, "Default"); + + lcheatpath.create(0, 475, 20, "Default cheat file path:"); + cheatpath.create(Editbox::Readonly, 265, 30); + cheatselect.create(0, 100, 30, "Select"); + cheatdefault.create(0, 100, 30, "Default"); + + autodetect.create(0, 475, 20, "Auto-detect file compression type (ignore file extension)"); + + unsigned y = 0; + attach(lrompath, 0, y); y += 20; + attach(rompath, 0, y); + attach(romselect, 270, y); + attach(romdefault, 375, y); y += 35; + attach(lsavepath, 0, y); y += 20; + attach(savepath, 0, y); + attach(saveselect, 270, y); + attach(savedefault, 375, y); y += 35; + attach(lcheatpath, 0, y); y += 20; + attach(cheatpath, 0, y); + attach(cheatselect, 270, y); + attach(cheatdefault, 375, y); y += 35; + attach(autodetect, 0, y); y += 20; + + romselect.on_tick = bind(&PathSettingsWindow::selectpath_rom, this); + romdefault.on_tick = bind(&PathSettingsWindow::defaultpath_rom, this); + saveselect.on_tick = bind(&PathSettingsWindow::selectpath_save, this); + savedefault.on_tick = bind(&PathSettingsWindow::defaultpath_save, this); + cheatselect.on_tick = bind(&PathSettingsWindow::selectpath_cheat, this); + cheatdefault.on_tick = bind(&PathSettingsWindow::defaultpath_cheat, this); + autodetect.on_tick = bind(&PathSettingsWindow::autodetect_tick, this); + + rompath.set_text(config::path.rom); + savepath.set_text(config::path.save); + cheatpath.set_text(config::path.cheat); + autodetect.check(config::file.autodetect_type); +} diff --git a/src/ui/settings/pathsettings.h b/src/ui/settings/pathsettings.h new file mode 100644 index 00000000..d1fb95bd --- /dev/null +++ b/src/ui/settings/pathsettings.h @@ -0,0 +1,28 @@ +class PathSettingsWindow : public Window { +public: + Label lrompath; + Editbox rompath; + Button romselect; + Button romdefault; + + Label lsavepath; + Editbox savepath; + Button saveselect; + Button savedefault; + + Label lcheatpath; + Editbox cheatpath; + Button cheatselect; + Button cheatdefault; + + Checkbox autodetect; + + uintptr_t selectpath_rom(Event); + uintptr_t defaultpath_rom(Event); + uintptr_t selectpath_save(Event); + uintptr_t defaultpath_save(Event); + uintptr_t selectpath_cheat(Event); + uintptr_t defaultpath_cheat(Event); + uintptr_t autodetect_tick(Event); + void setup(); +} window_path_settings; diff --git a/src/ui/settings/ui_rastersettings.cpp b/src/ui/settings/rastersettings.cpp similarity index 54% rename from src/ui/settings/ui_rastersettings.cpp rename to src/ui/settings/rastersettings.cpp index 45840b0e..c077564e 100644 --- a/src/ui/settings/ui_rastersettings.cpp +++ b/src/ui/settings/rastersettings.cpp @@ -15,7 +15,7 @@ void RasterSettingsWindow::setup() { preset_standard.create(0, 235, 30, "Standard Preset"); sync_ui(); -uint y = 0; + uint y = 0; attach(lcontrast, 0, y); y += 20; attach(contrast, 0, y); y += 30; attach(lbrightness, 0, y); y += 20; @@ -43,96 +43,105 @@ uint y = 0; //update all UI controls to match config file values ... void RasterSettingsWindow::sync_ui() { ui_lock = true; //supress event messages while syncing UI elements, prevents infinite recursion - contrast.set_position(config::snes.contrast + 96); - lcontrast.set_text(string() << "Contrast: " << (int)config::snes.contrast); - brightness.set_position(config::snes.brightness + 96); - lbrightness.set_text(string() << "Brightness: " << (int)config::snes.brightness); - gamma.set_position(config::snes.gamma - 10); - lgamma.set_text(string() << "Gamma: " << (int)config::snes.gamma); //TODO: print gamma as "%0.2f" / 100.0 - gamma_ramp.check(config::snes.gamma_ramp); - sepia.check(config::snes.sepia); - grayscale.check(config::snes.grayscale); - invert.check(config::snes.invert); - snes.update_color_lookup_table(); + contrast.set_position(config::system.contrast + 96); + lcontrast.set_text(string() << "Contrast: " << (int)config::system.contrast); + brightness.set_position(config::system.brightness + 96); + lbrightness.set_text(string() << "Brightness: " << (int)config::system.brightness); + gamma.set_position(config::system.gamma - 10); + lgamma.set_text(string() << "Gamma: " << (int)config::system.gamma); //TODO: print gamma as "%0.2f" / 100.0 + gamma_ramp.check(config::system.gamma_ramp); + sepia.check(config::system.sepia); + grayscale.check(config::system.grayscale); + invert.check(config::system.invert); + + libfilter::colortable.set_contrast(config::system.contrast); + libfilter::colortable.set_brightness(config::system.brightness); + libfilter::colortable.set_gamma(config::system.gamma); + libfilter::colortable.enable_gamma_ramp(config::system.gamma_ramp); + libfilter::colortable.enable_sepia(config::system.sepia); + libfilter::colortable.enable_grayscale(config::system.grayscale); + libfilter::colortable.enable_invert(config::system.invert); + libfilter::colortable.update(); + ui_lock = false; } uintptr_t RasterSettingsWindow::contrast_change(Event) { - if(!ui_lock && config::snes.contrast != contrast.get_position() - 96) { - config::snes.contrast = contrast.get_position() - 96; + if(!ui_lock && config::system.contrast != contrast.get_position() - 96) { + config::system.contrast = contrast.get_position() - 96; sync_ui(); } return true; } uintptr_t RasterSettingsWindow::brightness_change(Event) { - if(!ui_lock && config::snes.brightness != brightness.get_position() - 96) { - config::snes.brightness = brightness.get_position() - 96; + if(!ui_lock && config::system.brightness != brightness.get_position() - 96) { + config::system.brightness = brightness.get_position() - 96; sync_ui(); } return true; } uintptr_t RasterSettingsWindow::gamma_change(Event) { - if(!ui_lock && config::snes.gamma != gamma.get_position() + 10) { - config::snes.gamma = gamma.get_position() + 10; + if(!ui_lock && config::system.gamma != gamma.get_position() + 10) { + config::system.gamma = gamma.get_position() + 10; sync_ui(); } return true; } uintptr_t RasterSettingsWindow::gammaramp_tick(Event) { - if(!ui_lock && config::snes.gamma_ramp != gamma_ramp.checked()) { - config::snes.gamma_ramp = gamma_ramp.checked(); + if(!ui_lock && config::system.gamma_ramp != gamma_ramp.checked()) { + config::system.gamma_ramp = gamma_ramp.checked(); sync_ui(); } return true; } uintptr_t RasterSettingsWindow::sepia_tick(Event) { - if(!ui_lock && config::snes.sepia != sepia.checked()) { - config::snes.sepia = sepia.checked(); + if(!ui_lock && config::system.sepia != sepia.checked()) { + config::system.sepia = sepia.checked(); sync_ui(); } return true; } uintptr_t RasterSettingsWindow::grayscale_tick(Event) { - if(!ui_lock && config::snes.grayscale != grayscale.checked()) { - config::snes.grayscale = grayscale.checked(); + if(!ui_lock && config::system.grayscale != grayscale.checked()) { + config::system.grayscale = grayscale.checked(); sync_ui(); } return true; } uintptr_t RasterSettingsWindow::invert_tick(Event) { - if(!ui_lock && config::snes.invert != invert.checked()) { - config::snes.invert = invert.checked(); + if(!ui_lock && config::system.invert != invert.checked()) { + config::system.invert = invert.checked(); sync_ui(); } return true; } uintptr_t RasterSettingsWindow::optimal_tick(Event) { - config::snes.contrast = 0; - config::snes.brightness = 0; - config::snes.gamma = 80; - config::snes.gamma_ramp = true; - config::snes.sepia = false; - config::snes.grayscale = false; - config::snes.invert = false; + config::system.contrast = 0; + config::system.brightness = 0; + config::system.gamma = 80; + config::system.gamma_ramp = true; + config::system.sepia = false; + config::system.grayscale = false; + config::system.invert = false; sync_ui(); return true; } uintptr_t RasterSettingsWindow::standard_tick(Event) { - config::snes.contrast = 0; - config::snes.brightness = 0; - config::snes.gamma = 100; - config::snes.gamma_ramp = false; - config::snes.sepia = false; - config::snes.grayscale = false; - config::snes.invert = false; + config::system.contrast = 0; + config::system.brightness = 0; + config::system.gamma = 100; + config::system.gamma_ramp = false; + config::system.sepia = false; + config::system.grayscale = false; + config::system.invert = false; sync_ui(); return true; } diff --git a/src/ui/settings/ui_rastersettings.h b/src/ui/settings/rastersettings.h similarity index 100% rename from src/ui/settings/ui_rastersettings.h rename to src/ui/settings/rastersettings.h diff --git a/src/ui/settings/ui_settings.cpp b/src/ui/settings/settings.cpp similarity index 76% rename from src/ui/settings/ui_settings.cpp rename to src/ui/settings/settings.cpp index f88a3eae..b738cde7 100644 --- a/src/ui/settings/ui_settings.cpp +++ b/src/ui/settings/settings.cpp @@ -1,21 +1,23 @@ void SettingsWindow::setup() { create(Window::AutoCenter, 640, 365, "bsnes Configuration Settings"); + set_icon(48, 48, (uint32_t*)resource::icon48); panel_list.create(0, 150, 355); panel_list.add_item("Raster Settings"); panel_list.add_item("Input Configuration"); - panel_list.add_item("Cheat Code Editor"); + panel_list.add_item("Path Settings"); + panel_list.add_item("Cheat Code Editor"); panel_list.add_item("Advanced"); attach(panel_list, 5, 5); attach(window_raster_settings, 160, 5); attach(window_input_config, 160, 5); + attach(window_path_settings, 160, 5); attach(window_cheat_editor, 160, 5); attach(window_advanced, 160, 5); on_close = bind(&SettingsWindow::close, this); panel_list.on_change = bind(&SettingsWindow::list_change, this); - panel_list.set_selection(0); } @@ -27,14 +29,16 @@ uintptr_t SettingsWindow::close(Event) { uintptr_t SettingsWindow::list_change(Event) { window_raster_settings.hide(); window_input_config.hide(); + window_path_settings.hide(); window_cheat_editor.hide(); window_advanced.hide(); switch(panel_list.get_selection()) { case 0: window_raster_settings.show(); break; case 1: window_input_config.show(); break; - case 2: window_cheat_editor.show(); break; - case 3: window_advanced.show(); break; + case 2: window_path_settings.show(); break; + case 3: window_cheat_editor.show(); break; + case 4: window_advanced.show(); break; } panel_list.focus(); diff --git a/src/ui/settings/ui_settings.h b/src/ui/settings/settings.h similarity index 100% rename from src/ui/settings/ui_settings.h rename to src/ui/settings/settings.h diff --git a/src/ui/settings/ui_advanced.cpp b/src/ui/settings/ui_advanced.cpp deleted file mode 100644 index c6c4340d..00000000 --- a/src/ui/settings/ui_advanced.cpp +++ /dev/null @@ -1,93 +0,0 @@ -uintptr_t AdvancedWindow::list_change(Event) { -int pos = list.get_selection(); - set_val.enable(pos >= 0); - set_def.enable(pos >= 0); - if(pos >= 0 && pos < config::config().list.size()) { - string default_; - config::config().list[pos]->get_default(default_); - desc.set_text(string() << "(default = " << default_ << ")\n" << config::config().list[pos]->description); - string value_; - config::config().list[pos]->get(value_); - edit_val.set_text(value_); - } - return true; -} - -uintptr_t AdvancedWindow::setval_tick(Event) { -char t[4096]; - edit_val.get_text(t, sizeof t); - update(list.get_selection(), t); - return true; -} - -uintptr_t AdvancedWindow::setdef_tick(Event) { - update(list.get_selection(), 0); - return true; -} - -void AdvancedWindow::read_config(uint pos, string &data) { - data = "?\t?\t?"; - if(pos >= config::config().list.size()) return; - - string name, value_; - name = config::config().list[pos]->name; - config::config().list[pos]->get(value_); - string default_; - config::config().list[pos]->get_default(default_); - if(value_ != default_) { strcat(name, " (*)"); } - data = string() - << name << "\t" - << (config::config().list[pos]->type == setting::string_type ? "string" : "integral") << "\t" - << value_; -} - -void AdvancedWindow::update(uint pos, const char *data) { - if(pos >= config::config().list.size()) return; - - string default_; - config::config().list[pos]->get_default(default_); - config::config().list[pos]->set(data ? data : (const char*)default_); - string value_; - config::config().list[pos]->get(value_); - edit_val.set_text(value_); - read_config(pos, value_); - list.set_item(pos, value_); - list.autosize_columns(); -} - -void AdvancedWindow::setup() { - create(0, 475, 355); - - list.create(Listbox::Header | Listbox::VerticalScrollAlways, 475, 235, "Name\tType\tValue"); - desc.create(Editbox::Multiline | Editbox::VerticalScrollAlways | Editbox::Readonly, 475, 80, - "\n" - "Warning: modifification of certain variables will not take effect until\n" - "bsnes is restarted, and corresponding UI elements will not be updated\n" - "to reflect changes here. (*) = modified" - ); - edit_val.create(0, 265, 30, ""); - set_val.create (0, 100, 30, "Set"); - set_def.create (0, 100, 30, "Default"); - -uint y = 0; - attach(list, 0, y); y += 235 + 5; - attach(desc, 0, y); y += 80 + 5; - attach(edit_val, 0, y); - attach(set_val, 270, y); - attach(set_def, 375, y); y += 30 + 5; - - for(int i = 0; i < config::config().list.size(); i++) { - string val; - read_config(i, val); - list.add_item(val); - } - - list.autosize_columns(); - - set_val.disable(); - set_def.disable(); - - list.on_change = bind(&AdvancedWindow::list_change, this); - set_val.on_tick = bind(&AdvancedWindow::setval_tick, this); - set_def.on_tick = bind(&AdvancedWindow::setdef_tick, this); -} diff --git a/src/ui/ui.cpp b/src/ui/ui.cpp index 5761be2a..045ec5d5 100644 --- a/src/ui/ui.cpp +++ b/src/ui/ui.cpp @@ -1,17 +1,23 @@ -#include "ui_main.cpp" -#include "ui_about.cpp" -#include "ui_message.cpp" +#include "resource.cpp" + +#include "base/main.cpp" +#include "base/about.cpp" +#include "base/message.cpp" -#include "loader/ui_bsxloader.cpp" -#include "loader/ui_stloader.cpp" +#include "loader/bsxloader.cpp" +#include "loader/stloader.cpp" -#include "settings/ui_settings.cpp" -#include "settings/ui_rastersettings.cpp" -#include "settings/ui_inputconfig.cpp" -#include "settings/ui_cheateditor.cpp" -#include "settings/ui_advanced.cpp" +#include "settings/settings.cpp" +#include "settings/rastersettings.cpp" +#include "settings/inputconfig.cpp" +#include "settings/pathsettings.cpp" +#include "settings/cheateditor.cpp" +#include "settings/advanced.cpp" + +void ui_init() { + hiro().disable_screensaver(); + resource::init(); -void ui_init() { window_main.setup(); window_about.setup(); window_message.setup(); @@ -20,18 +26,17 @@ void ui_init() { window_stloader.setup(); window_raster_settings.setup(); - window_input_config.setup(); - window_cheat_editor.setup(); + window_input_config.setup(); + window_path_settings.setup(); + window_cheat_editor.setup(); window_advanced.setup(); window_settings.setup(); - + + event::update_opacity(); event::update_video_settings(); //call first time to resize main window and update menubar window_main.show(); while(hiro().pending()) hiro().run(); - //needed only by VideoGDI (default is RGB565) - if(config::system.video == "gdi") snes.set_video_pixel_format(SNES::PIXELFORMAT_RGB555); - video.driver(config::system.video); audio.driver(config::system.audio); input.driver(config::system.input); @@ -39,7 +44,7 @@ void ui_init() { video.set(Video::Handle, window_main.view.handle()); video.set(Video::Synchronize, false); audio.set(Audio::Handle, window_main.handle()); - audio.set(Audio::Synchronize, (bool)config::system.regulate_speed); + audio.set(Audio::Synchronize, config::system.speed_regulation != 0); audio.set(Audio::Frequency, 32000); input.set(Input::Handle, window_main.handle()); diff --git a/src/ui/ui.h b/src/ui/ui.h index dd9715d8..eaef0074 100644 --- a/src/ui/ui.h +++ b/src/ui/ui.h @@ -1,12 +1,16 @@ -#include "ui_main.h" -#include "ui_about.h" -#include "ui_message.h" +#include +#include -#include "loader/ui_bsxloader.h" -#include "loader/ui_stloader.h" +#include "base/main.h" +#include "base/about.h" +#include "base/message.h" -#include "settings/ui_settings.h" -#include "settings/ui_rastersettings.h" -#include "settings/ui_inputconfig.h" -#include "settings/ui_cheateditor.h" -#include "settings/ui_advanced.h" +#include "loader/bsxloader.h" +#include "loader/stloader.h" + +#include "settings/settings.h" +#include "settings/rastersettings.h" +#include "settings/inputconfig.h" +#include "settings/pathsettings.h" +#include "settings/cheateditor.h" +#include "settings/advanced.h" diff --git a/src/ui/ui_about.cpp b/src/ui/ui_about.cpp deleted file mode 100644 index 51742a19..00000000 --- a/src/ui/ui_about.cpp +++ /dev/null @@ -1,21 +0,0 @@ -const char AboutWindow::about_text[4096] = "" - "bsnes -- version " BSNES_VERSION "\n" - "Author: byuu\n" - "Project began: October 14th, 2004\n" - "\n\n" - "Contributors:\n" - " anomie, blargg, DMV27, GIGO, kode54, Nach,\n" - " Overload, Richard Bannister, TRAC, zones"; - -uintptr_t AboutWindow::close(Event) { - hide(); - return false; -} - -void AboutWindow::setup() { - create(Window::AutoCenter, 400, 200, "About bsnes ..."); - about.create(0, 390, 190, about_text); - attach(about, 5, 5); - - on_close = bind(&AboutWindow::close, this); -} diff --git a/src/ui/ui_about.h b/src/ui/ui_about.h deleted file mode 100644 index 04707d76..00000000 --- a/src/ui/ui_about.h +++ /dev/null @@ -1,8 +0,0 @@ -class AboutWindow : public Window { -public: - Label about; - static const char about_text[4096]; - - void setup(); - uintptr_t close(Event); -} window_about;