diff --git a/projectfiles/qtcreator/fba_qt.pro b/projectfiles/qtcreator/fba_qt.pro new file mode 100644 index 000000000..1a21b8416 --- /dev/null +++ b/projectfiles/qtcreator/fba_qt.pro @@ -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 diff --git a/src/burner/burner.h b/src/burner/burner.h index 5c2d93f37..b476e5576 100644 --- a/src/burner/burner.h +++ b/src/burner/burner.h @@ -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 #include #include @@ -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) diff --git a/src/burner/qt/aboutdialog.cpp b/src/burner/qt/aboutdialog.cpp new file mode 100644 index 000000000..0e1f8c735 --- /dev/null +++ b/src/burner/qt/aboutdialog.cpp @@ -0,0 +1,21 @@ +#include +#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; +} diff --git a/src/burner/qt/aboutdialog.h b/src/burner/qt/aboutdialog.h new file mode 100644 index 000000000..d462de289 --- /dev/null +++ b/src/burner/qt/aboutdialog.h @@ -0,0 +1,22 @@ +#ifndef ABOUTDIALOG_H +#define ABOUTDIALOG_H + +#include + +namespace Ui { +class AboutDialog; +} + +class AboutDialog : public QDialog +{ + Q_OBJECT + +public: + explicit AboutDialog(QWidget *parent = 0); + ~AboutDialog(); + +private: + Ui::AboutDialog *ui; +}; + +#endif // ABOUTDIALOG_H diff --git a/src/burner/qt/aboutdialog.ui b/src/burner/qt/aboutdialog.ui new file mode 100644 index 000000000..075e9c0a7 --- /dev/null +++ b/src/burner/qt/aboutdialog.ui @@ -0,0 +1,118 @@ + + + AboutDialog + + + + 0 + 0 + 645 + 350 + + + + About + + + + + + QFrame::StyledPanel + + + + + + :/resource/about.bmp + + + true + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + License + + + true + + + + + + + Ok + + + + + + + + + + 8 + + + + true + + + + + + + + + + + btnOk + clicked() + AboutDialog + accept() + + + 293 + 178 + + + 488 + 177 + + + + + btnLicense + toggled(bool) + teLicense + setVisible(bool) + + + 167 + 182 + + + 154 + 259 + + + + + diff --git a/src/burner/qt/burner_qt.h b/src/burner/qt/burner_qt.h new file mode 100644 index 000000000..90051c858 --- /dev/null +++ b/src/burner/qt/burner_qt.h @@ -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 diff --git a/src/burner/qt/bzip.cpp b/src/burner/qt/bzip.cpp new file mode 100644 index 000000000..d512d7628 --- /dev/null +++ b/src/burner/qt/bzip.cpp @@ -0,0 +1,518 @@ +// Burner Zip module +#include "burner.h" + +#include + +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; +} diff --git a/src/burner/qt/dipswitchdialog.cpp b/src/burner/qt/dipswitchdialog.cpp new file mode 100644 index 000000000..a362b0719 --- /dev/null +++ b/src/burner/qt/dipswitchdialog.cpp @@ -0,0 +1,188 @@ +#include +#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); +} diff --git a/src/burner/qt/dipswitchdialog.h b/src/burner/qt/dipswitchdialog.h new file mode 100644 index 000000000..9a595b4de --- /dev/null +++ b/src/burner/qt/dipswitchdialog.h @@ -0,0 +1,34 @@ +#ifndef DIPSWITCHDIALOG_H +#define DIPSWITCHDIALOG_H + +#include +#include + +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 diff --git a/src/burner/qt/dipswitchdialog.ui b/src/burner/qt/dipswitchdialog.ui new file mode 100644 index 000000000..7eaac4cc0 --- /dev/null +++ b/src/burner/qt/dipswitchdialog.ui @@ -0,0 +1,96 @@ + + + DipswitchDialog + + + + 0 + 0 + 555 + 374 + + + + Dialog + + + + + + QAbstractScrollArea::AdjustToContents + + + true + + + + DIPSwitch + + + + + Setting + + + + + + + + + + + + + Default + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Ok + + + + + + + + + + + btnOk + clicked() + DipswitchDialog + accept() + + + 477 + 355 + + + 290 + 359 + + + + + diff --git a/src/burner/qt/driver.cpp b/src/burner/qt/driver.cpp new file mode 100644 index 000000000..e5506823b --- /dev/null +++ b/src/burner/qt/driver.cpp @@ -0,0 +1,122 @@ +// Driver Init module +#include "burner.h" +#include + +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; +} + diff --git a/src/burner/qt/emuworker.cpp b/src/burner/qt/emuworker.cpp new file mode 100644 index 000000000..d7eb4e095 --- /dev/null +++ b/src/burner/qt/emuworker.cpp @@ -0,0 +1,128 @@ +#include +#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; +} diff --git a/src/burner/qt/emuworker.h b/src/burner/qt/emuworker.h new file mode 100644 index 000000000..9f44f21ca --- /dev/null +++ b/src/burner/qt/emuworker.h @@ -0,0 +1,26 @@ +#ifndef EMUWORKER_H +#define EMUWORKER_H + +#include + +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 diff --git a/src/burner/qt/main.cpp b/src/burner/qt/main.cpp new file mode 100644 index 000000000..7fbdf3a44 --- /dev/null +++ b/src/burner/qt/main.cpp @@ -0,0 +1,63 @@ +#include +#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); +} diff --git a/src/burner/qt/mainwindow.cpp b/src/burner/qt/mainwindow.cpp new file mode 100644 index 000000000..ae4e1fa05 --- /dev/null +++ b/src/burner/qt/mainwindow.cpp @@ -0,0 +1,240 @@ +#include +#ifdef Q_OS_MACX +#include +#else +#include +#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::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*))); +} diff --git a/src/burner/qt/mainwindow.h b/src/burner/qt/mainwindow.h new file mode 100644 index 000000000..fb392b92b --- /dev/null +++ b/src/burner/qt/mainwindow.h @@ -0,0 +1,78 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include +#include +#include +#include +#include +#include +#include +#include +#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 m_inputInterfaces; + QActionGroup *m_actionInputPlugins; + bool m_closeApp; +}; + +#endif // MAINWINDOW_H diff --git a/src/burner/qt/neocdlist.cpp b/src/burner/qt/neocdlist.cpp new file mode 100644 index 000000000..6899fc974 --- /dev/null +++ b/src/burner/qt/neocdlist.cpp @@ -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; + } +} diff --git a/src/burner/qt/progress.cpp b/src/burner/qt/progress.cpp new file mode 100644 index 000000000..00c4f0b08 --- /dev/null +++ b/src/burner/qt/progress.cpp @@ -0,0 +1,42 @@ +#include +#include +#include +#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; +} diff --git a/src/burner/qt/qaudiointerface.cpp b/src/burner/qt/qaudiointerface.cpp new file mode 100644 index 000000000..6af861a71 --- /dev/null +++ b/src/burner/qt/qaudiointerface.cpp @@ -0,0 +1,375 @@ +#include +#include +#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; +} diff --git a/src/burner/qt/qaudiointerface.h b/src/burner/qt/qaudiointerface.h new file mode 100644 index 000000000..ddefe5e3c --- /dev/null +++ b/src/burner/qt/qaudiointerface.h @@ -0,0 +1,64 @@ +#ifndef QAUDIOINTERFACE_H +#define QAUDIOINTERFACE_H + +#include +#include +#include +#include +#include +#include + +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 diff --git a/src/burner/qt/qinputinterface.cpp b/src/burner/qt/qinputinterface.cpp new file mode 100644 index 000000000..04200b5eb --- /dev/null +++ b/src/burner/qt/qinputinterface.cpp @@ -0,0 +1,282 @@ +#include +#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(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(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; +} diff --git a/src/burner/qt/qinputinterface.h b/src/burner/qt/qinputinterface.h new file mode 100644 index 000000000..a9432fac9 --- /dev/null +++ b/src/burner/qt/qinputinterface.h @@ -0,0 +1,30 @@ +#ifndef QINPUTINTERFACE_H +#define QINPUTINTERFACE_H + +#include +#include + +#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 diff --git a/src/burner/qt/qrubyviewport.cpp b/src/burner/qt/qrubyviewport.cpp new file mode 100644 index 000000000..0b24f2ba9 --- /dev/null +++ b/src/burner/qt/qrubyviewport.cpp @@ -0,0 +1,144 @@ +#include +#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(); +} diff --git a/src/burner/qt/qrubyviewport.h b/src/burner/qt/qrubyviewport.h new file mode 100644 index 000000000..74a900fc9 --- /dev/null +++ b/src/burner/qt/qrubyviewport.h @@ -0,0 +1,18 @@ +#ifndef QRUBYVIEWPORT_H +#define QRUBYVIEWPORT_H + +#include +#include + +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 diff --git a/src/burner/qt/qutil.cpp b/src/burner/qt/qutil.cpp new file mode 100644 index 000000000..89675f095 --- /dev/null +++ b/src/burner/qt/qutil.cpp @@ -0,0 +1,54 @@ +#include +#include +#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(); +} + +} diff --git a/src/burner/qt/qutil.h b/src/burner/qt/qutil.h new file mode 100644 index 000000000..8e051be82 --- /dev/null +++ b/src/burner/qt/qutil.h @@ -0,0 +1,30 @@ +#ifndef QUTIL_H +#define QUTIL_H + +#include +#include +#include +#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 diff --git a/src/burner/qt/resource/about.bmp b/src/burner/qt/resource/about.bmp new file mode 100644 index 000000000..938ae09a0 Binary files /dev/null and b/src/burner/qt/resource/about.bmp differ diff --git a/src/burner/qt/resource/license.txt b/src/burner/qt/resource/license.txt new file mode 100644 index 000000000..2d75f81d2 --- /dev/null +++ b/src/burner/qt/resource/license.txt @@ -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" diff --git a/src/burner/qt/resource/misc.bmp b/src/burner/qt/resource/misc.bmp new file mode 100644 index 000000000..408da009c Binary files /dev/null and b/src/burner/qt/resource/misc.bmp differ diff --git a/src/burner/qt/resource/splash.bmp b/src/burner/qt/resource/splash.bmp new file mode 100644 index 000000000..1690a818d Binary files /dev/null and b/src/burner/qt/resource/splash.bmp differ diff --git a/src/burner/qt/resource/tv-not-found-non-essential.ico b/src/burner/qt/resource/tv-not-found-non-essential.ico new file mode 100644 index 000000000..367cb47cf Binary files /dev/null and b/src/burner/qt/resource/tv-not-found-non-essential.ico differ diff --git a/src/burner/qt/resource/tv-not-found.ico b/src/burner/qt/resource/tv-not-found.ico new file mode 100644 index 000000000..9cfa60df9 Binary files /dev/null and b/src/burner/qt/resource/tv-not-found.ico differ diff --git a/src/burner/qt/resource/tv-not-working.ico b/src/burner/qt/resource/tv-not-working.ico new file mode 100644 index 000000000..9149eeba3 Binary files /dev/null and b/src/burner/qt/resource/tv-not-working.ico differ diff --git a/src/burner/qt/romdirsdialog.cpp b/src/burner/qt/romdirsdialog.cpp new file mode 100644 index 000000000..9f39e2255 --- /dev/null +++ b/src/burner/qt/romdirsdialog.cpp @@ -0,0 +1,48 @@ +#include +#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); +} diff --git a/src/burner/qt/romdirsdialog.h b/src/burner/qt/romdirsdialog.h new file mode 100644 index 000000000..359c73965 --- /dev/null +++ b/src/burner/qt/romdirsdialog.h @@ -0,0 +1,32 @@ +#ifndef ROMDIRSDIALOG_H +#define ROMDIRSDIALOG_H + +#include +#include +#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 diff --git a/src/burner/qt/romdirsdialog.ui b/src/burner/qt/romdirsdialog.ui new file mode 100644 index 000000000..1f2b2a2f2 --- /dev/null +++ b/src/burner/qt/romdirsdialog.ui @@ -0,0 +1,188 @@ + + + RomDirsDialog + + + + 0 + 0 + 510 + 244 + + + + Dialog + + + true + + + + + + + + Path #1 + + + + + + + true + + + + + + + Browse + + + + + + + + + + + Path #2 + + + + + + + true + + + + + + + Browse + + + + + + + + + + + Path #3 + + + + + + + true + + + + + + + Browse + + + + + + + + + + + Path #4 + + + + + + + true + + + + + + + Browse + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Cancel + + + + + + + Ok + + + + + + + + + + + btnCancel + clicked() + RomDirsDialog + reject() + + + 239 + 192 + + + 160 + 198 + + + + + btnOk + clicked() + RomDirsDialog + accept() + + + 355 + 197 + + + 316 + 217 + + + + + diff --git a/src/burner/qt/rominfodialog.cpp b/src/burner/qt/rominfodialog.cpp new file mode 100644 index 000000000..6861a5e1c --- /dev/null +++ b/src/burner/qt/rominfodialog.cpp @@ -0,0 +1,74 @@ +#include +#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(); +} diff --git a/src/burner/qt/rominfodialog.h b/src/burner/qt/rominfodialog.h new file mode 100644 index 000000000..1518ef3e5 --- /dev/null +++ b/src/burner/qt/rominfodialog.h @@ -0,0 +1,25 @@ +#ifndef ROMINFODIALOG_H +#define ROMINFODIALOG_H + +#include + +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 diff --git a/src/burner/qt/rominfodialog.ui b/src/burner/qt/rominfodialog.ui new file mode 100644 index 000000000..1984b5a26 --- /dev/null +++ b/src/burner/qt/rominfodialog.ui @@ -0,0 +1,121 @@ + + + RomInfoDialog + + + + 0 + 0 + 650 + 358 + + + + Dialog + + + + + + 0 + + + + Rom Info + + + + + + + Name + + + + + Size (bytes) + + + + + CRC32 + + + + + Type + + + + + Flags + + + + + + + + + Sample Info + + + + + + + Name + + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Close + + + + + + + + + + + btnClose + clicked() + RomInfoDialog + accept() + + + 604 + 332 + + + 514 + 334 + + + + + diff --git a/src/burner/qt/romscandialog.cpp b/src/burner/qt/romscandialog.cpp new file mode 100644 index 000000000..753ac68ce --- /dev/null +++ b/src/burner/qt/romscandialog.cpp @@ -0,0 +1,102 @@ +#include +#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 &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; +} diff --git a/src/burner/qt/romscandialog.h b/src/burner/qt/romscandialog.h new file mode 100644 index 000000000..658c84c0d --- /dev/null +++ b/src/burner/qt/romscandialog.h @@ -0,0 +1,47 @@ +#ifndef ROMSCANDIALOG_H +#define ROMSCANDIALOG_H + +#include +#include +#include + +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 m_status; + RomAnalyzer m_analyzer; +}; + +#endif // ROMSCANDIALOG_H diff --git a/src/burner/qt/romscandialog.ui b/src/burner/qt/romscandialog.ui new file mode 100644 index 000000000..c89648e5c --- /dev/null +++ b/src/burner/qt/romscandialog.ui @@ -0,0 +1,87 @@ + + + RomScanDialog + + + Qt::NonModal + + + + 0 + 0 + 373 + 80 + + + + Dialog + + + false + + + + + + QFrame::Box + + + QFrame::Sunken + + + + + + :/resource/misc.bmp + + + + + + + + + 0 + + + + + + + + + Scanning ROMs... + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Cancel + + + + + + + + + + + + + + diff --git a/src/burner/qt/rscr.qrc b/src/burner/qt/rscr.qrc new file mode 100644 index 000000000..3f0dc0019 --- /dev/null +++ b/src/burner/qt/rscr.qrc @@ -0,0 +1,11 @@ + + + resource/splash.bmp + resource/tv-not-working.ico + resource/tv-not-found.ico + resource/tv-not-found-non-essential.ico + resource/misc.bmp + resource/about.bmp + resource/license.txt + + diff --git a/src/burner/qt/ruby/Makefile b/src/burner/qt/ruby/Makefile new file mode 100755 index 000000000..8a68983d1 --- /dev/null +++ b/src/burner/qt/ruby/Makefile @@ -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 diff --git a/src/burner/qt/ruby/audio.hpp b/src/burner/qt/ruby/audio.hpp new file mode 100755 index 000000000..9d8603e35 --- /dev/null +++ b/src/burner/qt/ruby/audio.hpp @@ -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() {} +}; diff --git a/src/burner/qt/ruby/audio/alsa.cpp b/src/burner/qt/ruby/audio/alsa.cpp new file mode 100755 index 000000000..0c37a5fb7 --- /dev/null +++ b/src/burner/qt/ruby/audio/alsa.cpp @@ -0,0 +1,240 @@ +//audio.alsa (2009-11-30) +//authors: BearOso, byuu, Nach, RedDwarf + +#include + +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(value)) { + settings.synchronize = any_cast(value); + if(device.handle) init(); + } + return true; + } + + if(name == Audio::Frequency) { + if(settings.frequency != any_cast(value)) { + settings.frequency = any_cast(value); + if(device.handle) init(); + } + return true; + } + + if(name == Audio::Latency) { + if(settings.latency != any_cast(value)) { + settings.latency = any_cast(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) + +}; diff --git a/src/burner/qt/ruby/audio/ao.cpp b/src/burner/qt/ruby/audio/ao.cpp new file mode 100755 index 000000000..32bee07d7 --- /dev/null +++ b/src/burner/qt/ruby/audio/ao.cpp @@ -0,0 +1,94 @@ +/* + audio.ao (2008-06-01) + authors: Nach, RedDwarf +*/ + +#include + +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(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) + +}; diff --git a/src/burner/qt/ruby/audio/directsound.cpp b/src/burner/qt/ruby/audio/directsound.cpp new file mode 100755 index 000000000..9da869bc4 --- /dev/null +++ b/src/burner/qt/ruby/audio/directsound.cpp @@ -0,0 +1,209 @@ +/* + audio.directsound (2007-12-26) + author: byuu +*/ + +#include + +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(value); + return true; + } + + if(name == Audio::Synchronize) { + settings.synchronize = any_cast(value); + if(ds) clear(); + return true; + } + + if(name == Audio::Frequency) { + settings.frequency = any_cast(value); + if(ds) init(); + return true; + } + + if(name == Audio::Latency) { + settings.latency = any_cast(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) + +}; diff --git a/src/burner/qt/ruby/audio/openal.cpp b/src/burner/qt/ruby/audio/openal.cpp new file mode 100755 index 000000000..af478be0e --- /dev/null +++ b/src/burner/qt/ruby/audio/openal.cpp @@ -0,0 +1,210 @@ +/* + audio.openal (2007-12-26) + author: Nach + contributors: byuu, wertigon, _willow_ +*/ + +#if defined(PLATFORM_MACOSX) + #include + #include +#else + #include + #include +#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(value); + return true; + } + + if(name == Audio::Frequency) { + settings.frequency = any_cast(value); + return true; + } + + if(name == Audio::Latency) { + if(settings.latency != any_cast(value)) { + settings.latency = any_cast(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) + +}; diff --git a/src/burner/qt/ruby/audio/oss.cpp b/src/burner/qt/ruby/audio/oss.cpp new file mode 100755 index 000000000..2c77b6aed --- /dev/null +++ b/src/burner/qt/ruby/audio/oss.cpp @@ -0,0 +1,113 @@ +/* + audio.oss (2007-12-26) + author: Nach +*/ + +#include +#include +#include +#include + +//OSS4 soundcard.h includes below SNDCTL defines, but OSS3 does not +//However, OSS4 soundcard.h does not reside in +//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(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) + +}; diff --git a/src/burner/qt/ruby/audio/pulseaudio.cpp b/src/burner/qt/ruby/audio/pulseaudio.cpp new file mode 100755 index 000000000..e248bccf7 --- /dev/null +++ b/src/burner/qt/ruby/audio/pulseaudio.cpp @@ -0,0 +1,177 @@ +//audio.pulseaudio (2010-01-05) +//author: RedDwarf + +#include + +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(value); + return true; + } + + if(name == Audio::Frequency) { + settings.frequency = any_cast(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(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) + +} diff --git a/src/burner/qt/ruby/audio/pulseaudiosimple.cpp b/src/burner/qt/ruby/audio/pulseaudiosimple.cpp new file mode 100755 index 000000000..5815c6d70 --- /dev/null +++ b/src/burner/qt/ruby/audio/pulseaudiosimple.cpp @@ -0,0 +1,115 @@ +//audio.pulseaudiosimple (2010-01-05) +//author: byuu + +#include +#include + +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(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) + +}; diff --git a/src/burner/qt/ruby/audio/xaudio2.cpp b/src/burner/qt/ruby/audio/xaudio2.cpp new file mode 100755 index 000000000..19add1876 --- /dev/null +++ b/src/burner/qt/ruby/audio/xaudio2.cpp @@ -0,0 +1,198 @@ +/* + audio.xaudio2 (2010-08-14) + author: OV2 +*/ + +#include "xaudio2.hpp" +#include + +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(value); + if(pXAudio2) clear(); + return true; + } + + if(name == Audio::Frequency) { + settings.frequency = any_cast(value); + if(pXAudio2) init(); + return true; + } + + if(name == Audio::Latency) { + settings.latency = any_cast(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(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) + +}; diff --git a/src/burner/qt/ruby/audio/xaudio2.hpp b/src/burner/qt/ruby/audio/xaudio2.hpp new file mode 100755 index 000000000..e283f5038 --- /dev/null +++ b/src/burner/qt/ruby/audio/xaudio2.hpp @@ -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 + +#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 diff --git a/src/burner/qt/ruby/implementation.cpp b/src/burner/qt/ruby/implementation.cpp new file mode 100755 index 000000000..f33cb33b6 --- /dev/null +++ b/src/burner/qt/ruby/implementation.cpp @@ -0,0 +1,172 @@ +/* Global Headers */ + +#if defined(PLATFORM_X) + #include + #include + #include +#elif defined(PLATFORM_MACOSX) + #define decimal CocoaDecimal + #include + #include + #undef decimal +#elif defined(PLATFORM_WINDOWS) + #define _WIN32_WINNT 0x0501 + #include +#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 +#endif + +#ifdef VIDEO_DIRECT3D + #include +#endif + +#ifdef VIDEO_DIRECTDRAW + #include +#endif + +#ifdef VIDEO_GDI + #include +#endif + +#ifdef VIDEO_GLX + #include +#endif + +#ifdef VIDEO_SDL + #include +#endif + +#ifdef VIDEO_WGL + #include +#endif + +#ifdef VIDEO_XSHM + #include +#endif + +#ifdef VIDEO_XV + #include +#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 +#endif + +#ifdef AUDIO_AO + #include +#endif + +#ifdef AUDIO_DIRECTSOUND + #include +#endif + +#ifdef AUDIO_OPENAL + #include +#endif + +#ifdef AUDIO_OSS + #include +#endif + +#ifdef AUDIO_PULSEAUDIO + #include +#endif + +#ifdef AUDIO_PULSEAUDIOSIMPLE + #include +#endif + +#ifdef AUDIO_XAUDIO2 + #include +#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 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 +#endif + +#ifdef INPUT_SDL + #include +#endif + +#ifdef INPUT_UDEV + #include +#endif + +#ifdef INPUT_WINDOWS + #include +#endif + +#ifdef INPUT_XLIB + #include +#endif diff --git a/src/burner/qt/ruby/input.hpp b/src/burner/qt/ruby/input.hpp new file mode 100755 index 000000000..2582ccd28 --- /dev/null +++ b/src/burner/qt/ruby/input.hpp @@ -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 poll() { return {}; } + virtual bool rumble(uint64_t id, bool enable) {} + virtual bool init() { return true; } + virtual void term() {} + + Input() {} + virtual ~Input() {} +}; diff --git a/src/burner/qt/ruby/input/carbon.cpp b/src/burner/qt/ruby/input/carbon.cpp new file mode 100755 index 000000000..2ca5f55e4 --- /dev/null +++ b/src/burner/qt/ruby/input/carbon.cpp @@ -0,0 +1,182 @@ +namespace ruby { + +struct pInputCarbon { + struct Key { + uint8_t id; + string name; + }; + vector 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& 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) + +}; diff --git a/src/burner/qt/ruby/input/joypad/directinput.cpp b/src/burner/qt/ruby/input/joypad/directinput.cpp new file mode 100755 index 000000000..d88378952 --- /dev/null +++ b/src/burner/qt/ruby/input/joypad/directinput.cpp @@ -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 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& 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 diff --git a/src/burner/qt/ruby/input/joypad/sdl.cpp b/src/burner/qt/ruby/input/joypad/sdl.cpp new file mode 100755 index 000000000..fbe1d89f3 --- /dev/null +++ b/src/burner/qt/ruby/input/joypad/sdl.cpp @@ -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 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& 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 diff --git a/src/burner/qt/ruby/input/joypad/udev.cpp b/src/burner/qt/ruby/input/joypad/udev.cpp new file mode 100755 index 000000000..a0674d8e5 --- /dev/null +++ b/src/burner/qt/ruby/input/joypad/udev.cpp @@ -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 axes; + set hats; + set buttons; + bool rumble = false; + unsigned effectID = 0; + }; + vector 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& 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 diff --git a/src/burner/qt/ruby/input/joypad/xinput.cpp b/src/burner/qt/ruby/input/joypad/xinput.cpp new file mode 100755 index 000000000..bc058066b --- /dev/null +++ b/src/burner/qt/ruby/input/joypad/xinput.cpp @@ -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 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& 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 diff --git a/src/burner/qt/ruby/input/keyboard/rawinput.cpp b/src/burner/qt/ruby/input/keyboard/rawinput.cpp new file mode 100755 index 000000000..ace90547b --- /dev/null +++ b/src/burner/qt/ruby/input/keyboard/rawinput.cpp @@ -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 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& 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 diff --git a/src/burner/qt/ruby/input/keyboard/xlib.cpp b/src/burner/qt/ruby/input/keyboard/xlib.cpp new file mode 100755 index 000000000..002e57eaa --- /dev/null +++ b/src/burner/qt/ruby/input/keyboard/xlib.cpp @@ -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 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& 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 diff --git a/src/burner/qt/ruby/input/mouse/rawinput.cpp b/src/burner/qt/ruby/input/mouse/rawinput.cpp new file mode 100755 index 000000000..e118753aa --- /dev/null +++ b/src/burner/qt/ruby/input/mouse/rawinput.cpp @@ -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& 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 diff --git a/src/burner/qt/ruby/input/mouse/xlib.cpp b/src/burner/qt/ruby/input/mouse/xlib.cpp new file mode 100755 index 000000000..2def3a3f3 --- /dev/null +++ b/src/burner/qt/ruby/input/mouse/xlib.cpp @@ -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& 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 diff --git a/src/burner/qt/ruby/input/sdl.cpp b/src/burner/qt/ruby/input/sdl.cpp new file mode 100755 index 000000000..3a6ff3395 --- /dev/null +++ b/src/burner/qt/ruby/input/sdl.cpp @@ -0,0 +1,82 @@ +#include +#include +#include + +#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(value); + return true; + } + + return false; + } + + bool acquire() { + return xlibMouse.acquire(); + } + + bool unacquire() { + return xlibMouse.unacquire(); + } + + bool acquired() { + return xlibMouse.acquired(); + } + + vector poll() { + vector 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) + +} diff --git a/src/burner/qt/ruby/input/shared/rawinput.cpp b/src/burner/qt/ruby/input/shared/rawinput.cpp new file mode 100755 index 000000000..8e7a9cd8f --- /dev/null +++ b/src/burner/qt/ruby/input/shared/rawinput.cpp @@ -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 updateKeyboard; + function 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 devices; + + optional 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 diff --git a/src/burner/qt/ruby/input/udev.cpp b/src/burner/qt/ruby/input/udev.cpp new file mode 100755 index 000000000..a53d95d55 --- /dev/null +++ b/src/burner/qt/ruby/input/udev.cpp @@ -0,0 +1,88 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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(value); + return true; + } + return false; + } + + bool acquire() { + return xlibMouse.acquire(); + } + + bool unacquire() { + return xlibMouse.unacquire(); + } + + bool acquired() { + return xlibMouse.acquired(); + } + + vector poll() { + vector 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) + +} diff --git a/src/burner/qt/ruby/input/windows.cpp b/src/burner/qt/ruby/input/windows.cpp new file mode 100755 index 000000000..3833b3b56 --- /dev/null +++ b/src/burner/qt/ruby/input/windows.cpp @@ -0,0 +1,109 @@ +#include +#define DIRECTINPUT_VERSION 0x0800 +#include + +#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(value); + return true; + } + return false; + } + + bool acquire() { + return rawinputMouse.acquire(); + } + + bool unacquire() { + return rawinputMouse.unacquire(); + } + + bool acquired() { + return rawinputMouse.acquired(); + } + + vector poll() { + vector 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) + +} diff --git a/src/burner/qt/ruby/input/xlib.cpp b/src/burner/qt/ruby/input/xlib.cpp new file mode 100755 index 000000000..8c37d4a00 --- /dev/null +++ b/src/burner/qt/ruby/input/xlib.cpp @@ -0,0 +1,77 @@ +#include +#include +#include +#include +#include + +#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(value); + return true; + } + + return false; + } + + bool acquire() { + return xlibMouse.acquire(); + } + + bool unacquire() { + return xlibMouse.unacquire(); + } + + bool acquired() { + return xlibMouse.acquired(); + } + + vector poll() { + vector 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) + +} diff --git a/src/burner/qt/ruby/ruby.cpp b/src/burner/qt/ruby/ruby.cpp new file mode 100755 index 000000000..edb5581fb --- /dev/null +++ b/src/burner/qt/ruby/ruby.cpp @@ -0,0 +1,496 @@ +#include + +#undef mkdir +#undef usleep +#include + +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 InputInterface::poll() { return p ? p->poll() : vector(); } +bool InputInterface::rumble(uint64_t id, bool enable) { return p ? p->rumble(id, enable) : false; } +InputInterface::InputInterface() : p(nullptr) {} +InputInterface::~InputInterface() { term(); } + +}; diff --git a/src/burner/qt/ruby/ruby.hpp b/src/burner/qt/ruby/ruby.hpp new file mode 100755 index 000000000..ab4215647 --- /dev/null +++ b/src/burner/qt/ruby/ruby.hpp @@ -0,0 +1,98 @@ +/* + ruby + version: 0.11 (2013-12-19) + license: public domain +*/ + +#ifndef RUBY_H +#define RUBY_H + +#include + +namespace ruby { + +#include +#include +#include + +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 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 poll(); + bool rumble(uint64_t id, bool enable); + + InputInterface(); + ~InputInterface(); + +private: + Input* p = nullptr; +}; + +extern VideoInterface video; +extern AudioInterface audio; +extern InputInterface input; + +}; + +#endif diff --git a/src/burner/qt/ruby/video.hpp b/src/burner/qt/ruby/video.hpp new file mode 100755 index 000000000..2fb685257 --- /dev/null +++ b/src/burner/qt/ruby/video.hpp @@ -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() {} +}; diff --git a/src/burner/qt/ruby/video/cgl.cpp b/src/burner/qt/ruby/video/cgl.cpp new file mode 100755 index 000000000..dca80c07a --- /dev/null +++ b/src/burner/qt/ruby/video/cgl.cpp @@ -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(value); + return true; + } + + if(name == Video::Synchronize) { + if(settings.synchronize != any_cast(value)) { + settings.synchronize = any_cast(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(value); + if(settings.shader.empty()) OpenGL::filter = settings.filter ? GL_LINEAR : GL_NEAREST; + return true; + } + + if(name == Video::Shader) { + settings.shader = any_cast(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 diff --git a/src/burner/qt/ruby/video/direct3d.cpp b/src/burner/qt/ruby/video/direct3d.cpp new file mode 100755 index 000000000..33368da69 --- /dev/null +++ b/src/burner/qt/ruby/video/direct3d.cpp @@ -0,0 +1,463 @@ +#undef interface +#define interface struct +#include +#include +#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(value); + return true; + } + + if(name == Video::Synchronize) { + settings.synchronize = any_cast(value); + return true; + } + + if(name == Video::Filter) { + settings.filter = any_cast(value); + if(lpd3d) update_filter(); + return true; + } + + if(name == Video::Shader) { + return false; + set_shader(any_cast(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 diff --git a/src/burner/qt/ruby/video/directdraw.cpp b/src/burner/qt/ruby/video/directdraw.cpp new file mode 100755 index 000000000..f7731de99 --- /dev/null +++ b/src/burner/qt/ruby/video/directdraw.cpp @@ -0,0 +1,186 @@ +#include + +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(value); + return true; + } + + if(name == Video::Synchronize) { + settings.synchronize = any_cast(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) + +}; diff --git a/src/burner/qt/ruby/video/gdi.cpp b/src/burner/qt/ruby/video/gdi.cpp new file mode 100755 index 000000000..ada01b962 --- /dev/null +++ b/src/burner/qt/ruby/video/gdi.cpp @@ -0,0 +1,100 @@ +#include + +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(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) + +}; diff --git a/src/burner/qt/ruby/video/glx.cpp b/src/burner/qt/ruby/video/glx.cpp new file mode 100755 index 000000000..3e6ff3db1 --- /dev/null +++ b/src/burner/qt/ruby/video/glx.cpp @@ -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(value); + return true; + } + + if(name == Video::Synchronize) { + if(settings.synchronize != any_cast(value)) { + settings.synchronize = any_cast(value); + if(glXSwapInterval) glXSwapInterval(settings.synchronize); + return true; + } + } + + if(name == Video::Depth) { + unsigned depth = any_cast(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(value); + if(settings.shader.empty()) OpenGL::filter = settings.filter ? GL_LINEAR : GL_NEAREST; + return true; + } + + if(name == Video::Shader) { + settings.shader = any_cast(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) + +}; diff --git a/src/burner/qt/ruby/video/opengl/bind.hpp b/src/burner/qt/ruby/video/opengl/bind.hpp new file mode 100755 index 000000000..196a627f4 --- /dev/null +++ b/src/burner/qt/ruby/video/opengl/bind.hpp @@ -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 diff --git a/src/burner/qt/ruby/video/opengl/main.hpp b/src/burner/qt/ruby/video/opengl/main.hpp new file mode 100755 index 000000000..1213645d0 --- /dev/null +++ b/src/burner/qt/ruby/video/opengl/main.hpp @@ -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 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; } +} diff --git a/src/burner/qt/ruby/video/opengl/opengl.hpp b/src/burner/qt/ruby/video/opengl/opengl.hpp new file mode 100755 index 000000000..4bbc473b2 --- /dev/null +++ b/src/burner/qt/ruby/video/opengl/opengl.hpp @@ -0,0 +1,97 @@ +#if defined(PLATFORM_X) + #include + #include + #define glGetProcAddress(name) (*glXGetProcAddress)((const GLubyte*)(name)) +#elif defined(PLATFORM_MACOSX) + #include + #include +#elif defined(PLATFORM_WINDOWS) + #include + #include + #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 pixmaps; + + void bind(OpenGL* instance, const Markup::Node& node, const string& pathname); + void parse(OpenGL* instance, string& source); + void release(); +}; + +struct OpenGL : OpenGLProgram { + vector programs; + vector 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 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" + +} diff --git a/src/burner/qt/ruby/video/opengl/program.hpp b/src/burner/qt/ruby/video/opengl/program.hpp new file mode 100755 index 000000000..21950846b --- /dev/null +++ b/src/burner/qt/ruby/video/opengl/program.hpp @@ -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; +} diff --git a/src/burner/qt/ruby/video/opengl/shaders.hpp b/src/burner/qt/ruby/video/opengl/shaders.hpp new file mode 100755 index 000000000..91f9efce6 --- /dev/null +++ b/src/burner/qt/ruby/video/opengl/shaders.hpp @@ -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); + } +)"; diff --git a/src/burner/qt/ruby/video/opengl/surface.hpp b/src/burner/qt/ruby/video/opengl/surface.hpp new file mode 100755 index 000000000..9f368dec1 --- /dev/null +++ b/src/burner/qt/ruby/video/opengl/surface.hpp @@ -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); +} diff --git a/src/burner/qt/ruby/video/opengl/texture.hpp b/src/burner/qt/ruby/video/opengl/texture.hpp new file mode 100755 index 000000000..8685bef7e --- /dev/null +++ b/src/burner/qt/ruby/video/opengl/texture.hpp @@ -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; +} diff --git a/src/burner/qt/ruby/video/opengl/utility.hpp b/src/burner/qt/ruby/video/opengl/utility.hpp new file mode 100755 index 000000000..83c8b0c21 --- /dev/null +++ b/src/burner/qt/ruby/video/opengl/utility.hpp @@ -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"); + } +} diff --git a/src/burner/qt/ruby/video/sdl.cpp b/src/burner/qt/ruby/video/sdl.cpp new file mode 100755 index 000000000..0079f87d2 --- /dev/null +++ b/src/burner/qt/ruby/video/sdl.cpp @@ -0,0 +1,141 @@ +#include +#include +#include +#include +#include +#include + +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(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) + +}; diff --git a/src/burner/qt/ruby/video/wgl.cpp b/src/burner/qt/ruby/video/wgl.cpp new file mode 100755 index 000000000..a5801f96e --- /dev/null +++ b/src/burner/qt/ruby/video/wgl.cpp @@ -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(value); + return true; + } + + if(name == Video::Synchronize) { + if(settings.synchronize != any_cast(value)) { + settings.synchronize = any_cast(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(value); + if(settings.shader.empty()) OpenGL::filter = settings.filter ? GL_LINEAR : GL_NEAREST; + return true; + } + + if(name == Video::Shader) { + settings.shader = any_cast(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) + +}; diff --git a/src/burner/qt/ruby/video/xshm.cpp b/src/burner/qt/ruby/video/xshm.cpp new file mode 100755 index 000000000..bf8a26903 --- /dev/null +++ b/src/burner/qt/ruby/video/xshm.cpp @@ -0,0 +1,228 @@ +#include +#include + +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(value); + return true; + } + if(name == Video::Depth) { + return setDepth(any_cast(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) + +} diff --git a/src/burner/qt/ruby/video/xv.cpp b/src/burner/qt/ruby/video/xv.cpp new file mode 100755 index 000000000..2a73ea1b3 --- /dev/null +++ b/src/burner/qt/ruby/video/xv.cpp @@ -0,0 +1,499 @@ +#include +#include +#include +#include +#include + +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(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(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) + +}; diff --git a/src/burner/qt/selectdialog.cpp b/src/burner/qt/selectdialog.cpp new file mode 100644 index 000000000..100d1d2b1 --- /dev/null +++ b/src/burner/qt/selectdialog.cpp @@ -0,0 +1,307 @@ +#include +#include +#include +#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(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(item); + m_selectedDriver = driver->driverNo(); + accept(); +} + +void SelectDialog::playGame() +{ + TreeDriverItem *driver = static_cast(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(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; +} diff --git a/src/burner/qt/selectdialog.h b/src/burner/qt/selectdialog.h new file mode 100644 index 000000000..8c5d44067 --- /dev/null +++ b/src/burner/qt/selectdialog.h @@ -0,0 +1,84 @@ +#ifndef SELECTDIALOG_H +#define SELECTDIALOG_H + +#include +#include +#include +#include +#include +#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 m_parents; + + bool m_showAvailable; + bool m_showUnavailable; + bool m_showClones; + int m_showCount; + + QPixmap m_defaultImage; +}; + +#endif // SELECTDIALOG_H diff --git a/src/burner/qt/selectdialog.ui b/src/burner/qt/selectdialog.ui new file mode 100644 index 000000000..a906b7c22 --- /dev/null +++ b/src/burner/qt/selectdialog.ui @@ -0,0 +1,653 @@ + + + SelectDialog + + + + 0 + 0 + 899 + 650 + + + + Dialog + + + + + + + + + + + Select Game + + + + + + + + QFormLayout::AllNonFixedFieldsGrow + + + + + Game Info + + + + + + + true + + + + + + + ROM Name + + + + + + + true + + + + + + + ROM Info + + + + + + + true + + + + + + + Released by + + + + + + + true + + + + + + + Genre + + + + + + + true + + + + + + + Notes + + + + + + + true + + + + + + + + + + + + 0 + 0 + + + + ... + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Rom Info + + + + + + + + + + + + + QLayout::SetDefaultConstraint + + + + + + 0 + 0 + + + + + 320 + 200 + + + + + 320 + 200 + + + + QFrame::StyledPanel + + + + + + :/resource/splash.bmp + + + true + + + + + + + + + Options + + + + + + + + Show avaliable + + + true + + + + + + + Show unavaliable + + + true + + + + + + + Always show clones + + + true + + + + + + + Use zipnames + + + + + + + Latin text only + + + + + + + + + ROMs Dirs... + + + + + + + Scan ROMs + + + + + + + + + + + + + + + Filters + + + + + Filters + + + Unchecked + + + ItemIsSelectable|ItemIsUserCheckable|ItemIsEnabled|ItemIsTristate + + + + BoardType + + + Unchecked + + + ItemIsSelectable|ItemIsDragEnabled|ItemIsUserCheckable|ItemIsEnabled|ItemIsTristate + + + + Genuine + + + Unchecked + + + + + Bootleg + + + Unchecked + + + + + Demo + + + Unchecked + + + + + Hack + + + Unchecked + + + + + Homebrew + + + Unchecked + + + + + Prototype + + + Unchecked + + + + + + Family + + + Unchecked + + + + + Genre + + + Unchecked + + + + + Hardware + + + Unchecked + + + + Capcom (Other) + + + Unchecked + + + + + Cave + + + Unchecked + + + + + CPS-1 + + + Unchecked + + + + + CPS-2 + + + Unchecked + + + + + CPS-3 + + + Unchecked + + + + + Data East + + + Unchecked + + + + + Galaxian + + + Unchecked + + + + + Irem + + + Unchecked + + + + + Kaneko + + + Unchecked + + + + + Konami + + + Unchecked + + + + + Neo Geo + + + Unchecked + + + + + Pacman + + + Unchecked + + + + + PGM + + + Unchecked + + + + + Psikyo + + + Unchecked + + + + + Sega + + + Unchecked + + + + + Seta + + + Unchecked + + + + + Taito + + + Unchecked + + + + + Technos + + + Unchecked + + + + + Toaplan + + + Unchecked + + + + + Misc (pre 90s) + + + Unchecked + + + + + Misc (post 90s) + + + Unchecked + + + + + Master System + + + Unchecked + + + + + Megadrive + + + Unchecked + + + + + PC-Engine + + + Unchecked + + + + + SNES + + + Unchecked + + + + + + + + + + + + Search + + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Cancel + + + + + + + Play + + + + + + + + + + + + + + + + + btnCancel + clicked() + SelectDialog + reject() + + + 601 + 558 + + + 545 + 555 + + + + + diff --git a/src/burner/qt/stringset.cpp b/src/burner/qt/stringset.cpp new file mode 100644 index 000000000..74cc77a2e --- /dev/null +++ b/src/burner/qt/stringset.cpp @@ -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 +} diff --git a/src/burner/qt/supportdirsdialog.cpp b/src/burner/qt/supportdirsdialog.cpp new file mode 100644 index 000000000..e306259ec --- /dev/null +++ b/src/burner/qt/supportdirsdialog.cpp @@ -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(); + } +} diff --git a/src/burner/qt/supportdirsdialog.h b/src/burner/qt/supportdirsdialog.h new file mode 100644 index 000000000..d0edf4e24 --- /dev/null +++ b/src/burner/qt/supportdirsdialog.h @@ -0,0 +1,40 @@ +#ifndef SUPPORTDIRSDIALOG_H +#define SUPPORTDIRSDIALOG_H + +#include +#include +#include +#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 diff --git a/src/burner/qt/supportdirsdialog.ui b/src/burner/qt/supportdirsdialog.ui new file mode 100644 index 000000000..4a439b85c --- /dev/null +++ b/src/burner/qt/supportdirsdialog.ui @@ -0,0 +1,199 @@ + + + SupportDirsDialog + + + + 0 + 0 + 576 + 275 + + + + + 0 + 0 + + + + Dialog + + + true + + + + + + + + Previews + + + + + + + + + + Browse + + + + + + + + + + + Titles + + + + + + + + + + Browse + + + + + + + + + + + Hiscores + + + + + + + + + + Browse + + + + + + + + + + + Cheats + + + + + + + + + + Browse + + + + + + + + + + + Samples + + + + + + + + + + Browse + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Cancel + + + + + + + Ok + + + + + + + + + + + btnCancel + clicked() + SupportDirsDialog + reject() + + + 238 + 102 + + + 97 + 102 + + + + + btnOk + clicked() + SupportDirsDialog + accept() + + + 287 + 104 + + + 276 + 116 + + + + + diff --git a/src/burner/qt/tchar.h b/src/burner/qt/tchar.h new file mode 100644 index 000000000..ecc6adea4 --- /dev/null +++ b/src/burner/qt/tchar.h @@ -0,0 +1,65 @@ +#ifndef __PORT_TYPEDEFS_H +#define __PORT_TYPEDEFS_H + +#include +#include + +#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 +#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 diff --git a/src/dep/libs/nall/Makefile b/src/dep/libs/nall/Makefile new file mode 100755 index 000000000..bee3d22a7 --- /dev/null +++ b/src/dep/libs/nall/Makefile @@ -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,) diff --git a/src/dep/libs/nall/algorithm.hpp b/src/dep/libs/nall/algorithm.hpp new file mode 100755 index 000000000..5867c1cd1 --- /dev/null +++ b/src/dep/libs/nall/algorithm.hpp @@ -0,0 +1,19 @@ +#ifndef NALL_ALGORITHM_HPP +#define NALL_ALGORITHM_HPP + +#undef min +#undef max + +namespace nall { + +template T min(const T& t, const U& u) { + return t < u ? t : u; +} + +template T max(const T& t, const U& u) { + return t > u ? t : u; +} + +} + +#endif diff --git a/src/dep/libs/nall/any.hpp b/src/dep/libs/nall/any.hpp new file mode 100755 index 000000000..27a07ef20 --- /dev/null +++ b/src/dep/libs/nall/any.hpp @@ -0,0 +1,98 @@ +#ifndef NALL_ANY_HPP +#define NALL_ANY_HPP + +#include +#include + +namespace nall { + +struct any { + bool empty() const { return container; } + void reset() { if(container) { delete container; container = nullptr; } } + + const std::type_info& type() const { + return container ? container->type() : typeid(void); + } + + template any& operator=(const T& value) { + typedef typename type_if< + std::is_array::value, + typename std::remove_extent::type>::type*, + T + >::type auto_t; + + if(type() == typeid(auto_t)) { + static_cast*>(container)->value = (auto_t)value; + } else { + if(container) delete container; + container = new holder((auto_t)value); + } + + return *this; + } + + any& operator=(const any& source) { + if(container) { delete container; container = nullptr; } + if(source.container) container = source.container->copy(); + return *this; + } + + any& operator=(any&& source) { + if(container) delete container; + container = source.container; + source.container = nullptr; + return *this; + } + + any() = default; + any(const any& source) { operator=(source); } + any(any&& source) { operator=(std::move(source)); } + template any(const T& value) { operator=(value); } + ~any() { reset(); } + +private: + struct placeholder { + virtual const std::type_info& type() const = 0; + virtual placeholder* copy() const = 0; + virtual ~placeholder() {} + }; + placeholder* container = nullptr; + + template struct holder : placeholder { + T value; + const std::type_info& type() const { return typeid(T); } + placeholder* copy() const { return new holder(value); } + holder(const T& value) : value(value) {} + }; + + template friend T any_cast(any&); + template friend T any_cast(const any&); + template friend T* any_cast(any*); + template friend const T* any_cast(const any*); +}; + +template T any_cast(any& value) { + typedef typename std::remove_reference::type nonref; + if(value.type() != typeid(nonref)) throw; + return static_cast*>(value.container)->value; +} + +template T any_cast(const any& value) { + typedef const typename std::remove_reference::type nonref; + if(value.type() != typeid(nonref)) throw; + return static_cast*>(value.container)->value; +} + +template T* any_cast(any* value) { + if(!value || value->type() != typeid(T)) return nullptr; + return &static_cast*>(value->container)->value; +} + +template const T* any_cast(const any* value) { + if(!value || value->type() != typeid(T)) return nullptr; + return &static_cast*>(value->container)->value; +} + +} + +#endif diff --git a/src/dep/libs/nall/atoi.hpp b/src/dep/libs/nall/atoi.hpp new file mode 100755 index 000000000..6c2babae3 --- /dev/null +++ b/src/dep/libs/nall/atoi.hpp @@ -0,0 +1,103 @@ +#ifndef NALL_ATOI_HPP +#define NALL_ATOI_HPP + +#include + +namespace nall { + +constexpr inline uintmax_t binary_(const char* s, uintmax_t sum = 0) { + return ( + *s == '0' || *s == '1' ? binary_(s + 1, (sum << 1) | *s - '0') : + *s == '\'' ? binary_(s + 1, sum) : + sum + ); +} + +constexpr inline uintmax_t octal_(const char* s, uintmax_t sum = 0) { + return ( + *s >= '0' && *s <= '7' ? octal_(s + 1, (sum << 3) | *s - '0') : + *s == '\'' ? octal_(s + 1, sum) : + sum + ); +} + +constexpr inline uintmax_t decimal_(const char* s, uintmax_t sum = 0) { + return ( + *s >= '0' && *s <= '9' ? decimal_(s + 1, (sum * 10) + *s - '0') : + *s == '\'' ? decimal_(s + 1, sum) : + sum + ); +} + +constexpr inline uintmax_t hex_(const char* s, uintmax_t sum = 0) { + return ( + *s >= 'A' && *s <= 'F' ? hex_(s + 1, (sum << 4) | *s - 'A' + 10) : + *s >= 'a' && *s <= 'f' ? hex_(s + 1, (sum << 4) | *s - 'a' + 10) : + *s >= '0' && *s <= '9' ? hex_(s + 1, (sum << 4) | *s - '0') : + *s == '\'' ? hex_(s + 1, sum) : + sum + ); +} + +// + +constexpr inline uintmax_t binary(const char* s) { + return ( + *s == '0' && *(s + 1) == 'B' ? binary_(s + 2) : + *s == '0' && *(s + 1) == 'b' ? binary_(s + 2) : + *s == '%' ? binary_(s + 1) : + binary_(s) + ); +} + +constexpr inline uintmax_t octal(const char* s) { + return ( + *s == '0' && *(s + 1) == 'O' ? octal_(s + 2) : + *s == '0' && *(s + 1) == 'o' ? octal_(s + 2) : + octal_(s) + ); +} + +constexpr inline intmax_t integer(const char* s) { + return ( + *s == '+' ? +decimal_(s + 1) : + *s == '-' ? -decimal_(s + 1) : + decimal_(s) + ); +} + +constexpr inline uintmax_t decimal(const char* s) { + return ( + decimal_(s) + ); +} + +constexpr inline uintmax_t hex(const char* s) { + return ( + *s == '0' && *(s + 1) == 'X' ? hex_(s + 2) : + *s == '0' && *(s + 1) == 'x' ? hex_(s + 2) : + *s == '$' ? hex_(s + 1) : + hex_(s) + ); +} + +constexpr inline intmax_t numeral(const char* s) { + return ( + *s == '0' && *(s + 1) == 'X' ? hex_(s + 2) : + *s == '0' && *(s + 1) == 'x' ? hex_(s + 2) : + *s == '0' && *(s + 1) == 'B' ? binary_(s + 2) : + *s == '0' && *(s + 1) == 'b' ? binary_(s + 2) : + *s == '0' ? octal_(s + 1) : + *s == '+' ? +decimal_(s + 1) : + *s == '-' ? -decimal_(s + 1) : + decimal_(s) + ); +} + +inline double real(const char* s) { + return atof(s); +} + +} + +#endif diff --git a/src/dep/libs/nall/base64.hpp b/src/dep/libs/nall/base64.hpp new file mode 100755 index 000000000..7d9fb1160 --- /dev/null +++ b/src/dep/libs/nall/base64.hpp @@ -0,0 +1,137 @@ +#ifndef NALL_BASE64_HPP +#define NALL_BASE64_HPP + +#include +#include +#include + +namespace nall { + +struct Base64 { + enum class Format : unsigned { MIME, URI }; + + inline static string encode(const uint8_t* data, unsigned size, Format format = Format::MIME); + inline static string encode(const vector& buffer, Format format = Format::MIME); + inline static string encode(const string& text, Format format = Format::MIME); + + inline static vector decode(const string& text); + +private: + inline static void table(char* data, Format format); + inline static uint8_t value(char data); +}; + +string Base64::encode(const uint8_t* data, unsigned size, Format format) { + vector result; + + char lookup[65]; + table(lookup, format); + + unsigned overflow = (3 - (size % 3)) % 3; //bytes to round to nearest multiple of 3 + uint8_t buffer; + + for(unsigned i = 0; i < size; i++) { + switch(i % 3) { + case 0: + buffer = data[i] >> 2; + result.append(lookup[buffer]); + buffer = (data[i] & 3) << 4; + result.append(lookup[buffer]); + break; + + case 1: + buffer |= data[i] >> 4; + result.last() = lookup[buffer]; + buffer = (data[i] & 15) << 2; + result.append(lookup[buffer]); + break; + + case 2: + buffer |= data[i] >> 6; + result.last() = lookup[buffer]; + buffer = (data[i] & 63); + result.append(lookup[buffer]); + break; + } + } + + if(lookup[64]) { + if(overflow >= 1) result.append(lookup[64]); + if(overflow >= 2) result.append(lookup[64]); + } + + return result; +} + +string Base64::encode(const vector& buffer, Format format) { + return encode(buffer.data(), buffer.size(), format); +} + +string Base64::encode(const string& text, Format format) { + return encode((const uint8_t*)text.data(), text.size(), format); +} + +vector Base64::decode(const string& text) { + vector result; + + uint8_t buffer, output; + for(unsigned i = 0; i < text.size(); i++) { + uint8_t buffer = value(text[i]); + if(buffer == 0) break; + + switch(i & 3) { + case 0: + output = buffer << 2; + break; + + case 1: + result.append(output | buffer >> 4); + output = (buffer & 15) << 4; + break; + + case 2: + result.append(output | buffer >> 2); + output = (buffer & 3) << 6; + break; + + case 3: + result.append(output | buffer); + break; + } + } + + return result; +} + +void Base64::table(char* data, Format format) { + for(unsigned n = 0; n < 26; n++) data[ 0 + n] = 'A' + n; + for(unsigned n = 0; n < 26; n++) data[26 + n] = 'a' + n; + for(unsigned n = 0; n < 10; n++) data[52 + n] = '0' + n; + + switch(format) { + case Format::MIME: + data[62] = '+'; + data[63] = '/'; + data[64] = '='; + break; + + case Format::URI: + data[62] = '-'; + data[63] = '_'; + data[64] = 0; + break; + } +} + +uint8_t Base64::value(char n) { + if(n >= 'A' && n <= 'Z') return n - 'A' + 0; + if(n >= 'a' && n <= 'z') return n - 'a' + 26; + if(n >= '0' && n <= '9') return n - '0' + 52; + if(n == '+' || n == '-') return 62; + if(n == '/' || n == '_') return 63; + return 0; +} + +} + +#endif diff --git a/src/dep/libs/nall/beat/archive.hpp b/src/dep/libs/nall/beat/archive.hpp new file mode 100755 index 000000000..f8c6b6cf0 --- /dev/null +++ b/src/dep/libs/nall/beat/archive.hpp @@ -0,0 +1,84 @@ +#ifndef NALL_BEAT_ARCHIVE_HPP +#define NALL_BEAT_ARCHIVE_HPP + +#include + +namespace nall { + +struct beatArchive : beatBase { + bool create(const string& beatname, string pathname, const string& metadata = "") { + if(fp.open(beatname, file::mode::write) == false) return false; + if(pathname.endsWith("/") == false) pathname.append("/"); + + checksum = ~0; + writeString("BPA1"); + writeNumber(metadata.length()); + writeString(metadata); + + lstring list; + ls(list, pathname, pathname); + for(auto &name : list) { + if(name.endsWith("/")) { + name.rtrim<1>("/"); + writeNumber(0 | ((name.length() - 1) << 1)); + writeString(name); + } else { + file stream; + if(stream.open({pathname, name}, file::mode::read) == false) return false; + writeNumber(1 | ((name.length() - 1) << 1)); + writeString(name); + unsigned size = stream.size(); + writeNumber(size); + uint32_t checksum = ~0; + while(size--) { + uint8_t data = stream.read(); + write(data); + checksum = crc32_adjust(checksum, data); + } + writeChecksum(~checksum); + } + } + + writeChecksum(~checksum); + fp.close(); + return true; + } + + bool unpack(const string& beatname, string pathname) { + if(fp.open(beatname, file::mode::read) == false) return false; + if(pathname.endsWith("/") == false) pathname.append("/"); + + checksum = ~0; + if(readString(4) != "BPA1") return false; + unsigned length = readNumber(); + while(length--) read(); + + directory::create(pathname); + while(fp.offset() < fp.size() - 4) { + unsigned data = readNumber(); + string name = readString((data >> 1) + 1); + if(name.find("\\") || name.find("../")) return false; //block path exploits + + if((data & 1) == 0) { + directory::create({pathname, name}); + } else { + file stream; + if(stream.open({pathname, name}, file::mode::write) == false) return false; + unsigned size = readNumber(); + uint32_t checksum = ~0; + while(size--) { + uint8_t data = read(); + stream.write(data); + checksum = crc32_adjust(checksum, data); + } + if(readChecksum(~checksum) == false) return false; + } + } + + return readChecksum(~checksum); + } +}; + +} + +#endif diff --git a/src/dep/libs/nall/beat/base.hpp b/src/dep/libs/nall/beat/base.hpp new file mode 100755 index 000000000..d35b8e7c5 --- /dev/null +++ b/src/dep/libs/nall/beat/base.hpp @@ -0,0 +1,92 @@ +#ifndef NALL_BEAT_BASE_HPP +#define NALL_BEAT_BASE_HPP + +namespace nall { + +struct beatBase { +protected: + file fp; + uint32_t checksum; + + void ls(lstring& list, const string& path, const string& basepath) { + lstring paths = directory::folders(path); + for(auto& pathname : paths) { + list.append(string{path, pathname}.ltrim<1>(basepath)); + ls(list, {path, pathname}, basepath); + } + + lstring files = directory::files(path); + for(auto& filename : files) { + list.append(string{path, filename}.ltrim<1>(basepath)); + } + } + + void write(uint8_t data) { + fp.write(data); + checksum = crc32_adjust(checksum, data); + } + + void writeNumber(uint64_t data) { + while(true) { + uint64_t x = data & 0x7f; + data >>= 7; + if(data == 0) return write(0x80 | x); + write(x); + data--; + } + } + + void writeString(const string& text) { + unsigned length = text.length(); + for(unsigned n = 0; n < length; n++) write(text[n]); + } + + void writeChecksum(uint32_t checksum) { + write(checksum >> 0); + write(checksum >> 8); + write(checksum >> 16); + write(checksum >> 24); + } + + uint8_t read() { + uint8_t data = fp.read(); + checksum = crc32_adjust(checksum, data); + return data; + } + + uint64_t readNumber() { + uint64_t data = 0, shift = 1; + while(true) { + uint8_t x = read(); + data += (x & 0x7f) * shift; + if(x & 0x80) break; + shift <<= 7; + data += shift; + } + return data; + } + + string readString(unsigned length) { + string text; + text.reserve(length + 1); + for(unsigned n = 0; n < length; n++) { + text[n] = fp.read(); + checksum = crc32_adjust(checksum, text[n]); + } + text[length] = 0; + return text; + } + + bool readChecksum(uint32_t source) { + uint32_t checksum = 0; + checksum |= read() << 0; + checksum |= read() << 8; + checksum |= read() << 16; + checksum |= read() << 24; + return checksum == source; + } +}; + +} + +#endif diff --git a/src/dep/libs/nall/beat/delta.hpp b/src/dep/libs/nall/beat/delta.hpp new file mode 100755 index 000000000..04145efb5 --- /dev/null +++ b/src/dep/libs/nall/beat/delta.hpp @@ -0,0 +1,215 @@ +#ifndef NALL_BEAT_DELTA_HPP +#define NALL_BEAT_DELTA_HPP + +#include +#include +#include +#include +#include + +namespace nall { + +struct bpsdelta { + inline void source(const uint8_t* data, unsigned size); + inline void target(const uint8_t* data, unsigned size); + + inline bool source(const string& filename); + inline bool target(const string& filename); + inline bool create(const string& filename, const string& metadata = ""); + +protected: + enum : unsigned { SourceRead, TargetRead, SourceCopy, TargetCopy }; + enum : unsigned { Granularity = 1 }; + + struct Node { + unsigned offset = 0; + Node* next = nullptr; + Node() = default; + ~Node() { if(next) delete next; } + }; + + filemap sourceFile; + const uint8_t* sourceData; + unsigned sourceSize; + + filemap targetFile; + const uint8_t* targetData; + unsigned targetSize; +}; + +void bpsdelta::source(const uint8_t* data, unsigned size) { + sourceData = data; + sourceSize = size; +} + +void bpsdelta::target(const uint8_t* data, unsigned size) { + targetData = data; + targetSize = size; +} + +bool bpsdelta::source(const string& filename) { + if(sourceFile.open(filename, filemap::mode::read) == false) return false; + source(sourceFile.data(), sourceFile.size()); + return true; +} + +bool bpsdelta::target(const string& filename) { + if(targetFile.open(filename, filemap::mode::read) == false) return false; + target(targetFile.data(), targetFile.size()); + return true; +} + +bool bpsdelta::create(const string& filename, const string& metadata) { + file modifyFile; + if(modifyFile.open(filename, file::mode::write) == false) return false; + + uint32_t sourceChecksum = ~0, modifyChecksum = ~0; + unsigned sourceRelativeOffset = 0, targetRelativeOffset = 0, outputOffset = 0; + + auto write = [&](uint8_t data) { + modifyFile.write(data); + modifyChecksum = crc32_adjust(modifyChecksum, data); + }; + + auto encode = [&](uint64_t data) { + while(true) { + uint64_t x = data & 0x7f; + data >>= 7; + if(data == 0) { + write(0x80 | x); + break; + } + write(x); + data--; + } + }; + + write('B'); + write('P'); + write('S'); + write('1'); + + encode(sourceSize); + encode(targetSize); + + unsigned markupSize = metadata.length(); + encode(markupSize); + for(unsigned n = 0; n < markupSize; n++) write(metadata[n]); + + Node* sourceTree[65536]; + Node* targetTree[65536]; + for(unsigned n = 0; n < 65536; n++) sourceTree[n] = nullptr, targetTree[n] = nullptr; + + //source tree creation + for(unsigned offset = 0; offset < sourceSize; offset++) { + uint16_t symbol = sourceData[offset + 0]; + sourceChecksum = crc32_adjust(sourceChecksum, symbol); + if(offset < sourceSize - 1) symbol |= sourceData[offset + 1] << 8; + Node *node = new Node; + node->offset = offset; + node->next = sourceTree[symbol]; + sourceTree[symbol] = node; + } + + unsigned targetReadLength = 0; + + auto targetReadFlush = [&]() { + if(targetReadLength) { + encode(TargetRead | ((targetReadLength - 1) << 2)); + unsigned offset = outputOffset - targetReadLength; + while(targetReadLength) write(targetData[offset++]), targetReadLength--; + } + }; + + while(outputOffset < targetSize) { + unsigned maxLength = 0, maxOffset = 0, mode = TargetRead; + + uint16_t symbol = targetData[outputOffset + 0]; + if(outputOffset < targetSize - 1) symbol |= targetData[outputOffset + 1] << 8; + + { //source read + unsigned length = 0, offset = outputOffset; + while(offset < sourceSize && offset < targetSize && sourceData[offset] == targetData[offset]) { + length++; + offset++; + } + if(length > maxLength) maxLength = length, mode = SourceRead; + } + + { //source copy + Node* node = sourceTree[symbol]; + while(node) { + unsigned length = 0, x = node->offset, y = outputOffset; + while(x < sourceSize && y < targetSize && sourceData[x++] == targetData[y++]) length++; + if(length > maxLength) maxLength = length, maxOffset = node->offset, mode = SourceCopy; + node = node->next; + } + } + + { //target copy + Node* node = targetTree[symbol]; + while(node) { + unsigned length = 0, x = node->offset, y = outputOffset; + while(y < targetSize && targetData[x++] == targetData[y++]) length++; + if(length > maxLength) maxLength = length, maxOffset = node->offset, mode = TargetCopy; + node = node->next; + } + + //target tree append + node = new Node; + node->offset = outputOffset; + node->next = targetTree[symbol]; + targetTree[symbol] = node; + } + + { //target read + if(maxLength < 4) { + maxLength = min((unsigned)Granularity, targetSize - outputOffset); + mode = TargetRead; + } + } + + if(mode != TargetRead) targetReadFlush(); + + switch(mode) { + case SourceRead: + encode(SourceRead | ((maxLength - 1) << 2)); + break; + case TargetRead: + //delay write to group sequential TargetRead commands into one + targetReadLength += maxLength; + break; + case SourceCopy: + case TargetCopy: + encode(mode | ((maxLength - 1) << 2)); + signed relativeOffset; + if(mode == SourceCopy) { + relativeOffset = maxOffset - sourceRelativeOffset; + sourceRelativeOffset = maxOffset + maxLength; + } else { + relativeOffset = maxOffset - targetRelativeOffset; + targetRelativeOffset = maxOffset + maxLength; + } + encode((relativeOffset < 0) | (abs(relativeOffset) << 1)); + break; + } + + outputOffset += maxLength; + } + + targetReadFlush(); + + sourceChecksum = ~sourceChecksum; + for(unsigned n = 0; n < 32; n += 8) write(sourceChecksum >> n); + uint32_t targetChecksum = crc32_calculate(targetData, targetSize); + for(unsigned n = 0; n < 32; n += 8) write(targetChecksum >> n); + uint32_t outputChecksum = ~modifyChecksum; + for(unsigned n = 0; n < 32; n += 8) write(outputChecksum >> n); + + modifyFile.close(); + return true; +} + +} + +#endif diff --git a/src/dep/libs/nall/beat/linear.hpp b/src/dep/libs/nall/beat/linear.hpp new file mode 100755 index 000000000..4ee44a578 --- /dev/null +++ b/src/dep/libs/nall/beat/linear.hpp @@ -0,0 +1,152 @@ +#ifndef NALL_BEAT_LINEAR_HPP +#define NALL_BEAT_LINEAR_HPP + +#include +#include +#include +#include +#include + +namespace nall { + +struct bpslinear { + inline void source(const uint8_t* data, unsigned size); + inline void target(const uint8_t* data, unsigned size); + + inline bool source(const string& filename); + inline bool target(const string& filename); + inline bool create(const string& filename, const string& metadata = ""); + +protected: + enum : unsigned { SourceRead, TargetRead, SourceCopy, TargetCopy }; + enum : unsigned { Granularity = 1 }; + + filemap sourceFile; + const uint8_t* sourceData; + unsigned sourceSize; + + filemap targetFile; + const uint8_t* targetData; + unsigned targetSize; +}; + +void bpslinear::source(const uint8_t* data, unsigned size) { + sourceData = data; + sourceSize = size; +} + +void bpslinear::target(const uint8_t* data, unsigned size) { + targetData = data; + targetSize = size; +} + +bool bpslinear::source(const string& filename) { + if(sourceFile.open(filename, filemap::mode::read) == false) return false; + source(sourceFile.data(), sourceFile.size()); + return true; +} + +bool bpslinear::target(const string& filename) { + if(targetFile.open(filename, filemap::mode::read) == false) return false; + target(targetFile.data(), targetFile.size()); + return true; +} + +bool bpslinear::create(const string& filename, const string& metadata) { + file modifyFile; + if(modifyFile.open(filename, file::mode::write) == false) return false; + + uint32_t modifyChecksum = ~0; + unsigned targetRelativeOffset = 0, outputOffset = 0; + + auto write = [&](uint8_t data) { + modifyFile.write(data); + modifyChecksum = crc32_adjust(modifyChecksum, data); + }; + + auto encode = [&](uint64_t data) { + while(true) { + uint64_t x = data & 0x7f; + data >>= 7; + if(data == 0) { + write(0x80 | x); + break; + } + write(x); + data--; + } + }; + + unsigned targetReadLength = 0; + + auto targetReadFlush = [&]() { + if(targetReadLength) { + encode(TargetRead | ((targetReadLength - 1) << 2)); + unsigned offset = outputOffset - targetReadLength; + while(targetReadLength) write(targetData[offset++]), targetReadLength--; + } + }; + + write('B'); + write('P'); + write('S'); + write('1'); + + encode(sourceSize); + encode(targetSize); + + unsigned markupSize = metadata.length(); + encode(markupSize); + for(unsigned n = 0; n < markupSize; n++) write(metadata[n]); + + while(outputOffset < targetSize) { + unsigned sourceLength = 0; + for(unsigned n = 0; outputOffset + n < min(sourceSize, targetSize); n++) { + if(sourceData[outputOffset + n] != targetData[outputOffset + n]) break; + sourceLength++; + } + + unsigned rleLength = 0; + for(unsigned n = 1; outputOffset + n < targetSize; n++) { + if(targetData[outputOffset] != targetData[outputOffset + n]) break; + rleLength++; + } + + if(rleLength >= 4) { + //write byte to repeat + targetReadLength++; + outputOffset++; + targetReadFlush(); + + //copy starting from repetition byte + encode(TargetCopy | ((rleLength - 1) << 2)); + unsigned relativeOffset = (outputOffset - 1) - targetRelativeOffset; + encode(relativeOffset << 1); + outputOffset += rleLength; + targetRelativeOffset = outputOffset - 1; + } else if(sourceLength >= 4) { + targetReadFlush(); + encode(SourceRead | ((sourceLength - 1) << 2)); + outputOffset += sourceLength; + } else { + targetReadLength += Granularity; + outputOffset += Granularity; + } + } + + targetReadFlush(); + + uint32_t sourceChecksum = crc32_calculate(sourceData, sourceSize); + for(unsigned n = 0; n < 32; n += 8) write(sourceChecksum >> n); + uint32_t targetChecksum = crc32_calculate(targetData, targetSize); + for(unsigned n = 0; n < 32; n += 8) write(targetChecksum >> n); + uint32_t outputChecksum = ~modifyChecksum; + for(unsigned n = 0; n < 32; n += 8) write(outputChecksum >> n); + + modifyFile.close(); + return true; +} + +} + +#endif diff --git a/src/dep/libs/nall/beat/metadata.hpp b/src/dep/libs/nall/beat/metadata.hpp new file mode 100755 index 000000000..2b31fdfd2 --- /dev/null +++ b/src/dep/libs/nall/beat/metadata.hpp @@ -0,0 +1,121 @@ +#ifndef NALL_BEAT_METADATA_HPP +#define NALL_BEAT_METADATA_HPP + +#include +#include +#include +#include +#include + +namespace nall { + +struct bpsmetadata { + inline bool load(const string& filename); + inline bool save(const string& filename, const string& metadata); + inline string metadata() const; + +protected: + file sourceFile; + string metadataString; +}; + +bool bpsmetadata::load(const string& filename) { + if(sourceFile.open(filename, file::mode::read) == false) return false; + + auto read = [&]() -> uint8_t { + return sourceFile.read(); + }; + + auto decode = [&]() -> uint64_t { + uint64_t data = 0, shift = 1; + while(true) { + uint8_t x = read(); + data += (x & 0x7f) * shift; + if(x & 0x80) break; + shift <<= 7; + data += shift; + } + return data; + }; + + if(read() != 'B') return false; + if(read() != 'P') return false; + if(read() != 'S') return false; + if(read() != '1') return false; + decode(); + decode(); + unsigned metadataSize = decode(); + char data[metadataSize + 1]; + for(unsigned n = 0; n < metadataSize; n++) data[n] = read(); + data[metadataSize] = 0; + metadataString = (const char*)data; + + return true; +} + +bool bpsmetadata::save(const string& filename, const string& metadata) { + file targetFile; + if(targetFile.open(filename, file::mode::write) == false) return false; + if(sourceFile.open() == false) return false; + sourceFile.seek(0); + + auto read = [&]() -> uint8_t { + return sourceFile.read(); + }; + + auto decode = [&]() -> uint64_t { + uint64_t data = 0, shift = 1; + while(true) { + uint8_t x = read(); + data += (x & 0x7f) * shift; + if(x & 0x80) break; + shift <<= 7; + data += shift; + } + return data; + }; + + uint32_t checksum = ~0; + + auto write = [&](uint8_t data) { + targetFile.write(data); + checksum = crc32_adjust(checksum, data); + }; + + auto encode = [&](uint64_t data) { + while(true) { + uint64_t x = data & 0x7f; + data >>= 7; + if(data == 0) { + write(0x80 | x); + break; + } + write(x); + data--; + } + }; + + for(unsigned n = 0; n < 4; n++) write(read()); + encode(decode()); + encode(decode()); + unsigned sourceLength = decode(); + unsigned targetLength = metadata.length(); + encode(targetLength); + sourceFile.seek(sourceLength, file::index::relative); + for(unsigned n = 0; n < targetLength; n++) write(metadata[n]); + unsigned length = sourceFile.size() - sourceFile.offset() - 4; + for(unsigned n = 0; n < length; n++) write(read()); + uint32_t outputChecksum = ~checksum; + for(unsigned n = 0; n < 32; n += 8) write(outputChecksum >> n); + + targetFile.close(); + return true; +} + +string bpsmetadata::metadata() const { + return metadataString; +} + +} + +#endif diff --git a/src/dep/libs/nall/beat/multi.hpp b/src/dep/libs/nall/beat/multi.hpp new file mode 100755 index 000000000..c99001c8f --- /dev/null +++ b/src/dep/libs/nall/beat/multi.hpp @@ -0,0 +1,242 @@ +#ifndef NALL_BEAT_MULTI_HPP +#define NALL_BEAT_MULTI_HPP + +#include +#include +#include + +namespace nall { + +struct bpsmulti { + enum : unsigned { + CreatePath = 0, + CreateFile = 1, + ModifyFile = 2, + MirrorFile = 3, + }; + + enum : unsigned { + OriginSource = 0, + OriginTarget = 1, + }; + + bool create(const string& patchName, const string& sourcePath, const string& targetPath, bool delta = false, const string& metadata = "") { + if(fp.open()) fp.close(); + fp.open(patchName, file::mode::write); + checksum = ~0; + + writeString("BPM1"); //signature + writeNumber(metadata.length()); + writeString(metadata); + + lstring sourceList, targetList; + ls(sourceList, sourcePath, sourcePath); + ls(targetList, targetPath, targetPath); + + for(auto& targetName : targetList) { + if(targetName.endsWith("/")) { + targetName.rtrim<1>("/"); + writeNumber(CreatePath | ((targetName.length() - 1) << 2)); + writeString(targetName); + } else if(auto position = sourceList.find(targetName)) { //if sourceName == targetName + file sp, dp; + sp.open({sourcePath, targetName}, file::mode::read); + dp.open({targetPath, targetName}, file::mode::read); + + bool identical = sp.size() == dp.size(); + uint32_t cksum = ~0; + + for(unsigned n = 0; n < sp.size(); n++) { + uint8_t byte = sp.read(); + if(identical && byte != dp.read()) identical = false; + cksum = crc32_adjust(cksum, byte); + } + + if(identical) { + writeNumber(MirrorFile | ((targetName.length() - 1) << 2)); + writeString(targetName); + writeNumber(OriginSource); + writeChecksum(~cksum); + } else { + writeNumber(ModifyFile | ((targetName.length() - 1) << 2)); + writeString(targetName); + writeNumber(OriginSource); + + if(delta == false) { + bpslinear patch; + patch.source({sourcePath, targetName}); + patch.target({targetPath, targetName}); + patch.create({temppath(), "temp.bps"}); + } else { + bpsdelta patch; + patch.source({sourcePath, targetName}); + patch.target({targetPath, targetName}); + patch.create({temppath(), "temp.bps"}); + } + + auto buffer = file::read({temppath(), "temp.bps"}); + writeNumber(buffer.size()); + for(auto &byte : buffer) write(byte); + } + } else { + writeNumber(CreateFile | ((targetName.length() - 1) << 2)); + writeString(targetName); + auto buffer = file::read({targetPath, targetName}); + writeNumber(buffer.size()); + for(auto &byte : buffer) write(byte); + writeChecksum(crc32_calculate(buffer.data(), buffer.size())); + } + } + + //checksum + writeChecksum(~checksum); + fp.close(); + return true; + } + + bool apply(const string& patchName, const string& sourcePath, const string& targetPath) { + directory::remove(targetPath); //start with a clean directory + directory::create(targetPath); + + if(fp.open()) fp.close(); + fp.open(patchName, file::mode::read); + checksum = ~0; + + if(readString(4) != "BPM1") return false; + auto metadataLength = readNumber(); + while(metadataLength--) read(); + + while(fp.offset() < fp.size() - 4) { + auto encoding = readNumber(); + unsigned action = encoding & 3; + unsigned targetLength = (encoding >> 2) + 1; + string targetName = readString(targetLength); + + if(action == CreatePath) { + directory::create({targetPath, targetName, "/"}); + } else if(action == CreateFile) { + file fp; + fp.open({targetPath, targetName}, file::mode::write); + auto fileSize = readNumber(); + while(fileSize--) fp.write(read()); + uint32_t cksum = readChecksum(); + } else if(action == ModifyFile) { + auto encoding = readNumber(); + string originPath = encoding & 1 ? targetPath : sourcePath; + string sourceName = (encoding >> 1) == 0 ? targetName : readString(encoding >> 1); + auto patchSize = readNumber(); + vector buffer; + buffer.resize(patchSize); + for(unsigned n = 0; n < patchSize; n++) buffer[n] = read(); + bpspatch patch; + patch.modify(buffer.data(), buffer.size()); + patch.source({originPath, sourceName}); + patch.target({targetPath, targetName}); + if(patch.apply() != bpspatch::result::success) return false; + } else if(action == MirrorFile) { + auto encoding = readNumber(); + string originPath = encoding & 1 ? targetPath : sourcePath; + string sourceName = (encoding >> 1) == 0 ? targetName : readString(encoding >> 1); + file::copy({originPath, sourceName}, {targetPath, targetName}); + uint32_t cksum = readChecksum(); + } + } + + uint32_t cksum = ~checksum; + if(read() != (uint8_t)(cksum >> 0)) return false; + if(read() != (uint8_t)(cksum >> 8)) return false; + if(read() != (uint8_t)(cksum >> 16)) return false; + if(read() != (uint8_t)(cksum >> 24)) return false; + + fp.close(); + return true; + } + +protected: + file fp; + uint32_t checksum; + + //create() functions + void ls(lstring& list, const string& path, const string& basepath) { + lstring paths = directory::folders(path); + for(auto& pathname : paths) { + list.append(string{path, pathname}.ltrim<1>(basepath)); + ls(list, {path, pathname}, basepath); + } + + lstring files = directory::files(path); + for(auto& filename : files) { + list.append(string{path, filename}.ltrim<1>(basepath)); + } + } + + void write(uint8_t data) { + fp.write(data); + checksum = crc32_adjust(checksum, data); + } + + void writeNumber(uint64_t data) { + while(true) { + uint64_t x = data & 0x7f; + data >>= 7; + if(data == 0) { + write(0x80 | x); + break; + } + write(x); + data--; + } + } + + void writeString(const string& text) { + unsigned length = text.length(); + for(unsigned n = 0; n < length; n++) write(text[n]); + } + + void writeChecksum(uint32_t cksum) { + write(cksum >> 0); + write(cksum >> 8); + write(cksum >> 16); + write(cksum >> 24); + } + + //apply() functions + uint8_t read() { + uint8_t data = fp.read(); + checksum = crc32_adjust(checksum, data); + return data; + } + + uint64_t readNumber() { + uint64_t data = 0, shift = 1; + while(true) { + uint8_t x = read(); + data += (x & 0x7f) * shift; + if(x & 0x80) break; + shift <<= 7; + data += shift; + } + return data; + } + + string readString(unsigned length) { + string text; + text.reserve(length + 1); + for(unsigned n = 0; n < length; n++) text[n] = read(); + text[length] = 0; + return text; + } + + uint32_t readChecksum() { + uint32_t checksum = 0; + checksum |= read() << 0; + checksum |= read() << 8; + checksum |= read() << 16; + checksum |= read() << 24; + return checksum; + } +}; + +} + +#endif diff --git a/src/dep/libs/nall/beat/patch.hpp b/src/dep/libs/nall/beat/patch.hpp new file mode 100755 index 000000000..59c6c5d1b --- /dev/null +++ b/src/dep/libs/nall/beat/patch.hpp @@ -0,0 +1,219 @@ +#ifndef NALL_BEAT_PATCH_HPP +#define NALL_BEAT_PATCH_HPP + +#include +#include +#include +#include +#include + +namespace nall { + +struct bpspatch { + inline bool modify(const uint8_t* data, unsigned size); + inline void source(const uint8_t* data, unsigned size); + inline void target(uint8_t* data, unsigned size); + + inline bool modify(const string& filename); + inline bool source(const string& filename); + inline bool target(const string& filename); + + inline string metadata() const; + inline unsigned size() const; + + enum result : unsigned { + unknown, + success, + patch_too_small, + patch_invalid_header, + source_too_small, + target_too_small, + source_checksum_invalid, + target_checksum_invalid, + patch_checksum_invalid, + }; + + inline result apply(); + +protected: + enum : unsigned { SourceRead, TargetRead, SourceCopy, TargetCopy }; + + filemap modifyFile; + const uint8_t* modifyData; + unsigned modifySize; + + filemap sourceFile; + const uint8_t* sourceData; + unsigned sourceSize; + + filemap targetFile; + uint8_t* targetData; + unsigned targetSize; + + unsigned modifySourceSize; + unsigned modifyTargetSize; + unsigned modifyMarkupSize; + string metadataString; +}; + +bool bpspatch::modify(const uint8_t* data, unsigned size) { + if(size < 19) return false; + modifyData = data; + modifySize = size; + + unsigned offset = 4; + auto decode = [&]() -> uint64_t { + uint64_t data = 0, shift = 1; + while(true) { + uint8_t x = modifyData[offset++]; + data += (x & 0x7f) * shift; + if(x & 0x80) break; + shift <<= 7; + data += shift; + } + return data; + }; + + modifySourceSize = decode(); + modifyTargetSize = decode(); + modifyMarkupSize = decode(); + + char buffer[modifyMarkupSize + 1]; + for(unsigned n = 0; n < modifyMarkupSize; n++) buffer[n] = modifyData[offset++]; + buffer[modifyMarkupSize] = 0; + metadataString = (const char*)buffer; + + return true; +} + +void bpspatch::source(const uint8_t* data, unsigned size) { + sourceData = data; + sourceSize = size; +} + +void bpspatch::target(uint8_t* data, unsigned size) { + targetData = data; + targetSize = size; +} + +bool bpspatch::modify(const string& filename) { + if(modifyFile.open(filename, filemap::mode::read) == false) return false; + return modify(modifyFile.data(), modifyFile.size()); +} + +bool bpspatch::source(const string& filename) { + if(sourceFile.open(filename, filemap::mode::read) == false) return false; + source(sourceFile.data(), sourceFile.size()); + return true; +} + +bool bpspatch::target(const string& filename) { + file fp; + if(fp.open(filename, file::mode::write) == false) return false; + fp.truncate(modifyTargetSize); + fp.close(); + + if(targetFile.open(filename, filemap::mode::readwrite) == false) return false; + target(targetFile.data(), targetFile.size()); + return true; +} + +string bpspatch::metadata() const { + return metadataString; +} + +unsigned bpspatch::size() const { + return modifyTargetSize; +} + +bpspatch::result bpspatch::apply() { + if(modifySize < 19) return result::patch_too_small; + + uint32_t modifyChecksum = ~0, targetChecksum = ~0; + unsigned modifyOffset = 0, sourceRelativeOffset = 0, targetRelativeOffset = 0, outputOffset = 0; + + auto read = [&]() -> uint8_t { + uint8_t data = modifyData[modifyOffset++]; + modifyChecksum = crc32_adjust(modifyChecksum, data); + return data; + }; + + auto decode = [&]() -> uint64_t { + uint64_t data = 0, shift = 1; + while(true) { + uint8_t x = read(); + data += (x & 0x7f) * shift; + if(x & 0x80) break; + shift <<= 7; + data += shift; + } + return data; + }; + + auto write = [&](uint8_t data) { + targetData[outputOffset++] = data; + targetChecksum = crc32_adjust(targetChecksum, data); + }; + + if(read() != 'B') return result::patch_invalid_header; + if(read() != 'P') return result::patch_invalid_header; + if(read() != 'S') return result::patch_invalid_header; + if(read() != '1') return result::patch_invalid_header; + + modifySourceSize = decode(); + modifyTargetSize = decode(); + modifyMarkupSize = decode(); + for(unsigned n = 0; n < modifyMarkupSize; n++) read(); + + if(modifySourceSize > sourceSize) return result::source_too_small; + if(modifyTargetSize > targetSize) return result::target_too_small; + + while(modifyOffset < modifySize - 12) { + unsigned length = decode(); + unsigned mode = length & 3; + length = (length >> 2) + 1; + + switch(mode) { + case SourceRead: + while(length--) write(sourceData[outputOffset]); + break; + case TargetRead: + while(length--) write(read()); + break; + case SourceCopy: + case TargetCopy: + signed offset = decode(); + bool negative = offset & 1; + offset >>= 1; + if(negative) offset = -offset; + + if(mode == SourceCopy) { + sourceRelativeOffset += offset; + while(length--) write(sourceData[sourceRelativeOffset++]); + } else { + targetRelativeOffset += offset; + while(length--) write(targetData[targetRelativeOffset++]); + } + break; + } + } + + uint32_t modifySourceChecksum = 0, modifyTargetChecksum = 0, modifyModifyChecksum = 0; + for(unsigned n = 0; n < 32; n += 8) modifySourceChecksum |= read() << n; + for(unsigned n = 0; n < 32; n += 8) modifyTargetChecksum |= read() << n; + uint32_t checksum = ~modifyChecksum; + for(unsigned n = 0; n < 32; n += 8) modifyModifyChecksum |= read() << n; + + uint32_t sourceChecksum = crc32_calculate(sourceData, modifySourceSize); + targetChecksum = ~targetChecksum; + + if(sourceChecksum != modifySourceChecksum) return result::source_checksum_invalid; + if(targetChecksum != modifyTargetChecksum) return result::target_checksum_invalid; + if(checksum != modifyModifyChecksum) return result::patch_checksum_invalid; + + return result::success; +} + +} + +#endif diff --git a/src/dep/libs/nall/bit.hpp b/src/dep/libs/nall/bit.hpp new file mode 100755 index 000000000..c1cb15ee6 --- /dev/null +++ b/src/dep/libs/nall/bit.hpp @@ -0,0 +1,88 @@ +#ifndef NALL_BIT_HPP +#define NALL_BIT_HPP + +#include + +namespace nall { + +template inline uintmax_t uclamp(const uintmax_t x) { + enum : uintmax_t { b = 1ull << (bits - 1), y = b * 2 - 1 }; + return y + ((x - y) & -(x < y)); //min(x, y); +} + +template inline uintmax_t uclip(const uintmax_t x) { + enum : uintmax_t { b = 1ull << (bits - 1), m = b * 2 - 1 }; + return (x & m); +} + +template inline intmax_t sclamp(const intmax_t x) { + enum : intmax_t { b = 1ull << (bits - 1), m = b - 1 }; + return (x > m) ? m : (x < -b) ? -b : x; +} + +template inline intmax_t sclip(const intmax_t x) { + enum : uintmax_t { b = 1ull << (bits - 1), m = b * 2 - 1 }; + return ((x & m) ^ b) - b; +} + +namespace bit { + constexpr inline uintmax_t mask(const char* s, uintmax_t sum = 0) { + return ( + *s == '0' || *s == '1' ? mask(s + 1, (sum << 1) | 1) : + *s == ' ' || *s == '_' ? mask(s + 1, sum) : + *s ? mask(s + 1, sum << 1) : + sum + ); + } + + constexpr inline uintmax_t test(const char* s, uintmax_t sum = 0) { + return ( + *s == '0' || *s == '1' ? test(s + 1, (sum << 1) | (*s - '0')) : + *s == ' ' || *s == '_' ? test(s + 1, sum) : + *s ? test(s + 1, sum << 1) : + sum + ); + } + + //lowest(0b1110) == 0b0010 + constexpr inline uintmax_t lowest(const uintmax_t x) { + return x & -x; + } + + //clear_lowest(0b1110) == 0b1100 + constexpr inline uintmax_t clear_lowest(const uintmax_t x) { + return x & (x - 1); + } + + //set_lowest(0b0101) == 0b0111 + constexpr inline uintmax_t set_lowest(const uintmax_t x) { + return x | (x + 1); + } + + //count number of bits set in a byte + inline unsigned count(uintmax_t x) { + unsigned count = 0; + do count += x & 1; while(x >>= 1); + return count; + } + + //return index of the first bit set (or zero of no bits are set) + //first(0b1000) == 3 + inline unsigned first(uintmax_t x) { + unsigned first = 0; + while(x) { if(x & 1) break; x >>= 1; first++; } + return first; + } + + //round up to next highest single bit: + //round(15) == 16, round(16) == 16, round(17) == 32 + inline uintmax_t round(uintmax_t x) { + if((x & (x - 1)) == 0) return x; + while(x & (x - 1)) x &= x - 1; + return x << 1; + } +} + +} + +#endif diff --git a/src/dep/libs/nall/bmp.hpp b/src/dep/libs/nall/bmp.hpp new file mode 100755 index 000000000..9e68617ec --- /dev/null +++ b/src/dep/libs/nall/bmp.hpp @@ -0,0 +1,100 @@ +#ifndef NALL_BMP_HPP +#define NALL_BMP_HPP + +#include + +//BMP reader / writer +//note: only 24-bit RGB and 32-bit ARGB uncompressed images supported + +namespace nall { + +struct bmp { + inline static bool read(const string& filename, uint32_t*& data, unsigned& width, unsigned& height); + inline static bool write(const string& filename, const uint32_t* data, unsigned width, unsigned height, unsigned pitch, bool alpha = false); +}; + +bool bmp::read(const string& filename, uint32_t*& data, unsigned& width, unsigned& height) { + file fp; + if(fp.open(filename, file::mode::read) == false) return false; + if(fp.size() < 0x36) return false; + + if(fp.readm(2) != 0x424d) return false; + fp.seek(0x000a); + unsigned offset = fp.readl(4); + unsigned dibsize = fp.readl(4); + if(dibsize != 40) return false; + signed headerWidth = fp.readl(4); + if(headerWidth < 0) return false; + signed headerHeight = fp.readl(4); + fp.readl(2); + unsigned bitsPerPixel = fp.readl(2); + if(bitsPerPixel != 24 && bitsPerPixel != 32) return false; + unsigned compression = fp.readl(4); + if(compression != 0) return false; + fp.seek(offset); + + bool noFlip = headerHeight < 0; + width = headerWidth, height = abs(headerHeight); + data = new uint32_t[width * height]; + + unsigned bytesPerPixel = bitsPerPixel / 8; + unsigned alignedWidth = width * bytesPerPixel; + unsigned paddingLength = 0; + while(alignedWidth % 4) alignedWidth++, paddingLength++; + + for(unsigned y = 0; y < height; y++) { + uint32_t* p = noFlip ? data + y * width : data + (height - 1 - y) * width; + for(unsigned x = 0; x < width; x++, p++) { + *p = fp.readl(bytesPerPixel); + if(bytesPerPixel == 3) *p |= 255 << 24; + } + if(paddingLength) fp.readl(paddingLength); + } + + fp.close(); + return true; +} + +bool bmp::write(const string& filename, const uint32_t* data, unsigned width, unsigned height, unsigned pitch, bool alpha) { + file fp; + if(fp.open(filename, file::mode::write) == false) return false; + + unsigned bitsPerPixel = alpha ? 32 : 24; + unsigned bytesPerPixel = bitsPerPixel / 8; + unsigned alignedWidth = width * bytesPerPixel; + unsigned paddingLength = 0; + unsigned imageSize = alignedWidth * height; + unsigned fileSize = 0x36 + imageSize; + while(alignedWidth % 4) alignedWidth++, paddingLength++; + + fp.writem(0x424d, 2); //signature + fp.writel(fileSize, 4); //file size + fp.writel(0, 2); //reserved + fp.writel(0, 2); //reserved + fp.writel(0x36, 4); //offset + + fp.writel(40, 4); //DIB size + fp.writel(width, 4); //width + fp.writel(-height, 4); //height + fp.writel(1, 2); //color planes + fp.writel(bitsPerPixel, 2); //bits per pixel + fp.writel(0, 4); //compression method (BI_RGB) + fp.writel(imageSize, 4); //image data size + fp.writel(3780, 4); //horizontal resolution + fp.writel(3780, 4); //vertical resolution + fp.writel(0, 4); //palette size + fp.writel(0, 4); //important color count + + for(unsigned y = 0; y < height; y++) { + const uint32_t* p = (const uint32_t*)((const uint8_t*)data + y * pitch); + for(unsigned x = 0; x < width; x++) fp.writel(*p++, bytesPerPixel); + if(paddingLength) fp.writel(0, paddingLength); + } + + fp.close(); + return true; +} + +} + +#endif diff --git a/src/dep/libs/nall/compositor.hpp b/src/dep/libs/nall/compositor.hpp new file mode 100755 index 000000000..d895b8149 --- /dev/null +++ b/src/dep/libs/nall/compositor.hpp @@ -0,0 +1,152 @@ +#ifndef NALL_COMPOSITOR_HPP +#define NALL_COMPOSITOR_HPP + +#include + +namespace nall { + +struct compositor { + inline static bool enabled(); + inline static bool enable(bool status); + + #if defined(PLATFORM_X) + enum class Compositor : unsigned { Unknown, Metacity, Xfwm4 }; + inline static Compositor detect(); + + inline static bool enabled_metacity(); + inline static bool enable_metacity(bool status); + + inline static bool enabled_xfwm4(); + inline static bool enable_xfwm4(bool status); + #endif +}; + +#if defined(PLATFORM_X) + +//Metacity + +bool compositor::enabled_metacity() { + FILE* fp = popen("gconftool-2 --get /apps/metacity/general/compositing_manager", "r"); + if(!fp) return false; + + char buffer[512]; + if(!fgets(buffer, sizeof buffer, fp)) return false; + + if(!memcmp(buffer, "true", 4)) return true; + return false; +} + +bool compositor::enable_metacity(bool status) { + FILE* fp; + if(status) { + fp = popen("gconftool-2 --set --type bool /apps/metacity/general/compositing_manager true", "r"); + } else { + fp = popen("gconftool-2 --set --type bool /apps/metacity/general/compositing_manager false", "r"); + } + if(!fp) return false; + pclose(fp); + return true; +} + +//Xfwm4 + +bool compositor::enabled_xfwm4() { + FILE* fp = popen("xfconf-query -c xfwm4 -p '/general/use_compositing'", "r"); + if(!fp) return false; + + char buffer[512]; + if(!fgets(buffer, sizeof buffer, fp)) return false; + + if(!memcmp(buffer, "true", 4)) return true; + return false; +} + +bool compositor::enable_xfwm4(bool status) { + FILE* fp; + if(status) { + fp = popen("xfconf-query -c xfwm4 -p '/general/use_compositing' -t 'bool' -s 'true'", "r"); + } else { + fp = popen("xfconf-query -c xfwm4 -p '/general/use_compositing' -t 'bool' -s 'false'", "r"); + } + if(!fp) return false; + pclose(fp); + return true; +} + +//General + +compositor::Compositor compositor::detect() { + Compositor result = Compositor::Unknown; + + FILE* fp; + char buffer[512]; + + fp = popen("pidof metacity", "r"); + if(fp && fgets(buffer, sizeof buffer, fp)) result = Compositor::Metacity; + pclose(fp); + + fp = popen("pidof xfwm4", "r"); + if(fp && fgets(buffer, sizeof buffer, fp)) result = Compositor::Xfwm4; + pclose(fp); + + return result; +} + +bool compositor::enabled() { + switch(detect()) { + case Compositor::Metacity: return enabled_metacity(); + case Compositor::Xfwm4: return enabled_xfwm4(); + default: return false; + } +} + +bool compositor::enable(bool status) { + switch(detect()) { + case Compositor::Metacity: return enable_metacity(status); + case Compositor::Xfwm4: return enable_xfwm4(status); + default: return false; + } +} + +#elif defined(PLATFORM_WINDOWS) + +bool compositor::enabled() { + HMODULE module = GetModuleHandleW(L"dwmapi"); + if(module == nullptr) module = LoadLibraryW(L"dwmapi"); + if(module == nullptr) return false; + + auto pDwmIsCompositionEnabled = (HRESULT (WINAPI*)(BOOL*))GetProcAddress(module, "DwmIsCompositionEnabled"); + if(pDwmIsCompositionEnabled == nullptr) return false; + + BOOL result; + if(pDwmIsCompositionEnabled(&result) != S_OK) return false; + return result; +} + +bool compositor::enable(bool status) { + HMODULE module = GetModuleHandleW(L"dwmapi"); + if(module == nullptr) module = LoadLibraryW(L"dwmapi"); + if(module == nullptr) return false; + + auto pDwmEnableComposition = (HRESULT (WINAPI*)(UINT))GetProcAddress(module, "DwmEnableComposition"); + if(pDwmEnableComposition == nullptr) return false; + + if(pDwmEnableComposition(status) != S_OK) return false; + return true; +} + +#else + +bool compositor::enabled() { + return false; +} + +bool compositor::enable(bool) { + return false; +} + +#endif + +} + +#endif diff --git a/src/dep/libs/nall/config.hpp b/src/dep/libs/nall/config.hpp new file mode 100755 index 000000000..4c69bce87 --- /dev/null +++ b/src/dep/libs/nall/config.hpp @@ -0,0 +1,103 @@ +#ifndef NALL_CONFIG_HPP +#define NALL_CONFIG_HPP + +#include +#include +#include + +namespace nall { +namespace Configuration { + +struct Node { + string name; + string desc; + enum class Type : unsigned { Null, Bool, Signed, Unsigned, Double, String } type = Type::Null; + void* data = nullptr; + vector children; + + bool empty() const { + return data == nullptr; + } + + string get() const { + switch(type) { + case Type::Bool: return {*(bool*)data}; + case Type::Signed: return {*(signed*)data}; + case Type::Unsigned: return {*(unsigned*)data}; + case Type::Double: return {*(double*)data}; + case Type::String: return {*(string*)data}; + } + return ""; + } + + void set(const string& value) { + switch(type) { + case Type::Bool: *(bool*)data = (value != "false"); break; + case Type::Signed: *(signed*)data = integer(value); break; + case Type::Unsigned: *(unsigned*)data = decimal(value); break; + case Type::Double: *(double*)data = real(value); break; + case Type::String: *(string*)data = value; break; + } + } + + void assign() { type = Type::Null; data = nullptr; } + void assign(bool& bind) { type = Type::Bool; data = (void*)&bind; } + void assign(signed& bind) { type = Type::Signed; data = (void*)&bind; } + void assign(unsigned& bind) { type = Type::Unsigned; data = (void*)&bind; } + void assign(double& bind) { type = Type::Double; data = (void*)&bind; } + void assign(string& bind) { type = Type::String; data = (void*)&bind; } + void assign(const Node& node) { operator=(node); } + + template void append(T& data, const string& name, const string& desc = "") { + Node node; + node.assign(data); + node.name = name; + node.desc = desc; + children.append(node); + } + + void load(Markup::Node path) { + for(auto& child : children) { + auto leaf = path[child.name]; + if(!leaf.exists()) continue; + if(!child.empty()) child.set(leaf.data.trim<1>(" ", "\r")); + child.load(leaf); + } + } + + void save(file& fp, unsigned depth = 0) { + for(auto& child : children) { + if(child.desc) { + for(unsigned n = 0; n < depth; n++) fp.print(" "); + fp.print("//", child.desc, "\n"); + } + for(unsigned n = 0; n < depth; n++) fp.print(" "); + fp.print(child.name); + if(!child.empty()) fp.print(": ", child.get()); + fp.print("\n"); + child.save(fp, depth + 1); + if(depth == 0) fp.print("\n"); + } + } +}; + +struct Document : Node { + bool load(const string& filename) { + if(!file::exists(filename)) return false; + auto document = Markup::Document(string::read(filename)); + Node::load(document); + return true; + } + + bool save(const string& filename) { + file fp(filename, file::mode::write); + if(!fp.open()) return false; + Node::save(fp); + return true; + } +}; + +} +} + +#endif diff --git a/src/dep/libs/nall/crc16.hpp b/src/dep/libs/nall/crc16.hpp new file mode 100755 index 000000000..be79a5024 --- /dev/null +++ b/src/dep/libs/nall/crc16.hpp @@ -0,0 +1,27 @@ +#ifndef NALL_CRC16_HPP +#define NALL_CRC16_HPP + +#include + +namespace nall { + +inline uint16_t crc16_adjust(uint16_t crc16, uint8_t data) { + for(unsigned n = 0; n < 8; n++) { + if((crc16 & 1) ^ (data & 1)) crc16 = (crc16 >> 1) ^ 0x8408; + else crc16 >>= 1; + data >>= 1; + } + return crc16; +} + +inline uint16_t crc16_calculate(const uint8_t* data, unsigned length) { + uint16_t crc16 = ~0; + for(unsigned n = 0; n < length; n++) { + crc16 = crc16_adjust(crc16, data[n]); + } + return ~crc16; +} + +} + +#endif diff --git a/src/dep/libs/nall/crc32.hpp b/src/dep/libs/nall/crc32.hpp new file mode 100755 index 000000000..757001016 --- /dev/null +++ b/src/dep/libs/nall/crc32.hpp @@ -0,0 +1,68 @@ +#ifndef NALL_CRC32_HPP +#define NALL_CRC32_HPP + +#include + +namespace nall { + +const uint32_t crc32_table[256] = { + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, + 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, + 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, + 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, + 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, + 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, + 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, + 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, + 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, + 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, + 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, + 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, + 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, + 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, + 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, + 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, + 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, + 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, + 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, + 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, + 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, + 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d +}; + +inline uint32_t crc32_adjust(uint32_t crc32, uint8_t input) { + return ((crc32 >> 8) & 0x00ffffff) ^ crc32_table[(crc32 ^ input) & 0xff]; +} + +inline uint32_t crc32_calculate(const uint8_t* data, unsigned length) { + uint32_t crc32 = ~0; + for(unsigned i = 0; i < length; i++) { + crc32 = crc32_adjust(crc32, data[i]); + } + return ~crc32; +} + +} + +#endif diff --git a/src/dep/libs/nall/directory.hpp b/src/dep/libs/nall/directory.hpp new file mode 100755 index 000000000..45a062c70 --- /dev/null +++ b/src/dep/libs/nall/directory.hpp @@ -0,0 +1,230 @@ +#ifndef NALL_DIRECTORY_HPP +#define NALL_DIRECTORY_HPP + +#include +#include +#include +#include +#include + +#if defined(PLATFORM_WINDOWS) + #include +#else + #include + #include + #include +#endif + +namespace nall { + +struct directory { + static bool create(const string& pathname, unsigned permissions = 0755); //recursive + static bool remove(const string& pathname); //recursive + static bool exists(const string& pathname); + + static lstring folders(const string& pathname, const string& pattern = "*") { + lstring folders = directory::ufolders(pathname, pattern); + folders.sort(); + return folders; + } + + static lstring files(const string& pathname, const string& pattern = "*") { + lstring files = directory::ufiles(pathname, pattern); + files.sort(); + return files; + } + + static lstring contents(const string& pathname, const string& pattern = "*") { + lstring folders = directory::ufolders(pathname); //pattern search of contents should only filter files + lstring files = directory::ufiles(pathname, pattern); + folders.sort(); + files.sort(); + for(auto& file : files) folders.append(file); + return folders; + } + + static lstring ifolders(const string& pathname, const string& pattern = "*") { + lstring folders = ufolders(pathname, pattern); + folders.isort(); + return folders; + } + + static lstring ifiles(const string& pathname, const string& pattern = "*") { + lstring files = ufiles(pathname, pattern); + files.isort(); + return files; + } + + static lstring icontents(const string& pathname, const string& pattern = "*") { + lstring folders = directory::ufolders(pathname); //pattern search of contents should only filter files + lstring files = directory::ufiles(pathname, pattern); + folders.isort(); + files.isort(); + for(auto& file : files) folders.append(file); + return folders; + } + +private: + //internal functions; these return unsorted lists + static lstring ufolders(const string& pathname, const string& pattern = "*"); + static lstring ufiles(const string& pathname, const string& pattern = "*"); +}; + +#if defined(PLATFORM_WINDOWS) + inline bool directory::create(const string& pathname, unsigned permissions) { + string path; + lstring list = string{pathname}.transform("\\", "/").rtrim<1>("/").split("/"); + bool result = true; + for(auto& part : list) { + path.append(part, "/"); + result &= (_wmkdir(utf16_t(path)) == 0); + } + return result; + } + + inline bool directory::remove(const string& pathname) { + lstring list = directory::contents(pathname); + for(auto& name : list) { + if(name.endsWith("/")) directory::remove({pathname, name}); + else file::remove({pathname, name}); + } + return _wrmdir(utf16_t(pathname)) == 0; + } + + inline bool directory::exists(const string& pathname) { + string name = pathname; + name.trim<1>("\""); + DWORD result = GetFileAttributes(utf16_t(name)); + if(result == INVALID_FILE_ATTRIBUTES) return false; + return (result & FILE_ATTRIBUTE_DIRECTORY); + } + + inline lstring directory::ufolders(const string& pathname, const string& pattern) { + lstring list; + string path = pathname; + path.transform("/", "\\"); + if(!strend(path, "\\")) path.append("\\"); + path.append("*"); + HANDLE handle; + WIN32_FIND_DATA data; + handle = FindFirstFile(utf16_t(path), &data); + if(handle != INVALID_HANDLE_VALUE) { + if(wcscmp(data.cFileName, L".") && wcscmp(data.cFileName, L"..")) { + if(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + string name = (const char*)utf8_t(data.cFileName); + if(name.match(pattern)) list.append(name); + } + } + while(FindNextFile(handle, &data) != false) { + if(wcscmp(data.cFileName, L".") && wcscmp(data.cFileName, L"..")) { + if(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + string name = (const char*)utf8_t(data.cFileName); + if(name.match(pattern)) list.append(name); + } + } + } + FindClose(handle); + } + for(auto& name : list) name.append("/"); //must append after sorting + return list; + } + + inline lstring directory::ufiles(const string& pathname, const string& pattern) { + lstring list; + string path = pathname; + path.transform("/", "\\"); + if(!strend(path, "\\")) path.append("\\"); + path.append("*"); + HANDLE handle; + WIN32_FIND_DATA data; + handle = FindFirstFile(utf16_t(path), &data); + if(handle != INVALID_HANDLE_VALUE) { + if((data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) { + string name = (const char*)utf8_t(data.cFileName); + if(name.match(pattern)) list.append(name); + } + while(FindNextFile(handle, &data) != false) { + if((data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) { + string name = (const char*)utf8_t(data.cFileName); + if(name.match(pattern)) list.append(name); + } + } + FindClose(handle); + } + return list; + } +#else + inline bool directory::create(const string& pathname, unsigned permissions) { + string path; + lstring list = string{pathname}.rtrim<1>("/").split("/"); + bool result = true; + for(auto& part : list) { + path.append(part, "/"); + result &= (mkdir(path, permissions) == 0); + } + return result; + } + + inline bool directory::remove(const string& pathname) { + lstring list = directory::contents(pathname); + for(auto& name : list) { + if(name.endsWith("/")) directory::remove({pathname, name}); + else file::remove({pathname, name}); + } + return rmdir(pathname) == 0; + } + + inline bool directory::exists(const string& pathname) { + DIR *dp = opendir(pathname); + if(!dp) return false; + closedir(dp); + return true; + } + + inline lstring directory::ufolders(const string& pathname, const string& pattern) { + lstring list; + DIR* dp; + struct dirent* ep; + dp = opendir(pathname); + if(dp) { + while(ep = readdir(dp)) { + if(!strcmp(ep->d_name, ".")) continue; + if(!strcmp(ep->d_name, "..")) continue; + bool is_directory = ep->d_type & DT_DIR; + if(ep->d_type & DT_UNKNOWN) { + struct stat sp = {0}; + stat(string{pathname, ep->d_name}, &sp); + is_directory = S_ISDIR(sp.st_mode); + } + if(is_directory) { + if(strmatch(ep->d_name, pattern)) list.append(ep->d_name); + } + } + closedir(dp); + } + for(auto& name : list) name.append("/"); //must append after sorting + return list; + } + + inline lstring directory::ufiles(const string& pathname, const string& pattern) { + lstring list; + DIR* dp; + struct dirent* ep; + dp = opendir(pathname); + if(dp) { + while(ep = readdir(dp)) { + if(!strcmp(ep->d_name, ".")) continue; + if(!strcmp(ep->d_name, "..")) continue; + if((ep->d_type & DT_DIR) == 0) { + if(strmatch(ep->d_name, pattern)) list.append(ep->d_name); + } + } + closedir(dp); + } + return list; + } +#endif + +} + +#endif diff --git a/src/dep/libs/nall/dl.hpp b/src/dep/libs/nall/dl.hpp new file mode 100755 index 000000000..e61b45ba4 --- /dev/null +++ b/src/dep/libs/nall/dl.hpp @@ -0,0 +1,119 @@ +#ifndef NALL_DL_HPP +#define NALL_DL_HPP + +//dynamic linking support + +#include +#include +#include +#include + +#if defined(PLATFORM_X) || defined(PLATFORM_MACOSX) + #include +#elif defined(PLATFORM_WINDOWS) + #include + #include +#endif + +namespace nall { + +struct library { + explicit operator bool() const { return open(); } + bool open() const { return handle; } + bool open(const string&, const string& = ""); + bool open_absolute(const string&); + void* sym(const string&); + void close(); + + library() = default; + ~library() { close(); } + + library& operator=(const library&) = delete; + library(const library&) = delete; + +private: + uintptr_t handle = 0; +}; + +#if defined(PLATFORM_X) +inline bool library::open(const string& name, const string& path) { + if(handle) close(); + handle = (uintptr_t)dlopen(string(path, !path.empty() && !path.endsWith("/") ? "/" : "", "lib", name, ".so"), RTLD_LAZY); + if(!handle) handle = (uintptr_t)dlopen(string("/usr/local/lib/lib", name, ".so"), RTLD_LAZY); + return handle; +} + +inline bool library::open_absolute(const string& name) { + if(handle) close(); + handle = (uintptr_t)dlopen(name, RTLD_LAZY); + return handle; +} + +inline void* library::sym(const string& name) { + if(!handle) return nullptr; + return dlsym((void*)handle, name); +} + +inline void library::close() { + if(!handle) return; + dlclose((void*)handle); + handle = 0; +} +#elif defined(PLATFORM_MACOSX) +inline bool library::open(const string& name, const string& path) { + if(handle) close(); + handle = (uintptr_t)dlopen(string(path, !path.empty() && !path.endsWith("/") ? "/" : "", "lib", name, ".dylib"), RTLD_LAZY); + if(!handle) handle = (uintptr_t)dlopen(string("/usr/local/lib/lib", name, ".dylib"), RTLD_LAZY); + return handle; +} + +inline bool library::open_absolute(const string& name) { + if(handle) close(); + handle = (uintptr_t)dlopen(name, RTLD_LAZY); + return handle; +} + +inline void* library::sym(const string& name) { + if(!handle) return nullptr; + return dlsym((void*)handle, name); +} + +inline void library::close() { + if(!handle) return; + dlclose((void*)handle); + handle = 0; +} +#elif defined(PLATFORM_WINDOWS) +inline bool library::open(const string& name, const string& path) { + if(handle) close(); + string filepath(path, !path.empty() && !path.endsWith("/") && !path.endsWith("\\") ? "/" : "", name, ".dll"); + handle = (uintptr_t)LoadLibraryW(utf16_t(filepath)); + return handle; +} + +inline bool library::open_absolute(const string& name) { + if(handle) close(); + handle = (uintptr_t)LoadLibraryW(utf16_t(name)); + return handle; +} + +inline void* library::sym(const string& name) { + if(!handle) return nullptr; + return (void*)GetProcAddress((HMODULE)handle, name); +} + +inline void library::close() { + if(!handle) return; + FreeLibrary((HMODULE)handle); + handle = 0; +} +#else +inline bool library::open(const string&, const string&) { return false; } +inline bool library::open_absolute(const string&) { return false; } +inline void* library::sym(const string&) { return nullptr; } +inline void library::close() {} +#endif + +} + +#endif diff --git a/src/dep/libs/nall/dsp.hpp b/src/dep/libs/nall/dsp.hpp new file mode 100755 index 000000000..3f12a22a2 --- /dev/null +++ b/src/dep/libs/nall/dsp.hpp @@ -0,0 +1,15 @@ +#ifndef NALL_DSP_HPP +#define NALL_DSP_HPP + +#include + +#include +#ifdef __SSE__ + #include +#endif + +#define NALL_DSP_INTERNAL_HPP +#include +#undef NALL_DSP_INTERNAL_HPP + +#endif diff --git a/src/dep/libs/nall/dsp/buffer.hpp b/src/dep/libs/nall/dsp/buffer.hpp new file mode 100755 index 000000000..bdaab79cf --- /dev/null +++ b/src/dep/libs/nall/dsp/buffer.hpp @@ -0,0 +1,52 @@ +#ifdef NALL_DSP_INTERNAL_HPP + +struct Buffer { + double** sample = nullptr; + uint16_t rdoffset = 0; + uint16_t wroffset = 0; + unsigned channels = 0; + + void setChannels(unsigned channels) { + if(sample) { + for(unsigned c = 0; c < this->channels; c++) { + if(sample[c]) delete[] sample[c]; + } + delete[] sample; + } + + this->channels = channels; + if(channels == 0) return; + + sample = new double*[channels]; + for(unsigned c = 0; c < channels; c++) { + sample[c] = new double[65536](); + } + } + + inline double& read(unsigned channel, signed offset = 0) { + return sample[channel][(uint16_t)(rdoffset + offset)]; + } + + inline double& write(unsigned channel, signed offset = 0) { + return sample[channel][(uint16_t)(wroffset + offset)]; + } + + inline void clear() { + for(unsigned c = 0; c < channels; c++) { + for(unsigned n = 0; n < 65536; n++) { + sample[c][n] = 0; + } + } + rdoffset = 0; + wroffset = 0; + } + + Buffer() { + } + + ~Buffer() { + setChannels(0); + } +}; + +#endif diff --git a/src/dep/libs/nall/dsp/core.hpp b/src/dep/libs/nall/dsp/core.hpp new file mode 100755 index 000000000..ee730abca --- /dev/null +++ b/src/dep/libs/nall/dsp/core.hpp @@ -0,0 +1,168 @@ +#ifdef NALL_DSP_INTERNAL_HPP + +#include +#include +#include + +namespace nall { + +//precision: can be float, double or long double +#define real float + +struct DSP; + +struct Resampler { + DSP& dsp; + real frequency; + + virtual void setFrequency() = 0; + virtual void clear() = 0; + virtual void sample() = 0; + Resampler(DSP& dsp) : dsp(dsp) {} +}; + +struct DSP { + enum class ResampleEngine : unsigned { + Nearest, + Linear, + Cosine, + Cubic, + Hermite, + Average, + Sinc, + }; + + inline void setChannels(unsigned channels); + inline void setPrecision(unsigned precision); + inline void setFrequency(real frequency); //inputFrequency + inline void setVolume(real volume); + inline void setBalance(real balance); + + inline void setResampler(ResampleEngine resamplingEngine); + inline void setResamplerFrequency(real frequency); //outputFrequency + + inline void sample(signed channel[]); + inline bool pending(); + inline void read(signed channel[]); + + inline void clear(); + inline DSP(); + inline ~DSP(); + +protected: + friend class ResampleNearest; + friend class ResampleLinear; + friend class ResampleCosine; + friend class ResampleCubic; + friend class ResampleAverage; + friend class ResampleHermite; + friend class ResampleSinc; + + struct Settings { + unsigned channels; + unsigned precision; + real frequency; + real volume; + real balance; + + //internal + real intensity; + real intensityInverse; + } settings; + + Resampler* resampler = nullptr; + inline void write(real channel[]); + + #include "buffer.hpp" + Buffer buffer; + Buffer output; + + inline void adjustVolume(); + inline void adjustBalance(); + inline signed clamp(const unsigned bits, const signed x); +}; + +#include "resample/nearest.hpp" +#include "resample/linear.hpp" +#include "resample/cosine.hpp" +#include "resample/cubic.hpp" +#include "resample/hermite.hpp" +#include "resample/average.hpp" +#include "resample/sinc.hpp" +#include "settings.hpp" + +void DSP::sample(signed channel[]) { + for(unsigned c = 0; c < settings.channels; c++) { + buffer.write(c) = (real)channel[c] * settings.intensityInverse; + } + buffer.wroffset++; + resampler->sample(); +} + +bool DSP::pending() { + return output.rdoffset != output.wroffset; +} + +void DSP::read(signed channel[]) { + adjustVolume(); + adjustBalance(); + + for(unsigned c = 0; c < settings.channels; c++) { + channel[c] = clamp(settings.precision, output.read(c) * settings.intensity); + } + output.rdoffset++; +} + +void DSP::write(real channel[]) { + for(unsigned c = 0; c < settings.channels; c++) { + output.write(c) = channel[c]; + } + output.wroffset++; +} + +void DSP::adjustVolume() { + for(unsigned c = 0; c < settings.channels; c++) { + output.read(c) *= settings.volume; + } +} + +void DSP::adjustBalance() { + if(settings.channels != 2) return; //TODO: support > 2 channels + if(settings.balance < 0.0) output.read(1) *= 1.0 + settings.balance; + if(settings.balance > 0.0) output.read(0) *= 1.0 - settings.balance; +} + +signed DSP::clamp(const unsigned bits, const signed x) { + const signed b = 1U << (bits - 1); + const signed m = (1U << (bits - 1)) - 1; + return (x > m) ? m : (x < -b) ? -b : x; +} + +void DSP::clear() { + buffer.clear(); + output.clear(); + resampler->clear(); +} + +DSP::DSP() { + setResampler(ResampleEngine::Hermite); + setResamplerFrequency(44100.0); + + setChannels(2); + setPrecision(16); + setFrequency(44100.0); + setVolume(1.0); + setBalance(0.0); + + clear(); +} + +DSP::~DSP() { + if(resampler) delete resampler; +} + +#undef real + +} + +#endif diff --git a/src/dep/libs/nall/dsp/resample/average.hpp b/src/dep/libs/nall/dsp/resample/average.hpp new file mode 100755 index 000000000..db3b29f61 --- /dev/null +++ b/src/dep/libs/nall/dsp/resample/average.hpp @@ -0,0 +1,72 @@ +#ifdef NALL_DSP_INTERNAL_HPP + +struct ResampleAverage : Resampler { + inline void setFrequency(); + inline void clear(); + inline void sample(); + inline void sampleLinear(); + ResampleAverage(DSP& dsp) : Resampler(dsp) {} + + real fraction; + real step; +}; + +void ResampleAverage::setFrequency() { + fraction = 0.0; + step = dsp.settings.frequency / frequency; +} + +void ResampleAverage::clear() { + fraction = 0.0; +} + +void ResampleAverage::sample() { + //can only average if input frequency >= output frequency + if(step < 1.0) return sampleLinear(); + + fraction += 1.0; + + real scalar = 1.0; + if(fraction > step) scalar = 1.0 - (fraction - step); + + for(unsigned c = 0; c < dsp.settings.channels; c++) { + dsp.output.write(c) += dsp.buffer.read(c) * scalar; + } + + if(fraction >= step) { + for(unsigned c = 0; c < dsp.settings.channels; c++) { + dsp.output.write(c) /= step; + } + dsp.output.wroffset++; + + fraction -= step; + for(unsigned c = 0; c < dsp.settings.channels; c++) { + dsp.output.write(c) = dsp.buffer.read(c) * fraction; + } + } + + dsp.buffer.rdoffset++; +} + +void ResampleAverage::sampleLinear() { + while(fraction <= 1.0) { + real channel[dsp.settings.channels]; + + for(unsigned n = 0; n < dsp.settings.channels; n++) { + real a = dsp.buffer.read(n, -1); + real b = dsp.buffer.read(n, -0); + + real mu = fraction; + + channel[n] = a * (1.0 - mu) + b * mu; + } + + dsp.write(channel); + fraction += step; + } + + dsp.buffer.rdoffset++; + fraction -= 1.0; +} + +#endif diff --git a/src/dep/libs/nall/dsp/resample/cosine.hpp b/src/dep/libs/nall/dsp/resample/cosine.hpp new file mode 100755 index 000000000..ea65afdbd --- /dev/null +++ b/src/dep/libs/nall/dsp/resample/cosine.hpp @@ -0,0 +1,44 @@ +#ifdef NALL_DSP_INTERNAL_HPP + +struct ResampleCosine : Resampler { + inline void setFrequency(); + inline void clear(); + inline void sample(); + ResampleCosine(DSP& dsp) : Resampler(dsp) {} + + real fraction; + real step; +}; + +void ResampleCosine::setFrequency() { + fraction = 0.0; + step = dsp.settings.frequency / frequency; +} + +void ResampleCosine::clear() { + fraction = 0.0; +} + +void ResampleCosine::sample() { + while(fraction <= 1.0) { + real channel[dsp.settings.channels]; + + for(unsigned n = 0; n < dsp.settings.channels; n++) { + real a = dsp.buffer.read(n, -1); + real b = dsp.buffer.read(n, -0); + + real mu = fraction; + mu = (1.0 - cos(mu * 3.14159265)) / 2.0; + + channel[n] = a * (1.0 - mu) + b * mu; + } + + dsp.write(channel); + fraction += step; + } + + dsp.buffer.rdoffset++; + fraction -= 1.0; +} + +#endif diff --git a/src/dep/libs/nall/dsp/resample/cubic.hpp b/src/dep/libs/nall/dsp/resample/cubic.hpp new file mode 100755 index 000000000..6265e2a4a --- /dev/null +++ b/src/dep/libs/nall/dsp/resample/cubic.hpp @@ -0,0 +1,50 @@ +#ifdef NALL_DSP_INTERNAL_HPP + +struct ResampleCubic : Resampler { + inline void setFrequency(); + inline void clear(); + inline void sample(); + ResampleCubic(DSP& dsp) : Resampler(dsp) {} + + real fraction; + real step; +}; + +void ResampleCubic::setFrequency() { + fraction = 0.0; + step = dsp.settings.frequency / frequency; +} + +void ResampleCubic::clear() { + fraction = 0.0; +} + +void ResampleCubic::sample() { + while(fraction <= 1.0) { + real channel[dsp.settings.channels]; + + for(unsigned n = 0; n < dsp.settings.channels; n++) { + real a = dsp.buffer.read(n, -3); + real b = dsp.buffer.read(n, -2); + real c = dsp.buffer.read(n, -1); + real d = dsp.buffer.read(n, -0); + + real mu = fraction; + + real A = d - c - a + b; + real B = a - b - A; + real C = c - a; + real D = b; + + channel[n] = A * (mu * 3) + B * (mu * 2) + C * mu + D; + } + + dsp.write(channel); + fraction += step; + } + + dsp.buffer.rdoffset++; + fraction -= 1.0; +} + +#endif diff --git a/src/dep/libs/nall/dsp/resample/hermite.hpp b/src/dep/libs/nall/dsp/resample/hermite.hpp new file mode 100755 index 000000000..04c850c1d --- /dev/null +++ b/src/dep/libs/nall/dsp/resample/hermite.hpp @@ -0,0 +1,62 @@ +#ifdef NALL_DSP_INTERNAL_HPP + +struct ResampleHermite : Resampler { + inline void setFrequency(); + inline void clear(); + inline void sample(); + ResampleHermite(DSP& dsp) : Resampler(dsp) {} + + real fraction; + real step; +}; + +void ResampleHermite::setFrequency() { + fraction = 0.0; + step = dsp.settings.frequency / frequency; +} + +void ResampleHermite::clear() { + fraction = 0.0; +} + +void ResampleHermite::sample() { + while(fraction <= 1.0) { + real channel[dsp.settings.channels]; + + for(unsigned n = 0; n < dsp.settings.channels; n++) { + real a = dsp.buffer.read(n, -3); + real b = dsp.buffer.read(n, -2); + real c = dsp.buffer.read(n, -1); + real d = dsp.buffer.read(n, -0); + + const real tension = 0.0; //-1 = low, 0 = normal, +1 = high + const real bias = 0.0; //-1 = left, 0 = even, +1 = right + + real mu1, mu2, mu3, m0, m1, a0, a1, a2, a3; + + mu1 = fraction; + mu2 = mu1 * mu1; + mu3 = mu2 * mu1; + + m0 = (b - a) * (1.0 + bias) * (1.0 - tension) / 2.0; + m0 += (c - b) * (1.0 - bias) * (1.0 - tension) / 2.0; + m1 = (c - b) * (1.0 + bias) * (1.0 - tension) / 2.0; + m1 += (d - c) * (1.0 - bias) * (1.0 - tension) / 2.0; + + a0 = +2 * mu3 - 3 * mu2 + 1; + a1 = mu3 - 2 * mu2 + mu1; + a2 = mu3 - mu2; + a3 = -2 * mu3 + 3 * mu2; + + channel[n] = (a0 * b) + (a1 * m0) + (a2 * m1) + (a3 * c); + } + + dsp.write(channel); + fraction += step; + } + + dsp.buffer.rdoffset++; + fraction -= 1.0; +} + +#endif diff --git a/src/dep/libs/nall/dsp/resample/linear.hpp b/src/dep/libs/nall/dsp/resample/linear.hpp new file mode 100755 index 000000000..e302b5a31 --- /dev/null +++ b/src/dep/libs/nall/dsp/resample/linear.hpp @@ -0,0 +1,43 @@ +#ifdef NALL_DSP_INTERNAL_HPP + +struct ResampleLinear : Resampler { + inline void setFrequency(); + inline void clear(); + inline void sample(); + ResampleLinear(DSP& dsp) : Resampler(dsp) {} + + real fraction; + real step; +}; + +void ResampleLinear::setFrequency() { + fraction = 0.0; + step = dsp.settings.frequency / frequency; +} + +void ResampleLinear::clear() { + fraction = 0.0; +} + +void ResampleLinear::sample() { + while(fraction <= 1.0) { + real channel[dsp.settings.channels]; + + for(unsigned n = 0; n < dsp.settings.channels; n++) { + real a = dsp.buffer.read(n, -1); + real b = dsp.buffer.read(n, -0); + + real mu = fraction; + + channel[n] = a * (1.0 - mu) + b * mu; + } + + dsp.write(channel); + fraction += step; + } + + dsp.buffer.rdoffset++; + fraction -= 1.0; +} + +#endif diff --git a/src/dep/libs/nall/dsp/resample/nearest.hpp b/src/dep/libs/nall/dsp/resample/nearest.hpp new file mode 100755 index 000000000..51b261b13 --- /dev/null +++ b/src/dep/libs/nall/dsp/resample/nearest.hpp @@ -0,0 +1,43 @@ +#ifdef NALL_DSP_INTERNAL_HPP + +struct ResampleNearest : Resampler { + inline void setFrequency(); + inline void clear(); + inline void sample(); + ResampleNearest(DSP& dsp) : Resampler(dsp) {} + + real fraction; + real step; +}; + +void ResampleNearest::setFrequency() { + fraction = 0.0; + step = dsp.settings.frequency / frequency; +} + +void ResampleNearest::clear() { + fraction = 0.0; +} + +void ResampleNearest::sample() { + while(fraction <= 1.0) { + real channel[dsp.settings.channels]; + + for(unsigned n = 0; n < dsp.settings.channels; n++) { + real a = dsp.buffer.read(n, -1); + real b = dsp.buffer.read(n, -0); + + real mu = fraction; + + channel[n] = mu < 0.5 ? a : b; + } + + dsp.write(channel); + fraction += step; + } + + dsp.buffer.rdoffset++; + fraction -= 1.0; +} + +#endif diff --git a/src/dep/libs/nall/dsp/resample/sinc.hpp b/src/dep/libs/nall/dsp/resample/sinc.hpp new file mode 100755 index 000000000..e23e458c6 --- /dev/null +++ b/src/dep/libs/nall/dsp/resample/sinc.hpp @@ -0,0 +1,54 @@ +#ifdef NALL_DSP_INTERNAL_HPP + +#include "lib/sinc.hpp" + +struct ResampleSinc : Resampler { + inline void setFrequency(); + inline void clear(); + inline void sample(); + inline ResampleSinc(DSP& dsp); + +private: + inline void remakeSinc(); + SincResample* sinc_resampler[8]; +}; + +void ResampleSinc::setFrequency() { + remakeSinc(); +} + +void ResampleSinc::clear() { + remakeSinc(); +} + +void ResampleSinc::sample() { + for(unsigned c = 0; c < dsp.settings.channels; c++) { + sinc_resampler[c]->write(dsp.buffer.read(c)); + } + + if(sinc_resampler[0]->output_avail()) { + do { + for(unsigned c = 0; c < dsp.settings.channels; c++) { + dsp.output.write(c) = sinc_resampler[c]->read(); + } + dsp.output.wroffset++; + } while(sinc_resampler[0]->output_avail()); + } + + dsp.buffer.rdoffset++; +} + +ResampleSinc::ResampleSinc(DSP& dsp) : Resampler(dsp) { + for(unsigned n = 0; n < 8; n++) sinc_resampler[n] = nullptr; +} + +void ResampleSinc::remakeSinc() { + assert(dsp.settings.channels < 8); + + for(unsigned c = 0; c < dsp.settings.channels; c++) { + if(sinc_resampler[c]) delete sinc_resampler[c]; + sinc_resampler[c] = new SincResample(dsp.settings.frequency, frequency, 0.85, SincResample::QUALITY_HIGH); + } +} + +#endif diff --git a/src/dep/libs/nall/dsp/settings.hpp b/src/dep/libs/nall/dsp/settings.hpp new file mode 100755 index 000000000..3a8f24c62 --- /dev/null +++ b/src/dep/libs/nall/dsp/settings.hpp @@ -0,0 +1,50 @@ +#ifdef NALL_DSP_INTERNAL_HPP + +void DSP::setChannels(unsigned channels) { + assert(channels > 0); + buffer.setChannels(channels); + output.setChannels(channels); + settings.channels = channels; +} + +void DSP::setPrecision(unsigned precision) { + settings.precision = precision; + settings.intensity = 1 << (settings.precision - 1); + settings.intensityInverse = 1.0 / settings.intensity; +} + +void DSP::setFrequency(real frequency) { + settings.frequency = frequency; + resampler->setFrequency(); +} + +void DSP::setVolume(real volume) { + settings.volume = volume; +} + +void DSP::setBalance(real balance) { + settings.balance = balance; +} + +void DSP::setResampler(ResampleEngine engine) { + if(resampler) delete resampler; + + switch(engine) { + case ResampleEngine::Nearest: resampler = new ResampleNearest(*this); return; + case ResampleEngine::Linear: resampler = new ResampleLinear (*this); return; + case ResampleEngine::Cosine: resampler = new ResampleCosine (*this); return; + case ResampleEngine::Cubic: resampler = new ResampleCubic (*this); return; + case ResampleEngine::Hermite: resampler = new ResampleHermite(*this); return; + case ResampleEngine::Average: resampler = new ResampleAverage(*this); return; + case ResampleEngine::Sinc: resampler = new ResampleSinc (*this); return; + } + + throw; +} + +void DSP::setResamplerFrequency(real frequency) { + resampler->frequency = frequency; + resampler->setFrequency(); +} + +#endif diff --git a/src/dep/libs/nall/emulation/super-famicom-usart.hpp b/src/dep/libs/nall/emulation/super-famicom-usart.hpp new file mode 100755 index 000000000..4de4a1122 --- /dev/null +++ b/src/dep/libs/nall/emulation/super-famicom-usart.hpp @@ -0,0 +1,103 @@ +#ifndef NALL_EMULATION_SUPER_FAMICOM_USART_HPP +#define NALL_EMULATION_SUPER_FAMICOM_USART_HPP + +#include +#include +#include +#include + +#include +#include +#include + +#define usartproc dllexport + +static nall::function usart_quit; +static nall::function usart_usleep; +static nall::function usart_readable; +static nall::function usart_read; +static nall::function usart_writable; +static nall::function usart_write; + +extern "C" usartproc void usart_init( + nall::function quit, + nall::function usleep, + nall::function readable, + nall::function read, + nall::function writable, + nall::function write +) { + usart_quit = quit; + usart_usleep = usleep; + usart_readable = readable; + usart_read = read; + usart_writable = writable; + usart_write = write; +} + +extern "C" usartproc void usart_main(int, char**); + +// + +static nall::serial usart; +static bool usart_is_virtual = true; +static bool usart_sigint = false; + +static bool usart_virtual() { + return usart_is_virtual; +} + +// + +static bool usarthw_quit() { + return usart_sigint; +} + +static void usarthw_usleep(unsigned milliseconds) { + usleep(milliseconds); +} + +static bool usarthw_readable() { + return usart.readable(); +} + +static uint8_t usarthw_read() { + while(true) { + uint8_t buffer[1]; + signed length = usart.read((uint8_t*)&buffer, 1); + if(length > 0) return buffer[0]; + } +} + +static bool usarthw_writable() { + return usart.writable(); +} + +static void usarthw_write(uint8_t data) { + uint8_t buffer[1] = { data }; + usart.write((uint8_t*)&buffer, 1); +} + +static void sigint(int) { + signal(SIGINT, SIG_DFL); + usart_sigint = true; +} + +int main(int argc, char** argv) { + setpriority(PRIO_PROCESS, 0, -20); //requires superuser privileges; otherwise priority = +0 + signal(SIGINT, sigint); + + if(usart.open("/dev/ttyACM0", 57600, true) == false) { + printf("error: unable to open USART hardware device\n"); + return 0; + } + + usart_is_virtual = false; + usart_init(usarthw_quit, usarthw_usleep, usarthw_readable, usarthw_read, usarthw_writable, usarthw_write); + usart_main(argc, argv); + usart.close(); + + return 0; +} + +#endif diff --git a/src/dep/libs/nall/endian.hpp b/src/dep/libs/nall/endian.hpp new file mode 100755 index 000000000..1f834b5b9 --- /dev/null +++ b/src/dep/libs/nall/endian.hpp @@ -0,0 +1,42 @@ +#ifndef NALL_ENDIAN_HPP +#define NALL_ENDIAN_HPP + +#include + +#if defined(ENDIAN_LSB) + //little-endian: uint8_t[] { 0x01, 0x02, 0x03, 0x04 } == 0x04030201 + #define order_lsb2(a,b) a,b + #define order_lsb3(a,b,c) a,b,c + #define order_lsb4(a,b,c,d) a,b,c,d + #define order_lsb5(a,b,c,d,e) a,b,c,d,e + #define order_lsb6(a,b,c,d,e,f) a,b,c,d,e,f + #define order_lsb7(a,b,c,d,e,f,g) a,b,c,d,e,f,g + #define order_lsb8(a,b,c,d,e,f,g,h) a,b,c,d,e,f,g,h + #define order_msb2(a,b) b,a + #define order_msb3(a,b,c) c,b,a + #define order_msb4(a,b,c,d) d,c,b,a + #define order_msb5(a,b,c,d,e) e,d,c,b,a + #define order_msb6(a,b,c,d,e,f) f,e,d,c,b,a + #define order_msb7(a,b,c,d,e,f,g) g,f,e,d,c,b,a + #define order_msb8(a,b,c,d,e,f,g,h) h,g,f,e,d,c,b,a +#elif defined(ENDIAN_MSB) + //big-endian: uint8_t[] { 0x01, 0x02, 0x03, 0x04 } == 0x01020304 + #define order_lsb2(a,b) b,a + #define order_lsb3(a,b,c) c,b,a + #define order_lsb4(a,b,c,d) d,c,b,a + #define order_lsb5(a,b,c,d,e) e,d,c,b,a + #define order_lsb6(a,b,c,d,e,f) f,e,d,c,b,a + #define order_lsb7(a,b,c,d,e,f,g) g,f,e,d,c,b,a + #define order_lsb8(a,b,c,d,e,f,g,h) h,g,f,e,d,c,b,a + #define order_msb2(a,b) a,b + #define order_msb3(a,b,c) a,b,c + #define order_msb4(a,b,c,d) a,b,c,d + #define order_msb5(a,b,c,d,e) a,b,c,d,e + #define order_msb6(a,b,c,d,e,f) a,b,c,d,e,f + #define order_msb7(a,b,c,d,e,f,g) a,b,c,d,e,f,g + #define order_msb8(a,b,c,d,e,f,g,h) a,b,c,d,e,f,g,h +#else + #error "Unknown endian. Please specify in nall/intrinsics.hpp" +#endif + +#endif diff --git a/src/dep/libs/nall/file.hpp b/src/dep/libs/nall/file.hpp new file mode 100755 index 000000000..721253d7b --- /dev/null +++ b/src/dep/libs/nall/file.hpp @@ -0,0 +1,362 @@ +#ifndef NALL_FILE_HPP +#define NALL_FILE_HPP + +#include +#include +#include +#include +#include +#include +#include + +namespace nall { + +inline FILE* fopen_utf8(const string& filename, const string& mode) { + #if !defined(_WIN32) + return fopen(filename, mode); + #else + return _wfopen(utf16_t(filename), utf16_t(mode)); + #endif +} + +struct file : varint { + enum class mode : unsigned { read, write, modify, append, readwrite = modify, writeread = append }; + enum class index : unsigned { absolute, relative }; + enum class time : unsigned { create, modify, access }; + + static bool copy(const string& sourcename, const string& targetname) { + file rd, wr; + if(rd.open(sourcename, mode::read) == false) return false; + if(wr.open(targetname, mode::write) == false) return false; + for(unsigned n = 0; n < rd.size(); n++) wr.write(rd.read()); + return true; + } + + static bool move(const string& sourcename, const string& targetname) { + auto result = rename(sourcename, targetname); + if(result == 0) return true; + if(errno == EXDEV) { + //cannot move files between file systems; copy file instead of failing + if(file::copy(sourcename, targetname)) { + file::remove(sourcename); + return true; + } + } + return false; + } + + static bool remove(const string& filename) { + return unlink(filename) == 0; + } + + static bool truncate(const string& filename, unsigned size) { + #if !defined(_WIN32) + return truncate(filename, size) == 0; + #else + bool result = false; + FILE* fp = fopen(filename, "rb+"); + if(fp) { + result = _chsize(fileno(fp), size) == 0; + fclose(fp); + } + return result; + #endif + } + + static vector read(const string& filename) { + vector memory; + file fp; + if(fp.open(filename, mode::read)) { + memory.resize(fp.size()); + fp.read(memory.data(), memory.size()); + } + return memory; + } + + static bool read(const string& filename, uint8_t* data, unsigned size) { + file fp; + if(fp.open(filename, mode::read) == false) return false; + fp.read(data, size); + fp.close(); + return true; + } + + static bool write(const string& filename, const string& text) { + file fp; + if(fp.open(filename, mode::write) == false) return false; + fp.print(text); + fp.close(); + return true; + } + + static bool write(const string& filename, const vector& buffer) { + file fp; + if(fp.open(filename, mode::write) == false) return false; + fp.write(buffer.data(), buffer.size()); + fp.close(); + return true; + } + + static bool write(const string& filename, const uint8_t* data, unsigned size) { + file fp; + if(fp.open(filename, mode::write) == false) return false; + fp.write(data, size); + fp.close(); + return true; + } + + static bool create(const string& filename) { + //create an empty file (will replace existing files) + file fp; + if(fp.open(filename, mode::write) == false) return false; + fp.close(); + return true; + } + + static string sha256(const string& filename) { + auto buffer = read(filename); + return nall::sha256(buffer.data(), buffer.size()); + } + + uint8_t read() { + if(!fp) return 0xff; //file not open + if(file_mode == mode::write) return 0xff; //reads not permitted + if(file_offset >= file_size) return 0xff; //cannot read past end of file + buffer_sync(); + return buffer[(file_offset++) & buffer_mask]; + } + + uintmax_t readl(unsigned length = 1) { + uintmax_t data = 0; + for(int i = 0; i < length; i++) { + data |= (uintmax_t)read() << (i << 3); + } + return data; + } + + uintmax_t readm(unsigned length = 1) { + uintmax_t data = 0; + while(length--) { + data <<= 8; + data |= read(); + } + return data; + } + + void read(uint8_t* buffer, unsigned length) { + while(length--) *buffer++ = read(); + } + + void write(uint8_t data) { + if(!fp) return; //file not open + if(file_mode == mode::read) return; //writes not permitted + buffer_sync(); + buffer[(file_offset++) & buffer_mask] = data; + buffer_dirty = true; + if(file_offset > file_size) file_size = file_offset; + } + + void writel(uintmax_t data, unsigned length = 1) { + while(length--) { + write(data); + data >>= 8; + } + } + + void writem(uintmax_t data, unsigned length = 1) { + for(int i = length - 1; i >= 0; i--) { + write(data >> (i << 3)); + } + } + + void write(const uint8_t* buffer, unsigned length) { + while(length--) write(*buffer++); + } + + template void print(Args... args) { + string data(args...); + const char* p = data; + while(*p) write(*p++); + } + + void flush() { + buffer_flush(); + fflush(fp); + } + + void seek(int offset, index index_ = index::absolute) { + if(!fp) return; //file not open + buffer_flush(); + + uintmax_t req_offset = file_offset; + switch(index_) { + case index::absolute: req_offset = offset; break; + case index::relative: req_offset += offset; break; + } + + if(req_offset < 0) req_offset = 0; //cannot seek before start of file + if(req_offset > file_size) { + if(file_mode == mode::read) { //cannot seek past end of file + req_offset = file_size; + } else { //pad file to requested location + file_offset = file_size; + while(file_size < req_offset) write(0x00); + } + } + + file_offset = req_offset; + } + + unsigned offset() const { + if(!fp) return 0; //file not open + return file_offset; + } + + unsigned size() const { + if(!fp) return 0; //file not open + return file_size; + } + + bool truncate(unsigned size) { + if(!fp) return false; //file not open + #if !defined(_WIN32) + return ftruncate(fileno(fp), size) == 0; + #else + return _chsize(fileno(fp), size) == 0; + #endif + } + + bool end() { + if(!fp) return true; //file not open + return file_offset >= file_size; + } + + static bool exists(const string& filename) { + #if !defined(_WIN32) + struct stat data; + if(stat(filename, &data) != 0) return false; + #else + struct __stat64 data; + if(_wstat64(utf16_t(filename), &data) != 0) return false; + #endif + //return true if this is a file, and false if this is a directory + return !(data.st_mode & S_IFDIR); + } + + static uintmax_t size(const string& filename) { + #if !defined(_WIN32) + struct stat data; + stat(filename, &data); + #else + struct __stat64 data; + _wstat64(utf16_t(filename), &data); + #endif + return S_ISREG(data.st_mode) ? data.st_size : 0u; + } + + static time_t timestamp(const string& filename, file::time mode = file::time::create) { + #if !defined(_WIN32) + struct stat data; + stat(filename, &data); + #else + struct __stat64 data; + _wstat64(utf16_t(filename), &data); + #endif + switch(mode) { default: + case file::time::create: return data.st_ctime; + case file::time::modify: return data.st_mtime; + case file::time::access: return data.st_atime; + } + } + + bool open() const { + return fp; + } + + explicit operator bool() const { + return open(); + } + + bool open(const string& filename, mode mode_) { + if(fp) return false; + + switch(file_mode = mode_) { + #if !defined(_WIN32) + case mode::read: fp = fopen(filename, "rb" ); break; + case mode::write: fp = fopen(filename, "wb+"); break; //need read permission for buffering + case mode::readwrite: fp = fopen(filename, "rb+"); break; + case mode::writeread: fp = fopen(filename, "wb+"); break; + #else + case mode::read: fp = _wfopen(utf16_t(filename), L"rb" ); break; + case mode::write: fp = _wfopen(utf16_t(filename), L"wb+"); break; + case mode::readwrite: fp = _wfopen(utf16_t(filename), L"rb+"); break; + case mode::writeread: fp = _wfopen(utf16_t(filename), L"wb+"); break; + #endif + } + if(!fp) return false; + buffer_offset = -1; //invalidate buffer + file_offset = 0; + fseek(fp, 0, SEEK_END); + file_size = ftell(fp); + fseek(fp, 0, SEEK_SET); + return true; + } + + void close() { + if(!fp) return; + buffer_flush(); + fclose(fp); + fp = nullptr; + } + + file() { + } + + file(const string& filename, mode mode_) { + open(filename, mode_); + } + + ~file() { + close(); + } + + file& operator=(const file&) = delete; + file(const file&) = delete; + +private: + enum { buffer_size = 1 << 12, buffer_mask = buffer_size - 1 }; + char buffer[buffer_size] = {0}; + int buffer_offset = -1; //invalidate buffer + bool buffer_dirty = false; + FILE *fp = nullptr; + unsigned file_offset = 0; + unsigned file_size = 0; + mode file_mode = mode::read; + + void buffer_sync() { + if(!fp) return; //file not open + if(buffer_offset != (file_offset & ~buffer_mask)) { + buffer_flush(); + buffer_offset = file_offset & ~buffer_mask; + fseek(fp, buffer_offset, SEEK_SET); + unsigned length = (buffer_offset + buffer_size) <= file_size ? buffer_size : (file_size & buffer_mask); + if(length) unsigned unused = fread(buffer, 1, length, fp); + } + } + + void buffer_flush() { + if(!fp) return; //file not open + if(file_mode == mode::read) return; //buffer cannot be written to + if(buffer_offset < 0) return; //buffer unused + if(buffer_dirty == false) return; //buffer unmodified since read + fseek(fp, buffer_offset, SEEK_SET); + unsigned length = (buffer_offset + buffer_size) <= file_size ? buffer_size : (file_size & buffer_mask); + if(length) unsigned unused = fwrite(buffer, 1, length, fp); + buffer_offset = -1; //invalidate buffer + buffer_dirty = false; + } +}; + +} + +#endif diff --git a/src/dep/libs/nall/filemap.hpp b/src/dep/libs/nall/filemap.hpp new file mode 100755 index 000000000..0c9ef5fa7 --- /dev/null +++ b/src/dep/libs/nall/filemap.hpp @@ -0,0 +1,215 @@ +#ifndef NALL_FILEMAP_HPP +#define NALL_FILEMAP_HPP + +#include +#include +#include + +#include +#include +#if defined(_WIN32) + #include +#else + #include + #include + #include + #include + #include +#endif + +namespace nall { + +struct filemap { + enum class mode : unsigned { read, write, readwrite, writeread }; + + explicit operator bool() const { return open(); } + bool open() const { return p_open(); } + bool open(const string& filename, mode mode_) { return p_open(filename, mode_); } + void close() { return p_close(); } + unsigned size() const { return p_size; } + uint8_t* data() { return p_handle; } + const uint8_t* data() const { return p_handle; } + filemap() { p_ctor(); } + filemap(const string& filename, mode mode_) { p_ctor(); p_open(filename, mode_); } + ~filemap() { p_dtor(); } + +private: + uint8_t *p_handle = nullptr; + unsigned p_size = 0; + + #if defined(_WIN32) + //============= + //MapViewOfFile + //============= + + HANDLE p_filehandle, p_maphandle; + + bool p_open() const { + return p_handle; + } + + bool p_open(const string& filename, mode mode_) { + if(file::exists(filename) && file::size(filename) == 0) { + p_handle = nullptr; + p_size = 0; + return true; + } + + int desired_access, creation_disposition, flprotect, map_access; + + switch(mode_) { + default: return false; + case mode::read: + desired_access = GENERIC_READ; + creation_disposition = OPEN_EXISTING; + flprotect = PAGE_READONLY; + map_access = FILE_MAP_READ; + break; + case mode::write: + //write access requires read access + desired_access = GENERIC_WRITE; + creation_disposition = CREATE_ALWAYS; + flprotect = PAGE_READWRITE; + map_access = FILE_MAP_ALL_ACCESS; + break; + case mode::readwrite: + desired_access = GENERIC_READ | GENERIC_WRITE; + creation_disposition = OPEN_EXISTING; + flprotect = PAGE_READWRITE; + map_access = FILE_MAP_ALL_ACCESS; + break; + case mode::writeread: + desired_access = GENERIC_READ | GENERIC_WRITE; + creation_disposition = CREATE_NEW; + flprotect = PAGE_READWRITE; + map_access = FILE_MAP_ALL_ACCESS; + break; + } + + p_filehandle = CreateFileW(utf16_t(filename), desired_access, FILE_SHARE_READ, nullptr, + creation_disposition, FILE_ATTRIBUTE_NORMAL, nullptr); + if(p_filehandle == INVALID_HANDLE_VALUE) return false; + + p_size = GetFileSize(p_filehandle, nullptr); + + p_maphandle = CreateFileMapping(p_filehandle, nullptr, flprotect, 0, p_size, nullptr); + if(p_maphandle == INVALID_HANDLE_VALUE) { + CloseHandle(p_filehandle); + p_filehandle = INVALID_HANDLE_VALUE; + return false; + } + + p_handle = (uint8_t*)MapViewOfFile(p_maphandle, map_access, 0, 0, p_size); + return p_handle; + } + + void p_close() { + if(p_handle) { + UnmapViewOfFile(p_handle); + p_handle = nullptr; + } + + if(p_maphandle != INVALID_HANDLE_VALUE) { + CloseHandle(p_maphandle); + p_maphandle = INVALID_HANDLE_VALUE; + } + + if(p_filehandle != INVALID_HANDLE_VALUE) { + CloseHandle(p_filehandle); + p_filehandle = INVALID_HANDLE_VALUE; + } + } + + void p_ctor() { + p_filehandle = INVALID_HANDLE_VALUE; + p_maphandle = INVALID_HANDLE_VALUE; + } + + void p_dtor() { + close(); + } + + #else + //==== + //mmap + //==== + + int p_fd; + + bool p_open() const { + return p_handle; + } + + bool p_open(const string& filename, mode mode_) { + if(file::exists(filename) && file::size(filename) == 0) { + p_handle = nullptr; + p_size = 0; + return true; + } + + int open_flags, mmap_flags; + + switch(mode_) { + default: return false; + case mode::read: + open_flags = O_RDONLY; + mmap_flags = PROT_READ; + break; + case mode::write: + open_flags = O_RDWR | O_CREAT; //mmap() requires read access + mmap_flags = PROT_WRITE; + break; + case mode::readwrite: + open_flags = O_RDWR; + mmap_flags = PROT_READ | PROT_WRITE; + break; + case mode::writeread: + open_flags = O_RDWR | O_CREAT; + mmap_flags = PROT_READ | PROT_WRITE; + break; + } + + p_fd = ::open(filename, open_flags, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); + if(p_fd < 0) return false; + + struct stat p_stat; + fstat(p_fd, &p_stat); + p_size = p_stat.st_size; + + p_handle = (uint8_t*)mmap(nullptr, p_size, mmap_flags, MAP_SHARED, p_fd, 0); + if(p_handle == MAP_FAILED) { + p_handle = nullptr; + ::close(p_fd); + p_fd = -1; + return false; + } + + return p_handle; + } + + void p_close() { + if(p_handle) { + munmap(p_handle, p_size); + p_handle = nullptr; + } + + if(p_fd >= 0) { + ::close(p_fd); + p_fd = -1; + } + } + + void p_ctor() { + p_fd = -1; + } + + void p_dtor() { + p_close(); + } + + #endif +}; + +} + +#endif diff --git a/src/dep/libs/nall/function.hpp b/src/dep/libs/nall/function.hpp new file mode 100755 index 000000000..77d2f41a2 --- /dev/null +++ b/src/dep/libs/nall/function.hpp @@ -0,0 +1,64 @@ +#ifndef NALL_FUNCTION_HPP +#define NALL_FUNCTION_HPP + +namespace nall { + +template class function; + +template class function { + struct container { + virtual R operator()(P... p) const = 0; + virtual container* copy() const = 0; + virtual ~container() {} + }; + + container* callback = nullptr; + + struct global : container { + R (*function)(P...); + R operator()(P... p) const { return function(std::forward

(p)...); } + container* copy() const { return new global(function); } + global(R (*function)(P...)) : function(function) {} + }; + + template struct member : container { + R (C::*function)(P...); + C* object; + R operator()(P... p) const { return (object->*function)(std::forward

(p)...); } + container* copy() const { return new member(function, object); } + member(R (C::*function)(P...), C* object) : function(function), object(object) {} + }; + + template struct lambda : container { + mutable L object; + R operator()(P... p) const { return object(std::forward

(p)...); } + container* copy() const { return new lambda(object); } + lambda(const L& object) : object(object) {} + }; + +public: + explicit operator bool() const { return callback; } + R operator()(P... p) const { return (*callback)(std::forward

(p)...); } + void reset() { if(callback) { delete callback; callback = nullptr; } } + + function& operator=(const function& source) { + if(this != &source) { + if(callback) { delete callback; callback = nullptr; } + if(source.callback) callback = source.callback->copy(); + } + return *this; + } + + function() = default; + function(const function &source) { operator=(source); } + function(void* function) { if(function) callback = new global((R (*)(P...))function); } + function(R (*function)(P...)) { callback = new global(function); } + template function(R (C::*function)(P...), C* object) { callback = new member(function, object); } + template function(R (C::*function)(P...) const, C* object) { callback = new member((R (C::*)(P...))function, object); } + template function(const L& object) { callback = new lambda(object); } + ~function() { if(callback) delete callback; } +}; + +} + +#endif diff --git a/src/dep/libs/nall/group.hpp b/src/dep/libs/nall/group.hpp new file mode 100755 index 000000000..f4ae68a2a --- /dev/null +++ b/src/dep/libs/nall/group.hpp @@ -0,0 +1,66 @@ +#ifndef NALL_GROUP_HPP +#define NALL_GROUP_HPP + +//group +//vector of unique references + +#include + +namespace nall { + +template struct group : protected vector { + group& operator=(const group& source) { vector::operator=(source); return *this; } + group& operator=(group&& source) { vector::operator=(std::move(source)); return *this; } + template group(Args&&... args) { construct(std::forward(args)...); } + + bool empty() const { return vector::empty(); } + unsigned size() const { return vector::size(); } + void reset() { vector::reset(); } + + T& first() const { return *vector::operator[](0); } + + //return true if at least one item was appended + template bool append(T& value, Args&&... args) { + bool result = append(value); + return append(std::forward(args)...) | result; + } + + bool append(T& value) { + if(vector::find(&value)) return false; + return vector::append(&value), true; + } + + //return true if at least one item was removed + template bool remove(T& value, Args&&... args) { + bool result = remove(value); + return remove(std::forward(args)...) | result; + } + + bool remove(T& value) { + if(auto position = vector::find(&value)) return vector::remove(position()), true; + return false; + } + + struct iterator : protected vector::constIterator { + T& operator*() const { return *vector::constIterator::operator*(); } + bool operator!=(const iterator& source) const { return vector::constIterator::operator!=(source); } + iterator& operator++() { vector::constIterator::operator++(); return *this; } + iterator(const group& source, unsigned position) : vector::constIterator(source, position) {} + }; + + const iterator begin() const { return iterator(*this, 0); } + const iterator end() const { return iterator(*this, size()); } + +private: + void construct() {} + void construct(const group& source) { vector::operator=(source); } + void construct(group&& source) { vector::operator=(std::move(source)); } + template void construct(T& value, Args&&... args) { + append(value); + construct(std::forward(args)...); + } +}; + +} + +#endif diff --git a/src/dep/libs/nall/gzip.hpp b/src/dep/libs/nall/gzip.hpp new file mode 100755 index 000000000..2d43e7ab2 --- /dev/null +++ b/src/dep/libs/nall/gzip.hpp @@ -0,0 +1,85 @@ +#ifndef NALL_GZIP_HPP +#define NALL_GZIP_HPP + +#include +#include + +namespace nall { + +struct gzip { + string filename; + uint8_t* data = nullptr; + unsigned size = 0; + + inline bool decompress(const string& filename); + inline bool decompress(const uint8_t* data, unsigned size); + + inline gzip(); + inline ~gzip(); +}; + +bool gzip::decompress(const string& filename) { + if(auto memory = file::read(filename)) { + return decompress(memory.data(), memory.size()); + } + return false; +} + +bool gzip::decompress(const uint8_t* data, unsigned size) { + if(size < 18) return false; + if(data[0] != 0x1f) return false; + if(data[1] != 0x8b) return false; + unsigned cm = data[2]; + unsigned flg = data[3]; + unsigned mtime = data[4]; + mtime |= data[5] << 8; + mtime |= data[6] << 16; + mtime |= data[7] << 24; + unsigned xfl = data[8]; + unsigned os = data[9]; + unsigned p = 10; + unsigned isize = data[size - 4]; + isize |= data[size - 3] << 8; + isize |= data[size - 2] << 16; + isize |= data[size - 1] << 24; + filename = ""; + + if(flg & 0x04) { //FEXTRA + unsigned xlen = data[p + 0]; + xlen |= data[p + 1] << 8; + p += 2 + xlen; + } + + if(flg & 0x08) { //FNAME + char buffer[PATH_MAX]; + for(unsigned n = 0; n < PATH_MAX; n++, p++) { + buffer[n] = data[p]; + if(data[p] == 0) break; + } + if(data[p++]) return false; + filename = buffer; + } + + if(flg & 0x10) { //FCOMMENT + while(data[p++]); + } + + if(flg & 0x02) { //FHCRC + p += 2; + } + + this->size = isize; + this->data = new uint8_t[this->size]; + return inflate(this->data, this->size, data + p, size - p - 8); +} + +gzip::gzip() { +} + +gzip::~gzip() { + if(data) delete[] data; +} + +} + +#endif diff --git a/src/dep/libs/nall/hashset.hpp b/src/dep/libs/nall/hashset.hpp new file mode 100755 index 000000000..3f51199c0 --- /dev/null +++ b/src/dep/libs/nall/hashset.hpp @@ -0,0 +1,137 @@ +#ifndef NALL_HASHSET_HPP +#define NALL_HASHSET_HPP + +//hashset +// +//search: O(1) average; O(n) worst +//insert: O(1) average; O(n) worst +//remove: O(1) average; O(n) worst +// +//requirements: +// unsigned T::hash() const; +// bool T::operator==(const T&) const; + +namespace nall { + +template +struct hashset { +protected: + T** pool = nullptr; + unsigned length = 8; //length of pool + unsigned count = 0; //number of objects inside of the pool + +public: + hashset() {} + hashset(unsigned length) : length(bit::round(length)) {} + hashset(const hashset& source) { operator=(source); } + hashset(hashset&& source) { operator=(std::move(source)); } + ~hashset() { reset(); } + + hashset& operator=(const hashset& source) { + reset(); + if(source.pool) { + for(unsigned n = 0; n < source.count; n++) { + insert(*source.pool[n]); + } + } + return *this; + } + + hashset& operator=(hashset&& source) { + reset(); + pool = source.pool; + length = source.length; + count = source.count; + source.pool = nullptr; + source.length = 8; + source.count = 0; + return *this; + } + + unsigned capacity() const { return length; } + unsigned size() const { return count; } + bool empty() const { return count == 0; } + + void reset() { + if(pool) { + for(unsigned n = 0; n < length; n++) { + if(pool[n]) { + delete pool[n]; + pool[n] = nullptr; + } + } + delete pool; + pool = nullptr; + } + length = 8; + count = 0; + } + + void reserve(unsigned size) { + //ensure all items will fit into pool (with <= 50% load) and amortize growth + size = bit::round(max(size, count << 1)); + T** copy = new T*[size](); + + if(pool) { + for(unsigned n = 0; n < length; n++) { + if(pool[n]) { + unsigned hash = (*pool[n]).hash() & (size - 1); + while(copy[hash]) if(++hash >= size) hash = 0; + copy[hash] = pool[n]; + pool[n] = nullptr; + } + } + } + + delete pool; + pool = copy; + length = size; + } + + optional find(const T& value) { + if(!pool) return false; + + unsigned hash = value.hash() & (length - 1); + while(pool[hash]) { + if(value == *pool[hash]) return {true, *pool[hash]}; + if(++hash >= length) hash = 0; + } + + return false; + } + + optional insert(const T& value) { + if(!pool) pool = new T*[length](); + + //double pool size when load is >= 50% + if(count >= (length >> 1)) reserve(length << 1); + count++; + + unsigned hash = value.hash() & (length - 1); + while(pool[hash]) if(++hash >= length) hash = 0; + pool[hash] = new T(value); + + return {true, *pool[hash]}; + } + + bool remove(const T& value) { + if(!pool) return false; + + unsigned hash = value.hash() & (length - 1); + while(pool[hash]) { + if(value == *pool[hash]) { + delete pool[hash]; + pool[hash] = nullptr; + count--; + return true; + } + if(++hash >= length) hash = 0; + } + + return false; + } +}; + +} + +#endif diff --git a/src/dep/libs/nall/hid.hpp b/src/dep/libs/nall/hid.hpp new file mode 100755 index 000000000..dc7088869 --- /dev/null +++ b/src/dep/libs/nall/hid.hpp @@ -0,0 +1,120 @@ +#ifndef NALL_HID_HPP +#define NALL_HID_HPP + +namespace nall { + +namespace HID { + struct Input { + string name; + int16_t value = 0; + + Input() {} + Input(const string& name) : name(name) {} + }; + + struct Group { + string name; + vector input; + + Group() {} + Group(const string& name) : name(name) {} + + void append(const string& name) { + input.append({name}); + } + + optional find(const string& name) { + for(unsigned id = 0; id < input.size(); id++) { + if(input[id].name == name) return {true, id}; + } + return false; + } + }; + + struct Device { + uint64_t id = 0; + string name; + vector group; + + uint32_t pathID() const { return (uint32_t)(id >> 32); } + uint32_t deviceID() const { return (uint32_t)(id >> 0); } + uint16_t vendorID() const { return (uint16_t)(id >> 16); } + uint16_t productID() const { return (uint16_t)(id >> 0); } + + virtual bool isNull() const { return false; } + virtual bool isKeyboard() const { return false; } + virtual bool isMouse() const { return false; } + virtual bool isJoypad() const { return false; } + + void append(const string& name) { + group.append({name}); + } + + optional find(const string& name) { + for(unsigned id = 0; id < group.size(); id++) { + if(group[id].name == name) return {true, id}; + } + return false; + } + }; + + struct Null : Device { + Null() { + name = "Null"; + } + + bool isNull() const { return true; } + }; + + struct Keyboard : Device { + enum GroupID : unsigned { Button }; + + Group& button() { return group[GroupID::Button]; } + + Keyboard() { + name = "Keyboard"; + append("Button"); + } + + bool isKeyboard() const { return true; } + }; + + struct Mouse : Device { + enum GroupID : unsigned { Axis, Button }; + + Group& axis() { return group[GroupID::Axis]; } + Group& button() { return group[GroupID::Button]; } + + Mouse() { + name = "Mouse"; + append("Axis"); + append("Button"); + } + + bool isMouse() const { return true; } + }; + + struct Joypad : Device { + enum GroupID : unsigned { Axis, Hat, Trigger, Button }; + + Group& axis() { return group[GroupID::Axis]; } + Group& hat() { return group[GroupID::Hat]; } + Group& trigger() { return group[GroupID::Trigger]; } + Group& button() { return group[GroupID::Button]; } + bool rumble = false; + + Joypad() { + name = "Joypad"; + append("Axis"); + append("Hat"); + append("Trigger"); + append("Button"); + } + + bool isJoypad() const { return true; } + }; +} + +} + +#endif diff --git a/src/dep/libs/nall/http.hpp b/src/dep/libs/nall/http.hpp new file mode 100755 index 000000000..55421867e --- /dev/null +++ b/src/dep/libs/nall/http.hpp @@ -0,0 +1,176 @@ +#ifndef NALL_HTTP_HPP +#define NALL_HTTP_HPP + +#if !defined(_WIN32) + #include + #include + #include + #include +#else + #include + #include + #include +#endif + +#include +#include + +namespace nall { + +struct http { + string hostname; + addrinfo* serverinfo; + int serversocket; + string header; + + inline void download(const string& path, uint8_t*& data, unsigned& size) { + data = nullptr; + size = 0; + + send({ + "GET ", path, " HTTP/1.1\r\n" + "Host: ", hostname, "\r\n" + "Connection: close\r\n" + "\r\n" + }); + + header = downloadHeader(); + downloadContent(data, size); + } + + inline bool connect(string host, unsigned port) { + hostname = host; + + addrinfo hints; + memset(&hints, 0, sizeof(addrinfo)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_PASSIVE; + + int status = getaddrinfo(hostname, string(port), &hints, &serverinfo); + if(status != 0) return false; + + serversocket = socket(serverinfo->ai_family, serverinfo->ai_socktype, serverinfo->ai_protocol); + if(serversocket == -1) return false; + + int result = ::connect(serversocket, serverinfo->ai_addr, serverinfo->ai_addrlen); + if(result == -1) return false; + + return true; + } + + inline bool send(const string& data) { + return send((const uint8_t*)(const char*)data, data.length()); + } + + inline bool send(const uint8_t* data, unsigned size) { + while(size) { + int length = ::send(serversocket, (const char*)data, size, 0); + if(length == -1) return false; + data += length; + size -= length; + } + return true; + } + + inline string downloadHeader() { + string output; + do { + char buffer[2]; + int length = recv(serversocket, buffer, 1, 0); + if(length <= 0) return output; + buffer[1] = 0; + output.append(buffer); + } while(output.endsWith("\r\n\r\n") == false); + return output; + } + + inline string downloadChunkLength() { + string output; + do { + char buffer[2]; + int length = recv(serversocket, buffer, 1, 0); + if(length <= 0) return output; + buffer[1] = 0; + output.append(buffer); + } while(output.endsWith("\r\n") == false); + return output; + } + + inline void downloadContent(uint8_t*& data, unsigned& size) { + unsigned capacity = 0; + + if(header.ifind("\r\nTransfer-Encoding: chunked\r\n")) { + while(true) { + unsigned length = hex(downloadChunkLength()); + if(length == 0) break; + capacity += length; + data = (uint8_t*)realloc(data, capacity); + + char buffer[length]; + while(length) { + int packetlength = recv(serversocket, buffer, length, 0); + if(packetlength <= 0) break; + memcpy(data + size, buffer, packetlength); + size += packetlength; + length -= packetlength; + } + } + } else if(auto position = header.ifind("\r\nContent-Length: ")) { + unsigned length = decimal((const char*)header + position() + 18); + while(length) { + char buffer[256]; + int packetlength = recv(serversocket, buffer, min(256, length), 0); + if(packetlength <= 0) break; + capacity += packetlength; + data = (uint8_t*)realloc(data, capacity); + memcpy(data + size, buffer, packetlength); + size += packetlength; + length -= packetlength; + } + } else { + while(true) { + char buffer[256]; + int packetlength = recv(serversocket, buffer, 256, 0); + if(packetlength <= 0) break; + capacity += packetlength; + data = (uint8_t*)realloc(data, capacity); + memcpy(data + size, buffer, packetlength); + size += packetlength; + } + } + + data = (uint8_t*)realloc(data, capacity + 1); + data[capacity] = 0; + } + + inline void disconnect() { + close(serversocket); + freeaddrinfo(serverinfo); + serverinfo = nullptr; + serversocket = -1; + } + + #ifdef _WIN32 + inline int close(int sock) { + return closesocket(sock); + } + + inline http() { + int sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if(sock == INVALID_SOCKET && WSAGetLastError() == WSANOTINITIALISED) { + WSADATA wsaData; + if(WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { + WSACleanup(); + return; + } + } else { + close(sock); + } + } + #endif +}; + +} + +#endif diff --git a/src/dep/libs/nall/image.hpp b/src/dep/libs/nall/image.hpp new file mode 100755 index 000000000..5f39fce5f --- /dev/null +++ b/src/dep/libs/nall/image.hpp @@ -0,0 +1,22 @@ +#ifndef NALL_IMAGE_HPP +#define NALL_IMAGE_HPP + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#endif diff --git a/src/dep/libs/nall/image/base.hpp b/src/dep/libs/nall/image/base.hpp new file mode 100755 index 000000000..5697ec8dd --- /dev/null +++ b/src/dep/libs/nall/image/base.hpp @@ -0,0 +1,124 @@ +#ifndef NALL_IMAGE_BASE_HPP +#define NALL_IMAGE_BASE_HPP + +namespace nall { + +struct image { + uint8_t* data = nullptr; + unsigned width = 0; + unsigned height = 0; + unsigned pitch = 0; + unsigned size = 0; + + bool endian = 0; //0 = lsb, 1 = msb + unsigned depth = 32; + unsigned stride = 4; + + struct channel { + uint64_t mask; + unsigned depth; + unsigned shift; + + inline bool operator==(const channel& source) { + return mask == source.mask && depth == source.depth && shift == source.shift; + } + + inline bool operator!=(const channel& source) { + return !operator==(source); + } + }; + + channel alpha = {255u << 24, 8u, 24u}; + channel red = {255u << 16, 8u, 16u}; + channel green = {255u << 8, 8u, 8u}; + channel blue = {255u << 0, 8u, 0u}; + + enum class blend : unsigned { + add, + sourceAlpha, //color = sourceColor * sourceAlpha + targetColor * (1 - sourceAlpha) + sourceColor, //color = sourceColor + targetAlpha, //color = targetColor * targetAlpha + sourceColor * (1 - targetAlpha) + targetColor, //color = targetColor + }; + + //static.hpp + static inline unsigned bitDepth(uint64_t color); + static inline unsigned bitShift(uint64_t color); + static inline uint64_t normalize(uint64_t color, unsigned sourceDepth, unsigned targetDepth); + + //core.hpp + inline bool operator==(const image& source); + inline bool operator!=(const image& source); + + inline image& operator=(const image& source); + inline image& operator=(image&& source); + inline image(const image& source); + inline image(image&& source); + inline image(bool endian, unsigned depth, uint64_t alphaMask, uint64_t redMask, uint64_t greenMask, uint64_t blueMask); + inline image(const string& filename); + inline image(const uint8_t* data, unsigned size); + inline image(); + inline ~image(); + + inline uint64_t read(const uint8_t* data) const; + inline void write(uint8_t* data, uint64_t value) const; + + inline void free(); + inline bool empty() const; + inline bool load(const string& filename); + inline void allocate(unsigned width, unsigned height); + + //fill.hpp + inline void fill(uint64_t color = 0); + inline void gradient(uint64_t a, uint64_t b, uint64_t c, uint64_t d); + inline void gradient(uint64_t a, uint64_t b, signed radiusX, signed radiusY, signed centerX, signed centerY, function callback); + inline void crossGradient(uint64_t a, uint64_t b, signed radiusX, signed radiusY, signed centerX, signed centerY); + inline void diamondGradient(uint64_t a, uint64_t b, signed radiusX, signed radiusY, signed centerX, signed centerY); + inline void horizontalGradient(uint64_t a, uint64_t b, signed radiusX, signed radiusY, signed centerX, signed centerY); + inline void radialGradient(uint64_t a, uint64_t b, signed radiusX, signed radiusY, signed centerX, signed centerY); + inline void sphericalGradient(uint64_t a, uint64_t b, signed radiusX, signed radiusY, signed centerX, signed centerY); + inline void squareGradient(uint64_t a, uint64_t b, signed radiusX, signed radiusY, signed centerX, signed centerY); + inline void verticalGradient(uint64_t a, uint64_t b, signed radiusX, signed radiusY, signed centerX, signed centerY); + + //scale.hpp + inline void scale(unsigned width, unsigned height, bool linear = true); + + //blend.hpp + inline void impose(blend mode, unsigned targetX, unsigned targetY, image source, unsigned x, unsigned y, unsigned width, unsigned height); + + //utility.hpp + inline bool crop(unsigned x, unsigned y, unsigned width, unsigned height); + inline void alphaBlend(uint64_t alphaColor); + inline void transform(bool endian, unsigned depth, uint64_t alphaMask, uint64_t redMask, uint64_t greenMask, uint64_t blueMask); + +protected: + //core.hpp + inline uint8_t* allocate(unsigned width, unsigned height, unsigned stride); + + //scale.hpp + inline void scaleLinearWidth(unsigned width); + inline void scaleLinearHeight(unsigned height); + inline void scaleLinear(unsigned width, unsigned height); + inline void scaleNearest(unsigned width, unsigned height); + + //load.hpp + inline bool loadBMP(const string& filename); + inline bool loadPNG(const string& filename); + inline bool loadPNG(const uint8_t* data, unsigned size); + + //interpolation.hpp + alwaysinline void isplit(uint64_t* component, uint64_t color); + alwaysinline uint64_t imerge(const uint64_t* component); + alwaysinline uint64_t interpolate1f(uint64_t a, uint64_t b, double x); + alwaysinline uint64_t interpolate1f(uint64_t a, uint64_t b, uint64_t c, uint64_t d, double x, double y); + alwaysinline uint64_t interpolate1i(int64_t a, int64_t b, uint32_t x); + alwaysinline uint64_t interpolate1i(int64_t a, int64_t b, int64_t c, int64_t d, uint32_t x, uint32_t y); + inline uint64_t interpolate4f(uint64_t a, uint64_t b, double x); + inline uint64_t interpolate4f(uint64_t a, uint64_t b, uint64_t c, uint64_t d, double x, double y); + inline uint64_t interpolate4i(uint64_t a, uint64_t b, uint32_t x); + inline uint64_t interpolate4i(uint64_t a, uint64_t b, uint64_t c, uint64_t d, uint32_t x, uint32_t y); +}; + +} + +#endif diff --git a/src/dep/libs/nall/image/blend.hpp b/src/dep/libs/nall/image/blend.hpp new file mode 100755 index 000000000..6c8997a1e --- /dev/null +++ b/src/dep/libs/nall/image/blend.hpp @@ -0,0 +1,74 @@ +#ifndef NALL_IMAGE_BLEND_HPP +#define NALL_IMAGE_BLEND_HPP + +namespace nall { + +void image::impose(blend mode, unsigned targetX, unsigned targetY, image source, unsigned sourceX, unsigned sourceY, unsigned sourceWidth, unsigned sourceHeight) { + source.transform(endian, depth, alpha.mask, red.mask, green.mask, blue.mask); + + for(unsigned y = 0; y < sourceHeight; y++) { + const uint8_t* sp = source.data + source.pitch * (sourceY + y) + source.stride * sourceX; + uint8_t* dp = data + pitch * (targetY + y) + stride * targetX; + for(unsigned x = 0; x < sourceWidth; x++) { + uint64_t sourceColor = source.read(sp); + uint64_t targetColor = read(dp); + + int64_t sa = (sourceColor & alpha.mask) >> alpha.shift; + int64_t sr = (sourceColor & red.mask ) >> red.shift; + int64_t sg = (sourceColor & green.mask) >> green.shift; + int64_t sb = (sourceColor & blue.mask ) >> blue.shift; + + int64_t da = (targetColor & alpha.mask) >> alpha.shift; + int64_t dr = (targetColor & red.mask ) >> red.shift; + int64_t dg = (targetColor & green.mask) >> green.shift; + int64_t db = (targetColor & blue.mask ) >> blue.shift; + + uint64_t a, r, g, b; + + switch(mode) { + case blend::add: + a = max(sa, da); + r = min(red.mask >> red.shift, ((sr * sa) >> alpha.depth) + ((dr * da) >> alpha.depth)); + g = min(green.mask >> green.shift, ((sg * sa) >> alpha.depth) + ((dg * da) >> alpha.depth)); + b = min(blue.mask >> blue.shift, ((sb * sa) >> alpha.depth) + ((db * da) >> alpha.depth)); + break; + + case blend::sourceAlpha: + a = max(sa, da); + r = dr + (((sr - dr) * sa) >> alpha.depth); + g = dg + (((sg - dg) * sa) >> alpha.depth); + b = db + (((sb - db) * sa) >> alpha.depth); + break; + + case blend::sourceColor: + a = sa; + r = sr; + g = sg; + b = sb; + break; + + case blend::targetAlpha: + a = max(sa, da); + r = sr + (((dr - sr) * da) >> alpha.depth); + g = sg + (((dg - sg) * da) >> alpha.depth); + b = sb + (((db - sb) * da) >> alpha.depth); + break; + + case blend::targetColor: + a = da; + r = dr; + g = dg; + b = db; + break; + } + + write(dp, (a << alpha.shift) | (r << red.shift) | (g << green.shift) | (b << blue.shift)); + sp += source.stride; + dp += stride; + } + } +} + +} + +#endif diff --git a/src/dep/libs/nall/image/core.hpp b/src/dep/libs/nall/image/core.hpp new file mode 100755 index 000000000..5ee040426 --- /dev/null +++ b/src/dep/libs/nall/image/core.hpp @@ -0,0 +1,164 @@ +#ifndef NALL_IMAGE_CORE_HPP +#define NALL_IMAGE_CORE_HPP + +namespace nall { + +bool image::operator==(const image& source) { + if(width != source.width) return false; + if(height != source.height) return false; + if(pitch != source.pitch) return false; + + if(endian != source.endian) return false; + if(stride != source.stride) return false; + + if(alpha != source.alpha) return false; + if(red != source.red) return false; + if(green != source.green) return false; + if(blue != source.blue) return false; + + return memcmp(data, source.data, width * height * stride) == 0; +} + +bool image::operator!=(const image& source) { + return !operator==(source); +} + +image& image::operator=(const image& source) { + free(); + + width = source.width; + height = source.height; + pitch = source.pitch; + size = source.size; + + endian = source.endian; + stride = source.stride; + + alpha = source.alpha; + red = source.red; + green = source.green; + blue = source.blue; + + data = allocate(width, height, stride); + memcpy(data, source.data, source.size); + return *this; +} + +image& image::operator=(image&& source) { + free(); + + width = source.width; + height = source.height; + pitch = source.pitch; + size = source.size; + + endian = source.endian; + stride = source.stride; + + alpha = source.alpha; + red = source.red; + green = source.green; + blue = source.blue; + + data = source.data; + source.data = nullptr; + return *this; +} + +image::image(const image& source) { + operator=(source); +} + +image::image(image&& source) { + operator=(std::forward(source)); +} + +image::image(bool endian, unsigned depth, uint64_t alphaMask, uint64_t redMask, uint64_t greenMask, uint64_t blueMask) { + this->endian = endian; + this->depth = depth; + this->stride = (depth / 8) + ((depth & 7) > 0); + + alpha = {alphaMask, bitDepth(alphaMask), bitShift(alphaMask)}; + red = {redMask, bitDepth(redMask), bitShift(redMask )}; + green = {greenMask, bitDepth(greenMask), bitShift(greenMask)}; + blue = {blueMask, bitDepth(blueMask), bitShift(blueMask )}; +} + +image::image(const string& filename) { + load(filename); +} + +image::image(const uint8_t* data, unsigned size) { + loadPNG(data, size); +} + +image::image() { +} + +image::~image() { + free(); +} + +uint64_t image::read(const uint8_t* data) const { + uint64_t result = 0; + if(endian == 0) { + for(signed n = stride - 1; n >= 0; n--) result = (result << 8) | data[n]; + } else { + for(signed n = 0; n < stride; n++) result = (result << 8) | data[n]; + } + return result; +} + +void image::write(uint8_t* data, uint64_t value) const { + if(endian == 0) { + for(signed n = 0; n < stride; n++) { + data[n] = value; + value >>= 8; + } + } else { + for(signed n = stride - 1; n >= 0; n--) { + data[n] = value; + value >>= 8; + } + } +} + +void image::free() { + if(data) delete[] data; + data = nullptr; +} + +bool image::empty() const { + if(data == nullptr) return true; + if(width == 0 || height == 0) return true; + return false; +} + +bool image::load(const string& filename) { + if(loadBMP(filename) == true) return true; + if(loadPNG(filename) == true) return true; + return false; +} + +void image::allocate(unsigned width, unsigned height) { + if(data != nullptr && this->width == width && this->height == height) return; + free(); + data = allocate(width, height, stride); + pitch = width * stride; + size = height * pitch; + this->width = width; + this->height = height; +} + +uint8_t* image::allocate(unsigned width, unsigned height, unsigned stride) { + //allocate 1x1 larger than requested; so that linear interpolation does not require bounds-checking + unsigned size = width * height * stride; + unsigned padding = width * stride + stride; + uint8_t* data = new uint8_t[size + padding]; + memset(data + size, 0x00, padding); + return data; +} + +} + +#endif diff --git a/src/dep/libs/nall/image/fill.hpp b/src/dep/libs/nall/image/fill.hpp new file mode 100755 index 000000000..8009c40a0 --- /dev/null +++ b/src/dep/libs/nall/image/fill.hpp @@ -0,0 +1,88 @@ +#ifndef NALL_IMAGE_FILL_HPP +#define NALL_IMAGE_FILL_HPP + +namespace nall { + +void image::fill(uint64_t color) { + uint8_t* dp = data; + for(unsigned y = 0; y < height; y++) { + uint8_t* dp = data + pitch * y; + for(unsigned x = 0; x < width; x++) { + write(dp, color); + dp += stride; + } + } +} + +void image::gradient(uint64_t a, uint64_t b, uint64_t c, uint64_t d) { + for(unsigned y = 0; y < height; y++) { + uint8_t* dp = data + pitch * y; + double muY = (double)y / (double)height; + for(unsigned x = 0; x < width; x++) { + double muX = (double)x / (double)width; + write(dp, interpolate4f(a, b, c, d, muX, muY)); + dp += stride; + } + } +} + +void image::gradient(uint64_t a, uint64_t b, signed radiusX, signed radiusY, signed centerX, signed centerY, function callback) { + for(signed y = 0; y < height; y++) { + uint8_t* dp = data + pitch * y; + double py = max(-radiusY, min(+radiusY, y - centerY)) * 1.0 / radiusY; + for(signed x = 0; x < width; x++) { + double px = max(-radiusX, min(+radiusX, x - centerX)) * 1.0 / radiusX; + double mu = max(0.0, min(1.0, callback(px, py))); + if(mu != mu) mu = 1.0; //NaN + write(dp, interpolate4f(a, b, mu)); + dp += stride; + } + } +} + +void image::crossGradient(uint64_t a, uint64_t b, signed radiusX, signed radiusY, signed centerX, signed centerY) { + return gradient(a, b, radiusX, radiusY, centerX, centerY, [](double x, double y) -> double { + x = fabs(x), y = fabs(y); + return min(x, y) * min(x, y); + }); +} + +void image::diamondGradient(uint64_t a, uint64_t b, signed radiusX, signed radiusY, signed centerX, signed centerY) { + return gradient(a, b, radiusX, radiusY, centerX, centerY, [](double x, double y) -> double { + return fabs(x) + fabs(y); + }); +} + +void image::horizontalGradient(uint64_t a, uint64_t b, signed radiusX, signed radiusY, signed centerX, signed centerY) { + return gradient(a, b, radiusX, radiusY, centerX, centerY, [](double x, double y) -> double { + return fabs(x); + }); +} + +void image::radialGradient(uint64_t a, uint64_t b, signed radiusX, signed radiusY, signed centerX, signed centerY) { + return gradient(a, b, radiusX, radiusY, centerX, centerY, [](double x, double y) -> double { + return sqrt(x * x + y * y); + }); +} + +void image::sphericalGradient(uint64_t a, uint64_t b, signed radiusX, signed radiusY, signed centerX, signed centerY) { + return gradient(a, b, radiusX, radiusY, centerX, centerY, [](double x, double y) -> double { + return x * x + y * y; + }); +} + +void image::squareGradient(uint64_t a, uint64_t b, signed radiusX, signed radiusY, signed centerX, signed centerY) { + return gradient(a, b, radiusX, radiusY, centerX, centerY, [](double x, double y) -> double { + return max(fabs(x), fabs(y)); + }); +} + +void image::verticalGradient(uint64_t a, uint64_t b, signed radiusX, signed radiusY, signed centerX, signed centerY) { + return gradient(a, b, radiusX, radiusY, centerX, centerY, [](double x, double y) -> double { + return fabs(y); + }); +} + +} + +#endif diff --git a/src/dep/libs/nall/image/interpolation.hpp b/src/dep/libs/nall/image/interpolation.hpp new file mode 100755 index 000000000..1abe4d940 --- /dev/null +++ b/src/dep/libs/nall/image/interpolation.hpp @@ -0,0 +1,65 @@ +#ifndef NALL_IMAGE_INTERPOLATION_HPP +#define NALL_IMAGE_INTERPOLATION_HPP + +namespace nall { + +void image::isplit(uint64_t* c, uint64_t color) { + c[0] = (color & alpha.mask) >> alpha.shift; + c[1] = (color & red.mask ) >> red.shift; + c[2] = (color & green.mask) >> green.shift; + c[3] = (color & blue.mask ) >> blue.shift; +} + +uint64_t image::imerge(const uint64_t* c) { + return c[0] << alpha.shift | c[1] << red.shift | c[2] << green.shift | c[3] << blue.shift; +} + +uint64_t image::interpolate1f(uint64_t a, uint64_t b, double x) { + return a * (1.0 - x) + b * x; +} + +uint64_t image::interpolate1f(uint64_t a, uint64_t b, uint64_t c, uint64_t d, double x, double y) { + return a * (1.0 - x) * (1.0 - y) + b * x * (1.0 - y) + c * (1.0 - x) * y + d * x * y; +} + +uint64_t image::interpolate1i(int64_t a, int64_t b, uint32_t x) { + return a + (((b - a) * x) >> 32); //a + (b - a) * x +} + +uint64_t image::interpolate1i(int64_t a, int64_t b, int64_t c, int64_t d, uint32_t x, uint32_t y) { + a = a + (((b - a) * x) >> 32); //a + (b - a) * x + c = c + (((d - c) * x) >> 32); //c + (d - c) * x + return a + (((c - a) * y) >> 32); //a + (c - a) * y +} + +uint64_t image::interpolate4f(uint64_t a, uint64_t b, double x) { + uint64_t o[4], pa[4], pb[4]; + isplit(pa, a), isplit(pb, b); + for(unsigned n = 0; n < 4; n++) o[n] = interpolate1f(pa[n], pb[n], x); + return imerge(o); +} + +uint64_t image::interpolate4f(uint64_t a, uint64_t b, uint64_t c, uint64_t d, double x, double y) { + uint64_t o[4], pa[4], pb[4], pc[4], pd[4]; + isplit(pa, a), isplit(pb, b), isplit(pc, c), isplit(pd, d); + for(unsigned n = 0; n < 4; n++) o[n] = interpolate1f(pa[n], pb[n], pc[n], pd[n], x, y); + return imerge(o); +} + +uint64_t image::interpolate4i(uint64_t a, uint64_t b, uint32_t x) { + uint64_t o[4], pa[4], pb[4]; + isplit(pa, a), isplit(pb, b); + for(unsigned n = 0; n < 4; n++) o[n] = interpolate1i(pa[n], pb[n], x); + return imerge(o); +} + +uint64_t image::interpolate4i(uint64_t a, uint64_t b, uint64_t c, uint64_t d, uint32_t x, uint32_t y) { + uint64_t o[4], pa[4], pb[4], pc[4], pd[4]; + isplit(pa, a), isplit(pb, b), isplit(pc, c), isplit(pd, d); + for(unsigned n = 0; n < 4; n++) o[n] = interpolate1i(pa[n], pb[n], pc[n], pd[n], x, y); + return imerge(o); +} + +} + +#endif diff --git a/src/dep/libs/nall/image/load.hpp b/src/dep/libs/nall/image/load.hpp new file mode 100755 index 000000000..6ac17d800 --- /dev/null +++ b/src/dep/libs/nall/image/load.hpp @@ -0,0 +1,98 @@ +#ifndef NALL_IMAGE_LOAD_HPP +#define NALL_IMAGE_LOAD_HPP + +namespace nall { + +bool image::loadBMP(const string& filename) { + uint32_t* outputData; + unsigned outputWidth, outputHeight; + if(bmp::read(filename, outputData, outputWidth, outputHeight) == false) return false; + + allocate(outputWidth, outputHeight); + const uint32_t* sp = outputData; + uint8_t* dp = data; + + for(unsigned y = 0; y < outputHeight; y++) { + for(unsigned x = 0; x < outputWidth; x++) { + uint32_t color = *sp++; + uint64_t a = normalize((uint8_t)(color >> 24), 8, alpha.depth); + uint64_t r = normalize((uint8_t)(color >> 16), 8, red.depth); + uint64_t g = normalize((uint8_t)(color >> 8), 8, green.depth); + uint64_t b = normalize((uint8_t)(color >> 0), 8, blue.depth); + write(dp, (a << alpha.shift) | (r << red.shift) | (g << green.shift) | (b << blue.shift)); + dp += stride; + } + } + + delete[] outputData; + return true; +} + +bool image::loadPNG(const string& filename) { + if(!file::exists(filename)) return false; + auto buffer = file::read(filename); + return loadPNG(buffer.data(), buffer.size()); +} + +bool image::loadPNG(const uint8_t* pngData, unsigned pngSize) { + png source; + if(source.decode(pngData, pngSize) == false) return false; + + allocate(source.info.width, source.info.height); + const uint8_t* sp = source.data; + uint8_t* dp = data; + + auto decode = [&]() -> uint64_t { + uint64_t p, r, g, b, a; + + switch(source.info.colorType) { + case 0: //L + r = g = b = source.readbits(sp); + a = (1 << source.info.bitDepth) - 1; + break; + case 2: //R,G,B + r = source.readbits(sp); + g = source.readbits(sp); + b = source.readbits(sp); + a = (1 << source.info.bitDepth) - 1; + break; + case 3: //P + p = source.readbits(sp); + r = source.info.palette[p][0]; + g = source.info.palette[p][1]; + b = source.info.palette[p][2]; + a = (1 << source.info.bitDepth) - 1; + break; + case 4: //L,A + r = g = b = source.readbits(sp); + a = source.readbits(sp); + break; + case 6: //R,G,B,A + r = source.readbits(sp); + g = source.readbits(sp); + b = source.readbits(sp); + a = source.readbits(sp); + break; + } + + a = normalize(a, source.info.bitDepth, alpha.depth); + r = normalize(r, source.info.bitDepth, red.depth); + g = normalize(g, source.info.bitDepth, green.depth); + b = normalize(b, source.info.bitDepth, blue.depth); + + return (a << alpha.shift) | (r << red.shift) | (g << green.shift) | (b << blue.shift); + }; + + for(unsigned y = 0; y < height; y++) { + for(unsigned x = 0; x < width; x++) { + write(dp, decode()); + dp += stride; + } + } + + return true; +} + +} + +#endif diff --git a/src/dep/libs/nall/image/scale.hpp b/src/dep/libs/nall/image/scale.hpp new file mode 100755 index 000000000..9e6cdda04 --- /dev/null +++ b/src/dep/libs/nall/image/scale.hpp @@ -0,0 +1,190 @@ +#ifndef NALL_IMAGE_SCALE_HPP +#define NALL_IMAGE_SCALE_HPP + +namespace nall { + +void image::scale(unsigned outputWidth, unsigned outputHeight, bool linear) { + if(width == outputWidth && height == outputHeight) return; //no scaling necessary + if(linear == false) return scaleNearest(outputWidth, outputHeight); + + if(width == outputWidth ) return scaleLinearHeight(outputHeight); + if(height == outputHeight) return scaleLinearWidth(outputWidth); + + //find fastest scaling method, based on number of interpolation operations required + //magnification usually benefits from two-pass linear interpolation + //minification usually benefits from one-pass bilinear interpolation + unsigned d1wh = ((width * outputWidth ) + (outputWidth * outputHeight)) * 1; + unsigned d1hw = ((height * outputHeight) + (outputWidth * outputHeight)) * 1; + unsigned d2wh = (outputWidth * outputHeight) * 3; + + if(d1wh <= d1hw && d1wh <= d2wh) return scaleLinearWidth(outputWidth), scaleLinearHeight(outputHeight); + if(d1hw <= d2wh) return scaleLinearHeight(outputHeight), scaleLinearWidth(outputWidth); + return scaleLinear(outputWidth, outputHeight); +} + +void image::scaleLinearWidth(unsigned outputWidth) { + uint8_t* outputData = allocate(outputWidth, height, stride); + unsigned outputPitch = outputWidth * stride; + uint64_t xstride = ((uint64_t)(width - 1) << 32) / max(1u, outputWidth - 1); + + #pragma omp parallel for + for(unsigned y = 0; y < height; y++) { + uint64_t xfraction = 0; + + const uint8_t* sp = data + pitch * y; + uint8_t* dp = outputData + outputPitch * y; + + uint64_t a = read(sp); + uint64_t b = read(sp + stride); + sp += stride; + + unsigned x = 0; + while(true) { + while(xfraction < 0x100000000 && x++ < outputWidth) { + write(dp, interpolate4i(a, b, xfraction)); + dp += stride; + xfraction += xstride; + } + if(x >= outputWidth) break; + + sp += stride; + a = b; + b = read(sp); + xfraction -= 0x100000000; + } + } + + free(); + data = outputData; + width = outputWidth; + pitch = outputPitch; + size = height * pitch; +} + +void image::scaleLinearHeight(unsigned outputHeight) { + uint8_t* outputData = allocate(width, outputHeight, stride); + uint64_t ystride = ((uint64_t)(height - 1) << 32) / max(1u, outputHeight - 1); + + #pragma omp parallel for + for(unsigned x = 0; x < width; x++) { + uint64_t yfraction = 0; + + const uint8_t* sp = data + stride * x; + uint8_t* dp = outputData + stride * x; + + uint64_t a = read(sp); + uint64_t b = read(sp + pitch); + sp += pitch; + + unsigned y = 0; + while(true) { + while(yfraction < 0x100000000 && y++ < outputHeight) { + write(dp, interpolate4i(a, b, yfraction)); + dp += pitch; + yfraction += ystride; + } + if(y >= outputHeight) break; + + sp += pitch; + a = b; + b = read(sp); + yfraction -= 0x100000000; + } + } + + free(); + data = outputData; + height = outputHeight; + size = height * pitch; +} + +void image::scaleLinear(unsigned outputWidth, unsigned outputHeight) { + uint8_t* outputData = allocate(outputWidth, outputHeight, stride); + unsigned outputPitch = outputWidth * stride; + + uint64_t xstride = ((uint64_t)(width - 1) << 32) / max(1u, outputWidth - 1); + uint64_t ystride = ((uint64_t)(height - 1) << 32) / max(1u, outputHeight - 1); + + #pragma omp parallel for + for(unsigned y = 0; y < outputHeight; y++) { + uint64_t yfraction = ystride * y; + uint64_t xfraction = 0; + + const uint8_t* sp = data + pitch * (yfraction >> 32); + uint8_t* dp = outputData + outputPitch * y; + + uint64_t a = read(sp); + uint64_t b = read(sp + stride); + uint64_t c = read(sp + pitch); + uint64_t d = read(sp + pitch + stride); + sp += stride; + + unsigned x = 0; + while(true) { + while(xfraction < 0x100000000 && x++ < outputWidth) { + write(dp, interpolate4i(a, b, c, d, xfraction, yfraction)); + dp += stride; + xfraction += xstride; + } + if(x >= outputWidth) break; + + sp += stride; + a = b; + c = d; + b = read(sp); + d = read(sp + pitch); + xfraction -= 0x100000000; + } + } + + free(); + data = outputData; + width = outputWidth; + height = outputHeight; + pitch = outputPitch; + size = height * pitch; +} + +void image::scaleNearest(unsigned outputWidth, unsigned outputHeight) { + uint8_t* outputData = allocate(outputWidth, outputHeight, stride); + unsigned outputPitch = outputWidth * stride; + + uint64_t xstride = ((uint64_t)width << 32) / outputWidth; + uint64_t ystride = ((uint64_t)height << 32) / outputHeight; + + #pragma omp parallel for + for(unsigned y = 0; y < outputHeight; y++) { + uint64_t yfraction = ystride * y; + uint64_t xfraction = 0; + + const uint8_t* sp = data + pitch * (yfraction >> 32); + uint8_t* dp = outputData + outputPitch * y; + + uint64_t a = read(sp); + + unsigned x = 0; + while(true) { + while(xfraction < 0x100000000 && x++ < outputWidth) { + write(dp, a); + dp += stride; + xfraction += xstride; + } + if(x >= outputWidth) break; + + sp += stride; + a = read(sp); + xfraction -= 0x100000000; + } + } + + free(); + data = outputData; + width = outputWidth; + height = outputHeight; + pitch = outputPitch; + size = height * pitch; +} + +} + +#endif diff --git a/src/dep/libs/nall/image/static.hpp b/src/dep/libs/nall/image/static.hpp new file mode 100755 index 000000000..161407966 --- /dev/null +++ b/src/dep/libs/nall/image/static.hpp @@ -0,0 +1,31 @@ +#ifndef NALL_IMAGE_STATIC_HPP +#define NALL_IMAGE_STATIC_HPP + +namespace nall { + +unsigned image::bitDepth(uint64_t color) { + unsigned depth = 0; + if(color) while((color & 1) == 0) color >>= 1; + while((color & 1) == 1) { color >>= 1; depth++; } + return depth; +} + +unsigned image::bitShift(uint64_t color) { + unsigned shift = 0; + if(color) while((color & 1) == 0) { color >>= 1; shift++; } + return shift; +} + +uint64_t image::normalize(uint64_t color, unsigned sourceDepth, unsigned targetDepth) { + if(sourceDepth == 0 || targetDepth == 0) return 0; + while(sourceDepth < targetDepth) { + color = (color << sourceDepth) | color; + sourceDepth += sourceDepth; + } + if(targetDepth < sourceDepth) color >>= (sourceDepth - targetDepth); + return color; +} + +} + +#endif diff --git a/src/dep/libs/nall/image/utility.hpp b/src/dep/libs/nall/image/utility.hpp new file mode 100755 index 000000000..7b96ee9f9 --- /dev/null +++ b/src/dep/libs/nall/image/utility.hpp @@ -0,0 +1,95 @@ +#ifndef NALL_IMAGE_UTILITY_HPP +#define NALL_IMAGE_UTILITY_HPP + +namespace nall { + +bool image::crop(unsigned outputX, unsigned outputY, unsigned outputWidth, unsigned outputHeight) { + if(outputX + outputWidth > width) return false; + if(outputY + outputHeight > height) return false; + + uint8_t* outputData = allocate(outputWidth, outputHeight, stride); + unsigned outputPitch = outputWidth * stride; + + #pragma omp parallel for + for(unsigned y = 0; y < outputHeight; y++) { + const uint8_t* sp = data + pitch * (outputY + y) + stride * outputX; + uint8_t* dp = outputData + outputPitch * y; + for(unsigned x = 0; x < outputWidth; x++) { + write(dp, read(sp)); + sp += stride; + dp += stride; + } + } + + delete[] data; + data = outputData; + width = outputWidth; + height = outputHeight; + pitch = outputPitch; + size = width * pitch; + return true; +} + +void image::alphaBlend(uint64_t alphaColor) { + uint64_t alphaR = (alphaColor & red.mask ) >> red.shift; + uint64_t alphaG = (alphaColor & green.mask) >> green.shift; + uint64_t alphaB = (alphaColor & blue.mask ) >> blue.shift; + + #pragma omp parallel for + for(unsigned y = 0; y < height; y++) { + uint8_t* dp = data + pitch * y; + for(unsigned x = 0; x < width; x++) { + uint64_t color = read(dp); + + uint64_t colorA = (color & alpha.mask) >> alpha.shift; + uint64_t colorR = (color & red.mask ) >> red.shift; + uint64_t colorG = (color & green.mask) >> green.shift; + uint64_t colorB = (color & blue.mask ) >> blue.shift; + double alphaScale = (double)colorA / (double)((1 << alpha.depth) - 1); + + colorA = (1 << alpha.depth) - 1; + colorR = (colorR * alphaScale) + (alphaR * (1.0 - alphaScale)); + colorG = (colorG * alphaScale) + (alphaG * (1.0 - alphaScale)); + colorB = (colorB * alphaScale) + (alphaB * (1.0 - alphaScale)); + + write(dp, (colorA << alpha.shift) | (colorR << red.shift) | (colorG << green.shift) | (colorB << blue.shift)); + dp += stride; + } + } +} + +void image::transform(bool outputEndian, unsigned outputDepth, uint64_t outputAlphaMask, uint64_t outputRedMask, uint64_t outputGreenMask, uint64_t outputBlueMask) { + if(endian == outputEndian && depth == outputDepth && alpha.mask == outputAlphaMask && red.mask == outputRedMask && green.mask == outputGreenMask && blue.mask == outputBlueMask) return; + + image output(outputEndian, outputDepth, outputAlphaMask, outputRedMask, outputGreenMask, outputBlueMask); + output.allocate(width, height); + + #pragma omp parallel for + for(unsigned y = 0; y < height; y++) { + const uint8_t* sp = data + pitch * y; + uint8_t* dp = output.data + output.pitch * y; + for(unsigned x = 0; x < width; x++) { + uint64_t color = read(sp); + sp += stride; + + uint64_t a = (color & alpha.mask) >> alpha.shift; + uint64_t r = (color & red.mask) >> red.shift; + uint64_t g = (color & green.mask) >> green.shift; + uint64_t b = (color & blue.mask) >> blue.shift; + + a = normalize(a, alpha.depth, output.alpha.depth); + r = normalize(r, red.depth, output.red.depth); + g = normalize(g, green.depth, output.green.depth); + b = normalize(b, blue.depth, output.blue.depth); + + output.write(dp, (a << output.alpha.shift) | (r << output.red.shift) | (g << output.green.shift) | (b << output.blue.shift)); + dp += output.stride; + } + } + + operator=(std::move(output)); +} + +} + +#endif diff --git a/src/dep/libs/nall/inflate.hpp b/src/dep/libs/nall/inflate.hpp new file mode 100755 index 000000000..d096d1736 --- /dev/null +++ b/src/dep/libs/nall/inflate.hpp @@ -0,0 +1,349 @@ +#ifndef NALL_INFLATE_HPP +#define NALL_INFLATE_HPP + +#include + +namespace nall { + +namespace puff { + inline int puff( + unsigned char* dest, unsigned long* destlen, + unsigned char* source, unsigned long* sourcelen + ); +} + +inline bool inflate( + uint8_t* target, unsigned targetLength, + const uint8_t* source, unsigned sourceLength +) { + unsigned long tl = targetLength, sl = sourceLength; + int result = puff::puff((unsigned char*)target, &tl, (unsigned char*)source, &sl); + return result == 0; +} + +namespace puff { + +enum { + MAXBITS = 15, + MAXLCODES = 286, + MAXDCODES = 30, + FIXLCODES = 288, + MAXCODES = MAXLCODES + MAXDCODES, +}; + +struct state { + unsigned char* out; + unsigned long outlen; + unsigned long outcnt; + + unsigned char* in; + unsigned long inlen; + unsigned long incnt; + int bitbuf; + int bitcnt; + + jmp_buf env; +}; + +struct huffman { + short* count; + short* symbol; +}; + +inline int bits(state* s, int need) { + long val; + + val = s->bitbuf; + while(s->bitcnt < need) { + if(s->incnt == s->inlen) longjmp(s->env, 1); + val |= (long)(s->in[s->incnt++]) << s->bitcnt; + s->bitcnt += 8; + } + + s->bitbuf = (int)(val >> need); + s->bitcnt -= need; + + return (int)(val & ((1L << need) - 1)); +} + +inline int stored(state* s) { + unsigned len; + + s->bitbuf = 0; + s->bitcnt = 0; + + if(s->incnt + 4 > s->inlen) return 2; + len = s->in[s->incnt++]; + len |= s->in[s->incnt++] << 8; + if(s->in[s->incnt++] != (~len & 0xff) || + s->in[s->incnt++] != ((~len >> 8) & 0xff) + ) return 2; + + if(s->incnt + len > s->inlen) return 2; + if(s->out != nullptr) { + if(s->outcnt + len > s->outlen) return 1; + while(len--) s->out[s->outcnt++] = s->in[s->incnt++]; + } else { + s->outcnt += len; + s->incnt += len; + } + + return 0; +} + +inline int decode(state* s, huffman* h) { + int len, code, first, count, index, bitbuf, left; + short* next; + + bitbuf = s->bitbuf; + left = s->bitcnt; + code = first = index = 0; + len = 1; + next = h->count + 1; + while(true) { + while(left--) { + code |= bitbuf & 1; + bitbuf >>= 1; + count = *next++; + if(code - count < first) { + s->bitbuf = bitbuf; + s->bitcnt = (s->bitcnt - len) & 7; + return h->symbol[index + (code - first)]; + } + index += count; + first += count; + first <<= 1; + code <<= 1; + len++; + } + left = (MAXBITS + 1) - len; + if(left == 0) break; + if(s->incnt == s->inlen) longjmp(s->env, 1); + bitbuf = s->in[s->incnt++]; + if(left > 8) left = 8; + } + + return -10; +} + +inline int construct(huffman* h, short* length, int n) { + int symbol, len, left; + short offs[MAXBITS + 1]; + + for(len = 0; len <= MAXBITS; len++) h->count[len] = 0; + for(symbol = 0; symbol < n; symbol++) h->count[length[symbol]]++; + if(h->count[0] == n) return 0; + + left = 1; + for(len = 1; len <= MAXBITS; len++) { + left <<= 1; + left -= h->count[len]; + if(left < 0) return left; + } + + offs[1] = 0; + for(len = 1; len < MAXBITS; len++) offs[len + 1] = offs[len] + h->count[len]; + + for(symbol = 0; symbol < n; symbol++) { + if(length[symbol] != 0) h->symbol[offs[length[symbol]]++] = symbol; + } + + return left; +} + +inline int codes(state* s, huffman* lencode, huffman* distcode) { + int symbol, len; + unsigned dist; + static const short lens[29] = { + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, + 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258 + }; + static const short lext[29] = { + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, + 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0 + }; + static const short dists[30] = { + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, + 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, + 8193, 12289, 16385, 24577 + }; + static const short dext[30] = { + 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, + 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, + 12, 12, 13, 13 + }; + + do { + symbol = decode(s, lencode); + if(symbol < 0) return symbol; + if(symbol < 256) { + if(s->out != nullptr) { + if(s->outcnt == s->outlen) return 1; + s->out[s->outcnt] = symbol; + } + s->outcnt++; + } else if(symbol > 256) { + symbol -= 257; + if(symbol >= 29) return -10; + len = lens[symbol] + bits(s, lext[symbol]); + + symbol = decode(s, distcode); + if(symbol < 0) return symbol; + dist = dists[symbol] + bits(s, dext[symbol]); + #ifndef INFLATE_ALLOW_INVALID_DISTANCE_TOO_FAR + if(dist > s->outcnt) return -11; + #endif + + if(s->out != nullptr) { + if(s->outcnt + len > s->outlen) return 1; + while(len--) { + s->out[s->outcnt] = + #ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOO_FAR + dist > s->outcnt ? 0 : + #endif + s->out[s->outcnt - dist]; + s->outcnt++; + } + } else { + s->outcnt += len; + } + } + } while(symbol != 256); + + return 0; +} + +inline int fixed(state* s) { + static int virgin = 1; + static short lencnt[MAXBITS + 1], lensym[FIXLCODES]; + static short distcnt[MAXBITS + 1], distsym[MAXDCODES]; + static huffman lencode, distcode; + + if(virgin) { + int symbol = 0; + short lengths[FIXLCODES]; + + lencode.count = lencnt; + lencode.symbol = lensym; + distcode.count = distcnt; + distcode.symbol = distsym; + + for(; symbol < 144; symbol++) lengths[symbol] = 8; + for(; symbol < 256; symbol++) lengths[symbol] = 9; + for(; symbol < 280; symbol++) lengths[symbol] = 7; + for(; symbol < FIXLCODES; symbol++) lengths[symbol] = 8; + construct(&lencode, lengths, FIXLCODES); + + for(symbol = 0; symbol < MAXDCODES; symbol++) lengths[symbol] = 5; + construct(&distcode, lengths, MAXDCODES); + + virgin = 0; + } + + return codes(s, &lencode, &distcode); +} + +inline int dynamic(state* s) { + int nlen, ndist, ncode, index, err; + short lengths[MAXCODES]; + short lencnt[MAXBITS + 1], lensym[MAXLCODES]; + short distcnt[MAXBITS + 1], distsym[MAXDCODES]; + huffman lencode, distcode; + static const short order[19] = { + 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 + }; + + lencode.count = lencnt; + lencode.symbol = lensym; + distcode.count = distcnt; + distcode.symbol = distsym; + + nlen = bits(s, 5) + 257; + ndist = bits(s, 5) + 1; + ncode = bits(s, 4) + 4; + if(nlen > MAXLCODES || ndist > MAXDCODES) return -3; + + for(index = 0; index < ncode; index++) lengths[order[index]] = bits(s, 3); + for(; index < 19; index++) lengths[order[index]] = 0; + + err = construct(&lencode, lengths, 19); + if(err != 0) return -4; + + index = 0; + while(index < nlen + ndist) { + int symbol, len; + + symbol = decode(s, &lencode); + if(symbol < 16) { + lengths[index++] = symbol; + } else { + len = 0; + if(symbol == 16) { + if(index == 0) return -5; + len = lengths[index - 1]; + symbol = 3 + bits(s, 2); + } else if(symbol == 17) { + symbol = 3 + bits(s, 3); + } else { + symbol = 11 + bits(s, 7); + } + if(index + symbol > nlen + ndist) return -6; + while(symbol--) lengths[index++] = len; + } + } + + if(lengths[256] == 0) return -9; + + err = construct(&lencode, lengths, nlen); + if(err < 0 || (err > 0 && nlen - lencode.count[0] != 1)) return -7; + + err = construct(&distcode, lengths + nlen, ndist); + if(err < 0 || (err > 0 && ndist - distcode.count[0] != 1)) return -8; + + return codes(s, &lencode, &distcode); +} + +inline int puff( + unsigned char* dest, unsigned long* destlen, + unsigned char* source, unsigned long* sourcelen +) { + state s; + int last, type, err; + + s.out = dest; + s.outlen = *destlen; + s.outcnt = 0; + + s.in = source; + s.inlen = *sourcelen; + s.incnt = 0; + s.bitbuf = 0; + s.bitcnt = 0; + + if(setjmp(s.env) != 0) { + err = 2; + } else { + do { + last = bits(&s, 1); + type = bits(&s, 2); + err = type == 0 ? stored(&s) + : type == 1 ? fixed(&s) + : type == 2 ? dynamic(&s) + : -1; + if(err != 0) break; + } while(!last); + } + + if(err <= 0) { + *destlen = s.outcnt; + *sourcelen = s.incnt; + } + + return err; +} + +} + +} + +#endif diff --git a/src/dep/libs/nall/interpolation.hpp b/src/dep/libs/nall/interpolation.hpp new file mode 100755 index 000000000..afc7108b9 --- /dev/null +++ b/src/dep/libs/nall/interpolation.hpp @@ -0,0 +1,59 @@ +#ifndef NALL_INTERPOLATION_HPP +#define NALL_INTERPOLATION_HPP + +namespace nall { + +struct Interpolation { + static inline double Nearest(double mu, double a, double b, double c, double d) { + return (mu <= 0.5 ? b : c); + } + + static inline double Sublinear(double mu, double a, double b, double c, double d) { + mu = ((mu - 0.5) * 2.0) + 0.5; + if(mu < 0) mu = 0; + if(mu > 1) mu = 1; + return b * (1.0 - mu) + c * mu; + } + + static inline double Linear(double mu, double a, double b, double c, double d) { + return b * (1.0 - mu) + c * mu; + } + + static inline double Cosine(double mu, double a, double b, double c, double d) { + mu = (1.0 - cos(mu * 3.14159265)) / 2.0; + return b * (1.0 - mu) + c * mu; + } + + static inline double Cubic(double mu, double a, double b, double c, double d) { + double A = d - c - a + b; + double B = a - b - A; + double C = c - a; + double D = b; + return A * (mu * mu * mu) + B * (mu * mu) + C * mu + D; + } + + static inline double Hermite(double mu1, double a, double b, double c, double d) { + const double tension = 0.0; //-1 = low, 0 = normal, +1 = high + const double bias = 0.0; //-1 = left, 0 = even, +1 = right + double mu2, mu3, m0, m1, a0, a1, a2, a3; + + mu2 = mu1 * mu1; + mu3 = mu2 * mu1; + + m0 = (b - a) * (1.0 + bias) * (1.0 - tension) / 2.0; + m0 += (c - b) * (1.0 - bias) * (1.0 - tension) / 2.0; + m1 = (c - b) * (1.0 + bias) * (1.0 - tension) / 2.0; + m1 += (d - c) * (1.0 - bias) * (1.0 - tension) / 2.0; + + a0 = +2 * mu3 - 3 * mu2 + 1; + a1 = mu3 - 2 * mu2 + mu1; + a2 = mu3 - mu2; + a3 = -2 * mu3 + 3 * mu2; + + return (a0 * b) + (a1 * m0) + (a2 * m1) + (a3 * c); + } +}; + +} + +#endif diff --git a/src/dep/libs/nall/intrinsics.hpp b/src/dep/libs/nall/intrinsics.hpp new file mode 100755 index 000000000..2d6a43ed1 --- /dev/null +++ b/src/dep/libs/nall/intrinsics.hpp @@ -0,0 +1,82 @@ +#ifndef NALL_INTRINSICS_HPP +#define NALL_INTRINSICS_HPP + +namespace nall { + +struct Intrinsics { + enum class Compiler : unsigned { Clang, GCC, VisualCPP, Unknown }; + enum class Platform : unsigned { Windows, MacOSX, X, Unknown }; //X = Linux, BSD, etc + enum class Architecture : unsigned { x86, amd64, Unknown }; + enum class Endian : unsigned { LSB, MSB, Unknown }; + + static inline Compiler compiler(); + static inline Platform platform(); + static inline Architecture architecture(); + static inline Endian endian(); +}; + +/* Compiler detection */ + +#if defined(__clang__) + #define COMPILER_CLANG + Intrinsics::Compiler Intrinsics::compiler() { return Intrinsics::Compiler::Clang; } +#elif defined(__GNUC__) + #define COMPILER_GCC + Intrinsics::Compiler Intrinsics::compiler() { return Intrinsics::Compiler::GCC; } +#elif defined(_MSC_VER) + #define COMPILER_VISUALCPP + Intrinsics::Compiler Intrinsics::compiler() { return Intrinsics::Compiler::VisualCPP; } +#else + #warning "unable to detect compiler" + #define COMPILER_UNKNOWN + Intrinsics::Compiler Intrinsics::compiler() { return Intrinsics::Compiler::Unknown; } +#endif + +/* Platform detection */ + +#if defined(_WIN32) + #define PLATFORM_WINDOWS + Intrinsics::Platform Intrinsics::platform() { return Intrinsics::Platform::Windows; } +#elif defined(__APPLE__) + #define PLATFORM_MACOSX + Intrinsics::Platform Intrinsics::platform() { return Intrinsics::Platform::MacOSX; } +#elif defined(linux) || defined(__linux__) || defined(__sun__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__GNU__) + #define PLATFORM_X + Intrinsics::Platform Intrinsics::platform() { return Intrinsics::Platform::X; } +#else + #warning "unable to detect platform" + #define PLATFORM_UNKNOWN + Intrinsics::Platform Intrinsics::platform() { return Intrinsics::Platform::Unknown; } +#endif + +/* Architecture Detection */ + +#if defined(__i386__) || defined(_M_IX86) + #define ARCH_X86 + Intrinsics::Architecture Intrinsics::architecture() { return Intrinsics::Architecture::x86; } +#elif defined(__amd64__) || defined(_M_AMD64) + #define ARCH_AMD64 + Intrinsics::Architecture Intrinsics::architecture() { return Intrinsics::Architecture::amd64; } +#else + #warning "unable to detect architecture" + #define ARCH_UNKNOWN + Intrinsics::Architecture Intrinsics::architecture() { return Intrinsics::Architecture::Unknown; } +#endif + +/* Endian detection */ + +#if (defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && __BYTE_ORDER == __LITTLE_ENDIAN) || defined(__LITTLE_ENDIAN__) || defined(__i386__) || defined(__amd64__) || defined(_M_IX86) || defined(_M_AMD64) + #define ENDIAN_LSB + Intrinsics::Endian Intrinsics::endian() { return Intrinsics::Endian::LSB; } +#elif (defined(__BYTE_ORDER) && defined(__BIG_ENDIAN) && __BYTE_ORDER == __BIG_ENDIAN) || defined(__BIG_ENDIAN__) || defined(__powerpc__) || defined(_M_PPC) + #define ENDIAN_MSB + Intrinsics::Endian Intrinsics::endian() { return Intrinsics::Endian::MSB; } +#else + #warning "unable to detect endian" + #define ENDIAN_UNKNOWN + Intrinsics::Endian Intrinsics::endian() { return Intrinsics::Endian::Unknown; } +#endif + +} + +#endif diff --git a/src/dep/libs/nall/invoke.hpp b/src/dep/libs/nall/invoke.hpp new file mode 100755 index 000000000..3e6d29cbe --- /dev/null +++ b/src/dep/libs/nall/invoke.hpp @@ -0,0 +1,58 @@ +#ifndef NALL_INVOKE_HPP +#define NALL_INVOKE_HPP + +//void invoke(const string &name, const string& args...); +//if a program is specified, it is executed with the arguments provided +//if a file is specified, the file is opened using the program associated with said file type +//if a folder is specified, the folder is opened using the associated file explorer +//if a URL is specified, the default web browser is opened and pointed at the URL requested +//path environment variable is always consulted +//execution is asynchronous (non-blocking); use system() for synchronous execution + +#include +#include + +#if defined(PLATFORM_WINDOWS) + #include +#endif + +namespace nall { + +#if defined(PLATFORM_WINDOWS) + +template inline void invoke(const string& name, Args&&... args) { + lstring argl(std::forward(args)...); + for(auto& arg : argl) if(arg.find(" ")) arg = {"\"", arg, "\""}; + string arguments = argl.merge(" "); + ShellExecuteW(NULL, NULL, utf16_t(name), utf16_t(arguments), NULL, SW_SHOWNORMAL); +} + +#elif defined(PLATFORM_X) + +template inline void invoke(const string& name, Args&&... args) { + pid_t pid = fork(); + if(pid == 0) { + const char* argv[1 + sizeof...(args) + 1]; + const char** argp = argv; + lstring argl(std::forward(args)...); + *argp++ = (const char*)name; + for(auto &arg : argl) *argp++ = (const char*)arg; + *argp++ = nullptr; + + if(execvp(name, (char* const*)argv) < 0) { + execlp("xdg-open", "xdg-open", (const char*)name, nullptr); + } + exit(0); + } +} + +#else + +template inline void invoke(const string& name, Args&&... args) { +} + +#endif + +} + +#endif diff --git a/src/dep/libs/nall/ips.hpp b/src/dep/libs/nall/ips.hpp new file mode 100755 index 000000000..1159dee1c --- /dev/null +++ b/src/dep/libs/nall/ips.hpp @@ -0,0 +1,100 @@ +#ifndef NALL_IPS_HPP +#define NALL_IPS_HPP + +#include +#include +#include + +namespace nall { + +struct ips { + inline bool apply(); + inline void source(const uint8_t* data, unsigned size); + inline void modify(const uint8_t* data, unsigned size); + inline ips(); + inline ~ips(); + + uint8_t* data = nullptr; + unsigned size = 0; + const uint8_t* sourceData = nullptr; + unsigned sourceSize = 0; + const uint8_t* modifyData = nullptr; + unsigned modifySize = 0; +}; + +bool ips::apply() { + if(modifySize < 8) return false; + if(modifyData[0] != 'P') return false; + if(modifyData[1] != 'A') return false; + if(modifyData[2] != 'T') return false; + if(modifyData[3] != 'C') return false; + if(modifyData[4] != 'H') return false; + + if(data) delete[] data; + data = new uint8_t[16 * 1024 * 1024 + 65536](); //maximum size of IPS patch + single-tag padding + size = sourceSize; + memcpy(data, sourceData, sourceSize); + unsigned offset = 5; + + while(true) { + unsigned address, length; + + if(offset > modifySize - 3) break; + address = modifyData[offset++] << 16; + address |= modifyData[offset++] << 8; + address |= modifyData[offset++] << 0; + + if(address == 0x454f46) { //EOF + if(offset == modifySize) return true; + if(offset == modifySize - 3) { + size = modifyData[offset++] << 16; + size |= modifyData[offset++] << 8; + size |= modifyData[offset++] << 0; + return true; + } + } + + if(offset > modifySize - 2) break; + length = modifyData[offset++] << 8; + length |= modifyData[offset++] << 0; + + if(length) { //Copy + if(offset > modifySize - length) break; + while(length--) data[address++] = modifyData[offset++]; + } else { //RLE + if(offset > modifySize - 3) break; + length = modifyData[offset++] << 8; + length |= modifyData[offset++] << 0; + if(length == 0) break; //illegal + while(length--) data[address++] = modifyData[offset]; + offset++; + } + + size = max(size, address); + } + + delete[] data; + data = nullptr; + return false; +} + +void ips::source(const uint8_t* data, unsigned size) { + sourceData = data, sourceSize = size; +} + +void ips::modify(const uint8_t* data, unsigned size) { + modifyData = data, modifySize = size; +} + +ips::ips() { +} + +ips::~ips() { + if(data) delete[] data; + if(sourceData) delete[] sourceData; + if(modifyData) delete[] modifyData; +} + +} + +#endif diff --git a/src/dep/libs/nall/map.hpp b/src/dep/libs/nall/map.hpp new file mode 100755 index 000000000..56e4b5c89 --- /dev/null +++ b/src/dep/libs/nall/map.hpp @@ -0,0 +1,59 @@ +#ifndef NALL_MAP_HPP +#define NALL_MAP_HPP + +#include + +namespace nall { + +template struct map { + struct node_t { + T key; + U value; + bool operator< (const node_t& source) const { return key < source.key; } + bool operator==(const node_t& source) const { return key == source.key; } + node_t() = default; + node_t(const T& key) : key(key) {} + node_t(const T& key, const U& value) : key(key), value(value) {} + }; + + optional find(const T& key) const { + if(auto node = root.find({key})) return {true, node().value}; + return false; + } + + void insert(const T& key, const U& value) { root.insert({key, value}); } + void remove(const T& key) { root.remove({key}); } + unsigned size() const { return root.size(); } + void reset() { root.reset(); } + + typename set::iterator begin() { return root.begin(); } + typename set::iterator end() { return root.end(); } + const typename set::iterator begin() const { return root.begin(); } + const typename set::iterator end() const { return root.end(); } + +protected: + set root; +}; + +template struct bimap { + optional find(const T& key) const { return tmap.find(key); } + optional find(const U& key) const { return umap.find(key); } + void insert(const T& key, const U& value) { tmap.insert(key, value); umap.insert(value, key); } + void remove(const T& key) { if(auto p = tmap.find(key)) { umap.remove(p().value); tmap.remove(key); } } + void remove(const U& key) { if(auto p = umap.find(key)) { tmap.remove(p().value); umap.remove(key); } } + unsigned size() const { return tmap.size(); } + void reset() { tmap.reset(); umap.reset(); } + + typename set::node_t>::iterator begin() { return tmap.begin(); } + typename set::node_t>::iterator end() { return tmap.end(); } + const typename set::node_t>::iterator begin() const { return tmap.begin(); } + const typename set::node_t>::iterator end() const { return tmap.end(); } + +protected: + map tmap; + map umap; +}; + +} + +#endif diff --git a/src/dep/libs/nall/matrix.hpp b/src/dep/libs/nall/matrix.hpp new file mode 100755 index 000000000..991c7e682 --- /dev/null +++ b/src/dep/libs/nall/matrix.hpp @@ -0,0 +1,33 @@ +#ifndef NALL_MATRIX_HPP +#define NALL_MATRIX_HPP + +namespace nall { + +namespace Matrix { + +template inline void Multiply(T* output, const T* xdata, unsigned xrows, unsigned xcols, const T* ydata, unsigned yrows, unsigned ycols) { + if(xcols != yrows) return; + + for(unsigned y = 0; y < xrows; y++) { + for(unsigned x = 0; x < ycols; x++) { + T sum = 0; + for(unsigned z = 0; z < xcols; z++) { + sum += xdata[y * xcols + z] * ydata[z * ycols + x]; + } + *output++ = sum; + } + } +} + +template inline vector Multiply(const T* xdata, unsigned xrows, unsigned xcols, const T* ydata, unsigned yrows, unsigned ycols) { + vector output; + output.resize(xrows * ycols); + Multiply(output.data(), xdata, xrows, xcols, ydata, yrows, ycols); + return output; +} + +} + +} + +#endif diff --git a/src/dep/libs/nall/mosaic.hpp b/src/dep/libs/nall/mosaic.hpp new file mode 100755 index 000000000..16fd0bfd3 --- /dev/null +++ b/src/dep/libs/nall/mosaic.hpp @@ -0,0 +1,10 @@ +#ifndef NALL_MOSAIC_HPP +#define NALL_MOSAIC_HPP + +#define NALL_MOSAIC_INTERNAL_HPP +#include +#include +#include +#undef NALL_MOSAIC_INTERNAL_HPP + +#endif diff --git a/src/dep/libs/nall/mosaic/bitstream.hpp b/src/dep/libs/nall/mosaic/bitstream.hpp new file mode 100755 index 000000000..161246bd4 --- /dev/null +++ b/src/dep/libs/nall/mosaic/bitstream.hpp @@ -0,0 +1,55 @@ +#ifdef NALL_MOSAIC_INTERNAL_HPP + +namespace nall { +namespace mosaic { + +struct bitstream { + filemap fp; + uint8_t* data = nullptr; + unsigned size = 0; + bool readonly = false; + bool endian = 1; + + bool read(uint64_t addr) const { + if(data == nullptr || (addr >> 3) >= size) return 0; + unsigned mask = endian == 0 ? (0x01 << (addr & 7)) : (0x80 >> (addr & 7)); + return data[addr >> 3] & mask; + } + + void write(uint64_t addr, bool value) { + if(data == nullptr || readonly == true || (addr >> 3) >= size) return; + unsigned mask = endian == 0 ? (0x01 << (addr & 7)) : (0x80 >> (addr & 7)); + if(value == 0) data[addr >> 3] &= ~mask; + if(value == 1) data[addr >> 3] |= mask; + } + + bool open(const string& filename) { + readonly = false; + if(fp.open(filename, filemap::mode::readwrite) == false) { + readonly = true; + if(fp.open(filename, filemap::mode::read) == false) { + return false; + } + } + data = fp.data(); + size = fp.size(); + return true; + } + + void close() { + fp.close(); + data = nullptr; + } + + bitstream() { + } + + ~bitstream() { + close(); + } +}; + +} +} + +#endif diff --git a/src/dep/libs/nall/mosaic/context.hpp b/src/dep/libs/nall/mosaic/context.hpp new file mode 100755 index 000000000..ee6b5d532 --- /dev/null +++ b/src/dep/libs/nall/mosaic/context.hpp @@ -0,0 +1,227 @@ +#ifdef NALL_MOSAIC_INTERNAL_HPP + +namespace nall { +namespace mosaic { + +struct context { + unsigned offset; + unsigned width; + unsigned height; + unsigned count; + + bool endian; //0 = lsb, 1 = msb + bool order; //0 = linear, 1 = planar + unsigned depth; //1 - 24bpp + + unsigned blockWidth; + unsigned blockHeight; + unsigned blockStride; + unsigned blockOffset; + vector block; + + unsigned tileWidth; + unsigned tileHeight; + unsigned tileStride; + unsigned tileOffset; + vector tile; + + unsigned mosaicWidth; + unsigned mosaicHeight; + unsigned mosaicStride; + unsigned mosaicOffset; + vector mosaic; + + unsigned paddingWidth; + unsigned paddingHeight; + unsigned paddingColor; + vector palette; + + unsigned objectWidth() const { return blockWidth * tileWidth * mosaicWidth + paddingWidth; } + unsigned objectHeight() const { return blockHeight * tileHeight * mosaicHeight + paddingHeight; } + unsigned objectSize() const { + unsigned size = blockStride * tileWidth * tileHeight * mosaicWidth * mosaicHeight + + blockOffset * tileHeight * mosaicWidth * mosaicHeight + + tileStride * mosaicWidth * mosaicHeight + + tileOffset * mosaicHeight; + return max(1u, size); + } + + unsigned eval(const string& expression) { + if(auto result = Eval::integer(expression)) return result(); + return 0u; + } + + void eval(vector& buffer, const string& expression_) { + string expression = expression_; + bool function = false; + for(auto& c : expression) { + if(c == '(') function = true; + if(c == ')') function = false; + if(c == ',' && function == true) c = ';'; + } + + lstring list = expression.split(","); + for(auto& item : list) { + item.trim(); + if(item.match("f(?*) ?*")) { + item.ltrim<1>("f("); + lstring part = item.split<1>(") "); + lstring args = part[0].split<3>(";"); + for(auto &item : args) item.trim(); + + unsigned length = eval(args(0, "0")); + unsigned offset = eval(args(1, "0")); + unsigned stride = eval(args(2, "0")); + if(args.size() < 2) offset = buffer.size(); + if(args.size() < 3) stride = 1; + + for(unsigned n = 0; n < length; n++) { + string fn = part[1]; + fn.replace("n", string{n}); + fn.replace("o", string{offset}); + fn.replace("p", string{buffer.size()}); + buffer.resize(offset + 1); + buffer[offset] = eval(fn); + offset += stride; + } + } else if(item.match("base64*")) { + unsigned offset = 0; + item.ltrim<1>("base64"); + if(item.match("(?*) *")) { + item.ltrim<1>("("); + lstring part = item.split<1>(") "); + offset = eval(part[0]); + item = part(1, ""); + } + item.trim(); + for(auto& c : item) { + if(c >= 'A' && c <= 'Z') buffer.append(offset + c - 'A' + 0); + if(c >= 'a' && c <= 'z') buffer.append(offset + c - 'a' + 26); + if(c >= '0' && c <= '9') buffer.append(offset + c - '0' + 52); + if(c == '-') buffer.append(offset + 62); + if(c == '_') buffer.append(offset + 63); + } + } else if(item.match("file *")) { + item.ltrim<1>("file "); + item.trim(); + //... + } else if(item.empty() == false) { + buffer.append(eval(item)); + } + } + } + + void parse(const string& data) { + reset(); + + lstring lines = data.split("\n"); + for(auto& line : lines) { + lstring part = line.split<1>(":"); + if(part.size() != 2) continue; + part[0].trim(); + part[1].trim(); + + if(part[0] == "offset") offset = eval(part[1]); + if(part[0] == "width") width = eval(part[1]); + if(part[0] == "height") height = eval(part[1]); + if(part[0] == "count") count = eval(part[1]); + + if(part[0] == "endian") endian = eval(part[1]); + if(part[0] == "order") order = eval(part[1]); + if(part[0] == "depth") depth = eval(part[1]); + + if(part[0] == "blockWidth") blockWidth = eval(part[1]); + if(part[0] == "blockHeight") blockHeight = eval(part[1]); + if(part[0] == "blockStride") blockStride = eval(part[1]); + if(part[0] == "blockOffset") blockOffset = eval(part[1]); + if(part[0] == "block") eval(block, part[1]); + + if(part[0] == "tileWidth") tileWidth = eval(part[1]); + if(part[0] == "tileHeight") tileHeight = eval(part[1]); + if(part[0] == "tileStride") tileStride = eval(part[1]); + if(part[0] == "tileOffset") tileOffset = eval(part[1]); + if(part[0] == "tile") eval(tile, part[1]); + + if(part[0] == "mosaicWidth") mosaicWidth = eval(part[1]); + if(part[0] == "mosaicHeight") mosaicHeight = eval(part[1]); + if(part[0] == "mosaicStride") mosaicStride = eval(part[1]); + if(part[0] == "mosaicOffset") mosaicOffset = eval(part[1]); + if(part[0] == "mosaic") eval(mosaic, part[1]); + + if(part[0] == "paddingWidth") paddingWidth = eval(part[1]); + if(part[0] == "paddingHeight") paddingHeight = eval(part[1]); + if(part[0] == "paddingColor") paddingColor = eval(part[1]); + if(part[0] == "palette") eval(palette, part[1]); + } + + sanitize(); + } + + bool load(const string& filename) { + string filedata = string::read(filename); + if(filedata.empty()) return false; + parse(filedata); + return true; + } + + void sanitize() { + if(depth < 1) depth = 1; + if(depth > 24) depth = 24; + + if(blockWidth < 1) blockWidth = 1; + if(blockHeight < 1) blockHeight = 1; + + if(tileWidth < 1) tileWidth = 1; + if(tileHeight < 1) tileHeight = 1; + + if(mosaicWidth < 1) mosaicWidth = 1; + if(mosaicHeight < 1) mosaicHeight = 1; + + //set alpha to full opacity + paddingColor |= 255u << 24; + for(auto& color : palette) color |= 255u << 24; + } + + void reset() { + offset = 0; + width = 0; + height = 0; + count = 0; + + endian = 1; + order = 0; + depth = 1; + + blockWidth = 1; + blockHeight = 1; + blockStride = 0; + blockOffset = 0; + block.reset(); + + tileWidth = 1; + tileHeight = 1; + tileStride = 0; + tileOffset = 0; + tile.reset(); + + mosaicWidth = 1; + mosaicHeight = 1; + mosaicStride = 0; + mosaicOffset = 0; + mosaic.reset(); + + paddingWidth = 0; + paddingHeight = 0; + paddingColor = 0; + palette.reset(); + } + + context() { + reset(); + } +}; + +} +} + +#endif diff --git a/src/dep/libs/nall/mosaic/parser.hpp b/src/dep/libs/nall/mosaic/parser.hpp new file mode 100755 index 000000000..93cfd5b05 --- /dev/null +++ b/src/dep/libs/nall/mosaic/parser.hpp @@ -0,0 +1,126 @@ +#ifdef NALL_MOSAIC_INTERNAL_HPP + +namespace nall { +namespace mosaic { + +struct parser { + image canvas; + + //export from bitstream to canvas + void load(bitstream& stream, uint64_t offset, context& ctx, unsigned width, unsigned height) { + canvas.allocate(width, height); + canvas.fill(ctx.paddingColor); + parse(1, stream, offset, ctx, width, height); + } + + //import from canvas to bitstream + bool save(bitstream& stream, uint64_t offset, context& ctx) { + if(stream.readonly) return false; + parse(0, stream, offset, ctx, canvas.width, canvas.height); + return true; + } + + inline parser() : canvas(0, 32, 255u << 24, 255u << 16, 255u << 8, 255u << 0) { + } + +private: + uint32_t read(unsigned x, unsigned y) const { + unsigned addr = y * canvas.width + x; + if(addr >= canvas.width * canvas.height) return 0u; + uint32_t *buffer = (uint32_t*)canvas.data; + return buffer[addr]; + } + + void write(unsigned x, unsigned y, uint32_t data) { + unsigned addr = y * canvas.width + x; + if(addr >= canvas.width * canvas.height) return; + uint32_t *buffer = (uint32_t*)canvas.data; + buffer[addr] = data; + } + + void parse(bool load, bitstream& stream, uint64_t offset, context& ctx, unsigned width, unsigned height) { + stream.endian = ctx.endian; + unsigned canvasWidth = width / (ctx.mosaicWidth * ctx.tileWidth * ctx.blockWidth + ctx.paddingWidth); + unsigned canvasHeight = height / (ctx.mosaicHeight * ctx.tileHeight * ctx.blockHeight + ctx.paddingHeight); + unsigned bitsPerBlock = ctx.depth * ctx.blockWidth * ctx.blockHeight; + + unsigned objectOffset = 0; + for(unsigned objectY = 0; objectY < canvasHeight; objectY++) { + for(unsigned objectX = 0; objectX < canvasWidth; objectX++) { + if(objectOffset >= ctx.count && ctx.count > 0) break; + unsigned objectIX = objectX * ctx.objectWidth(); + unsigned objectIY = objectY * ctx.objectHeight(); + objectOffset++; + + unsigned mosaicOffset = 0; + for(unsigned mosaicY = 0; mosaicY < ctx.mosaicHeight; mosaicY++) { + for(unsigned mosaicX = 0; mosaicX < ctx.mosaicWidth; mosaicX++) { + unsigned mosaicData = ctx.mosaic(mosaicOffset, mosaicOffset); + unsigned mosaicIX = (mosaicData % ctx.mosaicWidth) * (ctx.tileWidth * ctx.blockWidth); + unsigned mosaicIY = (mosaicData / ctx.mosaicWidth) * (ctx.tileHeight * ctx.blockHeight); + mosaicOffset++; + + unsigned tileOffset = 0; + for(unsigned tileY = 0; tileY < ctx.tileHeight; tileY++) { + for(unsigned tileX = 0; tileX < ctx.tileWidth; tileX++) { + unsigned tileData = ctx.tile(tileOffset, tileOffset); + unsigned tileIX = (tileData % ctx.tileWidth) * ctx.blockWidth; + unsigned tileIY = (tileData / ctx.tileWidth) * ctx.blockHeight; + tileOffset++; + + unsigned blockOffset = 0; + for(unsigned blockY = 0; blockY < ctx.blockHeight; blockY++) { + for(unsigned blockX = 0; blockX < ctx.blockWidth; blockX++) { + if(load) { + unsigned palette = 0; + for(unsigned n = 0; n < ctx.depth; n++) { + unsigned index = blockOffset++; + if(ctx.order == 1) index = (index % ctx.depth) * ctx.blockWidth * ctx.blockHeight + (index / ctx.depth); + palette |= stream.read(offset + ctx.block(index, index)) << n; + } + + write( + objectIX + mosaicIX + tileIX + blockX, + objectIY + mosaicIY + tileIY + blockY, + ctx.palette(palette, palette) + ); + } else /* save */ { + uint32_t palette = read( + objectIX + mosaicIX + tileIX + blockX, + objectIY + mosaicIY + tileIY + blockY + ); + + for(unsigned n = 0; n < ctx.depth; n++) { + unsigned index = blockOffset++; + if(ctx.order == 1) index = (index % ctx.depth) * ctx.blockWidth * ctx.blockHeight + (index / ctx.depth); + stream.write(offset + ctx.block(index, index), palette & 1); + palette >>= 1; + } + } + } //blockX + } //blockY + + offset += ctx.blockStride; + } //tileX + + offset += ctx.blockOffset; + } //tileY + + offset += ctx.tileStride; + } //mosaicX + + offset += ctx.tileOffset; + } //mosaicY + + offset += ctx.mosaicStride; + } //objectX + + offset += ctx.mosaicOffset; + } //objectY + } +}; + +} +} + +#endif diff --git a/src/dep/libs/nall/nall.hpp b/src/dep/libs/nall/nall.hpp new file mode 100755 index 000000000..f021e134d --- /dev/null +++ b/src/dep/libs/nall/nall.hpp @@ -0,0 +1,63 @@ +#ifndef NALL_HPP +#define NALL_HPP + +//include the most common nall headers with one statement +//does not include the most obscure components with high cost and low usage + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(PLATFORM_WINDOWS) + #include + #include +#endif + +#if defined(PLATFORM_X) + #include +#endif + +#endif diff --git a/src/dep/libs/nall/odbc.hpp b/src/dep/libs/nall/odbc.hpp new file mode 100755 index 000000000..cfcd5c0ea --- /dev/null +++ b/src/dep/libs/nall/odbc.hpp @@ -0,0 +1,153 @@ +#ifndef NALL_ODBC_HPP +#define NALL_ODBC_HPP + +//minimal wrapper for core ODBC v3 API +//requires Windows or unixODBC + +#include +#include + +#include +#include +#include + +namespace nall { + +struct ODBC { + inline ODBC(); + inline ODBC(const string& database, const string& username, const string& password); + inline ~ODBC(); + + inline bool connected(); + inline bool connect(const string& hostname, const string& username, const string& password); + inline void disconnect(); + template inline bool execute(Args&&... args); + inline void release(); + inline unsigned rows(); + inline lstring read(); + +private: + char* buffer = nullptr; + SQLHANDLE sqlEnvironment = nullptr; + SQLHANDLE sqlConnection = nullptr; + SQLHANDLE sqlStatement = nullptr; +}; + +ODBC::ODBC() { + auto result = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &sqlEnvironment); + if(result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) return; + + SQLSetEnvAttr(sqlEnvironment, SQL_ATTR_ODBC_VERSION, (void*)SQL_OV_ODBC3, 0); + buffer = new char[65536](); +} + +ODBC::ODBC(const string& database, const string& username, const string& password) : ODBC() { + connect(database, username, password); +} + +ODBC::~ODBC() { + if(sqlEnvironment) { + disconnect(); + SQLFreeHandle(SQL_HANDLE_ENV, sqlEnvironment); + sqlEnvironment = nullptr; + + delete[] buffer; + buffer = nullptr; + } +} + +bool ODBC::connected() { + return sqlConnection; +} + +bool ODBC::connect(const string& hostname, const string& username, const string& password) { + if(!sqlEnvironment) return false; + disconnect(); + + auto result = SQLAllocHandle(SQL_HANDLE_DBC, sqlEnvironment, &sqlConnection); + if(result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) return false; + + SQLSetConnectAttr(sqlConnection, SQL_LOGIN_TIMEOUT, (SQLPOINTER)5, 0); + result = SQLConnectA(sqlConnection, + (SQLCHAR*)(const char*)hostname, SQL_NTS, + (SQLCHAR*)(const char*)username, SQL_NTS, + (SQLCHAR*)(const char*)password, SQL_NTS + ); + if(result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) { + disconnect(); + return false; + } + + return true; +} + +void ODBC::disconnect() { + if(sqlConnection) { + release(); + SQLDisconnect(sqlConnection); + SQLFreeHandle(SQL_HANDLE_DBC, sqlConnection); + sqlConnection = nullptr; + } +} + +template +bool ODBC::execute(Args&&... args) { + string statement({args...}); + + if(!sqlConnection) return false; + release(); + + auto result = SQLAllocHandle(SQL_HANDLE_STMT, sqlConnection, &sqlStatement); + if(result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) return false; + + result = SQLExecDirectA(sqlStatement, (SQLCHAR*)(const char*)statement, SQL_NTS); + if(result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) { + release(); + return false; + } + + return true; +} + +void ODBC::release() { + if(sqlStatement) { + SQLFreeHandle(SQL_HANDLE_STMT, sqlStatement); + sqlStatement = nullptr; + } +} + +//valid after update, insert or delete +unsigned ODBC::rows() { + if(!sqlStatement) return 0; + + SQLLEN sqlRows = 0; + auto result = SQLRowCount(sqlStatement, &sqlRows); + if(result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) return 0; + + return sqlRows; +} + +//valid after select +lstring ODBC::read() { + if(!sqlStatement) return {}; + + auto result = SQLFetch(sqlStatement); + if(result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) return {}; + + SQLSMALLINT sqlColumns = 0; + result = SQLNumResultCols(sqlStatement, &sqlColumns); + if(result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) return {}; + + lstring data; + for(unsigned column = 0; column < sqlColumns; column++) { + SQLLEN length = 0; + SQLGetData(sqlStatement, 1 + column, SQL_C_CHAR, buffer, 65535, &length); + data.append(buffer); + } + + return data; +} + +} + +#endif diff --git a/src/dep/libs/nall/platform.hpp b/src/dep/libs/nall/platform.hpp new file mode 100755 index 000000000..245767927 --- /dev/null +++ b/src/dep/libs/nall/platform.hpp @@ -0,0 +1,96 @@ +#ifndef NALL_PLATFORM_HPP +#define NALL_PLATFORM_HPP + +namespace Math { + static const long double e = 2.71828182845904523536; + static const long double Pi = 3.14159265358979323846; +} + +#if defined(_WIN32) + //minimum version needed for _wstat64, etc + #undef __MSVCRT_VERSION__ + #define __MSVCRT_VERSION__ 0x0601 + #include +#endif + +//========================= +//standard platform headers +//========================= + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#if defined(_WIN32) + #include + #include + #include + #include + #undef interface + #define dllexport __declspec(dllexport) +#else + #include + #include + #include + #define dllexport +#endif + +//========== +//Visual C++ +//========== + +#if defined(_MSC_VER) + #pragma warning(disable:4996) //disable libc "deprecation" warnings + #define va_copy(dest, src) ((dest) = (src)) +#endif + +#if defined(_WIN32) + __declspec(dllimport) int _fileno(FILE*); + + inline int access(const char* path, int amode) { return _waccess(nall::utf16_t(path), amode); } + inline int fileno(FILE* stream) { return _fileno(stream); } + inline char* getcwd(char* buf, size_t size) { wchar_t wpath[PATH_MAX] = L""; if(!_wgetcwd(wpath, size)) return nullptr; strcpy(buf, nall::utf8_t(wpath)); return buf; } + inline int putenv(char* string) { return _wputenv(nall::utf16_t(string)); } + inline char* realpath(const char* file_name, char* resolved_name) { wchar_t wfile_name[PATH_MAX] = L""; if(!_wfullpath(wfile_name, nall::utf16_t(file_name), PATH_MAX)) return nullptr; strcpy(resolved_name, nall::utf8_t(wfile_name)); return resolved_name; } + inline int rename(const char* oldname, const char* newname) { return _wrename(nall::utf16_t(oldname), nall::utf16_t(newname)); } + inline void usleep(unsigned milliseconds) { Sleep(milliseconds / 1000); } +#endif + +//================ +//inline expansion +//================ + +#if defined(__clang__) || defined(__GNUC__) + #define noinline __attribute__((noinline)) + #define alwaysinline inline __attribute__((always_inline)) +#elif defined(_MSC_VER) + #define noinline __declspec(noinline) + #define alwaysinline inline __forceinline +#else + #define noinline + #define alwaysinline inline +#endif + +//=========== +//unreachable +//=========== + +#if defined(__clang__) || defined(__GNUC__) + #define unreachable __builtin_unreachable() +#else + #define unreachable throw +#endif + +#endif diff --git a/src/dep/libs/nall/png.hpp b/src/dep/libs/nall/png.hpp new file mode 100755 index 000000000..ccdf64eb7 --- /dev/null +++ b/src/dep/libs/nall/png.hpp @@ -0,0 +1,337 @@ +#ifndef NALL_PNG_HPP +#define NALL_PNG_HPP + +//PNG image decoder +//author: byuu + +#include +#include + +namespace nall { + +struct png { + struct Info { + unsigned width; + unsigned height; + unsigned bitDepth; + //colorType: + //0 = L (luma) + //2 = R,G,B + //3 = P (palette) + //4 = L,A + //6 = R,G,B,A + unsigned colorType; + unsigned compressionMethod; + unsigned filterType; + unsigned interlaceMethod; + + unsigned bytesPerPixel; + unsigned pitch; + + uint8_t palette[256][3]; + } info; + + uint8_t* data = nullptr; + unsigned size = 0; + + inline bool decode(const string& filename); + inline bool decode(const uint8_t* sourceData, unsigned sourceSize); + inline unsigned readbits(const uint8_t*& data); + unsigned bitpos = 0; + + inline png(); + inline ~png(); + +protected: + enum class FourCC : unsigned { + IHDR = 0x49484452, + PLTE = 0x504c5445, + IDAT = 0x49444154, + IEND = 0x49454e44, + }; + + inline unsigned interlace(unsigned pass, unsigned index); + inline unsigned inflateSize(); + inline bool deinterlace(const uint8_t*& inputData, unsigned pass); + inline bool filter(uint8_t* outputData, const uint8_t* inputData, unsigned width, unsigned height); + inline unsigned read(const uint8_t* data, unsigned length); +}; + +bool png::decode(const string& filename) { + if(auto memory = file::read(filename)) { + return decode(memory.data(), memory.size()); + } + return false; +} + +bool png::decode(const uint8_t* sourceData, unsigned sourceSize) { + if(sourceSize < 8) return false; + if(read(sourceData + 0, 4) != 0x89504e47) return false; + if(read(sourceData + 4, 4) != 0x0d0a1a0a) return false; + + uint8_t* compressedData = nullptr; + unsigned compressedSize = 0; + + unsigned offset = 8; + while(offset < sourceSize) { + unsigned length = read(sourceData + offset + 0, 4); + unsigned fourCC = read(sourceData + offset + 4, 4); + unsigned checksum = read(sourceData + offset + 8 + length, 4); + + if(fourCC == (unsigned)FourCC::IHDR) { + info.width = read(sourceData + offset + 8, 4); + info.height = read(sourceData + offset + 12, 4); + info.bitDepth = read(sourceData + offset + 16, 1); + info.colorType = read(sourceData + offset + 17, 1); + info.compressionMethod = read(sourceData + offset + 18, 1); + info.filterType = read(sourceData + offset + 19, 1); + info.interlaceMethod = read(sourceData + offset + 20, 1); + + if(info.bitDepth == 0 || info.bitDepth > 16) return false; + if(info.bitDepth & (info.bitDepth - 1)) return false; //not a power of two + if(info.compressionMethod != 0) return false; + if(info.filterType != 0) return false; + if(info.interlaceMethod != 0 && info.interlaceMethod != 1) return false; + + switch(info.colorType) { + case 0: info.bytesPerPixel = info.bitDepth * 1; break; //L + case 2: info.bytesPerPixel = info.bitDepth * 3; break; //R,G,B + case 3: info.bytesPerPixel = info.bitDepth * 1; break; //P + case 4: info.bytesPerPixel = info.bitDepth * 2; break; //L,A + case 6: info.bytesPerPixel = info.bitDepth * 4; break; //R,G,B,A + default: return false; + } + + if(info.colorType == 2 || info.colorType == 4 || info.colorType == 6) { + if(info.bitDepth != 8 && info.bitDepth != 16) return false; + } + if(info.colorType == 3 && info.bitDepth == 16) return false; + + info.bytesPerPixel = (info.bytesPerPixel + 7) / 8; + info.pitch = (int)info.width * info.bytesPerPixel; + } + + if(fourCC == (unsigned)FourCC::PLTE) { + if(length % 3) return false; + for(unsigned n = 0, p = offset + 8; n < length / 3; n++) { + info.palette[n][0] = sourceData[p++]; + info.palette[n][1] = sourceData[p++]; + info.palette[n][2] = sourceData[p++]; + } + } + + if(fourCC == (unsigned)FourCC::IDAT) { + compressedData = (uint8_t*)realloc(compressedData, compressedSize + length); + memcpy(compressedData + compressedSize, sourceData + offset + 8, length); + compressedSize += length; + } + + if(fourCC == (unsigned)FourCC::IEND) { + break; + } + + offset += 4 + 4 + length + 4; + } + + unsigned interlacedSize = inflateSize(); + uint8_t *interlacedData = new uint8_t[interlacedSize]; + + bool result = inflate(interlacedData, interlacedSize, compressedData + 2, compressedSize - 6); + free(compressedData); + + if(result == false) { + delete[] interlacedData; + return false; + } + + size = info.width * info.height * info.bytesPerPixel; + data = new uint8_t[size]; + + if(info.interlaceMethod == 0) { + if(filter(data, interlacedData, info.width, info.height) == false) { + delete[] interlacedData; + delete[] data; + data = nullptr; + return false; + } + } else { + const uint8_t *passData = interlacedData; + for(unsigned pass = 0; pass < 7; pass++) { + if(deinterlace(passData, pass) == false) { + delete[] interlacedData; + delete[] data; + data = nullptr; + return false; + } + } + } + + delete[] interlacedData; + return true; +} + +unsigned png::interlace(unsigned pass, unsigned index) { + static const unsigned data[7][4] = { + //x-distance, y-distance, x-origin, y-origin + {8, 8, 0, 0}, + {8, 8, 4, 0}, + {4, 8, 0, 4}, + {4, 4, 2, 0}, + {2, 4, 0, 2}, + {2, 2, 1, 0}, + {1, 2, 0, 1}, + }; + return data[pass][index]; +} + +unsigned png::inflateSize() { + if(info.interlaceMethod == 0) { + return info.width * info.height * info.bytesPerPixel + info.height; + } + + unsigned size = 0; + for(unsigned pass = 0; pass < 7; pass++) { + unsigned xd = interlace(pass, 0), yd = interlace(pass, 1); + unsigned xo = interlace(pass, 2), yo = interlace(pass, 3); + unsigned width = (info.width + (xd - xo - 1)) / xd; + unsigned height = (info.height + (yd - yo - 1)) / yd; + if(width == 0 || height == 0) continue; + size += width * height * info.bytesPerPixel + height; + } + return size; +} + +bool png::deinterlace(const uint8_t*& inputData, unsigned pass) { + unsigned xd = interlace(pass, 0), yd = interlace(pass, 1); + unsigned xo = interlace(pass, 2), yo = interlace(pass, 3); + unsigned width = (info.width + (xd - xo - 1)) / xd; + unsigned height = (info.height + (yd - yo - 1)) / yd; + if(width == 0 || height == 0) return true; + + unsigned outputSize = width * height * info.bytesPerPixel; + uint8_t* outputData = new uint8_t[outputSize]; + bool result = filter(outputData, inputData, width, height); + + const uint8_t* rd = outputData; + for(unsigned y = yo; y < info.height; y += yd) { + uint8_t* wr = data + y * info.pitch; + for(unsigned x = xo; x < info.width; x += xd) { + for(unsigned b = 0; b < info.bytesPerPixel; b++) { + wr[x * info.bytesPerPixel + b] = *rd++; + } + } + } + + inputData += outputSize + height; + delete[] outputData; + return result; +} + +bool png::filter(uint8_t* outputData, const uint8_t* inputData, unsigned width, unsigned height) { + uint8_t* wr = outputData; + const uint8_t* rd = inputData; + int bpp = info.bytesPerPixel, pitch = width * bpp; + for(int y = 0; y < height; y++) { + uint8_t filter = *rd++; + + switch(filter) { + case 0x00: //None + for(int x = 0; x < pitch; x++) { + wr[x] = rd[x]; + } + break; + + case 0x01: //Subtract + for(int x = 0; x < pitch; x++) { + wr[x] = rd[x] + (x - bpp < 0 ? 0 : wr[x - bpp]); + } + break; + + case 0x02: //Above + for(int x = 0; x < pitch; x++) { + wr[x] = rd[x] + (y - 1 < 0 ? 0 : wr[x - pitch]); + } + break; + + case 0x03: //Average + for(int x = 0; x < pitch; x++) { + short a = x - bpp < 0 ? 0 : wr[x - bpp]; + short b = y - 1 < 0 ? 0 : wr[x - pitch]; + + wr[x] = rd[x] + (uint8_t)((a + b) / 2); + } + break; + + case 0x04: //Paeth + for(int x = 0; x < pitch; x++) { + short a = x - bpp < 0 ? 0 : wr[x - bpp]; + short b = y - 1 < 0 ? 0 : wr[x - pitch]; + short c = x - bpp < 0 || y - 1 < 0 ? 0 : wr[x - pitch - bpp]; + + short p = a + b - c; + short pa = p > a ? p - a : a - p; + short pb = p > b ? p - b : b - p; + short pc = p > c ? p - c : c - p; + + uint8_t paeth = (uint8_t)((pa <= pb && pa <= pc) ? a : (pb <= pc) ? b : c); + + wr[x] = rd[x] + paeth; + } + break; + + default: //Invalid + return false; + } + + rd += pitch; + wr += pitch; + } + + return true; +} + +unsigned png::read(const uint8_t* data, unsigned length) { + unsigned result = 0; + while(length--) result = (result << 8) | (*data++); + return result; +} + +unsigned png::readbits(const uint8_t*& data) { + unsigned result = 0; + switch(info.bitDepth) { + case 1: + result = (*data >> bitpos) & 1; + bitpos++; + if(bitpos == 8) { data++; bitpos = 0; } + break; + case 2: + result = (*data >> bitpos) & 3; + bitpos += 2; + if(bitpos == 8) { data++; bitpos = 0; } + break; + case 4: + result = (*data >> bitpos) & 15; + bitpos += 4; + if(bitpos == 8) { data++; bitpos = 0; } + break; + case 8: + result = *data++; + break; + case 16: + result = (data[0] << 8) | (data[1] << 0); + data += 2; + break; + } + return result; +} + +png::png() { +} + +png::~png() { + if(data) delete[] data; +} + +} + +#endif diff --git a/src/dep/libs/nall/priority-queue.hpp b/src/dep/libs/nall/priority-queue.hpp new file mode 100755 index 000000000..b15769930 --- /dev/null +++ b/src/dep/libs/nall/priority-queue.hpp @@ -0,0 +1,110 @@ +#ifndef NALL_PRIORITY_QUEUE_HPP +#define NALL_PRIORITY_QUEUE_HPP + +#include +#include +#include +#include + +namespace nall { + +template void priority_queue_nocallback(type_t) {} + +//priority queue implementation using binary min-heap array; +//does not require normalize() function. +//O(1) find (tick) +//O(log n) append (enqueue) +//O(log n) remove (dequeue) +template struct priority_queue { + inline void tick(unsigned ticks) { + basecounter += ticks; + while(heapsize && gte(basecounter, heap[0].counter)) callback(dequeue()); + } + + //counter is relative to current time (eg enqueue(64, ...) fires in 64 ticks); + //counter cannot exceed std::numeric_limits::max() >> 1. + void enqueue(unsigned counter, type_t event) { + unsigned child = heapsize++; + counter += basecounter; + + while(child) { + unsigned parent = (child - 1) >> 1; + if(gte(counter, heap[parent].counter)) break; + + heap[child].counter = heap[parent].counter; + heap[child].event = heap[parent].event; + child = parent; + } + + heap[child].counter = counter; + heap[child].event = event; + } + + type_t dequeue() { + type_t event(heap[0].event); + unsigned parent = 0; + unsigned counter = heap[--heapsize].counter; + + while(true) { + unsigned child = (parent << 1) + 1; + if(child >= heapsize) break; + if(child + 1 < heapsize && gte(heap[child].counter, heap[child + 1].counter)) child++; + if(gte(heap[child].counter, counter)) break; + + heap[parent].counter = heap[child].counter; + heap[parent].event = heap[child].event; + parent = child; + } + + heap[parent].counter = counter; + heap[parent].event = heap[heapsize].event; + return event; + } + + void reset() { + basecounter = 0; + heapsize = 0; + } + + void serialize(serializer& s) { + s.integer(basecounter); + s.integer(heapsize); + for(unsigned n = 0; n < heapcapacity; n++) { + s.integer(heap[n].counter); + s.integer(heap[n].event); + } + } + + priority_queue(unsigned size, function callback = &priority_queue_nocallback) + : callback(callback) { + heap = new heap_t[size]; + heapcapacity = size; + reset(); + } + + ~priority_queue() { + delete[] heap; + } + + priority_queue& operator=(const priority_queue&) = delete; + priority_queue(const priority_queue&) = delete; + +private: + function callback; + unsigned basecounter; + unsigned heapsize; + unsigned heapcapacity; + struct heap_t { + unsigned counter; + type_t event; + } *heap; + + //return true if x is greater than or equal to y + inline bool gte(unsigned x, unsigned y) { + return x - y < (std::numeric_limits::max() >> 1); + } +}; + +} + +#endif diff --git a/src/dep/libs/nall/property.hpp b/src/dep/libs/nall/property.hpp new file mode 100755 index 000000000..575acba11 --- /dev/null +++ b/src/dep/libs/nall/property.hpp @@ -0,0 +1,44 @@ +#ifndef NALL_PROPERTY_HPP +#define NALL_PROPERTY_HPP + +namespace nall { + +template struct property { + template struct readonly { + const T* operator->() const { return &value; } + const T& operator()() const { return value; } + operator const T&() const { return value; } + private: + T* operator->() { return &value; } + operator T&() { return value; } + const T& operator=(const T& value_) { return value = value_; } + T value; + friend C; + }; + + template struct writeonly { + void operator=(const T& value_) { value = value_; } + private: + const T* operator->() const { return &value; } + const T& operator()() const { return value; } + operator const T&() const { return value; } + T* operator->() { return &value; } + operator T&() { return value; } + T value; + friend C; + }; + + template struct readwrite { + const T* operator->() const { return &value; } + const T& operator()() const { return value; } + operator const T&() const { return value; } + T* operator->() { return &value; } + operator T&() { return value; } + const T& operator=(const T& value_) { return value = value_; } + T value; + }; +}; + +} + +#endif diff --git a/src/dep/libs/nall/public-cast.hpp b/src/dep/libs/nall/public-cast.hpp new file mode 100755 index 000000000..c9c4940db --- /dev/null +++ b/src/dep/libs/nall/public-cast.hpp @@ -0,0 +1,34 @@ +#ifndef NALL_PUBLIC_CAST_HPP +#define NALL_PUBLIC_CAST_HPP + +//this is a proof-of-concept-*only* C++ access-privilege elevation exploit. +//this code is 100% legal C++, per C++98 section 14.7.2 paragraph 8: +//"access checking rules do not apply to names in explicit instantiations." +//usage example: + +//struct N { typedef void (Class::*)(); }; +//template class public_cast; +//(class.*public_cast::value); + +//Class::Reference may be public, protected or private +//Class::Reference may be a function, object or variable + +namespace nall { + +template struct public_cast; + +template struct public_cast { + static typename T::type value; +}; + +template typename T::type public_cast::value; + +template struct public_cast { + static typename T::type value; +}; + +template typename T::type public_cast::value = public_cast::value = P; + +} + +#endif diff --git a/src/dep/libs/nall/random.hpp b/src/dep/libs/nall/random.hpp new file mode 100755 index 000000000..bd443f406 --- /dev/null +++ b/src/dep/libs/nall/random.hpp @@ -0,0 +1,43 @@ +#ifndef NALL_RANDOM_HPP +#define NALL_RANDOM_HPP + +#include +#include + +namespace nall { + +struct RandomNumberGenerator { + virtual void seed(uint64_t) = 0; + virtual uint64_t operator()() = 0; + virtual void serialize(serializer&) = 0; +}; + +//Galois LFSR using CRC64 polynomials +struct LinearFeedbackShiftRegisterGenerator : RandomNumberGenerator { + void seed(uint64_t seed) { + lfsr = seed; + for(unsigned n = 0; n < 8; n++) operator()(); + } + + uint64_t operator()() { + return lfsr = (lfsr >> 1) ^ (-(lfsr & 1) & crc64jones); + } + + void serialize(serializer& s) { + s.integer(lfsr); + } + +private: + static const uint64_t crc64ecma = 0x42f0e1eba9ea3693; + static const uint64_t crc64jones = 0xad93d23594c935a9; + uint64_t lfsr = crc64ecma; +}; + +inline uint64_t random() { + static LinearFeedbackShiftRegisterGenerator lfsr; + return lfsr(); +} + +} + +#endif diff --git a/src/dep/libs/nall/serial.hpp b/src/dep/libs/nall/serial.hpp new file mode 100755 index 000000000..ce6bcf8f9 --- /dev/null +++ b/src/dep/libs/nall/serial.hpp @@ -0,0 +1,118 @@ +#ifndef NALL_SERIAL_HPP +#define NALL_SERIAL_HPP + +#include +#include +#include + +#if !defined(PLATFORM_X) && !defined(PLATFORM_MACOSX) + #error "nall/serial: unsupported platform" +#endif + +#include +#include +#include +#include + +namespace nall { + +struct serial { + bool readable() { + if(port_open == false) return false; + fd_set fdset; + FD_ZERO(&fdset); + FD_SET(port, &fdset); + timeval timeout; + timeout.tv_sec = 0; + timeout.tv_usec = 0; + int result = select(FD_SETSIZE, &fdset, nullptr, nullptr, &timeout); + if(result < 1) return false; + return FD_ISSET(port, &fdset); + } + + //-1 on error, otherwise return bytes read + int read(uint8_t* data, unsigned length) { + if(port_open == false) return -1; + return ::read(port, (void*)data, length); + } + + bool writable() { + if(port_open == false) return false; + fd_set fdset; + FD_ZERO(&fdset); + FD_SET(port, &fdset); + timeval timeout; + timeout.tv_sec = 0; + timeout.tv_usec = 0; + int result = select(FD_SETSIZE, nullptr, &fdset, nullptr, &timeout); + if(result < 1) return false; + return FD_ISSET(port, &fdset); + } + + //-1 on error, otherwise return bytes written + int write(const uint8_t* data, unsigned length) { + if(port_open == false) return -1; + return ::write(port, (void*)data, length); + } + + bool open(const string& portname, unsigned rate, bool flowcontrol) { + close(); + + port = ::open(portname, O_RDWR | O_NOCTTY | O_NDELAY | O_NONBLOCK); + if(port == -1) return false; + + if(ioctl(port, TIOCEXCL) == -1) { close(); return false; } + if(fcntl(port, F_SETFL, 0) == -1) { close(); return false; } + if(tcgetattr(port, &original_attr) == -1) { close(); return false; } + + termios attr = original_attr; + cfmakeraw(&attr); + cfsetspeed(&attr, rate); + + attr.c_lflag &=~ (ECHO | ECHONL | ISIG | ICANON | IEXTEN); + attr.c_iflag &=~ (BRKINT | PARMRK | INPCK | ISTRIP | INLCR | IGNCR | ICRNL | IXON | IXOFF | IXANY); + attr.c_iflag |= (IGNBRK | IGNPAR); + attr.c_oflag &=~ (OPOST); + attr.c_cflag &=~ (CSIZE | CSTOPB | PARENB | CLOCAL); + attr.c_cflag |= (CS8 | CREAD); + if(flowcontrol == false) { + attr.c_cflag &= ~CRTSCTS; + } else { + attr.c_cflag |= CRTSCTS; + } + attr.c_cc[VTIME] = attr.c_cc[VMIN] = 0; + + if(tcsetattr(port, TCSANOW, &attr) == -1) { close(); return false; } + return port_open = true; + } + + void close() { + if(port != -1) { + tcdrain(port); + if(port_open == true) { + tcsetattr(port, TCSANOW, &original_attr); + port_open = false; + } + ::close(port); + port = -1; + } + } + + serial() { + port = -1; + port_open = false; + } + + ~serial() { + close(); + } + +private: + int port; + bool port_open; + termios original_attr; +}; + +} + +#endif diff --git a/src/dep/libs/nall/serializer.hpp b/src/dep/libs/nall/serializer.hpp new file mode 100755 index 000000000..5b2c8ef62 --- /dev/null +++ b/src/dep/libs/nall/serializer.hpp @@ -0,0 +1,150 @@ +#ifndef NALL_SERIALIZER_HPP +#define NALL_SERIALIZER_HPP + +//serializer: a class designed to save and restore the state of classes. +// +//benefits: +//- data() will be portable in size (it is not necessary to specify type sizes.) +//- data() will be portable in endianness (always stored internally as little-endian.) +//- one serialize function can both save and restore class states. +// +//caveats: +//- only plain-old-data can be stored. complex classes must provide serialize(serializer&); +//- floating-point usage is not portable across different implementations + +#include +#include +#include +#include + +namespace nall { + +struct serializer; + +template +struct has_serialize { + template static char test(decltype(std::declval().serialize(std::declval()))*); + template static long test(...); + static const bool value = sizeof(test(0)) == sizeof(char); +}; + +struct serializer { + enum mode_t { Load, Save, Size }; + + mode_t mode() const { + return _mode; + } + + const uint8_t* data() const { + return _data; + } + + unsigned size() const { + return _size; + } + + unsigned capacity() const { + return _capacity; + } + + template serializer& floatingpoint(T& value) { + enum { size = sizeof(T) }; + //this is rather dangerous, and not cross-platform safe; + //but there is no standardized way to export FP-values + uint8_t* p = (uint8_t*)&value; + if(_mode == Save) { + for(unsigned n = 0; n < size; n++) _data[_size++] = p[n]; + } else if(_mode == Load) { + for(unsigned n = 0; n < size; n++) p[n] = _data[_size++]; + } else { + _size += size; + } + return *this; + } + + template serializer& integer(T& value) { + enum { size = std::is_same::value ? 1 : sizeof(T) }; + if(_mode == Save) { + for(unsigned n = 0; n < size; n++) _data[_size++] = (uintmax_t)value >> (n << 3); + } else if(_mode == Load) { + value = 0; + for(unsigned n = 0; n < size; n++) value |= (uintmax_t)_data[_size++] << (n << 3); + } else if(_mode == Size) { + _size += size; + } + return *this; + } + + template serializer& array(T (&array)[N]) { + for(unsigned n = 0; n < N; n++) operator()(array[n]); + return *this; + } + + template serializer& array(T array, unsigned size) { + for(unsigned n = 0; n < size; n++) operator()(array[n]); + return *this; + } + + template serializer& operator()(T& value, typename std::enable_if::value>::type* = 0) { value.serialize(*this); return *this; } + template serializer& operator()(T& value, typename std::enable_if::value>::type* = 0) { return integer(value); } + template serializer& operator()(T& value, typename std::enable_if::value>::type* = 0) { return floatingpoint(value); } + template serializer& operator()(T& value, typename std::enable_if::value>::type* = 0) { return array(value); } + template serializer& operator()(T& value, unsigned size, typename std::enable_if::value>::type* = 0) { return array(value, size); } + + serializer& operator=(const serializer& s) { + if(_data) delete[] _data; + + _mode = s._mode; + _data = new uint8_t[s._capacity]; + _size = s._size; + _capacity = s._capacity; + + memcpy(_data, s._data, s._capacity); + return *this; + } + + serializer& operator=(serializer&& s) { + if(_data) delete[] _data; + + _mode = s._mode; + _data = s._data; + _size = s._size; + _capacity = s._capacity; + + s._data = nullptr; + return *this; + } + + serializer() = default; + serializer(const serializer& s) { operator=(s); } + serializer(serializer&& s) { operator=(std::move(s)); } + + serializer(unsigned capacity) { + _mode = Save; + _data = new uint8_t[capacity](); + _size = 0; + _capacity = capacity; + } + + serializer(const uint8_t* data, unsigned capacity) { + _mode = Load; + _data = new uint8_t[capacity]; + _size = 0; + _capacity = capacity; + memcpy(_data, data, capacity); + } + + ~serializer() { + if(_data) delete[] _data; + } + +private: + mode_t _mode = Size; + uint8_t* _data = nullptr; + unsigned _size = 0; + unsigned _capacity = 0; +}; + +}; + +#endif diff --git a/src/dep/libs/nall/set.hpp b/src/dep/libs/nall/set.hpp new file mode 100755 index 000000000..c8003ec00 --- /dev/null +++ b/src/dep/libs/nall/set.hpp @@ -0,0 +1,267 @@ +#ifndef NALL_SET_HPP +#define NALL_SET_HPP + +//set +//implementation: red-black tree +// +//search: O(log n) average; O(log n) worst +//insert: O(log n) average; O(log n) worst +//remove: O(log n) average; O(log n) worst +// +//requirements: +// bool T::operator==(const T&) const; +// bool T::operator< (const T&) const; + +#include +#include + +namespace nall { + +template struct set { + struct node_t { + T value; + bool red = 1; + node_t* link[2] = {nullptr, nullptr}; + node_t() = default; + node_t(const T& value) : value(value) {} + }; + + node_t* root = nullptr; + unsigned nodes = 0; + + set& operator=(const set& source) { copy(source); return *this; } + set& operator=(set&& source) { move(std::move(source)); return *this; } + set(const set& source) { operator=(source); } + set(set&& source) { operator=(std::move(source)); } + set(std::initializer_list list) { for(auto& value : list) insert(value); } + set() = default; + ~set() { reset(); } + + unsigned size() const { return nodes; } + bool empty() const { return nodes == 0; } + + void reset() { + reset(root); + nodes = 0; + } + + optional find(const T& value) { + if(node_t* node = find(root, value)) return node->value; + return false; + } + + optional find(const T& value) const { + if(node_t* node = find(root, value)) return node->value; + return false; + } + + optional insert(const T& value) { + unsigned count = size(); + node_t* v = insert(root, value); + root->red = 0; + if(size() == count) return false; + return {true, v->value}; + } + + template bool insert(const T& value, Args&&... args) { + bool result = insert(value); + insert(std::forward(args)...) | result; + return result; + } + + bool remove(const T& value) { + unsigned count = size(); + bool done = 0; + remove(root, &value, done); + if(root) root->red = 0; + return size() < count; + } + + template bool remove(const T& value, Args&&... args) { + bool result = remove(value); + return remove(std::forward(args)...) | result; + } + + struct base_iterator { + bool operator!=(const base_iterator& source) const { return position != source.position; } + + base_iterator& operator++() { + if(++position >= source.size()) { position = source.size(); return *this; } + + if(stack.last()->link[1]) { + stack.append(stack.last()->link[1]); + while(stack.last()->link[0]) stack.append(stack.last()->link[0]); + } else { + node_t* child; + do child = stack.take(); + while(child == stack.last()->link[1]); + } + + return *this; + } + + base_iterator(const set& source, unsigned position) : source(source), position(position) { + node_t* node = source.root; + while(node) { + stack.append(node); + node = node->link[0]; + } + } + + protected: + const set& source; + unsigned position; + vector stack; + }; + + struct iterator : base_iterator { + T& operator*() const { return base_iterator::stack.last()->value; } + iterator(const set& source, unsigned position) : base_iterator(source, position) {} + }; + + iterator begin() { return iterator(*this, 0); } + iterator end() { return iterator(*this, size()); } + + struct const_iterator : base_iterator { + const T& operator*() const { return base_iterator::stack.last()->value; } + const_iterator(const set& source, unsigned position) : base_iterator(source, position) {} + }; + + const const_iterator begin() const { return const_iterator(*this, 0); } + const const_iterator end() const { return const_iterator(*this, size()); } + +private: + void reset(node_t*& node) { + if(!node) return; + if(node->link[0]) reset(node->link[0]); + if(node->link[1]) reset(node->link[1]); + delete node; + node = nullptr; + } + + void copy(const set& source) { + reset(); + copy(root, source.root); + nodes = source.nodes; + } + + void copy(node_t*& target, const node_t* source) { + if(!source) return; + target = new node_t(source->value); + target->red = source->red; + copy(target->link[0], source->link[0]); + copy(target->link[1], source->link[1]); + } + + void move(set&& source) { + root = source.root; + nodes = source.nodes; + source.root = nullptr; + source.nodes = 0; + } + + node_t* find(node_t* node, const T& value) const { + if(node == nullptr) return nullptr; + if(node->value == value) return node; + return find(node->link[node->value < value], value); + } + + bool red(node_t* node) const { return node && node->red; } + bool black(node_t* node) const { return !red(node); } + + void rotate(node_t*& a, bool dir) { + node_t*& b = a->link[!dir]; + node_t*& c = b->link[dir]; + a->red = 1, b->red = 0; + std::swap(a, b); + std::swap(b, c); + } + + void rotateTwice(node_t*& node, bool dir) { + rotate(node->link[!dir], !dir); + rotate(node, dir); + } + + node_t* insert(node_t*& node, const T& value) { + if(!node) { nodes++; node = new node_t(value); return node; } + if(node->value == value) { node->value = value; return node; } //prevent duplicate entries + + bool dir = node->value < value; + node_t* v = insert(node->link[dir], value); + if(black(node->link[dir])) return v; + + if(red(node->link[!dir])) { + node->red = 1; + node->link[0]->red = 0; + node->link[1]->red = 0; + } else if(red(node->link[dir]->link[dir])) { + rotate(node, !dir); + } else if(red(node->link[dir]->link[!dir])) { + rotateTwice(node, !dir); + } + + return v; + } + + void balance(node_t*& node, bool dir, bool& done) { + node_t* p = node; + node_t* s = node->link[!dir]; + if(!s) return; + + if(red(s)) { + rotate(node, dir); + s = p->link[!dir]; + } + + if(black(s->link[0]) && black(s->link[1])) { + if(red(p)) done = 1; + p->red = 0, s->red = 1; + } else { + bool save = p->red; + bool head = node == p; + + if(red(s->link[!dir])) rotate(p, dir); + else rotateTwice(p, dir); + + p->red = save; + p->link[0]->red = 0; + p->link[1]->red = 0; + + if(head) node = p; + else node->link[dir] = p; + + done = 1; + } + } + + void remove(node_t*& node, const T* value, bool& done) { + if(!node) { done = 1; return; } + + if(node->value == *value) { + if(!node->link[0] || !node->link[1]) { + node_t* save = node->link[!node->link[0]]; + + if(red(node)) done = 1; + else if(red(save)) save->red = 0, done = 1; + + nodes--; + delete node; + node = save; + return; + } else { + node_t* heir = node->link[0]; + while(heir->link[1]) heir = heir->link[1]; + node->value = heir->value; + value = &heir->value; + } + } + + bool dir = node->value < *value; + remove(node->link[dir], value, done); + if(!done) balance(node, dir, done); + } +}; + +} + +#endif diff --git a/src/dep/libs/nall/sha256.hpp b/src/dep/libs/nall/sha256.hpp new file mode 100755 index 000000000..2be2d7b26 --- /dev/null +++ b/src/dep/libs/nall/sha256.hpp @@ -0,0 +1,147 @@ +#ifndef NALL_SHA256_HPP +#define NALL_SHA256_HPP + +//author: vladitx + +#include + +namespace nall { + +#define PTR(t, a) ((t*)(a)) + +#define SWAP32(x) ((uint32_t)( \ + (((uint32_t)(x) & 0x000000ff) << 24) | \ + (((uint32_t)(x) & 0x0000ff00) << 8) | \ + (((uint32_t)(x) & 0x00ff0000) >> 8) | \ + (((uint32_t)(x) & 0xff000000) >> 24) \ +)) + +#define ST32(a, d) *PTR(uint32_t, a) = (d) +#define ST32BE(a, d) ST32(a, SWAP32(d)) + +#define LD32(a) *PTR(uint32_t, a) +#define LD32BE(a) SWAP32(LD32(a)) + +#define LSL32(x, n) ((uint32_t)(x) << (n)) +#define LSR32(x, n) ((uint32_t)(x) >> (n)) +#define ROR32(x, n) (LSR32(x, n) | LSL32(x, 32 - (n))) + +//first 32 bits of the fractional parts of the square roots of the first 8 primes 2..19 +static const uint32_t T_H[8] = { + 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19, +}; + +//first 32 bits of the fractional parts of the cube roots of the first 64 primes 2..311 +static const uint32_t T_K[64] = { + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, + 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, + 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2, +}; + +struct sha256_ctx { + uint8_t in[64]; + unsigned inlen; + + uint32_t w[64]; + uint32_t h[8]; + uint64_t len; +}; + +inline void sha256_init(sha256_ctx* p) { + memset(p, 0, sizeof(sha256_ctx)); + memcpy(p->h, T_H, sizeof(T_H)); +} + +static void sha256_block(sha256_ctx* p) { + unsigned i; + uint32_t s0, s1; + uint32_t a, b, c, d, e, f, g, h; + uint32_t t1, t2, maj, ch; + + for(i = 0; i < 16; i++) p->w[i] = LD32BE(p->in + i * 4); + + for(i = 16; i < 64; i++) { + s0 = ROR32(p->w[i - 15], 7) ^ ROR32(p->w[i - 15], 18) ^ LSR32(p->w[i - 15], 3); + s1 = ROR32(p->w[i - 2], 17) ^ ROR32(p->w[i - 2], 19) ^ LSR32(p->w[i - 2], 10); + p->w[i] = p->w[i - 16] + s0 + p->w[i - 7] + s1; + } + + a = p->h[0]; b = p->h[1]; c = p->h[2]; d = p->h[3]; + e = p->h[4]; f = p->h[5]; g = p->h[6]; h = p->h[7]; + + for(i = 0; i < 64; i++) { + s0 = ROR32(a, 2) ^ ROR32(a, 13) ^ ROR32(a, 22); + maj = (a & b) ^ (a & c) ^ (b & c); + t2 = s0 + maj; + s1 = ROR32(e, 6) ^ ROR32(e, 11) ^ ROR32(e, 25); + ch = (e & f) ^ (~e & g); + t1 = h + s1 + ch + T_K[i] + p->w[i]; + + h = g; g = f; f = e; e = d + t1; + d = c; c = b; b = a; a = t1 + t2; + } + + p->h[0] += a; p->h[1] += b; p->h[2] += c; p->h[3] += d; + p->h[4] += e; p->h[5] += f; p->h[6] += g; p->h[7] += h; + + //next block + p->inlen = 0; +} + +inline void sha256_chunk(sha256_ctx* p, const uint8_t* s, unsigned len) { + unsigned l; + p->len += len; + + while(len) { + l = 64 - p->inlen; + l = (len < l) ? len : l; + + memcpy(p->in + p->inlen, s, l); + s += l; + p->inlen += l; + len -= l; + + if(p->inlen == 64) sha256_block(p); + } +} + +inline void sha256_final(sha256_ctx* p) { + uint64_t len; + p->in[p->inlen++] = 0x80; + + if(p->inlen > 56) { + memset(p->in + p->inlen, 0, 64 - p->inlen); + sha256_block(p); + } + + memset(p->in + p->inlen, 0, 56 - p->inlen); + + len = p->len << 3; + ST32BE(p->in + 56, len >> 32); + ST32BE(p->in + 60, len); + sha256_block(p); +} + +inline void sha256_hash(sha256_ctx* p, uint8_t* s) { + uint32_t *t = (uint32_t*)s; + for(unsigned i = 0; i < 8; i++) ST32BE(t++, p->h[i]); +} + +#undef PTR +#undef SWAP32 +#undef ST32 +#undef ST32BE +#undef LD32 +#undef LD32BE +#undef LSL32 +#undef LSR32 +#undef ROR32 + +} + +#endif diff --git a/src/dep/libs/nall/smtp.hpp b/src/dep/libs/nall/smtp.hpp new file mode 100755 index 000000000..ce1a02ad6 --- /dev/null +++ b/src/dep/libs/nall/smtp.hpp @@ -0,0 +1,318 @@ +#ifndef NALL_SMTP_HPP +#define NALL_SMTP_HPP + +#include +#include +#include + +#if !defined(_WIN32) + #include + #include + #include + #include +#else + #include + #include + #include +#endif + +namespace nall { + +struct SMTP { + enum class Format : unsigned { Plain, HTML }; + + inline void server(string server, uint16_t port = 25); + inline void from(string mail, string name = ""); + inline void to(string mail, string name = ""); + inline void cc(string mail, string name = ""); + inline void bcc(string mail, string name = ""); + inline void attachment(const uint8_t* data, unsigned size, string name); + inline bool attachment(string filename, string name = ""); + inline void subject(string subject); + inline void body(string body, Format format = Format::Plain); + + inline bool send(); + inline string message(); + inline string response(); + + #ifdef _WIN32 + inline int close(int); + inline SMTP(); + #endif + +private: + struct Information { + string server; + uint16_t port; + struct Contact { + string mail; + string name; + }; + Contact from; + vector to; + vector cc; + vector bcc; + struct Attachment { + vector buffer; + string name; + }; + string subject; + string body; + Format format = Format::Plain; + vector attachments; + + string message; + string response; + } info; + + inline bool send(int sock, const string& text); + inline string recv(int sock); + inline string boundary(); + inline string filename(const string& filename); + inline string contact(const Information::Contact& contact); + inline string contacts(const vector& contacts); + inline string split(const string& text); +}; + +void SMTP::server(string server, uint16_t port) { + info.server = server; + info.port = port; +} + +void SMTP::from(string mail, string name) { + info.from = {mail, name}; +} + +void SMTP::to(string mail, string name) { + info.to.append({mail, name}); +} + +void SMTP::cc(string mail, string name) { + info.cc.append({mail, name}); +} + +void SMTP::bcc(string mail, string name) { + info.bcc.append({mail, name}); +} + +void SMTP::attachment(const uint8_t* data, unsigned size, string name) { + vector buffer; + buffer.resize(size); + memcpy(buffer.data(), data, size); + info.attachments.append({std::move(buffer), name}); +} + +bool SMTP::attachment(string filename, string name) { + if(!file::exists(filename)) return false; + if(name == "") name = notdir(filename); + auto buffer = file::read(filename); + info.attachments.append({std::move(buffer), name}); + return true; +} + +void SMTP::subject(string subject) { + info.subject = subject; +} + +void SMTP::body(string body, Format format) { + info.body = body; + info.format = format; +} + +bool SMTP::send() { + info.message.append("From: =?UTF-8?B?", Base64::encode(contact(info.from)), "?=\r\n"); + info.message.append("To: =?UTF-8?B?", Base64::encode(contacts(info.to)), "?=\r\n"); + info.message.append("Cc: =?UTF-8?B?", Base64::encode(contacts(info.cc)), "?=\r\n"); + info.message.append("Subject: =?UTF-8?B?", Base64::encode(info.subject), "?=\r\n"); + + string uniqueID = boundary(); + + info.message.append("MIME-Version: 1.0\r\n"); + info.message.append("Content-Type: multipart/mixed; boundary=", uniqueID, "\r\n"); + info.message.append("\r\n"); + + string format = (info.format == Format::Plain ? "text/plain" : "text/html"); + + info.message.append("--", uniqueID, "\r\n"); + info.message.append("Content-Type: ", format, "; charset=UTF-8\r\n"); + info.message.append("Content-Transfer-Encoding: base64\r\n"); + info.message.append("\r\n"); + info.message.append(split(Base64::encode(info.body)), "\r\n"); + info.message.append("\r\n"); + + for(auto& attachment : info.attachments) { + info.message.append("--", uniqueID, "\r\n"); + info.message.append("Content-Type: application/octet-stream\r\n"); + info.message.append("Content-Transfer-Encoding: base64\r\n"); + info.message.append("Content-Disposition: attachment; size=", attachment.buffer.size(), "; filename*=UTF-8''", filename(attachment.name), "\r\n"); + info.message.append("\r\n"); + info.message.append(split(Base64::encode(attachment.buffer)), "\r\n"); + info.message.append("\r\n"); + } + + info.message.append("--", uniqueID, "--\r\n"); + + addrinfo hints; + memset(&hints, 0, sizeof(addrinfo)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_PASSIVE; + + addrinfo* serverinfo; + int status = getaddrinfo(info.server, string(info.port), &hints, &serverinfo); + if(status != 0) return false; + + int sock = socket(serverinfo->ai_family, serverinfo->ai_socktype, serverinfo->ai_protocol); + if(sock == -1) return false; + + int result = connect(sock, serverinfo->ai_addr, serverinfo->ai_addrlen); + if(result == -1) return false; + + string response; + info.response.append(response = recv(sock)); + if(!response.beginswith("220 ")) { close(sock); return false; } + + send(sock, {"HELO ", info.server, "\r\n"}); + info.response.append(response = recv(sock)); + if(!response.beginswith("250 ")) { close(sock); return false; } + + send(sock, {"MAIL FROM: <", info.from.mail, ">\r\n"}); + info.response.append(response = recv(sock)); + if(!response.beginswith("250 ")) { close(sock); return false; } + + for(auto& contact : info.to) { + send(sock, {"RCPT TO: <", contact.mail, ">\r\n"}); + info.response.append(response = recv(sock)); + if(!response.beginswith("250 ")) { close(sock); return false; } + } + + for(auto& contact : info.cc) { + send(sock, {"RCPT TO: <", contact.mail, ">\r\n"}); + info.response.append(response = recv(sock)); + if(!response.beginswith("250 ")) { close(sock); return false; } + } + + for(auto& contact : info.bcc) { + send(sock, {"RCPT TO: <", contact.mail, ">\r\n"}); + info.response.append(response = recv(sock)); + if(!response.beginswith("250 ")) { close(sock); return false; } + } + + send(sock, {"DATA\r\n"}); + info.response.append(response = recv(sock)); + if(!response.beginswith("354 ")) { close(sock); return false; } + + send(sock, {info.message, "\r\n", ".\r\n"}); + info.response.append(response = recv(sock)); + if(!response.beginswith("250 ")) { close(sock); return false; } + + send(sock, {"QUIT\r\n"}); + info.response.append(response = recv(sock)); +//if(!response.beginswith("221 ")) { close(sock); return false; } + + close(sock); + return true; +} + +string SMTP::message() { + return info.message; +} + +string SMTP::response() { + return info.response; +} + +bool SMTP::send(int sock, const string& text) { + const char* data = text.data(); + unsigned size = text.size(); + while(size) { + int length = ::send(sock, (const char*)data, size, 0); + if(length == -1) return false; + data += length; + size -= length; + } + return true; +} + +string SMTP::recv(int sock) { + vector buffer; + while(true) { + char c; + if(::recv(sock, &c, sizeof(char), 0) < 1) break; + buffer.append(c); + if(c == '\n') break; + } + buffer.append(0); + return buffer; +} + +string SMTP::boundary() { + random_lfsr random; + random.seed(time(0)); + string boundary; + for(unsigned n = 0; n < 16; n++) boundary.append(hex<2>(random())); + return boundary; +} + +string SMTP::filename(const string& filename) { + string result; + for(auto& n : filename) { + if(n <= 32 || n >= 127) result.append("%", hex<2>(n)); + else result.append(n); + } + return result; +} + +string SMTP::contact(const Information::Contact& contact) { + if(!contact.name) return contact.mail; + return {"\"", contact.name, "\" <", contact.mail, ">"}; +} + +string SMTP::contacts(const vector& contacts) { + string result; + for(auto& contact : contacts) { + result.append(this->contact(contact), "; "); + } + result.rtrim<1>("; "); + return result; +} + +string SMTP::split(const string& text) { + string result; + + unsigned offset = 0; + while(offset < text.size()) { + unsigned length = min(76, text.size() - offset); + if(length < 76) { + result.append(text.slice(offset)); + } else { + result.append(text.slice(offset, 76), "\r\n"); + } + offset += length; + } + + return result; +} + +#ifdef _WIN32 +int SMTP::close(int sock) { + return closesocket(sock); +} + +SMTP::SMTP() { + int sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if(sock == INVALID_SOCKET && WSAGetLastError() == WSANOTINITIALISED) { + WSADATA wsaData; + if(WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { + WSACleanup(); + return; + } + } else { + close(sock); + } +} +#endif + +} + +#endif diff --git a/src/dep/libs/nall/sort.hpp b/src/dep/libs/nall/sort.hpp new file mode 100755 index 000000000..5517dd3a5 --- /dev/null +++ b/src/dep/libs/nall/sort.hpp @@ -0,0 +1,77 @@ +#ifndef NALL_SORT_HPP +#define NALL_SORT_HPP + +#include +#include + +//class: merge sort +//average: O(n log n) +//worst: O(n log n) +//memory: O(n) +//stack: O(log n) +//stable?: yes + +//note: merge sort was chosen over quick sort, because: +//* it is a stable sort +//* it lacks O(n^2) worst-case overhead + +#define NALL_SORT_INSERTION +//#define NALL_SORT_SELECTION + +namespace nall { + +template void sort(T list[], unsigned size, const Comparator& lessthan) { + if(size <= 1) return; //nothing to sort + + //use insertion sort to quickly sort smaller blocks + if(size < 64) { + #if defined(NALL_SORT_INSERTION) + for(signed i = 1, j; i < size; i++) { + T copy = std::move(list[i]); + for(j = i - 1; j >= 0; j--) { + if(!lessthan(copy, list[j])) break; + list[j + 1] = std::move(list[j]); + } + list[j + 1] = std::move(copy); + } + #elif defined(NALL_SORT_SELECTION) + for(unsigned i = 0; i < size; i++) { + unsigned min = i; + for(unsigned j = i + 1; j < size; j++) { + if(lessthan(list[j], list[min])) min = j; + } + if(min != i) std::swap(list[i], list[min]); + } + #endif + return; + } + + //split list in half and recursively sort both + unsigned middle = size / 2; + sort(list, middle, lessthan); + sort(list + middle, size - middle, lessthan); + + //left and right are sorted here; perform merge sort + T* buffer = new T[size]; + unsigned offset = 0, left = 0, right = middle; + while(left < middle && right < size) { + if(!lessthan(list[right], list[left])) { + buffer[offset++] = std::move(list[left++]); + } else { + buffer[offset++] = std::move(list[right++]); + } + } + while(left < middle) buffer[offset++] = std::move(list[left++]); + while(right < size) buffer[offset++] = std::move(list[right++]); + + for(unsigned i = 0; i < size; i++) list[i] = std::move(buffer[i]); + delete[] buffer; +} + +template void sort(T list[], unsigned size) { + return sort(list, size, [](const T& l, const T& r) { return l < r; }); +} + +} + +#endif diff --git a/src/dep/libs/nall/stdint.hpp b/src/dep/libs/nall/stdint.hpp new file mode 100755 index 000000000..b6eed36fd --- /dev/null +++ b/src/dep/libs/nall/stdint.hpp @@ -0,0 +1,44 @@ +#ifndef NALL_STDINT_HPP +#define NALL_STDINT_HPP + +#if defined(_MSC_VER) + typedef signed char int8_t; + typedef signed short int16_t; + typedef signed int int32_t; + typedef signed long long int64_t; + typedef int64_t intmax_t; + #if defined(_WIN64) + typedef int64_t intptr_t; + #else + typedef int32_t intptr_t; + #endif + + typedef unsigned char uint8_t; + typedef unsigned short uint16_t; + typedef unsigned int uint32_t; + typedef unsigned long long uint64_t; + typedef uint64_t uintmax_t; + #if defined(_WIN64) + typedef uint64_t uintptr_t; + #else + typedef uint32_t uintptr_t; + #endif +#else + #include +#endif + +namespace nall { + +static_assert(sizeof(int8_t) == 1, "int8_t is not of the correct size" ); +static_assert(sizeof(int16_t) == 2, "int16_t is not of the correct size"); +static_assert(sizeof(int32_t) == 4, "int32_t is not of the correct size"); +static_assert(sizeof(int64_t) == 8, "int64_t is not of the correct size"); + +static_assert(sizeof(uint8_t) == 1, "int8_t is not of the correct size" ); +static_assert(sizeof(uint16_t) == 2, "int16_t is not of the correct size"); +static_assert(sizeof(uint32_t) == 4, "int32_t is not of the correct size"); +static_assert(sizeof(uint64_t) == 8, "int64_t is not of the correct size"); + +} + +#endif diff --git a/src/dep/libs/nall/stream.hpp b/src/dep/libs/nall/stream.hpp new file mode 100755 index 000000000..586ccda72 --- /dev/null +++ b/src/dep/libs/nall/stream.hpp @@ -0,0 +1,26 @@ +#ifndef NALL_STREAM_HPP +#define NALL_STREAM_HPP + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#define NALL_STREAM_INTERNAL_HPP +#include +#include +#include +#include +#include +#include +#include +#include +#undef NALL_STREAM_INTERNAL_HPP + +#endif diff --git a/src/dep/libs/nall/stream/auto.hpp b/src/dep/libs/nall/stream/auto.hpp new file mode 100755 index 000000000..0e4d8705e --- /dev/null +++ b/src/dep/libs/nall/stream/auto.hpp @@ -0,0 +1,25 @@ +#ifndef NALL_STREAM_AUTO_HPP +#define NALL_STREAM_AUTO_HPP + +namespace nall { + +#define autostream(...) (*makestream(__VA_ARGS__)) + +inline std::unique_ptr makestream(const string& path) { + if(path.ibeginsWith("http://")) return std::unique_ptr(new httpstream(path, 80)); + if(path.iendsWith(".gz")) return std::unique_ptr(new gzipstream(filestream{path})); + if(path.iendsWith(".zip")) return std::unique_ptr(new zipstream(filestream{path})); + return std::unique_ptr(new mmapstream(path)); +} + +inline std::unique_ptr makestream(uint8_t* data, unsigned size) { + return std::unique_ptr(new memorystream(data, size)); +} + +inline std::unique_ptr makestream(const uint8_t* data, unsigned size) { + return std::unique_ptr(new memorystream(data, size)); +} + +} + +#endif diff --git a/src/dep/libs/nall/stream/file.hpp b/src/dep/libs/nall/stream/file.hpp new file mode 100755 index 000000000..be8c2b709 --- /dev/null +++ b/src/dep/libs/nall/stream/file.hpp @@ -0,0 +1,42 @@ +#ifndef NALL_STREAM_FILE_HPP +#define NALL_STREAM_FILE_HPP + +#include + +namespace nall { + +struct filestream : stream { + using stream::read; + using stream::write; + + bool seekable() const { return true; } + bool readable() const { return true; } + bool writable() const { return pwritable; } + bool randomaccess() const { return false; } + + unsigned size() const { return pfile.size(); } + unsigned offset() const { return pfile.offset(); } + void seek(unsigned offset) const { pfile.seek(offset); } + + uint8_t read() const { return pfile.read(); } + void write(uint8_t data) const { pfile.write(data); } + + filestream(const string& filename) { + pfile.open(filename, file::mode::readwrite); + pwritable = pfile.open(); + if(!pwritable) pfile.open(filename, file::mode::read); + } + + filestream(const string& filename, file::mode mode) { + pfile.open(filename, mode); + pwritable = mode == file::mode::write || mode == file::mode::readwrite; + } + +private: + mutable file pfile; + bool pwritable; +}; + +} + +#endif diff --git a/src/dep/libs/nall/stream/gzip.hpp b/src/dep/libs/nall/stream/gzip.hpp new file mode 100755 index 000000000..4af7a24d0 --- /dev/null +++ b/src/dep/libs/nall/stream/gzip.hpp @@ -0,0 +1,34 @@ +#ifndef NALL_STREAM_GZIP_HPP +#define NALL_STREAM_GZIP_HPP + +#include + +namespace nall { + +struct gzipstream : memorystream { + using stream::read; + using stream::write; + + gzipstream(const stream& stream) { + unsigned size = stream.size(); + uint8_t *data = new uint8_t[size]; + stream.read(data, size); + + gzip archive; + bool result = archive.decompress(data, size); + delete[] data; + if(result == false) return; + + psize = archive.size; + pdata = new uint8_t[psize]; + memcpy(pdata, archive.data, psize); + } + + ~gzipstream() { + if(pdata) delete[] pdata; + } +}; + +} + +#endif diff --git a/src/dep/libs/nall/stream/http.hpp b/src/dep/libs/nall/stream/http.hpp new file mode 100755 index 000000000..fb38a770f --- /dev/null +++ b/src/dep/libs/nall/stream/http.hpp @@ -0,0 +1,49 @@ +#ifndef NALL_STREAM_HTTP_HPP +#define NALL_STREAM_HTTP_HPP + +#include + +namespace nall { + +struct httpstream : stream { + using stream::read; + using stream::write; + + bool seekable() const { return true; } + bool readable() const { return true; } + bool writable() const { return true; } + bool randomaccess() const { return true; } + + unsigned size() const { return psize; } + unsigned offset() const { return poffset; } + void seek(unsigned offset) const { poffset = offset; } + + uint8_t read() const { return pdata[poffset++]; } + void write(uint8_t data) const { pdata[poffset++] = data; } + + uint8_t read(unsigned offset) const { return pdata[offset]; } + void write(unsigned offset, uint8_t data) const { pdata[offset] = data; } + + httpstream(const string& url, unsigned port) : pdata(nullptr), psize(0), poffset(0) { + string uri = url; + uri.ltrim<1>("http://"); + lstring part = uri.split<1>("/"); + part[1] = { "/", part[1] }; + + http connection; + if(connection.connect(part[0], port) == false) return; + connection.download(part[1], pdata, psize); + } + + ~httpstream() { + if(pdata) delete[] pdata; + } + +private: + mutable uint8_t* pdata; + mutable unsigned psize, poffset; +}; + +} + +#endif diff --git a/src/dep/libs/nall/stream/memory.hpp b/src/dep/libs/nall/stream/memory.hpp new file mode 100755 index 000000000..81e68b334 --- /dev/null +++ b/src/dep/libs/nall/stream/memory.hpp @@ -0,0 +1,47 @@ +#ifndef NALL_STREAM_MEMORY_HPP +#define NALL_STREAM_MEMORY_HPP + +#include + +namespace nall { + +struct memorystream : stream { + using stream::read; + using stream::write; + + bool seekable() const { return true; } + bool readable() const { return true; } + bool writable() const { return pwritable; } + bool randomaccess() const { return true; } + + uint8_t* data() const { return pdata; } + unsigned size() const { return psize; } + unsigned offset() const { return poffset; } + void seek(unsigned offset) const { poffset = offset; } + + uint8_t read() const { return pdata[poffset++]; } + void write(uint8_t data) const { pdata[poffset++] = data; } + + uint8_t read(unsigned offset) const { return pdata[offset]; } + void write(unsigned offset, uint8_t data) const { pdata[offset] = data; } + + memorystream() : pdata(nullptr), psize(0), poffset(0), pwritable(true) {} + + memorystream(uint8_t* data, unsigned size) { + pdata = data, psize = size, poffset = 0; + pwritable = true; + } + + memorystream(const uint8_t* data, unsigned size) { + pdata = (uint8_t*)data, psize = size, poffset = 0; + pwritable = false; + } + +protected: + mutable uint8_t* pdata; + mutable unsigned psize, poffset, pwritable; +}; + +} + +#endif diff --git a/src/dep/libs/nall/stream/mmap.hpp b/src/dep/libs/nall/stream/mmap.hpp new file mode 100755 index 000000000..6ea18a512 --- /dev/null +++ b/src/dep/libs/nall/stream/mmap.hpp @@ -0,0 +1,42 @@ +#ifndef NALL_STREAM_MMAP_HPP +#define NALL_STREAM_MMAP_HPP + +#include + +namespace nall { + +struct mmapstream : stream { + using stream::read; + using stream::write; + + bool seekable() const { return true; } + bool readable() const { return true; } + bool writable() const { return pwritable; } + bool randomaccess() const { return true; } + + unsigned size() const { return pmmap.size(); } + unsigned offset() const { return poffset; } + void seek(unsigned offset) const { poffset = offset; } + + uint8_t read() const { return pdata[poffset++]; } + void write(uint8_t data) const { pdata[poffset++] = data; } + + uint8_t read(unsigned offset) const { return pdata[offset]; } + void write(unsigned offset, uint8_t data) const { pdata[offset] = data; } + + mmapstream(const string& filename) { + pmmap.open(filename, filemap::mode::readwrite); + pwritable = pmmap.open(); + if(!pwritable) pmmap.open(filename, filemap::mode::read); + pdata = pmmap.data(), poffset = 0; + } + +private: + mutable filemap pmmap; + mutable uint8_t* pdata; + mutable unsigned pwritable, poffset; +}; + +} + +#endif diff --git a/src/dep/libs/nall/stream/stream.hpp b/src/dep/libs/nall/stream/stream.hpp new file mode 100755 index 000000000..6e78b18a0 --- /dev/null +++ b/src/dep/libs/nall/stream/stream.hpp @@ -0,0 +1,101 @@ +#ifndef NALL_STREAM_STREAM_HPP +#define NALL_STREAM_STREAM_HPP + +namespace nall { + +struct stream { + virtual bool seekable() const = 0; + virtual bool readable() const = 0; + virtual bool writable() const = 0; + virtual bool randomaccess() const = 0; + + virtual uint8_t* data() const { return nullptr; } + virtual unsigned size() const = 0; + virtual unsigned offset() const = 0; + virtual void seek(unsigned offset) const = 0; + + virtual uint8_t read() const = 0; + virtual void write(uint8_t data) const = 0; + + virtual uint8_t read(unsigned) const { return 0; } + virtual void write(unsigned, uint8_t) const {} + + operator bool() const { + return size(); + } + + bool empty() const { + return size() == 0; + } + + bool end() const { + return offset() >= size(); + } + + uintmax_t readl(unsigned length = 1) const { + uintmax_t data = 0, shift = 0; + while(length--) { data |= read() << shift; shift += 8; } + return data; + } + + uintmax_t readm(unsigned length = 1) const { + uintmax_t data = 0; + while(length--) data = (data << 8) | read(); + return data; + } + + void read(uint8_t* data, unsigned length) const { + while(length--) *data++ = read(); + } + + string text() const { + string buffer; + buffer.resize(size() + 1); + buffer[size()] = 0; + seek(0); + read((uint8_t*)buffer.data(), size()); + return buffer; + } + + void writel(uintmax_t data, unsigned length = 1) const { + while(length--) { + write(data); + data >>= 8; + } + } + + void writem(uintmax_t data, unsigned length = 1) const { + uintmax_t shift = 8 * length; + while(length--) { + shift -= 8; + write(data >> shift); + } + } + + void write(const uint8_t* data, unsigned length) const { + while(length--) write(*data++); + } + + struct byte { + operator uint8_t() const { return s.read(offset); } + byte& operator=(uint8_t data) { s.write(offset, data); return *this; } + byte(const stream& s, unsigned offset) : s(s), offset(offset) {} + + private: + const stream& s; + const unsigned offset; + }; + + byte operator[](unsigned offset) const { + return byte(*this, offset); + } + + stream() {} + virtual ~stream() {} + stream(const stream&) = delete; + stream& operator=(const stream&) = delete; +}; + +} + +#endif diff --git a/src/dep/libs/nall/stream/vector.hpp b/src/dep/libs/nall/stream/vector.hpp new file mode 100755 index 000000000..0210e93ad --- /dev/null +++ b/src/dep/libs/nall/stream/vector.hpp @@ -0,0 +1,39 @@ +#ifndef NALL_STREAM_VECTOR_HPP +#define NALL_STREAM_VECTOR_HPP + +#include +#include + +namespace nall { + +struct vectorstream : stream { + using stream::read; + using stream::write; + + bool seekable() const { return true; } + bool readable() const { return true; } + bool writable() const { return pwritable; } + bool randomaccess() const { return true; } + + uint8_t* data() const { return memory.data(); } + unsigned size() const { return memory.size(); } + unsigned offset() const { return poffset; } + void seek(unsigned offset) const { poffset = offset; } + + uint8_t read() const { return memory[poffset++]; } + void write(uint8_t data) const { memory[poffset++] = data; } + + uint8_t read(unsigned offset) const { return memory[offset]; } + void write(unsigned offset, uint8_t data) const { memory[offset] = data; } + + vectorstream(vector& memory) : memory(memory), poffset(0), pwritable(true) {} + vectorstream(const vector& memory) : memory((vector&)memory), poffset(0), pwritable(false) {} + +protected: + vector& memory; + mutable unsigned poffset, pwritable; +}; + +} + +#endif diff --git a/src/dep/libs/nall/stream/zip.hpp b/src/dep/libs/nall/stream/zip.hpp new file mode 100755 index 000000000..dfb4767bc --- /dev/null +++ b/src/dep/libs/nall/stream/zip.hpp @@ -0,0 +1,38 @@ +#ifndef NALL_STREAM_ZIP_HPP +#define NALL_STREAM_ZIP_HPP + +#include + +namespace nall { + +struct zipstream : memorystream { + using stream::read; + using stream::write; + + zipstream(const stream& stream, const string& filter = "*") { + unsigned size = stream.size(); + uint8_t* data = new uint8_t[size]; + stream.read(data, size); + + unzip archive; + if(archive.open(data, size) == false) return; + delete[] data; + + for(auto& file : archive.file) { + if(file.name.match(filter)) { + auto buffer = archive.extract(file); + psize = buffer.size(); + pdata = buffer.move(); + return; + } + } + } + + ~zipstream() { + if(pdata) delete[] pdata; + } +}; + +} + +#endif diff --git a/src/dep/libs/nall/string.hpp b/src/dep/libs/nall/string.hpp new file mode 100755 index 000000000..adf86b31c --- /dev/null +++ b/src/dep/libs/nall/string.hpp @@ -0,0 +1,53 @@ +#ifndef NALL_STRING_HPP +#define NALL_STRING_HPP + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define NALL_STRING_INTERNAL_HPP +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#undef NALL_STRING_INTERNAL_HPP + +#endif diff --git a/src/dep/libs/nall/string/allocator/copy-on-write.hpp b/src/dep/libs/nall/string/allocator/copy-on-write.hpp new file mode 100755 index 000000000..3890ce4ca --- /dev/null +++ b/src/dep/libs/nall/string/allocator/copy-on-write.hpp @@ -0,0 +1,104 @@ +#ifdef NALL_STRING_INTERNAL_HPP + +/* +copy on write (COW) allocator +sizeof(string) == 24 (amd64) + +utilizes a shared_ptr to reference count strings +allows string copies to execute as fast as string moves +requires extra computations, which will be slower for all string sizes + +pros: +* lower memory usage +* pass-by-value does not require heap allocation; obviates pass-by-const-reference + +cons: +* added overhead to fetch data() +* added heap allocation for reference-count pool +* no potential for in-place resize (always copies) +* larger sizeof(string) + +*/ + +namespace nall { + +char* string::data() { + if(!_data.unique()) _copy(); + return _data.get(); +} + +const char* string::data() const { + if(!_data) return ""; + return _data.get(); +} + +//copy _data (to make unique or to grow in size) +void string::_copy() { + auto copy = new char[_capacity + 1]; + if(_data.get()) memcpy(copy, _data.get(), min(_capacity, _size)); + copy[_size] = 0; + copy[_capacity] = 0; + _data.reset(copy); +} + +//amortize growth to O(log n) +//allocate one extra byte to always store null-terminator for libc usage +void string::reserve(unsigned capacity) { + if(capacity > _capacity) { + _capacity = bit::round(capacity + 1) - 1; + _copy(); + } +} + +void string::resize(unsigned size) { + reserve(size); + data()[_size = size] = 0; +} + +void string::reset() { + _data.reset(); + _capacity = 0; + _size = 0; +} + +string& string::operator=(const string& source) { + if(&source == this) return *this; + reset(); + _data = source._data; + _capacity = source._capacity; + _size = source._size; + return *this; +} + +string& string::operator=(string&& source) { + if(&source == this) return *this; + reset(); + _data = std::move(source._data); + _capacity = source._capacity; + _size = source._size; + source._capacity = 0; + source._size = 0; + return *this; +} + +template string::string(T&& source, Args&&... args) { + construct(); + sprint(*this, std::forward(source), std::forward(args)...); +} + +string::string() { + construct(); +} + +string::~string() { + reset(); +} + +void string::construct() { + _capacity = 0; + _size = 0; +} + +} + +#endif diff --git a/src/dep/libs/nall/string/allocator/small-string-optimization.hpp b/src/dep/libs/nall/string/allocator/small-string-optimization.hpp new file mode 100755 index 000000000..8e3c41178 --- /dev/null +++ b/src/dep/libs/nall/string/allocator/small-string-optimization.hpp @@ -0,0 +1,111 @@ +#ifdef NALL_STRING_INTERNAL_HPP + +/* +small string optimization (SSO) allocator +sizeof(string) == 16 (amd64) + +utilizes a union to store small strings directly into text pointer +bypasses the need to allocate heap memory for small strings +requires extra computations, which can be slower for large strings + +pros: +* potential for in-place resize +* no heap allocation when (capacity < 8) + +cons: +* added overhead to fetch data() +* 32-bit platforms limited to (capacity < 4) +* pass-by-value requires heap allocation + +*/ + +namespace nall { + +char* string::data() { + if(_capacity < SSO) return _text; + return _data; +} + +const char* string::data() const { + if(_capacity < SSO) return _text; + return _data; +} + +void string::reserve(unsigned capacity) { + if(capacity > _capacity) { + if(capacity >= SSO) { + capacity = bit::round(capacity + 1) - 1; + if(_capacity < SSO) { + char temp[SSO]; + memcpy(temp, _text, SSO); + _data = (char*)malloc(capacity + 1); + memcpy(_data, temp, SSO); + } else { + _data = (char*)realloc(_data, capacity + 1); + } + } + _capacity = capacity; + data()[_capacity] = 0; + } +} + +void string::resize(unsigned size) { + reserve(size); + data()[_size = size] = 0; +} + +void string::reset() { + if(_capacity >= SSO) free(_data); + _data = nullptr; + _capacity = SSO - 1; + _size = 0; +} + +string& string::operator=(const string& source) { + if(&source == this) return *this; + reset(); + if(source._capacity >= SSO) { + _data = (char*)malloc(source._capacity + 1); + _capacity = source._capacity; + _size = source._size; + memcpy(_data, source.data(), source.size() + 1); + } else { + memcpy(_text, source._text, SSO); + _capacity = SSO - 1; + _size = strlen(_text); + } + return *this; +} + +string& string::operator=(string&& source) { + if(&source == this) return *this; + reset(); + memcpy(this, &source, sizeof(string)); + source._data = nullptr; + source._capacity = SSO - 1; + source._size = 0; + return *this; +} + +template string::string(T&& source, Args&&... args) { + construct(); + sprint(*this, std::forward(source), std::forward(args)...); +} + +string::string() { + construct(); +} + +string::~string() { + reset(); +} + +void string::construct() { + _data = nullptr; + _capacity = SSO - 1; + _size = 0; +} + +} + +#endif diff --git a/src/dep/libs/nall/string/allocator/vector.hpp b/src/dep/libs/nall/string/allocator/vector.hpp new file mode 100755 index 000000000..7b7aaaaf7 --- /dev/null +++ b/src/dep/libs/nall/string/allocator/vector.hpp @@ -0,0 +1,94 @@ +#ifdef NALL_STRING_INTERNAL_HPP + +/* +vector allocator +sizeof(string) == 16 (amd64) + +utilizes a raw string pointer +always allocates memory onto the heap when string is not empty + +pros: +* potential for in-place resize +* simplicity + +cons: +* always allocates heap memory on (capacity > 0) +* pass-by-value requires heap allocation + +*/ + +namespace nall { + +char* string::data() { + if(_capacity == 0) reserve(1); + return _data; +} + +const char* string::data() const { + if(_capacity == 0) return ""; + return _data; +} + +void string::reserve(unsigned capacity) { + if(capacity > _capacity) { + _capacity = bit::round(capacity + 1) - 1; + _data = (char*)realloc(_data, _capacity + 1); + _data[_capacity] = 0; + } +} + +void string::resize(unsigned size) { + reserve(size); + data()[_size = size] = 0; +} + +void string::reset() { + if(_data) { free(_data); _data = nullptr; } + _capacity = 0; + _size = 0; +} + +string& string::operator=(const string& source) { + if(&source == this) return *this; + reset(); + _data = (char*)malloc(source._size + 1); + _capacity = source._size; + _size = source._size; + memcpy(_data, source.data(), source.size() + 1); + return *this; +} + +string& string::operator=(string&& source) { + if(&source == this) return *this; + reset(); + _data = source._data; + _capacity = source._capacity; + _size = source._size; + source._data = nullptr; + source._capacity = 0; + source._size = 0; + return *this; +} + +template string::string(T&& source, Args&&... args) { + construct(); + sprint(*this, std::forward(source), std::forward(args)...); +} + +string::string() { + construct(); +} + +string::~string() { + reset(); +} + +void string::construct() { + _data = nullptr; + _capacity = 0; + _size = 0; +} + +} + +#endif diff --git a/src/dep/libs/nall/string/base.hpp b/src/dep/libs/nall/string/base.hpp new file mode 100755 index 000000000..284c23124 --- /dev/null +++ b/src/dep/libs/nall/string/base.hpp @@ -0,0 +1,227 @@ +#ifdef NALL_STRING_INTERNAL_HPP + +namespace nall { + +struct string; +struct stringref; +struct lstring; +typedef const stringref& rstring; + +//#define NALL_STRING_ALLOCATOR_COPY_ON_WRITE +#define NALL_STRING_ALLOCATOR_SMALL_STRING_OPTIMIZATION +//#define NALL_STRING_ALLOCATOR_VECTOR + +struct string { +protected: + #if defined(NALL_STRING_ALLOCATOR_COPY_ON_WRITE) + inline void _copy(); + std::shared_ptr _data; + #endif + + #if defined(NALL_STRING_ALLOCATOR_SMALL_STRING_OPTIMIZATION) + enum : unsigned { SSO = 24 }; + union { + char* _data; + char _text[SSO]; + }; + #endif + + #if defined(NALL_STRING_ALLOCATOR_VECTOR) + char* _data; + #endif + + unsigned _capacity; + unsigned _size; + +public: + //core.hpp + inline char* data(); + inline const char* data() const; + inline unsigned length() const; + inline unsigned size() const; + inline unsigned capacity() const; + inline bool empty() const; + + inline void reset(); + inline void reserve(unsigned); + inline void resize(unsigned); + inline void clear(char); + + inline unsigned hash() const; + + template inline string& assign(Args&&... args); + template inline string& append(Args&&... args); + + //file.hpp + inline static string read(const string& filename); + + //datetime.hpp + inline static string date(); + inline static string time(); + inline static string datetime(); + + //replace.hpp + template inline string& replace(rstring, rstring); + template inline string& ireplace(rstring, rstring); + template inline string& qreplace(rstring, rstring); + template inline string& iqreplace(rstring, rstring); + + //wrapper.hpp + template inline lstring split(rstring) const; + template inline lstring isplit(rstring) const; + template inline lstring qsplit(rstring) const; + template inline lstring iqsplit(rstring) const; + + inline signed compare(rstring) const; + inline signed icompare(rstring) const; + + inline bool equals(rstring) const; + inline bool iequals(rstring) const; + + inline bool match(rstring) const; + inline bool imatch(rstring) const; + + inline bool beginsWith(rstring) const; + inline bool ibeginsWith(rstring) const; + inline bool endsWith(rstring) const; + inline bool iendsWith(rstring) const; + + inline string slice(unsigned offset, unsigned length = ~0u) const; + + inline string& lower(); + inline string& upper(); + inline string& qlower(); + inline string& qupper(); + inline string& transform(rstring before, rstring after); + inline string& reverse(); + + template inline string& ltrim() { return ltrim(" "); } + template inline string& ltrim(rstring key); + + template inline string& rtrim() { return rtrim(" "); } + template inline string& rtrim(rstring key); + + template inline string& trim() { return trim(" "); } + template inline string& trim(rstring key); + template inline string& trim(rstring key, rstring rkey); + + inline string& strip(); + + inline optional find(rstring key) const; + inline optional ifind(rstring key) const; + inline optional qfind(rstring key) const; + inline optional iqfind(rstring key) const; + + //core.hpp + inline explicit operator bool() const; + inline operator const char*() const; + inline char& operator[](signed); + inline const char& operator[](signed) const; + + inline bool operator==(const char*) const; + inline bool operator!=(const char*) const; + inline bool operator< (const char*) const; + inline bool operator<=(const char*) const; + inline bool operator> (const char*) const; + inline bool operator>=(const char*) const; + + inline string& operator=(const string&); + inline string& operator=(string&&); + + template inline string(T&& source, Args&&... args); + inline string(); + inline string(const string&); + inline string(string&&); + inline ~string(); + + inline char* begin() { return &data()[0]; } + inline char* end() { return &data()[size()]; } + inline const char* begin() const { return &data()[0]; } + inline const char* end() const { return &data()[size()]; } + +//protected: + struct exception_out_of_bounds{}; + template inline string& ureplace(rstring, rstring); + inline string& _append(const char*); + +private: + inline void construct(); + +#if defined(QSTRING_H) +public: + inline operator QString() const; +#endif +}; + +//list.hpp +struct lstring : vector { + inline optional find(rstring) const; + inline string merge(const string&) const; + inline lstring& isort(); + inline lstring& strip(); + inline void append() {} + template inline void append(const string&, Args&&...); + + inline bool operator==(const lstring&) const; + inline bool operator!=(const lstring&) const; + + inline lstring& operator=(const lstring&); + inline lstring& operator=(lstring&); + inline lstring& operator=(lstring&&); + + template inline lstring(Args&&... args); + inline lstring(const lstring&); + inline lstring(lstring&); + inline lstring(lstring&&); + + //split.hpp + template inline lstring& split(rstring, rstring); + template inline lstring& isplit(rstring, rstring); + template inline lstring& qsplit(rstring, rstring); + template inline lstring& iqsplit(rstring, rstring); + +protected: + template inline lstring& usplit(rstring, rstring); +}; + +//filename.hpp +inline string dir(string name); +inline string notdir(string name); +inline string parentdir(string name); +inline string basename(string name); +inline string extension(string name); +inline string tempname(); + +//format.hpp +template inline string format(const string& value); +template inline string hex(uintmax_t value); +template inline string octal(uintmax_t value); +template inline string binary(uintmax_t value); + +//platform.hpp +inline string activepath(); +inline string realpath(const string& name); +inline string userpath(); +inline string configpath(); +inline string sharedpath(); +inline string temppath(); + +//utility.hpp +inline string substr(rstring source, unsigned offset = 0, unsigned length = ~0u); +inline string sha256(const uint8_t* data, unsigned size); +inline bool tokenize(lstring& list, const char* s, const char* p); + +inline char* integer(char* result, intmax_t value); +inline char* decimal(char* result, uintmax_t value); + +inline unsigned real(char* str, long double value); +inline string real(long double value); + +//variadic.hpp +inline void sprint(string& output); +template inline void sprint(string& output, const T& value, Args&&... args); +template inline void print(Args&&... args); + +} + +#endif diff --git a/src/dep/libs/nall/string/cast.hpp b/src/dep/libs/nall/string/cast.hpp new file mode 100755 index 000000000..ef79a47d2 --- /dev/null +++ b/src/dep/libs/nall/string/cast.hpp @@ -0,0 +1,199 @@ +#ifdef NALL_STRING_INTERNAL_HPP + +namespace nall { + +//convert any (supported) type to a const char* without constructing a new nall::string +//this is used inside istring(...) to build nall::string values +template struct stringify; + +// base types + +template<> struct stringify { + bool value; + operator const char*() const { return value ? "true" : "false"; } + stringify(bool value) : value(value) {} +}; + +template<> struct stringify { + char data[2]; + operator const char*() const { return data; } + stringify(char value) { data[0] = value, data[1] = 0; } +}; + +// signed integers + +template<> struct stringify { + char data[256]; + operator const char*() const { return data; } + stringify(signed char value) { integer(data, value); } +}; + +template<> struct stringify { + char data[256]; + operator const char*() const { return data; } + stringify(signed short value) { integer(data, value); } +}; + +template<> struct stringify { + char data[256]; + operator const char*() const { return data; } + stringify(signed int value) { integer(data, value); } +}; + +template<> struct stringify { + char data[256]; + operator const char*() const { return data; } + stringify(signed long value) { integer(data, value); } +}; + +template<> struct stringify { + char data[256]; + operator const char*() const { return data; } + stringify(signed long long value) { integer(data, value); } +}; + +template struct stringify> { + char data[256]; + operator const char*() const { return data; } + stringify(int_t value) { integer(data, value); } +}; + +// unsigned integers + +template<> struct stringify { + char data[256]; + operator const char*() const { return data; } + stringify(unsigned char value) { decimal(data, value); } +}; + +template<> struct stringify { + char data[256]; + operator const char*() const { return data; } + stringify(unsigned short value) { decimal(data, value); } +}; + +template<> struct stringify { + char data[256]; + operator const char*() const { return data; } + stringify(unsigned int value) { decimal(data, value); } +}; + +template<> struct stringify { + char data[256]; + operator const char*() const { return data; } + stringify(unsigned long value) { decimal(data, value); } +}; + +template<> struct stringify { + char data[256]; + operator const char*() const { return data; } + stringify(unsigned long long value) { decimal(data, value); } +}; + +template struct stringify> { + char data[256]; + operator const char*() const { return data; } + stringify(uint_t value) { decimal(data, value); } +}; + +// floating-point + +template<> struct stringify { + char data[256]; + operator const char*() const { return data; } + stringify(float value) { real(data, value); } +}; + +template<> struct stringify { + char data[256]; + operator const char*() const { return data; } + stringify(double value) { real(data, value); } +}; + +template<> struct stringify { + char data[256]; + operator const char*() const { return data; } + stringify(long double value) { real(data, value); } +}; + +// arrays + +template<> struct stringify> { + char* text; + operator const char*() const { return text; } + stringify(vector value) { + text = new char[value.size() + 1](); + memcpy(text, value.data(), value.size()); + } + ~stringify() { + delete[] text; + } +}; + +template<> struct stringify&> { + char* text; + operator const char*() const { return text; } + stringify(const vector& value) { + text = new char[value.size() + 1](); + memcpy(text, value.data(), value.size()); + } + ~stringify() { + delete[] text; + } +}; + +// strings + +template<> struct stringify { + const char* value; + operator const char*() const { return value; } + stringify(char* value) : value(value) {} +}; + +template<> struct stringify { + const char* value; + operator const char*() const { return value; } + stringify(const char* value) : value(value) {} +}; + +template<> struct stringify { + const string& value; + operator const char*() const { return value; } + stringify(const string& value) : value(value) {} +}; + +template<> struct stringify { + const string& value; + operator const char*() const { return value; } + stringify(const string& value) : value(value) {} +}; + +#if defined(QSTRING_H) + +template<> struct stringify { + const QString& value; + operator const char*() const { return value.toUtf8().constData(); } + stringify(const QString& value) : value(value) {} +}; + +template<> struct stringify { + const QString& value; + operator const char*() const { return value.toUtf8().constData(); } + stringify(const QString& value) : value(value) {} +}; + +string::operator QString() const { + return QString::fromUtf8(*this); +} + +#endif + +// + +template stringify make_string(T value) { + return stringify(std::forward(value)); +} + +} + +#endif diff --git a/src/dep/libs/nall/string/char.hpp b/src/dep/libs/nall/string/char.hpp new file mode 100755 index 000000000..29ac2c77b --- /dev/null +++ b/src/dep/libs/nall/string/char.hpp @@ -0,0 +1,13 @@ +#ifdef NALL_STRING_INTERNAL_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#endif diff --git a/src/dep/libs/nall/string/char/base.hpp b/src/dep/libs/nall/string/char/base.hpp new file mode 100755 index 000000000..0a029b2d1 --- /dev/null +++ b/src/dep/libs/nall/string/char/base.hpp @@ -0,0 +1,69 @@ +#ifdef NALL_STRING_INTERNAL_HPP + +//collection of functions to extend libc +//none of these functions require nall::string +//and thus, require no changes when nall::string is modified + +namespace nall { + +//compare.hpp +inline char chrlower(char c); +inline char chrupper(char c); +inline int imemcmp(const char* str1, const char* str2, unsigned size); +inline int istrcmp(const char* str1, const char* str2); +inline bool strbegin(const char* str, const char* key); +inline bool istrbegin(const char* str, const char* key); +inline bool strend(const char* str, const char* key); +inline bool istrend(const char* str, const char* key); + +//convert.hpp +inline char* strlower(char* str); +inline char* strupper(char* str); +inline char* qstrlower(char* str); +inline char* qstrupper(char* str); +inline char* strtr(char* dest, const char* before, const char* after); + +//match.hpp +inline bool strmatch(const char* str, const char* pattern); +inline bool istrmatch(const char* str, const char* pattern); +inline bool tokenize(const char* s, const char* p); + +//strm.hpp +inline unsigned strmcpy(char* target, const char* source, unsigned length); +inline unsigned strmcat(char* target, const char* source, unsigned length); +inline bool strccpy(char* target, const char* source, unsigned length); +inline bool strccat(char* target, const char* source, unsigned length); +inline void strpcpy(char*& target, const char* source, unsigned& length); + +//strpos.hpp +inline optional strpos(const char* str, const char* key); +inline optional istrpos(const char* str, const char* key); +inline optional qstrpos(const char* str, const char* key); +inline optional iqstrpos(const char* str, const char* key); +template inline optional ustrpos(const char* str, const char* key); + +//trim.hpp +template inline char* ltrim(char* str, const char* key = " "); +template inline char* rtrim(char* str, const char* key = " "); +template inline char* trim(char* str, const char* key = " "); +template inline char* trim(char* str, const char* lkey, const char* rkey); +inline char* strip(char* s); + +//utf8.hpp +struct UTF8 { + unsigned size; //size of encoded codepoint + uint64_t data; //encoded codepoint + unsigned codepoint; //decoded codepoint +}; +inline UTF8 utf8_read(const char* s); +inline void utf8_write(char* s, const UTF8& utf8); + +//utility.hpp +template alwaysinline bool chrequal(char x, char y); +template alwaysinline bool quoteskip(T*& p); +template alwaysinline bool quotecopy(char*& t, T*& p); +inline char* strduplicate(const char* s); + +} + +#endif diff --git a/src/dep/libs/nall/string/char/compare.hpp b/src/dep/libs/nall/string/char/compare.hpp new file mode 100755 index 000000000..ff9bf54b0 --- /dev/null +++ b/src/dep/libs/nall/string/char/compare.hpp @@ -0,0 +1,81 @@ +#ifdef NALL_STRING_INTERNAL_HPP + +namespace nall { + +char chrlower(char c) { + return (c >= 'A' && c <= 'Z') ? c + ('a' - 'A') : c; +} + +char chrupper(char c) { + return (c >= 'a' && c <= 'z') ? c - ('a' - 'A') : c; +} + +int imemcmp(const char* str1, const char* str2, unsigned size) { + while(size--) { + if(chrlower(*str1) != chrlower(*str2)) break; + str1++, str2++; + } + return (int)chrlower(*str1) - (int)chrlower(*str2); +} + +int istrcmp(const char* str1, const char* str2) { + while(*str1) { + if(chrlower(*str1) != chrlower(*str2)) break; + str1++, str2++; + } + return (int)chrlower(*str1) - (int)chrlower(*str2); +} + +bool strbegin(const char* str, const char* key) { + if(!str || !key) return false; + int i, ssl = strlen(str), ksl = strlen(key); + + if(ksl > ssl) return false; + return (!memcmp(str, key, ksl)); +} + +bool istrbegin(const char* str, const char* key) { + if(!str || !key) return false; + int ssl = strlen(str), ksl = strlen(key); + + if(ksl > ssl) return false; + for(int i = 0; i < ksl; i++) { + if(str[i] >= 'A' && str[i] <= 'Z') { + if(str[i] != key[i] && str[i]+0x20 != key[i])return false; + } else if(str[i] >= 'a' && str[i] <= 'z') { + if(str[i] != key[i] && str[i]-0x20 != key[i])return false; + } else { + if(str[i] != key[i])return false; + } + } + return true; +} + +bool strend(const char* str, const char* key) { + if(!str || !key) return false; + int ssl = strlen(str), ksl = strlen(key); + + if(ksl > ssl) return false; + return (!memcmp(str + ssl - ksl, key, ksl)); +} + +bool istrend(const char* str, const char* key) { + if(!str || !key) return false; + int ssl = strlen(str), ksl = strlen(key); + + if(ksl > ssl) return false; + for(int i = ssl - ksl, z = 0; i < ssl; i++, z++) { + if(str[i] >= 'A' && str[i] <= 'Z') { + if(str[i] != key[z] && str[i]+0x20 != key[z])return false; + } else if(str[i] >= 'a' && str[i] <= 'z') { + if(str[i] != key[z] && str[i]-0x20 != key[z])return false; + } else { + if(str[i] != key[z])return false; + } + } + return true; +} + +} + +#endif diff --git a/src/dep/libs/nall/string/char/convert.hpp b/src/dep/libs/nall/string/char/convert.hpp new file mode 100755 index 000000000..72e30a12f --- /dev/null +++ b/src/dep/libs/nall/string/char/convert.hpp @@ -0,0 +1,64 @@ +#ifdef NALL_STRING_INTERNAL_HPP + +namespace nall { + +char* strlower(char* str) { + if(!str) return nullptr; + int i = 0; + while(str[i]) { + str[i] = chrlower(str[i]); + i++; + } + return str; +} + +char* strupper(char* str) { + if(!str) return nullptr; + int i = 0; + while(str[i]) { + str[i] = chrupper(str[i]); + i++; + } + return str; +} + +char* qstrlower(char* s) { + if(!s) return nullptr; + bool quoted = false; + while(*s) { + if(*s == '\"' || *s == '\'') quoted ^= 1; + if(quoted == false && *s >= 'A' && *s <= 'Z') *s += 0x20; + s++; + } +} + +char* qstrupper(char* s) { + if(!s) return nullptr; + bool quoted = false; + while(*s) { + if(*s == '\"' || *s == '\'') quoted ^= 1; + if(quoted == false && *s >= 'a' && *s <= 'z') *s -= 0x20; + s++; + } +} + +char* strtr(char* dest, const char* before, const char* after) { + if(!dest || !before || !after) return dest; + int sl = strlen(dest), bsl = strlen(before), asl = strlen(after); + + if(bsl != asl || bsl == 0) return dest; //patterns must be the same length for 1:1 replace + for(unsigned i = 0; i < sl; i++) { + for(unsigned l = 0; l < bsl; l++) { + if(dest[i] == before[l]) { + dest[i] = after[l]; + break; + } + } + } + + return dest; +} + +} + +#endif diff --git a/src/dep/libs/nall/string/char/match.hpp b/src/dep/libs/nall/string/char/match.hpp new file mode 100755 index 000000000..2471d1ce7 --- /dev/null +++ b/src/dep/libs/nall/string/char/match.hpp @@ -0,0 +1,61 @@ +#ifdef NALL_STRING_INTERNAL_HPP + +namespace nall { + +bool strmatch(const char* s, const char* p) { + const char* cp = nullptr; + const char* mp = nullptr; + while(*s && *p != '*') { + if(*p != '?' && *s != *p) return false; + p++, s++; + } + while(*s) { + if(*p == '*') { + if(!*++p) return true; + mp = p, cp = s + 1; + } else if(*p == '?' || *p == *s) { + p++, s++; + } else { + p = mp, s = cp++; + } + } + while(*p == '*') p++; + return !*p; +} + +bool istrmatch(const char* s, const char* p) { + const char* cp = nullptr; + const char* mp = nullptr; + while(*s && *p != '*') { + if(*p != '?' && chrlower(*s) != chrlower(*p)) return false; + p++, s++; + } + while(*s) { + if(*p == '*') { + if(!*++p) return true; + mp = p, cp = s + 1; + } else if(*p == '?' || chrlower(*p) == chrlower(*s)) { + p++, s++; + } else { + p = mp, s = cp++; + } + } + while(*p == '*') p++; + return !*p; +} + +bool tokenize(const char* s, const char* p) { + while(*s) { + if(*p == '*') { + while(*s) if(tokenize(s++, p + 1)) return true; + return !*++p; + } + if(*s++ != *p++) return false; + } + while(*p == '*') p++; + return !*p; +} + +} + +#endif diff --git a/src/dep/libs/nall/string/char/strm.hpp b/src/dep/libs/nall/string/char/strm.hpp new file mode 100755 index 000000000..7e2b6e7c4 --- /dev/null +++ b/src/dep/libs/nall/string/char/strm.hpp @@ -0,0 +1,41 @@ +#ifdef NALL_STRING_INTERNAL_HPP + +namespace nall { + +//return = strlen(target) +unsigned strmcpy(char* target, const char* source, unsigned length) { + const char* origin = target; + if(length) { + while(*source && --length) *target++ = *source++; + *target = 0; + } + return target - origin; +} + +//return = strlen(target) +unsigned strmcat(char* target, const char* source, unsigned length) { + const char* origin = target; + while(*target && length) target++, length--; + return (target - origin) + strmcpy(target, source, length); +} + +//return = true when all of source was copied +bool strccpy(char* target, const char* source, unsigned length) { + return !source[strmcpy(target, source, length)]; +} + +//return = true when all of source was copied +bool strccat(char* target, const char* source, unsigned length) { + while(*target && length) target++, length--; + return !source[strmcpy(target, source, length)]; +} + +//return = reserved for future use +void strpcpy(char*& target, const char* source, unsigned& length) { + unsigned offset = strmcpy(target, source, length); + target += offset, length -= offset; +} + +} + +#endif diff --git a/src/dep/libs/nall/string/char/strpos.hpp b/src/dep/libs/nall/string/char/strpos.hpp new file mode 100755 index 000000000..19c232172 --- /dev/null +++ b/src/dep/libs/nall/string/char/strpos.hpp @@ -0,0 +1,33 @@ +#ifdef NALL_STRING_INTERNAL_HPP + +//usage example: +//if(auto position = strpos(str, key)) print(position(), "\n"); +//prints position of key within str; but only if it is found + +namespace nall { + +template +optional ustrpos(const char* str, const char* key) { + const char* base = str; + + while(*str) { + if(quoteskip(str)) continue; + for(unsigned n = 0;; n++) { + if(key[n] == 0) return {true, (unsigned)(str - base)}; + if(str[n] == 0) return false; + if(!chrequal(str[n], key[n])) break; + } + str++; + } + + return false; +} + +optional strpos(const char* str, const char* key) { return ustrpos(str, key); } +optional istrpos(const char* str, const char* key) { return ustrpos(str, key); } +optional qstrpos(const char* str, const char* key) { return ustrpos(str, key); } +optional iqstrpos(const char* str, const char* key) { return ustrpos(str, key); } + +} + +#endif diff --git a/src/dep/libs/nall/string/char/trim.hpp b/src/dep/libs/nall/string/char/trim.hpp new file mode 100755 index 000000000..969b6926d --- /dev/null +++ b/src/dep/libs/nall/string/char/trim.hpp @@ -0,0 +1,62 @@ +#ifdef NALL_STRING_INTERNAL_HPP + +namespace nall { + +//limit defaults to zero, which will underflow on first compare; equivalent to no limit +template char* ltrim(char* str, const char* key) { + if(!str || !key || !*key) return str; + unsigned limit = Limit; + while(strbegin(str, key)) { + char* dest = str; + char* src = str + strlen(key); + while(true) { + *dest = *src++; + if(!*dest) break; + dest++; + } + if(--limit == 0) break; + } + return str; +} + +template char* rtrim(char* str, const char* key) { + if(!str || !key || !*key) return str; + unsigned limit = Limit; + while(strend(str, key)) { + str[strlen(str) - strlen(key)] = 0; + if(--limit == 0) break; + } + return str; +} + +template char* trim(char* str, const char* key) { + return ltrim(rtrim(str, key), key); +} + +template char* trim(char* str, const char* lkey, const char* rkey) { + return ltrim(rtrim(str, rkey), lkey); +} + +//remove whitespace characters from both left and right sides of string +char* strip(char* s) { + if(!s) return nullptr; + + signed n = 0, p = 0; + while(s[n]) { + if(s[n] != ' ' && s[n] != '\t' && s[n] != '\r' && s[n] != '\n') break; + n++; + } + while(s[n]) s[p++] = s[n++]; + s[p--] = 0; + while(p >= 0) { + if(s[p] != ' ' && s[p] != '\t' && s[p] != '\r' && s[p] != '\n') break; + p--; + } + s[++p] = 0; + + return s; +} + +} + +#endif diff --git a/src/dep/libs/nall/string/char/utf8.hpp b/src/dep/libs/nall/string/char/utf8.hpp new file mode 100755 index 000000000..03eb5ef0f --- /dev/null +++ b/src/dep/libs/nall/string/char/utf8.hpp @@ -0,0 +1,37 @@ +#ifdef NALL_STRING_INTERNAL_HPP + +namespace nall { + +UTF8 utf8_read(const char* s) { + UTF8 utf8; + + if((*s & 0xfe) == 0xfc) utf8.size = 6; + else if((*s & 0xfc) == 0xf8) utf8.size = 5; + else if((*s & 0xf8) == 0xf0) utf8.size = 4; + else if((*s & 0xf0) == 0xe0) utf8.size = 3; + else if((*s & 0xe0) == 0xc0) utf8.size = 2; + else utf8.size = 1; + + utf8.data = 0; + for(unsigned n = 0; n < utf8.size; n++) { + utf8.data = (utf8.data << 8) | (uint8_t)s[n]; + } + + static uint8_t mask[] = { 0, 0x7f, 0x1f, 0x0f, 0x07, 0x03, 0x01 }; + utf8.codepoint = s[0] & mask[utf8.size]; + for(unsigned n = 1; n < utf8.size; n++) { + utf8.codepoint = (utf8.codepoint << 6) | (s[n] & 0x3f); + } + + return utf8; +} + +void utf8_write(char* s, const UTF8& utf8) { + for(signed n = utf8.size - 1, shift = 0; n >= 0; n--, shift += 8) { + s[n] = utf8.data >> shift; + } +} + +} + +#endif diff --git a/src/dep/libs/nall/string/char/utility.hpp b/src/dep/libs/nall/string/char/utility.hpp new file mode 100755 index 000000000..bb35c0202 --- /dev/null +++ b/src/dep/libs/nall/string/char/utility.hpp @@ -0,0 +1,50 @@ +#ifdef NALL_STRING_INTERNAL_HPP + +namespace nall { + +template +bool chrequal(char x, char y) { + if(Insensitive) return chrlower(x) == chrlower(y); + return x == y; +} + +template +bool quoteskip(T*& p) { + if(Quoted == false) return false; + if(*p != '\"') return false; + + while(*p == '\"') { + char x = *p++; + while(*p && *p++ != x); + } + + return true; +} + +template +bool quotecopy(char*& t, T*& p) { + if(Quoted == false) return false; + if(*p != '\"') return false; + + while(*p == '\"') { + char x = *p++; + *t++ = x; + while(*p && *p != x) *t++ = *p++; + *t++ = *p++; + } + + return true; +} + +//strdup() is not a standard function, so recreate it +char* strduplicate(const char* s) { + if(s == nullptr) return nullptr; + unsigned length = strlen(s); + char* result = (char*)malloc(length + 1); + strcpy(result, s); + return result; +} + +} + +#endif diff --git a/src/dep/libs/nall/string/core.hpp b/src/dep/libs/nall/string/core.hpp new file mode 100755 index 000000000..8a00645e8 --- /dev/null +++ b/src/dep/libs/nall/string/core.hpp @@ -0,0 +1,89 @@ +#ifdef NALL_STRING_INTERNAL_HPP + +//only allocators may access _data or modify _size and _capacity +//all other functions must use data(), size(), capacity() + +#if defined(NALL_STRING_ALLOCATOR_COPY_ON_WRITE) + #include +#elif defined(NALL_STRING_ALLOCATOR_SMALL_STRING_OPTIMIZATION) + #include +#elif defined(NALL_STRING_ALLOCATOR_VECTOR) + #include +#endif + +namespace nall { + +unsigned string::length() const { return strlen(data()); } +unsigned string::size() const { return _size; } +unsigned string::capacity() const { return _capacity; } +bool string::empty() const { return _size == 0; } + +void string::clear(char c) { + for(unsigned n = 0; n < size(); n++) data()[n] = c; +} + +unsigned string::hash() const { + const char* p = data(); + unsigned result = 5381; + while(*p) result = (result << 5) + result + *p++; + return result; +} + +template string& string::assign(Args&&... args) { + resize(0); + sprint(*this, std::forward(args)...); + return *this; +} + +template string& string::append(Args&&... args) { + sprint(*this, std::forward(args)...); + return *this; +} + +string& string::_append(const char* s) { + if(s == nullptr) return *this; + unsigned basesize = size(), length = strlen(s); + reserve(basesize + length); + memcpy(data() + basesize, s, length); + resize(basesize + length); + return *this; +} + +string::operator bool() const { + return !empty(); +} + +string::operator const char*() const { + return data(); +} + +char& string::operator[](signed position) { + if(position > size() + 1) throw exception_out_of_bounds{}; + return data()[position]; +} + +const char& string::operator[](signed position) const { + if(position > size() + 1) throw exception_out_of_bounds{}; + return data()[position]; +} + +bool string::operator==(const char* str) const { return strcmp(data(), str) == 0; } +bool string::operator!=(const char* str) const { return strcmp(data(), str) != 0; } +bool string::operator< (const char* str) const { return strcmp(data(), str) < 0; } +bool string::operator<=(const char* str) const { return strcmp(data(), str) <= 0; } +bool string::operator> (const char* str) const { return strcmp(data(), str) > 0; } +bool string::operator>=(const char* str) const { return strcmp(data(), str) >= 0; } + +string::string(const string& source) { + construct(); + operator=(source); +} + +string::string(string&& source) { + construct(); + operator=(std::move(source)); +} + +} + +#endif diff --git a/src/dep/libs/nall/string/datetime.hpp b/src/dep/libs/nall/string/datetime.hpp new file mode 100755 index 000000000..b55659a10 --- /dev/null +++ b/src/dep/libs/nall/string/datetime.hpp @@ -0,0 +1,31 @@ +#ifdef NALL_STRING_INTERNAL_HPP + +namespace nall { + +string string::date() { + time_t timestamp = ::time(nullptr); + tm* info = localtime(×tamp); + return { + format<4, '0'>(1900 + info->tm_year), "-", + format<2, '0'>(1 + info->tm_mon), "-", + format<2, '0'>(info->tm_mday) + }; +} + +string string::time() { + time_t timestamp = ::time(nullptr); + tm* info = localtime(×tamp); + return { + format<2, '0'>(info->tm_hour), ":", + format<2, '0'>(info->tm_min), ":", + format<2, '0'>(info->tm_sec) + }; +} + +string string::datetime() { + return {string::date(), " ", string::time()}; +} + +} + +#endif diff --git a/src/dep/libs/nall/string/eval/evaluator.hpp b/src/dep/libs/nall/string/eval/evaluator.hpp new file mode 100755 index 000000000..49ac851e7 --- /dev/null +++ b/src/dep/libs/nall/string/eval/evaluator.hpp @@ -0,0 +1,157 @@ +#ifdef NALL_STRING_INTERNAL_HPP + +namespace nall { +namespace Eval { + +inline string evaluateExpression(Node* node) { + #define p(n) evaluateExpression(node->link[n]) + switch(node->type) { + case Node::Type::Null: return "Null"; + case Node::Type::Literal: return {"Literal:", node->literal}; + case Node::Type::Function: return {"Function(0:", p(0), ", 1:", p(1), ")"}; + case Node::Type::Subscript: return {"Subscript(0:", p(0), ", 1:", p(1), ")"}; + case Node::Type::Member: return {"Member(0:", p(0), ", 1:", p(1), ")"}; + case Node::Type::SuffixIncrement: return {"SuffixIncrement(0:", p(0), ")"}; + case Node::Type::SuffixDecrement: return {"SuffixDecrement(0:", p(0), ")"}; + case Node::Type::Reference: return {"Reference(0:", p(0), ")"}; + case Node::Type::Dereference: return {"Dereference(0:", p(0), ")"}; + case Node::Type::BitwiseNot: return {"Complement(0:", p(0), ")"}; + case Node::Type::PrefixIncrement: return {"PrefixIncrement(0:", p(0), ")"}; + case Node::Type::PrefixDecrement: return {"PrefixDecrement(0:", p(0), ")"}; + case Node::Type::Add: return {"Add(0:", p(0), ", 1:", p(1), ")"}; + case Node::Type::Multiply: return {"Multiply(0:", p(0), ", 1:", p(1), ")"}; + case Node::Type::Concatenate: return {"Concatenate(0:", p(0), ", ", p(1), ")"}; + case Node::Type::Coalesce: return {"Coalesce(0:", p(0), ", ", p(1), ")"}; + case Node::Type::Condition: return {"Condition(0:", p(0), ", ", p(1), ", ", p(2), ")"}; + case Node::Type::Assign: return {"Assign(0:", p(0), ", ", p(1), ")"}; + case Node::Type::Separator: { + string result = "Separator("; + for(auto& link : node->link) { + result.append(evaluateExpression(link), ", "); + } + return result.rtrim<1>(", ").append(")"); + } + } + #undef p + + throw "invalid operator"; +} + +inline int64_t evaluateInteger(Node* node) { + if(node->type == Node::Type::Literal) { + if(node->literal.beginsWith("0b")) return nall::binary(node->literal); + if(node->literal.beginsWith("0o")) return nall::octal(node->literal); + if(node->literal.beginsWith("0x")) return nall::hex(node->literal); + if(node->literal.beginsWith("%")) return nall::binary(node->literal); + if(node->literal.beginsWith("$")) return nall::hex(node->literal); + return nall::integer(node->literal); + } + + #define p(n) evaluateInteger(node->link[n]) + switch(node->type) { + case Node::Type::SuffixIncrement: return p(0); + case Node::Type::SuffixDecrement: return p(0); + case Node::Type::LogicalNot: return !p(0); + case Node::Type::BitwiseNot: return ~p(0); + case Node::Type::Positive: return +p(0); + case Node::Type::Negative: return -p(0); + case Node::Type::PrefixIncrement: return p(0) + 1; + case Node::Type::PrefixDecrement: return p(0) - 1; + case Node::Type::Multiply: return p(0) * p(1); + case Node::Type::Divide: return p(0) / p(1); + case Node::Type::Modulo: return p(0) % p(1); + case Node::Type::Add: return p(0) + p(1); + case Node::Type::Subtract: return p(0) - p(1); + case Node::Type::ShiftLeft: return p(0) << p(1); + case Node::Type::ShiftRight: return p(0) >> p(1); + case Node::Type::BitwiseAnd: return p(0) & p(1); + case Node::Type::BitwiseOr: return p(0) | p(1); + case Node::Type::BitwiseXor: return p(0) ^ p(1); + case Node::Type::Equal: return p(0) == p(1); + case Node::Type::NotEqual: return p(0) != p(1); + case Node::Type::LessThanEqual: return p(0) <= p(1); + case Node::Type::GreaterThanEqual: return p(0) >= p(1); + case Node::Type::LessThan: return p(0) < p(1); + case Node::Type::GreaterThan: return p(0) > p(1); + case Node::Type::LogicalAnd: return p(0) && p(1); + case Node::Type::LogicalOr: return p(0) || p(1); + case Node::Type::Condition: return p(0) ? p(1) : p(2); + case Node::Type::Assign: return p(1); + case Node::Type::AssignMultiply: return p(0) * p(1); + case Node::Type::AssignDivide: return p(0) / p(1); + case Node::Type::AssignModulo: return p(0) % p(1); + case Node::Type::AssignAdd: return p(0) + p(1); + case Node::Type::AssignSubtract: return p(0) - p(1); + case Node::Type::AssignShiftLeft: return p(0) << p(1); + case Node::Type::AssignShiftRight: return p(0) >> p(1); + case Node::Type::AssignBitwiseAnd: return p(0) & p(1); + case Node::Type::AssignBitwiseOr: return p(0) | p(1); + case Node::Type::AssignBitwiseXor: return p(0) ^ p(1); + } + #undef p + + throw "invalid operator"; +} + +inline optional integer(const string& expression) { + try { + auto tree = new Node; + const char* p = expression; + parse(tree, p, 0); + auto result = evaluateInteger(tree); + delete tree; + return {true, result}; + } catch(const char*) { + return false; + } +} + +inline long double evaluateReal(Node* node) { + if(node->type == Node::Type::Literal) return nall::real(node->literal); + + #define p(n) evaluateReal(node->link[n]) + switch(node->type) { + case Node::Type::LogicalNot: return !p(0); + case Node::Type::Positive: return +p(0); + case Node::Type::Negative: return -p(0); + case Node::Type::Multiply: return p(0) * p(1); + case Node::Type::Divide: return p(0) / p(1); + case Node::Type::Add: return p(0) + p(1); + case Node::Type::Subtract: return p(0) - p(1); + case Node::Type::Equal: return p(0) == p(1); + case Node::Type::NotEqual: return p(0) != p(1); + case Node::Type::LessThanEqual: return p(0) <= p(1); + case Node::Type::GreaterThanEqual: return p(0) >= p(1); + case Node::Type::LessThan: return p(0) < p(1); + case Node::Type::GreaterThan: return p(0) > p(1); + case Node::Type::LogicalAnd: return p(0) && p(1); + case Node::Type::LogicalOr: return p(0) || p(1); + case Node::Type::Condition: return p(0) ? p(1) : p(2); + case Node::Type::Assign: return p(1); + case Node::Type::AssignMultiply: return p(0) * p(1); + case Node::Type::AssignDivide: return p(0) / p(1); + case Node::Type::AssignAdd: return p(0) + p(1); + case Node::Type::AssignSubtract: return p(0) - p(1); + } + #undef p + + throw "invalid operator"; +} + +inline optional real(const string& expression) { + try { + auto tree = new Node; + const char* p = expression; + parse(tree, p, 0); + auto result = evaluateReal(tree); + delete tree; + return {true, result}; + } catch(const char*) { + return false; + } +} + +} +} + +#endif diff --git a/src/dep/libs/nall/string/eval/literal.hpp b/src/dep/libs/nall/string/eval/literal.hpp new file mode 100755 index 000000000..fd0f6549e --- /dev/null +++ b/src/dep/libs/nall/string/eval/literal.hpp @@ -0,0 +1,103 @@ +#ifdef NALL_STRING_INTERNAL_HPP + +namespace nall { +namespace Eval { + +inline bool isLiteral(const char*& s) { + char n = s[0]; + return (n >= 'A' && n <= 'Z') + || (n >= 'a' && n <= 'z') + || (n >= '0' && n <= '9') + || (n == '%' || n == '$' || n == '_' || n == '.') + || (n == '\'' || n == '\"'); +} + +inline string literalNumber(const char*& s) { + const char* p = s; + + //binary + if(p[0] == '%' || (p[0] == '0' && p[1] == 'b')) { + unsigned prefix = 1 + (p[0] == '0'); + p += prefix; + while(p[0] == '\'' || p[0] == '0' || p[0] == '1') p++; + if(p - s <= prefix) throw "invalid binary literal"; + string result = substr(s, 0, p - s); + s = p; + return result; + } + + //octal + if(p[0] == '0' && p[1] == 'o') { + unsigned prefix = 1 + (p[0] == '0'); + p += prefix; + while(p[0] == '\'' || (p[0] >= '0' && p[0] <= '7')) p++; + if(p - s <= prefix) throw "invalid octal literal"; + string result = substr(s, 0, p - s); + s = p; + return result; + } + + //hex + if(p[0] == '$' || (p[0] == '0' && p[1] == 'x')) { + unsigned prefix = 1 + (p[0] == '0'); + p += prefix; + while(p[0] == '\'' || (p[0] >= '0' && p[0] <= '9') || (p[0] >= 'A' && p[0] <= 'F') || (p[0] >= 'a' && p[0] <= 'f')) p++; + if(p - s <= prefix) throw "invalid hex literal"; + string result = substr(s, 0, p - s); + s = p; + return result; + } + + //decimal + while(p[0] == '\'' || (p[0] >= '0' && p[0] <= '9')) p++; + if(p[0] != '.') { + string result = substr(s, 0, p - s); + s = p; + return result; + } + + //floating-point + p++; + while(p[0] == '\'' || (p[0] >= '0' && p[0] <= '9')) p++; + string result = substr(s, 0, p - s); + s = p; + return result; +} + +inline string literalString(const char*& s) { + const char* p = s; + char escape = *p++; + + while(p[0] && p[0] != escape) p++; + if(*p++ != escape) throw "unclosed string literal"; + + string result = substr(s, 0, p - s); + s = p; + return result; +} + +inline string literalVariable(const char*& s) { + const char* p = s; + + while(p[0] == '_' || p[0] == '.' || (p[0] >= 'A' && p[0] <= 'Z') || (p[0] >= 'a' && p[0] <= 'z') || (p[0] >= '0' && p[0] <= '9')) p++; + + string result = substr(s, 0, p - s); + s = p; + return result; +} + +inline string literal(const char*& s) { + const char* p = s; + + if(p[0] >= '0' && p[0] <= '9') return literalNumber(s); + if(p[0] == '%' || p[0] == '$') return literalNumber(s); + if(p[0] == '\'' || p[0] == '\"') return literalString(s); + if(p[0] == '_' || p[0] == '.' || (p[0] >= 'A' && p[0] <= 'Z') || (p[0] >= 'a' && p[0] <= 'z')) return literalVariable(s); + + throw "invalid literal"; +} + +} +} + +#endif diff --git a/src/dep/libs/nall/string/eval/node.hpp b/src/dep/libs/nall/string/eval/node.hpp new file mode 100755 index 000000000..b60b3aa72 --- /dev/null +++ b/src/dep/libs/nall/string/eval/node.hpp @@ -0,0 +1,41 @@ +#ifdef NALL_STRING_INTERNAL_HPP + +namespace nall { +namespace Eval { + +struct Node { + enum class Type : unsigned { + Null, + Literal, + Function, Subscript, Member, SuffixIncrement, SuffixDecrement, + Reference, Dereference, LogicalNot, BitwiseNot, Positive, Negative, PrefixIncrement, PrefixDecrement, + Multiply, Divide, Modulo, + Add, Subtract, + RotateLeft, RotateRight, ShiftLeft, ShiftRight, + BitwiseAnd, BitwiseOr, BitwiseXor, + Concatenate, + Equal, NotEqual, LessThanEqual, GreaterThanEqual, LessThan, GreaterThan, + LogicalAnd, LogicalOr, + Coalesce, Condition, + Assign, Create, //all assignment operators have the same precedence + AssignMultiply, AssignDivide, AssignModulo, + AssignAdd, AssignSubtract, + AssignRotateLeft, AssignRotateRight, AssignShiftLeft, AssignShiftRight, + AssignBitwiseAnd, AssignBitwiseOr, AssignBitwiseXor, + AssignConcatenate, + Separator, + }; + + Type type; + string literal; + vector link; + + Node() : type(Type::Null) {} + Node(Type type) : type(type) {} + ~Node() { for(auto& node : link) delete node; } +}; + +} +} + +#endif diff --git a/src/dep/libs/nall/string/eval/parser.hpp b/src/dep/libs/nall/string/eval/parser.hpp new file mode 100755 index 000000000..2aee1474c --- /dev/null +++ b/src/dep/libs/nall/string/eval/parser.hpp @@ -0,0 +1,168 @@ +#ifdef NALL_STRING_INTERNAL_HPP + +namespace nall { +namespace Eval { + +inline bool whitespace(char n) { + return n == ' ' || n == '\t' || n == '\r' || n == '\n'; +} + +inline void parse(Node*& node, const char*& s, unsigned depth) { + auto unaryPrefix = [&](Node::Type type, unsigned seek, unsigned depth) { + auto parent = new Node(type); + parse(parent->link(0) = new Node, s += seek, depth); + node = parent; + }; + + auto unarySuffix = [&](Node::Type type, unsigned seek, unsigned depth) { + auto parent = new Node(type); + parent->link(0) = node; + parse(parent, s += seek, depth); + node = parent; + }; + + auto binary = [&](Node::Type type, unsigned seek, unsigned depth) { + auto parent = new Node(type); + parent->link(0) = node; + parse(parent->link(1) = new Node, s += seek, depth); + node = parent; + }; + + auto ternary = [&](Node::Type type, unsigned seek, unsigned depth) { + auto parent = new Node(type); + parent->link(0) = node; + parse(parent->link(1) = new Node, s += seek, depth); + if(s[0] != ':') throw "mismatched ternary"; + parse(parent->link(2) = new Node, s += seek, depth); + node = parent; + }; + + auto separator = [&](Node::Type type, unsigned seek, unsigned depth) { + if(node->type != Node::Type::Separator) return binary(type, seek, depth); + unsigned n = node->link.size(); + parse(node->link(n) = new Node, s += seek, depth); + }; + + while(whitespace(s[0])) s++; + if(!s[0]) return; + + if(s[0] == '(' && node->link.empty()) { + parse(node, s += 1, 1); + if(*s++ != ')') throw "mismatched group"; + } + + if(isLiteral(s)) { + node->type = Node::Type::Literal; + node->literal = literal(s); + } + + #define p() (node->literal.empty() && node->link.empty()) + while(true) { + while(whitespace(s[0])) s++; + if(!s[0]) return; + + if(depth >= 13) break; + if(s[0] == '(' && !p()) { + binary(Node::Type::Function, 1, 1); + if(*s++ != ')') throw "mismatched function"; + continue; + } + if(s[0] == '[') { + binary(Node::Type::Subscript, 1, 1); + if(*s++ != ']') throw "mismatched subscript"; + continue; + } + if(s[0] == '.') { binary(Node::Type::Member, 1, 13); continue; } + if(s[0] == '+' && s[1] == '+' && !p()) { unarySuffix(Node::Type::SuffixIncrement, 2, 13); continue; } + if(s[0] == '-' && s[1] == '-' && !p()) { unarySuffix(Node::Type::SuffixDecrement, 2, 13); continue; } + + if(s[0] == '&' && p()) { unaryPrefix(Node::Type::Reference, 1, 12); continue; } + if(s[0] == '*' && p()) { unaryPrefix(Node::Type::Dereference, 1, 12); continue; } + if(s[0] == '!' && p()) { unaryPrefix(Node::Type::LogicalNot, 1, 12); continue; } + if(s[0] == '~' && p()) { unaryPrefix(Node::Type::BitwiseNot, 1, 12); continue; } + if(s[0] == '+' && s[1] != '+' && p()) { unaryPrefix(Node::Type::Positive, 1, 12); continue; } + if(s[0] == '-' && s[1] != '-' && p()) { unaryPrefix(Node::Type::Negative, 1, 12); continue; } + if(s[0] == '+' && s[1] == '+' && p()) { unaryPrefix(Node::Type::PrefixIncrement, 2, 12); continue; } + if(s[0] == '-' && s[1] == '-' && p()) { unaryPrefix(Node::Type::PrefixDecrement, 2, 12); continue; } + if(depth >= 12) break; + + if(depth >= 11) break; + if(s[0] == '*' && s[1] != '=') { binary(Node::Type::Multiply, 1, 11); continue; } + if(s[0] == '/' && s[1] != '=') { binary(Node::Type::Divide, 1, 11); continue; } + if(s[0] == '%' && s[1] != '=') { binary(Node::Type::Modulo, 1, 11); continue; } + + if(depth >= 10) break; + if(s[0] == '+' && s[1] != '=') { binary(Node::Type::Add, 1, 10); continue; } + if(s[0] == '-' && s[1] != '=') { binary(Node::Type::Subtract, 1, 10); continue; } + + if(depth >= 9) break; + if(s[0] == '<' && s[1] == '<' && s[2] == '<' && s[3] != '=') { binary(Node::Type::RotateLeft, 3, 9); continue; } + if(s[0] == '>' && s[1] == '>' && s[2] == '>' && s[3] != '=') { binary(Node::Type::RotateRight, 3, 9); continue; } + if(s[0] == '<' && s[1] == '<' && s[2] != '=') { binary(Node::Type::ShiftLeft, 2, 9); continue; } + if(s[0] == '>' && s[1] == '>' && s[2] != '=') { binary(Node::Type::ShiftRight, 2, 9); continue; } + + if(depth >= 8) break; + if(s[0] == '&' && s[1] != '&' && s[1] != '=') { binary(Node::Type::BitwiseAnd, 1, 8); continue; } + if(s[0] == '|' && s[1] != '|' && s[1] != '=') { binary(Node::Type::BitwiseOr, 1, 8); continue; } + if(s[0] == '^' && s[1] != '^' && s[1] != '=') { binary(Node::Type::BitwiseXor, 1, 8); continue; } + + if(depth >= 7) break; + if(s[0] == '~' && s[1] != '=') { binary(Node::Type::Concatenate, 1, 7); continue; } + + if(depth >= 6) break; + if(s[0] == '=' && s[1] == '=') { binary(Node::Type::Equal, 2, 6); continue; } + if(s[0] == '!' && s[1] == '=') { binary(Node::Type::NotEqual, 2, 6); continue; } + if(s[0] == '<' && s[1] == '=') { binary(Node::Type::LessThanEqual, 2, 6); continue; } + if(s[0] == '>' && s[1] == '=') { binary(Node::Type::GreaterThanEqual, 2, 6); continue; } + if(s[0] == '<') { binary(Node::Type::LessThan, 1, 6); continue; } + if(s[0] == '>') { binary(Node::Type::GreaterThan, 1, 6); continue; } + + if(depth >= 5) break; + if(s[0] == '&' && s[1] == '&') { binary(Node::Type::LogicalAnd, 2, 5); continue; } + if(s[0] == '|' && s[1] == '|') { binary(Node::Type::LogicalOr, 2, 5); continue; } + + if(s[0] == '?' && s[1] == '?') { binary(Node::Type::Coalesce, 2, 4); continue; } + if(s[0] == '?' && s[1] != '?') { ternary(Node::Type::Condition, 1, 4); continue; } + if(depth >= 4) break; + + if(s[0] == '=') { binary(Node::Type::Assign, 1, 3); continue; } + if(s[0] == ':' && s[1] == '=') { binary(Node::Type::Create, 2, 3); continue; } + if(s[0] == '*' && s[1] == '=') { binary(Node::Type::AssignMultiply, 2, 3); continue; } + if(s[0] == '/' && s[1] == '=') { binary(Node::Type::AssignDivide, 2, 3); continue; } + if(s[0] == '%' && s[1] == '=') { binary(Node::Type::AssignModulo, 2, 3); continue; } + if(s[0] == '+' && s[1] == '=') { binary(Node::Type::AssignAdd, 2, 3); continue; } + if(s[0] == '-' && s[1] == '=') { binary(Node::Type::AssignSubtract, 2, 3); continue; } + if(s[0] == '<' && s[1] == '<' && s[2] == '<' && s[3] == '=') { binary(Node::Type::AssignRotateLeft, 4, 3); continue; } + if(s[0] == '>' && s[1] == '>' && s[2] == '>' && s[3] == '=') { binary(Node::Type::AssignRotateRight, 4, 3); continue; } + if(s[0] == '<' && s[1] == '<' && s[2] == '=') { binary(Node::Type::AssignShiftLeft, 3, 3); continue; } + if(s[0] == '>' && s[1] == '>' && s[2] == '=') { binary(Node::Type::AssignShiftRight, 3, 3); continue; } + if(s[0] == '&' && s[1] == '=') { binary(Node::Type::AssignBitwiseAnd, 2, 3); continue; } + if(s[0] == '|' && s[1] == '=') { binary(Node::Type::AssignBitwiseOr, 2, 3); continue; } + if(s[0] == '^' && s[1] == '=') { binary(Node::Type::AssignBitwiseXor, 2, 3); continue; } + if(s[0] == '~' && s[1] == '=') { binary(Node::Type::AssignConcatenate, 2, 3); continue; } + if(depth >= 3) break; + + if(depth >= 2) break; + if(s[0] == ',') { separator(Node::Type::Separator, 1, 2); continue; } + + if(depth >= 1 && (s[0] == ')' || s[0] == ']')) break; + + while(whitespace(s[0])) s++; + if(!s[0]) break; + + throw "unrecognized terminal"; + } + #undef p +} + +inline Node* parse(const string& expression) { + auto result = new Node; + const char* p = expression; + parse(result, p, 0); + return result; +} + +} +} + +#endif diff --git a/src/dep/libs/nall/string/file.hpp b/src/dep/libs/nall/string/file.hpp new file mode 100755 index 000000000..1b809c2f5 --- /dev/null +++ b/src/dep/libs/nall/string/file.hpp @@ -0,0 +1,31 @@ +#ifdef NALL_STRING_INTERNAL_HPP + +namespace nall { + +string string::read(const string& filename) { + string result; + + #if !defined(_WIN32) + FILE* fp = fopen(filename, "rb"); + #else + FILE* fp = _wfopen(utf16_t(filename), L"rb"); + #endif + if(!fp) return result; + + fseek(fp, 0, SEEK_END); + unsigned fsize = ftell(fp); + rewind(fp); + char* fdata = new char[fsize + 1]; + unsigned unused = fread(fdata, 1, fsize, fp); + fclose(fp); + fdata[fsize] = 0; + result.resize(fsize); + memcpy(result.data(), fdata, fsize); + delete[] fdata; + + return result; +} + +} + +#endif diff --git a/src/dep/libs/nall/string/filename.hpp b/src/dep/libs/nall/string/filename.hpp new file mode 100755 index 000000000..d748a8ba9 --- /dev/null +++ b/src/dep/libs/nall/string/filename.hpp @@ -0,0 +1,84 @@ +#ifdef NALL_STRING_INTERNAL_HPP + +namespace nall { + +// "/foo/bar.c" -> "/foo/" +// "/foo/" -> "/foo/" +// "bar.c" -> "./" +string dir(string name) { + for(signed i = name.length(); i >= 0; i--) { + if(name[i] == '/' || name[i] == '\\') { + name.resize(i + 1); + break; + } + if(i == 0) name = "./"; + } + return name; +} + +// "/foo/bar.c" -> "bar.c" +// "/foo/" -> "" +// "bar.c" -> "bar.c" +string notdir(string name) { + for(signed i = name.length(); i >= 0; i--) { + if(name[i] == '/' || name[i] == '\\') { + return (const char*)name + i + 1; + } + } + return name; +} + +// "/foo/bar/baz" -> "/foo/bar/" +// "/foo/bar/" -> "/foo/" +// "/foo/bar" -> "/foo/" +string parentdir(string name) { + unsigned length = name.length(), paths = 0, prev, last; + for(unsigned i = 0; i < length; i++) { + if(name[i] == '/' || name[i] == '\\') { + paths++; + prev = last; + last = i; + } + } + if(last + 1 == length) last = prev; //if name ends in slash; use previous slash + if(paths > 1) name.resize(last + 1); + return name; +} + +// "/foo/bar.c" -> "/foo/bar" +string basename(string name) { + for(signed i = name.length(); i >= 0; i--) { + if(name[i] == '/' || name[i] == '\\') break; //file has no extension + if(name[i] == '.') { + name.resize(i); + break; + } + } + return name; +} + +// "/foo/bar.c" -> "c" +// "/foo/bar" -> "" +string extension(string name) { + for(signed i = name.length(); i >= 0; i--) { + if(name[i] == '/' || name[i] == '\\') return ""; //file has no extension + if(name[i] == '.') { + return (const char*)name + i + 1; + } + } + return name; +} + +string tempname() { + string path = temppath(); + srand(time(nullptr)); + while(true) { + uint32_t seed = rand(); + string filename = {path, ".temporary-", hex<8>(seed)}; + if(access(filename, F_OK) != 0) return filename; + } +} + +} + +#endif diff --git a/src/dep/libs/nall/string/format.hpp b/src/dep/libs/nall/string/format.hpp new file mode 100755 index 000000000..98c0c5b79 --- /dev/null +++ b/src/dep/libs/nall/string/format.hpp @@ -0,0 +1,72 @@ +#ifdef NALL_STRING_INTERNAL_HPP + +namespace nall { + +template string format(const string& value) { + if(precision == 0) return value; + + bool padright = precision >= 0; + unsigned padding = abs(precision); + + if(padding <= value.size()) { + if(padright) return substr(value, value.size() - padding); + else return substr(value, 0, padding); + } + + string buffer; + buffer.resize(padding); + buffer.clear(padchar); + + memcpy(buffer.data() + (padright ? padding - value.size() : 0), value, value.size()); + return buffer; +} + +template string hex(uintmax_t value) { + string buffer; + buffer.resize(sizeof(uintmax_t) * 2); + + unsigned size = 0; + do { + unsigned n = value & 15; + buffer[size++] = n < 10 ? '0' + n : 'a' + n - 10; + value >>= 4; + } while(value); + buffer.resize(size); + buffer.reverse(); + + return format(buffer); +} + +template string octal(uintmax_t value) { + string buffer; + buffer.resize(sizeof(uintmax_t) * 3); + + unsigned size = 0; + do { + buffer[size++] = '0' + (value & 7); + value >>= 3; + } while(value); + buffer.resize(size); + buffer.reverse(); + + return format(buffer); +} + +template string binary(uintmax_t value) { + string buffer; + buffer.resize(sizeof(uintmax_t) * 8); + + unsigned size = 0; + do { + buffer[size++] = '0' + (value & 1); + value >>= 1; + } while(value); + buffer.resize(size); + buffer.reverse(); + + return format(buffer); +} + +} + +#endif diff --git a/src/dep/libs/nall/string/list.hpp b/src/dep/libs/nall/string/list.hpp new file mode 100755 index 000000000..196a7a567 --- /dev/null +++ b/src/dep/libs/nall/string/list.hpp @@ -0,0 +1,86 @@ +#ifdef NALL_STRING_INTERNAL_HPP + +namespace nall { + +optional lstring::find(rstring key) const { + for(unsigned i = 0; i < size(); i++) { + if(operator[](i) == key) return {true, i}; + } + return false; +} + +string lstring::merge(const string& separator) const { + string output; + for(unsigned i = 0; i < size(); i++) { + output.append(operator[](i)); + if(i < size() - 1) output.append(separator); + } + return output; +} + +lstring& lstring::isort() { + nall::sort(pool, objectsize, [](const string& x, const string& y) { + return istrcmp(x, y) < 0; + }); + return *this; +} + +lstring& lstring::strip() { + for(unsigned n = 0; n < size(); n++) { + operator[](n).strip(); + } + return *this; +} + +template void lstring::append(const string& data, Args&&... args) { + vector::append(data); + append(std::forward(args)...); +} + +bool lstring::operator==(const lstring& source) const { + if(this == &source) return true; + if(size() != source.size()) return false; + for(unsigned n = 0; n < size(); n++) { + if(operator[](n) != source[n]) return false; + } + return true; +} + +bool lstring::operator!=(const lstring& source) const { + return !operator==(source); +} + +lstring& lstring::operator=(const lstring& source) { + vector::operator=(source); + return *this; +} + +lstring& lstring::operator=(lstring& source) { + vector::operator=(source); + return *this; +} + +lstring& lstring::operator=(lstring&& source) { + vector::operator=(std::move(source)); + return *this; +} + +template lstring::lstring(Args&&... args) { + append(std::forward(args)...); +} + +lstring::lstring(const lstring& source) { + vector::operator=(source); +} + +lstring::lstring(lstring& source) { + vector::operator=(source); +} + +lstring::lstring(lstring&& source) { + vector::operator=(std::move(source)); +} + +} + +#endif diff --git a/src/dep/libs/nall/string/markup/bml.hpp b/src/dep/libs/nall/string/markup/bml.hpp new file mode 100755 index 000000000..6743a80e2 --- /dev/null +++ b/src/dep/libs/nall/string/markup/bml.hpp @@ -0,0 +1,157 @@ +#ifdef NALL_STRING_INTERNAL_HPP + +//BML v1.0 parser +//revision 0.03 + +namespace nall { +namespace BML { + +struct Node : Markup::Node { +protected: + //test to verify if a valid character for a node name + bool valid(char p) const { //A-Z, a-z, 0-9, -. + return p - 'A' < 26u || p - 'a' < 26u || p - '0' < 10u || p - '-' < 2u; + } + + //determine indentation level, without incrementing pointer + unsigned readDepth(const char* p) { + unsigned depth = 0; + while(p[depth] == '\t' || p[depth] == ' ') depth++; + return depth; + } + + //determine indentation level + unsigned parseDepth(const char*& p) { + unsigned depth = readDepth(p); + p += depth; + return depth; + } + + //read name + void parseName(const char*& p) { + unsigned length = 0; + while(valid(p[length])) length++; + if(length == 0) throw "Invalid node name"; + name = substr(p, 0, length); + p += length; + } + + void parseData(const char*& p) { + if(*p == '=' && *(p + 1) == '\"') { + unsigned length = 2; + while(p[length] && p[length] != '\n' && p[length] != '\"') length++; + if(p[length] != '\"') throw "Unescaped value"; + data = {substr(p, 2, length - 2), "\n"}; + p += length + 1; + } else if(*p == '=') { + unsigned length = 1; + while(p[length] && p[length] != '\n' && p[length] != '\"' && p[length] != ' ') length++; + if(p[length] == '\"') throw "Illegal character in value"; + data = {substr(p, 1, length - 1), "\n"}; + p += length; + } else if(*p == ':') { + unsigned length = 1; + while(p[length] && p[length] != '\n') length++; + data = {substr(p, 1, length - 1), "\n"}; + p += length; + } + } + + //read all attributes for a node + void parseAttributes(const char*& p) { + while(*p && *p != '\n') { + if(*p != ' ') throw "Invalid node name"; + while(*p == ' ') p++; //skip excess spaces + if(*(p + 0) == '/' && *(p + 1) == '/') break; //skip comments + + Node node; + node.attribute = true; + unsigned length = 0; + while(valid(p[length])) length++; + if(length == 0) throw "Invalid attribute name"; + node.name = substr(p, 0, length); + node.parseData(p += length); + node.data.rtrim<1>("\n"); + children.append(node); + } + } + + //read a node and all of its child nodes + void parseNode(const lstring& text, unsigned& y) { + const char* p = text[y++]; + level = parseDepth(p); + parseName(p); + parseData(p); + parseAttributes(p); + + while(y < text.size()) { + unsigned depth = readDepth(text[y]); + if(depth <= level) break; + + if(text[y][depth] == ':') { + data.append(substr(text[y++], depth + 1), "\n"); + continue; + } + + Node node; + node.parseNode(text, y); + children.append(node); + } + + data.rtrim<1>("\n"); + } + + //read top-level nodes + void parse(const string& document) { + lstring text = string{document}.replace("\r", "").split("\n"); + + //remove empty lines and comment lines + for(unsigned y = 0; y < text.size();) { + unsigned x = 0; + bool empty = true; + while(x < text[y].size()) { + if(text[y][x] == ' ' || text[y][x] == '\t') { x++; continue; } + empty = (text[y][x + 0] == '/' && text[y][x + 1] == '/'); + break; + } + if(empty) text.remove(y); + else y++; + } + + unsigned y = 0; + while(y < text.size()) { + Node node; + node.parseNode(text, y); + if(node.level > 0) throw "Root nodes cannot be indented"; + children.append(node); + } + } + + friend class Document; +}; + +struct Document : Node { + string error; + + bool load(const string& document) { + name = "", data = ""; + + try { + parse(document); + } catch(const char* error) { + this->error = error; + children.reset(); + return false; + } + return true; + } + + Document(const string& document = "") { + load(document); + } +}; + +} +} + +#endif diff --git a/src/dep/libs/nall/string/markup/document.hpp b/src/dep/libs/nall/string/markup/document.hpp new file mode 100755 index 000000000..d9a8027e9 --- /dev/null +++ b/src/dep/libs/nall/string/markup/document.hpp @@ -0,0 +1,14 @@ +#ifdef NALL_STRING_INTERNAL_HPP + +namespace nall { +namespace Markup { + +inline Node Document(const string& markup) { + if(markup.beginsWith("<")) return XML::Document(markup); + return BML::Document(markup); +} + +} +} + +#endif diff --git a/src/dep/libs/nall/string/markup/node.hpp b/src/dep/libs/nall/string/markup/node.hpp new file mode 100755 index 000000000..ff7ea3d69 --- /dev/null +++ b/src/dep/libs/nall/string/markup/node.hpp @@ -0,0 +1,147 @@ +#ifdef NALL_STRING_INTERNAL_HPP + +//note: specific markups inherit from Markup::Node +//vector will slice any data; so derived nodes must not contain data nor virtual functions +//vector would incur a large performance penalty and greatly increased complexity + +namespace nall { +namespace Markup { + +struct Node { + string name; + string data; + bool attribute; + + bool exists() const { + return !name.empty(); + } + + string text() const { + return string{data}.strip(); + } + + intmax_t integer() const { + return numeral(text()); + } + + uintmax_t decimal() const { + return numeral(text()); + } + + void reset() { + children.reset(); + } + + bool evaluate(const string& query) const { + if(query.empty()) return true; + lstring rules = string{query}.replace(" ", "").split(","); + + for(auto& rule : rules) { + enum class Comparator : unsigned { ID, EQ, NE, LT, LE, GT, GE }; + auto comparator = Comparator::ID; + if(rule.match("*!=*")) comparator = Comparator::NE; + else if(rule.match("*<=*")) comparator = Comparator::LE; + else if(rule.match("*>=*")) comparator = Comparator::GE; + else if(rule.match ("*=*")) comparator = Comparator::EQ; + else if(rule.match ("*<*")) comparator = Comparator::LT; + else if(rule.match ("*>*")) comparator = Comparator::GT; + + if(comparator == Comparator::ID) { + if(find(rule).size()) continue; + return false; + } + + lstring side; + switch(comparator) { + case Comparator::EQ: side = rule.split<1> ("="); break; + case Comparator::NE: side = rule.split<1>("!="); break; + case Comparator::LT: side = rule.split<1> ("<"); break; + case Comparator::LE: side = rule.split<1>("<="); break; + case Comparator::GT: side = rule.split<1> (">"); break; + case Comparator::GE: side = rule.split<1>(">="); break; + } + + string data = text(); + if(side(0).empty() == false) { + auto result = find(side(0)); + if(result.size() == 0) return false; + data = result(0).data; + } + + switch(comparator) { + case Comparator::EQ: if(data.match(side(1)) == true) continue; break; + case Comparator::NE: if(data.match(side(1)) == false) continue; break; + case Comparator::LT: if(numeral(data) < numeral(side(1))) continue; break; + case Comparator::LE: if(numeral(data) <= numeral(side(1))) continue; break; + case Comparator::GT: if(numeral(data) > numeral(side(1))) continue; break; + case Comparator::GE: if(numeral(data) >= numeral(side(1))) continue; break; + } + + return false; + } + + return true; + } + + vector find(const string& query) const { + vector result; + + lstring path = query.split("/"); + string name = path.take(0), rule; + unsigned lo = 0u, hi = ~0u; + + if(name.match("*[*]")) { + lstring side = name.split<1>("["); + name = side(0); + side = side(1).rtrim<1>("]").split<1>("-"); + lo = side(0).empty() ? 0u : numeral(side(0)); + hi = side(1).empty() ? ~0u : numeral(side(1)); + } + + if(name.match("*(*)")) { + lstring side = name.split<1>("("); + name = side(0); + rule = side(1).rtrim<1>(")"); + } + + unsigned position = 0; + for(auto& node : children) { + if(node.name.match(name) == false) continue; + if(node.evaluate(rule) == false) continue; + + bool inrange = position >= lo && position <= hi; + position++; + if(inrange == false) continue; + + if(path.size() == 0) result.append(node); + else { + auto list = node.find(path.merge("/")); + for(auto& item : list) result.append(item); + } + } + + return result; + } + + Node operator[](const string& query) const { + auto result = find(query); + return result(0); + } + + vector::iterator begin() { return children.begin(); } + vector::iterator end() { return children.end(); } + + const vector::constIterator begin() const { return children.begin(); } + const vector::constIterator end() const { return children.end(); } + + Node() : attribute(false), level(0) {} + +protected: + unsigned level; + vector children; +}; + +} +} + +#endif diff --git a/src/dep/libs/nall/string/markup/xml.hpp b/src/dep/libs/nall/string/markup/xml.hpp new file mode 100755 index 000000000..6debcc373 --- /dev/null +++ b/src/dep/libs/nall/string/markup/xml.hpp @@ -0,0 +1,219 @@ +#ifdef NALL_STRING_INTERNAL_HPP + +//XML v1.0 subset parser +//revision 0.03 + +namespace nall { +namespace XML { + +struct Node : Markup::Node { +protected: + inline string escape() const { + string result = data; + result.replace("&", "&"); + result.replace("<", "<"); + result.replace(">", ">"); + if(attribute == false) return result; + result.replace("\'", "'"); + result.replace("\"", """); + return result; + } + + inline bool isName(char c) const { + if(c >= 'A' && c <= 'Z') return true; + if(c >= 'a' && c <= 'z') return true; + if(c >= '0' && c <= '9') return true; + if(c == '.' || c == '_') return true; + if(c == '?') return true; + return false; + } + + inline bool isWhitespace(char c) const { + if(c == ' ' || c == '\t') return true; + if(c == '\r' || c == '\n') return true; + return false; + } + + //copy part of string from source document into target string; decode markup while copying + inline void copy(string& target, const char* source, unsigned length) { + target.reserve(length + 1); + + #if defined(NALL_XML_LITERAL) + memcpy(target(), source, length); + target[length] = 0; + return; + #endif + + char* output = target.data(); + while(length) { + if(*source == '&') { + if(!memcmp(source, "<", 4)) { *output++ = '<'; source += 4; length -= 4; continue; } + if(!memcmp(source, ">", 4)) { *output++ = '>'; source += 4; length -= 4; continue; } + if(!memcmp(source, "&", 5)) { *output++ = '&'; source += 5; length -= 5; continue; } + if(!memcmp(source, "'", 6)) { *output++ = '\''; source += 6; length -= 6; continue; } + if(!memcmp(source, """, 6)) { *output++ = '\"'; source += 6; length -= 6; continue; } + } + + if(attribute == false && source[0] == '<' && source[1] == '!') { + //comment + if(!memcmp(source, "", 3)) source++, length--; + source += 3, length -= 3; + continue; + } + + //CDATA + if(!memcmp(source, "", 3)) *output++ = *source++, length--; + source += 3, length -= 3; + continue; + } + } + + *output++ = *source++, length--; + } + *output = 0; + } + + inline bool parseExpression(const char*& p) { + if(*(p + 1) != '!') return false; + + //comment + if(!memcmp(p, "", 3)) p++; + if(!*p) throw "unclosed comment"; + p += 3; + return true; + } + + //CDATA + if(!memcmp(p, "", 3)) p++; + if(!*p) throw "unclosed CDATA"; + p += 3; + return true; + } + + //DOCTYPE + if(!memcmp(p, "') counter--; + } while(counter); + return true; + } + + return false; + } + + //returns true if tag closes itself (); false if not () + inline bool parseHead(const char*& p) { + //parse name + const char* nameStart = ++p; //skip '<' + while(isName(*p)) p++; + const char* nameEnd = p; + copy(name, nameStart, nameEnd - nameStart); + if(name.empty()) throw "missing element name"; + + //parse attributes + while(*p) { + while(isWhitespace(*p)) p++; + if(!*p) throw "unclosed attribute"; + if(*p == '?' || *p == '/' || *p == '>') break; + + //parse attribute name + Node attribute; + attribute.attribute = true; + + const char* nameStart = p; + while(isName(*p)) p++; + const char* nameEnd = p; + copy(attribute.name, nameStart, nameEnd - nameStart); + if(attribute.name.empty()) throw "missing attribute name"; + + //parse attribute data + if(*p++ != '=') throw "missing attribute value"; + char terminal = *p++; + if(terminal != '\'' && terminal != '\"') throw "attribute value not quoted"; + const char* dataStart = p; + while(*p && *p != terminal) p++; + if(!*p) throw "missing attribute data terminal"; + const char* dataEnd = p++; //skip closing terminal + + copy(attribute.data, dataStart, dataEnd - dataStart); + children.append(attribute); + } + + //parse closure + if(*p == '?' && *(p + 1) == '>') { p += 2; return true; } + if(*p == '/' && *(p + 1) == '>') { p += 2; return true; } + if(*p == '>') { p += 1; return false; } + throw "invalid element tag"; + } + + //parse element and all of its child elements + inline void parseElement(const char*& p) { + Node node; + if(node.parseHead(p) == false) node.parse(p); + children.append(node); + } + + //return true if matches this node's name + inline bool parseClosureElement(const char*& p) { + if(p[0] != '<' || p[1] != '/') return false; + p += 2; + const char* nameStart = p; + while(*p && *p != '>') p++; + if(*p != '>') throw "unclosed closure element"; + const char* nameEnd = p++; + if(memcmp(name, nameStart, nameEnd - nameStart)) throw "closure element name mismatch"; + return true; + } + + //parse contents of an element + inline void parse(const char*& p) { + const char* dataStart = p; + const char* dataEnd = p; + + while(*p) { + while(*p && *p != '<') p++; + if(!*p) break; + dataEnd = p; + if(parseClosureElement(p) == true) break; + if(parseExpression(p) == true) continue; + parseElement(p); + } + + copy(data, dataStart, dataEnd - dataStart); + } +}; + +struct Document : Node { + string error; + + inline bool load(const char* document) { + if(document == nullptr) return false; + reset(); + try { + parse(document); + } catch(const char* error) { + reset(); + this->error = error; + return false; + } + return true; + } + + inline Document() {} + inline Document(const char* document) { load(document); } +}; + +} +} + +#endif diff --git a/src/dep/libs/nall/string/platform.hpp b/src/dep/libs/nall/string/platform.hpp new file mode 100755 index 000000000..e71bd32fa --- /dev/null +++ b/src/dep/libs/nall/string/platform.hpp @@ -0,0 +1,98 @@ +#ifdef NALL_STRING_INTERNAL_HPP + +namespace nall { + +string activepath() { + char path[PATH_MAX] = ""; + auto unused = getcwd(path, PATH_MAX); + string result = path; + if(result.empty()) result = "."; + result.transform("\\", "/"); + if(result.endsWith("/") == false) result.append("/"); + return result; +} + +string realpath(const string& name) { + string result; + char path[PATH_MAX] = ""; + if(::realpath(name, path)) result = dir(path); + if(result.empty()) result = activepath(); + result.transform("\\", "/"); + if(result.endsWith("/") == false) result.append("/"); + return result; +} + +// /home/username/ +// c:/users/username/ +string userpath() { + #if defined(PLATFORM_WINDOWS) + wchar_t path[PATH_MAX] = L""; + SHGetFolderPathW(nullptr, CSIDL_PROFILE | CSIDL_FLAG_CREATE, nullptr, 0, path); + string result = (const char*)utf8_t(path); + result.transform("\\", "/"); + #else + struct passwd* userinfo = getpwuid(getuid()); + string result = userinfo->pw_dir; + #endif + if(result.empty()) result = "."; + if(result.endsWith("/") == false) result.append("/"); + return result; +} + +// /home/username/.config/ +// c:/users/username/appdata/roaming/ +string configpath() { + #if defined(PLATFORM_WINDOWS) + wchar_t path[PATH_MAX] = L""; + SHGetFolderPathW(nullptr, CSIDL_APPDATA | CSIDL_FLAG_CREATE, nullptr, 0, path); + string result = (const char*)utf8_t(path); + result.transform("\\", "/"); + #elif defined(PLATFORM_MACOSX) + string result = {userpath(), "Library/Application Support/"}; + #else + string result = {userpath(), ".config/"}; + #endif + if(result.empty()) result = "."; + if(result.endsWith("/") == false) result.append("/"); + return result; +} + +// /usr/share +// /Library/Application Support/ +// c:/ProgramData/ +string sharedpath() { + #if defined(PLATFORM_WINDOWS) + wchar_t path[PATH_MAX] = L""; + SHGetFolderPathW(nullptr, CSIDL_COMMON_APPDATA | CSIDL_FLAG_CREATE, nullptr, 0, path); + string result = (const char*)utf8_t(path); + result.transform("\\", "/"); + #elif defined(PLATFORM_MACOSX) + string result = "/Library/Application Support/"; + #else + string result = "/usr/share/"; + #endif + if(result.empty()) result = "."; + if(result.endsWith("/") == false) result.append("/"); + return result; +} + +// /tmp +// c:/users/username/AppData/Local/Temp/ +string temppath() { + #if defined(PLATFORM_WINDOWS) + wchar_t path[PATH_MAX] = L""; + GetTempPathW(PATH_MAX, path); + string result = (const char*)utf8_t(path); + result.transform("\\", "/"); + #elif defined(P_tmpdir) + string result = P_tmpdir; + #else + string result = "/tmp/"; + #endif + if(result.endsWith("/") == false) result.append("/"); + return result; +} + +} + +#endif diff --git a/src/dep/libs/nall/string/ref.hpp b/src/dep/libs/nall/string/ref.hpp new file mode 100755 index 000000000..19200891b --- /dev/null +++ b/src/dep/libs/nall/string/ref.hpp @@ -0,0 +1,45 @@ +#ifdef NALL_STRING_INTERNAL_HPP + +namespace nall { + +struct stringref { + operator const char*() const { + return _data; + } + + const char* data() const { + return _data; + } + + unsigned size() const { + if(!_initialized) { + _initialized = true; + _size = strlen(_data); + } + return _size; + } + + stringref() = delete; + stringref(const stringref& source) = delete; + stringref(stringref&& source) = delete; + + stringref(const char* source) { + _data = source; + _initialized = false; + } + + stringref(const string& source) { + _data = source.data(); + _size = source.size(); + _initialized = true; + } + +protected: + const char* _data; + mutable unsigned _size; + mutable bool _initialized; +}; + +} + +#endif diff --git a/src/dep/libs/nall/string/replace.hpp b/src/dep/libs/nall/string/replace.hpp new file mode 100755 index 000000000..65af7be3d --- /dev/null +++ b/src/dep/libs/nall/string/replace.hpp @@ -0,0 +1,55 @@ +#ifdef NALL_STRING_INTERNAL_HPP + +namespace nall { + +template +string& string::ureplace(rstring key, rstring token) { + if(key.size() == 0) return *this; + enum : unsigned { limit = Limit ? Limit : ~0u }; + + const char* p = data(); + unsigned counter = 0; + + while(*p) { + if(quoteskip(p)) continue; + for(unsigned n = 0;; n++) { + if(key[n] == 0) { counter++; p += n; break; } + if(!chrequal(key[n], p[n])) { p++; break; } + } + } + if(counter == 0) return *this; + if(Limit) counter = min(counter, Limit); + + char* t = data(); + char* base = nullptr; + signed displacement = token.size() - key.size(); + signed displacementSize = displacement * counter; + + if(token.size() > key.size()) { + t = base = strduplicate(data()); + reserve((unsigned)(p - data()) + displacementSize); + } + char* o = data(); + + while(*t && counter) { + if(quotecopy(o, t)) continue; + for(unsigned n = 0;; n++) { + if(key[n] == 0) { counter--; memcpy(o, token, token.size()); t += key.size(); o += token.size(); break; } + if(!chrequal(key[n], t[n])) { *o++ = *t++; break; } + } + } + do *o++ = *t; while(*t++); + if(base) free(base); + + resize(_size + displacementSize); + return *this; +} + +template string& string::replace(rstring key, rstring token) { return ureplace(key, token); } +template string& string::ireplace(rstring key, rstring token) { return ureplace(key, token); } +template string& string::qreplace(rstring key, rstring token) { return ureplace(key, token); } +template string& string::iqreplace(rstring key, rstring token) { return ureplace(key, token); } + +}; + +#endif diff --git a/src/dep/libs/nall/string/split.hpp b/src/dep/libs/nall/string/split.hpp new file mode 100755 index 000000000..90521e1f0 --- /dev/null +++ b/src/dep/libs/nall/string/split.hpp @@ -0,0 +1,37 @@ +#ifdef NALL_STRING_INTERNAL_HPP + +namespace nall { + +template lstring& lstring::usplit(rstring key, rstring base) { + reset(); + if(key.size() == 0) return *this; + + const char* b = base; + const char* p = base; + + while(*p) { + if(Limit) if(size() >= Limit) break; + if(quoteskip(p)) continue; + for(unsigned n = 0;; n++) { + if(key[n] == 0) { + append(substr(b, 0, p - b)); + p += n; + b = p; + break; + } + if(!chrequal(key[n], p[n])) { p++; break; } + } + } + + append(b); + return *this; +} + +template lstring& lstring::split(rstring key, rstring src) { return usplit(key, src); } +template lstring& lstring::isplit(rstring key, rstring src) { return usplit(key, src); } +template lstring& lstring::qsplit(rstring key, rstring src) { return usplit(key, src); } +template lstring& lstring::iqsplit(rstring key, rstring src) { return usplit(key, src); } + +}; + +#endif diff --git a/src/dep/libs/nall/string/utility.hpp b/src/dep/libs/nall/string/utility.hpp new file mode 100755 index 000000000..429636cd4 --- /dev/null +++ b/src/dep/libs/nall/string/utility.hpp @@ -0,0 +1,117 @@ +#ifdef NALL_STRING_INTERNAL_HPP + +namespace nall { + +string substr(rstring source, unsigned offset, unsigned length) { + string result; + if(length == ~0u) length = source.size() - offset; + result.resize(length); + memcpy(result.data(), source.data() + offset, length); + return result; +} + +string sha256(const uint8_t* data, unsigned size) { + sha256_ctx sha; + uint8_t hash[32]; + sha256_init(&sha); + sha256_chunk(&sha, data, size); + sha256_final(&sha); + sha256_hash(&sha, hash); + string result; + for(auto& byte : hash) result.append(hex<2>(byte)); + return result; +} + +bool tokenize(lstring& list, const char* s, const char* p) { + while(*s) { + if(*p == '*') { + const char* b = s; + while(*s) { + if(tokenize(list, s++, p + 1)) { + list.prepend(substr(b, 0, --s - b)); + return true; + } + } + list.prepend(b); + return !*++p; + } + if(*s++ != *p++) return false; + } + while(*p == '*') { list.prepend(s); p++; } + return !*p; +} + +char* integer(char* result, intmax_t value) { + bool negative = value < 0; + if(negative) value = -value; + + char buffer[64]; + unsigned size = 0; + + do { + unsigned n = value % 10; + buffer[size++] = '0' + n; + value /= 10; + } while(value); + if(negative) buffer[size++] = '-'; +//buffer[size++] = negative ? '-' : '+'; + + for(signed x = size - 1, y = 0; x >= 0 && y < size; x--, y++) result[x] = buffer[y]; + result[size] = 0; + return result; +} + +char* decimal(char* result, uintmax_t value) { + char buffer[64]; + unsigned size = 0; + + do { + unsigned n = value % 10; + buffer[size++] = '0' + n; + value /= 10; + } while(value); + + for(signed x = size - 1, y = 0; x >= 0 && y < size; x--, y++) result[x] = buffer[y]; + result[size] = 0; + return result; +} + +//using sprintf is certainly not the most ideal method to convert +//a double to a string ... but attempting to parse a double by +//hand, digit-by-digit, results in subtle rounding errors. +unsigned real(char* str, long double value) { + char buffer[256]; + #ifdef _WIN32 + //Windows C-runtime does not support long double via sprintf() + sprintf(buffer, "%f", (double)value); + #else + sprintf(buffer, "%Lf", value); + #endif + + //remove excess 0's in fraction (2.500000 -> 2.5) + for(char* p = buffer; *p; p++) { + if(*p == '.') { + char* p = buffer + strlen(buffer) - 1; + while(*p == '0') { + if(*(p - 1) != '.') *p = 0; //... but not for eg 1.0 -> 1. + p--; + } + break; + } + } + + unsigned length = strlen(buffer); + if(str) strcpy(str, buffer); + return length + 1; +} + +string real(long double value) { + string temp; + temp.resize(real(nullptr, value)); + real(temp.data(), value); + return temp; +} + +} + +#endif diff --git a/src/dep/libs/nall/string/variadic.hpp b/src/dep/libs/nall/string/variadic.hpp new file mode 100755 index 000000000..fed6b7f34 --- /dev/null +++ b/src/dep/libs/nall/string/variadic.hpp @@ -0,0 +1,20 @@ +#ifdef NALL_STRING_INTERNAL_HPP + +namespace nall { + +void sprint(string& output) { +} + +template +void sprint(string& output, const T& value, Args&&... args) { + output._append(make_string(value)); + sprint(output, std::forward(args)...); +} + +template void print(Args&&... args) { + printf("%s", (const char*)string(std::forward(args)...)); +} + +} + +#endif diff --git a/src/dep/libs/nall/string/wrapper.hpp b/src/dep/libs/nall/string/wrapper.hpp new file mode 100755 index 000000000..0422ecdcd --- /dev/null +++ b/src/dep/libs/nall/string/wrapper.hpp @@ -0,0 +1,124 @@ +#ifdef NALL_STRING_INTERNAL_HPP + +namespace nall { + +template lstring string::split(rstring key) const { lstring result; result.split(key, data()); return result; } +template lstring string::isplit(rstring key) const { lstring result; result.isplit(key, data()); return result; } +template lstring string::qsplit(rstring key) const { lstring result; result.qsplit(key, data()); return result; } +template lstring string::iqsplit(rstring key) const { lstring result; result.iqsplit(key, data()); return result; } + +bool string::match(rstring source) const { return nall::strmatch(data(), source); } +bool string::imatch(rstring source) const { return nall::istrmatch(data(), source); } + +signed string::compare(rstring source) const { + return strcmp(data(), source.data()); +} + +signed string::icompare(rstring source) const { + return istrcmp(data(), source.data()); +} + +bool string::equals(rstring source) const { + if(size() != source.size()) return false; + return compare(source) == 0; +} + +bool string::iequals(rstring source) const { + if(size() != source.size()) return false; + return icompare(source) == 0; +} + +bool string::beginsWith(rstring source) const { + if(source.size() > size()) return false; + return memcmp(data(), source.data(), source.size()) == 0; +} + +bool string::ibeginsWith(rstring source) const { + if(source.size() > size()) return false; + return imemcmp(data(), source.data(), source.size()) == 0; +} + +bool string::endsWith(rstring source) const { + if(source.size() > size()) return false; + return memcmp(data() + size() - source.size(), source.data(), source.size()) == 0; +} + +bool string::iendsWith(rstring source) const { + if(source.size() > size()) return false; + return imemcmp(data() + size() - source.size(), source.data(), source.size()) == 0; +} + +string string::slice(unsigned offset, unsigned length) const { + if(offset >= size()) return ""; + if(length == ~0u) length = size() - offset; + return substr(data(), offset, length); +} + +string& string::lower() { nall::strlower(data()); return *this; } +string& string::upper() { nall::strupper(data()); return *this; } +string& string::qlower() { nall::qstrlower(data()); return *this; } +string& string::qupper() { nall::qstrupper(data()); return *this; } + +string& string::transform(rstring before, rstring after) { nall::strtr(data(), before, after); return *this; } + +string& string::reverse() { + unsigned length = size(), pivot = length >> 1; + for(signed x = 0, y = length - 1; x < pivot && y >= 0; x++, y--) std::swap(data()[x], data()[y]); + return *this; +} + +template string& string::ltrim(rstring key) { + if(key.size() == 0) return *this; + unsigned limit = Limit ? Limit : ~0u, offset = 0; + + while(limit && size() - offset >= key.size()) { + if(memcmp(data() + offset, key.data(), key.size())) break; + offset += key.size(); + limit--; + } + + if(offset) memmove(data(), data() + offset, size() - offset); + resize(size() - offset); + return *this; +} + +template string& string::rtrim(rstring key) { + if(key.size() == 0) return *this; + unsigned limit = Limit ? Limit : ~0u, offset = 0; + + while(limit && size() - offset >= key.size()) { + if(memcmp(data() + size() - key.size() - offset, key.data(), key.size())) break; + offset += key.size(); + limit--; + } + + resize(size() - offset); + return *this; +} + +template string& string::trim(rstring key) { + rtrim(key); + ltrim(key); + return *this; +} + +template string& string::trim(rstring lkey, rstring rkey) { + rtrim(rkey); + ltrim(lkey); + return *this; +} + +string& string::strip() { + nall::strip(data()); + resize(length()); + return *this; +} + +optional string::find(rstring key) const { return strpos(data(), key); } +optional string::ifind(rstring key) const { return istrpos(data(), key); } +optional string::qfind(rstring key) const { return qstrpos(data(), key); } +optional string::iqfind(rstring key) const { return iqstrpos(data(), key); } + +} + +#endif diff --git a/src/dep/libs/nall/thread.hpp b/src/dep/libs/nall/thread.hpp new file mode 100755 index 000000000..96536df01 --- /dev/null +++ b/src/dep/libs/nall/thread.hpp @@ -0,0 +1,127 @@ +#ifndef NALL_THREAD_HPP +#define NALL_THREAD_HPP + +#include +#include +#include + +#if defined(PLATFORM_X) || defined(PLATFORM_MACOSX) + #include + +namespace nall { + +inline void* thread_entry_point(void*); + +struct thread { + thread(function entryPoint) : entryPoint(entryPoint), completed(false), dead(false) { + initialize(); + pthread_create(&pthread, nullptr, thread_entry_point, (void*)this); + } + + ~thread() { + join(); + } + + bool active() const { + return completed == false; + } + + void join() { + if(dead) return; + dead = true; + pthread_join(pthread, nullptr); + } + + static bool primary() { + initialize(); + return pthread_equal(primaryThread(), pthread_self()); + } + +private: + pthread_t pthread; + function entryPoint; + volatile bool completed, dead; + friend void* thread_entry_point(void*); + + static void initialize() { + static bool initialized = false; + if(initialized) return; + initialized = true; + primaryThread() = pthread_self(); + } + + static pthread_t& primaryThread() { + static pthread_t thread; + return thread; + } +}; + +void* thread_entry_point(void* parameter) { + thread* context = (thread*)parameter; + context->entryPoint(); + context->completed = true; + pthread_exit(nullptr); +} + +} +#elif defined(PLATFORM_WINDOWS) +namespace nall { + +inline DWORD WINAPI thread_entry_point(LPVOID); + +struct thread { + thread(function entryPoint) : entryPoint(entryPoint), completed(false), dead(false) { + initialize(); + hthread = CreateThread(nullptr, 0, thread_entry_point, (void*)this, 0, nullptr); + } + + ~thread() { + join(); + } + + bool active() const { + return completed == false; + } + + void join() { + if(dead) return; + dead = true; + WaitForSingleObject(hthread, INFINITE); + CloseHandle(hthread); + } + + static bool primary() { + initialize(); + return primaryThread() == GetCurrentThreadId(); + } + +private: + HANDLE hthread; + function entryPoint; + volatile bool completed, dead; + friend DWORD WINAPI thread_entry_point(LPVOID); + + static void initialize() { + static bool initialized = false; + if(initialized) return; + initialized = true; + primaryThread() = GetCurrentThreadId(); + } + + static DWORD& primaryThread() { + static DWORD thread; + return thread; + } +}; + +inline DWORD WINAPI thread_entry_point(LPVOID parameter) { + thread *context = (thread*)parameter; + context->entryPoint(); + context->completed = true; + return 0; +} + +} +#endif + +#endif diff --git a/src/dep/libs/nall/traits.hpp b/src/dep/libs/nall/traits.hpp new file mode 100755 index 000000000..6a140c2b9 --- /dev/null +++ b/src/dep/libs/nall/traits.hpp @@ -0,0 +1,33 @@ +#ifndef NALL_TRAITS_HPP +#define NALL_TRAITS_HPP + +#include + +namespace nall { + +template class has_default_constructor { + template class receive_size{}; + template static signed sfinae(receive_size*); + template static char sfinae(...); + +public: + enum : bool { value = sizeof(sfinae(0)) == sizeof(signed) }; +}; + +template struct enable_if { typedef T type; }; +template struct enable_if {}; + +template struct type_if { typedef T type; }; +template struct type_if { typedef F type; }; + +template struct static_and { enum { value = false }; }; +template<> struct static_and { enum { value = true }; }; + +template struct static_or { enum { value = false }; }; +template<> struct static_or { enum { value = true }; }; +template<> struct static_or { enum { value = true }; }; +template<> struct static_or { enum { value = true }; }; + +} + +#endif diff --git a/src/dep/libs/nall/unzip.hpp b/src/dep/libs/nall/unzip.hpp new file mode 100755 index 000000000..269a15d2f --- /dev/null +++ b/src/dep/libs/nall/unzip.hpp @@ -0,0 +1,126 @@ +#ifndef NALL_UNZIP_HPP +#define NALL_UNZIP_HPP + +#include +#include +#include +#include + +namespace nall { + +struct unzip { + struct File { + string name; + const uint8_t* data; + unsigned size; + unsigned csize; + unsigned cmode; //0 = uncompressed, 8 = deflate + unsigned crc32; + }; + + inline bool open(const string& filename) { + close(); + if(fm.open(filename, filemap::mode::read) == false) return false; + if(open(fm.data(), fm.size()) == false) { + fm.close(); + return false; + } + return true; + } + + inline bool open(const uint8_t* data, unsigned size) { + if(size < 22) return false; + + filedata = data; + filesize = size; + + file.reset(); + + const uint8_t* footer = data + size - 22; + while(true) { + if(footer <= data + 22) return false; + if(read(footer, 4) == 0x06054b50) { + unsigned commentlength = read(footer + 20, 2); + if(footer + 22 + commentlength == data + size) break; + } + footer--; + } + const uint8_t* directory = data + read(footer + 16, 4); + + while(true) { + unsigned signature = read(directory + 0, 4); + if(signature != 0x02014b50) break; + + File file; + file.cmode = read(directory + 10, 2); + file.crc32 = read(directory + 16, 4); + file.csize = read(directory + 20, 4); + file.size = read(directory + 24, 4); + + unsigned namelength = read(directory + 28, 2); + unsigned extralength = read(directory + 30, 2); + unsigned commentlength = read(directory + 32, 2); + + char* filename = new char[namelength + 1]; + memcpy(filename, directory + 46, namelength); + filename[namelength] = 0; + file.name = filename; + delete[] filename; + + unsigned offset = read(directory + 42, 4); + unsigned offsetNL = read(data + offset + 26, 2); + unsigned offsetEL = read(data + offset + 28, 2); + file.data = data + offset + 30 + offsetNL + offsetEL; + + directory += 46 + namelength + extralength + commentlength; + + this->file.append(file); + } + + return true; + } + + inline vector extract(File& file) { + vector buffer; + + if(file.cmode == 0) { + buffer.resize(file.size); + memcpy(buffer.data(), file.data, file.size); + } + + if(file.cmode == 8) { + buffer.resize(file.size); + if(inflate(buffer.data(), buffer.size(), file.data, file.csize) == false) { + buffer.reset(); + } + } + + return buffer; + } + + inline void close() { + if(fm.open()) fm.close(); + } + + ~unzip() { + close(); + } + +protected: + filemap fm; + const uint8_t* filedata; + unsigned filesize; + + unsigned read(const uint8_t* data, unsigned size) { + unsigned result = 0, shift = 0; + while(size--) { result |= *data++ << shift; shift += 8; } + return result; + } + +public: + vector file; +}; + +} + +#endif diff --git a/src/dep/libs/nall/ups.hpp b/src/dep/libs/nall/ups.hpp new file mode 100755 index 000000000..d26a65199 --- /dev/null +++ b/src/dep/libs/nall/ups.hpp @@ -0,0 +1,225 @@ +#ifndef NALL_UPS_HPP +#define NALL_UPS_HPP + +#include +#include +#include +#include + +namespace nall { + +struct ups { + enum class result : unsigned { + unknown, + success, + patch_unwritable, + patch_invalid, + source_invalid, + target_invalid, + target_too_small, + patch_checksum_invalid, + source_checksum_invalid, + target_checksum_invalid, + }; + + function progress; + + result create( + const uint8_t* sourcedata, unsigned sourcelength, + const uint8_t* targetdata, unsigned targetlength, + const char* patchfilename + ) { + source_data = (uint8_t*)sourcedata, target_data = (uint8_t*)targetdata; + source_length = sourcelength, target_length = targetlength; + source_offset = target_offset = 0; + source_checksum = target_checksum = patch_checksum = ~0; + + if(patch_file.open(patchfilename, file::mode::write) == false) return result::patch_unwritable; + + patch_write('U'); + patch_write('P'); + patch_write('S'); + patch_write('1'); + encode(source_length); + encode(target_length); + + unsigned output_length = source_length > target_length ? source_length : target_length; + unsigned relative = 0; + for(unsigned offset = 0; offset < output_length;) { + uint8_t x = source_read(); + uint8_t y = target_read(); + + if(x == y) { + offset++; + continue; + } + + encode(offset++ - relative); + patch_write(x ^ y); + + while(true) { + if(offset >= output_length) { + patch_write(0x00); + break; + } + + x = source_read(); + y = target_read(); + offset++; + patch_write(x ^ y); + if(x == y) break; + } + + relative = offset; + } + + source_checksum = ~source_checksum; + target_checksum = ~target_checksum; + for(unsigned i = 0; i < 4; i++) patch_write(source_checksum >> (i * 8)); + for(unsigned i = 0; i < 4; i++) patch_write(target_checksum >> (i * 8)); + uint32_t patch_result_checksum = ~patch_checksum; + for(unsigned i = 0; i < 4; i++) patch_write(patch_result_checksum >> (i * 8)); + + patch_file.close(); + return result::success; + } + + result apply( + const uint8_t* patchdata, unsigned patchlength, + const uint8_t* sourcedata, unsigned sourcelength, + uint8_t* targetdata, unsigned& targetlength + ) { + patch_data = (uint8_t*)patchdata, source_data = (uint8_t*)sourcedata, target_data = targetdata; + patch_length = patchlength, source_length = sourcelength, target_length = targetlength; + patch_offset = source_offset = target_offset = 0; + patch_checksum = source_checksum = target_checksum = ~0; + + if(patch_length < 18) return result::patch_invalid; + if(patch_read() != 'U') return result::patch_invalid; + if(patch_read() != 'P') return result::patch_invalid; + if(patch_read() != 'S') return result::patch_invalid; + if(patch_read() != '1') return result::patch_invalid; + + unsigned source_read_length = decode(); + unsigned target_read_length = decode(); + + if(source_length != source_read_length && source_length != target_read_length) return result::source_invalid; + targetlength = (source_length == source_read_length ? target_read_length : source_read_length); + if(target_length < targetlength) return result::target_too_small; + target_length = targetlength; + + while(patch_offset < patch_length - 12) { + unsigned length = decode(); + while(length--) target_write(source_read()); + while(true) { + uint8_t patch_xor = patch_read(); + target_write(patch_xor ^ source_read()); + if(patch_xor == 0) break; + } + } + while(source_offset < source_length) target_write(source_read()); + while(target_offset < target_length) target_write(source_read()); + + uint32_t patch_read_checksum = 0, source_read_checksum = 0, target_read_checksum = 0; + for(unsigned i = 0; i < 4; i++) source_read_checksum |= patch_read() << (i * 8); + for(unsigned i = 0; i < 4; i++) target_read_checksum |= patch_read() << (i * 8); + uint32_t patch_result_checksum = ~patch_checksum; + source_checksum = ~source_checksum; + target_checksum = ~target_checksum; + for(unsigned i = 0; i < 4; i++) patch_read_checksum |= patch_read() << (i * 8); + + if(patch_result_checksum != patch_read_checksum) return result::patch_invalid; + if(source_checksum == source_read_checksum && source_length == source_read_length) { + if(target_checksum == target_read_checksum && target_length == target_read_length) return result::success; + return result::target_invalid; + } else if(source_checksum == target_read_checksum && source_length == target_read_length) { + if(target_checksum == source_read_checksum && target_length == source_read_length) return result::success; + return result::target_invalid; + } else { + return result::source_invalid; + } + } + +private: + uint8_t* patch_data = nullptr; + uint8_t* source_data = nullptr; + uint8_t* target_data = nullptr; + unsigned patch_length, source_length, target_length; + unsigned patch_offset, source_offset, target_offset; + unsigned patch_checksum, source_checksum, target_checksum; + file patch_file; + + uint8_t patch_read() { + if(patch_offset < patch_length) { + uint8_t n = patch_data[patch_offset++]; + patch_checksum = crc32_adjust(patch_checksum, n); + return n; + } + return 0x00; + } + + uint8_t source_read() { + if(source_offset < source_length) { + uint8_t n = source_data[source_offset++]; + source_checksum = crc32_adjust(source_checksum, n); + return n; + } + return 0x00; + } + + uint8_t target_read() { + uint8_t result = 0x00; + if(target_offset < target_length) { + result = target_data[target_offset]; + target_checksum = crc32_adjust(target_checksum, result); + } + if(((target_offset++ & 255) == 0) && progress) { + progress(target_offset, source_length > target_length ? source_length : target_length); + } + return result; + } + + void patch_write(uint8_t n) { + patch_file.write(n); + patch_checksum = crc32_adjust(patch_checksum, n); + } + + void target_write(uint8_t n) { + if(target_offset < target_length) { + target_data[target_offset] = n; + target_checksum = crc32_adjust(target_checksum, n); + } + if(((target_offset++ & 255) == 0) && progress) { + progress(target_offset, source_length > target_length ? source_length : target_length); + } + } + + void encode(uint64_t offset) { + while(true) { + uint64_t x = offset & 0x7f; + offset >>= 7; + if(offset == 0) { + patch_write(0x80 | x); + break; + } + patch_write(x); + offset--; + } + } + + uint64_t decode() { + uint64_t offset = 0, shift = 1; + while(true) { + uint8_t x = patch_read(); + offset += (x & 0x7f) * shift; + if(x & 0x80) break; + shift <<= 7; + offset += shift; + } + return offset; + } +}; + +} + +#endif diff --git a/src/dep/libs/nall/utility.hpp b/src/dep/libs/nall/utility.hpp new file mode 100755 index 000000000..0447b94af --- /dev/null +++ b/src/dep/libs/nall/utility.hpp @@ -0,0 +1,138 @@ +#ifndef NALL_UTILITY_HPP +#define NALL_UTILITY_HPP + +#include +#include +#include + +namespace nall { + +template struct base_from_member { + T value; + base_from_member(T value) : value(value) {} +}; + +template struct ref { + T& operator*() { + if(type == Type::Reference) return *any_cast(value); + return any_cast(value); + } + + operator T&() { return operator*(); } + + ref(T& value) : type(Type::Reference), value(&value) {} + ref(T&& value) : type(Type::Temporary), value(value) {} + +protected: + enum class Type : unsigned { Reference, Temporary } type; + any value; +}; + +template struct optional { + typedef typename std::remove_reference::type T; + static const bool isConst = std::is_const::value; + static const bool isReference = std::is_reference::value; + struct optional_value_not_valid{}; + + bool valid = false; + T* value = nullptr; + + operator bool() const { return valid; } + + void reset() { + valid = false; + if(value) { + if(!isReference) delete value; + value = nullptr; + } + } + + template::type> + T& operator*() { + if(!valid) throw optional_value_not_valid{}; + return *value; + } + + template::type> + T& operator()() { + if(!valid) throw optional_value_not_valid{}; + return *value; + } + + const T& operator*() const { + if(!valid) throw optional_value_not_valid{}; + return *value; + } + + const T& operator()() const { + if(!valid) throw optional_value_not_valid{}; + return *value; + } + + const T& operator()(const T& alternate) const { + if(!valid) return alternate; + return *value; + } + + const bool operator==(const optional& source) const { + if(valid && source.valid) return *value == *source.value; + if(!valid && !source.valid) return true; + return false; + } + + const bool operator!=(const optional& source) const { + return !operator==(source); + } + + optional& operator=(const T& source) { + reset(); + valid = true; + if(isReference) value = (T*)&source; + else value = new T(source); + return *this; + } + + optional& operator=(T&& source) { + reset(); + valid = true; + if(isReference) value = &source; + else value = new T(std::move(source)); + return *this; + } + + optional& operator=(const optional& source) { + reset(); + valid = source.valid; + if(valid) operator=(source); + return *this; + } + + optional& operator=(optional&& source) { + reset(); + valid = source.valid; + value = source.value; + source.valid = false; + source.value = nullptr; + return *this; + } + + optional() = default; + optional(bool valid) : valid(valid) {} + optional(const T& value) { operator=(value); } + optional(T&& value) { operator=(std::move(value)); } + optional(bool valid, const T& value) : valid(valid) { if(valid) operator=(value); } + optional(bool valid, T&& value) : valid(valid) { if(valid) operator=(std::move(value)); } + optional(const optional& source) { operator=(source); } + optional(optional&& source) { operator=(std::move(source)); } + ~optional() { reset(); } +}; + +template inline T* allocate(unsigned size, const T& value) { + T* array = new T[size]; + for(unsigned i = 0; i < size; i++) array[i] = value; + return array; +} + +} + +#endif diff --git a/src/dep/libs/nall/varint.hpp b/src/dep/libs/nall/varint.hpp new file mode 100755 index 000000000..944e193e7 --- /dev/null +++ b/src/dep/libs/nall/varint.hpp @@ -0,0 +1,220 @@ +#ifndef NALL_VARINT_HPP +#define NALL_VARINT_HPP + +#include +#include +#include +#include + +namespace nall { + +struct varint { + virtual uint8_t read() = 0; + virtual void write(uint8_t) = 0; + + uintmax_t readvu() { + uintmax_t data = 0, shift = 1; + while(true) { + uint8_t x = read(); + data += (x & 0x7f) * shift; + if(x & 0x80) break; + shift <<= 7; + data += shift; + } + return data; + } + + intmax_t readvs() { + uintmax_t data = readvu(); + bool sign = data & 1; + data >>= 1; + if(sign) data = -data; + return data; + } + + void writevu(uintmax_t data) { + while(true) { + uint8_t x = data & 0x7f; + data >>= 7; + if(data == 0) return write(0x80 | x); + write(x); + data--; + } + } + + void writevs(intmax_t data) { + bool sign = data < 0; + if(sign) data = -data; + data = (data << 1) | sign; + writevu(data); + } +}; + +template struct uint_t { +private: + typedef typename type_if::type type_t; + type_t data; + +public: + inline operator type_t() const { return data; } + inline type_t operator ++(int) { type_t r = data; data = uclip(data + 1); return r; } + inline type_t operator --(int) { type_t r = data; data = uclip(data - 1); return r; } + inline type_t operator ++() { return data = uclip(data + 1); } + inline type_t operator --() { return data = uclip(data - 1); } + inline type_t operator =(const type_t i) { return data = uclip(i); } + inline type_t operator |=(const type_t i) { return data = uclip(data | i); } + inline type_t operator ^=(const type_t i) { return data = uclip(data ^ i); } + inline type_t operator &=(const type_t i) { return data = uclip(data & i); } + inline type_t operator<<=(const type_t i) { return data = uclip(data << i); } + inline type_t operator>>=(const type_t i) { return data = uclip(data >> i); } + inline type_t operator +=(const type_t i) { return data = uclip(data + i); } + inline type_t operator -=(const type_t i) { return data = uclip(data - i); } + inline type_t operator *=(const type_t i) { return data = uclip(data * i); } + inline type_t operator /=(const type_t i) { return data = uclip(data / i); } + inline type_t operator %=(const type_t i) { return data = uclip(data % i); } + + inline uint_t() : data(0) {} + inline uint_t(const type_t i) : data(uclip(i)) {} + + template inline type_t operator=(const uint_t &i) { return data = uclip((type_t)i); } + template inline uint_t(const uint_t &i) : data(uclip(i)) {} + + void serialize(serializer& s) { s(data); } +}; + +template struct int_t { +private: + typedef typename type_if::type type_t; + type_t data; + +public: + inline operator type_t() const { return data; } + inline type_t operator ++(int) { type_t r = data; data = sclip(data + 1); return r; } + inline type_t operator --(int) { type_t r = data; data = sclip(data - 1); return r; } + inline type_t operator ++() { return data = sclip(data + 1); } + inline type_t operator --() { return data = sclip(data - 1); } + inline type_t operator =(const type_t i) { return data = sclip(i); } + inline type_t operator |=(const type_t i) { return data = sclip(data | i); } + inline type_t operator ^=(const type_t i) { return data = sclip(data ^ i); } + inline type_t operator &=(const type_t i) { return data = sclip(data & i); } + inline type_t operator<<=(const type_t i) { return data = sclip(data << i); } + inline type_t operator>>=(const type_t i) { return data = sclip(data >> i); } + inline type_t operator +=(const type_t i) { return data = sclip(data + i); } + inline type_t operator -=(const type_t i) { return data = sclip(data - i); } + inline type_t operator *=(const type_t i) { return data = sclip(data * i); } + inline type_t operator /=(const type_t i) { return data = sclip(data / i); } + inline type_t operator %=(const type_t i) { return data = sclip(data % i); } + + inline int_t() : data(0) {} + inline int_t(const type_t i) : data(sclip(i)) {} + + template inline type_t operator=(const int_t &i) { return data = sclip((type_t)i); } + template inline int_t(const int_t &i) : data(sclip(i)) {} + + void serialize(serializer& s) { s(data); } +}; + +template struct varuint_t { +private: + type_t data; + type_t mask; + +public: + inline operator type_t() const { return data; } + inline type_t operator ++(int) { type_t r = data; data = (data + 1) & mask; return r; } + inline type_t operator --(int) { type_t r = data; data = (data - 1) & mask; return r; } + inline type_t operator ++() { return data = (data + 1) & mask; } + inline type_t operator --() { return data = (data - 1) & mask; } + inline type_t operator =(const type_t i) { return data = (i) & mask; } + inline type_t operator |=(const type_t i) { return data = (data | i) & mask; } + inline type_t operator ^=(const type_t i) { return data = (data ^ i) & mask; } + inline type_t operator &=(const type_t i) { return data = (data & i) & mask; } + inline type_t operator<<=(const type_t i) { return data = (data << i) & mask; } + inline type_t operator>>=(const type_t i) { return data = (data >> i) & mask; } + inline type_t operator +=(const type_t i) { return data = (data + i) & mask; } + inline type_t operator -=(const type_t i) { return data = (data - i) & mask; } + inline type_t operator *=(const type_t i) { return data = (data * i) & mask; } + inline type_t operator /=(const type_t i) { return data = (data / i) & mask; } + inline type_t operator %=(const type_t i) { return data = (data % i) & mask; } + + inline void bits(type_t bits) { mask = (1ull << (bits - 1)) + ((1ull << (bits - 1)) - 1); data &= mask; } + inline varuint_t() : data(0ull), mask((type_t)~0ull) {} + inline varuint_t(const type_t i) : data(i), mask((type_t)~0ull) {} + + void serialize(serializer& s) { s(data); s(mask); } +}; + +} + +//typedefs + typedef nall::uint_t< 1> uint1_t; + typedef nall::uint_t< 2> uint2_t; + typedef nall::uint_t< 3> uint3_t; + typedef nall::uint_t< 4> uint4_t; + typedef nall::uint_t< 5> uint5_t; + typedef nall::uint_t< 6> uint6_t; + typedef nall::uint_t< 7> uint7_t; +//typedef nall::uint_t< 8> uint8_t; + + typedef nall::uint_t< 9> uint9_t; + typedef nall::uint_t<10> uint10_t; + typedef nall::uint_t<11> uint11_t; + typedef nall::uint_t<12> uint12_t; + typedef nall::uint_t<13> uint13_t; + typedef nall::uint_t<14> uint14_t; + typedef nall::uint_t<15> uint15_t; +//typedef nall::uint_t<16> uint16_t; + + typedef nall::uint_t<17> uint17_t; + typedef nall::uint_t<18> uint18_t; + typedef nall::uint_t<19> uint19_t; + typedef nall::uint_t<20> uint20_t; + typedef nall::uint_t<21> uint21_t; + typedef nall::uint_t<22> uint22_t; + typedef nall::uint_t<23> uint23_t; + typedef nall::uint_t<24> uint24_t; + typedef nall::uint_t<25> uint25_t; + typedef nall::uint_t<26> uint26_t; + typedef nall::uint_t<27> uint27_t; + typedef nall::uint_t<28> uint28_t; + typedef nall::uint_t<29> uint29_t; + typedef nall::uint_t<30> uint30_t; + typedef nall::uint_t<31> uint31_t; +//typedef nall::uint_t<32> uint32_t; + + typedef nall::int_t< 1> int1_t; + typedef nall::int_t< 2> int2_t; + typedef nall::int_t< 3> int3_t; + typedef nall::int_t< 4> int4_t; + typedef nall::int_t< 5> int5_t; + typedef nall::int_t< 6> int6_t; + typedef nall::int_t< 7> int7_t; +//typedef nall::int_t< 8> int8_t; + + typedef nall::int_t< 9> int9_t; + typedef nall::int_t<10> int10_t; + typedef nall::int_t<11> int11_t; + typedef nall::int_t<12> int12_t; + typedef nall::int_t<13> int13_t; + typedef nall::int_t<14> int14_t; + typedef nall::int_t<15> int15_t; +//typedef nall::int_t<16> int16_t; + + typedef nall::int_t<17> int17_t; + typedef nall::int_t<18> int18_t; + typedef nall::int_t<19> int19_t; + typedef nall::int_t<20> int20_t; + typedef nall::int_t<21> int21_t; + typedef nall::int_t<22> int22_t; + typedef nall::int_t<23> int23_t; + typedef nall::int_t<24> int24_t; + typedef nall::int_t<25> int25_t; + typedef nall::int_t<26> int26_t; + typedef nall::int_t<27> int27_t; + typedef nall::int_t<28> int28_t; + typedef nall::int_t<29> int29_t; + typedef nall::int_t<30> int30_t; + typedef nall::int_t<31> int31_t; +//typedef nall::int_t<32> int32_t; + +#endif diff --git a/src/dep/libs/nall/vector.hpp b/src/dep/libs/nall/vector.hpp new file mode 100755 index 000000000..7ccb53dbe --- /dev/null +++ b/src/dep/libs/nall/vector.hpp @@ -0,0 +1,275 @@ +#ifndef NALL_VECTOR_HPP +#define NALL_VECTOR_HPP + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace nall { + +template struct vector { + struct exception_out_of_bounds{}; + +protected: + T* pool = nullptr; + unsigned poolbase = 0; + unsigned poolsize = 0; + unsigned objectsize = 0; + +public: + explicit operator bool() const { return objectsize; } + T* data() { return pool + poolbase; } + const T* data() const { return pool + poolbase; } + + bool empty() const { return objectsize == 0; } + unsigned size() const { return objectsize; } + unsigned capacity() const { return poolsize; } + + T* move() { + T* result = pool + poolbase; + pool = nullptr; + poolbase = 0; + poolsize = 0; + objectsize = 0; + return result; + } + + void reset() { + if(pool) { + for(unsigned n = 0; n < objectsize; n++) pool[poolbase + n].~T(); + free(pool); + } + pool = nullptr; + poolbase = 0; + poolsize = 0; + objectsize = 0; + } + + void reserve(unsigned size) { + if(size <= poolsize) return; + size = bit::round(size); //amortize growth + + T* copy = (T*)calloc(size, sizeof(T)); + for(unsigned n = 0; n < objectsize; n++) new(copy + n) T(std::move(pool[poolbase + n])); + free(pool); + pool = copy; + poolbase = 0; + poolsize = size; + } + + void resize(unsigned size) { + T* copy = (T*)calloc(size, sizeof(T)); + for(unsigned n = 0; n < size && n < objectsize; n++) new(copy + n) T(std::move(pool[poolbase + n])); + reset(); + pool = copy; + poolbase = 0; + poolsize = size; + objectsize = size; + } + + template void prepend(const T& data, Args&&... args) { + prepend(std::forward(args)...); + prepend(data); + } + + T& prepend(const T& data) { + reserve(objectsize + 1); + if(poolbase == 0) { + unsigned available = poolsize - objectsize; + poolbase = max(1u, available >> 1); + for(signed n = objectsize - 1; n >= 0; n--) { + pool[poolbase + n] = std::move(pool[n]); + } + } + new(pool + --poolbase) T(data); + objectsize++; + return first(); + } + + template void append(const T& data, Args&&... args) { + append(data); + append(std::forward(args)...); + } + + T& append(const T& data) { + reserve(poolbase + objectsize + 1); + new(pool + poolbase + objectsize++) T(data); + return last(); + } + + bool appendOnce(const T& data) { + if(find(data)) return false; + return append(data), true; + } + + void insert(unsigned position, const T& data) { + if(position == 0) return prepend(data); + append(data); + if(position == ~0u) return; + for(signed n = objectsize - 1; n > position; n--) { + pool[poolbase + n] = std::move(pool[poolbase + n - 1]); + } + pool[poolbase + position] = data; + } + + void remove(unsigned position = ~0u, unsigned length = 1) { + if(position == ~0u) position = objectsize - 1; + if(position + length > objectsize) throw exception_out_of_bounds{}; + + if(position == 0) { + for(unsigned n = 0; n < length; n++) pool[poolbase + n].~T(); + poolbase += length; + } else { + for(unsigned n = position; n < objectsize; n++) { + if(n + length < objectsize) { + pool[poolbase + n] = std::move(pool[poolbase + n + length]); + } else { + pool[poolbase + n].~T(); + } + } + } + objectsize -= length; + } + + void removeFirst() { return remove(0); } + void removeLast() { return remove(~0u); } + + T take(unsigned position = ~0u) { + if(position == ~0u) position = objectsize - 1; + T object = pool[poolbase + position]; + remove(position); + return object; + } + + T takeFirst() { return take(0); } + T takeLast() { return take(~0u); } + + void reverse() { + unsigned pivot = size() / 2; + for(unsigned l = 0, r = size() - 1; l < pivot; l++, r--) { + std::swap(pool[poolbase + l], pool[poolbase + r]); + } + } + + void sort() { + nall::sort(pool + poolbase, objectsize); + } + + template void sort(const Comparator &lessthan) { + nall::sort(pool + poolbase, objectsize, lessthan); + } + + optional find(const T& data) { + for(unsigned n = 0; n < objectsize; n++) if(pool[poolbase + n] == data) return {true, n}; + return false; + } + + T& first() { + if(objectsize == 0) throw exception_out_of_bounds(); + return pool[poolbase]; + } + + const T& first() const { + if(objectsize == 0) throw exception_out_of_bounds(); + return pool[poolbase]; + } + + T& last() { + if(objectsize == 0) throw exception_out_of_bounds(); + return pool[poolbase + objectsize - 1]; + } + + const T& last() const { + if(objectsize == 0) throw exception_out_of_bounds(); + return pool[poolbase + objectsize - 1]; + } + + //access + inline T& operator[](unsigned position) { + if(position >= objectsize) throw exception_out_of_bounds(); + return pool[poolbase + position]; + } + + inline const T& operator[](unsigned position) const { + if(position >= objectsize) throw exception_out_of_bounds(); + return pool[poolbase + position]; + } + + inline T& operator()(unsigned position) { + if(position >= poolsize) reserve(position + 1); + while(position >= objectsize) append(T()); + return pool[poolbase + position]; + } + + inline const T& operator()(unsigned position, const T& data) const { + if(position >= objectsize) return data; + return pool[poolbase + position]; + } + + //iteration + struct iterator { + T& operator*() { return source.operator[](position); } + bool operator!=(const iterator& source) const { return position != source.position; } + iterator& operator++() { position++; return *this; } + iterator(vector& source, unsigned position) : source(source), position(position) {} + + private: + vector& source; + unsigned position; + }; + + iterator begin() { return iterator(*this, 0); } + iterator end() { return iterator(*this, size()); } + + struct constIterator { + const T& operator*() const { return source.operator[](position); } + bool operator!=(const constIterator& source) const { return position != source.position; } + constIterator& operator++() { position++; return *this; } + constIterator(const vector& source, unsigned position) : source(source), position(position) {} + + private: + const vector& source; + unsigned position; + }; + + const constIterator begin() const { return constIterator(*this, 0); } + const constIterator end() const { return constIterator(*this, size()); } + + //copy + inline vector& operator=(const vector& source) { + reset(); + reserve(source.size()); + for(auto& data : source) append(data); + return *this; + } + + //move + inline vector& operator=(vector&& source) { + reset(); + pool = source.pool; + poolbase = source.poolbase; + poolsize = source.poolsize; + objectsize = source.objectsize; + source.pool = nullptr; + source.poolbase = 0; + source.poolsize = 0; + source.objectsize = 0; + return *this; + } + + //construction and destruction + vector() = default; + vector(std::initializer_list list) { for(auto& data : list) append(data); } + vector(const vector& source) { operator=(source); } + vector(vector&& source) { operator=(std::move(source)); } + ~vector() { reset(); } +}; + +} + +#endif diff --git a/src/dep/libs/nall/windows/detour.hpp b/src/dep/libs/nall/windows/detour.hpp new file mode 100755 index 000000000..34a5d3f8b --- /dev/null +++ b/src/dep/libs/nall/windows/detour.hpp @@ -0,0 +1,192 @@ +#ifndef NALL_WINDOWS_DETOUR_HPP +#define NALL_WINDOWS_DETOUR_HPP + +#include +#include +#include +#include +#include + +namespace nall { + +#define Copy 0 +#define RelNear 1 + +struct detour { + static bool insert(const string& moduleName, const string& functionName, void*& source, void* target); + static bool remove(const string& moduleName, const string& functionName, void*& source); + +protected: + static unsigned length(const uint8_t* function); + static unsigned mirror(uint8_t* target, const uint8_t* source); + + struct opcode { + uint16_t prefix; + unsigned length; + unsigned mode; + uint16_t modify; + }; + static opcode opcodes[]; +}; + +//TODO: +//* fs:, gs: should force another opcode copy +//* conditional branches within +5-byte range should fail +detour::opcode detour::opcodes[] = { + { 0x50, 1 }, //push eax + { 0x51, 1 }, //push ecx + { 0x52, 1 }, //push edx + { 0x53, 1 }, //push ebx + { 0x54, 1 }, //push esp + { 0x55, 1 }, //push ebp + { 0x56, 1 }, //push esi + { 0x57, 1 }, //push edi + { 0x58, 1 }, //pop eax + { 0x59, 1 }, //pop ecx + { 0x5a, 1 }, //pop edx + { 0x5b, 1 }, //pop ebx + { 0x5c, 1 }, //pop esp + { 0x5d, 1 }, //pop ebp + { 0x5e, 1 }, //pop esi + { 0x5f, 1 }, //pop edi + { 0x64, 1 }, //fs: + { 0x65, 1 }, //gs: + { 0x68, 5 }, //push dword + { 0x6a, 2 }, //push byte + { 0x74, 2, RelNear, 0x0f84 }, //je near -> je far + { 0x75, 2, RelNear, 0x0f85 }, //jne near -> jne far + { 0x89, 2 }, //mov reg,reg + { 0x8b, 2 }, //mov reg,reg + { 0x90, 1 }, //nop + { 0xa1, 5 }, //mov eax,[dword] + { 0xeb, 2, RelNear, 0xe9 }, //jmp near -> jmp far +}; + +bool detour::insert(const string& moduleName, const string& functionName, void*& source, void* target) { + HMODULE module = GetModuleHandleW(utf16_t(moduleName)); + if(!module) return false; + + uint8_t* sourceData = (uint8_t*)GetProcAddress(module, functionName); + if(!sourceData) return false; + + unsigned sourceLength = detour::length(sourceData); + if(sourceLength < 5) { + //unable to clone enough bytes to insert hook + #if 1 + string output = { "detour::insert(", moduleName, "::", functionName, ") failed: " }; + for(unsigned n = 0; n < 16; n++) output.append(hex<2>(sourceData[n]), " "); + output.rtrim<1>(" "); + MessageBoxA(0, output, "nall::detour", MB_OK); + #endif + return false; + } + + uint8_t* mirrorData = new uint8_t[512](); + detour::mirror(mirrorData, sourceData); + + DWORD privileges; + VirtualProtect((void*)mirrorData, 512, PAGE_EXECUTE_READWRITE, &privileges); + VirtualProtect((void*)sourceData, 256, PAGE_EXECUTE_READWRITE, &privileges); + uintmax_t address = (uintmax_t)target - ((uintmax_t)sourceData + 5); + sourceData[0] = 0xe9; //jmp target + sourceData[1] = address >> 0; + sourceData[2] = address >> 8; + sourceData[3] = address >> 16; + sourceData[4] = address >> 24; + VirtualProtect((void*)sourceData, 256, privileges, &privileges); + + source = (void*)mirrorData; + return true; +} + +bool detour::remove(const string& moduleName, const string& functionName, void*& source) { + HMODULE module = GetModuleHandleW(utf16_t(moduleName)); + if(!module) return false; + + uint8_t* sourceData = (uint8_t*)GetProcAddress(module, functionName); + if(!sourceData) return false; + + uint8_t* mirrorData = (uint8_t*)source; + if(mirrorData == sourceData) return false; //hook was never installed + + unsigned length = detour::length(256 + mirrorData); + if(length < 5) return false; + + DWORD privileges; + VirtualProtect((void*)sourceData, 256, PAGE_EXECUTE_READWRITE, &privileges); + for(unsigned n = 0; n < length; n++) sourceData[n] = mirrorData[256 + n]; + VirtualProtect((void*)sourceData, 256, privileges, &privileges); + + source = (void*)sourceData; + delete[] mirrorData; + return true; +} + +unsigned detour::length(const uint8_t* function) { + unsigned length = 0; + while(length < 5) { + detour::opcode *opcode = 0; + foreach(op, detour::opcodes) { + if(function[length] == op.prefix) { + opcode = &op; + break; + } + } + if(opcode == 0) break; + length += opcode->length; + } + return length; +} + +unsigned detour::mirror(uint8_t* target, const uint8_t* source) { + const uint8_t *entryPoint = source; + for(unsigned n = 0; n < 256; n++) target[256 + n] = source[n]; + + unsigned size = detour::length(source); + while(size) { + detour::opcode *opcode = 0; + foreach(op, detour::opcodes) { + if(*source == op.prefix) { + opcode = &op; + break; + } + } + + switch(opcode->mode) { + case Copy: + for(unsigned n = 0; n < opcode->length; n++) *target++ = *source++; + break; + case RelNear: { + source++; + uintmax_t sourceAddress = (uintmax_t)source + 1 + (int8_t)*source; + *target++ = opcode->modify; + if(opcode->modify >> 8) *target++ = opcode->modify >> 8; + uintmax_t targetAddress = (uintmax_t)target + 4; + uintmax_t address = sourceAddress - targetAddress; + *target++ = address >> 0; + *target++ = address >> 8; + *target++ = address >> 16; + *target++ = address >> 24; + source += 2; + } break; + } + + size -= opcode->length; + } + + uintmax_t address = (entryPoint + detour::length(entryPoint)) - (target + 5); + *target++ = 0xe9; //jmp entryPoint + *target++ = address >> 0; + *target++ = address >> 8; + *target++ = address >> 16; + *target++ = address >> 24; + + return source - entryPoint; +} + +#undef Implied +#undef RelNear + +} + +#endif diff --git a/src/dep/libs/nall/windows/guid.hpp b/src/dep/libs/nall/windows/guid.hpp new file mode 100755 index 000000000..adee891d0 --- /dev/null +++ b/src/dep/libs/nall/windows/guid.hpp @@ -0,0 +1,30 @@ +#ifndef NALL_WINDOWS_GUID_HPP +#define NALL_WINDOWS_GUID_HPP + +#include +#include + +namespace nall { + +//generate unique GUID +inline string guid() { + random_lfsr lfsr; + lfsr.seed(time(nullptr)); + for(unsigned n = 0; n < 256; n++) lfsr(); + + string output; + for(unsigned n = 0; n < 4; n++) output.append(hex<2>(lfsr())); + output.append("-"); + for(unsigned n = 0; n < 2; n++) output.append(hex<2>(lfsr())); + output.append("-"); + for(unsigned n = 0; n < 2; n++) output.append(hex<2>(lfsr())); + output.append("-"); + for(unsigned n = 0; n < 2; n++) output.append(hex<2>(lfsr())); + output.append("-"); + for(unsigned n = 0; n < 6; n++) output.append(hex<2>(lfsr())); + return {"{", output, "}"}; +} + +} + +#endif diff --git a/src/dep/libs/nall/windows/launcher.hpp b/src/dep/libs/nall/windows/launcher.hpp new file mode 100755 index 000000000..7ff8e9392 --- /dev/null +++ b/src/dep/libs/nall/windows/launcher.hpp @@ -0,0 +1,94 @@ +#ifndef NALL_WINDOWS_LAUNCHER_HPP +#define NALL_WINDOWS_LAUNCHER_HPP + +namespace nall { + +//launch a new process and inject specified DLL into it + +bool launch(const char* applicationName, const char* libraryName, uint32_t entryPoint) { + //if a launcher does not send at least one message, a wait cursor will appear + PostThreadMessage(GetCurrentThreadId(), WM_USER, 0, 0); + MSG msg; + GetMessage(&msg, 0, 0, 0); + + STARTUPINFOW si; + PROCESS_INFORMATION pi; + + memset(&si, 0, sizeof(STARTUPINFOW)); + BOOL result = CreateProcessW( + utf16_t(applicationName), GetCommandLineW(), NULL, NULL, TRUE, + DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS, //do not break if application creates its own processes + NULL, NULL, &si, &pi + ); + if(result == false) return false; + + uint8_t entryData[1024], entryHook[1024] = { + 0x68, 0x00, 0x00, 0x00, 0x00, //push libraryName + 0xb8, 0x00, 0x00, 0x00, 0x00, //mov eax,LoadLibraryW + 0xff, 0xd0, //call eax + 0xcd, 0x03, //int 3 + }; + + entryHook[1] = (uint8_t)((entryPoint + 14) >> 0); + entryHook[2] = (uint8_t)((entryPoint + 14) >> 8); + entryHook[3] = (uint8_t)((entryPoint + 14) >> 16); + entryHook[4] = (uint8_t)((entryPoint + 14) >> 24); + + uint32_t pLoadLibraryW = (uint32_t)GetProcAddress(GetModuleHandleW(L"kernel32"), "LoadLibraryW"); + entryHook[6] = pLoadLibraryW >> 0; + entryHook[7] = pLoadLibraryW >> 8; + entryHook[8] = pLoadLibraryW >> 16; + entryHook[9] = pLoadLibraryW >> 24; + + utf16_t buffer = utf16_t(libraryName); + memcpy(entryHook + 14, buffer, 2 * wcslen(buffer) + 2); + + while(true) { + DEBUG_EVENT event; + WaitForDebugEvent(&event, INFINITE); + + if(event.dwDebugEventCode == EXIT_PROCESS_DEBUG_EVENT) break; + + if(event.dwDebugEventCode == EXCEPTION_DEBUG_EVENT) { + if(event.u.Exception.ExceptionRecord.ExceptionCode == EXCEPTION_BREAKPOINT) { + if(event.u.Exception.ExceptionRecord.ExceptionAddress == (void*)(entryPoint + 14 - 1)) { + HANDLE hProcess = OpenProcess(0, FALSE, event.dwProcessId); + HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, event.dwThreadId); + + CONTEXT context; + context.ContextFlags = CONTEXT_FULL; + GetThreadContext(hThread, &context); + + WriteProcessMemory(pi.hProcess, (void*)entryPoint, (void*)&entryData, sizeof entryData, NULL); + context.Eip = entryPoint; + SetThreadContext(hThread, &context); + + CloseHandle(hThread); + CloseHandle(hProcess); + } + + ContinueDebugEvent(event.dwProcessId, event.dwThreadId, DBG_CONTINUE); + continue; + } + + ContinueDebugEvent(event.dwProcessId, event.dwThreadId, DBG_EXCEPTION_NOT_HANDLED); + continue; + } + + if(event.dwDebugEventCode == CREATE_PROCESS_DEBUG_EVENT) { + ReadProcessMemory(pi.hProcess, (void*)entryPoint, (void*)&entryData, sizeof entryData, NULL); + WriteProcessMemory(pi.hProcess, (void*)entryPoint, (void*)&entryHook, sizeof entryHook, NULL); + + ContinueDebugEvent(event.dwProcessId, event.dwThreadId, DBG_CONTINUE); + continue; + } + + ContinueDebugEvent(event.dwProcessId, event.dwThreadId, DBG_CONTINUE); + } + + return true; +} + +} + +#endif diff --git a/src/dep/libs/nall/windows/registry.hpp b/src/dep/libs/nall/windows/registry.hpp new file mode 100755 index 000000000..f702afe4f --- /dev/null +++ b/src/dep/libs/nall/windows/registry.hpp @@ -0,0 +1,121 @@ +#ifndef NALL_WINDOWS_REGISTRY_HPP +#define NALL_WINDOWS_REGISTRY_HPP + +#include +#include + +#include +#undef interface +#ifndef KEY_WOW64_64KEY + #define KEY_WOW64_64KEY 0x0100 +#endif +#ifndef KEY_WOW64_32KEY + #define KEY_WOW64_32KEY 0x0200 +#endif + +#ifndef NWR_FLAGS + #define NWR_FLAGS KEY_WOW64_64KEY +#endif + +#ifndef NWR_SIZE + #define NWR_SIZE 4096 +#endif + +namespace nall { + +struct registry { + static bool exists(const string& name) { + lstring part = name.split("/"); + HKEY handle, rootKey = root(part.take(0)); + string node = part.take(); + string path = part.merge("\\"); + if(RegOpenKeyExW(rootKey, utf16_t(path), 0, NWR_FLAGS | KEY_READ, &handle) == ERROR_SUCCESS) { + wchar_t data[NWR_SIZE] = L""; + DWORD size = NWR_SIZE * sizeof(wchar_t); + LONG result = RegQueryValueExW(handle, utf16_t(node), nullptr, nullptr, (LPBYTE)&data, (LPDWORD)&size); + RegCloseKey(handle); + if(result == ERROR_SUCCESS) return true; + } + return false; + } + + static string read(const string& name) { + lstring part = name.split("/"); + HKEY handle, rootKey = root(part.take(0)); + string node = part.take(); + string path = part.merge("\\"); + if(RegOpenKeyExW(rootKey, utf16_t(path), 0, NWR_FLAGS | KEY_READ, &handle) == ERROR_SUCCESS) { + wchar_t data[NWR_SIZE] = L""; + DWORD size = NWR_SIZE * sizeof(wchar_t); + LONG result = RegQueryValueExW(handle, utf16_t(node), nullptr, nullptr, (LPBYTE)&data, (LPDWORD)&size); + RegCloseKey(handle); + if(result == ERROR_SUCCESS) return (const char*)utf8_t(data); + } + return ""; + } + + static void write(const string& name, const string& data = "") { + lstring part = name.split("/"); + HKEY handle, rootKey = root(part.take(0)); + string node = part.take(), path; + DWORD disposition; + for(unsigned n = 0; n < part.size(); n++) { + path.append(part[n]); + if(RegCreateKeyExW(rootKey, utf16_t(path), 0, nullptr, 0, NWR_FLAGS | KEY_ALL_ACCESS, nullptr, &handle, &disposition) == ERROR_SUCCESS) { + if(n == part.size() - 1) { + RegSetValueExW(handle, utf16_t(node), 0, REG_SZ, (BYTE*)(wchar_t*)utf16_t(data), (data.length() + 1) * sizeof(wchar_t)); + } + RegCloseKey(handle); + } + path.append("\\"); + } + } + + static bool remove(const string& name) { + lstring part = name.split("/"); + HKEY rootKey = root(part.take(0)); + string node = part.take(); + string path = part.merge("\\"); + if(node.empty()) return SHDeleteKeyW(rootKey, utf16_t(path)) == ERROR_SUCCESS; + return SHDeleteValueW(rootKey, utf16_t(path), utf16_t(node)) == ERROR_SUCCESS; + } + + static lstring contents(const string& name) { + lstring part = name.split("/"), result; + HKEY handle, rootKey = root(part.take(0)); + part.remove(); + string path = part.merge("\\"); + if(RegOpenKeyExW(rootKey, utf16_t(path), 0, NWR_FLAGS | KEY_READ, &handle) == ERROR_SUCCESS) { + DWORD folders, nodes; + RegQueryInfoKey(handle, nullptr, nullptr, nullptr, &folders, nullptr, nullptr, &nodes, nullptr, nullptr, nullptr, nullptr); + for(unsigned n = 0; n < folders; n++) { + wchar_t name[NWR_SIZE] = L""; + DWORD size = NWR_SIZE * sizeof(wchar_t); + RegEnumKeyEx(handle, n, (wchar_t*)&name, &size, nullptr, nullptr, nullptr, nullptr); + result.append({(const char*)utf8_t(name), "/"}); + } + for(unsigned n = 0; n < nodes; n++) { + wchar_t name[NWR_SIZE] = L""; + DWORD size = NWR_SIZE * sizeof(wchar_t); + RegEnumValueW(handle, n, (wchar_t*)&name, &size, nullptr, nullptr, nullptr, nullptr); + result.append((const char*)utf8_t(name)); + } + RegCloseKey(handle); + } + return result; + } + +private: + static HKEY root(const string& name) { + if(name == "HKCR") return HKEY_CLASSES_ROOT; + if(name == "HKCC") return HKEY_CURRENT_CONFIG; + if(name == "HKCU") return HKEY_CURRENT_USER; + if(name == "HKLM") return HKEY_LOCAL_MACHINE; + if(name == "HKU" ) return HKEY_USERS; + return nullptr; + } +}; + +} + +#endif diff --git a/src/dep/libs/nall/windows/utf8.hpp b/src/dep/libs/nall/windows/utf8.hpp new file mode 100755 index 000000000..dc3ea3bb8 --- /dev/null +++ b/src/dep/libs/nall/windows/utf8.hpp @@ -0,0 +1,89 @@ +#ifndef NALL_UTF8_HPP +#define NALL_UTF8_HPP + +//UTF-8 <> UTF-16 conversion +//used only for Win32; Linux, etc use UTF-8 internally + +#if defined(_WIN32) + +#undef UNICODE +#undef _WIN32_WINNT +#undef NOMINMAX +#define UNICODE +#define _WIN32_WINNT 0x0501 +#define NOMINMAX +#include +#include +#undef interface + +#if !defined(PATH_MAX) + #define PATH_MAX 260 +#endif + +namespace nall { + //UTF-8 to UTF-16 + struct utf16_t { + operator wchar_t*() { + return buffer; + } + + operator const wchar_t*() const { + return buffer; + } + + utf16_t(const char* s = "") { + if(!s) s = ""; + unsigned length = MultiByteToWideChar(CP_UTF8, 0, s, -1, nullptr, 0); + buffer = new wchar_t[length + 1](); + MultiByteToWideChar(CP_UTF8, 0, s, -1, buffer, length); + } + + ~utf16_t() { + delete[] buffer; + } + + private: + wchar_t* buffer; + }; + + //UTF-16 to UTF-8 + struct utf8_t { + operator char*() { + return buffer; + } + + operator const char*() const { + return buffer; + } + + utf8_t(const wchar_t* s = L"") { + if(!s) s = L""; + unsigned length = WideCharToMultiByte(CP_UTF8, 0, s, -1, nullptr, 0, nullptr, nullptr); + buffer = new char[length + 1](); + WideCharToMultiByte(CP_UTF8, 0, s, -1, buffer, length, nullptr, nullptr); + } + + ~utf8_t() { + delete[] buffer; + } + + utf8_t(const utf8_t&) = delete; + utf8_t& operator=(const utf8_t&) = delete; + + private: + char* buffer; + }; + + inline void utf8_args(int& argc, char**& argv) { + wchar_t** wargv = CommandLineToArgvW(GetCommandLineW(), &argc); + argv = new char*[argc]; + for(unsigned i = 0; i < argc; i++) { + argv[i] = new char[PATH_MAX]; + strcpy(argv[i], nall::utf8_t(wargv[i])); + } + } +} + +#endif //if defined(_WIN32) + +#endif diff --git a/src/dep/libs/nall/xorg/guard.hpp b/src/dep/libs/nall/xorg/guard.hpp new file mode 100755 index 000000000..9b5288662 --- /dev/null +++ b/src/dep/libs/nall/xorg/guard.hpp @@ -0,0 +1,31 @@ +#ifndef NALL_XORG_GUARD_HPP +#define NALL_XORG_GUARD_HPP + +#define None +#undef XlibNone +#define XlibNone 0L +#define Bool XlibBool +#define Button1 XlibButton1 +#define Button2 XlibButton2 +#define Button3 XlibButton3 +#define Button4 XlibButton4 +#define Button5 XlibButton5 +#define Display XlibDisplay +#define Screen XlibScreen +#define Window XlibWindow + +#else +#undef NALL_XORG_GUARD_HPP + +#undef None +#undef Bool +#undef Button1 +#undef Button2 +#undef Button3 +#undef Button4 +#undef Button5 +#undef Display +#undef Screen +#undef Window + +#endif diff --git a/src/dep/libs/nall/xorg/xorg.hpp b/src/dep/libs/nall/xorg/xorg.hpp new file mode 100755 index 000000000..bcf48b463 --- /dev/null +++ b/src/dep/libs/nall/xorg/xorg.hpp @@ -0,0 +1,12 @@ +#ifndef NALL_XORG_XORG_HPP +#define NALL_XORG_XORG_HPP + +#include +#include +#include +#include +#include +#include +#include + +#endif diff --git a/src/dep/libs/nall/zip.hpp b/src/dep/libs/nall/zip.hpp new file mode 100755 index 000000000..2c27a3288 --- /dev/null +++ b/src/dep/libs/nall/zip.hpp @@ -0,0 +1,95 @@ +#ifndef NALL_ZIP_HPP +#define NALL_ZIP_HPP + +//creates uncompressed ZIP archives + +#include +#include + +namespace nall { + +struct zip { + zip(const string& filename) { + fp.open(filename, file::mode::write); + time_t currentTime = time(nullptr); + tm* info = localtime(¤tTime); + dosTime = (info->tm_hour << 11) | (info->tm_min << 5) | (info->tm_sec >> 1); + dosDate = ((info->tm_year - 80) << 9) | ((1 + info->tm_mon) << 5) + (info->tm_mday); + } + + //append path: append("path/"); + //append file: append("path/file", data, size); + void append(string filename, const uint8_t* data = nullptr, unsigned size = 0u) { + filename.transform("\\", "/"); + uint32_t checksum = crc32_calculate(data, size); + directory.append({filename, checksum, size, fp.offset()}); + + fp.writel(0x04034b50, 4); //signature + fp.writel(0x0014, 2); //minimum version (2.0) + fp.writel(0x0000, 2); //general purpose bit flags + fp.writel(0x0000, 2); //compression method (0 = uncompressed) + fp.writel(dosTime, 2); + fp.writel(dosDate, 2); + fp.writel(checksum, 4); + fp.writel(size, 4); //compressed size + fp.writel(size, 4); //uncompressed size + fp.writel(filename.length(), 2); //file name length + fp.writel(0x0000, 2); //extra field length + fp.print(filename); //file name + + fp.write(data, size); //file data + } + + ~zip() { + //central directory + unsigned baseOffset = fp.offset(); + for(auto& entry : directory) { + fp.writel(0x02014b50, 4); //signature + fp.writel(0x0014, 2); //version made by (2.0) + fp.writel(0x0014, 2); //version needed to extract (2.0) + fp.writel(0x0000, 2); //general purpose bit flags + fp.writel(0x0000, 2); //compression method (0 = uncompressed) + fp.writel(dosTime, 2); + fp.writel(dosDate, 2); + fp.writel(entry.checksum, 4); + fp.writel(entry.size, 4); //compressed size + fp.writel(entry.size, 4); //uncompressed size + fp.writel(entry.filename.length(), 2); //file name length + fp.writel(0x0000, 2); //extra field length + fp.writel(0x0000, 2); //file comment length + fp.writel(0x0000, 2); //disk number start + fp.writel(0x0000, 2); //internal file attributes + fp.writel(0x00000000, 4); //external file attributes + fp.writel(entry.offset, 4); //relative offset of file header + fp.print(entry.filename); + } + unsigned finishOffset = fp.offset(); + + //end of central directory + fp.writel(0x06054b50, 4); //signature + fp.writel(0x0000, 2); //number of this disk + fp.writel(0x0000, 2); //disk where central directory starts + fp.writel(directory.size(), 2); //number of central directory records on this disk + fp.writel(directory.size(), 2); //total number of central directory records + fp.writel(finishOffset - baseOffset, 4); //size of central directory + fp.writel(baseOffset, 4); //offset of central directory + fp.writel(0x0000, 2); //comment length + + fp.close(); + } + +protected: + file fp; + uint16_t dosTime, dosDate; + struct entry_t { + string filename; + uint32_t checksum; + uint32_t size; + uint32_t offset; + }; + vector directory; +}; + +} + +#endif diff --git a/src/intf/audio/aud_interface.cpp b/src/intf/audio/aud_interface.cpp index b46de2005..a434c390b 100644 --- a/src/intf/audio/aud_interface.cpp +++ b/src/intf/audio/aud_interface.cpp @@ -23,6 +23,8 @@ static UINT32 nAudActive = 0; extern struct AudOut AudOutSDL; #elif defined (_XBOX) extern struct AudOut AudOutXAudio2; +#elif defined (BUILD_QT) + extern struct AudOut AudOutQtSound; #endif static struct AudOut *pAudOut[]= @@ -34,6 +36,8 @@ static struct AudOut *pAudOut[]= &AudOutSDL, #elif defined (_XBOX) &AudOutXAudio2, +#elif defined (BUILD_QT) + &AudOutQtSound, #endif }; diff --git a/src/intf/input/inp_interface.cpp b/src/intf/input/inp_interface.cpp index 16250d22e..2c90f2669 100644 --- a/src/intf/input/inp_interface.cpp +++ b/src/intf/input/inp_interface.cpp @@ -13,6 +13,8 @@ static bool bCinpOkay; extern struct InputInOut InputInOutSDL; #elif defined (_XBOX) extern struct InputInOut InputInOutXInput2; +#elif defined (BUILD_QT) + extern struct InputInOut InputInOutQt; #endif static struct InputInOut *pInputInOut[]= @@ -23,6 +25,8 @@ static struct InputInOut *pInputInOut[]= &InputInOutSDL, #elif defined (_XBOX) &InputInOutXInput2, +#elif defined (BUILD_QT) + &InputInOutQt, #endif }; diff --git a/src/intf/video/vid_interface.cpp b/src/intf/video/vid_interface.cpp index 360cf1dea..417742cba 100644 --- a/src/intf/video/vid_interface.cpp +++ b/src/intf/video/vid_interface.cpp @@ -17,6 +17,8 @@ extern struct VidOut VidOutSDLFX; #elif defined (_XBOX) extern struct VidOut VidOutD3D; +#elif defined (BUILD_QT) + extern struct VidOut VidRuby; #endif static struct VidOut *pVidOut[] = { @@ -31,6 +33,8 @@ static struct VidOut *pVidOut[] = { &VidOutSDLFX, #elif defined (_XBOX) &VidOutD3D, +#elif defined (BUILD_QT) + &VidRuby, #endif };