mirror of
https://github.com/bsnes-emu/bsnes.git
synced 2025-09-13 23:22:17 +02:00
Compare commits
18 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
def86470f4 | ||
|
3e7578761a | ||
|
45feae8f75 | ||
|
4d10c36870 | ||
|
cca8164005 | ||
|
0f83e39d5c | ||
|
ebbcc998d0 | ||
|
78da6946c6 | ||
|
4d31452bca | ||
|
eb1eca4a6d | ||
|
07c9df31a6 | ||
|
c64232a479 | ||
|
85b08fd24b | ||
|
3b3214d1be | ||
|
b5a38d2a07 | ||
|
94004f86ec | ||
|
148bbddb1a | ||
|
e5b2e87ff8 |
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>
|
||||
|
BIN
src/bsnes.lnk
Normal file
BIN
src/bsnes.lnk
Normal file
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
|
244
src/cart/cart_loader.cpp
Normal file
244
src/cart/cart_loader.cpp
Normal file
@@ -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);
|
||||
|
8
src/data/bsnes.desktop
Normal file
8
src/data/bsnes.desktop
Normal file
@@ -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
81
src/data/documentation.html
Normal file
81
src/data/documentation.html
Normal file
@@ -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"
|
||||
};
|
BIN
src/data/joypad.png
Normal file
BIN
src/data/joypad.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 190 KiB |
87
src/data/license.html
Normal file
87
src/data/license.html
Normal file
@@ -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>
|
BIN
src/data/logo.png
Normal file
BIN
src/data/logo.png
Normal file
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;
|
||||
|
||||
|
45
src/lib/nall/property.hpp
Normal file
45
src/lib/nall/property.hpp
Normal file
@@ -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;
|
||||
|
103
src/lib/nall/string/core.cpp
Normal file
103
src/lib/nall/string/core.cpp
Normal file
@@ -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
|
||||
|
74
src/lib/nall/string/utility.cpp
Normal file
74
src/lib/nall/string/utility.cpp
Normal file
@@ -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
|
31
src/ui_hiro/Makefile
Normal file
31
src/ui_hiro/Makefile
Normal file
@@ -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();
|
12
src/ui_hiro/resource.cpp
Normal file
12
src/ui_hiro/resource.cpp
Normal file
@@ -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
Reference in New Issue
Block a user