mirror of https://github.com/bsnes-emu/bsnes.git
Update to bsnes v040 release.
Too much to really name. The biggest news is that the entire user interface has been re-written from scratch. It is now far more polished and professional. To name one example; the cheat code editor now has checkboxes in the list to quickly toggle codes on an off, there is now a global hotkey to toggle all cheat codes, and each cheat code can contain multiple individual Game Genie or Pro Action Replay codes, allowing easy grouping of multi-part codes. You'll also notice new artwork: a logo created by Derrick Sobodash (note that the logo contest from below is still active — if someone can design a better logo, it can appear in v041), and a new photo-realistic SNES controller graphic by FirebrandX. I was finally able to utilize MinGW's profile-guided optimizations, which means this release is approximately ~12% faster than v039. And emulation itself was even improved(!), such as with Jonas Quinn's fix for a sprite overflow bug. There were many other changes as well: Linux users will be happy to see RGB overlay support for the X-Video driver, many will benefit from greatly enhanced warning messages and tooltips throughout the GUI, Windows users will now be able to access the menu without freezing emulation, etc etc.
This commit is contained in:
parent
3e7578761a
commit
def86470f4
82
src/Makefile
82
src/Makefile
|
@ -1,5 +1,6 @@
|
|||
include lib/nall/Makefile.string
|
||||
prefix = /usr/local
|
||||
ui = ui_qt
|
||||
|
||||
################
|
||||
### compiler ###
|
||||
|
@ -7,23 +8,33 @@ prefix = /usr/local
|
|||
|
||||
ifneq ($(findstring gcc,$(compiler)),) # GCC family
|
||||
flags = -O3 -fomit-frame-pointer -Ilib
|
||||
c = $(compiler) $(flags)
|
||||
cpp = $(subst cc,++,$(compiler)) $(flags)
|
||||
# note: libco *requires* -fomit-frame-pointer on i386 arch
|
||||
libcoflags := $(flags) -static
|
||||
c = $(compiler)
|
||||
cpp = $(subst cc,++,$(compiler))
|
||||
obj = o
|
||||
rule = -c $< -o $@
|
||||
link = -s
|
||||
mkbin = -o$1
|
||||
mkdef = -D$1
|
||||
mkinc = -I$1
|
||||
mklib = -l$1
|
||||
|
||||
# profile-guided optimization:
|
||||
# flags += -fprofile-generate
|
||||
# link += -lgcov
|
||||
# flags += -fprofile-use
|
||||
else ifeq ($(compiler),cl) # Visual C++
|
||||
flags = /nologo /wd4355 /wd4805 /wd4996 /Ox /GL /EHsc /Ilib
|
||||
c = cl $(flags)
|
||||
cpp = cl $(flags)
|
||||
libcoflags = $(flags)
|
||||
c = cl
|
||||
cpp = cl
|
||||
obj = obj
|
||||
rule = /c $< /Fo$@
|
||||
link = /link
|
||||
mkbin = /Fe$1
|
||||
mkdef = /D$1
|
||||
mkinc = /I$1
|
||||
mklib = $1.lib
|
||||
else
|
||||
unknown_compiler: help;
|
||||
|
@ -35,21 +46,19 @@ endif
|
|||
|
||||
ifeq ($(platform),x) # X11
|
||||
ruby = video.glx video.xv video.sdl audio.alsa audio.openal audio.oss audio.pulseaudio audio.ao input.sdl input.x
|
||||
link += `pkg-config --libs gtk+-2.0`
|
||||
link += $(call mklib,Xtst)
|
||||
delete = rm -f $1
|
||||
else ifeq ($(platform),win) # Windows
|
||||
# enable static linking to Qt for Windows build
|
||||
mingw_link_flags = -mwindows -enable-stdcall-fixup -Wl,-s -Wl,-enable-auto-import -Wl,-enable-runtime-pseudo-reloc
|
||||
|
||||
ruby = video.direct3d video.wgl video.directdraw video.gdi audio.directsound input.directinput
|
||||
link += $(if $(findstring mingw,$(compiler)),-mwindows)
|
||||
delete = $(if $(findstring i586-mingw-gcc,$(compiler)),rm -f $1,del $(subst /,\,$1))
|
||||
link += $(if $(findstring mingw,$(compiler)),$(mingw_link_flags))
|
||||
link += $(call mklib,uuid)
|
||||
link += $(call mklib,kernel32)
|
||||
link += $(call mklib,user32)
|
||||
link += $(call mklib,gdi32)
|
||||
link += $(call mklib,shell32)
|
||||
link += $(call mklib,winmm)
|
||||
link += $(call mklib,comdlg32)
|
||||
link += $(call mklib,comctl32)
|
||||
delete = $(if $(findstring i586-mingw-gcc,$(compiler)),rm -f $1,del $(subst /,\,$1))
|
||||
else
|
||||
unknown_platform: help;
|
||||
endif
|
||||
|
@ -58,8 +67,7 @@ endif
|
|||
### ruby ###
|
||||
############
|
||||
|
||||
rubyflags =
|
||||
rubyflags += $(if $(findstring .sdl,$(ruby)),`sdl-config --cflags`)
|
||||
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))
|
||||
|
@ -74,11 +82,11 @@ link += $(if $(findstring audio.pulseaudio,$(ruby)),$(call mklib,pulse-simple))
|
|||
link += $(if $(findstring input.directinput,$(ruby)),$(call mklib,dinput8) $(call mklib,dxguid))
|
||||
link += $(if $(findstring input.sdl,$(ruby)),`sdl-config --libs`)
|
||||
|
||||
####################################
|
||||
### main target and dependencies ###
|
||||
####################################
|
||||
####################
|
||||
### core objects ###
|
||||
####################
|
||||
|
||||
objects = main libco hiro ruby libfilter string \
|
||||
objects = libco ruby libfilter string \
|
||||
reader cart cheat \
|
||||
memory smemory cpu scpu smp ssmp sdsp ppu bppu snes \
|
||||
bsx srtc sdd1 spc7110 cx4 dsp1 dsp2 dsp3 dsp4 obc1 st010
|
||||
|
@ -93,38 +101,27 @@ ifeq ($(enable_jma),true)
|
|||
flags += $(call mkdef,JMA_SUPPORT)
|
||||
endif
|
||||
|
||||
objects := $(patsubst %,obj/%.$(obj),$(objects))
|
||||
rubydef := $(foreach c,$(subst .,_,$(call strupper,$(ruby))),$(call mkdef,$c))
|
||||
|
||||
# Windows resource file
|
||||
ifeq ($(platform),win)
|
||||
objects += obj/resource.$(obj)
|
||||
endif
|
||||
|
||||
################
|
||||
### implicit ###
|
||||
################
|
||||
######################
|
||||
### implicit rules ###
|
||||
######################
|
||||
|
||||
compile = \
|
||||
$(strip \
|
||||
$(if $(filter %.c,$<), \
|
||||
$(c) $1 $(rule), \
|
||||
$(c) $(flags) $1 $(rule), \
|
||||
$(if $(filter %.cpp,$<), \
|
||||
$(cpp) $1 $(rule) \
|
||||
$(cpp) $(flags) $1 $(rule) \
|
||||
) \
|
||||
) \
|
||||
)
|
||||
|
||||
%.$(obj): $<; $(call compile)
|
||||
%.$(obj): $<; $(call compile)
|
||||
|
||||
all: build;
|
||||
|
||||
############
|
||||
### main ###
|
||||
############
|
||||
|
||||
obj/main.$(obj) : ui/main.cpp ui/* ui/base/* ui/event/* ui/loader/* ui/settings/*
|
||||
obj/resource.$(obj): ui/bsnes.rc; windres ui/bsnes.rc obj/resource.$(obj)
|
||||
include $(ui)/Makefile
|
||||
objects := $(patsubst %,obj/%.$(obj),$(objects))
|
||||
rubydef := $(foreach c,$(subst .,_,$(call strupper,$(ruby))),$(call mkdef,$c))
|
||||
|
||||
#################
|
||||
### libraries ###
|
||||
|
@ -132,10 +129,8 @@ obj/resource.$(obj): ui/bsnes.rc; windres ui/bsnes.rc obj/resource.$(obj)
|
|||
|
||||
obj/ruby.$(obj): lib/ruby/ruby.cpp lib/ruby/* lib/ruby/video/* lib/ruby/audio/* lib/ruby/input/*
|
||||
$(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,$(if $(call strne,$(compiler),cl),-static))
|
||||
$(c) $(libcoflags) $(rule)
|
||||
obj/libfilter.$(obj): lib/libfilter/libfilter.cpp lib/libfilter/*
|
||||
obj/string.$(obj): lib/nall/string.cpp lib/nall/*
|
||||
|
||||
|
@ -239,14 +234,15 @@ obj/winout.$(obj) : reader/jma/winout.cpp reader/jma/*
|
|||
### targets ###
|
||||
###############
|
||||
|
||||
build: $(objects)
|
||||
build: ui_build $(objects)
|
||||
$(strip $(cpp) $(call mkbin,../bsnes) $(objects) $(link))
|
||||
|
||||
install:
|
||||
install -D -m 755 ../bsnes $(DESTDIR)$(prefix)/bin/bsnes
|
||||
install -D -m 644 data/bsnes.png $(DESTDIR)$(prefix)/share/icons/bsnes.png
|
||||
install -D -m 644 data/bsnes.png $(DESTDIR)$(prefix)/share/pixmaps/bsnes.png
|
||||
install -D -m 644 data/bsnes.desktop $(DESTDIR)$(prefix)/share/applications/bsnes.desktop
|
||||
|
||||
clean:
|
||||
clean: ui_clean
|
||||
-@$(call delete,obj/*.$(obj))
|
||||
-@$(call delete,*.res)
|
||||
-@$(call delete,*.pgd)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#define BSNES_VERSION "0.039"
|
||||
#define BSNES_VERSION "0.040"
|
||||
#define BSNES_TITLE "bsnes v" BSNES_VERSION
|
||||
|
||||
#define BUSCORE sBus
|
||||
|
@ -27,10 +27,10 @@
|
|||
#include <nall/detect.hpp>
|
||||
#include <nall/endian.hpp>
|
||||
#include <nall/file.hpp>
|
||||
#include <nall/function.hpp>
|
||||
#include <nall/moduloarray.hpp>
|
||||
#include <nall/new.hpp>
|
||||
#include <nall/platform.hpp>
|
||||
#include <nall/property.hpp>
|
||||
#include <nall/stdint.hpp>
|
||||
#include <nall/string.hpp>
|
||||
#include <nall/utility.hpp>
|
||||
|
|
Binary file not shown.
|
@ -7,14 +7,9 @@
|
|||
#include <nall/ups.hpp>
|
||||
|
||||
#include "cart.hpp"
|
||||
#include "cart_load.cpp"
|
||||
#include "cart_normal.cpp"
|
||||
#include "cart_bsx.cpp"
|
||||
#include "cart_bsc.cpp"
|
||||
#include "cart_st.cpp"
|
||||
|
||||
#include "cart_file.cpp"
|
||||
#include "cart_header.cpp"
|
||||
#include "cart_loader.cpp"
|
||||
|
||||
namespace memory {
|
||||
MappedRAM cartrom, cartram, cartrtc;
|
||||
|
@ -25,13 +20,7 @@ namespace memory {
|
|||
|
||||
Cartridge cartridge;
|
||||
|
||||
const char* Cartridge::name() const { return info.filename; }
|
||||
Cartridge::CartridgeMode Cartridge::mode() const { return info.mode; }
|
||||
Cartridge::MemoryMapper Cartridge::mapper() const { return info.mapper; }
|
||||
Cartridge::Region Cartridge::region() const { return info.region; }
|
||||
bool Cartridge::loaded() const { return cart.loaded; }
|
||||
|
||||
void Cartridge::load_begin(CartridgeMode mode) {
|
||||
void Cartridge::load_begin(Mode cartridge_mode) {
|
||||
cart.rom = cart.ram = cart.rtc = 0;
|
||||
bs.ram = 0;
|
||||
stA.rom = stA.ram = 0;
|
||||
|
@ -42,11 +31,10 @@ void Cartridge::load_begin(CartridgeMode mode) {
|
|||
stA.rom_size = stA.ram_size = 0;
|
||||
stB.rom_size = stB.ram_size = 0;
|
||||
|
||||
info.mode = mode;
|
||||
info.patched = false;
|
||||
|
||||
info.bsxcart = false;
|
||||
info.bsxflash = false;
|
||||
set(loaded, false);
|
||||
set(bsx_flash_loaded, false);
|
||||
set(patched, false);
|
||||
set(mode, cartridge_mode);
|
||||
}
|
||||
|
||||
void Cartridge::load_end() {
|
||||
|
@ -67,25 +55,25 @@ void Cartridge::load_end() {
|
|||
memory::stBrom.write_protect(true);
|
||||
memory::stBram.write_protect(false);
|
||||
|
||||
if(file::exists(get_cheat_filename(cart.fn, "cht"))) {
|
||||
string cheat_file = get_filename(cart.filename, "cht", snes.config.path.cheat);
|
||||
if(file::exists(cheat_file)) {
|
||||
cheat.clear();
|
||||
cheat.load(cheatfn);
|
||||
cheat.load(cheat_file);
|
||||
}
|
||||
|
||||
cart.loaded = true;
|
||||
bus.load_cart();
|
||||
set(loaded, true);
|
||||
}
|
||||
|
||||
bool Cartridge::unload() {
|
||||
if(cart.loaded == false) return false;
|
||||
|
||||
void Cartridge::unload() {
|
||||
if(loaded() == false) return;
|
||||
bus.unload_cart();
|
||||
|
||||
switch(info.mode) {
|
||||
case ModeNormal: unload_cart_normal(); break;
|
||||
case ModeBSX: unload_cart_bsx(); break;
|
||||
case ModeBSC: unload_cart_bsc(); break;
|
||||
case ModeSufamiTurbo: unload_cart_st(); break;
|
||||
switch(mode()) {
|
||||
case ModeNormal: unload_normal(); break;
|
||||
case ModeBsxSlotted: unload_bsx_slotted(); break;
|
||||
case ModeBsx: unload_bsx(); break;
|
||||
case ModeSufamiTurbo: unload_sufami_turbo(); break;
|
||||
}
|
||||
|
||||
if(cart.rom) { delete[] cart.rom; cart.rom = 0; }
|
||||
|
@ -97,21 +85,44 @@ bool Cartridge::unload() {
|
|||
if(stB.rom) { delete[] stB.rom; stB.rom = 0; }
|
||||
if(stB.ram) { delete[] stB.ram; stB.ram = 0; }
|
||||
|
||||
if(cheat.count() > 0 || file::exists(get_cheat_filename(cart.fn, "cht"))) {
|
||||
cheat.save(cheatfn);
|
||||
string cheat_file = get_filename(cart.filename, "cht", snes.config.path.cheat);
|
||||
if(cheat.count() > 0 || file::exists(cheat_file)) {
|
||||
cheat.save(cheat_file);
|
||||
cheat.clear();
|
||||
}
|
||||
|
||||
cart.loaded = false;
|
||||
return true;
|
||||
set(loaded, false);
|
||||
}
|
||||
|
||||
Cartridge::Cartridge() {
|
||||
cart.loaded = false;
|
||||
set(loaded, false);
|
||||
}
|
||||
|
||||
Cartridge::~Cartridge() {
|
||||
if(cart.loaded == true) unload();
|
||||
if(loaded() == true) unload();
|
||||
}
|
||||
|
||||
void Cartridge::set_cartinfo(const Cartridge::cartinfo_t &source) {
|
||||
set(region, source.region);
|
||||
set(mapper, source.mapper);
|
||||
set(dsp1_mapper, source.dsp1_mapper);
|
||||
|
||||
set(has_bsx_slot, source.bsx_slot);
|
||||
set(has_superfx, source.superfx);
|
||||
set(has_sa1, source.sa1);
|
||||
set(has_srtc, source.srtc);
|
||||
set(has_sdd1, source.sdd1);
|
||||
set(has_spc7110, source.spc7110);
|
||||
set(has_spc7110rtc, source.spc7110rtc);
|
||||
set(has_cx4, source.cx4);
|
||||
set(has_dsp1, source.dsp1);
|
||||
set(has_dsp2, source.dsp2);
|
||||
set(has_dsp3, source.dsp3);
|
||||
set(has_dsp4, source.dsp4);
|
||||
set(has_obc1, source.obc1);
|
||||
set(has_st010, source.st010);
|
||||
set(has_st011, source.st011);
|
||||
set(has_st018, source.st018);
|
||||
}
|
||||
|
||||
//==========
|
||||
|
@ -127,7 +138,7 @@ void Cartridge::cartinfo_t::reset() {
|
|||
rom_size = 0;
|
||||
ram_size = 0;
|
||||
|
||||
bsxslot = false;
|
||||
bsx_slot = false;
|
||||
superfx = false;
|
||||
sa1 = false;
|
||||
srtc = false;
|
||||
|
@ -145,46 +156,24 @@ void Cartridge::cartinfo_t::reset() {
|
|||
st018 = false;
|
||||
}
|
||||
|
||||
//apply cart-specific settings to current cartridge mode settings
|
||||
Cartridge::info_t& Cartridge::info_t::operator=(const Cartridge::cartinfo_t &source) {
|
||||
mapper = source.mapper;
|
||||
dsp1_mapper = source.dsp1_mapper;
|
||||
region = source.region;
|
||||
|
||||
bsxslot = source.bsxslot;
|
||||
superfx = source.superfx;
|
||||
sa1 = source.sa1;
|
||||
srtc = source.srtc;
|
||||
sdd1 = source.sdd1;
|
||||
spc7110 = source.spc7110;
|
||||
spc7110rtc = source.spc7110rtc;
|
||||
cx4 = source.cx4;
|
||||
dsp1 = source.dsp1;
|
||||
dsp2 = source.dsp2;
|
||||
dsp3 = source.dsp3;
|
||||
dsp4 = source.dsp4;
|
||||
obc1 = source.obc1;
|
||||
st010 = source.st010;
|
||||
st011 = source.st011;
|
||||
st018 = source.st018;
|
||||
|
||||
return *this;
|
||||
Cartridge::cartinfo_t::cartinfo_t() {
|
||||
reset();
|
||||
}
|
||||
|
||||
//=======
|
||||
//utility
|
||||
//=======
|
||||
|
||||
//ensure file path is absolute (eg resolve relative paths)
|
||||
string Cartridge::filepath(const char *filename, const char *pathname) {
|
||||
//if no pathname, return filename as-is
|
||||
string file(filename);
|
||||
replace(file, "\\", "/");
|
||||
if(!pathname || !*pathname) return file;
|
||||
file.replace("\\", "/");
|
||||
|
||||
string path = (!pathname || !*pathname) ? (const char*)snes.config.path.current : pathname;
|
||||
//ensure path ends with trailing '/'
|
||||
string path(pathname);
|
||||
replace(path, "\\", "/");
|
||||
if(!strend(path, "/")) strcat(path, "/");
|
||||
path.replace("\\", "/");
|
||||
if(!strend(path, "/")) path.append("/");
|
||||
|
||||
//replace relative path with absolute path
|
||||
if(strbegin(path, "./")) {
|
||||
|
@ -194,6 +183,52 @@ string Cartridge::filepath(const char *filename, const char *pathname) {
|
|||
|
||||
//remove folder part of filename
|
||||
lstring part;
|
||||
split(part, "/", file);
|
||||
return path << part[count(part) - 1];
|
||||
part.split("/", file);
|
||||
return path << part[part.size() - 1];
|
||||
}
|
||||
|
||||
//remove directory information and file extension ("/foo/bar.ext" -> "bar")
|
||||
string Cartridge::basename(const char *filename) {
|
||||
string name(filename);
|
||||
|
||||
//remove extension
|
||||
for(signed i = strlen(name) - 1; i >= 0; i--) {
|
||||
if(name[i] == '.') {
|
||||
name[i] = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//remove directory information
|
||||
for(signed i = strlen(name) - 1; i >= 0; i--) {
|
||||
if(name[i] == '/' || name[i] == '\\') {
|
||||
i++;
|
||||
char *output = name();
|
||||
while(true) {
|
||||
*output++ = name[i];
|
||||
if(!name[i]) break;
|
||||
i++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
//remove filename and return path only ("/foo/bar.ext" -> "/foo/bar/")
|
||||
string Cartridge::basepath(const char *filename) {
|
||||
string path(filename);
|
||||
path.replace("\\", "/");
|
||||
|
||||
//remove filename
|
||||
for(signed i = strlen(path) - 1; i >= 0; i--) {
|
||||
if(path[i] == '/') {
|
||||
path[i] = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!strend(path, "/")) path.append("/");
|
||||
return path;
|
||||
}
|
||||
|
|
|
@ -1,36 +1,22 @@
|
|||
class Cartridge {
|
||||
class Cartridge : public property {
|
||||
public:
|
||||
enum CartridgeMode {
|
||||
enum Mode {
|
||||
ModeNormal,
|
||||
ModeBSC,
|
||||
ModeBSX,
|
||||
ModeBsxSlotted,
|
||||
ModeBsx,
|
||||
ModeSufamiTurbo,
|
||||
};
|
||||
|
||||
enum CartridgeType {
|
||||
enum Type {
|
||||
TypeNormal,
|
||||
TypeBSC,
|
||||
TypeBSXBIOS,
|
||||
TypeBSX,
|
||||
TypeSufamiTurboBIOS,
|
||||
TypeBsxSlotted,
|
||||
TypeBsxBios,
|
||||
TypeBsx,
|
||||
TypeSufamiTurboBios,
|
||||
TypeSufamiTurbo,
|
||||
TypeUnknown,
|
||||
};
|
||||
|
||||
enum HeaderField {
|
||||
CartName = 0x00,
|
||||
Mapper = 0x15,
|
||||
RomType = 0x16,
|
||||
RomSize = 0x17,
|
||||
RamSize = 0x18,
|
||||
CartRegion = 0x19,
|
||||
Company = 0x1a,
|
||||
Version = 0x1b,
|
||||
Complement = 0x1c, //inverse checksum
|
||||
Checksum = 0x1e,
|
||||
ResetVector = 0x3c,
|
||||
};
|
||||
|
||||
enum Region {
|
||||
NTSC,
|
||||
PAL,
|
||||
|
@ -55,144 +41,131 @@ public:
|
|||
DSP1HiROM,
|
||||
};
|
||||
|
||||
const char* name() const;
|
||||
CartridgeMode mode() const;
|
||||
MemoryMapper mapper() const;
|
||||
Region region() const;
|
||||
//properties can be read via operator(), eg "if(cartridge.loaded() == true)";
|
||||
//warning: if loaded() == false, no other property is considered valid!
|
||||
|
||||
struct {
|
||||
bool loaded;
|
||||
char fn[PATH_MAX];
|
||||
uint8 *rom, *ram, *rtc;
|
||||
unsigned rom_size, ram_size, rtc_size;
|
||||
} cart;
|
||||
property_t<bool> loaded; //is a base cartridge inserted?
|
||||
property_t<bool> bsx_flash_loaded; //is a BS-X flash cart connected?
|
||||
property_t<bool> patched; //has a UPS patch been applied?
|
||||
property_t<string> name; //display name (filename sans path and extension)
|
||||
|
||||
struct {
|
||||
char fn[PATH_MAX];
|
||||
uint8 *ram;
|
||||
unsigned ram_size;
|
||||
} bs;
|
||||
property_t<Mode> mode;
|
||||
property_t<Region> region;
|
||||
property_t<MemoryMapper> mapper;
|
||||
property_t<DSP1MemoryMapper> dsp1_mapper;
|
||||
|
||||
struct {
|
||||
char fn[PATH_MAX];
|
||||
uint8 *rom, *ram;
|
||||
unsigned rom_size, ram_size;
|
||||
} stA, stB;
|
||||
property_t<bool> has_bsx_slot;
|
||||
property_t<bool> has_superfx;
|
||||
property_t<bool> has_sa1;
|
||||
property_t<bool> has_srtc;
|
||||
property_t<bool> has_sdd1;
|
||||
property_t<bool> has_spc7110, has_spc7110rtc;
|
||||
property_t<bool> has_cx4;
|
||||
property_t<bool> has_dsp1, has_dsp2, has_dsp3, has_dsp4;
|
||||
property_t<bool> has_obc1;
|
||||
property_t<bool> has_st010, has_st011, has_st018;
|
||||
|
||||
//main interface
|
||||
bool load_normal (const char *base);
|
||||
bool load_bsx_slotted (const char *base, const char *slot = "");
|
||||
bool load_bsx (const char *base, const char *slot = "");
|
||||
bool load_sufami_turbo(const char *base, const char *slotA = "", const char *slotB = "");
|
||||
void unload();
|
||||
|
||||
//utility functions
|
||||
static string filepath(const char *filename, const char *pathname); //"./bar.ext" -> "/foo/bar.ext"
|
||||
static string basename(const char *filename); //"/foo/bar.ext" -> "bar"
|
||||
static string basepath(const char *filename); //"/foo/bar.ext" -> "/foo/bar/"
|
||||
//this function will load 'filename', decompress it if needed, and determine what type of
|
||||
//image file 'filename' refers to (eg normal cart, BS-X flash cart, Sufami Turbo cart, etc.)
|
||||
//warning: this operation is very expensive, use sparingly!
|
||||
Type detect_image_type(const char *filename) const;
|
||||
|
||||
Cartridge();
|
||||
~Cartridge();
|
||||
|
||||
private:
|
||||
void load_begin(Mode);
|
||||
void load_end();
|
||||
void unload_normal();
|
||||
void unload_bsx_slotted();
|
||||
void unload_bsx();
|
||||
void unload_sufami_turbo();
|
||||
|
||||
struct cartinfo_t {
|
||||
CartridgeType type;
|
||||
Type type;
|
||||
Region region;
|
||||
MemoryMapper mapper;
|
||||
DSP1MemoryMapper dsp1_mapper;
|
||||
Region region;
|
||||
unsigned rom_size, ram_size;
|
||||
|
||||
unsigned rom_size;
|
||||
unsigned ram_size;
|
||||
|
||||
bool bsxslot;
|
||||
bool bsx_slot;
|
||||
bool superfx;
|
||||
bool sa1;
|
||||
bool srtc;
|
||||
bool sdd1;
|
||||
bool spc7110;
|
||||
bool spc7110rtc;
|
||||
bool spc7110, spc7110rtc;
|
||||
bool cx4;
|
||||
bool dsp1;
|
||||
bool dsp2;
|
||||
bool dsp3;
|
||||
bool dsp4;
|
||||
bool dsp1, dsp2, dsp3, dsp4;
|
||||
bool obc1;
|
||||
bool st010;
|
||||
bool st011;
|
||||
bool st018;
|
||||
bool st010, st011, st018;
|
||||
|
||||
void reset();
|
||||
cartinfo_t();
|
||||
};
|
||||
|
||||
struct info_t {
|
||||
char filename[PATH_MAX * 4];
|
||||
bool patched;
|
||||
enum HeaderField {
|
||||
CartName = 0x00,
|
||||
Mapper = 0x15,
|
||||
RomType = 0x16,
|
||||
RomSize = 0x17,
|
||||
RamSize = 0x18,
|
||||
CartRegion = 0x19,
|
||||
Company = 0x1a,
|
||||
Version = 0x1b,
|
||||
Complement = 0x1c, //inverse checksum
|
||||
Checksum = 0x1e,
|
||||
ResetVector = 0x3c,
|
||||
};
|
||||
|
||||
CartridgeMode mode;
|
||||
MemoryMapper mapper;
|
||||
DSP1MemoryMapper dsp1_mapper;
|
||||
Region region;
|
||||
void read_header(cartinfo_t &info, const uint8_t *data, unsigned size) const;
|
||||
unsigned find_header(const uint8_t *data, unsigned size) const;
|
||||
unsigned score_header(const uint8_t *data, unsigned size, unsigned addr) const;
|
||||
void set_cartinfo(const cartinfo_t&);
|
||||
|
||||
bool bsxcart; //is BS-X cart inserted?
|
||||
bool bsxflash; //is BS-X flash cart inserted into BS-X cart?
|
||||
|
||||
bool bsxslot;
|
||||
bool superfx;
|
||||
bool sa1;
|
||||
bool srtc;
|
||||
bool sdd1;
|
||||
bool spc7110;
|
||||
bool spc7110rtc;
|
||||
bool cx4;
|
||||
bool dsp1;
|
||||
bool dsp2;
|
||||
bool dsp3;
|
||||
bool dsp4;
|
||||
bool obc1;
|
||||
bool st010;
|
||||
bool st011;
|
||||
bool st018;
|
||||
|
||||
info_t& operator=(const cartinfo_t&);
|
||||
} info;
|
||||
|
||||
struct {
|
||||
char fn[PATH_MAX];
|
||||
uint8_t *data;
|
||||
unsigned size;
|
||||
} image;
|
||||
bool load_image(const char*);
|
||||
bool inspect_image(cartinfo_t &cartinfo, const char *filename);
|
||||
bool load_ram(const char *filename, uint8_t *&data, unsigned size, uint8_t init);
|
||||
|
||||
void load_cart_normal(const char*);
|
||||
void load_cart_bsc(const char*, const char*);
|
||||
void load_cart_bsx(const char*, const char*);
|
||||
void load_cart_st(const char*, const char*, const char*);
|
||||
|
||||
void unload_cart_normal();
|
||||
void unload_cart_bsx();
|
||||
void unload_cart_bsc();
|
||||
void unload_cart_st();
|
||||
|
||||
bool loaded() const;
|
||||
void load_begin(CartridgeMode);
|
||||
void load_end();
|
||||
bool unload();
|
||||
|
||||
void read_header(cartinfo_t &info, const uint8_t *data, unsigned size);
|
||||
unsigned find_header(const uint8_t *data, unsigned size);
|
||||
unsigned score_header(const uint8_t *data, unsigned size, unsigned addr);
|
||||
bool load_image(const char *filename, uint8_t *&data, unsigned &size, bool &patched) const;
|
||||
bool load_ram (const char *filename, uint8_t *&data, unsigned size, uint8_t init_value) const;
|
||||
|
||||
enum CompressionMode {
|
||||
CompressionNone, //always load without compression
|
||||
CompressionInspect, //use file header inspection
|
||||
CompressionAuto, //use file extension or file header inspection (configured by user)
|
||||
};
|
||||
bool load_file(const char *fn, uint8 *&data, unsigned &size, CompressionMode compression = CompressionNone);
|
||||
bool save_file(const char *fn, uint8 *data, unsigned size);
|
||||
bool apply_patch(const uint8_t *pdata, unsigned psize, uint8_t *&data, unsigned &size);
|
||||
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_patch_filename(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);
|
||||
|
||||
static string filepath(const char *filename, const char *pathname);
|
||||
bool load_file(const char *fn, uint8 *&data, unsigned &size, CompressionMode compression = CompressionNone) const;
|
||||
bool save_file(const char *fn, uint8 *data, unsigned size) const;
|
||||
bool apply_patch(const uint8_t *pdata, unsigned psize, uint8_t *&data, unsigned &size) const;
|
||||
|
||||
Cartridge();
|
||||
~Cartridge();
|
||||
string modify_extension(const char *filename, const char *extension) const;
|
||||
string get_filename(const char *source, const char *extension, const char *path) const;
|
||||
|
||||
private:
|
||||
char patchfn[PATH_MAX];
|
||||
char savefn[PATH_MAX];
|
||||
char rtcfn[PATH_MAX];
|
||||
char cheatfn[PATH_MAX];
|
||||
struct {
|
||||
string filename;
|
||||
uint8_t *rom, *ram, *rtc;
|
||||
unsigned rom_size, ram_size, rtc_size;
|
||||
} cart;
|
||||
|
||||
struct {
|
||||
string filename;
|
||||
uint8_t *ram;
|
||||
unsigned ram_size;
|
||||
} bs;
|
||||
|
||||
struct {
|
||||
string filename;
|
||||
uint8_t *rom, *ram;
|
||||
unsigned rom_size, ram_size;
|
||||
} stA, stB;
|
||||
};
|
||||
|
||||
namespace memory {
|
||||
|
|
|
@ -1,44 +0,0 @@
|
|||
#ifdef CART_CPP
|
||||
|
||||
void Cartridge::load_cart_bsc(const char *base, const char *slot) {
|
||||
uint8_t *data;
|
||||
unsigned size;
|
||||
strcpy(cart.fn, base);
|
||||
strcpy(bs.fn, slot);
|
||||
|
||||
load_begin(ModeBSC);
|
||||
if(load_image(base) == false) return;
|
||||
|
||||
cartinfo_t cartinfo;
|
||||
read_header(cartinfo, cart.rom = image.data, cart.rom_size = image.size);
|
||||
info = cartinfo;
|
||||
|
||||
if(load_image(slot) == true) {
|
||||
info.bsxflash = true;
|
||||
bs.ram = image.data;
|
||||
bs.ram_size = image.size;
|
||||
}
|
||||
|
||||
if(cartinfo.ram_size > 0) {
|
||||
load_ram(get_save_filename(base, "srm"), cart.ram, cart.ram_size = cartinfo.ram_size, 0xff);
|
||||
}
|
||||
|
||||
load_end();
|
||||
|
||||
//set base filename
|
||||
strcpy(info.filename, base);
|
||||
get_base_filename(info.filename);
|
||||
if(*slot) {
|
||||
char filenameBS[PATH_MAX];
|
||||
strcpy(filenameBS, slot);
|
||||
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
|
|
@ -1,49 +0,0 @@
|
|||
#ifdef CART_CPP
|
||||
|
||||
void Cartridge::load_cart_bsx(const char *base, const char *slot) {
|
||||
uint8_t *data;
|
||||
unsigned size;
|
||||
strcpy(cart.fn, base);
|
||||
strcpy(bs.fn, slot);
|
||||
|
||||
load_begin(ModeBSX);
|
||||
if(load_image(base) == false) return;
|
||||
info.bsxcart = true;
|
||||
|
||||
cartinfo_t cartinfo;
|
||||
read_header(cartinfo, cart.rom = image.data, cart.rom_size = image.size);
|
||||
info = cartinfo;
|
||||
cart.ram = 0;
|
||||
cart.ram_size = 0;
|
||||
|
||||
memset(bsxcart.sram.handle (), 0x00, bsxcart.sram.size ());
|
||||
memset(bsxcart.psram.handle(), 0x00, bsxcart.psram.size());
|
||||
|
||||
if(load_file(get_save_filename(cart.fn, "srm"), data, size, CompressionNone) == true) {
|
||||
memcpy(bsxcart.sram.handle (), data, min(bsxcart.sram.size (), size));
|
||||
delete[] data;
|
||||
}
|
||||
|
||||
if(load_file(get_save_filename(cart.fn, "psr"), data, size, CompressionNone) == true) {
|
||||
memcpy(bsxcart.psram.handle(), data, min(bsxcart.psram.size(), size));
|
||||
delete[] data;
|
||||
}
|
||||
|
||||
if(load_image(slot)) {
|
||||
info.bsxflash = true;
|
||||
bs.ram = image.data;
|
||||
bs.ram_size = image.size;
|
||||
}
|
||||
|
||||
load_end();
|
||||
|
||||
strcpy(info.filename, !*slot ? base : slot);
|
||||
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
|
|
@ -11,66 +11,23 @@
|
|||
#include "../reader/jmareader.hpp"
|
||||
#endif
|
||||
|
||||
char* Cartridge::modify_extension(char *filename, const char *extension) {
|
||||
string Cartridge::modify_extension(const char *filename_, const char *extension) const {
|
||||
string filename = filename_;
|
||||
int i;
|
||||
for(i = strlen(filename); i >= 0; i--) {
|
||||
if(filename[i] == '.') break;
|
||||
if(filename[i] == '/') break;
|
||||
if(filename[i] == '.') break;
|
||||
if(filename[i] == '/') break;
|
||||
if(filename[i] == '\\') break;
|
||||
}
|
||||
if(i > 0 && filename[i] == '.') filename[i] = 0;
|
||||
strcat(filename, ".");
|
||||
strcat(filename, extension);
|
||||
return filename;
|
||||
return filename << "." << extension;
|
||||
}
|
||||
|
||||
//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;
|
||||
}
|
||||
}
|
||||
|
||||
return filename;
|
||||
string Cartridge::get_filename(const char *source, const char *extension, const char *path) const {
|
||||
return filepath(modify_extension(source, extension), path);
|
||||
}
|
||||
|
||||
char* Cartridge::get_path_filename(char *filename, const char *path, const char *source, const char *extension) {
|
||||
strcpy(filename, source);
|
||||
modify_extension(filename, extension);
|
||||
strcpy(filename, filepath(filename, path));
|
||||
return filename;
|
||||
}
|
||||
|
||||
char* Cartridge::get_patch_filename(const char *source, const char *extension) {
|
||||
return get_path_filename(patchfn, snes.config.path.patch, source, extension);
|
||||
}
|
||||
|
||||
char* Cartridge::get_save_filename(const char *source, const char *extension) {
|
||||
return get_path_filename(savefn, snes.config.path.save, source, extension);
|
||||
}
|
||||
|
||||
char* Cartridge::get_cheat_filename(const char *source, const char *extension) {
|
||||
return get_path_filename(cheatfn, snes.config.path.cheat, source, extension);
|
||||
}
|
||||
|
||||
bool Cartridge::load_file(const char *fn, uint8 *&data, unsigned &size, CompressionMode compression) {
|
||||
bool Cartridge::load_file(const char *fn, uint8 *&data, unsigned &size, CompressionMode compression) const {
|
||||
if(file::exists(fn) == false) return false;
|
||||
|
||||
Reader::Type filetype = Reader::Normal;
|
||||
|
@ -117,7 +74,7 @@ bool Cartridge::load_file(const char *fn, uint8 *&data, unsigned &size, Compress
|
|||
return true;
|
||||
}
|
||||
|
||||
bool Cartridge::apply_patch(const uint8_t *pdata, const unsigned psize, uint8_t *&data, unsigned &size) {
|
||||
bool Cartridge::apply_patch(const uint8_t *pdata, const unsigned psize, uint8_t *&data, unsigned &size) const {
|
||||
uint8_t *outdata = 0;
|
||||
unsigned outsize;
|
||||
ups patcher;
|
||||
|
@ -126,7 +83,7 @@ bool Cartridge::apply_patch(const uint8_t *pdata, const unsigned psize, uint8_t
|
|||
bool apply = false;
|
||||
if(result == ups::ok) apply = true;
|
||||
if(snes.config.file.bypass_patch_crc32 == true) {
|
||||
if(result == ups::input_crc32_invalid) apply = true;
|
||||
if(result == ups::input_crc32_invalid) apply = true;
|
||||
if(result == ups::output_crc32_invalid) apply = true;
|
||||
}
|
||||
|
||||
|
@ -141,7 +98,7 @@ bool Cartridge::apply_patch(const uint8_t *pdata, const unsigned psize, uint8_t
|
|||
return apply;
|
||||
}
|
||||
|
||||
bool Cartridge::save_file(const char *fn, uint8 *data, unsigned size) {
|
||||
bool Cartridge::save_file(const char *fn, uint8 *data, unsigned size) const {
|
||||
file fp;
|
||||
if(!fp.open(fn, file::mode_write)) return false;
|
||||
fp.write(data, size);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#ifdef CART_CPP
|
||||
|
||||
void Cartridge::read_header(cartinfo_t &info, const uint8_t *data, unsigned size) {
|
||||
void Cartridge::read_header(cartinfo_t &info, const uint8_t *data, unsigned size) const {
|
||||
info.reset();
|
||||
unsigned index = find_header(data, size);
|
||||
|
||||
|
@ -13,7 +13,7 @@ void Cartridge::read_header(cartinfo_t &info, const uint8_t *data, unsigned size
|
|||
const uint8_t n15 = data[index + 0x15];
|
||||
if(n15 == 0x00 || n15 == 0x80 || n15 == 0x84 || n15 == 0x9c || n15 == 0xbc || n15 == 0xfc) {
|
||||
if(data[index + 0x1a] == 0x33 || data[index + 0x1a] == 0xff) {
|
||||
info.type = TypeBSX;
|
||||
info.type = TypeBsx;
|
||||
info.mapper = BSXROM;
|
||||
info.region = NTSC; //BS-X only released in Japan
|
||||
return;
|
||||
|
@ -28,7 +28,7 @@ void Cartridge::read_header(cartinfo_t &info, const uint8_t *data, unsigned size
|
|||
|
||||
if(!memcmp(data, "BANDAI SFC-ADX", 14)) {
|
||||
if(!memcmp(data + 16, "SFC-ADX BACKUP", 14)) {
|
||||
info.type = TypeSufamiTurboBIOS;
|
||||
info.type = TypeSufamiTurboBios;
|
||||
} else {
|
||||
info.type = TypeSufamiTurbo;
|
||||
}
|
||||
|
@ -53,22 +53,21 @@ void Cartridge::read_header(cartinfo_t &info, const uint8_t *data, unsigned size
|
|||
uint8 n13 = data[index - 13];
|
||||
if((n13 >= 'A' && n13 <= 'Z') || (n13 >= '0' && n13 <= '9')) {
|
||||
if(company == 0x33 || (data[index - 10] == 0x00 && data[index - 4] == 0x00)) {
|
||||
info.bsxslot = true;
|
||||
info.bsx_slot = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(info.bsxslot == true) {
|
||||
if(info.bsx_slot == true) {
|
||||
if(!memcmp(data + index, "Satellaview BS-X ", 21)) {
|
||||
//BS-X base cart
|
||||
info.type = TypeBSXBIOS;
|
||||
info.type = TypeBsxBios;
|
||||
info.mapper = BSXROM;
|
||||
info.region = NTSC; //BS-X only released in Japan
|
||||
return; //RAM size handled internally by load_cart_bsx() -> BSXCart class
|
||||
} else {
|
||||
//BS-X slotted cart
|
||||
info.type = TypeBSC;
|
||||
info.type = TypeBsxSlotted;
|
||||
info.mapper = (index == 0x7fc0 ? BSCLoROM : BSCHiROM);
|
||||
}
|
||||
} else {
|
||||
|
@ -174,7 +173,7 @@ void Cartridge::read_header(cartinfo_t &info, const uint8_t *data, unsigned size
|
|||
info.region = (region <= 1 || region >= 13) ? NTSC : PAL;
|
||||
}
|
||||
|
||||
unsigned Cartridge::find_header(const uint8_t *data, unsigned size) {
|
||||
unsigned Cartridge::find_header(const uint8_t *data, unsigned size) const {
|
||||
unsigned score_lo = score_header(data, size, 0x007fc0);
|
||||
unsigned score_hi = score_header(data, size, 0x00ffc0);
|
||||
unsigned score_ex = score_header(data, size, 0x40ffc0);
|
||||
|
@ -189,7 +188,7 @@ unsigned Cartridge::find_header(const uint8_t *data, unsigned size) {
|
|||
}
|
||||
}
|
||||
|
||||
unsigned Cartridge::score_header(const uint8_t *data, unsigned size, unsigned addr) {
|
||||
unsigned Cartridge::score_header(const uint8_t *data, unsigned size, unsigned addr) const {
|
||||
if(size < addr + 64) return 0; //image too small to contain header at this location?
|
||||
int score = 0;
|
||||
|
||||
|
|
|
@ -1,50 +0,0 @@
|
|||
#ifdef CART_CPP
|
||||
|
||||
bool Cartridge::load_image(const char *filename) {
|
||||
if(!filename || !*filename) return false;
|
||||
|
||||
uint8_t *data;
|
||||
unsigned size;
|
||||
if(!load_file(filename, data, size, CompressionAuto)) return false;
|
||||
|
||||
if((size & 0x7fff) != 512) {
|
||||
image.data = data;
|
||||
image.size = size;
|
||||
} else {
|
||||
//remove 512-byte header
|
||||
image.data = new uint8_t[image.size = size - 512];
|
||||
memcpy(image.data, data + 512, image.size);
|
||||
}
|
||||
|
||||
if(load_file(get_patch_filename(filename, "ups"), data, size, CompressionInspect) == true) {
|
||||
apply_patch(data, size, image.data, image.size);
|
||||
delete[] data;
|
||||
info.patched = true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Cartridge::inspect_image(cartinfo_t &cartinfo, const char *filename) {
|
||||
cartinfo.reset();
|
||||
if(!load_image(filename)) return false;
|
||||
|
||||
read_header(cartinfo, image.data, image.size);
|
||||
delete[] image.data;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Cartridge::load_ram(const char *filename, uint8_t *&data, unsigned size, uint8_t init) {
|
||||
data = new uint8_t[size];
|
||||
memset(data, init, size);
|
||||
|
||||
uint8_t *savedata;
|
||||
unsigned savesize;
|
||||
if(load_file(filename, savedata, savesize, CompressionNone) == false) return false;
|
||||
|
||||
memcpy(data, savedata, min(size, savesize));
|
||||
delete[] savedata;
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,244 @@
|
|||
#ifdef CART_CPP
|
||||
|
||||
//================
|
||||
//Normal cartridge
|
||||
//================
|
||||
|
||||
bool Cartridge::load_normal(const char *base) {
|
||||
uint8_t *data;
|
||||
unsigned size;
|
||||
bool patch_applied;
|
||||
cart.filename = base;
|
||||
|
||||
load_begin(ModeNormal);
|
||||
if(load_image(base, data, size, patch_applied) == false) return false;
|
||||
|
||||
snes.config.path.current = basepath(cart.filename);
|
||||
if(patch_applied) set(patched, true);
|
||||
|
||||
cartinfo_t cartinfo;
|
||||
read_header(cartinfo, cart.rom = data, cart.rom_size = size);
|
||||
set_cartinfo(cartinfo);
|
||||
|
||||
if(cartinfo.ram_size > 0) {
|
||||
load_ram(get_filename(base, "srm", snes.config.path.save), cart.ram, cart.ram_size = cartinfo.ram_size, 0xff);
|
||||
}
|
||||
|
||||
if(cartinfo.srtc || cartinfo.spc7110rtc) {
|
||||
load_ram(get_filename(base, "rtc", snes.config.path.save), cart.rtc, cart.rtc_size = 20, 0x00);
|
||||
}
|
||||
|
||||
load_end();
|
||||
set(name, basename(base));
|
||||
return true;
|
||||
}
|
||||
|
||||
void Cartridge::unload_normal() {
|
||||
if(cart.ram) save_file(get_filename(cart.filename, "srm", snes.config.path.save), cart.ram, cart.ram_size);
|
||||
if(cart.rtc) save_file(get_filename(cart.filename, "rtc", snes.config.path.save), cart.rtc, cart.rtc_size);
|
||||
}
|
||||
|
||||
//======================
|
||||
//BS-X slotted cartridge
|
||||
//======================
|
||||
|
||||
bool Cartridge::load_bsx_slotted(const char *base, const char *slot) {
|
||||
uint8_t *data;
|
||||
unsigned size;
|
||||
bool patch_applied;
|
||||
cart.filename = base;
|
||||
bs.filename = slot;
|
||||
|
||||
load_begin(ModeBsxSlotted);
|
||||
if(load_image(base, data, size, patch_applied) == false) return false;
|
||||
|
||||
snes.config.path.current = basepath(cart.filename);
|
||||
if(patch_applied) set(patched, true);
|
||||
|
||||
cartinfo_t cartinfo;
|
||||
read_header(cartinfo, cart.rom = data, cart.rom_size = size);
|
||||
set_cartinfo(cartinfo);
|
||||
|
||||
if(load_image(slot, data, size, patch_applied) == true) {
|
||||
set(bsx_flash_loaded, true);
|
||||
if(patch_applied) set(patched, true);
|
||||
bs.ram = data;
|
||||
bs.ram_size = size;
|
||||
}
|
||||
|
||||
if(cartinfo.ram_size > 0) {
|
||||
load_ram(get_filename(base, "srm", snes.config.path.save), cart.ram, cart.ram_size = cartinfo.ram_size, 0xff);
|
||||
}
|
||||
|
||||
load_end();
|
||||
string filename = basename(base);
|
||||
if(*slot) filename << " + " << basename(slot);
|
||||
set(name, filename);
|
||||
return true;
|
||||
}
|
||||
|
||||
void Cartridge::unload_bsx_slotted() {
|
||||
if(cart.ram) save_file(get_filename(cart.filename, "srm", snes.config.path.save), cart.ram, cart.ram_size);
|
||||
}
|
||||
|
||||
//====================
|
||||
//BS-X flash cartridge
|
||||
//====================
|
||||
|
||||
bool Cartridge::load_bsx(const char *base, const char *slot) {
|
||||
uint8_t *data;
|
||||
unsigned size;
|
||||
bool patch_applied;
|
||||
cart.filename = base;
|
||||
bs.filename = slot;
|
||||
|
||||
load_begin(ModeBsx);
|
||||
if(load_image(base, data, size, patch_applied) == false) return false;
|
||||
|
||||
snes.config.path.current = basepath(cart.filename);
|
||||
if(patch_applied) set(patched, true);
|
||||
|
||||
cartinfo_t cartinfo;
|
||||
read_header(cartinfo, cart.rom = data, cart.rom_size = size);
|
||||
set_cartinfo(cartinfo);
|
||||
|
||||
cart.ram = 0;
|
||||
cart.ram_size = 0;
|
||||
|
||||
memset(bsxcart.sram.handle (), 0x00, bsxcart.sram.size ());
|
||||
memset(bsxcart.psram.handle(), 0x00, bsxcart.psram.size());
|
||||
|
||||
if(load_file(get_filename(base, "srm", snes.config.path.save), data, size, CompressionNone) == true) {
|
||||
memcpy(bsxcart.sram.handle (), data, min(bsxcart.sram.size (), size));
|
||||
delete[] data;
|
||||
}
|
||||
|
||||
if(load_file(get_filename(base, "psr", snes.config.path.save), data, size, CompressionNone) == true) {
|
||||
memcpy(bsxcart.psram.handle(), data, min(bsxcart.psram.size(), size));
|
||||
delete[] data;
|
||||
}
|
||||
|
||||
if(load_image(slot, data, size, patch_applied)) {
|
||||
set(bsx_flash_loaded, true);
|
||||
if(patch_applied) set(patched, true);
|
||||
bs.ram = data;
|
||||
bs.ram_size = size;
|
||||
}
|
||||
|
||||
load_end();
|
||||
set(name, !*slot ? basename(base) : basename(slot));
|
||||
return true;
|
||||
}
|
||||
|
||||
void Cartridge::unload_bsx() {
|
||||
save_file(get_filename(cart.filename, "srm", snes.config.path.save), bsxcart.sram.handle (), bsxcart.sram.size ());
|
||||
save_file(get_filename(cart.filename, "psr", snes.config.path.save), bsxcart.psram.handle(), bsxcart.psram.size());
|
||||
}
|
||||
|
||||
//============================
|
||||
//Sufami Turbo flash cartridge
|
||||
//============================
|
||||
|
||||
bool Cartridge::load_sufami_turbo(const char *base, const char *slotA, const char *slotB) {
|
||||
uint8_t *data;
|
||||
unsigned size;
|
||||
bool patch_applied;
|
||||
cart.filename = base;
|
||||
stA.filename = slotA;
|
||||
stB.filename = slotB;
|
||||
|
||||
load_begin(ModeSufamiTurbo);
|
||||
if(load_image(base, data, size, patch_applied) == false) return false;
|
||||
|
||||
snes.config.path.current = basepath(cart.filename);
|
||||
if(patch_applied) set(patched, true);
|
||||
|
||||
cartinfo_t cartinfo;
|
||||
read_header(cartinfo, cart.rom = data, cart.rom_size = size);
|
||||
set_cartinfo(cartinfo);
|
||||
|
||||
if(load_image(slotA, data, size, patch_applied)) {
|
||||
if(patch_applied) set(patched, true);
|
||||
stA.rom = new(zeromemory) uint8_t[stA.rom_size = 0x100000];
|
||||
memcpy(stA.rom, data, min(size, stA.rom_size));
|
||||
delete[] data;
|
||||
|
||||
load_ram(get_filename(slotA, "srm", snes.config.path.save), stA.ram, stA.ram_size = 0x020000, 0xff);
|
||||
}
|
||||
|
||||
if(load_image(slotB, data, size, patch_applied)) {
|
||||
if(patch_applied) set(patched, true);
|
||||
stB.rom = new(zeromemory) uint8_t[stB.rom_size = 0x100000];
|
||||
memcpy(stB.rom, data, min(size, stB.rom_size));
|
||||
delete[] data;
|
||||
|
||||
load_ram(get_filename(slotB, "srm", snes.config.path.save), stB.ram, stB.ram_size = 0x020000, 0xff);
|
||||
}
|
||||
|
||||
load_end();
|
||||
string filename;
|
||||
if(!*slotA && !*slotB) filename << basename(base);
|
||||
else if( *slotA && !*slotB) filename << basename(slotA);
|
||||
else if(!*slotA && *slotB) filename << basename(slotB);
|
||||
else filename << basename(slotA) << " + " << basename(slotB);
|
||||
set(name, filename);
|
||||
return true;
|
||||
}
|
||||
|
||||
void Cartridge::unload_sufami_turbo() {
|
||||
if(stA.ram) save_file(get_filename(stA.filename, "srm", snes.config.path.save), stA.ram, stA.ram_size);
|
||||
if(stB.ram) save_file(get_filename(stB.filename, "srm", snes.config.path.save), stB.ram, stB.ram_size);
|
||||
}
|
||||
|
||||
//=================
|
||||
//utility functions
|
||||
//=================
|
||||
|
||||
Cartridge::Type Cartridge::detect_image_type(const char *filename) const {
|
||||
uint8_t *data;
|
||||
unsigned size;
|
||||
bool patch_applied;
|
||||
if(!load_image(filename, data, size, patch_applied)) return TypeUnknown;
|
||||
|
||||
cartinfo_t info;
|
||||
read_header(info, data, size);
|
||||
delete[] data;
|
||||
return info.type;
|
||||
}
|
||||
|
||||
bool Cartridge::load_image(const char *filename, uint8_t *&data, unsigned &size, bool &patched) const {
|
||||
if(!filename || !*filename) return false;
|
||||
if(!load_file(filename, data, size, CompressionAuto)) return false;
|
||||
|
||||
if((size & 0x7fff) == 512) {
|
||||
//remove 512-byte header
|
||||
memmove(data, data + 512, size -= 512);
|
||||
}
|
||||
|
||||
uint8_t *pdata;
|
||||
unsigned psize;
|
||||
if(load_file(get_filename(filename, "ups", snes.config.path.patch), pdata, psize, CompressionInspect) == true) {
|
||||
apply_patch(pdata, psize, data, size);
|
||||
delete[] pdata;
|
||||
patched = true;
|
||||
} else {
|
||||
patched = false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Cartridge::load_ram(const char *filename, uint8_t *&data, unsigned size, uint8_t init) const {
|
||||
data = new uint8_t[size];
|
||||
memset(data, init, size);
|
||||
|
||||
uint8_t *savedata;
|
||||
unsigned savesize;
|
||||
if(load_file(filename, savedata, savesize, CompressionNone) == false) return false;
|
||||
|
||||
memcpy(data, savedata, min(size, savesize));
|
||||
delete[] savedata;
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,35 +0,0 @@
|
|||
#ifdef CART_CPP
|
||||
|
||||
void Cartridge::load_cart_normal(const char *base) {
|
||||
uint8_t *data;
|
||||
unsigned size;
|
||||
strcpy(cart.fn, base);
|
||||
|
||||
load_begin(ModeNormal);
|
||||
if(load_image(base) == false) return;
|
||||
|
||||
cartinfo_t cartinfo;
|
||||
read_header(cartinfo, cart.rom = image.data, cart.rom_size = image.size);
|
||||
info = cartinfo;
|
||||
|
||||
if(cartinfo.ram_size > 0) {
|
||||
load_ram(get_save_filename(base, "srm"), cart.ram, cart.ram_size = cartinfo.ram_size, 0xff);
|
||||
}
|
||||
|
||||
if(cartinfo.srtc || cartinfo.spc7110rtc) {
|
||||
load_ram(get_save_filename(base, "rtc"), cart.rtc, cart.rtc_size = 20, 0x00);
|
||||
}
|
||||
|
||||
load_end();
|
||||
|
||||
//set base filename
|
||||
strcpy(info.filename, base);
|
||||
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);
|
||||
if(cart.rtc) save_file(get_save_filename(cart.fn, "rtc"), cart.rtc, cart.rtc_size);
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,62 +0,0 @@
|
|||
#ifdef CART_CPP
|
||||
|
||||
void Cartridge::load_cart_st(const char *base, const char *slotA, const char *slotB) {
|
||||
uint8_t *data;
|
||||
unsigned size;
|
||||
strcpy(cart.fn, base);
|
||||
strcpy(stA.fn, slotA);
|
||||
strcpy(stB.fn, slotB);
|
||||
|
||||
load_begin(ModeSufamiTurbo);
|
||||
if(load_image(base) == false) return;
|
||||
|
||||
cartinfo_t cartinfo;
|
||||
read_header(cartinfo, cart.rom = image.data, cart.rom_size = image.size);
|
||||
info = cartinfo;
|
||||
|
||||
if(load_image(slotA)) {
|
||||
stA.rom = new(zeromemory) uint8_t[stA.rom_size = 0x100000];
|
||||
memcpy(stA.rom, image.data, min(image.size, stA.rom_size));
|
||||
delete[] image.data;
|
||||
|
||||
load_ram(get_save_filename(slotA, "srm"), stA.ram, stA.ram_size = 0x020000, 0xff);
|
||||
}
|
||||
|
||||
if(load_image(slotB)) {
|
||||
stB.rom = new(zeromemory) uint8_t[stB.rom_size = 0x100000];
|
||||
memcpy(stB.rom, image.data, min(image.size, stB.rom_size));
|
||||
delete[] image.data;
|
||||
|
||||
load_ram(get_save_filename(slotB, "srm"), stB.ram, stB.ram_size = 0x020000, 0xff);
|
||||
}
|
||||
|
||||
load_end();
|
||||
|
||||
//set base filename
|
||||
if(!*slotA && !*slotB) {
|
||||
strcpy(info.filename, cart.fn);
|
||||
get_base_filename(info.filename);
|
||||
} else if(*slotA && !*slotB) {
|
||||
strcpy(info.filename, slotA);
|
||||
get_base_filename(info.filename);
|
||||
} else if(!*slotA && *slotB) {
|
||||
strcpy(info.filename, slotB);
|
||||
get_base_filename(info.filename);
|
||||
} else {
|
||||
char filenameA[PATH_MAX], filenameB[PATH_MAX];
|
||||
strcpy(filenameA, slotA);
|
||||
get_base_filename(filenameA);
|
||||
strcpy(filenameB, slotB);
|
||||
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
|
|
@ -1,5 +1,4 @@
|
|||
#include <../base.hpp>
|
||||
#include <nall/sort.hpp>
|
||||
|
||||
Cheat cheat;
|
||||
|
||||
|
@ -31,7 +30,7 @@ bool Cheat::decode(const char *s, Cheat::cheat_t &item) const {
|
|||
item.count = 0;
|
||||
|
||||
lstring list;
|
||||
split(list, "+", s);
|
||||
list.split("+", s);
|
||||
|
||||
for(unsigned n = 0; n < list.size(); n++) {
|
||||
unsigned addr;
|
||||
|
@ -67,6 +66,28 @@ bool Cheat::read(unsigned addr, uint8_t &data) const {
|
|||
return false;
|
||||
}
|
||||
|
||||
//==============
|
||||
//master control
|
||||
//==============
|
||||
|
||||
//global cheat system enable/disable:
|
||||
//if disabled, *all* cheat codes are disabled;
|
||||
//otherwise only individually disabled codes are.
|
||||
|
||||
bool Cheat::enabled() const {
|
||||
return cheat_system_enabled;
|
||||
}
|
||||
|
||||
void Cheat::enable() {
|
||||
cheat_system_enabled = true;
|
||||
cheat_enabled = (cheat_system_enabled && cheat_enabled_code_exists);
|
||||
}
|
||||
|
||||
void Cheat::disable() {
|
||||
cheat_system_enabled = false;
|
||||
cheat_enabled = false;
|
||||
}
|
||||
|
||||
//================================
|
||||
//cheat list manipulation routines
|
||||
//================================
|
||||
|
@ -159,15 +180,15 @@ void Cheat::disable(unsigned i) {
|
|||
|
||||
bool Cheat::load(const char *fn) {
|
||||
string data;
|
||||
if(!fread(data, fn)) return false;
|
||||
replace(data, "\r\n", "\n");
|
||||
qreplace(data, " ", "");
|
||||
if(!data.readfile(fn)) return false;
|
||||
data.replace("\r\n", "\n");
|
||||
data.qreplace(" ", "");
|
||||
|
||||
lstring line;
|
||||
split(line, "\n", data);
|
||||
line.split("\n", data);
|
||||
for(unsigned i = 0; i < line.size(); i++) {
|
||||
lstring part;
|
||||
qsplit(part, ",", line[i]);
|
||||
part.qsplit(",", line[i]);
|
||||
if(part.size() != 3) continue;
|
||||
trim(part[0], "\"");
|
||||
add(part[1] == "enabled", /* code = */ part[2], /* desc = */ part[0]);
|
||||
|
@ -189,22 +210,13 @@ bool Cheat::save(const char *fn) const {
|
|||
return true;
|
||||
}
|
||||
|
||||
void Cheat::sort() {
|
||||
if(code.size() <= 1) return; //nothing to sort?
|
||||
cheat_t *buffer = new cheat_t[code.size()];
|
||||
for(unsigned i = 0; i < code.size(); i++) buffer[i] = code[i];
|
||||
nall::sort(buffer, code.size());
|
||||
for(unsigned i = 0; i < code.size(); i++) code[i] = buffer[i];
|
||||
delete[] buffer;
|
||||
}
|
||||
|
||||
void Cheat::clear() {
|
||||
cheat_system_enabled = false;
|
||||
cheat_enabled_code_exists = false;
|
||||
memset(mask, 0, 0x200000);
|
||||
code.reset();
|
||||
}
|
||||
|
||||
Cheat::Cheat() {
|
||||
Cheat::Cheat() : cheat_system_enabled(true) {
|
||||
clear();
|
||||
}
|
||||
|
||||
|
@ -294,17 +306,18 @@ bool Cheat::encode(string &s, unsigned addr, uint8_t data, type_t type) const {
|
|||
}
|
||||
}
|
||||
|
||||
//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.
|
||||
//speed up S-CPU memory reads by disabling cheat code lookup when either:
|
||||
//a) cheat system is disabled by user, or b) no enabled cheat codes exist
|
||||
void Cheat::update_cheat_status() {
|
||||
for(unsigned i = 0; i < code.size(); i++) {
|
||||
if(code[i].enabled) {
|
||||
cheat_system_enabled = true;
|
||||
cheat_enabled_code_exists = true;
|
||||
cheat_enabled = (cheat_system_enabled && cheat_enabled_code_exists);
|
||||
return;
|
||||
}
|
||||
}
|
||||
cheat_system_enabled = false;
|
||||
cheat_enabled_code_exists = false;
|
||||
cheat_enabled = false;
|
||||
}
|
||||
|
||||
//address lookup table manipulation and mirroring
|
||||
|
@ -367,13 +380,13 @@ void Cheat::clear(unsigned addr) {
|
|||
//these two functions are used to safely store description text inside .cfg file format.
|
||||
|
||||
string& Cheat::encode_description(string &desc) const {
|
||||
replace(desc, "\"", "\\q");
|
||||
replace(desc, "\n", "\\n");
|
||||
desc.replace("\"", "\\q");
|
||||
desc.replace("\n", "\\n");
|
||||
return desc;
|
||||
}
|
||||
|
||||
string& Cheat::decode_description(string &desc) const {
|
||||
replace(desc, "\\q", "\"");
|
||||
replace(desc, "\\n", "\n");
|
||||
desc.replace("\\q", "\"");
|
||||
desc.replace("\\n", "\n");
|
||||
return desc;
|
||||
}
|
||||
|
|
|
@ -21,8 +21,12 @@ public:
|
|||
bool decode(const char *s, cheat_t &item) const;
|
||||
bool read(unsigned addr, uint8_t &data) const;
|
||||
|
||||
inline bool enabled() const { return cheat_system_enabled; }
|
||||
bool enabled() const;
|
||||
void enable();
|
||||
void disable();
|
||||
|
||||
inline unsigned count() const { return code.size(); }
|
||||
inline bool active() const { return cheat_enabled; }
|
||||
inline bool exists(unsigned addr) const { return mask[addr >> 3] & 1 << (addr & 7); }
|
||||
|
||||
bool add(bool enable, const char *code, const char *desc);
|
||||
|
@ -36,14 +40,15 @@ public:
|
|||
|
||||
bool load(const char *fn);
|
||||
bool save(const char *fn) const;
|
||||
|
||||
void sort();
|
||||
void clear();
|
||||
|
||||
Cheat();
|
||||
|
||||
private:
|
||||
bool cheat_enabled; //cheat_enabled == (cheat_enabled_code_exists && cheat_system_enabled);
|
||||
bool cheat_enabled_code_exists;
|
||||
bool cheat_system_enabled;
|
||||
|
||||
uint8_t mask[0x200000];
|
||||
vector<cheat_t> code;
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@ void DSP1::reset() {
|
|||
* of expected ranges
|
||||
*****/
|
||||
bool DSP1::addr_decode(uint16 addr) {
|
||||
switch(cartridge.info.dsp1_mapper) {
|
||||
switch(cartridge.dsp1_mapper()) {
|
||||
case Cartridge::DSP1LoROM1MB: {
|
||||
//$[20-3f]:[8000-bfff] = DR, $[20-3f]:[c000-ffff] = SR
|
||||
return (addr >= 0xc000);
|
||||
|
|
|
@ -10,7 +10,7 @@ const unsigned SPC7110::months[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 3
|
|||
void SPC7110::init() {}
|
||||
|
||||
void SPC7110::enable() {
|
||||
uint16_t limit = (cartridge.info.spc7110rtc ? 0x4842 : 0x483f);
|
||||
uint16_t limit = (cartridge.has_spc7110rtc() ? 0x4842 : 0x483f);
|
||||
for(uint16_t i = 0x4800; i <= limit; i++) memory::mmio.map(i, *this);
|
||||
}
|
||||
|
||||
|
@ -74,7 +74,7 @@ void SPC7110::reset() {
|
|||
r4841 = 0x00;
|
||||
r4842 = 0x00;
|
||||
|
||||
if(cartridge.info.spc7110rtc) {
|
||||
if(cartridge.has_spc7110rtc()) {
|
||||
rtc_state = RTCS_Inactive;
|
||||
rtc_mode = RTCM_Linear;
|
||||
rtc_index = 0;
|
||||
|
@ -99,7 +99,7 @@ void SPC7110::update_time(int offset) {
|
|||
| (memory::cartrtc.read(17) << 8)
|
||||
| (memory::cartrtc.read(18) << 16)
|
||||
| (memory::cartrtc.read(19) << 24);
|
||||
time_t current_time = time(0);
|
||||
time_t current_time = time(0) - offset;
|
||||
|
||||
//sizeof(time_t) is platform-dependent; though memory::cartrtc needs to be platform-agnostic.
|
||||
//yet platforms with 32-bit signed time_t will overflow every ~68 years. handle this by
|
||||
|
|
|
@ -3,8 +3,14 @@
|
|||
|
||||
#include "dcpu.cpp"
|
||||
|
||||
void CPU::power() {
|
||||
cpu_version = snes.config.cpu.version;
|
||||
}
|
||||
|
||||
void CPU::reset() {
|
||||
}
|
||||
|
||||
CPU::CPU() {
|
||||
cpu_version = 2;
|
||||
}
|
||||
|
||||
CPU::~CPU() {
|
||||
|
|
|
@ -17,8 +17,8 @@ public:
|
|||
regs_t regs;
|
||||
|
||||
virtual void scanline() = 0;
|
||||
virtual void power() = 0;
|
||||
virtual void reset() = 0;
|
||||
virtual void power();
|
||||
virtual void reset();
|
||||
|
||||
/*****
|
||||
* in opcode-based CPU emulators, the main emulation routine
|
||||
|
|
|
@ -11,6 +11,8 @@ priority_queue<unsigned> event(512, bind(&sCPU::queue_event, &cpu));
|
|||
#include "timing/timing.cpp"
|
||||
|
||||
void sCPU::power() {
|
||||
CPU::power();
|
||||
|
||||
regs.a = regs.x = regs.y = 0x0000;
|
||||
regs.s = 0x01ff;
|
||||
|
||||
|
@ -22,6 +24,8 @@ void sCPU::power() {
|
|||
}
|
||||
|
||||
void sCPU::reset() {
|
||||
CPU::reset();
|
||||
|
||||
regs.pc.d = 0x000000;
|
||||
regs.pc.l = bus.read(0xfffc);
|
||||
regs.pc.h = bus.read(0xfffd);
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
[Desktop Entry]
|
||||
Name=bsnes
|
||||
Comment=SNES emulator
|
||||
Exec=bsnes
|
||||
Icon=bsnes
|
||||
Terminal=false
|
||||
Type=Application
|
||||
Categories=Game;Emulator;
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,81 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head></head>
|
||||
<body>
|
||||
|
||||
<h1>bsnes™ Usage Documentation</h1><br>
|
||||
|
||||
bsnes is a Super Nintendo / Super Famicom emulator that strives to provide
|
||||
the most faithful emulation experience possible. It focuses on accuracy and
|
||||
clean code; over speed and features.
|
||||
<hr>
|
||||
|
||||
<h2><u>Modes of Operation</u></h2><br>
|
||||
|
||||
bsnes is capable of running both in its default multi-user mode, as well as
|
||||
in single-user mode.<br>
|
||||
<br>
|
||||
|
||||
In multi-user mode, configuration data is stored inside the user's home
|
||||
directory. On Windows, this is located at "%APPDATA%/.bsnes". On other operating
|
||||
systems, this is located at "~/.bsnes".<br>
|
||||
<br>
|
||||
|
||||
To enable single-user mode, create a blank "bsnes.cfg" file inside the same
|
||||
folder as the bsnes executable. bsnes will then use this file to store
|
||||
configuration data.
|
||||
<hr>
|
||||
|
||||
<h2><u>Supported Filetypes</u></h2><br>
|
||||
|
||||
<b>SFC, SMC, SWC, FIG:</b> SNES cartridge — ROM image.<br>
|
||||
<b>BS:</b> Satellaview BS-X flash cartridge — EEPROM image.<br>
|
||||
<b>ST:</b> Sufami Turbo cartridge — ROM image.<br>
|
||||
<b>SRM, PSR:</b> non-volatile memory, often used to save game data — (P)SRAM image.<br>
|
||||
<b>RTC:</b> real-time clock non-volatile memory.<br>
|
||||
<b>UPS:</b> patch data, used to dynamically modify cartridge of same base filename upon load.<br>
|
||||
<b>CHT:</b> plain-text list of "Game Genie" / "Pro Action Replay" codes.
|
||||
<hr>
|
||||
|
||||
<h2><u>Known Limitations</u></h2><br>
|
||||
|
||||
<b>Cartridge co-processors:</b> certain cartridges contain special co-processor chips to enhance
|
||||
their functionality. Some of these are either partially or completely unsupported. A message box
|
||||
warning will pop up when attempting to load such a cartridge.<br>
|
||||
<br>
|
||||
|
||||
<b>Satellaview BS-X emulation:</b> this hardware is only partially supported. As a result,
|
||||
most BS-X software will not function correctly.<br>
|
||||
<br>
|
||||
|
||||
<b>Savestates:</b> due to the design of bsnes, it is not plausible to
|
||||
implement support for savestate and/or rewind functionality.<br>
|
||||
<br>
|
||||
|
||||
<b>Netplay:</b> internet multiplay is not currently supported nor planned.
|
||||
<hr>
|
||||
|
||||
<h2><u>Contributors</u></h2>
|
||||
• Andreas Naive<br>
|
||||
• anomie<br>
|
||||
• Derrick Sobodash<br>
|
||||
• DMV27<br>
|
||||
• FirebrandX<br>
|
||||
• FitzRoy<br>
|
||||
• GIGO<br>
|
||||
• Jonas Quinn<br>
|
||||
• kode54<br>
|
||||
• krom<br>
|
||||
• Matthew Callis<br>
|
||||
• Nach<br>
|
||||
• neviksti<br>
|
||||
• Overload<br>
|
||||
• RedDwarf<br>
|
||||
• Richard Bannister<br>
|
||||
• Shay Green<br>
|
||||
• tetsuo55<br>
|
||||
• TRAC<br>
|
||||
• zones<br>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -1,41 +0,0 @@
|
|||
static char enc_icon48[] = {
|
||||
"_gAB8AHwAfAB8AHwAfAB8P8B8AHwAfAB8AHwAfAB8AHw_wHwAfAB8AHwAfAB8AHw"
|
||||
"AfD_AfAB8AHwAfAB8AHwAfAB8P8B8AHwAfAB8AHwAfAB8AHw_wHwAfAB8AHwAfAB"
|
||||
"8AHwAfD_AfAB8AHwAfAB8AHwAfAB8P8B8AHwAfAB8AHwAfAB8AHw_wHwAfAB8AHw"
|
||||
"AfAB8AHwAfD_AfAB8AHwAfAB8AHwAfABAAD_qgAD_p8VOwD_oRaI_qEWthD-oBbL"
|
||||
"CADE_qEAFaP_oBZm_p38GBU28AHwAfAB8AHwAfBfAfAB8AFQuBCoAMe0AP4twAD_"
|
||||
"BPAEIPAkAIX_-JkZCkLwAfAB8AHwAfBHAfAB8HSB_wABtABvPQQA-7DwBPAEECwA"
|
||||
"0P74oBIbTvAB8AHwAfAB8EcB8AHweEGgFWwkAv0HqPAE8MSwFdX-oRr-E1LwAfAB"
|
||||
"8AHwAfAB8Lzg6KEUMRAC96DwBPAE8P3EIKXAEVrwAfAB8AHwAfDzAfDAsBa3mPAE"
|
||||
"8ATwwED_uAE4E1rwAfAB8AHwAfAB8PG8YKIVJDjyBPAE8ATA-KAWoF7wAfAB8AHw"
|
||||
"AfB7AfD4gmmQ8ATwBPDM4-Z_XvAB8AHwAfAB8AHwwICPH_wSePQE8ATwBMClDxEB"
|
||||
"YNBERP8PPz_-oEw-Pv56CACMCACCgggAXT8__yD2BA808wHwAfDAkKMSDv6AmxwS"
|
||||
"_qQYH8QC6kjIAJTUAOug8ATwhOFDRABYUDw8_hWsAI9QPj7_8KwA_wTw_oL6BAC1"
|
||||
"PT3-MpbwjwHwAfAB8AxmpRkUFAOOkxjzBPAEoJ8PEEAQdZQAOrwA47TwBPAoAPh9"
|
||||
"yAB0hvAB8AHwAfBcsha6XFwC-KzwBPDsAOQ0EHWQAD0EAPSs8ATwBMCAn4LwAfAB"
|
||||
"8AHwXLUVakz4gwTwKACbQkL-F0wBTuac8ATwiPH-_AQATh-C8AHwAfAB8ExifwAC"
|
||||
"DcAAtbTwfKT8_qETejSAAJOc8ATwBPAUY9p9CAAIhvAB8AHwAfCEYZ4MGDWE8YCn"
|
||||
"nEs8_3oRgAD1lPAE8ATwBKBVh4LwAQAUCwDNACQEAKBNAMwAaggAewgAoH8AywBz"
|
||||
"CABbCABgNwDIAA458Nxp2IO88Hwhzf-ZEQ-0A15XjPAE8ATwhOGlfaDOVAA1oACY"
|
||||
"sADhCAD9jQgA_wTwBEDMAPQEAHq7NABjCAAcq2QRvPAW9sUMXHwAiIzwBPAE8MDg"
|
||||
"otZxIMsAGYQArQQATv6s8ATwzPAA4wQAUVcKAGQmYAR9MILpBAB3sP-qKgbgUPwC"
|
||||
"mwQAevuAAP6U8ATwBPDAYO3QANEAHIQA3JjwBPCrBPAEoPwEAGyAQVRwAeCV_qIX"
|
||||
"WPQWZNJ8AKoQgAQbSAI2hABtzAC6xAgA_aDwBPCIxOc4Ab6mjPAE8ATwBPAEAPYE"
|
||||
"AK9YZwHwAfA2QwLAAEgEAK7dqPAE8MCAx8AA5Ijw_wTwBPAE8ExCFPQB8AHwAQDQ"
|
||||
"Q0P-E5QAw6zwBPD1wECLcAC2iPAE8ATwBPD1BAD70AMqmvAB8AHwATDQODj-EpgA"
|
||||
"1LDwxPbFBAAxcCDNAOyM8ATwewTwBOCGkvAB8AHwIOM8t-AzuPC8g66UasQwyJDw"
|
||||
"VwTwBPAEIPHEBG4KAAJBDcYKAP7-LAQAT1UEAGUEAHAEAGoEAFd1BAA5BAASJAAY"
|
||||
"-8Qg_gaytPB8Yeg7O_8eUTGgvwAEyABUUAK3XZAA9aTw6OJsFdgIAIOQAM8AG_11"
|
||||
"_guUAKpgCAC1BADvDAD_BPAxBED-_vgIAEoA_nzDBAD4o0BA_lO88Lwgjuy4FPLw"
|
||||
"AYDPABCYAKpCoABtBACKBACbEACKngQAk4wnzQBXoCd3qBe5plgh4azwBPDM8P76"
|
||||
"9QQAiwwA1CjgGeQSvMD6yAQAKbHwAfAB8AHwvMC9RBH-nPAE8ATwBKDAxCFVVDXu"
|
||||
"sADzCAC9dEgEX3nwAfAB8AHwvMAxBAD8X4zwBPAE8ATwTAB-3Ewh_bQADHHwAfAB"
|
||||
"8AHwAfDAYL50iPAE8ATwBPDAQMRl8L8B8AHwAfAB8AHwcABMiPDvBPAE8ATwwECc"
|
||||
"ZfAB8AHwrwHwAfAB8HQAAnQAsIzw9wTwBPC84OQIABy1AfAB8O8B8AHwAfDs1ZUI"
|
||||
"8wTwBPD1vGDHBAAkXfAB8AHwAfCnAfAB8Mjw_zCEAJ8EAK7rpPAE8CgA9wQAvAQA"
|
||||
"_lQQAJC1AfAB8AHwAfAB8FcB8MjwkBE6BABsBACPVQQApAQArwQAqQQAl_UgAHgI"
|
||||
"AEsEANQsAfAB8P8B8AHwAfAB8AHwAfAB8AHw_wHwAfAB8AHwAfAB8AHwAfD_AfAB"
|
||||
"8AHwAfAB8AHwAfAB8P8B8AHwAfAB8AHwAfAB8AHw_wHwAfAB8AHwAfAB8AHwAfD_"
|
||||
"AfAB8AHwAfAB8AHwAfAB8P8B8AHwAfAB8AHwAfAB8AHw_wHwAfAB8AHwAfAB8AHw"
|
||||
"AfD_AfAB8AHwAfAB8AHwAfAB8A8B8AHwAfABsA"
|
||||
};
|
Binary file not shown.
After Width: | Height: | Size: 190 KiB |
|
@ -0,0 +1,87 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head></head>
|
||||
<body>
|
||||
|
||||
<h1>bsnes™ Reference License</h1><br>
|
||||
<b>Copyright © 2004–2009 byuu<br>
|
||||
All rights reserved</b>
|
||||
<hr>
|
||||
|
||||
<h2><u>1. Definitions</u></h2><br>
|
||||
|
||||
The terms "reproduce", "reproduction", "distribute" and "distribution" have the
|
||||
same meaning here as under U.S. copyright law.<br><br>
|
||||
|
||||
"The software" means this software package as a whole, including, but not
|
||||
limited to, this license, binaries, source code, documentation, and data.<br><br>
|
||||
|
||||
"You" means the licensee of the software.<br><br>
|
||||
|
||||
"The licensor" means the copyright holder of the software, byuu.
|
||||
<hr>
|
||||
|
||||
<h2><u>2. Grant of Rights</u></h2><br>
|
||||
|
||||
Subject to the terms of this license, the licensor grants you a
|
||||
non-transferable, non-exclusive, worldwide, royalty-free copyright license to
|
||||
reproduce the software for non-commercial use only, provided the software
|
||||
remains unmodified, and there is no charge for the software itself, nor for the
|
||||
medium upon which the software is distributed. The reproduction of modified or
|
||||
derivative works of the software is strictly prohibited without the express
|
||||
consent of the licensor.
|
||||
<hr>
|
||||
|
||||
<h2><u>3. Limitations</u></h2><br>
|
||||
|
||||
This license does not grant you any rights to use the licensor's name, logo or
|
||||
trademarks.<br>
|
||||
<br>
|
||||
|
||||
The software is provided "as is", and any express or implied warranties,
|
||||
including, but not limited to, the implied warranties of merchantability and
|
||||
fitness for a particular purpose are disclaimed. In no event shall the licensor
|
||||
be liable for any direct, indirect, incidental, special, exemplary, or
|
||||
consequential damages (including, but not limited to, procurement of sbustitute
|
||||
goods or services; loss of use, data, or profits; or business interruption)
|
||||
however caused and on any theory of liability, whether in contract, strict
|
||||
liability, or tort (including negligence or otherwise) arising in any way out of
|
||||
the use of the software, even if advised of the possibility of such damage.<br>
|
||||
<br>
|
||||
|
||||
In the event that this license is determined to be invalid or unenforceable, the
|
||||
Grant of Rights will become null and void, and no rights shall be granted to the
|
||||
licensee, within the scope of U.S. copyright law.
|
||||
<hr>
|
||||
|
||||
<h2><u>4. Exemptions</u></h2><br>
|
||||
|
||||
The software includes the work of other copyrights holders, which is licensed
|
||||
under different agreements, and exempt from this license. Below is a complete
|
||||
list of all such software, and their respective copyright holders and licenses.
|
||||
Note that explicit permission has been granted to the licensor to use included
|
||||
software which is ordinarily not compatible with this license, such as the GPL.
|
||||
<br>
|
||||
|
||||
<table border="1" cellpadding="3">
|
||||
<tr><td><b>Name</b></td><td><b>License</b></td><td><b>Author(s)</b></td></tr>
|
||||
|
||||
<tr><td>Cx4 emulator</td><td></td><td>anomie, Kris Bleakley, Nach, zsKnight</td></tr>
|
||||
<tr><td>DSP-1 emulator</td><td></td><td>Andreas Naive, John Weidman, Kris Bleakley, neviksti</td></tr>
|
||||
<tr><td>DSP-2 emulator</td><td></td><td>Kris Bleakley</td></tr>
|
||||
<tr><td>DSP-3 emulator</td><td></td><td>John Weidman, Kris Bleakley, Lancer, z80 gaiden</td></tr>
|
||||
<tr><td>DSP-4 emulator</td><td></td><td>Dreamer Nom, John Weidman, Kris Bleakley, Nach, z80 gaiden</td></tr>
|
||||
<tr><td>S-DD1 decompressor</td><td>Public Domain</td><td>Andreas Naive</td></tr>
|
||||
<tr><td>S-DSP emulator</td><td>LGPL 2.1</td><td>Shay Green</td></tr>
|
||||
<tr><td>SPC7110 decompressor</td><td>Public Domain</td><td>neviksti</td></tr>
|
||||
<tr><td>ST-0010 emulator</td><td></td><td>Feather, John Weidman, Kris Bleakley, Matthew Kendora</td></tr>
|
||||
|
||||
<tr><td>Qt toolkit</td><td>LGPL 2.1</td><td>Nokia</td></tr>
|
||||
<tr><td>HQ2x filter</td><td>LGPL 2.1</td><td>MaxST</td></tr>
|
||||
<tr><td>JMA decompressor</td><td>GPL 2</td><td>NSRT team</td></tr>
|
||||
<tr><td>NTSC filter</td><td>LGPL 2.1</td><td>Shay Green</td></tr>
|
||||
<tr><td>zlib decompressor</td><td>zlib license</td><td>zlib team</td></tr>
|
||||
</table>
|
||||
|
||||
</body>
|
||||
</html>
|
Binary file not shown.
After Width: | Height: | Size: 21 KiB |
|
@ -131,15 +131,15 @@ bool pHiro::file_open(Window *focus, char *filename, const char *path, const cha
|
|||
|
||||
if(filter && *filter) {
|
||||
lstring filterlist;
|
||||
split(filterlist, "\n", filter);
|
||||
for(unsigned i = 0; i < count(filterlist); i++) {
|
||||
filterlist.split("\n", filter);
|
||||
for(unsigned i = 0; i < filterlist.size(); i++) {
|
||||
GtkFileFilter *filter = gtk_file_filter_new();
|
||||
lstring filterpart;
|
||||
split(filterpart, "\t", filterlist[i]);
|
||||
filterpart.split("\t", filterlist[i]);
|
||||
gtk_file_filter_set_name(filter, string() << filterpart[0] << " (" << filterpart[1] << ")");
|
||||
lstring patternlist;
|
||||
split(patternlist, ",", filterpart[1]);
|
||||
for(unsigned l = 0; l < count(patternlist); l++) {
|
||||
patternlist.split(",", filterpart[1]);
|
||||
for(unsigned l = 0; l < patternlist.size(); l++) {
|
||||
gtk_file_filter_add_pattern(filter, patternlist[l]);
|
||||
}
|
||||
gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filter);
|
||||
|
@ -174,15 +174,15 @@ bool pHiro::file_save(Window *focus, char *filename, const char *path, const cha
|
|||
|
||||
if(filter && *filter) {
|
||||
lstring filterlist;
|
||||
split(filterlist, "\n", filter);
|
||||
for(unsigned i = 0; i < count(filterlist); i++) {
|
||||
filterlist.split("\n", filter);
|
||||
for(unsigned i = 0; i < filterlist.size(); i++) {
|
||||
GtkFileFilter *filter = gtk_file_filter_new();
|
||||
lstring filterpart;
|
||||
split(filterpart, "\t", filterlist[i]);
|
||||
filterpart.split("\t", filterlist[i]);
|
||||
gtk_file_filter_set_name(filter, string() << filterpart[0] << " (" << filterpart[1] << ")");
|
||||
lstring patternlist;
|
||||
split(patternlist, ",", filterpart[1]);
|
||||
for(unsigned l = 0; l < count(patternlist); l++) {
|
||||
patternlist.split(",", filterpart[1]);
|
||||
for(unsigned l = 0; l < patternlist.size(); l++) {
|
||||
gtk_file_filter_add_pattern(filter, patternlist[l]);
|
||||
}
|
||||
gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filter);
|
||||
|
|
|
@ -29,11 +29,11 @@ void pListbox::create(unsigned style, unsigned width, unsigned height, const cha
|
|||
gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrollbox), GTK_SHADOW_ETCHED_IN);
|
||||
|
||||
lstring list;
|
||||
split(list, "\t", columns);
|
||||
list.split("\t", columns);
|
||||
|
||||
GType *v = (GType*)malloc(count(list) * sizeof(GType));
|
||||
for(unsigned i = 0; i < count(list); i++) v[i] = G_TYPE_STRING;
|
||||
store = gtk_list_store_newv(count(list), v);
|
||||
GType *v = (GType*)malloc(list.size() * sizeof(GType));
|
||||
for(unsigned i = 0; i < list.size(); i++) v[i] = G_TYPE_STRING;
|
||||
store = gtk_list_store_newv(list.size(), v);
|
||||
free(v);
|
||||
|
||||
listbox = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
|
||||
|
@ -44,8 +44,8 @@ void pListbox::create(unsigned style, unsigned width, unsigned height, const cha
|
|||
gtk_widget_show(scrollbox);
|
||||
|
||||
//alternate colors for each listbox entry if there are multiple columns
|
||||
gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(listbox), count(list) >= 2 ? true : false);
|
||||
for(unsigned i = 0; i < count(list); i++) {
|
||||
gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(listbox), list.size() >= 2 ? true : false);
|
||||
for(unsigned i = 0; i < list.size(); i++) {
|
||||
unsigned i = column.size();
|
||||
column[i].renderer = gtk_cell_renderer_text_new();
|
||||
column[i].column = gtk_tree_view_column_new_with_attributes(
|
||||
|
@ -61,8 +61,8 @@ void pListbox::create(unsigned style, unsigned width, unsigned height, const cha
|
|||
}
|
||||
|
||||
if(text && *text) {
|
||||
split(list, "\n", text);
|
||||
for(unsigned i = 0; i < count(list); i++) add_item(list[i]);
|
||||
list.split("\n", text);
|
||||
for(unsigned i = 0; i < list.size(); i++) add_item(list[i]);
|
||||
}
|
||||
|
||||
gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(listbox), header);
|
||||
|
@ -85,9 +85,9 @@ void pListbox::set_column_width(unsigned index, unsigned width) {
|
|||
|
||||
void pListbox::add_item(const char *text) {
|
||||
lstring list;
|
||||
split(list, "\t", text);
|
||||
list.split("\t", text);
|
||||
gtk_list_store_append(store, &iter);
|
||||
for(unsigned i = 0; i < count(list); i++) {
|
||||
for(unsigned i = 0; i < list.size(); i++) {
|
||||
gtk_list_store_set(store, &iter, i, (const char*)list[i], -1);
|
||||
}
|
||||
}
|
||||
|
@ -101,8 +101,8 @@ void pListbox::set_item(unsigned index, const char *text) {
|
|||
}
|
||||
|
||||
lstring list;
|
||||
split(list, "\t", text);
|
||||
for(unsigned i = 0; i < count(list); i++) {
|
||||
list.split("\t", text);
|
||||
for(unsigned i = 0; i < list.size(); i++) {
|
||||
gtk_list_store_set(store, &iter, i, (const char*)list[i], -1);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,8 +28,8 @@ void pEditbox::resize(unsigned width, unsigned height) {
|
|||
|
||||
void pEditbox::set_text(const char *text) {
|
||||
string temp = text ? text : "";
|
||||
replace(temp, "\r", "");
|
||||
replace(temp, "\n", "\r\n");
|
||||
temp.replace("\r", "");
|
||||
temp.replace("\n", "\r\n");
|
||||
SetWindowText(hwnd, utf16(temp));
|
||||
update();
|
||||
}
|
||||
|
@ -39,7 +39,7 @@ unsigned pEditbox::get_text(char *text, unsigned length) {
|
|||
GetWindowText(hwnd, buffer, length);
|
||||
string temp = (const char*)utf8(buffer);
|
||||
delete[] buffer;
|
||||
replace(temp, "\r", "");
|
||||
temp.replace("\r", "");
|
||||
strlcpy(text, temp, length);
|
||||
return strlen(text);
|
||||
}
|
||||
|
|
|
@ -106,23 +106,22 @@ bool pHiro::folder_select(Window *focus, char *filename, const char *path) {
|
|||
|
||||
bool pHiro::file_open(Window *focus, char *filename, const char *path, const char *filter) {
|
||||
string dir, f;
|
||||
strcpy(dir, path ? path : "");
|
||||
replace(dir, "/", "\\");
|
||||
dir = path ? path : "";
|
||||
dir.replace("/", "\\");
|
||||
|
||||
lstring type, part;
|
||||
strcpy(f, "");
|
||||
split(type, "\n", filter);
|
||||
for(int i = 0; i < count(type); i++) {
|
||||
split(part, "\t", type[i]);
|
||||
if(count(part) != 2) continue;
|
||||
type.split("\n", filter);
|
||||
for(int i = 0; i < type.size(); i++) {
|
||||
part.split("\t", type[i]);
|
||||
if(part.size() != 2) continue;
|
||||
|
||||
strcat(f, part[0]);
|
||||
strcat(f, " (");
|
||||
strcat(f, part[1]);
|
||||
strcat(f, ")\t");
|
||||
replace(part[1], ",", ";");
|
||||
strcat(f, part[1]);
|
||||
strcat(f, "\t");
|
||||
f.append(part[0]);
|
||||
f.append(" (");
|
||||
f.append(part[1]);
|
||||
f.append(")\t");
|
||||
part[1].replace(",", ";");
|
||||
f.append(part[1]);
|
||||
f.append("\t");
|
||||
}
|
||||
|
||||
utf16 wfilter(f);
|
||||
|
@ -154,23 +153,22 @@ bool pHiro::file_open(Window *focus, char *filename, const char *path, const cha
|
|||
|
||||
bool pHiro::file_save(Window *focus, char *filename, const char *path, const char *filter) {
|
||||
string dir, f;
|
||||
strcpy(dir, path ? path : "");
|
||||
replace(dir, "/", "\\");
|
||||
dir = path ? path : "";
|
||||
dir.replace("/", "\\");
|
||||
|
||||
lstring type, part;
|
||||
strcpy(f, "");
|
||||
split(type, "\n", filter);
|
||||
for(int i = 0; i < count(type); i++) {
|
||||
split(part, "\t", type[i]);
|
||||
if(count(part) != 2) continue;
|
||||
type.split("\n", filter);
|
||||
for(int i = 0; i < type.size(); i++) {
|
||||
part.split("\t", type[i]);
|
||||
if(part.size() != 2) continue;
|
||||
|
||||
strcat(f, part[0]);
|
||||
strcat(f, " (");
|
||||
strcat(f, part[1]);
|
||||
strcat(f, ")\t");
|
||||
replace(part[1], ",", ";");
|
||||
strcat(f, part[1]);
|
||||
strcat(f, "\t");
|
||||
f.append(part[0]);
|
||||
f.append(" (");
|
||||
f.append(part[1]);
|
||||
f.append(")\t");
|
||||
part[1].replace(",", ";");
|
||||
f.append(part[1]);
|
||||
f.append("\t");
|
||||
}
|
||||
|
||||
utf16 wfilter(f);
|
||||
|
|
|
@ -27,8 +27,8 @@ using nall::min;
|
|||
using nall::max;
|
||||
|
||||
#include <nall/utf8.hpp>
|
||||
using nall::utf8;
|
||||
using nall::utf16;
|
||||
#define utf8 nall::utf8_t
|
||||
#define utf16 nall::utf16_t
|
||||
|
||||
extern int hiromain(int argc, const char *const argv[]);
|
||||
|
||||
|
|
|
@ -16,21 +16,21 @@ void pListbox::create(unsigned style, unsigned width, unsigned height, const cha
|
|||
ListView_SetExtendedListViewStyle(hwnd, LVS_EX_FULLROWSELECT);
|
||||
|
||||
lstring list;
|
||||
split(list, "\t", columns ? columns : "");
|
||||
column_count = count(list);
|
||||
for(unsigned i = 0; i < count(list); i++) {
|
||||
list.split("\t", columns ? columns : "");
|
||||
column_count = list.size();
|
||||
for(unsigned i = 0; i < list.size(); i++) {
|
||||
LVCOLUMN column;
|
||||
column.mask = LVCF_FMT | LVCF_TEXT | LVCF_SUBITEM;
|
||||
column.fmt = LVCFMT_LEFT;
|
||||
column.iSubItem = count(list);
|
||||
column.iSubItem = list.size();
|
||||
utf16 ulist(list[i]);
|
||||
column.pszText = ulist;
|
||||
ListView_InsertColumn(hwnd, i, &column);
|
||||
}
|
||||
|
||||
if(text && *text) {
|
||||
split(list, "\n", text);
|
||||
for(unsigned i = 0; i < count(list); i++) add_item(list[i]);
|
||||
list.split("\n", text);
|
||||
for(unsigned i = 0; i < list.size(); i++) add_item(list[i]);
|
||||
}
|
||||
autosize_columns();
|
||||
}
|
||||
|
@ -47,7 +47,7 @@ void pListbox::set_column_width(unsigned column, unsigned width) {
|
|||
|
||||
void pListbox::add_item(const char *text) {
|
||||
lstring list;
|
||||
split(list, "\t", text ? text : "");
|
||||
list.split("\t", text ? text : "");
|
||||
LVITEM item;
|
||||
unsigned pos = ListView_GetItemCount(hwnd);
|
||||
item.mask = LVIF_TEXT;
|
||||
|
@ -57,7 +57,7 @@ void pListbox::add_item(const char *text) {
|
|||
item.pszText = wtext;
|
||||
ListView_InsertItem(hwnd, &item);
|
||||
|
||||
for(unsigned i = 1; i < count(list); i++) {
|
||||
for(unsigned i = 1; i < list.size(); i++) {
|
||||
utf16 wtext(list[i]);
|
||||
ListView_SetItemText(hwnd, pos, i, wtext);
|
||||
}
|
||||
|
@ -65,8 +65,8 @@ void pListbox::add_item(const char *text) {
|
|||
|
||||
void pListbox::set_item(unsigned index, const char *text) {
|
||||
lstring list;
|
||||
split(list, "\t", text ? text : "");
|
||||
for(unsigned i = 0; i < count(list); i++) {
|
||||
list.split("\t", text ? text : "");
|
||||
for(unsigned i = 0; i < list.size(); i++) {
|
||||
utf16 wtext(list[i]);
|
||||
ListView_SetItemText(hwnd, index, i, wtext);
|
||||
}
|
||||
|
|
|
@ -48,6 +48,11 @@ namespace nall {
|
|||
operator[](buffersize) = data;
|
||||
}
|
||||
|
||||
signed find(const T data) {
|
||||
for(unsigned i = 0; i < size(); i++) if(pool[i] == data) return i;
|
||||
return -1; //not found
|
||||
}
|
||||
|
||||
void clear() {
|
||||
memset(pool, 0, buffersize * sizeof(T));
|
||||
}
|
||||
|
|
|
@ -16,24 +16,28 @@ namespace nall {
|
|||
template<typename T> struct is_unsigned { enum { value = false }; };
|
||||
template<> struct is_unsigned<unsigned> { enum { value = true }; };
|
||||
|
||||
template<typename T> struct is_double { enum { value = false }; };
|
||||
template<> struct is_double<double> { enum { value = true }; };
|
||||
|
||||
template<typename T> struct is_string { enum { value = false }; };
|
||||
template<> struct is_string<string> { enum { value = true }; };
|
||||
}
|
||||
|
||||
class configuration {
|
||||
public:
|
||||
enum type_t { boolean_t, signed_t, unsigned_t, string_t, unknown_t };
|
||||
enum type_t { boolean_t, signed_t, unsigned_t, double_t, string_t, unknown_t };
|
||||
struct item_t {
|
||||
uintptr_t data;
|
||||
string name;
|
||||
string desc;
|
||||
type_t type;
|
||||
|
||||
string get() {
|
||||
string get() const {
|
||||
switch(type) {
|
||||
case boolean_t: return (*(bool*)data == false ? "false" : "true");
|
||||
case signed_t: return string() << (int)*(signed*)data;
|
||||
case unsigned_t: return string() << (int)*(unsigned*)data;
|
||||
case boolean_t: return string() << *(bool*)data;
|
||||
case signed_t: return string() << *(signed*)data;
|
||||
case unsigned_t: return string() << *(unsigned*)data;
|
||||
case double_t: return string() << *(double*)data;
|
||||
case string_t: return string() << "\"" << *(string*)data << "\"";
|
||||
}
|
||||
return "???";
|
||||
|
@ -42,8 +46,9 @@ namespace nall {
|
|||
void set(string s) {
|
||||
switch(type) {
|
||||
case boolean_t: *(bool*)data = (s == "true"); break;
|
||||
case signed_t: *(signed*)data = s; break;
|
||||
case unsigned_t: *(unsigned*)data = s; break;
|
||||
case signed_t: *(signed*)data = strsigned(s); break;
|
||||
case unsigned_t: *(unsigned*)data = strunsigned(s); break;
|
||||
case double_t: *(double*)data = strdouble(s); break;
|
||||
case string_t: trim(s, "\""); *(string*)data = s; break;
|
||||
}
|
||||
}
|
||||
|
@ -60,24 +65,25 @@ namespace nall {
|
|||
if(configuration_traits::is_boolean<T>::value) list[n].type = boolean_t;
|
||||
else if(configuration_traits::is_signed<T>::value) list[n].type = signed_t;
|
||||
else if(configuration_traits::is_unsigned<T>::value) list[n].type = unsigned_t;
|
||||
else if(configuration_traits::is_double<T>::value) list[n].type = double_t;
|
||||
else if(configuration_traits::is_string<T>::value) list[n].type = string_t;
|
||||
else list[n].type = unknown_t;
|
||||
}
|
||||
|
||||
bool load(const char *filename) {
|
||||
virtual bool load(const char *filename) {
|
||||
string data;
|
||||
if(fread(data, filename) == true) {
|
||||
replace(data, "\r", "");
|
||||
if(data.readfile(filename) == true) {
|
||||
data.replace("\r", "");
|
||||
lstring line;
|
||||
split(line, "\n", data);
|
||||
line.split("\n", data);
|
||||
|
||||
for(unsigned i = 0; i < count(line); i++) {
|
||||
for(unsigned i = 0; i < line.size(); i++) {
|
||||
int position = qstrpos(line[i], "#");
|
||||
if(position >= 0) line[i][position] = 0;
|
||||
if(qstrpos(line[i], " = ") < 0) continue;
|
||||
|
||||
lstring part;
|
||||
qsplit(part, " = ", line[i]);
|
||||
part.qsplit(" = ", line[i]);
|
||||
trim(part[0]);
|
||||
trim(part[1]);
|
||||
|
||||
|
@ -95,11 +101,15 @@ namespace nall {
|
|||
}
|
||||
}
|
||||
|
||||
bool save(const char *filename) {
|
||||
virtual bool save(const char *filename) const {
|
||||
file fp;
|
||||
if(fp.open(filename, file::mode_write)) {
|
||||
for(unsigned i = 0; i < list.size(); i++) {
|
||||
fp.print(string() << list[i].name << " = " << list[i].get() << " # " << list[i].desc << "\r\n");
|
||||
string output;
|
||||
output << list[i].name << " = " << list[i].get();
|
||||
if(list[i].desc != "") output << " # " << list[i].desc;
|
||||
output << "\r\n";
|
||||
fp.print(output);
|
||||
}
|
||||
|
||||
fp.close();
|
||||
|
|
|
@ -1,176 +0,0 @@
|
|||
#ifndef NALL_CONFIG_HPP
|
||||
#define NALL_CONFIG_HPP
|
||||
|
||||
#include <nall/array.hpp>
|
||||
#include <nall/file.hpp>
|
||||
#include <nall/stdint.hpp>
|
||||
#include <nall/string.hpp>
|
||||
|
||||
namespace nall {
|
||||
class setting;
|
||||
|
||||
class configuration {
|
||||
public:
|
||||
array<setting*> list;
|
||||
|
||||
bool load(const char *fn);
|
||||
bool save(const char *fn) const;
|
||||
void add(setting *setting_) { list.add(setting_); }
|
||||
};
|
||||
|
||||
class setting {
|
||||
public:
|
||||
enum setting_type {
|
||||
integral_type,
|
||||
string_type,
|
||||
} type;
|
||||
|
||||
const char *name;
|
||||
const char *description;
|
||||
|
||||
virtual void set(const char *input) = 0;
|
||||
virtual void get(string &output) const = 0;
|
||||
virtual void get_default(string &output) const = 0;
|
||||
};
|
||||
|
||||
class integral_setting : public setting {
|
||||
public:
|
||||
enum integral_type {
|
||||
boolean,
|
||||
decimal,
|
||||
hex,
|
||||
} type;
|
||||
|
||||
intmax_t value;
|
||||
intmax_t default_value;
|
||||
|
||||
void set(const char *input) {
|
||||
if(type == boolean) { value = !strcmp(input, "true"); }
|
||||
if(type == decimal) { value = strdec(input); }
|
||||
if(type == hex) { value = strhex(input); }
|
||||
}
|
||||
|
||||
void get(string &output) const {
|
||||
if(type == boolean) { output = value ? "true" : "false"; }
|
||||
if(type == decimal) { output = strdec(value); }
|
||||
if(type == hex) { output = string() << "0x" << strhex(value); }
|
||||
}
|
||||
|
||||
void get_default(string &output) const {
|
||||
if(type == boolean) { output = default_value ? "true" : "false"; }
|
||||
if(type == decimal) { output = strdec(default_value); }
|
||||
if(type == hex) { output = string() << "0x" << strhex(default_value); }
|
||||
}
|
||||
|
||||
operator intmax_t() const { return value; }
|
||||
integral_setting& operator=(intmax_t value_) { value = value_; return *this; }
|
||||
|
||||
integral_setting(const char *name_, const char *description_, integral_type type_, intmax_t value_) {
|
||||
initialize(name_, description_, type_, value_);
|
||||
}
|
||||
|
||||
integral_setting(configuration &parent, const char *name_, const char *description_, integral_type type_, intmax_t value_) {
|
||||
initialize(name_, description_, type_, value_);
|
||||
parent.add(this);
|
||||
}
|
||||
|
||||
private:
|
||||
void initialize(const char *name_, const char *description_, integral_type type_, intmax_t value_) {
|
||||
setting::type = setting::integral_type;
|
||||
name = name_;
|
||||
description = description_;
|
||||
type = type_;
|
||||
value = default_value = value_;
|
||||
}
|
||||
};
|
||||
|
||||
class string_setting : public setting {
|
||||
public:
|
||||
string value;
|
||||
string default_value;
|
||||
|
||||
void set(const char *input) { value = input; trim(value(), "\""); }
|
||||
void get(string &output) const { output = string() << "\"" << value << "\""; }
|
||||
void get_default(string &output) const { output = string() << "\"" << default_value << "\""; }
|
||||
|
||||
operator const char*() const { return value; }
|
||||
string_setting& operator=(const char *value_) { value = value_; return *this; }
|
||||
bool operator==(const char *value_) const { return value == value_; }
|
||||
bool operator!=(const char *value_) const { return value != value_; }
|
||||
|
||||
string_setting(const char *name_, const char *description_, const char *value_) {
|
||||
initialize(name_, description_, value_);
|
||||
}
|
||||
|
||||
string_setting(configuration &parent, const char *name_, const char *description_, const char *value_) {
|
||||
initialize(name_, description_, value_);
|
||||
parent.add(this);
|
||||
}
|
||||
|
||||
private:
|
||||
void initialize(const char *name_, const char *description_, const char *value_) {
|
||||
setting::type = setting::string_type;
|
||||
name = name_;
|
||||
description = description_;
|
||||
value = default_value = value_;
|
||||
}
|
||||
};
|
||||
|
||||
inline bool configuration::load(const char *fn) {
|
||||
//load the config file into memory
|
||||
string data;
|
||||
if(!fread(data, fn)) return false;
|
||||
|
||||
//split the file into lines
|
||||
replace(data, "\r\n", "\n");
|
||||
qreplace(data, "\t", "");
|
||||
qreplace(data, " ", "");
|
||||
|
||||
lstring line, part, subpart;
|
||||
split(line, "\n", data);
|
||||
|
||||
for(unsigned i = 0; i < count(line); i++) {
|
||||
if(strlen(line[i]) == 0) continue;
|
||||
if(strbegin(line[i], "#")) continue;
|
||||
|
||||
qsplit(part, "=", line[i]);
|
||||
for(unsigned l = 0; l < list.size(); l++) {
|
||||
if(part[0] == list[l]->name) {
|
||||
list[l]->set(part[1]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool configuration::save(const char *fn) const {
|
||||
file fp;
|
||||
if(!fp.open(fn, file::mode_write)) return false;
|
||||
|
||||
for(unsigned i = 0; i < list.size(); i++) {
|
||||
string data;
|
||||
lstring line, part, subpart;
|
||||
strcpy(data, list[i]->description);
|
||||
replace(data, "\r\n", "\n");
|
||||
split(line, "\n", data);
|
||||
|
||||
string temp;
|
||||
for(unsigned l = 0; l < count(line); l++) {
|
||||
if(line[l] != "") fp.print(string() << "# " << line[l] << "\r\n");
|
||||
}
|
||||
|
||||
string default_, value_;
|
||||
list[i]->get_default(default_);
|
||||
fp.print(string() << "# (default = " << default_ << ")\r\n");
|
||||
list[i]->get(value_);
|
||||
fp.print(string() << list[i]->name << " = " << value_ << "\r\n\r\n");
|
||||
}
|
||||
|
||||
fp.close();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -27,17 +27,17 @@ namespace nall {
|
|||
|
||||
bool import(const char *filename) {
|
||||
string data;
|
||||
if(fread(data, filename) == false) return false;
|
||||
if(data.readfile(filename) == false) return false;
|
||||
ltrim_once(data, "\xef\xbb\xbf"); //remove UTF-8 marker, if it exists
|
||||
replace(data, "\r", "");
|
||||
data.replace("\r", "");
|
||||
|
||||
lstring line;
|
||||
split(line, "\n", data);
|
||||
for(unsigned i = 0; i < count(line); i++) {
|
||||
line.split("\n", data);
|
||||
for(unsigned i = 0; i < line.size(); i++) {
|
||||
lstring part;
|
||||
//format: "Input" = "Output"
|
||||
qsplit(part, "=", line[i]);
|
||||
if(count(part) != 2) continue;
|
||||
part.qsplit("=", line[i]);
|
||||
if(part.size() != 2) continue;
|
||||
|
||||
//remove whitespace
|
||||
trim(part[0]);
|
||||
|
|
|
@ -121,7 +121,7 @@ namespace nall {
|
|||
#if !defined(_WIN32)
|
||||
FILE *fp = fopen(fn, "rb");
|
||||
#else
|
||||
FILE *fp = _wfopen(utf16(fn), L"rb");
|
||||
FILE *fp = _wfopen(utf16_t(fn), L"rb");
|
||||
#endif
|
||||
if(fp) {
|
||||
fclose(fp);
|
||||
|
@ -144,10 +144,10 @@ namespace nall {
|
|||
case mode_readwrite: fp = fopen(fn, "rb+"); break;
|
||||
case mode_writeread: fp = fopen(fn, "wb+"); break;
|
||||
#else
|
||||
case mode_read: fp = _wfopen(utf16(fn), L"rb"); break;
|
||||
case mode_write: fp = _wfopen(utf16(fn), L"wb+"); break;
|
||||
case mode_readwrite: fp = _wfopen(utf16(fn), L"rb+"); break;
|
||||
case mode_writeread: fp = _wfopen(utf16(fn), L"wb+"); break;
|
||||
case mode_read: fp = _wfopen(utf16_t(fn), L"rb"); break;
|
||||
case mode_write: fp = _wfopen(utf16_t(fn), L"wb+"); break;
|
||||
case mode_readwrite: fp = _wfopen(utf16_t(fn), L"rb+"); break;
|
||||
case mode_writeread: fp = _wfopen(utf16_t(fn), L"wb+"); break;
|
||||
#endif
|
||||
}
|
||||
if(!fp) return false;
|
||||
|
|
|
@ -72,7 +72,7 @@ namespace nall {
|
|||
break;
|
||||
}
|
||||
|
||||
p_filehandle = CreateFileW(utf16(filename), desired_access, FILE_SHARE_READ, NULL,
|
||||
p_filehandle = CreateFileW(utf16_t(filename), desired_access, FILE_SHARE_READ, NULL,
|
||||
creation_disposition, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if(p_filehandle == INVALID_HANDLE_VALUE) return false;
|
||||
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
#ifndef NALL_PROPERTY_HPP
|
||||
#define NALL_PROPERTY_HPP
|
||||
|
||||
//nall::property implements a variable container that disallows write access
|
||||
//to non-derived objects. This requires use of property::set(), as C++ lacks
|
||||
//the ability to make this implementation completely transparent.
|
||||
|
||||
namespace nall {
|
||||
class property {
|
||||
public:
|
||||
template<typename T> class property_t;
|
||||
|
||||
protected:
|
||||
template<typename T> T& get(property_t<T>&);
|
||||
template<typename T> property_t<T>& set(property_t<T>&, const T);
|
||||
|
||||
public:
|
||||
template<typename T>
|
||||
class property_t {
|
||||
public:
|
||||
const T& operator()() const { return value; }
|
||||
property_t() : value() {}
|
||||
property_t(const T value_) : value(value_) {}
|
||||
|
||||
protected:
|
||||
T value;
|
||||
operator T&() { return value; }
|
||||
property_t& operator=(const T newValue) { value = newValue; return *this; }
|
||||
friend T& property::get<T>(property_t<T>&);
|
||||
friend property_t<T>& property::set<T>(property_t<T>&, const T);
|
||||
};
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
T& property::get(property::property_t<T> &p) {
|
||||
return p.operator T&();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
property::property_t<T>& property::set(property::property_t<T> &p, const T value) {
|
||||
return p.operator=(value);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -7,14 +7,18 @@
|
|||
#include <nall/utf8.hpp>
|
||||
|
||||
#include <nall/string.hpp>
|
||||
#include <nall/string/class.cpp>
|
||||
#include <nall/string/compare.cpp>
|
||||
#include <nall/string/convert.cpp>
|
||||
#include <nall/string/match.cpp>
|
||||
#include <nall/string/math.cpp>
|
||||
#include <nall/string/replace.cpp>
|
||||
#include <nall/string/split.cpp>
|
||||
#include <nall/string/strl.cpp>
|
||||
#include <nall/string/trim.cpp>
|
||||
#include <nall/string/utility.cpp>
|
||||
|
||||
namespace nall {
|
||||
#include <nall/string/core.cpp>
|
||||
#include <nall/string/replace.cpp>
|
||||
#include <nall/string/split.cpp>
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -7,113 +7,169 @@
|
|||
#include <nall/stdint.hpp>
|
||||
#include <nall/vector.hpp>
|
||||
|
||||
//compare
|
||||
//===============
|
||||
//libc extensions
|
||||
//===============
|
||||
|
||||
//compare.cpp
|
||||
char chrlower(char c);
|
||||
char chrupper(char c);
|
||||
|
||||
int stricmp(const char *dest, const char *src);
|
||||
int strpos(const char *str, const char *key);
|
||||
|
||||
int strpos (const char *str, const char *key);
|
||||
int qstrpos(const char *str, const char *key);
|
||||
bool strbegin(const char *str, const char *key);
|
||||
|
||||
bool strbegin (const char *str, const char *key);
|
||||
bool stribegin(const char *str, const char *key);
|
||||
bool strend(const char *str, const char *key);
|
||||
|
||||
bool strend (const char *str, const char *key);
|
||||
bool striend(const char *str, const char *key);
|
||||
|
||||
//convert
|
||||
//convert.cpp
|
||||
char* strlower(char *str);
|
||||
char* strupper(char *str);
|
||||
char* strtr(char *dest, const char *before, const char *after);
|
||||
uintmax_t strhex(const char *str);
|
||||
intmax_t strdec(const char *str);
|
||||
uintmax_t strbin(const char *str);
|
||||
double strdouble(const char *str);
|
||||
size_t strhex(char *str, uintmax_t value, size_t length = 0);
|
||||
size_t strdec(char *str, intmax_t value, size_t length = 0);
|
||||
size_t strbin(char *str, uintmax_t value, size_t length = 0);
|
||||
size_t strdouble(char *str, double value, size_t length = 0);
|
||||
|
||||
//match
|
||||
char* strtr(char *dest, const char *before, const char *after);
|
||||
|
||||
uintmax_t strhex (const char *str);
|
||||
intmax_t strsigned (const char *str);
|
||||
uintmax_t strunsigned(const char *str);
|
||||
uintmax_t strbin (const char *str);
|
||||
double strdouble (const char *str);
|
||||
|
||||
size_t strhex (char *str, uintmax_t value, size_t length = 0);
|
||||
size_t strsigned (char *str, intmax_t value, size_t length = 0);
|
||||
size_t strunsigned(char *str, uintmax_t value, size_t length = 0);
|
||||
size_t strbin (char *str, uintmax_t value, size_t length = 0);
|
||||
size_t strdouble (char *str, double value, size_t length = 0);
|
||||
|
||||
//match.cpp
|
||||
bool match(const char *pattern, const char *str);
|
||||
|
||||
//math
|
||||
//math.cpp
|
||||
bool strint (const char *str, int &result);
|
||||
bool strmath(const char *str, int &result);
|
||||
|
||||
//strl
|
||||
//strl.cpp
|
||||
size_t strlcpy(char *dest, const char *src, size_t length);
|
||||
size_t strlcat(char *dest, const char *src, size_t length);
|
||||
|
||||
//trim
|
||||
//trim.cpp
|
||||
char* ltrim(char *str, const char *key = " ");
|
||||
char* rtrim(char *str, const char *key = " ");
|
||||
char* trim (char *str, const char *key = " ");
|
||||
|
||||
char* ltrim_once(char *str, const char *key = " ");
|
||||
char* rtrim_once(char *str, const char *key = " ");
|
||||
char* trim_once (char *str, const char *key = " ");
|
||||
|
||||
//================
|
||||
//string + lstring
|
||||
//================
|
||||
|
||||
namespace nall {
|
||||
class string;
|
||||
template<typename T> inline string to_string(T);
|
||||
|
||||
class string {
|
||||
public:
|
||||
char *data;
|
||||
size_t size;
|
||||
void reserve(size_t);
|
||||
unsigned length() const;
|
||||
|
||||
void reserve(size_t size_);
|
||||
string& assign(const char*);
|
||||
string& append(const char*);
|
||||
template<typename T> string& operator= (T value) { return assign(to_string<T>(value)); }
|
||||
template<typename T> string& operator<<(T value) { return append(to_string<T>(value)); }
|
||||
|
||||
operator int() const;
|
||||
operator const char*() const;
|
||||
char* operator()();
|
||||
char& operator[](int);
|
||||
|
||||
string& operator=(int num);
|
||||
string& operator=(double num);
|
||||
string& operator=(const char *str);
|
||||
string& operator=(const string &str);
|
||||
string& operator<<(int num);
|
||||
string& operator<<(double num);
|
||||
string& operator<<(const char *str);
|
||||
string& operator<<(const string& str);
|
||||
|
||||
bool operator==(const char *str) const;
|
||||
bool operator!=(const char *str) const;
|
||||
bool operator< (const char *str) const;
|
||||
bool operator<=(const char *str) const;
|
||||
bool operator> (const char *str) const;
|
||||
bool operator>=(const char *str) const;
|
||||
bool operator==(const char*) const;
|
||||
bool operator!=(const char*) const;
|
||||
bool operator< (const char*) const;
|
||||
bool operator<=(const char*) const;
|
||||
bool operator> (const char*) const;
|
||||
bool operator>=(const char*) const;
|
||||
|
||||
string();
|
||||
string(int num);
|
||||
string(double num);
|
||||
string(const char *source);
|
||||
string(const string &source);
|
||||
string(const char*);
|
||||
string(const string&);
|
||||
string& operator=(const string&);
|
||||
~string();
|
||||
|
||||
//core.cpp
|
||||
bool readfile(const char*);
|
||||
|
||||
//replace.cpp
|
||||
string& replace (const char*, const char*);
|
||||
string& qreplace(const char*, const char*);
|
||||
|
||||
protected:
|
||||
char *data;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
typedef vector<string> lstring;
|
||||
class lstring : public vector<string> {
|
||||
public:
|
||||
template<typename T> lstring& operator<<(T value) {
|
||||
operator[](size()).assign(to_string<T>(value));
|
||||
return *this;
|
||||
}
|
||||
|
||||
//core.cpp
|
||||
int find(const char*);
|
||||
|
||||
//split.cpp
|
||||
void split (const char*, const char*, unsigned = 0);
|
||||
void qsplit(const char*, const char*, unsigned = 0);
|
||||
};
|
||||
}
|
||||
|
||||
size_t count(nall::lstring&);
|
||||
int find(nall::lstring &str, const char *key);
|
||||
//=====================
|
||||
//string<>libc wrappers
|
||||
//=====================
|
||||
|
||||
size_t strlcpy(nall::string &dest, const char *src, size_t length);
|
||||
size_t strlcat(nall::string &dest, const char *src, size_t length);
|
||||
void strcpy(nall::string &dest, const char *src);
|
||||
void strcat(nall::string &dest, const char *src);
|
||||
nall::string substr(const char *src, size_t start = 0, size_t length = 0);
|
||||
|
||||
nall::string& strlower(nall::string &str);
|
||||
nall::string& strupper(nall::string &str);
|
||||
|
||||
nall::string& strtr(nall::string &dest, const char *before, const char *after);
|
||||
nall::string strhex(uintmax_t value);
|
||||
nall::string strdec(intmax_t value);
|
||||
nall::string strbin(uintmax_t value);
|
||||
nall::string strdouble(double value);
|
||||
bool fread(nall::string &str, const char *filename);
|
||||
nall::string& replace (nall::string &str, const char *key, const char *token);
|
||||
nall::string& qreplace(nall::string &str, const char *key, const char *token);
|
||||
void split (nall::lstring &dest, const char *key, const char *src, size_t limit = 0);
|
||||
void qsplit(nall::lstring &dest, const char *key, const char *src, size_t limit = 0);
|
||||
|
||||
nall::string& ltrim(nall::string &str, const char *key = " ");
|
||||
nall::string& rtrim(nall::string &str, const char *key = " ");
|
||||
nall::string& trim (nall::string &str, const char *key = " ");
|
||||
|
||||
nall::string& ltrim_once(nall::string &str, const char *key = " ");
|
||||
nall::string& rtrim_once(nall::string &str, const char *key = " ");
|
||||
nall::string& trim_once (nall::string &str, const char *key = " ");
|
||||
|
||||
//==============
|
||||
//misc functions
|
||||
//==============
|
||||
|
||||
nall::string substr(const char *src, size_t start = 0, size_t length = 0);
|
||||
|
||||
nall::string strhex (uintmax_t value);
|
||||
nall::string strsigned (intmax_t value);
|
||||
nall::string strunsigned(uintmax_t value);
|
||||
nall::string strbin (uintmax_t value);
|
||||
nall::string strdouble (double value);
|
||||
|
||||
namespace nall {
|
||||
//this is needed, as C++98 does not support explicit template specialization inside classes;
|
||||
//redundant memory allocation should hopefully be avoided via compiler optimizations.
|
||||
template<> inline string to_string<bool> (bool v) { return v ? "true" : "false"; }
|
||||
template<> inline string to_string<signed int> (signed int v) { return strsigned(v); }
|
||||
template<> inline string to_string<unsigned int> (unsigned int v) { return strunsigned(v); }
|
||||
template<> inline string to_string<double> (double v) { return strdouble(v); }
|
||||
template<> inline string to_string<char*> (char *v) { return v; }
|
||||
template<> inline string to_string<const char*> (const char *v) { return v; }
|
||||
template<> inline string to_string<string> (string v) { return v; }
|
||||
template<> inline string to_string<const string&>(const string &v) { return v; }
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,232 +0,0 @@
|
|||
#ifdef NALL_STRING_CPP
|
||||
|
||||
size_t count(nall::lstring &str) {
|
||||
return str.size();
|
||||
}
|
||||
|
||||
int find(nall::lstring &str, const char *key) {
|
||||
for(size_t i = 0; i < count(str); i++) {
|
||||
if(str[i] == key) return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
namespace nall {
|
||||
|
||||
void string::reserve(size_t size_) {
|
||||
if(size_ > size) {
|
||||
size = size_;
|
||||
data = (char*)realloc(data, size + 1);
|
||||
data[size] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
//implicit-conversion, read-only
|
||||
string::operator int() const {
|
||||
return strdec(data);
|
||||
}
|
||||
|
||||
//implicit-conversion, read-only
|
||||
string::operator const char*() const {
|
||||
return data;
|
||||
}
|
||||
|
||||
//explicit-coversion, read-write
|
||||
char* string::operator()() {
|
||||
return data;
|
||||
}
|
||||
|
||||
//index, read-write
|
||||
char& string::operator[](int index) {
|
||||
reserve(index);
|
||||
return data[index];
|
||||
}
|
||||
|
||||
string& string::operator=(int num) {
|
||||
operator=(strdec(num));
|
||||
return *this;
|
||||
}
|
||||
|
||||
string& string::operator=(double num) {
|
||||
operator=(strdouble(num));
|
||||
return *this;
|
||||
}
|
||||
|
||||
string& string::operator=(const char *str) {
|
||||
strcpy(*this, str);
|
||||
return *this;
|
||||
}
|
||||
|
||||
string& string::operator=(const string &str) {
|
||||
strcpy(*this, str);
|
||||
return *this;
|
||||
}
|
||||
|
||||
string& string::operator<<(int num) {
|
||||
string temp(num);
|
||||
strcat(*this, temp);
|
||||
return *this;
|
||||
}
|
||||
|
||||
string& string::operator<<(double num) {
|
||||
string temp(num);
|
||||
strcat(*this, temp);
|
||||
return *this;
|
||||
}
|
||||
|
||||
string& string::operator<<(const char *str) {
|
||||
strcat(*this, str);
|
||||
return *this;
|
||||
}
|
||||
|
||||
string& string::operator<<(const string &str) {
|
||||
strcat(*this, str);
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool string::operator==(const char *str) const { return strcmp(data, str) == 0; }
|
||||
bool string::operator!=(const char *str) const { return strcmp(data, str) != 0; }
|
||||
bool string::operator< (const char *str) const { return strcmp(data, str) < 0; }
|
||||
bool string::operator<=(const char *str) const { return strcmp(data, str) <= 0; }
|
||||
bool string::operator> (const char *str) const { return strcmp(data, str) > 0; }
|
||||
bool string::operator>=(const char *str) const { return strcmp(data, str) >= 0; }
|
||||
|
||||
string::string() {
|
||||
size = 64;
|
||||
data = (char*)malloc(size + 1);
|
||||
*data = 0;
|
||||
}
|
||||
|
||||
string::string(int source) {
|
||||
size = 64;
|
||||
data = (char*)malloc(size + 1);
|
||||
*data = 0;
|
||||
operator=(strdec(source));
|
||||
}
|
||||
|
||||
string::string(double source) {
|
||||
size = 64;
|
||||
data = (char*)malloc(size + 1);
|
||||
*data = 0;
|
||||
operator=(strdouble(source));
|
||||
}
|
||||
|
||||
string::string(const char *source) {
|
||||
size = strlen(source);
|
||||
data = (char*)malloc(size + 1);
|
||||
strcpy(data, source);
|
||||
}
|
||||
|
||||
string::string(const string &source) {
|
||||
size = strlen(source);
|
||||
data = (char*)malloc(size + 1);
|
||||
strcpy(data, source);
|
||||
}
|
||||
|
||||
string::~string() {
|
||||
if(data) free(data);
|
||||
}
|
||||
|
||||
} //namespace nall
|
||||
|
||||
void strcpy(nall::string &dest, const char *src) {
|
||||
int srclen = strlen(src);
|
||||
dest.reserve(srclen);
|
||||
strcpy(dest(), src);
|
||||
}
|
||||
|
||||
void strcat(nall::string &dest, const char *src) {
|
||||
int srclen = strlen(src);
|
||||
int destlen = strlen(dest);
|
||||
dest.reserve(srclen + destlen);
|
||||
strcat(dest(), src);
|
||||
}
|
||||
|
||||
size_t strlcpy(nall::string &dest, const char *src, size_t length) {
|
||||
dest.reserve(length);
|
||||
return strlcpy(dest(), src, length);
|
||||
}
|
||||
|
||||
size_t strlcat(nall::string &dest, const char *src, size_t length) {
|
||||
dest.reserve(length);
|
||||
return strlcat(dest(), src, length);
|
||||
}
|
||||
|
||||
nall::string substr(const char *src, size_t start, size_t length) {
|
||||
nall::string dest;
|
||||
if(length == 0) { //copy entire string
|
||||
strcpy(dest, src + start);
|
||||
} else { //copy partial string
|
||||
strlcpy(dest, src + start, length + 1);
|
||||
}
|
||||
return dest;
|
||||
}
|
||||
|
||||
/* very simplistic wrappers to return nall::string& instead of char* type */
|
||||
|
||||
nall::string& strlower(nall::string &str) { strlower(str()); return str; }
|
||||
nall::string& strupper(nall::string &str) { strupper(str()); return str; }
|
||||
nall::string& strtr(nall::string &dest, const char *before, const char *after) { strtr(dest(), before, after); return dest; }
|
||||
nall::string& ltrim(nall::string &str, const char *key) { ltrim(str(), key); return str; }
|
||||
nall::string& rtrim(nall::string &str, const char *key) { rtrim(str(), key); return str; }
|
||||
nall::string& trim (nall::string &str, const char *key) { trim (str(), key); return str; }
|
||||
nall::string& ltrim_once(nall::string &str, const char *key) { ltrim_once(str(), key); return str; }
|
||||
nall::string& rtrim_once(nall::string &str, const char *key) { rtrim_once(str(), key); return str; }
|
||||
nall::string& trim_once (nall::string &str, const char *key) { trim_once (str(), key); return str; }
|
||||
|
||||
/* arithmetic <> string */
|
||||
|
||||
nall::string strhex(uintmax_t value) {
|
||||
nall::string temp;
|
||||
temp.reserve(strhex(0, value));
|
||||
strhex(temp(), value);
|
||||
return temp;
|
||||
}
|
||||
|
||||
nall::string strdec(intmax_t value) {
|
||||
nall::string temp;
|
||||
temp.reserve(strdec(0, value));
|
||||
strdec(temp(), value);
|
||||
return temp;
|
||||
}
|
||||
|
||||
nall::string strbin(uintmax_t value) {
|
||||
nall::string temp;
|
||||
temp.reserve(strbin(0, value));
|
||||
strbin(temp(), value);
|
||||
return temp;
|
||||
}
|
||||
|
||||
nall::string strdouble(double value) {
|
||||
nall::string temp;
|
||||
temp.reserve(strdouble(0, value));
|
||||
strdouble(temp(), value);
|
||||
return temp;
|
||||
}
|
||||
|
||||
/* ... */
|
||||
|
||||
bool fread(nall::string &str, const char *filename) {
|
||||
strcpy(str, "");
|
||||
|
||||
#if !defined(_WIN32)
|
||||
FILE *fp = fopen(filename, "rb");
|
||||
#else
|
||||
FILE *fp = _wfopen(nall::utf16(filename), L"rb");
|
||||
#endif
|
||||
if(!fp) return false;
|
||||
|
||||
fseek(fp, 0, SEEK_END);
|
||||
size_t size = ftell(fp);
|
||||
rewind(fp);
|
||||
char *fdata = (char*)malloc(size + 1);
|
||||
unsigned unused = fread(fdata, 1, size, fp);
|
||||
fclose(fp);
|
||||
fdata[size] = 0;
|
||||
strcpy(str, fdata);
|
||||
free(fdata);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
|
@ -37,18 +37,6 @@ char* strtr(char *dest, const char *before, const char *after) {
|
|||
return dest;
|
||||
}
|
||||
|
||||
//note: ISO C++ only guarantees that 0-9 is contigious,
|
||||
//it does not guarantee that either A-F or a-f are also contigious
|
||||
//however, A-F and a-f are contigious on virtually every platform in existence
|
||||
//the optimizations and simplifications made possible by this are therefore unignorable
|
||||
//however, just to be safe, this is tested at compile-time with the below assertion ...
|
||||
//if false, a compiler error will be thrown
|
||||
|
||||
static nall::static_assert<
|
||||
('A' == 'B' - 1) && ('B' == 'C' - 1) && ('C' == 'D' - 1) && ('D' == 'E' - 1) && ('E' == 'F' - 1) &&
|
||||
('a' == 'b' - 1) && ('b' == 'c' - 1) && ('c' == 'd' - 1) && ('d' == 'e' - 1) && ('e' == 'f' - 1)
|
||||
> hex_contigious_assertion_;
|
||||
|
||||
uintmax_t strhex(const char *str) {
|
||||
if(!str) return 0;
|
||||
uintmax_t result = 0;
|
||||
|
@ -69,7 +57,7 @@ uintmax_t strhex(const char *str) {
|
|||
return result;
|
||||
}
|
||||
|
||||
intmax_t strdec(const char *str) {
|
||||
intmax_t strsigned(const char *str) {
|
||||
if(!str) return 0;
|
||||
intmax_t result = 0;
|
||||
bool negate = false;
|
||||
|
@ -90,6 +78,20 @@ intmax_t strdec(const char *str) {
|
|||
return !negate ? result : -result;
|
||||
}
|
||||
|
||||
uintmax_t strunsigned(const char *str) {
|
||||
if(!str) return 0;
|
||||
uintmax_t result = 0;
|
||||
|
||||
while(*str) {
|
||||
uint8_t x = *str++;
|
||||
if(x >= '0' && x <= '9') x -= '0';
|
||||
else break; //stop at first invalid character
|
||||
result = result * 10 + x;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
uintmax_t strbin(const char *str) {
|
||||
if(!str) return 0;
|
||||
uintmax_t result = 0;
|
||||
|
@ -143,6 +145,8 @@ double strdouble(const char *str) {
|
|||
return !negate ? result : -result;
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
size_t strhex(char *str, uintmax_t value, size_t length /* = 0 */) {
|
||||
if(length == 0) length -= 1U; //"infinite" length
|
||||
size_t initial_length = length;
|
||||
|
@ -168,7 +172,7 @@ size_t strhex(char *str, uintmax_t value, size_t length /* = 0 */) {
|
|||
return nall::min(initial_length, digits + 1);
|
||||
}
|
||||
|
||||
size_t strdec(char *str, intmax_t value_, size_t length /* = 0 */) {
|
||||
size_t strsigned(char *str, intmax_t value_, size_t length /* = 0 */) {
|
||||
if(length == 0) length = -1U; //"infinite" length
|
||||
size_t initial_length = length;
|
||||
|
||||
|
@ -200,6 +204,31 @@ size_t strdec(char *str, intmax_t value_, size_t length /* = 0 */) {
|
|||
return nall::min(initial_length, digits + 1);
|
||||
}
|
||||
|
||||
size_t strunsigned(char *str, uintmax_t value, size_t length /* = 0 */) {
|
||||
if(length == 0) length = -1U; //"infinite" length
|
||||
size_t initial_length = length;
|
||||
|
||||
//count number of digits in value
|
||||
int digits_integral = 1;
|
||||
uintmax_t digits_integral_ = value;
|
||||
while(digits_integral_ /= 10) digits_integral++;
|
||||
|
||||
int digits = digits_integral;
|
||||
if(!str) return digits_integral + 1; //only computing required length?
|
||||
|
||||
length = nall::min(digits, length - 1);
|
||||
str += length; //seek to end of target string
|
||||
*str = 0; //set null terminator
|
||||
|
||||
while(length--) {
|
||||
uint8_t x = '0' + (value % 10);
|
||||
value /= 10;
|
||||
*--str = x; //iterate backwards to write string
|
||||
}
|
||||
|
||||
return nall::min(initial_length, digits + 1);
|
||||
}
|
||||
|
||||
size_t strbin(char *str, uintmax_t value, size_t length /* = 0 */) {
|
||||
if(length == 0) length = -1U; //"infinite" length
|
||||
size_t initial_length = length;
|
||||
|
|
|
@ -0,0 +1,103 @@
|
|||
#ifdef NALL_STRING_CPP
|
||||
|
||||
void string::reserve(size_t size_) {
|
||||
if(size_ > size) {
|
||||
size = size_;
|
||||
data = (char*)realloc(data, size + 1);
|
||||
data[size] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned string::length() const {
|
||||
return strlen(data);
|
||||
}
|
||||
|
||||
string& string::assign(const char *s) {
|
||||
unsigned length = strlen(s);
|
||||
reserve(length);
|
||||
strcpy(data, s);
|
||||
return *this;
|
||||
}
|
||||
|
||||
string& string::append(const char *s) {
|
||||
unsigned length = strlen(data) + strlen(s);
|
||||
reserve(length);
|
||||
strcat(data, s);
|
||||
return *this;
|
||||
}
|
||||
|
||||
string::operator const char*() const {
|
||||
return data;
|
||||
}
|
||||
|
||||
char* string::operator()() {
|
||||
return data;
|
||||
}
|
||||
|
||||
char& string::operator[](int index) {
|
||||
reserve(index);
|
||||
return data[index];
|
||||
}
|
||||
|
||||
bool string::operator==(const char *str) const { return strcmp(data, str) == 0; }
|
||||
bool string::operator!=(const char *str) const { return strcmp(data, str) != 0; }
|
||||
bool string::operator< (const char *str) const { return strcmp(data, str) < 0; }
|
||||
bool string::operator<=(const char *str) const { return strcmp(data, str) <= 0; }
|
||||
bool string::operator> (const char *str) const { return strcmp(data, str) > 0; }
|
||||
bool string::operator>=(const char *str) const { return strcmp(data, str) >= 0; }
|
||||
|
||||
string::string() {
|
||||
size = 64;
|
||||
data = (char*)malloc(size + 1);
|
||||
*data = 0;
|
||||
}
|
||||
|
||||
string::string(const char *value) {
|
||||
size = strlen(value);
|
||||
data = strdup(value);
|
||||
}
|
||||
|
||||
string::string(const string &value) {
|
||||
size = strlen(value);
|
||||
data = strdup(value);
|
||||
}
|
||||
|
||||
string& string::operator=(const string &value) {
|
||||
assign(value);
|
||||
}
|
||||
|
||||
string::~string() {
|
||||
free(data);
|
||||
}
|
||||
|
||||
bool string::readfile(const char *filename) {
|
||||
assign("");
|
||||
|
||||
#if !defined(_WIN32)
|
||||
FILE *fp = fopen(filename, "rb");
|
||||
#else
|
||||
FILE *fp = _wfopen(nall::utf16_t(filename), L"rb");
|
||||
#endif
|
||||
if(!fp) return false;
|
||||
|
||||
fseek(fp, 0, SEEK_END);
|
||||
size_t size = ftell(fp);
|
||||
rewind(fp);
|
||||
char *fdata = new char[size + 1];
|
||||
unsigned unused = fread(fdata, 1, size, fp);
|
||||
fclose(fp);
|
||||
fdata[size] = 0;
|
||||
assign(fdata);
|
||||
delete[] fdata;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int lstring::find(const char *key) {
|
||||
for(unsigned i = 0; i < size(); i++) {
|
||||
if(operator[](i) == key) return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,90 +1,98 @@
|
|||
#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);
|
||||
string& string::replace(const char *key, const char *token) {
|
||||
int i, z, ksl = strlen(key), tsl = strlen(token), ssl = length();
|
||||
unsigned int replace_count = 0, size = ssl;
|
||||
char *data;
|
||||
char *buffer;
|
||||
|
||||
if(ksl > ssl)return str;
|
||||
if(tsl > ksl) { //the new string may be longer than the old string...
|
||||
for(i = 0; i <= ssl - ksl;) { //so let's find out how big of a string we'll need...
|
||||
if(!memcmp(str() + i, key, ksl)) {
|
||||
replace_count++;
|
||||
i += ksl;
|
||||
} else i++;
|
||||
if(ksl <= ssl) {
|
||||
if(tsl > ksl) { //the new string may be longer than the old string...
|
||||
for(i = 0; i <= ssl - ksl;) { //so let's find out how big of a string we'll need...
|
||||
if(!memcmp(data + i, key, ksl)) {
|
||||
replace_count++;
|
||||
i += ksl;
|
||||
} else i++;
|
||||
}
|
||||
size = ssl + ((tsl - ksl) * replace_count);
|
||||
reserve(size);
|
||||
}
|
||||
size = ssl + ((tsl - ksl) * replace_count);
|
||||
str.reserve(size);
|
||||
|
||||
buffer = new char[size + 1];
|
||||
for(i = z = 0; i < ssl;) {
|
||||
if(i <= ssl - ksl) {
|
||||
if(!memcmp(data + i, key, ksl)) {
|
||||
memcpy(buffer + z, token, tsl);
|
||||
z += tsl;
|
||||
i += ksl;
|
||||
} else buffer[z++] = data[i++];
|
||||
} else buffer[z++] = data[i++];
|
||||
}
|
||||
buffer[z] = 0;
|
||||
|
||||
assign(buffer);
|
||||
delete[] buffer;
|
||||
}
|
||||
data = (char*)malloc(size + 1);
|
||||
for(i = z = 0; i < ssl;) {
|
||||
if(i <= ssl - ksl) {
|
||||
if(!memcmp(str() + i, key, ksl)) {
|
||||
memcpy(data + z, token, tsl);
|
||||
z += tsl;
|
||||
i += ksl;
|
||||
} else data[z++] = str()[i++];
|
||||
} else data[z++] = str()[i++];
|
||||
}
|
||||
data[z] = 0;
|
||||
strcpy(str, data);
|
||||
free(data);
|
||||
return str;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
nall::string &qreplace(nall::string &str, const char *key, const char *token) {
|
||||
int i, l, z, ksl = strlen(key), tsl = strlen(token), ssl = strlen(str);
|
||||
string& string::qreplace(const char *key, const char *token) {
|
||||
int i, l, z, ksl = strlen(key), tsl = strlen(token), ssl = length();
|
||||
unsigned int replace_count = 0, size = ssl;
|
||||
uint8_t x;
|
||||
char *data;
|
||||
char *buffer;
|
||||
|
||||
if(ksl > ssl)return str;
|
||||
if(tsl > ksl) {
|
||||
for(i = 0; i <= ssl - ksl;) {
|
||||
x = str()[i];
|
||||
if(x == '\"' || x == '\'') {
|
||||
l = i;
|
||||
i++;
|
||||
while(str()[i++] != x) {
|
||||
if(i == ssl) {
|
||||
i = l;
|
||||
break;
|
||||
if(ksl <= ssl) {
|
||||
if(tsl > ksl) {
|
||||
for(i = 0; i <= ssl - ksl;) {
|
||||
x = data[i];
|
||||
if(x == '\"' || x == '\'') {
|
||||
l = i;
|
||||
i++;
|
||||
while(data[i++] != x) {
|
||||
if(i == ssl) {
|
||||
i = l;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!memcmp(data + i, key, ksl)) {
|
||||
replace_count++;
|
||||
i += ksl;
|
||||
} else i++;
|
||||
}
|
||||
if(!memcmp(str() + i, key, ksl)) {
|
||||
replace_count++;
|
||||
i += ksl;
|
||||
} else i++;
|
||||
size = ssl + ((tsl - ksl) * replace_count);
|
||||
reserve(size);
|
||||
}
|
||||
size = ssl + ((tsl - ksl) * replace_count);
|
||||
str.reserve(size);
|
||||
}
|
||||
data = (char*)malloc(size + 1);
|
||||
for(i = z = 0; i < ssl;) {
|
||||
x = str()[i];
|
||||
if(x == '\"' || x == '\'') {
|
||||
l = i++;
|
||||
while(str()[i] != x && i < ssl)i++;
|
||||
if(i >= ssl)i = l;
|
||||
else {
|
||||
memcpy(data + z, str() + l, i - l);
|
||||
z += i - l;
|
||||
|
||||
buffer = new char[size + 1];
|
||||
for(i = z = 0; i < ssl;) {
|
||||
x = data[i];
|
||||
if(x == '\"' || x == '\'') {
|
||||
l = i++;
|
||||
while(data[i] != x && i < ssl)i++;
|
||||
if(i >= ssl)i = l;
|
||||
else {
|
||||
memcpy(buffer + z, data + l, i - l);
|
||||
z += i - l;
|
||||
}
|
||||
}
|
||||
if(i <= ssl - ksl) {
|
||||
if(!memcmp(data + i, key, ksl)) {
|
||||
memcpy(buffer + z, token, tsl);
|
||||
z += tsl;
|
||||
i += ksl;
|
||||
replace_count++;
|
||||
} else buffer[z++] = data[i++];
|
||||
} else buffer[z++] = data[i++];
|
||||
}
|
||||
if(i <= ssl - ksl) {
|
||||
if(!memcmp(str() + i, key, ksl)) {
|
||||
memcpy(data + z, token, tsl);
|
||||
z += tsl;
|
||||
i += ksl;
|
||||
replace_count++;
|
||||
} else data[z++] = str()[i++];
|
||||
} else data[z++] = str()[i++];
|
||||
buffer[z] = 0;
|
||||
|
||||
assign(buffer);
|
||||
delete[] buffer;
|
||||
}
|
||||
data[z] = 0;
|
||||
strcpy(str, data);
|
||||
free(data);
|
||||
return str;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,25 +1,25 @@
|
|||
#ifdef NALL_STRING_CPP
|
||||
|
||||
void split(nall::lstring &dest, const char *key, const char *src, size_t limit) {
|
||||
dest.reset();
|
||||
void lstring::split(const char *key, const char *src, unsigned limit) {
|
||||
reset();
|
||||
|
||||
int ssl = strlen(src), ksl = strlen(key);
|
||||
int lp = 0, split_count = 0;
|
||||
|
||||
for(int i = 0; i <= ssl - ksl;) {
|
||||
if(!memcmp(src + i, key, ksl)) {
|
||||
strlcpy(dest[split_count++], src + lp, i - lp + 1);
|
||||
strlcpy(operator[](split_count++), src + lp, i - lp + 1);
|
||||
i += ksl;
|
||||
lp = i;
|
||||
if(!--limit) break;
|
||||
} else i++;
|
||||
}
|
||||
|
||||
strcpy(dest[split_count++], src + lp);
|
||||
operator[](split_count++) = src + lp;
|
||||
}
|
||||
|
||||
void qsplit(nall::lstring &dest, const char *key, const char *src, size_t limit) {
|
||||
dest.reset();
|
||||
void lstring::qsplit(const char *key, const char *src, unsigned limit) {
|
||||
reset();
|
||||
|
||||
int ssl = strlen(src), ksl = strlen(key);
|
||||
int lp = 0, split_count = 0;
|
||||
|
@ -28,24 +28,24 @@ void qsplit(nall::lstring &dest, const char *key, const char *src, size_t limit)
|
|||
uint8_t x = src[i];
|
||||
|
||||
if(x == '\"' || x == '\'') {
|
||||
int z = i++; //skip opening quote
|
||||
int z = i++; //skip opening quote
|
||||
while(i < ssl && src[i] != x) i++;
|
||||
if(i >= ssl) i = z; //failed match, rewind i
|
||||
if(i >= ssl) i = z; //failed match, rewind i
|
||||
else {
|
||||
i++; //skip closing quote
|
||||
continue; //restart in case next char is also a quote
|
||||
i++; //skip closing quote
|
||||
continue; //restart in case next char is also a quote
|
||||
}
|
||||
}
|
||||
|
||||
if(!memcmp(src + i, key, ksl)) {
|
||||
strlcpy(dest[split_count++], src + lp, i - lp + 1);
|
||||
strlcpy(operator[](split_count++), src + lp, i - lp + 1);
|
||||
i += ksl;
|
||||
lp = i;
|
||||
if(!--limit) break;
|
||||
} else i++;
|
||||
}
|
||||
|
||||
strcpy(dest[split_count++], src + lp);
|
||||
operator[](split_count++) = src + lp;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,74 @@
|
|||
#ifdef NALL_STRING_CPP
|
||||
|
||||
size_t strlcpy(nall::string &dest, const char *src, size_t length) {
|
||||
dest.reserve(length);
|
||||
return strlcpy(dest(), src, length);
|
||||
}
|
||||
|
||||
size_t strlcat(nall::string &dest, const char *src, size_t length) {
|
||||
dest.reserve(length);
|
||||
return strlcat(dest(), src, length);
|
||||
}
|
||||
|
||||
nall::string substr(const char *src, size_t start, size_t length) {
|
||||
nall::string dest;
|
||||
if(length == 0) {
|
||||
//copy entire string
|
||||
dest = src + start;
|
||||
} else {
|
||||
//copy partial string
|
||||
strlcpy(dest, src + start, length + 1);
|
||||
}
|
||||
return dest;
|
||||
}
|
||||
|
||||
/* very simplistic wrappers to return nall::string& instead of char* type */
|
||||
|
||||
nall::string& strlower(nall::string &str) { strlower(str()); return str; }
|
||||
nall::string& strupper(nall::string &str) { strupper(str()); return str; }
|
||||
nall::string& strtr(nall::string &dest, const char *before, const char *after) { strtr(dest(), before, after); return dest; }
|
||||
nall::string& ltrim(nall::string &str, const char *key) { ltrim(str(), key); return str; }
|
||||
nall::string& rtrim(nall::string &str, const char *key) { rtrim(str(), key); return str; }
|
||||
nall::string& trim (nall::string &str, const char *key) { trim (str(), key); return str; }
|
||||
nall::string& ltrim_once(nall::string &str, const char *key) { ltrim_once(str(), key); return str; }
|
||||
nall::string& rtrim_once(nall::string &str, const char *key) { rtrim_once(str(), key); return str; }
|
||||
nall::string& trim_once (nall::string &str, const char *key) { trim_once (str(), key); return str; }
|
||||
|
||||
/* arithmetic <> string */
|
||||
|
||||
nall::string strhex(uintmax_t value) {
|
||||
nall::string temp;
|
||||
temp.reserve(strhex(0, value));
|
||||
strhex(temp(), value);
|
||||
return temp;
|
||||
}
|
||||
|
||||
nall::string strsigned(intmax_t value) {
|
||||
nall::string temp;
|
||||
temp.reserve(strsigned(0, value));
|
||||
strsigned(temp(), value);
|
||||
return temp;
|
||||
}
|
||||
|
||||
nall::string strunsigned(uintmax_t value) {
|
||||
nall::string temp;
|
||||
temp.reserve(strunsigned(0, value));
|
||||
strunsigned(temp(), value);
|
||||
return temp;
|
||||
}
|
||||
|
||||
nall::string strbin(uintmax_t value) {
|
||||
nall::string temp;
|
||||
temp.reserve(strbin(0, value));
|
||||
strbin(temp(), value);
|
||||
return temp;
|
||||
}
|
||||
|
||||
nall::string strdouble(double value) {
|
||||
nall::string temp;
|
||||
temp.reserve(strdouble(0, value));
|
||||
strdouble(temp(), value);
|
||||
return temp;
|
||||
}
|
||||
|
||||
#endif
|
|
@ -14,7 +14,7 @@
|
|||
|
||||
namespace nall {
|
||||
//UTF-8 to UTF-16
|
||||
class utf16 {
|
||||
class utf16_t {
|
||||
public:
|
||||
operator wchar_t*() {
|
||||
return buffer;
|
||||
|
@ -24,14 +24,14 @@ namespace nall {
|
|||
return buffer;
|
||||
}
|
||||
|
||||
utf16(const char *s = "") {
|
||||
utf16_t(const char *s = "") {
|
||||
if(!s) s = "";
|
||||
unsigned length = MultiByteToWideChar(CP_UTF8, 0, s, -1, 0, 0);
|
||||
buffer = new(zeromemory) wchar_t[length + 1];
|
||||
MultiByteToWideChar(CP_UTF8, 0, s, -1, buffer, length);
|
||||
}
|
||||
|
||||
~utf16() {
|
||||
~utf16_t() {
|
||||
delete[] buffer;
|
||||
}
|
||||
|
||||
|
@ -40,7 +40,7 @@ namespace nall {
|
|||
};
|
||||
|
||||
//UTF-16 to UTF-8
|
||||
class utf8 {
|
||||
class utf8_t {
|
||||
public:
|
||||
operator char*() {
|
||||
return buffer;
|
||||
|
@ -50,14 +50,14 @@ namespace nall {
|
|||
return buffer;
|
||||
}
|
||||
|
||||
utf8(const wchar_t *s = L"") {
|
||||
utf8_t(const wchar_t *s = L"") {
|
||||
if(!s) s = L"";
|
||||
unsigned length = WideCharToMultiByte(CP_UTF8, 0, s, -1, 0, 0, (const char*)0, (BOOL*)0);
|
||||
buffer = new(zeromemory) char[length + 1];
|
||||
WideCharToMultiByte(CP_UTF8, 0, s, -1, buffer, length, (const char*)0, (BOOL*)0);
|
||||
}
|
||||
|
||||
~utf8() {
|
||||
~utf8_t() {
|
||||
delete[] buffer;
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
//================
|
||||
//Keyboard and mouse are controlled directly via Xlib,
|
||||
//as SDL cannot capture input from windows it does not create itself.
|
||||
//SDL is used only to handle joysticks.
|
||||
//SDL is used only to handle joysticks / gamepads.
|
||||
|
||||
#include <SDL/SDL.h>
|
||||
#include <sys/ipc.h>
|
||||
|
@ -15,21 +15,21 @@
|
|||
namespace ruby {
|
||||
|
||||
#include "sdl.hpp"
|
||||
using namespace nall;
|
||||
|
||||
class pInputSDL {
|
||||
public:
|
||||
struct pInputSDL {
|
||||
#include "xlibkeys.hpp"
|
||||
InputSDL &self;
|
||||
Display *display;
|
||||
Window rootwindow;
|
||||
unsigned screenwidth, screenheight;
|
||||
unsigned relativex, relativey;
|
||||
bool mouseacquired;
|
||||
Cursor InvisibleCursor;
|
||||
SDL_Joystick *gamepad[joypad<>::count];
|
||||
|
||||
struct {
|
||||
Display *display;
|
||||
Window rootwindow;
|
||||
Cursor InvisibleCursor;
|
||||
SDL_Joystick *gamepad[joypad<>::count];
|
||||
|
||||
unsigned screenwidth, screenheight;
|
||||
unsigned relativex, relativey;
|
||||
bool mouseacquired;
|
||||
|
||||
//mouse device settings
|
||||
int accel_numerator;
|
||||
int accel_denominator;
|
||||
|
@ -73,35 +73,35 @@ public:
|
|||
bool acquire() {
|
||||
if(acquired()) return true;
|
||||
|
||||
if(XGrabPointer(display, settings.handle, True, 0, GrabModeAsync, GrabModeAsync,
|
||||
rootwindow, InvisibleCursor, CurrentTime) == GrabSuccess) {
|
||||
if(XGrabPointer(device.display, settings.handle, True, 0, GrabModeAsync, GrabModeAsync,
|
||||
device.rootwindow, device.InvisibleCursor, CurrentTime) == GrabSuccess) {
|
||||
//backup existing cursor acceleration settings
|
||||
XGetPointerControl(display, &device.accel_numerator, &device.accel_denominator, &device.threshold);
|
||||
XGetPointerControl(device.display, &device.accel_numerator, &device.accel_denominator, &device.threshold);
|
||||
|
||||
//disable cursor acceleration
|
||||
XChangePointerControl(display, True, False, 1, 1, 0);
|
||||
XChangePointerControl(device.display, True, False, 1, 1, 0);
|
||||
|
||||
//center cursor (so that first relative poll returns 0, 0 if mouse has not moved)
|
||||
XWarpPointer(display, None, rootwindow, 0, 0, 0, 0, screenwidth / 2, screenheight / 2);
|
||||
XWarpPointer(device.display, None, device.rootwindow, 0, 0, 0, 0, device.screenwidth / 2, device.screenheight / 2);
|
||||
|
||||
return mouseacquired = true;
|
||||
return device.mouseacquired = true;
|
||||
} else {
|
||||
return mouseacquired = false;
|
||||
return device.mouseacquired = false;
|
||||
}
|
||||
}
|
||||
|
||||
bool unacquire() {
|
||||
if(acquired()) {
|
||||
//restore cursor acceleration and release cursor
|
||||
XChangePointerControl(display, True, True, device.accel_numerator, device.accel_denominator, device.threshold);
|
||||
XUngrabPointer(display, CurrentTime);
|
||||
mouseacquired = false;
|
||||
XChangePointerControl(device.display, True, True, device.accel_numerator, device.accel_denominator, device.threshold);
|
||||
XUngrabPointer(device.display, CurrentTime);
|
||||
device.mouseacquired = false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool acquired() {
|
||||
return mouseacquired;
|
||||
return device.mouseacquired;
|
||||
}
|
||||
|
||||
bool poll(int16_t *table) {
|
||||
|
@ -112,7 +112,7 @@ public:
|
|||
//========
|
||||
|
||||
char state[32];
|
||||
XQueryKeymap(display, state);
|
||||
XQueryKeymap(device.display, state);
|
||||
|
||||
for(unsigned i = 0; i < keyboard::limit; i++) {
|
||||
uint8_t code = keycode[i];
|
||||
|
@ -128,28 +128,28 @@ public:
|
|||
int root_x_return = 0, root_y_return = 0;
|
||||
int win_x_return = 0, win_y_return = 0;
|
||||
unsigned int mask_return = 0;
|
||||
XQueryPointer(display, settings.handle,
|
||||
XQueryPointer(device.display, settings.handle,
|
||||
&root_return, &child_return, &root_x_return, &root_y_return,
|
||||
&win_x_return, &win_y_return, &mask_return);
|
||||
|
||||
if(acquired()) {
|
||||
XWindowAttributes attributes;
|
||||
XGetWindowAttributes(display, settings.handle, &attributes);
|
||||
XGetWindowAttributes(device.display, settings.handle, &attributes);
|
||||
|
||||
//absolute -> relative conversion
|
||||
table[mouse::x] = (int16_t)(root_x_return - screenwidth / 2);
|
||||
table[mouse::y] = (int16_t)(root_y_return - screenheight / 2);
|
||||
table[mouse::x] = (int16_t)(root_x_return - device.screenwidth / 2);
|
||||
table[mouse::y] = (int16_t)(root_y_return - device.screenheight / 2);
|
||||
|
||||
if(table[mouse::x] != 0 || table[mouse::y] != 0) {
|
||||
//if mouse movement occurred, re-center mouse for next poll
|
||||
XWarpPointer(display, None, rootwindow, 0, 0, 0, 0, screenwidth / 2, screenheight / 2);
|
||||
XWarpPointer(device.display, None, device.rootwindow, 0, 0, 0, 0, device.screenwidth / 2, device.screenheight / 2);
|
||||
}
|
||||
} else {
|
||||
table[mouse::x] = (int16_t)(root_x_return - relativex);
|
||||
table[mouse::y] = (int16_t)(root_y_return - relativey);
|
||||
table[mouse::x] = (int16_t)(root_x_return - device.relativex);
|
||||
table[mouse::y] = (int16_t)(root_y_return - device.relativey);
|
||||
|
||||
relativex = root_x_return;
|
||||
relativey = root_y_return;
|
||||
device.relativex = root_x_return;
|
||||
device.relativey = root_y_return;
|
||||
}
|
||||
|
||||
//manual device polling is limited to only five buttons ...
|
||||
|
@ -165,7 +165,7 @@ public:
|
|||
|
||||
SDL_JoystickUpdate();
|
||||
for(unsigned i = 0; i < joypad<>::count; i++) {
|
||||
if(!gamepad[i]) continue;
|
||||
if(!device.gamepad[i]) continue;
|
||||
|
||||
unsigned index = joypad<>::index(i, joypad<>::none);
|
||||
table[index + joypad<>::up ] = false;
|
||||
|
@ -177,11 +177,12 @@ public:
|
|||
resistance = max(1, min(99, resistance));
|
||||
resistance = (int)((double)resistance * 32768.0 / 100.0);
|
||||
|
||||
unsigned axes = min((unsigned)joypad<>::axes, SDL_JoystickNumAxes(gamepad[i]));
|
||||
//axes
|
||||
unsigned axes = min((unsigned)joypad<>::axes, SDL_JoystickNumAxes(device.gamepad[i]));
|
||||
for(unsigned axis = 0; axis < axes; axis++) {
|
||||
int16_t value = (int16_t)SDL_JoystickGetAxis(gamepad[i], axis);
|
||||
int16_t value = (int16_t)SDL_JoystickGetAxis(device.gamepad[i], axis);
|
||||
table[index + joypad<>::axis + axis] = value;
|
||||
if(axis == 0) { //X-axis
|
||||
if(axis == 0) { //X-axis
|
||||
table[index + joypad<>::left ] |= value < -resistance;
|
||||
table[index + joypad<>::right] |= value > +resistance;
|
||||
} else if(axis == 1) { //Y-axis
|
||||
|
@ -190,8 +191,18 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
//POV hats
|
||||
if(SDL_JoystickNumHats(device.gamepad[i]) >= 1) {
|
||||
uint8_t state = SDL_JoystickGetHat(device.gamepad[i], 0);
|
||||
table[index + joypad<>::up ] |= state & SDL_HAT_UP;
|
||||
table[index + joypad<>::down ] |= state & SDL_HAT_DOWN;
|
||||
table[index + joypad<>::left ] |= state & SDL_HAT_LEFT;
|
||||
table[index + joypad<>::right] |= state & SDL_HAT_RIGHT;
|
||||
}
|
||||
|
||||
//buttons
|
||||
for(unsigned button = 0; button < joypad<>::buttons; button++) {
|
||||
table[index + joypad<>::button + button] = SDL_JoystickGetButton(gamepad[i], button);
|
||||
table[index + joypad<>::button + button] = SDL_JoystickGetButton(device.gamepad[i], button);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -203,12 +214,12 @@ public:
|
|||
SDL_InitSubSystem(SDL_INIT_JOYSTICK);
|
||||
SDL_JoystickEventState(SDL_IGNORE);
|
||||
|
||||
display = XOpenDisplay(0);
|
||||
rootwindow = DefaultRootWindow(display);
|
||||
device.display = XOpenDisplay(0);
|
||||
device.rootwindow = DefaultRootWindow(device.display);
|
||||
XWindowAttributes attributes;
|
||||
XGetWindowAttributes(display, rootwindow, &attributes);
|
||||
screenwidth = attributes.width;
|
||||
screenheight = attributes.height;
|
||||
XGetWindowAttributes(device.display, device.rootwindow, &attributes);
|
||||
device.screenwidth = attributes.width;
|
||||
device.screenheight = attributes.height;
|
||||
|
||||
//Xlib: "because XShowCursor(false) would be too easy."
|
||||
//create a fully transparent cursor named InvisibleCursor,
|
||||
|
@ -216,19 +227,19 @@ public:
|
|||
Pixmap pixmap;
|
||||
XColor black, unused;
|
||||
static char invisible_data[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
|
||||
Colormap colormap = DefaultColormap(display, DefaultScreen(display));
|
||||
XAllocNamedColor(display, colormap, "black", &black, &unused);
|
||||
pixmap = XCreateBitmapFromData(display, settings.handle, invisible_data, 8, 8);
|
||||
InvisibleCursor = XCreatePixmapCursor(display, pixmap, pixmap, &black, &black, 0, 0);
|
||||
XFreePixmap(display, pixmap);
|
||||
XFreeColors(display, colormap, &black.pixel, 1, 0);
|
||||
Colormap colormap = DefaultColormap(device.display, DefaultScreen(device.display));
|
||||
XAllocNamedColor(device.display, colormap, "black", &black, &unused);
|
||||
pixmap = XCreateBitmapFromData(device.display, settings.handle, invisible_data, 8, 8);
|
||||
device.InvisibleCursor = XCreatePixmapCursor(device.display, pixmap, pixmap, &black, &black, 0, 0);
|
||||
XFreePixmap(device.display, pixmap);
|
||||
XFreeColors(device.display, colormap, &black.pixel, 1, 0);
|
||||
|
||||
mouseacquired = false;
|
||||
relativex = 0;
|
||||
relativey = 0;
|
||||
device.mouseacquired = false;
|
||||
device.relativex = 0;
|
||||
device.relativey = 0;
|
||||
|
||||
for(unsigned i = 0; i < joypad<>::count && i < SDL_NumJoysticks(); i++) {
|
||||
gamepad[i] = SDL_JoystickOpen(i);
|
||||
device.gamepad[i] = SDL_JoystickOpen(i);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -236,18 +247,18 @@ public:
|
|||
|
||||
void term() {
|
||||
unacquire();
|
||||
XFreeCursor(display, InvisibleCursor);
|
||||
XFreeCursor(device.display, device.InvisibleCursor);
|
||||
|
||||
for(unsigned i = 0; i < joypad<>::count; i++) {
|
||||
if(gamepad[i]) SDL_JoystickClose(gamepad[i]);
|
||||
gamepad[i] = 0;
|
||||
if(device.gamepad[i]) SDL_JoystickClose(device.gamepad[i]);
|
||||
device.gamepad[i] = 0;
|
||||
}
|
||||
|
||||
SDL_QuitSubSystem(SDL_INIT_JOYSTICK);
|
||||
}
|
||||
|
||||
pInputSDL(InputSDL &self_) : self(self_) {
|
||||
for(unsigned i = 0; i < joypad<>::count; i++) gamepad[i] = 0;
|
||||
for(unsigned i = 0; i < joypad<>::count; i++) device.gamepad[i] = 0;
|
||||
settings.analog_axis_resistance = 75;
|
||||
}
|
||||
};
|
||||
|
@ -265,4 +276,4 @@ void InputSDL::term() { p.term(); }
|
|||
InputSDL::InputSDL() : p(*new pInputSDL(*this)) {}
|
||||
InputSDL::~InputSDL() { delete &p; }
|
||||
|
||||
} //namespace ruby
|
||||
}
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
#include <ruby/ruby.hpp>
|
||||
using namespace nall;
|
||||
|
||||
#include <ruby/ruby_impl.cpp>
|
||||
|
||||
namespace ruby {
|
||||
|
|
|
@ -12,10 +12,6 @@
|
|||
#include <nall/input.hpp>
|
||||
#include <nall/new.hpp>
|
||||
#include <nall/stdint.hpp>
|
||||
using nall::min;
|
||||
using nall::max;
|
||||
using nall::sclamp;
|
||||
using nall::zeromemory;
|
||||
|
||||
namespace ruby {
|
||||
|
||||
|
|
|
@ -3,9 +3,9 @@
|
|||
#include <X11/Xlib.h>
|
||||
#include <X11/Xutil.h>
|
||||
#include <X11/Xatom.h>
|
||||
#include <X11/extensions/XShm.h>
|
||||
#include <X11/extensions/Xv.h>
|
||||
#include <X11/extensions/Xvlib.h>
|
||||
#include <X11/extensions/XShm.h>
|
||||
|
||||
extern "C" XvImage* XvShmCreateImage(Display*, XvPortID, int, char*, int, int, XShmSegmentInfo*);
|
||||
|
||||
|
@ -16,18 +16,35 @@ namespace ruby {
|
|||
class pVideoXv {
|
||||
public:
|
||||
VideoXv &self;
|
||||
Display *display;
|
||||
GC gc;
|
||||
int screen, xv_port, xv_depth, xv_visualid;
|
||||
XvImage *xvimage;
|
||||
XShmSegmentInfo shminfo;
|
||||
|
||||
uint32_t *buffer;
|
||||
uint8_t *ytable, *utable, *vtable;
|
||||
|
||||
bool use_child_window;
|
||||
Window xwindow;
|
||||
Colormap colormap;
|
||||
enum XvFormat {
|
||||
XvFormatRGB32,
|
||||
XvFormatRGB24,
|
||||
XvFormatRGB16,
|
||||
XvFormatRGB15,
|
||||
XvFormatYUY2,
|
||||
XvFormatUYVY,
|
||||
XvFormatUnknown
|
||||
};
|
||||
|
||||
uint8_t *ytable, *utable, *vtable;
|
||||
struct {
|
||||
Display *display;
|
||||
GC gc;
|
||||
Window window;
|
||||
Colormap colormap;
|
||||
XShmSegmentInfo shminfo;
|
||||
|
||||
int port;
|
||||
int depth;
|
||||
int visualid;
|
||||
|
||||
XvImage *image;
|
||||
XvFormat format;
|
||||
uint32_t fourcc;
|
||||
} device;
|
||||
|
||||
struct {
|
||||
Window handle;
|
||||
|
@ -57,9 +74,9 @@ public:
|
|||
if(setting == Video::Synchronize) {
|
||||
Display *display = XOpenDisplay(0);
|
||||
Atom atom = XInternAtom(display, "XV_SYNC_TO_VBLANK", true);
|
||||
if(atom != None) {
|
||||
if(atom != None && device.port >= 0) {
|
||||
settings.synchronize = param;
|
||||
XvSetPortAttribute(display, xv_port, atom, settings.synchronize);
|
||||
XvSetPortAttribute(display, device.port, atom, settings.synchronize);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -85,136 +102,181 @@ public:
|
|||
|
||||
void refresh(unsigned width, unsigned height) {
|
||||
XWindowAttributes target;
|
||||
XGetWindowAttributes(display, xwindow, &target);
|
||||
XGetWindowAttributes(device.display, device.window, &target);
|
||||
|
||||
if(use_child_window) {
|
||||
//we must ensure that the child window is the same size as the parent window.
|
||||
//unfortunately, we cannot hook the parent window resize event notification,
|
||||
//as we did not create the parent window, nor have any knowledge of the toolkit used.
|
||||
//therefore, inelegant as it may be, we query each window size and resize as needed.
|
||||
XWindowAttributes parent;
|
||||
XGetWindowAttributes(display, settings.handle, &parent);
|
||||
if(target.width != parent.width || target.height != parent.height) {
|
||||
XResizeWindow(display, xwindow, parent.width, parent.height);
|
||||
}
|
||||
|
||||
//update target width and height attributes
|
||||
XGetWindowAttributes(display, xwindow, &target);
|
||||
}
|
||||
|
||||
uint32_t *input = (uint32_t*)buffer;
|
||||
uint16_t *output = (uint16_t*)xvimage->data;
|
||||
for(unsigned y = 0; y < height; y++) {
|
||||
for(unsigned 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;
|
||||
|
||||
*output++ = (u << 8) | ytable[p0];
|
||||
*output++ = (v << 8) | ytable[p1];
|
||||
}
|
||||
input += 1024 - width;
|
||||
output += 1024 - width;
|
||||
//we must ensure that the child window is the same size as the parent window.
|
||||
//unfortunately, we cannot hook the parent window resize event notification,
|
||||
//as we did not create the parent window, nor have any knowledge of the toolkit used.
|
||||
//therefore, query each window size and resize as needed.
|
||||
XWindowAttributes parent;
|
||||
XGetWindowAttributes(device.display, settings.handle, &parent);
|
||||
if(target.width != parent.width || target.height != parent.height) {
|
||||
XResizeWindow(device.display, device.window, parent.width, parent.height);
|
||||
}
|
||||
|
||||
XvShmPutImage(display, xv_port, xwindow, gc, xvimage,
|
||||
//update target width and height attributes
|
||||
XGetWindowAttributes(device.display, device.window, &target);
|
||||
|
||||
switch(device.format) {
|
||||
case XvFormatRGB32: render_rgb32(width, height); break;
|
||||
case XvFormatRGB24: render_rgb24(width, height); break;
|
||||
case XvFormatRGB16: render_rgb16(width, height); break;
|
||||
case XvFormatRGB15: render_rgb15(width, height); break;
|
||||
case XvFormatYUY2: render_yuy2 (width, height); break;
|
||||
case XvFormatUYVY: render_uyvy (width, height); break;
|
||||
}
|
||||
|
||||
XvShmPutImage(device.display, device.port, device.window, device.gc, device.image,
|
||||
0, 0, width, height,
|
||||
0, 0, target.width, target.height,
|
||||
true);
|
||||
}
|
||||
|
||||
bool init() {
|
||||
display = XOpenDisplay(0);
|
||||
screen = DefaultScreen(display);
|
||||
device.display = XOpenDisplay(0);
|
||||
|
||||
//XShm is required for rendering
|
||||
if(!XShmQueryExtension(display)) {
|
||||
if(!XShmQueryExtension(device.display)) {
|
||||
fprintf(stderr, "VideoXv: XShm extension not found.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
//find an appropriate port, if possible
|
||||
xv_port = -1;
|
||||
//find an appropriate Xv port
|
||||
device.port = -1;
|
||||
XvAdaptorInfo *adaptor_info;
|
||||
unsigned adaptor_count;
|
||||
XvQueryAdaptors(display, DefaultRootWindow(display), &adaptor_count, &adaptor_info);
|
||||
XvQueryAdaptors(device.display, DefaultRootWindow(device.display), &adaptor_count, &adaptor_info);
|
||||
for(unsigned i = 0; i < adaptor_count; i++) {
|
||||
//find adaptor that supports both input (memory->drawable) and image (drawable->screen) masks
|
||||
if(adaptor_info[i].num_formats < 1) continue;
|
||||
if(!(adaptor_info[i].type & XvInputMask)) continue;
|
||||
if(!(adaptor_info[i].type & XvImageMask)) continue;
|
||||
|
||||
xv_port = adaptor_info[i].base_id;
|
||||
xv_depth = adaptor_info[i].formats->depth;
|
||||
xv_visualid = adaptor_info[i].formats->visual_id;
|
||||
device.port = adaptor_info[i].base_id;
|
||||
device.depth = adaptor_info[i].formats->depth;
|
||||
device.visualid = adaptor_info[i].formats->visual_id;
|
||||
break;
|
||||
}
|
||||
XvFreeAdaptorInfo(adaptor_info);
|
||||
if(xv_port == -1) {
|
||||
if(device.port < 0) {
|
||||
fprintf(stderr, "VideoXv: failed to find valid XvPort.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
//create child window to attach to parent window.
|
||||
//this is so that even if parent window visual depth doesn't match Xv visual
|
||||
//(common with composited windows), Xv can still render to child window.
|
||||
XWindowAttributes window_attributes;
|
||||
XGetWindowAttributes(display, settings.handle, &window_attributes);
|
||||
XGetWindowAttributes(device.display, settings.handle, &window_attributes);
|
||||
|
||||
if(xv_depth == window_attributes.depth) {
|
||||
//Xv port is depth-compatible with target output window
|
||||
use_child_window = false;
|
||||
xwindow = settings.handle;
|
||||
} else {
|
||||
//Xv port is not depth-compatible with target output window
|
||||
//this is often the case when a 32bpp composited window is used with a 24bpp-only Xv adaptor
|
||||
//the only way to render to target is to create a child window with the Xv ports' depth
|
||||
use_child_window = true;
|
||||
XVisualInfo visualtemplate;
|
||||
visualtemplate.visualid = xv_visualid;
|
||||
visualtemplate.screen = screen;
|
||||
visualtemplate.depth = xv_depth;
|
||||
visualtemplate.visual = 0;
|
||||
int visualmatches = 0;
|
||||
XVisualInfo *visualinfo = XGetVisualInfo(display, VisualIDMask | VisualScreenMask | VisualDepthMask, &visualtemplate, &visualmatches);
|
||||
if(visualmatches < 1 || !visualinfo->visual) {
|
||||
if(visualinfo) XFree(visualinfo);
|
||||
fprintf(stderr, "VideoXv: unable to find Xv-compatible visual.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
colormap = XCreateColormap(display, settings.handle, visualinfo->visual, AllocNone);
|
||||
XSetWindowAttributes attributes;
|
||||
attributes.colormap = colormap;
|
||||
attributes.border_pixel = 0;
|
||||
attributes.event_mask = StructureNotifyMask;
|
||||
xwindow = XCreateWindow(display, /* parent = */ settings.handle,
|
||||
/* x = */ 0, /* y = */ 0, window_attributes.width, window_attributes.height,
|
||||
/* border_width = */ 0, xv_depth, InputOutput, visualinfo->visual,
|
||||
CWColormap | CWBorderPixel | CWEventMask, &attributes);
|
||||
XFree(visualinfo);
|
||||
XSetWindowBackground(display, xwindow, /* color = */ 0);
|
||||
XMapWindow(display, xwindow);
|
||||
XVisualInfo visualtemplate;
|
||||
visualtemplate.visualid = device.visualid;
|
||||
visualtemplate.screen = DefaultScreen(device.display);
|
||||
visualtemplate.depth = device.depth;
|
||||
visualtemplate.visual = 0;
|
||||
int visualmatches = 0;
|
||||
XVisualInfo *visualinfo = XGetVisualInfo(device.display, VisualIDMask | VisualScreenMask | VisualDepthMask, &visualtemplate, &visualmatches);
|
||||
if(visualmatches < 1 || !visualinfo->visual) {
|
||||
if(visualinfo) XFree(visualinfo);
|
||||
fprintf(stderr, "VideoXv: unable to find Xv-compatible visual.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
gc = XCreateGC(display, xwindow, 0, 0);
|
||||
device.colormap = XCreateColormap(device.display, settings.handle, visualinfo->visual, AllocNone);
|
||||
XSetWindowAttributes attributes;
|
||||
attributes.colormap = device.colormap;
|
||||
attributes.border_pixel = 0;
|
||||
attributes.event_mask = StructureNotifyMask;
|
||||
device.window = XCreateWindow(device.display, /* parent = */ settings.handle,
|
||||
/* x = */ 0, /* y = */ 0, window_attributes.width, window_attributes.height,
|
||||
/* border_width = */ 0, device.depth, InputOutput, visualinfo->visual,
|
||||
CWColormap | CWBorderPixel | CWEventMask, &attributes);
|
||||
XFree(visualinfo);
|
||||
XSetWindowBackground(device.display, device.window, /* color = */ 0);
|
||||
XMapWindow(device.display, device.window);
|
||||
|
||||
device.gc = XCreateGC(device.display, device.window, 0, 0);
|
||||
|
||||
//set colorkey to auto paint, so that Xv video output is always visible
|
||||
Atom atom = XInternAtom(display, "XV_AUTOPAINT_COLORKEY", true);
|
||||
if(atom != None) XvSetPortAttribute(display, xv_port, atom, 1);
|
||||
Atom atom = XInternAtom(device.display, "XV_AUTOPAINT_COLORKEY", true);
|
||||
if(atom != None) XvSetPortAttribute(device.display, device.port, atom, 1);
|
||||
|
||||
//0x32595559 = 16-bit Y8U8,Y8V8 (YUY2)
|
||||
xvimage = XvShmCreateImage(display, xv_port, 0x32595559, 0, 1024, 1024, &shminfo);
|
||||
if(!xvimage) {
|
||||
//find optimal rendering format
|
||||
device.format = XvFormatUnknown;
|
||||
signed format_count;
|
||||
XvImageFormatValues *format = XvListImageFormats(device.display, device.port, &format_count);
|
||||
|
||||
if(device.format == XvFormatUnknown) for(signed i = 0; i < format_count; i++) {
|
||||
if(format[i].type == XvRGB && format[i].bits_per_pixel == 32) {
|
||||
device.format = XvFormatRGB32;
|
||||
device.fourcc = format[i].id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(device.format == XvFormatUnknown) for(signed i = 0; i < format_count; i++) {
|
||||
if(format[i].type == XvRGB && format[i].bits_per_pixel == 24) {
|
||||
device.format = XvFormatRGB24;
|
||||
device.fourcc = format[i].id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(device.format == XvFormatUnknown) for(signed i = 0; i < format_count; i++) {
|
||||
if(format[i].type == XvRGB && format[i].bits_per_pixel == 16) {
|
||||
device.format = XvFormatRGB16;
|
||||
device.fourcc = format[i].id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(device.format == XvFormatUnknown) for(signed i = 0; i < format_count; i++) {
|
||||
if(format[i].type == XvRGB && format[i].bits_per_pixel == 15) {
|
||||
device.format = XvFormatRGB15;
|
||||
device.fourcc = format[i].id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(device.format == XvFormatUnknown) for(signed i = 0; i < format_count; i++) {
|
||||
if(format[i].type == XvYUV && format[i].bits_per_pixel == 16 && format[i].format == XvPacked) {
|
||||
if(format[i].component_order[0] == 'Y' && format[i].component_order[1] == 'U'
|
||||
&& format[i].component_order[2] == 'Y' && format[i].component_order[3] == 'V'
|
||||
) {
|
||||
device.format = XvFormatYUY2;
|
||||
device.fourcc = format[i].id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(device.format == XvFormatUnknown) for(signed i = 0; i < format_count; i++) {
|
||||
if(format[i].type == XvYUV && format[i].bits_per_pixel == 16 && format[i].format == XvPacked) {
|
||||
if(format[i].component_order[0] == 'U' && format[i].component_order[1] == 'Y'
|
||||
&& format[i].component_order[2] == 'V' && format[i].component_order[3] == 'Y'
|
||||
) {
|
||||
device.format = XvFormatUYVY;
|
||||
device.fourcc = format[i].id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
free(format);
|
||||
if(device.format == XvFormatUnknown) {
|
||||
fprintf(stderr, "VideoXv: unable to find a supported image format.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
device.image = XvShmCreateImage(device.display, device.port, device.fourcc, 0, 1024, 1024, &device.shminfo);
|
||||
if(!device.image) {
|
||||
fprintf(stderr, "VideoXv: XShmCreateImage failed.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
shminfo.shmid = shmget(IPC_PRIVATE, xvimage->data_size, IPC_CREAT | 0777);
|
||||
shminfo.shmaddr = xvimage->data = (char*)shmat(shminfo.shmid, 0, 0);
|
||||
shminfo.readOnly = false;
|
||||
if(!XShmAttach(display, &shminfo)) {
|
||||
device.shminfo.shmid = shmget(IPC_PRIVATE, device.image->data_size, IPC_CREAT | 0777);
|
||||
device.shminfo.shmaddr = device.image->data = (char*)shmat(device.shminfo.shmid, 0, 0);
|
||||
device.shminfo.readOnly = false;
|
||||
if(!XShmAttach(device.display, &device.shminfo)) {
|
||||
fprintf(stderr, "VideoXv: XShmAttach failed.\n");
|
||||
return false;
|
||||
}
|
||||
|
@ -226,18 +288,16 @@ public:
|
|||
}
|
||||
|
||||
void term() {
|
||||
XShmDetach(display, &shminfo);
|
||||
XShmDetach(device.display, &device.shminfo);
|
||||
|
||||
if(use_child_window) {
|
||||
if(xwindow) {
|
||||
XUnmapWindow(display, xwindow);
|
||||
xwindow = 0;
|
||||
}
|
||||
if(device.window) {
|
||||
XUnmapWindow(device.display, device.window);
|
||||
device.window = 0;
|
||||
}
|
||||
|
||||
if(colormap) {
|
||||
XFreeColormap(display, colormap);
|
||||
colormap = 0;
|
||||
}
|
||||
if(device.colormap) {
|
||||
XFreeColormap(device.display, device.colormap);
|
||||
device.colormap = 0;
|
||||
}
|
||||
|
||||
if(buffer) { delete[] buffer; buffer = 0; }
|
||||
|
@ -246,6 +306,110 @@ public:
|
|||
if(vtable) { delete[] vtable; vtable = 0; }
|
||||
}
|
||||
|
||||
void render_rgb32(unsigned width, unsigned height) {
|
||||
uint32_t *input = (uint32_t*)buffer;
|
||||
uint32_t *output = (uint32_t*)device.image->data;
|
||||
|
||||
for(unsigned y = 0; y < height; y++) {
|
||||
memcpy(output, input, width * 4);
|
||||
input += 1024 - width;
|
||||
output += 1024 - width;
|
||||
}
|
||||
}
|
||||
|
||||
void render_rgb24(unsigned width, unsigned height) {
|
||||
uint32_t *input = (uint32_t*)buffer;
|
||||
uint8_t *output = (uint8_t*)device.image->data;
|
||||
|
||||
for(unsigned y = 0; y < height; y++) {
|
||||
for(unsigned x = 0; x < width; x++) {
|
||||
uint32_t p = *input++;
|
||||
*output++ = p;
|
||||
*output++ = p >> 8;
|
||||
*output++ = p >> 16;
|
||||
}
|
||||
|
||||
input += (1024 - width);
|
||||
output += (1024 - width) * 3;
|
||||
}
|
||||
}
|
||||
|
||||
void render_rgb16(unsigned width, unsigned height) {
|
||||
uint32_t *input = (uint32_t*)buffer;
|
||||
uint16_t *output = (uint16_t*)device.image->data;
|
||||
|
||||
for(unsigned y = 0; y < height; y++) {
|
||||
for(unsigned x = 0; x < width; x++) {
|
||||
uint32_t p = *input++;
|
||||
*output++ = ((p >> 8) & 0xf800) | ((p >> 5) & 0x07e0) | ((p >> 3) & 0x001f); //RGB32->RGB16
|
||||
}
|
||||
|
||||
input += 1024 - width;
|
||||
output += 1024 - width;
|
||||
}
|
||||
}
|
||||
|
||||
void render_rgb15(unsigned width, unsigned height) {
|
||||
uint32_t *input = (uint32_t*)buffer;
|
||||
uint16_t *output = (uint16_t*)device.image->data;
|
||||
|
||||
for(unsigned y = 0; y < height; y++) {
|
||||
for(unsigned x = 0; x < width; x++) {
|
||||
uint32_t p = *input++;
|
||||
*output++ = ((p >> 9) & 0x7c00) | ((p >> 6) & 0x03e0) | ((p >> 3) & 0x001f); //RGB32->RGB15
|
||||
}
|
||||
|
||||
input += 1024 - width;
|
||||
output += 1024 - width;
|
||||
}
|
||||
}
|
||||
|
||||
void render_yuy2(unsigned width, unsigned height) {
|
||||
uint32_t *input = (uint32_t*)buffer;
|
||||
uint16_t *output = (uint16_t*)device.image->data;
|
||||
|
||||
for(unsigned y = 0; y < height; y++) {
|
||||
for(unsigned x = 0; x < width >> 1; x++) {
|
||||
uint32_t p0 = *input++;
|
||||
uint32_t p1 = *input++;
|
||||
p0 = ((p0 >> 8) & 0xf800) + ((p0 >> 5) & 0x07e0) + ((p0 >> 3) & 0x001f); //RGB32->RGB16
|
||||
p1 = ((p1 >> 8) & 0xf800) + ((p1 >> 5) & 0x07e0) + ((p1 >> 3) & 0x001f); //RGB32->RGB16
|
||||
|
||||
uint8_t u = (utable[p0] + utable[p1]) >> 1;
|
||||
uint8_t v = (vtable[p0] + vtable[p1]) >> 1;
|
||||
|
||||
*output++ = (u << 8) | ytable[p0];
|
||||
*output++ = (v << 8) | ytable[p1];
|
||||
}
|
||||
|
||||
input += 1024 - width;
|
||||
output += 1024 - width;
|
||||
}
|
||||
}
|
||||
|
||||
void render_uyvy(unsigned width, unsigned height) {
|
||||
uint32_t *input = (uint32_t*)buffer;
|
||||
uint16_t *output = (uint16_t*)device.image->data;
|
||||
|
||||
for(unsigned y = 0; y < height; y++) {
|
||||
for(unsigned 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;
|
||||
|
||||
*output++ = (ytable[p0] << 8) | u;
|
||||
*output++ = (ytable[p1] << 8) | v;
|
||||
}
|
||||
|
||||
input += 1024 - width;
|
||||
output += 1024 - width;
|
||||
}
|
||||
}
|
||||
|
||||
void init_yuv_tables() {
|
||||
ytable = new uint8_t[65536];
|
||||
utable = new uint8_t[65536];
|
||||
|
@ -254,17 +418,18 @@ public:
|
|||
for(unsigned i = 0; i < 65536; i++) {
|
||||
//extract RGB565 color data from i
|
||||
uint8_t r = (i >> 11) & 31, g = (i >> 5) & 63, b = (i) & 31;
|
||||
r = (r << 3) | (r >> 2); //R5->R8
|
||||
g = (g << 2) | (g >> 4); //G6->G8
|
||||
b = (b << 3) | (b >> 2); //B5->B8
|
||||
r = (r << 3) | (r >> 2); //R5->R8
|
||||
g = (g << 2) | (g >> 4); //G6->G8
|
||||
b = (b << 3) | (b >> 2); //B5->B8
|
||||
|
||||
//RGB->YUV conversion
|
||||
//ITU-R Recommendation BT.601
|
||||
//double lr = 0.299, lg = 0.587, lb = 0.114;
|
||||
int y = int( +(double(r) * 0.257) + (double(g) * 0.504) + (double(b) * 0.098) + 16.0 );
|
||||
int u = int( -(double(r) * 0.148) - (double(g) * 0.291) + (double(b) * 0.439) + 128.0 );
|
||||
int v = int( +(double(r) * 0.439) - (double(g) * 0.368) - (double(b) * 0.071) + 128.0 );
|
||||
|
||||
//RGB->YCbCr conversion
|
||||
//double lr = 0.2126, lb = 0.0722, lg = (1.0 - lr - lb);
|
||||
//ITU-R Recommendation BT.709
|
||||
//double lr = 0.2126, lg = 0.7152, lb = 0.0722;
|
||||
//int y = int( double(r) * lr + double(g) * lg + double(b) * lb );
|
||||
//int u = int( (double(b) - y) / (2.0 - 2.0 * lb) + 128.0 );
|
||||
//int v = int( (double(r) - y) / (2.0 - 2.0 * lr) + 128.0 );
|
||||
|
@ -276,15 +441,15 @@ public:
|
|||
}
|
||||
|
||||
pVideoXv(VideoXv &self_) : self(self_) {
|
||||
use_child_window = false;
|
||||
xwindow = 0;
|
||||
colormap = 0;
|
||||
device.window = 0;
|
||||
device.colormap = 0;
|
||||
device.port = -1;
|
||||
|
||||
ytable = 0;
|
||||
utable = 0;
|
||||
vtable = 0;
|
||||
|
||||
settings.handle = 0;
|
||||
settings.handle = 0;
|
||||
settings.synchronize = false;
|
||||
}
|
||||
};
|
||||
|
@ -301,4 +466,4 @@ void VideoXv::term() { p.term(); }
|
|||
VideoXv::VideoXv() : p(*new pVideoXv(*this)) {}
|
||||
VideoXv::~VideoXv() { delete &p; }
|
||||
|
||||
} //namespace ruby
|
||||
}
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
#include <../base.hpp>
|
||||
#define MEMORY_CPP
|
||||
|
||||
#include "memory_rw.cpp"
|
||||
|
||||
namespace memory {
|
||||
MMIOAccess mmio;
|
||||
StaticRAM wram(128 * 1024);
|
||||
|
|
|
@ -1,14 +1,7 @@
|
|||
struct Memory {
|
||||
virtual unsigned size() { return 0; }
|
||||
virtual unsigned size() const { return 0; }
|
||||
virtual uint8 read(unsigned addr) = 0;
|
||||
virtual void write(unsigned addr, uint8 data) = 0;
|
||||
|
||||
//deprecated, still used by S-CPU, S-SMP disassemblers
|
||||
enum { WRAP_NONE = 0, WRAP_BANK = 1, WRAP_PAGE = 2 };
|
||||
virtual uint16 read_word(unsigned addr, unsigned wrap = WRAP_NONE);
|
||||
virtual void write_word(unsigned addr, uint16 data, unsigned wrap = WRAP_NONE);
|
||||
virtual uint32 read_long(unsigned addr, unsigned wrap = WRAP_NONE);
|
||||
virtual void write_long(unsigned addr, uint32 data, unsigned wrap = WRAP_NONE);
|
||||
};
|
||||
|
||||
struct MMIO {
|
||||
|
@ -28,7 +21,7 @@ struct UnmappedMMIO : MMIO {
|
|||
|
||||
struct StaticRAM : Memory {
|
||||
uint8* handle() { return data; }
|
||||
unsigned size() { return datasize; }
|
||||
unsigned size() const { return datasize; }
|
||||
|
||||
inline uint8 read(unsigned addr) { return data[addr]; }
|
||||
inline void write(unsigned addr, uint8 n) { data[addr] = n; }
|
||||
|
@ -47,7 +40,7 @@ struct MappedRAM : Memory {
|
|||
void map(uint8 *source, unsigned length) { data = source; datasize = length > 0 ? length : -1U; }
|
||||
void write_protect(bool status) { write_protection = status; }
|
||||
uint8* handle() { return data; }
|
||||
unsigned size() { return datasize; }
|
||||
unsigned size() const { return datasize; }
|
||||
|
||||
inline uint8 read(unsigned addr) { return data[addr]; }
|
||||
inline void write(unsigned addr, uint8 n) { if(!write_protection) data[addr] = n; }
|
||||
|
@ -83,7 +76,7 @@ public:
|
|||
|
||||
alwaysinline uint8 read(unsigned addr) {
|
||||
#if defined(CHEAT_SYSTEM)
|
||||
if(cheat.enabled() && cheat.exists(addr)) {
|
||||
if(cheat.active() && cheat.exists(addr)) {
|
||||
uint8 r;
|
||||
if(cheat.read(addr, r)) return r;
|
||||
}
|
||||
|
@ -112,9 +105,8 @@ public:
|
|||
return 12;
|
||||
}
|
||||
|
||||
virtual void load_cart() = 0;
|
||||
virtual bool load_cart() = 0;
|
||||
virtual void unload_cart() = 0;
|
||||
virtual bool cart_loaded() = 0;
|
||||
|
||||
virtual void power() = 0;
|
||||
virtual void reset() = 0;
|
||||
|
@ -131,12 +123,12 @@ protected:
|
|||
};
|
||||
|
||||
namespace memory {
|
||||
extern MMIOAccess mmio; //S-CPU, S-PPU
|
||||
extern StaticRAM wram; //S-CPU
|
||||
extern StaticRAM apuram; //S-SMP, S-DSP
|
||||
extern StaticRAM vram; //S-PPU
|
||||
extern StaticRAM oam; //S-PPU
|
||||
extern StaticRAM cgram; //S-PPU
|
||||
extern MMIOAccess mmio; //S-CPU, S-PPU
|
||||
extern StaticRAM wram; //S-CPU
|
||||
extern StaticRAM apuram; //S-SMP, S-DSP
|
||||
extern StaticRAM vram; //S-PPU
|
||||
extern StaticRAM oam; //S-PPU
|
||||
extern StaticRAM cgram; //S-PPU
|
||||
|
||||
extern UnmappedMemory memory_unmapped;
|
||||
extern UnmappedMMIO mmio_unmapped;
|
||||
|
|
|
@ -1,81 +0,0 @@
|
|||
#ifdef MEMORY_CPP
|
||||
|
||||
uint16 Memory::read_word(unsigned addr, unsigned wrap) {
|
||||
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;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
void Memory::write_word(unsigned addr, uint16 data, unsigned 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;
|
||||
}
|
||||
}
|
||||
|
||||
uint32 Memory::read_long(unsigned addr, unsigned wrap) {
|
||||
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;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
void Memory::write_long(unsigned addr, uint32 data, unsigned 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;
|
||||
}
|
||||
}
|
||||
|
||||
#endif //ifdef MEMORY_CPP
|
|
@ -6,7 +6,7 @@ void sBus::map_cx4() {
|
|||
}
|
||||
|
||||
void sBus::map_dsp1() {
|
||||
switch(cartridge.info.dsp1_mapper) {
|
||||
switch(cartridge.dsp1_mapper()) {
|
||||
case Cartridge::DSP1LoROM1MB: {
|
||||
map(MapDirect, 0x20, 0x3f, 0x8000, 0xffff, dsp1);
|
||||
map(MapDirect, 0xa0, 0xbf, 0x8000, 0xffff, dsp1);
|
||||
|
@ -51,4 +51,4 @@ void sBus::map_st010() {
|
|||
map(MapDirect, 0xe8, 0xef, 0x0000, 0x0fff, st010);
|
||||
}
|
||||
|
||||
#endif //ifdef SMEMORY_CPP
|
||||
#endif
|
||||
|
|
|
@ -33,13 +33,13 @@ void sBus::map_generic() {
|
|||
} break;
|
||||
|
||||
case Cartridge::SPC7110ROM: {
|
||||
map(MapDirect, 0x00, 0x00, 0x6000, 0x7fff, spc7110); //save RAM w/custom logic
|
||||
map(MapShadow, 0x00, 0x0f, 0x8000, 0xffff, memory::cartrom); //program ROM
|
||||
map(MapDirect, 0x30, 0x30, 0x6000, 0x7fff, spc7110); //save RAM w/custom logic
|
||||
map(MapDirect, 0x50, 0x50, 0x0000, 0xffff, spc7110); //decompression MMIO port
|
||||
map(MapShadow, 0x80, 0x8f, 0x8000, 0xffff, memory::cartrom); //program ROM
|
||||
map(MapLinear, 0xc0, 0xcf, 0x0000, 0xffff, memory::cartrom); //program ROM
|
||||
map(MapDirect, 0xd0, 0xff, 0x0000, 0xffff, spc7110); //MMC-controlled data ROM
|
||||
map(MapDirect, 0x00, 0x00, 0x6000, 0x7fff, spc7110); //save RAM w/custom logic
|
||||
map(MapShadow, 0x00, 0x0f, 0x8000, 0xffff, memory::cartrom); //program ROM
|
||||
map(MapDirect, 0x30, 0x30, 0x6000, 0x7fff, spc7110); //save RAM w/custom logic
|
||||
map(MapDirect, 0x50, 0x50, 0x0000, 0xffff, spc7110); //decompression MMIO port
|
||||
map(MapShadow, 0x80, 0x8f, 0x8000, 0xffff, memory::cartrom); //program ROM
|
||||
map(MapLinear, 0xc0, 0xcf, 0x0000, 0xffff, memory::cartrom); //program ROM
|
||||
map(MapDirect, 0xd0, 0xff, 0x0000, 0xffff, spc7110); //MMC-controlled data ROM
|
||||
} break;
|
||||
|
||||
case Cartridge::BSXROM: {
|
||||
|
@ -97,8 +97,8 @@ void sBus::map_generic_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;
|
||||
if(cartridge.mapper() != Cartridge::LoROM) return;
|
||||
map(MapLinear, 0xf0, 0xff, 0x0000, addr_hi, memory::cartram);
|
||||
}
|
||||
|
||||
#endif //ifdef SMEMORY_CPP
|
||||
#endif
|
||||
|
|
|
@ -18,4 +18,4 @@ void sBus::map_system() {
|
|||
map(MapLinear, 0x7e, 0x7f, 0x0000, 0xffff, memory::wram);
|
||||
}
|
||||
|
||||
#endif //ifdef SMEMORY_CPP
|
||||
#endif
|
||||
|
|
|
@ -17,38 +17,29 @@ void sBus::reset() {
|
|||
set_speed(false);
|
||||
}
|
||||
|
||||
void sBus::load_cart() {
|
||||
if(is_cart_loaded == true) return;
|
||||
bool sBus::load_cart() {
|
||||
if(cartridge.loaded() == true) return false;
|
||||
|
||||
map_reset();
|
||||
map_generic();
|
||||
map_system();
|
||||
|
||||
if(cartridge.info.cx4) map_cx4();
|
||||
if(cartridge.info.dsp1) map_dsp1();
|
||||
if(cartridge.info.dsp2) map_dsp2();
|
||||
if(cartridge.info.dsp3) map_dsp3();
|
||||
if(cartridge.info.dsp4) map_dsp4();
|
||||
if(cartridge.info.obc1) map_obc1();
|
||||
if(cartridge.info.st010) map_st010();
|
||||
if(cartridge.has_cx4()) map_cx4();
|
||||
if(cartridge.has_dsp1()) map_dsp1();
|
||||
if(cartridge.has_dsp2()) map_dsp2();
|
||||
if(cartridge.has_dsp3()) map_dsp3();
|
||||
if(cartridge.has_dsp4()) map_dsp4();
|
||||
if(cartridge.has_obc1()) map_obc1();
|
||||
if(cartridge.has_st010()) map_st010();
|
||||
|
||||
is_cart_loaded = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void sBus::unload_cart() {
|
||||
if(is_cart_loaded == false) return;
|
||||
|
||||
is_cart_loaded = false;
|
||||
}
|
||||
|
||||
bool sBus::cart_loaded() {
|
||||
return is_cart_loaded;
|
||||
}
|
||||
|
||||
sBus::sBus() {
|
||||
is_cart_loaded = false;
|
||||
}
|
||||
|
||||
sBus::~sBus() {
|
||||
unload_cart();
|
||||
}
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
class sBus : public Bus {
|
||||
public:
|
||||
void load_cart();
|
||||
bool load_cart();
|
||||
void unload_cart();
|
||||
bool cart_loaded();
|
||||
|
||||
void power();
|
||||
void reset();
|
||||
|
@ -11,8 +10,6 @@ public:
|
|||
~sBus();
|
||||
|
||||
private:
|
||||
bool is_cart_loaded;
|
||||
|
||||
void map_reset();
|
||||
void map_system();
|
||||
void map_generic();
|
||||
|
|
|
@ -70,7 +70,7 @@ void bPPU::render_line_mode7(uint8 bg, uint8 pri0_pos, uint8 pri1_pos) {
|
|||
py >>= 8;
|
||||
|
||||
switch(regs.mode7_repeat) {
|
||||
case 0: //screen repitition outside of screen area
|
||||
case 0: //screen repetition outside of screen area
|
||||
case 1: { //same as case 0
|
||||
px &= 1023;
|
||||
py &= 1023;
|
||||
|
|
|
@ -39,7 +39,7 @@ void bPPU::build_sprite_list() {
|
|||
}
|
||||
|
||||
sprite_list[i].x = (x << 8) + tableA[0];
|
||||
sprite_list[i].y = tableA[1] + 1;
|
||||
sprite_list[i].y = (tableA[1] + 1) & 0xff;
|
||||
sprite_list[i].character = tableA[2];
|
||||
sprite_list[i].vflip = !!(tableA[3] & 0x80);
|
||||
sprite_list[i].hflip = !!(tableA[3] & 0x40);
|
||||
|
@ -66,7 +66,7 @@ bool bPPU::is_sprite_on_scanline() {
|
|||
void bPPU::load_oam_tiles() {
|
||||
uint16 tile_width = spr->width >> 3;
|
||||
int x = spr->x;
|
||||
int y = line - spr->y;
|
||||
int y = (line - spr->y) & 0xff;
|
||||
if(regs.oam_interlace == true) {
|
||||
y <<= 1;
|
||||
}
|
||||
|
|
|
@ -25,6 +25,8 @@ void PPU::frame() {
|
|||
}
|
||||
|
||||
void PPU::power() {
|
||||
ppu1_version = snes.config.ppu1.version;
|
||||
ppu2_version = snes.config.ppu2.version;
|
||||
}
|
||||
|
||||
void PPU::reset() {
|
||||
|
@ -38,9 +40,6 @@ PPU::PPU() {
|
|||
status.frames_updated = false;
|
||||
status.frames_rendered = 0;
|
||||
status.frames_executed = 0;
|
||||
|
||||
ppu1_version = 1;
|
||||
ppu2_version = 3;
|
||||
}
|
||||
|
||||
PPU::~PPU() {
|
||||
|
|
|
@ -38,7 +38,7 @@ GZReader::GZReader(const char *fn) : gp(0) {
|
|||
#if !defined(_WIN32)
|
||||
fp = fopen(fn, "rb");
|
||||
#else
|
||||
fp = _wfopen(utf16(fn), L"rb");
|
||||
fp = _wfopen(utf16_t(fn), L"rb");
|
||||
#endif
|
||||
if(!fp) return;
|
||||
|
||||
|
|
|
@ -1,17 +1,12 @@
|
|||
/*****
|
||||
* SNES Interface class
|
||||
*
|
||||
* Interfaces SNES core with platform-specific functionality
|
||||
* (video, audio, input, ...)
|
||||
*****/
|
||||
//====================
|
||||
//SNES interface class
|
||||
//====================
|
||||
//Interfaces SNES core with platform-specific functionality (video, audio, input, ...)
|
||||
|
||||
class SNESInterface {
|
||||
public:
|
||||
void video_refresh(uint16_t *data, unsigned pitch, unsigned *line, unsigned width, unsigned height);
|
||||
|
||||
void audio_sample(uint16_t l_sample, uint16_t r_sample);
|
||||
|
||||
function<bool ()> input_ready;
|
||||
void input_poll();
|
||||
int16_t input_poll(unsigned deviceid, unsigned id);
|
||||
|
||||
|
|
|
@ -80,20 +80,20 @@ void SNES::power() {
|
|||
ppu.power();
|
||||
bus.power();
|
||||
|
||||
if(expansion() == ExpansionBSX) bsxbase.power();
|
||||
if(expansion() == ExpansionBSX) bsxbase.power();
|
||||
if(cartridge.mode() == Cartridge::ModeBsx) bsxcart.power();
|
||||
if(cartridge.bsx_flash_loaded()) bsxflash.power();
|
||||
|
||||
if(cartridge.info.bsxcart) bsxcart.power();
|
||||
if(cartridge.info.bsxflash) bsxflash.power();
|
||||
if(cartridge.info.srtc) srtc.power();
|
||||
if(cartridge.info.sdd1) sdd1.power();
|
||||
if(cartridge.info.spc7110) spc7110.power();
|
||||
if(cartridge.info.cx4) cx4.power();
|
||||
if(cartridge.info.dsp1) dsp1.power();
|
||||
if(cartridge.info.dsp2) dsp2.power();
|
||||
if(cartridge.info.dsp3) dsp3.power();
|
||||
if(cartridge.info.dsp4) dsp4.power();
|
||||
if(cartridge.info.obc1) obc1.power();
|
||||
if(cartridge.info.st010) st010.power();
|
||||
if(cartridge.has_srtc()) srtc.power();
|
||||
if(cartridge.has_sdd1()) sdd1.power();
|
||||
if(cartridge.has_spc7110()) spc7110.power();
|
||||
if(cartridge.has_cx4()) cx4.power();
|
||||
if(cartridge.has_dsp1()) dsp1.power();
|
||||
if(cartridge.has_dsp2()) dsp2.power();
|
||||
if(cartridge.has_dsp3()) dsp3.power();
|
||||
if(cartridge.has_dsp4()) dsp4.power();
|
||||
if(cartridge.has_obc1()) obc1.power();
|
||||
if(cartridge.has_st010()) st010.power();
|
||||
|
||||
for(unsigned i = 0x2100; i <= 0x213f; i++) memory::mmio.map(i, ppu);
|
||||
for(unsigned i = 0x2140; i <= 0x217f; i++) memory::mmio.map(i, cpu);
|
||||
|
@ -102,20 +102,20 @@ void SNES::power() {
|
|||
for(unsigned i = 0x4200; i <= 0x421f; i++) memory::mmio.map(i, cpu);
|
||||
for(unsigned i = 0x4300; i <= 0x437f; i++) memory::mmio.map(i, cpu);
|
||||
|
||||
if(expansion() == ExpansionBSX) bsxbase.enable();
|
||||
if(expansion() == ExpansionBSX) bsxbase.enable();
|
||||
if(cartridge.mode() == Cartridge::ModeBsx) bsxcart.enable();
|
||||
if(cartridge.bsx_flash_loaded()) bsxflash.enable();
|
||||
|
||||
if(cartridge.info.bsxcart) bsxcart.enable();
|
||||
if(cartridge.info.bsxflash) bsxflash.enable();
|
||||
if(cartridge.info.srtc) srtc.enable();
|
||||
if(cartridge.info.sdd1) sdd1.enable();
|
||||
if(cartridge.info.spc7110) spc7110.enable();
|
||||
if(cartridge.info.cx4) cx4.enable();
|
||||
if(cartridge.info.dsp1) dsp1.enable();
|
||||
if(cartridge.info.dsp2) dsp2.enable();
|
||||
if(cartridge.info.dsp3) dsp3.enable();
|
||||
if(cartridge.info.dsp4) dsp4.enable();
|
||||
if(cartridge.info.obc1) obc1.enable();
|
||||
if(cartridge.info.st010) st010.enable();
|
||||
if(cartridge.has_srtc()) srtc.enable();
|
||||
if(cartridge.has_sdd1()) sdd1.enable();
|
||||
if(cartridge.has_spc7110()) spc7110.enable();
|
||||
if(cartridge.has_cx4()) cx4.enable();
|
||||
if(cartridge.has_dsp1()) dsp1.enable();
|
||||
if(cartridge.has_dsp2()) dsp2.enable();
|
||||
if(cartridge.has_dsp3()) dsp3.enable();
|
||||
if(cartridge.has_dsp4()) dsp4.enable();
|
||||
if(cartridge.has_obc1()) obc1.enable();
|
||||
if(cartridge.has_st010()) st010.enable();
|
||||
|
||||
input.port_set_device(0, snes.config.controller_port1);
|
||||
input.port_set_device(1, snes.config.controller_port2);
|
||||
|
@ -133,20 +133,20 @@ void SNES::reset() {
|
|||
ppu.reset();
|
||||
bus.reset();
|
||||
|
||||
if(expansion() == ExpansionBSX) bsxbase.reset();
|
||||
if(expansion() == ExpansionBSX) bsxbase.reset();
|
||||
if(cartridge.mode() == Cartridge::ModeBsx) bsxcart.reset();
|
||||
if(cartridge.bsx_flash_loaded()) bsxflash.reset();
|
||||
|
||||
if(cartridge.info.bsxcart) bsxcart.reset();
|
||||
if(cartridge.info.bsxflash) bsxflash.reset();
|
||||
if(cartridge.info.srtc) srtc.reset();
|
||||
if(cartridge.info.sdd1) sdd1.reset();
|
||||
if(cartridge.info.spc7110) spc7110.reset();
|
||||
if(cartridge.info.cx4) cx4.reset();
|
||||
if(cartridge.info.dsp1) dsp1.reset();
|
||||
if(cartridge.info.dsp2) dsp2.reset();
|
||||
if(cartridge.info.dsp3) dsp3.reset();
|
||||
if(cartridge.info.dsp4) dsp4.reset();
|
||||
if(cartridge.info.obc1) obc1.reset();
|
||||
if(cartridge.info.st010) st010.reset();
|
||||
if(cartridge.has_srtc()) srtc.reset();
|
||||
if(cartridge.has_sdd1()) sdd1.reset();
|
||||
if(cartridge.has_spc7110()) spc7110.reset();
|
||||
if(cartridge.has_cx4()) cx4.reset();
|
||||
if(cartridge.has_dsp1()) dsp1.reset();
|
||||
if(cartridge.has_dsp2()) dsp2.reset();
|
||||
if(cartridge.has_dsp3()) dsp3.reset();
|
||||
if(cartridge.has_dsp4()) dsp4.reset();
|
||||
if(cartridge.has_obc1()) obc1.reset();
|
||||
if(cartridge.has_st010()) st010.reset();
|
||||
|
||||
input.port_set_device(0, snes.config.controller_port1);
|
||||
input.port_set_device(1, snes.config.controller_port2);
|
||||
|
@ -184,16 +184,18 @@ SNES::SNES() : snes_region(NTSC), snes_expansion(ExpansionNone) {
|
|||
config.file.autodetect_type = false;
|
||||
config.file.bypass_patch_crc32 = false;
|
||||
|
||||
config.path.base = "";
|
||||
config.path.user = "";
|
||||
config.path.rom = "";
|
||||
config.path.save = "";
|
||||
config.path.patch = "";
|
||||
config.path.cheat = "";
|
||||
config.path.exportdata = "";
|
||||
config.path.bsx = "";
|
||||
config.path.st = "";
|
||||
config.path.base = "";
|
||||
config.path.user = "";
|
||||
config.path.current = "";
|
||||
config.path.rom = "";
|
||||
config.path.save = "";
|
||||
config.path.patch = "";
|
||||
config.path.cheat = "";
|
||||
config.path.data = "";
|
||||
config.path.bsx = "";
|
||||
config.path.st = "";
|
||||
|
||||
config.cpu.version = 2;
|
||||
config.cpu.ntsc_clock_rate = 21477272;
|
||||
config.cpu.pal_clock_rate = 21281370;
|
||||
config.cpu.alu_mul_delay = 2;
|
||||
|
@ -202,4 +204,7 @@ SNES::SNES() : snes_region(NTSC), snes_expansion(ExpansionNone) {
|
|||
|
||||
config.smp.ntsc_clock_rate = 32041 * 768;
|
||||
config.smp.pal_clock_rate = 32041 * 768;
|
||||
|
||||
config.ppu1.version = 1;
|
||||
config.ppu2.version = 3;
|
||||
}
|
||||
|
|
|
@ -22,12 +22,15 @@ public:
|
|||
} file;
|
||||
|
||||
struct Path {
|
||||
string base, user;
|
||||
string rom, save, patch, cheat, exportdata;
|
||||
string base; //binary path
|
||||
string user; //user profile path (bsnes.cfg, ...)
|
||||
string current; //current working directory (path to currently loaded cartridge)
|
||||
string rom, save, patch, cheat, data;
|
||||
string bsx, st;
|
||||
} path;
|
||||
|
||||
struct CPU {
|
||||
unsigned version;
|
||||
unsigned ntsc_clock_rate;
|
||||
unsigned pal_clock_rate;
|
||||
unsigned alu_mul_delay;
|
||||
|
@ -39,6 +42,14 @@ public:
|
|||
unsigned ntsc_clock_rate;
|
||||
unsigned pal_clock_rate;
|
||||
} smp;
|
||||
|
||||
struct PPU1 {
|
||||
unsigned version;
|
||||
} ppu1;
|
||||
|
||||
struct PPU2 {
|
||||
unsigned version;
|
||||
} ppu2;
|
||||
} config;
|
||||
|
||||
//system functions
|
||||
|
|
|
@ -47,7 +47,7 @@ void Tracer::trace_smpop() {
|
|||
|
||||
void Tracer::enable(bool en) {
|
||||
if(en == true && enabled() == false) {
|
||||
fp = fopen(Cartridge::filepath("trace.log", snes.config.path.exportdata), "wb");
|
||||
fp = fopen(Cartridge::filepath("trace.log", snes.config.path.data), "wb");
|
||||
} else if(en == false && enabled() == true) {
|
||||
fclose(fp);
|
||||
fp = 0;
|
||||
|
|
|
@ -1,36 +0,0 @@
|
|||
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
|
|
@ -0,0 +1,31 @@
|
|||
##############################
|
||||
### platform configuration ###
|
||||
##############################
|
||||
|
||||
objects := main hiro $(if $(call streq,$(platform),win),resource) $(objects)
|
||||
|
||||
ifeq ($(platform),x)
|
||||
link += `pkg-config --libs gtk+-2.0`
|
||||
link += $(call mklib,Xtst)
|
||||
hiroflags = `pkg-config --cflags gtk+-2.0`
|
||||
else ifeq ($(platform),win)
|
||||
link += $(call mklib,comctl32)
|
||||
link += $(call mklib,comdlg32)
|
||||
hiroflags =
|
||||
endif
|
||||
|
||||
#############
|
||||
### rules ###
|
||||
#############
|
||||
|
||||
obj/main.$(obj): $(ui)/main.cpp $(ui)/* $(ui)/base/* $(ui)/loader/* $(ui)/settings/* $(ui)/event/*
|
||||
obj/hiro.$(obj): lib/hiro/hiro.cpp lib/hiro/*
|
||||
$(call compile,$(hiroflags))
|
||||
obj/resource.$(obj): $(ui)/bsnes.rc; windres $(ui)/bsnes.rc obj/resource.$(obj)
|
||||
|
||||
###############
|
||||
### targets ###
|
||||
###############
|
||||
|
||||
ui_build:;
|
||||
ui_clean:;
|
|
@ -1,4 +1,4 @@
|
|||
#define IDI_APP_ICON 100
|
||||
|
||||
1 24 "ui/bsnes.Manifest"
|
||||
1 24 "data/bsnes.Manifest"
|
||||
IDI_APP_ICON ICON DISCARDABLE "data/bsnes.ico"
|
|
@ -79,7 +79,6 @@ public:
|
|||
struct Misc {
|
||||
bool start_in_fullscreen_mode;
|
||||
unsigned window_opacity;
|
||||
unsigned cheat_autosort;
|
||||
bool show_advanced_options;
|
||||
} misc;
|
||||
|
||||
|
@ -96,13 +95,13 @@ public:
|
|||
attach(snes.config.file.autodetect_type = false, "file.autodetect_type", "Detect filetype by header, rather than file extension");
|
||||
attach(snes.config.file.bypass_patch_crc32 = false, "file.bypass_patch_crc32", "Apply UPS patches even when checksum match fails");
|
||||
|
||||
attach(snes.config.path.rom = "", "path.rom");
|
||||
attach(snes.config.path.save = "", "path.save");
|
||||
attach(snes.config.path.patch = "", "path.patch");
|
||||
attach(snes.config.path.cheat = "", "path.cheat");
|
||||
attach(snes.config.path.exportdata = "", "path.exportdata");
|
||||
attach(snes.config.path.bsx = "", "path.bsx");
|
||||
attach(snes.config.path.st = "", "path.st");
|
||||
attach(snes.config.path.rom = "", "path.rom");
|
||||
attach(snes.config.path.save = "", "path.save");
|
||||
attach(snes.config.path.patch = "", "path.patch");
|
||||
attach(snes.config.path.cheat = "", "path.cheat");
|
||||
attach(snes.config.path.data = "", "path.data");
|
||||
attach(snes.config.path.bsx = "", "path.bsx");
|
||||
attach(snes.config.path.st = "", "path.st");
|
||||
|
||||
attach(snes.config.cpu.ntsc_clock_rate = 21477272, "cpu.ntsc_clock_rate");
|
||||
attach(snes.config.cpu.pal_clock_rate = 21281370, "cpu.pal_clock_rate");
|
||||
|
@ -242,7 +241,6 @@ public:
|
|||
|
||||
attach(misc.start_in_fullscreen_mode = false, "misc.start_in_fullscreen_mode");
|
||||
attach(misc.window_opacity = 100, "misc.window_opacity", "Translucency percentage of helper windows (50%-100%)");
|
||||
attach(misc.cheat_autosort = false, "misc.cheat_autosort");
|
||||
attach(misc.show_advanced_options = false, "misc.show_advanced_options", "Enable developer-oriented GUI options");
|
||||
}
|
||||
|
|
@ -1,23 +1,23 @@
|
|||
void export_memory() {
|
||||
file fp;
|
||||
|
||||
fp.open(Cartridge::filepath("wram.bin", snes.config.path.exportdata), file::mode_write);
|
||||
fp.open(Cartridge::filepath("wram.bin", snes.config.path.data), file::mode_write);
|
||||
for(unsigned i = 0; i < memory::wram.size(); i++) fp.write(memory::wram[i]);
|
||||
fp.close();
|
||||
|
||||
fp.open(Cartridge::filepath("apuram.bin", snes.config.path.exportdata), file::mode_write);
|
||||
fp.open(Cartridge::filepath("apuram.bin", snes.config.path.data), file::mode_write);
|
||||
for(unsigned i = 0; i < memory::apuram.size(); i++) fp.write(memory::apuram[i]);
|
||||
fp.close();
|
||||
|
||||
fp.open(Cartridge::filepath("vram.bin", snes.config.path.exportdata), file::mode_write);
|
||||
fp.open(Cartridge::filepath("vram.bin", snes.config.path.data), file::mode_write);
|
||||
for(unsigned i = 0; i < memory::vram.size(); i++) fp.write(memory::vram[i]);
|
||||
fp.close();
|
||||
|
||||
fp.open(Cartridge::filepath("oam.bin", snes.config.path.exportdata), file::mode_write);
|
||||
fp.open(Cartridge::filepath("oam.bin", snes.config.path.data), file::mode_write);
|
||||
for(unsigned i = 0; i < memory::oam.size(); i++) fp.write(memory::oam[i]);
|
||||
fp.close();
|
||||
|
||||
fp.open(Cartridge::filepath("cgram.bin", snes.config.path.exportdata), file::mode_write);
|
||||
fp.open(Cartridge::filepath("cgram.bin", snes.config.path.data), file::mode_write);
|
||||
for(unsigned i = 0; i < memory::cgram.size(); i++) fp.write(memory::cgram[i]);
|
||||
fp.close();
|
||||
|
|
@ -155,20 +155,20 @@ void modify_system_state(system_state_t state) {
|
|||
|
||||
status.flush();
|
||||
string t = translate["Loaded $."];
|
||||
replace(t, "$", cartridge.info.filename);
|
||||
t.replace("$", cartridge.name());
|
||||
status.enqueue(t);
|
||||
if(cartridge.info.patched) status.enqueue(translate["UPS patch applied."]);
|
||||
if(cartridge.patched()) status.enqueue(translate["UPS patch applied."]);
|
||||
|
||||
//warn if unsupported hardware detected
|
||||
string message;
|
||||
message = translate["Warning: unsupported $ chip detected."];
|
||||
if(cartridge.info.superfx) { replace(message, "$", "SuperFX"); status.enqueue(message); }
|
||||
if(cartridge.info.sa1) { replace(message, "$", "SA-1"); status.enqueue(message); }
|
||||
if(cartridge.info.st011) { replace(message, "$", "ST011"); status.enqueue(message); }
|
||||
if(cartridge.info.st018) { replace(message, "$", "ST018"); status.enqueue(message); }
|
||||
if(cartridge.has_superfx()) { message.replace("$", "SuperFX"); status.enqueue(message); }
|
||||
if(cartridge.has_sa1()) { message.replace("$", "SA-1"); status.enqueue(message); }
|
||||
if(cartridge.has_st011()) { message.replace("$", "ST011"); status.enqueue(message); }
|
||||
if(cartridge.has_st018()) { message.replace("$", "ST018"); status.enqueue(message); }
|
||||
|
||||
message = translate["Warning: partially supported $ chip detected."];
|
||||
if(cartridge.info.dsp3) { replace(message, "$", "DSP-3"); status.enqueue(message); }
|
||||
if(cartridge.has_dsp3()) { message.replace("$", "DSP-3"); status.enqueue(message); }
|
||||
} break;
|
||||
|
||||
case UnloadCart: {
|
||||
|
@ -180,7 +180,7 @@ void modify_system_state(system_state_t state) {
|
|||
|
||||
status.flush();
|
||||
string t = translate["Unloaded $."];
|
||||
replace(t, "$", cartridge.info.filename);
|
||||
t.replace("$", cartridge.name());
|
||||
status.enqueue(t);
|
||||
} break;
|
||||
|
||||
|
@ -368,16 +368,16 @@ bool load_cart(char *fn) {
|
|||
|
||||
lstring dir;
|
||||
strcpy(fn, "");
|
||||
strcpy(dir[0], snes.config.path.rom);
|
||||
replace(dir[0], "\\", "/");
|
||||
if(strlen(dir[0]) && !strend(dir[0], "/")) strcat(dir[0], "/");
|
||||
dir[0] = snes.config.path.rom;
|
||||
dir[0].replace("\\", "/");
|
||||
if(dir[0].length() && !strend(dir[0], "/")) dir[0].append("/");
|
||||
|
||||
//append base path if rom path is relative
|
||||
if(strbegin(dir[0], "./")) {
|
||||
ltrim(dir[0], "./");
|
||||
strcpy(dir[1], dir[0]);
|
||||
strcpy(dir[0], snes.config.path.base);
|
||||
strcat(dir[0], dir[1]);
|
||||
dir[1].assign(dir[0]);
|
||||
dir[0].assign(snes.config.path.base);
|
||||
dir[0].append(dir[1]);
|
||||
}
|
||||
|
||||
return hiro().file_open(0, fn,
|
||||
|
@ -404,15 +404,12 @@ void load_cart() {
|
|||
}
|
||||
|
||||
void load_image(const char *filename) {
|
||||
Cartridge::cartinfo_t cartinfo;
|
||||
if(!cartridge.inspect_image(cartinfo, filename)) return;
|
||||
|
||||
switch(cartinfo.type) {
|
||||
switch(cartridge.detect_image_type(filename)) {
|
||||
case Cartridge::TypeNormal: {
|
||||
load_cart_normal(filename);
|
||||
} break;
|
||||
|
||||
case Cartridge::TypeBSC: {
|
||||
case Cartridge::TypeBsxSlotted: {
|
||||
window_bsxloader.mode = BSXLoaderWindow::ModeBSC;
|
||||
window_bsxloader.set_text(translate["Load BS-X Slotted Cartridge"]);
|
||||
window_bsxloader.tbase.set_text(filename);
|
||||
|
@ -421,7 +418,7 @@ void load_image(const char *filename) {
|
|||
window_bsxloader.focus();
|
||||
} break;
|
||||
|
||||
case Cartridge::TypeBSXBIOS: {
|
||||
case Cartridge::TypeBsxBios: {
|
||||
window_bsxloader.mode = BSXLoaderWindow::ModeBSX;
|
||||
window_bsxloader.set_text(translate["Load BS-X Cartridge"]);
|
||||
window_bsxloader.tbase.set_text(filename);
|
||||
|
@ -430,7 +427,7 @@ void load_image(const char *filename) {
|
|||
window_bsxloader.focus();
|
||||
} break;
|
||||
|
||||
case Cartridge::TypeBSX: {
|
||||
case Cartridge::TypeBsx: {
|
||||
window_bsxloader.mode = BSXLoaderWindow::ModeBSX;
|
||||
window_bsxloader.set_text(translate["Load BS-X Cartridge"]);
|
||||
window_bsxloader.tbase.set_text(snes.config.path.bsx);
|
||||
|
@ -439,7 +436,7 @@ void load_image(const char *filename) {
|
|||
window_bsxloader.focus();
|
||||
} break;
|
||||
|
||||
case Cartridge::TypeSufamiTurboBIOS: {
|
||||
case Cartridge::TypeSufamiTurboBios: {
|
||||
window_stloader.tbase.set_text(filename);
|
||||
window_stloader.tslotA.set_text("");
|
||||
window_stloader.tslotB.set_text("");
|
||||
|
@ -461,7 +458,7 @@ void load_cart_normal(const char *base) {
|
|||
if(!base || !*base) return;
|
||||
|
||||
unload_cart();
|
||||
cartridge.load_cart_normal(base);
|
||||
cartridge.load_normal(base);
|
||||
if(cartridge.loaded() == false) return;
|
||||
modify_system_state(LoadCart);
|
||||
}
|
||||
|
@ -470,7 +467,7 @@ void load_cart_bsc(const char *base, const char *slot) {
|
|||
if(!base || !*base) return;
|
||||
|
||||
unload_cart();
|
||||
cartridge.load_cart_bsc(base, slot);
|
||||
cartridge.load_bsx_slotted(base, slot);
|
||||
if(cartridge.loaded() == false) return;
|
||||
modify_system_state(LoadCart);
|
||||
}
|
||||
|
@ -479,7 +476,7 @@ void load_cart_bsx(const char *base, const char *slot) {
|
|||
if(!base || !*base) return;
|
||||
|
||||
unload_cart();
|
||||
cartridge.load_cart_bsx(base, slot);
|
||||
cartridge.load_bsx(base, slot);
|
||||
if(cartridge.loaded() == false) return;
|
||||
modify_system_state(LoadCart);
|
||||
}
|
||||
|
@ -488,7 +485,7 @@ void load_cart_st(const char *base, const char *slotA, const char *slotB) {
|
|||
if(!base || !*base) return;
|
||||
|
||||
unload_cart();
|
||||
cartridge.load_cart_st(base, slotA, slotB);
|
||||
cartridge.load_sufami_turbo(base, slotA, slotB);
|
||||
if(cartridge.loaded() == false) return;
|
||||
modify_system_state(LoadCart);
|
||||
}
|
|
@ -51,7 +51,7 @@ void SNESInterface::audio_sample(uint16 l_sample, uint16 r_sample) {
|
|||
//input
|
||||
|
||||
void SNESInterface::input_poll() {
|
||||
if(input_ready && input_ready() == false) {
|
||||
if(window_main.input_ready() == false) {
|
||||
input_manager.clear();
|
||||
} else {
|
||||
input_manager.poll();
|
|
@ -0,0 +1,12 @@
|
|||
namespace resource {
|
||||
|
||||
static uint8_t *icon48;
|
||||
static uint8_t *controller;
|
||||
|
||||
void init() {
|
||||
//note: resources were removed, as hiro port is deprecated
|
||||
icon48 = new(zeromemory) uint8_t[48 * 48 * 4];
|
||||
controller = new(zeromemory) uint8_t[372 * 178 * 4];
|
||||
}
|
||||
|
||||
} //namespace resource
|
|
@ -69,7 +69,6 @@ void AdvancedWindow::load() {
|
|||
if(strbegin(name, "input.justifier")) continue;
|
||||
if(strbegin(name, "input.gui")) continue;
|
||||
if(strbegin(name, "input.debugger")) continue;
|
||||
if(name == "misc.cheat_autosort") continue;
|
||||
|
||||
list.add_item(string()
|
||||
<< name << "\t"
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue