Initial Qt port. Deps: nall and ruby are included.

This commit is contained in:
Marcos Medeiros 2014-07-03 02:59:57 +00:00
parent 77fefeb08d
commit 92307f31a3
240 changed files with 28200 additions and 2 deletions

View File

@ -0,0 +1,940 @@
#===============================================================================
# FINAL BURN ALPHA QT
#===============================================================================
QT += widgets multimedia
TARGET = fbaqt
linux:QT += x11extras
#===============================================================================
# DRIVERS
#===============================================================================
DRV_CAPCOM = true
DRV_CAVE = true
DRV_CPS3 = true
DRV_DATAEAST = true
DRV_GALAXIAN = true
DRV_IREM = true
DRV_KONAMI = true
DRV_SMS = true
DRV_MEGADRIVE = true
DRV_NEOGEO = true
DRV_PCE = true
DRV_PGM = true
DRV_PRE90S = true
DRV_PST90S = true
DRV_SEGA = true
DRV_SNES = true
DRV_TAITO = true
DRV_TOAPLAN = true
DRV_PSIKYO = true
#===============================================================================
# DEPENDENCIES
#===============================================================================
#-------------------------------------------------------------------------------
# for Linux, absolute paths
#-------------------------------------------------------------------------------
SRC = $$system(readlink -m $$PWD/../../src)
SCRIPTS = $$SRC/dep/scripts
GEN = $$SRC/dep/generated
# We need ld
FBA_LD = ld
#-------------------------------------------------------------------------------
# Additional include paths
#-------------------------------------------------------------------------------
INCLUDEPATH += \
$$GEN \
$$SRC/dep/qtcreator \
$$SRC/burn \
$$SRC/burn/snd \
$$SRC/burn/devices \
$$SRC/burn/drv/capcom \
$$SRC/burn/drv/cave \
$$SRC/burn/drv/konami \
$$SRC/burn/drv/neogeo \
$$SRC/burn/drv/pgm \
$$SRC/burn/drv/pre90s \
$$SRC/burn/drv/psikyo \
$$SRC/burn/drv/pst90s \
$$SRC/burn/drv/sega \
$$SRC/burn/drv/taito \
$$SRC/burn/drv/toaplan \
$$SRC/burner \
$$SRC/burner/qt \
$$SRC/cpu \
$$SRC/cpu/i8039 \
$$SRC/cpu/konami \
$$SRC/cpu/m68k \
$$SRC/intf \
$$SRC/intf/video \
$$SRC/intf/video/scalers \
$$SRC/intf/input \
$$SRC/intf/audio \
$$SRC/intf/cd \
$$SRC/dep/libs \
$$SRC/dep/libs/libpng \
$$SRC/dep/libs/zlib \
DEFINES += BUILD_QT \
LSB_FIRST \
"__fastcall=" \
"_fastcall=" \
WITH_QTCREATOR \
INCLUDE_LIB_PNGH
# no warnings...
QMAKE_CXXFLAGS += -w
QMAKE_CFLAGS += -w
#-------------------------------------------------------------------------------
# C++11
#-------------------------------------------------------------------------------
CONFIG += c++11
#-------------------------------------------------------------------------------
# src/dep/generated
#-------------------------------------------------------------------------------
GENERATED.target = $$GEN/empty
GENERATED.commands = \
@echo "Creating generated directory..."; \
cd $$SRC/dep; \
mkdir generated; \
touch generated/empty
#-------------------------------------------------------------------------------
# src/dep/generated/ctv.h
#-------------------------------------------------------------------------------
CTV_HEADER.depends = GENERATED
CTV_HEADER.target = $$GEN/ctv.h
CTV_HEADER.commands = \
@echo "Building ctv..."; \
cd $$SRC/burn/drv/capcom; \
$$QMAKE_CXX ctv_make.cpp -o ctv_make; \
./ctv_make > $$CTV_HEADER.target; \
rm ctv_make
#-------------------------------------------------------------------------------
# perl scripts
#-------------------------------------------------------------------------------
# cave_sprite_func.h
CAVE_SPRFUNC_HEADER.depends = GENERATED
CAVE_SPRFUNC_HEADER.target = $$GEN/cave_sprite_func.h
CAVE_SPRFUNC_HEADER.commands = \
@echo "Generating cave_sprite_func.h"; \
perl $$SCRIPTS/cave_sprite_func.pl -o $$CAVE_SPRFUNC_HEADER.target;
# cave_tile_func.h
CAVE_TILEFUNC_HEADER.depends = GENERATED
CAVE_TILEFUNC_HEADER.target = $$GEN/cave_tile_func.h
CAVE_TILEFUNC_HEADER.commands = \
@echo "Generating cave_tile_func.h"; \
perl $$SCRIPTS/cave_tile_func.pl -o $$CAVE_TILEFUNC_HEADER.target;
# neo_sprite_func.h
NEO_SPRFUNC_HEADER.depends = GENERATED
NEO_SPRFUNC_HEADER.target = $$GEN/neo_sprite_func.h
NEO_SPRFUNC_HEADER.commands = \
@echo "Generating neo_sprite_func.h"; \
perl $$SCRIPTS/neo_sprite_func.pl -o $$NEO_SPRFUNC_HEADER.target;
# psikyo_tile_func.h
PSIKYO_TILEFUNC_HEADER.depends = GENERATED
PSIKYO_TILEFUNC_HEADER.target = $$GEN/psikyo_tile_func.h
PSIKYO_TILEFUNC_HEADER.commands = \
@echo "Generating psikyo_tile_func.h"; \
perl $$SCRIPTS/psikyo_tile_func.pl -o $$PSIKYO_TILEFUNC_HEADER.target;
# toa_gp9001_func
TOA_GP9001_FUNC_HEADER.depends = GENERATED
TOA_GP9001_FUNC_HEADER.target = $$GEN/toa_gp9001_func.h
TOA_GP9001_FUNC_HEADER.commands = \
@echo "Generating toa_gp9001_func.h"; \
perl $$SCRIPTS/toa_gp9001_func.pl -o $$TOA_GP9001_FUNC_HEADER.target;
#-------------------------------------------------------------------------------
# pgm_sprite.h
#-------------------------------------------------------------------------------
PGM_SPRITE_CREATE.depends = GENERATED
PGM_SPRITE_CREATE.target = $$GEN/pgm_sprite_create
PGM_SPRITE_CREATE.commands =\
@echo "Compiling pgm_sprite_create.cpp"; \
$$QMAKE_CXX $$SRC/burn/drv/pgm/pgm_sprite_create.cpp -o $$PGM_SPRITE_CREATE.target;
PGM_SPRITE_HEADER.depends = PGM_SPRITE_CREATE
PGM_SPRITE_HEADER.target = $$GEN/pgm_sprite.h
PGM_SPRITE_HEADER.commands =\
@echo "Generating pgm_sprite.h"; \
$$PGM_SPRITE_CREATE.target > $$PGM_SPRITE_HEADER.target
#-------------------------------------------------------------------------------
# Musashi68k
#-------------------------------------------------------------------------------
M68K_MAKE.target = $$GEN/m68kmake
M68K_MAKE.depends = $$SRC/cpu/m68k/m68kmake.c GENERATED
M68K_MAKE.commands = \
@echo "Compiling Musashi MC680x0 core: m68kmake.c..."; \
$$QMAKE_CC $$SRC/cpu/m68k/m68kmake.c -o $$M68K_MAKE.target; \
$$M68K_MAKE.target $$GEN/ $$SRC/cpu/m68k/m68k_in.c;
# objects
M68K_OPAC.target = $$GEN/m68kopac.o
M68K_OPAC.depends = M68K_MAKE
M68K_OPAC.commands = \
@echo "Compiling Musashi MC680x0 core: m68kopac.c..."; \
$$QMAKE_CC $$QMAKE_CFLAGS -I$$SRC/cpu/m68k $$GEN/m68kopac.c -c -o $$M68K_OPAC.target;
M68K_OPDM.target = $$GEN/m68kopdm.o
M68K_OPDM.depends = M68K_MAKE
M68K_OPDM.commands = \
@echo "Compiling Musashi MC680x0 core: m68kopdm.c..."; \
$$QMAKE_CC $$QMAKE_CFLAGS -I$$SRC/cpu/m68k $$GEN/m68kopdm.c -c -o $$M68K_OPDM.target;
M68K_OPNZ.target = $$GEN/m68kopnz.o
M68K_OPNZ.depends = M68K_MAKE
M68K_OPNZ.commands = \
@echo "Compiling Musashi MC680x0 core: m68kopnz.c..."; \
$$QMAKE_CC $$QMAKE_CFLAGS -I$$SRC/cpu/m68k $$GEN/m68kopnz.c -c -o $$M68K_OPNZ.target;
M68K_OPS.target = $$GEN/m68kops.o
M68K_OPS.depends = M68K_MAKE
M68K_OPS.commands = \
@echo "Compiling Musashi MC680x0 core: m68kops.c..."; \
$$QMAKE_CC $$QMAKE_CFLAGS -I$$SRC/cpu/m68k $$GEN/m68kops.c -c -o $$M68K_OPS.target;
M68K_OPS_HEADER.target = $$GEN/m68kops.h
M68K_OPS_HEADER.depends = M68K_MAKE
M68K_OPS_HEADER.commands = \
$$M68K_MAKE.target $$GEN/ $$SRC/cpu/m68k/m68k_in.c;
M68K_LIB.target = $$GEN/libm68kops.o
M68K_LIB.depends = M68K_MAKE M68K_OPAC M68K_OPDM M68K_OPNZ M68K_OPS M68K_OPS_HEADER
M68K_LIB.commands = \
@echo "Partially linking Musashi MC680x0 core: libm68kops.o..."; \
$$FBA_LD -r $$M68K_OPAC.target $$M68K_OPDM.target \
$$M68K_OPNZ.target $$M68K_OPS.target -o $$M68K_LIB.target
OBJECTS += $$M68K_LIB.target
#-------------------------------------------------------------------------------
# Gamelist
#-------------------------------------------------------------------------------
DRIVERLIST.depends = GENERATED
DRIVERLIST.target = $$GEN/driverlist.h
DRIVERLIST_PATHS =
$$DRV_SMS:DRIVERLIST_PATHS += $$SRC/burn/drv/sms
$$DRV_PCE:DRIVERLIST_PATHS += $$SRC/burn/drv/pce
$$DRV_PGM:DRIVERLIST_PATHS += $$SRC/burn/drv/pgm
$$DRV_CAVE:DRIVERLIST_PATHS += $$SRC/burn/drv/cave
$$DRV_CPS3:DRIVERLIST_PATHS += $$SRC/burn/drv/cps3
$$DRV_IREM:DRIVERLIST_PATHS += $$SRC/burn/drv/irem
$$DRV_SEGA:DRIVERLIST_PATHS += $$SRC/burn/drv/sega
$$DRV_SNES:DRIVERLIST_PATHS += $$SRC/burn/drv/snes
$$DRV_TAITO:DRIVERLIST_PATHS += $$SRC/burn/drv/taito
$$DRV_CAPCOM:DRIVERLIST_PATHS += $$SRC/burn/drv/capcom
$$DRV_NEOGEO:DRIVERLIST_PATHS += $$SRC/burn/drv/neogeo
$$DRV_KONAMI:DRIVERLIST_PATHS += $$SRC/burn/drv/konami
$$DRV_PRE90S:DRIVERLIST_PATHS += $$SRC/burn/drv/pre90s
$$DRV_PST90S:DRIVERLIST_PATHS += $$SRC/burn/drv/pst90s
$$DRV_PSIKYO:DRIVERLIST_PATHS += $$SRC/burn/drv/psikyo
$$DRV_TOAPLAN:DRIVERLIST_PATHS += $$SRC/burn/drv/toaplan
$$DRV_DATAEAST:DRIVERLIST_PATHS += $$SRC/burn/drv/dataeast
$$DRV_GALAXIAN:DRIVERLIST_PATHS += $$SRC/burn/drv/galaxian
$$DRV_MEGADRIVE:DRIVERLIST_PATHS += $$SRC/burn/drv/megadrive
DRIVERLIST.commands = \
@echo "Generating driverlist.h"; \
perl $$SCRIPTS/gamelist.pl -o $$DRIVERLIST.target $$DRIVERLIST_PATHS;
#===============================================================================
# RUBY CONFIG
#===============================================================================
LINUX_VIDEO_XV = true
LINUX_VIDEO_SDL = true
LINUX_VIDEO_XSHM = true
LINUX_VIDEO_GLX = true
MACX_VIDEO_CGL = true
MACX_VIDEO_SDL = true
linux {
DEFINES += PLATFORM_X
$${LINUX_VIDEO_XV} {
DEFINES += VIDEO_XV
SOURCES += $$SRC/burner/qt/ruby/video/xv.cpp
LIBS += -lX11 -lXext -lXv
}
$${LINUX_VIDEO_SDL} {
DEFINES += VIDEO_SDL
SOURCES += $$SRC/burner/qt/ruby/video/sdl.cpp
LIBS *= -lSDL
}
$${LINUX_VIDEO_XSHM} {
DEFINES += VIDEO_XSHM
SOURCES += $$SRC/burner/qt/ruby/video/xshm.cpp
LIBS += -lX11 -lXext
}
$${LINUX_VIDEO_GLX} {
DEFINES += VIDEO_GLX
SOURCES += $$SRC/burner/qt/ruby/video/glx.cpp
LIBS += -lX11 -lXext -lGL
}
}
macx {
DEFINES += PLATFORM_MACOSX
$${MACX_VIDEO_CGL} {
SOURCES += $$SRC/burner/qt/ruby/video/cgl.cpp
# LIBS +=
}
$${MACX_VIDEO_SDL} {
DEFINES += VIDEO_SDL
SOURCES += $$SRC/burner/qt/ruby/video/sdl.cpp
}
}
$${LINUX_VIDEO_GLX} | $${MACX_VIDEO_CGL} {
HEADERS += $$SRC/burner/qt/ruby/video/opengl/bind.hpp \
$$SRC/burner/qt/ruby/video/opengl/main.hpp \
$$SRC/burner/qt/ruby/video/opengl/opengl.hpp \
$$SRC/burner/qt/ruby/video/opengl/program.hpp \
$$SRC/burner/qt/ruby/video/opengl/shaders.hpp \
$$SRC/burner/qt/ruby/video/opengl/surface.hpp \
$$SRC/burner/qt/ruby/video/opengl/texture.hpp \
$$SRC/burner/qt/ruby/video/opengl/utility.hpp
}
#-------------------------------------------------------------------------------
#-------------------------------------------------------------------------------
QMAKE_EXTRA_TARGETS += \
GENERATED \
CTV_HEADER \
CAVE_SPRFUNC_HEADER \
CAVE_TILEFUNC_HEADER \
NEO_SPRFUNC_HEADER \
PSIKYO_TILEFUNC_HEADER \
PGM_SPRITE_CREATE \
PGM_SPRITE_HEADER \
TOA_GP9001_FUNC_HEADER \
DRIVERLIST \
M68K_MAKE \
M68K_OPAC \
M68K_OPDM \
M68K_OPNZ \
M68K_OPS \
M68K_OPS_HEADER \
M68K_LIB
PRE_TARGETDEPS += \
$$CTV_HEADER.target \
$$CAVE_SPRFUNC_HEADER.target \
$$CAVE_TILEFUNC_HEADER.target \
$$NEO_SPRFUNC_HEADER.target \
$$PSIKYO_TILEFUNC_HEADER.target \
$$PGM_SPRITE_HEADER.target \
$$TOA_GP9001_FUNC_HEADER.target \
$$DRIVERLIST.target \
$$M68K_LIB.target \
LIBS += -lSDL
QMAKE_CLEAN += $$GEN/*
#===============================================================================
# CAPCOM DRIVERS
#===============================================================================
$$DRV_CAPCOM {
message("Capcom drivers enabled")
HEADERS += $$files(../../src/burn/drv/capcom/*.h)
SOURCES += $$files(../../src/burn/drv/capcom/*.cpp)
SOURCES -= ../../src/burn/drv/capcom/ctv_make.cpp
}
#===============================================================================
# CAVE DRIVERS
#===============================================================================
$$DRV_CAVE {
message("Cave drivers enabled")
HEADERS += $$files(../../src/burn/drv/cave/*.h)
SOURCES += $$files(../../src/burn/drv/cave/*.cpp)
}
#===============================================================================
# CPS3 DRIVERS
#===============================================================================
$$DRV_CPS3 {
message("CPS3 drivers enabled")
HEADERS += $$files(../../src/burn/drv/cps3/*.h)
SOURCES += $$files(../../src/burn/drv/cps3/*.cpp)
}
#===============================================================================
# DATAEAST DRIVERS
#===============================================================================
$$DRV_DATAEAST {
message("Dataeast drivers enabled")
HEADERS += $$files(../../src/burn/drv/dataeast/*.h)
SOURCES += $$files(../../src/burn/drv/dataeast/*.cpp)
}
#===============================================================================
# GALAXIAN DRIVERS
#===============================================================================
$$DRV_GALAXIAN {
message("Galaxian drivers enabled")
HEADERS += $$files(../../src/burn/drv/galaxian/*.h)
SOURCES += $$files(../../src/burn/drv/galaxian/*.cpp)
}
#===============================================================================
# IREM DRIVERS
#===============================================================================
$$DRV_IREM {
message("Irem drivers enabled")
HEADERS += $$files(../../src/burn/drv/irem/*.h)
SOURCES += $$files(../../src/burn/drv/irem/*.cpp)
}
#===============================================================================
# KONAMI DRIVERS
#===============================================================================
$$DRV_KONAMI {
message("Konami drivers enabled")
HEADERS += $$files(../../src/burn/drv/konami/*.h)
SOURCES += $$files(../../src/burn/drv/konami/*.cpp)
}
#===============================================================================
# MEGADRIVE DRIVERS
#===============================================================================
$$DRV_MEGADRIVE {
message("Megadrive drivers enabled")
HEADERS += $$files(../../src/burn/drv/megadrive/*.h)
SOURCES += $$files(../../src/burn/drv/megadrive/*.cpp)
}
#===============================================================================
# NEOGEO DRIVERS
#===============================================================================
$$DRV_NEOGEO {
message("Neogeo drivers enabled")
HEADERS += $$files(../../src/burn/drv/neogeo/*.h)
SOURCES += $$files(../../src/burn/drv/neogeo/*.cpp)
SOURCES *= ../../src/burner/qt/neocdlist.cpp
}
#===============================================================================
# PC-ENGINE DRIVERS
#===============================================================================
$$DRV_PCE {
message("PC-Engine drivers enabled")
HEADERS += $$files(../../src/burn/drv/pce/*.h)
SOURCES += $$files(../../src/burn/drv/pce/*.cpp)
}
#===============================================================================
# PGM DRIVERS
#===============================================================================
$$DRV_PGM {
message("PGM drivers enabled")
HEADERS += $$files(../../src/burn/drv/pgm/*.h)
SOURCES += $$files(../../src/burn/drv/pgm/*.cpp)
SOURCES -= ../../src/burn/drv/pgm/pgm_sprite_create.cpp
}
#===============================================================================
# PRE90S DRIVERS
#===============================================================================
$$DRV_PRE90S {
message("Pre90s drivers enabled")
HEADERS += $$files(../../src/burn/drv/pre90s/*.h)
SOURCES += $$files(../../src/burn/drv/pre90s/*.cpp)
HEADERS *= ../../src/burn/drv/sega/mc8123.h
SOURCES *= ../../src/burn/drv/sega/mc8123.cpp
# CAPCOM deps...
HEADERS *= $$files(../../src/burn/drv/capcom/*.h)
SOURCES *= $$files(../../src/burn/drv/capcom/*.cpp)
SOURCES -= ../../src/burn/drv/capcom/ctv_make.cpp
# KONAMI deps...
HEADERS *= $$files(../../src/burn/drv/konami/*.h)
SOURCES *= $$files(../../src/burn/drv/konami/k*.cpp)
}
#===============================================================================
# PSIKYO DRIVERS
#===============================================================================
$$DRV_PSIKYO {
message("Psikyo drivers enabled")
HEADERS += $$files(../../src/burn/drv/psikyo/*.h)
SOURCES += $$files(../../src/burn/drv/psikyo/*.cpp)
}
#===============================================================================
# PST90S DRIVERS
#===============================================================================
$$DRV_PST90S {
message("Pst90s drivers enabled")
HEADERS += $$files(../../src/burn/drv/pst90s/*.h)
SOURCES += $$files(../../src/burn/drv/pst90s/*.cpp)
}
#===============================================================================
# SEGA DRIVERS
#===============================================================================
$$DRV_SEGA {
message("Sega drivers enabled")
HEADERS *= $$files(../../src/burn/drv/sega/*.h)
SOURCES *= $$files(../../src/burn/drv/sega/*.cpp)
}
#===============================================================================
# MASTERSYSTEM DRIVERS
#===============================================================================
$$DRV_SMS {
message("SMS drivers enabled")
HEADERS += $$files(../../src/burn/drv/sms/*.h)
SOURCES += $$files(../../src/burn/drv/sms/*.cpp)
}
#===============================================================================
# SNES DRIVERS
#===============================================================================
$$DRV_SNES {
message("SNES drivers enabled")
HEADERS += $$files(../../src/burn/drv/snes/*.h)
SOURCES += $$files(../../src/burn/drv/snes/*.cpp)
}
#===============================================================================
# TAITO DRIVERS
#===============================================================================
$$DRV_TAITO {
message("Taito drivers enabled")
HEADERS += $$files(../../src/burn/drv/taito/*.h)
SOURCES += $$files(../../src/burn/drv/taito/*.cpp)
}
#===============================================================================
# TOAPLAN DRIVERS
#===============================================================================
$$DRV_TOAPLAN {
message("Toaplan drivers enabled")
HEADERS += $$files(../../src/burn/drv/toaplan/*.h)
SOURCES += $$files(../../src/burn/drv/toaplan/*.cpp)
}
SOURCES += \
../../src/burn/devices/8255ppi.cpp \
../../src/burn/devices/8257dma.cpp \
../../src/burn/devices/eeprom.cpp \
../../src/burn/devices/pandora.cpp \
../../src/burn/devices/seibusnd.cpp \
../../src/burn/devices/sknsspr.cpp \
../../src/burn/devices/slapstic.cpp \
../../src/burn/devices/t5182.cpp \
../../src/burn/devices/timekpr.cpp \
../../src/burn/devices/tms34061.cpp \
../../src/burn/devices/v3021.cpp \
../../src/burn/devices/vdc.cpp \
../../src/burn/snd/burn_y8950.cpp \
../../src/burn/snd/burn_ym2151.cpp \
../../src/burn/snd/burn_ym2203.cpp \
../../src/burn/snd/burn_ym2413.cpp \
../../src/burn/snd/burn_ym2608.cpp \
../../src/burn/snd/burn_ym2610.cpp \
../../src/burn/snd/burn_ym2612.cpp \
../../src/burn/snd/burn_ym3526.cpp \
../../src/burn/snd/burn_ym3812.cpp \
../../src/burn/snd/burn_ymf278b.cpp \
../../src/burn/snd/c6280.cpp \
../../src/burn/snd/dac.cpp \
../../src/burn/snd/es5506.cpp \
../../src/burn/snd/es8712.cpp \
../../src/burn/snd/flt_rc.cpp \
../../src/burn/snd/ics2115.cpp \
../../src/burn/snd/iremga20.cpp \
../../src/burn/snd/k005289.cpp \
../../src/burn/snd/k007232.cpp \
../../src/burn/snd/k051649.cpp \
../../src/burn/snd/k053260.cpp \
../../src/burn/snd/k054539.cpp \
../../src/burn/snd/msm5205.cpp \
../../src/burn/snd/msm5232.cpp \
../../src/burn/snd/msm6295.cpp \
../../src/burn/snd/namco_snd.cpp \
../../src/burn/snd/nes_apu.cpp \
../../src/burn/snd/rf5c68.cpp \
../../src/burn/snd/saa1099.cpp \
../../src/burn/snd/samples.cpp \
../../src/burn/snd/segapcm.cpp \
../../src/burn/snd/sn76496.cpp \
../../src/burn/snd/upd7759.cpp \
../../src/burn/snd/vlm5030.cpp \
../../src/burn/snd/x1010.cpp \
../../src/burn/snd/ymz280b.cpp \
../../src/burn/snd/ay8910.c \
../../src/burn/snd/fm.c \
../../src/burn/snd/fmopl.c \
../../src/burn/snd/ym2151.c \
../../src/burn/snd/ym2413.c \
../../src/burn/snd/ymdeltat.c \
../../src/burn/snd/ymf278b.c \
../../src/burn/burn_sound.cpp \
../../src/burn/burn.cpp \
../../src/burn/cheat.cpp \
../../src/burn/debug_track.cpp \
../../src/burn/hiscore.cpp \
../../src/burn/load.cpp \
../../src/burn/tiles_generic.cpp \
../../src/burn/timer.cpp \
../../src/burn/vector.cpp \
../../src/burn/burn_sound_c.cpp \
../../src/burn/burn_memory.cpp \
../../src/burn/burn_led.cpp \
../../src/burn/burn_gun.cpp \
../../src/cpu/hd6309_intf.cpp \
../../src/cpu/konami_intf.cpp \
../../src/cpu/m6502_intf.cpp \
../../src/cpu/m6800_intf.cpp \
../../src/cpu/m6805_intf.cpp \
../../src/cpu/m6809_intf.cpp \
../../src/cpu/m68000_intf.cpp \
../../src/cpu/nec_intf.cpp \
../../src/cpu/pic16c5x_intf.cpp \
../../src/cpu/s2650_intf.cpp \
../../src/cpu/h6280_intf.cpp \
../../src/cpu/arm7_intf.cpp \
../../src/cpu/arm_intf.cpp \
../../src/cpu/arm/arm.cpp \
../../src/cpu/arm7/arm7.cpp \
../../src/cpu/arm7/arm7core.c \
../../src/cpu/arm7/arm7exec.c \
../../src/cpu/h6280/h6280.cpp \
../../src/cpu/h6280/tblh6280.c \
../../src/cpu/hd6309/hd6309.cpp \
../../src/cpu/hd6309/6309ops.c \
../../src/cpu/hd6309/6309tbl.c \
../../src/cpu/i8039/i8039.cpp \
../../src/cpu/konami/konami.cpp \
../../src/cpu/konami/konamops.c \
../../src/cpu/konami/konamtbl.c \
../../src/cpu/m6502/m6502.cpp \
../../src/cpu/m6502/t65c02.c \
../../src/cpu/m6502/t65sc02.c \
../../src/cpu/m6502/t6502.c \
../../src/cpu/m6502/tdeco16.c \
../../src/cpu/m6502/tn2a03.c \
../../src/cpu/m6800/m6800.cpp \
../../src/cpu/m6800/6800ops.c \
../../src/cpu/m6800/6800tbl.c \
../../src/cpu/m6805/m6805.cpp \
../../src/cpu/m6805/6805ops.c \
../../src/cpu/m6809/m6809.cpp \
../../src/cpu/m6809/6809ops.c \
../../src/cpu/m6809/6809tbl.c \
../../src/cpu/nec/nec.cpp \
../../src/cpu/nec/v25.cpp \
../../src/cpu/nec/necinstr.c \
../../src/cpu/nec/v25instr.c \
../../src/cpu/nec/v25sfr.c \
../../src/cpu/pic16c5x/pic16c5x.cpp \
../../src/cpu/s2650/s2650.cpp \
../../src/cpu/sh2/sh2.cpp \
../../src/cpu/z80/z80.cpp \
../../src/cpu/z80/z80daisy.cpp \
../../src/cpu/m68k/m68kcpu.c \
../../src/burner/conc.cpp \
../../src/burner/cong.cpp \
../../src/burner/dat.cpp \
../../src/burner/gamc.cpp \
../../src/burner/gami.cpp \
../../src/burner/image.cpp \
../../src/burner/misc.cpp \
../../src/burner/sshot.cpp \
../../src/burner/state.cpp \
../../src/burner/statec.cpp \
../../src/burner/zipfn.cpp \
../../src/burner/ioapi.c \
../../src/burner/unzip.c \
../../src/dep/libs/libpng/png.c \
../../src/dep/libs/libpng/pngerror.c \
../../src/dep/libs/libpng/pngget.c \
../../src/dep/libs/libpng/pngmem.c \
../../src/dep/libs/libpng/pngpread.c \
../../src/dep/libs/libpng/pngread.c \
../../src/dep/libs/libpng/pngrio.c \
../../src/dep/libs/libpng/pngrtran.c \
../../src/dep/libs/libpng/pngrutil.c \
../../src/dep/libs/libpng/pngset.c \
../../src/dep/libs/libpng/pngtrans.c \
../../src/dep/libs/libpng/pngwio.c \
../../src/dep/libs/libpng/pngwrite.c \
../../src/dep/libs/libpng/pngwtran.c \
../../src/dep/libs/libpng/pngwutil.c \
../../src/intf/interface.cpp \
../../src/intf/audio/aud_dsp.cpp \
../../src/intf/audio/aud_interface.cpp \
../../src/intf/audio/lowpass2.cpp \
../../src/intf/cd/cd_interface.cpp \
../../src/intf/input/inp_interface.cpp \
../../src/intf/video/vid_interface.cpp \
../../src/intf/video/vid_support.cpp \
../../src/cpu/z80_intf.cpp \
../../src/burner/qt/main.cpp \
../../src/burner/qt/aboutdialog.cpp \
../../src/burner/qt/bzip.cpp \
../../src/burner/qt/dipswitchdialog.cpp \
../../src/burner/qt/driver.cpp \
../../src/burner/qt/emuworker.cpp \
../../src/burner/qt/mainwindow.cpp \
../../src/burner/qt/progress.cpp \
../../src/burner/qt/qaudiointerface.cpp \
../../src/burner/qt/qinputinterface.cpp \
../../src/burner/qt/qrubyviewport.cpp \
../../src/burner/qt/qutil.cpp \
../../src/burner/qt/romdirsdialog.cpp \
../../src/burner/qt/rominfodialog.cpp \
../../src/burner/qt/romscandialog.cpp \
../../src/burner/qt/selectdialog.cpp \
../../src/burner/qt/stringset.cpp \
../../src/burner/qt/supportdirsdialog.cpp \
../../src/burner/qt/ruby/implementation.cpp \
../../src/burner/qt/ruby/ruby.cpp \
../../src/dep/libs/zlib/adler32.c \
../../src/dep/libs/zlib/compress.c \
../../src/dep/libs/zlib/crc32.c \
../../src/dep/libs/zlib/deflate.c \
../../src/dep/libs/zlib/gzclose.c \
../../src/dep/libs/zlib/gzlib.c \
../../src/dep/libs/zlib/gzread.c \
../../src/dep/libs/zlib/gzwrite.c \
../../src/dep/libs/zlib/infback.c \
../../src/dep/libs/zlib/inffast.c \
../../src/dep/libs/zlib/inflate.c \
../../src/dep/libs/zlib/inftrees.c \
../../src/dep/libs/zlib/trees.c \
../../src/dep/libs/zlib/uncompr.c \
../../src/dep/libs/zlib/zutil.c \
../../src/burn/devices/nmk004.cpp \
../../src/cpu/tlcs90/tlcs90.cpp \
../../src/cpu/tlcs90_intf.cpp
HEADERS += \
../../src/burn/devices/8255ppi.h \
../../src/burn/devices/8257dma.h \
../../src/burn/devices/eeprom.h \
../../src/burn/devices/pandora.h \
../../src/burn/devices/seibusnd.h \
../../src/burn/devices/sknsspr.h \
../../src/burn/devices/slapstic.h \
../../src/burn/devices/t5182.h \
../../src/burn/devices/timekpr.h \
../../src/burn/devices/tms34061.h \
../../src/burn/devices/v3021.h \
../../src/burn/devices/vdc.h \
../../src/burn/snd/ay8910.h \
../../src/burn/snd/burn_y8950.h \
../../src/burn/snd/burn_ym2151.h \
../../src/burn/snd/burn_ym2203.h \
../../src/burn/snd/burn_ym2413.h \
../../src/burn/snd/burn_ym2608.h \
../../src/burn/snd/burn_ym2610.h \
../../src/burn/snd/burn_ym2612.h \
../../src/burn/snd/burn_ym3526.h \
../../src/burn/snd/burn_ym3812.h \
../../src/burn/snd/burn_ymf278b.h \
../../src/burn/snd/c6280.h \
../../src/burn/snd/dac.h \
../../src/burn/snd/es5506.h \
../../src/burn/snd/es8712.h \
../../src/burn/snd/flt_rc.h \
../../src/burn/snd/fm.h \
../../src/burn/snd/fmopl.h \
../../src/burn/snd/ics2115.h \
../../src/burn/snd/iremga20.h \
../../src/burn/snd/k005289.h \
../../src/burn/snd/k007232.h \
../../src/burn/snd/k051649.h \
../../src/burn/snd/k053260.h \
../../src/burn/snd/k054539.h \
../../src/burn/snd/msm5205.h \
../../src/burn/snd/msm5232.h \
../../src/burn/snd/msm6295.h \
../../src/burn/snd/namco_snd.h \
../../src/burn/snd/nes_apu.h \
../../src/burn/snd/nes_defs.h \
../../src/burn/snd/rescap.h \
../../src/burn/snd/rf5c68.h \
../../src/burn/snd/saa1099.h \
../../src/burn/snd/samples.h \
../../src/burn/snd/segapcm.h \
../../src/burn/snd/sn76496.h \
../../src/burn/snd/upd7759.h \
../../src/burn/snd/vlm5030.h \
../../src/burn/snd/x1010.h \
../../src/burn/snd/ym2151.h \
../../src/burn/snd/ym2413.h \
../../src/burn/snd/ymdeltat.h \
../../src/burn/snd/ymf278b.h \
../../src/burn/snd/ymz280b.h \
../../src/burn/burn_sound.h \
../../src/burn/burn.h \
../../src/burn/burnint.h \
../../src/burn/cheat.h \
../../src/burn/driver.h \
../../src/burn/hiscore.h \
../../src/burn/state.h \
../../src/burn/stdfunc.h \
../../src/burn/tiles_generic.h \
../../src/burn/timer.h \
../../src/burn/vector.h \
../../src/burn/version.h \
../../src/burn/burn_led.h \
../../src/burn/burn_gun.h \
../../src/burn/bitswap.h \
../../src/cpu/h6280_intf.h \
../../src/cpu/hd6309_intf.h \
../../src/cpu/konami_intf.h \
../../src/cpu/m6502_intf.h \
../../src/cpu/m6800_intf.h \
../../src/cpu/m6805_intf.h \
../../src/cpu/m6809_intf.h \
../../src/cpu/m68000_debug.h \
../../src/cpu/m68000_intf.h \
../../src/cpu/nec_intf.h \
../../src/cpu/pic16c5x_intf.h \
../../src/cpu/s2650_intf.h \
../../src/cpu/arm7_intf.h \
../../src/cpu/arm_intf.h \
../../src/cpu/arm7/arm7core.h \
../../src/cpu/h6280/h6280.h \
../../src/cpu/h6280/h6280ops.h \
../../src/cpu/hd6309/hd6309.h \
../../src/cpu/i8039/i8039.h \
../../src/cpu/konami/konami.h \
../../src/cpu/m6502/ill02.h \
../../src/cpu/m6502/m6502.h \
../../src/cpu/m6502/ops02.h \
../../src/cpu/m6502/opsc02.h \
../../src/cpu/m6502/opsn2a03.h \
../../src/cpu/m6800/m6800.h \
../../src/cpu/m6805/m6805.h \
../../src/cpu/m6809/m6809.h \
../../src/cpu/nec/nec.h \
../../src/cpu/nec/necea.h \
../../src/cpu/nec/necinstr.h \
../../src/cpu/nec/necmacro.h \
../../src/cpu/nec/necmodrm.h \
../../src/cpu/nec/necpriv.h \
../../src/cpu/nec/v25instr.h \
../../src/cpu/nec/v25priv.h \
../../src/cpu/pic16c5x/pic16c5x.h \
../../src/cpu/s2650/s2650.h \
../../src/cpu/z80/z80.h \
../../src/cpu/z80/z80daisy.h \
../../src/cpu/m68k/m68kcpu.h \
../../src/cpu/m68k/m68kconf.h \
../../src/burner/burner.h \
../../src/burner/gameinp.h \
../../src/burner/ioapi.h \
../../src/burner/neocdlist.h \
../../src/burner/title.h \
../../src/burner/unzip.h \
../../src/dep/libs/libpng/png.h \
../../src/dep/libs/libpng/pngconf.h \
../../src/dep/libs/libpng/pngdebug.h \
../../src/dep/libs/libpng/pnginfo.h \
../../src/dep/libs/libpng/pnglibconf.h \
../../src/dep/libs/libpng/pngpriv.h \
../../src/dep/libs/libpng/pngstruct.h \
../../src/intf/interface.h \
../../src/intf/audio/aud_dsp.h \
../../src/intf/audio/lowpass2.h \
../../src/intf/cd/cd_interface.h \
../../src/intf/input/inp_keys.h \
../../src/intf/video/vid_support.h \
../../src/cpu/sh2_intf.h \
../../src/cpu/z80_intf.h \
../../src/burner/qt/aboutdialog.h \
../../src/burner/qt/burner_qt.h \
../../src/burner/qt/dipswitchdialog.h \
../../src/burner/qt/emuworker.h \
../../src/burner/qt/mainwindow.h \
../../src/burner/qt/qaudiointerface.h \
../../src/burner/qt/qinputinterface.h \
../../src/burner/qt/qrubyviewport.h \
../../src/burner/qt/qutil.h \
../../src/burner/qt/romdirsdialog.h \
../../src/burner/qt/rominfodialog.h \
../../src/burner/qt/romscandialog.h \
../../src/burner/qt/selectdialog.h \
../../src/burner/qt/supportdirsdialog.h \
../../src/burner/qt/tchar.h \
../../src/burner/qt/ruby/audio.hpp \
../../src/burner/qt/ruby/input.hpp \
../../src/burner/qt/ruby/ruby.hpp \
../../src/burner/qt/ruby/video.hpp \
../../src/dep/libs/zlib/crc32.h \
../../src/dep/libs/zlib/deflate.h \
../../src/dep/libs/zlib/gzguts.h \
../../src/dep/libs/zlib/inffast.h \
../../src/dep/libs/zlib/inffixed.h \
../../src/dep/libs/zlib/inflate.h \
../../src/dep/libs/zlib/inftrees.h \
../../src/dep/libs/zlib/trees.h \
../../src/dep/libs/zlib/zconf.h \
../../src/dep/libs/zlib/zconf.h.in \
../../src/dep/libs/zlib/zlib.h \
../../src/dep/libs/zlib/zutil.h \
../../src/burn/devices/nmk004.h
OTHER_FILES +=
RESOURCES += \
../../src/burner/qt/rscr.qrc
FORMS += \
../../src/burner/qt/aboutdialog.ui \
../../src/burner/qt/dipswitchdialog.ui \
../../src/burner/qt/romdirsdialog.ui \
../../src/burner/qt/rominfodialog.ui \
../../src/burner/qt/romscandialog.ui \
../../src/burner/qt/selectdialog.ui \
../../src/burner/qt/supportdirsdialog.ui

View File

@ -1,6 +1,6 @@
// FB Alpha - Emulator for MC68000/Z80 based arcade games
// Refer to the "license.txt" file for more info
#pragma once
#include <limits.h>
#include <stdarg.h>
#include <stdio.h>
@ -17,7 +17,11 @@
#define MAKE_STRING(s) MAKE_STRING_2(s)
#define BZIP_MAX (20) // Maximum zip files to search through
#define DIRS_MAX (20) // Maximum number of directories to search
#if defined (BUILD_QT)
#define DIRS_MAX (4) // Maximum number of directories to search
#else
#define DIRS_MAX (20) // Maximum number of directories to search
#endif
#include "title.h"
#include "burn.h"
@ -42,6 +46,8 @@ typedef struct tagIMAGE {
#include "burner_xbox.h"
#elif defined(__LIBRETRO__)
#include "burner_libretro.h"
#elif defined(BUILD_QT)
#include "burner_qt.h"
#endif
#if defined (INCLUDE_LIB_PNGH)

View File

@ -0,0 +1,21 @@
#include <QtCore>
#include "aboutdialog.h"
#include "ui_aboutdialog.h"
#include "qutil.h"
AboutDialog::AboutDialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::AboutDialog)
{
ui->setupUi(this);
ui->teLicense->hide();
layout()->setContentsMargins(0, 0, 0, 0);
layout()->setSizeConstraint(QLayout::SetFixedSize);
ui->teLicense->setText(util::loadText(tr(":/resource/license.txt")));
}
AboutDialog::~AboutDialog()
{
delete ui;
}

View File

@ -0,0 +1,22 @@
#ifndef ABOUTDIALOG_H
#define ABOUTDIALOG_H
#include <QDialog>
namespace Ui {
class AboutDialog;
}
class AboutDialog : public QDialog
{
Q_OBJECT
public:
explicit AboutDialog(QWidget *parent = 0);
~AboutDialog();
private:
Ui::AboutDialog *ui;
};
#endif // ABOUTDIALOG_H

View File

@ -0,0 +1,118 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>AboutDialog</class>
<widget class="QDialog" name="AboutDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>645</width>
<height>350</height>
</rect>
</property>
<property name="windowTitle">
<string>About</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="label">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="text">
<string notr="true"/>
</property>
<property name="pixmap">
<pixmap resource="rscr.qrc">:/resource/about.bmp</pixmap>
</property>
<property name="scaledContents">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="btnLicense">
<property name="text">
<string>License</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnOk">
<property name="text">
<string>Ok</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QTextEdit" name="teLicense">
<property name="font">
<font>
<pointsize>8</pointsize>
</font>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
<resources>
<include location="rscr.qrc"/>
</resources>
<connections>
<connection>
<sender>btnOk</sender>
<signal>clicked()</signal>
<receiver>AboutDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>293</x>
<y>178</y>
</hint>
<hint type="destinationlabel">
<x>488</x>
<y>177</y>
</hint>
</hints>
</connection>
<connection>
<sender>btnLicense</sender>
<signal>toggled(bool)</signal>
<receiver>teLicense</receiver>
<slot>setVisible(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>167</x>
<y>182</y>
</hint>
<hint type="destinationlabel">
<x>154</x>
<y>259</y>
</hint>
</hints>
</connection>
</connections>
</ui>

58
src/burner/qt/burner_qt.h Normal file
View File

@ -0,0 +1,58 @@
#ifndef _BURNER_QT_H
#define _BURNER_QT_H
typedef unsigned char BYTE;
/*
typedef unsigned short WORD;
*/
typedef unsigned long DWORD;
extern int bDrvOkay;
extern int bRunPause;
extern bool bAlwaysProcessKeyboardInput;
extern int nAppVirtualFps;
// main.cpp
extern bool AppProcessKeyboardInput();
extern void InpDIPSWResetDIPs (void);
extern void IpsApplyPatches(UINT8 *, char *);
extern void Reinitialise(void);
extern TCHAR *GetIsoPath();
extern int VidRecalcPal();
extern void wav_pause(bool bResume);
extern TCHAR szAppRomPaths[DIRS_MAX][MAX_PATH];
// drv.cpp
int DrvInit(int nDrvNum, bool bRestore);
int DrvInitCallback();
int DrvExit();
// progress.cpp
int ProgressUpdateBurner(double dProgress, const TCHAR* pszText, bool bAbs);
void ProgressCreate();
void ProgressDestroy();
bool ProgressIsRunning();
#ifdef _MSC_VER
#define snprintf _snprintf
#define ANSIToTCHAR(str, foo, bar) (str)
#endif
extern char *TCHARToANSI(const TCHAR* pszInString, char* pszOutString, int nOutSize);
class StringSet {
public:
TCHAR* szText;
int nLen;
// printf function to add text to the Bzip string
int __cdecl Add(TCHAR* szFormat, ...);
int Reset();
StringSet();
~StringSet();
};
extern StringSet BzipText;
extern StringSet BzipDetail;
#endif

518
src/burner/qt/bzip.cpp Normal file
View File

@ -0,0 +1,518 @@
// Burner Zip module
#include "burner.h"
#include <QDebug>
int nBzipError = 0; // non-zero if there is a problem with the opened romset
static TCHAR* szBzipName[BZIP_MAX] = { NULL, }; // Zip files to search through
struct RomFind { int nState; int nZip; int nPos; }; // State is non-zero if found. 1 = found totally okay.
static struct RomFind* RomFind = NULL;
static int nRomCount = 0; static int nTotalSize = 0;
static struct ZipEntry* List = NULL; static int nListCount = 0; // List of entries for current zip file
static int nCurrentZip = -1; // Zip which is currently open
static int nZipsFound = 0;
StringSet BzipText; // Text which describes any problems with loading the zip
StringSet BzipDetail; // Text which describes in detail any problems with loading the zip
int BzipStatus()
{
if (!(nBzipError & 0x0F0F)) {
return BZIP_STATUS_OK;
}
if (nBzipError & 1) {
return BZIP_STATUS_ERROR;
}
return BZIP_STATUS_BADDATA;
}
void BzipListFree()
{
if (List) {
for (int i = 0; i < nListCount; i++) {
if (List[i].szName) {
free(List[i].szName);
List[i].szName = NULL;
}
}
free(List);
}
List = NULL;
nListCount = 0;
}
static char* GetFilenameA(char* szFull)
{
int nLen = strlen(szFull);
if (nLen <= 0) {
return szFull;
}
for (int i = nLen - 1; i >= 0; i--) {
if (szFull[i] == '\\' || szFull[i] == '/') {
return szFull + i + 1;
}
}
return szFull;
}
static TCHAR* GetFilenameW(TCHAR* szFull)
{
int nLen = _tcslen(szFull);
if (nLen <= 0) {
return szFull;
}
for (int i = nLen - 1; i >= 0; i--) {
if (szFull[i] == _T('\\') || szFull[i] == _T('/')) {
return szFull + i + 1;
}
}
return szFull;
}
static int FindRomByName(TCHAR* szName)
{
struct ZipEntry* pl;
int i;
// Find the rom named szName in the List
for (i = 0, pl = List; i < nListCount; i++, pl++) {
TCHAR szCurrentName[MAX_PATH];
if (_tcsicmp(szName, GetFilenameW(ANSIToTCHAR(pl->szName, szCurrentName, MAX_PATH))) == 0) {
return i;
}
}
return -1; // couldn't find the rom
}
static int FindRomByCrc(unsigned int nCrc)
{
struct ZipEntry* pl;
int i;
// Find the rom named szName in the List
for (i = 0, pl = List; i< nListCount; i++, pl++) {
if (nCrc == pl->nCrc) {
return i;
}
}
return -1; // couldn't find the rom
}
// Find rom number i from the pBzipDriver game
static int FindRom(int i)
{
struct BurnRomInfo ri;
int nRet;
memset(&ri, 0, sizeof(ri));
nRet = BurnDrvGetRomInfo(&ri, i);
if (nRet != 0) { // Failure: no such rom
return -2;
}
if (ri.nCrc) { // Search by crc first
nRet = FindRomByCrc(ri.nCrc);
if (nRet >= 0) {
return nRet;
}
}
for (int nAka = 0; nAka < 0x10000; nAka++) { // Failing that, search for possible names
char *szPossibleName = NULL;
nRet = BurnDrvGetRomName(&szPossibleName, i, nAka);
if (nRet) { // No more rom names
break;
}
nRet = FindRomByName(ANSIToTCHAR(szPossibleName, NULL, 0));
if (nRet >= 0) {
return nRet;
}
}
return -1; // Couldn't find the rom
}
static int RomDescribe(StringSet* pss, struct BurnRomInfo* pri)
{
pss->Add(_T("The "));
if (pri->nType & 0x10) {
pss->Add(_T("essential "));
}
if (pri->nType & 0x80) {
pss->Add(_T("BIOS "));
}
if (pri->nType & 0x01) {
pss->Add(_T("graphics "));
}
if (pri->nType & 0x02) {
pss->Add(_T("sound "));
}
pss->Add(_T("ROM "));
return 0;
}
static int CheckRomsBoot()
{
for (int i = 0; i < nRomCount; i++) {
struct BurnRomInfo ri;
int nState;
memset(&ri, 0, sizeof(ri));
BurnDrvGetRomInfo(&ri, i); // Find information about the wanted rom
nState = RomFind[i].nState; // Get the state of the rom in the zip file
if (nState != 1 && ri.nType && ri.nCrc) {
if (ri.nType & 0x80) {
return 2;
}
return 1;
}
}
return 0;
}
static int GetBZipError(int nState)
{
switch (nState) {
case 1: // OK
return 0x00;
case 0: // Not present
return 0x01;
case 3: // Incomplete
return 0x01;
default: // CRC wrong or too large
return 0x10;
}
return 0x10;
}
// Check the roms to see if they code, graphics etc are complete
static int CheckRoms()
{
nBzipError = 0; // Assume romset is fine
for (int i = 0; i < nRomCount; i++) {
struct BurnRomInfo ri;
memset(&ri, 0, sizeof(ri));
BurnDrvGetRomInfo(&ri, i); // Find information about the wanted rom
if (ri.nCrc && (ri.nType & 0x80) == 0) {
int nState = RomFind[i].nState; // Get the state of the rom in the zip file
if (nState == 0 && ri.nType) { // (A type of 0 means empty slot - no rom)
char* szName = "Unknown";
RomDescribe(&BzipDetail, &ri);
BurnDrvGetRomName(&szName, i, 0);
BzipDetail.Add(_T("%hs was not found.\n"), szName);
}
if (ri.nType & 0x90) { // essential rom - without it the game may not run at all
nBzipError |= GetBZipError(nState) << 0;
}
if (ri.nType & 0x01) { // rom which contains graphics information
nBzipError |= GetBZipError(nState) << 1;
}
if (ri.nType & 0x02) { // rom which contains sound information
nBzipError |= GetBZipError(nState) << 2;
}
}
}
if (!nZipsFound) {
nBzipError |= 0x08; // No data at all!
}
return 0;
}
static int __cdecl BzipBurnLoadRom(unsigned char* Dest, int* pnWrote, int i)
{
#if defined (BUILD_WIN32)
MSG Msg;
#endif
struct BurnRomInfo ri;
int nWantZip = 0;
TCHAR szText[128];
char* pszRomName = NULL;
int nRet = 0;
if (i < 0 || i >= nRomCount) {
return 1;
}
ri.nLen = 0;
BurnDrvGetRomInfo(&ri, i); // Get info
// show what we're doing
BurnDrvGetRomName(&pszRomName, i, 0);
if (pszRomName == NULL) {
pszRomName = "unknown";
}
_stprintf(szText, _T("Loading"));
if (ri.nType & 0x83) {
if (ri.nType & 0x80) {
_stprintf (szText + _tcslen(szText), _T(" %s"), _T("BIOS "));
}
if (ri.nType & 0x10) {
_stprintf (szText + _tcslen(szText), _T(" %s"), _T("program "));
}
if (ri.nType & 0x01) {
_stprintf (szText + _tcslen(szText), _T(" %s"), _T("graphics "));
}
if (ri.nType & 0x02) {
_stprintf (szText + _tcslen(szText), _T(" %s"), _T("sound "));
}
_stprintf(szText + _tcslen(szText), _T("(%hs)..."), pszRomName);
} else {
_stprintf(szText + _tcslen(szText), _T(" %hs..."), pszRomName);
}
ProgressUpdateBurner(ri.nLen ? 1.0 / ((double)nTotalSize / ri.nLen) : 0, szText, 0);
#if defined (BUILD_WIN32)
// Check for messages:
while (PeekMessage(&Msg, NULL, 0, 0, PM_REMOVE)) {
DispatchMessage(&Msg);
}
#endif
if (RomFind[i].nState == 0) { // Rom not found in zip at all
return 1;
}
nWantZip = RomFind[i].nZip; // Which zip file it is in
if (nCurrentZip != nWantZip) { // If we haven't got the right zip file currently open
ZipClose();
nCurrentZip = -1;
if (ZipOpen(TCHARToANSI(szBzipName[nWantZip], NULL, 0))) {
return 1;
}
nCurrentZip = nWantZip;
}
// Read in file and return how many bytes we read
if (ZipLoadFile(Dest, ri.nLen, pnWrote, RomFind[i].nPos)) {
// Error loading from the zip file
TCHAR szTemp[128] = _T("");
_stprintf(szTemp, _T("%s reading %.30hs from %.30s"), nRet == 2 ? _T("CRC error") : _T("Error"), pszRomName, GetFilenameW(szBzipName[nCurrentZip]));
// AppError(szTemp, 1);
return 1;
}
return 0;
}
int BzipOpen(bool bootApp)
{
int nMemLen; // Zip name number
nZipsFound = 0; // Haven't found zips yet
nTotalSize = 0;
if (szBzipName == NULL) {
return 1;
}
BzipClose(); // Make sure nothing is open
if(!bootApp) { // reset information strings
BzipText.Reset();
BzipDetail.Reset();
}
// Count the number of roms needed
for (nRomCount = 0; ; nRomCount++) {
if (BurnDrvGetRomInfo(NULL, nRomCount)) {
break;
}
}
if (nRomCount <= 0) {
return 1;
}
// Create an array for holding lookups for each rom -> zip entries
nMemLen = nRomCount * sizeof(struct RomFind);
RomFind = (struct RomFind*)malloc(nMemLen);
if (RomFind == NULL) {
return 1;
}
memset(RomFind, 0, nMemLen);
for (int z = 0; z < BZIP_MAX; z++) {
char* szName = NULL;
if (BurnDrvGetZipName(&szName, z)) {
break;
}
for (int d = 0; d < DIRS_MAX; d++) {
free(szBzipName[z]);
szBzipName[z] = (TCHAR*)malloc(MAX_PATH * sizeof(TCHAR));
_stprintf(szBzipName[z], _T("%s%hs"), szAppRomPaths[d], szName);
if (ZipOpen(TCHARToANSI(szBzipName[z], NULL, 0)) == 0) { // Open the rom zip file
nZipsFound++;
nCurrentZip = z;
break;
}
}
if (nCurrentZip >= 0) {
if (!bootApp) {
BzipText.Add(_T("Found %s;\n"), szBzipName[z]);
}
ZipGetList(&List, &nListCount); // Get the list of entries
for (int i = 0; i < nRomCount; i++) {
struct BurnRomInfo ri;
int nFind;
if (RomFind[i].nState == 1) { // Already found this and it's okay
continue;
}
memset(&ri, 0, sizeof(ri));
nFind = FindRom(i);
if (nFind < 0) { // Couldn't find this rom at all
continue;
}
RomFind[i].nZip = z; // Remember which zip file it is in
RomFind[i].nPos = nFind;
RomFind[i].nState = 1; // Set to found okay
BurnDrvGetRomInfo(&ri, i); // Get info about the rom
if ((ri.nType & 0x80) == 0) {
nTotalSize += ri.nLen;
}
if (List[nFind].nLen == ri.nLen) {
if (ri.nCrc) { // If we know the CRC
if (List[nFind].nCrc != ri.nCrc) { // Length okay, but CRC wrong
RomFind[i].nState = 2;
}
}
} else {
if (List[nFind].nLen < ri.nLen) {
RomFind[i].nState = 3; // Too small
} else {
RomFind[i].nState = 4; // Too big
}
}
if (!bootApp) {
if (RomFind[i].nState != 1) {
RomDescribe(&BzipDetail, &ri);
if (RomFind[i].nState == 2) {
BzipDetail.Add(_T("%hs has a CRC of %.8X. (It should be %.8X.)\n"), GetFilenameA(List[nFind].szName), List[nFind].nCrc, ri.nCrc);
}
if (RomFind[i].nState == 3) {
BzipDetail.Add(_T("%hs is %dk which is incomplete. (It should be %dkB.)\n"), GetFilenameA(List[nFind].szName), List[nFind].nLen >> 10, ri.nLen >> 10);
}
if (RomFind[i].nState == 4) {
BzipDetail.Add(_T("%hs is %dk which is too big. (It should be %dkB.)\n"), GetFilenameA(List[nFind].szName), List[nFind].nLen >> 10, ri.nLen >> 10);
}
}
}
}
BzipListFree();
} else {
if (!bootApp) {
BzipText.Add(_T("Couldn't find %hs;\n"), szName);
}
}
ZipClose(); // Close the last zip file if open
nCurrentZip = -1;
}
if (!bootApp) {
// Check the roms to see if they code, graphics etc are complete
CheckRoms();
if (nZipsFound) {
if (nBzipError == 0) {
BzipText.Add(_T("The romset is fine.\n"));
}
if (nBzipError & 0x07) {
BzipText.Add(_T("However the romset is INCOMPLETE.\n"));
}
if (nBzipError & 0x01) {
BzipText.Add(_T("Essential rom data is missing; the game probably won't run.\n"));
} else {
if (nBzipError & 0x10) {
BzipText.Add(_T("Some essential roms are different. "));
}
}
if (nBzipError & 0x02) {
BzipText.Add(_T("Graphical data is missing. "));
} else {
if (nBzipError & 0x20) {
BzipText.Add(_T("Some graphics roms are different. "));
}
}
if (nBzipError & 0x04) {
BzipText.Add(_T("Sound data is missing. "));
} else {
if (nBzipError & 0x40) {
BzipText.Add(_T("Some sound roms are different. "));
}
}
if (nBzipError & 0x76) {
BzipText.Add(_T("\n"));
}
}
BurnExtLoadRom = BzipBurnLoadRom; // Okay to call our function to load each rom
} else {
return CheckRomsBoot();
}
return 0;
}
int BzipClose()
{
ZipClose();
nCurrentZip = -1; // Close the last zip file if open
BurnExtLoadRom = NULL; // Can't call our function to load each rom anymore
nBzipError = 0; // reset romset errors
free(RomFind);
RomFind = NULL;
nRomCount = 0;
for (int z = 0; z < BZIP_MAX; z++) {
free(szBzipName[z]);
szBzipName[z] = NULL;
}
return 0;
}

View File

@ -0,0 +1,188 @@
#include <QtWidgets>
#include "dipswitchdialog.h"
#include "ui_dipswitchdialog.h"
#include "burner.h"
DipswitchDialog::DipswitchDialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::DipswitchDialog)
{
ui->setupUi(this);
setWindowTitle("DIPSwitches");
connect(ui->tvSettings, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)),
this, SLOT(dipChange(QTreeWidgetItem*,QTreeWidgetItem*)));
connect(ui->cbValues, SIGNAL(currentIndexChanged(int)), this, SLOT(dipValueChange(int)));
m_dipGroup = 0;
}
DipswitchDialog::~DipswitchDialog()
{
delete ui;
}
int DipswitchDialog::exec()
{
getDipOffset();
reset();
makeList();
int ret = QDialog::exec();
return ret;
}
void DipswitchDialog::reset()
{
int i = 0;
BurnDIPInfo bdi;
struct GameInp* pgi;
getDipOffset();
while (BurnDrvGetDIPInfo(&bdi, i) == 0) {
if (bdi.nFlags == 0xFF) {
pgi = GameInp + bdi.nInput + m_dipOffset;
pgi->Input.Constant.nConst = (pgi->Input.Constant.nConst & ~bdi.nMask) | (bdi.nSetting & bdi.nMask);
}
i++;
}
}
void DipswitchDialog::dipChange(QTreeWidgetItem *item, QTreeWidgetItem *prev)
{
ui->cbValues->clear();
if (item == nullptr)
return;
qDebug() << "DIP Change";
m_dipGroup = item->data(0, Qt::UserRole).toInt();
BurnDIPInfo bdiGroup;
BurnDrvGetDIPInfo(&bdiGroup, m_dipGroup);
int nCurrentSetting = 0;
for (int i = 0, j = 0; i < bdiGroup.nSetting; i++) {
TCHAR szText[256];
BurnDIPInfo bdi;
do {
BurnDrvGetDIPInfo(&bdi, m_dipGroup + 1 + j++);
} while (bdi.nFlags == 0);
if (bdiGroup.szText) {
_stprintf(szText, _T("%hs: %hs"), bdiGroup.szText, bdi.szText);
} else {
_stprintf(szText, _T("%hs"), bdi.szText);
}
ui->cbValues->insertItem(i, szText);
if (checkSetting(m_dipGroup + j)) {
nCurrentSetting = i;
}
}
ui->cbValues->setCurrentIndex(nCurrentSetting);
}
void DipswitchDialog::dipValueChange(int index)
{
if (ui->cbValues->count() <= 0)
return;
qDebug() <<"Value changed";
BurnDIPInfo bdi = {0, 0, 0, 0, NULL};
struct GameInp *pgi;
int j = 0;
for (int i = 0; i <= index; i++) {
do {
BurnDrvGetDIPInfo(&bdi, m_dipGroup + 1 + j++);
} while (bdi.nFlags == 0);
}
pgi = GameInp + bdi.nInput + m_dipOffset;
pgi->Input.Constant.nConst = (pgi->Input.Constant.nConst & ~bdi.nMask) | (bdi.nSetting & bdi.nMask);
if (bdi.nFlags & 0x40) {
while (BurnDrvGetDIPInfo(&bdi, m_dipGroup + 1 + j++) == 0) {
if (bdi.nFlags == 0) {
pgi = GameInp + bdi.nInput + m_dipOffset;
pgi->Input.Constant.nConst = (pgi->Input.Constant.nConst & ~bdi.nMask) | (bdi.nSetting & bdi.nMask);
} else {
break;
}
}
}
}
bool DipswitchDialog::checkSetting(int i)
{
BurnDIPInfo bdi;
BurnDrvGetDIPInfo(&bdi, i);
struct GameInp* pgi = GameInp + bdi.nInput + m_dipOffset;
if ((pgi->Input.Constant.nConst & bdi.nMask) == bdi.nSetting) {
unsigned char nFlags = bdi.nFlags;
if ((nFlags & 0x0F) <= 1) {
return true;
} else {
for (int j = 1; j < (nFlags & 0x0F); j++) {
BurnDrvGetDIPInfo(&bdi, i + j);
pgi = GameInp + bdi.nInput + m_dipOffset;
if (nFlags & 0x80) {
if ((pgi->Input.Constant.nConst & bdi.nMask) == bdi.nSetting) {
return false;
}
} else {
if ((pgi->Input.Constant.nConst & bdi.nMask) != bdi.nSetting) {
return false;
}
}
}
return true;
}
}
return false;
}
void DipswitchDialog::getDipOffset()
{
BurnDIPInfo bdi;
m_dipOffset = 0;
for (int i = 0; BurnDrvGetDIPInfo(&bdi, i) == 0; i++) {
if (bdi.nFlags == 0xF0) {
m_dipOffset = bdi.nInput;
break;
}
}
}
void DipswitchDialog::makeList()
{
clearList();
BurnDIPInfo bdi;
unsigned int i = 0, j = 0, k = 0;
char* pDIPGroup = NULL;
while (BurnDrvGetDIPInfo(&bdi, i) == 0) {
if ((bdi.nFlags & 0xF0) == 0xF0) {
if (bdi.nFlags == 0xFE || bdi.nFlags == 0xFD) {
pDIPGroup = bdi.szText;
k = i;
}
i++;
} else {
if (checkSetting(i)) {
QTreeWidgetItem *item = new QTreeWidgetItem();
item->setText(0, pDIPGroup);
item->setText(1, bdi.szText);
item->setData(0, Qt::UserRole, k);
item->setData(1, Qt::UserRole, k);
ui->tvSettings->addTopLevelItem(item);
j++;
}
i += (bdi.nFlags & 0x0F);
}
}
}
void DipswitchDialog::clearList()
{
while (ui->tvSettings->topLevelItemCount() > 0)
ui->tvSettings->takeTopLevelItem(0);
}

View File

@ -0,0 +1,34 @@
#ifndef DIPSWITCHDIALOG_H
#define DIPSWITCHDIALOG_H
#include <QDialog>
#include <QTreeWidgetItem>
namespace Ui {
class DipswitchDialog;
}
class DipswitchDialog : public QDialog
{
Q_OBJECT
public:
explicit DipswitchDialog(QWidget *parent = 0);
~DipswitchDialog();
public slots:
int exec();
void reset();
void dipChange(QTreeWidgetItem * item, QTreeWidgetItem * prev);
void dipValueChange(int index);
private:
bool checkSetting(int i);
void getDipOffset();
void makeList();
void clearList();
Ui::DipswitchDialog *ui;
unsigned m_dipOffset;
unsigned m_dipGroup;
};
#endif // DIPSWITCHDIALOG_H

View File

@ -0,0 +1,96 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>DipswitchDialog</class>
<widget class="QDialog" name="DipswitchDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>555</width>
<height>374</height>
</rect>
</property>
<property name="windowTitle">
<string>Dialog</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QTreeWidget" name="tvSettings">
<property name="sizeAdjustPolicy">
<enum>QAbstractScrollArea::AdjustToContents</enum>
</property>
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<column>
<property name="text">
<string>DIPSwitch</string>
</property>
</column>
<column>
<property name="text">
<string>Setting</string>
</property>
</column>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2" stretch="1,0">
<item>
<widget class="QComboBox" name="cbValues"/>
</item>
<item>
<widget class="QPushButton" name="btnDefault">
<property name="text">
<string>Default</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="btnOk">
<property name="text">
<string>Ok</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>btnOk</sender>
<signal>clicked()</signal>
<receiver>DipswitchDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>477</x>
<y>355</y>
</hint>
<hint type="destinationlabel">
<x>290</x>
<y>359</y>
</hint>
</hints>
</connection>
</connections>
</ui>

122
src/burner/qt/driver.cpp Normal file
View File

@ -0,0 +1,122 @@
// Driver Init module
#include "burner.h"
#include <QtWidgets>
int bDrvOkay = 0; // 1 if the Driver has been initted okay, and it's okay to use the BurnDrv functions
static int DrvBzipOpen()
{
BzipOpen(false);
if (BzipStatus())
return 1;
return 0;
}
static int DoLibInit() // Do Init of Burn library driver
{
int nRet = 0;
if (DrvBzipOpen()) {
return 1;
}
ProgressCreate();
nRet = BurnDrvInit();
BzipClose();
ProgressDestroy();
if (nRet) {
return 3;
} else {
return 0;
}
}
// Catch calls to BurnLoadRom() once the emulation has started;
// Intialise the zip module before forwarding the call, and exit cleanly.
static int __cdecl DrvLoadRom(unsigned char* Dest, int* pnWrote, int i)
{
int nRet;
BzipOpen(false);
if ((nRet = BurnExtLoadRom(Dest, pnWrote, i)) != 0) {
char* pszFilename;
BurnDrvGetRomName(&pszFilename, i, 0);
}
BzipClose();
return nRet;
}
int DrvInit(int nDrvNum, bool bRestore)
{
int nStatus;
bDrvOkay = 0;
DrvExit(); // Make sure exitted
nBurnDrvActive = nDrvNum; // Set the driver number
nMaxPlayers = BurnDrvGetMaxPlayers();
GameInpInit(); // Init game input
if (ConfigGameLoad(true)) {
ConfigGameLoadHardwareDefaults();
}
InputMake(true);
GameInpDefault();
nStatus = DoLibInit(); // Init the Burn library's driver
if (nStatus) {
if (nStatus & 2) {
BurnDrvExit(); // Exit the driver
}
return 1;
}
BurnExtLoadRom = DrvLoadRom;
bDrvOkay = 1; // Okay to use all BurnDrv functions
if (BurnDrvGetFlags() & BDF_ORIENTATION_VERTICAL) {
bVidArcaderes = bVidArcaderesVer;
nVidWidth = nVidVerWidth;
nVidHeight = nVidVerHeight;
} else {
bVidArcaderes = bVidArcaderesHor;
nVidWidth = nVidHorWidth;
nVidHeight = nVidHorHeight;
}
nBurnLayer = 0xFF; // show all layers
// Reset the speed throttling code, so we don't 'jump' after the load
//RunReset();
VidExit();
return 0;
}
int DrvInitCallback()
{
return DrvInit(nBurnDrvActive, false);
}
int DrvExit()
{
if (bDrvOkay) {
ConfigGameSave(bSaveInputs);
GameInpExit();
BurnDrvExit(); // Exit the driver
}
BurnExtLoadRom = NULL;
bDrvOkay = 0; // Stop using the BurnDrv functions
bRunPause = 0; // Don't pause when exitted
nBurnDrvActive = ~0U; // no driver selected
return 0;
}

128
src/burner/qt/emuworker.cpp Normal file
View File

@ -0,0 +1,128 @@
#include <QtWidgets>
#include "emuworker.h"
#include "burner.h"
static int GetInput(bool bCopy);
static int RunFrame(int bDraw, int bPause);
static int RunGetNextSound(int bDraw);
EmuWorker::EmuWorker(QObject *parent) :
QObject(parent)
{
m_isRunning = false;
}
bool EmuWorker::init()
{
AudSoundStop();
if (bDrvOkay)
DrvExit();
// we need to initialize sound first
AudSoundInit();
AudSetCallback(RunGetNextSound);
bAudOkay = 1;
DrvInit(m_game, false);
if (!bDrvOkay)
return false;
VidInit();
return true;
}
void EmuWorker::resume()
{
AudSoundPlay();
if (!bDrvOkay) {
m_isRunning = false;
return;
}
m_isRunning = true;
}
void EmuWorker::close()
{
AudSoundStop();
DrvExit();
}
void EmuWorker::pause()
{
m_isRunning = false;
}
void EmuWorker::setGame(int no)
{
m_game = no;
}
void EmuWorker::run()
{
if (m_isRunning && bDrvOkay) {
AudSoundCheck();
}
}
int GetInput(bool bCopy)
{
InputMake(bCopy); // get input
return 0;
}
int RunFrame(int bDraw, int bPause)
{
static int bPrevPause = 0;
static int bPrevDraw = 0;
if (bPrevDraw && !bPause) {
VidPaint(0); // paint the screen (no need to validate)
}
if (!bDrvOkay) {
return 1;
}
if (bPause)
{
GetInput(false); // Update burner inputs, but not game inputs
if (bPause != bPrevPause)
{
VidPaint(2); // Redraw the screen (to ensure mode indicators are updated)
}
}
else
{
nFramesEmulated++;
nCurrentFrame++;
GetInput(true); // Update inputs
}
if (bDraw) {
nFramesRendered++;
if (VidFrame()) { // Do one frame
AudBlankSound();
}
}
else { // frame skipping
pBurnDraw = NULL; // Make sure no image is drawn
BurnDrvFrame();
}
bPrevPause = bPause;
bPrevDraw = bDraw;
return 0;
}
int RunGetNextSound(int bDraw)
{
if (nAudNextSound == NULL) {
return 1;
}
// Render frame with sound
pBurnSoundOut = nAudNextSound;
RunFrame(bDraw, 0);
return 0;
}

26
src/burner/qt/emuworker.h Normal file
View File

@ -0,0 +1,26 @@
#ifndef EMUWORKER_H
#define EMUWORKER_H
#include <QObject>
class EmuWorker : public QObject
{
Q_OBJECT
public:
explicit EmuWorker(QObject *parent = 0);
signals:
public slots:
bool init();
void resume();
void close();
void pause();
void setGame(int no);
void run();
private:
int m_game;
bool m_isRunning;
};
#endif // EMUWORKER_H

63
src/burner/qt/main.cpp Normal file
View File

@ -0,0 +1,63 @@
#include <QtWidgets>
#include "burner.h"
#include "mainwindow.h"
int bRunPause = 0;
bool bAlwaysProcessKeyboardInput;
bool bDoIpsPatch;
TCHAR szAppBurnVer[16];
int nAppVirtualFps = 6000;
TCHAR *GetIsoPath()
{
return NULL;
}
void IpsApplyPatches(UINT8 *, char *)
{
}
void InpDIPSWResetDIPs()
{
}
void Reinitialise(void)
{
}
void wav_pause(bool bResume)
{
}
bool AppProcessKeyboardInput()
{
return true;
}
char *TCHARToANSI(const TCHAR* pszInString, char* pszOutString, int nOutSize)
{
if (pszOutString) {
strcpy(pszOutString, pszInString);
return pszOutString;
}
return (char*)pszInString;
}
int main(int argc, char **argv)
{
QApplication app(argc, argv);
MainWindow mw;
mw.show();
return mw.main(app);
}

View File

@ -0,0 +1,240 @@
#include <QtWidgets>
#ifdef Q_OS_MACX
#include <OpenGL/gl.h>
#else
#include <GL/gl.h>
#endif
#include "mainwindow.h"
#include "burner.h"
#include "selectdialog.h"
#include "ruby/ruby.hpp"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent)
{
createDefaultDirs();
BurnLibInit();
createActions();
createMenus();
createControls();
setWindowTitle("FB Alpha Q");
connectActions();
m_isRunning = false;
InputInit();
show();
ruby::video.driver("X-Video");
ruby::video.set(ruby::Video::Handle, m_viewport->id());
ruby::video.set(ruby::Video::Depth, 24u);
if (ruby::video.init()) {
qDebug() << "Ruby Succefully initialized";
}
}
MainWindow::~MainWindow()
{
BurnLibExit();
AudSoundStop();
AudSoundExit();
ruby::video.term();
}
int MainWindow::main(QApplication &app)
{
m_closeApp = false;
while (!m_closeApp) {
app.processEvents();
m_emulation->run();
}
return 0;
}
void MainWindow::loadGame()
{
int ret = m_selectDlg->exec();
if (ret != QDialog::Accepted) {
return;
}
m_emulation->pause();
m_emulation->setGame(m_selectDlg->selectedDriver());
bool okay = m_emulation->init();
if (!okay) {
QStringList str;
str << BzipText.szText << BzipDetail.szText;
QMessageBox::critical(this, "Error", str.join("\n"));
return;
}
m_isRunning = true;
enableInGame();
m_emulation->resume();
}
void MainWindow::exitEmulator()
{
close();
}
void MainWindow::closeGame()
{
if (bDrvOkay) {
m_emulation->close();
disableInGame();
ruby::video.clear();
ruby::video.refresh();
}
}
void MainWindow::setupInputInterface(QAction *action)
{
InputExit();
nInputSelect = action->data().toInt();
InputInit();
}
void MainWindow::toogleMenu()
{
if (menuBar()->isHidden()) {
menuBar()->show();
}
else menuBar()->hide();
}
void MainWindow::toogleFullscreen()
{
if (isFullScreen())
showNormal();
else showFullScreen();
}
void MainWindow::closeEvent(QCloseEvent *event)
{
m_closeApp = true;
event->accept();
}
void MainWindow::createDefaultDirs()
{
QStringList dirs;
dirs << "config" << "config/games" << "config/ips" << "config/localisation"
<< "config/presets" << "recordings" << "roms" << "savestates"
<< "screenshots" << "support" << "support/previews"
<< "support/titles" << "support/icons" << "support/cheats"
<< "support/hiscores" << "support/samples" << "support/ips"
<< "support/neocdz" << "neocdiso";
QDir current = QDir::currentPath();
foreach (QString dirName, dirs)
current.mkpath(dirName);
}
void MainWindow::createMenus()
{
m_menuGame = menuBar()->addMenu(tr("Game"));
m_menuGame->addAction(m_actionLoadGame);
m_menuGame->addAction(m_actionCloseGame);
m_menuGame->addSeparator();
m_menuGame->addAction(m_actionExitEmulator);
m_menuInput = menuBar()->addMenu(tr("Input"));
m_menuInputPlugin = new QMenu(tr("Plugin"), this);
m_menuInput->addMenu(m_menuInputPlugin);
setupInputInterfaces();
m_menuInput->addAction(m_actionDipswitch);
m_menuMisc = menuBar()->addMenu(tr("Misc"));
m_menuMisc->addAction(m_actionConfigureRomPaths);
m_menuMisc->addAction(m_actionConfigureSupportPaths);
m_menuMisc->addSeparator();
m_menuMisc->addAction(m_actionToogleMenu);
m_menuHelp = menuBar()->addMenu(tr("Help"));
m_menuHelp->addAction(m_actionAbout);
}
void MainWindow::createControls()
{
m_audio = QAudioInterface::get(this);
m_viewport = QRubyViewport::get();
setCentralWidget(m_viewport);
resize(640, 480);
m_emulation = new EmuWorker();
m_selectDlg = new SelectDialog(this);
m_supportPathDlg = new SupportDirsDialog(this);
m_aboutDlg = new AboutDialog(this);
m_dipSwitchDlg = new DipswitchDialog(this);
}
void MainWindow::createActions()
{
m_actionLoadGame = new QAction(tr("Load Game"), this);
m_actionLoadGame->setShortcut(QKeySequence("F6"));
m_actionCloseGame = new QAction(tr("Close Game"), this);
m_actionCloseGame->setEnabled(false);
m_actionConfigureRomPaths = new QAction(tr("Configure ROM paths..."), this);
m_actionConfigureSupportPaths = new QAction(tr("Configure support paths..."), this);
m_actionExitEmulator = new QAction(tr("Exit emulator"), this);
m_actionToogleMenu = new QAction(tr("Toogle Menu"), this);
m_scutToogleMenu = new QShortcut(QKeySequence(tr("F12")), this);
m_scutToogleFullscreen = new QShortcut(QKeySequence(tr("Alt+Return")), this);
m_scutToogleMenu->setContext(Qt::ApplicationShortcut);
m_actionAbout = new QAction(tr("About FBA"), this);
m_actionDipswitch = new QAction(tr("Configure DIPs"), this);
m_actionDipswitch->setEnabled(false);
}
void MainWindow::connectActions()
{
connect(m_actionLoadGame, SIGNAL(triggered()), this, SLOT(loadGame()));
connect(m_actionCloseGame, SIGNAL(triggered()), this, SLOT(closeGame()));
connect(m_actionConfigureRomPaths, SIGNAL(triggered()), m_selectDlg, SLOT(editRomPaths()));
connect(m_actionExitEmulator, SIGNAL(triggered()), this, SLOT(exitEmulator()));
connect(m_actionConfigureSupportPaths, SIGNAL(triggered()), m_supportPathDlg, SLOT(exec()));
connect(m_actionAbout, SIGNAL(triggered()), m_aboutDlg, SLOT(exec()));
connect(m_actionToogleMenu, SIGNAL(triggered()), this, SLOT(toogleMenu()));
connect(m_scutToogleMenu, SIGNAL(activated()), this, SLOT(toogleMenu()));
connect(m_scutToogleFullscreen, SIGNAL(activated()), this, SLOT(toogleFullscreen()));
connect(m_actionDipswitch, SIGNAL(triggered()), m_dipSwitchDlg, SLOT(exec()));
}
void MainWindow::enableInGame()
{
m_actionCloseGame->setEnabled(true);
m_actionDipswitch->setEnabled(true);
}
void MainWindow::disableInGame()
{
m_actionCloseGame->setEnabled(false);
m_actionDipswitch->setEnabled(false);
}
void MainWindow::setupInputInterfaces()
{
m_actionInputPlugins = new QActionGroup(this);
m_actionInputPlugins->setExclusive(true);
m_inputInterfaces = QVector<const InputInOut *>::fromStdVector(InputGetInterfaces());
for (int i = 0; i < m_inputInterfaces.size(); i++) {
const InputInOut *intf = m_inputInterfaces[i];
QAction *action = new QAction(QString(intf->szModuleName), this);
action->setCheckable(true);
action->setChecked(i ? false : true);
action->setData(QVariant(i));
m_actionInputPlugins->addAction(action);
}
m_menuInputPlugin->addActions(m_actionInputPlugins->actions());
connect(m_actionInputPlugins, SIGNAL(triggered(QAction*)),
this, SLOT(setupInputInterface(QAction*)));
}

View File

@ -0,0 +1,78 @@
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QTimer>
#include <QAction>
#include <QActionGroup>
#include <QShortcut>
#include <QMenu>
#include <QThread>
#include <QVector>
#include "selectdialog.h"
#include "qrubyviewport.h"
#include "qaudiointerface.h"
#include "supportdirsdialog.h"
#include "aboutdialog.h"
#include "dipswitchdialog.h"
#include "emuworker.h"
#include "burner.h"
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
int main(QApplication &app);
signals:
public slots:
void loadGame();
void exitEmulator();
void closeGame();
void setupInputInterface(QAction *action);
void toogleMenu();
void toogleFullscreen();
void closeEvent(QCloseEvent *event);
private:
void createDefaultDirs();
void createMenus();
void createControls();
void createActions();
void connectActions();
void enableInGame();
void disableInGame();
int m_game;
bool m_isRunning;
QRubyViewport *m_viewport;
QAudioInterface *m_audio;
EmuWorker *m_emulation;
SelectDialog *m_selectDlg;
SupportDirsDialog *m_supportPathDlg;
AboutDialog *m_aboutDlg;
DipswitchDialog *m_dipSwitchDlg;
QMenu *m_menuGame;
QMenu *m_menuMisc;
QMenu *m_menuHelp;
QMenu *m_menuInput;
QMenu *m_menuInputPlugin;
QAction *m_actionConfigureRomPaths;
QAction *m_actionConfigureSupportPaths;
QAction *m_actionAbout;
QAction *m_actionLoadGame;
QAction *m_actionExitEmulator;
QAction *m_actionCloseGame;
QAction *m_actionToogleMenu;
QAction *m_actionDipswitch;
QShortcut *m_scutToogleMenu;
QShortcut *m_scutToogleFullscreen;
void setupInputInterfaces();
QVector<const InputInOut *> m_inputInterfaces;
QActionGroup *m_actionInputPlugins;
bool m_closeApp;
};
#endif // MAINWINDOW_H

500
src/burner/qt/neocdlist.cpp Normal file
View File

@ -0,0 +1,500 @@
// ---------------------------------------------------------------------------------------
// NeoGeo CD Game Info Module (by CaptainCPS-X)
// ---------------------------------------------------------------------------------------
#include "burner.h"
#include "../neocdlist.h"
struct NGCDGAME games[] =
{
// ---------------------------------------------------------------------------------------------------------------------------------------------//
// * Name * Title * Year * Company * Game ID //
// ---------------------------------------------------------------------------------------------------------------------------------------------//
{ _T("nam1975") , _T("NAM-1975") , _T("1990") , _T("SNK") , 0x0001 }, //
{ _T("bstars") , _T("Baseball Stars Professional") , _T("1991") , _T("SNK") , 0x0002 }, //
{ _T("tpgolf") , _T("Top Player's Golf") , _T("1990") , _T("SNK") , 0x0003 }, //
{ _T("mahretsu") , _T("Mahjong Kyo Retsuden - Nishi Nihon Hen") , _T("1990") , _T("SNK") , 0x0004 }, //
{ _T("maglord") , _T("Magician Lord") , _T("1990") , _T("ADK") , 0x0005 }, //
{ _T("ridhero") , _T("Riding Hero") , _T("1991") , _T("SNK") , 0x0006 }, //
{ _T("alpham2") , _T("Alpha Mission II / ASO II - Last Guardian") , _T("1994") , _T("SNK") , 0x0007 }, //
{ _T("ncombat") , _T("Ninja Combat") , _T("1990") , _T("SNK/ADK") , 0x0009 }, //
{ _T("cyberlip") , _T("Cyber-Lip") , _T("1991") , _T("SNK") , 0x0010 }, //
{ _T("superspy") , _T("The Super Spy") , _T("1990") , _T("SNK") , 0x0011 }, //
{ _T("mutnat") , _T("Mutation Nation") , _T("1995") , _T("SNK") , 0x0014 }, //
{ _T("sengoku") , _T("Sengoku / Sengoku Denshou") , _T("1994") , _T("SNK") , 0x0017 }, //
{ _T("burningf") , _T("Burning Fight") , _T("1994") , _T("SNK") , 0x0018 }, //
{ _T("lbowling") , _T("League Bowling") , _T("1994") , _T("SNK") , 0x0019 }, //
{ _T("gpilots") , _T("Ghost Pilots") , _T("1991") , _T("SNK") , 0x0020 }, //
{ _T("joyjoy") , _T("Puzzled / Joy Joy Kid") , _T("1990") , _T("SNK") , 0x0021 }, //
{ _T("bjourney") , _T("Blue's Journey / Raguy") , _T("1990") , _T("SNK/ADK") , 0x0022 }, //
{ _T("lresort") , _T("Last Resort") , _T("1994") , _T("SNK") , 0x0024 }, //
{ _T("2020bb") , _T("2020 Super Baseball") , _T("1994") , _T("SNK") , 0x0030 }, //
{ _T("socbrawl") , _T("Soccer Brawl") , _T("1991") , _T("SNK") , 0x0031 }, //
{ _T("roboarmy") , _T("Robo Army") , _T("1991") , _T("SNK") , 0x0032 }, //
{ _T("fatfury") , _T("Fatal Fury - The Battle of Fury") , _T("1994") , _T("SNK") , 0x0033 }, //
{ _T("fbfrenzy") , _T("Football Frenzy") , _T("1994") , _T("SNK") , 0x0034 }, //
{ _T("crswords") , _T("Crossed Swords") , _T("1994") , _T("SNK/ADK") , 0x0037 }, //
{ _T("rallych") , _T("Rally Chase") , _T("1991") , _T("SNK/ADK") , 0x0038 }, //
{ _T("kotm2") , _T("King of the Monsters 2") , _T("1992") , _T("SNK") , 0x0039 }, //
{ _T("sengoku2") , _T("Sengoku 2 / Sengoku Denshou 2") , _T("1994") , _T("SNK") , 0x0040 }, //
{ _T("bstars2") , _T("Baseball Stars 2") , _T("1992") , _T("SNK") , 0x0041 }, //
{ _T("3countb") , _T("3 Count Bout / Fire Suplex") , _T("1995") , _T("SNK") , 0x0043 }, //
{ _T("aof") , _T("Art of Fighting / Ryuuko no Ken") , _T("1994") , _T("SNK") , 0x0044 }, //
{ _T("samsho") , _T("Samurai Shodown / Samurai Spirits") , _T("1993") , _T("SNK") , 0x0045 }, //
{ _T("tophuntr") , _T("Top Hunter - Roddy & Cathy") , _T("1994") , _T("SNK") , 0x0046 }, //
{ _T("fatfury2") , _T("Fatal Fury 2 / Garou Densetsu 2 - Aratanaru Tatakai") , _T("1994") , _T("SNK") , 0x0047 }, //
{ _T("janshin") , _T("Janshin Densetsu - Quest of the Jongmaster") , _T("1995") , _T("Yubis") , 0x0048 }, //
{ _T("ncommand") , _T("Ninja Commando") , _T("1992") , _T("SNK") , 0x0050 }, //
{ _T("viewpoin") , _T("Viewpoint") , _T("1992") , _T("Sammy/Aicom") , 0x0051 }, //
{ _T("ssideki") , _T("Super Sidekicks / Tokuten Oh") , _T("1993") , _T("SNK") , 0x0052 }, //
{ _T("wh1") , _T("World Heroes") , _T("1992") , _T("ADK") , 0x0053 }, //
{ _T("crsword2") , _T("Crossed Swords II") , _T("1995") , _T("SNK/ADK") , 0x0054 }, //
{ _T("kof94") , _T("The King of Fighters '94 (JP)") , _T("1994") , _T("SNK") , 0x0055 }, //
{ _T("kof94ju") , _T("The King of Fighters '94 (JP-US)") , _T("1994") , _T("SNK") , 0x1055 }, // custom id
{ _T("aof2") , _T("Art of Fighting 2 / Ryuuko no Ken 2") , _T("1994") , _T("SNK") , 0x0056 }, //
{ _T("wh2") , _T("World Heroes 2") , _T("1995") , _T("SNK/ADK") , 0x0057 }, //
{ _T("fatfursp") , _T("Fatal Fury Special / Garou Densetsu Special") , _T("1994") , _T("SNK") , 0x0058 }, //
{ _T("savagere") , _T("Savage Reign / Fu'un Mokujiroku - Kakutou Sousei") , _T("1995") , _T("SNK") , 0x0059 }, //
{ _T("ssideki2") , _T("Super Sidekicks 2 / Tokuten Oh 2") , _T("1994") , _T("SNK") , 0x0061 }, //
{ _T("samsho2") , _T("Samurai Shodown 2 / Shin Samurai Spirits") , _T("1994") , _T("SNK") , 0x0063 }, //
{ _T("wh2j") , _T("World Heroes 2 Jet") , _T("1995") , _T("SNK/ADK") , 0x0064 }, //
{ _T("wjammers") , _T("Windjammers / Flying Power Disc") , _T("1994") , _T("Data East") , 0x0065 }, //
{ _T("karnovr") , _T("Karnov's Revenge / Fighters History Dynamite") , _T("1994") , _T("Data East") , 0x0066 }, //
{ _T("pspikes2") , _T("Power Spikes II") , _T("1994") , _T("Video System") , 0x0068 }, //
{ _T("aodk") , _T("Aggressors of Dark Kombat / Tsuukai GanGan Koushinkyoku") , _T("1994"), _T("SNK/ADK") , 0x0074 }, //
{ _T("sonicwi2") , _T("Aero Fighters 2 / Sonic Wings 2") , _T("1994") , _T("Video System") , 0x0075 }, //
{ _T("galaxyfg") , _T("Galaxy Fight - Universal Warriors") , _T("1995") , _T("Sunsoft") , 0x0078 }, //
{ _T("strhoop") , _T("Street Hoop / Dunk Dream") , _T("1994") , _T("Data East") , 0x0079 }, //
{ _T("quizkof") , _T("Quiz King of Fighters") , _T("1993") , _T("SNK/Saurus") , 0x0080 }, //
{ _T("ssideki3") , _T("Super Sidekicks 3 - The Next Glory / Tokuten Oh 3 - Eikoue No Chousen"), _T("1995") , _T("SNK") , 0x0081 }, //
{ _T("doubledr") , _T("Double Dragon") , _T("1995") , _T("Technos") , 0x0082 }, //
{ _T("pbobblen") , _T("Puzzle Bobble / Bust-A-Move") , _T("1994") , _T("SNK") , 0x0083 }, //
{ _T("kof95") , _T("The King of Fighters '95 (JP-US)") , _T("1995") , _T("SNK") , 0x0084 }, //
{ _T("kof95r1") , _T("The King of Fighters '95 (JP-US)(Rev 1)") , _T("1995") , _T("SNK") , 0x1084 }, //
{ _T("ssrpg") , _T("Shinsetsu Samurai Spirits - Bushidohretsuden") , _T("1997") , _T("SNK") , 0x0085 }, //
{ _T("samsho3") , _T("Samurai Shodown 3 / Samurai Spirits 3") , _T("1995") , _T("SNK") , 0x0087 }, //
{ _T("stakwin") , _T("Stakes Winner - GI Kanzen Seiha Heno Machi") , _T("1995") , _T("Saurus") , 0x0088 }, //
{ _T("pulstar") , _T("Pulstar") , _T("1995") , _T("Aicom") , 0x0089 }, //
{ _T("whp") , _T("World Heroes Perfect") , _T("1995") , _T("ADK") , 0x0090 }, //
{ _T("kabukikl") , _T("Kabuki Klash / Tengai Makyou Shinden - Far East of Eden") , _T("1995"), _T("Hudson") , 0x0092 }, //
{ _T("gowcaizr") , _T("Voltage Fighter Gowcaizer / Choujin Gakuen Gowcaizer"), _T("1995") , _T("Technos") , 0x0094 }, //
{ _T("rbff1") , _T("Real Bout Fatal Fury") , _T("1995") , _T("SNK") , 0x0095 }, //
{ _T("aof3") , _T("Art of Fighting 3: Path of the Warrior") , _T("1996") , _T("SNK") , 0x0096 }, //
{ _T("sonicwi3") , _T("Aero Fighters 3 / Sonic Wings 3") , _T("1995") , _T("SNK") , 0x0097 }, //
{ _T("fromanc2") , _T("Idol Mahjong Final Romance 2") , _T("1995") , _T("Video Systems") , 0x0098 }, //
{ _T("turfmast") , _T("Neo Turf Masters / Big Tournament Golf") , _T("1996") , _T("Nazca") , 0x0200 }, //
{ _T("mslug") , _T("Metal Slug - Super Vehicle-001") , _T("1996") , _T("Nazca") , 0x0201 }, //
{ _T("mosyougi") , _T("Shougi no Tatsujin - Master of Syougi") , _T("1995") , _T("ADK") , 0x0203 }, //
{ _T("adkworld") , _T("ADK World / ADK Special") , _T("1995") , _T("ADK") , 0x0204 }, //
{ _T("ngcdsp") , _T("Neo Geo CD Special") , _T("1995") , _T("SNK") , 0x0205 }, //
{ _T("zintrick") , _T("Zintrick / Oshidashi Zintrick") , _T("1996") , _T("ADK") , 0x0211 }, //
{ _T("overtop") , _T("OverTop") , _T("1996") , _T("ADK") , 0x0212 }, //
{ _T("neodrift") , _T("Neo DriftOut") , _T("1996") , _T("Visco") , 0x0213 }, //
{ _T("kof96") , _T("The King of Fighters '96") , _T("1996") , _T("SNK") , 0x0214 }, //
{ _T("ninjamas") , _T("Ninja Master's - Haou Ninpou-Chou") , _T("1996") , _T("ADK/SNK") , 0x0217 }, //
{ _T("ragnagrd") , _T("Ragnagard / Shinouken") , _T("1996") , _T("Saurus") , 0x0218 }, //
{ _T("pgoal") , _T("Pleasuregoal - 5 on 5 Mini Soccer / Futsal") , _T("1996") , _T("Saurus") , 0x0219 }, //
{ _T("ironclad") , _T("Ironclad / Choutetsu Brikin'ger") , _T("1996") , _T("Saurus") , 0x0220 }, //
{ _T("magdrop2") , _T("Magical Drop 2") , _T("1996") , _T("Data East") , 0x0221 }, //
{ _T("samsho4") , _T("Samurai Shodown IV - Amakusa's Revenge") , _T("1996") , _T("SNK") , 0x0222 }, //
{ _T("rbffspec") , _T("Real Bout Fatal Fury Special") , _T("1996") , _T("SNK") , 0x0223 }, //
{ _T("twinspri") , _T("Twinkle Star Sprites") , _T("1996") , _T("ADK") , 0x0224 }, //
{ _T("kof96ngc") , _T("The King of Fighters '96 NEOGEO Collection") , _T("1996") , _T("SNK") , 0x0229 }, //
{ _T("breakers") , _T("Breakers") , _T("1996") , _T("Visco") , 0x0230 }, //
{ _T("kof97") , _T("The King of Fighters '97") , _T("1997") , _T("SNK") , 0x0232 }, //
{ _T("lastblad") , _T("The Last Blade / Bakumatsu Roman - Gekka no Kenshi") , _T("1997") , _T("SNK") , 0x0234 }, //
{ _T("rbff2") , _T("Real Bout Fatal Fury 2 / Garou Densetsu 2 - Aratanaru Tatakai"), _T("1998"), _T("SNK") , 0x0240 }, //
{ _T("mslug2") , _T("Metal Slug 2 - Super Vehicle-001/II") , _T("1998") , _T("SNK") , 0x0241 }, //
{ _T("kof98") , _T("The King of Fighters '98 - The Slugfest") , _T("1998") , _T("SNK") , 0x0242 }, //
{ _T("lastbld2") , _T("The Last Blade 2") , _T("1998") , _T("SNK") , 0x0243 }, //
{ _T("kof99") , _T("The King of Fighters '99 - Millennium Battle") , _T("1999") , _T("SNK") , 0x0251 }, //
{ _T("fatfury3") , _T("Fatal Fury 3 - Road to the Final Victory / Garou Densetsu 3 - Harukanaru Tatakai"), _T("1995"), _T("SNK"), 0x069c }, //
};
NGCDGAME* GetNeoGeoCDInfo(unsigned int nID)
{
for(unsigned int nGame = 0; nGame < (sizeof(games) / sizeof(NGCDGAME)); nGame++) {
if(nID == games[nGame].id ) {
return &games[nGame];
}
}
return NULL;
}
NGCDGAME* game;
// Get the title
int GetNeoCDTitle(unsigned int nGameID)
{
game = (NGCDGAME*)malloc(sizeof(NGCDGAME));
memset(game, 0, sizeof(NGCDGAME));
if(GetNeoGeoCDInfo(nGameID))
{
memcpy(game, GetNeoGeoCDInfo(nGameID), sizeof(NGCDGAME));
return 1;
}
game = NULL;
return 0;
}
void iso9660_ReadOffset(unsigned char *Dest, FILE* fp, unsigned int lOffset, unsigned int lSize, unsigned int lLength)
{
if(fp == NULL) return;
if (Dest == NULL) return;
fseek(fp, lOffset, SEEK_SET);
fread(Dest, lLength, lSize, fp);
}
static void NeoCDList_iso9660_CheckDirRecord(FILE* fp, int nSector)
{
int nSectorLength = 2048;
//int nFile = 0;
unsigned int lBytesRead = 0;
//int nLen = 0;
unsigned int lOffset = 0;
bool bNewSector = false;
bool bRevisionQueve = false;
int nRevisionQueveID = 0;
lOffset = (nSector * nSectorLength);
unsigned char* nLenDR = (unsigned char*)malloc(1 * sizeof(unsigned char));
unsigned char* Flags = (unsigned char*)malloc(1 * sizeof(unsigned char));
unsigned char* ExtentLoc = (unsigned char*)malloc(8 * sizeof(unsigned char));
unsigned char* Data = (unsigned char*)malloc(0x10b * sizeof(unsigned char));
unsigned char* LEN_FI = (unsigned char*)malloc(1 * sizeof(unsigned char));
char *File = (char*)malloc(32 * sizeof(char));
while(1)
{
iso9660_ReadOffset(nLenDR, fp, lOffset, 1, sizeof(unsigned char));
if(nLenDR[0] == 0x22) {
lOffset += nLenDR[0];
lBytesRead += nLenDR[0];
continue;
}
if(nLenDR[0] < 0x22)
{
if(bNewSector)
{
if(bRevisionQueve) {
bRevisionQueve = false;
GetNeoCDTitle(nRevisionQueveID);
}
return;
}
nLenDR[0] = 0;
iso9660_ReadOffset(nLenDR, fp, lOffset + 1, 1, sizeof(unsigned char));
if(nLenDR[0] < 0x22) {
lOffset += (2048 - lBytesRead);
lBytesRead = 0;
bNewSector = true;
continue;
}
}
bNewSector = false;
iso9660_ReadOffset(Flags, fp, lOffset + 25, 1, sizeof(unsigned char));
if(!(Flags[0] & (1 << 1)))
{
iso9660_ReadOffset(ExtentLoc, fp, lOffset + 2, 8, sizeof(unsigned char));
char szValue[9];
sprintf(szValue, "%02x%02x%02x%02x", ExtentLoc[4], ExtentLoc[5], ExtentLoc[6], ExtentLoc[7]);
unsigned int nValue = 0;
sscanf(szValue, "%x", &nValue);
iso9660_ReadOffset(Data, fp, nValue * 2048, 0x10a, sizeof(unsigned char));
char szData[8];
sprintf(szData, "%c%c%c%c%c%c%c", Data[0x100], Data[0x101], Data[0x102], Data[0x103], Data[0x104], Data[0x105], Data[0x106]);
if(!strncmp(szData, "NEO-GEO", 7))
{
char id[] = "0000";
sprintf(id, "%02X%02X", Data[0x108], Data[0x109]);
unsigned int nID = 0;
sscanf(id, "%x", &nID);
iso9660_ReadOffset(LEN_FI, fp, lOffset + 32, 1, sizeof(unsigned char));
iso9660_ReadOffset((unsigned char*)File, fp, lOffset + 33, LEN_FI[0], sizeof(char));
strncpy(File, File, LEN_FI[0]);
File[LEN_FI[0]] = 0;
// King of Fighters '94, The (1994)(SNK)(JP)
// 10-6-1994 (P1.PRG)
if(nID == 0x0055 && Data[0x67] == 0xDE) {
// ...continue
}
// King of Fighters '94, The (1994)(SNK)(JP-US)
// 11-21-1994 (P1.PRG)
if(nID == 0x0055 && Data[0x67] == 0xE6) {
// Change to custom revision id
nID = 0x1055;
}
// King of Fighters '95, The (1995)(SNK)(JP-US)[!][NGCD-084 MT B01, B03-B06, NGCD-084E MT B01]
// 9-11-1995 (P1.PRG)
if(nID == 0x0084 && Data[0x6C] == 0xC0) {
// ... continue
}
// King of Fighters '95, The (1995)(SNK)(JP-US)[!][NGCD-084 MT B10, NGCD-084E MT B03]
// 10-5-1995 (P1.PRG)
if(nID == 0x0084 && Data[0x6C] == 0xFF) {
// Change to custom revision id
nID = 0x1084;
}
// King of Fighters '96 NEOGEO Collection, The
if(nID == 0x0229) {
bRevisionQueve = false;
GetNeoCDTitle(nID);
break;
}
// King of Fighters '96, The
if(nID == 0x0214) {
bRevisionQueve = true;
nRevisionQueveID = nID;
lOffset += nLenDR[0];
lBytesRead += nLenDR[0];
continue;
}
GetNeoCDTitle(nID);
break;
}
}
lOffset += nLenDR[0];
lBytesRead += nLenDR[0];
}
if (nLenDR) {
free(nLenDR);
nLenDR = NULL;
}
if (Flags) {
free(Flags);
Flags = NULL;
}
if (ExtentLoc) {
free(ExtentLoc);
ExtentLoc = NULL;
}
if (Data) {
free(Data);
Data = NULL;
}
if (LEN_FI) {
free(LEN_FI);
LEN_FI = NULL;
}
if (File) {
free(File);
File = NULL;
}
}
// Check the specified ISO, and proceed accordingly
static int NeoCDList_CheckISO(TCHAR* pszFile)
{
if(!pszFile) {
// error
return 0;
}
// Make sure we have a valid ISO file extension...
if(_tcsstr(pszFile, _T(".iso")) || _tcsstr(pszFile, _T(".ISO")) )
{
FILE* fp = _tfopen(pszFile, _T("rb"));
if(fp)
{
// Read ISO and look for 68K ROM standard program header, ID is always there
// Note: This function works very quick, doesn't compromise performance :)
// it just read each sector first 264 bytes aproximately only.
// Get ISO Size (bytes)
fseek(fp, 0, SEEK_END);
unsigned int lSize = 0;
lSize = ftell(fp);
//rewind(fp);
fseek(fp, 0, SEEK_SET);
// If it has at least 16 sectors proceed
if(lSize > (2048 * 16))
{
// Check for Standard ISO9660 Identifier
unsigned char IsoHeader[2048 * 16 + 1];
unsigned char IsoCheck[6];
fread(IsoHeader, 1, 2048 * 16 + 1, fp); // advance to sector 16 and PVD Field 2
fread(IsoCheck, 1, 5, fp); // get Standard Identifier Field from PVD
// Verify that we have indeed a valid ISO9660 MODE1/2048
if(!memcmp(IsoCheck, "CD001", 5))
{
//bprintf(PRINT_NORMAL, _T(" Standard ISO9660 Identifier Found. \n"));
iso9660_VDH vdh;
// Get Volume Descriptor Header
memset(&vdh, 0, sizeof(vdh));
//memcpy(&vdh, iso9660_ReadOffset(fp, (2048 * 16), sizeof(vdh)), sizeof(vdh));
iso9660_ReadOffset((unsigned char*)&vdh, fp, 2048 * 16, 1, sizeof(vdh));
// Check for a valid Volume Descriptor Type
if(vdh.vdtype == 0x01)
{
#if 0
// This will fail on 64-bit due to differing variable sizes in the pvd struct
// Get Primary Volume Descriptor
iso9660_PVD pvd;
memset(&pvd, 0, sizeof(pvd));
//memcpy(&pvd, iso9660_ReadOffset(fp, (2048 * 16), sizeof(pvd)), sizeof(pvd));
iso9660_ReadOffset((unsigned char*)&pvd, fp, 2048 * 16, 1, sizeof(pvd));
// ROOT DIRECTORY RECORD
// Handle Path Table Location
char szRootSector[32];
unsigned int nRootSector = 0;
sprintf(szRootSector, "%02X%02X%02X%02X",
pvd.root_directory_record.location_of_extent[4],
pvd.root_directory_record.location_of_extent[5],
pvd.root_directory_record.location_of_extent[6],
pvd.root_directory_record.location_of_extent[7]);
// Convert HEX string to Decimal
sscanf(szRootSector, "%X", &nRootSector);
#else
// Just read from the file directly at the correct offset (0x8000 + 0x9e for the start of the root directory record)
unsigned char buffer[8];
char szRootSector[4];
unsigned int nRootSector = 0;
fseek(fp, 0x809e, SEEK_SET);
fread(buffer, 1, 8, fp);
sprintf(szRootSector, "%02x%02x%02x%02x", buffer[4], buffer[5], buffer[6], buffer[7]);
sscanf(szRootSector, "%x", &nRootSector);
#endif
// Process the Root Directory Records
NeoCDList_iso9660_CheckDirRecord(fp, nRootSector);
// Path Table Records are not processed, since NeoGeo CD should not have subdirectories
// ...
}
} else {
//bprintf(PRINT_NORMAL, _T(" Standard ISO9660 Identifier Not Found, cannot continue. \n"));
return 0;
}
}
} else {
//bprintf(PRINT_NORMAL, _T(" Couldn't open %s \n"), GetIsoPath());
return 0;
}
if(fp) fclose(fp);
} else {
//bprintf(PRINT_NORMAL, _T(" File doesn't have a valid ISO extension [ .iso / .ISO ] \n"));
return 0;
}
return 1;
}
// This will do everything
int GetNeoGeoCD_Identifier()
{
if(!GetIsoPath() || !IsNeoGeoCD()) {
return 0;
}
// Make sure we have a valid ISO file extension...
if(_tcsstr(GetIsoPath(), _T(".iso")) || _tcsstr(GetIsoPath(), _T(".ISO")) )
{
if(_tfopen(GetIsoPath(), _T("rb")))
{
// Read ISO and look for 68K ROM standard program header, ID is always there
// Note: This function works very quick, doesn't compromise performance :)
// it just read each sector first 264 bytes aproximately only.
NeoCDList_CheckISO(GetIsoPath());
} else {
bprintf(PRINT_NORMAL, _T(" Couldn't open %s \n"), GetIsoPath());
return 0;
}
} else {
bprintf(PRINT_NORMAL, _T(" File doesn't have a valid ISO extension [ .iso / .ISO ] \n"));
return 0;
}
return 1;
}
int NeoCDInfo_Init()
{
NeoCDInfo_Exit();
return GetNeoGeoCD_Identifier();
}
TCHAR* NeoCDInfo_Text(int nText)
{
if(!game || !IsNeoGeoCD() || !bDrvOkay) return NULL;
switch(nText)
{
case DRV_NAME: return game->pszName;
case DRV_FULLNAME: return game->pszTitle;
case DRV_MANUFACTURER: return game->pszCompany;
case DRV_DATE: return game->pszYear;
}
return NULL;
}
int NeoCDInfo_ID()
{
if(!game || !IsNeoGeoCD() || !bDrvOkay) return 0;
return game->id;
}
void NeoCDInfo_Exit()
{
if(game) {
free(game);
game = NULL;
}
}

View File

@ -0,0 +1,42 @@
#include <QProgressDialog>
#include <QDebug>
#include <QApplication>
#include "burner.h"
QProgressDialog *dlgProgress = nullptr;
void ProgressCreate()
{
if (dlgProgress == nullptr) {
dlgProgress = new QProgressDialog();
dlgProgress->setModal(true);
dlgProgress->setRange(0, 100);
dlgProgress->setWindowTitle(QObject::tr("Working..."));
}
dlgProgress->setValue(0);
dlgProgress->show();
QApplication::processEvents();
}
void ProgressDestroy()
{
dlgProgress->close();
}
int ProgressUpdateBurner(double dProgress, const TCHAR* pszText, bool bAbs)
{
if (dlgProgress != nullptr && dlgProgress->isVisible()) {
dlgProgress->setValue(dlgProgress->value() + dProgress * 100);
dlgProgress->setLabelText(pszText);
QApplication::processEvents();
}
}
bool ProgressIsRunning()
{
if (dlgProgress != nullptr) {
if (dlgProgress->isVisible())
return true;
}
return false;
}

View File

@ -0,0 +1,375 @@
#include <QtWidgets>
#include <QtMultimedia>
#include "burner.h"
#include "qaudiointerface.h"
#define QT_DEBUG_SOUNDBACKEND 0
QAudioInterface *qAudio = nullptr;
int (*QtSoundGetNextSound)(int);
static int nQtAudioFps = 0;
static int cbLoopLen = 0;
static QAudioInterfaceBuffer *qSoundBuffer = nullptr;
static int QtSoundGetNextSoundFiller(int)
{
qDebug() << __func__;
if (nAudNextSound == nullptr)
return 1;
memset(nAudNextSound, 0, nAudSegLen * 4);
return 0;
}
static int QtSoundBlankSound()
{
qDebug() << __func__;
if (nAudNextSound != nullptr)
AudWriteSilence();
return 0;
}
static int QtSoundCheck()
{
int avail = 0;
while ((avail = qSoundBuffer->bytesAvailable()) > (nAudAllocSegLen * 3)) {
return 0;
}
QtSoundGetNextSound(1);
qSoundBuffer->writeData((const char *)nAudNextSound, nAudSegLen << 2);
return 0;
}
static int QtSoundExit()
{
qDebug() << __func__;
return 0;
}
static int QtSoundSetCallback(int (*pCallback)(int))
{
qDebug() << __func__;
if (pCallback == nullptr)
QtSoundGetNextSound = QtSoundGetNextSoundFiller;
else
QtSoundGetNextSound = pCallback;
return 0;
}
static int QtSoundInit()
{
qDebug() << __func__;
nQtAudioFps = nAppVirtualFps;
nAudSegLen = (nAudSampleRate[0] * 100 + (nQtAudioFps / 2)) / nQtAudioFps;
// seglen * 2 channels * 2 bytes per sample (16bits)
nAudAllocSegLen = nAudSegLen * 4;
// seg * nsegs * 2 channels
if (qSoundBuffer != nullptr)
delete qSoundBuffer;
qSoundBuffer = new QAudioInterfaceBuffer();
qSoundBuffer->setBufferSize(nAudAllocSegLen * nAudSegCount);
nAudNextSound = new short[nAudAllocSegLen / sizeof(short)];
QtSoundSetCallback(nullptr);
QtSoundGetNextSoundFiller(0);
//qSoundBuffer->writeData((const char *) nAudNextSound, nAudAllocSegLen);
//qSoundBuffer->writeData((const char *) nAudNextSound, nAudAllocSegLen);
pBurnSoundOut = nAudNextSound;
nBurnSoundRate = nAudSampleRate[0];
nBurnSoundLen = nAudAllocSegLen;
qAudio = QAudioInterface::get();
qAudio->setBuffer(qSoundBuffer);
QAudioFormat format;
format.setChannelCount(2);
format.setSampleRate(nAudSampleRate[0]);
format.setSampleType(QAudioFormat::SignedInt);
format.setByteOrder(QAudioFormat::LittleEndian);
format.setSampleSize(16);
qAudio->setFormat(format);
// we need to invoke this method on sound's thread, so we can't call this
// method from our object
QMetaObject::invokeMethod(qAudio, "init", Qt::QueuedConnection);
bAudOkay = 1;
return 0;
}
static int QtSoundPlay()
{
qDebug() << __func__;
QMetaObject::invokeMethod(qAudio, "play", Qt::QueuedConnection);
bAudPlaying = 1;
return 0;
}
static int QtSoundStop()
{
qDebug() << __func__;
if (qAudio)
QMetaObject::invokeMethod(qAudio, "stop", Qt::QueuedConnection);
bAudPlaying = 0;
return 0;
}
static int QtSoundSetVolume()
{
qDebug() << __func__;
return 1;
}
static int QtSoundGetSettings(InterfaceInfo *info)
{
Q_UNUSED(info);
return 0;
}
struct AudOut AudOutQtSound = { QtSoundBlankSound, QtSoundCheck,
QtSoundInit, QtSoundSetCallback, QtSoundPlay,
QtSoundStop, QtSoundExit, QtSoundSetVolume,
QtSoundGetSettings, ("qt-audio") };
QAudioInterface *QAudioInterface::m_onlyInstance = nullptr;
QAudioInterface::QAudioInterface(QObject *parent) :
QThread()
{
// start thread
start();
m_audioOutput = nullptr;
QObject::moveToThread(this);
}
QAudioInterface::~QAudioInterface()
{
if (m_audioOutput != nullptr) {
delete m_audioOutput;
m_audioOutput = nullptr;
}
}
void QAudioInterface::run()
{
exec();
}
QAudioInterface *QAudioInterface::get(QObject *parent)
{
if (m_onlyInstance != nullptr)
return m_onlyInstance;
m_onlyInstance = new QAudioInterface(parent);
return m_onlyInstance;
}
void QAudioInterface::init()
{
if (m_audioOutput != nullptr) {
stop();
delete m_audioOutput;
m_audioOutput = nullptr;
}
m_audioOutput = new QAudioOutput(m_format, this);
m_audioOutput->setBufferSize(nAudAllocSegLen);
}
void QAudioInterface::play()
{
if (m_buffer != nullptr)
m_buffer->start();
m_audioOutput->start(m_buffer);
}
void QAudioInterface::stop()
{
m_audioOutput->stop();
if (m_buffer != nullptr)
m_buffer->stop();
}
void QAudioInterface::setBuffer(QAudioInterfaceBuffer *buffer)
{
m_buffer = buffer;
}
void QAudioInterface::setFormat(QAudioFormat &format)
{
m_format = format;
}
void QAudioInterface::writeMoreData()
{
}
QAudioInterfaceBuffer::QAudioInterfaceBuffer(int size) :
QIODevice()
{
m_buffer = nullptr;
if (size > 0) {
m_buffer = new char[size];
}
m_size = size;
m_readPos = 0;
m_writePos = 0;
m_readWrap = 0;
m_writeWrap = 0;
}
QAudioInterfaceBuffer::~QAudioInterfaceBuffer()
{
delete[] m_buffer;
m_buffer = nullptr;
m_size = 0;
}
void QAudioInterfaceBuffer::start()
{
open(QIODevice::ReadWrite | QIODevice::Unbuffered);
}
void QAudioInterfaceBuffer::stop()
{
close();
}
qint64 QAudioInterfaceBuffer::readData(char *data, qint64 maxlen)
{
if (m_size <= 0 || maxlen <= 0) {
return 0;
}
int avail = this->bytesAvailable();
#if QT_DEBUG_SOUNDBACKEND
qDebug() << QThread::currentThreadId() << (quint64)m_elapsed.elapsed() <<
"rd:" << maxlen << "/" << avail;
#endif
if (avail <= 0) {
memset(data, 0, maxlen);
return maxlen;
}
m_mutex.lock();
int count = maxlen;
char *pdata = data;
int increment = 0;
while (count > 0) {
bool wrap = (m_readPos + count) >= m_size;
if (wrap) {
int block = m_size - m_readPos;
memcpy(pdata, m_buffer + m_readPos, block);
count -= block;
pdata += block;
increment = block;
m_readWrap++;
} else {
memcpy(pdata, m_buffer + m_readPos, count);
increment = count;
count -= count;
pdata += count;
}
m_readPos = (m_readPos + increment) % m_size;
}
int retval = avail;
if (avail >= maxlen)
retval = maxlen;
m_mutex.unlock();
return retval;
}
qint64 QAudioInterfaceBuffer::writeData(const char *data, qint64 len)
{
if (m_size <= 0 || len <= 0) {
return 0;
}
m_mutex.lock();
int count = len;
const char *pdata = data;
int increment = 0;
#if QT_DEBUG_SOUNDBACKEND
qDebug() << QThread::currentThreadId() << (quint64)m_elapsed.elapsed() <<
"wr:" << len << "+" << (writeCounter() - readCounter());
#endif
while (count > 0) {
bool wrap = (m_writePos + count) >= m_size;
if (wrap) {
int block = m_size - m_writePos;
memcpy(m_buffer + m_writePos, pdata, block);
increment = block;
count -= block;
pdata += block;
m_writeWrap++;
m_writePos = 0;
} else {
memcpy(m_buffer + m_writePos, pdata, count);
increment = count;
count -= count;
pdata += count;
m_writePos = (m_writePos + increment) % m_size;
}
}
m_mutex.unlock();
return len;
}
qint64 QAudioInterfaceBuffer::bytesAvailable()
{
m_mutex.lock();
int count = writeCounter() - readCounter();
m_mutex.unlock();
if (count >= 0)
return count;
return 0;
}
void QAudioInterfaceBuffer::setBufferSize(int len)
{
if (m_buffer != nullptr)
delete[] m_buffer;
#if QT_DEBUG_SOUNDBACKEND
qDebug() << "buffer size" << len;
#endif
m_buffer = new char[len];
m_size = len;
m_readPos = 0;
m_writePos = 0;
m_readWrap = 0;
m_writeWrap = 0;
zero();
}
void QAudioInterfaceBuffer::zero()
{
if (m_buffer != nullptr)
memset(m_buffer, 0, m_size);
}
int QAudioInterfaceBuffer::readCounter() const
{
return (m_readWrap * m_size) + m_readPos;
}
int QAudioInterfaceBuffer::writeCounter() const
{
return (m_writeWrap * m_size) + m_writePos;
}

View File

@ -0,0 +1,64 @@
#ifndef QAUDIOINTERFACE_H
#define QAUDIOINTERFACE_H
#include <QAudioFormat>
#include <QAudioOutput>
#include <QIODevice>
#include <QThread>
#include <QMutex>
#include <QElapsedTimer>
class QAudioInterfaceBuffer : public QIODevice
{
Q_OBJECT
public:
QAudioInterfaceBuffer(int size = 0);
~QAudioInterfaceBuffer();
void start();
void stop();
qint64 readData(char *data, qint64 maxlen);
qint64 writeData(const char *data, qint64 len);
qint64 bytesAvailable();
void setBufferSize(int len);
private:
QElapsedTimer m_elapsed;
QMutex m_mutex;
void zero();
int readCounter() const;
int writeCounter() const;
int m_writeWrap;
int m_readWrap;
int m_size;
char *m_buffer;
int m_readPos;
int m_writePos;
};
class QAudioInterface : public QThread
{
Q_OBJECT
QAudioInterface(QObject *parent=0);
static QAudioInterface *m_onlyInstance;
public:
~QAudioInterface();
void run();
static QAudioInterface *get(QObject *parent=0);
void setBuffer(QAudioInterfaceBuffer *buffer);
void setFormat(QAudioFormat &format);
signals:
public slots:
void play();
void stop();
void init();
void writeMoreData();
private:
QAudioOutput *m_audioOutput;
QAudioFormat m_format;
QAudioInterfaceBuffer *m_buffer;
};
#endif // QAUDIOINTERFACE_H

View File

@ -0,0 +1,282 @@
#include <QtWidgets>
#include "qinputinterface.h"
#include "burner.h"
QInputInterface *qInput = nullptr;
int QtKToFBAK(int key);
static char qKeyboardState[QINPUT_MAX_KEYS] = { 0 };
static bool bKeyboardRead = false;
int QtInputSetCooperativeLevel(bool bExclusive, bool)
{
qDebug() << __func__;
return 0;
}
int QtInputExit()
{
qDebug() << __func__;
if (qInput) {
delete qInput;
qInput = nullptr;
}
return 0;
}
int QtInputInit()
{
qDebug() << __func__;
qInput = QInputInterface::get();
qInput->install();
memset(qKeyboardState, 0, QINPUT_MAX_KEYS);
bKeyboardRead = false;
return 0;
}
int QtInputStart()
{
bKeyboardRead = false;
return 0;
}
static int ReadJoystick()
{
return 0;
}
int QtInputJoyAxis(int i, int nAxis)
{
return 0;
}
static int ReadKeyboard()
{
if (bKeyboardRead)
return 0;
qInput->snapshot(qKeyboardState);
bKeyboardRead = true;
return 0;
}
static int ReadMouse()
{
return 0;
}
int QtInputMouseAxis(int i, int nAxis)
{
return 0;
}
static int JoystickState(int i, int nSubCode)
{
return 0;
}
static int CheckMouseState(unsigned int nSubCode)
{
return 0;
}
int QtInputState(int nCode)
{
if (nCode < 0)
return 0;
if (nCode < 256) {
#if 1
if (!bKeyboardRead)
ReadKeyboard();
return qKeyboardState[nCode & 0xFF];
#else
return qInput->state(nCode);
#endif
}
return 0;
}
int QtInputFind(bool CreateBaseline)
{
return -1;
}
int QtInputGetControlName(int nCode, TCHAR* pszDeviceName, TCHAR* pszControlName)
{
return 0;
}
struct InputInOut InputInOutQt = { QtInputInit, QtInputExit,
QtInputSetCooperativeLevel, QtInputStart,
QtInputState, QtInputJoyAxis, QtInputMouseAxis,
QtInputFind, QtInputGetControlName, NULL,
("Qt") };
QInputInterface *QInputInterface::m_onlyInstance = nullptr;
QInputInterface::QInputInterface(QObject *parent) :
QObject(parent)
{
memset(m_keys, 0, QINPUT_MAX_KEYS);
m_isInitialized = false;
}
QInputInterface::~QInputInterface()
{
if (m_isInitialized)
uninstall();
m_onlyInstance = nullptr;
}
QInputInterface *QInputInterface::get(QObject *parent)
{
if (m_onlyInstance != nullptr)
return m_onlyInstance;
m_onlyInstance = new QInputInterface(parent);
return m_onlyInstance;
}
void QInputInterface::install()
{
qApp->installEventFilter(this);
m_isInitialized = true;
}
void QInputInterface::uninstall()
{
qApp->removeEventFilter(this);
m_isInitialized = false;
}
void QInputInterface::snapshot(char *buffer, int keys)
{
if (keys >= QINPUT_MAX_KEYS)
keys = QINPUT_MAX_KEYS;
memcpy(buffer, m_keys, keys);
}
int QInputInterface::state(int key)
{
if (key >= 0 && key < 256)
return m_keys[key];
return 0;
}
bool QInputInterface::eventFilter(QObject *obj, QEvent *event)
{
if (event->type() == QEvent::KeyPress) {
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
//qDebug() << m_timer.elapsed() << "pressed";
int fbak = QtKToFBAK(keyEvent->key());
if (fbak < QINPUT_MAX_KEYS)
m_keys[fbak] = 1;
}
if (event->type() == QEvent::KeyRelease) {
//qDebug() << m_timer.elapsed() << "released";
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
int fbak = QtKToFBAK(keyEvent->key());
if (fbak < QINPUT_MAX_KEYS)
m_keys[fbak] = 0;
}
return false;
}
int QtKToFBAK(int key)
{
/*
* TODO:
* FBK_CAPITAL 0x3A
* RIGHT-(SHIFT/CONTROL/ALT)
* NUMPADS
*/
switch (key) {
case Qt::Key_Escape: return FBK_ESCAPE;
case Qt::Key_0: return FBK_0;
case Qt::Key_1: return FBK_1;
case Qt::Key_2: return FBK_2;
case Qt::Key_3: return FBK_3;
case Qt::Key_4: return FBK_4;
case Qt::Key_5: return FBK_5;
case Qt::Key_6: return FBK_6;
case Qt::Key_7: return FBK_7;
case Qt::Key_8: return FBK_8;
case Qt::Key_9: return FBK_9;
case Qt::Key_Minus: return FBK_MINUS;
case Qt::Key_Equal: return FBK_EQUALS;
case Qt::Key_Back: return FBK_BACK;
case Qt::Key_Tab: return FBK_TAB;
case Qt::Key_Q: return FBK_Q;
case Qt::Key_W: return FBK_W;
case Qt::Key_E: return FBK_E;
case Qt::Key_R: return FBK_R;
case Qt::Key_T: return FBK_T;
case Qt::Key_Y: return FBK_Y;
case Qt::Key_U: return FBK_U;
case Qt::Key_I: return FBK_I;
case Qt::Key_O: return FBK_O;
case Qt::Key_P: return FBK_P;
case Qt::Key_BracketLeft: return FBK_LBRACKET;
case Qt::Key_BracketRight: return FBK_RBRACKET;
case Qt::Key_Return: return FBK_RETURN;
case Qt::Key_Control: return FBK_LCONTROL;
case Qt::Key_A: return FBK_A;
case Qt::Key_S: return FBK_S;
case Qt::Key_D: return FBK_D;
case Qt::Key_F: return FBK_F;
case Qt::Key_G: return FBK_G;
case Qt::Key_H: return FBK_H;
case Qt::Key_J: return FBK_J;
case Qt::Key_K: return FBK_K;
case Qt::Key_L: return FBK_L;
case Qt::Key_Semicolon: return FBK_SEMICOLON;
case Qt::Key_Apostrophe: return FBK_APOSTROPHE;
case Qt::Key_Dead_Grave: return FBK_GRAVE;
case Qt::Key_Shift: return FBK_LSHIFT;
case Qt::Key_Backslash: return FBK_BACKSLASH;
case Qt::Key_Z: return FBK_Z;
case Qt::Key_X: return FBK_X;
case Qt::Key_C: return FBK_C;
case Qt::Key_V: return FBK_V;
case Qt::Key_B: return FBK_B;
case Qt::Key_N: return FBK_N;
case Qt::Key_M: return FBK_M;
case Qt::Key_Comma: return FBK_COMMA;
case Qt::Key_Period: return FBK_PERIOD;
case Qt::Key_Slash: return FBK_SLASH;
case Qt::Key_multiply: return FBK_MULTIPLY;
case Qt::Key_Alt: return FBK_LALT;
case Qt::Key_Space: return FBK_SPACE;
case Qt::Key_F1: return FBK_F1;
case Qt::Key_F2: return FBK_F2;
case Qt::Key_F3: return FBK_F3;
case Qt::Key_F4: return FBK_F4;
case Qt::Key_F5: return FBK_F5;
case Qt::Key_F6: return FBK_F6;
case Qt::Key_F7: return FBK_F7;
case Qt::Key_F8: return FBK_F8;
case Qt::Key_F9: return FBK_F9;
case Qt::Key_F10: return FBK_F10;
case Qt::Key_NumLock: return FBK_NUMLOCK;
case Qt::Key_ScrollLock: return FBK_SCROLL;
case Qt::Key_Pause: return FBK_PAUSE;
case Qt::Key_Home: return FBK_HOME;
case Qt::Key_Up: return FBK_UPARROW;
case Qt::Key_PageUp: return FBK_PRIOR;
case Qt::Key_Left: return FBK_LEFTARROW;
case Qt::Key_Right: return FBK_RIGHTARROW;
case Qt::Key_End: return FBK_END;
case Qt::Key_Down: return FBK_DOWNARROW;
case Qt::Key_PageDown: return FBK_NEXT;
case Qt::Key_Insert: return FBK_INSERT;
case Qt::Key_Delete: return FBK_DELETE;
case Qt::Key_F11: return FBK_F11;
case Qt::Key_F12: return FBK_F12;
case Qt::Key_F13: return FBK_F13;
case Qt::Key_F14: return FBK_F14;
case Qt::Key_F15: return FBK_F15;
default:
return 0;
}
return 0;
}

View File

@ -0,0 +1,30 @@
#ifndef QINPUTINTERFACE_H
#define QINPUTINTERFACE_H
#include <QObject>
#include <QElapsedTimer>
#define QINPUT_MAX_KEYS 256
class QInputInterface : public QObject
{
Q_OBJECT
explicit QInputInterface(QObject *parent = 0);
static QInputInterface *m_onlyInstance;
char m_keys[QINPUT_MAX_KEYS];
QElapsedTimer m_timer;
bool m_isInitialized;
public:
~QInputInterface();
static QInputInterface *get(QObject *parent=nullptr);
void install();
void uninstall();
void snapshot(char *buffer, int keys=QINPUT_MAX_KEYS);
int state(int key);
signals:
public slots:
protected:
bool eventFilter(QObject *obj, QEvent *event);
};
#endif // QINPUTINTERFACE_H

View File

@ -0,0 +1,144 @@
#include <QtWidgets>
#include "qrubyviewport.h"
#include "burner.h"
#include "ruby/ruby.hpp"
#include "nall/traits.hpp"
static QRubyViewport *viewport = nullptr;
static int VidMemLen = 0;
static int VidMemPitch = 0;
static int VidBpp = 0;
static int clipx = 0;
static int clipy = 0;
static int sizex = 0;
static int sizey = 0;
static unsigned char* VidMem = NULL;
static inline unsigned c16to32(unsigned color)
{
// convert from BGR565 to BGRA32
unsigned b = color & 0x001F;
unsigned g = color & 0x07E0;
unsigned r = color & 0xF800;
return 0xFF000000 | (r << 8) | (g << 5) | (b << 3);
}
void videoRefresh(const uint32_t* palette, const uint32_t* data, unsigned pitch,
unsigned width, unsigned height) {
uint32_t *output;
unsigned outputPitch;
if (ruby::video.lock(output, outputPitch, width, height)) {
pitch >>= 2, outputPitch >>= 2;
for (unsigned y = 0; y < height; y++) {
const uint32_t *psrc = data + y * pitch;
uint32_t *pdst = output + y * outputPitch;
for (unsigned x = 0; x < width; x++) {
*pdst++ = c16to32(*psrc);
psrc = (uint32_t*)((uint16_t*)psrc + 1);
}
}
ruby::video.unlock();
ruby::video.refresh();
}
}
static INT32 RubyVidInit()
{
qDebug() << __func__;
viewport = QRubyViewport::get();
if (BurnDrvGetFlags() & BDF_ORIENTATION_VERTICAL) {
BurnDrvGetVisibleSize(&clipy, &clipx);
BurnDrvGetFullSize(&sizex, &sizey);
} else {
BurnDrvGetVisibleSize(&clipx, &clipy);
BurnDrvGetFullSize(&sizex, &sizey);
}
VidBpp = nBurnBpp = 2;
VidMemPitch = sizex * VidBpp;
VidMemLen = sizey * VidMemPitch;
VidMem = (unsigned char*)malloc(VidMemLen);
SetBurnHighCol(16);
return 0;
}
static INT32 RubyVidExit()
{
qDebug() << __func__;
free(VidMem);
return 0;
}
static INT32 RubyVidFrame(bool bRedraw)
{
nBurnBpp=2;
nBurnPitch = VidMemPitch;
pBurnDraw = VidMem;
if (bDrvOkay) {
pBurnSoundOut = nAudNextSound;
nBurnSoundLen = nAudSegLen;
BurnDrvFrame(); // Run one frame and draw the screen
}
nFramesRendered++;
pBurnDraw = NULL;
nBurnPitch = 0;
return 0;
}
static INT32 RubyVidPaint(INT32 bValidate)
{
if (bValidate & 2) {
}
videoRefresh(nullptr, (const uint32_t*) VidMem, VidMemPitch, sizex, sizey);
return 0;
}
static INT32 RubyVidImageSize(RECT* pRect, INT32 nGameWidth, INT32 nGameHeight)
{
return 0;
}
// Get plugin info
static INT32 RubyVidGetPluginSettings(InterfaceInfo* pInfo)
{
return 0;
}
struct VidOut VidRuby = { RubyVidInit, RubyVidExit, RubyVidFrame, RubyVidPaint,
RubyVidImageSize, RubyVidGetPluginSettings,
("ruby-video") };
QRubyViewport *QRubyViewport::m_onlyInstance = nullptr;
QRubyViewport::QRubyViewport(QWidget *parent) :
QWidget(parent)
{
setAutoFillBackground(false);
}
QRubyViewport::~QRubyViewport()
{
m_onlyInstance = nullptr;
}
QRubyViewport *QRubyViewport::get(QWidget *parent)
{
if (m_onlyInstance != nullptr)
return m_onlyInstance;
m_onlyInstance = new QRubyViewport(parent);
return m_onlyInstance;
}
uintptr_t QRubyViewport::id()
{
return (uintptr_t)winId();
}

View File

@ -0,0 +1,18 @@
#ifndef QRUBYVIEWPORT_H
#define QRUBYVIEWPORT_H
#include <QWidget>
#include <nall/traits.hpp>
class QRubyViewport : public QWidget
{
Q_OBJECT
explicit QRubyViewport(QWidget *parent = 0);
static QRubyViewport *m_onlyInstance;
public:
~QRubyViewport();
static QRubyViewport *get(QWidget *parent = nullptr);
uintptr_t id();
};
#endif // QRUBYVIEWPORT_H

54
src/burner/qt/qutil.cpp Normal file
View File

@ -0,0 +1,54 @@
#include <QFileDialog>
#include <QDebug>
#include "qutil.h"
namespace util {
void fixPath(QString &path)
{
if (!path.isEmpty()) {
QChar lastChar = path.at(path.size() - 1);
if (lastChar != QChar('\\') && lastChar != QChar('/'))
path.append('/');
}
}
void PathHandler::editorToString()
{
if (str == nullptr || edit == nullptr)
return;
QByteArray bytes;
QString path = edit->text();
fixPath(path);
bytes = path.toLocal8Bit();
_sntprintf(str, MAX_PATH, bytes.data());
}
void PathHandler::stringToEditor()
{
if (edit == nullptr)
return;
edit->setText(QString(str));
}
void PathHandler::browse(QWidget *parent)
{
QString path = QFileDialog::getExistingDirectory(parent);
if (path.isEmpty())
return;
if (edit == nullptr)
return;
edit->setText(path);
}
QString loadText(const QString &fileName)
{
QFile file(fileName);
if (file.exists()) {
file.open(QFile::ReadOnly | QFile::Text);
return QString(file.readAll());
}
return QString();
}
}

30
src/burner/qt/qutil.h Normal file
View File

@ -0,0 +1,30 @@
#ifndef QUTIL_H
#define QUTIL_H
#include <QString>
#include <QChar>
#include <QLineEdit>
#include "burner.h"
namespace util {
struct PathHandler {
TCHAR *str;
QLineEdit *edit;
int number;
PathHandler(TCHAR *s=nullptr, QLineEdit *e=nullptr, int n=-1) {
str = s;
edit = e;
number = n;
}
void editorToString();
void stringToEditor();
void browse(QWidget *parent=nullptr);
};
void fixPath(QString &path);
QString loadText(const QString &fileName);
}
#endif // QUTIL_H

Binary file not shown.

After

Width:  |  Height:  |  Size: 192 KiB

View File

@ -0,0 +1,48 @@
You may freely use, modify, and distribute both the FB Alpha source code and binary, however the following restrictions apply to the FB Alpha original material (see below for a list of libraries with differing licenses, please consult their respective documentation for more information):
- You may not sell, lease, rent or otherwise seek to gain monetary profit from FB Alpha;
- You must make public any changes you make to the source code;
- You must include, verbatim, the full text of this license;
- You may not distribute binaries which support games with copyright dates less then three years old;
- You may not distribute FB Alpha with ROM images unless you have the legal right to distribute them;
- You may use dynamic linked libraries (dlls) for network play only (all other use is prohibited);
- You may not ask for donations to support your work on any project that uses the FB Alpha source code.
FB Alpha can currently be obtained from http://www.barryharris.me.uk/.
FB Alpha would not exist without a lot of code from the MAME project. The MAME project is subject to it's own license, which can be found at http://mamedev.org/legal.html. Due to the use of MAME code in FB Alpha, FB Alpha is also subject to the terms of the MAME license.
FB Alpha is based on Final Burn (formally at http://www.finalburn.com/), see additional text below.
Musashi MC68000/MC68010/MC68EC020 core by Karl Stenerud (http://www.mamedev.org/).
A68K MC68000 core by Mike Coates & Darren Olafson (http://www.mamedev.org/).
YM3812/YM3526/Y8950 core by Jarek Burczynski & Tatsuyuki Satoh (http://www.mamedev.org/).
YM2151 core by Jarek Burczynski (http://www.mamedev.org/).
YM2413 core by Jarek Burczynski (http://www.mamedev.org/).
YMF278B core by R. Belmont & O.Galibert (http://www.mamedev.org/).
YM2608/YM2610/YM2612/YM2203 cores by Jarek Burczynski & Tatsuyuki Satoh (http://www.mamedev.org/).
AY8910/YM2149 core by various authors (http://www.mamedev.org/).
M6502 emulation core by Marat (http://fms.komkon.org/EMUL8/).
I8039 emulation core by Mirko Buffoni (http://www.mamedev.org/).
ARM7 emulation core by Steve Ellenoff (http://www.mamedev.org/).
Z80 emulation core by Juergen Buchmueller (http://www.mamedev.org/).
PNG functionality provided by libpng (http://www.libpng.org/) and PNGlib (http://www.madwizard.org/).
Zip functionality provided by zlib (http://www.zlib.net/).
Ruby functionality provided by ruby (http://http://byuu.org/programming/ruby/)
Some graphics effects provided by the Scale2x, 2xPM, Eagle Graphics, 2xSaI, hq2x/hq3x/hq4x, hq2xS/hq3xS/SuperEagle/2xSaI (VBA), hq2xS/hq3xS/hq2xBold/hq3xBold/EPXB/EPXC (SNES9X ReRecording) and SuperScale libraries (http://scale2x.sourceforge.net/, http://2xpm.freeservers.com/, http://retrofx.com/, http://elektron.its.tudelft.nl/~dalikifa/, http://www.hiend3d.com/, http://code.google.com/p/vba-rerecording/, http://code.google.com/p/snes9x151-rerecording/, http://nebula.emulatronia.com/).
Miscellaneous other components from various sources. Copyright and license information are contained in the relevant parts of the source code.
All material not covered above © 2004-2013 Team FB Alpha.
DISCLAIMER: The authors of FB Alpha don't guarantee its fitness for any purpose, implied or otherwise, and do not accept responsibility for any damages whatsoever that might occur when using FB Alpha. All games emulated by FB Alpha, including any images and sounds therein, are copyrighted by their respective copyright holders. FB Alpha DOES NOT INCLUDE any ROM images of emulated games.
The following information and license conditions accompanied the original Final Burn emulator. They also apply to FB Alpha:
"Copyright (c)2001 Dave (formally of www.finalburn.com), all rights reserved. This refers to all code except where stated otherwise (e.g. unzip and zlib code)."
"You can use, modify and redistribute this code freely as long as you don't do so commercially. This copyright notice must remain with the code. If your program uses this code, you must either distribute or link to the source code. If you modify or improve this code, you must distribute the source code improvements."
"Dave"
"Former Homepage: www.finalburn.com"
"E-mail: dave@finalburn.com"

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 200 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

@ -0,0 +1,48 @@
#include <QtWidgets>
#include "romdirsdialog.h"
#include "ui_romdirsdialog.h"
#include "burner.h"
TCHAR szAppRomPaths[DIRS_MAX][MAX_PATH] = { { _T("roms/") } };
RomDirsDialog::RomDirsDialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::RomDirsDialog)
{
ui->setupUi(this);
setWindowTitle(tr("Edit ROM paths"));
m_group = new QButtonGroup(this);
m_group->addButton(ui->btnEditPath1, 0);
m_group->addButton(ui->btnEditPath2, 1);
m_group->addButton(ui->btnEditPath3, 2);
m_group->addButton(ui->btnEditPath4, 3);
m_handlers[0] = util::PathHandler(szAppRomPaths[0], ui->lePath1, 0);
m_handlers[1] = util::PathHandler(szAppRomPaths[1], ui->lePath2, 1);
m_handlers[2] = util::PathHandler(szAppRomPaths[2], ui->lePath3, 2);
m_handlers[3] = util::PathHandler(szAppRomPaths[3], ui->lePath4, 3);
connect(m_group, SIGNAL(buttonClicked(int)), this, SLOT(editPath(int)));
}
RomDirsDialog::~RomDirsDialog()
{
delete ui;
}
int RomDirsDialog::exec()
{
for (int i = 0; i < DIRS_MAX; i++)
m_handlers[i].stringToEditor();
if (QDialog::exec() == QDialog::Accepted) {
for (int i = 0; i < DIRS_MAX; i++)
m_handlers[i].editorToString();
}
}
void RomDirsDialog::editPath(int no)
{
m_handlers[no].browse(this);
}

View File

@ -0,0 +1,32 @@
#ifndef ROMDIRSDIALOG_H
#define ROMDIRSDIALOG_H
#include <QDialog>
#include <QButtonGroup>
#include "qutil.h"
namespace Ui {
class RomDirsDialog;
}
class RomDirsDialog : public QDialog
{
Q_OBJECT
public:
explicit RomDirsDialog(QWidget *parent = 0);
~RomDirsDialog();
public slots:
int exec();
void editPath(int no);
private:
Ui::RomDirsDialog *ui;
int m_activePath;
QButtonGroup *m_group;
util::PathHandler m_handlers[DIRS_MAX];
};
#endif // ROMDIRSDIALOG_H

View File

@ -0,0 +1,188 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>RomDirsDialog</class>
<widget class="QDialog" name="RomDirsDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>510</width>
<height>244</height>
</rect>
</property>
<property name="windowTitle">
<string>Dialog</string>
</property>
<property name="modal">
<bool>true</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Path #1</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="lePath1">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnEditPath1">
<property name="text">
<string>Browse</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>Path #2</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="lePath2">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnEditPath2">
<property name="text">
<string>Browse</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLabel" name="label_3">
<property name="text">
<string>Path #3</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="lePath3">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnEditPath3">
<property name="text">
<string>Browse</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QLabel" name="label_4">
<property name="text">
<string>Path #4</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="lePath4">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnEditPath4">
<property name="text">
<string>Browse</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="btnCancel">
<property name="text">
<string>Cancel</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnOk">
<property name="text">
<string>Ok</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>btnCancel</sender>
<signal>clicked()</signal>
<receiver>RomDirsDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>239</x>
<y>192</y>
</hint>
<hint type="destinationlabel">
<x>160</x>
<y>198</y>
</hint>
</hints>
</connection>
<connection>
<sender>btnOk</sender>
<signal>clicked()</signal>
<receiver>RomDirsDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>355</x>
<y>197</y>
</hint>
<hint type="destinationlabel">
<x>316</x>
<y>217</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -0,0 +1,74 @@
#include <QtWidgets>
#include "rominfodialog.h"
#include "ui_rominfodialog.h"
#include "burner.h"
RomInfoDialog::RomInfoDialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::RomInfoDialog)
{
ui->setupUi(this);
}
RomInfoDialog::~RomInfoDialog()
{
delete ui;
}
void RomInfoDialog::setDriverNo(int no)
{
clear();
m_driverNo = no;
int tmp = nBurnDrvActive;
nBurnDrvActive = m_driverNo;
setWindowTitle(QString(BurnDrvGetText(DRV_FULLNAME)));
for (int i = 0; i < 0x100; i++) {
BurnRomInfo info;
char *romName = nullptr;
memset(&info, 0, sizeof(info));
BurnDrvGetRomInfo(&info, i);
BurnDrvGetRomName(&romName, i, 0);
if (info.nLen == 0 || info.nType & BRF_BIOS)
continue;
QTreeWidgetItem *item = new QTreeWidgetItem();
item->setText(0, QString(romName));
item->setText(1, QString::number(info.nLen));
item->setText(2, QString::number(info.nCrc, 16));
QStringList type;
if (info.nType & BRF_ESS)
type << "Essential";
if (info.nType & BRF_OPT)
type << "Optional";
if (info.nType & BRF_PRG)
type << "Program";
if (info.nType & BRF_GRA)
type << "Graphics";
if (info.nType & BRF_SND)
type << "Sound";
if (info.nType & BRF_BIOS)
type << "BIOS";
item->setText(3, type.join(", "));
if (info.nType & BRF_NODUMP)
item->setText(4, "No Dump");
item->setTextAlignment(0, Qt::AlignLeft);
item->setTextAlignment(1, Qt::AlignRight);
item->setTextAlignment(2, Qt::AlignRight);
ui->tvRoms->addTopLevelItem(item);
}
nBurnDrvActive = tmp;
}
void RomInfoDialog::clear()
{
ui->tvRoms->clear();
}

View File

@ -0,0 +1,25 @@
#ifndef ROMINFODIALOG_H
#define ROMINFODIALOG_H
#include <QDialog>
namespace Ui {
class RomInfoDialog;
}
class RomInfoDialog : public QDialog
{
Q_OBJECT
public:
explicit RomInfoDialog(QWidget *parent = 0);
~RomInfoDialog();
void setDriverNo(int no);
private:
void clear();
int m_driverNo;
Ui::RomInfoDialog *ui;
};
#endif // ROMINFODIALOG_H

View File

@ -0,0 +1,121 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>RomInfoDialog</class>
<widget class="QDialog" name="RomInfoDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>650</width>
<height>358</height>
</rect>
</property>
<property name="windowTitle">
<string>Dialog</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QTabWidget" name="tabWidget">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="tab">
<attribute name="title">
<string>Rom Info</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QTreeWidget" name="tvRoms">
<column>
<property name="text">
<string>Name</string>
</property>
</column>
<column>
<property name="text">
<string>Size (bytes)</string>
</property>
</column>
<column>
<property name="text">
<string>CRC32</string>
</property>
</column>
<column>
<property name="text">
<string>Type</string>
</property>
</column>
<column>
<property name="text">
<string>Flags</string>
</property>
</column>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="tab_2">
<attribute name="title">
<string>Sample Info</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QTreeWidget" name="tvSamples">
<column>
<property name="text">
<string>Name</string>
</property>
</column>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="btnClose">
<property name="text">
<string>Close</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>btnClose</sender>
<signal>clicked()</signal>
<receiver>RomInfoDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>604</x>
<y>332</y>
</hint>
<hint type="destinationlabel">
<x>514</x>
<y>334</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -0,0 +1,102 @@
#include <QtWidgets>
#include "romscandialog.h"
#include "ui_romscandialog.h"
#include "burner.h"
RomScanDialog::RomScanDialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::RomScanDialog),
m_analyzer(this)
{
ui->setupUi(this);
setWindowTitle(tr("Wait..."));
setFixedSize(size());
m_status.resize(nBurnDrvCount);
for (int i = 0; i < nBurnDrvCount; i++)
m_status[i] = 0;
connect(&m_analyzer, SIGNAL(finished()), this, SLOT(accept()));
connect(ui->btnCancel, SIGNAL(clicked()), &m_analyzer, SLOT(terminate()));
connect(&m_analyzer, SIGNAL(setRange(int,int)),
ui->progressBar, SLOT(setRange(int,int)));
connect(&m_analyzer, SIGNAL(setValue(int)),
ui->progressBar, SLOT(setValue(int)));
connect(this, SIGNAL(rejected()), &m_analyzer, SLOT(terminate()));
}
RomScanDialog::~RomScanDialog()
{
delete ui;
}
int RomScanDialog::status(int drvNo)
{
if (drvNo < 0 && drvNo >= nBurnDrvCount)
return 0;
return m_status[drvNo];
}
void RomScanDialog::cancel()
{
close();
}
void RomScanDialog::showEvent(QShowEvent *event)
{
ui->progressBar->setValue(0);
m_analyzer.start();
}
bool RomScanDialog::load()
{
}
bool RomScanDialog::save()
{
}
RomAnalyzer::RomAnalyzer(RomScanDialog *parent) :
m_scanDlg(parent)
{
}
void RomAnalyzer::run()
{
QVector<char> &status = m_scanDlg->m_status;
if (status.size() != nBurnDrvCount)
status.resize(nBurnDrvCount);
int tmp = nBurnDrvActive;
emit setRange(0, nBurnDrvCount - 1);
for (int i = 0; i < nBurnDrvCount; i++) {
nBurnDrvActive = i;
emit setValue(i);
int stat = BzipOpen(1);
switch (stat) {
case 0:
status[i] = 3;
break;
case 2:
status[i] = 1;
break;
case 1:
status[i] = 0;
break;
default:
break;
}
BzipClose();
}
msleep(100);
nBurnDrvActive = tmp;
}

View File

@ -0,0 +1,47 @@
#ifndef ROMSCANDIALOG_H
#define ROMSCANDIALOG_H
#include <QDialog>
#include <QThread>
#include <QVector>
namespace Ui {
class RomScanDialog;
}
class RomScanDialog;
class RomAnalyzer : public QThread {
Q_OBJECT
RomScanDialog *m_scanDlg;
public:
RomAnalyzer(RomScanDialog *parent);
signals:
void setRange(int, int);
void setValue(int);
private:
void run();
};
class RomScanDialog : public QDialog
{
friend class RomAnalyzer;
Q_OBJECT
public:
explicit RomScanDialog(QWidget *parent = 0);
~RomScanDialog();
int status(int drvNo);
public slots:
void cancel();
protected:
virtual void showEvent(QShowEvent *event);
private:
bool load();
bool save();
Ui::RomScanDialog *ui;
QVector<char> m_status;
RomAnalyzer m_analyzer;
};
#endif // ROMSCANDIALOG_H

View File

@ -0,0 +1,87 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>RomScanDialog</class>
<widget class="QDialog" name="RomScanDialog">
<property name="windowModality">
<enum>Qt::NonModal</enum>
</property>
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>373</width>
<height>80</height>
</rect>
</property>
<property name="windowTitle">
<string>Dialog</string>
</property>
<property name="modal">
<bool>false</bool>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="lblImage">
<property name="frameShape">
<enum>QFrame::Box</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Sunken</enum>
</property>
<property name="text">
<string/>
</property>
<property name="pixmap">
<pixmap resource="rscr.qrc">:/resource/misc.bmp</pixmap>
</property>
</widget>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QProgressBar" name="progressBar">
<property name="value">
<number>0</number>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="lblText">
<property name="text">
<string>Scanning ROMs...</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="btnCancel">
<property name="text">
<string>Cancel</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</widget>
<resources>
<include location="rscr.qrc"/>
</resources>
<connections/>
</ui>

11
src/burner/qt/rscr.qrc Normal file
View File

@ -0,0 +1,11 @@
<RCC>
<qresource prefix="/">
<file>resource/splash.bmp</file>
<file>resource/tv-not-working.ico</file>
<file>resource/tv-not-found.ico</file>
<file>resource/tv-not-found-non-essential.ico</file>
<file>resource/misc.bmp</file>
<file>resource/about.bmp</file>
<file>resource/license.txt</file>
</qresource>
</RCC>

37
src/burner/qt/ruby/Makefile Executable file
View File

@ -0,0 +1,37 @@
ifeq ($(platform),macosx)
rubyflags = $(objcppflags) $(flags)
else
rubyflags = $(cppflags) $(flags)
endif
rubyflags += $(foreach c,$(subst .,_,$(call strupper,$(ruby))),-D$c)
rubyflags += $(if $(findstring .sdl,$(ruby)),`sdl-config --cflags`)
rubylink =
rubylink += $(if $(findstring video.cgl,$(ruby)),-framework OpenGL)
rubylink += $(if $(findstring video.direct3d,$(ruby)),-ld3d9)
rubylink += $(if $(findstring video.directdraw,$(ruby)),-lddraw)
rubylink += $(if $(findstring video.glx,$(ruby)),-lGL)
rubylink += $(if $(findstring video.wgl,$(ruby)),-lopengl32)
rubylink += $(if $(findstring video.xv,$(ruby)),-lXv)
rubylink += $(if $(findstring audio.alsa,$(ruby)),-lasound)
rubylink += $(if $(findstring audio.ao,$(ruby)),-lao)
rubylink += $(if $(findstring audio.directsound,$(ruby)),-ldsound)
rubylink += $(if $(findstring audio.pulseaudio,$(ruby)),-lpulse)
rubylink += $(if $(findstring audio.pulseaudiosimple,$(ruby)),-lpulse-simple)
rubylink += $(if $(findstring audio.xaudio2,$(ruby)),-lole32)
rubylink += $(if $(findstring input.udev,$(ruby)),-ludev)
rubylink += $(if $(findstring input.windows,$(ruby)),-ldinput8 -ldxguid)
rubylink += $(if $(findstring .sdl,$(ruby)),`sdl-config --libs`)
ifeq ($(platform),windows)
rubylink += $(if $(findstring audio.openal,$(ruby)),-lopenal32)
else ifeq ($(platform),macosx)
rubylink += $(if $(findstring audio.openal,$(ruby)),-framework OpenAL)
else
rubylink += $(if $(findstring audio.openal,$(ruby)),-lopenal)
endif

18
src/burner/qt/ruby/audio.hpp Executable file
View File

@ -0,0 +1,18 @@
struct Audio {
static const char* Handle;
static const char* Synchronize;
static const char* Frequency;
static const char* Latency;
virtual bool cap(const nall::string& name) { return false; }
virtual nall::any get(const nall::string& name) { return false; }
virtual bool set(const nall::string& name, const nall::any& value) { return false; }
virtual void sample(uint16_t left, uint16_t right) {}
virtual void clear() {}
virtual bool init() { return true; }
virtual void term() {}
Audio() {}
virtual ~Audio() {}
};

240
src/burner/qt/ruby/audio/alsa.cpp Executable file
View File

@ -0,0 +1,240 @@
//audio.alsa (2009-11-30)
//authors: BearOso, byuu, Nach, RedDwarf
#include <alsa/asoundlib.h>
namespace ruby {
class pAudioALSA {
public:
struct {
snd_pcm_t* handle;
snd_pcm_format_t format;
snd_pcm_uframes_t buffer_size;
snd_pcm_uframes_t period_size;
int channels;
const char* name;
} device;
struct {
uint32_t* data;
unsigned length;
} buffer;
struct {
bool synchronize;
unsigned frequency;
unsigned latency;
} settings;
bool cap(const string& name) {
if(name == Audio::Synchronize) return true;
if(name == Audio::Frequency) return true;
if(name == Audio::Latency) return true;
return false;
}
any get(const string& name) {
if(name == Audio::Synchronize) return settings.synchronize;
if(name == Audio::Frequency) return settings.frequency;
if(name == Audio::Latency) return settings.latency;
return false;
}
bool set(const string& name, const any& value) {
if(name == Audio::Synchronize) {
if(settings.synchronize != any_cast<bool>(value)) {
settings.synchronize = any_cast<bool>(value);
if(device.handle) init();
}
return true;
}
if(name == Audio::Frequency) {
if(settings.frequency != any_cast<unsigned>(value)) {
settings.frequency = any_cast<unsigned>(value);
if(device.handle) init();
}
return true;
}
if(name == Audio::Latency) {
if(settings.latency != any_cast<unsigned>(value)) {
settings.latency = any_cast<unsigned>(value);
if(device.handle) init();
}
return true;
}
return false;
}
void sample(uint16_t left, uint16_t right) {
if(!device.handle) return;
buffer.data[buffer.length++] = left + (right << 16);
if(buffer.length < device.period_size) return;
snd_pcm_sframes_t avail;
do {
avail = snd_pcm_avail_update(device.handle);
if(avail < 0) snd_pcm_recover(device.handle, avail, 1);
if(avail < buffer.length) {
if(settings.synchronize == false) {
buffer.length = 0;
return;
}
int error = snd_pcm_wait(device.handle, -1);
if(error < 0) snd_pcm_recover(device.handle, error, 1);
}
} while(avail < buffer.length);
//below code has issues with PulseAudio sound server
#if 0
if(settings.synchronize == false) {
snd_pcm_sframes_t avail = snd_pcm_avail_update(device.handle);
if(avail < device.period_size) {
buffer.length = 0;
return;
}
}
#endif
uint32_t* buffer_ptr = buffer.data;
int i = 4;
while((buffer.length > 0) && i--) {
snd_pcm_sframes_t written = snd_pcm_writei(device.handle, buffer_ptr, buffer.length);
if(written < 0) {
//no samples written
snd_pcm_recover(device.handle, written, 1);
} else if(written <= buffer.length) {
buffer.length -= written;
buffer_ptr += written;
}
}
if(i < 0) {
if(buffer.data == buffer_ptr) {
buffer.length--;
buffer_ptr++;
}
memmove(buffer.data, buffer_ptr, buffer.length * sizeof(uint32_t));
}
}
void clear() {
}
bool init() {
term();
if(snd_pcm_open(&device.handle, device.name, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK) < 0) {
term();
return false;
}
//below code will not work with 24khz frequency rate (ALSA library bug)
#if 0
if(snd_pcm_set_params(device.handle, device.format, SND_PCM_ACCESS_RW_INTERLEAVED,
device.channels, settings.frequency, 1, settings.latency * 1000) < 0) {
//failed to set device parameters
term();
return false;
}
if(snd_pcm_get_params(device.handle, &device.buffer_size, &device.period_size) < 0) {
device.period_size = settings.latency * 1000 * 1e-6 * settings.frequency / 4;
}
#endif
snd_pcm_hw_params_t* hwparams;
snd_pcm_sw_params_t* swparams;
unsigned rate = settings.frequency;
unsigned buffer_time = settings.latency * 1000;
unsigned period_time = settings.latency * 1000 / 4;
snd_pcm_hw_params_alloca(&hwparams);
if(snd_pcm_hw_params_any(device.handle, hwparams) < 0) {
term();
return false;
}
if(snd_pcm_hw_params_set_access(device.handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED) < 0
|| snd_pcm_hw_params_set_format(device.handle, hwparams, device.format) < 0
|| snd_pcm_hw_params_set_channels(device.handle, hwparams, device.channels) < 0
|| snd_pcm_hw_params_set_rate_near(device.handle, hwparams, &rate, 0) < 0
|| snd_pcm_hw_params_set_period_time_near(device.handle, hwparams, &period_time, 0) < 0
|| snd_pcm_hw_params_set_buffer_time_near(device.handle, hwparams, &buffer_time, 0) < 0
) {
term();
return false;
}
if(snd_pcm_hw_params(device.handle, hwparams) < 0) {
term();
return false;
}
if(snd_pcm_get_params(device.handle, &device.buffer_size, &device.period_size) < 0) {
term();
return false;
}
snd_pcm_sw_params_alloca(&swparams);
if(snd_pcm_sw_params_current(device.handle, swparams) < 0) {
term();
return false;
}
if(snd_pcm_sw_params_set_start_threshold(device.handle, swparams,
(device.buffer_size / device.period_size) * device.period_size) < 0
) {
term();
return false;
}
if(snd_pcm_sw_params(device.handle, swparams) < 0) {
term();
return false;
}
buffer.data = new uint32_t[device.period_size];
return true;
}
void term() {
if(device.handle) {
//snd_pcm_drain(device.handle); //prevents popping noise; but causes multi-second lag
snd_pcm_close(device.handle);
device.handle = 0;
}
if(buffer.data) {
delete[] buffer.data;
buffer.data = 0;
}
}
pAudioALSA() {
device.handle = 0;
device.format = SND_PCM_FORMAT_S16_LE;
device.channels = 2;
device.name = "default";
buffer.data = 0;
buffer.length = 0;
settings.synchronize = false;
settings.frequency = 22050;
settings.latency = 60;
}
~pAudioALSA() {
term();
}
};
DeclareAudio(ALSA)
};

94
src/burner/qt/ruby/audio/ao.cpp Executable file
View File

@ -0,0 +1,94 @@
/*
audio.ao (2008-06-01)
authors: Nach, RedDwarf
*/
#include <ao/ao.h>
namespace ruby {
class pAudioAO {
public:
int driver_id;
ao_sample_format driver_format;
ao_device* audio_device;
struct {
unsigned frequency;
} settings;
bool cap(const string& name) {
if(name == Audio::Frequency) return true;
return false;
}
any get(const string& name) {
if(name == Audio::Frequency) return settings.frequency;
return false;
}
bool set(const string& name, const any& value) {
if(name == Audio::Frequency) {
settings.frequency = any_cast<unsigned>(value);
if(audio_device) init();
return true;
}
return false;
}
void sample(uint16_t l_sample, uint16_t r_sample) {
uint32_t samp = (l_sample << 0) + (r_sample << 16);
ao_play(audio_device, (char*)&samp, 4); //This may need to be byte swapped for Big Endian
}
void clear() {
}
bool init() {
term();
driver_id = ao_default_driver_id(); //ao_driver_id((const char*)driver)
if(driver_id < 0) return false;
driver_format.bits = 16;
driver_format.channels = 2;
driver_format.rate = settings.frequency;
driver_format.byte_format = AO_FMT_LITTLE;
ao_option* options = nullptr;
ao_info *di = ao_driver_info(driver_id);
if(!di) return false;
if(!strcmp(di->short_name, "alsa")) {
ao_append_option(&options, "buffer_time", "100000"); //100ms latency (default was 500ms)
}
audio_device = ao_open_live(driver_id, &driver_format, options);
if(!audio_device) return false;
return true;
}
void term() {
if(audio_device) {
ao_close(audio_device);
audio_device = 0;
}
}
pAudioAO() {
audio_device = 0;
ao_initialize();
settings.frequency = 22050;
}
~pAudioAO() {
term();
//ao_shutdown(); //FIXME: this is causing a segfault for some reason when called ...
}
};
DeclareAudio(AO)
};

View File

@ -0,0 +1,209 @@
/*
audio.directsound (2007-12-26)
author: byuu
*/
#include <dsound.h>
namespace ruby {
class pAudioDS {
public:
LPDIRECTSOUND ds;
LPDIRECTSOUNDBUFFER dsb_p, dsb_b;
DSBUFFERDESC dsbd;
WAVEFORMATEX wfx;
struct {
unsigned rings;
unsigned latency;
uint32_t* buffer;
unsigned bufferoffset;
unsigned readring;
unsigned writering;
int distance;
} device;
struct {
HWND handle;
bool synchronize;
unsigned frequency;
unsigned latency;
} settings;
bool cap(const string& name) {
if(name == Audio::Handle) return true;
if(name == Audio::Synchronize) return true;
if(name == Audio::Frequency) return true;
if(name == Audio::Latency) return true;
return false;
}
any get(const string& name) {
if(name == Audio::Handle) return (uintptr_t)settings.handle;
if(name == Audio::Synchronize) return settings.synchronize;
if(name == Audio::Frequency) return settings.frequency;
if(name == Audio::Latency) return settings.latency;
return false;
}
bool set(const string& name, const any& value) {
if(name == Audio::Handle) {
settings.handle = (HWND)any_cast<uintptr_t>(value);
return true;
}
if(name == Audio::Synchronize) {
settings.synchronize = any_cast<bool>(value);
if(ds) clear();
return true;
}
if(name == Audio::Frequency) {
settings.frequency = any_cast<unsigned>(value);
if(ds) init();
return true;
}
if(name == Audio::Latency) {
settings.latency = any_cast<unsigned>(value);
if(ds) init();
return true;
}
return false;
}
void sample(uint16_t left, uint16_t right) {
device.buffer[device.bufferoffset++] = left + (right << 16);
if(device.bufferoffset < device.latency) return;
device.bufferoffset = 0;
DWORD pos, size;
void* output;
if(settings.synchronize) {
//wait until playback buffer has an empty ring to write new audio data to
while(device.distance >= device.rings - 1) {
dsb_b->GetCurrentPosition(&pos, 0);
unsigned activering = pos / (device.latency * 4);
if(activering == device.readring) continue;
//subtract number of played rings from ring distance counter
device.distance -= (device.rings + activering - device.readring) % device.rings;
device.readring = activering;
if(device.distance < 2) {
//buffer underflow; set max distance to recover quickly
device.distance = device.rings - 1;
device.writering = (device.rings + device.readring - 1) % device.rings;
break;
}
}
}
device.writering = (device.writering + 1) % device.rings;
device.distance = (device.distance + 1) % device.rings;
if(dsb_b->Lock(device.writering * device.latency * 4, device.latency * 4, &output, &size, 0, 0, 0) == DS_OK) {
memcpy(output, device.buffer, device.latency * 4);
dsb_b->Unlock(output, size, 0, 0);
}
}
void clear() {
device.readring = 0;
device.writering = device.rings - 1;
device.distance = device.rings - 1;
device.bufferoffset = 0;
if(device.buffer) memset(device.buffer, 0, device.latency * device.rings * 4);
if(!dsb_b) return;
dsb_b->Stop();
dsb_b->SetCurrentPosition(0);
DWORD size;
void* output;
dsb_b->Lock(0, device.latency * device.rings * 4, &output, &size, 0, 0, 0);
memset(output, 0, size);
dsb_b->Unlock(output, size, 0, 0);
dsb_b->Play(0, 0, DSBPLAY_LOOPING);
}
bool init() {
term();
device.rings = 8;
device.latency = settings.frequency * settings.latency / device.rings / 1000.0 + 0.5;
device.buffer = new uint32_t[device.latency * device.rings];
device.bufferoffset = 0;
DirectSoundCreate(0, &ds, 0);
ds->SetCooperativeLevel((HWND)settings.handle, DSSCL_PRIORITY);
memset(&dsbd, 0, sizeof(dsbd));
dsbd.dwSize = sizeof(dsbd);
dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER;
dsbd.dwBufferBytes = 0;
dsbd.lpwfxFormat = 0;
ds->CreateSoundBuffer(&dsbd, &dsb_p, 0);
memset(&wfx, 0, sizeof(wfx));
wfx.wFormatTag = WAVE_FORMAT_PCM;
wfx.nChannels = 2;
wfx.nSamplesPerSec = settings.frequency;
wfx.wBitsPerSample = 16;
wfx.nBlockAlign = wfx.wBitsPerSample / 8 * wfx.nChannels;
wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign;
dsb_p->SetFormat(&wfx);
memset(&dsbd, 0, sizeof(dsbd));
dsbd.dwSize = sizeof(dsbd);
dsbd.dwFlags = DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_CTRLFREQUENCY | DSBCAPS_GLOBALFOCUS | DSBCAPS_LOCSOFTWARE;
dsbd.dwBufferBytes = device.latency * device.rings * sizeof(uint32_t);
dsbd.guid3DAlgorithm = GUID_NULL;
dsbd.lpwfxFormat = &wfx;
ds->CreateSoundBuffer(&dsbd, &dsb_b, 0);
dsb_b->SetFrequency(settings.frequency);
dsb_b->SetCurrentPosition(0);
clear();
return true;
}
void term() {
if(device.buffer) {
delete[] device.buffer;
device.buffer = 0;
}
if(dsb_b) { dsb_b->Stop(); dsb_b->Release(); dsb_b = 0; }
if(dsb_p) { dsb_p->Stop(); dsb_p->Release(); dsb_p = 0; }
if(ds) { ds->Release(); ds = 0; }
}
pAudioDS() {
ds = 0;
dsb_p = 0;
dsb_b = 0;
device.buffer = 0;
device.bufferoffset = 0;
device.readring = 0;
device.writering = 0;
device.distance = 0;
settings.handle = GetDesktopWindow();
settings.synchronize = false;
settings.frequency = 22050;
settings.latency = 120;
}
};
DeclareAudio(DS)
};

View File

@ -0,0 +1,210 @@
/*
audio.openal (2007-12-26)
author: Nach
contributors: byuu, wertigon, _willow_
*/
#if defined(PLATFORM_MACOSX)
#include <OpenAL/al.h>
#include <OpenAL/alc.h>
#else
#include <AL/al.h>
#include <AL/alc.h>
#endif
namespace ruby {
class pAudioOpenAL {
public:
struct {
ALCdevice* handle;
ALCcontext* context;
ALuint source;
ALenum format;
unsigned latency;
unsigned queue_length;
} device;
struct {
uint32_t* data;
unsigned length;
unsigned size;
} buffer;
struct {
bool synchronize;
unsigned frequency;
unsigned latency;
} settings;
bool cap(const string& name) {
if(name == Audio::Synchronize) return true;
if(name == Audio::Frequency) return true;
if(name == Audio::Latency) return true;
return false;
}
any get(const string& name) {
if(name == Audio::Synchronize) return settings.synchronize;
if(name == Audio::Frequency) return settings.frequency;
if(name == Audio::Latency) return settings.latency;
return false;
}
bool set(const string& name, const any& value) {
if(name == Audio::Synchronize) {
settings.synchronize = any_cast<bool>(value);
return true;
}
if(name == Audio::Frequency) {
settings.frequency = any_cast<unsigned>(value);
return true;
}
if(name == Audio::Latency) {
if(settings.latency != any_cast<unsigned>(value)) {
settings.latency = any_cast<unsigned>(value);
update_latency();
}
return true;
}
return false;
}
void sample(uint16_t sl, uint16_t sr) {
buffer.data[buffer.length++] = sl + (sr << 16);
if(buffer.length < buffer.size) return;
ALuint albuffer = 0;
int processed = 0;
while(true) {
alGetSourcei(device.source, AL_BUFFERS_PROCESSED, &processed);
while(processed--) {
alSourceUnqueueBuffers(device.source, 1, &albuffer);
alDeleteBuffers(1, &albuffer);
device.queue_length--;
}
//wait for buffer playback to catch up to sample generation if not synchronizing
if(settings.synchronize == false || device.queue_length < 3) break;
}
if(device.queue_length < 3) {
alGenBuffers(1, &albuffer);
alBufferData(albuffer, device.format, buffer.data, buffer.size * 4, settings.frequency);
alSourceQueueBuffers(device.source, 1, &albuffer);
device.queue_length++;
}
ALint playing;
alGetSourcei(device.source, AL_SOURCE_STATE, &playing);
if(playing != AL_PLAYING) alSourcePlay(device.source);
buffer.length = 0;
}
void clear() {
}
void update_latency() {
if(buffer.data) delete[] buffer.data;
buffer.size = settings.frequency * settings.latency / 1000.0 + 0.5;
buffer.data = new uint32_t[buffer.size];
}
bool init() {
update_latency();
device.queue_length = 0;
bool success = false;
if(device.handle = alcOpenDevice(NULL)) {
if(device.context = alcCreateContext(device.handle, NULL)) {
alcMakeContextCurrent(device.context);
alGenSources(1, &device.source);
//alSourcef (device.source, AL_PITCH, 1.0);
//alSourcef (device.source, AL_GAIN, 1.0);
//alSource3f(device.source, AL_POSITION, 0.0, 0.0, 0.0);
//alSource3f(device.source, AL_VELOCITY, 0.0, 0.0, 0.0);
//alSource3f(device.source, AL_DIRECTION, 0.0, 0.0, 0.0);
//alSourcef (device.source, AL_ROLLOFF_FACTOR, 0.0);
//alSourcei (device.source, AL_SOURCE_RELATIVE, AL_TRUE);
alListener3f(AL_POSITION, 0.0, 0.0, 0.0);
alListener3f(AL_VELOCITY, 0.0, 0.0, 0.0);
ALfloat listener_orientation[] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0};
alListenerfv(AL_ORIENTATION, listener_orientation);
success = true;
}
}
if(success == false) {
term();
return false;
}
return true;
}
void term() {
if(alIsSource(device.source) == AL_TRUE) {
int playing = 0;
alGetSourcei(device.source, AL_SOURCE_STATE, &playing);
if(playing == AL_PLAYING) {
alSourceStop(device.source);
int queued = 0;
alGetSourcei(device.source, AL_BUFFERS_QUEUED, &queued);
while(queued--) {
ALuint albuffer = 0;
alSourceUnqueueBuffers(device.source, 1, &albuffer);
alDeleteBuffers(1, &albuffer);
device.queue_length--;
}
}
alDeleteSources(1, &device.source);
device.source = 0;
}
if(device.context) {
alcMakeContextCurrent(NULL);
alcDestroyContext(device.context);
device.context = 0;
}
if(device.handle) {
alcCloseDevice(device.handle);
device.handle = 0;
}
if(buffer.data) {
delete[] buffer.data;
buffer.data = 0;
}
}
pAudioOpenAL() {
device.source = 0;
device.handle = 0;
device.context = 0;
device.format = AL_FORMAT_STEREO16;
device.queue_length = 0;
buffer.data = 0;
buffer.length = 0;
buffer.size = 0;
settings.synchronize = true;
settings.frequency = 22050;
settings.latency = 40;
}
~pAudioOpenAL() {
term();
}
};
DeclareAudio(OpenAL)
};

113
src/burner/qt/ruby/audio/oss.cpp Executable file
View File

@ -0,0 +1,113 @@
/*
audio.oss (2007-12-26)
author: Nach
*/
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/soundcard.h>
//OSS4 soundcard.h includes below SNDCTL defines, but OSS3 does not
//However, OSS4 soundcard.h does not reside in <sys/>
//Therefore, attempt to manually define SNDCTL values if using OSS3 header
//Note that if the defines below fail to work on any specific platform, one can point soundcard.h
//above to the correct location for OSS4 (usually /usr/lib/oss/include/sys/soundcard.h)
//Failing that, one can disable OSS4 ioctl calls inside init() and remove the below defines
#ifndef SNDCTL_DSP_COOKEDMODE
#define SNDCTL_DSP_COOKEDMODE _IOW('P', 30, int)
#endif
#ifndef SNDCTL_DSP_POLICY
#define SNDCTL_DSP_POLICY _IOW('P', 45, int)
#endif
namespace ruby {
class pAudioOSS {
public:
struct {
int fd;
int format;
int channels;
const char* name;
} device;
struct {
unsigned frequency;
} settings;
bool cap(const string& name) {
if(name == Audio::Frequency) return true;
return false;
}
any get(const string& name) {
if(name == Audio::Frequency) return settings.frequency;
return false;
}
bool set(const string& name, const any& value) {
if(name == Audio::Frequency) {
settings.frequency = any_cast<unsigned>(value);
if(device.fd > 0) init();
return true;
}
return false;
}
void sample(uint16_t sl, uint16_t sr) {
uint32_t sample = sl + (sr << 16);
unsigned unused = write(device.fd, &sample, 4);
}
void clear() {
}
bool init() {
term();
device.fd = open(device.name, O_WRONLY, O_NONBLOCK);
if(device.fd < 0) return false;
#if 1 //SOUND_VERSION >= 0x040000
//attempt to enable OSS4-specific features regardless of version
//OSS3 ioctl calls will silently fail, but sound will still work
int cooked = 1, policy = 4; //policy should be 0 - 10, lower = less latency, more CPU usage
ioctl(device.fd, SNDCTL_DSP_COOKEDMODE, &cooked);
ioctl(device.fd, SNDCTL_DSP_POLICY, &policy);
#endif
int freq = settings.frequency;
ioctl(device.fd, SNDCTL_DSP_CHANNELS, &device.channels);
ioctl(device.fd, SNDCTL_DSP_SETFMT, &device.format);
ioctl(device.fd, SNDCTL_DSP_SPEED, &freq);
return true;
}
void term() {
if(device.fd > 0) {
close(device.fd);
device.fd = -1;
}
}
pAudioOSS() {
device.fd = -1;
device.format = AFMT_S16_LE;
device.channels = 2;
device.name = "/dev/dsp";
settings.frequency = 22050;
}
~pAudioOSS() {
term();
}
};
DeclareAudio(OSS)
};

View File

@ -0,0 +1,177 @@
//audio.pulseaudio (2010-01-05)
//author: RedDwarf
#include <pulse/pulseaudio.h>
namespace ruby {
class pAudioPulseAudio {
public:
struct {
pa_mainloop* mainloop;
pa_context* context;
pa_stream* stream;
pa_sample_spec spec;
pa_buffer_attr buffer_attr;
bool first;
} device;
struct {
uint32_t* data;
size_t size;
unsigned offset;
} buffer;
struct {
bool synchronize;
unsigned frequency;
unsigned latency;
} settings;
bool cap(const string& name) {
if(name == Audio::Synchronize) return true;
if(name == Audio::Frequency) return true;
if(name == Audio::Latency) return true;
}
any get(const string& name) {
if(name == Audio::Synchronize) return settings.synchronize;
if(name == Audio::Frequency) return settings.frequency;
if(name == Audio::Latency) return settings.latency;
}
bool set(const string& name, const any& value) {
if(name == Audio::Synchronize) {
settings.synchronize = any_cast<bool>(value);
return true;
}
if(name == Audio::Frequency) {
settings.frequency = any_cast<unsigned>(value);
if(device.stream) {
pa_operation_unref(pa_stream_update_sample_rate(device.stream, settings.frequency, NULL, NULL));
}
return true;
}
if(name == Audio::Latency) {
settings.latency = any_cast<unsigned>(value);
if(device.stream) {
device.buffer_attr.tlength = pa_usec_to_bytes(settings.latency * PA_USEC_PER_MSEC, &device.spec);
pa_stream_set_buffer_attr(device.stream, &device.buffer_attr, NULL, NULL);
}
return true;
}
}
void sample(uint16_t left, uint16_t right) {
pa_stream_begin_write(device.stream, (void**)&buffer.data, &buffer.size);
buffer.data[buffer.offset++] = left + (right << 16);
if((buffer.offset + 1) * pa_frame_size(&device.spec) <= buffer.size) return;
while(true) {
if(device.first) {
device.first = false;
pa_mainloop_iterate(device.mainloop, 0, NULL);
} else {
pa_mainloop_iterate(device.mainloop, 1, NULL);
}
unsigned length = pa_stream_writable_size(device.stream);
if(length >= buffer.offset * pa_frame_size(&device.spec)) break;
if(settings.synchronize == false) {
buffer.offset = 0;
return;
}
}
pa_stream_write(device.stream, (const void*)buffer.data, buffer.offset * pa_frame_size(&device.spec), NULL, 0LL, PA_SEEK_RELATIVE);
buffer.data = 0;
buffer.offset = 0;
}
void clear() {
}
bool init() {
device.mainloop = pa_mainloop_new();
device.context = pa_context_new(pa_mainloop_get_api(device.mainloop), "ruby::pulseaudio");
pa_context_connect(device.context, NULL, PA_CONTEXT_NOFLAGS, NULL);
pa_context_state_t cstate;
do {
pa_mainloop_iterate(device.mainloop, 1, NULL);
cstate = pa_context_get_state(device.context);
if(!PA_CONTEXT_IS_GOOD(cstate)) return false;
} while(cstate != PA_CONTEXT_READY);
device.spec.format = PA_SAMPLE_S16LE;
device.spec.channels = 2;
device.spec.rate = settings.frequency;
device.stream = pa_stream_new(device.context, "audio", &device.spec, NULL);
device.buffer_attr.maxlength = -1;
device.buffer_attr.tlength = pa_usec_to_bytes(settings.latency * PA_USEC_PER_MSEC, &device.spec);
device.buffer_attr.prebuf = -1;
device.buffer_attr.minreq = -1;
device.buffer_attr.fragsize = -1;
pa_stream_flags_t flags = (pa_stream_flags_t)(PA_STREAM_ADJUST_LATENCY | PA_STREAM_VARIABLE_RATE);
pa_stream_connect_playback(device.stream, NULL, &device.buffer_attr, flags, NULL, NULL);
pa_stream_state_t sstate;
do {
pa_mainloop_iterate(device.mainloop, 1, NULL);
sstate = pa_stream_get_state(device.stream);
if(!PA_STREAM_IS_GOOD(sstate)) return false;
} while(sstate != PA_STREAM_READY);
buffer.size = 960;
buffer.offset = 0;
device.first = true;
return true;
}
void term() {
if(buffer.data) {
pa_stream_cancel_write(device.stream);
buffer.data = 0;
}
if(device.stream) {
pa_stream_disconnect(device.stream);
pa_stream_unref(device.stream);
device.stream = 0;
}
if(device.context) {
pa_context_disconnect(device.context);
pa_context_unref(device.context);
device.context = 0;
}
if(device.mainloop) {
pa_mainloop_free(device.mainloop);
device.mainloop = 0;
}
}
pAudioPulseAudio() {
device.mainloop = 0;
device.context = 0;
device.stream = 0;
buffer.data = 0;
settings.synchronize = false;
settings.frequency = 22050;
settings.latency = 60;
}
~pAudioPulseAudio() {
term();
}
};
DeclareAudio(PulseAudio)
}

View File

@ -0,0 +1,115 @@
//audio.pulseaudiosimple (2010-01-05)
//author: byuu
#include <pulse/simple.h>
#include <pulse/error.h>
namespace ruby {
class pAudioPulseAudioSimple {
public:
struct {
pa_simple* handle;
pa_sample_spec spec;
} device;
struct {
uint32_t* data;
unsigned offset;
} buffer;
struct {
unsigned frequency;
} settings;
bool cap(const string& name) {
if(name == Audio::Frequency) return true;
return false;
}
any get(const string& name) {
if(name == Audio::Frequency) return settings.frequency;
return false;
}
bool set(const string& name, const any& value) {
if(name == Audio::Frequency) {
settings.frequency = any_cast<unsigned>(value);
if(device.handle) init();
return true;
}
return false;
}
void sample(uint16_t left, uint16_t right) {
if(!device.handle) return;
buffer.data[buffer.offset++] = left + (right << 16);
if(buffer.offset >= 64) {
int error;
pa_simple_write(device.handle, (const void*)buffer.data, buffer.offset * sizeof(uint32_t), &error);
buffer.offset = 0;
}
}
void clear() {
}
bool init() {
term();
device.spec.format = PA_SAMPLE_S16LE;
device.spec.channels = 2;
device.spec.rate = settings.frequency;
int error = 0;
device.handle = pa_simple_new(
0, //default server
"ruby::pulseaudiosimple", //application name
PA_STREAM_PLAYBACK, //direction
0, //default device
"audio", //stream description
&device.spec, //sample format
0, //default channel map
0, //default buffering attributes
&error //error code
);
if(!device.handle) {
fprintf(stderr, "ruby::pulseaudiosimple failed to initialize - %s\n", pa_strerror(error));
return false;
}
buffer.data = new uint32_t[64];
buffer.offset = 0;
return true;
}
void term() {
if(device.handle) {
int error;
pa_simple_flush(device.handle, &error);
pa_simple_free(device.handle);
device.handle = nullptr;
}
if(buffer.data) {
delete[] buffer.data;
buffer.data = nullptr;
}
}
pAudioPulseAudioSimple() {
device.handle = nullptr;
buffer.data = nullptr;
settings.frequency = 22050;
}
~pAudioPulseAudioSimple() {
term();
}
};
DeclareAudio(PulseAudioSimple)
};

View File

@ -0,0 +1,198 @@
/*
audio.xaudio2 (2010-08-14)
author: OV2
*/
#include "xaudio2.hpp"
#include <windows.h>
namespace ruby {
class pAudioXAudio2: public IXAudio2VoiceCallback {
public:
IXAudio2* pXAudio2;
IXAudio2MasteringVoice* pMasterVoice;
IXAudio2SourceVoice* pSourceVoice;
//inherited from IXAudio2VoiceCallback
STDMETHODIMP_(void) OnBufferStart(void* pBufferContext){}
STDMETHODIMP_(void) OnLoopEnd(void* pBufferContext){}
STDMETHODIMP_(void) OnStreamEnd() {}
STDMETHODIMP_(void) OnVoiceError(void* pBufferContext, HRESULT Error) {}
STDMETHODIMP_(void) OnVoiceProcessingPassEnd() {}
STDMETHODIMP_(void) OnVoiceProcessingPassStart(UINT32 BytesRequired) {}
struct {
unsigned buffers;
unsigned latency;
uint32_t* buffer;
unsigned bufferoffset;
volatile long submitbuffers;
unsigned writebuffer;
} device;
struct {
bool synchronize;
unsigned frequency;
unsigned latency;
} settings;
bool cap(const string& name) {
if(name == Audio::Synchronize) return true;
if(name == Audio::Frequency) return true;
if(name == Audio::Latency) return true;
return false;
}
any get(const string& name) {
if(name == Audio::Synchronize) return settings.synchronize;
if(name == Audio::Frequency) return settings.frequency;
if(name == Audio::Latency) return settings.latency;
return false;
}
bool set(const string& name, const any& value) {
if(name == Audio::Synchronize) {
settings.synchronize = any_cast<bool>(value);
if(pXAudio2) clear();
return true;
}
if(name == Audio::Frequency) {
settings.frequency = any_cast<unsigned>(value);
if(pXAudio2) init();
return true;
}
if(name == Audio::Latency) {
settings.latency = any_cast<unsigned>(value);
if(pXAudio2) init();
return true;
}
return false;
}
void pushbuffer(unsigned bytes, uint32_t* pAudioData) {
XAUDIO2_BUFFER xa2buffer = {0};
xa2buffer.AudioBytes = bytes;
xa2buffer.pAudioData = reinterpret_cast<BYTE*>(pAudioData);
xa2buffer.pContext = 0;
InterlockedIncrement(&device.submitbuffers);
pSourceVoice->SubmitSourceBuffer(&xa2buffer);
}
void sample(uint16_t left, uint16_t right) {
device.buffer[device.writebuffer * device.latency + device.bufferoffset++] = left + (right << 16);
if(device.bufferoffset < device.latency) return;
device.bufferoffset = 0;
if(device.submitbuffers == device.buffers - 1) {
if(settings.synchronize == true) {
//wait until there is at least one other free buffer for the next sample
while(device.submitbuffers == device.buffers - 1) {
//Sleep(0);
}
} else { //we need one free buffer for the next sample, so ignore the current contents
return;
}
}
pushbuffer(device.latency * 4,device.buffer + device.writebuffer * device.latency);
device.writebuffer = (device.writebuffer + 1) % device.buffers;
}
void clear() {
if(!pSourceVoice) return;
pSourceVoice->Stop(0);
pSourceVoice->FlushSourceBuffers(); //calls OnBufferEnd for all currently submitted buffers
device.writebuffer = 0;
device.bufferoffset = 0;
if(device.buffer) memset(device.buffer, 0, device.latency * device.buffers * 4);
pSourceVoice->Start(0);
}
bool init() {
term();
device.buffers = 8;
device.latency = settings.frequency * settings.latency / device.buffers / 1000.0 + 0.5;
device.buffer = new uint32_t[device.latency * device.buffers];
device.bufferoffset = 0;
device.submitbuffers = 0;
HRESULT hr;
if(FAILED(hr = XAudio2Create(&pXAudio2, 0 , XAUDIO2_DEFAULT_PROCESSOR))) {
return false;
}
if(FAILED(hr = pXAudio2->CreateMasteringVoice( &pMasterVoice, 2, settings.frequency, 0, 0 , NULL))) {
return false;
}
WAVEFORMATEX wfx;
wfx.wFormatTag = WAVE_FORMAT_PCM;
wfx.nChannels = 2;
wfx.nSamplesPerSec = settings.frequency;
wfx.nBlockAlign = 4;
wfx.wBitsPerSample = 16;
wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign;
wfx.cbSize = 0;
if(FAILED(hr = pXAudio2->CreateSourceVoice(&pSourceVoice, (WAVEFORMATEX*)&wfx, XAUDIO2_VOICE_NOSRC , XAUDIO2_DEFAULT_FREQ_RATIO, this, NULL, NULL))) {
return false;
}
clear();
return true;
}
void term() {
if(pSourceVoice) {
pSourceVoice->Stop(0);
pSourceVoice->DestroyVoice();
pSourceVoice = nullptr;
}
if(pMasterVoice) {
pMasterVoice->DestroyVoice();
pMasterVoice = nullptr;
}
if(pXAudio2) {
pXAudio2->Release();
pXAudio2 = nullptr;
}
if(device.buffer) {
delete[] device.buffer;
device.buffer = nullptr;
}
}
STDMETHODIMP_(void) OnBufferEnd(void* pBufferContext) {
InterlockedDecrement(&device.submitbuffers);
}
pAudioXAudio2() {
pXAudio2 = nullptr;
pMasterVoice = nullptr;
pSourceVoice = nullptr;
device.buffer = nullptr;
device.bufferoffset = 0;
device.submitbuffers = 0;
device.writebuffer = 0;
settings.synchronize = false;
settings.frequency = 22050;
settings.latency = 120;
}
};
DeclareAudio(XAudio2)
};

View File

@ -0,0 +1,340 @@
/*
xaudio2.hpp (2010-08-14)
author: OV2
ruby-specific header to provide mingw-friendly xaudio2 interfaces
*/
#ifndef XAUDIO2_RUBY_H
#define XAUDIO2_RUBY_H
//64-bit GCC fix
#define GUID_EXT EXTERN_C
#define GUID_SECT
#include <BaseTyps.h>
#define DEFINE_GUID_X(n,l,w1,w2,b1,b2,b3,b4,b5,b6,b7,b8) GUID_EXT const GUID n GUID_SECT = {l,w1,w2,{b1,b2,b3,b4,b5,b6,b7,b8}}
#define DEFINE_CLSID_X(className, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \
DEFINE_GUID_X(CLSID_##className, 0x##l, 0x##w1, 0x##w2, 0x##b1, 0x##b2, 0x##b3, 0x##b4, 0x##b5, 0x##b6, 0x##b7, 0x##b8)
#define DEFINE_IID_X(interfaceName, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \
DEFINE_GUID_X(IID_##interfaceName, 0x##l, 0x##w1, 0x##w2, 0x##b1, 0x##b2, 0x##b3, 0x##b4, 0x##b5, 0x##b6, 0x##b7, 0x##b8)
#define X2DEFAULT(x) =x
DEFINE_CLSID_X(XAudio2, e21a7345, eb21, 468e, be, 50, 80, 4d, b9, 7c, f7, 08);
DEFINE_CLSID_X(XAudio2_Debug, f7a76c21, 53d4, 46bb, ac, 53, 8b, 45, 9c, ae, 46, bd);
DEFINE_IID_X(IXAudio2, 8bcf1f58, 9fe7, 4583, 8a, c6, e2, ad, c4, 65, c8, bb);
DECLARE_INTERFACE(IXAudio2Voice);
#define XAUDIO2_COMMIT_NOW 0
#define XAUDIO2_DEFAULT_CHANNELS 0
#define XAUDIO2_DEFAULT_SAMPLERATE 0
#define XAUDIO2_DEFAULT_FREQ_RATIO 4.0f
#define XAUDIO2_DEBUG_ENGINE 0x0001
#define XAUDIO2_VOICE_NOSRC 0x0004
typedef struct
{
WAVEFORMATEX Format;
union
{
WORD wValidBitsPerSample;
WORD wSamplesPerBlock;
WORD wReserved;
} Samples;
DWORD dwChannelMask;
GUID SubFormat;
} WAVEFORMATEXTENSIBLE, *PWAVEFORMATEXTENSIBLE, *LPPWAVEFORMATEXTENSIBLE;
typedef const WAVEFORMATEXTENSIBLE* LPCWAVEFORMATEXTENSIBLE;
typedef enum XAUDIO2_DEVICE_ROLE
{
NotDefaultDevice = 0x0,
DefaultConsoleDevice = 0x1,
DefaultMultimediaDevice = 0x2,
DefaultCommunicationsDevice = 0x4,
DefaultGameDevice = 0x8,
GlobalDefaultDevice = 0xf,
InvalidDeviceRole = ~GlobalDefaultDevice
} XAUDIO2_DEVICE_ROLE;
typedef struct XAUDIO2_DEVICE_DETAILS
{
WCHAR DeviceID[256];
WCHAR DisplayName[256];
XAUDIO2_DEVICE_ROLE Role;
WAVEFORMATEXTENSIBLE OutputFormat;
} XAUDIO2_DEVICE_DETAILS;
typedef struct XAUDIO2_VOICE_DETAILS
{
UINT32 CreationFlags;
UINT32 InputChannels;
UINT32 InputSampleRate;
} XAUDIO2_VOICE_DETAILS;
typedef enum XAUDIO2_WINDOWS_PROCESSOR_SPECIFIER
{
Processor1 = 0x00000001,
Processor2 = 0x00000002,
Processor3 = 0x00000004,
Processor4 = 0x00000008,
Processor5 = 0x00000010,
Processor6 = 0x00000020,
Processor7 = 0x00000040,
Processor8 = 0x00000080,
Processor9 = 0x00000100,
Processor10 = 0x00000200,
Processor11 = 0x00000400,
Processor12 = 0x00000800,
Processor13 = 0x00001000,
Processor14 = 0x00002000,
Processor15 = 0x00004000,
Processor16 = 0x00008000,
Processor17 = 0x00010000,
Processor18 = 0x00020000,
Processor19 = 0x00040000,
Processor20 = 0x00080000,
Processor21 = 0x00100000,
Processor22 = 0x00200000,
Processor23 = 0x00400000,
Processor24 = 0x00800000,
Processor25 = 0x01000000,
Processor26 = 0x02000000,
Processor27 = 0x04000000,
Processor28 = 0x08000000,
Processor29 = 0x10000000,
Processor30 = 0x20000000,
Processor31 = 0x40000000,
Processor32 = 0x80000000,
XAUDIO2_ANY_PROCESSOR = 0xffffffff,
XAUDIO2_DEFAULT_PROCESSOR = XAUDIO2_ANY_PROCESSOR
} XAUDIO2_WINDOWS_PROCESSOR_SPECIFIER, XAUDIO2_PROCESSOR;
typedef struct XAUDIO2_VOICE_SENDS
{
UINT32 OutputCount;
IXAudio2Voice** pOutputVoices;
} XAUDIO2_VOICE_SENDS;
typedef struct XAUDIO2_EFFECT_DESCRIPTOR
{
IUnknown* pEffect;
BOOL InitialState;
UINT32 OutputChannels;
} XAUDIO2_EFFECT_DESCRIPTOR;
typedef struct XAUDIO2_EFFECT_CHAIN
{
UINT32 EffectCount;
const XAUDIO2_EFFECT_DESCRIPTOR* pEffectDescriptors;
} XAUDIO2_EFFECT_CHAIN;
typedef enum XAUDIO2_FILTER_TYPE
{
LowPassFilter,
BandPassFilter,
HighPassFilter
} XAUDIO2_FILTER_TYPE;
typedef struct XAUDIO2_FILTER_PARAMETERS
{
XAUDIO2_FILTER_TYPE Type;
float Frequency;
float OneOverQ;
} XAUDIO2_FILTER_PARAMETERS;
typedef struct XAUDIO2_BUFFER
{
UINT32 Flags;
UINT32 AudioBytes;
const BYTE* pAudioData;
UINT32 PlayBegin;
UINT32 PlayLength;
UINT32 LoopBegin;
UINT32 LoopLength;
UINT32 LoopCount;
void* pContext;
} XAUDIO2_BUFFER;
typedef struct XAUDIO2_BUFFER_WMA
{
const UINT32* pDecodedPacketCumulativeBytes;
UINT32 PacketCount;
} XAUDIO2_BUFFER_WMA;
typedef struct XAUDIO2_VOICE_STATE
{
void* pCurrentBufferContext;
UINT32 BuffersQueued;
UINT64 SamplesPlayed;
} XAUDIO2_VOICE_STATE;
typedef struct XAUDIO2_PERFORMANCE_DATA
{
UINT64 AudioCyclesSinceLastQuery;
UINT64 TotalCyclesSinceLastQuery;
UINT32 MinimumCyclesPerQuantum;
UINT32 MaximumCyclesPerQuantum;
UINT32 MemoryUsageInBytes;
UINT32 CurrentLatencyInSamples;
UINT32 GlitchesSinceEngineStarted;
UINT32 ActiveSourceVoiceCount;
UINT32 TotalSourceVoiceCount;
UINT32 ActiveSubmixVoiceCount;
UINT32 TotalSubmixVoiceCount;
UINT32 ActiveXmaSourceVoices;
UINT32 ActiveXmaStreams;
} XAUDIO2_PERFORMANCE_DATA;
typedef struct XAUDIO2_DEBUG_CONFIGURATION
{
UINT32 TraceMask;
UINT32 BreakMask;
BOOL LogThreadID;
BOOL LogFileline;
BOOL LogFunctionName;
BOOL LogTiming;
} XAUDIO2_DEBUG_CONFIGURATION;
DECLARE_INTERFACE(IXAudio2EngineCallback)
{
STDMETHOD_(void, OnProcessingPassStart) (THIS) PURE;
STDMETHOD_(void, OnProcessingPassEnd) (THIS) PURE;
STDMETHOD_(void, OnCriticalError) (THIS_ HRESULT Error) PURE;
};
DECLARE_INTERFACE(IXAudio2VoiceCallback)
{
STDMETHOD_(void, OnVoiceProcessingPassStart) (THIS_ UINT32 BytesRequired) PURE;
STDMETHOD_(void, OnVoiceProcessingPassEnd) (THIS) PURE;
STDMETHOD_(void, OnStreamEnd) (THIS) PURE;
STDMETHOD_(void, OnBufferStart) (THIS_ void* pBufferContext) PURE;
STDMETHOD_(void, OnBufferEnd) (THIS_ void* pBufferContext) PURE;
STDMETHOD_(void, OnLoopEnd) (THIS_ void* pBufferContext) PURE;
STDMETHOD_(void, OnVoiceError) (THIS_ void* pBufferContext, HRESULT Error) PURE;
};
DECLARE_INTERFACE(IXAudio2Voice)
{
#define Declare_IXAudio2Voice_Methods() \
STDMETHOD_(void, GetVoiceDetails) (THIS_ XAUDIO2_VOICE_DETAILS* pVoiceDetails) PURE; \
STDMETHOD(SetOutputVoices) (THIS_ const XAUDIO2_VOICE_SENDS* pSendList) PURE; \
STDMETHOD(SetEffectChain) (THIS_ const XAUDIO2_EFFECT_CHAIN* pEffectChain) PURE; \
STDMETHOD(EnableEffect) (THIS_ UINT32 EffectIndex, \
UINT32 OperationSet X2DEFAULT(XAUDIO2_COMMIT_NOW)) PURE; \
STDMETHOD(DisableEffect) (THIS_ UINT32 EffectIndex, \
UINT32 OperationSet X2DEFAULT(XAUDIO2_COMMIT_NOW)) PURE; \
STDMETHOD_(void, GetEffectState) (THIS_ UINT32 EffectIndex, BOOL* pEnabled) PURE; \
STDMETHOD(SetEffectParameters) (THIS_ UINT32 EffectIndex, \
const void* pParameters, \
UINT32 ParametersByteSize, \
UINT32 OperationSet X2DEFAULT(XAUDIO2_COMMIT_NOW)) PURE; \
STDMETHOD(GetEffectParameters) (THIS_ UINT32 EffectIndex, void* pParameters, \
UINT32 ParametersByteSize) PURE; \
STDMETHOD(SetFilterParameters) (THIS_ const XAUDIO2_FILTER_PARAMETERS* pParameters, \
UINT32 OperationSet X2DEFAULT(XAUDIO2_COMMIT_NOW)) PURE; \
STDMETHOD_(void, GetFilterParameters) (THIS_ XAUDIO2_FILTER_PARAMETERS* pParameters) PURE; \
STDMETHOD(SetVolume) (THIS_ float Volume, \
UINT32 OperationSet X2DEFAULT(XAUDIO2_COMMIT_NOW)) PURE; \
STDMETHOD_(void, GetVolume) (THIS_ float* pVolume) PURE; \
STDMETHOD(SetChannelVolumes) (THIS_ UINT32 Channels, const float* pVolumes, \
UINT32 OperationSet X2DEFAULT(XAUDIO2_COMMIT_NOW)) PURE; \
STDMETHOD_(void, GetChannelVolumes) (THIS_ UINT32 Channels, float* pVolumes) PURE; \
STDMETHOD(SetOutputMatrix) (THIS_ IXAudio2Voice* pDestinationVoice, \
UINT32 SourceChannels, UINT32 DestinationChannels, \
const float* pLevelMatrix, \
UINT32 OperationSet X2DEFAULT(XAUDIO2_COMMIT_NOW)) PURE; \
STDMETHOD_(void, GetOutputMatrix) (THIS_ IXAudio2Voice* pDestinationVoice, \
UINT32 SourceChannels, UINT32 DestinationChannels, \
float* pLevelMatrix) PURE; \
STDMETHOD_(void, DestroyVoice) (THIS) PURE
Declare_IXAudio2Voice_Methods();
};
DECLARE_INTERFACE_(IXAudio2MasteringVoice, IXAudio2Voice)
{
Declare_IXAudio2Voice_Methods();
};
DECLARE_INTERFACE_(IXAudio2SubmixVoice, IXAudio2Voice)
{
Declare_IXAudio2Voice_Methods();
};
DECLARE_INTERFACE_(IXAudio2SourceVoice, IXAudio2Voice)
{
Declare_IXAudio2Voice_Methods();
STDMETHOD(Start) (THIS_ UINT32 Flags, UINT32 OperationSet X2DEFAULT(XAUDIO2_COMMIT_NOW)) PURE;
STDMETHOD(Stop) (THIS_ UINT32 Flags, UINT32 OperationSet X2DEFAULT(XAUDIO2_COMMIT_NOW)) PURE;
STDMETHOD(SubmitSourceBuffer) (THIS_ const XAUDIO2_BUFFER* pBuffer, const XAUDIO2_BUFFER_WMA* pBufferWMA X2DEFAULT(NULL)) PURE;
STDMETHOD(FlushSourceBuffers) (THIS) PURE;
STDMETHOD(Discontinuity) (THIS) PURE;
STDMETHOD(ExitLoop) (THIS_ UINT32 OperationSet X2DEFAULT(XAUDIO2_COMMIT_NOW)) PURE;
STDMETHOD_(void, GetState) (THIS_ XAUDIO2_VOICE_STATE* pVoiceState) PURE;
STDMETHOD(SetFrequencyRatio) (THIS_ float Ratio,
UINT32 OperationSet X2DEFAULT(XAUDIO2_COMMIT_NOW)) PURE;
STDMETHOD_(void, GetFrequencyRatio) (THIS_ float* pRatio) PURE;
};
DECLARE_INTERFACE_(IXAudio2, IUnknown)
{
STDMETHOD(QueryInterface) (THIS_ REFIID riid, void** ppvInterface) PURE;
STDMETHOD_(ULONG, AddRef) (THIS) PURE;
STDMETHOD_(ULONG, Release) (THIS) PURE;
STDMETHOD(GetDeviceCount) (THIS_ UINT32* pCount) PURE;
STDMETHOD(GetDeviceDetails) (THIS_ UINT32 Index, XAUDIO2_DEVICE_DETAILS* pDeviceDetails) PURE;
STDMETHOD(Initialize) (THIS_ UINT32 Flags X2DEFAULT(0),
XAUDIO2_PROCESSOR XAudio2Processor X2DEFAULT(XAUDIO2_DEFAULT_PROCESSOR)) PURE;
STDMETHOD(RegisterForCallbacks) (IXAudio2EngineCallback* pCallback) PURE;
STDMETHOD_(void, UnregisterForCallbacks) (IXAudio2EngineCallback* pCallback) PURE;
STDMETHOD(CreateSourceVoice) (THIS_ IXAudio2SourceVoice** ppSourceVoice,
const WAVEFORMATEX* pSourceFormat,
UINT32 Flags X2DEFAULT(0),
float MaxFrequencyRatio X2DEFAULT(XAUDIO2_DEFAULT_FREQ_RATIO),
IXAudio2VoiceCallback* pCallback X2DEFAULT(NULL),
const XAUDIO2_VOICE_SENDS* pSendList X2DEFAULT(NULL),
const XAUDIO2_EFFECT_CHAIN* pEffectChain X2DEFAULT(NULL)) PURE;
STDMETHOD(CreateSubmixVoice) (THIS_ IXAudio2SubmixVoice** ppSubmixVoice,
UINT32 InputChannels, UINT32 InputSampleRate,
UINT32 Flags X2DEFAULT(0), UINT32 ProcessingStage X2DEFAULT(0),
const XAUDIO2_VOICE_SENDS* pSendList X2DEFAULT(NULL),
const XAUDIO2_EFFECT_CHAIN* pEffectChain X2DEFAULT(NULL)) PURE;
STDMETHOD(CreateMasteringVoice) (THIS_ IXAudio2MasteringVoice** ppMasteringVoice,
UINT32 InputChannels X2DEFAULT(XAUDIO2_DEFAULT_CHANNELS),
UINT32 InputSampleRate X2DEFAULT(XAUDIO2_DEFAULT_SAMPLERATE),
UINT32 Flags X2DEFAULT(0), UINT32 DeviceIndex X2DEFAULT(0),
const XAUDIO2_EFFECT_CHAIN* pEffectChain X2DEFAULT(NULL)) PURE;
STDMETHOD(StartEngine) (THIS) PURE;
STDMETHOD_(void, StopEngine) (THIS) PURE;
STDMETHOD(CommitChanges) (THIS_ UINT32 OperationSet) PURE;
STDMETHOD_(void, GetPerformanceData) (THIS_ XAUDIO2_PERFORMANCE_DATA* pPerfData) PURE;
STDMETHOD_(void, SetDebugConfiguration) (THIS_ const XAUDIO2_DEBUG_CONFIGURATION* pDebugConfiguration,
void* pReserved X2DEFAULT(NULL)) PURE;
};
__inline HRESULT XAudio2Create(IXAudio2** ppXAudio2, UINT32 Flags X2DEFAULT(0),
XAUDIO2_PROCESSOR XAudio2Processor X2DEFAULT(XAUDIO2_DEFAULT_PROCESSOR))
{
IXAudio2* pXAudio2;
HRESULT hr = CoCreateInstance((Flags & XAUDIO2_DEBUG_ENGINE) ? CLSID_XAudio2_Debug : CLSID_XAudio2,
NULL, CLSCTX_INPROC_SERVER, IID_IXAudio2, (void**)&pXAudio2);
if (SUCCEEDED(hr))
{
hr = pXAudio2->Initialize(Flags, XAudio2Processor);
if (SUCCEEDED(hr))
{
*ppXAudio2 = pXAudio2;
}
else
{
pXAudio2->Release();
}
}
return hr;
}
#endif

View File

@ -0,0 +1,172 @@
/* Global Headers */
#if defined(PLATFORM_X)
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>
#elif defined(PLATFORM_MACOSX)
#define decimal CocoaDecimal
#include <Cocoa/Cocoa.h>
#include <Carbon/Carbon.h>
#undef decimal
#elif defined(PLATFORM_WINDOWS)
#define _WIN32_WINNT 0x0501
#include <windows.h>
#endif
using namespace nall;
/* Video */
#define DeclareVideo(Name) \
struct Video##Name : Video { \
bool cap(const string& name) { return p.cap(name); } \
any get(const string& name) { return p.get(name); } \
bool set(const string& name, const any& value) { return p.set(name, value); } \
\
bool lock(uint32_t*& data, unsigned& pitch, unsigned width, unsigned height) { return p.lock(data, pitch, width, height); } \
void unlock() { p.unlock(); } \
\
void clear() { p.clear(); } \
void refresh() { p.refresh(); } \
bool init() { return p.init(); } \
void term() { p.term(); } \
\
Video##Name() : p(*new pVideo##Name) {} \
~Video##Name() { delete &p; } \
\
private: \
pVideo##Name& p; \
};
#ifdef VIDEO_CGL
#include <ruby/video/cgl.cpp>
#endif
#ifdef VIDEO_DIRECT3D
#include <ruby/video/direct3d.cpp>
#endif
#ifdef VIDEO_DIRECTDRAW
#include <ruby/video/directdraw.cpp>
#endif
#ifdef VIDEO_GDI
#include <ruby/video/gdi.cpp>
#endif
#ifdef VIDEO_GLX
#include <ruby/video/glx.cpp>
#endif
#ifdef VIDEO_SDL
#include <ruby/video/sdl.cpp>
#endif
#ifdef VIDEO_WGL
#include <ruby/video/wgl.cpp>
#endif
#ifdef VIDEO_XSHM
#include <ruby/video/xshm.cpp>
#endif
#ifdef VIDEO_XV
#include <ruby/video/xv.cpp>
#endif
/* Audio */
#define DeclareAudio(Name) \
struct Audio##Name : Audio { \
bool cap(const string& name) { return p.cap(name); } \
any get(const string& name) { return p.get(name); } \
bool set(const string& name, const any& value) { return p.set(name, value); } \
\
void sample(uint16_t left, uint16_t right) { p.sample(left, right); } \
void clear() { p.clear(); } \
bool init() { return p.init(); } \
void term() { p.term(); } \
\
Audio##Name() : p(*new pAudio##Name) {} \
~Audio##Name() { delete &p; } \
\
private: \
pAudio##Name& p; \
};
#ifdef AUDIO_ALSA
#include <ruby/audio/alsa.cpp>
#endif
#ifdef AUDIO_AO
#include <ruby/audio/ao.cpp>
#endif
#ifdef AUDIO_DIRECTSOUND
#include <ruby/audio/directsound.cpp>
#endif
#ifdef AUDIO_OPENAL
#include <ruby/audio/openal.cpp>
#endif
#ifdef AUDIO_OSS
#include <ruby/audio/oss.cpp>
#endif
#ifdef AUDIO_PULSEAUDIO
#include <ruby/audio/pulseaudio.cpp>
#endif
#ifdef AUDIO_PULSEAUDIOSIMPLE
#include <ruby/audio/pulseaudiosimple.cpp>
#endif
#ifdef AUDIO_XAUDIO2
#include <ruby/audio/xaudio2.cpp>
#endif
/* Input */
#define DeclareInput(Name) \
struct Input##Name : Input { \
bool cap(const string& name) { return p.cap(name); } \
any get(const string& name) { return p.get(name); } \
bool set(const string& name, const any& value) { return p.set(name, value); } \
\
bool acquire() { return p.acquire(); } \
bool unacquire() { return p.unacquire(); } \
bool acquired() { return p.acquired(); } \
\
vector<HID::Device*> poll() { return p.poll(); } \
bool rumble(uint64_t id, bool enable) { return p.rumble(id, enable); } \
bool init() { return p.init(); } \
void term() { p.term(); } \
\
Input##Name() : p(*new pInput##Name) {} \
~Input##Name() { delete &p; } \
\
private: \
pInput##Name& p; \
};
#ifdef INPUT_CARBON
#include <ruby/input/carbon.cpp>
#endif
#ifdef INPUT_SDL
#include <ruby/input/sdl.cpp>
#endif
#ifdef INPUT_UDEV
#include <ruby/input/udev.cpp>
#endif
#ifdef INPUT_WINDOWS
#include <ruby/input/windows.cpp>
#endif
#ifdef INPUT_XLIB
#include <ruby/input/xlib.cpp>
#endif

23
src/burner/qt/ruby/input.hpp Executable file
View File

@ -0,0 +1,23 @@
struct Input {
static const char* Handle;
static const char* KeyboardSupport;
static const char* MouseSupport;
static const char* JoypadSupport;
static const char* JoypadRumbleSupport;
virtual bool cap(const nall::string& name) { return false; }
virtual nall::any get(const nall::string& name) { return false; }
virtual bool set(const nall::string& name, const nall::any& value) { return false; }
virtual bool acquire() { return false; }
virtual bool unacquire() { return false; }
virtual bool acquired() { return false; }
virtual nall::vector<nall::HID::Device*> poll() { return {}; }
virtual bool rumble(uint64_t id, bool enable) {}
virtual bool init() { return true; }
virtual void term() {}
Input() {}
virtual ~Input() {}
};

View File

@ -0,0 +1,182 @@
namespace ruby {
struct pInputCarbon {
struct Key {
uint8_t id;
string name;
};
vector<Key> keys;
struct Keyboard {
HID::Keyboard hid;
} kb;
bool cap(const string& name) {
if(name == Input::KeyboardSupport) return true;
return false;
}
any get(const string& name) {
return false;
}
bool set(const string& name, const any& value) {
return false;
}
bool acquire() { return false; }
bool unacquire() { return false; }
bool acquired() { return false; }
void assign(HID::Device& hid, unsigned groupID, unsigned inputID, int16_t value) {
auto& group = hid.group[groupID];
if(group.input[inputID].value == value) return;
if(input.onChange) input.onChange(hid, groupID, inputID, group.input[inputID].value, value);
group.input[inputID].value = value;
}
void poll(vector<HID::Device*>& devices) {
KeyMap keymap;
GetKeys(keymap);
uint8_t* buffer = (uint8_t*)keymap;
unsigned inputID = 0;
for(auto& key : keys) {
bool value = buffer[key.id >> 3] & (1 << (key.id & 7)));
assign(kb.hid, HID::Keyboard::GroupID::Button, inputID++, value);
}
devices.append(&kb.hid);
}
bool rumble(uint64_t id, bool enable) {
return false;
}
bool init() {
keys.append({0x35, "Escape"});
keys.append({0x7a, "F1"});
keys.append({0x78, "F2"});
keys.append({0x63, "F3"});
keys.append({0x76, "F4"});
keys.append({0x60, "F5"});
keys.append({0x61, "F6"});
keys.append({0x62, "F7"});
keys.append({0x64, "F8"});
keys.append({0x65, "F9"});
keys.append({0x6d, "F10"});
keys.append({0x67, "F11"});
//keys.append({0x??, "F12"});
keys.append({0x69, "PrintScreen"});
//keys.append({0x??, "ScrollLock"});
keys.append({0x71, "Pause"});
keys.append({0x32, "Tilde"});
keys.append({0x12, "Num1"});
keys.append({0x13, "Num2"});
keys.append({0x14, "Num3"});
keys.append({0x15, "Num4"});
keys.append({0x17, "Num5"});
keys.append({0x16, "Num6"});
keys.append({0x1a, "Num7"});
keys.append({0x1c, "Num8"});
keys.append({0x19, "Num9"});
keys.append({0x1d, "Num0"});
keys.append({0x1b, "Dash"});
keys.append({0x18, "Equal"});
keys.append({0x33, "Backspace"});
keys.append({0x72, "Insert"});
keys.append({0x75, "Delete"});
keys.append({0x73, "Home"});
keys.append({0x77, "End"});
keys.append({0x74, "PageUp"});
keys.append({0x79, "PageDown"});
keys.append({0x00, "A"});
keys.append({0x0b, "B"});
keys.append({0x08, "C"});
keys.append({0x02, "D"});
keys.append({0x0e, "E"});
keys.append({0x03, "F"});
keys.append({0x05, "G"});
keys.append({0x04, "H"});
keys.append({0x22, "I"});
keys.append({0x26, "J"});
keys.append({0x28, "K"});
keys.append({0x25, "L"});
keys.append({0x2e, "M"});
keys.append({0x2d, "N"});
keys.append({0x1f, "O"});
keys.append({0x23, "P"});
keys.append({0x0c, "Q"});
keys.append({0x0f, "R"});
keys.append({0x01, "S"});
keys.append({0x11, "T"});
keys.append({0x20, "U"});
keys.append({0x09, "V"});
keys.append({0x0d, "W"});
keys.append({0x07, "X"});
keys.append({0x10, "Y"});
keys.append({0x06, "Z"});
keys.append({0x21, "LeftBracket"});
keys.append({0x1e, "RightBracket"});
keys.append({0x2a, "Backslash"});
keys.append({0x29, "Semicolon"});
keys.append({0x27, "Apostrophe"});
keys.append({0x2b, "Comma"});
keys.append({0x2f, "Period"});
keys.append({0x2c, "Slash"});
keys.append({0x53, "Keypad1"});
keys.append({0x54, "Keypad2"});
keys.append({0x55, "Keypad3"});
keys.append({0x56, "Keypad4"});
keys.append({0x57, "Keypad5"});
keys.append({0x58, "Keypad6"});
keys.append({0x59, "Keypad7"});
keys.append({0x5b, "Keypad8"});
keys.append({0x5c, "Keypad9"});
keys.append({0x52, "Keypad0"});
//keys.append({0x??, "Point"});
keys.append({0x4c, "Enter"});
keys.append({0x45, "Add"});
keys.append({0x4e, "Subtract"});
keys.append({0x43, "Multiply"});
keys.append({0x4b, "Divide"});
keys.append({0x47, "NumLock"});
//keys.append({0x39, "CapsLock"});
keys.append({0x7e, "Up"});
keys.append({0x7d, "Down"});
keys.append({0x7b, "Left"});
keys.append({0x7c, "Right"});
keys.append({0x30, "Tab"});
keys.append({0x24, "Return"});
keys.append({0x31, "Spacebar"});
//keys.append({0x??, "Menu"});
keys.append({0x38, "Shift"});
keys.append({0x3b, "Control"});
keys.append({0x3a, "Alt"});
keys.append({0x37, "Super"});
kb.hid.id = 1;
for(auto& key : keys) kb.hid.button().append({key.name});
return true;
}
void term() {
}
};
DeclareInput(Carbon)
};

View File

@ -0,0 +1,211 @@
#ifndef RUBY_INPUT_JOYPAD_DIRECTINPUT
#define RUBY_INPUT_JOYPAD_DIRECTINPUT
namespace ruby {
BOOL CALLBACK DirectInput_EnumJoypadsCallback(const DIDEVICEINSTANCE* instance, void* p);
BOOL CALLBACK DirectInput_EnumJoypadAxesCallback(const DIDEVICEOBJECTINSTANCE* instance, void* p);
BOOL CALLBACK DirectInput_EnumJoypadEffectsCallback(const DIDEVICEOBJECTINSTANCE* instance, void* p);
struct InputJoypadDirectInput {
struct Joypad {
HID::Joypad hid;
LPDIRECTINPUTDEVICE8 device = nullptr;
LPDIRECTINPUTEFFECT effect = nullptr;
uint32_t pathID = 0;
uint16_t vendorID = 0;
uint16_t productID = 0;
bool isXInputDevice = false;
};
vector<Joypad> joypads;
uintptr_t handle = 0;
LPDIRECTINPUT8 context = nullptr;
LPDIRECTINPUTDEVICE8 device = nullptr;
bool xinputAvailable = false;
unsigned effects = 0;
void assign(HID::Joypad& hid, unsigned groupID, unsigned inputID, int16_t value) {
auto& group = hid.group[groupID];
if(group.input[inputID].value == value) return;
if(input.onChange) input.onChange(hid, groupID, inputID, group.input[inputID].value, value);
group.input[inputID].value = value;
}
void poll(vector<HID::Device*>& devices) {
for(auto& jp : joypads) {
if(FAILED(jp.device->Poll())) jp.device->Acquire();
DIJOYSTATE2 state;
if(FAILED(jp.device->GetDeviceState(sizeof(DIJOYSTATE2), &state))) continue;
for(unsigned n = 0; n < 4; n++) {
assign(jp.hid, HID::Joypad::GroupID::Axis, 0, state.lX);
assign(jp.hid, HID::Joypad::GroupID::Axis, 1, state.lY);
assign(jp.hid, HID::Joypad::GroupID::Axis, 2, state.lZ);
assign(jp.hid, HID::Joypad::GroupID::Axis, 3, state.lRx);
assign(jp.hid, HID::Joypad::GroupID::Axis, 4, state.lRy);
assign(jp.hid, HID::Joypad::GroupID::Axis, 5, state.lRz);
unsigned pov = state.rgdwPOV[n];
int16_t xaxis = 0;
int16_t yaxis = 0;
if(pov < 36000) {
if(pov >= 31500 || pov <= 4500) yaxis = -32768;
if(pov >= 4500 && pov <= 13500) xaxis = +32767;
if(pov >= 13500 && pov <= 22500) yaxis = +32767;
if(pov >= 22500 && pov <= 31500) xaxis = -32768;
}
assign(jp.hid, HID::Joypad::GroupID::Hat, n * 2 + 0, xaxis);
assign(jp.hid, HID::Joypad::GroupID::Hat, n * 2 + 1, yaxis);
}
for(unsigned n = 0; n < 128; n++) {
assign(jp.hid, HID::Joypad::GroupID::Button, n, (bool)state.rgbButtons[n]);
}
devices.append(&jp.hid);
}
}
bool rumble(uint64_t id, bool enable) {
for(auto& jp : joypads) {
if(jp.hid.id != id) continue;
if(jp.effect == nullptr) continue;
if(enable) jp.effect->Start(1, 0);
else jp.effect->Stop();
return true;
}
return false;
}
bool init(uintptr_t handle, LPDIRECTINPUT8 context, bool xinputAvailable) {
this->handle = handle;
this->context = context;
this->xinputAvailable = xinputAvailable;
context->EnumDevices(DI8DEVCLASS_GAMECTRL, DirectInput_EnumJoypadsCallback, (void*)this, DIEDFL_ATTACHEDONLY);
return true;
}
void term() {
for(auto& jp : joypads) {
jp.device->Unacquire();
if(jp.effect) jp.effect->Release();
jp.device->Release();
}
joypads.reset();
context = nullptr;
}
bool initJoypad(const DIDEVICEINSTANCE* instance) {
Joypad jp;
jp.vendorID = instance->guidProduct.Data1 >> 0;
jp.productID = instance->guidProduct.Data1 >> 16;
if(auto device = rawinput.find(jp.vendorID, jp.productID)) {
jp.pathID = crc32_calculate((const uint8_t*)device().path.data(), device().path.size());
jp.hid.id = (uint64_t)jp.pathID << 32 | jp.vendorID << 16 | jp.productID << 0;
jp.isXInputDevice = device().isXInputDevice;
} else {
//this should never occur
return DIENUM_CONTINUE;
}
//Microsoft has intentionally imposed artificial restrictions on XInput devices when used with DirectInput
//a) the two triggers are merged into a single axis, making uniquely distinguishing them impossible
//b) rumble support is not exposed
//thus, it's always preferred to let the XInput driver handle these joypads
//but if the driver is not available (XInput 1.3 does not ship with stock Windows XP), fall back on DirectInput
if(jp.isXInputDevice && xinputAvailable) return DIENUM_CONTINUE;
if(FAILED(context->CreateDevice(instance->guidInstance, &device, 0))) return DIENUM_CONTINUE;
jp.device = device;
device->SetDataFormat(&c_dfDIJoystick2);
device->SetCooperativeLevel((HWND)handle, DISCL_NONEXCLUSIVE | DISCL_BACKGROUND);
effects = 0;
device->EnumObjects(DirectInput_EnumJoypadAxesCallback, (void*)this, DIDFT_ABSAXIS);
device->EnumObjects(DirectInput_EnumJoypadEffectsCallback, (void*)this, DIDFT_FFACTUATOR);
jp.hid.rumble = effects > 0;
if(jp.hid.rumble) {
//disable auto-centering spring for rumble support
DIPROPDWORD property;
memset(&property, 0, sizeof(DIPROPDWORD));
property.diph.dwSize = sizeof(DIPROPDWORD);
property.diph.dwHeaderSize = sizeof(DIPROPHEADER);
property.diph.dwObj = 0;
property.diph.dwHow = DIPH_DEVICE;
property.dwData = false;
device->SetProperty(DIPROP_AUTOCENTER, &property.diph);
DWORD dwAxes[2] = {DIJOFS_X, DIJOFS_Y};
LONG lDirection[2] = {0, 0};
DICONSTANTFORCE force;
force.lMagnitude = DI_FFNOMINALMAX; //full force
DIEFFECT effect;
memset(&effect, 0, sizeof(DIEFFECT));
effect.dwSize = sizeof(DIEFFECT);
effect.dwFlags = DIEFF_CARTESIAN | DIEFF_OBJECTOFFSETS;
effect.dwDuration = INFINITE;
effect.dwSamplePeriod = 0;
effect.dwGain = DI_FFNOMINALMAX;
effect.dwTriggerButton = DIEB_NOTRIGGER;
effect.dwTriggerRepeatInterval = 0;
effect.cAxes = 2;
effect.rgdwAxes = dwAxes;
effect.rglDirection = lDirection;
effect.lpEnvelope = 0;
effect.cbTypeSpecificParams = sizeof(DICONSTANTFORCE);
effect.lpvTypeSpecificParams = &force;
effect.dwStartDelay = 0;
device->CreateEffect(GUID_ConstantForce, &effect, &jp.effect, NULL);
}
for(unsigned n = 0; n < 6; n++) jp.hid.axis().append({n});
for(unsigned n = 0; n < 8; n++) jp.hid.hat().append({n});
for(unsigned n = 0; n < 128; n++) jp.hid.button().append({n});
joypads.append(jp);
return DIENUM_CONTINUE;
}
bool initAxis(const DIDEVICEOBJECTINSTANCE* instance) {
DIPROPRANGE range;
memset(&range, 0, sizeof(DIPROPRANGE));
range.diph.dwSize = sizeof(DIPROPRANGE);
range.diph.dwHeaderSize = sizeof(DIPROPHEADER);
range.diph.dwHow = DIPH_BYID;
range.diph.dwObj = instance->dwType;
range.lMin = -32768;
range.lMax = +32767;
device->SetProperty(DIPROP_RANGE, &range.diph);
return DIENUM_CONTINUE;
}
bool initEffect(const DIDEVICEOBJECTINSTANCE* instance) {
effects++;
return DIENUM_CONTINUE;
}
};
BOOL CALLBACK DirectInput_EnumJoypadsCallback(const DIDEVICEINSTANCE* instance, void* p) {
return ((InputJoypadDirectInput*)p)->initJoypad(instance);
}
BOOL CALLBACK DirectInput_EnumJoypadAxesCallback(const DIDEVICEOBJECTINSTANCE* instance, void* p) {
return ((InputJoypadDirectInput*)p)->initAxis(instance);
}
BOOL CALLBACK DirectInput_EnumJoypadEffectsCallback(const DIDEVICEOBJECTINSTANCE* instance, void* p) {
return ((InputJoypadDirectInput*)p)->initEffect(instance);
}
}
#endif

View File

@ -0,0 +1,81 @@
#ifndef RUBY_INPUT_JOYPAD_SDL
#define RUBY_INPUT_JOYPAD_SDL
namespace ruby {
struct InputJoypadSDL {
struct Joypad {
HID::Joypad hid;
unsigned id = 0;
SDL_Joystick* handle = nullptr;
};
vector<Joypad> joypads;
void assign(HID::Joypad& hid, unsigned groupID, unsigned inputID, int16_t value) {
auto& group = hid.group[groupID];
if(group.input[inputID].value == value) return;
if(input.onChange) input.onChange(hid, groupID, inputID, group.input[inputID].value, value);
group.input[inputID].value = value;
}
void poll(vector<HID::Device*>& devices) {
SDL_JoystickUpdate();
for(auto& jp : joypads) {
for(unsigned n = 0; n < jp.hid.axis().input.size(); n++) {
assign(jp.hid, HID::Joypad::GroupID::Axis, n, (int16_t)SDL_JoystickGetAxis(jp.handle, n));
}
for(signed n = 0; n < (signed)jp.hid.hat().input.size() - 1; n += 2) {
uint8_t state = SDL_JoystickGetHat(jp.handle, n >> 1);
assign(jp.hid, HID::Joypad::GroupID::Hat, n + 0, state & SDL_HAT_LEFT ? -32768 : state & SDL_HAT_RIGHT ? +32767 : 0);
assign(jp.hid, HID::Joypad::GroupID::Hat, n + 1, state & SDL_HAT_UP ? -32768 : state & SDL_HAT_DOWN ? +32767 : 0);
}
for(unsigned n = 0; n < jp.hid.button().input.size(); n++) {
assign(jp.hid, HID::Joypad::GroupID::Button, n, (bool)SDL_JoystickGetButton(jp.handle, n));
}
devices.append(&jp.hid);
}
}
bool init() {
SDL_InitSubSystem(SDL_INIT_JOYSTICK);
SDL_JoystickEventState(SDL_IGNORE);
unsigned joypadCount = SDL_NumJoysticks();
for(unsigned id = 0; id < joypadCount; id++) {
Joypad jp;
jp.id = id;
jp.handle = SDL_JoystickOpen(id);
unsigned axes = SDL_JoystickNumAxes(jp.handle);
unsigned hats = SDL_JoystickNumHats(jp.handle) * 2;
unsigned buttons = 32; //there is no SDL_JoystickNumButtons()
jp.hid.id = 2 + jp.id;
for(unsigned n = 0; n < axes; n++) jp.hid.axis().append({n});
for(unsigned n = 0; n < hats; n++) jp.hid.hat().append({n});
for(unsigned n = 0; n < buttons; n++) jp.hid.button().append({n});
jp.hid.rumble = false;
joypads.append(jp);
}
return true;
}
void term() {
for(auto& jp : joypads) {
SDL_JoystickClose(jp.handle);
}
joypads.reset();
SDL_QuitSubSystem(SDL_INIT_JOYSTICK);
}
};
}
#endif

View File

@ -0,0 +1,280 @@
#ifndef RUBY_INPUT_JOYPAD_UDEV
#define RUBY_INPUT_JOYPAD_UDEV
namespace ruby {
struct InputJoypadUdev {
udev* context = nullptr;
udev_monitor* monitor = nullptr;
udev_enumerate* enumerator = nullptr;
udev_list_entry* devices = nullptr;
udev_list_entry* item = nullptr;
struct JoypadInput {
signed code = 0;
unsigned id = 0;
int16_t value = 0;
input_absinfo info;
JoypadInput() {}
JoypadInput(signed code) : code(code) {}
JoypadInput(signed code, unsigned id) : code(code), id(id) {}
bool operator< (const JoypadInput& source) const { return code < source.code; }
bool operator==(const JoypadInput& source) const { return code == source.code; }
};
struct Joypad {
HID::Joypad hid;
int fd = -1;
dev_t device = 0;
string deviceName;
string deviceNode;
uint8_t evbit[(EV_MAX + 7) / 8] = {0};
uint8_t keybit[(KEY_MAX + 7) / 8] = {0};
uint8_t absbit[(ABS_MAX + 7) / 8] = {0};
uint8_t ffbit[(FF_MAX + 7) / 8] = {0};
unsigned effects = 0;
string name;
string manufacturer;
string product;
string serial;
string vendorID;
string productID;
set<JoypadInput> axes;
set<JoypadInput> hats;
set<JoypadInput> buttons;
bool rumble = false;
unsigned effectID = 0;
};
vector<Joypad> joypads;
void assign(HID::Joypad& hid, unsigned groupID, unsigned inputID, int16_t value) {
auto& group = hid.group[groupID];
if(group.input[inputID].value == value) return;
if(input.onChange) input.onChange(hid, groupID, inputID, group.input[inputID].value, value);
group.input[inputID].value = value;
}
void poll(vector<HID::Device*>& devices) {
while(hotplugDevicesAvailable()) hotplugDevice();
for(auto& jp : joypads) {
input_event events[32];
signed length = 0;
while((length = read(jp.fd, events, sizeof(events))) > 0) {
length /= sizeof(input_event);
for(unsigned i = 0; i < length; i++) {
signed code = events[i].code;
signed type = events[i].type;
signed value = events[i].value;
if(type == EV_ABS) {
if(auto input = jp.axes.find({code})) {
signed range = input().info.maximum - input().info.minimum;
value = (value - input().info.minimum) * 65535ll / range - 32767;
assign(jp.hid, HID::Joypad::GroupID::Axis, input().id, sclamp<16>(value));
} else if(auto input = jp.hats.find({code})) {
signed range = input().info.maximum - input().info.minimum;
value = (value - input().info.minimum) * 65535ll / range - 32767;
assign(jp.hid, HID::Joypad::GroupID::Hat, input().id, sclamp<16>(value));
}
} else if(type == EV_KEY) {
if(code >= BTN_MISC) {
if(auto input = jp.buttons.find({code})) {
assign(jp.hid, HID::Joypad::GroupID::Button, input().id, (bool)value);
}
}
}
}
}
devices.append(&jp.hid);
}
}
bool rumble(uint64_t id, bool enable) {
for(auto& jp : joypads) {
if(jp.hid.id != id) continue;
if(jp.hid.rumble == false) continue;
input_event play;
memset(&play, 0, sizeof(input_event));
play.type = EV_FF;
play.code = jp.effectID;
play.value = enable;
write(jp.fd, &play, sizeof(input_event));
return true;
}
return false;
}
bool init() {
context = udev_new();
if(context == nullptr) return false;
monitor = udev_monitor_new_from_netlink(context, "udev");
if(monitor) {
udev_monitor_filter_add_match_subsystem_devtype(monitor, "input", nullptr);
udev_monitor_enable_receiving(monitor);
}
enumerator = udev_enumerate_new(context);
if(enumerator) {
udev_enumerate_add_match_property(enumerator, "ID_INPUT_JOYSTICK", "1");
udev_enumerate_scan_devices(enumerator);
devices = udev_enumerate_get_list_entry(enumerator);
for(udev_list_entry* item = devices; item != nullptr; item = udev_list_entry_get_next(item)) {
string name = udev_list_entry_get_name(item);
udev_device* device = udev_device_new_from_syspath(context, name);
string deviceNode = udev_device_get_devnode(device);
if(deviceNode) createJoypad(device, deviceNode);
udev_device_unref(device);
}
}
return true;
}
void term() {
if(enumerator) { udev_enumerate_unref(enumerator); enumerator = nullptr; }
}
private:
bool hotplugDevicesAvailable() {
pollfd fd = {0};
fd.fd = udev_monitor_get_fd(monitor);
fd.events = POLLIN;
return (::poll(&fd, 1, 0) == 1) && (fd.revents & POLLIN);
}
void hotplugDevice() {
udev_device* device = udev_monitor_receive_device(monitor);
if(device == nullptr) return;
string value = udev_device_get_property_value(device, "ID_INPUT_JOYSTICK");
string action = udev_device_get_action(device);
string deviceNode = udev_device_get_devnode(device);
if(value == "1") {
if(action == "add") {
createJoypad(device, deviceNode);
}
if(action == "remove") {
removeJoypad(device, deviceNode);
}
}
}
void createJoypad(udev_device* device, const string& deviceNode) {
Joypad jp;
jp.deviceNode = deviceNode;
struct stat st;
if(stat(deviceNode, &st) < 0) return;
jp.device = st.st_rdev;
jp.fd = open(deviceNode, O_RDWR | O_NONBLOCK);
if(jp.fd < 0) return;
uint8_t evbit[(EV_MAX + 7) / 8] = {0};
uint8_t keybit[(KEY_MAX + 7) / 8] = {0};
uint8_t absbit[(ABS_MAX + 7) / 8] = {0};
ioctl(jp.fd, EVIOCGBIT(0, sizeof(jp.evbit)), jp.evbit);
ioctl(jp.fd, EVIOCGBIT(EV_KEY, sizeof(jp.keybit)), jp.keybit);
ioctl(jp.fd, EVIOCGBIT(EV_ABS, sizeof(jp.absbit)), jp.absbit);
ioctl(jp.fd, EVIOCGBIT(EV_FF, sizeof(jp.ffbit)), jp.ffbit);
ioctl(jp.fd, EVIOCGEFFECTS, &jp.effects);
#define testBit(buffer, bit) (buffer[(bit) >> 3] & 1 << ((bit) & 7))
if(testBit(jp.evbit, EV_KEY)) {
if(udev_device* parent = udev_device_get_parent_with_subsystem_devtype(device, "input", nullptr)) {
jp.name = udev_device_get_sysattr_value(parent, "name");
jp.vendorID = udev_device_get_sysattr_value(parent, "id/vendor");
jp.productID = udev_device_get_sysattr_value(parent, "id/product");
if(udev_device* root = udev_device_get_parent_with_subsystem_devtype(parent, "usb", "usb_device")) {
if(jp.vendorID == udev_device_get_sysattr_value(root, "idVendor")
&& jp.productID == udev_device_get_sysattr_value(root, "idProduct")
) {
jp.deviceName = udev_device_get_devpath(root);
jp.manufacturer = udev_device_get_sysattr_value(root, "manufacturer");
jp.product = udev_device_get_sysattr_value(root, "product");
jp.serial = udev_device_get_sysattr_value(root, "serial");
}
}
}
unsigned axes = 0;
unsigned hats = 0;
unsigned buttons = 0;
for(signed i = 0; i < ABS_MISC; i++) {
if(testBit(jp.absbit, i)) {
if(i >= ABS_HAT0X && i <= ABS_HAT3Y) {
if(auto hat = jp.hats.insert({i, hats++})) {
ioctl(jp.fd, EVIOCGABS(i), &hat().info);
}
} else {
if(auto axis = jp.axes.insert({i, axes++})) {
ioctl(jp.fd, EVIOCGABS(i), &axis().info);
}
}
}
}
for(signed i = BTN_JOYSTICK; i < KEY_MAX; i++) {
if(testBit(jp.keybit, i)) {
jp.buttons.insert({i, buttons++});
}
}
for(signed i = BTN_MISC; i < BTN_JOYSTICK; i++) {
if(testBit(jp.keybit, i)) {
jp.buttons.insert({i, buttons++});
}
}
jp.rumble = jp.effects >= 2 && testBit(jp.ffbit, FF_RUMBLE);
if(jp.rumble) {
ff_effect effect;
memset(&effect, 0, sizeof(ff_effect));
effect.type = FF_RUMBLE;
effect.id = -1;
effect.u.rumble.strong_magnitude = 65535;
effect.u.rumble.weak_magnitude = 65535;
ioctl(jp.fd, EVIOCSFF, &effect);
jp.effectID = effect.id;
}
createJoypadHID(jp);
joypads.append(jp);
}
#undef testBit
}
void createJoypadHID(Joypad& jp) {
uint64_t pathID = crc32_calculate((const uint8_t*)jp.deviceName.data(), jp.deviceName.size());
jp.hid.id = pathID << 32 | hex(jp.vendorID) << 16 | hex(jp.productID) << 0;
for(unsigned n = 0; n < jp.axes.size(); n++) jp.hid.axis().append({n});
for(unsigned n = 0; n < jp.hats.size(); n++) jp.hid.hat().append({n});
for(unsigned n = 0; n < jp.buttons.size(); n++) jp.hid.button().append({n});
jp.hid.rumble = jp.rumble;
}
void removeJoypad(udev_device* device, const string& deviceNode) {
for(unsigned n = 0; n < joypads.size(); n++) {
if(joypads[n].deviceNode == deviceNode) {
close(joypads[n].fd);
joypads.remove(n);
return;
}
}
}
};
}
#endif

View File

@ -0,0 +1,162 @@
#ifndef RUBY_INPUT_JOYPAD_XINPUT
#define RUBY_INPUT_JOYPAD_XINPUT
//documented functionality
#define oXInputGetState "XInputGetState"
#define oXInputSetState "XInputSetState"
typedef DWORD WINAPI (*pXInputGetState)(DWORD dwUserIndex, XINPUT_STATE* pState);
typedef DWORD WINAPI (*pXInputSetState)(DWORD dwUserIndex, XINPUT_VIBRATION* pVibration);
//undocumented functionality
#define oXInputGetStateEx (LPCSTR)100
#define oXInputWaitForGuideButton (LPCSTR)101
#define oXInputCancelGuideButtonWait (LPCSTR)102
#define oXInputPowerOffController (LPCSTR)103
typedef DWORD WINAPI (*pXInputGetStateEx)(DWORD dwUserIndex, XINPUT_STATE* pState);
typedef DWORD WINAPI (*pXInputWaitForGuideButton)(DWORD dwUserIndex, DWORD dwFlag, void* pUnknown);
typedef DWORD WINAPI (*pXInputCancelGuideButtonWait)(DWORD dwUserIndex);
typedef DWORD WINAPI (*pXInputPowerOffController)(DWORD dwUserIndex);
#define XINPUT_GAMEPAD_GUIDE 0x0400
namespace ruby {
struct InputJoypadXInput {
HMODULE libxinput = nullptr;
pXInputGetStateEx XInputGetStateEx = nullptr;
pXInputSetState XInputSetState = nullptr;
struct Joypad {
HID::Joypad hid;
unsigned id;
};
vector<Joypad> joypads;
void assign(HID::Joypad& hid, unsigned groupID, unsigned inputID, int16_t value) {
auto& group = hid.group[groupID];
if(group.input[inputID].value == value) return;
if(input.onChange) input.onChange(hid, groupID, inputID, group.input[inputID].value, value);
group.input[inputID].value = value;
}
void poll(vector<HID::Device*>& devices) {
for(auto& jp : joypads) {
XINPUT_STATE state;
if(XInputGetStateEx(jp.id, &state) != ERROR_SUCCESS) continue;
//flip vertical axes so that -32768 = up, +32767 = down
uint16_t axisLY = 32768 + state.Gamepad.sThumbLY;
uint16_t axisRY = 32768 + state.Gamepad.sThumbRY;
assign(jp.hid, HID::Joypad::GroupID::Axis, 0, (int16_t)state.Gamepad.sThumbLX);
assign(jp.hid, HID::Joypad::GroupID::Axis, 1, (int16_t)(~axisLY - 32768));
assign(jp.hid, HID::Joypad::GroupID::Axis, 2, (int16_t)state.Gamepad.sThumbRX);
assign(jp.hid, HID::Joypad::GroupID::Axis, 3, (int16_t)(~axisRY - 32768));
int16_t hatX = 0;
int16_t hatY = 0;
if(state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_UP ) hatY = -32768;
if(state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_DOWN ) hatY = +32767;
if(state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_LEFT ) hatX = -32768;
if(state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_RIGHT) hatX = +32767;
assign(jp.hid, HID::Joypad::GroupID::Hat, 0, hatX);
assign(jp.hid, HID::Joypad::GroupID::Hat, 1, hatY);
//scale trigger ranges for up to down from (0 to 255) to (-32768 to +32767)
uint16_t triggerL = state.Gamepad.bLeftTrigger;
uint16_t triggerR = state.Gamepad.bRightTrigger;
triggerL = triggerL << 8 | triggerL << 0;
triggerR = triggerR << 8 | triggerR << 0;
assign(jp.hid, HID::Joypad::GroupID::Trigger, 0, (int16_t)(triggerL - 32768));
assign(jp.hid, HID::Joypad::GroupID::Trigger, 1, (int16_t)(triggerR - 32768));
assign(jp.hid, HID::Joypad::GroupID::Button, 0, (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_A));
assign(jp.hid, HID::Joypad::GroupID::Button, 1, (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_B));
assign(jp.hid, HID::Joypad::GroupID::Button, 2, (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_X));
assign(jp.hid, HID::Joypad::GroupID::Button, 3, (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_Y));
assign(jp.hid, HID::Joypad::GroupID::Button, 4, (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_BACK));
assign(jp.hid, HID::Joypad::GroupID::Button, 5, (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_START));
assign(jp.hid, HID::Joypad::GroupID::Button, 6, (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_SHOULDER));
assign(jp.hid, HID::Joypad::GroupID::Button, 7, (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_SHOULDER));
assign(jp.hid, HID::Joypad::GroupID::Button, 8, (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_THUMB));
assign(jp.hid, HID::Joypad::GroupID::Button, 9, (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_THUMB));
assign(jp.hid, HID::Joypad::GroupID::Button, 10, (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_GUIDE));
devices.append(&jp.hid);
}
}
bool rumble(uint64_t id, bool enable) {
for(auto& jp : joypads) {
if(jp.hid.id != id) continue;
XINPUT_VIBRATION vibration;
memset(&vibration, 0, sizeof(XINPUT_VIBRATION));
vibration.wLeftMotorSpeed = enable ? 65535 : 0; //low-frequency motor (0 = off, 65535 = max)
vibration.wRightMotorSpeed = enable ? 65535 : 0; //high-frequency motor (0 = off, 65535 = max)
XInputSetState(jp.id, &vibration);
return true;
}
return false;
}
bool init() {
if(!libxinput) libxinput = LoadLibraryA("xinput1_3.dll");
if(!libxinput) return false;
//XInputGetStateEx is an undocumented function; but is required to get the state of the guide button
//if for some reason it is not available, fall back on XInputGetState, which takes the same parameters
XInputGetStateEx = (pXInputGetStateEx)GetProcAddress(libxinput, oXInputGetStateEx);
XInputSetState = (pXInputSetState)GetProcAddress(libxinput, oXInputSetState);
if(!XInputGetStateEx) XInputGetStateEx = (pXInputGetStateEx)GetProcAddress(libxinput, oXInputGetState);
if(!XInputGetStateEx || !XInputSetState) return term(), false;
//XInput supports a maximum of four controllers
//add all four to devices list now. If they are not connected, they will not show up in poll() results
for(unsigned id = 0; id < 4; id++) {
Joypad jp;
jp.id = id;
jp.hid.id = (uint64_t)(1 + id) << 32 | 0x045e << 16 | 0x028e << 0; //Xbox 360 Player# + VendorID + ProductID
jp.hid.axis().append({"LeftThumbX"});
jp.hid.axis().append({"LeftThumbY"});
jp.hid.axis().append({"RightThumbX"});
jp.hid.axis().append({"RightThumbY"});
jp.hid.hat().append({"HatX"});
jp.hid.hat().append({"HatY"});
jp.hid.trigger().append({"LeftTrigger"});
jp.hid.trigger().append({"RightTrigger"});
jp.hid.button().append({"A"});
jp.hid.button().append({"B"});
jp.hid.button().append({"X"});
jp.hid.button().append({"Y"});
jp.hid.button().append({"Back"});
jp.hid.button().append({"Start"});
jp.hid.button().append({"LeftShoulder"});
jp.hid.button().append({"RightShoulder"});
jp.hid.button().append({"LeftThumb"});
jp.hid.button().append({"RightThumb"});
jp.hid.button().append({"Guide"});
joypads.append(jp);
}
return true;
}
void term() {
if(!libxinput) return;
FreeLibrary(libxinput);
libxinput = nullptr;
}
};
}
#endif

View File

@ -0,0 +1,178 @@
#ifndef RUBY_INPUT_KEYBOARD_RAWINPUT
#define RUBY_INPUT_KEYBOARD_RAWINPUT
namespace ruby {
struct InputKeyboardRawInput {
struct Key {
uint16_t code;
uint16_t flag;
string name;
bool value;
};
vector<Key> keys;
struct Keyboard {
HID::Keyboard hid;
} kb;
void update(RAWINPUT* input) {
unsigned code = input->data.keyboard.MakeCode;
unsigned flag = input->data.keyboard.Flags;
for(auto& key : keys) {
if(key.code != code) continue;
key.value = (key.flag == flag);
}
}
void assign(unsigned inputID, bool value) {
auto& group = kb.hid.group[HID::Keyboard::GroupID::Button];
if(group.input[inputID].value == value) return;
if(input.onChange) input.onChange(kb.hid, HID::Keyboard::GroupID::Button, inputID, group.input[inputID].value, value);
group.input[inputID].value = value;
}
void poll(vector<HID::Device*>& devices) {
for(unsigned n = 0; n < keys.size(); n++) assign(n, keys[n].value);
devices.append(&kb.hid);
}
bool init() {
rawinput.updateKeyboard = {&InputKeyboardRawInput::update, this};
//Pause sends 0x001d,4 + 0x0045,0; NumLock sends only 0x0045,0
//pressing Pause will falsely trigger NumLock
//further, pause sends its key release even while button is held down
//because of this, we cannot map either reliably
keys.append({0x0001, 0, "Escape"});
keys.append({0x003b, 0, "F1"});
keys.append({0x003c, 0, "F2"});
keys.append({0x003d, 0, "F3"});
keys.append({0x003e, 0, "F4"});
keys.append({0x003f, 0, "F5"});
keys.append({0x0040, 0, "F6"});
keys.append({0x0041, 0, "F7"});
keys.append({0x0042, 0, "F8"});
keys.append({0x0043, 0, "F9"});
keys.append({0x0044, 0, "F10"});
keys.append({0x0057, 0, "F11"});
keys.append({0x0058, 0, "F12"});
keys.append({0x0037, 2, "PrintScreen"});
keys.append({0x0046, 0, "ScrollLock"});
//keys.append({0x001d, 4, "Pause"});
keys.append({0x0029, 0, "Tilde"});
keys.append({0x0002, 0, "Num1"});
keys.append({0x0003, 0, "Num2"});
keys.append({0x0004, 0, "Num3"});
keys.append({0x0005, 0, "Num4"});
keys.append({0x0006, 0, "Num5"});
keys.append({0x0007, 0, "Num6"});
keys.append({0x0008, 0, "Num7"});
keys.append({0x0009, 0, "Num8"});
keys.append({0x000a, 0, "Num9"});
keys.append({0x000b, 0, "Num0"});
keys.append({0x000c, 0, "Dash"});
keys.append({0x000d, 0, "Equal"});
keys.append({0x000e, 0, "Backspace"});
keys.append({0x0052, 2, "Insert"});
keys.append({0x0053, 2, "Delete"});
keys.append({0x0047, 2, "Home"});
keys.append({0x004f, 2, "End"});
keys.append({0x0049, 2, "PageUp"});
keys.append({0x0051, 2, "PageDown"});
keys.append({0x001e, 0, "A"});
keys.append({0x0030, 0, "B"});
keys.append({0x002e, 0, "C"});
keys.append({0x0020, 0, "D"});
keys.append({0x0012, 0, "E"});
keys.append({0x0021, 0, "F"});
keys.append({0x0022, 0, "G"});
keys.append({0x0023, 0, "H"});
keys.append({0x0017, 0, "I"});
keys.append({0x0024, 0, "J"});
keys.append({0x0025, 0, "K"});
keys.append({0x0026, 0, "L"});
keys.append({0x0032, 0, "M"});
keys.append({0x0031, 0, "N"});
keys.append({0x0018, 0, "O"});
keys.append({0x0019, 0, "P"});
keys.append({0x0010, 0, "Q"});
keys.append({0x0013, 0, "R"});
keys.append({0x001f, 0, "S"});
keys.append({0x0014, 0, "T"});
keys.append({0x0016, 0, "U"});
keys.append({0x002f, 0, "V"});
keys.append({0x0011, 0, "W"});
keys.append({0x002d, 0, "X"});
keys.append({0x0015, 0, "Y"});
keys.append({0x002c, 0, "Z"});
keys.append({0x001a, 0, "LeftBracket"});
keys.append({0x001b, 0, "RightBracket"});
keys.append({0x002b, 0, "Backslash"});
keys.append({0x0027, 0, "Semicolon"});
keys.append({0x0028, 0, "Apostrophe"});
keys.append({0x0033, 0, "Comma"});
keys.append({0x0034, 0, "Period"});
keys.append({0x0035, 0, "Slash"});
keys.append({0x004f, 0, "Keypad1"});
keys.append({0x0050, 0, "Keypad2"});
keys.append({0x0051, 0, "Keypad3"});
keys.append({0x004b, 0, "Keypad4"});
keys.append({0x004c, 0, "Keypad5"});
keys.append({0x004d, 0, "Keypad6"});
keys.append({0x0047, 0, "Keypad7"});
keys.append({0x0048, 0, "Keypad8"});
keys.append({0x0049, 0, "Keypad9"});
keys.append({0x0052, 0, "Keypad0"});
keys.append({0x0053, 0, "Point"});
keys.append({0x001c, 2, "Enter"});
keys.append({0x004e, 0, "Add"});
keys.append({0x004a, 0, "Subtract"});
keys.append({0x0037, 0, "Multiply"});
keys.append({0x0035, 2, "Divide"});
//keys.append({0x0045, 0, "NumLock"});
keys.append({0x003a, 0, "CapsLock"});
keys.append({0x0048, 2, "Up"});
keys.append({0x0050, 2, "Down"});
keys.append({0x004b, 2, "Left"});
keys.append({0x004d, 2, "Right"});
keys.append({0x000f, 0, "Tab"});
keys.append({0x001c, 0, "Return"});
keys.append({0x0039, 0, "Spacebar"});
keys.append({0x002a, 0, "LeftShift"});
keys.append({0x0036, 0, "RightShift"});
keys.append({0x001d, 0, "LeftControl"});
keys.append({0x001d, 2, "RightControl"});
keys.append({0x0038, 0, "LeftAlt"});
keys.append({0x0038, 2, "RightAlt"});
keys.append({0x005b, 2, "LeftSuper"});
keys.append({0x005c, 2, "RightSuper"});
keys.append({0x005d, 2, "Menu"});
kb.hid.id = 1;
for(auto& key : keys) kb.hid.button().append({key.name});
return true;
}
void term() {
}
};
}
#endif

View File

@ -0,0 +1,174 @@
#ifndef RUBY_INPUT_KEYBOARD_XLIB
#define RUBY_INPUT_KEYBOARD_XLIB
namespace ruby {
struct InputKeyboardXlib {
HID::Keyboard hid;
Display* display = nullptr;
struct Key {
string name;
unsigned keysym;
unsigned keycode;
};
vector<Key> keys;
void assign(unsigned inputID, bool value) {
auto& group = hid.group[HID::Keyboard::GroupID::Button];
if(group.input[inputID].value == value) return;
if(input.onChange) input.onChange(hid, HID::Keyboard::GroupID::Button, inputID, group.input[inputID].value, value);
group.input[inputID].value = value;
}
void poll(vector<HID::Device*>& devices) {
char state[32];
XQueryKeymap(display, state);
for(unsigned n = 0; n < keys.size(); n++) {
bool value = state[keys[n].keycode >> 3] & (1 << (keys[n].keycode & 7));
assign(n, value);
}
devices.append(&hid);
}
bool init() {
display = XOpenDisplay(0);
keys.append({"Escape", XK_Escape});
keys.append({"F1", XK_F1});
keys.append({"F2", XK_F2});
keys.append({"F3", XK_F3});
keys.append({"F4", XK_F4});
keys.append({"F5", XK_F5});
keys.append({"F6", XK_F6});
keys.append({"F7", XK_F7});
keys.append({"F8", XK_F8});
keys.append({"F9", XK_F9});
keys.append({"F10", XK_F10});
keys.append({"F11", XK_F11});
keys.append({"F12", XK_F12});
keys.append({"ScrollLock", XK_Scroll_Lock});
keys.append({"Pause", XK_Pause});
keys.append({"Tilde", XK_asciitilde});
keys.append({"Num0", XK_0});
keys.append({"Num1", XK_1});
keys.append({"Num2", XK_2});
keys.append({"Num3", XK_3});
keys.append({"Num4", XK_4});
keys.append({"Num5", XK_5});
keys.append({"Num6", XK_6});
keys.append({"Num7", XK_7});
keys.append({"Num8", XK_8});
keys.append({"Num9", XK_9});
keys.append({"Dash", XK_minus});
keys.append({"Equal", XK_equal});
keys.append({"Backspace", XK_BackSpace});
keys.append({"Insert", XK_Insert});
keys.append({"Delete", XK_Delete});
keys.append({"Home", XK_Home});
keys.append({"End", XK_End});
keys.append({"PageUp", XK_Prior});
keys.append({"PageDown", XK_Next});
keys.append({"A", XK_A});
keys.append({"B", XK_B});
keys.append({"C", XK_C});
keys.append({"D", XK_D});
keys.append({"E", XK_E});
keys.append({"F", XK_F});
keys.append({"G", XK_G});
keys.append({"H", XK_H});
keys.append({"I", XK_I});
keys.append({"J", XK_J});
keys.append({"K", XK_K});
keys.append({"L", XK_L});
keys.append({"M", XK_M});
keys.append({"N", XK_N});
keys.append({"O", XK_O});
keys.append({"P", XK_P});
keys.append({"Q", XK_Q});
keys.append({"R", XK_R});
keys.append({"S", XK_S});
keys.append({"T", XK_T});
keys.append({"U", XK_U});
keys.append({"V", XK_V});
keys.append({"W", XK_W});
keys.append({"X", XK_X});
keys.append({"Y", XK_Y});
keys.append({"Z", XK_Z});
keys.append({"LeftBracket", XK_bracketleft});
keys.append({"RightBracket", XK_bracketright});
keys.append({"Backslash", XK_backslash});
keys.append({"Semicolon", XK_semicolon});
keys.append({"Apostrophe", XK_apostrophe});
keys.append({"Comma", XK_comma});
keys.append({"Period", XK_period});
keys.append({"Slash", XK_slash});
keys.append({"Keypad0", XK_KP_0});
keys.append({"Keypad1", XK_KP_1});
keys.append({"Keypad2", XK_KP_2});
keys.append({"Keypad3", XK_KP_3});
keys.append({"Keypad4", XK_KP_4});
keys.append({"Keypad5", XK_KP_5});
keys.append({"Keypad6", XK_KP_6});
keys.append({"Keypad7", XK_KP_7});
keys.append({"Keypad8", XK_KP_8});
keys.append({"Keypad9", XK_KP_9});
keys.append({"Add", XK_KP_Add});
keys.append({"Subtract", XK_KP_Subtract});
keys.append({"Multiply", XK_KP_Multiply});
keys.append({"Divide", XK_KP_Divide});
keys.append({"Enter", XK_KP_Enter});
keys.append({"Up", XK_Up});
keys.append({"Down", XK_Down});
keys.append({"Left", XK_Left});
keys.append({"Right", XK_Right});
keys.append({"Tab", XK_Tab});
keys.append({"Return", XK_Return});
keys.append({"Spacebar", XK_space});
keys.append({"LeftControl", XK_Control_L});
keys.append({"RightControl", XK_Control_R});
keys.append({"LeftAlt", XK_Alt_L});
keys.append({"RightAlt", XK_Alt_R});
keys.append({"LeftShift", XK_Shift_L});
keys.append({"RightShift", XK_Shift_R});
keys.append({"LeftSuper", XK_Super_L});
keys.append({"RightSuper", XK_Super_R});
keys.append({"Menu", XK_Menu});
hid.id = 1;
for(unsigned n = 0; n < keys.size(); n++) {
hid.button().append(keys[n].name);
keys[n].keycode = XKeysymToKeycode(display, keys[n].keysym);
}
return true;
}
void term() {
if(display) {
XCloseDisplay(display);
display = nullptr;
}
}
};
}
#endif

View File

@ -0,0 +1,123 @@
#ifndef RUBY_INPUT_MOUSE_RAWINPUT
#define RUBY_INPUT_MOUSE_RAWINPUT
namespace ruby {
struct InputMouseRawInput {
uintptr_t handle = 0;
bool mouseAcquired = false;
struct Mouse {
HID::Mouse hid;
signed relativeX = 0;
signed relativeY = 0;
signed relativeZ = 0;
bool buttons[5] = {0};
} ms;
bool acquire() {
if(mouseAcquired == false) {
mouseAcquired = true;
ShowCursor(false);
}
return true;
}
bool unacquire() {
if(mouseAcquired == true) {
mouseAcquired = false;
ReleaseCapture();
ClipCursor(NULL);
ShowCursor(true);
}
return true;
}
bool acquired() {
if(mouseAcquired == true) {
SetFocus((HWND)handle);
SetCapture((HWND)handle);
RECT rc;
GetWindowRect((HWND)handle, &rc);
ClipCursor(&rc);
}
return GetCapture() == (HWND)handle;
}
void update(RAWINPUT* input) {
if((input->data.mouse.usFlags & 1) == MOUSE_MOVE_RELATIVE) {
ms.relativeX += input->data.mouse.lLastX;
ms.relativeY += input->data.mouse.lLastY;
}
if(input->data.mouse.usButtonFlags & RI_MOUSE_WHEEL) {
ms.relativeZ += (int16_t)input->data.mouse.usButtonData;
}
if(input->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_1_DOWN) ms.buttons[0] = 1;
if(input->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_1_UP ) ms.buttons[0] = 0;
if(input->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_2_DOWN) ms.buttons[1] = 1;
if(input->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_2_UP ) ms.buttons[1] = 0;
if(input->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_3_DOWN) ms.buttons[2] = 1;
if(input->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_3_UP ) ms.buttons[2] = 0;
if(input->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_4_DOWN) ms.buttons[3] = 1;
if(input->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_4_UP ) ms.buttons[3] = 0;
if(input->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_5_DOWN) ms.buttons[4] = 1;
if(input->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_5_UP ) ms.buttons[4] = 0;
}
void assign(unsigned groupID, unsigned inputID, int16_t value) {
auto& group = ms.hid.group[groupID];
if(group.input[inputID].value == value) return;
if(input.onChange) input.onChange(ms.hid, groupID, inputID, group.input[inputID].value, value);
group.input[inputID].value = value;
}
void poll(vector<HID::Device*>& devices) {
assign(HID::Mouse::GroupID::Axis, 0, ms.relativeX);
assign(HID::Mouse::GroupID::Axis, 1, ms.relativeY);
assign(HID::Mouse::GroupID::Axis, 2, ms.relativeZ);
//keys are intentionally reordered below:
//in ruby, button order is {left, middle, right, up, down}
assign(HID::Mouse::GroupID::Button, 0, ms.buttons[0]);
assign(HID::Mouse::GroupID::Button, 2, ms.buttons[1]);
assign(HID::Mouse::GroupID::Button, 1, ms.buttons[2]);
assign(HID::Mouse::GroupID::Button, 4, ms.buttons[3]);
assign(HID::Mouse::GroupID::Button, 3, ms.buttons[4]);
ms.relativeX = 0;
ms.relativeY = 0;
ms.relativeZ = 0;
devices.append(&ms.hid);
}
bool init(uintptr_t handle) {
this->handle = handle;
ms.hid.id = 2;
ms.hid.axis().append({"X"});
ms.hid.axis().append({"Y"});
ms.hid.axis().append({"Z"});
ms.hid.button().append({"Left"});
ms.hid.button().append({"Middle"});
ms.hid.button().append({"Right"});
ms.hid.button().append({"Up"});
ms.hid.button().append({"Down"});
rawinput.updateMouse = {&InputMouseRawInput::update, this};
return true;
}
void term() {
unacquire();
}
};
}
#endif

View File

@ -0,0 +1,154 @@
#ifndef RUBY_INPUT_MOUSE_XLIB
#define RUBY_INPUT_MOUSE_XLIB
namespace ruby {
struct InputMouseXlib {
HID::Mouse hid;
uintptr_t handle = 0;
Display* display = nullptr;
Window rootWindow;
Cursor invisibleCursor;
unsigned screenWidth = 0;
unsigned screenHeight = 0;
struct Mouse {
bool acquired = false;
signed numerator = 0;
signed denominator = 0;
signed threshold = 0;
unsigned relativeX = 0;
unsigned relativeY = 0;
} ms;
bool acquire() {
if(acquired()) return true;
if(XGrabPointer(display, handle, True, 0, GrabModeAsync, GrabModeAsync, rootWindow, invisibleCursor, CurrentTime) == GrabSuccess) {
//backup existing cursor acceleration settings
XGetPointerControl(display, &ms.numerator, &ms.denominator, &ms.threshold);
//disable cursor acceleration
XChangePointerControl(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);
return ms.acquired = true;
} else {
return ms.acquired = false;
}
}
bool unacquire() {
if(acquired()) {
//restore cursor acceleration and release cursor
XChangePointerControl(display, True, True, ms.numerator, ms.denominator, ms.threshold);
XUngrabPointer(display, CurrentTime);
ms.acquired = false;
}
return true;
}
bool acquired() {
return ms.acquired;
}
void assign(unsigned groupID, unsigned inputID, int16_t value) {
auto& group = hid.group[groupID];
if(group.input[inputID].value == value) return;
if(input.onChange) input.onChange(hid, groupID, inputID, group.input[inputID].value, value);
group.input[inputID].value = value;
}
void poll(vector<HID::Device*>& devices) {
Window rootReturn;
Window childReturn;
signed rootXReturn = 0;
signed rootYReturn = 0;
signed windowXReturn = 0;
signed windowYReturn = 0;
unsigned maskReturn = 0;
XQueryPointer(display, handle, &rootReturn, &childReturn, &rootXReturn, &rootYReturn, &windowXReturn, &windowYReturn, &maskReturn);
if(acquired()) {
XWindowAttributes attributes;
XGetWindowAttributes(display, handle, &attributes);
//absolute -> relative conversion
assign(HID::Mouse::GroupID::Axis, 0, (int16_t)(rootXReturn - screenWidth / 2));
assign(HID::Mouse::GroupID::Axis, 1, (int16_t)(rootYReturn - screenHeight / 2));
if(hid.axis().input[0].value != 0 || hid.axis().input[1].value != 0) {
//if mouse moved, re-center mouse for next poll
XWarpPointer(display, None, rootWindow, 0, 0, 0, 0, screenWidth / 2, screenHeight / 2);
}
} else {
assign(HID::Mouse::GroupID::Axis, 0, (int16_t)(rootXReturn - ms.relativeX));
assign(HID::Mouse::GroupID::Axis, 1, (int16_t)(rootYReturn - ms.relativeY));
ms.relativeX = rootXReturn;
ms.relativeY = rootYReturn;
}
assign(HID::Mouse::GroupID::Button, 0, (bool)(maskReturn & Button1Mask));
assign(HID::Mouse::GroupID::Button, 1, (bool)(maskReturn & Button2Mask));
assign(HID::Mouse::GroupID::Button, 2, (bool)(maskReturn & Button3Mask));
assign(HID::Mouse::GroupID::Button, 3, (bool)(maskReturn & Button4Mask));
assign(HID::Mouse::GroupID::Button, 4, (bool)(maskReturn & Button5Mask));
devices.append(&hid);
}
bool init(uintptr_t handle) {
this->handle = handle;
display = XOpenDisplay(0);
rootWindow = DefaultRootWindow(display);
XWindowAttributes attributes;
XGetWindowAttributes(display, rootWindow, &attributes);
screenWidth = attributes.width;
screenHeight = attributes.height;
//Xlib: because XShowCursor(display, false) would just be too easy
//create invisible cursor for use when mouse is acquired
Pixmap pixmap;
XColor black, unused;
static char invisibleData[8] = {0};
Colormap colormap = DefaultColormap(display, DefaultScreen(display));
XAllocNamedColor(display, colormap, "black", &black, &unused);
pixmap = XCreateBitmapFromData(display, handle, invisibleData, 8, 8);
invisibleCursor = XCreatePixmapCursor(display, pixmap, pixmap, &black, &black, 0, 0);
XFreePixmap(display, pixmap);
XFreeColors(display, colormap, &black.pixel, 1, 0);
ms.acquired = false;
ms.relativeX = 0;
ms.relativeY = 0;
hid.id = 2;
hid.axis().append({"X"});
hid.axis().append({"Y"});
hid.button().append({"Left"});
hid.button().append({"Middle"});
hid.button().append({"Right"});
hid.button().append({"Up"});
hid.button().append({"Down"});
return true;
}
void term() {
unacquire();
XFreeCursor(display, invisibleCursor);
XCloseDisplay(display);
}
};
}
#endif

View File

@ -0,0 +1,82 @@
#include <SDL/SDL.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include "keyboard/xlib.cpp"
#include "mouse/xlib.cpp"
#include "joypad/sdl.cpp"
namespace ruby {
struct pInputSDL {
InputKeyboardXlib xlibKeyboard;
InputMouseXlib xlibMouse;
InputJoypadSDL sdl;
struct Settings {
uintptr_t handle = 0;
} settings;
bool cap(const string& name) {
if(name == Input::Handle) return true;
if(name == Input::KeyboardSupport) return true;
if(name == Input::MouseSupport) return true;
if(name == Input::JoypadSupport) return true;
return false;
}
any get(const string& name) {
if(name == Input::Handle) return (uintptr_t)settings.handle;
return false;
}
bool set(const string& name, const any &value) {
if(name == Input::Handle) {
settings.handle = any_cast<uintptr_t>(value);
return true;
}
return false;
}
bool acquire() {
return xlibMouse.acquire();
}
bool unacquire() {
return xlibMouse.unacquire();
}
bool acquired() {
return xlibMouse.acquired();
}
vector<HID::Device*> poll() {
vector<HID::Device*> devices;
xlibKeyboard.poll(devices);
xlibMouse.poll(devices);
sdl.poll(devices);
return devices;
}
bool rumble(uint64_t id, bool enable) {
return false;
}
bool init() {
if(xlibKeyboard.init() == false) return false;
if(xlibMouse.init(settings.handle) == false) return false;
if(sdl.init() == false) return false;
return true;
}
void term() {
xlibKeyboard.term();
xlibMouse.term();
sdl.term();
}
};
DeclareInput(SDL)
}

View File

@ -0,0 +1,162 @@
#ifndef RUBY_INPUT_SHARED_RAWINPUT
#define RUBY_INPUT_SHARED_RAWINPUT
namespace ruby {
LRESULT CALLBACK RawInputWindowProc(HWND, UINT, WPARAM, LPARAM);
struct RawInput {
HANDLE mutex;
HWND hwnd;
bool ready = false;
bool initialized = false;
function<void (RAWINPUT*)> updateKeyboard;
function<void (RAWINPUT*)> updateMouse;
struct Device {
HANDLE handle;
string path;
enum class Type : unsigned { Keyboard, Mouse, Joypad } type;
uint16_t vendorID = 0;
uint16_t productID = 0;
bool isXInputDevice = false;
};
vector<Device> devices;
optional<Device&> find(uint16_t vendorID, uint16_t productID) {
for(auto& device : devices) {
if(device.vendorID == vendorID && device.productID == productID) return {true, device};
}
return false;
}
void scanDevices() {
devices.reset();
unsigned deviceCount = 0;
GetRawInputDeviceList(NULL, &deviceCount, sizeof(RAWINPUTDEVICELIST));
RAWINPUTDEVICELIST* list = new RAWINPUTDEVICELIST[deviceCount];
GetRawInputDeviceList(list, &deviceCount, sizeof(RAWINPUTDEVICELIST));
for(unsigned n = 0; n < deviceCount; n++) {
wchar_t path[4096];
unsigned size = sizeof(path) - 1;
GetRawInputDeviceInfo(list[n].hDevice, RIDI_DEVICENAME, &path, &size);
RID_DEVICE_INFO info;
info.cbSize = size = sizeof(RID_DEVICE_INFO);
GetRawInputDeviceInfo(list[n].hDevice, RIDI_DEVICEINFO, &info, &size);
Device device;
device.path = (const char*)utf8_t(path);
device.handle = list[n].hDevice;
if(info.dwType == RIM_TYPEKEYBOARD) {
device.type = Device::Type::Keyboard;
device.vendorID = 0;
device.productID = 1;
}
if(info.dwType == RIM_TYPEMOUSE) {
device.type = Device::Type::Mouse;
device.vendorID = 0;
device.productID = 2;
}
if(info.dwType == RIM_TYPEHID) {
//verify that this is a joypad device
if(info.hid.usUsagePage != 1 || (info.hid.usUsage != 4 && info.hid.usUsage != 5)) continue;
device.type = Device::Type::Joypad;
device.vendorID = info.hid.dwVendorId;
device.productID = info.hid.dwProductId;
if(device.path.find("IG_")) device.isXInputDevice = true; //"IG_" is only found inside XInput device paths
}
devices.append(device);
}
delete[] list;
}
LRESULT windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
if(msg != WM_INPUT) return DefWindowProc(hwnd, msg, wparam, lparam);
unsigned size = 0;
GetRawInputData((HRAWINPUT)lparam, RID_INPUT, NULL, &size, sizeof(RAWINPUTHEADER));
RAWINPUT* input = new RAWINPUT[size];
GetRawInputData((HRAWINPUT)lparam, RID_INPUT, input, &size, sizeof(RAWINPUTHEADER));
WaitForSingleObject(mutex, INFINITE);
if(input->header.dwType == RIM_TYPEKEYBOARD) {
if(updateKeyboard) updateKeyboard(input);
}
if(input->header.dwType == RIM_TYPEMOUSE) {
if(updateMouse) updateMouse(input);
}
ReleaseMutex(mutex);
LRESULT result = DefRawInputProc(&input, size, sizeof(RAWINPUTHEADER));
delete[] input;
return result;
}
void main() {
WNDCLASS wc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
wc.hCursor = LoadCursor(0, IDC_ARROW);
wc.hIcon = LoadIcon(0, IDI_APPLICATION);
wc.hInstance = GetModuleHandle(0);
wc.lpfnWndProc = RawInputWindowProc;
wc.lpszClassName = L"RawInputClass";
wc.lpszMenuName = 0;
wc.style = CS_VREDRAW | CS_HREDRAW;
RegisterClass(&wc);
hwnd = CreateWindow(L"RawInputClass", L"RawInputClass", WS_POPUP, 0, 0, 64, 64, 0, 0, GetModuleHandle(0), 0);
scanDevices();
RAWINPUTDEVICE device[2];
//capture all keyboard input
device[0].usUsagePage = 1;
device[0].usUsage = 6;
device[0].dwFlags = RIDEV_INPUTSINK;
device[0].hwndTarget = hwnd;
//capture all mouse input
device[1].usUsagePage = 1;
device[1].usUsage = 2;
device[1].dwFlags = RIDEV_INPUTSINK;
device[1].hwndTarget = hwnd;
RegisterRawInputDevices(device, 2, sizeof(RAWINPUTDEVICE));
WaitForSingleObject(mutex, INFINITE);
ready = true;
ReleaseMutex(mutex);
while(true) {
MSG msg;
GetMessage(&msg, hwnd, 0, 0);
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
};
static RawInput rawinput;
DWORD WINAPI RawInputThreadProc(void*) {
rawinput.main();
return 0;
}
LRESULT CALLBACK RawInputWindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
return rawinput.windowProc(hwnd, msg, wparam, lparam);
}
}
#endif

View File

@ -0,0 +1,88 @@
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/poll.h>
#include <fcntl.h>
#include <libudev.h>
#include <linux/types.h>
#include <linux/input.h>
#include "keyboard/xlib.cpp"
#include "mouse/xlib.cpp"
#include "joypad/udev.cpp"
namespace ruby {
struct pInputUdev {
InputKeyboardXlib xlibKeyboard;
InputMouseXlib xlibMouse;
InputJoypadUdev udev;
struct Settings {
uintptr_t handle = 0;
} settings;
bool cap(const string& name) {
if(name == Input::Handle) return true;
if(name == Input::KeyboardSupport) return true;
if(name == Input::MouseSupport) return true;
if(name == Input::JoypadSupport) return true;
if(name == Input::JoypadRumbleSupport) return true;
return false;
}
any get(const string& name) {
if(name == Input::Handle) return (uintptr_t)settings.handle;
return false;
}
bool set(const string& name, const any& value) {
if(name == Input::Handle) {
settings.handle = any_cast<uintptr_t>(value);
return true;
}
return false;
}
bool acquire() {
return xlibMouse.acquire();
}
bool unacquire() {
return xlibMouse.unacquire();
}
bool acquired() {
return xlibMouse.acquired();
}
vector<HID::Device*> poll() {
vector<HID::Device*> devices;
xlibKeyboard.poll(devices);
xlibMouse.poll(devices);
udev.poll(devices);
return devices;
}
bool rumble(uint64_t id, bool enable) {
return udev.rumble(id, enable);
}
bool init() {
if(xlibKeyboard.init() == false) return false;
if(xlibMouse.init(settings.handle) == false) return false;
if(udev.init() == false) return false;
return true;
}
void term() {
xlibKeyboard.term();
xlibMouse.term();
udev.term();
}
};
DeclareInput(Udev)
}

View File

@ -0,0 +1,109 @@
#include <xinput.h>
#define DIRECTINPUT_VERSION 0x0800
#include <dinput.h>
#include "shared/rawinput.cpp"
#include "keyboard/rawinput.cpp"
#include "mouse/rawinput.cpp"
#include "joypad/xinput.cpp"
#include "joypad/directinput.cpp"
namespace ruby {
struct pInputWindows {
InputKeyboardRawInput rawinputKeyboard;
InputMouseRawInput rawinputMouse;
InputJoypadXInput xinput;
InputJoypadDirectInput directinput;
LPDIRECTINPUT8 directinputContext = nullptr;
struct Settings {
uintptr_t handle = 0;
} settings;
bool cap(const string& name) {
if(name == Input::Handle) return true;
if(name == Input::KeyboardSupport) return true;
if(name == Input::MouseSupport) return true;
if(name == Input::JoypadSupport) return true;
if(name == Input::JoypadRumbleSupport) return true;
return false;
}
any get(const string& name) {
if(name == Input::Handle) return (uintptr_t)settings.handle;
return false;
}
bool set(const string& name, const any& value) {
if(name == Input::Handle) {
settings.handle = any_cast<uintptr_t>(value);
return true;
}
return false;
}
bool acquire() {
return rawinputMouse.acquire();
}
bool unacquire() {
return rawinputMouse.unacquire();
}
bool acquired() {
return rawinputMouse.acquired();
}
vector<HID::Device*> poll() {
vector<HID::Device*> devices;
rawinputKeyboard.poll(devices);
rawinputMouse.poll(devices);
xinput.poll(devices);
directinput.poll(devices);
return devices;
}
bool rumble(uint64_t id, bool enable) {
if(xinput.rumble(id, enable)) return true;
if(directinput.rumble(id, enable)) return true;
return false;
}
bool init() {
if(rawinput.initialized == false) {
rawinput.initialized = true;
rawinput.mutex = CreateMutex(NULL, FALSE, NULL);
CreateThread(NULL, 0, RawInputThreadProc, 0, 0, NULL);
do {
Sleep(1);
WaitForSingleObject(rawinput.mutex, INFINITE);
ReleaseMutex(rawinput.mutex);
} while(rawinput.ready == false);
}
DirectInput8Create(GetModuleHandle(0), DIRECTINPUT_VERSION, IID_IDirectInput8, (void**)&directinputContext, 0);
if(directinputContext == nullptr) return false;
if(rawinputKeyboard.init() == false) return false;
if(rawinputMouse.init(settings.handle) == false) return false;
bool xinputAvailable = xinput.init();
if(directinput.init(settings.handle, directinputContext, xinputAvailable) == false) return false;
return true;
}
void term() {
rawinputKeyboard.term();
rawinputMouse.term();
xinput.term();
directinput.term();
if(directinputContext) { directinputContext->Release(); directinputContext = nullptr; }
}
};
DeclareInput(Windows)
}

View File

@ -0,0 +1,77 @@
#include <sys/ipc.h>
#include <sys/shm.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>
#include "keyboard/xlib.cpp"
#include "mouse/xlib.cpp"
namespace ruby {
struct pInputXlib {
InputKeyboardXlib xlibKeyboard;
InputMouseXlib xlibMouse;
struct Settings {
uintptr_t handle = 0;
} settings;
bool cap(const string& name) {
if(name == Input::KeyboardSupport) return true;
if(name == Input::MouseSupport) return true;
return false;
}
any get(const string& name) {
if(name == Input::Handle) return (uintptr_t)settings.handle;
return false;
}
bool set(const string& name, const any &value) {
if(name == Input::Handle) {
settings.handle = any_cast<uintptr_t>(value);
return true;
}
return false;
}
bool acquire() {
return xlibMouse.acquire();
}
bool unacquire() {
return xlibMouse.unacquire();
}
bool acquired() {
return xlibMouse.acquired();
}
vector<HID::Device*> poll() {
vector<HID::Device*> devices;
xlibKeyboard.poll(devices);
xlibMouse.poll(devices);
return devices;
}
bool rumble(uint64_t id, bool enable) {
return false;
}
bool init() {
if(xlibKeyboard.init() == false) return false;
if(xlibMouse.init(settings.handle) == false) return false;
return true;
}
void term() {
xlibKeyboard.term();
xlibMouse.term();
}
};
DeclareInput(Xlib)
}

496
src/burner/qt/ruby/ruby.cpp Executable file
View File

@ -0,0 +1,496 @@
#include <ruby/ruby.hpp>
#undef mkdir
#undef usleep
#include <ruby/implementation.cpp>
namespace ruby {
VideoInterface video;
AudioInterface audio;
InputInterface input;
/* VideoInterface */
const char* Video::Handle = "Handle";
const char* Video::Synchronize = "Synchronize";
const char* Video::Depth = "Depth";
const char* Video::Filter = "Filter";
const char* Video::Shader = "Shader";
const unsigned Video::FilterNearest = 0;
const unsigned Video::FilterLinear = 1;
void VideoInterface::driver(const char* driver) {
if(p) term();
if(!driver || !*driver) driver = optimalDriver();
if(0);
#ifdef VIDEO_CGL
else if(!strcmp(driver, "OpenGL")) p = new VideoCGL();
#endif
#ifdef VIDEO_DIRECT3D
else if(!strcmp(driver, "Direct3D")) p = new VideoD3D();
#endif
#ifdef VIDEO_DIRECTDRAW
else if(!strcmp(driver, "DirectDraw")) p = new VideoDD();
#endif
#ifdef VIDEO_GDI
else if(!strcmp(driver, "GDI")) p = new VideoGDI();
#endif
#ifdef VIDEO_GLX
else if(!strcmp(driver, "OpenGL")) p = new VideoGLX();
#endif
#ifdef VIDEO_QTOPENGL
else if(!strcmp(driver, "Qt-OpenGL")) p = new VideoQtOpenGL();
#endif
#ifdef VIDEO_QTRASTER
else if(!strcmp(driver, "Qt-Raster")) p = new VideoQtRaster();
#endif
#ifdef VIDEO_SDL
else if(!strcmp(driver, "SDL")) p = new VideoSDL();
#endif
#ifdef VIDEO_WGL
else if(!strcmp(driver, "OpenGL")) p = new VideoWGL();
#endif
#ifdef VIDEO_XSHM
else if(!strcmp(driver, "XShm")) p = new VideoXShm();
#endif
#ifdef VIDEO_XV
else if(!strcmp(driver, "X-Video")) p = new VideoXv();
#endif
else p = new Video();
}
const char* VideoInterface::optimalDriver() {
#if defined(VIDEO_WGL)
return "OpenGL";
#elif defined(VIDEO_DIRECT3D)
return "Direct3D";
#elif defined(VIDEO_DIRECTDRAW)
return "DirectDraw";
#elif defined(VIDEO_GDI)
return "GDI";
#elif defined(VIDEO_CGL)
return "OpenGL";
#elif defined(VIDEO_GLX)
return "OpenGL";
#elif defined(VIDEO_XV)
return "X-Video";
#elif defined(VIDEO_XSHM)
return "XShm";
#elif defined(VIDEO_SDL)
return "SDL";
#else
return "None";
#endif
}
const char* VideoInterface::safestDriver() {
#if defined(VIDEO_DIRECT3D)
return "Direct3D";
#elif defined(VIDEO_WGL)
return "OpenGL";
#elif defined(VIDEO_DIRECTDRAW)
return "DirectDraw";
#elif defined(VIDEO_GDI)
return "GDI";
#elif defined(VIDEO_CGL)
return "OpenGL";
#elif defined(VIDEO_XSHM)
return "XShm";
#elif defined(VIDEO_SDL)
return "SDL";
#elif defined(VIDEO_XV)
return "X-Video";
#elif defined(VIDEO_GLX)
return "OpenGL";
#else
return "None";
#endif
}
const char* VideoInterface::availableDrivers() {
return
//Windows
#if defined(VIDEO_WGL)
"OpenGL;"
#endif
#if defined(VIDEO_DIRECT3D)
"Direct3D;"
#endif
#if defined(VIDEO_DIRECTDRAW)
"DirectDraw;"
#endif
#if defined(VIDEO_GDI)
"GDI;"
#endif
//OS X
#if defined(VIDEO_CGL)
"OpenGL;"
#endif
//Linux
#if defined(VIDEO_GLX)
"OpenGL;"
#endif
#if defined(VIDEO_XV)
"X-Video;"
#endif
#if defined(VIDEO_XSHM)
"XShm;"
#endif
#if defined(VIDEO_SDL)
"SDL;"
#endif
"None";
}
bool VideoInterface::init() {
if(!p) driver();
return p->init();
}
void VideoInterface::term() {
if(p) {
p->term();
delete p;
p = nullptr;
}
}
bool VideoInterface::cap(const string& name) { return p ? p->cap(name) : false; }
any VideoInterface::get(const string& name) { return p ? p->get(name) : false; }
bool VideoInterface::set(const string& name, const any& value) { return p ? p->set(name, value) : false; }
bool VideoInterface::lock(uint32_t*& data, unsigned& pitch, unsigned width, unsigned height) { return p ? p->lock(data, pitch, width, height) : false; }
void VideoInterface::unlock() { if(p) p->unlock(); }
void VideoInterface::clear() { if(p) p->clear(); }
void VideoInterface::refresh() { if(p) p->refresh(); }
VideoInterface::VideoInterface() : p(nullptr) {}
VideoInterface::~VideoInterface() { term(); }
/* AudioInterface */
const char* Audio::Handle = "Handle";
const char* Audio::Synchronize = "Synchronize";
const char* Audio::Frequency = "Frequency";
const char* Audio::Latency = "Latency";
void AudioInterface::driver(const char* driver) {
if(p) term();
if(!driver || !*driver) driver = optimalDriver();
if(0);
#ifdef AUDIO_ALSA
else if(!strcmp(driver, "ALSA")) p = new AudioALSA();
#endif
#ifdef AUDIO_AO
else if(!strcmp(driver, "libao")) p = new AudioAO();
#endif
#ifdef AUDIO_DIRECTSOUND
else if(!strcmp(driver, "DirectSound")) p = new AudioDS();
#endif
#ifdef AUDIO_OPENAL
else if(!strcmp(driver, "OpenAL")) p = new AudioOpenAL();
#endif
#ifdef AUDIO_OSS
else if(!strcmp(driver, "OSS")) p = new AudioOSS();
#endif
#ifdef AUDIO_PULSEAUDIO
else if(!strcmp(driver, "PulseAudio")) p = new AudioPulseAudio();
#endif
#ifdef AUDIO_PULSEAUDIOSIMPLE
else if(!strcmp(driver, "PulseAudioSimple")) p = new AudioPulseAudioSimple();
#endif
#ifdef AUDIO_XAUDIO2
else if(!strcmp(driver, "XAudio2")) p = new AudioXAudio2();
#endif
else p = new Audio();
}
const char* AudioInterface::optimalDriver() {
#if defined(AUDIO_XAUDIO2)
return "XAudio2";
#elif defined(AUDIO_DIRECTSOUND)
return "DirectSound";
#elif defined(AUDIO_ALSA)
return "ALSA";
#elif defined(AUDIO_OPENAL)
return "OpenAL";
#elif defined(AUDIO_OSS)
return "OSS";
#elif defined(AUDIO_PULSEAUDIO)
return "PulseAudio";
#elif defined(AUDIO_PULSEAUDIOSIMPLE)
return "PulseAudioSimple";
#elif defined(AUDIO_AO)
return "libao";
#else
return "None";
#endif
}
const char* AudioInterface::safestDriver() {
#if defined(AUDIO_DIRECTSOUND)
return "DirectSound";
#elif defined(AUDIO_XAUDIO2)
return "XAudio2";
#elif defined(AUDIO_ALSA)
return "ALSA";
#elif defined(AUDIO_OPENAL)
return "OpenAL";
#elif defined(AUDIO_PULSEAUDIO)
return "PulseAudio";
#elif defined(AUDIO_PULSEAUDIOSIMPLE)
return "PulseAudioSimple";
#elif defined(AUDIO_AO)
return "libao";
#elif defined(AUDIO_OSS)
return "OSS";
#else
return "None";
#endif
}
const char* AudioInterface::availableDrivers() {
return
//Windows
#if defined(AUDIO_XAUDIO2)
"XAudio2;"
#endif
#if defined(AUDIO_DIRECTSOUND)
"DirectSound;"
#endif
//Linux
#if defined(AUDIO_ALSA)
"ALSA;"
#endif
#if defined(AUDIO_OPENAL)
"OpenAL;"
#endif
#if defined(AUDIO_OSS)
"OSS;"
#endif
#if defined(AUDIO_PULSEAUDIO)
"PulseAudio;"
#endif
#if defined(AUDIO_PULSEAUDIOSIMPLE)
"PulseAudioSimple;"
#endif
#if defined(AUDIO_AO)
"libao;"
#endif
"None";
}
bool AudioInterface::init() {
if(!p) driver();
return p->init();
}
void AudioInterface::term() {
if(p) {
p->term();
delete p;
p = nullptr;
}
}
bool AudioInterface::cap(const string& name) { return p ? p->cap(name) : false; }
any AudioInterface::get(const string& name) { return p ? p->get(name) : false; }
bool AudioInterface::set(const string& name, const any& value) { return p ? p->set(name, value) : false; }
void AudioInterface::sample(uint16_t left, uint16_t right) { if(p) p->sample(left, right); }
void AudioInterface::clear() { if(p) p->clear(); }
AudioInterface::AudioInterface() : p(nullptr) {}
AudioInterface::~AudioInterface() { term(); }
/* InputInterface */
const char* Input::Handle = "Handle";
const char* Input::KeyboardSupport = "KeyboardSupport";
const char* Input::MouseSupport = "MouseSupport";
const char* Input::JoypadSupport = "JoypadSupport";
const char* Input::JoypadRumbleSupport = "JoypadRumbleSupport";
void InputInterface::driver(const char* driver) {
if(p) term();
if(!driver || !*driver) driver = optimalDriver();
if(0);
#ifdef INPUT_WINDOWS
else if(!strcmp(driver, "Windows")) p = new InputWindows();
#endif
#ifdef INPUT_CARBON
else if(!strcmp(driver, "Carbon")) p = new InputCarbon();
#endif
#ifdef INPUT_UDEV
else if(!strcmp(driver, "udev")) p = new InputUdev();
#endif
#ifdef INPUT_SDL
else if(!strcmp(driver, "SDL")) p = new InputSDL();
#endif
#ifdef INPUT_XLIB
else if(!strcmp(driver, "Xlib")) p = new InputXlib();
#endif
else p = new Input();
}
const char* InputInterface::optimalDriver() {
#if defined(INPUT_WINDOWS)
return "Windows";
#elif defined(INPUT_CARBON)
return "Carbon";
#elif defined(INPUT_UDEV)
return "udev";
#elif defined(INPUT_SDL)
return "SDL";
#elif defined(INPUT_XLIB)
return "Xlib";
#else
return "None";
#endif
}
const char* InputInterface::safestDriver() {
#if defined(INPUT_WINDOWS)
return "Windows";
#elif defined(INPUT_CARBON)
return "Carbon";
#elif defined(INPUT_UDEV)
return "udev";
#elif defined(INPUT_SDL)
return "SDL";
#elif defined(INPUT_XLIB)
return "Xlib";
#else
return "none";
#endif
}
const char* InputInterface::availableDrivers() {
return
//Windows
#if defined(INPUT_WINDOWS)
"Windows;"
#endif
//OS X
#if defined(INPUT_CARBON)
"Carbon;"
#endif
//Linux
#if defined(INPUT_UDEV)
"udev;"
#endif
#if defined(INPUT_SDL)
"SDL;"
#endif
#if defined(INPUT_XLIB)
"Xlib;"
#endif
"None";
}
bool InputInterface::init() {
if(!p) driver();
return p->init();
}
void InputInterface::term() {
if(p) {
p->term();
delete p;
p = nullptr;
}
}
bool InputInterface::cap(const string& name) { return p ? p->cap(name) : false; }
any InputInterface::get(const string& name) { return p ? p->get(name) : false; }
bool InputInterface::set(const string& name, const any& value) { return p ? p->set(name, value) : false; }
bool InputInterface::acquire() { return p ? p->acquire() : false; }
bool InputInterface::unacquire() { return p ? p->unacquire() : false; }
bool InputInterface::acquired() { return p ? p->acquired() : false; }
vector<HID::Device*> InputInterface::poll() { return p ? p->poll() : vector<HID::Device*>(); }
bool InputInterface::rumble(uint64_t id, bool enable) { return p ? p->rumble(id, enable) : false; }
InputInterface::InputInterface() : p(nullptr) {}
InputInterface::~InputInterface() { term(); }
};

98
src/burner/qt/ruby/ruby.hpp Executable file
View File

@ -0,0 +1,98 @@
/*
ruby
version: 0.11 (2013-12-19)
license: public domain
*/
#ifndef RUBY_H
#define RUBY_H
#include <nall/nall.hpp>
namespace ruby {
#include <ruby/video.hpp>
#include <ruby/audio.hpp>
#include <ruby/input.hpp>
struct VideoInterface {
void driver(const char* driver = "");
const char* optimalDriver();
const char* safestDriver();
const char* availableDrivers();
bool init();
void term();
bool cap(const nall::string& name);
nall::any get(const nall::string& name);
bool set(const nall::string& name, const nall::any& value);
bool lock(uint32_t*& data, unsigned& pitch, unsigned width, unsigned height);
void unlock();
void clear();
void refresh();
VideoInterface();
~VideoInterface();
private:
Video* p = nullptr;
};
struct AudioInterface {
void driver(const char* driver = "");
const char* optimalDriver();
const char* safestDriver();
const char* availableDrivers();
bool init();
void term();
bool cap(const nall::string& name);
nall::any get(const nall::string& name);
bool set(const nall::string& name, const nall::any& value);
void sample(uint16_t left, uint16_t right);
void clear();
AudioInterface();
~AudioInterface();
private:
Audio* p = nullptr;
};
struct InputInterface {
nall::function<void (nall::HID::Device& device, unsigned group, unsigned input, int16_t oldValue, int16_t newValue)> onChange;
void driver(const char* driver = "");
const char* optimalDriver();
const char* safestDriver();
const char* availableDrivers();
bool init();
void term();
bool cap(const nall::string& name);
nall::any get(const nall::string& name);
bool set(const nall::string& name, const nall::any& value);
bool acquire();
bool unacquire();
bool acquired();
nall::vector<nall::HID::Device*> poll();
bool rumble(uint64_t id, bool enable);
InputInterface();
~InputInterface();
private:
Input* p = nullptr;
};
extern VideoInterface video;
extern AudioInterface audio;
extern InputInterface input;
};
#endif

25
src/burner/qt/ruby/video.hpp Executable file
View File

@ -0,0 +1,25 @@
struct Video {
static const char* Handle;
static const char* Synchronize;
static const char* Depth;
static const char* Filter;
static const char* Shader;
static const unsigned FilterNearest;
static const unsigned FilterLinear;
virtual bool cap(const nall::string& name) { return false; }
virtual nall::any get(const nall::string& name) { return false; }
virtual bool set(const nall::string& name, const nall::any& value) { return false; }
virtual bool lock(uint32_t*& data, unsigned& pitch, unsigned width, unsigned height) { return false; }
virtual void unlock() {}
virtual void clear() {}
virtual void refresh() {}
virtual bool init() { return true; }
virtual void term() {}
Video() {}
virtual ~Video() {}
};

190
src/burner/qt/ruby/video/cgl.cpp Executable file
View File

@ -0,0 +1,190 @@
#include "opengl/opengl.hpp"
namespace ruby {
class pVideoCGL;
}
@interface RubyVideoCGL : NSOpenGLView {
@public
ruby::pVideoCGL* video;
}
-(id) initWith:(ruby::pVideoCGL*)video pixelFormat:(NSOpenGLPixelFormat*)pixelFormat;
-(void) reshape;
@end
namespace ruby {
struct pVideoCGL : OpenGL {
RubyVideoCGL* view;
struct {
NSView* handle;
bool synchronize;
unsigned filter;
string shader;
} settings;
bool cap(const string& name) {
if(name == Video::Handle) return true;
if(name == Video::Synchronize) return true;
if(name == Video::Filter) return true;
if(name == Video::Shader) return true;
return false;
}
any get(const string& name) {
if(name == Video::Handle) return (uintptr_t)settings.handle;
if(name == Video::Synchronize) return settings.synchronize;
if(name == Video::Filter) return settings.filter;
return false;
}
bool set(const string& name, const any& value) {
if(name == Video::Handle) {
settings.handle = (NSView*)any_cast<uintptr_t>(value);
return true;
}
if(name == Video::Synchronize) {
if(settings.synchronize != any_cast<bool>(value)) {
settings.synchronize = any_cast<bool>(value);
if(view) {
@autoreleasepool {
[[view openGLContext] makeCurrentContext];
int synchronize = settings.synchronize;
[[view openGLContext] setValues:&synchronize forParameter:NSOpenGLCPSwapInterval];
}
}
}
return true;
}
if(name == Video::Filter) {
settings.filter = any_cast<unsigned>(value);
if(settings.shader.empty()) OpenGL::filter = settings.filter ? GL_LINEAR : GL_NEAREST;
return true;
}
if(name == Video::Shader) {
settings.shader = any_cast<const char*>(value);
@autoreleasepool {
[[view openGLContext] makeCurrentContext];
}
OpenGL::shader(settings.shader);
if(settings.shader.empty()) OpenGL::filter = settings.filter ? GL_LINEAR : GL_NEAREST;
return true;
}
return false;
}
bool lock(uint32_t*& data, unsigned& pitch, unsigned width, unsigned height) {
OpenGL::size(width, height);
return OpenGL::lock(data, pitch);
}
void unlock() {
}
void clear() {
@autoreleasepool {
[view lockFocus];
OpenGL::clear();
[[view openGLContext] flushBuffer];
[view unlockFocus];
}
}
void refresh() {
@autoreleasepool {
if([view lockFocusIfCanDraw]) {
auto area = [view frame];
outputWidth = area.size.width, outputHeight = area.size.height;
OpenGL::refresh();
[[view openGLContext] flushBuffer];
[view unlockFocus];
}
}
}
bool init() {
term();
@autoreleasepool {
NSOpenGLPixelFormatAttribute attributes[] = {
NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core,
NSOpenGLPFAColorSize, 24,
NSOpenGLPFAAlphaSize, 8,
NSOpenGLPFADoubleBuffer,
0
};
auto size = [settings.handle frame].size;
auto format = [[[NSOpenGLPixelFormat alloc] initWithAttributes:attributes] autorelease];
auto context = [[[NSOpenGLContext alloc] initWithFormat:format shareContext:nil] autorelease];
view = [[RubyVideoCGL alloc] initWith:this pixelFormat:format];
[view setOpenGLContext:context];
[view setFrame:NSMakeRect(0, 0, size.width, size.height)];
[view setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
[settings.handle addSubview:view];
[context setView:view];
[view lockFocus];
OpenGL::init();
//print((const char*)glGetString(GL_VERSION), "\n");
int synchronize = settings.synchronize;
[[view openGLContext] setValues:&synchronize forParameter:NSOpenGLCPSwapInterval];
[view unlockFocus];
}
clear();
return true;
}
void term() {
OpenGL::term();
@autoreleasepool {
[view removeFromSuperview];
[view release];
view = nil;
}
}
pVideoCGL() {
view = nil;
settings.handle = nil;
settings.synchronize = false;
settings.filter = 0;
}
~pVideoCGL() {
term();
}
};
DeclareVideo(CGL)
}
@implementation RubyVideoCGL : NSOpenGLView
-(id) initWith:(ruby::pVideoCGL*)videoPointer pixelFormat:(NSOpenGLPixelFormat*)pixelFormat {
if(self = [super initWithFrame:NSMakeRect(0, 0, 0, 0) pixelFormat:pixelFormat]) {
video = videoPointer;
}
return self;
}
-(void) reshape {
video->refresh();
}
@end

View File

@ -0,0 +1,463 @@
#undef interface
#define interface struct
#include <d3d9.h>
#include <d3dx9.h>
#undef interface
#define D3DVERTEX (D3DFVF_XYZRHW | D3DFVF_TEX1)
typedef HRESULT (__stdcall* EffectProc)(LPDIRECT3DDEVICE9, LPCVOID, UINT, D3DXMACRO const*, LPD3DXINCLUDE, DWORD, LPD3DXEFFECTPOOL, LPD3DXEFFECT*, LPD3DXBUFFER*);
typedef HRESULT (__stdcall* TextureProc)(LPDIRECT3DDEVICE9, LPCTSTR, LPDIRECT3DTEXTURE9*);
namespace ruby {
class pVideoD3D {
public:
LPDIRECT3D9 lpd3d;
LPDIRECT3DDEVICE9 device;
LPDIRECT3DVERTEXBUFFER9 vertex_buffer;
LPDIRECT3DVERTEXBUFFER9* vertex_ptr;
D3DPRESENT_PARAMETERS presentation;
D3DSURFACE_DESC d3dsd;
D3DLOCKED_RECT d3dlr;
D3DRASTER_STATUS d3drs;
D3DCAPS9 d3dcaps;
LPDIRECT3DTEXTURE9 texture;
LPDIRECT3DSURFACE9 surface;
LPD3DXEFFECT effect;
string shader_source_markup;
bool lost;
unsigned iwidth, iheight;
struct d3dvertex {
float x, y, z, rhw; //screen coords
float u, v; //texture coords
};
struct {
uint32_t t_usage, v_usage;
uint32_t t_pool, v_pool;
uint32_t lock;
uint32_t filter;
} flags;
struct {
bool dynamic; //device supports dynamic textures
bool shader; //device supports pixel shaders
} caps;
struct {
HWND handle;
bool synchronize;
unsigned filter;
unsigned width;
unsigned height;
} settings;
struct {
unsigned width;
unsigned height;
} state;
bool cap(const string& name) {
if(name == Video::Handle) return true;
if(name == Video::Synchronize) return true;
if(name == Video::Filter) return true;
if(name == Video::Shader) return false;
return false;
}
any get(const string& name) {
if(name == Video::Handle) return (uintptr_t)settings.handle;
if(name == Video::Synchronize) return settings.synchronize;
if(name == Video::Filter) return settings.filter;
return false;
}
bool set(const string& name, const any& value) {
if(name == Video::Handle) {
settings.handle = (HWND)any_cast<uintptr_t>(value);
return true;
}
if(name == Video::Synchronize) {
settings.synchronize = any_cast<bool>(value);
return true;
}
if(name == Video::Filter) {
settings.filter = any_cast<unsigned>(value);
if(lpd3d) update_filter();
return true;
}
if(name == Video::Shader) {
return false;
set_shader(any_cast<const char*>(value));
return true;
}
return false;
}
bool recover() {
if(!device) return false;
if(lost) {
release_resources();
if(device->Reset(&presentation) != D3D_OK) return false;
}
lost = false;
device->SetDialogBoxMode(false);
device->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
device->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
device->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE);
device->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
device->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
device->SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE);
device->SetRenderState(D3DRS_LIGHTING, false);
device->SetRenderState(D3DRS_ZENABLE, false);
device->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
device->SetRenderState(D3DRS_ALPHABLENDENABLE, false);
device->SetVertexShader(NULL);
device->SetFVF(D3DVERTEX);
device->CreateVertexBuffer(sizeof(d3dvertex) * 4, flags.v_usage, D3DVERTEX, (D3DPOOL)flags.v_pool, &vertex_buffer, NULL);
iwidth = 0;
iheight = 0;
resize(settings.width = 256, settings.height = 256);
update_filter();
clear();
return true;
}
unsigned rounded_power_of_two(unsigned n) {
n--;
n |= n >> 1;
n |= n >> 2;
n |= n >> 4;
n |= n >> 8;
n |= n >> 16;
return n + 1;
}
void resize(unsigned width, unsigned height) {
if(iwidth >= width && iheight >= height) return;
iwidth = rounded_power_of_two(max(width, iwidth ));
iheight = rounded_power_of_two(max(height, iheight));
if(d3dcaps.MaxTextureWidth < iwidth || d3dcaps.MaxTextureWidth < iheight) {
//TODO: attempt to handle this more gracefully
return;
}
if(texture) texture->Release();
device->CreateTexture(iwidth, iheight, 1, flags.t_usage, D3DFMT_X8R8G8B8, (D3DPOOL)flags.t_pool, &texture, NULL);
}
void update_filter() {
if(!device) return;
if(lost && !recover()) return;
flags.filter = (settings.filter == Video::FilterNearest ? D3DTEXF_POINT : D3DTEXF_LINEAR);
device->SetSamplerState(0, D3DSAMP_MINFILTER, flags.filter);
device->SetSamplerState(0, D3DSAMP_MAGFILTER, flags.filter);
}
// Vertex format:
//
// 0----------1
// | /|
// | / |
// | / |
// | / |
// | / |
// 2----------3
//
// (x,y) screen coords, in pixels
// (u,v) texture coords, betweeen 0.0 (top, left) to 1.0 (bottom, right)
void set_vertex(
uint32_t px, uint32_t py, uint32_t pw, uint32_t ph,
uint32_t tw, uint32_t th,
uint32_t x, uint32_t y, uint32_t w, uint32_t h
) {
d3dvertex vertex[4];
vertex[0].x = vertex[2].x = (double)(x - 0.5);
vertex[1].x = vertex[3].x = (double)(x + w - 0.5);
vertex[0].y = vertex[1].y = (double)(y - 0.5);
vertex[2].y = vertex[3].y = (double)(y + h - 0.5);
//Z-buffer and RHW are unused for 2D blit, set to normal values
vertex[0].z = vertex[1].z = vertex[2].z = vertex[3].z = 0.0;
vertex[0].rhw = vertex[1].rhw = vertex[2].rhw = vertex[3].rhw = 1.0;
double rw = (double)w / (double)pw * (double)tw;
double rh = (double)h / (double)ph * (double)th;
vertex[0].u = vertex[2].u = (double)(px ) / rw;
vertex[1].u = vertex[3].u = (double)(px + w) / rw;
vertex[0].v = vertex[1].v = (double)(py ) / rh;
vertex[2].v = vertex[3].v = (double)(py + h) / rh;
vertex_buffer->Lock(0, sizeof(d3dvertex) * 4, (void**)&vertex_ptr, 0);
memcpy(vertex_ptr, vertex, sizeof(d3dvertex) * 4);
vertex_buffer->Unlock();
device->SetStreamSource(0, vertex_buffer, 0, sizeof(d3dvertex));
}
void clear() {
if(lost && !recover()) return;
texture->GetLevelDesc(0, &d3dsd);
texture->GetSurfaceLevel(0, &surface);
if(surface) {
device->ColorFill(surface, 0, D3DCOLOR_XRGB(0x00, 0x00, 0x00));
surface->Release();
surface = nullptr;
}
//clear primary display and all backbuffers
for(unsigned i = 0; i < 3; i++) {
device->Clear(0, 0, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0x00, 0x00, 0x00), 1.0f, 0);
device->Present(0, 0, 0, 0);
}
}
bool lock(uint32_t*& data, unsigned& pitch, unsigned width, unsigned height) {
if(lost && !recover()) return false;
if(width != settings.width || height != settings.height) {
resize(settings.width = width, settings.height = height);
}
texture->GetLevelDesc(0, &d3dsd);
texture->GetSurfaceLevel(0, &surface);
surface->LockRect(&d3dlr, 0, flags.lock);
pitch = d3dlr.Pitch;
return data = (uint32_t*)d3dlr.pBits;
}
void unlock() {
surface->UnlockRect();
surface->Release();
surface = nullptr;
}
void refresh() {
if(lost && !recover()) return;
RECT rd, rs; //dest, source rectangles
GetClientRect(settings.handle, &rd);
SetRect(&rs, 0, 0, settings.width, settings.height);
//if output size changed, driver must be re-initialized.
//failure to do so causes scaling issues on some video drivers.
if(state.width != rd.right || state.height != rd.bottom) {
init();
set_shader(shader_source_markup);
return;
}
if(caps.shader && effect) {
device->BeginScene();
set_vertex(0, 0, settings.width, settings.height, iwidth, iheight, 0, 0, rd.right, rd.bottom);
D3DXVECTOR4 rubyTextureSize;
rubyTextureSize.x = iwidth;
rubyTextureSize.y = iheight;
rubyTextureSize.z = 1.0 / iheight;
rubyTextureSize.w = 1.0 / iwidth;
effect->SetVector("rubyTextureSize", &rubyTextureSize);
D3DXVECTOR4 rubyInputSize;
rubyInputSize.x = settings.width;
rubyInputSize.y = settings.height;
rubyInputSize.z = 1.0 / settings.height;
rubyInputSize.w = 1.0 / settings.width;
effect->SetVector("rubyInputSize", &rubyInputSize);
D3DXVECTOR4 rubyOutputSize;
rubyOutputSize.x = rd.right;
rubyOutputSize.y = rd.bottom;
rubyOutputSize.z = 1.0 / rd.bottom;
rubyOutputSize.w = 1.0 / rd.right;
effect->SetVector("rubyOutputSize", &rubyOutputSize);
UINT passes;
effect->Begin(&passes, 0);
effect->SetTexture("rubyTexture", texture);
device->SetTexture(0, texture);
for(unsigned pass = 0; pass < passes; pass++) {
effect->BeginPass(pass);
device->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2);
effect->EndPass();
}
effect->End();
device->EndScene();
} else {
device->BeginScene();
set_vertex(0, 0, settings.width, settings.height, iwidth, iheight, 0, 0, rd.right, rd.bottom);
device->SetTexture(0, texture);
device->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2);
device->EndScene();
}
if(settings.synchronize) {
D3DRASTER_STATUS status;
//wait for a previous vblank to finish, if necessary
while(true) {
device->GetRasterStatus(0, &status);
if(status.InVBlank == false) break;
}
//wait for next vblank to begin
while(true) {
device->GetRasterStatus(0, &status);
if(status.InVBlank == true) break;
}
}
if(device->Present(0, 0, 0, 0) == D3DERR_DEVICELOST) lost = true;
}
void set_shader(const char* source) {
if(!caps.shader) return;
if(effect) {
effect->Release();
effect = NULL;
}
if(!source || !*source) {
shader_source_markup = "";
return;
}
shader_source_markup = source;
XML::Document document(shader_source_markup);
bool is_hlsl = document["shader"]["language"].data == "HLSL";
string shader_source = document["shader"]["source"].data;
if(shader_source == "") return;
HMODULE d3dx;
for(unsigned i = 0; i < 256; i++) {
char t[256];
sprintf(t, "d3dx9_%u.dll", i);
d3dx = LoadLibraryW(utf16_t(t));
if(d3dx) break;
}
if(!d3dx) d3dx = LoadLibraryW(L"d3dx9.dll");
if(!d3dx) return;
EffectProc effectProc = (EffectProc)GetProcAddress(d3dx, "D3DXCreateEffect");
TextureProc textureProc = (TextureProc)GetProcAddress(d3dx, "D3DXCreateTextureFromFileA");
LPD3DXBUFFER pBufferErrors = NULL;
effectProc(device, shader_source, lstrlenA(shader_source), NULL, NULL, 0, NULL, &effect, &pBufferErrors);
D3DXHANDLE hTech;
effect->FindNextValidTechnique(NULL, &hTech);
effect->SetTechnique(hTech);
}
bool init() {
term();
RECT rd;
GetClientRect(settings.handle, &rd);
state.width = rd.right;
state.height = rd.bottom;
lpd3d = Direct3DCreate9(D3D_SDK_VERSION);
if(!lpd3d) return false;
memset(&presentation, 0, sizeof(presentation));
presentation.Flags = D3DPRESENTFLAG_VIDEO;
presentation.SwapEffect = D3DSWAPEFFECT_FLIP;
presentation.hDeviceWindow = settings.handle;
presentation.BackBufferCount = 1;
presentation.MultiSampleType = D3DMULTISAMPLE_NONE;
presentation.MultiSampleQuality = 0;
presentation.EnableAutoDepthStencil = false;
presentation.AutoDepthStencilFormat = D3DFMT_UNKNOWN;
presentation.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
presentation.Windowed = true;
presentation.BackBufferFormat = D3DFMT_UNKNOWN;
presentation.BackBufferWidth = 0;
presentation.BackBufferHeight = 0;
if(lpd3d->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, settings.handle,
D3DCREATE_FPU_PRESERVE | D3DCREATE_SOFTWARE_VERTEXPROCESSING, &presentation, &device) != D3D_OK) {
return false;
}
device->GetDeviceCaps(&d3dcaps);
caps.dynamic = bool(d3dcaps.Caps2 & D3DCAPS2_DYNAMICTEXTURES);
caps.shader = d3dcaps.PixelShaderVersion > D3DPS_VERSION(1, 4);
if(caps.dynamic == true) {
flags.t_usage = D3DUSAGE_DYNAMIC;
flags.v_usage = D3DUSAGE_WRITEONLY | D3DUSAGE_DYNAMIC;
flags.t_pool = D3DPOOL_DEFAULT;
flags.v_pool = D3DPOOL_DEFAULT;
flags.lock = D3DLOCK_NOSYSLOCK | D3DLOCK_DISCARD;
} else {
flags.t_usage = 0;
flags.v_usage = D3DUSAGE_WRITEONLY;
flags.t_pool = D3DPOOL_MANAGED;
flags.v_pool = D3DPOOL_MANAGED;
flags.lock = D3DLOCK_NOSYSLOCK | D3DLOCK_DISCARD;
}
lost = false;
recover();
return true;
}
void release_resources() {
if(effect) { effect->Release(); effect = 0; }
if(vertex_buffer) { vertex_buffer->Release(); vertex_buffer = 0; }
if(surface) { surface->Release(); surface = 0; }
if(texture) { texture->Release(); texture = 0; }
}
void term() {
release_resources();
if(device) { device->Release(); device = 0; }
if(lpd3d) { lpd3d->Release(); lpd3d = 0; }
}
pVideoD3D() {
effect = 0;
vertex_buffer = 0;
surface = 0;
texture = 0;
device = 0;
lpd3d = 0;
lost = true;
settings.handle = 0;
settings.synchronize = false;
settings.filter = Video::FilterLinear;
}
};
DeclareVideo(D3D)
};
#undef D3DVERTEX

View File

@ -0,0 +1,186 @@
#include <ddraw.h>
namespace ruby {
class pVideoDD {
public:
LPDIRECTDRAW lpdd;
LPDIRECTDRAW7 lpdd7;
LPDIRECTDRAWSURFACE7 screen, raster;
LPDIRECTDRAWCLIPPER clipper;
DDSURFACEDESC2 ddsd;
DDSCAPS2 ddscaps;
unsigned iwidth, iheight;
struct {
HWND handle;
bool synchronize;
unsigned width;
unsigned height;
} settings;
bool cap(const string& name) {
if(name == Video::Handle) return true;
if(name == Video::Synchronize) return true;
return false;
}
any get(const string& name) {
if(name == Video::Handle) return (uintptr_t)settings.handle;
if(name == Video::Synchronize) return settings.synchronize;
return false;
}
bool set(const string& name, const any& value) {
if(name == Video::Handle) {
settings.handle = (HWND)any_cast<uintptr_t>(value);
return true;
}
if(name == Video::Synchronize) {
settings.synchronize = any_cast<bool>(value);
return true;
}
return false;
}
void resize(unsigned width, unsigned height) {
if(iwidth >= width && iheight >= height) return;
iwidth = max(width, iwidth);
iheight = max(height, iheight);
if(raster) raster->Release();
screen->GetSurfaceDesc(&ddsd);
int depth = ddsd.ddpfPixelFormat.dwRGBBitCount;
if(depth == 32) goto try_native_surface;
memset(&ddsd, 0, sizeof(DDSURFACEDESC2));
ddsd.dwSize = sizeof(DDSURFACEDESC2);
ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT | DDSD_PIXELFORMAT;
ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_VIDEOMEMORY; //DDSCAPS_SYSTEMMEMORY
ddsd.dwWidth = iwidth;
ddsd.dwHeight = iheight;
ddsd.ddpfPixelFormat.dwSize = sizeof(DDPIXELFORMAT);
ddsd.ddpfPixelFormat.dwFlags = DDPF_RGB;
ddsd.ddpfPixelFormat.dwRGBBitCount = 32;
ddsd.ddpfPixelFormat.dwRBitMask = 0xff0000;
ddsd.ddpfPixelFormat.dwGBitMask = 0x00ff00;
ddsd.ddpfPixelFormat.dwBBitMask = 0x0000ff;
if(lpdd7->CreateSurface(&ddsd, &raster, 0) == DD_OK) return clear();
try_native_surface:
memset(&ddsd, 0, sizeof(DDSURFACEDESC2));
ddsd.dwSize = sizeof(DDSURFACEDESC2);
ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT;
ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_VIDEOMEMORY; //DDSCAPS_SYSTEMMEMORY
ddsd.dwWidth = iwidth;
ddsd.dwHeight = iheight;
if(lpdd7->CreateSurface(&ddsd, &raster, 0) == DD_OK) return clear();
}
void clear() {
DDBLTFX fx;
fx.dwSize = sizeof(DDBLTFX);
fx.dwFillColor = 0x00000000;
screen->Blt(0, 0, 0, DDBLT_WAIT | DDBLT_COLORFILL, &fx);
raster->Blt(0, 0, 0, DDBLT_WAIT | DDBLT_COLORFILL, &fx);
}
bool lock(uint32_t*& data, unsigned& pitch, unsigned width, unsigned height) {
if(width != settings.width || height != settings.height) {
resize(settings.width = width, settings.height = height);
}
if(raster->Lock(0, &ddsd, DDLOCK_WAIT, 0) != DD_OK) {
raster->Restore();
if(raster->Lock(0, &ddsd, DDLOCK_WAIT, 0) != DD_OK) return false;
}
pitch = ddsd.lPitch;
return data = (uint32_t*)ddsd.lpSurface;
}
void unlock() {
raster->Unlock(0);
}
void refresh() {
if(settings.synchronize) {
while(true) {
BOOL in_vblank;
lpdd7->GetVerticalBlankStatus(&in_vblank);
if(in_vblank == true) break;
}
}
HRESULT hr;
RECT rd, rs;
SetRect(&rs, 0, 0, settings.width, settings.height);
POINT p = {0, 0};
ClientToScreen(settings.handle, &p);
GetClientRect(settings.handle, &rd);
OffsetRect(&rd, p.x, p.y);
if(screen->Blt(&rd, raster, &rs, DDBLT_WAIT, 0) == DDERR_SURFACELOST) {
screen->Restore();
raster->Restore();
}
}
bool init() {
term();
DirectDrawCreate(0, &lpdd, 0);
lpdd->QueryInterface(IID_IDirectDraw7, (void**)&lpdd7);
if(lpdd) { lpdd->Release(); lpdd = 0; }
lpdd7->SetCooperativeLevel(settings.handle, DDSCL_NORMAL);
memset(&ddsd, 0, sizeof(DDSURFACEDESC2));
ddsd.dwSize = sizeof(DDSURFACEDESC2);
ddsd.dwFlags = DDSD_CAPS;
ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
lpdd7->CreateSurface(&ddsd, &screen, 0);
lpdd7->CreateClipper(0, &clipper, 0);
clipper->SetHWnd(0, settings.handle);
screen->SetClipper(clipper);
raster = 0;
iwidth = 0;
iheight = 0;
resize(settings.width = 256, settings.height = 256);
return true;
}
void term() {
if(clipper) { clipper->Release(); clipper = 0; }
if(raster) { raster->Release(); raster = 0; }
if(screen) { screen->Release(); screen = 0; }
if(lpdd7) { lpdd7->Release(); lpdd7 = 0; }
if(lpdd) { lpdd->Release(); lpdd = 0; }
}
pVideoDD() {
lpdd = 0;
lpdd7 = 0;
screen = 0;
raster = 0;
clipper = 0;
settings.handle = 0;
}
};
DeclareVideo(DD)
};

100
src/burner/qt/ruby/video/gdi.cpp Executable file
View File

@ -0,0 +1,100 @@
#include <assert.h>
namespace ruby {
class pVideoGDI {
public:
uint32_t* buffer;
HBITMAP bitmap;
HDC bitmapdc;
BITMAPINFO bmi;
struct {
HWND handle;
unsigned width;
unsigned height;
} settings;
bool cap(const string& name) {
if(name == Video::Handle) return true;
return false;
}
any get(const string& name) {
if(name == Video::Handle) return (uintptr_t)settings.handle;
return false;
}
bool set(const string& name, const any& value) {
if(name == Video::Handle) {
settings.handle = (HWND)any_cast<uintptr_t>(value);
return true;
}
return false;
}
bool lock(uint32_t*& data, unsigned& pitch, unsigned width, unsigned height) {
settings.width = width;
settings.height = height;
pitch = 1024 * 4;
return data = buffer;
}
void unlock() {}
void clear() {}
void refresh() {
RECT rc;
GetClientRect(settings.handle, &rc);
SetDIBits(bitmapdc, bitmap, 0, settings.height, (void*)buffer, &bmi, DIB_RGB_COLORS);
HDC hdc = GetDC(settings.handle);
StretchBlt(hdc, rc.left, rc.top, rc.right, rc.bottom, bitmapdc, 0, 1024 - settings.height, settings.width, settings.height, SRCCOPY);
ReleaseDC(settings.handle, hdc);
}
bool init() {
HDC hdc = GetDC(settings.handle);
bitmapdc = CreateCompatibleDC(hdc);
assert(bitmapdc);
bitmap = CreateCompatibleBitmap(hdc, 1024, 1024);
assert(bitmap);
SelectObject(bitmapdc, bitmap);
ReleaseDC(settings.handle, hdc);
memset(&bmi, 0, sizeof(BITMAPINFO));
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmi.bmiHeader.biWidth = 1024;
bmi.bmiHeader.biHeight = -1024;
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = 32; //biBitCount of 15 is invalid, biBitCount of 16 is really RGB555
bmi.bmiHeader.biCompression = BI_RGB;
bmi.bmiHeader.biSizeImage = 1024 * 1024 * sizeof(uint32_t);
settings.width = 256;
settings.height = 256;
return true;
}
void term() {
DeleteObject(bitmap);
DeleteDC(bitmapdc);
}
pVideoGDI() {
buffer = (uint32_t*)malloc(1024 * 1024 * sizeof(uint32_t));
settings.handle = 0;
}
~pVideoGDI() {
if(buffer) free(buffer);
}
};
DeclareVideo(GDI)
};

251
src/burner/qt/ruby/video/glx.cpp Executable file
View File

@ -0,0 +1,251 @@
#include "opengl/opengl.hpp"
#define GLX_CONTEXT_MAJOR_VERSION_ARB 0x2091
#define GLX_CONTEXT_MINOR_VERSION_ARB 0x2092
namespace ruby {
struct pVideoGLX : OpenGL {
GLXContext (*glXCreateContextAttribs)(Display*, GLXFBConfig, GLXContext, int, const int*) = nullptr;
int (*glXSwapInterval)(int) = nullptr;
Display* display;
int screen;
Window xwindow;
Colormap colormap;
GLXContext glxcontext;
GLXWindow glxwindow;
struct {
int version_major, version_minor;
bool double_buffer;
bool is_direct;
} glx;
struct {
Window handle;
bool synchronize;
unsigned depth;
unsigned filter;
string shader;
} settings;
bool cap(const string& name) {
if(name == Video::Handle) return true;
if(name == Video::Synchronize) return true;
if(name == Video::Depth) return true;
if(name == Video::Filter) return true;
if(name == Video::Shader) return true;
return false;
}
any get(const string& name) {
if(name == Video::Handle) return (uintptr_t)settings.handle;
if(name == Video::Synchronize) return settings.synchronize;
if(name == Video::Depth) return settings.depth;
if(name == Video::Filter) return settings.filter;
return false;
}
bool set(const string& name, const any& value) {
if(name == Video::Handle) {
settings.handle = any_cast<uintptr_t>(value);
return true;
}
if(name == Video::Synchronize) {
if(settings.synchronize != any_cast<bool>(value)) {
settings.synchronize = any_cast<bool>(value);
if(glXSwapInterval) glXSwapInterval(settings.synchronize);
return true;
}
}
if(name == Video::Depth) {
unsigned depth = any_cast<unsigned>(value);
if(depth > DefaultDepth(display, screen)) return false;
switch(depth) {
case 24: inputFormat = GL_RGBA8; break;
case 30: inputFormat = GL_RGB10_A2; break;
default: return false;
}
settings.depth = depth;
return true;
}
if(name == Video::Filter) {
settings.filter = any_cast<unsigned>(value);
if(settings.shader.empty()) OpenGL::filter = settings.filter ? GL_LINEAR : GL_NEAREST;
return true;
}
if(name == Video::Shader) {
settings.shader = any_cast<const char*>(value);
OpenGL::shader(settings.shader);
if(settings.shader.empty()) OpenGL::filter = settings.filter ? GL_LINEAR : GL_NEAREST;
return true;
}
return false;
}
bool lock(uint32_t*& data, unsigned& pitch, unsigned width, unsigned height) {
OpenGL::size(width, height);
return OpenGL::lock(data, pitch);
}
void unlock() {
}
void clear() {
OpenGL::clear();
if(glx.double_buffer) glXSwapBuffers(display, glxwindow);
}
void refresh() {
//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, child;
XGetWindowAttributes(display, settings.handle, &parent);
XGetWindowAttributes(display, xwindow, &child);
if(child.width != parent.width || child.height != parent.height) {
XResizeWindow(display, xwindow, parent.width, parent.height);
}
outputWidth = parent.width, outputHeight = parent.height;
OpenGL::refresh();
if(glx.double_buffer) glXSwapBuffers(display, glxwindow);
}
bool init() {
term();
glXQueryVersion(display, &glx.version_major, &glx.version_minor);
//require GLX 1.2+ API
if(glx.version_major < 1 || (glx.version_major == 1 && glx.version_minor < 2)) return false;
XWindowAttributes window_attributes;
XGetWindowAttributes(display, settings.handle, &window_attributes);
//let GLX determine the best Visual to use for GL output; provide a few hints
//note: some video drivers will override double buffering attribute
int attributeList[] = {
GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT,
GLX_RENDER_TYPE, GLX_RGBA_BIT,
GLX_DOUBLEBUFFER, True,
GLX_RED_SIZE, (signed)(settings.depth / 3),
GLX_GREEN_SIZE, (signed)(settings.depth / 3) + (signed)(settings.depth % 3),
GLX_BLUE_SIZE, (signed)(settings.depth / 3),
None
};
int fbCount;
GLXFBConfig* fbConfig = glXChooseFBConfig(display, screen, attributeList, &fbCount);
if(fbCount == 0) return false;
XVisualInfo* vi = glXGetVisualFromFBConfig(display, fbConfig[0]);
//Window settings.handle has already been realized, most likely with DefaultVisual.
//GLX requires that the GL output window has the same Visual as the GLX context.
//it is not possible to change the Visual of an already realized (created) window.
//therefore a new child window, using the same GLX Visual, must be created and binded to settings.handle.
colormap = XCreateColormap(display, RootWindow(display, vi->screen), vi->visual, AllocNone);
XSetWindowAttributes attributes;
attributes.colormap = colormap;
attributes.border_pixel = 0;
xwindow = XCreateWindow(display, /* parent = */ settings.handle,
/* x = */ 0, /* y = */ 0, window_attributes.width, window_attributes.height,
/* border_width = */ 0, vi->depth, InputOutput, vi->visual,
CWColormap | CWBorderPixel, &attributes);
XSetWindowBackground(display, xwindow, /* color = */ 0);
XMapWindow(display, xwindow);
XFlush(display);
//window must be realized (appear onscreen) before we make the context current
while(XPending(display)) {
XEvent event;
XNextEvent(display, &event);
}
glxcontext = glXCreateContext(display, vi, /* sharelist = */ 0, /* direct = */ GL_TRUE);
glXMakeCurrent(display, glxwindow = xwindow, glxcontext);
glXCreateContextAttribs = (GLXContext (*)(Display*, GLXFBConfig, GLXContext, int, const int*))glGetProcAddress("glXCreateContextAttribsARB");
glXSwapInterval = (int (*)(int))glGetProcAddress("glXSwapIntervalSGI");
if(!glXSwapInterval) glXSwapInterval = (int (*)(int))glGetProcAddress("glXSwapIntervalMESA");
if(glXCreateContextAttribs) {
int attributes[] = {
GLX_CONTEXT_MAJOR_VERSION_ARB, 3,
GLX_CONTEXT_MINOR_VERSION_ARB, 2,
None
};
GLXContext context = glXCreateContextAttribs(display, fbConfig[0], nullptr, true, attributes);
if(context) {
glXMakeCurrent(display, 0, nullptr);
glXDestroyContext(display, glxcontext);
glXMakeCurrent(display, glxwindow, glxcontext = context);
}
}
if(glXSwapInterval) {
glXSwapInterval(settings.synchronize);
}
//read attributes of frame buffer for later use, as requested attributes from above are not always granted
int value = 0;
glXGetConfig(display, vi, GLX_DOUBLEBUFFER, &value);
glx.double_buffer = value;
glx.is_direct = glXIsDirect(display, glxcontext);
OpenGL::init();
return true;
}
void term() {
OpenGL::term();
if(glxcontext) {
glXDestroyContext(display, glxcontext);
glxcontext = nullptr;
}
if(xwindow) {
XUnmapWindow(display, xwindow);
xwindow = 0;
}
if(colormap) {
XFreeColormap(display, colormap);
colormap = 0;
}
}
pVideoGLX() {
display = XOpenDisplay(0);
screen = DefaultScreen(display);
settings.handle = 0;
settings.synchronize = false;
settings.depth = 24;
settings.filter = 1; //linear
xwindow = 0;
colormap = 0;
glxcontext = nullptr;
glxwindow = 0;
}
~pVideoGLX() {
term();
XCloseDisplay(display);
}
};
DeclareVideo(GLX)
};

View File

@ -0,0 +1,99 @@
#if defined(PLATFORM_MACOSX)
static bool OpenGLBind() {
return true;
}
#else
PFNGLCREATEPROGRAMPROC glCreateProgram = nullptr;
PFNGLDELETEPROGRAMPROC glDeleteProgram = nullptr;
PFNGLUSEPROGRAMPROC glUseProgram = nullptr;
PFNGLCREATESHADERPROC glCreateShader = nullptr;
PFNGLDELETESHADERPROC glDeleteShader = nullptr;
PFNGLSHADERSOURCEPROC glShaderSource = nullptr;
PFNGLCOMPILESHADERPROC glCompileShader = nullptr;
PFNGLGETSHADERIVPROC glGetShaderiv = nullptr;
PFNGLGETSHADERINFOLOGPROC glGetShaderInfoLog = nullptr;
PFNGLATTACHSHADERPROC glAttachShader = nullptr;
PFNGLDETACHSHADERPROC glDetachShader = nullptr;
PFNGLLINKPROGRAMPROC glLinkProgram = nullptr;
PFNGLVALIDATEPROGRAMPROC glValidateProgram = nullptr;
PFNGLGETPROGRAMIVPROC glGetProgramiv = nullptr;
PFNGLGETPROGRAMINFOLOGPROC glGetProgramInfoLog = nullptr;
PFNGLGENVERTEXARRAYSPROC glGenVertexArrays = nullptr;
PFNGLDELETEVERTEXARRAYSPROC glDeleteVertexArrays = nullptr;
PFNGLBINDVERTEXARRAYPROC glBindVertexArray = nullptr;
PFNGLGENBUFFERSPROC glGenBuffers = nullptr;
PFNGLDELETEBUFFERSPROC glDeleteBuffers = nullptr;
PFNGLBINDBUFFERPROC glBindBuffer = nullptr;
PFNGLBUFFERDATAPROC glBufferData = nullptr;
PFNGLGETATTRIBLOCATIONPROC glGetAttribLocation = nullptr;
PFNGLVERTEXATTRIBPOINTERPROC glVertexAttribPointer = nullptr;
PFNGLENABLEVERTEXATTRIBARRAYPROC glEnableVertexAttribArray = nullptr;
PFNGLDISABLEVERTEXATTRIBARRAYPROC glDisableVertexAttribArray = nullptr;
PFNGLBINDFRAGDATALOCATIONPROC glBindFragDataLocation = nullptr;
PFNGLGETUNIFORMLOCATIONPROC glGetUniformLocation = nullptr;
PFNGLGETUNIFORMIVPROC glGetUniformiv = nullptr;
PFNGLUNIFORM1IPROC glUniform1i = nullptr;
PFNGLUNIFORM1FPROC glUniform1f = nullptr;
PFNGLUNIFORM2FPROC glUniform2f = nullptr;
PFNGLUNIFORM2FVPROC glUniform2fv = nullptr;
PFNGLUNIFORM4FPROC glUniform4f = nullptr;
PFNGLUNIFORM4FVPROC glUniform4fv = nullptr;
PFNGLUNIFORMMATRIX4FVPROC glUniformMatrix4fv = nullptr;
PFNGLGENFRAMEBUFFERSPROC glGenFramebuffers = nullptr;
PFNGLDELETEFRAMEBUFFERSPROC glDeleteFramebuffers = nullptr;
PFNGLBINDFRAMEBUFFERPROC glBindFramebuffer = nullptr;
PFNGLFRAMEBUFFERTEXTURE2DPROC glFramebufferTexture2D = nullptr;
PFNGLACTIVETEXTUREPROC glActiveTexture = nullptr;
static bool OpenGLBind() {
#define bind(prototype, function) \
function = (prototype)glGetProcAddress(#function); \
if(function == nullptr) return false
bind(PFNGLCREATEPROGRAMPROC, glCreateProgram);
bind(PFNGLDELETEPROGRAMPROC, glDeleteProgram);
bind(PFNGLUSEPROGRAMPROC, glUseProgram);
bind(PFNGLCREATESHADERPROC, glCreateShader);
bind(PFNGLDELETESHADERPROC, glDeleteShader);
bind(PFNGLSHADERSOURCEPROC, glShaderSource);
bind(PFNGLCOMPILESHADERPROC, glCompileShader);
bind(PFNGLGETSHADERIVPROC, glGetShaderiv);
bind(PFNGLGETSHADERINFOLOGPROC, glGetShaderInfoLog);
bind(PFNGLATTACHSHADERPROC, glAttachShader);
bind(PFNGLDETACHSHADERPROC, glDetachShader);
bind(PFNGLLINKPROGRAMPROC, glLinkProgram);
bind(PFNGLVALIDATEPROGRAMPROC, glValidateProgram);
bind(PFNGLGETPROGRAMIVPROC, glGetProgramiv);
bind(PFNGLGETPROGRAMINFOLOGPROC, glGetProgramInfoLog);
bind(PFNGLGENVERTEXARRAYSPROC, glGenVertexArrays);
bind(PFNGLDELETEVERTEXARRAYSPROC, glDeleteVertexArrays);
bind(PFNGLBINDVERTEXARRAYPROC, glBindVertexArray);
bind(PFNGLGENBUFFERSPROC, glGenBuffers);
bind(PFNGLDELETEBUFFERSPROC, glDeleteBuffers);
bind(PFNGLBINDBUFFERPROC, glBindBuffer);
bind(PFNGLBUFFERDATAPROC, glBufferData);
bind(PFNGLGETATTRIBLOCATIONPROC, glGetAttribLocation);
bind(PFNGLVERTEXATTRIBPOINTERPROC, glVertexAttribPointer);
bind(PFNGLENABLEVERTEXATTRIBARRAYPROC, glEnableVertexAttribArray);
bind(PFNGLDISABLEVERTEXATTRIBARRAYPROC, glDisableVertexAttribArray);
bind(PFNGLBINDFRAGDATALOCATIONPROC, glBindFragDataLocation);
bind(PFNGLGETUNIFORMLOCATIONPROC, glGetUniformLocation);
bind(PFNGLGETUNIFORMIVPROC, glGetUniformiv);
bind(PFNGLUNIFORM1IPROC, glUniform1i);
bind(PFNGLUNIFORM1FPROC, glUniform1f);
bind(PFNGLUNIFORM2FPROC, glUniform2f);
bind(PFNGLUNIFORM2FVPROC, glUniform2fv);
bind(PFNGLUNIFORM4FPROC, glUniform4f);
bind(PFNGLUNIFORM4FVPROC, glUniform4fv);
bind(PFNGLUNIFORMMATRIX4FVPROC, glUniformMatrix4fv);
bind(PFNGLGENFRAMEBUFFERSPROC, glGenFramebuffers);
bind(PFNGLDELETEFRAMEBUFFERSPROC, glDeleteFramebuffers);
bind(PFNGLBINDFRAMEBUFFERPROC, glBindFramebuffer);
bind(PFNGLFRAMEBUFFERTEXTURE2DPROC, glFramebufferTexture2D);
bind(PFNGLACTIVETEXTUREPROC, glActiveTexture);
#undef bind
return true;
}
#endif

View File

@ -0,0 +1,210 @@
void OpenGL::shader(const char* pathname) {
for(auto& program : programs) program.release();
programs.reset();
settings.reset();
format = inputFormat;
filter = GL_LINEAR;
wrap = GL_CLAMP_TO_BORDER;
absoluteWidth = 0, absoluteHeight = 0;
relativeWidth = 0, relativeHeight = 0;
unsigned historySize = 0;
if(pathname) {
auto document = Markup::Document(file::read({pathname, "manifest.bml"}));
for(auto& node : document["settings"]) {
settings.insert({node.name, node.text()});
}
for(auto& node : document["input"]) {
if(node.name == "history") historySize = node.decimal();
if(node.name == "format") format = glrFormat(node.text());
if(node.name == "filter") filter = glrFilter(node.text());
if(node.name == "wrap") wrap = glrWrap(node.text());
}
for(auto& node : document["output"]) {
string text = node.text();
if(node.name == "width") {
if(text.endsWith("%")) relativeWidth = real(text.rtrim<1>("%")) / 100.0;
else absoluteWidth = decimal(text);
}
if(node.name == "height") {
if(text.endsWith("%")) relativeHeight = real(text.rtrim<1>("%")) / 100.0;
else absoluteHeight = decimal(text);
}
}
for(auto& node : document.find("program")) {
unsigned n = programs.size();
programs(n).bind(this, node, pathname);
}
}
//changing shaders may change input format, which requires the input texture to be recreated
if(texture) { glDeleteTextures(1, &texture); texture = 0; }
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, getFormat(), getType(), buffer);
allocateHistory(historySize);
}
void OpenGL::allocateHistory(unsigned size) {
for(auto& frame : history) glDeleteTextures(1, &frame.texture);
history.reset();
while(size--) {
OpenGLTexture frame;
frame.filter = filter;
frame.wrap = wrap;
glGenTextures(1, &frame.texture);
glBindTexture(GL_TEXTURE_2D, frame.texture);
glTexImage2D(GL_TEXTURE_2D, 0, format, frame.width = width, frame.height = height, 0, getFormat(), getType(), buffer);
history.append(frame);
}
}
bool OpenGL::lock(uint32_t*& data, unsigned& pitch) {
pitch = width * sizeof(uint32_t);
return data = buffer;
}
void OpenGL::clear() {
for(auto& p : programs) {
glUseProgram(p.program);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, p.framebuffer);
glClearColor(0, 0, 0, 1);
glClear(GL_COLOR_BUFFER_BIT);
}
glUseProgram(0);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
glClearColor(0, 0, 0, 1);
glClear(GL_COLOR_BUFFER_BIT);
}
void OpenGL::refresh() {
clear();
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, getFormat(), getType(), buffer);
struct Source {
GLuint texture;
unsigned width, height;
GLuint filter, wrap;
};
vector<Source> sources;
sources.prepend({texture, width, height, filter, wrap});
for(auto& p : programs) {
unsigned targetWidth = p.absoluteWidth ? p.absoluteWidth : outputWidth;
unsigned targetHeight = p.absoluteHeight ? p.absoluteHeight : outputHeight;
if(p.relativeWidth) targetWidth = sources[0].width * p.relativeWidth;
if(p.relativeHeight) targetHeight = sources[0].height * p.relativeHeight;
p.size(targetWidth, targetHeight);
glUseProgram(p.program);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, p.framebuffer);
glrUniform1i("phase", p.phase);
glrUniform1i("historyLength", history.size());
glrUniform1i("sourceLength", sources.size());
glrUniform1i("pixmapLength", p.pixmaps.size());
glrUniform4f("targetSize", targetWidth, targetHeight, 1.0 / targetWidth, 1.0 / targetHeight);
glrUniform4f("outputSize", outputWidth, outputHeight, 1.0 / outputWidth, 1.0 / outputHeight);
unsigned aid = 0;
for(auto& frame : history) {
glrUniform1i({"history[", aid, "]"}, aid);
glrUniform4f({"historySize[", aid, "]"}, frame.width, frame.height, 1.0 / frame.width, 1.0 / frame.height);
glActiveTexture(GL_TEXTURE0 + (aid++));
glBindTexture(GL_TEXTURE_2D, frame.texture);
glrParameters(frame.filter, frame.wrap);
}
unsigned bid = 0;
for(auto& source : sources) {
glrUniform1i({"source[", bid, "]"}, aid + bid);
glrUniform4f({"sourceSize[", bid, "]"}, source.width, source.height, 1.0 / source.width, 1.0 / source.height);
glActiveTexture(GL_TEXTURE0 + aid + (bid++));
glBindTexture(GL_TEXTURE_2D, source.texture);
glrParameters(source.filter, source.wrap);
}
unsigned cid = 0;
for(auto& pixmap : p.pixmaps) {
glrUniform1i({"pixmap[", cid, "]"}, aid + bid + cid);
glrUniform4f({"pixmapSize[", bid, "]"}, pixmap.width, pixmap.height, 1.0 / pixmap.width, 1.0 / pixmap.height);
glActiveTexture(GL_TEXTURE0 + aid + bid + (cid++));
glBindTexture(GL_TEXTURE_2D, pixmap.texture);
glrParameters(pixmap.filter, pixmap.wrap);
}
glActiveTexture(GL_TEXTURE0);
glrParameters(sources[0].filter, sources[0].wrap);
p.render(sources[0].width, sources[0].height, targetWidth, targetHeight);
glBindTexture(GL_TEXTURE_2D, p.texture);
p.phase = (p.phase + 1) % p.modulo;
sources.prepend({p.texture, p.width, p.height, p.filter, p.wrap});
}
unsigned targetWidth = absoluteWidth ? absoluteWidth : outputWidth;
unsigned targetHeight = absoluteHeight ? absoluteHeight : outputHeight;
if(relativeWidth) targetWidth = sources[0].width * relativeWidth;
if(relativeHeight) targetHeight = sources[0].height * relativeHeight;
glUseProgram(program);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
glrUniform1i("source[0]", 0);
glrUniform4f("targetSize", targetWidth, targetHeight, 1.0 / targetWidth, 1.0 / targetHeight);
glrUniform4f("outputSize", outputWidth, outputHeight, 1.0 / outputWidth, 1.0 / outputHeight);
glrParameters(sources[0].filter, sources[0].wrap);
render(sources[0].width, sources[0].height, outputWidth, outputHeight);
if(history.size() > 0) {
OpenGLTexture frame = history.takeLast();
glBindTexture(GL_TEXTURE_2D, frame.texture);
if(width == frame.width && height == frame.height) {
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, getFormat(), getType(), buffer);
} else {
glTexImage2D(GL_TEXTURE_2D, 0, format, frame.width = width, frame.height = height, 0, getFormat(), getType(), buffer);
}
history.prepend(frame);
}
}
bool OpenGL::init() {
if(!OpenGLBind()) return false;
glDisable(GL_ALPHA_TEST);
glDisable(GL_BLEND);
glDisable(GL_DEPTH_TEST);
glDisable(GL_POLYGON_SMOOTH);
glDisable(GL_STENCIL_TEST);
glEnable(GL_DITHER);
glEnable(GL_TEXTURE_2D);
program = glCreateProgram();
vertex = glrCreateShader(program, GL_VERTEX_SHADER, OpenGLOutputVertexShader);
//geometry = glrCreateShader(program, GL_GEOMETRY_SHADER, OpenGLGeometryShader);
fragment = glrCreateShader(program, GL_FRAGMENT_SHADER, OpenGLFragmentShader);
OpenGLSurface::allocate();
glrLinkProgram(program);
shader(nullptr);
return true;
}
void OpenGL::term() {
shader(nullptr); //release shader resources (eg frame[] history)
OpenGLSurface::release();
if(buffer) { delete[] buffer; buffer = nullptr; }
}

View File

@ -0,0 +1,97 @@
#if defined(PLATFORM_X)
#include <GL/gl.h>
#include <GL/glx.h>
#define glGetProcAddress(name) (*glXGetProcAddress)((const GLubyte*)(name))
#elif defined(PLATFORM_MACOSX)
#include <OpenGL/gl.h>
#include <OpenGL/gl3.h>
#elif defined(PLATFORM_WINDOWS)
#include <GL/gl.h>
#include <GL/glext.h>
#define glGetProcAddress(name) wglGetProcAddress(name)
#else
#error "ruby::OpenGL: unsupported platform"
#endif
namespace ruby {
#include "bind.hpp"
#include "shaders.hpp"
#include "utility.hpp"
struct OpenGL;
struct OpenGLTexture {
GLuint texture = 0;
unsigned width = 0;
unsigned height = 0;
GLuint format = GL_RGBA8;
GLuint filter = GL_LINEAR;
GLuint wrap = GL_CLAMP_TO_BORDER;
GLuint getFormat() const;
GLuint getType() const;
};
struct OpenGLSurface : OpenGLTexture {
GLuint program = 0;
GLuint framebuffer = 0;
GLuint vao = 0;
GLuint vbo[3] = {0, 0, 0};
GLuint vertex = 0;
GLuint geometry = 0;
GLuint fragment = 0;
uint32_t* buffer = nullptr;
void allocate();
void size(unsigned width, unsigned height);
void release();
void render(unsigned sourceWidth, unsigned sourceHeight, unsigned targetWidth, unsigned targetHeight);
};
struct OpenGLProgram : OpenGLSurface {
unsigned phase = 0; //frame counter
unsigned modulo = 0; //frame counter modulus
unsigned absoluteWidth = 0;
unsigned absoluteHeight = 0;
double relativeWidth = 0;
double relativeHeight = 0;
vector<OpenGLTexture> pixmaps;
void bind(OpenGL* instance, const Markup::Node& node, const string& pathname);
void parse(OpenGL* instance, string& source);
void release();
};
struct OpenGL : OpenGLProgram {
vector<OpenGLProgram> programs;
vector<OpenGLTexture> history;
GLuint inputFormat = GL_RGBA8;
unsigned outputWidth = 0;
unsigned outputHeight = 0;
struct Setting {
string name;
string value;
bool operator< (const Setting& source) { return name < source.name; }
bool operator==(const Setting& source) { return name == source.name; }
Setting() {}
Setting(const string& name) : name(name) {}
Setting(const string& name, const string& value) : name(name), value(value) {}
};
set<Setting> settings;
void shader(const char* pathname);
void allocateHistory(unsigned size);
bool lock(uint32_t*& data, unsigned& pitch);
void clear();
void refresh();
bool init();
void term();
};
#include "texture.hpp"
#include "surface.hpp"
#include "program.hpp"
#include "main.hpp"
}

View File

@ -0,0 +1,108 @@
void OpenGLProgram::bind(OpenGL* instance, const Markup::Node& node, const string& pathname) {
filter = glrFilter(node["filter"].text());
wrap = glrWrap(node["wrap"].text());
modulo = glrModulo(node["modulo"].integer());
string w = node["width"].text(), h = node["height"].text();
if(w.endsWith("%")) relativeWidth = real(w.rtrim<1>("%")) / 100.0;
else absoluteWidth = decimal(w);
if(h.endsWith("%")) relativeHeight = real(h.rtrim<1>("%")) / 100.0;
else absoluteHeight = decimal(h);
format = glrFormat(node["format"].text());
program = glCreateProgram();
glGenFramebuffers(1, &framebuffer);
if(file::exists({pathname, node["vertex"].text()})) {
string source = file::read({pathname, node["vertex"].text()});
parse(instance, source);
vertex = glrCreateShader(program, GL_VERTEX_SHADER, source);
} else {
vertex = glrCreateShader(program, GL_VERTEX_SHADER, OpenGLVertexShader);
}
if(file::exists({pathname, node["geometry"].text()})) {
string source = file::read({pathname, node["geometry"].text()});
parse(instance, source);
geometry = glrCreateShader(program, GL_GEOMETRY_SHADER, source);
} else {
//geometry shaders, when attached, must pass all vertex output through to the fragment shaders
//geometry = glrCreateShader(program, GL_GEOMETRY_SHADER, OpenGLGeometryShader);
}
if(file::exists({pathname, node["fragment"].text()})) {
string source = file::read({pathname, node["fragment"].text()});
parse(instance, source);
fragment = glrCreateShader(program, GL_FRAGMENT_SHADER, source);
} else {
fragment = glrCreateShader(program, GL_FRAGMENT_SHADER, OpenGLFragmentShader);
}
for(auto& leaf : node.find("pixmap")) {
nall::image image({pathname, leaf.text()});
image.transform(0, 32, 255u << 24, 255u << 16, 255u << 8, 255u << 0);
if(image.empty()) continue;
GLuint texture;
glGenTextures(1, &texture);
unsigned n = pixmaps.size();
pixmaps(n).texture = texture;
pixmaps(n).width = image.width;
pixmaps(n).height = image.height;
pixmaps(n).format = format;
pixmaps(n).filter = filter;
pixmaps(n).wrap = wrap;
if(leaf["format"].exists()) pixmaps(n).format = glrFormat(leaf["format"].text());
if(leaf["filter"].exists()) pixmaps(n).filter = glrFilter(leaf["filter"].text());
if(leaf["wrap"].exists()) pixmaps(n).wrap = glrWrap(leaf["wrap"].text());
unsigned w = glrSize(image.width), h = glrSize(image.height);
uint32_t* buffer = new uint32_t[w * h]();
glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(GL_TEXTURE_2D, 0, pixmaps(n).format, w, h, 0, pixmaps(n).getFormat(), pixmaps(n).getType(), buffer);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, image.width, image.height, getFormat(), getType(), image.data);
delete[] buffer;
}
OpenGLSurface::allocate();
glrLinkProgram(program);
}
//apply manifest settings to shader source #in tags
void OpenGLProgram::parse(OpenGL* instance, string& source) {
lstring lines = source.split("\n");
for(auto& line : lines) {
string s = line;
if(auto position = s.find("//")) s.resize(position()); //strip comments
s.strip(); //remove extraneous whitespace
if(s.match("#in ?*")) {
s.ltrim<1>("#in ").strip();
if(auto setting = instance->settings.find({s})) {
line = {"#define ", setting().name, " ", setting().value};
} else {
line.reset(); //undefined variable (test in source with #ifdef)
}
}
}
source = lines.merge("\n");
}
void OpenGLProgram::release() {
OpenGLSurface::release();
for(auto& pixmap : pixmaps) glDeleteTextures(1, &pixmap.texture);
pixmaps.reset();
width = 0;
height = 0;
format = GL_RGBA8;
filter = GL_LINEAR;
wrap = GL_CLAMP_TO_BORDER;
phase = 0;
modulo = 0;
absoluteWidth = 0;
absoluteHeight = 0;
relativeWidth = 0;
relativeHeight = 0;
}

View File

@ -0,0 +1,91 @@
static string OpenGLOutputVertexShader = R"(
#version 150
uniform vec4 targetSize;
uniform vec4 outputSize;
in vec2 texCoord;
out Vertex {
vec2 texCoord;
} vertexOut;
void main() {
//center image within output window
if(gl_VertexID == 0 || gl_VertexID == 2) {
gl_Position.x = -(targetSize.x / outputSize.x);
} else {
gl_Position.x = +(targetSize.x / outputSize.x);
}
//center and flip vertically (buffer[0, 0] = top-left; OpenGL[0, 0] = bottom-left)
if(gl_VertexID == 0 || gl_VertexID == 1) {
gl_Position.y = +(targetSize.y / outputSize.y);
} else {
gl_Position.y = -(targetSize.y / outputSize.y);
}
//align image to even pixel boundary to prevent aliasing
vec2 align = fract((outputSize.xy + targetSize.xy) / 2.0) * 2.0;
gl_Position.xy -= align / outputSize.xy;
gl_Position.zw = vec2(0.0, 1.0);
vertexOut.texCoord = texCoord;
}
)";
static string OpenGLVertexShader = R"(
#version 150
in vec4 position;
in vec2 texCoord;
out Vertex {
vec2 texCoord;
} vertexOut;
void main() {
gl_Position = position;
vertexOut.texCoord = texCoord;
}
)";
static string OpenGLGeometryShader = R"(
#version 150
layout(triangles) in;
layout(triangle_strip, max_vertices = 3) out;
in Vertex {
vec2 texCoord;
} vertexIn[];
out Vertex {
vec2 texCoord;
};
void main() {
for(int i = 0; i < gl_in.length(); i++) {
gl_Position = gl_in[i].gl_Position;
texCoord = vertexIn[i].texCoord;
EmitVertex();
}
EndPrimitive();
}
)";
static string OpenGLFragmentShader = R"(
#version 150
uniform sampler2D source[];
in Vertex {
vec2 texCoord;
};
out vec4 fragColor;
void main() {
fragColor = texture(source[0], texCoord);
}
)";

View File

@ -0,0 +1,114 @@
void OpenGLSurface::allocate() {
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
glGenBuffers(3, &vbo[0]);
}
void OpenGLSurface::size(unsigned w, unsigned h) {
if(width == w && height == h) return;
width = w, height = h;
w = glrSize(w), h = glrSize(h);
if(texture) { glDeleteTextures(1, &texture); texture = 0; }
if(buffer) { delete[] buffer; buffer = nullptr; }
buffer = new uint32_t[w * h]();
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(GL_TEXTURE_2D, 0, format, w, h, 0, getFormat(), getType(), buffer);
if(framebuffer) {
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffer);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
delete[] buffer;
buffer = nullptr;
}
}
void OpenGLSurface::release() {
if(vbo[0]) { glDeleteBuffers(3, &vbo[0]); for(auto &o : vbo) o = 0; }
if(vao) { glDeleteVertexArrays(1, &vao); vao = 0; }
if(vertex) { glDetachShader(program, vertex); glDeleteShader(vertex); vertex = 0; }
if(geometry) { glDetachShader(program, geometry); glDeleteShader(geometry); geometry = 0; }
if(fragment) { glDetachShader(program, fragment); glDeleteShader(fragment); fragment = 0; }
if(texture) { glDeleteTextures(1, &texture); texture = 0; }
if(framebuffer) { glDeleteFramebuffers(1, &framebuffer); framebuffer = 0; }
if(program) { glDeleteProgram(program); program = 0; }
width = 0, height = 0;
}
void OpenGLSurface::render(unsigned sourceWidth, unsigned sourceHeight, unsigned targetWidth, unsigned targetHeight) {
glViewport(0, 0, targetWidth, targetHeight);
float w = (float)sourceWidth / (float)glrSize(sourceWidth);
float h = (float)sourceHeight / (float)glrSize(sourceHeight);
float u = (float)targetWidth, v = (float)targetHeight;
GLint location;
GLfloat modelView[] = {
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1,
};
GLfloat projection[] = {
2.0f/u, 0.0f, 0.0f, 0.0f,
0.0f, 2.0f/v, 0.0f, 0.0f,
0.0f, 0.0f, -1.0f, 0.0f,
-1.0f, -1.0f, 0.0f, 1.0f,
};
GLfloat modelViewProjection[4 * 4];
Matrix::Multiply(modelViewProjection, modelView, 4, 4, projection, 4, 4);
GLfloat vertices[] = {
0, 0, 0, 1,
u, 0, 0, 1,
0, v, 0, 1,
u, v, 0, 1,
};
GLfloat positions[4 * 4];
for(unsigned n = 0; n < 16; n += 4) {
Matrix::Multiply(&positions[n], &vertices[n], 1, 4, modelViewProjection, 4, 4);
}
GLfloat texCoords[] = {
0, 0,
w, 0,
0, h,
w, h,
};
glrUniformMatrix4fv("modelView", modelView);
glrUniformMatrix4fv("projection", projection);
glrUniformMatrix4fv("modelViewProjection", modelViewProjection);
glBindVertexArray(vao);
glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
glBufferData(GL_ARRAY_BUFFER, 16 * sizeof(GLfloat), vertices, GL_STATIC_DRAW);
GLuint locationVertex = glGetAttribLocation(program, "vertex");
glEnableVertexAttribArray(locationVertex);
glVertexAttribPointer(locationVertex, 4, GL_FLOAT, GL_FALSE, 0, 0);
glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);
glBufferData(GL_ARRAY_BUFFER, 16 * sizeof(GLfloat), positions, GL_STATIC_DRAW);
GLuint locationPosition = glGetAttribLocation(program, "position");
glEnableVertexAttribArray(locationPosition);
glVertexAttribPointer(locationPosition, 4, GL_FLOAT, GL_FALSE, 0, 0);
glBindBuffer(GL_ARRAY_BUFFER, vbo[2]);
glBufferData(GL_ARRAY_BUFFER, 8 * sizeof(GLfloat), texCoords, GL_STATIC_DRAW);
GLuint locationTexCoord = glGetAttribLocation(program, "texCoord");
glEnableVertexAttribArray(locationTexCoord);
glVertexAttribPointer(locationTexCoord, 2, GL_FLOAT, GL_FALSE, 0, 0);
glBindFragDataLocation(program, 0, "fragColor");
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glDisableVertexAttribArray(locationVertex);
glDisableVertexAttribArray(locationPosition);
glDisableVertexAttribArray(locationTexCoord);
}

View File

@ -0,0 +1,12 @@
GLuint OpenGLTexture::getFormat() const {
if(format == GL_R32I) return GL_RED_INTEGER;
if(format == GL_R32UI) return GL_RED_INTEGER;
return GL_BGRA;
}
GLuint OpenGLTexture::getType() const {
if(format == GL_R32I) return GL_UNSIGNED_INT;
if(format == GL_R32UI) return GL_UNSIGNED_INT;
if(format == GL_RGB10_A2) return GL_UNSIGNED_INT_2_10_10_10_REV;
return GL_UNSIGNED_INT_8_8_8_8_REV;
}

View File

@ -0,0 +1,106 @@
static unsigned glrSize(unsigned size) {
return size;
//return bit::round(size); //return nearest power of two
}
static GLuint glrFormat(const string& format) {
if(format == "r32i" ) return GL_R32I;
if(format == "r32ui" ) return GL_R32UI;
if(format == "rgba8" ) return GL_RGBA8;
if(format == "rgb10a2") return GL_RGB10_A2;
if(format == "rgba12" ) return GL_RGBA12;
if(format == "rgba16" ) return GL_RGBA16;
if(format == "rgba16f") return GL_RGBA16F;
if(format == "rgba32f") return GL_RGBA32F;
return GL_RGBA8;
}
static GLuint glrFilter(const string& filter) {
if(filter == "nearest") return GL_NEAREST;
if(filter == "linear" ) return GL_LINEAR;
return GL_LINEAR;
}
static GLuint glrWrap(const string& wrap) {
if(wrap == "border") return GL_CLAMP_TO_BORDER;
if(wrap == "edge" ) return GL_CLAMP_TO_EDGE;
if(wrap == "repeat") return GL_REPEAT;
return GL_CLAMP_TO_BORDER;
}
static unsigned glrModulo(unsigned modulo) {
if(modulo) return modulo;
return 300; //divisible by 2, 3, 4, 5, 6, 10, 12, 15, 20, 25, 30, 50, 60, 100, 150
}
static GLuint glrProgram() {
GLuint program = 0;
glGetIntegerv(GL_CURRENT_PROGRAM, (GLint*)&program);
return program;
}
static void glrUniform1i(const string& name, GLint value) {
GLint location = glGetUniformLocation(glrProgram(), name);
glUniform1i(location, value);
}
static void glrUniform4f(const string& name, GLfloat value0, GLfloat value1, GLfloat value2, GLfloat value3) {
GLint location = glGetUniformLocation(glrProgram(), name);
glUniform4f(location, value0, value1, value2, value3);
}
static void glrUniformMatrix4fv(const string& name, GLfloat *values) {
GLint location = glGetUniformLocation(glrProgram(), name);
glUniformMatrix4fv(location, 1, GL_FALSE, values);
}
static void glrParameters(GLuint filter, GLuint wrap) {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrap);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrap);
}
static GLuint glrCreateShader(GLuint program, GLuint type, const char* source) {
GLuint shader = glCreateShader(type);
glShaderSource(shader, 1, &source, 0);
glCompileShader(shader);
GLint result = GL_FALSE;
glGetShaderiv(shader, GL_COMPILE_STATUS, &result);
if(result == GL_FALSE) {
GLint length = 0;
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &length);
char text[length + 1];
glGetShaderInfoLog(shader, length, &length, text);
text[length] = 0;
print("[ruby::OpenGL: shader compiler error]\n", (const char*)text, "\n\n");
return 0;
}
glAttachShader(program, shader);
return shader;
}
static void glrLinkProgram(GLuint program) {
glLinkProgram(program);
GLint result = GL_FALSE;
glGetProgramiv(program, GL_LINK_STATUS, &result);
if(result == GL_FALSE) {
GLint length = 0;
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &length);
char text[length + 1];
glGetProgramInfoLog(program, length, &length, text);
text[length] = 0;
print("[ruby::OpenGL: shader linker error]\n", (const char*)text, "\n\n");
}
glValidateProgram(program);
result = GL_FALSE;
glGetProgramiv(program, GL_VALIDATE_STATUS, &result);
if(result == GL_FALSE) {
GLint length = 0;
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &length);
char text[length + 1];
glGetProgramInfoLog(program, length, &length, text);
text[length] = 0;
print("[ruby::OpenGL: shader validation error]\n", (const char*)text, "\n\n");
}
}

141
src/burner/qt/ruby/video/sdl.cpp Executable file
View File

@ -0,0 +1,141 @@
#include <sys/ipc.h>
#include <sys/shm.h>
#include <X11/extensions/Xv.h>
#include <X11/extensions/Xvlib.h>
#include <X11/extensions/XShm.h>
#include <SDL/SDL.h>
namespace ruby {
struct pVideoSDL {
Display* display;
SDL_Surface* screen;
SDL_Surface* buffer;
unsigned iwidth, iheight;
struct {
uintptr_t handle;
unsigned width;
unsigned height;
} settings;
bool cap(const string& name) {
if(name == Video::Handle) return true;
return false;
}
any get(const string& name) {
if(name == Video::Handle) return settings.handle;
return false;
}
bool set(const string& name, const any& value) {
if(name == Video::Handle) {
settings.handle = any_cast<uintptr_t>(value);
return true;
}
return false;
}
void resize(unsigned width, unsigned height) {
if(iwidth >= width && iheight >= height) return;
iwidth = max(width, iwidth);
iheight = max(height, iheight);
if(buffer) SDL_FreeSurface(buffer);
buffer = SDL_CreateRGBSurface(
SDL_SWSURFACE, iwidth, iheight, 32,
0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000
);
}
bool lock(uint32_t*& data, unsigned& pitch, unsigned width, unsigned height) {
if(width != settings.width || height != settings.height) {
resize(settings.width = width, settings.height = height);
}
if(SDL_MUSTLOCK(buffer)) SDL_LockSurface(buffer);
pitch = buffer->pitch;
return data = (uint32_t*)buffer->pixels;
}
void unlock() {
if(SDL_MUSTLOCK(buffer)) SDL_UnlockSurface(buffer);
}
void clear() {
if(SDL_MUSTLOCK(buffer)) SDL_LockSurface(buffer);
for(unsigned y = 0; y < iheight; y++) {
uint32_t* data = (uint32_t*)buffer->pixels + y * (buffer->pitch >> 2);
for(unsigned x = 0; x < iwidth; x++) *data++ = 0xff000000;
}
if(SDL_MUSTLOCK(buffer)) SDL_UnlockSurface(buffer);
refresh();
}
void refresh() {
//ruby input is X8R8G8B8, top 8-bits are ignored.
//as SDL forces us to use a 32-bit buffer, we must set alpha to 255 (full opacity)
//to prevent blending against the window beneath when X window visual is 32-bits.
if(SDL_MUSTLOCK(buffer)) SDL_LockSurface(buffer);
for(unsigned y = 0; y < settings.height; y++) {
uint32_t *data = (uint32_t*)buffer->pixels + y * (buffer->pitch >> 2);
for(unsigned x = 0; x < settings.width; x++) *data++ |= 0xff000000;
}
if(SDL_MUSTLOCK(buffer)) SDL_UnlockSurface(buffer);
XWindowAttributes attributes;
XGetWindowAttributes(display, settings.handle, &attributes);
SDL_Rect src, dest;
src.x = 0;
src.y = 0;
src.w = settings.width;
src.h = settings.height;
dest.x = 0;
dest.y = 0;
dest.w = attributes.width;
dest.h = attributes.height;
SDL_SoftStretch(buffer, &src, screen, &dest);
SDL_UpdateRect(screen, dest.x, dest.y, dest.w, dest.h);
}
bool init() {
display = XOpenDisplay(0);
char env[512];
sprintf(env, "SDL_WINDOWID=%ld", (long int)settings.handle);
putenv(env);
SDL_InitSubSystem(SDL_INIT_VIDEO);
screen = SDL_SetVideoMode(2560, 1600, 32, SDL_HWSURFACE);
XUndefineCursor(display, settings.handle);
buffer = 0;
iwidth = 0;
iheight = 0;
resize(settings.width = 256, settings.height = 256);
return true;
}
void term() {
XCloseDisplay(display);
SDL_FreeSurface(buffer);
SDL_QuitSubSystem(SDL_INIT_VIDEO);
}
pVideoSDL() {
settings.handle = 0;
}
};
DeclareVideo(SDL)
};

160
src/burner/qt/ruby/video/wgl.cpp Executable file
View File

@ -0,0 +1,160 @@
#include "opengl/opengl.hpp"
#define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091
#define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092
namespace ruby {
struct pVideoWGL : OpenGL {
HGLRC (APIENTRY* wglCreateContextAttribs)(HDC, HGLRC, const int*) = nullptr;
BOOL (APIENTRY* wglSwapInterval)(int) = nullptr;
HDC display;
HGLRC wglcontext;
HWND window;
HINSTANCE glwindow;
struct {
HWND handle;
bool synchronize;
unsigned filter;
string shader;
} settings;
bool cap(const string& name) {
if(name == Video::Handle) return true;
if(name == Video::Synchronize) return true;
if(name == Video::Filter) return true;
if(name == Video::Shader) return true;
return false;
}
any get(const string& name) {
if(name == Video::Handle) return (uintptr_t)settings.handle;
if(name == Video::Synchronize) return settings.synchronize;
if(name == Video::Filter) return settings.filter;
return false;
}
bool set(const string& name, const any& value) {
if(name == Video::Handle) {
settings.handle = (HWND)any_cast<uintptr_t>(value);
return true;
}
if(name == Video::Synchronize) {
if(settings.synchronize != any_cast<bool>(value)) {
settings.synchronize = any_cast<bool>(value);
if(wglcontext) {
init();
OpenGL::shader(settings.shader);
if(settings.shader.empty()) OpenGL::filter = settings.filter ? GL_LINEAR : GL_NEAREST;
}
}
}
if(name == Video::Filter) {
settings.filter = any_cast<unsigned>(value);
if(settings.shader.empty()) OpenGL::filter = settings.filter ? GL_LINEAR : GL_NEAREST;
return true;
}
if(name == Video::Shader) {
settings.shader = any_cast<const char*>(value);
OpenGL::shader(settings.shader);
if(settings.shader.empty()) OpenGL::filter = settings.filter ? GL_LINEAR : GL_NEAREST;
return true;
}
return false;
}
bool lock(uint32_t*& data, unsigned& pitch, unsigned width, unsigned height) {
OpenGL::size(width, height);
return OpenGL::lock(data, pitch);
}
void unlock() {
}
void clear() {
OpenGL::clear();
SwapBuffers(display);
}
void refresh() {
RECT rc;
GetClientRect(settings.handle, &rc);
outputWidth = rc.right - rc.left, outputHeight = rc.bottom - rc.top;
OpenGL::refresh();
SwapBuffers(display);
}
bool init() {
term();
GLuint pixel_format;
PIXELFORMATDESCRIPTOR pfd;
memset(&pfd, 0, sizeof(PIXELFORMATDESCRIPTOR));
pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR);
pfd.nVersion = 1;
pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
pfd.iPixelType = PFD_TYPE_RGBA;
display = GetDC(settings.handle);
pixel_format = ChoosePixelFormat(display, &pfd);
SetPixelFormat(display, pixel_format, &pfd);
wglcontext = wglCreateContext(display);
wglMakeCurrent(display, wglcontext);
wglCreateContextAttribs = (HGLRC (APIENTRY*)(HDC, HGLRC, const int*))glGetProcAddress("wglCreateContextAttribsARB");
wglSwapInterval = (BOOL (APIENTRY*)(int))glGetProcAddress("wglSwapIntervalEXT");
if(wglCreateContextAttribs) {
int attributes[] = {
WGL_CONTEXT_MAJOR_VERSION_ARB, 3,
WGL_CONTEXT_MINOR_VERSION_ARB, 2,
0
};
HGLRC context = wglCreateContextAttribs(display, 0, attributes);
if(context) {
wglMakeCurrent(NULL, NULL);
wglDeleteContext(wglcontext);
wglMakeCurrent(display, wglcontext = context);
}
}
if(wglSwapInterval) {
wglSwapInterval(settings.synchronize);
}
OpenGL::init();
return true;
}
void term() {
OpenGL::term();
if(wglcontext) {
wglDeleteContext(wglcontext);
wglcontext = 0;
}
}
pVideoWGL() {
settings.handle = 0;
settings.synchronize = false;
settings.filter = 0;
window = 0;
wglcontext = 0;
glwindow = 0;
}
~pVideoWGL() { term(); }
};
DeclareVideo(WGL)
};

228
src/burner/qt/ruby/video/xshm.cpp Executable file
View File

@ -0,0 +1,228 @@
#include <sys/shm.h>
#include <X11/extensions/XShm.h>
namespace ruby {
struct pVideoXShm {
struct Device {
Display* display = nullptr;
int screen;
int depth;
Visual* visual = nullptr;
Window window;
XShmSegmentInfo shmInfo;
XImage* image = nullptr;
uint32_t* buffer = nullptr;
unsigned width, height;
} device;
struct Settings {
uintptr_t handle;
unsigned depth = 24;
uint32_t* buffer = nullptr;
unsigned width, height;
} settings;
struct Color {
unsigned depth;
unsigned shift;
unsigned idepth;
unsigned ishift;
} red, green, blue;
bool cap(const string& name) {
if(name == Video::Handle) return true;
if(name == Video::Depth) return true;
return false;
}
any get(const string& name) {
if(name == Video::Handle) return settings.handle;
if(name == Video::Depth) return settings.depth;
return false;
}
bool set(const string& name, const any& value) {
if(name == Video::Handle) {
settings.handle = any_cast<uintptr_t>(value);
return true;
}
if(name == Video::Depth) {
return setDepth(any_cast<unsigned>(value));
}
return false;
}
bool lock(uint32_t*& data, unsigned& pitch, unsigned width, unsigned height) {
if(settings.buffer == nullptr || settings.width != width || settings.height != height) {
if(settings.buffer) delete[] settings.buffer;
settings.width = width, settings.height = height;
settings.buffer = new uint32_t[width * height]();
}
data = settings.buffer;
pitch = settings.width * sizeof(uint32_t);
return true;
}
void unlock() {
}
void clear() {
if(settings.buffer == nullptr) return;
memset(settings.buffer, 0, settings.width * settings.height * sizeof(uint32_t));
refresh();
}
void refresh() {
if(settings.buffer == nullptr) return;
size();
float xRatio = (float)settings.width / (float)device.width;
float yRatio = (float)settings.height / (float)device.height;
float yStep = 0;
for(unsigned y = 0; y < device.height; y++) {
uint32_t* sp = settings.buffer + (unsigned)yStep * settings.width;
uint32_t* dp = device.buffer + y * device.width;
yStep += yRatio;
float xStep = 0;
for(unsigned x = 0; x < device.width; x++) {
uint32_t color = sp[(unsigned)xStep];
xStep += xRatio;
unsigned r = (color >> red.ishift ) & ((1 << red.idepth ) - 1);
unsigned g = (color >> green.ishift) & ((1 << green.idepth) - 1);
unsigned b = (color >> blue.ishift ) & ((1 << blue.idepth ) - 1);
*dp++ = image::normalize(r, red.idepth, red.depth ) << red.shift
| image::normalize(g, green.idepth, green.depth) << green.shift
| image::normalize(b, blue.idepth, blue.depth ) << blue.shift;
}
}
GC gc = XCreateGC(device.display, device.window, 0, 0);
XShmPutImage(
device.display, device.window, gc, device.image,
0, 0, 0, 0, device.width, device.height, False
);
XFreeGC(device.display, gc);
XFlush(device.display);
}
bool init() {
device.display = XOpenDisplay(0);
device.screen = DefaultScreen(device.display);
XWindowAttributes getAttributes;
XGetWindowAttributes(device.display, (Window)settings.handle, &getAttributes);
device.depth = getAttributes.depth;
device.visual = getAttributes.visual;
unsigned visualID = XVisualIDFromVisual(device.visual);
XVisualInfo visualTemplate = {0};
visualTemplate.screen = device.screen;
visualTemplate.depth = device.depth;
int visualsMatched = 0;
XVisualInfo* visualList = XGetVisualInfo(device.display, VisualScreenMask | VisualDepthMask, &visualTemplate, &visualsMatched);
for(unsigned n = 0; n < visualsMatched; n++) {
auto& v = visualList[n];
if(v.visualid == visualID) {
red.depth = bit::count(v.red_mask), red.shift = bit::first(v.red_mask);
green.depth = bit::count(v.green_mask), green.shift = bit::first(v.green_mask);
blue.depth = bit::count(v.blue_mask), blue.shift = bit::first(v.blue_mask);
break;
}
}
XFree(visualList);
setDepth(settings.depth);
XSetWindowAttributes setAttributes = {0};
setAttributes.border_pixel = 0;
device.window = XCreateWindow(device.display, (Window)settings.handle,
0, 0, 256, 256, 0,
getAttributes.depth, InputOutput, getAttributes.visual,
CWBorderPixel, &setAttributes
);
XSetWindowBackground(device.display, device.window, 0);
XMapWindow(device.display, device.window);
XFlush(device.display);
while(XPending(device.display)) {
XEvent event;
XNextEvent(device.display, &event);
}
if(size() == false) return false;
return true;
}
void term() {
free();
if(device.display) { XCloseDisplay(device.display); device.display = nullptr; }
}
~pVideoXShm() {
term();
}
//internal:
bool setDepth(unsigned depth) {
if(depth == 24) {
settings.depth = 24;
red.idepth = 8, red.ishift = 16;
green.idepth = 8, green.ishift = 8;
blue.idepth = 8, blue.ishift = 0;
return true;
}
if(depth == 30) {
settings.depth = 30;
red.idepth = 10, red.ishift = 20;
green.idepth = 10, green.ishift = 10;
blue.idepth = 10, blue.ishift = 0;
return true;
}
return false;
}
bool size() {
XWindowAttributes windowAttributes;
XGetWindowAttributes(device.display, settings.handle, &windowAttributes);
if(device.buffer && device.width == windowAttributes.width && device.height == windowAttributes.height) return true;
device.width = windowAttributes.width, device.height = windowAttributes.height;
XResizeWindow(device.display, device.window, device.width, device.height);
free();
//create
device.shmInfo.shmid = shmget(IPC_PRIVATE, device.width * device.height * sizeof(uint32_t), IPC_CREAT | 0777);
if(device.shmInfo.shmid < 0) return false;
device.shmInfo.shmaddr = (char*)shmat(device.shmInfo.shmid, 0, 0);
device.shmInfo.readOnly = False;
XShmAttach(device.display, &device.shmInfo);
device.buffer = (uint32_t*)device.shmInfo.shmaddr;
device.image = XShmCreateImage(device.display, device.visual, device.depth,
ZPixmap, device.shmInfo.shmaddr, &device.shmInfo, device.width, device.height
);
return true;
}
void free() {
if(device.buffer == nullptr) return;
device.buffer = nullptr;
XShmDetach(device.display, &device.shmInfo);
XDestroyImage(device.image);
shmdt(device.shmInfo.shmaddr);
shmctl(device.shmInfo.shmid, IPC_RMID, 0);
}
};
DeclareVideo(XShm)
}

499
src/burner/qt/ruby/video/xv.cpp Executable file
View File

@ -0,0 +1,499 @@
#include <sys/ipc.h>
#include <sys/shm.h>
#include <X11/extensions/XShm.h>
#include <X11/extensions/Xv.h>
#include <X11/extensions/Xvlib.h>
extern "C" XvImage* XvShmCreateImage(Display*, XvPortID, int, char*, int, int, XShmSegmentInfo*);
namespace ruby {
struct pVideoXv {
uint32_t* buffer;
uint8_t* ytable;
uint8_t* utable;
uint8_t* vtable;
enum XvFormat {
XvFormatRGB32,
XvFormatRGB24,
XvFormatRGB16,
XvFormatRGB15,
XvFormatYUY2,
XvFormatUYVY,
XvFormatUnknown
};
struct {
Display* display;
GC gc;
Window window;
Colormap colormap;
XShmSegmentInfo shminfo;
int port;
int depth;
int visualid;
XvImage* image;
XvFormat format;
uint32_t fourcc;
unsigned width;
unsigned height;
} device;
struct {
Window handle;
bool synchronize;
unsigned width;
unsigned height;
} settings;
bool cap(const string& name) {
if(name == Video::Handle) return true;
if(name == Video::Synchronize) {
return XInternAtom(XOpenDisplay(0), "XV_SYNC_TO_VBLANK", true) != None;
}
return false;
}
any get(const string& name) {
if(name == Video::Handle) return settings.handle;
if(name == Video::Synchronize) return settings.synchronize;
return false;
}
bool set(const string& name, const any& value) {
if(name == Video::Handle) {
settings.handle = any_cast<uintptr_t>(value);
return true;
}
if(name == Video::Synchronize) {
Display* display = XOpenDisplay(0);
Atom atom = XInternAtom(display, "XV_SYNC_TO_VBLANK", true);
if(atom != None && device.port >= 0) {
settings.synchronize = any_cast<bool>(value);
XvSetPortAttribute(display, device.port, atom, settings.synchronize);
return true;
}
return false;
}
return false;
}
void resize(unsigned width, unsigned height) {
if(device.width >= width && device.height >= height) return;
device.width = max(width, device.width);
device.height = max(height, device.height);
XShmDetach(device.display, &device.shminfo);
shmdt(device.shminfo.shmaddr);
shmctl(device.shminfo.shmid, IPC_RMID, NULL);
XFree(device.image);
delete[] buffer;
device.image = XvShmCreateImage(device.display, device.port, device.fourcc, 0, device.width, device.height, &device.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;
XShmAttach(device.display, &device.shminfo);
buffer = new uint32_t[device.width * device.height];
}
bool lock(uint32_t*& data, unsigned& pitch, unsigned width, unsigned height) {
if(width != settings.width || height != settings.height) {
resize(settings.width = width, settings.height = height);
}
pitch = device.width * 4;
return data = buffer;
}
void unlock() {
}
void clear() {
memset(buffer, 0, device.width * device.height * sizeof(uint32_t));
//clear twice in case video is double buffered ...
refresh();
refresh();
}
void refresh() {
unsigned width = settings.width;
unsigned height = settings.height;
XWindowAttributes target;
XGetWindowAttributes(device.display, device.window, &target);
//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);
}
//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() {
device.display = XOpenDisplay(0);
if(!XShmQueryExtension(device.display)) {
fprintf(stderr, "VideoXv: XShm extension not found.\n");
return false;
}
//find an appropriate Xv port
device.port = -1;
XvAdaptorInfo* adaptor_info;
unsigned adaptor_count;
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;
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(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(device.display, settings.handle, &window_attributes);
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;
}
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(device.display, "XV_AUTOPAINT_COLORKEY", true);
if(atom != None) XvSetPortAttribute(device.display, device.port, atom, 1);
//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 && format[i].red_mask == 0xf800) {
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 <= 16 && format[i].red_mask == 0x7c00) {
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.width = 256;
device.height = 256;
device.image = XvShmCreateImage(device.display, device.port, device.fourcc, 0, device.width, device.height, &device.shminfo);
if(!device.image) {
fprintf(stderr, "VideoXv: XShmCreateImage failed.\n");
return false;
}
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;
}
buffer = new uint32_t[device.width * device.height];
settings.width = 256;
settings.height = 256;
init_yuv_tables();
clear();
return true;
}
void term() {
XShmDetach(device.display, &device.shminfo);
shmdt(device.shminfo.shmaddr);
shmctl(device.shminfo.shmid, IPC_RMID, NULL);
XFree(device.image);
if(device.window) {
XUnmapWindow(device.display, device.window);
device.window = 0;
}
if(device.colormap) {
XFreeColormap(device.display, device.colormap);
device.colormap = 0;
}
if(buffer) { delete[] buffer; buffer = 0; }
if(ytable) { delete[] ytable; ytable = 0; }
if(utable) { delete[] utable; utable = 0; }
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 += device.width;
output += device.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 += (device.width - width);
output += (device.width - 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 += device.width - width;
output += device.width - 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 += device.width - width;
output += device.width - 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 += device.width - width;
output += device.width - 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 += device.width - width;
output += device.width - width;
}
}
void init_yuv_tables() {
ytable = new uint8_t[65536];
utable = new uint8_t[65536];
vtable = new uint8_t[65536];
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
//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 );
//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 );
ytable[i] = y < 0 ? 0 : y > 255 ? 255 : y;
utable[i] = u < 0 ? 0 : u > 255 ? 255 : u;
vtable[i] = v < 0 ? 0 : v > 255 ? 255 : v;
}
}
pVideoXv() {
device.window = 0;
device.colormap = 0;
device.port = -1;
ytable = 0;
utable = 0;
vtable = 0;
settings.handle = 0;
settings.synchronize = false;
}
~pVideoXv() {
term();
}
};
DeclareVideo(Xv)
};

View File

@ -0,0 +1,307 @@
#include <QtWidgets>
#include <QtAlgorithms>
#include <QMap>
#include "selectdialog.h"
#include "ui_selectdialog.h"
#include "burner.h"
#include "rominfodialog.h"
SelectDialog::SelectDialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::SelectDialog)
{
ui->setupUi(this);
m_icoNotFound = QIcon(tr(":/resource/tv-not-found.ico"));
m_icoNotFoundNonEssential = QIcon(tr(":/resource/tv-not-found-non-essential.ico"));
m_icoNotWorking = QIcon(tr(":/resource/tv-not-working.ico"));
m_defaultImage = QPixmap(tr(":/resource/splash.bmp"));
m_romInfoDlg = new RomInfoDialog(this);
m_romScanner = new RomScanDialog(this);
m_romPathEditor = new RomDirsDialog(this);
setWindowTitle(tr("Select Game"));
buildDriverTree();
connect(ui->tvDrivers, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)),
this, SLOT(driverChange(QTreeWidgetItem*,QTreeWidgetItem*)));
connect(ui->tvDrivers, SIGNAL(itemDoubleClicked(QTreeWidgetItem*,int)),
this, SLOT(driverSelect(QTreeWidgetItem*,int)));
connect(ui->btnRomInfo, SIGNAL(clicked()), this, SLOT(openRomInfo()));
connect(ui->btnScanRoms, SIGNAL(clicked()), this, SLOT(rescanRoms()));
connect(ui->btnRomDirs, SIGNAL(clicked()), this, SLOT(editRomPaths()));
connect(ui->btnPlay, SIGNAL(clicked()), this, SLOT(playGame()));
connect(ui->ckbShowAvaliable, SIGNAL(toggled(bool)), this, SLOT(itemShowAvaliable(bool)));
connect(ui->ckbShowUnavaliable, SIGNAL(toggled(bool)), this, SLOT(itemShowUnavaliable(bool)));
connect(ui->ckbShowClones, SIGNAL(toggled(bool)), this, SLOT(itemShowClones(bool)));
m_selectedDriver = 0;
m_showAvailable = true;
m_showUnavailable = true;
m_showClones = true;
m_showCount = 0;
}
SelectDialog::~SelectDialog()
{
delete ui;
}
void SelectDialog::driverChange(QTreeWidgetItem *item, QTreeWidgetItem *prev)
{
TreeDriverItem *driver = static_cast<TreeDriverItem*>(item);
int tmp = nBurnDrvActive;
int flags = DRV_ASCIIONLY;
m_selectedDriver = driver->driverNo();
// atualiza as informações sobre o driver
nBurnDrvActive = driver->driverNo();
ui->leGameInfo->setText(BurnDrvGetText(flags | DRV_FULLNAME));
updateTitleScreen();
{
QString manufacturer = BurnDrvGetTextA(DRV_MANUFACTURER) ?
BurnDrvGetText(flags | DRV_MANUFACTURER) : tr("Unknown");
QString date = BurnDrvGetTextA(DRV_DATE);
QString system = BurnDrvGetText(flags | DRV_SYSTEM);
QString prefix = (BurnDrvGetHardwareCode() & HARDWARE_PREFIX_CARTRIDGE)
? tr("Cartridge") : tr("Hardware");
QString releaseInfo = QString("%0 (%1, %2 %3)").arg(manufacturer,
date,
system,
prefix);
ui->leReleasedBy->setText(releaseInfo);
}
ui->leRomName->setText(driver->romName());
nBurnDrvActive = tmp;
}
void SelectDialog::driverSelect(QTreeWidgetItem *item, int column)
{
TreeDriverItem *driver = static_cast<TreeDriverItem*>(item);
m_selectedDriver = driver->driverNo();
accept();
}
void SelectDialog::playGame()
{
TreeDriverItem *driver = static_cast<TreeDriverItem*>(ui->tvDrivers->currentItem());
if (driver != nullptr) {
m_selectedDriver = driver->driverNo();
accept();
}
}
void SelectDialog::itemShowAvaliable(bool state)
{
if (state == m_showAvailable)
return;
m_showAvailable = state;
filterDrivers();
}
void SelectDialog::itemShowUnavaliable(bool state)
{
if (state == m_showUnavailable)
return;
m_showUnavailable = state;
filterDrivers();
}
void SelectDialog::itemShowClones(bool state)
{
if (state == m_showClones)
return;
m_showClones = state;
filterDrivers();
}
void SelectDialog::updateTitleScreen()
{
QString drv = BurnDrvGetTextA(DRV_NAME);
QString path = (QString(tr("support/titles/%0.png").arg(drv)));
if (QFile(path).exists()) {
QPixmap p(path);
ui->imgTitleScreen->setPixmap(p);
} else {
ui->imgTitleScreen->setPixmap(m_defaultImage);
}
}
void SelectDialog::updateLabelCounter()
{
QString text(tr("Showing %0 of %1 sets").
arg(m_showCount).arg(nBurnDrvCount));
ui->lblCounter->setText(text);
}
int SelectDialog::selectedDriver() const
{
return m_selectedDriver;
}
void SelectDialog::openRomInfo()
{
m_romInfoDlg->setDriverNo(m_selectedDriver);
m_romInfoDlg->exec();
}
void SelectDialog::rescanRoms()
{
if (m_romScanner->exec() == QDialog::Accepted) {
filterDrivers();
}
}
void SelectDialog::editRomPaths()
{
m_romPathEditor->exec();
}
void SelectDialog::buildDriverTree()
{
int nTmpDrv = nBurnDrvActive;
// build parent list
for (int i = 0; i < nBurnDrvCount; i++) {
nBurnDrvActive = i;
if (BurnDrvGetFlags() & BDF_BOARDROM)
continue;
// skip clones
if (BurnDrvGetText(DRV_PARENT) != NULL && (BurnDrvGetFlags() & BDF_CLONE))
continue;
TreeDriverItem *ditem = new TreeDriverItem();
ditem->setIcon(0, m_icoNotFound);
ditem->setText(0, BurnDrvGetText(DRV_ASCIIONLY | DRV_FULLNAME));
ditem->setRomName(BurnDrvGetTextA(DRV_NAME));
ditem->setDriverNo(i);
ditem->setIsParent(true);
ui->tvDrivers->addTopLevelItem(ditem);
m_parents[tr(ditem->romName())] = ditem;
}
// build clones tree
for (int i = 0; i < nBurnDrvCount; i++) {
nBurnDrvActive = i;
if (BurnDrvGetFlags() & BDF_BOARDROM)
continue;
// skip parents
if (BurnDrvGetTextA(DRV_PARENT) == NULL || !(BurnDrvGetFlags() & BDF_CLONE))
continue;
TreeDriverItem *itemParent = m_parents[tr(BurnDrvGetTextA(DRV_PARENT))];
if (itemParent) {
TreeDriverItem *ditem = new TreeDriverItem();
ditem->setIcon(0, m_icoNotFound);
ditem->setText(0, BurnDrvGetText(DRV_ASCIIONLY | DRV_FULLNAME));
ditem->setRomName(BurnDrvGetTextA(DRV_NAME));
ditem->setDriverNo(i);
ditem->setIsParent(false);
ditem->setBackgroundColor(0, QColor(230, 230, 230));
itemParent->addChild(ditem);
}
}
nBurnDrvActive = nTmpDrv;
}
bool SelectDialog::isFiltered(TreeDriverItem *driver)
{
bool status = m_romScanner->status(driver->driverNo()) ? true : false;
if (!m_showUnavailable)
if (!(m_showAvailable && status))
return false;
return true;
}
void SelectDialog::filterDrivers()
{
auto setupIcon = [&](int stat, TreeDriverItem *item) -> void {
switch (stat) {
case 0: item->setIcon(0, m_icoNotFound); break;
case 2:
case 3: item->setIcon(0, m_icoNotFoundNonEssential); break;
case 1: item->setIcon(0, m_icoNotWorking); break;
}
};
m_showCount = 0;
foreach (TreeDriverItem *driver, m_parents.values()) {
// skip it
if (driver == nullptr)
continue;
// show all
driver->setHidden(true);
// setup icon
int stat = m_romScanner->status(driver->driverNo());
setupIcon(stat, driver);
if (!isFiltered(driver))
continue;
driver->setHidden(false);
for (int idx = 0; idx < driver->childCount(); idx++) {
TreeDriverItem *clone = static_cast<TreeDriverItem *>(driver->child(idx));
clone->setHidden(true);
int cstat = m_romScanner->status(driver->driverNo());
setupIcon(cstat, clone);
if (m_showClones) {
if (!isFiltered(clone))
continue;
clone->setHidden(false);
}
}
m_showCount++;
}
updateLabelCounter();
}
TreeDriverItem::TreeDriverItem() : QTreeWidgetItem()
{
}
int TreeDriverItem::driverNo() const
{
return m_driverNo;
}
void TreeDriverItem::setDriverNo(int driverNo)
{
m_driverNo = driverNo;
}
const char *TreeDriverItem::romName() const
{
return m_romName;
}
void TreeDriverItem::setRomName(const char *romName)
{
m_romName = romName;
}
bool TreeDriverItem::isParent() const
{
return m_isParent;
}
void TreeDriverItem::setIsParent(bool isParent)
{
m_isParent = isParent;
}

View File

@ -0,0 +1,84 @@
#ifndef SELECTDIALOG_H
#define SELECTDIALOG_H
#include <QDialog>
#include <QTreeWidget>
#include <QTreeWidgetItem>
#include <QMap>
#include <QPixmap>
#include "rominfodialog.h"
#include "romscandialog.h"
#include "romdirsdialog.h"
namespace Ui {
class SelectDialog;
}
class TreeDriverItem : public QTreeWidgetItem
{
public:
TreeDriverItem();
int driverNo() const;
void setDriverNo(int driverNo);
const char *romName() const;
void setRomName(const char *romName);
bool isParent() const;
void setIsParent(bool isParent);
private:
int m_driverNo;
const char *m_romName;
bool m_isParent;
};
class SelectDialog : public QDialog
{
Q_OBJECT
public:
explicit SelectDialog(QWidget *parent = 0);
~SelectDialog();
int selectedDriver() const;
public slots:
void openRomInfo();
void rescanRoms();
void editRomPaths();
void driverChange(QTreeWidgetItem * item, QTreeWidgetItem * prev);
void driverSelect(QTreeWidgetItem * item, int column);
void playGame();
void itemShowAvaliable(bool state);
void itemShowUnavaliable(bool state);
void itemShowClones(bool state);
signals:
void driverSelected(int no);
private:
int m_selectedDriver;
void updateTitleScreen();
void updateLabelCounter();
void buildDriverTree();
bool isFiltered(TreeDriverItem *driver);
void filterDrivers();
Ui::SelectDialog *ui;
QIcon m_icoNotFound;
QIcon m_icoNotFoundNonEssential;
QIcon m_icoNotWorking;
RomInfoDialog *m_romInfoDlg;
RomScanDialog *m_romScanner;
RomDirsDialog *m_romPathEditor;
QMap<QString, TreeDriverItem*> m_parents;
bool m_showAvailable;
bool m_showUnavailable;
bool m_showClones;
int m_showCount;
QPixmap m_defaultImage;
};
#endif // SELECTDIALOG_H

View File

@ -0,0 +1,653 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>SelectDialog</class>
<widget class="QDialog" name="SelectDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>899</width>
<height>650</height>
</rect>
</property>
<property name="windowTitle">
<string>Dialog</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_7">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_6" stretch="10,0">
<item>
<layout class="QVBoxLayout" name="verticalLayout" stretch="0,0,0">
<item>
<widget class="QTreeWidget" name="tvDrivers">
<column>
<property name="text">
<string>Select Game</string>
</property>
</column>
</widget>
</item>
<item>
<layout class="QFormLayout" name="formLayout">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
</property>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Game Info</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="leGameInfo">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>ROM Name</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="leRomName">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>ROM Info</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="leRomInfo">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Released by</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLineEdit" name="leReleasedBy">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>Genre</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QLineEdit" name="leGenre">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Notes</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QLineEdit" name="leNotes">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="lblCounter">
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>...</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="btnRomInfo">
<property name="text">
<string>Rom Info</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_5">
<property name="sizeConstraint">
<enum>QLayout::SetDefaultConstraint</enum>
</property>
<item>
<widget class="QLabel" name="imgTitleScreen">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>320</width>
<height>200</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>320</width>
<height>200</height>
</size>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="text">
<string/>
</property>
<property name="pixmap">
<pixmap resource="rscr.qrc">:/resource/splash.bmp</pixmap>
</property>
<property name="scaledContents">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Options</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QCheckBox" name="ckbShowAvaliable">
<property name="text">
<string>Show avaliable</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="ckbShowUnavaliable">
<property name="text">
<string>Show unavaliable</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="ckbShowClones">
<property name="text">
<string>Always show clones</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="ckbUseZipnames">
<property name="text">
<string>Use zipnames</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="ckbLatinTextOnly">
<property name="text">
<string>Latin text only</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QPushButton" name="btnRomDirs">
<property name="text">
<string>ROMs Dirs...</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnScanRoms">
<property name="text">
<string>Scan ROMs</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QTreeWidget" name="tvFilters">
<column>
<property name="text">
<string>Filters</string>
</property>
</column>
<item>
<property name="text">
<string>Filters</string>
</property>
<property name="checkState">
<enum>Unchecked</enum>
</property>
<property name="flags">
<set>ItemIsSelectable|ItemIsUserCheckable|ItemIsEnabled|ItemIsTristate</set>
</property>
<item>
<property name="text">
<string>BoardType</string>
</property>
<property name="checkState">
<enum>Unchecked</enum>
</property>
<property name="flags">
<set>ItemIsSelectable|ItemIsDragEnabled|ItemIsUserCheckable|ItemIsEnabled|ItemIsTristate</set>
</property>
<item>
<property name="text">
<string>Genuine</string>
</property>
<property name="checkState">
<enum>Unchecked</enum>
</property>
</item>
<item>
<property name="text">
<string>Bootleg</string>
</property>
<property name="checkState">
<enum>Unchecked</enum>
</property>
</item>
<item>
<property name="text">
<string>Demo</string>
</property>
<property name="checkState">
<enum>Unchecked</enum>
</property>
</item>
<item>
<property name="text">
<string>Hack</string>
</property>
<property name="checkState">
<enum>Unchecked</enum>
</property>
</item>
<item>
<property name="text">
<string>Homebrew</string>
</property>
<property name="checkState">
<enum>Unchecked</enum>
</property>
</item>
<item>
<property name="text">
<string>Prototype</string>
</property>
<property name="checkState">
<enum>Unchecked</enum>
</property>
</item>
</item>
<item>
<property name="text">
<string>Family</string>
</property>
<property name="checkState">
<enum>Unchecked</enum>
</property>
</item>
<item>
<property name="text">
<string>Genre</string>
</property>
<property name="checkState">
<enum>Unchecked</enum>
</property>
</item>
<item>
<property name="text">
<string>Hardware</string>
</property>
<property name="checkState">
<enum>Unchecked</enum>
</property>
<item>
<property name="text">
<string>Capcom (Other)</string>
</property>
<property name="checkState">
<enum>Unchecked</enum>
</property>
</item>
<item>
<property name="text">
<string>Cave</string>
</property>
<property name="checkState">
<enum>Unchecked</enum>
</property>
</item>
<item>
<property name="text">
<string>CPS-1</string>
</property>
<property name="checkState">
<enum>Unchecked</enum>
</property>
</item>
<item>
<property name="text">
<string>CPS-2</string>
</property>
<property name="checkState">
<enum>Unchecked</enum>
</property>
</item>
<item>
<property name="text">
<string>CPS-3</string>
</property>
<property name="checkState">
<enum>Unchecked</enum>
</property>
</item>
<item>
<property name="text">
<string>Data East</string>
</property>
<property name="checkState">
<enum>Unchecked</enum>
</property>
</item>
<item>
<property name="text">
<string>Galaxian</string>
</property>
<property name="checkState">
<enum>Unchecked</enum>
</property>
</item>
<item>
<property name="text">
<string>Irem</string>
</property>
<property name="checkState">
<enum>Unchecked</enum>
</property>
</item>
<item>
<property name="text">
<string>Kaneko</string>
</property>
<property name="checkState">
<enum>Unchecked</enum>
</property>
</item>
<item>
<property name="text">
<string>Konami</string>
</property>
<property name="checkState">
<enum>Unchecked</enum>
</property>
</item>
<item>
<property name="text">
<string>Neo Geo</string>
</property>
<property name="checkState">
<enum>Unchecked</enum>
</property>
</item>
<item>
<property name="text">
<string>Pacman</string>
</property>
<property name="checkState">
<enum>Unchecked</enum>
</property>
</item>
<item>
<property name="text">
<string>PGM</string>
</property>
<property name="checkState">
<enum>Unchecked</enum>
</property>
</item>
<item>
<property name="text">
<string>Psikyo</string>
</property>
<property name="checkState">
<enum>Unchecked</enum>
</property>
</item>
<item>
<property name="text">
<string>Sega</string>
</property>
<property name="checkState">
<enum>Unchecked</enum>
</property>
</item>
<item>
<property name="text">
<string>Seta</string>
</property>
<property name="checkState">
<enum>Unchecked</enum>
</property>
</item>
<item>
<property name="text">
<string>Taito</string>
</property>
<property name="checkState">
<enum>Unchecked</enum>
</property>
</item>
<item>
<property name="text">
<string>Technos</string>
</property>
<property name="checkState">
<enum>Unchecked</enum>
</property>
</item>
<item>
<property name="text">
<string>Toaplan</string>
</property>
<property name="checkState">
<enum>Unchecked</enum>
</property>
</item>
<item>
<property name="text">
<string>Misc (pre 90s)</string>
</property>
<property name="checkState">
<enum>Unchecked</enum>
</property>
</item>
<item>
<property name="text">
<string>Misc (post 90s)</string>
</property>
<property name="checkState">
<enum>Unchecked</enum>
</property>
</item>
<item>
<property name="text">
<string>Master System</string>
</property>
<property name="checkState">
<enum>Unchecked</enum>
</property>
</item>
<item>
<property name="text">
<string>Megadrive</string>
</property>
<property name="checkState">
<enum>Unchecked</enum>
</property>
</item>
<item>
<property name="text">
<string>PC-Engine</string>
</property>
<property name="checkState">
<enum>Unchecked</enum>
</property>
</item>
<item>
<property name="text">
<string>SNES</string>
</property>
<property name="checkState">
<enum>Unchecked</enum>
</property>
</item>
</item>
</item>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLabel" name="label_6">
<property name="text">
<string>Search</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="leSearch"/>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="btnCancel">
<property name="text">
<string>Cancel</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnPlay">
<property name="text">
<string>Play</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</widget>
<resources>
<include location="rscr.qrc"/>
</resources>
<connections>
<connection>
<sender>btnCancel</sender>
<signal>clicked()</signal>
<receiver>SelectDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>601</x>
<y>558</y>
</hint>
<hint type="destinationlabel">
<x>545</x>
<y>555</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -0,0 +1,52 @@
// StringSet C++ class
#include "burner.h"
int __cdecl StringSet::Add(TCHAR* szFormat,...)
{
TCHAR szAdd[256];
int nAddLen = 0;
TCHAR* NewMem;
va_list Arg;
va_start(Arg, szFormat);
vsprintf(szAdd, szFormat, Arg);
nAddLen = _tcslen(szAdd); // find out the length of the new text
NewMem = (TCHAR*)realloc(szText, (nLen + nAddLen + 1) * sizeof(TCHAR));
if (NewMem) {
szText = NewMem;
// copy the new text to the end
_tcsncpy(szText + nLen, szAdd, nAddLen);
nLen += nAddLen;
szText[nLen] = 0; // zero-terminate
}
va_end(Arg);
return 0;
}
int StringSet::Reset()
{
// Reset the text
nLen = 0;
szText= (TCHAR*)realloc(szText, sizeof(TCHAR));
if (szText == NULL) {
return 1;
}
szText[0] = 0;
return 0;
}
StringSet::StringSet()
{
szText = NULL;
nLen = 0;
Reset(); // reset string to nothing
}
StringSet::~StringSet()
{
realloc(szText, 0); // Free BZip text
}

View File

@ -0,0 +1,54 @@
#include "supportdirsdialog.h"
#include "ui_supportdirsdialog.h"
#include "burner.h"
TCHAR szAppHiscorePath[MAX_PATH] = _T("support/hiscores/");
TCHAR szAppSamplesPath[MAX_PATH] = _T("support/samples/");
TCHAR szAppCheatsPath[MAX_PATH] = _T("support/cheats/");
TCHAR szAppPreviewsPath[MAX_PATH] = _T("support/previews/");
TCHAR szAppTitlesPath[MAX_PATH] = _T("support/titles/");
SupportDirsDialog::SupportDirsDialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::SupportDirsDialog)
{
ui->setupUi(this);
setWindowTitle(tr("Edit support paths"));
setFixedHeight(height());
m_group = new QButtonGroup(this);
m_handlers[PATH_PREVIEWS] = util::PathHandler { szAppPreviewsPath, ui->lePreviews, PATH_PREVIEWS };
m_handlers[PATH_TITLES] = util::PathHandler { szAppTitlesPath, ui->leTitles, PATH_TITLES };
m_handlers[PATH_HISCORES] = util::PathHandler { szAppHiscorePath, ui->leHiscores, PATH_HISCORES };
m_handlers[PATH_SAMPLES] = util::PathHandler { szAppSamplesPath, ui->leSamples, PATH_SAMPLES };
m_handlers[PATH_CHEATS] = util::PathHandler { szAppCheatsPath, ui->leCheats, PATH_CHEATS };
m_group->addButton(ui->btnPreviews, PATH_PREVIEWS);
m_group->addButton(ui->btnTitles, PATH_TITLES);
m_group->addButton(ui->btnHiscores, PATH_HISCORES);
m_group->addButton(ui->btnSamples, PATH_SAMPLES);
m_group->addButton(ui->btnCheats, PATH_CHEATS);
connect(m_group, SIGNAL(buttonClicked(int)), this, SLOT(editPath(int)));
}
SupportDirsDialog::~SupportDirsDialog()
{
delete ui;
}
void SupportDirsDialog::editPath(int no)
{
m_handlers[no].browse(this);
}
int SupportDirsDialog::exec()
{
for (int i = 0; i < PATH_MAX_HANDLERS; i++)
m_handlers[i].stringToEditor();
if (QDialog::exec() == QDialog::Accepted) {
for (int i = 0; i < PATH_MAX_HANDLERS; i++)
m_handlers[i].editorToString();
}
}

View File

@ -0,0 +1,40 @@
#ifndef SUPPORTDIRSDIALOG_H
#define SUPPORTDIRSDIALOG_H
#include <QDialog>
#include <QButtonGroup>
#include <QLineEdit>
#include "qutil.h"
namespace Ui {
class SupportDirsDialog;
}
class SupportDirsDialog : public QDialog
{
Q_OBJECT
public:
explicit SupportDirsDialog(QWidget *parent = 0);
~SupportDirsDialog();
public slots:
void editPath(int no);
int exec();
private:
Ui::SupportDirsDialog *ui;
QButtonGroup *m_group;
enum {
PATH_PREVIEWS = 0,
PATH_TITLES,
PATH_HISCORES,
PATH_SAMPLES,
PATH_CHEATS,
PATH_MAX_HANDLERS
};
util::PathHandler m_handlers[PATH_MAX_HANDLERS];
};
#endif // SUPPORTDIRSDIALOG_H

View File

@ -0,0 +1,199 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>SupportDirsDialog</class>
<widget class="QDialog" name="SupportDirsDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>576</width>
<height>275</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>Dialog</string>
</property>
<property name="modal">
<bool>true</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Previews</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="lePreviews"/>
</item>
<item>
<widget class="QPushButton" name="btnPreviews">
<property name="text">
<string>Browse</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>Titles</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="leTitles"/>
</item>
<item>
<widget class="QPushButton" name="btnTitles">
<property name="text">
<string>Browse</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QLabel" name="label_3">
<property name="text">
<string>Hiscores</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="leHiscores"/>
</item>
<item>
<widget class="QPushButton" name="btnHiscores">
<property name="text">
<string>Browse</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<widget class="QLabel" name="label_4">
<property name="text">
<string>Cheats</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="leCheats"/>
</item>
<item>
<widget class="QPushButton" name="btnCheats">
<property name="text">
<string>Browse</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_6">
<item>
<widget class="QLabel" name="label_5">
<property name="text">
<string>Samples</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="leSamples"/>
</item>
<item>
<widget class="QPushButton" name="btnSamples">
<property name="text">
<string>Browse</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="btnCancel">
<property name="text">
<string>Cancel</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnOk">
<property name="text">
<string>Ok</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>btnCancel</sender>
<signal>clicked()</signal>
<receiver>SupportDirsDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>238</x>
<y>102</y>
</hint>
<hint type="destinationlabel">
<x>97</x>
<y>102</y>
</hint>
</hints>
</connection>
<connection>
<sender>btnOk</sender>
<signal>clicked()</signal>
<receiver>SupportDirsDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>287</x>
<y>104</y>
</hint>
<hint type="destinationlabel">
<x>276</x>
<y>116</y>
</hint>
</hints>
</connection>
</connections>
</ui>

65
src/burner/qt/tchar.h Normal file
View File

@ -0,0 +1,65 @@
#ifndef __PORT_TYPEDEFS_H
#define __PORT_TYPEDEFS_H
#include <stdint.h>
#include <wchar.h>
#include "inp_keys.h"
#define TCHAR char
#define _T(x) x
#define _tfopen fopen
#define _tcstol strtol
#define _tcsstr strstr
#define _istspace(x) isspace(x)
#define _stprintf sprintf
#define _tcslen strlen
#define _tcsicmp(a, b) strcasecmp(a, b)
#define _tcscpy(to, from) strcpy(to, from)
#define _fgetts fgets
#define _strnicmp(s1, s2, n) strncasecmp(s1, s2, n)
#define _sntprintf snprintf
#define _tcscmp strcmp
#define _tcsncmp strncmp
#define _tcsncpy strncpy
#define _stscanf sscanf
#define _ftprintf fprintf
#ifdef _MSC_VER
#include <tchar.h>
#define strncasecmp(s1, s2, n) _strnicmp(s1, s2, n)
#define strcasecmp(x, y) _stricmp(x, y)
#define snprintf _snprintf
#else
#define _stricmp(x, y) strcasecmp(x,y)
typedef struct { int x, y, width, height; } RECT;
#undef __cdecl
#define __cdecl
#define bprintf(...) {}
#endif
#undef __fastcall
#undef _fastcall
#define __fastcall /*what does this correspond to?*/
#define _fastcall /*same as above - what does this correspond to?*/
#define ANSIToTCHAR(str, foo, bar) (str)
/* for Windows / Xbox 360 (below VS2010) - typedefs for missing stdint.h types such as uintptr_t?*/
/*FBA defines*/
#define PUF_TEXT_NO_TRANSLATE (0)
#define PUF_TYPE_ERROR (1)
extern TCHAR szAppBurnVer[16];
typedef int HWND;
extern int bDrvOkay;
extern int bRunPause;
extern bool bAlwaysProcessKeyboardInput;
extern HWND hScrnWnd; // Handle to the screen window
extern void InpDIPSWResetDIPs (void);
#endif

122
src/dep/libs/nall/Makefile Executable file
View File

@ -0,0 +1,122 @@
# Makefile
# author: byuu
# license: public domain
[A-Z] = A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
[a-z] = a b c d e f g h i j k l m n o p q r s t u v w x y z
[0-9] = 0 1 2 3 4 5 6 7 8 9
[markup] = ` ~ ! @ \# $$ % ^ & * ( ) - _ = + [ { ] } \ | ; : ' " , < . > / ?
[all] = $([A-Z]) $([a-z]) $([0-9]) $([markup])
[space] :=
[space] +=
# platform detection
ifeq ($(platform),)
uname := $(shell uname -s)
ifeq ($(uname),)
platform := windows
delete = del $(subst /,\,$1)
else ifneq ($(findstring Windows,$(uname)),)
platform := windows
delete = del $(subst /,\,$1)
else ifneq ($(findstring CYGWIN,$(uname)),)
platform := windows
delete = del $(subst /,\,$1)
else ifneq ($(findstring Darwin,$(uname)),)
platform := macosx
delete = rm -f $1
else ifneq ($(findstring BSD,$(uname)),)
platform := bsd
delete = rm -f $1
else
platform := linux
delete = rm -f $1
endif
endif
# compiler detection
ifeq ($(compiler),)
ifeq ($(platform),windows)
compiler := g++
flags :=
link :=
else ifeq ($(platform),macosx)
compiler := clang++
flags := -w -stdlib=libc++
link := -lc++ -lobjc
else ifeq ($(platform),bsd)
compiler := clang++
flags := -w -I/usr/local/include
else
compiler := g++
flags :=
link :=
endif
cflags := -x c -std=c99
objcflags := -x objective-c -std=c99
cppflags := -x c++ -std=c++11
objcppflags := -x objective-c++ -std=c++11
endif
# cross-compilation support
ifeq ($(arch),x86)
flags := -m32 $(flags)
link := -m32 $(link)
endif
ifeq ($(prefix),)
prefix := /usr/local
endif
# function rwildcard(directory, pattern)
rwildcard = \
$(strip \
$(filter $(if $2,$2,%), \
$(foreach f, \
$(wildcard $1*), \
$(eval t = $(call rwildcard,$f/)) \
$(if $t,$t,$f) \
) \
) \
)
# function strtr(source, from, to)
strtr = \
$(eval __temp := $1) \
$(strip \
$(foreach c, \
$(join $(addsuffix :,$2),$3), \
$(eval __temp := \
$(subst $(word 1,$(subst :, ,$c)),$(word 2,$(subst :, ,$c)),$(__temp)) \
) \
) \
$(__temp) \
)
# function strupper(source)
strupper = $(call strtr,$1,$([a-z]),$([A-Z]))
# function strlower(source)
strlower = $(call strtr,$1,$([A-Z]),$([a-z]))
# function strlen(source)
strlen = \
$(eval __temp := $(subst $([space]),_,$1)) \
$(words \
$(strip \
$(foreach c, \
$([all]), \
$(eval __temp := \
$(subst $c,$c ,$(__temp)) \
) \
) \
$(__temp) \
) \
)
# function streq(source)
streq = $(if $(filter-out xx,x$(subst $1,,$2)$(subst $2,,$1)x),,1)
# function strne(source)
strne = $(if $(filter-out xx,x$(subst $1,,$2)$(subst $2,,$1)x),1,)

19
src/dep/libs/nall/algorithm.hpp Executable file
View File

@ -0,0 +1,19 @@
#ifndef NALL_ALGORITHM_HPP
#define NALL_ALGORITHM_HPP
#undef min
#undef max
namespace nall {
template<typename T, typename U> T min(const T& t, const U& u) {
return t < u ? t : u;
}
template<typename T, typename U> T max(const T& t, const U& u) {
return t > u ? t : u;
}
}
#endif

Some files were not shown because too many files have changed in this diff Show More