all current vba-m Wx patches applied to trunk.
git-svn-id: https://svn.code.sf.net/p/vbam/code/trunk@1021 a31d4220-a93d-0410-bf67-fe4944624d44
This commit is contained in:
parent
314f860bfb
commit
7a19748d88
|
@ -8,6 +8,7 @@ if( COMMAND cmake_policy )
|
|||
endif( COMMAND cmake_policy )
|
||||
SET( CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/CMakeScripts )
|
||||
|
||||
option( ENABLE_WX "Build the wxWidgets port" ON )
|
||||
option( ENABLE_SDL "Build the SDL port" ON )
|
||||
option( ENABLE_GTK "Build the GTK+ GUI" ON )
|
||||
option( ENABLE_DEBUGGER "Enable the debugger" ON )
|
||||
|
@ -63,6 +64,17 @@ FIND_PACKAGE ( SDL REQUIRED )
|
|||
if( ENABLE_LINK )
|
||||
FIND_PACKAGE ( SFML REQUIRED )
|
||||
endif( ENABLE_LINK )
|
||||
# set the standard libraries all ports use
|
||||
SET(VBAMCORE_LIBS
|
||||
vbamcore
|
||||
fex
|
||||
${SDL_LIBRARY}
|
||||
${SFML_LIBRARY}
|
||||
${OPENGL_LIBRARIES}
|
||||
${ZLIB_LIBRARY}
|
||||
${PNG_LIBRARY})
|
||||
|
||||
|
||||
|
||||
# Disable looking for GTK if not going to build the GTK frontend
|
||||
# so that pkg-config is not required
|
||||
|
@ -111,9 +123,20 @@ ENDIF( NOT SYSCONFDIR )
|
|||
ADD_DEFINITIONS (-DHAVE_NETINET_IN_H -DHAVE_ARPA_INET_H -DHAVE_ZLIB_H -DFINAL_VERSION -DSDL -DUSE_OPENGL -DSYSCONFDIR='"${SYSCONFDIR}"' -DWITH_LIRC='${WITHLIRC}')
|
||||
ADD_DEFINITIONS (-DVERSION='"${VERSION}"' -DPKGDATADIR='"${PKGDATADIR}"' -DPACKAGE='')
|
||||
|
||||
if( NOT ENABLE_LINK )
|
||||
if( ENABLE_LINK )
|
||||
FIND_LIBRARY(RT_LIB rt)
|
||||
IF(RT_LIB)
|
||||
SET(CMAKE_REQUIRED_LIBRARIES ${RT_LIB})
|
||||
SET(VBAMCORE_LIBS ${VBAMCORE_LIBS} ${RT_LIB})
|
||||
ENDIF(RT_LIB)
|
||||
INCLUDE(CheckFunctionExists)
|
||||
CHECK_FUNCTION_EXISTS(sem_timedwait SEM_TIMEDWAIT)
|
||||
IF( SEM_TIMEDWAIT)
|
||||
ADD_DEFINITIONS (-DHAVE_SEM_TIMEDWAIT)
|
||||
ENDIF( SEM_TIMEDWAIT)
|
||||
else( ENABLE_LINK )
|
||||
ADD_DEFINITIONS (-DNO_LINK)
|
||||
endif( NOT ENABLE_LINK )
|
||||
endif( ENABLE_LINK )
|
||||
|
||||
# The debugger is enabled by default
|
||||
if( NOT ENABLE_DEBUGGER )
|
||||
|
@ -132,6 +155,21 @@ if( ENABLE_NLS )
|
|||
SET( LOCALEDIR ${CMAKE_INSTALL_PREFIX}/share/locale )
|
||||
ADD_DEFINITIONS ( -DENABLE_NLS )
|
||||
ADD_DEFINITIONS ( -DLOCALEDIR=\\\"${LOCALEDIR}\\\" )
|
||||
# for now, only GBALink.cpp uses gettext() directly
|
||||
IF(ENABLE_LINK)
|
||||
FIND_PATH(LIBINTL_INC libintl.h )
|
||||
FIND_LIBRARY(LIBINTL_LIB intl )
|
||||
IF(LIBINTL_LIB)
|
||||
SET(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} ${LIBINTL_LIB})
|
||||
SET(VBAMCORE_LIBS ${VBAMCORE_LIBS} ${LIBINTL_LIB})
|
||||
ENDIF(LIBINTL_LIB)
|
||||
INCLUDE(CheckFunctionExists)
|
||||
CHECK_FUNCTION_EXISTS(gettext GETTEXT_FN)
|
||||
IF(NOT LIBINTL_INC OR NOT GETTEXT_FN)
|
||||
message( SEND_ERROR "NLS requires libintl" )
|
||||
ENDIF(NOT LIBINTL_INC OR NOT GETTEXT_FN)
|
||||
INCLUDE_DIRECTORIES(${LIBINTL_INC})
|
||||
ENDIF(ENABLE_LINK)
|
||||
endif( ENABLE_NLS )
|
||||
|
||||
# Compiler flags
|
||||
|
@ -256,6 +294,7 @@ SET(SRC_FILTERS
|
|||
src/filters/interframe.cpp
|
||||
src/filters/pixel.cpp
|
||||
src/filters/scanline.cpp
|
||||
src/filters/simpleFilter.cpp
|
||||
)
|
||||
|
||||
SET(SRC_HQ_C
|
||||
|
@ -270,6 +309,13 @@ SET(SRC_HQ_ASM
|
|||
src/filters/hq/asm/hq3x32.cpp
|
||||
)
|
||||
|
||||
if( ENABLE_ASM_SCALERS )
|
||||
SET(SRC_FILTERS ${SRC_FILTERS} ${SRC_HQ_ASM})
|
||||
else( ENABLE_ASM_SCALERS )
|
||||
SET(SRC_FILTERS ${SRC_FILTERS} ${SRC_HQ_C})
|
||||
ADD_DEFINITIONS ( -DNO_ASM )
|
||||
endif( ENABLE_ASM_SCALERS )
|
||||
|
||||
SET(SRC_GTK
|
||||
src/gtk/configfile.cpp
|
||||
src/gtk/main.cpp
|
||||
|
@ -303,12 +349,6 @@ if( ENABLE_DEBUGGER )
|
|||
)
|
||||
endif( ENABLE_DEBUGGER )
|
||||
|
||||
if( ENABLE_ASM_SCALERS )
|
||||
SET(SRC_HQ ${SRC_HQ_ASM})
|
||||
else( ENABLE_ASM_SCALERS )
|
||||
SET(SRC_HQ ${SRC_HQ_C})
|
||||
endif( ENABLE_ASM_SCALERS )
|
||||
|
||||
INCLUDE_DIRECTORIES(
|
||||
${ZLIB_INCLUDE_DIR}
|
||||
fex
|
||||
|
@ -354,7 +394,6 @@ IF( ENABLE_SDL )
|
|||
vbam
|
||||
WIN32
|
||||
${SRC_SDL}
|
||||
${SRC_HQ}
|
||||
)
|
||||
|
||||
IF( WIN32 )
|
||||
|
@ -367,15 +406,9 @@ IF( ENABLE_SDL )
|
|||
|
||||
TARGET_LINK_LIBRARIES (
|
||||
vbam
|
||||
vbamcore
|
||||
${SDL_LIBRARY}
|
||||
${ZLIB_LIBRARY}
|
||||
${PNG_LIBRARY}
|
||||
${OPENGL_LIBRARY}
|
||||
${VBAMCORE_LIBS}
|
||||
${WIN32_LIBRARIES}
|
||||
${LIRC_CLIENT_LIBRARY}
|
||||
${SFML_LIBRARY}
|
||||
fex
|
||||
)
|
||||
|
||||
INSTALL(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/vbam DESTINATION bin)
|
||||
|
@ -394,14 +427,9 @@ IF( ENABLE_GTK )
|
|||
|
||||
TARGET_LINK_LIBRARIES (
|
||||
gvbam
|
||||
vbamcore
|
||||
${ZLIB_LIBRARY}
|
||||
${PNG_LIBRARY}
|
||||
${SDL_LIBRARY}
|
||||
${VBAMCORE_LIBS}
|
||||
${GTKMM_LIBRARIES}
|
||||
${GTKGLMM_LIBRARIES}
|
||||
${SFML_LIBRARY}
|
||||
fex
|
||||
)
|
||||
|
||||
INSTALL(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/gvbam DESTINATION bin)
|
||||
|
@ -410,8 +438,20 @@ IF( ENABLE_GTK )
|
|||
INSTALL(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/src/gtk/ui DESTINATION ${DATA_INSTALL_DIR} PATTERN ".svn" EXCLUDE)
|
||||
INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/src/vba-over.ini DESTINATION ${DATA_INSTALL_DIR})
|
||||
|
||||
ENDIF( ENABLE_GTK )
|
||||
|
||||
IF( ENABLE_WX )
|
||||
# since this has generated source files, it's easier to just
|
||||
# make from the subdir
|
||||
# otherwise out-of-tree builds have trouble
|
||||
|
||||
add_subdirectory(src/wx)
|
||||
|
||||
ENDIF( ENABLE_WX )
|
||||
|
||||
if( ENABLE_GTK OR ENABLE_WX )
|
||||
# Native Language Support
|
||||
if( ENABLE_NLS )
|
||||
add_subdirectory(po)
|
||||
endif( ENABLE_NLS )
|
||||
ENDIF( ENABLE_GTK )
|
||||
endif( ENABLE_GTK OR ENABLE_WX )
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
# -*- cmake -*-
|
||||
|
||||
# mostly taken from emeraldviewer
|
||||
if (WIN32)
|
||||
find_path(DIRECTX_INCLUDE_DIR dxdiag.h
|
||||
"$ENV{DXSDK_DIR}/Include"
|
||||
"$ENV{PROGRAMFILES}/Microsoft DirectX SDK (March 2009)/Include"
|
||||
"$ENV{PROGRAMFILES}/Microsoft DirectX SDK (August 2008)/Include"
|
||||
"$ENV{PROGRAMFILES}/Microsoft DirectX SDK (June 2008)/Include"
|
||||
"$ENV{PROGRAMFILES}/Microsoft DirectX SDK (March 2008)/Include"
|
||||
"$ENV{PROGRAMFILES}/Microsoft DirectX SDK (November 2007)/Include"
|
||||
"$ENV{PROGRAMFILES}/Microsoft DirectX SDK (August 2007)/Include"
|
||||
"C:/DX90SDK/Include"
|
||||
"$ENV{PROGRAMFILES}/DX90SDK/Include"
|
||||
)
|
||||
if (DIRECTX_INCLUDE_DIR)
|
||||
include_directories(${DIRECTX_INCLUDE_DIR})
|
||||
if (DIRECTX_FIND_QUIETLY)
|
||||
message(STATUS "Found DirectX include: ${DIRECTX_INCLUDE_DIR}")
|
||||
endif (DIRECTX_FIND_QUIETLY)
|
||||
else (DIRECTX_INCLUDE_DIR)
|
||||
message(FATAL_ERROR "Could not find DirectX SDK Include")
|
||||
endif (DIRECTX_INCLUDE_DIR)
|
||||
|
||||
|
||||
find_path(DIRECTX_LIBRARY_DIR dxguid.lib
|
||||
"$ENV{DXSDK_DIR}/Lib/x86"
|
||||
"$ENV{PROGRAMFILES}/Microsoft DirectX SDK (March 2009)/Lib/x86"
|
||||
"$ENV{PROGRAMFILES}/Microsoft DirectX SDK (August 2008)/Lib/x86"
|
||||
"$ENV{PROGRAMFILES}/Microsoft DirectX SDK (June 2008)/Lib/x86"
|
||||
"$ENV{PROGRAMFILES}/Microsoft DirectX SDK (March 2008)/Lib/x86"
|
||||
"$ENV{PROGRAMFILES}/Microsoft DirectX SDK (November 2007)/Lib/x86"
|
||||
"$ENV{PROGRAMFILES}/Microsoft DirectX SDK (August 2007)/Lib/x86"
|
||||
"C:/DX90SDK/Lib"
|
||||
"$ENV{PROGRAMFILES}/DX90SDK/Lib"
|
||||
)
|
||||
if (DIRECTX_LIBRARY_DIR)
|
||||
if (DIRECTX_FIND_QUIETLY)
|
||||
message(STATUS "Found DirectX include: ${DIRECTX_LIBRARY_DIR}")
|
||||
endif (DIRECTX_FIND_QUIETLY)
|
||||
else (DIRECTX_LIBRARY_DIR)
|
||||
message(FATAL_ERROR "Could not find DirectX SDK Libraries")
|
||||
endif (DIRECTX_LIBRARY_DIR)
|
||||
|
||||
endif (WIN32)
|
||||
|
||||
INCLUDE(FindPackageHandleStandardArgs)
|
||||
FIND_PACKAGE_HANDLE_STANDARD_ARGS(DirectX DEFAULT_MSG DIRECTX_LIBRARY_DIR DIRECTX_INCLUDE_DIR)
|
|
@ -1,3 +1,9 @@
|
|||
vbam (1.8.0.1016-1) unstable; urgency=low
|
||||
|
||||
* Use new version from upstream, with wxwidgets patches
|
||||
|
||||
-- Thomas J. Moore <darktjm@gmail.com> Sun, 22 May 2011 14:08:05 -0500
|
||||
|
||||
vbam (1.8.0.1001-1) unstable; urgency=low
|
||||
|
||||
* Use new version from upstream
|
||||
|
|
|
@ -2,7 +2,7 @@ Source: vbam
|
|||
Section: otherosfs
|
||||
Priority: optional
|
||||
Maintainer: Fernando Tarlá Cardoso Lemos <fernandotcl@gmail.com>
|
||||
Build-Depends: debhelper (>= 7), cmake, nasm [i386 amd64], libsdl1.2-dev, libgl-dev, libgtkmm-2.4-dev, libgtkglextmm-x11-1.2-dev
|
||||
Build-Depends: debhelper (>= 7), cmake, nasm [i386 amd64], libsdl1.2-dev, libgl-dev, libgtkmm-2.4-dev, libgtkglextmm-x11-1.2-dev, libavcodec-dev, libavformat-dev, libswscale-dev, libavutil-dev, wx-common, libcairo2-dev, liblircclient-dev, libopenal-dev, libwxgtk2.8-dev
|
||||
Standards-Version: 3.8.1
|
||||
Homepage: http://www.vbam.com
|
||||
|
||||
|
@ -17,6 +17,7 @@ Description: Nintendo Game Boy Advance emulator
|
|||
.
|
||||
This package does not provide a GUI version of VisualBoyAdvance-M.
|
||||
See the vbam-gtk package for the GTK+ version of this program.
|
||||
See the vbam-wx package for the wxWidgets version of this program.
|
||||
|
||||
Package: vbam-gtk
|
||||
Architecture: any
|
||||
|
@ -27,4 +28,16 @@ Description: Nintendo Game Boy Advance emulator (GTK+ frontend)
|
|||
Boy Advance handheld console, in addition to the original Game Boy
|
||||
handhelds and its Super and Color variants.
|
||||
.
|
||||
This package provides the GUI version of VisualBoyAdvance-M.
|
||||
This package provides the GTK+ GUI version of VisualBoyAdvance-M.
|
||||
|
||||
Package: vbam-wx
|
||||
Architecture: any
|
||||
Depends: ${shlibs:Depends}, ${misc:Depends}
|
||||
Description: Nintendo Game Boy Advance emulator (GTK+ frontend)
|
||||
VisualBoyAdvance-M is a Nintendo Game Boy Emulator with high
|
||||
compatibility with commercial games. It emulates the Nintendo Game
|
||||
Boy Advance handheld console, in addition to the original Game Boy
|
||||
handhelds and its Super and Color variants.
|
||||
.
|
||||
This package provides the wxWidgets GUI version of VisualBoyAdvance-M.
|
||||
|
||||
|
|
|
@ -23,16 +23,23 @@ endif
|
|||
|
||||
builddir/Makefile:
|
||||
dh_testdir
|
||||
mkdir -p builddir
|
||||
mkdir -p builddir builddir-wx
|
||||
cd builddir && \
|
||||
cmake .. -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_C_FLAGS="$(CFLAGS)" \
|
||||
-DCMAKE_LD_FLAGS="-Wl,-z,defs" -DCMAKE_CXX_FLAGS="$(CXXFLAGS)" \
|
||||
-DENABLE_LIRC=ON -DENABLE_WX=OFF \
|
||||
-DCMAKE_SKIP_RPATH=ON $(EXTRA_CMAKE_FLAGS)
|
||||
cd builddir-wx && \
|
||||
cmake .. -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_C_FLAGS="$(CFLAGS)" \
|
||||
-DCMAKE_LD_FLAGS="-Wl,-z,defs" -DCMAKE_CXX_FLAGS="$(CXXFLAGS)" \
|
||||
-DENABLE_LINK=ON -DENABLE_WX=ON -DENABLE_SDL=OFF -DENABLE_GTK=OFF\
|
||||
-DCMAKE_SKIP_RPATH=ON $(EXTRA_CMAKE_FLAGS)
|
||||
|
||||
build: build-stamp
|
||||
|
||||
build-stamp: builddir/Makefile
|
||||
dh_testdir
|
||||
$(MAKE) -C builddir-wx
|
||||
$(MAKE) -C builddir
|
||||
touch $@
|
||||
|
||||
|
@ -40,7 +47,7 @@ clean:
|
|||
dh_testdir
|
||||
dh_testroot
|
||||
rm -f build-stamp
|
||||
rm -rf builddir
|
||||
rm -rf builddir builddir-wx
|
||||
dh_clean
|
||||
|
||||
install: build
|
||||
|
@ -49,6 +56,7 @@ install: build
|
|||
dh_prep
|
||||
dh_installdirs
|
||||
$(MAKE) -C builddir DESTDIR=$(CURDIR)/debian/tmp install
|
||||
$(MAKE) -C builddir-wx DESTDIR=$(CURDIR)/debian/tmp install
|
||||
|
||||
binary-indep: install
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
usr/bin/gvbam
|
||||
usr/share/icons
|
||||
usr/share/vbam
|
||||
usr/share/applications
|
||||
usr/share/locale/*/*/gvbam.*
|
||||
usr/share/applications/gvbam.desktop
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
doc/DevInfo.txt
|
||||
doc/ips.htm
|
|
@ -0,0 +1,4 @@
|
|||
usr/bin/wxvbam
|
||||
usr/share/icons
|
||||
usr/share/locale/*/*/wxvbam.*
|
||||
usr/share/applications/wxvbam.desktop
|
|
@ -0,0 +1,2 @@
|
|||
?package(vbam-wx):needs="X11" section="Applications/Emulators"\
|
||||
title="VisualBoyAdvance-M" command="/usr/bin/wxvbam"
|
|
@ -1,2 +1,47 @@
|
|||
GETTEXT_CREATE_TRANSLATIONS(gvbam.pot gvbam/cs.po gvbam/de.po gvbam/en.po gvbam/es_ES.po gvbam/fr.po gvbam/pt_BR.po gvbam/zh_CN.po)
|
||||
ADD_DEPENDENCIES(gvbam translations)
|
||||
IF(ENABLE_GTK)
|
||||
GETTEXT_CREATE_TRANSLATIONS(gvbam.pot gvbam/cs.po gvbam/de.po gvbam/en.po gvbam/es_ES.po gvbam/fr.po gvbam/pt_BR.po gvbam/zh_CN.po)
|
||||
ADD_DEPENDENCIES(gvbam translations)
|
||||
ENDIF(ENABLE_GTK)
|
||||
IF(ENABLE_WX)
|
||||
# Extract message strings from xrc and source files (NLS only for 2nd cmd)
|
||||
ADD_CUSTOM_COMMAND(OUTPUT wx-xrc-strings.h
|
||||
COMMAND wxrc -g ../src/wx/wxvbam.xrc -o ${CMAKE_CURRENT_BINARY_DIR}/wx-xrc-strings.h
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
DEPENDS ../src/wx/wxvbam.xrc)
|
||||
SET(XGETTEXTCMD ${XGETTEXT} -k_ -kN_ -kwxTRANSLATE -s --copyright-holder=\"VBA-M development team\" --package-name=VBA-M --package-version=${VERSION})
|
||||
# note that GLOB wil return absolute paths unless otherwise requested
|
||||
# and it runs in CMAKE_CURRENT_SOURCE_DIR
|
||||
FILE(GLOB PO_SRC_FILES ../src/wx/*.cpp ../src/wx/*.h ../src/wx/widgets/*.cpp
|
||||
../src/Util.cpp ../src/gba/*.cpp ../src/gb/*.cpp)
|
||||
ADD_CUSTOM_COMMAND(OUTPUT wxvbam.pot
|
||||
COMMAND ${XGETTEXTCMD} -o wxvbam.pot ${PO_SRC_FILES}
|
||||
COMMAND ${XGETTEXTCMD} -o wxvbam.pot ../src/wx/cmdtab.cpp
|
||||
COMMAND ${XGETTEXTCMD} --from-code=utf-8 -j -o wxvbam.pot wx-xrc-strings.h
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
|
||||
DEPENDS wx-xrc-strings.h) # there is no direct cmdtab.cpp rule
|
||||
|
||||
# is there really any point in auto-generating en?
|
||||
ADD_CUSTOM_COMMAND(OUTPUT wxvbam/en.po
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory wxvbam
|
||||
COMMAND ${MSGINIT} -i wxvbam.pot -o wxvbam/en.po --no-translator -l en_US.utf-8
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
|
||||
DEPENDS wxvbam.pot)
|
||||
|
||||
FILE(GLOB POFILES wxvbam/*.po)
|
||||
IF(NOT POFILES)
|
||||
SET(POFILES ${CMAKE_CURRENT_BINARY_DIR}/wxvbam/en.po)
|
||||
ENDIF(NOT POFILES)
|
||||
# only one GETTEXT_CREATE_TRANSLATIONS call can be made
|
||||
# probably need to simulate effects of call or force spearate cmake
|
||||
# invocations
|
||||
IF(NOT ENABLE_GTK)
|
||||
GETTEXT_CREATE_TRANSLATIONS(${CMAKE_CURRENT_BINARY_DIR}/wxvbam.pot ALL ${POFILES})
|
||||
# try to build wxvbam first so cmdtab is generated
|
||||
ADD_DEPENDENCIES(translations wxvbam)
|
||||
ELSE(NOT ENABLE_GTK)
|
||||
MESSAGE(WARNING "wxWidgets translations overridden by GTK.\nBuild again without GTK to correct.")
|
||||
ADD_CUSTOM_TARGET(extra_translations ALL DEPENDS ${POFILES})
|
||||
ADD_DEPENDENCIES(extra_translations wxvbam)
|
||||
ENDIF(NOT ENABLE_GTK)
|
||||
ENDIF(ENABLE_WX)
|
||||
|
||||
|
|
|
@ -68,7 +68,11 @@ extern void Sm60FPS_Init();
|
|||
extern bool Sm60FPS_CanSkipFrame();
|
||||
extern void Sm60FPS_Sleep();
|
||||
extern void DbgMsg(const char *msg, ...);
|
||||
#ifdef SDL
|
||||
#define winlog log
|
||||
#else
|
||||
extern void winlog(const char *,...);
|
||||
#endif
|
||||
|
||||
extern void (*dbgOutput)(const char *s, u32 addr);
|
||||
extern void (*dbgSignal)(int sig,int number);
|
||||
|
|
19
src/Util.cpp
19
src/Util.cpp
|
@ -436,12 +436,17 @@ static bool utilIsImage(const char *file)
|
|||
}
|
||||
|
||||
#ifdef WIN32
|
||||
#include <Windows.h>
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
IMAGE_TYPE utilFindType(const char *file)
|
||||
{
|
||||
char buffer [2048];
|
||||
utilFindType(file, buffer);
|
||||
}
|
||||
|
||||
IMAGE_TYPE utilFindType(const char *file, char (&buffer)[2048])
|
||||
{
|
||||
#ifdef WIN32
|
||||
DWORD dwNum = MultiByteToWideChar (CP_ACP, 0, file, -1, NULL, 0);
|
||||
wchar_t *pwText;
|
||||
|
@ -453,24 +458,24 @@ IMAGE_TYPE utilFindType(const char *file)
|
|||
MultiByteToWideChar (CP_ACP, 0, file, -1, pwText, dwNum );
|
||||
char* file_conv = fex_wide_to_path( pwText);
|
||||
delete []pwText;
|
||||
if ( !utilIsImage( file_conv ) ) // TODO: utilIsArchive() instead?
|
||||
{
|
||||
// if ( !utilIsImage( file_conv ) ) // TODO: utilIsArchive() instead?
|
||||
// {
|
||||
fex_t* fe = scan_arc(file_conv,utilIsImage,buffer);
|
||||
if(!fe)
|
||||
return IMAGE_UNKNOWN;
|
||||
fex_close(fe);
|
||||
file = buffer;
|
||||
}
|
||||
// }
|
||||
free(file_conv);
|
||||
#else
|
||||
if ( !utilIsImage( file ) ) // TODO: utilIsArchive() instead?
|
||||
{
|
||||
// if ( !utilIsImage( file ) ) // TODO: utilIsArchive() instead?
|
||||
// {
|
||||
fex_t* fe = scan_arc(file,utilIsImage,buffer);
|
||||
if(!fe)
|
||||
return IMAGE_UNKNOWN;
|
||||
fex_close(fe);
|
||||
file = buffer;
|
||||
}
|
||||
// }
|
||||
#endif
|
||||
return utilIsGBAImage(file) ? IMAGE_GBA : IMAGE_GB;
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ bool utilIsGBImage(const char *);
|
|||
bool utilIsGzipFile(const char *);
|
||||
void utilStripDoubleExtension(const char *, char *);
|
||||
IMAGE_TYPE utilFindType(const char *);
|
||||
IMAGE_TYPE utilFindType(const char *, char (&)[2048]);
|
||||
u8 *utilLoad(const char *, bool (*)(const char*), u8 *, int &);
|
||||
|
||||
void utilPutDword(u8 *, u32);
|
||||
|
|
|
@ -41,12 +41,12 @@ void InterframeCleanup()
|
|||
}
|
||||
|
||||
#ifdef MMX
|
||||
static void SmartIB_MMX(u8 *srcPtr, u32 srcPitch, int width, int height)
|
||||
static void SmartIB_MMX(u8 *srcPtr, u32 srcPitch, int width, int starty, int height)
|
||||
{
|
||||
u16 *src0 = (u16 *)srcPtr;
|
||||
u16 *src1 = (u16 *)frm1;
|
||||
u16 *src2 = (u16 *)frm2;
|
||||
u16 *src3 = (u16 *)frm3;
|
||||
u16 *src0 = (u16 *)srcPtr + starty * srcPitch / 2;
|
||||
u16 *src1 = (u16 *)frm1 + srcPitch * starty / 2;
|
||||
u16 *src2 = (u16 *)frm2 + srcPitch * starty / 2;
|
||||
u16 *src3 = (u16 *)frm3 + srcPitch * starty / 2;
|
||||
|
||||
int count = width >> 2;
|
||||
|
||||
|
@ -159,24 +159,24 @@ static void SmartIB_MMX(u8 *srcPtr, u32 srcPitch, int width, int height)
|
|||
}
|
||||
#endif
|
||||
|
||||
void SmartIB(u8 *srcPtr, u32 srcPitch, int width, int height)
|
||||
void SmartIB(u8 *srcPtr, u32 srcPitch, int width, int starty, int height)
|
||||
{
|
||||
if(frm1 == NULL) {
|
||||
Init();
|
||||
}
|
||||
#ifdef MMX
|
||||
if(cpu_mmx) {
|
||||
SmartIB_MMX(srcPtr, srcPitch, width, height);
|
||||
SmartIB_MMX(srcPtr, srcPitch, width, starty, height);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
u16 colorMask = ~RGB_LOW_BITS_MASK;
|
||||
|
||||
u16 *src0 = (u16 *)srcPtr;
|
||||
u16 *src1 = (u16 *)frm1;
|
||||
u16 *src2 = (u16 *)frm2;
|
||||
u16 *src3 = (u16 *)frm3;
|
||||
u16 *src0 = (u16 *)srcPtr + starty * srcPitch / 2;
|
||||
u16 *src1 = (u16 *)frm1 + srcPitch * starty / 2;
|
||||
u16 *src2 = (u16 *)frm2 + srcPitch * starty / 2;
|
||||
u16 *src3 = (u16 *)frm3 + srcPitch * starty / 2;
|
||||
|
||||
int sPitch = srcPitch >> 1;
|
||||
|
||||
|
@ -201,13 +201,18 @@ void SmartIB(u8 *srcPtr, u32 srcPitch, int width, int height)
|
|||
frm2 = temp;
|
||||
}
|
||||
|
||||
#ifdef MMX
|
||||
static void SmartIB32_MMX(u8 *srcPtr, u32 srcPitch, int width, int height)
|
||||
void SmartIB(u8 *srcPtr, u32 srcPitch, int width, int height)
|
||||
{
|
||||
u32 *src0 = (u32 *)srcPtr;
|
||||
u32 *src1 = (u32 *)frm1;
|
||||
u32 *src2 = (u32 *)frm2;
|
||||
u32 *src3 = (u32 *)frm3;
|
||||
SmartIB(srcPtr, srcPitch, width, 0, height);
|
||||
}
|
||||
|
||||
#ifdef MMX
|
||||
static void SmartIB32_MMX(u8 *srcPtr, u32 srcPitch, int width, int starty, int height)
|
||||
{
|
||||
u32 *src0 = (u32 *)srcPtr + starty * srcPitch / 4;
|
||||
u32 *src1 = (u32 *)frm1 + starty * srcPitch / 4;
|
||||
u32 *src2 = (u32 *)frm2 + starty * srcPitch / 4;
|
||||
u32 *src3 = (u32 *)frm3 + starty * srcPitch / 4;
|
||||
|
||||
int count = width >> 1;
|
||||
|
||||
|
@ -320,22 +325,22 @@ static void SmartIB32_MMX(u8 *srcPtr, u32 srcPitch, int width, int height)
|
|||
}
|
||||
#endif
|
||||
|
||||
void SmartIB32(u8 *srcPtr, u32 srcPitch, int width, int height)
|
||||
void SmartIB32(u8 *srcPtr, u32 srcPitch, int width, int starty, int height)
|
||||
{
|
||||
if(frm1 == NULL) {
|
||||
Init();
|
||||
}
|
||||
#ifdef MMX
|
||||
if(cpu_mmx) {
|
||||
SmartIB32_MMX(srcPtr, srcPitch, width, height);
|
||||
SmartIB32_MMX(srcPtr, srcPitch, width, starty, height);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
u32 *src0 = (u32 *)srcPtr;
|
||||
u32 *src1 = (u32 *)frm1;
|
||||
u32 *src2 = (u32 *)frm2;
|
||||
u32 *src3 = (u32 *)frm3;
|
||||
u32 *src0 = (u32 *)srcPtr + starty * srcPitch / 4;
|
||||
u32 *src1 = (u32 *)frm1 + starty * srcPitch / 4;
|
||||
u32 *src2 = (u32 *)frm2 + starty * srcPitch / 4;
|
||||
u32 *src3 = (u32 *)frm3 + starty * srcPitch / 4;
|
||||
|
||||
u32 colorMask = 0xfefefe;
|
||||
|
||||
|
@ -362,11 +367,16 @@ void SmartIB32(u8 *srcPtr, u32 srcPitch, int width, int height)
|
|||
frm2 = temp;
|
||||
}
|
||||
|
||||
#ifdef MMX
|
||||
static void MotionBlurIB_MMX(u8 *srcPtr, u32 srcPitch, int width, int height)
|
||||
void SmartIB32(u8 *srcPtr, u32 srcPitch, int width, int height)
|
||||
{
|
||||
u16 *src0 = (u16 *)srcPtr;
|
||||
u16 *src1 = (u16 *)frm1;
|
||||
SmartIB32(srcPtr, srcPitch, width, 0, height);
|
||||
}
|
||||
|
||||
#ifdef MMX
|
||||
static void MotionBlurIB_MMX(u8 *srcPtr, u32 srcPitch, int width, int starty, int height)
|
||||
{
|
||||
u16 *src0 = (u16 *)srcPtr + starty * srcPitch / 2;
|
||||
u16 *src1 = (u16 *)frm1 + starty * srcPitch / 2;
|
||||
|
||||
int count = width >> 2;
|
||||
|
||||
|
@ -431,7 +441,7 @@ static void MotionBlurIB_MMX(u8 *srcPtr, u32 srcPitch, int width, int height)
|
|||
}
|
||||
#endif
|
||||
|
||||
void MotionBlurIB(u8 *srcPtr, u32 srcPitch, int width, int height)
|
||||
void MotionBlurIB(u8 *srcPtr, u32 srcPitch, int width, int starty, int height)
|
||||
{
|
||||
if(frm1 == NULL) {
|
||||
Init();
|
||||
|
@ -439,15 +449,15 @@ void MotionBlurIB(u8 *srcPtr, u32 srcPitch, int width, int height)
|
|||
|
||||
#ifdef MMX
|
||||
if(cpu_mmx) {
|
||||
MotionBlurIB_MMX(srcPtr, srcPitch, width, height);
|
||||
MotionBlurIB_MMX(srcPtr, srcPitch, width, starty, height);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
u16 colorMask = ~RGB_LOW_BITS_MASK;
|
||||
|
||||
u16 *src0 = (u16 *)srcPtr;
|
||||
u16 *src1 = (u16 *)frm1;
|
||||
u16 *src0 = (u16 *)srcPtr + starty * srcPitch / 2;
|
||||
u16 *src1 = (u16 *)frm1 + starty * srcPitch / 2;
|
||||
|
||||
int sPitch = srcPitch >> 1;
|
||||
|
||||
|
@ -462,11 +472,16 @@ void MotionBlurIB(u8 *srcPtr, u32 srcPitch, int width, int height)
|
|||
}
|
||||
}
|
||||
|
||||
#ifdef MMX
|
||||
static void MotionBlurIB32_MMX(u8 *srcPtr, u32 srcPitch, int width, int height)
|
||||
void MotionBlurIB(u8 *srcPtr, u32 srcPitch, int width, int height)
|
||||
{
|
||||
u32 *src0 = (u32 *)srcPtr;
|
||||
u32 *src1 = (u32 *)frm1;
|
||||
MotionBlurIB(srcPtr, srcPitch, width, 0, height);
|
||||
}
|
||||
|
||||
#ifdef MMX
|
||||
static void MotionBlurIB32_MMX(u8 *srcPtr, u32 srcPitch, int width, int starty, int height)
|
||||
{
|
||||
u32 *src0 = (u32 *)srcPtr + starty * srcPitch / 4;
|
||||
u32 *src1 = (u32 *)frm1 + starty * srcPitch / 4;
|
||||
|
||||
int count = width >> 1;
|
||||
|
||||
|
@ -531,7 +546,7 @@ static void MotionBlurIB32_MMX(u8 *srcPtr, u32 srcPitch, int width, int height)
|
|||
}
|
||||
#endif
|
||||
|
||||
void MotionBlurIB32(u8 *srcPtr, u32 srcPitch, int width, int height)
|
||||
void MotionBlurIB32(u8 *srcPtr, u32 srcPitch, int width, int starty, int height)
|
||||
{
|
||||
if(frm1 == NULL) {
|
||||
Init();
|
||||
|
@ -539,13 +554,13 @@ void MotionBlurIB32(u8 *srcPtr, u32 srcPitch, int width, int height)
|
|||
|
||||
#ifdef MMX
|
||||
if(cpu_mmx) {
|
||||
MotionBlurIB32_MMX(srcPtr, srcPitch, width, height);
|
||||
MotionBlurIB32_MMX(srcPtr, srcPitch, width, starty, height);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
u32 *src0 = (u32 *)srcPtr;
|
||||
u32 *src1 = (u32 *)frm1;
|
||||
u32 *src0 = (u32 *)srcPtr + starty * srcPitch / 4;
|
||||
u32 *src1 = (u32 *)frm1 + starty * srcPitch / 4;
|
||||
|
||||
u32 colorMask = 0xfefefe;
|
||||
|
||||
|
@ -561,3 +576,8 @@ void MotionBlurIB32(u8 *srcPtr, u32 srcPitch, int width, int height)
|
|||
pos++;
|
||||
}
|
||||
}
|
||||
|
||||
void MotionBlurIB32(u8 *srcPtr, u32 srcPitch, int width, int height)
|
||||
{
|
||||
MotionBlurIB32(srcPtr, srcPitch, width, 0, height);
|
||||
}
|
||||
|
|
|
@ -21,7 +21,6 @@
|
|||
|
||||
extern u8 *pix;
|
||||
extern bool speedup;
|
||||
bool gbUpdateSizes();
|
||||
bool inBios = false;
|
||||
|
||||
// debugging
|
||||
|
|
|
@ -17,7 +17,12 @@ typedef union {
|
|||
u16 W;
|
||||
} gbRegister;
|
||||
|
||||
extern gbRegister AF, BC, DE, HL, SP, PC;
|
||||
extern u16 IFF;
|
||||
int gbDis(char *, u16);
|
||||
|
||||
bool gbLoadRom(const char *);
|
||||
bool gbUpdateSizes();
|
||||
void gbEmulate(int);
|
||||
void gbWriteMemory(register u16, register u8);
|
||||
void gbDrawLine();
|
||||
|
|
|
@ -27,11 +27,11 @@ bool genericflashcardEnable = false;
|
|||
int gbCgbMode = 0;
|
||||
|
||||
u16 gbColorFilter[32768];
|
||||
int gbColorOption = 0;
|
||||
bool gbColorOption = false;
|
||||
int gbPaletteOption = 0;
|
||||
int gbEmulatorType = 0;
|
||||
int gbBorderOn = 1;
|
||||
int gbBorderAutomatic = 0;
|
||||
bool gbBorderOn = true;
|
||||
bool gbBorderAutomatic = false;
|
||||
int gbBorderLineSkip = 160;
|
||||
int gbBorderRowSkip = 0;
|
||||
int gbBorderColumnSkip = 0;
|
||||
|
|
|
@ -25,11 +25,11 @@ extern u8 *gbMemoryMap[16];
|
|||
|
||||
extern int gbFrameSkip;
|
||||
extern u16 gbColorFilter[32768];
|
||||
extern int gbColorOption;
|
||||
extern bool gbColorOption;
|
||||
extern int gbPaletteOption;
|
||||
extern int gbEmulatorType;
|
||||
extern int gbBorderOn;
|
||||
extern int gbBorderAutomatic;
|
||||
extern bool gbBorderOn;
|
||||
extern bool gbBorderAutomatic;
|
||||
extern int gbCgbMode;
|
||||
extern int gbSgbMode;
|
||||
extern int gbWindowLine;
|
||||
|
|
|
@ -113,7 +113,6 @@ bool fxOn = false;
|
|||
bool windowOn = false;
|
||||
int frameCount = 0;
|
||||
char buffer[1024];
|
||||
FILE *out = NULL;
|
||||
u32 lastTime = 0;
|
||||
int count = 0;
|
||||
|
||||
|
@ -2826,27 +2825,12 @@ void CPUUpdateRegister(u32 address, u16 value)
|
|||
|
||||
case COMM_SIOCNT:
|
||||
StartLink(value);
|
||||
/*
|
||||
// old code path for no linking...
|
||||
{
|
||||
if (value & 0x80) {
|
||||
value &= 0xff7f;
|
||||
if ((value & 1) && (value & 0x4000)) {
|
||||
UPDATE_REG(COMM_SIODATA8, 0xFF);
|
||||
IF |= 0x80;
|
||||
UPDATE_REG(0x202, IF);
|
||||
value &= 0x7f7f;
|
||||
}
|
||||
}
|
||||
UPDATE_REG(COMM_SIOCNT, value);
|
||||
}
|
||||
*/
|
||||
break;
|
||||
|
||||
case COMM_SIODATA8:
|
||||
if (gba_link_enabled)
|
||||
LinkSSend(value);
|
||||
UPDATE_REG(COMM_RCNT, value);
|
||||
UPDATE_REG(COMM_SIODATA8, value);
|
||||
break;
|
||||
|
||||
case 0x130:
|
||||
|
@ -3452,27 +3436,6 @@ void CPUInterrupt()
|
|||
biosProtected[3] = 0xe5;
|
||||
}
|
||||
|
||||
#ifdef SDL
|
||||
void log(const char *defaultMsg, ...)
|
||||
{
|
||||
char buffer[2048];
|
||||
va_list valist;
|
||||
|
||||
va_start(valist, defaultMsg);
|
||||
vsprintf(buffer, defaultMsg, valist);
|
||||
|
||||
if(out == NULL) {
|
||||
out = fopen("trace.log","w");
|
||||
}
|
||||
|
||||
fputs(buffer, out);
|
||||
|
||||
va_end(valist);
|
||||
}
|
||||
#else
|
||||
extern void winlog(const char *, ...);
|
||||
#endif
|
||||
|
||||
void CPULoop(int ticks)
|
||||
{
|
||||
int clockTicks;
|
||||
|
@ -3498,30 +3461,20 @@ void CPULoop(int ticks)
|
|||
#ifdef BKPT_SUPPORT
|
||||
if (debugger_last)
|
||||
{
|
||||
sprintf(buffer, "R00=%08x R01=%08x R02=%08x R03=%08x R04=%08x R05=%08x R06=%08x R07=%08x R08=%08x R09=%08x R10=%08x R11=%08x R12=%08x R13=%08x R14=%08x R15=%08x R16=%08x R17=%08x\n",
|
||||
winlog("R00=%08x R01=%08x R02=%08x R03=%08x R04=%08x R05=%08x R06=%08x R07=%08x R08=%08x R09=%08x R10=%08x R11=%08x R12=%08x R13=%08x R14=%08x R15=%08x R16=%08x R17=%08x\n",
|
||||
oldreg[0], oldreg[1], oldreg[2], oldreg[3], oldreg[4], oldreg[5],
|
||||
oldreg[6], oldreg[7], oldreg[8], oldreg[9], oldreg[10], oldreg[11],
|
||||
oldreg[12], oldreg[13], oldreg[14], oldreg[15], oldreg[16],
|
||||
oldreg[17]);
|
||||
}
|
||||
#endif
|
||||
sprintf(buffer, "R00=%08x R01=%08x R02=%08x R03=%08x R04=%08x R05=%08x R06=%08x R07=%08x R08=%08x R09=%08x R10=%08x R11=%08x R12=%08x R13=%08x R14=%08x R15=%08x R16=%08x R17=%08x\n",
|
||||
winlog("R00=%08x R01=%08x R02=%08x R03=%08x R04=%08x R05=%08x R06=%08x R07=%08x R08=%08x R09=%08x R10=%08x R11=%08x R12=%08x R13=%08x R14=%08x R15=%08x R16=%08x R17=%08x\n",
|
||||
reg[0].I, reg[1].I, reg[2].I, reg[3].I, reg[4].I, reg[5].I,
|
||||
reg[6].I, reg[7].I, reg[8].I, reg[9].I, reg[10].I, reg[11].I,
|
||||
reg[12].I, reg[13].I, reg[14].I, reg[15].I, reg[16].I,
|
||||
reg[17].I);
|
||||
#ifdef SDL
|
||||
log(buffer);
|
||||
#else
|
||||
winlog(buffer);
|
||||
#endif
|
||||
} else if(!holdState) {
|
||||
sprintf(buffer, "PC=%08x\n", armNextPC);
|
||||
#ifdef SDL
|
||||
log(buffer);
|
||||
#else
|
||||
winlog(buffer);
|
||||
#endif
|
||||
winlog("PC=%08x\n", armNextPC);
|
||||
}
|
||||
}
|
||||
#endif /* FINAL_VERSION */
|
||||
|
|
1101
src/gba/GBALink.cpp
1101
src/gba/GBALink.cpp
File diff suppressed because it is too large
Load Diff
|
@ -1,10 +1,10 @@
|
|||
#ifndef GBA_GBALINK_H
|
||||
#define GBA_GBALINK_H
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef NO_LINK
|
||||
#include <SFML/Network.hpp>
|
||||
#endif
|
||||
// register definitions; these are always present
|
||||
|
||||
#define LINK_PARENTLOST 0x80
|
||||
#define UNSUPPORTED -1
|
||||
#define MULTIPLAYER 0
|
||||
#define NORMAL8 1
|
||||
|
@ -12,6 +12,7 @@
|
|||
#define UART 3
|
||||
#define JOYBUS 4
|
||||
#define GP 5
|
||||
|
||||
#define RFU_INIT 0
|
||||
#define RFU_COMM 1
|
||||
#define RFU_SEND 2
|
||||
|
@ -21,6 +22,11 @@
|
|||
#define COMM_SIODATA32_H 0x122
|
||||
#define COMM_SIOCNT 0x128
|
||||
#define COMM_SIODATA8 0x12a
|
||||
#define COMM_SIOMLT_SEND 0x12a
|
||||
#define COMM_SIOMULTI0 0x120
|
||||
#define COMM_SIOMULTI1 0x122
|
||||
#define COMM_SIOMULTI2 0x124
|
||||
#define COMM_SIOMULTI3 0x126
|
||||
#define COMM_RCNT 0x134
|
||||
#define COMM_JOYCNT 0x140
|
||||
#define COMM_JOY_RECV_L 0x150
|
||||
|
@ -45,13 +51,27 @@ enum
|
|||
JOY_CMD_WRITE = 0x15
|
||||
};
|
||||
|
||||
#ifdef _MSC_VER
|
||||
// Link implementation; only present if enabled
|
||||
#ifndef NO_LINK
|
||||
#include <SFML/System.hpp>
|
||||
#include <SFML/Network.hpp>
|
||||
|
||||
class ServerInfoDisplay
|
||||
{
|
||||
public:
|
||||
virtual void ShowServerIP(sf::IPAddress addr) = 0;
|
||||
virtual void ShowConnect(int player) = 0;
|
||||
virtual void Ping() = 0;
|
||||
virtual void Connected() = 0;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
u16 linkdata[4];
|
||||
u16 linkcmd[4];
|
||||
u16 linkdata[5];
|
||||
u16 linkcmd;
|
||||
u16 numtransfers;
|
||||
int lastlinktime;
|
||||
u8 numgbas;
|
||||
u8 trgbas;
|
||||
u8 linkflags;
|
||||
int rfu_q[4];
|
||||
u8 rfu_request[4];
|
||||
|
@ -62,72 +82,77 @@ typedef struct {
|
|||
|
||||
class lserver{
|
||||
int numbytes;
|
||||
fd_set fdset;
|
||||
timeval wsocktimeout;
|
||||
sf::Selector<sf::SocketTCP> fdset;
|
||||
//timeval udptimeout;
|
||||
char inbuffer[256], outbuffer[256];
|
||||
int *intinbuffer;
|
||||
s32 *intinbuffer;
|
||||
u16 *u16inbuffer;
|
||||
int *intoutbuffer;
|
||||
s32 *intoutbuffer;
|
||||
u16 *u16outbuffer;
|
||||
int counter;
|
||||
int done;
|
||||
public:
|
||||
int howmanytimes;
|
||||
SOCKET tcpsocket[4];
|
||||
SOCKADDR_IN udpaddr[4];
|
||||
sf::SocketTCP tcpsocket[4];
|
||||
sf::IPAddress udpaddr[4];
|
||||
lserver(void);
|
||||
int Init(void*);
|
||||
bool Init(ServerInfoDisplay *);
|
||||
void Send(void);
|
||||
void Recv(void);
|
||||
};
|
||||
|
||||
class ClientInfoDisplay {
|
||||
public:
|
||||
virtual void ConnectStart(sf::IPAddress addr) = 0;
|
||||
virtual void Ping() = 0;
|
||||
virtual void ShowConnect(int player, int togo) = 0;
|
||||
virtual void Connected() = 0;
|
||||
};
|
||||
|
||||
class lclient{
|
||||
fd_set fdset;
|
||||
timeval wsocktimeout;
|
||||
sf::Selector<sf::SocketTCP> fdset;
|
||||
char inbuffer[256], outbuffer[256];
|
||||
int *intinbuffer;
|
||||
s32 *intinbuffer;
|
||||
u16 *u16inbuffer;
|
||||
int *intoutbuffer;
|
||||
s32 *intoutbuffer;
|
||||
u16 *u16outbuffer;
|
||||
int numbytes;
|
||||
public:
|
||||
bool oncesend;
|
||||
SOCKADDR_IN serverinfo;
|
||||
SOCKET noblock;
|
||||
sf::IPAddress serveraddr;
|
||||
unsigned short serverport;
|
||||
sf::SocketTCP noblock;
|
||||
int numtransfers;
|
||||
lclient(void);
|
||||
int Init(LPHOSTENT, void*);
|
||||
bool Init(sf::IPAddress, ClientInfoDisplay *);
|
||||
void Send(void);
|
||||
void Recv(void);
|
||||
void CheckConn(void);
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
SOCKET tcpsocket;
|
||||
//SOCKET udpsocket;
|
||||
int numgbas;
|
||||
HANDLE thread;
|
||||
u8 type;
|
||||
u8 server;
|
||||
sf::SocketTCP tcpsocket;
|
||||
//sf::SocketUDP udpsocket;
|
||||
int numslaves;
|
||||
sf::Thread *thread;
|
||||
int type;
|
||||
bool server;
|
||||
bool terminate;
|
||||
bool connected;
|
||||
bool speed;
|
||||
bool active;
|
||||
} LANLINKDATA;
|
||||
#endif
|
||||
|
||||
extern bool gba_joybus_enabled;
|
||||
#ifndef NO_LINK
|
||||
extern sf::IPAddress joybusHostAddr;
|
||||
#endif
|
||||
extern void JoyBusConnect();
|
||||
extern void JoyBusShutdown();
|
||||
extern void JoyBusUpdate(int ticks);
|
||||
|
||||
extern bool gba_link_enabled;
|
||||
|
||||
#ifdef _MSC_VER
|
||||
extern bool InitLink();
|
||||
extern void CloseLink();
|
||||
extern void StartLink(u16);
|
||||
extern void StartGPLink(u16);
|
||||
extern void LinkSSend(u16);
|
||||
|
@ -135,16 +160,28 @@ extern void LinkUpdate(int);
|
|||
extern void LinkChildStop();
|
||||
extern void LinkChildSend(u16);
|
||||
extern void CloseLanLink();
|
||||
extern char *MakeInstanceFilename(const char *Input);
|
||||
extern void CleanLocalLink();
|
||||
extern const char *MakeInstanceFilename(const char *Input);
|
||||
extern LANLINKDATA lanlink;
|
||||
extern int vbaid;
|
||||
extern bool rfu_enabled;
|
||||
extern int linktimeout;
|
||||
extern lclient lc;
|
||||
extern lserver ls;
|
||||
extern int linkid;
|
||||
#else // These are stubbed for now
|
||||
inline void StartLink(u16){}
|
||||
inline void StartGPLink(u16){}
|
||||
inline void LinkSSend(u16){}
|
||||
inline void LinkUpdate(int){}
|
||||
|
||||
#else
|
||||
|
||||
// stubs to keep #ifdef's out of mainline
|
||||
#define StartLink(x)
|
||||
#define StartGPLink(x)
|
||||
#define LinkSSend(x)
|
||||
#define LinkUpdate(x)
|
||||
#define JoyBusUpdate(x)
|
||||
#define InitLink() false
|
||||
#define CloseLink()
|
||||
#define gba_link_enabled false
|
||||
#define gba_joybus_enabled false
|
||||
#endif
|
||||
|
||||
#endif /* GBA_GBALINK_H */
|
||||
|
|
|
@ -234,7 +234,8 @@ void remotePutPacket(const char *packet)
|
|||
char c = 0;
|
||||
while(c != '+'){
|
||||
remoteSendFnc(buffer, (int)count + 4);
|
||||
remoteRecvFnc(&c, 1);
|
||||
if(remoteRecvFnc(&c, 1) < 0)
|
||||
return;
|
||||
// fprintf(stderr,"sent:%s recieved:%c\n",buffer,c);
|
||||
}
|
||||
}
|
||||
|
@ -571,7 +572,8 @@ void remoteStubMain()
|
|||
#endif
|
||||
debugger = false;
|
||||
break;
|
||||
}
|
||||
} else if(res == -2)
|
||||
break;
|
||||
if(res < 1024){
|
||||
buffer[res] = 0;
|
||||
}else{
|
||||
|
|
|
@ -175,3 +175,18 @@ void debuggerBreakOnWrite(u32 address, u32 oldvalue, u32 value, int size, int t)
|
|||
void (*dbgMain)() = debuggerMain;
|
||||
void (*dbgSignal)(int, int) = debuggerSignal;
|
||||
void (*dbgOutput)(const char *, u32) = debuggerOutput;
|
||||
|
||||
void log(const char *defaultMsg, ...)
|
||||
{
|
||||
static FILE *out = NULL;
|
||||
|
||||
if(out == NULL) {
|
||||
out = fopen("trace.log","w");
|
||||
}
|
||||
|
||||
va_list valist;
|
||||
|
||||
va_start(valist, defaultMsg);
|
||||
vfprintf(out, defaultMsg, valist);
|
||||
va_end(valist);
|
||||
}
|
||||
|
|
|
@ -2697,3 +2697,18 @@ void systemOnSoundShutdown()
|
|||
void systemOnWriteDataToSoundBuffer(const u16 * finalWave, int length)
|
||||
{
|
||||
}
|
||||
|
||||
void log(const char *defaultMsg, ...)
|
||||
{
|
||||
static FILE *out = NULL;
|
||||
|
||||
if(out == NULL) {
|
||||
out = fopen("trace.log","w");
|
||||
}
|
||||
|
||||
va_list valist;
|
||||
|
||||
va_start(valist, defaultMsg);
|
||||
vfprintf(out, defaultMsg, valist);
|
||||
va_end(valist);
|
||||
}
|
||||
|
|
|
@ -195,7 +195,7 @@ BOOL LinkServer::OnInitDialog()
|
|||
{
|
||||
CDialog::OnInitDialog();
|
||||
|
||||
m_numplayers = lanlink.numgbas;
|
||||
m_numplayers = lanlink.numslaves;
|
||||
m_prottype = lanlink.type;
|
||||
m_speed = lanlink.speed;
|
||||
|
||||
|
@ -490,6 +490,25 @@ void LinkOptions::OnCancel()
|
|||
return;
|
||||
}
|
||||
|
||||
class Win32ServerInfoDisplay : public ServerInfoDisplay
|
||||
{
|
||||
Win32ServerInfoDisplay(ServerWait _dlg) : dlg(_dlg) {}
|
||||
void ShowServerIP(sf::IPAddress addr) {
|
||||
dlg->m_serveraddress.Format("Server IP address is: %s", addr.ToString);
|
||||
}
|
||||
void ShowConnect(int player) {
|
||||
dlg->m_plconn[i].Format("Player %d connected", player);
|
||||
dlg->UpdateData(false);
|
||||
}
|
||||
void Ping() { dlg->m_prgctrl.StepIt(); }
|
||||
void Connected() {
|
||||
MessageBox(NULL, "All players connected", "Link", MB_OK);
|
||||
dlg->SendMessage(WM_CLOSE, 0, 0);
|
||||
}
|
||||
private:
|
||||
ServerWait dlg;
|
||||
}
|
||||
|
||||
void LinkServer::OnServerStart()
|
||||
{
|
||||
int errorcode;
|
||||
|
@ -497,15 +516,15 @@ void LinkServer::OnServerStart()
|
|||
|
||||
UpdateData(TRUE);
|
||||
|
||||
lanlink.numgbas = m_numplayers+1;
|
||||
lanlink.numslaves = m_numplayers+1;
|
||||
lanlink.type = m_prottype;
|
||||
lanlink.server = 1;
|
||||
lanlink.server = true;
|
||||
lanlink.speed = m_speed==1 ? true : false;
|
||||
sf::IPAddress addr;
|
||||
|
||||
if((errorcode=ls.Init(&dlg))!=0){
|
||||
char message[50];
|
||||
sprintf(message, "Error %d occured.\nPlease try again.", errorcode);
|
||||
MessageBox(message, "Error", MB_OK);
|
||||
Win32ServerInfoDisplay dlginfo(dlg);
|
||||
if(!ls.Init(&dlginfo)){
|
||||
MessageBox("Error occured.\nPlease try again.", "Error", MB_OK);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -526,6 +545,26 @@ BOOL LinkClient::OnInitDialog()
|
|||
return TRUE;
|
||||
}
|
||||
|
||||
class Win32ClientInfoDisplay : public ClientInfoDisplay
|
||||
{
|
||||
Win32ClientInfoDisplay(ServerWait _dlg) : dlg(_dlg) {}
|
||||
void ConnectStart(sf::IPAddress addr) {
|
||||
dlg->SetWindowText("Connecting...");
|
||||
}
|
||||
void ShowConnect(int player, int togo) {
|
||||
dlg->m_serveraddress.Format("Connected as #%d", player);
|
||||
if(togo) dlg->m_plconn[0].Format("Waiting for %d players to join", togo);
|
||||
else dlg->m_plconn[0].Format("All players joined.");
|
||||
}
|
||||
void Ping() { dlg->m_prgctrl.StepIt(); }
|
||||
void Connected() {
|
||||
MessageBox(NULL, "Connected.", "Link", MB_OK);
|
||||
dlg->SendMessage(WM_CLOSE, 0, 0);
|
||||
}
|
||||
private:
|
||||
ServerWait dlg;
|
||||
}
|
||||
|
||||
void LinkClient::OnLinkConnect()
|
||||
{
|
||||
char ipaddress[31];
|
||||
|
@ -535,12 +574,13 @@ void LinkClient::OnLinkConnect()
|
|||
UpdateData(TRUE);
|
||||
|
||||
lanlink.type = m_prottype;
|
||||
lanlink.server = 0;
|
||||
lanlink.server = false;
|
||||
lanlink.speed = m_hacks==1 ? true : false;
|
||||
|
||||
m_serverip.GetWindowText(ipaddress, 30);
|
||||
|
||||
if((errorcode=lc.Init(gethostbyname(ipaddress), &dlg))!=0){
|
||||
Win32ClientInfoDisplay dlginfo(dlg);
|
||||
if((errorcode=lc.Init(sf::IPAddress(std::string(ipaddress)), &dlginfo))!=0){
|
||||
char message[50];
|
||||
sprintf(message, "Error %d occured.\nPlease try again.", errorcode);
|
||||
MessageBox(message, "Error", MB_OK);
|
||||
|
|
|
@ -7,7 +7,11 @@
|
|||
// http://www.hiend3d.com/hq2x.html
|
||||
// Modified by suanyuan
|
||||
//---------------------------------------------------------------------------------------------------------------------------
|
||||
#ifdef __MSW__
|
||||
#include <windows.h>
|
||||
#else
|
||||
#define HMODULE void *
|
||||
#endif
|
||||
|
||||
//---------------------------------------------------------------------------------------------------------------------------
|
||||
typedef struct
|
||||
|
@ -54,10 +58,16 @@ typedef RENDER_PLUGIN_INFO *(*RENDPLUG_GetInfo)(void);
|
|||
#define RPI_565_SUPP 0x000000800
|
||||
#define RPI_888_SUPP 0x000001000
|
||||
|
||||
#define RPI_DST_WIDE 0x000008000
|
||||
|
||||
#define RPI_OUT_SCL1 0x000010000
|
||||
#define RPI_OUT_SCL2 0x000020000
|
||||
#define RPI_OUT_SCL3 0x000030000
|
||||
#define RPI_OUT_SCL4 0x000040000
|
||||
|
||||
#define RPI_OUT_SCLMSK 0x0000f0000
|
||||
#define RPI_OUT_SCLSH 16
|
||||
|
||||
//---------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
int rpiScaleFactor();
|
||||
|
|
|
@ -0,0 +1,229 @@
|
|||
# This build is much easier if we just do it here.
|
||||
|
||||
# not yet implemented
|
||||
option( ENABLE_CAIRO "Enable Cairo rendering for the wxWidgets port" ON )
|
||||
if( WIN32 )
|
||||
# not yet implemented
|
||||
option( ENABLE_DIRECT3D "Enable Direct3D rendering for the wxWidgets port" ON )
|
||||
endif( WIN32 )
|
||||
option( ENABLE_OPENAL "Enable OpenAL for the wxWidgets port" ON )
|
||||
|
||||
if( NOT ENABLE_CAIRO )
|
||||
ADD_DEFINITIONS (-DNO_CAIRO)
|
||||
endif( NOT ENABLE_CAIRO )
|
||||
|
||||
if(NOT ENABLE_DIRECT3D)
|
||||
ADD_DEFINITIONS(-DNO_D3D)
|
||||
endif(NOT ENABLE_DIRECT3D)
|
||||
|
||||
if(ENABLE_OPENAL)
|
||||
FIND_PACKAGE(OpenAL REQUIRED)
|
||||
INCLUDE_DIRECTORIES(${OPENAL_INCLUDE_DIR})
|
||||
else(ENABLE_OPENAL)
|
||||
ADD_DEFINITIONS (-DNO_OAL)
|
||||
endif(ENABLE_OPENAL)
|
||||
|
||||
|
||||
# adv is for wxAboutBox
|
||||
SET( wxWidgets_USE_LIBS core base xrc gl adv net )
|
||||
FIND_PACKAGE ( wxWidgets REQUIRED )
|
||||
EXECUTE_PROCESS(COMMAND sh "${wxWidgets_CONFIG_EXECUTABLE}" --cxxflags
|
||||
OUTPUT_VARIABLE CXF)
|
||||
INCLUDE( ${wxWidgets_USE_FILE} )
|
||||
FIND_PACKAGE ( Gettext REQUIRED )
|
||||
FIND_PROGRAM(XGETTEXT xgettext)
|
||||
FIND_PROGRAM(MSGINIT msginit)
|
||||
if(ENABLE_NLS AND (NOT XGETTEXT OR NOT MSGINIT))
|
||||
message(SEND_ERROR "Cannot find gettext ${XGETTEXT} ${MSGINIT}")
|
||||
endif(ENABLE_NLS AND (NOT XGETTEXT OR NOT MSGINIT))
|
||||
IF(ENABLE_CAIRO)
|
||||
FIND_PACKAGE ( PkgConfig REQUIRED )
|
||||
PKG_CHECK_MODULES(CAIRO REQUIRED cairo)
|
||||
include_directories(${CAIRO_INCLUDE_DIRS})
|
||||
IF(WIN32)
|
||||
# need gdiplus to extract hdc for cairo context
|
||||
SET(CAIRO_LIBRARIES ${CAIRO_LIBRARIES} -lgdiplus)
|
||||
ENDIF(WIN32)
|
||||
# SET(CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} ${CAIRO_CFLAGS})
|
||||
ELSE(ENABLE_CAIRO)
|
||||
ADD_DEFINITIONS (-DNO_CAIRO)
|
||||
SET(CAIRO_LIBRARIES )
|
||||
ENDIF(ENABLE_CAIRO)
|
||||
IF(WIN32 AND ENABLE_DIRECTX)
|
||||
FIND_PACKGE ( DirectX REQUIRED )
|
||||
ENDIF(WIN32 AND ENABLE_DIRECTX)
|
||||
|
||||
# contrib widgets
|
||||
include_directories(widgets)
|
||||
|
||||
# for out-of-tree builds, grab includes from both target and source dirs
|
||||
include_directories(${CMAKE_CURRENT_BINARY_DIR})
|
||||
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
||||
# external deps
|
||||
SET(ICO_FILE ${CMAKE_CURRENT_SOURCE_DIR}/../win32/res/VBA.ico)
|
||||
SET(ICO_PARENT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../gtk)
|
||||
SET(WX_APP_ICON ${ICO_PARENT_DIR}/icons/32x32/apps/vbam.png)
|
||||
#SET(WX_APP_ICON VBA_4_32x32x24.png)
|
||||
SET(ICOX_FILES VBA_4_32x32x24.png)
|
||||
#wxvbam.xrc now uses gvbam icon as well
|
||||
#SET(ICOX_FILES ${ICOX_FILES} VBA_9_48x48x32.png)
|
||||
|
||||
# Extract icons using icoutils (http://www.nongnu.org/icoutils/)
|
||||
# Used for main prog. icon and about dialog (in xrc file)
|
||||
# or, just use the icons already extracted for gtk
|
||||
ADD_CUSTOM_COMMAND(OUTPUT ${ICOX_FILES}
|
||||
COMMAND icotool -x ${ICO_FILE}
|
||||
DEPENDS ${ICO_FILE})
|
||||
|
||||
# Convert to xpm using ImageMagick (http://www.imagemagick.org)
|
||||
# not executed on win32
|
||||
IF( NOT WIN32 )
|
||||
FIND_PACKAGE(ImageMagick REQUIRED convert)
|
||||
ADD_CUSTOM_COMMAND(OUTPUT wxvbam.xpm
|
||||
COMMAND ${ImageMagick_convert_EXECUTABLE} ${WX_APP_ICON} wxvbam.xpm
|
||||
# following is done using #define in wxvbam.cpp
|
||||
# so there is no dependency on sed
|
||||
# COMMAND sed -i 's/wxvbam\\[/wxvbam_xpm[/;s/char \\*/const char */' wxvbam.xpm
|
||||
DEPENDS ${WX_APP_ICON})
|
||||
ENDIF( NOT WIN32 )
|
||||
|
||||
# wxrc does not support xrs files in -c output (> 10x compression)
|
||||
# so do it manually using slow but portable bin2c.cmake script
|
||||
SET(WX_XRC_ICON icons/32x32/apps/vbam.png)
|
||||
ADD_CUSTOM_COMMAND(OUTPUT wxvbam.xrs
|
||||
# doing this in its own dir prevents name prefixes
|
||||
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/wxvbam.xrc wxvbam.xrc
|
||||
COMMAND ${CMAKE_COMMAND} -E copy ${ICO_PARENT_DIR}/${WX_XRC_ICON} ${WX_XRC_ICON}
|
||||
COMMAND wxrc wxvbam.xrc -o wxvbam.xrs
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
|
||||
DEPENDS ${ICO_PARENT_DIR}/${XRC_ITEM}
|
||||
DEPENDS wxvbam.xrc)
|
||||
ADD_CUSTOM_COMMAND(OUTPUT builtin-xrc.h
|
||||
COMMAND ${CMAKE_COMMAND} -DINFILE=wxvbam.xrs -DOUTFILE=builtin-xrc.h -DVARNAME=builtin_xrs -P ${CMAKE_CURRENT_SOURCE_DIR}/bin2c.cmake
|
||||
DEPENDS wxvbam.xrs)
|
||||
|
||||
# use a built-in vba-over.ini if no config file present
|
||||
ADD_CUSTOM_COMMAND(OUTPUT builtin-over.h
|
||||
COMMAND ${CMAKE_COMMAND} -DINFILE=${CMAKE_CURRENT_SOURCE_DIR}/../vba-over.ini -DOUTFILE=builtin-over.h -DVARNAME=builtin_over -P ${CMAKE_CURRENT_SOURCE_DIR}/bin2c.cmake
|
||||
DEPENDS ../vba-over.ini)
|
||||
|
||||
|
||||
# I don't like duplicating/triplicating code, so I only declare
|
||||
# event handlers once, and copy them in other places they are needed
|
||||
# all using portable cmake code
|
||||
ADD_CUSTOM_COMMAND(OUTPUT cmdtab.cpp cmdhandlers.h cmd-evtable.h
|
||||
COMMAND
|
||||
${CMAKE_COMMAND} -D OUTDIR=${CMAKE_CURRENT_BINARY_DIR} -P copy-events.cmake
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
DEPENDS cmdevents.cpp)
|
||||
|
||||
#
|
||||
# the following should be in the main file for consistency with
|
||||
# other front ends, but can't due to cmake issues
|
||||
# then again, the main file should be split up into separate dirs anyway
|
||||
#
|
||||
|
||||
SET( SRC_WX
|
||||
wxvbam.cpp
|
||||
guiinit.cpp
|
||||
viewers.cpp
|
||||
gfxviewers.cpp
|
||||
cmdevents.cpp
|
||||
opts.cpp
|
||||
sys.cpp
|
||||
panel.cpp
|
||||
viewsupt.cpp
|
||||
widgets/keyedit.cpp
|
||||
widgets/joyedit.cpp
|
||||
widgets/sdljoy.cpp
|
||||
widgets/wxmisc.cpp
|
||||
# common, but not in lib, apparently
|
||||
../common/SoundSDL.cpp
|
||||
# probably ought to be in common
|
||||
../sdl/text.cpp
|
||||
# from external source with minor modifications
|
||||
widgets/checkedlistctrl.cpp
|
||||
# generated
|
||||
cmdtab.cpp
|
||||
# generated includes must be explicitly listed
|
||||
builtin-xrc.h
|
||||
builtin-over.h
|
||||
cmdhandlers.h
|
||||
cmd-evtable.h
|
||||
)
|
||||
|
||||
IF(ENABLE_OPENAL)
|
||||
SET( SRC_WX ${SRC_WX} openal.cpp )
|
||||
ENDIF(ENABLE_OPENAL)
|
||||
|
||||
IF( WIN32 )
|
||||
SET( SRC_WX ${SRC_WX} wxvbam.rc dsound.cpp xaudio2.cpp )
|
||||
SET(DIRECTX_LIBRARIES -ldxguid -ldsound)
|
||||
# not strictly directx, but win32-related
|
||||
IF(ENABLE_DEBUGGER)
|
||||
SET(DIRECTX_LIBRARIES ${DIRECTX_LIBRARIES} -lwsock32)
|
||||
ENDIF(ENABLE_DEBUGGER)
|
||||
ELSE( WIN32 )
|
||||
SET(DIRECTX_LIBRARIES )
|
||||
# generated file must be explicitly listed
|
||||
SET( SRC_WX ${SRC_WX} wxvbam.xpm )
|
||||
ENDIF( WIN32 )
|
||||
|
||||
IF(APPLE)
|
||||
# icon must be generated manually
|
||||
SET( SRC_WX ${SRC_WX} wxvbam.icns )
|
||||
# png2icns is provided with libicns (http://icns.sourceforge.net/)
|
||||
FIND_PROGRAM(PNG2ICNS png2icns)
|
||||
# note: could add more icons, if available and proper size
|
||||
SET(WX_APP_ICONS
|
||||
${WX_APP_ICON}
|
||||
${ICO_PARENT_DIR}/icons/16x16/apps/vbam.png)
|
||||
ADD_CUSTOM_COMMAND(OUTPUT wxvbam.icns
|
||||
COMMAND ${PNG2ICNS} wxvbam.icns ${WX_APP_ICONS})
|
||||
SET(MACOSX_BUNDLE_ICON_FILE wxvbam.icns)
|
||||
SET_SOURCE_FILES_PROPERTIES(wxvbam.icns PROPERTIES MACOSX_PACKAGE_LOCATION Resources)
|
||||
ENDIF(APPLE)
|
||||
|
||||
link_directories( ${CMAKE_BINARY_DIR} )
|
||||
|
||||
ADD_EXECUTABLE (
|
||||
wxvbam
|
||||
WIN32
|
||||
MACOSX_BUNDLE
|
||||
${SRC_WX}
|
||||
)
|
||||
|
||||
TARGET_LINK_LIBRARIES (
|
||||
wxvbam
|
||||
${VBAMCORE_LIBS}
|
||||
${wxWidgets_LIBRARIES}
|
||||
${FFMPEG_LIBRARIES}
|
||||
${DIRECTX_LIBRARIES}
|
||||
${CAIRO_LIBRARIES}
|
||||
)
|
||||
|
||||
INSTALL(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/wxvbam DESTINATION bin)
|
||||
IF(NOT WIN32 AND NOT APPLE)
|
||||
INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/wxvbam.desktop DESTINATION share/applications)
|
||||
INSTALL(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../gtk/icons/ DESTINATION share/icons/hicolor PATTERN ".svn" EXCLUDE)
|
||||
ENDIF(NOT WIN32 AND NOT APPLE)
|
||||
|
||||
# for consistency with others, copy exe to top-level dir
|
||||
if(WIN32)
|
||||
SET(WX_EXE_NAME wxvbam${CMAKE_EXECUTABLE_SUFFIX})
|
||||
ADD_CUSTOM_COMMAND(TARGET wxvbam POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy ${WX_EXE_NAME} ../../${WX_EXE_NAME})
|
||||
else(WIN32)
|
||||
if(APPLE)
|
||||
SET(WX_EXE_NAME wxvbam.app)
|
||||
# this should set ROM file types correctly
|
||||
SET_PROPERTY(TARGET wxvbam APPEND PROPERTY MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/wxplist.in)
|
||||
else(APPLE)
|
||||
SET(WX_EXE_NAME wxvbam${CMAKE_EXECUTABLE_SUFFIX})
|
||||
endif(APPLE)
|
||||
ADD_CUSTOM_COMMAND(TARGET wxvbam POST_BUILD
|
||||
# I'd rather make this link relative, but it's too hard
|
||||
COMMAND rm -rf ../../${WX_EXE_NAME}
|
||||
COMMAND ln -s ${CMAKE_CURRENT_BINARY_DIR}/${WX_EXE_NAME} ../../${WX_EXE_NAME})
|
||||
endif(WIN32)
|
|
@ -0,0 +1,24 @@
|
|||
# portably convert binary file to header
|
||||
FUNCTION(FILE2C INFILE VARNAME OUTFILE)
|
||||
FILE(READ ${INFILE} HEXFILE HEX)
|
||||
STRING(LENGTH ${HEXFILE} XRSLEN)
|
||||
SET(HEXPOS 0)
|
||||
FILE(WRITE ${OUTFILE}
|
||||
"/* generated from ${INFILE}; do not edit */\n"
|
||||
"const unsigned char ${VARNAME}[] = {")
|
||||
WHILE(${HEXPOS} LESS ${XRSLEN})
|
||||
MATH(EXPR LPOS "${HEXPOS} % 32")
|
||||
IF(NOT ${LPOS})
|
||||
FILE(APPEND ${OUTFILE} "\n")
|
||||
ENDIF(NOT ${LPOS})
|
||||
STRING(SUBSTRING ${HEXFILE} ${HEXPOS} 2 HEXBYTE)
|
||||
FILE(APPEND ${OUTFILE} "0x${HEXBYTE}")
|
||||
MATH(EXPR HEXPOS "${HEXPOS} + 2")
|
||||
IF(${HEXPOS} LESS ${XRSLEN})
|
||||
FILE(APPEND ${OUTFILE} ",")
|
||||
ENDIF(${HEXPOS} LESS ${XRSLEN})
|
||||
ENDWHILE(${HEXPOS} LESS ${XRSLEN})
|
||||
FILE(APPEND ${OUTFILE} "};\n")
|
||||
ENDFUNCTION(FILE2C)
|
||||
|
||||
FILE2C(${INFILE} ${VARNAME} ${OUTFILE})
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,56 @@
|
|||
# Create cmdtab.cpp, cmdhandlers.h, cmd-evtable.h from cmdevents.cpp
|
||||
|
||||
IF(NOT OUTDIR)
|
||||
SET(OUTDIR ".")
|
||||
ENDIF(NOT OUTDIR)
|
||||
|
||||
SET(CMDTAB "${OUTDIR}/cmdtab.cpp")
|
||||
SET(EVPROTO "${OUTDIR}/cmdhandlers.h")
|
||||
SET(EVTABLE "${OUTDIR}/cmd-evtable.h")
|
||||
|
||||
FILE(READ cmdevents.cpp MW)
|
||||
STRING(REGEX MATCHALL "\nEVT_HANDLER([^\")]|\"[^\"]*\")*\\)" MW "${MW}")
|
||||
|
||||
# cmdtab.cpp is a table of cmd-id-name/cmd-name pairs
|
||||
# sorted for binary searching
|
||||
FILE(WRITE "${CMDTAB}" "// Generated from cmdevents.cpp; do not edit\n#include \"wxvbam.h\"\n\nstruct cmditem cmdtab[] = {\n")
|
||||
SET(EVLINES )
|
||||
FOREACH(EV ${MW})
|
||||
# stripping the wxID_ makes it look better, but it's still all-caps
|
||||
STRING(REGEX REPLACE "^[^\"]*\\((wxID_|)([^,]*),[^\"]*(\"[^\"]*\")[^,)]*(,[^)]*|).*"
|
||||
" {wxT(\"\\2\"), wxTRANSLATE(\\3), XRCID(\"\\1\\2\")\\4 }"
|
||||
EV "${EV}")
|
||||
STRING(REGEX REPLACE "XRCID\\(\"(wxID_[^\"]*)\"\\)" "\\1" EV ${EV})
|
||||
LIST(APPEND EVLINES "${EV},\n")
|
||||
ENDFOREACH(EV)
|
||||
LIST(SORT EVLINES)
|
||||
STRING(REGEX REPLACE ",\n\$" "\n" EVLINES "${EVLINES}")
|
||||
FILE(APPEND "${CMDTAB}" ${EVLINES})
|
||||
FILE(APPEND "${CMDTAB}" "};\nconst int ncmds = sizeof(cmdtab) / sizeof(cmdtab[0]);\n")
|
||||
|
||||
# cmdhandlers.h contains prototypes for all handlers
|
||||
FILE(WRITE "${EVPROTO}" "// Generated from cmdevents.cpp; do not edit\n")
|
||||
FOREACH(EV ${MW})
|
||||
STRING(REGEX REPLACE "^[^\"]*\\(" "void On" P1 "${EV}")
|
||||
STRING(REGEX REPLACE ",.*" "(wxCommandEvent&);\n" P1 "${P1}")
|
||||
FILE(APPEND "${EVPROTO}" "${P1}")
|
||||
IF(EV MATCHES "EVT_HANDLER_MASK")
|
||||
STRING(REGEX REPLACE "^[^\"]*\\(" "void Do" P1 "${EV}")
|
||||
STRING(REGEX REPLACE ",.*" "();\n" P1 "${P1}")
|
||||
FILE(APPEND "${EVPROTO}" "${P1}")
|
||||
ENDIF(EV MATCHES "EVT_HANDLER_MASK")
|
||||
ENDFOREACH(EV)
|
||||
|
||||
# cmd-evtable.h has the event table entries for all handlers
|
||||
FILE(WRITE "${EVTABLE}" "// Generated from cmdevents.cpp; do not edit\n")
|
||||
FOREACH(EV ${MW})
|
||||
FILE(APPEND "${EVTABLE}" "EVT_MENU(")
|
||||
STRING(REGEX REPLACE "[^\"]*\\(" "" EV "${EV}")
|
||||
STRING(REGEX REPLACE ",.*" "" EV "${EV}")
|
||||
IF("${EV}" MATCHES "wx.*")
|
||||
FILE(APPEND "${EVTABLE}" "${EV}")
|
||||
ELSE("${EV}" MATCHES "wx.*")
|
||||
FILE(APPEND "${EVTABLE}" "XRCID(\"${EV}\")")
|
||||
ENDIF("${EV}" MATCHES "wx.*")
|
||||
FILE(APPEND "${EVTABLE}" ", MainFrame::On${EV})\n")
|
||||
ENDFOREACH(EV)
|
|
@ -0,0 +1,87 @@
|
|||
#ifndef GAME_DRAWING_H
|
||||
#define GAME_DRAWING_H
|
||||
|
||||
class BasicDrawingPanel : public DrawingPanel, public wxPanel
|
||||
{
|
||||
public:
|
||||
BasicDrawingPanel(wxWindow *parent, int _width, int _height);
|
||||
wxWindow *GetWindow() { return this; }
|
||||
void Delete() { Destroy(); }
|
||||
|
||||
protected:
|
||||
void PaintEv2(wxPaintEvent &ev) { PaintEv(ev); }
|
||||
void DrawArea(wxWindowDC &dc);
|
||||
|
||||
DECLARE_CLASS()
|
||||
DECLARE_EVENT_TABLE()
|
||||
};
|
||||
|
||||
#ifndef NO_OGL
|
||||
#include <wx/glcanvas.h>
|
||||
|
||||
class GLDrawingPanel : public DrawingPanel, public wxGLCanvas
|
||||
{
|
||||
public:
|
||||
GLDrawingPanel(wxWindow *parent, int _width, int _height);
|
||||
virtual ~GLDrawingPanel();
|
||||
wxWindow *GetWindow() { return this; }
|
||||
void Delete() { Destroy(); }
|
||||
|
||||
protected:
|
||||
void PaintEv2(wxPaintEvent &ev) { PaintEv(ev); }
|
||||
void OnSize(wxSizeEvent &);
|
||||
void DrawArea(wxWindowDC &dc);
|
||||
#if wxCHECK_VERSION(2,9,0) || !defined(__WXMAC__)
|
||||
wxGLContext ctx;
|
||||
#endif
|
||||
bool did_init;
|
||||
void Init();
|
||||
GLuint texid, vlist;
|
||||
int texsize;
|
||||
|
||||
DECLARE_CLASS()
|
||||
DECLARE_EVENT_TABLE()
|
||||
};
|
||||
#endif
|
||||
|
||||
#if defined(__WXMSW__) && !defined(NO_D3D)
|
||||
class DXDrawingPanel : public DrawingPanel, public wxPanel
|
||||
{
|
||||
public:
|
||||
DXDrawingPanel(wxWindow *parent, int _width, int _height);
|
||||
wxWindow *GetWindow() { return this; }
|
||||
void Delete() { Destroy(); }
|
||||
|
||||
protected:
|
||||
void PaintEv2(wxPaintEvent &ev) { PaintEv(ev); }
|
||||
void DrawArea(wxWindowDC&);
|
||||
bool did_init;
|
||||
void Init();
|
||||
|
||||
DECLARE_CLASS()
|
||||
DECLARE_EVENT_TABLE()
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifndef NO_CAIRO
|
||||
#include <cairo.h>
|
||||
|
||||
class CairoDrawingPanel : public DrawingPanel, public wxPanel
|
||||
{
|
||||
public:
|
||||
CairoDrawingPanel(wxWindow *parent, int _width, int _height);
|
||||
~CairoDrawingPanel();
|
||||
wxWindow *GetWindow() { return this; }
|
||||
void Delete() { Destroy(); }
|
||||
|
||||
protected:
|
||||
void PaintEv2(wxPaintEvent &ev) { PaintEv(ev); }
|
||||
void DrawArea(wxWindowDC&);
|
||||
cairo_surface_t *conv_surf;
|
||||
|
||||
DECLARE_CLASS()
|
||||
DECLARE_EVENT_TABLE()
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif /* GAME_DRAWING_H */
|
|
@ -0,0 +1,334 @@
|
|||
// Application
|
||||
#include "wxvbam.h"
|
||||
|
||||
// Internals
|
||||
#include "../System.h"
|
||||
#include "../gba/GBA.h"
|
||||
#include "../gba/Globals.h"
|
||||
#include "../gba/Sound.h"
|
||||
#include "../common/SoundDriver.h"
|
||||
|
||||
// DirectSound8
|
||||
#define DIRECTSOUND_VERSION 0x0800
|
||||
#include <dsound.h>
|
||||
|
||||
extern bool soundBufferLow;
|
||||
|
||||
class DirectSound : public SoundDriver
|
||||
{
|
||||
private:
|
||||
LPDIRECTSOUND8 pDirectSound; // DirectSound interface
|
||||
LPDIRECTSOUNDBUFFER dsbPrimary; // Primary DirectSound buffer
|
||||
LPDIRECTSOUNDBUFFER dsbSecondary; // Secondary DirectSound buffer
|
||||
LPDIRECTSOUNDNOTIFY8 dsbNotify;
|
||||
HANDLE dsbEvent;
|
||||
WAVEFORMATEX wfx; // Primary buffer wave format
|
||||
int soundBufferLen;
|
||||
int soundBufferTotalLen;
|
||||
unsigned int soundNextPosition;
|
||||
|
||||
public:
|
||||
DirectSound();
|
||||
virtual ~DirectSound();
|
||||
|
||||
bool init(long sampleRate); // initialize the primary and secondary sound buffer
|
||||
void pause(); // pause the secondary sound buffer
|
||||
void reset(); // stop and reset the secondary sound buffer
|
||||
void resume(); // resume the secondary sound buffer
|
||||
void write(u16 * finalWave, int length); // write the emulated sound to the secondary sound buffer
|
||||
};
|
||||
|
||||
|
||||
DirectSound::DirectSound()
|
||||
{
|
||||
pDirectSound = NULL;
|
||||
dsbPrimary = NULL;
|
||||
dsbSecondary = NULL;
|
||||
dsbNotify = NULL;
|
||||
dsbEvent = NULL;
|
||||
|
||||
soundBufferTotalLen = 14700;
|
||||
soundNextPosition = 0;
|
||||
}
|
||||
|
||||
|
||||
DirectSound::~DirectSound()
|
||||
{
|
||||
if(dsbNotify) {
|
||||
dsbNotify->Release();
|
||||
dsbNotify = NULL;
|
||||
}
|
||||
|
||||
if(dsbEvent) {
|
||||
CloseHandle(dsbEvent);
|
||||
dsbEvent = NULL;
|
||||
}
|
||||
|
||||
if(pDirectSound) {
|
||||
if(dsbPrimary) {
|
||||
dsbPrimary->Release();
|
||||
dsbPrimary = NULL;
|
||||
}
|
||||
|
||||
if(dsbSecondary) {
|
||||
dsbSecondary->Release();
|
||||
dsbSecondary = NULL;
|
||||
}
|
||||
|
||||
pDirectSound->Release();
|
||||
pDirectSound = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool DirectSound::init(long sampleRate)
|
||||
{
|
||||
HRESULT hr;
|
||||
DWORD freq;
|
||||
DSBUFFERDESC dsbdesc;
|
||||
int i;
|
||||
|
||||
hr = CoCreateInstance( CLSID_DirectSound8, NULL, CLSCTX_INPROC_SERVER, IID_IDirectSound8, (LPVOID *)&pDirectSound );
|
||||
if( hr != S_OK ) {
|
||||
wxLogError(_("Cannot create DirectSound %08x"), hr);
|
||||
return false;
|
||||
}
|
||||
|
||||
GUID dev;
|
||||
if(gopts.audio_dev.empty())
|
||||
dev = DSDEVID_DefaultPlayback;
|
||||
else
|
||||
CLSIDFromString(const_cast<wxChar *>(gopts.audio_dev.c_str()), &dev);
|
||||
pDirectSound->Initialize( &dev );
|
||||
if( hr != DS_OK ) {
|
||||
wxLogError(_("Cannot create DirectSound %08x"), hr);
|
||||
return false;
|
||||
}
|
||||
|
||||
if( FAILED( hr = pDirectSound->SetCooperativeLevel( (HWND)wxGetApp().frame->GetHandle(), DSSCL_EXCLUSIVE ) ) ) {
|
||||
wxLogError(_("Cannot SetCooperativeLevel %08x"), hr);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Create primary sound buffer
|
||||
ZeroMemory( &dsbdesc, sizeof(DSBUFFERDESC) );
|
||||
dsbdesc.dwSize = sizeof(DSBUFFERDESC);
|
||||
dsbdesc.dwFlags = DSBCAPS_PRIMARYBUFFER;
|
||||
if( !gopts.dsound_hw_accel ) {
|
||||
dsbdesc.dwFlags |= DSBCAPS_LOCSOFTWARE;
|
||||
}
|
||||
|
||||
if( FAILED( hr = pDirectSound->CreateSoundBuffer( &dsbdesc, &dsbPrimary, NULL ) ) ) {
|
||||
wxLogError(_("Cannot CreateSoundBuffer %08x"), hr);
|
||||
return false;
|
||||
}
|
||||
|
||||
freq = sampleRate;
|
||||
// calculate the number of samples per frame first
|
||||
// then multiply it with the size of a sample frame (16 bit * stereo)
|
||||
soundBufferLen = ( freq / 60 ) * 4;
|
||||
soundBufferTotalLen = soundBufferLen * 10;
|
||||
soundNextPosition = 0;
|
||||
|
||||
ZeroMemory( &wfx, sizeof(WAVEFORMATEX) );
|
||||
wfx.wFormatTag = WAVE_FORMAT_PCM;
|
||||
wfx.nChannels = 2;
|
||||
wfx.nSamplesPerSec = freq;
|
||||
wfx.wBitsPerSample = 16;
|
||||
wfx.nBlockAlign = wfx.nChannels * wfx.wBitsPerSample / 8;
|
||||
wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign;
|
||||
|
||||
if( FAILED( hr = dsbPrimary->SetFormat( &wfx ) ) ) {
|
||||
wxLogError( _("CreateSoundBuffer(primary) failed %08x"), hr );
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Create secondary sound buffer
|
||||
ZeroMemory( &dsbdesc, sizeof(DSBUFFERDESC) );
|
||||
dsbdesc.dwSize = sizeof(DSBUFFERDESC);
|
||||
dsbdesc.dwFlags = DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_CTRLPOSITIONNOTIFY | DSBCAPS_GLOBALFOCUS;
|
||||
if( !gopts.dsound_hw_accel ) {
|
||||
dsbdesc.dwFlags |= DSBCAPS_LOCSOFTWARE;
|
||||
}
|
||||
dsbdesc.dwBufferBytes = soundBufferTotalLen;
|
||||
dsbdesc.lpwfxFormat = &wfx;
|
||||
|
||||
if( FAILED( hr = pDirectSound->CreateSoundBuffer( &dsbdesc, &dsbSecondary, NULL ) ) ) {
|
||||
wxLogError(_("CreateSoundBuffer(secondary) failed %08x"), hr );
|
||||
return false;
|
||||
}
|
||||
|
||||
if( FAILED( hr = dsbSecondary->SetCurrentPosition( 0 ) ) ) {
|
||||
wxLogError(_("dsbSecondary->SetCurrentPosition failed %08x"), hr );
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
if( SUCCEEDED( hr = dsbSecondary->QueryInterface( IID_IDirectSoundNotify8, (LPVOID*)&dsbNotify ) ) ) {
|
||||
dsbEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
|
||||
DSBPOSITIONNOTIFY notify[10];
|
||||
for( i = 0; i < 10; i++ ) {
|
||||
notify[i].dwOffset = i * soundBufferLen;
|
||||
notify[i].hEventNotify = dsbEvent;
|
||||
}
|
||||
|
||||
if( FAILED( dsbNotify->SetNotificationPositions( 10, notify ) ) ) {
|
||||
dsbNotify->Release();
|
||||
dsbNotify = NULL;
|
||||
CloseHandle(dsbEvent);
|
||||
dsbEvent = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Play primary buffer
|
||||
if( FAILED( hr = dsbPrimary->Play( 0, 0, DSBPLAY_LOOPING ) ) ) {
|
||||
wxLogError( _("Cannot Play primary %08x"), hr );
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void DirectSound::pause()
|
||||
{
|
||||
if( dsbSecondary == NULL ) return;
|
||||
|
||||
DWORD status;
|
||||
|
||||
dsbSecondary->GetStatus( &status );
|
||||
|
||||
if( status & DSBSTATUS_PLAYING ) dsbSecondary->Stop();
|
||||
}
|
||||
|
||||
|
||||
void DirectSound::reset()
|
||||
{
|
||||
if( dsbSecondary == NULL ) return;
|
||||
|
||||
dsbSecondary->Stop();
|
||||
|
||||
dsbSecondary->SetCurrentPosition( 0 );
|
||||
|
||||
soundNextPosition = 0;
|
||||
}
|
||||
|
||||
|
||||
void DirectSound::resume()
|
||||
{
|
||||
if( dsbSecondary == NULL ) return;
|
||||
|
||||
dsbSecondary->Play( 0, 0, DSBPLAY_LOOPING );
|
||||
}
|
||||
|
||||
|
||||
void DirectSound::write(u16 * finalWave, int length)
|
||||
{
|
||||
if(!pDirectSound) return;
|
||||
|
||||
|
||||
HRESULT hr;
|
||||
DWORD status = 0;
|
||||
DWORD play = 0;
|
||||
LPVOID lpvPtr1;
|
||||
DWORD dwBytes1 = 0;
|
||||
LPVOID lpvPtr2;
|
||||
DWORD dwBytes2 = 0;
|
||||
|
||||
if( !speedup && synchronize && !gopts.throttle ) {
|
||||
hr = dsbSecondary->GetStatus(&status);
|
||||
if( status & DSBSTATUS_PLAYING ) {
|
||||
if( !soundPaused ) {
|
||||
while( true ) {
|
||||
dsbSecondary->GetCurrentPosition(&play, NULL);
|
||||
int BufferLeft = ((soundNextPosition <= play) ?
|
||||
play - soundNextPosition :
|
||||
soundBufferTotalLen - soundNextPosition + play);
|
||||
|
||||
if(BufferLeft > soundBufferLen)
|
||||
{
|
||||
if (BufferLeft > soundBufferTotalLen - (soundBufferLen * 3))
|
||||
soundBufferLow = true;
|
||||
break;
|
||||
}
|
||||
soundBufferLow = false;
|
||||
|
||||
if(dsbEvent) {
|
||||
WaitForSingleObject(dsbEvent, 50);
|
||||
}
|
||||
}
|
||||
}
|
||||
}/* else {
|
||||
// TODO: remove?
|
||||
setsoundPaused(true);
|
||||
}*/
|
||||
}
|
||||
|
||||
|
||||
// Obtain memory address of write block.
|
||||
// This will be in two parts if the block wraps around.
|
||||
if( DSERR_BUFFERLOST == ( hr = dsbSecondary->Lock(
|
||||
soundNextPosition,
|
||||
soundBufferLen,
|
||||
&lpvPtr1,
|
||||
&dwBytes1,
|
||||
&lpvPtr2,
|
||||
&dwBytes2,
|
||||
0 ) ) ) {
|
||||
// If DSERR_BUFFERLOST is returned, restore and retry lock.
|
||||
dsbSecondary->Restore();
|
||||
hr = dsbSecondary->Lock(
|
||||
soundNextPosition,
|
||||
soundBufferLen,
|
||||
&lpvPtr1,
|
||||
&dwBytes1,
|
||||
&lpvPtr2,
|
||||
&dwBytes2,
|
||||
0 );
|
||||
}
|
||||
|
||||
soundNextPosition += soundBufferLen;
|
||||
soundNextPosition = soundNextPosition % soundBufferTotalLen;
|
||||
|
||||
if( SUCCEEDED( hr ) ) {
|
||||
// Write to pointers.
|
||||
CopyMemory( lpvPtr1, finalWave, dwBytes1 );
|
||||
if ( lpvPtr2 ) {
|
||||
CopyMemory( lpvPtr2, finalWave + dwBytes1, dwBytes2 );
|
||||
}
|
||||
|
||||
// Release the data back to DirectSound.
|
||||
hr = dsbSecondary->Unlock( lpvPtr1, dwBytes1, lpvPtr2, dwBytes2 );
|
||||
} else {
|
||||
wxLogError(_("dsbSecondary->Lock() failed: %08x"), hr );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
SoundDriver *newDirectSound()
|
||||
{
|
||||
return new DirectSound();
|
||||
}
|
||||
|
||||
struct devnames {
|
||||
wxArrayString *names, *ids;
|
||||
};
|
||||
|
||||
static BOOL CALLBACK DSEnumCB(LPGUID guid, LPCTSTR desc, LPCTSTR drvnam, LPVOID user)
|
||||
{
|
||||
devnames *dn = (devnames *)user;
|
||||
dn->names->push_back(desc);
|
||||
WCHAR buf[32 + 4 + 2 + 1]; // hex digits + "-" + "{}" + \0
|
||||
StringFromGUID2(*guid, buf, sizeof(buf));
|
||||
dn->ids->push_back(buf);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
bool GetDSDevices(wxArrayString &names, wxArrayString &ids)
|
||||
{
|
||||
devnames dn = { &names, &ids };
|
||||
return DirectSoundEnumerate(DSEnumCB, (LPVOID)&dn) == DS_OK;
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
#ifndef FILTERS_H
|
||||
#define FILTERS_H
|
||||
|
||||
// FIXME: these should be in a system header included by all users and all
|
||||
// files which define these functions
|
||||
// most 16-bit filters require space in src rounded up to u32
|
||||
// those that take delta take 1 src line of pixels, rounded up to u32 size
|
||||
// initial value appears to be all-0xff
|
||||
void Pixelate32(u8 *src, u32 spitch, u8 *, u8 *dst, u32 dstp, int w, int h);
|
||||
void Pixelate(u8 *src, u32 spitch, u8 *delta, u8 *dst, u32 dstp, int w, int h);
|
||||
// next 3*2 use Init_2xSaI(555|565) and do not take into account
|
||||
int Init_2xSaI(u32 BitFormat);
|
||||
// endianness or bit shift variables in init.
|
||||
// next 4*2 may be MMX-accelerated
|
||||
void _2xSaI32(u8 *src, u32 spitch, u8 *delta, u8 *dst, u32 dstp, int w, int h);
|
||||
void _2xSaI(u8 *src, u32 spitch, u8 *delta, u8 *dst, u32 dstp, int w, int h);
|
||||
// void Scale_2xSaI(u8 *src, u32 spitch, u8 *, u8 *dst, u32 dstp, int w, int h);
|
||||
void Super2xSaI32(u8 *src, u32 spitch, u8 *delta, u8 *dst, u32 dstp, int w, int h);
|
||||
void Super2xSaI(u8 *src, u32 spitch, u8 *delta, u8 *dst, u32 dstp, int w, int h);
|
||||
void SuperEagle32(u8 *src, u32 spitch, u8 *delta, u8 *dst, u32 dstp, int w, int h);
|
||||
void SuperEagle(u8 *src, u32 spitch, u8 *delta, u8 *dst, u32 dstp, int w, int h);
|
||||
void AdMame2x32(u8 *src, u32 spitch, u8 *, u8 *dst, u32 dstp, int w, int h);
|
||||
void AdMame2x(u8 *src, u32 spitch, u8 *, u8 *dst, u32 dstp, int w, int h);
|
||||
// next 4 convert to rgb24 in internal buffers first, and then back again
|
||||
void Bilinear32(u8 *src, u32 spitch, u8 *, u8 *dst, u32 dstp, int w, int h);
|
||||
void Bilinear(u8 *src, u32 spitch, u8 *, u8 *dst, u32 dstp, int w, int h);
|
||||
void BilinearPlus32(u8 *src, u32 spitch, u8 *, u8 *dst, u32 dstp, int w, int h);
|
||||
void BilinearPlus(u8 *src, u32 spitch, u8 *, u8 *dst, u32 dstp, int w, int h);
|
||||
void Scanlines32(u8 *src, u32 spitch, u8 *, u8 *dst, u32 dstp, int w, int h);
|
||||
void Scanlines(u8 *src, u32 spitch, u8 *, u8 *dst, u32 dstp, int w, int h);
|
||||
void ScanlinesTV32(u8 *src, u32 spitch, u8 *, u8 *dst, u32 dstp, int w, int h);
|
||||
// "TV" here means each pixel is faded horizontally & vertically rather than
|
||||
// inserting black scanlines
|
||||
// this depends on RGB_LOW_BITS_MASK
|
||||
extern int RGB_LOW_BITS_MASK;
|
||||
void ScanlinesTV(u8 *src, u32 spitch, u8 *, u8 *dst, u32 dstp, int w, int h);
|
||||
// next 2 require calling hq2x_init first and whenever bpp changes
|
||||
void hq2x_init(unsigned bpp);
|
||||
void hq2x32(u8 *src, u32 spitch, u8 *, u8 *dst, u32 dstp, int w, int h);
|
||||
void hq2x(u8 *src, u32 spitch, u8 *, u8 *dst, u32 dstp, int w, int h);
|
||||
void lq2x32(u8 *src, u32 spitch, u8 *, u8 *dst, u32 dstp, int w, int h);
|
||||
void lq2x(u8 *src, u32 spitch, u8 *, u8 *dst, u32 dstp, int w, int h);
|
||||
// the simple ones could greatly benefit from correct usage of preprocessor..
|
||||
// in any case, they are worthless, since all renderers do "simple" or
|
||||
// better by default
|
||||
void Simple2x32(u8 *src, u32 spitch, u8 *, u8 *dst, u32 dstp, int w, int h);
|
||||
void Simple2x(u8 *src, u32 spitch, u8 *, u8 *dst, u32 dstp, int w, int h);
|
||||
void Simple3x32(u8 *src, u32 spitch, u8 *, u8 *dst, u32 dstp, int w, int h);
|
||||
void Simple3x(u8 *src, u32 spitch, u8 *, u8 *dst, u32 dstp, int w, int h);
|
||||
void Simple4x32(u8 *src, u32 spitch, u8 *, u8 *dst, u32 dstp, int w, int h);
|
||||
void Simple4x(u8 *src, u32 spitch, u8 *, u8 *dst, u32 dstp, int w, int h);
|
||||
// note: 16-bit input for asm version only!
|
||||
void hq3x32(u8 *src, u32 spitch, u8 *, u8 *dst, u32 dstp, int w, int h);
|
||||
// this takes 32-bit input
|
||||
// (by converting to 16-bit first in asm version)
|
||||
void hq3x32_32(u8 *src, u32 spitch, u8 *, u8 *dst, u32 dstp, int w, int h);
|
||||
void hq3x16(u8 *src, u32 spitch, u8 *, u8 *dst, u32 dstp, int w, int h);
|
||||
// note: 16-bit input for asm version only!
|
||||
void hq4x32(u8 *src, u32 spitch, u8 *, u8 *dst, u32 dstp, int w, int h);
|
||||
// this takes 32-bit input
|
||||
// (by converting to 16-bit first in asm version)
|
||||
void hq4x32_32(u8 *src, u32 spitch, u8 *, u8 *dst, u32 dstp, int w, int h);
|
||||
void hq4x16(u8 *src, u32 spitch, u8 *, u8 *dst, u32 dstp, int w, int h);
|
||||
|
||||
// call ifc to ignore previous frame / when starting new
|
||||
void InterframeCleanup();
|
||||
// all 4 are MMX-accelerated if enabled
|
||||
void SmartIB(u8 *src, u32 spitch, int width, int height);
|
||||
void SmartIB32(u8 *src, u32 spitch, int width, int height);
|
||||
void MotionBlurIB(u8 *src, u32 spitch, int width, int height);
|
||||
void MotionBlurIB32(u8 *src, u32 spitch, int width, int height);
|
||||
void SmartIB(u8 *src, u32 spitch, int width, int starty, int height);
|
||||
void SmartIB32(u8 *src, u32 spitch, int width, int starty, int height);
|
||||
void MotionBlurIB(u8 *src, u32 spitch, int width, int starty, int height);
|
||||
void MotionBlurIB32(u8 *src, u32 spitch, int width, int starty, int height);
|
||||
|
||||
#endif /* FILTERS_H */
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,490 @@
|
|||
// === LOGALL writes very detailed informations to vba-trace.log ===
|
||||
//#define LOGALL
|
||||
|
||||
#ifndef NO_OAL
|
||||
|
||||
// for gopts
|
||||
// also, wx-related
|
||||
#include "wxvbam.h"
|
||||
|
||||
// Interface
|
||||
#include "../common/SoundDriver.h"
|
||||
|
||||
// OpenAL
|
||||
#include "openal.h"
|
||||
|
||||
// Internals
|
||||
#include "../gba/Sound.h"
|
||||
#include "../gba/Globals.h" // for 'speedup' and 'synchronize'
|
||||
|
||||
// Debug
|
||||
#include <assert.h>
|
||||
#define ASSERT_SUCCESS assert( AL_NO_ERROR == ALFunction.alGetError() )
|
||||
|
||||
#ifndef LOGALL
|
||||
// replace logging functions with comments
|
||||
#ifdef winlog
|
||||
#undef winlog
|
||||
#endif
|
||||
#define winlog //
|
||||
#define debugState() //
|
||||
#endif
|
||||
|
||||
struct OPENALFNTABLE;
|
||||
|
||||
class OpenAL : public SoundDriver
|
||||
{
|
||||
public:
|
||||
OpenAL();
|
||||
virtual ~OpenAL();
|
||||
|
||||
static wxDynamicLibrary Lib;
|
||||
static bool LoadOAL();
|
||||
static bool GetDevices(wxArrayString &names, wxArrayString &ids);
|
||||
bool init(long sampleRate); // initialize the sound buffer queue
|
||||
void pause(); // pause the secondary sound buffer
|
||||
void reset(); // stop and reset the secondary sound buffer
|
||||
void resume(); // play/resume the secondary sound buffer
|
||||
void write(u16 * finalWave, int length); // write the emulated sound to a sound buffer
|
||||
|
||||
private:
|
||||
static OPENALFNTABLE ALFunction;
|
||||
bool initialized;
|
||||
bool buffersLoaded;
|
||||
ALCdevice *device;
|
||||
ALCcontext *context;
|
||||
ALuint *buffer;
|
||||
ALuint tempBuffer;
|
||||
ALuint source;
|
||||
int freq;
|
||||
int soundBufferLen;
|
||||
|
||||
#ifdef LOGALL
|
||||
void debugState();
|
||||
#endif
|
||||
};
|
||||
|
||||
OpenAL::OpenAL()
|
||||
{
|
||||
initialized = false;
|
||||
buffersLoaded = false;
|
||||
device = NULL;
|
||||
context = NULL;
|
||||
buffer = (ALuint*)malloc( gopts.audio_buffers * sizeof( ALuint ) );
|
||||
memset( buffer, 0, gopts.audio_buffers * sizeof( ALuint ) );
|
||||
tempBuffer = 0;
|
||||
source = 0;
|
||||
}
|
||||
|
||||
|
||||
OpenAL::~OpenAL()
|
||||
{
|
||||
if( !initialized ) return;
|
||||
|
||||
ALFunction.alSourceStop( source );
|
||||
ASSERT_SUCCESS;
|
||||
|
||||
ALFunction.alSourcei( source, AL_BUFFER, 0 );
|
||||
ASSERT_SUCCESS;
|
||||
|
||||
ALFunction.alDeleteSources( 1, &source );
|
||||
ASSERT_SUCCESS;
|
||||
|
||||
ALFunction.alDeleteBuffers( gopts.audio_buffers, buffer );
|
||||
ASSERT_SUCCESS;
|
||||
|
||||
free( buffer );
|
||||
|
||||
ALFunction.alcMakeContextCurrent( NULL );
|
||||
// Wine incorrectly returns ALC_INVALID_VALUE
|
||||
// and then fails the rest of these functions as well
|
||||
// so there will be a leak under Wine, but that's a bug in Wine, not
|
||||
// this code
|
||||
//ASSERT_SUCCESS;
|
||||
|
||||
ALFunction.alcDestroyContext( context );
|
||||
//ASSERT_SUCCESS;
|
||||
|
||||
ALFunction.alcCloseDevice( device );
|
||||
//ASSERT_SUCCESS;
|
||||
ALFunction.alGetError(); // reset error state
|
||||
}
|
||||
|
||||
#ifdef LOGALL
|
||||
void OpenAL::debugState()
|
||||
{
|
||||
|
||||
ALint value = 0;
|
||||
ALFunction.alGetSourcei( source, AL_SOURCE_STATE, &value );
|
||||
ASSERT_SUCCESS;
|
||||
|
||||
winlog( " soundPaused = %i\n", soundPaused );
|
||||
winlog( " Source:\n" );
|
||||
winlog( " State: " );
|
||||
switch( value )
|
||||
{
|
||||
case AL_INITIAL:
|
||||
winlog( "AL_INITIAL\n" );
|
||||
break;
|
||||
case AL_PLAYING:
|
||||
winlog( "AL_PLAYING\n" );
|
||||
break;
|
||||
case AL_PAUSED:
|
||||
winlog( "AL_PAUSED\n" );
|
||||
break;
|
||||
case AL_STOPPED:
|
||||
winlog( "AL_STOPPED\n" );
|
||||
break;
|
||||
default:
|
||||
winlog( "!unknown!\n" );
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
ALFunction.alGetSourcei( source, AL_BUFFERS_QUEUED, &value );
|
||||
ASSERT_SUCCESS;
|
||||
winlog( " Buffers in queue: %i\n", value );
|
||||
|
||||
ALFunction.alGetSourcei( source, AL_BUFFERS_PROCESSED, &value );
|
||||
ASSERT_SUCCESS;
|
||||
winlog( " Buffers processed: %i\n", value );
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
bool OpenAL::init(long sampleRate)
|
||||
{
|
||||
winlog( "OpenAL::init\n" );
|
||||
assert( initialized == false );
|
||||
|
||||
if( !LoadOAL() ) {
|
||||
wxLogError( _("OpenAL library could not be found on your system. Please install the runtime from http://openal.org") );
|
||||
return false;
|
||||
}
|
||||
|
||||
if( !gopts.audio_dev.empty() ) {
|
||||
device = ALFunction.alcOpenDevice( gopts.audio_dev.mb_str() );
|
||||
} else {
|
||||
device = ALFunction.alcOpenDevice( NULL );
|
||||
}
|
||||
assert( device != NULL );
|
||||
|
||||
context = ALFunction.alcCreateContext( device, NULL );
|
||||
assert( context != NULL );
|
||||
|
||||
ALCboolean retVal = ALFunction.alcMakeContextCurrent( context );
|
||||
assert( ALC_TRUE == retVal );
|
||||
|
||||
ALFunction.alGenBuffers( gopts.audio_buffers, buffer );
|
||||
ASSERT_SUCCESS;
|
||||
|
||||
ALFunction.alGenSources( 1, &source );
|
||||
ASSERT_SUCCESS;
|
||||
|
||||
freq = sampleRate;
|
||||
|
||||
// calculate the number of samples per frame first
|
||||
// then multiply it with the size of a sample frame (16 bit * stereo)
|
||||
soundBufferLen = ( freq / 60 ) * 4;
|
||||
|
||||
|
||||
initialized = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void OpenAL::resume()
|
||||
{
|
||||
if( !initialized ) return;
|
||||
winlog( "OpenAL::resume\n" );
|
||||
if( !buffersLoaded ) return;
|
||||
debugState();
|
||||
|
||||
|
||||
ALint sourceState = 0;
|
||||
ALFunction.alGetSourcei( source, AL_SOURCE_STATE, &sourceState );
|
||||
ASSERT_SUCCESS;
|
||||
if( sourceState != AL_PLAYING ) {
|
||||
ALFunction.alSourcePlay( source );
|
||||
ASSERT_SUCCESS;
|
||||
}
|
||||
debugState();
|
||||
}
|
||||
|
||||
|
||||
void OpenAL::pause()
|
||||
{
|
||||
if( !initialized ) return;
|
||||
winlog( "OpenAL::pause\n" );
|
||||
if( !buffersLoaded ) return;
|
||||
debugState();
|
||||
|
||||
|
||||
ALint sourceState = 0;
|
||||
ALFunction.alGetSourcei( source, AL_SOURCE_STATE, &sourceState );
|
||||
ASSERT_SUCCESS;
|
||||
if( sourceState == AL_PLAYING ) {
|
||||
ALFunction.alSourcePause( source );
|
||||
ASSERT_SUCCESS;
|
||||
}
|
||||
debugState();
|
||||
}
|
||||
|
||||
|
||||
void OpenAL::reset()
|
||||
{
|
||||
if( !initialized ) return;
|
||||
winlog( "OpenAL::reset\n" );
|
||||
if( !buffersLoaded ) return;
|
||||
debugState();
|
||||
|
||||
ALint sourceState = 0;
|
||||
ALFunction.alGetSourcei( source, AL_SOURCE_STATE, &sourceState );
|
||||
ASSERT_SUCCESS;
|
||||
if( sourceState != AL_STOPPED ) {
|
||||
ALFunction.alSourceStop( source );
|
||||
ASSERT_SUCCESS;
|
||||
}
|
||||
debugState();
|
||||
}
|
||||
|
||||
|
||||
void OpenAL::write(u16 * finalWave, int length)
|
||||
{
|
||||
if( !initialized ) return;
|
||||
winlog( "OpenAL::write\n" );
|
||||
|
||||
debugState();
|
||||
|
||||
ALint sourceState = 0;
|
||||
ALint nBuffersProcessed = 0;
|
||||
|
||||
if( !buffersLoaded ) {
|
||||
// ==initial buffer filling==
|
||||
winlog( " initial buffer filling\n" );
|
||||
for( int i = 0 ; i < gopts.audio_buffers ; i++ ) {
|
||||
// Filling the buffers explicitly with silence would be cleaner,
|
||||
// but the very first sample is usually silence anyway.
|
||||
ALFunction.alBufferData( buffer[i], AL_FORMAT_STEREO16, finalWave, soundBufferLen, freq );
|
||||
ASSERT_SUCCESS;
|
||||
}
|
||||
|
||||
ALFunction.alSourceQueueBuffers( source, gopts.audio_buffers, buffer );
|
||||
ASSERT_SUCCESS;
|
||||
|
||||
buffersLoaded = true;
|
||||
} else {
|
||||
// ==normal buffer refreshing==
|
||||
nBuffersProcessed = 0;
|
||||
ALFunction.alGetSourcei( source, AL_BUFFERS_PROCESSED, &nBuffersProcessed );
|
||||
ASSERT_SUCCESS;
|
||||
|
||||
if( nBuffersProcessed == gopts.audio_buffers ) {
|
||||
// we only want to know about it when we are emulating at full speed or faster:
|
||||
if( ( gopts.throttle >= 100 ) || ( gopts.throttle == 0 ) ) {
|
||||
if( systemVerbose & VERBOSE_SOUNDOUTPUT ) {
|
||||
static unsigned int i = 0;
|
||||
log( "OpenAL: Buffers were not refilled fast enough (i=%i)\n", i++ );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( !speedup && synchronize && !gopts.throttle ) {
|
||||
// wait until at least one buffer has finished
|
||||
while( nBuffersProcessed == 0 ) {
|
||||
winlog( " waiting...\n" );
|
||||
// wait for about half the time one buffer needs to finish
|
||||
// unoptimized: ( sourceBufferLen * 1000 ) / ( freq * 2 * 2 ) * 1/2
|
||||
wxMilliSleep( soundBufferLen / ( freq >> 7 ) );
|
||||
ALFunction.alGetSourcei( source, AL_BUFFERS_PROCESSED, &nBuffersProcessed );
|
||||
ASSERT_SUCCESS;
|
||||
}
|
||||
} else {
|
||||
if( nBuffersProcessed == 0 ) return;
|
||||
}
|
||||
|
||||
assert( nBuffersProcessed > 0 );
|
||||
|
||||
// unqueue buffer
|
||||
tempBuffer = 0;
|
||||
ALFunction.alSourceUnqueueBuffers( source, 1, &tempBuffer );
|
||||
ASSERT_SUCCESS;
|
||||
|
||||
// refill buffer
|
||||
ALFunction.alBufferData( tempBuffer, AL_FORMAT_STEREO16, finalWave, soundBufferLen, freq );
|
||||
ASSERT_SUCCESS;
|
||||
|
||||
// requeue buffer
|
||||
ALFunction.alSourceQueueBuffers( source, 1, &tempBuffer );
|
||||
ASSERT_SUCCESS;
|
||||
}
|
||||
|
||||
// start playing the source if necessary
|
||||
ALFunction.alGetSourcei( source, AL_SOURCE_STATE, &sourceState );
|
||||
ASSERT_SUCCESS;
|
||||
if( !soundPaused && ( sourceState != AL_PLAYING ) ) {
|
||||
ALFunction.alSourcePlay( source );
|
||||
ASSERT_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
SoundDriver *newOpenAL()
|
||||
{
|
||||
winlog( "newOpenAL\n" );
|
||||
return new OpenAL();
|
||||
}
|
||||
|
||||
// no more use of copyrighted OpenAL code just to load the stupid library
|
||||
// this is for compatibility with MFC version
|
||||
// positive: make an OpenAL-capable binary which does not require OpenAL
|
||||
// negative: openal lib may not be in library path
|
||||
// openal lib name is OS-dependent, and may even change based
|
||||
// on where it was installed from
|
||||
// On UNIX, it would probably be better to just hard link with libopenal
|
||||
|
||||
OPENALFNTABLE OpenAL::ALFunction = {NULL};
|
||||
wxDynamicLibrary OpenAL::Lib;
|
||||
|
||||
bool OpenAL::LoadOAL()
|
||||
{
|
||||
if(!Lib.IsLoaded() &&
|
||||
#ifdef __WXMSW__
|
||||
// on win32, it's openal32.dll
|
||||
!Lib.Load(wxT("openal32")) &&
|
||||
#else
|
||||
#ifdef __WXMAC__
|
||||
// on macosx, it's just plain OpenAL
|
||||
!Lib.Load(wxT("OpenAL"), wxDL_NOW|wxDL_VERBATIM) &&
|
||||
#endif
|
||||
#endif
|
||||
// on linux, it's libopenal.so
|
||||
// try standard name on all platforms
|
||||
!Lib.Load(wxDynamicLibrary::CanonicalizeName(wxT("openal"))))
|
||||
return false;
|
||||
#define loadfn(t, n) do { \
|
||||
if(!(ALFunction.n = (t)Lib.GetSymbol(wxT(#n)))) \
|
||||
return false; \
|
||||
} while(0)
|
||||
//loadfn(LPALENABLE, alEnable);
|
||||
//loadfn(LPALDISABLE, alDisable);
|
||||
//loadfn(LPALISENABLED, alIsEnabled);
|
||||
//loadfn(LPALGETSTRING, alGetString);
|
||||
//loadfn(LPALGETBOOLEANV, alGetBooleanv);
|
||||
//loadfn(LPALGETINTEGERV, alGetIntegerv);
|
||||
//loadfn(LPALGETFLOATV, alGetFloatv);
|
||||
//loadfn(LPALGETDOUBLEV, alGetDoublev);
|
||||
//loadfn(LPALGETBOOLEAN, alGetBoolean);
|
||||
//loadfn(LPALGETINTEGER, alGetInteger);
|
||||
//loadfn(LPALGETFLOAT, alGetFloat);
|
||||
//loadfn(LPALGETDOUBLE, alGetDouble);
|
||||
loadfn(LPALGETERROR, alGetError);
|
||||
//loadfn(LPALISEXTENSIONPRESENT, alIsExtensionPresent);
|
||||
//loadfn(LPALGETPROCADDRESS, alGetProcAddress);
|
||||
//loadfn(LPALGETENUMVALUE, alGetEnumValue);
|
||||
//loadfn(LPALLISTENERF, alListenerf);
|
||||
//loadfn(LPALLISTENER3F, alListener3f);
|
||||
//loadfn(LPALLISTENERFV, alListenerfv);
|
||||
//loadfn(LPALLISTENERI, alListeneri);
|
||||
//loadfn(LPALLISTENER3I, alListener3i);
|
||||
//loadfn(LPALLISTENERIV, alListeneriv);
|
||||
//loadfn(LPALGETLISTENERF, alGetListenerf);
|
||||
//loadfn(LPALGETLISTENER3F, alGetListener3f);
|
||||
//loadfn(LPALGETLISTENERFV, alGetListenerfv);
|
||||
//loadfn(LPALGETLISTENERI, alGetListeneri);
|
||||
//loadfn(LPALGETLISTENER3I, alGetListener3i);
|
||||
//loadfn(LPALGETLISTENERIV, alGetListeneriv);
|
||||
loadfn(LPALGENSOURCES, alGenSources);
|
||||
loadfn(LPALDELETESOURCES, alDeleteSources);
|
||||
//loadfn(LPALISSOURCE, alIsSource);
|
||||
//loadfn(LPALSOURCEF, alSourcef);
|
||||
//loadfn(LPALSOURCE3F, alSource3f);
|
||||
//loadfn(LPALSOURCEFV, alSourcefv);
|
||||
loadfn(LPALSOURCEI, alSourcei);
|
||||
//loadfn(LPALSOURCE3I, alSource3i);
|
||||
//loadfn(LPALSOURCEIV, alSourceiv);
|
||||
//loadfn(LPALGETSOURCEF, alGetSourcef);
|
||||
//loadfn(LPALGETSOURCE3F, alGetSource3f);
|
||||
//loadfn(LPALGETSOURCEFV, alGetSourcefv);
|
||||
loadfn(LPALGETSOURCEI, alGetSourcei);
|
||||
//loadfn(LPALGETSOURCE3I, alGetSource3i);
|
||||
//loadfn(LPALGETSOURCEIV, alGetSourceiv);
|
||||
//loadfn(LPALSOURCEPLAYV, alSourcePlayv);
|
||||
//loadfn(LPALSOURCESTOPV, alSourceStopv);
|
||||
//loadfn(LPALSOURCEREWINDV, alSourceRewindv);
|
||||
//loadfn(LPALSOURCEPAUSEV, alSourcePausev);
|
||||
loadfn(LPALSOURCEPLAY, alSourcePlay);
|
||||
loadfn(LPALSOURCESTOP, alSourceStop);
|
||||
//loadfn(LPALSOURCEREWIND, alSourceRewind);
|
||||
loadfn(LPALSOURCEPAUSE, alSourcePause);
|
||||
loadfn(LPALSOURCEQUEUEBUFFERS, alSourceQueueBuffers);
|
||||
loadfn(LPALSOURCEUNQUEUEBUFFERS, alSourceUnqueueBuffers);
|
||||
loadfn(LPALGENBUFFERS, alGenBuffers);
|
||||
loadfn(LPALDELETEBUFFERS, alDeleteBuffers);
|
||||
//loadfn(LPALISBUFFER, alIsBuffer);
|
||||
loadfn(LPALBUFFERDATA, alBufferData);
|
||||
//loadfn(LPALBUFFERF, alBufferf);
|
||||
//loadfn(LPALBUFFER3F, alBuffer3f);
|
||||
//loadfn(LPALBUFFERFV, alBufferfv);
|
||||
//loadfn(LPALBUFFERI, alBufferi);
|
||||
//loadfn(LPALBUFFER3I, alBuffer3i);
|
||||
//loadfn(LPALBUFFERIV, alBufferiv);
|
||||
//loadfn(LPALGETBUFFERF, alGetBufferf);
|
||||
//loadfn(LPALGETBUFFER3F, alGetBuffer3f);
|
||||
//loadfn(LPALGETBUFFERFV, alGetBufferfv);
|
||||
//loadfn(LPALGETBUFFERI, alGetBufferi);
|
||||
//loadfn(LPALGETBUFFER3I, alGetBuffer3i);
|
||||
//loadfn(LPALGETBUFFERIV, alGetBufferiv);
|
||||
//loadfn(LPALDOPPLERFACTOR, alDopplerFactor);
|
||||
//loadfn(LPALDOPPLERVELOCITY, alDopplerVelocity);
|
||||
//loadfn(LPALSPEEDOFSOUND, alSpeedOfSound);
|
||||
//loadfn(LPALDISTANCEMODEL, alDistanceModel);
|
||||
|
||||
loadfn(LPALCCREATECONTEXT, alcCreateContext);
|
||||
loadfn(LPALCMAKECONTEXTCURRENT, alcMakeContextCurrent);
|
||||
//loadfn(LPALCPROCESSCONTEXT, alcProcessContext);
|
||||
//loadfn(LPALCSUSPENDCONTEXT, alcSuspendContext);
|
||||
loadfn(LPALCDESTROYCONTEXT, alcDestroyContext);
|
||||
//loadfn(LPALCGETCURRENTCONTEXT, alcGetCurrentContext);
|
||||
//loadfn(LPALCGETCONTEXTSDEVICE, alcGetContextsDevice);
|
||||
loadfn(LPALCOPENDEVICE, alcOpenDevice);
|
||||
loadfn(LPALCCLOSEDEVICE, alcCloseDevice);
|
||||
//loadfn(LPALCGETERROR, alcGetError);
|
||||
loadfn(LPALCISEXTENSIONPRESENT, alcIsExtensionPresent);
|
||||
//loadfn(LPALCGETPROCADDRESS, alcGetProcAddress);
|
||||
//loadfn(LPALCGETENUMVALUE, alcGetEnumValue);
|
||||
loadfn(LPALCGETSTRING, alcGetString);
|
||||
//loadfn(LPALCGETINTEGERV, alcGetIntegerv);
|
||||
//loadfn(LPALCCAPTUREOPENDEVICE, alcCaptureOpenDevice);
|
||||
//loadfn(LPALCCAPTURECLOSEDEVICE, alcCaptureCloseDevice);
|
||||
//loadfn(LPALCCAPTURESTART, alcCaptureStart);
|
||||
//loadfn(LPALCCAPTURESTOP, alcCaptureStop);
|
||||
//loadfn(LPALCCAPTURESAMPLES, alcCaptureSamples);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GetOALDevices(wxArrayString &names, wxArrayString &ids)
|
||||
{
|
||||
return OpenAL::GetDevices(names, ids);
|
||||
}
|
||||
|
||||
bool OpenAL::GetDevices(wxArrayString &names, wxArrayString &ids)
|
||||
{
|
||||
if(!OpenAL::LoadOAL())
|
||||
return false;
|
||||
#ifdef ALC_DEVICE_SPECIFIER
|
||||
if(ALFunction.alcIsExtensionPresent(NULL, "ALC_ENUMERATION_EXT") == AL_FALSE)
|
||||
// this extension isn't critical to OpenAL operating
|
||||
return true;
|
||||
const char *devs = ALFunction.alcGetString(NULL, ALC_DEVICE_SPECIFIER);
|
||||
while(*devs) {
|
||||
names.push_back(wxString(devs, wxConvLibc));
|
||||
ids.push_back(names[names.size() - 1]);
|
||||
devs += strlen(devs) + 1;
|
||||
}
|
||||
#else
|
||||
// should work anyway, but must always use default driver
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,123 @@
|
|||
// on win32 and mac, pointer typedefs only happen with AL_NO_PROTOTYPES
|
||||
// on mac, ALC_NO_PROTOTYPES as well
|
||||
|
||||
#define AL_NO_PROTOTYPES 1
|
||||
|
||||
// on mac, alc pointer typedefs ony happen for ALC if ALC_NO_PROTOTYPES
|
||||
// unfortunately, there is a bug in the system headers (use of ALCvoid when
|
||||
// void should be used; shame on Apple for introducing this error, and shame
|
||||
// on Creative for making a typedef to void in the first place)
|
||||
//#define ALC_NO_PROTOTYPES 1
|
||||
|
||||
#include <al.h>
|
||||
#include <alc.h>
|
||||
|
||||
// since the ALC typedefs are broken on Mac:
|
||||
|
||||
#ifdef __WXMAC__
|
||||
typedef ALCcontext * (ALC_APIENTRY *LPALCCREATECONTEXT) (ALCdevice *device, const ALCint *attrlist);
|
||||
typedef ALCboolean (ALC_APIENTRY *LPALCMAKECONTEXTCURRENT)( ALCcontext *context );
|
||||
typedef void (ALC_APIENTRY *LPALCDESTROYCONTEXT)( ALCcontext *context );
|
||||
typedef ALCdevice * (ALC_APIENTRY *LPALCOPENDEVICE)( const ALCchar *devicename );
|
||||
typedef ALCboolean (ALC_APIENTRY *LPALCCLOSEDEVICE)( ALCdevice *device );
|
||||
typedef ALCboolean (ALC_APIENTRY *LPALCISEXTENSIONPRESENT)( ALCdevice *device, const ALCchar *extname );
|
||||
typedef const ALCchar* (ALC_APIENTRY *LPALCGETSTRING)( ALCdevice *device, ALCenum param );
|
||||
#endif
|
||||
|
||||
// no more use of copyrighted OpenAL code just to load the stupid library
|
||||
struct OPENALFNTABLE {
|
||||
//LPALENABLE alEnable;
|
||||
//LPALDISABLE alDisable;
|
||||
//LPALISENABLED alIsEnabled;
|
||||
//LPALGETSTRING alGetString;
|
||||
//LPALGETBOOLEANV alGetBooleanv;
|
||||
//LPALGETINTEGERV alGetIntegerv;
|
||||
//LPALGETFLOATV alGetFloatv;
|
||||
//LPALGETDOUBLEV alGetDoublev;
|
||||
//LPALGETBOOLEAN alGetBoolean;
|
||||
//LPALGETINTEGER alGetInteger;
|
||||
//LPALGETFLOAT alGetFloat;
|
||||
//LPALGETDOUBLE alGetDouble;
|
||||
LPALGETERROR alGetError;
|
||||
LPALISEXTENSIONPRESENT alIsExtensionPresent;
|
||||
//LPALGETPROCADDRESS alGetProcAddress;
|
||||
//LPALGETENUMVALUE alGetEnumValue;
|
||||
//LPALLISTENERF alListenerf;
|
||||
//LPALLISTENER3F alListener3f;
|
||||
//LPALLISTENERFV alListenerfv;
|
||||
//LPALLISTENERI alListeneri;
|
||||
//LPALLISTENER3I alListener3i;
|
||||
//LPALLISTENERIV alListeneriv;
|
||||
//LPALGETLISTENERF alGetListenerf;
|
||||
//LPALGETLISTENER3F alGetListener3f;
|
||||
//LPALGETLISTENERFV alGetListenerfv;
|
||||
//LPALGETLISTENERI alGetListeneri;
|
||||
//LPALGETLISTENER3I alGetListener3i;
|
||||
//LPALGETLISTENERIV alGetListeneriv;
|
||||
LPALGENSOURCES alGenSources;
|
||||
LPALDELETESOURCES alDeleteSources;
|
||||
//LPALISSOURCE alIsSource;
|
||||
//LPALSOURCEF alSourcef;
|
||||
//LPALSOURCE3F alSource3f;
|
||||
//LPALSOURCEFV alSourcefv;
|
||||
LPALSOURCEI alSourcei;
|
||||
//LPALSOURCE3I alSource3i;
|
||||
//LPALSOURCEIV alSourceiv;
|
||||
//LPALGETSOURCEF alGetSourcef;
|
||||
//LPALGETSOURCE3F alGetSource3f;
|
||||
//LPALGETSOURCEFV alGetSourcefv;
|
||||
LPALGETSOURCEI alGetSourcei;
|
||||
//LPALGETSOURCE3I alGetSource3i;
|
||||
//LPALGETSOURCEIV alGetSourceiv;
|
||||
//LPALSOURCEPLAYV alSourcePlayv;
|
||||
//LPALSOURCESTOPV alSourceStopv;
|
||||
//LPALSOURCEREWINDV alSourceRewindv;
|
||||
//LPALSOURCEPAUSEV alSourcePausev;
|
||||
LPALSOURCEPLAY alSourcePlay;
|
||||
LPALSOURCESTOP alSourceStop;
|
||||
//LPALSOURCEREWIND alSourceRewind;
|
||||
LPALSOURCEPAUSE alSourcePause;
|
||||
LPALSOURCEQUEUEBUFFERS alSourceQueueBuffers;
|
||||
LPALSOURCEUNQUEUEBUFFERS alSourceUnqueueBuffers;
|
||||
LPALGENBUFFERS alGenBuffers;
|
||||
LPALDELETEBUFFERS alDeleteBuffers;
|
||||
//LPALISBUFFER alIsBuffer;
|
||||
LPALBUFFERDATA alBufferData;
|
||||
//LPALBUFFERF alBufferf;
|
||||
//LPALBUFFER3F alBuffer3f;
|
||||
//LPALBUFFERFV alBufferfv;
|
||||
//LPALBUFFERI alBufferi;
|
||||
//LPALBUFFER3I alBuffer3i;
|
||||
//LPALBUFFERIV alBufferiv;
|
||||
//LPALGETBUFFERF alGetBufferf;
|
||||
//LPALGETBUFFER3F alGetBuffer3f;
|
||||
//LPALGETBUFFERFV alGetBufferfv;
|
||||
//LPALGETBUFFERI alGetBufferi;
|
||||
//LPALGETBUFFER3I alGetBuffer3i;
|
||||
//LPALGETBUFFERIV alGetBufferiv;
|
||||
//LPALDOPPLERFACTOR alDopplerFactor;
|
||||
//LPALDOPPLERVELOCITY alDopplerVelocity;
|
||||
//LPALSPEEDOFSOUND alSpeedOfSound;
|
||||
//LPALDISTANCEMODEL alDistanceModel;
|
||||
|
||||
LPALCCREATECONTEXT alcCreateContext;
|
||||
LPALCMAKECONTEXTCURRENT alcMakeContextCurrent;
|
||||
//LPALCPROCESSCONTEXT alcProcessContext;
|
||||
//LPALCSUSPENDCONTEXT alcSuspendContext;
|
||||
LPALCDESTROYCONTEXT alcDestroyContext;
|
||||
//LPALCGETCURRENTCONTEXT alcGetCurrentContext;
|
||||
//LPALCGETCONTEXTSDEVICE alcGetContextsDevice;
|
||||
LPALCOPENDEVICE alcOpenDevice;
|
||||
LPALCCLOSEDEVICE alcCloseDevice;
|
||||
//LPALCGETERROR alcGetError;
|
||||
LPALCISEXTENSIONPRESENT alcIsExtensionPresent;
|
||||
//LPALCGETPROCADDRESS alcGetProcAddress;
|
||||
//LPALCGETENUMVALUE alcGetEnumValue;
|
||||
LPALCGETSTRING alcGetString;
|
||||
//LPALCGETINTEGERV alcGetIntegerv;
|
||||
//LPALCCAPTUREOPENDEVICE alcCaptureOpenDevice;
|
||||
//LPALCCAPTURECLOSEDEVICE alcCaptureCloseDevice;
|
||||
//LPALCCAPTURESTART alcCaptureStart;
|
||||
//LPALCCAPTURESTOP alcCaptureStop;
|
||||
//LPALCCAPTURESAMPLES alcCaptureSamples;
|
||||
};
|
|
@ -0,0 +1,773 @@
|
|||
#include "wxvbam.h"
|
||||
#include <algorithm>
|
||||
#include <wx/display.h>
|
||||
/*
|
||||
* disableSfx(F) -> cpuDisableSfx
|
||||
* priority(2) -> threadPriority
|
||||
* saveMoreCPU(F) -> Sm60FPS
|
||||
*
|
||||
* SDL:
|
||||
* -p/--profile=hz
|
||||
*/
|
||||
|
||||
/* not sure how well other compilers support field-init syntax */
|
||||
#define STROPT(n, d, v) {wxT(n), d, &v}
|
||||
#define INTOPT(n, d, v, max, min) {wxT(n), d, NULL, &v, NULL, max, min}
|
||||
#define BOOLOPT(n, d, v) {wxT(n), d, NULL, NULL, NULL, 0, 0, &v}
|
||||
#define ENUMOPT(n, d, v, e) {wxT(n), d, NULL, &v, e}
|
||||
|
||||
opts_t gopts;
|
||||
|
||||
// having the standard menu accels here means they will work even without menus
|
||||
const wxAcceleratorEntry default_accels[] = {
|
||||
wxAcceleratorEntry(wxMOD_CMD, wxT('C'), XRCID("CheatsList")),
|
||||
wxAcceleratorEntry(wxMOD_CMD, wxT('N'), XRCID("NextFrame")),
|
||||
// some ports add ctrl-q anyway, so may as well make it official
|
||||
// maybe make alt-f4 universal as well...
|
||||
// FIXME: ctrl-Q does not work on wxMSW
|
||||
// FIXME: esc does not work on wxMSW
|
||||
wxAcceleratorEntry(wxMOD_NONE, WXK_ESCAPE, wxID_EXIT),
|
||||
wxAcceleratorEntry(wxMOD_CMD, wxT('X'), wxID_EXIT),
|
||||
wxAcceleratorEntry(wxMOD_CMD, wxT('Q'), wxID_EXIT),
|
||||
// FIXME: ctrl-W does not work on wxMSW
|
||||
wxAcceleratorEntry(wxMOD_CMD, wxT('W'), wxID_CLOSE),
|
||||
// load most recent is more commonly used than load other
|
||||
//wxAcceleratorEntry(wxMOD_CMD, wxT('L'), XRCID("Load")),
|
||||
wxAcceleratorEntry(wxMOD_CMD, wxT('L'), XRCID("LoadGameRecent")),
|
||||
wxAcceleratorEntry(wxMOD_NONE, WXK_F1, XRCID("LoadGame01")),
|
||||
wxAcceleratorEntry(wxMOD_NONE, WXK_F2, XRCID("LoadGame02")),
|
||||
wxAcceleratorEntry(wxMOD_NONE, WXK_F3, XRCID("LoadGame03")),
|
||||
wxAcceleratorEntry(wxMOD_NONE, WXK_F4, XRCID("LoadGame04")),
|
||||
wxAcceleratorEntry(wxMOD_NONE, WXK_F5, XRCID("LoadGame05")),
|
||||
wxAcceleratorEntry(wxMOD_NONE, WXK_F6, XRCID("LoadGame06")),
|
||||
wxAcceleratorEntry(wxMOD_NONE, WXK_F7, XRCID("LoadGame07")),
|
||||
wxAcceleratorEntry(wxMOD_NONE, WXK_F8, XRCID("LoadGame08")),
|
||||
wxAcceleratorEntry(wxMOD_NONE, WXK_F9, XRCID("LoadGame09")),
|
||||
wxAcceleratorEntry(wxMOD_NONE, WXK_F10, XRCID("LoadGame10")),
|
||||
wxAcceleratorEntry(wxMOD_NONE, WXK_PAUSE, XRCID("Pause")),
|
||||
wxAcceleratorEntry(wxMOD_CMD, wxT('P'), XRCID("Pause")),
|
||||
wxAcceleratorEntry(wxMOD_CMD, wxT('R'), XRCID("Reset")),
|
||||
// save oldest is more commonly used than save other
|
||||
//wxAcceleratorEntry(wxMOD_CMD, wxT('S'), XRCID("Save")),
|
||||
wxAcceleratorEntry(wxMOD_CMD, wxT('S'), XRCID("SaveGameOldest")),
|
||||
wxAcceleratorEntry(wxMOD_SHIFT, WXK_F1, XRCID("SaveGame01")),
|
||||
wxAcceleratorEntry(wxMOD_SHIFT, WXK_F2, XRCID("SaveGame02")),
|
||||
wxAcceleratorEntry(wxMOD_SHIFT, WXK_F3, XRCID("SaveGame03")),
|
||||
wxAcceleratorEntry(wxMOD_SHIFT, WXK_F4, XRCID("SaveGame04")),
|
||||
wxAcceleratorEntry(wxMOD_SHIFT, WXK_F5, XRCID("SaveGame05")),
|
||||
wxAcceleratorEntry(wxMOD_SHIFT, WXK_F6, XRCID("SaveGame06")),
|
||||
wxAcceleratorEntry(wxMOD_SHIFT, WXK_F7, XRCID("SaveGame07")),
|
||||
wxAcceleratorEntry(wxMOD_SHIFT, WXK_F8, XRCID("SaveGame08")),
|
||||
wxAcceleratorEntry(wxMOD_SHIFT, WXK_F9, XRCID("SaveGame09")),
|
||||
wxAcceleratorEntry(wxMOD_SHIFT, WXK_F10, XRCID("SaveGame10")),
|
||||
// I prefer the SDL ESC key binding
|
||||
//wxAcceleratorEntry(wxMOD_NONE, WXK_ESCAPE, XRCID("ToggleFullscreen"),
|
||||
// alt-enter is more standard anyway
|
||||
wxAcceleratorEntry(wxMOD_ALT, WXK_RETURN, XRCID("ToggleFullscreen")),
|
||||
wxAcceleratorEntry(wxMOD_ALT, wxT('1'), XRCID("JoypadAutofireA")),
|
||||
wxAcceleratorEntry(wxMOD_ALT, wxT('2'), XRCID("JoypadAutofireB")),
|
||||
wxAcceleratorEntry(wxMOD_ALT, wxT('3'), XRCID("JoypadAutofireL")),
|
||||
wxAcceleratorEntry(wxMOD_ALT, wxT('4'), XRCID("JoypadAutofireR")),
|
||||
wxAcceleratorEntry(wxMOD_CMD, wxT('1'), XRCID("VideoLayersBG0")),
|
||||
wxAcceleratorEntry(wxMOD_CMD, wxT('2'), XRCID("VideoLayersBG1")),
|
||||
wxAcceleratorEntry(wxMOD_CMD, wxT('3'), XRCID("VideoLayersBG2")),
|
||||
wxAcceleratorEntry(wxMOD_CMD, wxT('4'), XRCID("VideoLayersBG3")),
|
||||
wxAcceleratorEntry(wxMOD_CMD, wxT('5'), XRCID("VideoLayersOBJ")),
|
||||
wxAcceleratorEntry(wxMOD_CMD, wxT('6'), XRCID("VideoLayersWIN0")),
|
||||
wxAcceleratorEntry(wxMOD_CMD, wxT('7'), XRCID("VideoLayersWIN1")),
|
||||
wxAcceleratorEntry(wxMOD_CMD, wxT('8'), XRCID("VideoLayersOBJWIN")),
|
||||
wxAcceleratorEntry(wxMOD_CMD, wxT('B'), XRCID("Rewind")),
|
||||
// following are not in standard menus
|
||||
// FILExx are filled in when recent menu is filled
|
||||
wxAcceleratorEntry(wxMOD_CMD, WXK_F1, wxID_FILE1),
|
||||
wxAcceleratorEntry(wxMOD_CMD, WXK_F2, wxID_FILE2),
|
||||
wxAcceleratorEntry(wxMOD_CMD, WXK_F3, wxID_FILE3),
|
||||
wxAcceleratorEntry(wxMOD_CMD, WXK_F4, wxID_FILE4),
|
||||
wxAcceleratorEntry(wxMOD_CMD, WXK_F5, wxID_FILE5),
|
||||
wxAcceleratorEntry(wxMOD_CMD, WXK_F6, wxID_FILE6),
|
||||
wxAcceleratorEntry(wxMOD_CMD, WXK_F7, wxID_FILE7),
|
||||
wxAcceleratorEntry(wxMOD_CMD, WXK_F8, wxID_FILE8),
|
||||
wxAcceleratorEntry(wxMOD_CMD, WXK_F9, wxID_FILE9),
|
||||
wxAcceleratorEntry(wxMOD_CMD, WXK_F10, wxID_FILE10),
|
||||
wxAcceleratorEntry(wxMOD_CMD, wxT('0'), XRCID("VideoLayersReset")),
|
||||
wxAcceleratorEntry(wxMOD_CMD, wxT('G'), XRCID("ChangeFilter")),
|
||||
wxAcceleratorEntry(wxMOD_NONE, WXK_NUMPAD_ADD, XRCID("IncreaseVolume")),
|
||||
wxAcceleratorEntry(wxMOD_NONE, WXK_NUMPAD_SUBTRACT, XRCID("DecreaseVolume")),
|
||||
wxAcceleratorEntry(wxMOD_NONE, WXK_NUMPAD_ENTER, XRCID("ToggleSound"))
|
||||
};
|
||||
const int num_def_accels = sizeof(default_accels)/sizeof(default_accels[0]);
|
||||
|
||||
// Note: this must match GUI widget names or GUI won't work
|
||||
// This table's order determines tab order as well
|
||||
const wxChar * const joynames[NUM_KEYS] = {
|
||||
wxT("Up"), wxT("Down"), wxT("Left"), wxT("Right"),
|
||||
wxT("A"), wxT("B"), wxT("L"), wxT("R"),
|
||||
wxT("Select"), wxT("Start"),
|
||||
wxT("MotionUp"), wxT("MotionDown"), wxT("MotionLeft"), wxT("MotionRight"),
|
||||
wxT("AutoA"), wxT("AutoB"),
|
||||
wxT("Speed"), wxT("Capture"), wxT("GS")
|
||||
};
|
||||
|
||||
wxJoyKeyBinding defkeys[NUM_KEYS * 2] = {
|
||||
{ WXK_UP }, { 1, WXJB_AXIS_MINUS, 1 }, { WXK_DOWN }, { 1, WXJB_AXIS_PLUS, 1 },
|
||||
{ WXK_LEFT }, { 0, WXJB_AXIS_MINUS, 1 }, { WXK_RIGHT }, { 0, WXJB_AXIS_PLUS, 1 },
|
||||
{ wxT('X') }, { 0, WXJB_BUTTON, 1 }, { wxT('Z') }, { 1, WXJB_BUTTON, 1 },
|
||||
{ wxT('A') }, { 2, WXJB_BUTTON, 1 }, { wxT('S') }, { 3, WXJB_BUTTON, 1 },
|
||||
{ WXK_BACK }, { 4, WXJB_BUTTON, 1 }, { WXK_RETURN }, { 5, WXJB_BUTTON, 1 },
|
||||
{ WXK_NUMPAD_UP }, { 2, WXJB_AXIS_PLUS, 1 }, { WXK_NUMPAD_DOWN }, { 2, WXJB_AXIS_MINUS, 1 },
|
||||
{ WXK_NUMPAD_LEFT }, { 3, WXJB_AXIS_MINUS, 1 }, { WXK_NUMPAD_RIGHT }, { 3, WXJB_AXIS_PLUS, 1 },
|
||||
{ wxT('W') }, { 0 }, { wxT('Q') }, { 0 },
|
||||
{ WXK_SPACE }, { 0 }, { WXK_F11 }, { 0 },
|
||||
{ 0 } , { 0 }
|
||||
};
|
||||
|
||||
wxAcceleratorEntry_v sys_accels;
|
||||
|
||||
// Note: this table must be sorted in option name order
|
||||
// Both for better user display and for (fast) searching by name
|
||||
opt_desc opts[] = {
|
||||
BOOLOPT("Display/Bilinear", wxTRANSLATE("Use bilinear filter with 3d renderer"), gopts.bilinear),
|
||||
BOOLOPT("Display/DisableStatus", wxTRANSLATE("Disable on-screen status messages"), gopts.no_osd_status),
|
||||
#ifdef MMX
|
||||
BOOLOPT("Display/EnableMMX", wxTRANSLATE("Enable MMX"), cpu_mmx),
|
||||
#endif
|
||||
ENUMOPT("Display/Filter", wxTRANSLATE("Full-screen filter to apply"), gopts.filter,
|
||||
wxTRANSLATE("none|2xsai|super2xsai|supereagle|pixelate|advmame|"
|
||||
"bilinear|bilinearplus|scanlines|tvmode|hq2x|lq2x|"
|
||||
"simple2x|simple3x|hq3x|simple4x|hq4x|plugin")),
|
||||
STROPT ("Display/FilterPlugin", wxTRANSLATE("Filter plugin library"), gopts.filter_plugin),
|
||||
BOOLOPT("Display/Fullscreen", wxTRANSLATE("Enter fullscreen mode at startup"), gopts.fullscreen),
|
||||
INTOPT ("Display/FullscreenDepth", wxTRANSLATE("Fullscreen mode color depth (0 = any)"), gopts.fs_mode.bpp, 0, 999),
|
||||
INTOPT ("Display/FullscreenFreq", wxTRANSLATE("Fullscreen mode frequency (0 = any)"), gopts.fs_mode.refresh, 0, 999),
|
||||
INTOPT ("Display/FullscreenHeight", wxTRANSLATE("Fullscreen mode height (0 = desktop)"), gopts.fs_mode.h, 0, 99999),
|
||||
INTOPT ("Display/FullscreenWidth", wxTRANSLATE("Fullscreen mode width (0 = desktop)"), gopts.fs_mode.w, 0, 99999),
|
||||
ENUMOPT("Display/IFB", wxTRANSLATE("Interframe blending function"), gopts.ifb, wxTRANSLATE("none|smart|motionblur")),
|
||||
INTOPT ("Display/MaxScale", wxTRANSLATE("Maximum scale factor (0 = no limit)"), gopts.max_scale, 0, 100),
|
||||
INTOPT ("Display/MaxThreads", wxTRANSLATE("Maximum number of threads to run filters in"), gopts.max_threads, 1, 8),
|
||||
ENUMOPT("Display/RenderMethod", wxTRANSLATE("Render method; if unsupported, simple method will be used"), gopts.render_method,
|
||||
#ifdef __WXMSW__
|
||||
// try to keep variations to a minimum to ease translation
|
||||
// since the config file stores strings rather than numbers, the
|
||||
// ordering does not have to stay fixed
|
||||
// but the numbers need to match the usage in code
|
||||
wxTRANSLATE("simple|opengl|cairo|direct3d")
|
||||
#else
|
||||
wxTRANSLATE("simple|opengl|cairo")
|
||||
#endif
|
||||
),
|
||||
INTOPT ("Display/Scale", wxTRANSLATE("Default scale factor"), gopts.video_scale, 1, 6),
|
||||
ENUMOPT("Display/ShowSpeed", wxTRANSLATE("Show speed indicator"), gopts.osd_speed, wxTRANSLATE("no|percent|detailed")),
|
||||
BOOLOPT("Display/Stretch", wxTRANSLATE("Retain aspect ratio when resizing"), gopts.retain_aspect),
|
||||
BOOLOPT("Display/Transparent", wxTRANSLATE("Draw on-screen messages transparently"), gopts.osd_transparent),
|
||||
BOOLOPT("Display/Vsync", wxTRANSLATE("Wait for vertical sync"), gopts.vsync),
|
||||
BOOLOPT("GB/AutomaticBorder", wxTRANSLATE("Automatically enable border for Super GameBoy games"), gbBorderAutomatic),
|
||||
STROPT ("GB/BiosFile", wxTRANSLATE("BIOS file to use for GB, if enabled"), gopts.gb_bios),
|
||||
BOOLOPT("GB/Border", wxTRANSLATE("Always enable border"), gbBorderOn),
|
||||
ENUMOPT("GB/EmulatorType", wxTRANSLATE("Type of system to emulate"), gbEmulatorType, wxTRANSLATE("auto|gba|gbc|sgb|sgb2|gb")),
|
||||
BOOLOPT("GB/EnablePrinter", wxTRANSLATE("Enable printer emulation"), gopts.gbprint),
|
||||
INTOPT ("GB/FrameSkip", wxTRANSLATE("Skip frames. Values are 0-9 or -1 to skip automatically based on time."), gopts.gb_frameskip, -1, 9),
|
||||
STROPT ("GB/GBCBiosFile", wxTRANSLATE("BIOS file to use for GBC, if enabled"), gopts.gbc_bios),
|
||||
BOOLOPT("GB/GBCUseBiosFile", wxTRANSLATE("Use the specified BIOS file for GBC"), gopts.gbc_use_bios),
|
||||
BOOLOPT("GB/LCDColor", wxTRANSLATE("Emulate washed colors of LCD"), gbColorOption),
|
||||
ENUMOPT("GB/Palette", wxTRANSLATE("The palette to use"), gbPaletteOption, wxTRANSLATE("default|user1|user2")),
|
||||
{ wxT("GB/Palette0"), wxTRANSLATE("The default palette, as 8 comma-separated 4-digit hex integers (rgb555).") },
|
||||
{ wxT("GB/Palette1"), wxTRANSLATE("The first user palette, as 8 comma-separated 4-digit hex integers (rgb555).") },
|
||||
{ wxT("GB/Palette2"), wxTRANSLATE("The second user palette, as 8 comma-separated 4-digit hex integers (rgb555).") },
|
||||
BOOLOPT("GB/PrintAutoPage", wxTRANSLATE("Automatically gather a full page before printing"), gopts.print_auto_page),
|
||||
BOOLOPT("GB/PrintScreenCap", wxTRANSLATE("Automatically save printouts as screen captures with -print suffix"), gopts.print_screen_cap),
|
||||
STROPT ("GB/ROMDir", wxTRANSLATE("Directory to look for ROM files"), gopts.gb_rom_dir),
|
||||
BOOLOPT("GB/UseBiosFile", wxTRANSLATE("Use the specified BIOS file for GB"), gopts.gb_use_bios),
|
||||
BOOLOPT("GBA/AGBPrinter", wxTRANSLATE("Enable AGB printer"), gopts.agbprint),
|
||||
STROPT ("GBA/BiosFile", wxTRANSLATE("BIOS file to use, if enabled"), gopts.gba_bios),
|
||||
BOOLOPT("GBA/EnableRTC", wxTRANSLATE("Enable RTC (vba-over.ini override is rtcEnabled"), gopts.rtc),
|
||||
ENUMOPT("GBA/FlashSize", wxTRANSLATE("Flash size (kb) (vba-over.ini override is flashSize in bytes)"), gopts.flash_size, wxTRANSLATE("64|128")),
|
||||
INTOPT ("GBA/FrameSkip", wxTRANSLATE("Skip frames. Values are 0-9 or -1 to skip automatically based on time."), gopts.gba_frameskip, -1, 9),
|
||||
#ifndef NO_LINK
|
||||
BOOLOPT("GBA/Joybus", wxTRANSLATE("Enable joybus"), gba_joybus_enabled),
|
||||
STROPT ("GBA/JoybusHost", wxTRANSLATE("Joybus host address"), gopts.joybus_host),
|
||||
BOOLOPT("GBA/Link", wxTRANSLATE("Enable link cable"), gba_link_enabled),
|
||||
BOOLOPT("GBA/LinkFast", wxTRANSLATE("Enable faster network protocol by default"), lanlink.speed),
|
||||
STROPT ("GBA/LinkHost", wxTRANSLATE("Default network link client host"), gopts.link_host),
|
||||
ENUMOPT("GBA/LinkProto", wxTRANSLATE("Default network protocol"), gopts.link_proto, wxTRANSLATE("tcp|udp")),
|
||||
BOOLOPT("GBA/LinkRFU", wxTRANSLATE("Enable RFU for link"), rfu_enabled),
|
||||
INTOPT ("GBA/LinkTimeout", wxTRANSLATE("Link timeout (ms)"), linktimeout, 0, 9999999),
|
||||
#endif
|
||||
STROPT ("GBA/ROMDir", wxTRANSLATE("Directory to look for ROM files"), gopts.gba_rom_dir),
|
||||
ENUMOPT("GBA/SaveType", wxTRANSLATE("Native save (\"battery\") hardware type (vba-over.ini override is saveType integer 0-5)"), gopts.save_type, wxTRANSLATE("auto|eeprom|sram|flash|eeprom+sensor|none")),
|
||||
#if 0 // currently disabled
|
||||
BOOLOPT("GBA/SkipIntro", wxTRANSLATE("Skip intro"), gopts.skip_intro),
|
||||
#endif
|
||||
BOOLOPT("GBA/UseBiosFile", wxTRANSLATE("Use the specified BIOS file"), gopts.gba_use_bios),
|
||||
BOOLOPT("General/ApplyPatches", wxTRANSLATE("Apply IPS/UPS/IPF patches if found"), gopts.apply_patches),
|
||||
BOOLOPT("General/AutoLoadLastState", wxTRANSLATE("Automatically load last saved state"), gopts.autoload_state),
|
||||
BOOLOPT("General/AutoSaveCheatList", wxTRANSLATE("Automatically save and load cheat list"), gopts.autoload_cheats),
|
||||
STROPT ("General/BatteryDir", wxTRANSLATE("Directory to store game save files (relative paths are relative to ROM; blank is config dir)"), gopts.battery_dir),
|
||||
ENUMOPT("General/CaptureFormat", wxTRANSLATE("Screen capture file format"), gopts.cap_format, wxTRANSLATE("png|bmp")),
|
||||
BOOLOPT("General/EnableCheats", wxTRANSLATE("Enable cheats"), cheatsEnabled),
|
||||
BOOLOPT("General/FreezeRecent", wxTRANSLATE("Freeze recent load list"), gopts.recent_freeze),
|
||||
BOOLOPT("General/PauseWhenInactive", wxTRANSLATE("Pause game when main window loses focus"), gopts.defocus_pause),
|
||||
STROPT ("General/RecordingDir", wxTRANSLATE("Directory to store A/V and game recordings (relative paths are relative to ROM)"), gopts.recording_dir),
|
||||
INTOPT ("General/RewindInterval", wxTRANSLATE("Number of seconds between rewind snapshots (0 to disable)"), gopts.rewind_interval, 0, 600),
|
||||
STROPT ("General/ScreenshotDir", wxTRANSLATE("Directory to store screenshots (relative paths are relative to ROM)"), gopts.scrshot_dir),
|
||||
BOOLOPT("General/SkipBios", wxTRANSLATE("Skip BIOS initialization"), skipBios),
|
||||
STROPT ("General/StateDir", wxTRANSLATE("Directory to store saved state files (relative paths are relative to BatteryDir)"), gopts.state_dir),
|
||||
BOOLOPT("General/StateLoadNoBattery", wxTRANSLATE("Do not overwrite native (battery) save when loading state"), skipSaveGameBattery),
|
||||
BOOLOPT("General/StateLoadNoCheat", wxTRANSLATE("Do not overwrite cheat list when loading state"), skipSaveGameCheats),
|
||||
INTOPT ("General/Throttle", wxTRANSLATE("Throttle game speed, even when accelerated (0-1000%, 0 = disabled)"), gopts.throttle, 0, 1000),
|
||||
{ wxT("Joypad/*/*"), wxTRANSLATE("The parameter Joypad/<n>/<button> contains a comma-separated list of key names which map to joypad #<n> button <button>. Button is one of Up, Down, Left, Right, A, B, L, R, Select, Start, MotionUp, MotionDown, MotionLeft, MotionRight, AutoA, AutoB, Speed, Capture, GS") },
|
||||
INTOPT ("Joypad/AutofireThrottle", wxTRANSLATE("The autofire toggle period, in frames (1/60 s)"), gopts.autofire_rate, 1, 1000),
|
||||
INTOPT ("Joypad/Default", wxTRANSLATE("The number of the stick to use in single-player mode"), gopts.default_stick, 1, 4),
|
||||
{ wxT("Keyboard/*"), wxTRANSLATE("The parameter Keyboard/<cmd> contains a comma-separated list of key names (e.g. Alt-Shift-F1). When the named key is pressed, the command <cmd> is executed.") },
|
||||
ENUMOPT("Sound/AudioAPI", wxTRANSLATE("Sound API; if unsupported, default API will be used"), gopts.audio_api,
|
||||
#ifdef __WXMSW__
|
||||
// see comment on Display/RenderMethod
|
||||
wxTRANSLATE("sdl|openal|directsound|xaudio2")
|
||||
#else
|
||||
wxTRANSLATE("sdl|openal")
|
||||
#endif
|
||||
),
|
||||
INTOPT ("Sound/Buffers", wxTRANSLATE("Number of sound buffers"), gopts.audio_buffers, 2, 10),
|
||||
INTOPT ("Sound/Enable", wxTRANSLATE("Bit mask of sound channels to enable"), gopts.sound_en, 0, 0x30f),
|
||||
INTOPT ("Sound/GBAFiltering", wxTRANSLATE("GBA sound filtering (%)"), gopts.gba_sound_filter, 0, 100),
|
||||
BOOLOPT("Sound/GBAInterpolation", wxTRANSLATE("GBA sound interpolation"), soundInterpolation),
|
||||
BOOLOPT("Sound/GBDeclicking", wxTRANSLATE("GB sound declicking"), gopts.gb_declick),
|
||||
INTOPT ("Sound/GBEcho", wxTRANSLATE("GB echo effect (%)"), gopts.gb_echo, 0, 100),
|
||||
BOOLOPT("Sound/GBEnableEffects", wxTRANSLATE("Enable GB sound effects"), gb_effects_config.enabled),
|
||||
INTOPT ("Sound/GBStereo", wxTRANSLATE("GB stereo effect (%)"), gopts.gb_stereo, 0, 100),
|
||||
BOOLOPT("Sound/GBSurround", wxTRANSLATE("GB surround sound effect (%)"), gb_effects_config.surround),
|
||||
ENUMOPT("Sound/Quality", wxTRANSLATE("Sound sample rate (kHz)"), gopts.sound_qual, wxTRANSLATE("48|44|22|11")),
|
||||
BOOLOPT("Sound/Synchronize", wxTRANSLATE("Synchronize game to audio"), synchronize),
|
||||
INTOPT ("Sound/Volume", wxTRANSLATE("Sound volume (%)"), gopts.sound_vol, 0, 200),
|
||||
};
|
||||
const int num_opts = sizeof(opts)/sizeof(opts[0]);
|
||||
|
||||
|
||||
// This constructor only works with globally allocated gopts. It relies on
|
||||
// the default value of every non-object to be 0.
|
||||
opts_t::opts_t()
|
||||
{
|
||||
gba_frameskip = -1;
|
||||
gb_frameskip = -1;
|
||||
#ifdef __WXMSW__
|
||||
audio_api = AUD_DIRECTSOUND;
|
||||
#endif
|
||||
video_scale = 3;
|
||||
retain_aspect = true;
|
||||
max_threads = wxThread::GetCPUCount();
|
||||
if(max_threads > 8)
|
||||
max_threads = 8;
|
||||
if(max_threads < 0)
|
||||
max_threads = 2;
|
||||
audio_buffers = 5;
|
||||
sound_en = 0x30f;
|
||||
sound_vol = 100;
|
||||
sound_qual = 1;
|
||||
gb_echo = 20;
|
||||
gb_stereo = 15;
|
||||
gb_declick = true;
|
||||
gba_sound_filter = 50;
|
||||
bilinear = true;
|
||||
default_stick = 1;
|
||||
for(int i = 0; i < NUM_KEYS; i++) {
|
||||
if(defkeys[i*2].key)
|
||||
joykey_bindings[0][i].push_back(defkeys[i*2]);
|
||||
if(defkeys[i*2+1].joy)
|
||||
joykey_bindings[0][i].push_back(defkeys[i*2+1]);
|
||||
}
|
||||
recent = new wxFileHistory(10);
|
||||
autofire_rate = 1;
|
||||
gbprint = print_auto_page = true;
|
||||
apply_patches = true;
|
||||
}
|
||||
|
||||
// for binary_search() and friends
|
||||
bool opt_lt(const opt_desc &opt1, const opt_desc &opt2)
|
||||
{
|
||||
return wxStrcmp(opt1.opt, opt2.opt) < 0;
|
||||
}
|
||||
|
||||
// FIXME: simulate MakeInstanceFilename(VBA.ini) using subkeys (Slave%d/*)
|
||||
|
||||
void load_opts()
|
||||
{
|
||||
// just for sanity...
|
||||
bool did_init = false;
|
||||
if(did_init)
|
||||
return;
|
||||
did_init = true;
|
||||
|
||||
// Translations can't be initialized in static structures (before locale
|
||||
// is initialized), so do them now
|
||||
for(int i = 0; i < num_opts; i++)
|
||||
opts[i].desc = wxGetTranslation(opts[i].desc);
|
||||
// enumvals should not be translated, since they would cause config file
|
||||
// change after lang change
|
||||
// instead, translate when presented to user
|
||||
|
||||
wxConfig *cfg = wxGetApp().cfg;
|
||||
cfg->SetPath(wxT("/"));
|
||||
|
||||
// enure there are no unknown options present
|
||||
// note that items cannot be deleted until after loop or loop will fail
|
||||
wxArrayString item_del, grp_del;
|
||||
long grp_idx;
|
||||
wxString s;
|
||||
bool cont;
|
||||
for(cont = cfg->GetFirstEntry(s, grp_idx); cont;
|
||||
cont = cfg->GetNextEntry(s, grp_idx)) {
|
||||
wxLogWarning(_("Invalid option %s present; removing if possible"), s.c_str());
|
||||
item_del.push_back(s);
|
||||
}
|
||||
for(cont = cfg->GetFirstGroup(s, grp_idx); cont;
|
||||
cont = cfg->GetNextGroup(s, grp_idx)) {
|
||||
// ignore wxWidgets-managed global library settings
|
||||
if(s == wxT("wxWindows"))
|
||||
continue;
|
||||
// ignore file history
|
||||
if(s == wxT("Recent"))
|
||||
continue;
|
||||
cfg->SetPath(s);
|
||||
int poff = s.size();
|
||||
long entry_idx;
|
||||
wxString e;
|
||||
for(cont = cfg->GetFirstGroup(e, entry_idx); cont;
|
||||
cont = cfg->GetNextGroup(e, entry_idx)) {
|
||||
// the only one with subgroups
|
||||
if(s == wxT("Joypad") && e.size() == 1 && e[0] >= wxT('1') && e[0] <= wxT('4')) {
|
||||
s.append(wxT('/'));
|
||||
s.append(e);
|
||||
s.append(wxT('/'));
|
||||
int poff2 = s.size();
|
||||
cfg->SetPath(e);
|
||||
long key_idx;
|
||||
for(cont = cfg->GetFirstGroup(e, key_idx); cont;
|
||||
cont = cfg->GetNextGroup(e, key_idx)) {
|
||||
s.append(e);
|
||||
wxLogWarning(_("Invalid option group %s present; removing if possible"), s.c_str());
|
||||
grp_del.push_back(s);
|
||||
s.resize(poff2);
|
||||
}
|
||||
for(cont = cfg->GetFirstEntry(e, key_idx); cont;
|
||||
cont = cfg->GetNextEntry(e, key_idx)) {
|
||||
int i;
|
||||
for(i = 0; i < NUM_KEYS; i++)
|
||||
if(e == joynames[i])
|
||||
break;
|
||||
if(i == NUM_KEYS) {
|
||||
s.append(e);
|
||||
wxLogWarning(_("Invalid option %s present; removing if possible"), s.c_str());
|
||||
item_del.push_back(s);
|
||||
s.resize(poff2);
|
||||
}
|
||||
}
|
||||
s.resize(poff);
|
||||
cfg->SetPath(wxT("/"));
|
||||
cfg->SetPath(s);
|
||||
} else {
|
||||
s.append(wxT('/'));
|
||||
s.append(e);
|
||||
wxLogWarning(_("Invalid option group %s present; removing if possible"), s.c_str());
|
||||
grp_del.push_back(s);
|
||||
s.resize(poff);
|
||||
}
|
||||
}
|
||||
for(cont = cfg->GetFirstEntry(e, entry_idx); cont;
|
||||
cont = cfg->GetNextEntry(e, entry_idx)) {
|
||||
// kb options come from a different list
|
||||
if(s == wxT("Keyboard")) {
|
||||
const cmditem dummy = { e.c_str() };
|
||||
if(!std::binary_search(&cmdtab[0], &cmdtab[ncmds], dummy, cmditem_lt)) {
|
||||
s.append(wxT('/'));
|
||||
s.append(e);
|
||||
wxLogWarning(_("Invalid option %s present; removing if possible"), s.c_str());
|
||||
item_del.push_back(s);
|
||||
s.resize(poff);
|
||||
}
|
||||
} else {
|
||||
s.append(wxT('/'));
|
||||
s.append(e);
|
||||
const opt_desc dummy = { s.c_str() };
|
||||
if(!std::binary_search(&opts[0], &opts[num_opts], dummy, opt_lt)) {
|
||||
wxLogWarning(_("Invalid option %s present; removing if possible"), s.c_str());
|
||||
item_del.push_back(s);
|
||||
}
|
||||
s.resize(poff);
|
||||
}
|
||||
}
|
||||
cfg->SetPath(wxT("/"));
|
||||
}
|
||||
for(int i = 0; i < item_del.size(); i++)
|
||||
cfg->DeleteEntry(item_del[i]);
|
||||
for(int i = 0; i < grp_del.size(); i++)
|
||||
cfg->DeleteGroup(grp_del[i]);
|
||||
|
||||
// now read actual values and set to default if unset
|
||||
// config file will be updated with unset options
|
||||
cfg->SetRecordDefaults();
|
||||
for(int i = 0; i < num_opts; i++) {
|
||||
opt_desc &opt = opts[i];
|
||||
if(opt.stropt) {
|
||||
bool gotit = cfg->Read(opt.opt, opt.stropt, *opt.stropt);
|
||||
opt.curstr = *opt.stropt;
|
||||
} else if(opt.enumvals) {
|
||||
opt.curint = *opt.intopt;
|
||||
bool gotit = cfg->Read(opt.opt, &s);
|
||||
const wxChar *ev = opt.enumvals;
|
||||
if(gotit && s.size()) {
|
||||
// wx provides no case-insensitive Find()
|
||||
s.MakeLower();
|
||||
for( ; (ev = wxStrstr(ev, (const wxChar *)s.c_str())); ev++) {
|
||||
if(ev != opt.enumvals && ev[-1] != wxT('|'))
|
||||
continue;
|
||||
if(!ev[s.size()] || ev[s.size()] == wxT('|'))
|
||||
break;
|
||||
}
|
||||
if(!ev) {
|
||||
opt.curint = 0;
|
||||
ev = opt.enumvals;
|
||||
const wxChar *evx = wxGetTranslation(ev);
|
||||
bool isx = wxStrcmp(ev, evx) != 0;
|
||||
// technically, the translation for this string could incorproate
|
||||
// the equals sign if necessary instead of doing it this way
|
||||
wxLogWarning(_("Invalid value %s for option %s; valid values are %s%s%s"),
|
||||
s.c_str(), opt.opt, ev,
|
||||
isx ? wxT(" = ") : wxT(""),
|
||||
isx ? evx : wxT(""));
|
||||
s = wxString(ev, wxStrchr(ev, wxT('|')) - ev);
|
||||
cfg->Write(opt.opt, s);
|
||||
} else {
|
||||
const wxChar *ev2;
|
||||
for(ev2 = opt.enumvals, opt.curint = 0; ev2 != ev; opt.curint++)
|
||||
ev2 = wxStrchr(ev2, wxT('|')) + 1;
|
||||
}
|
||||
*opt.intopt = opt.curint;
|
||||
} else {
|
||||
for(int i = 0; i != opt.curint; i++)
|
||||
ev = wxStrchr(ev, wxT('|')) + 1;
|
||||
const wxChar *ev2 = wxStrchr(ev, wxT('|'));
|
||||
s = ev2 ? wxString(ev, ev2 - ev) : wxString(ev);
|
||||
cfg->Write(opt.opt, s);
|
||||
}
|
||||
} else if(opt.intopt) {
|
||||
cfg->Read(opt.opt, &opt.curint, *opt.intopt);
|
||||
if(opt.curint < opt.min || opt.curint > opt.max) {
|
||||
wxLogWarning(_("Invalid value %d for option %s; valid values are %d - %d"), opt.curint, opt.opt, opt.min, opt.max);
|
||||
} else
|
||||
*opt.intopt = opt.curint;
|
||||
} else if(opt.boolopt) {
|
||||
cfg->Read(opt.opt, opt.boolopt, *opt.boolopt);
|
||||
opt.curbool = *opt.boolopt;
|
||||
}
|
||||
}
|
||||
// GB/Palette[0-2] is special
|
||||
for(int i = 0; i < 3; i++) {
|
||||
wxString optn;
|
||||
optn.Printf(wxT("GB/Palette%d"), i);
|
||||
wxString val;
|
||||
const opt_desc dummy = { optn.c_str() };
|
||||
opt_desc *opt = std::lower_bound(&opts[0], &opts[num_opts], dummy, opt_lt);
|
||||
wxString entry;
|
||||
for(int j = 0; j < 8; j++) {
|
||||
// stupid wxString.Printf doesn't support printing at offset
|
||||
entry.Printf(wxT("%04X,"), (int)systemGbPalette[i * 8 + j]);
|
||||
val.append(entry);
|
||||
}
|
||||
val.resize(val.size() - 1);
|
||||
cfg->Read(optn, &val, val);
|
||||
opt->curstr = val;
|
||||
for(int j = 0, cpos = 0; j < 8; j++) {
|
||||
int start = cpos;
|
||||
cpos = val.find(wxT(','), cpos);
|
||||
if(cpos == wxString::npos)
|
||||
cpos = val.size();
|
||||
long ival;
|
||||
// ignoring errors; if the value is bad, palette will be corrupt
|
||||
// -- tough.
|
||||
// stupid wxString.ToLong doesn't support start @ offset
|
||||
entry = val.substr(start, cpos - start);
|
||||
entry.ToLong(&ival, 16);
|
||||
systemGbPalette[i * 8 + j] = ival;
|
||||
if(cpos != val.size())
|
||||
cpos++;
|
||||
}
|
||||
}
|
||||
// joypad is special
|
||||
for(int i = 0; i < 4; i++) {
|
||||
for(int j = 0; j < NUM_KEYS; j++) {
|
||||
wxString optname;
|
||||
optname.Printf(wxT("Joypad/%d/%s"), i + 1, joynames[j]);
|
||||
bool gotit = cfg->Read(optname, &s);
|
||||
if(gotit) {
|
||||
gopts.joykey_bindings[i][j] = wxJoyKeyTextCtrl::FromString(s);
|
||||
if(s.size() && !gopts.joykey_bindings[i][j].size())
|
||||
wxLogWarning(_("Invalid key binding %s for %s"), s.c_str(), optname.c_str());
|
||||
} else {
|
||||
s = wxJoyKeyTextCtrl::ToString(gopts.joykey_bindings[i][j]);
|
||||
cfg->Write(optname, s);
|
||||
}
|
||||
}
|
||||
}
|
||||
// keyboard is special
|
||||
// Keyboard does not get written with defaults
|
||||
wxString kbopt(wxT("Keyboard/"));
|
||||
int kboff = kbopt.size();
|
||||
for(int i = 0; i < ncmds; i++) {
|
||||
kbopt.resize(kboff);
|
||||
kbopt.append(cmdtab[i].cmd);
|
||||
if(cfg->Read(kbopt, &s) && s.size()) {
|
||||
wxAcceleratorEntry_v val = wxKeyTextCtrl::FromString(s);
|
||||
if(!val.size())
|
||||
wxLogWarning(_("Invalid key binding %s for %s"), s.c_str(), kbopt.c_str());
|
||||
else {
|
||||
for(int j = 0; j < val.size(); j++)
|
||||
val[j].Set(val[j].GetFlags(), val[j].GetKeyCode(),
|
||||
cmdtab[i].cmd_id);
|
||||
gopts.accels.insert(gopts.accels.end(),
|
||||
val.begin(), val.end());
|
||||
}
|
||||
}
|
||||
}
|
||||
// recent is special
|
||||
// Recent does not get written with defaults
|
||||
cfg->SetPath(wxT("/Recent"));
|
||||
gopts.recent->Load(*cfg);
|
||||
cfg->SetPath(wxT("/"));
|
||||
cfg->Flush();
|
||||
}
|
||||
|
||||
// Note: run load_opts() first to guarantee all config opts exist
|
||||
void update_opts()
|
||||
{
|
||||
wxConfig *cfg = wxGetApp().cfg;
|
||||
for(int i = 0; i < num_opts; i++) {
|
||||
opt_desc &opt = opts[i];
|
||||
if(opt.stropt) {
|
||||
if(opt.curstr != *opt.stropt) {
|
||||
opt.curstr = *opt.stropt;
|
||||
cfg->Write(opt.opt, opt.curstr);
|
||||
}
|
||||
} else if(opt.enumvals) {
|
||||
if(*opt.intopt != opt.curint) {
|
||||
opt.curint = *opt.intopt;
|
||||
const wxChar *ev = opt.enumvals;
|
||||
for(int i = 0; i != opt.curint; i++)
|
||||
ev = wxStrchr(ev, wxT('|')) + 1;
|
||||
const wxChar *ev2 = wxStrchr(ev, wxT('|'));
|
||||
wxString s = ev2 ? wxString(ev, ev2 - ev) : wxString(ev);
|
||||
cfg->Write(opt.opt, s);
|
||||
}
|
||||
} else if(opt.intopt) {
|
||||
if(*opt.intopt != opt.curint)
|
||||
cfg->Write(opt.opt, (opt.curint = *opt.intopt));
|
||||
} else if(opt.boolopt) {
|
||||
if(*opt.boolopt != opt.curbool)
|
||||
cfg->Write(opt.opt, (opt.curbool = *opt.boolopt));
|
||||
}
|
||||
}
|
||||
// gbpalette requires doing the conversion to string over
|
||||
// it may trigger a write even with no changes if # of digits changes
|
||||
for(int i = 0; i < 3; i++) {
|
||||
wxString optn;
|
||||
optn.Printf(wxT("GB/Palette%d"), i);
|
||||
const opt_desc dummy = { optn.c_str() };
|
||||
opt_desc *opt = std::lower_bound(&opts[0], &opts[num_opts], dummy, opt_lt);
|
||||
wxString val;
|
||||
wxString entry;
|
||||
for(int j = 0; j < 8; j++) {
|
||||
// stupid wxString.Printf doesn't support printing at offset
|
||||
entry.Printf(wxT("%04X,"), (int)systemGbPalette[i * 8 + j]);
|
||||
val.append(entry);
|
||||
}
|
||||
val.resize(val.size() - 1);
|
||||
if(val != opt->curstr) {
|
||||
opt->curstr = val;
|
||||
cfg->Write(optn, val);
|
||||
}
|
||||
}
|
||||
// for joypad, use ToString comparisons. It may trigger changes
|
||||
// even when there are none (e.g. multi-binding ordering changes)
|
||||
// not worth worrying about
|
||||
for(int i = 0; i < 4; i++) {
|
||||
for(int j = 0; j < NUM_KEYS; j++) {
|
||||
wxString s, o;
|
||||
wxString optname;
|
||||
optname.Printf(wxT("Joypad/%d/%s"), i + 1, joynames[j]);
|
||||
s = wxJoyKeyTextCtrl::ToString(gopts.joykey_bindings[i][j]);
|
||||
cfg->Read(optname, &o);
|
||||
if(o != s)
|
||||
cfg->Write(optname, s);
|
||||
}
|
||||
}
|
||||
// for keyboard, first remove any commands that aren't bound at all
|
||||
if(cfg->HasGroup(wxT("/Keyboard"))) {
|
||||
cfg->SetPath(wxT("/Keyboard"));
|
||||
wxString s;
|
||||
long entry_idx;
|
||||
wxArrayString item_del;
|
||||
|
||||
for(bool cont = cfg->GetFirstEntry(s, entry_idx); cont;
|
||||
cont = cfg->GetNextEntry(s, entry_idx)) {
|
||||
const cmditem dummy = { s.c_str() };
|
||||
cmditem *cmd = std::lower_bound(&cmdtab[0], &cmdtab[ncmds], dummy, cmditem_lt);
|
||||
int i;
|
||||
for(i = 0; i < gopts.accels.size(); i++)
|
||||
if(gopts.accels[i].GetCommand() == cmd->cmd_id)
|
||||
break;
|
||||
if(i == gopts.accels.size())
|
||||
item_del.push_back(s);
|
||||
}
|
||||
for(int i = 0; i < item_del.size(); i++)
|
||||
cfg->DeleteEntry(item_del[i]);
|
||||
}
|
||||
// then, add/update the commands that are bound
|
||||
// even if only ordering changed, a write will be triggered.
|
||||
// nothing to worry about...
|
||||
if(gopts.accels.size())
|
||||
cfg->SetPath(wxT("/Keyboard"));
|
||||
for(wxAcceleratorEntry_v::iterator i = gopts.accels.begin();
|
||||
i < gopts.accels.end(); i++) {
|
||||
int cmd_id = i->GetCommand();
|
||||
int cmd;
|
||||
for(cmd = 0; cmd < ncmds; cmd++)
|
||||
if(cmdtab[cmd].cmd_id == cmd_id)
|
||||
break;
|
||||
wxAcceleratorEntry_v::iterator j;
|
||||
for(j = i + 1; j < gopts.accels.end(); j++)
|
||||
if(j->GetCommand() != cmd_id)
|
||||
break;
|
||||
wxAcceleratorEntry_v nv(i, j);
|
||||
wxString nvs = wxKeyTextCtrl::ToString(nv);
|
||||
if(nvs != cfg->Read(cmdtab[cmd].cmd))
|
||||
cfg->Write(cmdtab[cmd].cmd, nvs);
|
||||
}
|
||||
cfg->SetPath(wxT("/"));
|
||||
// recent items are updated separately
|
||||
cfg->Flush();
|
||||
}
|
||||
|
||||
bool opt_set(const wxChar *name, const wxChar *val)
|
||||
{
|
||||
const opt_desc dummy = { name };
|
||||
const opt_desc *opt = std::lower_bound(&opts[0], &opts[num_opts], dummy, opt_lt);
|
||||
if(!wxStrcmp(name, opt->opt)) {
|
||||
if(opt->stropt)
|
||||
*opt->stropt = wxString(val);
|
||||
else if(opt->boolopt) {
|
||||
if(!*val || val[1] || (*val != wxT('0') && *val != wxT('1')))
|
||||
wxLogWarning(_("Invalid flag option %s - %s ignored"),
|
||||
name, val);
|
||||
else
|
||||
*opt->boolopt = *val == wxT('1');
|
||||
} else if(opt->enumvals) {
|
||||
wxString s(val);
|
||||
s.MakeLower();
|
||||
const wxChar *ev;
|
||||
for(ev = opt->enumvals; (ev = wxStrstr(ev, (const wxChar *)s.c_str())); ev++) {
|
||||
if(ev != opt->enumvals && ev[-1] != wxT('|'))
|
||||
continue;
|
||||
if(!ev[s.size()] || ev[s.size()] == wxT('|'))
|
||||
break;
|
||||
}
|
||||
if(!ev) {
|
||||
const wxChar *evx = wxGetTranslation(opt->enumvals);
|
||||
bool isx = wxStrcmp(opt->enumvals, evx) != 0;
|
||||
// technically, the translation for this string could incorproate
|
||||
// the equals sign if necessary instead of doing it this way
|
||||
wxLogWarning(_("Invalid value %s for option %s; valid values are %s%s%s"),
|
||||
s.c_str(), opt->opt, opt->enumvals,
|
||||
isx ? wxT(" = ") : wxT(""),
|
||||
isx ? evx : wxT(""));
|
||||
} else {
|
||||
const wxChar *ev2;
|
||||
int val;
|
||||
for(ev2 = opt->enumvals, val = 0; ev2 != ev; val++)
|
||||
ev2 = wxStrchr(ev2, wxT('|')) + 1;
|
||||
*opt->intopt = val;
|
||||
}
|
||||
} else if(opt->intopt) {
|
||||
const wxString s(val);
|
||||
long ival;
|
||||
if(!s.ToLong(&ival) || ival < opt->min || ival > opt->max)
|
||||
wxLogWarning(_("Invalid value %d for option %s; valid values are %d - %d"), ival, name, opt->min, opt->max);
|
||||
else
|
||||
*opt->intopt = ival;
|
||||
} else {
|
||||
// GB/Palette[0-2] is virtual
|
||||
for(int i = 0; i < 3; i++) {
|
||||
wxString optn;
|
||||
optn.Printf(wxT("GB/Palette%d"), i);
|
||||
if(optn != name)
|
||||
continue;
|
||||
wxString vals(val);
|
||||
for(int j = 0, cpos = 0; j < 8; j++) {
|
||||
int start = cpos;
|
||||
cpos = vals.find(wxT(','), cpos);
|
||||
if(cpos == wxString::npos)
|
||||
cpos = vals.size();
|
||||
long ival;
|
||||
// ignoring errors; if the value is bad, palette will be corrupt
|
||||
// -- tough.
|
||||
// stupid wxString.ToLong doesn't support start @ offset
|
||||
wxString entry = vals.substr(start, cpos - start);
|
||||
entry.ToLong(&ival, 16);
|
||||
systemGbPalette[i * 8 + j] = ival;
|
||||
if(cpos != vals.size())
|
||||
cpos++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
const wxChar *slat = wxStrchr(name, wxT('/'));
|
||||
if(!slat)
|
||||
return false;
|
||||
if(!wxStrncmp(name, wxT("Keyboard"), (int)(slat - name))) {
|
||||
const cmditem dummy2 = { slat + 1 };
|
||||
cmditem *cmd = std::lower_bound(&cmdtab[0], &cmdtab[ncmds], dummy2, cmditem_lt);
|
||||
if(cmd == &cmdtab[ncmds] || wxStrcmp(slat + 1, cmd->cmd))
|
||||
return false;
|
||||
for(wxAcceleratorEntry_v::iterator i = gopts.accels.begin();
|
||||
i < gopts.accels.end(); i++)
|
||||
if(i->GetCommand() == cmd->cmd_id) {
|
||||
wxAcceleratorEntry_v::iterator j;
|
||||
for(j = i; j < gopts.accels.end(); j++)
|
||||
if(j->GetCommand() != cmd->cmd_id)
|
||||
break;
|
||||
gopts.accels.erase(i, j);
|
||||
break;
|
||||
}
|
||||
if(*val) {
|
||||
wxAcceleratorEntry_v aval = wxKeyTextCtrl::FromString(val);
|
||||
for(int i = 0; i < aval.size(); i++)
|
||||
aval[i].Set(aval[i].GetFlags(), aval[i].GetKeyCode(),
|
||||
cmd->cmd_id);
|
||||
if(!aval.size())
|
||||
wxLogWarning(_("Invalid key binding %s for %s"), val, name);
|
||||
else
|
||||
gopts.accels.insert(gopts.accels.end(), aval.begin(), aval.end());
|
||||
}
|
||||
return true;
|
||||
} else if(!wxStrncmp(name, wxT("Joypad"), (int)(slat - name))) {
|
||||
if(slat[1] < wxT('1') || slat[1] > wxT('4') || slat[2] != wxT('/'))
|
||||
return false;
|
||||
int jno = slat[1] - wxT('1');
|
||||
int kno;
|
||||
for(kno = 0; kno < NUM_KEYS; kno++)
|
||||
if(!wxStrcmp(joynames[kno], slat + 3))
|
||||
break;
|
||||
if(kno == NUM_KEYS)
|
||||
return false;
|
||||
if(!*val)
|
||||
gopts.joykey_bindings[jno][kno].clear();
|
||||
else {
|
||||
wxJoyKeyBinding_v b = wxJoyKeyTextCtrl::FromString(val);
|
||||
if(!b.size())
|
||||
wxLogWarning(_("Invalid key binding %s for %s"), val, name);
|
||||
else
|
||||
gopts.joykey_bindings[jno][kno] = b;
|
||||
}
|
||||
return true;
|
||||
} else
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,142 @@
|
|||
#ifndef WX_OPTS_H
|
||||
#define WX_OPTS_H
|
||||
|
||||
#define NUM_KEYS 19
|
||||
extern const wxChar * const joynames[NUM_KEYS];
|
||||
extern wxJoyKeyBinding defkeys[NUM_KEYS * 2]; // keyboard + joystick defaults
|
||||
|
||||
extern struct opts_t {
|
||||
opts_t();
|
||||
// while I would normally put large objects in front to reduce gaps,
|
||||
// I instead organized this by opts.cpp table order
|
||||
|
||||
/// Display
|
||||
bool bilinear;
|
||||
// bool cpu_mmx;
|
||||
bool no_osd_status;
|
||||
int filter;
|
||||
wxString filter_plugin;
|
||||
int ifb;
|
||||
bool fullscreen;
|
||||
wxVideoMode fs_mode;
|
||||
int max_scale;
|
||||
int max_threads;
|
||||
int render_method;
|
||||
int video_scale;
|
||||
int osd_speed;
|
||||
bool retain_aspect;
|
||||
bool osd_transparent;
|
||||
bool vsync;
|
||||
|
||||
/// GB
|
||||
// bool gbBorderAutomatic;
|
||||
wxString gb_bios;
|
||||
// bool gbBorderOn;
|
||||
// int gbEmulatorType;
|
||||
bool gbprint; // gbSerialFunction
|
||||
int gb_frameskip; // systemFrameSkip
|
||||
wxString gbc_bios;
|
||||
bool gbc_use_bios;
|
||||
// bool gbcColorOption;
|
||||
// bool gbPaletteOption;
|
||||
// u16 systemGbPalette[8*3];
|
||||
bool print_auto_page, print_screen_cap;
|
||||
wxString gb_rom_dir;
|
||||
bool gb_use_bios;
|
||||
|
||||
/// GBA
|
||||
bool agbprint; // AGBPrintEnable()
|
||||
wxString gba_bios;
|
||||
bool rtc; // rtcEnable // has per-game override
|
||||
int flash_size; // flashSetSize // has per-game override
|
||||
int gba_frameskip; // systemFrameSkip
|
||||
// bool gba_joybus_enabled;
|
||||
wxString joybus_host;
|
||||
// bool gba_link_enabled;
|
||||
// bool lanlink.speed;
|
||||
wxString link_host;
|
||||
int link_proto;
|
||||
// bool rfu_enabled;
|
||||
// int linktimeout;
|
||||
wxString gba_rom_dir;
|
||||
bool skip_intro;
|
||||
int save_type; // cpuSaveType
|
||||
bool gba_use_bios;
|
||||
|
||||
/// General
|
||||
bool apply_patches;
|
||||
bool autoload_state, autoload_cheats;
|
||||
wxString battery_dir;
|
||||
int cap_format;
|
||||
// bool cheatsEnabled;
|
||||
bool recent_freeze;
|
||||
bool defocus_pause;
|
||||
wxString recording_dir;
|
||||
int rewind_interval;
|
||||
wxString scrshot_dir;
|
||||
// bool skipBios;
|
||||
wxString state_dir;
|
||||
// bool skipSaveGameBattery, skipSaveGameCheats;
|
||||
int throttle; // updateThrottle()
|
||||
|
||||
/// Joypad
|
||||
wxJoyKeyBinding_v joykey_bindings[4][NUM_KEYS];
|
||||
int autofire_rate;
|
||||
int default_stick;
|
||||
|
||||
/// Keyboard
|
||||
wxAcceleratorEntry_v accels;
|
||||
|
||||
/// Sound
|
||||
int audio_api;
|
||||
int audio_buffers;
|
||||
wxString audio_dev;
|
||||
int sound_en; // soundSetEnable()
|
||||
int gba_sound_filter;
|
||||
// int soundInterpolation;
|
||||
bool gb_declick;
|
||||
int gb_echo;
|
||||
// bool gb_effects_config.enabled;
|
||||
bool dsound_hw_accel;
|
||||
int gb_stereo;
|
||||
// int gb_effects_config.surround;
|
||||
int sound_qual; // soundSetSampleRate() / gbSoundSetSampleRate()
|
||||
// bool synchronize;
|
||||
int sound_vol; // soundSetVolume()
|
||||
bool upmix; // xa2 only
|
||||
|
||||
/// Recent
|
||||
wxFileHistory *recent;
|
||||
|
||||
/// wxWindows
|
||||
// wxWidgets-generated options (opaque)
|
||||
} gopts;
|
||||
|
||||
extern struct opt_desc {
|
||||
const wxChar *opt, *desc;
|
||||
wxString *stropt;
|
||||
int *intopt;
|
||||
const wxChar *enumvals;
|
||||
int min, max;
|
||||
bool *boolopt;
|
||||
// current configured value
|
||||
wxString curstr;
|
||||
int curint;
|
||||
#define curbool curint
|
||||
} opts[];
|
||||
extern const int num_opts;
|
||||
|
||||
extern const wxAcceleratorEntry default_accels[];
|
||||
extern const int num_def_accels;
|
||||
|
||||
// call to load config (once)
|
||||
// will write defaults for options not present and delete bad opts
|
||||
// will also initialize opts[] array translations
|
||||
void load_opts();
|
||||
// call whenever opt vars change
|
||||
// will detect changes and write config if necessary
|
||||
void update_opts();
|
||||
// returns true if option name correct; prints error if val invalid
|
||||
bool opt_set(const wxChar *name, const wxChar *val);
|
||||
|
||||
#endif /* WX_OPTS_H */
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,899 @@
|
|||
|
||||
// these are all the viewer dialogs except for the ones with graphical areas
|
||||
// they can be instantiated multiple times
|
||||
|
||||
#include "wxvbam.h"
|
||||
#include "viewsupt.h"
|
||||
#include "../gba/armdis.h"
|
||||
#include <wx/vlbox.h>
|
||||
#include <wx/ffile.h>
|
||||
|
||||
// avoid exporting classes
|
||||
namespace Viewers
|
||||
{
|
||||
#define gethex(v, n, l) do { \
|
||||
v = XRCCTRL(*this, n, wxTextCtrl); \
|
||||
if(!v) \
|
||||
baddialog(); \
|
||||
wxTextValidator hv(wxFILTER_INCLUDE_CHAR_LIST); \
|
||||
hv.SetIncludes(val_hexdigits); \
|
||||
v->SetValidator(hv); \
|
||||
v->SetMaxLength(l); \
|
||||
} while(0)
|
||||
|
||||
class DisassembleViewer : public Viewer
|
||||
{
|
||||
public:
|
||||
DisassembleViewer() : Viewer(wxT("Disassemble"))
|
||||
{
|
||||
gethex(goto_addr, "GotoAddress", 8);
|
||||
goto_addr->SetFocus();
|
||||
for(int i = 0; i < 17; i++) {
|
||||
// it is unfortunately impossible to reliably assign
|
||||
// number ranges to XRC IDs, so the string name has to be
|
||||
// used every time
|
||||
wxString n;
|
||||
n.Printf(wxT("R%d"), i);
|
||||
regv[i] = XRCCTRL_D(*this, n, wxControl);
|
||||
if(!regv[i])
|
||||
baddialog();
|
||||
}
|
||||
#define flagctrl(n) do { \
|
||||
n = XRCCTRL(*this, #n, wxCheckBox); \
|
||||
if(!n) \
|
||||
baddialog(); \
|
||||
} while(0)
|
||||
#define regctrl(n) do { \
|
||||
n##v = XRCCTRL(*this, #n, wxControl); \
|
||||
if(!n##v) \
|
||||
baddialog(); \
|
||||
} while(0)
|
||||
flagctrl(N);
|
||||
flagctrl(Z);
|
||||
flagctrl(C);
|
||||
flagctrl(V);
|
||||
flagctrl(I);
|
||||
flagctrl(F);
|
||||
flagctrl(T);
|
||||
regctrl(Mode);
|
||||
dis = XRCCTRL(*this, "Disassembly", DisList);
|
||||
if(!dis)
|
||||
baddialog();
|
||||
// refit listing for longest line
|
||||
dis->Refit(70);
|
||||
Fit();
|
||||
SetMinSize(GetSize());
|
||||
dis->maxaddr = (u32)~0;
|
||||
dismode = 0;
|
||||
GotoPC();
|
||||
}
|
||||
void Update()
|
||||
{
|
||||
GotoPC();
|
||||
}
|
||||
void Next(wxCommandEvent &ev)
|
||||
{
|
||||
CPULoop(1);
|
||||
GotoPC();
|
||||
}
|
||||
void Goto(wxCommandEvent &ev)
|
||||
{
|
||||
wxString as = goto_addr->GetValue();
|
||||
if(!as.size())
|
||||
return;
|
||||
long a;
|
||||
as.ToLong(&a, 16);
|
||||
dis->SetSel(a);
|
||||
UpdateDis();
|
||||
}
|
||||
// wx-2.8.4 or MacOSX compiler can't resolve overloads in evt table
|
||||
void GotoPCEv(wxCommandEvent &ev)
|
||||
{
|
||||
GotoPC();
|
||||
}
|
||||
void GotoPC()
|
||||
{
|
||||
#if 0
|
||||
// this is what the win32 interface used
|
||||
if(armState)
|
||||
dis->SetSel(armNextPC - 16);
|
||||
else
|
||||
dis->SetSel(armNextPC - 8);
|
||||
// doesn't make sense, though. Maybe it's just trying to keep the
|
||||
// sel 4 instructions below top...
|
||||
#endif
|
||||
dis->SetSel(armNextPC);
|
||||
UpdateDis();
|
||||
}
|
||||
void RefreshCmd(wxCommandEvent &ev)
|
||||
{
|
||||
UpdateDis();
|
||||
}
|
||||
void UpdateDis()
|
||||
{
|
||||
N->SetValue(reg[16].I & 0x80000000);
|
||||
Z->SetValue(reg[16].I & 0x40000000);
|
||||
C->SetValue(reg[16].I & 0x20000000);
|
||||
V->SetValue(reg[16].I & 0x10000000);
|
||||
I->SetValue(reg[16].I & 0x00000080);
|
||||
F->SetValue(reg[16].I & 0x00000040);
|
||||
T->SetValue(reg[16].I & 0x00000020);
|
||||
wxString s;
|
||||
s.Printf(wxT("%02X"), reg[16].I & 0x1f);
|
||||
Modev->SetLabel(s);
|
||||
for(int i = 0; i < 17; i++) {
|
||||
s.Printf(wxT("%08X"), reg[i].I);
|
||||
regv[i]->SetLabel(s);
|
||||
}
|
||||
}
|
||||
|
||||
void RefillListEv(wxCommandEvent &ev)
|
||||
{
|
||||
// what an unsafe calling convention
|
||||
// examination of disArm shows that max len is 69 chars
|
||||
// (e.g. 0x081cb6db), and I assume disThumb is shorter
|
||||
char buf[80];
|
||||
dis->strings.clear();
|
||||
dis->addrs.clear();
|
||||
u32 addr = dis->topaddr;
|
||||
bool arm = dismode == 1 || (armState && dismode != 2);
|
||||
dis->back_size = arm ? 4 : 2;
|
||||
for(int i = 0; i < dis->nlines; i++) {
|
||||
dis->addrs.push_back(addr);
|
||||
if(arm)
|
||||
addr += disArm(addr, buf, DIS_VIEW_CODE|DIS_VIEW_ADDRESS);
|
||||
else
|
||||
addr += disThumb(addr, buf, DIS_VIEW_CODE|DIS_VIEW_ADDRESS);
|
||||
dis->strings.push_back(wxString(buf, wxConvLibc));
|
||||
}
|
||||
dis->Refill();
|
||||
}
|
||||
|
||||
DisList *dis;
|
||||
wxTextCtrl *goto_addr;
|
||||
wxCheckBox *N, *Z, *C, *V, *I, *F, *T;
|
||||
int dismode;
|
||||
wxControl *regv[17], *Modev;
|
||||
DECLARE_EVENT_TABLE()
|
||||
};
|
||||
|
||||
BEGIN_EVENT_TABLE(DisassembleViewer, Viewer)
|
||||
EVT_COMMAND(wxID_ANY, EVT_REFILL_NEEDED, DisassembleViewer::RefillListEv)
|
||||
EVT_BUTTON(XRCID("Goto"), DisassembleViewer::Goto)
|
||||
EVT_TEXT_ENTER(XRCID("GotoAddress"), DisassembleViewer::Goto)
|
||||
EVT_BUTTON(XRCID("GotoPC"), DisassembleViewer::GotoPCEv)
|
||||
EVT_BUTTON(XRCID("Next"), DisassembleViewer::Next)
|
||||
EVT_BUTTON(XRCID("Refresh"), DisassembleViewer::RefreshCmd)
|
||||
END_EVENT_TABLE()
|
||||
|
||||
class GBDisassembleViewer : public Viewer
|
||||
{
|
||||
public:
|
||||
GBDisassembleViewer() : Viewer(wxT("GBDisassemble"))
|
||||
{
|
||||
gethex(goto_addr, "GotoAddress", 4);
|
||||
goto_addr->SetFocus();
|
||||
regctrl(AF);
|
||||
regctrl(BC);
|
||||
regctrl(DE);
|
||||
regctrl(HL);
|
||||
regctrl(SP);
|
||||
regctrl(PC);
|
||||
regctrl(LY);
|
||||
regctrl(IFF);
|
||||
flagctrl(Z);
|
||||
flagctrl(N);
|
||||
flagctrl(H);
|
||||
flagctrl(C);
|
||||
dis = XRCCTRL(*this, "Disassembly", DisList);
|
||||
if(!dis)
|
||||
baddialog();
|
||||
// refit listing for longest line
|
||||
dis->Refit(26);
|
||||
Fit();
|
||||
SetMinSize(GetSize());
|
||||
dis->maxaddr = (u32)~0;
|
||||
GotoPC();
|
||||
}
|
||||
void Update()
|
||||
{
|
||||
GotoPC();
|
||||
}
|
||||
void Next(wxCommandEvent &ev)
|
||||
{
|
||||
gbEmulate(1);
|
||||
GotoPC();
|
||||
}
|
||||
void Goto(wxCommandEvent &ev)
|
||||
{
|
||||
wxString as = goto_addr->GetValue();
|
||||
if(!as.size())
|
||||
return;
|
||||
long a;
|
||||
as.ToLong(&a, 16);
|
||||
dis->SetSel(a);
|
||||
UpdateDis();
|
||||
}
|
||||
// wx-2.8.4 or MacOSX compiler can't resolve overloads in evt table
|
||||
void GotoPCEv(wxCommandEvent &ev)
|
||||
{
|
||||
GotoPC();
|
||||
}
|
||||
void GotoPC()
|
||||
{
|
||||
dis->SetSel(PC.W);
|
||||
UpdateDis();
|
||||
}
|
||||
void RefreshCmd(wxCommandEvent &ev)
|
||||
{
|
||||
UpdateDis();
|
||||
}
|
||||
void UpdateDis()
|
||||
{
|
||||
Z->SetValue(AF.B.B0 & Z_FLAG);
|
||||
N->SetValue(AF.B.B0 & N_FLAG);
|
||||
H->SetValue(AF.B.B0 & H_FLAG);
|
||||
C->SetValue(AF.B.B0 & C_FLAG);
|
||||
#define grv16(n, val) do { \
|
||||
wxString s; \
|
||||
s.Printf(wxT("%04X"), (int)val); \
|
||||
n##v->SetLabel(s); \
|
||||
} while(0)
|
||||
#define rv16(n) grv16(n, n.W)
|
||||
#define srv16(n) grv16(n, n)
|
||||
rv16(AF);
|
||||
rv16(BC);
|
||||
rv16(DE);
|
||||
rv16(HL);
|
||||
rv16(SP);
|
||||
rv16(PC);
|
||||
srv16(IFF);
|
||||
wxString s;
|
||||
s.Printf(wxT("%02X"), register_LY);
|
||||
LYv->SetLabel(s);
|
||||
}
|
||||
|
||||
void RefillListEv(wxCommandEvent &ev)
|
||||
{
|
||||
// what an unsafe calling convention
|
||||
// examination of gbDis shows that max len is 26 chars
|
||||
// (e.g. 0xe2)
|
||||
char buf[30];
|
||||
u16 addr = dis->topaddr;
|
||||
dis->strings.clear();
|
||||
dis->addrs.clear();
|
||||
dis->back_size = 1;
|
||||
for(int i = 0; i < dis->nlines; i++) {
|
||||
dis->addrs.push_back(addr);
|
||||
addr += gbDis(buf, addr);
|
||||
dis->strings.push_back(wxString(buf, wxConvLibc));
|
||||
}
|
||||
dis->Refill();
|
||||
}
|
||||
|
||||
DisList *dis;
|
||||
wxTextCtrl *goto_addr;
|
||||
wxControl *AFv, *BCv, *DEv, *HLv, *SPv, *PCv, *LYv, *IFFv;
|
||||
wxCheckBox *Z, *N, *H, *C;
|
||||
DECLARE_EVENT_TABLE()
|
||||
};
|
||||
BEGIN_EVENT_TABLE(GBDisassembleViewer, Viewer)
|
||||
EVT_COMMAND(wxID_ANY, EVT_REFILL_NEEDED, GBDisassembleViewer::RefillListEv)
|
||||
EVT_BUTTON(XRCID("Goto"), GBDisassembleViewer::Goto)
|
||||
EVT_TEXT_ENTER(XRCID("GotoAddress"), GBDisassembleViewer::Goto)
|
||||
EVT_BUTTON(XRCID("GotoPC"), GBDisassembleViewer::GotoPCEv)
|
||||
EVT_BUTTON(XRCID("Next"), GBDisassembleViewer::Next)
|
||||
EVT_BUTTON(XRCID("Refresh"), GBDisassembleViewer::RefreshCmd)
|
||||
END_EVENT_TABLE()
|
||||
}
|
||||
|
||||
void MainFrame::Disassemble(void)
|
||||
{
|
||||
switch(panel->game_type()) {
|
||||
case IMAGE_GBA:
|
||||
LoadXRCViewer(Disassemble);
|
||||
break;
|
||||
case IMAGE_GB:
|
||||
LoadXRCViewer(GBDisassemble);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// for CPUWriteHalfWord
|
||||
// and CPURead... below
|
||||
#include "../gba/GBAinline.h"
|
||||
|
||||
namespace Viewers {
|
||||
#include "ioregs.h"
|
||||
class IOViewer : public Viewer
|
||||
{
|
||||
public:
|
||||
IOViewer() : Viewer(wxT("IOViewer"))
|
||||
{
|
||||
for(int i = 0; i < 16; i++) {
|
||||
wxString s;
|
||||
s.Printf(wxT("B%d"), i);
|
||||
bit[i] = XRCCTRL_D(*this, s, wxCheckBox);
|
||||
s.append(wxT("lab"));
|
||||
bitlab[i] = XRCCTRL_D(*this, s, wxControl);
|
||||
if(!bit[i] || !bitlab[i])
|
||||
baddialog();
|
||||
}
|
||||
addr = XRCCTRL(*this, "Address", wxChoice);
|
||||
val = XRCCTRL(*this, "Value", wxControl);
|
||||
if(!addr || !val)
|
||||
baddialog();
|
||||
addr->Clear();
|
||||
const wxChar *longline = lline;
|
||||
int lwidth = 0;
|
||||
for(int i = 0; i < NUM_IOREGS; i++) {
|
||||
addr->Append(wxGetTranslation(ioregs[i].name));
|
||||
// find longest label
|
||||
// this is probably horribly expensive
|
||||
// cache for entire app...
|
||||
// and while at it, translate all the strings
|
||||
if(!lline) {
|
||||
for(int j = 0; j < 16; j++) {
|
||||
if(ioregs[i].bits[j][0]) {
|
||||
ioregs[i].bits[j] = wxGetTranslation(ioregs[i].bits[j]);
|
||||
int w, h;
|
||||
bitlab[0]->GetTextExtent(ioregs[i].bits[j], &w, &h);
|
||||
if(w > lwidth) {
|
||||
lwidth = w;
|
||||
longline = ioregs[i].bits[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!lline)
|
||||
lline = longline;
|
||||
bitlab[0]->SetLabel(lline);
|
||||
Fit();
|
||||
addr->SetSelection(0);
|
||||
Select(0);
|
||||
}
|
||||
|
||||
void SelectEv(wxCommandEvent &ev)
|
||||
{
|
||||
Select(addr->GetSelection());
|
||||
}
|
||||
|
||||
void Select(int sel)
|
||||
{
|
||||
int i;
|
||||
u16 mask;
|
||||
for(mask = 1, i = 0; mask; mask <<= 1, i++) {
|
||||
bit[i]->Enable(mask & ioregs[sel].write);
|
||||
bitlab[i]->SetLabel(ioregs[sel].bits[i]);
|
||||
}
|
||||
Update(sel);
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
Update(addr->GetSelection());
|
||||
}
|
||||
|
||||
void Update(int sel)
|
||||
{
|
||||
u16 *addr = ioregs[sel].address ? ioregs[sel].address :
|
||||
(u16 *)&ioMem[ioregs[sel].offset];
|
||||
u16 mask, reg = *addr;
|
||||
int i;
|
||||
for(mask = 1, i = 0; mask; mask <<= 1, i++)
|
||||
bit[i]->SetValue(mask & reg);
|
||||
wxString s;
|
||||
s.Printf(wxT("%04X"), reg);
|
||||
val->SetLabel(s);
|
||||
}
|
||||
|
||||
void CheckBit(wxCommandEvent &ev)
|
||||
{
|
||||
for(int i = 0; i < 16; i++)
|
||||
if(ev.GetEventObject() == bit[i]) {
|
||||
// it'd be faster to store the value and just flip
|
||||
// the bit, but it's easier this way
|
||||
u16 mask, reg = 0;
|
||||
int j;
|
||||
for(mask = 1, j = 0; mask; mask <<= 1, j++)
|
||||
if(bit[j]->GetValue())
|
||||
reg |= mask;
|
||||
wxString s;
|
||||
s.Printf(wxT("%04X"), reg);
|
||||
val->SetLabel(s);
|
||||
return;
|
||||
}
|
||||
ev.Skip();
|
||||
}
|
||||
|
||||
void RefreshEv(wxCommandEvent &ev)
|
||||
{
|
||||
Update();
|
||||
}
|
||||
|
||||
void Apply(wxCommandEvent &ev)
|
||||
{
|
||||
int sel = addr->GetSelection();
|
||||
u16 *addr = ioregs[sel].address ? ioregs[sel].address :
|
||||
(u16 *)&ioMem[ioregs[sel].offset];
|
||||
u16 mask, reg = *addr;
|
||||
reg &= ~ioregs[sel].write;
|
||||
int i;
|
||||
for(mask = 1, i = 0; mask; mask <<= 1, i++) {
|
||||
if((mask & ioregs[sel].write) && bit[i]->GetValue())
|
||||
reg |= mask;
|
||||
}
|
||||
CPUWriteHalfWord(0x4000000+ioregs[sel].offset, reg);
|
||||
Update(sel);
|
||||
}
|
||||
|
||||
static const wxChar *lline;
|
||||
wxChoice *addr;
|
||||
wxControl *val;
|
||||
wxCheckBox *bit[16];
|
||||
wxControl *bitlab[16];
|
||||
DECLARE_EVENT_TABLE()
|
||||
};
|
||||
const wxChar *IOViewer::lline = NULL;
|
||||
BEGIN_EVENT_TABLE(IOViewer, Viewer)
|
||||
EVT_BUTTON(XRCID("Refresh"), IOViewer::RefreshEv)
|
||||
EVT_BUTTON(wxID_APPLY, IOViewer::Apply)
|
||||
EVT_CHOICE(XRCID("Address"), IOViewer::SelectEv)
|
||||
EVT_CHECKBOX(wxID_ANY, IOViewer::CheckBit)
|
||||
END_EVENT_TABLE()
|
||||
}
|
||||
|
||||
void MainFrame::IOViewer()
|
||||
{
|
||||
LoadXRCViewer(IO);
|
||||
}
|
||||
|
||||
#define getlogf(n, val) do { \
|
||||
wxCheckBox *cb = XRCCTRL(*this, n, wxCheckBox); \
|
||||
if(!cb) \
|
||||
baddialog(); \
|
||||
cb->SetValidator(wxBoolIntValidator(&systemVerbose, val, val)); \
|
||||
} while(0)
|
||||
LogDialog::LogDialog()
|
||||
{
|
||||
const wxChar *dname = wxT("Logging");
|
||||
if(!wxXmlResource::Get()->LoadDialog(this, wxGetApp().frame, dname))
|
||||
baddialog();
|
||||
SetEscapeId(wxID_OK);
|
||||
getlogf("SWI", VERBOSE_SWI);
|
||||
getlogf("UnalignedMemory", VERBOSE_UNALIGNED_MEMORY);
|
||||
getlogf("IllWrite", VERBOSE_ILLEGAL_WRITE);
|
||||
getlogf("IllRead", VERBOSE_ILLEGAL_READ);
|
||||
getlogf("DMA0", VERBOSE_DMA0);
|
||||
getlogf("DMA1", VERBOSE_DMA1);
|
||||
getlogf("DMA2", VERBOSE_DMA2);
|
||||
getlogf("DMA3", VERBOSE_DMA3);
|
||||
getlogf("UndefInstruction", VERBOSE_UNDEFINED);
|
||||
getlogf("AGBPrint", VERBOSE_AGBPRINT);
|
||||
getlogf("SoundOut", VERBOSE_SOUNDOUTPUT);
|
||||
log = XRCCTRL(*this, "Log", wxTextCtrl);
|
||||
if(!log)
|
||||
baddialog();
|
||||
Fit();
|
||||
}
|
||||
|
||||
void LogDialog::Update()
|
||||
{
|
||||
wxString l = wxGetApp().log;
|
||||
log->SetValue(l);
|
||||
log->ShowPosition(l.size() > 2 ? l.size() - 2 : 0);
|
||||
}
|
||||
|
||||
void LogDialog::Save(wxCommandEvent &ev)
|
||||
{
|
||||
static wxString logdir = wxEmptyString, def_name = wxEmptyString;
|
||||
if(def_name.empty())
|
||||
def_name = wxGetApp().frame->GetPanel()->game_name() + wxT(".log");
|
||||
wxString pats = _("Text files (*.txt;*.log)|*.txt;*.log|");
|
||||
pats.append(wxALL_FILES);
|
||||
wxFileDialog dlg(this, _("Select output file"), logdir, def_name,
|
||||
pats, wxFD_SAVE|wxFD_OVERWRITE_PROMPT);
|
||||
int ret = dlg.ShowModal();
|
||||
def_name = dlg.GetPath();
|
||||
logdir = dlg.GetDirectory();
|
||||
if(ret != wxID_OK)
|
||||
return;
|
||||
wxFFile f(def_name, wxT("w"));
|
||||
if(f.IsOpened()) {
|
||||
f.Write(wxGetApp().log);
|
||||
f.Close();
|
||||
}
|
||||
}
|
||||
|
||||
void LogDialog::Clear(wxCommandEvent &ev)
|
||||
{
|
||||
wxGetApp().log.clear();
|
||||
Update();
|
||||
}
|
||||
|
||||
BEGIN_EVENT_TABLE(LogDialog, wxDialog)
|
||||
EVT_BUTTON(wxID_SAVE, LogDialog::Save)
|
||||
EVT_BUTTON(XRCID("Clear"), LogDialog::Clear)
|
||||
EVT_CHECKBOX(wxID_ANY, Viewers::Viewer::ActiveCtrl)
|
||||
END_EVENT_TABLE()
|
||||
|
||||
// These are what mfc interface used. Maybe it would be safer
|
||||
// to avoid the Quick() routines in favor of the long ones...
|
||||
#define CPUWriteByteQuick(addr, b) \
|
||||
::map[(addr)>>24].address[(addr) & ::map[(addr)>>24].mask] = (b)
|
||||
#define CPUWriteHalfWordQuick(addr, b) \
|
||||
WRITE16LE((u16 *)&::map[(addr)>>24].address[(addr) & ::map[(addr)>>24].mask], b)
|
||||
#define CPUWriteMemoryQuick(addr, b) \
|
||||
WRITE32LE((u32 *)&::map[(addr)>>24].address[(addr) & ::map[(addr)>>24].mask], b)
|
||||
#define GBWriteByteQuick(addr, b) \
|
||||
*((u8 *)&gbMemoryMap[(addr)>>12][(addr) & 0xfff]) = (b)
|
||||
#define GBWriteHalfWordQuick(addr, b) \
|
||||
WRITE16LE((u16 *)&gbMemoryMap[(addr)>>12][(addr) & 0xfff], b)
|
||||
#define GBWriteMemoryQuick(addr, b) \
|
||||
WRITE32LE((u32 *)&gbMemoryMap[(addr)>>12][(addr) & 0xfff], b)
|
||||
#define GBReadMemoryQuick(addr) \
|
||||
READ32LE((u32 *)&gbMemoryMap[(addr)>>12][(addr) & 0xfff])
|
||||
|
||||
namespace Viewers {
|
||||
static wxString memsave_dir = wxEmptyString;
|
||||
class MemViewerBase : public Viewer
|
||||
{
|
||||
public:
|
||||
MemViewerBase(u32 max) : Viewer(wxT("MemViewer"))
|
||||
{
|
||||
if(!(mv = XRCCTRL(*this, "MemView", MemView)))
|
||||
baddialog();
|
||||
if(!(bs = XRCCTRL(*this, "BlockStart", wxChoice)))
|
||||
baddialog();
|
||||
bs->Append(wxT(""));
|
||||
bs->SetFocus();
|
||||
mv->fmt = max > 0xffff ? 2 : 1;
|
||||
getradio(,"Fmt8", mv->fmt, 0);
|
||||
getradio(,"Fmt16", mv->fmt, 1);
|
||||
getradio(,"Fmt32", mv->fmt, 2);
|
||||
mv->maxaddr = max;
|
||||
addrlen = max > 0xffff ? 8 : 4;
|
||||
gethex(goto_addr, "GotoAddress", addrlen);
|
||||
wxControl *addr = XRCCTRL(*this, "CurAddress", wxControl);
|
||||
if(!addr)
|
||||
baddialog();
|
||||
addr->SetLabel(addrlen == 8 ? wxT("0xWWWWWWWW") : wxT("0xWWWW"));
|
||||
// refit listing for longest line
|
||||
mv->Refit();
|
||||
Fit();
|
||||
// don't let address display resize when window size changes
|
||||
addr->SetMinSize(addr->GetSize());
|
||||
mv->addrlab = addr;
|
||||
SetMinSize(GetSize());
|
||||
Goto(0);
|
||||
|
||||
// initialize load/save support dialog already
|
||||
{
|
||||
const wxChar *dname = wxT("MemSelRegion");
|
||||
selregion = wxXmlResource::Get()->LoadDialog(this, dname);
|
||||
if(!selregion)
|
||||
baddialog();
|
||||
#define this selregion // for gethex()
|
||||
gethex(selreg_addr, "Address", addrlen);
|
||||
gethex(selreg_len, "Size", addrlen);
|
||||
#undef this
|
||||
selreg_lenlab = XRCCTRL(*selregion, "SizeLab", wxControl);
|
||||
selregion->Fit();
|
||||
}
|
||||
}
|
||||
void BlockStart(wxCommandEvent &ev)
|
||||
{
|
||||
unsigned long l;
|
||||
bs->GetStringSelection().ToULong(&l, 0);
|
||||
Goto(l);
|
||||
}
|
||||
void GotoEv(wxCommandEvent &ev)
|
||||
{
|
||||
unsigned long l;
|
||||
wxString v = goto_addr->GetValue();
|
||||
if(v.empty())
|
||||
return;
|
||||
v.ToULong(&l, 16);
|
||||
Goto(l);
|
||||
}
|
||||
void Goto(u32 addr)
|
||||
{
|
||||
mv->Show(addr, true);
|
||||
}
|
||||
void RefreshCmd(wxCommandEvent &ev)
|
||||
{
|
||||
Update();
|
||||
}
|
||||
|
||||
virtual void Update() {}
|
||||
|
||||
void Load(wxCommandEvent &ev)
|
||||
{
|
||||
if(memsave_fn.empty())
|
||||
memsave_fn = wxGetApp().frame->GetPanel()->game_name() + wxT(".dmp");
|
||||
wxString pats = _("Memory dumps (*.dmp;*.bin)|*.dmp;*.bin|");
|
||||
pats.append(wxALL_FILES);
|
||||
wxFileDialog dlg(this, _("Select memory dump file"), memsave_dir, memsave_fn,
|
||||
pats, wxFD_OPEN|wxFD_FILE_MUST_EXIST);
|
||||
int ret = dlg.ShowModal();
|
||||
memsave_fn = dlg.GetPath();
|
||||
memsave_dir = dlg.GetDirectory();
|
||||
if(ret != wxID_OK)
|
||||
return;
|
||||
wxFileName fn(memsave_fn);
|
||||
if(!fn.IsFileReadable()) {
|
||||
wxLogError(wxT("Can't open file %s"), memsave_fn.c_str());
|
||||
return;
|
||||
}
|
||||
unsigned long addr, len = fn.GetSize().ToULong();
|
||||
if(!len)
|
||||
return;
|
||||
wxString s;
|
||||
s.Printf(addrlen == 4 ? wxT("%04X") : wxT("%08X"), mv->GetAddr());
|
||||
selreg_addr->SetValue(s);
|
||||
selreg_len->Disable();
|
||||
selreg_lenlab->Disable();
|
||||
s.Printf(addrlen == 4 ? wxT("%04X") : wxT("%08X"), len);
|
||||
selreg_len->SetValue(s);
|
||||
if(selregion->ShowModal() != wxID_OK)
|
||||
return;
|
||||
selreg_addr->GetValue().ToULong(&addr, 16);
|
||||
MemLoad(memsave_fn, addr, len);
|
||||
}
|
||||
|
||||
virtual void MemLoad(wxString &name, u32 addr, u32 len) = 0;
|
||||
|
||||
void Save(wxCommandEvent &ev)
|
||||
{
|
||||
wxString s;
|
||||
s.Printf(addrlen == 4 ? wxT("%04X") : wxT("%08X"), mv->GetAddr());
|
||||
selreg_addr->SetValue(s);
|
||||
selreg_len->Enable();
|
||||
selreg_lenlab->Enable();
|
||||
selreg_len->SetValue(wxEmptyString);
|
||||
if(selregion->ShowModal() != wxID_OK)
|
||||
return;
|
||||
unsigned long addr, len;
|
||||
selreg_addr->GetValue().ToULong(&addr, 16);
|
||||
selreg_len->GetValue().ToULong(&len, 16);
|
||||
if(memsave_fn.empty())
|
||||
memsave_fn = wxGetApp().frame->GetPanel()->game_name() + wxT(".dmp");
|
||||
wxString pats = _("Memory dumps (*.dmp;*.bin)|*.dmp;*.bin|");
|
||||
pats.append(wxALL_FILES);
|
||||
wxFileDialog dlg(this, _("Select output file"), memsave_dir, memsave_fn,
|
||||
pats, wxFD_SAVE|wxFD_OVERWRITE_PROMPT);
|
||||
int ret = dlg.ShowModal();
|
||||
memsave_dir = dlg.GetDirectory();
|
||||
memsave_fn = dlg.GetPath();
|
||||
if(ret != wxID_OK)
|
||||
return;
|
||||
MemSave(memsave_fn, addr, len);
|
||||
}
|
||||
|
||||
virtual void MemSave(wxString &name, u32 addr, u32 len) = 0;
|
||||
protected:
|
||||
int addrlen;
|
||||
wxChoice *bs;
|
||||
wxTextCtrl *goto_addr;
|
||||
MemView *mv;
|
||||
|
||||
wxDialog *selregion;
|
||||
wxTextCtrl *selreg_addr, *selreg_len;
|
||||
wxControl *selreg_lenlab;
|
||||
wxString memsave_fn;
|
||||
|
||||
DECLARE_EVENT_TABLE()
|
||||
};
|
||||
|
||||
BEGIN_EVENT_TABLE(MemViewerBase, Viewer)
|
||||
EVT_CHOICE(XRCID("BlockStart"), MemViewerBase::BlockStart)
|
||||
EVT_BUTTON(XRCID("Goto"), MemViewerBase::GotoEv)
|
||||
EVT_TEXT_ENTER(XRCID("GotoAddress"), MemViewerBase::GotoEv)
|
||||
EVT_BUTTON(XRCID("Refresh"), MemViewerBase::RefreshCmd)
|
||||
EVT_BUTTON(wxID_SAVE, MemViewerBase::Save)
|
||||
EVT_BUTTON(wxID_OPEN, MemViewerBase::Load)
|
||||
END_EVENT_TABLE()
|
||||
|
||||
class MemViewer : public MemViewerBase
|
||||
{
|
||||
public:
|
||||
MemViewer() : MemViewerBase(~0)
|
||||
{
|
||||
bs->Append(_("0x00000000 - BIOS"));
|
||||
bs->Append(_("0x02000000 - WRAM"));
|
||||
bs->Append(_("0x03000000 - IRAM"));
|
||||
bs->Append(_("0x04000000 - I/O"));
|
||||
bs->Append(_("0x05000000 - PALETTE"));
|
||||
bs->Append(_("0x06000000 - VRAM"));
|
||||
bs->Append(_("0x07000000 - OAM"));
|
||||
bs->Append(_("0x08000000 - ROM"));
|
||||
bs->SetSelection(1);
|
||||
Fit();
|
||||
}
|
||||
|
||||
// wx-2.8.4 or MacOSX compiler can't resolve overloads in evt table
|
||||
void RefillListEv(wxCommandEvent &ev)
|
||||
{
|
||||
Update();
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
u32 addr = mv->topaddr;
|
||||
mv->words.resize(mv->nlines * 4);
|
||||
for(int i = 0; i < mv->nlines; i++) {
|
||||
if(i && !addr)
|
||||
break;
|
||||
for(int j = 0; j < 4; j++, addr += 4)
|
||||
mv->words[i * 4 + j] = CPUReadMemoryQuick(addr);
|
||||
}
|
||||
mv->Refill();
|
||||
}
|
||||
|
||||
void WriteVal(wxCommandEvent &ev)
|
||||
{
|
||||
switch(mv->fmt) {
|
||||
case 0:
|
||||
CPUWriteByteQuick(mv->writeaddr, mv->writeval);
|
||||
break;
|
||||
case 1:
|
||||
CPUWriteHalfWordQuick(mv->writeaddr, mv->writeval);
|
||||
break;
|
||||
case 2:
|
||||
CPUWriteMemoryQuick(mv->writeaddr, mv->writeval);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void MemLoad(wxString &name, u32 addr, u32 len)
|
||||
{
|
||||
wxFFile f(name, wxT("rb"));
|
||||
if(!f.IsOpened())
|
||||
return;
|
||||
// this does the equivalent of the CPUWriteMemoryQuick()
|
||||
while(len > 0) {
|
||||
memoryMap m = map[addr >> 24];
|
||||
u32 off = addr & m.mask;
|
||||
u32 wlen = (off + len) > m.mask ? m.mask + 1 - off : len;
|
||||
wlen = f.Read(m.address + off, wlen);
|
||||
if(wlen < 0)
|
||||
return; // FIXME: give error
|
||||
len -= wlen;
|
||||
addr += wlen;
|
||||
}
|
||||
}
|
||||
|
||||
void MemSave(wxString &name, u32 addr, u32 len)
|
||||
{
|
||||
wxFFile f(name, wxT("wb"));
|
||||
if(!f.IsOpened())
|
||||
return;
|
||||
// this does the equivalent of the CPUReadMemoryQuick()
|
||||
while(len > 0) {
|
||||
memoryMap m = map[addr >> 24];
|
||||
u32 off = addr & m.mask;
|
||||
u32 wlen = (off + len) > m.mask ? m.mask + 1 - off : len;
|
||||
wlen = f.Write(m.address + off, wlen);
|
||||
if(wlen < 0)
|
||||
return; // FIXME: give error
|
||||
len -= wlen;
|
||||
addr += wlen;
|
||||
}
|
||||
}
|
||||
|
||||
DECLARE_EVENT_TABLE()
|
||||
};
|
||||
|
||||
BEGIN_EVENT_TABLE(MemViewer, MemViewerBase)
|
||||
EVT_COMMAND(wxID_ANY, EVT_REFILL_NEEDED, MemViewer::RefillListEv)
|
||||
EVT_COMMAND(wxID_ANY, EVT_WRITEVAL, MemViewer::WriteVal)
|
||||
END_EVENT_TABLE()
|
||||
|
||||
class GBMemViewer : public MemViewerBase
|
||||
{
|
||||
public:
|
||||
GBMemViewer() : MemViewerBase((u16)~0)
|
||||
{
|
||||
bs->Append(_("0x0000 - ROM"));
|
||||
bs->Append(_("0x4000 - ROM"));
|
||||
bs->Append(_("0x8000 - VRAM"));
|
||||
bs->Append(_("0xA000 - SRAM"));
|
||||
bs->Append(_("0xC000 - RAM"));
|
||||
bs->Append(_("0xD000 - WRAM"));
|
||||
bs->Append(_("0xFF00 - I/O"));
|
||||
bs->Append(_("0xFF80 - RAM"));
|
||||
bs->SetSelection(1);
|
||||
Fit();
|
||||
}
|
||||
|
||||
// wx-2.8.4 or MacOSX compiler can't resolve overloads in evt table
|
||||
void RefillListEv(wxCommandEvent &ev)
|
||||
{
|
||||
Update();
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
u32 addr = mv->topaddr;
|
||||
mv->words.resize(mv->nlines * 4);
|
||||
for(int i = 0; i < mv->nlines; i++) {
|
||||
if(i && !(u16)addr)
|
||||
break;
|
||||
for(int j = 0; j < 4; j++, addr += 4)
|
||||
mv->words[i * 4 + j] = GBReadMemoryQuick(addr);
|
||||
}
|
||||
mv->Refill();
|
||||
}
|
||||
|
||||
void WriteVal(wxCommandEvent &ev)
|
||||
{
|
||||
switch(mv->fmt) {
|
||||
case 0:
|
||||
GBWriteByteQuick(mv->writeaddr, mv->writeval);
|
||||
break;
|
||||
case 1:
|
||||
GBWriteHalfWordQuick(mv->writeaddr, mv->writeval);
|
||||
break;
|
||||
case 2:
|
||||
GBWriteMemoryQuick(mv->writeaddr, mv->writeval);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void MemLoad(wxString &name, u32 addr, u32 len)
|
||||
{
|
||||
wxFFile f(name, wxT("rb"));
|
||||
if(!f.IsOpened())
|
||||
return;
|
||||
// this does the equivalent of the GBWriteMemoryQuick()
|
||||
while(len > 0) {
|
||||
u8 *maddr = gbMemoryMap[addr >> 12];
|
||||
u32 off = addr & 0xfff;
|
||||
u32 wlen = (off + len) > 0xfff ? 0x1000 - off : len;
|
||||
wlen = f.Read(maddr + off, wlen);
|
||||
if(wlen < 0)
|
||||
return; // FIXME: give error
|
||||
len -= wlen;
|
||||
addr += wlen;
|
||||
}
|
||||
}
|
||||
|
||||
void MemSave(wxString &name, u32 addr, u32 len)
|
||||
{
|
||||
wxFFile f(name, wxT("wb"));
|
||||
if(!f.IsOpened())
|
||||
return;
|
||||
// this does the equivalent of the GBReadMemoryQuick()
|
||||
while(len > 0) {
|
||||
u8 *maddr = gbMemoryMap[addr >> 12];
|
||||
u32 off = addr & 0xfff;
|
||||
u32 wlen = (off + len) > 0xfff ? 0x1000 - off : len;
|
||||
wlen = f.Write(maddr + off, wlen);
|
||||
if(wlen < 0)
|
||||
return; // FIXME: give error
|
||||
len -= wlen;
|
||||
addr += wlen;
|
||||
}
|
||||
}
|
||||
|
||||
DECLARE_EVENT_TABLE()
|
||||
};
|
||||
|
||||
BEGIN_EVENT_TABLE(GBMemViewer, MemViewerBase)
|
||||
EVT_COMMAND(wxID_ANY, EVT_REFILL_NEEDED, GBMemViewer::RefillListEv)
|
||||
EVT_COMMAND(wxID_ANY, EVT_WRITEVAL, GBMemViewer::WriteVal)
|
||||
END_EVENT_TABLE()
|
||||
}
|
||||
|
||||
void MainFrame::MemViewer()
|
||||
{
|
||||
switch(panel->game_type()) {
|
||||
case IMAGE_GBA:
|
||||
LoadXRCViewer(Mem);
|
||||
break;
|
||||
case IMAGE_GB:
|
||||
LoadXRCViewer(GBMem);
|
||||
break;
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,363 @@
|
|||
#ifndef WX_VIEWSUPT_H
|
||||
#define WX_VIEWSUPT_H
|
||||
|
||||
#include <wx/spinctrl.h>
|
||||
#include <wx/caret.h>
|
||||
|
||||
// avoid exporting too much stuff
|
||||
namespace Viewers {
|
||||
// common to all viewers:
|
||||
// - track in MainFrame::popups
|
||||
// - wxID_CLOSE button closes window
|
||||
// - AutoUpdate checkbox toggles calling Update() every screen refresh
|
||||
class Viewer : public wxDialog
|
||||
{
|
||||
public:
|
||||
void CloseDlg(wxCloseEvent &ev);
|
||||
Viewer(const wxChar *name);
|
||||
virtual ~Viewer() {}
|
||||
virtual void Update() = 0;
|
||||
bool auto_update;
|
||||
|
||||
// A lot of viewers have GUI elements to set parameters. Almost all
|
||||
// of them just read back the value and update the display. This
|
||||
// event handler does that with validators.
|
||||
void ActiveCtrl(wxCommandEvent &ev);
|
||||
void ActiveCtrlScr(wxScrollEvent &ev) { ActiveCtrl(ev); }
|
||||
void ActiveCtrlSpin(wxSpinEvent &ev) { ActiveCtrl(ev); }
|
||||
|
||||
protected:
|
||||
const wxChar *dname;
|
||||
void SetAutoUpdate(wxCommandEvent &ev) { auto_update = ev.IsChecked(); }
|
||||
|
||||
DECLARE_EVENT_TABLE()
|
||||
};
|
||||
|
||||
// on errors, abort program
|
||||
#define baddialog() do { \
|
||||
wxLogError(_("Unable to load dialog %s from resources"), dname); \
|
||||
wxGetApp().frame->Close(true); \
|
||||
return; \
|
||||
} while(0)
|
||||
|
||||
// widgets to use with auto validator
|
||||
#define getvfld(sv, n, t, v) do { \
|
||||
t *_w = sv XRCCTRL(*this, n, t); \
|
||||
if(!_w) \
|
||||
baddialog(); \
|
||||
_w->SetValidator(v); \
|
||||
} while(0)
|
||||
#define getradio(sv, n, var, val) \
|
||||
getvfld(sv, n, wxRadioButton, wxBoolIntValidator(&var, val))
|
||||
#define getslider(sv, n, var) \
|
||||
getvfld(sv, n, wxSlider, wxGenericValidator(&var))
|
||||
#define getspin(sv, n, var) \
|
||||
getvfld(sv, n, wxSpinCtrl, wxGenericValidator(&var))
|
||||
|
||||
#define LoadXRCViewer(t) do { \
|
||||
wxDialog *d = new Viewers::t##Viewer; \
|
||||
if(d) \
|
||||
d->Show(); \
|
||||
} while(0)
|
||||
|
||||
// a list box with no horizontal scrollbar and a funky vertical scrollbar:
|
||||
// range = 1 - 500
|
||||
// but/pagesz = # of lines shown/# of lines shown - 1
|
||||
// 1-100 = normal
|
||||
// 101-200 = 10x semi-stationary
|
||||
// 201-300 = stationary @ center
|
||||
// 301-400 = 10x semi-stationary
|
||||
// 401-500 = normal
|
||||
|
||||
// note that since listboxes' size is impossible to control correctly (at
|
||||
// least with wxGTK), this uses a textctrl to display the items.
|
||||
|
||||
class DisList : public wxPanel
|
||||
{
|
||||
public:
|
||||
DisList();
|
||||
// called after init to create subcontrols and size panel
|
||||
void Refit(int cols);
|
||||
void MoveSB();
|
||||
void MoveView(wxScrollEvent &ev);
|
||||
private:
|
||||
void RefillNeeded();
|
||||
public:
|
||||
// called by parent's refill handler or any other time strings have
|
||||
// changed
|
||||
void Refill();
|
||||
private:
|
||||
void Resize(wxSizeEvent &ev);
|
||||
void SetSel();
|
||||
|
||||
public:
|
||||
// make addr visible and then select it
|
||||
void SetSel(u32 addr);
|
||||
void UnSel() { issel = false; }
|
||||
|
||||
// currently visible lines
|
||||
int nlines;
|
||||
// at least nlines strings to display
|
||||
wxArrayString strings;
|
||||
// and their starting addrs (mostly for scrollbar)
|
||||
wxArrayInt addrs;
|
||||
// how far back to scroll for single line
|
||||
int back_size;
|
||||
// address of top line
|
||||
u32 topaddr;
|
||||
// max address for scrollbar
|
||||
u32 maxaddr;
|
||||
protected:
|
||||
// assigned to textctrl to avoid mouse input
|
||||
void MouseEvent(wxMouseEvent &ev) {}
|
||||
// the subwidgets
|
||||
wxTextCtrl tc;
|
||||
wxScrollBar sb;
|
||||
// cached computed line tc size
|
||||
int lineheight, extraheight;
|
||||
// need to know if tc/sb have been Create()d yet
|
||||
bool didinit;
|
||||
// selection info
|
||||
u32 seladdr;
|
||||
bool issel;
|
||||
|
||||
DECLARE_DYNAMIC_CLASS() // for xrc
|
||||
DECLARE_EVENT_TABLE()
|
||||
};
|
||||
|
||||
BEGIN_DECLARE_EVENT_TYPES()
|
||||
// event generated when fewer lines available than needed
|
||||
DECLARE_LOCAL_EVENT_TYPE(EVT_REFILL_NEEDED, 0)
|
||||
END_DECLARE_EVENT_TYPES()
|
||||
|
||||
// a hex editor with a funky scrollbar like above
|
||||
|
||||
// since it's impossible to exercise this much control over a text
|
||||
// control, it uses a panel into which text is drawn.
|
||||
// Maybe some day the above will be changed to do that as well, since
|
||||
// it allows better mouse control as well.
|
||||
|
||||
class MemView : public wxPanel
|
||||
{
|
||||
public:
|
||||
MemView();
|
||||
// called after init to create subcontrols and size panel
|
||||
void Refit();
|
||||
void MoveSB();
|
||||
void MoveView(wxScrollEvent &ev);
|
||||
private:
|
||||
void RefillNeeded();
|
||||
public:
|
||||
// called by parent's refill handler or any other time strings have
|
||||
// changed
|
||||
void Refill();
|
||||
private:
|
||||
void Refill(wxDC &dc);
|
||||
void RepaintEv(wxPaintEvent &ev);
|
||||
void Repaint();
|
||||
void Resize(wxSizeEvent &ev);
|
||||
|
||||
public:
|
||||
// make addr visible
|
||||
void Show(u32 addr, bool force_update = false);
|
||||
|
||||
// current selection, or topaddr if none
|
||||
u32 GetAddr();
|
||||
// currently visible lines
|
||||
int nlines;
|
||||
// at least nlines * 4 words to display
|
||||
wxArrayInt words;
|
||||
// address of top line
|
||||
u32 topaddr;
|
||||
// max address for scrollbar
|
||||
u32 maxaddr;
|
||||
// bytes per word == (1 << fmt)
|
||||
int fmt;
|
||||
// after write, these contain write addr and val
|
||||
u32 writeaddr, writeval;
|
||||
// when selection is made, this widget is updated w/ addr
|
||||
wxControl *addrlab;
|
||||
protected:
|
||||
// easier than checking maxaddr
|
||||
int addrlen;
|
||||
|
||||
void MouseEvent(wxMouseEvent &ev);
|
||||
void KeyEvent(wxKeyEvent &ev);
|
||||
// the subwidgets
|
||||
wxPanel disp;
|
||||
wxScrollBar sb;
|
||||
wxCaret *caret;
|
||||
// cached text size
|
||||
int charheight, charwidth;
|
||||
// need to know if tc/sb have been Create()d yet
|
||||
bool didinit;
|
||||
// selection info
|
||||
int selnib, seladdr;
|
||||
bool isasc;
|
||||
void ShowCaret();
|
||||
|
||||
DECLARE_DYNAMIC_CLASS() // for xrc
|
||||
DECLARE_EVENT_TABLE()
|
||||
};
|
||||
|
||||
BEGIN_DECLARE_EVENT_TYPES()
|
||||
// event generated when write occurs
|
||||
// check writeaddr/writeval/fmt
|
||||
DECLARE_LOCAL_EVENT_TYPE(EVT_WRITEVAL, 0)
|
||||
END_DECLARE_EVENT_TYPES()
|
||||
|
||||
// Display a color in a square, with the RGB value to its right.
|
||||
|
||||
// this is too hard to integrate into xrc (impossible to initialize
|
||||
// correctly) so no accomodations are made for this
|
||||
|
||||
class ColorView : public wxControl
|
||||
{
|
||||
public:
|
||||
ColorView(wxWindow *parent, wxWindowID id);
|
||||
void SetRGB(int r, int g, int b);
|
||||
void GetRGB(int &_r, int &_g, int &_b) { _r = r; _g = g; _b = b; }
|
||||
|
||||
protected:
|
||||
int r, g, b;
|
||||
wxPanel *cp;
|
||||
wxStaticText *rt, *gt, *bt;
|
||||
};
|
||||
#define unkctrl(n, v) do { \
|
||||
if(!wxXmlResource::Get()->AttachUnknownControl(wxT(n), v, this)) \
|
||||
baddialog(); \
|
||||
} while(0)
|
||||
#define colorctrl(v, n) do { \
|
||||
v = new ColorView(this, XRCID(n)); \
|
||||
if(!v) \
|
||||
baddialog(); \
|
||||
unkctrl(n, v); \
|
||||
} while(0)
|
||||
|
||||
// Display a small bitmap in jumbopixel style. If a pixel is selected, it
|
||||
// is highlighted with a border. For wxvbam, no event is generated.
|
||||
// Instead, a ColorView can be assigned to it, and on selection, that
|
||||
// widget will be updated to the selected color.
|
||||
// The whole class can also be derived to add more functionality to the
|
||||
// button click.
|
||||
|
||||
// It must be intialized in 3 phases: 2-phase xrc-style (new + Create()),
|
||||
// and then InitBMP()
|
||||
|
||||
class PixView : public wxPanel
|
||||
{
|
||||
public:
|
||||
PixView() : wxPanel(), bm(0) {}
|
||||
bool InitBMP(int w = 8, int h = 8, ColorView *cv = NULL);
|
||||
// stride is in pixels
|
||||
// format is rgb24 (aka wxImage format)
|
||||
// x/y is added to data and returned coords
|
||||
// if data == NULL, bitmap will be reset to default (all-black)
|
||||
virtual void SetData(const unsigned char *data, int stride, int x = 0,
|
||||
int y = 0);
|
||||
// desel if out of displayed range
|
||||
void SetSel(int x, int y, bool dsel_cview_update = true);
|
||||
// -1, -1 = no sel
|
||||
void GetSel(int &x, int &y) {
|
||||
x = selx < 0 ? -1 : ox + selx;
|
||||
y = sely < 0 ? -1 : oy + sely;
|
||||
}
|
||||
ColorView *cview;
|
||||
protected:
|
||||
wxImage im;
|
||||
wxBitmap *bm;
|
||||
void Redraw(wxPaintEvent &ev);
|
||||
virtual void SelPoint(wxMouseEvent &ev);
|
||||
int ox, oy, selx, sely;
|
||||
|
||||
DECLARE_EVENT_TABLE()
|
||||
DECLARE_DYNAMIC_CLASS()
|
||||
};
|
||||
#define pixview(v, n, w, h, cv) do { \
|
||||
v = XRCCTRL(*this, n, PixView); \
|
||||
if(!v) \
|
||||
baddialog(); \
|
||||
v->InitBMP(w, h, cv); \
|
||||
} while(0)
|
||||
|
||||
// a graphics viewer panel; expected to be inside of a wxScrollWindow
|
||||
class GfxPanel : public wxPanel
|
||||
{
|
||||
public:
|
||||
GfxPanel() : wxPanel(), bm(0), selx(-1), sely(-1) {}
|
||||
int bmw, bmh;
|
||||
wxBitmap *bm;
|
||||
wxImage *im;
|
||||
PixView *pv;
|
||||
|
||||
protected:
|
||||
void DrawBitmap(wxPaintEvent &ev);
|
||||
void Click(wxMouseEvent &ev);
|
||||
void MouseMove(wxMouseEvent &ev);
|
||||
void DoSel(wxMouseEvent &ev, bool force = false);
|
||||
private:
|
||||
int selx, sely;
|
||||
|
||||
DECLARE_DYNAMIC_CLASS()
|
||||
DECLARE_EVENT_TABLE()
|
||||
};
|
||||
|
||||
|
||||
BEGIN_DECLARE_EVENT_TYPES()
|
||||
// event generated on mouse click
|
||||
// generates wxMouseEvent with coords adjusted to original bitmap
|
||||
// size regardless of scaling
|
||||
DECLARE_LOCAL_EVENT_TYPE(EVT_COMMAND_GFX_CLICK, 0)
|
||||
END_DECLARE_EVENT_TYPES()
|
||||
#define EVT_GFX_CLICK(id, fun) \
|
||||
DECLARE_EVENT_TABLE_ENTRY(EVT_COMMAND_GFX_CLICK, id, wxID_ANY, wxMouseEventHandler(fun), NULL),
|
||||
|
||||
// like Viewer, common stuff to all gfx viewers
|
||||
// this is what actually manages the GfxPanel
|
||||
class GfxViewer : public Viewer
|
||||
{
|
||||
public:
|
||||
GfxViewer(const wxChar *dname, int maxw, int maxh);
|
||||
void ChangeBMP();
|
||||
void BMPSize(int w, int h);
|
||||
protected:
|
||||
void StretchTog(wxCommandEvent &ev);
|
||||
void RefreshEv(wxCommandEvent &ev);
|
||||
void SaveBMP(wxCommandEvent &ev);
|
||||
wxImage image;
|
||||
GfxPanel *gv;
|
||||
private:
|
||||
static wxString bmp_save_dir;
|
||||
wxScrolledWindow *gvs;
|
||||
wxCheckBox *str;
|
||||
|
||||
DECLARE_EVENT_TABLE()
|
||||
};
|
||||
|
||||
// if the jumbopixel view is all there is, maybe send a GFX_CLICK..
|
||||
class PixViewEvt : public PixView
|
||||
{
|
||||
// generates a GFX_CLICK if a point is selected
|
||||
void SetData(const unsigned char *data, int stride, int x = 0,
|
||||
int y = 0);
|
||||
protected:
|
||||
// always generates a GFX_CLICK
|
||||
void SelPoint(wxMouseEvent &ev);
|
||||
void click();
|
||||
DECLARE_DYNAMIC_CLASS()
|
||||
};
|
||||
|
||||
// a display-only checkbox which does not look like it's disabled
|
||||
class DispCheckBox : public wxCheckBox
|
||||
{
|
||||
public:
|
||||
bool AcceptsFocus() { return false; }
|
||||
void MouseEvent(wxMouseEvent &ev) {}
|
||||
DECLARE_EVENT_TABLE()
|
||||
DECLARE_DYNAMIC_CLASS()
|
||||
};
|
||||
|
||||
// standard widgets in graphical viewers
|
||||
|
||||
}
|
||||
#endif /* WX_VIEWSUPT_H */
|
|
@ -0,0 +1,488 @@
|
|||
/////////////////////////////////////////////////////////////////////////////
|
||||
// Name: checkedlistctrl.cpp
|
||||
// Purpose: wxCheckedListCtrl
|
||||
// Author: Uknown ? (found at http://wiki.wxwidgets.org/wiki.pl?WxListCtrl)
|
||||
// Modified by: Francesco Montorsi
|
||||
// Created: 2005/06/29
|
||||
// RCS-ID: $Id$
|
||||
// Copyright: (c) 2005 Francesco Montorsi
|
||||
// Licence: wxWidgets licence
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
|
||||
|
||||
// For compilers that support precompilation, includes "wx.h".
|
||||
#include "wx/wxprec.h"
|
||||
|
||||
#ifdef __BORLANDC__
|
||||
#pragma hdrstop
|
||||
#endif
|
||||
|
||||
// includes
|
||||
#include "wx/checkedlistctrl.h"
|
||||
#include <wx/icon.h>
|
||||
#include <wx/settings.h>
|
||||
|
||||
|
||||
#if wxUSE_CHECKEDLISTCTRL
|
||||
|
||||
#if !CLC_VBAM_USAGE || !CLC_USE_SYSICONS
|
||||
// resources
|
||||
#include "wx/checked.xpm"
|
||||
#include "wx/unchecked.xpm"
|
||||
#include "wx/checked_dis.xpm"
|
||||
#include "wx/unchecked_dis.xpm"
|
||||
#else
|
||||
#include <wx/dcbuffer.h>
|
||||
#include <wx/renderer.h>
|
||||
#include <wx/checkbox.h>
|
||||
#endif
|
||||
|
||||
#if CLC_VBAM_USAGE
|
||||
IMPLEMENT_DYNAMIC_CLASS(wxCheckedListCtrl, wxListCtrl)
|
||||
#else
|
||||
IMPLEMENT_CLASS(wxCheckedListCtrl, wxListCtrl)
|
||||
#endif
|
||||
BEGIN_EVENT_TABLE(wxCheckedListCtrl, wxListCtrl)
|
||||
EVT_LEFT_DOWN(wxCheckedListCtrl::OnMouseEvent)
|
||||
END_EVENT_TABLE()
|
||||
|
||||
DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_ITEM_CHECKED);
|
||||
DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_ITEM_UNCHECKED);
|
||||
|
||||
|
||||
|
||||
// ------------------
|
||||
// wxCHECKEDLISTCTRL
|
||||
// ------------------
|
||||
|
||||
bool wxCheckedListCtrl::Create(wxWindow* parent, wxWindowID id, const wxPoint& pt,
|
||||
const wxSize& sz, long style, const wxValidator& validator, const wxString& name)
|
||||
{
|
||||
if (!wxListCtrl::Create(parent, id, pt, sz, style, validator, name))
|
||||
return FALSE;
|
||||
|
||||
#if CLC_VBAM_USAGE
|
||||
// support xrc by making init separate
|
||||
return Init();
|
||||
}
|
||||
|
||||
bool wxCheckedListCtrl::Init()
|
||||
{
|
||||
#if CLC_USE_SYSICONS
|
||||
// use native size images instead of 16x16
|
||||
wxCheckBox *cb = new wxCheckBox(GetParent(), wxID_ANY, wxEmptyString);
|
||||
wxSize cbsz = cb->GetBestSize();
|
||||
delete cb;
|
||||
m_imageList.Create(cbsz.GetWidth(), cbsz.GetHeight(), TRUE);
|
||||
#else
|
||||
m_imageList.Create(16, 16, TRUE);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
SetImageList(&m_imageList, wxIMAGE_LIST_SMALL);
|
||||
|
||||
#if CLC_VBAM_USAGE && CLC_USE_SYSICONS
|
||||
// pasted from wxWiki
|
||||
// but with native size instead of 16x16
|
||||
// constructor only takes wxSize in 2.9+, apparently
|
||||
wxBitmap unchecked_bmp(cbsz.GetWidth(), cbsz.GetHeight()),
|
||||
checked_bmp(cbsz.GetWidth(), cbsz.GetHeight()),
|
||||
unchecked_disabled_bmp(cbsz.GetWidth(), cbsz.GetHeight()),
|
||||
checked_disabled_bmp(cbsz.GetWidth(), cbsz.GetHeight());
|
||||
|
||||
// Bitmaps must not be selected by a DC for addition to the image list but I don't see
|
||||
// a way of diselecting them in wxMemoryDC so let's just use a code block to end the scope
|
||||
{
|
||||
wxMemoryDC renderer_dc;
|
||||
|
||||
// Unchecked
|
||||
renderer_dc.SelectObject(unchecked_bmp);
|
||||
renderer_dc.SetBackground(*wxTheBrushList->FindOrCreateBrush(GetBackgroundColour(), wxSOLID));
|
||||
renderer_dc.Clear();
|
||||
wxRendererNative::Get().DrawCheckBox(this, renderer_dc, wxRect(0, 0, 16, 16), 0);
|
||||
|
||||
// Checked
|
||||
renderer_dc.SelectObject(checked_bmp);
|
||||
renderer_dc.SetBackground(*wxTheBrushList->FindOrCreateBrush(GetBackgroundColour(), wxSOLID));
|
||||
renderer_dc.Clear();
|
||||
wxRendererNative::Get().DrawCheckBox(this, renderer_dc, wxRect(0, 0, 16, 16), wxCONTROL_CHECKED);
|
||||
|
||||
// Unchecked and Disabled
|
||||
renderer_dc.SelectObject(unchecked_disabled_bmp);
|
||||
renderer_dc.SetBackground(*wxTheBrushList->FindOrCreateBrush(GetBackgroundColour(), wxSOLID));
|
||||
renderer_dc.Clear();
|
||||
wxRendererNative::Get().DrawCheckBox(this, renderer_dc, wxRect(0, 0, 16, 16), 0 | wxCONTROL_DISABLED);
|
||||
|
||||
// Checked and Disabled
|
||||
renderer_dc.SelectObject(checked_disabled_bmp);
|
||||
renderer_dc.SetBackground(*wxTheBrushList->FindOrCreateBrush(GetBackgroundColour(), wxSOLID));
|
||||
renderer_dc.Clear();
|
||||
wxRendererNative::Get().DrawCheckBox(this, renderer_dc, wxRect(0, 0, 16, 16), wxCONTROL_CHECKED | wxCONTROL_DISABLED);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// the add order must respect the wxCLC_XXX_IMGIDX defines in the headers !
|
||||
m_imageList.Add(unchecked_bmp);
|
||||
m_imageList.Add(checked_bmp);
|
||||
m_imageList.Add(unchecked_disabled_bmp);
|
||||
m_imageList.Add(checked_disabled_bmp);
|
||||
#else
|
||||
// the add order must respect the wxCLC_XXX_IMGIDX defines in the headers !
|
||||
m_imageList.Add(wxIcon(unchecked_xpm));
|
||||
m_imageList.Add(wxIcon(checked_xpm));
|
||||
m_imageList.Add(wxIcon(unchecked_dis_xpm));
|
||||
m_imageList.Add(wxIcon(checked_dis_xpm));
|
||||
#endif
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* static */
|
||||
int wxCheckedListCtrl::GetItemImageFromAdditionalState(int addstate)
|
||||
{
|
||||
bool checked = (addstate & wxLIST_STATE_CHECKED) != 0;
|
||||
bool enabled = (addstate & wxLIST_STATE_ENABLED) != 0;
|
||||
|
||||
if (checked && enabled)
|
||||
return wxCLC_CHECKED_IMGIDX;
|
||||
else if (checked && !enabled)
|
||||
return wxCLC_DISABLED_CHECKED_IMGIDX;
|
||||
else if (!checked && enabled)
|
||||
return wxCLC_UNCHECKED_IMGIDX;
|
||||
|
||||
wxASSERT(!checked && !enabled); // this is the last possibility
|
||||
return wxCLC_DISABLED_UNCHECKED_IMGIDX;
|
||||
}
|
||||
|
||||
wxColour wxCheckedListCtrl::GetBgColourFromAdditionalState(int additionalstate)
|
||||
{
|
||||
if ((additionalstate & wxLIST_STATE_ENABLED) &&
|
||||
this->IsEnabled())
|
||||
return *wxWHITE;
|
||||
#ifdef __WXMSW__
|
||||
return wxColour(212, 208, 200);
|
||||
#else
|
||||
return wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* static */
|
||||
int wxCheckedListCtrl::GetAndRemoveAdditionalState(long *state, int statemask)
|
||||
{
|
||||
int additionalstate = 0;
|
||||
if (!state) return -1;
|
||||
|
||||
// extract the bits we are interested in
|
||||
bool checked = (*state & wxLIST_STATE_CHECKED) != 0;
|
||||
bool enabled = (*state & wxLIST_STATE_ENABLED) != 0;
|
||||
|
||||
// and set them in a different variable if they are included in the statemask
|
||||
if (checked && (statemask & wxLIST_STATE_CHECKED)) additionalstate |= wxLIST_STATE_CHECKED;
|
||||
if (enabled && (statemask & wxLIST_STATE_ENABLED)) additionalstate |= wxLIST_STATE_ENABLED;
|
||||
|
||||
// remove them from the original state var...
|
||||
*state &= ~wxLIST_STATE_CHECKED;
|
||||
*state &= ~wxLIST_STATE_ENABLED;
|
||||
return additionalstate;
|
||||
}
|
||||
|
||||
bool wxCheckedListCtrl::GetItem(wxListItem& info) const
|
||||
{
|
||||
// wx internal wxListCtrl::GetItem remove from the state mask the
|
||||
// wxLIST_STATE_CHECKED & wxLIST_STATE_ENABLED bits since they
|
||||
// are not part of wx standard flags... so we need to check those
|
||||
// flags against the original wxListItem's statemask...
|
||||
wxListItem original(info);
|
||||
|
||||
#ifdef __WXDEBUG__
|
||||
// we always want to retrieve also the image state for checking purposes...
|
||||
info.m_mask |= wxLIST_MASK_IMAGE;
|
||||
#endif
|
||||
|
||||
if (!wxListCtrl::GetItem(info))
|
||||
return FALSE;
|
||||
|
||||
// these are our additional supported states: read them from m_stateList
|
||||
bool checked = (m_stateList[info.m_itemId] & wxLIST_STATE_CHECKED) != 0;
|
||||
bool enabled = (m_stateList[info.m_itemId] & wxLIST_STATE_ENABLED) != 0;
|
||||
|
||||
// now intercept state requests about enable or check mode
|
||||
if ((original.m_mask & wxLIST_MASK_STATE) &&
|
||||
(original.m_stateMask & wxLIST_STATE_CHECKED)) {
|
||||
info.m_state |= (m_stateList[info.m_itemId] & wxLIST_STATE_CHECKED);
|
||||
info.m_stateMask |= wxLIST_STATE_CHECKED;
|
||||
info.m_mask |= wxLIST_MASK_STATE; // contains valid info !
|
||||
}
|
||||
if ((original.m_mask & wxLIST_MASK_STATE) &&
|
||||
(original.m_stateMask & wxLIST_STATE_ENABLED)) {
|
||||
info.m_state |= (m_stateList[info.m_itemId] & wxLIST_STATE_ENABLED);
|
||||
info.m_stateMask |= wxLIST_STATE_ENABLED;
|
||||
info.m_mask |= wxLIST_MASK_STATE; // contains valid info !
|
||||
}
|
||||
|
||||
// check that state & image are synch
|
||||
#if CLC_VBAM_USAGE
|
||||
if(info.GetColumn())
|
||||
return TRUE;
|
||||
#endif
|
||||
#ifdef __WXDEBUG__
|
||||
|
||||
wxASSERT_MSG((int)m_stateList.GetCount() == (int)GetItemCount(),
|
||||
wxT("Something wrong ! See InsertItem()"));
|
||||
|
||||
// read info by image index
|
||||
bool imagecheck = (info.m_image == wxCLC_CHECKED_IMGIDX) ||
|
||||
(info.m_image == wxCLC_DISABLED_CHECKED_IMGIDX);
|
||||
bool imageenabled = (info.m_image == wxCLC_CHECKED_IMGIDX) ||
|
||||
(info.m_image == wxCLC_UNCHECKED_IMGIDX);
|
||||
wxASSERT_MSG((checked && imagecheck) || (!checked && !imagecheck),
|
||||
wxT("This is item has checked state but it's shown as unchecked (or viceversa)"));
|
||||
wxASSERT_MSG((enabled && imageenabled) || (!enabled && !imageenabled),
|
||||
wxT("This is item has enabled state but it's shown as disabled (or viceversa)"));
|
||||
#endif
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
bool wxCheckedListCtrl::SetItem(wxListItem& info)
|
||||
{
|
||||
#if CLC_VBAM_USAGE
|
||||
// only col 0 gets a checkbox
|
||||
if(info.GetColumn())
|
||||
return wxListCtrl::SetItem(info);
|
||||
#endif
|
||||
// remove the checked & enabled states from the state flag:
|
||||
// we'll store them in our separate array
|
||||
int additionalstate = GetAndRemoveAdditionalState(&info.m_state, info.m_stateMask);
|
||||
|
||||
// set image index
|
||||
// we will ignore the info.m_image field since we need
|
||||
// to overwrite it...
|
||||
if (info.m_mask & wxLIST_MASK_STATE) {
|
||||
|
||||
// if some state is not included in the state mask, then get the state info
|
||||
// from our internal state array
|
||||
if (!(info.m_stateMask & wxLIST_STATE_ENABLED))
|
||||
additionalstate |= (m_stateList[info.m_itemId] & wxLIST_STATE_ENABLED);
|
||||
if (!(info.m_stateMask & wxLIST_STATE_CHECKED))
|
||||
additionalstate |= (m_stateList[info.m_itemId] & wxLIST_STATE_CHECKED);
|
||||
|
||||
// state is valid: use it to determine the image to set...
|
||||
info.m_mask |= wxLIST_MASK_IMAGE;
|
||||
info.m_image = GetItemImageFromAdditionalState(additionalstate);
|
||||
|
||||
// since when changing the background color, also the foreground color
|
||||
// and the font of the item are changed, we try to respect the user
|
||||
// choices of such attributes
|
||||
info.SetTextColour(this->GetItemTextColour(info.GetId()));
|
||||
#if wxCHECK_VERSION(2, 6, 2)
|
||||
// before wx 2.6.2 the wxListCtrl::SetItemFont function is missing
|
||||
info.SetFont(this->GetItemFont(info.GetId()));
|
||||
#endif
|
||||
|
||||
// change the background color to respect the enabled/disabled status...
|
||||
info.SetBackgroundColour(GetBgColourFromAdditionalState(additionalstate));
|
||||
|
||||
m_stateList[info.m_itemId] = additionalstate;
|
||||
|
||||
} else {
|
||||
|
||||
// state is invalid; don't change image
|
||||
info.m_mask &= ~wxLIST_MASK_IMAGE;
|
||||
}
|
||||
|
||||
// save the changes
|
||||
return wxListCtrl::SetItem(info);
|
||||
}
|
||||
|
||||
long wxCheckedListCtrl::InsertItem(wxListItem &info)
|
||||
{
|
||||
int additionalstate = GetAndRemoveAdditionalState(&info.m_state, info.m_stateMask);
|
||||
if (!(info.m_mask & wxLIST_MASK_STATE) ||
|
||||
!(info.m_stateMask & wxLIST_STATE_ENABLED)) {
|
||||
|
||||
// if not specified, the default additional state is ENABLED
|
||||
additionalstate = wxLIST_STATE_ENABLED;
|
||||
}
|
||||
|
||||
// we always want to insert items with images...
|
||||
info.m_mask |= wxLIST_MASK_IMAGE;
|
||||
info.m_image = GetItemImageFromAdditionalState(additionalstate);
|
||||
info.SetBackgroundColour(GetBgColourFromAdditionalState(additionalstate));
|
||||
|
||||
int itemcount = GetItemCount();
|
||||
wxASSERT_MSG(info.m_itemId <= itemcount, wxT("Invalid index !"));
|
||||
wxASSERT_MSG((int)m_stateList.GetCount() == (int)GetItemCount(),
|
||||
wxT("Something wrong !"));
|
||||
if (info.m_itemId == itemcount) {
|
||||
|
||||
// we are adding a new item at the end of the list
|
||||
m_stateList.Add(additionalstate);
|
||||
|
||||
} else {
|
||||
|
||||
// we must shift all following items
|
||||
m_stateList.Add(m_stateList[itemcount - 1]);
|
||||
for (int i=itemcount - 1; i > info.m_itemId; i--)
|
||||
m_stateList[i] = m_stateList[i-1];
|
||||
m_stateList[info.m_itemId] = additionalstate;
|
||||
}
|
||||
|
||||
return wxListCtrl::InsertItem(info);
|
||||
}
|
||||
|
||||
bool wxCheckedListCtrl::SetItemState(long item, long state, long stateMask)
|
||||
{
|
||||
wxListItem li;
|
||||
li.SetId(item);
|
||||
li.SetMask(wxLIST_MASK_STATE);
|
||||
li.SetState(state);
|
||||
li.SetStateMask(stateMask);
|
||||
|
||||
// so we are sure to use wxCheckedListCtrl::SetItem
|
||||
// (and not wxListCtrl::SetItem)
|
||||
return SetItem(li);
|
||||
}
|
||||
|
||||
int wxCheckedListCtrl::GetItemState(long item, long stateMask) const
|
||||
{
|
||||
wxListItem li;
|
||||
li.SetId(item);
|
||||
li.SetMask(wxLIST_MASK_STATE);
|
||||
li.SetStateMask(stateMask);
|
||||
|
||||
// so we are sure to use wxCheckedListCtrl::GetItem
|
||||
// (and not wxListCtrl::GetItem)
|
||||
if (!GetItem(li))
|
||||
return -1;
|
||||
return li.GetState();
|
||||
}
|
||||
|
||||
long wxCheckedListCtrl::SetItem(long index, int col, const wxString& label, int WXUNUSED(imageId))
|
||||
{
|
||||
wxListItem li;
|
||||
li.SetId(index);
|
||||
li.SetColumn(col);
|
||||
li.SetText(label);
|
||||
li.SetMask(wxLIST_MASK_TEXT);
|
||||
|
||||
// so we are sure to use wxCheckedListCtrl::SetItem
|
||||
// (and not wxListCtrl::SetItem)
|
||||
return SetItem(li);
|
||||
}
|
||||
|
||||
long wxCheckedListCtrl::InsertItem( long index, const wxString& label, int WXUNUSED(imageIndex) )
|
||||
{
|
||||
wxListItem info;
|
||||
info.m_text = label;
|
||||
info.m_mask = wxLIST_MASK_TEXT;
|
||||
info.m_itemId = index;
|
||||
return InsertItem(info);
|
||||
}
|
||||
|
||||
void wxCheckedListCtrl::Check(long item, bool checked)
|
||||
{
|
||||
// NB: the "statemask" is not the "mask" of a list item;
|
||||
// in the "mask" you use the wxLIST_MASK_XXXX defines;
|
||||
// in the "statemask" you use the wxLIST_STATE_XXX defines
|
||||
// to set a specific bit of the wxListInfo::m_state var
|
||||
if (checked)
|
||||
// the 2nd parameter says: activate the STATE bit relative to CHECK feature
|
||||
// the 3rd parameter says: set only *that* bit
|
||||
SetItemState(item, wxLIST_STATE_CHECKED, wxLIST_STATE_CHECKED);
|
||||
else
|
||||
SetItemState(item, 0, wxLIST_STATE_CHECKED);
|
||||
}
|
||||
|
||||
void wxCheckedListCtrl::Enable(long item, bool enable)
|
||||
{
|
||||
if (enable)
|
||||
// the 2nd parameter says: activate the STATE bit relative to ENABLE feature
|
||||
// the 3rd parameter says: set only *that* bit
|
||||
SetItemState(item, wxLIST_STATE_ENABLED, wxLIST_STATE_ENABLED);
|
||||
else
|
||||
SetItemState(item, 0, wxLIST_STATE_ENABLED);
|
||||
}
|
||||
|
||||
void wxCheckedListCtrl::EnableAll(bool enable)
|
||||
{
|
||||
for (int i=0; i < GetItemCount(); i++)
|
||||
Enable(i, enable);
|
||||
}
|
||||
|
||||
void wxCheckedListCtrl::CheckAll(bool check)
|
||||
{
|
||||
for (int i=0; i < GetItemCount(); i++)
|
||||
Check(i, check);
|
||||
}
|
||||
|
||||
bool wxCheckedListCtrl::DeleteItem(long item)
|
||||
{
|
||||
// shift our additional state array
|
||||
//for (int i=item,max=GetItemCount(); i < max-1; i++)
|
||||
// m_stateList[i] = m_stateList[i+1];
|
||||
m_stateList.RemoveAt(item, 1);
|
||||
|
||||
return wxListCtrl::DeleteItem(item);
|
||||
}
|
||||
|
||||
int wxCheckedListCtrl::GetCheckedItemCount() const
|
||||
{
|
||||
int res = 0;
|
||||
for (int i=0; i<GetItemCount(); i++)
|
||||
if (IsChecked(i))
|
||||
res++;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
// event handlers
|
||||
|
||||
void wxCheckedListCtrl::OnMouseEvent(wxMouseEvent& event)
|
||||
{
|
||||
if (!event.LeftDown()) {
|
||||
event.Skip();
|
||||
return;
|
||||
}
|
||||
|
||||
int flags;
|
||||
long item = HitTest(event.GetPosition(), flags);
|
||||
if (item == wxNOT_FOUND || !IsEnabled(item)) {
|
||||
|
||||
// skip this item
|
||||
event.Skip();
|
||||
return;
|
||||
}
|
||||
|
||||
// user clicked exactly on the checkbox or on the item row ?
|
||||
bool processcheck = (flags & wxLIST_HITTEST_ONITEMICON) ||
|
||||
((GetWindowStyle() & wxCLC_CHECK_WHEN_SELECTING) &&
|
||||
(flags & wxLIST_HITTEST_ONITEM));
|
||||
|
||||
if (processcheck) {
|
||||
|
||||
wxListEvent ev(wxEVT_NULL, GetId());
|
||||
ev.m_itemIndex = item;
|
||||
|
||||
// send the check event
|
||||
if (IsChecked(item)) {
|
||||
|
||||
ev.SetEventType(wxEVT_COMMAND_LIST_ITEM_UNCHECKED);
|
||||
Check(item, FALSE);
|
||||
AddPendingEvent(ev);
|
||||
|
||||
} else {
|
||||
|
||||
ev.SetEventType(wxEVT_COMMAND_LIST_ITEM_CHECKED);
|
||||
Check(item, TRUE);
|
||||
AddPendingEvent(ev);
|
||||
}
|
||||
}
|
||||
|
||||
event.Skip();
|
||||
}
|
||||
|
||||
#endif // wxUSE_CHECKEDLISTCTRL
|
|
@ -0,0 +1,272 @@
|
|||
#include "wx/joyedit.h"
|
||||
|
||||
// FIXME: suppport analog/digital flag on per-axis basis
|
||||
|
||||
IMPLEMENT_DYNAMIC_CLASS(wxJoyKeyTextCtrl, wxKeyTextCtrl)
|
||||
|
||||
BEGIN_EVENT_TABLE(wxJoyKeyTextCtrl, wxKeyTextCtrl)
|
||||
EVT_SDLJOY(wxJoyKeyTextCtrl::OnJoy)
|
||||
END_EVENT_TABLE()
|
||||
|
||||
int wxJoyKeyTextCtrl::DigitalButton(wxSDLJoyEvent &event)
|
||||
{
|
||||
int sdlval = event.GetControlValue();
|
||||
int sdltype = event.GetControlType();
|
||||
|
||||
switch(sdltype) {
|
||||
case WXSDLJOY_AXIS:
|
||||
// for val = 0 return arbitrary direction; val means "off"
|
||||
return sdlval > 0 ? WXJB_AXIS_PLUS : WXJB_AXIS_MINUS;
|
||||
case WXSDLJOY_HAT:
|
||||
/* URDL = 1248 */
|
||||
switch(sdlval) {
|
||||
case 1:
|
||||
return WXJB_HAT_N;
|
||||
case 2:
|
||||
return WXJB_HAT_E;
|
||||
case 3:
|
||||
return WXJB_HAT_NE;
|
||||
case 4:
|
||||
return WXJB_HAT_S;
|
||||
case 6:
|
||||
return WXJB_HAT_SE;
|
||||
case 8:
|
||||
return WXJB_HAT_W;
|
||||
case 9:
|
||||
return WXJB_HAT_NW;
|
||||
case 12:
|
||||
return WXJB_HAT_SW;
|
||||
default:
|
||||
return WXJB_HAT_N; // arbitrary direction; val = 0 means "off"
|
||||
}
|
||||
case WXSDLJOY_BUTTON:
|
||||
return WXJB_BUTTON;
|
||||
default:
|
||||
// unknown ctrl type
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
void wxJoyKeyTextCtrl::OnJoy(wxSDLJoyEvent &event)
|
||||
{
|
||||
short val = event.GetControlValue();
|
||||
int mod = DigitalButton(event);
|
||||
int key = event.GetControlIndex(), joy = event.GetJoy() + 1;
|
||||
|
||||
if(!val || mod < 0)
|
||||
return;
|
||||
wxString nv = ToString(mod, key, joy);
|
||||
if(nv.empty())
|
||||
return;
|
||||
if(multikey) {
|
||||
wxString ov = GetValue();
|
||||
if(!ov.empty())
|
||||
nv = ov + multikey + nv;
|
||||
}
|
||||
SetValue(nv);
|
||||
if(keyenter)
|
||||
Navigate();
|
||||
}
|
||||
|
||||
wxString wxJoyKeyTextCtrl::ToString(int mod, int key, int joy)
|
||||
{
|
||||
if(!joy)
|
||||
return wxKeyTextCtrl::ToString(mod, key);
|
||||
wxString s;
|
||||
// Note: wx translates unconditionally (2.8.12, 2.9.1)!
|
||||
// So any strings added below must also be translated unconditionally
|
||||
s.Printf(_("Joy%d-"), joy);
|
||||
wxString mk;
|
||||
switch(mod) {
|
||||
case WXJB_AXIS_PLUS:
|
||||
mk.Printf(_("Axis%d+"), key);
|
||||
break;
|
||||
case WXJB_AXIS_MINUS:
|
||||
mk.Printf(_("Axis%d-"), key);
|
||||
break;
|
||||
case WXJB_BUTTON:
|
||||
mk.Printf(_("Button%d"), key);
|
||||
break;
|
||||
case WXJB_HAT_N:
|
||||
mk.Printf(_("Hat%dN"), key);
|
||||
break;
|
||||
case WXJB_HAT_S:
|
||||
mk.Printf(_("Hat%dS"), key);
|
||||
break;
|
||||
case WXJB_HAT_W:
|
||||
mk.Printf(_("Hat%dW"), key);
|
||||
break;
|
||||
case WXJB_HAT_E:
|
||||
mk.Printf(_("Hat%dE"), key);
|
||||
break;
|
||||
case WXJB_HAT_NW:
|
||||
mk.Printf(_("Hat%dNW"), key);
|
||||
break;
|
||||
case WXJB_HAT_NE:
|
||||
mk.Printf(_("Hat%dNE"), key);
|
||||
break;
|
||||
case WXJB_HAT_SW:
|
||||
mk.Printf(_("Hat%dSW"), key);
|
||||
break;
|
||||
case WXJB_HAT_SE:
|
||||
mk.Printf(_("Hat%dSE"), key);
|
||||
break;
|
||||
}
|
||||
s += mk;
|
||||
return s;
|
||||
}
|
||||
|
||||
wxString wxJoyKeyTextCtrl::ToString(wxJoyKeyBinding_v keys, wxChar sep)
|
||||
{
|
||||
wxString ret;
|
||||
for(int i = 0; i < keys.size(); i++) {
|
||||
if(i > 0)
|
||||
ret += sep;
|
||||
wxString key = ToString(keys[i].mod, keys[i].key, keys[i].joy);
|
||||
if(key.empty())
|
||||
return wxEmptyString;
|
||||
ret += key;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
#include <wx/regex.h>
|
||||
|
||||
// only parse regex once
|
||||
// Note: wx translates unconditionally (2.8.12, 2.9.1)!
|
||||
// So any strings added below must also be translated unconditionally
|
||||
// \1 is joy #
|
||||
static wxRegEx joyre(_("^Joy([0-9]+)[-+]"), wxRE_EXTENDED|wxRE_ICASE);
|
||||
// \1 is axis# and \2 is + or -
|
||||
static wxRegEx axre(_("Axis([0-9]+)([+-])"), wxRE_EXTENDED|wxRE_ICASE);
|
||||
// \1 is button#
|
||||
static wxRegEx butre(_("Button([0-9]+)"), wxRE_EXTENDED|wxRE_ICASE);
|
||||
// \1 is hat#, \3 is N, \4 is S, \5 is E, \6 is W, \7 is NE, \8 is SE,
|
||||
// \9 is SW, \10 is NW
|
||||
static wxRegEx hatre(_("Hat([0-9]+)((N|North|U|Up)|(S|South|D|Down)|"
|
||||
"(E|East|R|Right)|(W|West|L|Left)|"
|
||||
"(NE|NorthEast|UR|UpRight)|(SE|SouthEast|DR|DownRight)|"
|
||||
"(SW|SouthWest|DL|DownLeft)|(NW|NorthWest|UL|UpLeft))"),
|
||||
wxRE_EXTENDED|wxRE_ICASE);
|
||||
// use of static wxRegeEx is not thread-safe
|
||||
static wxCriticalSection recs;
|
||||
|
||||
// wx provides no atoi for wxChar
|
||||
// this is not a universal function; assumes valid number
|
||||
static int simple_atoi(const wxChar *s, int len)
|
||||
{
|
||||
int ret = 0;
|
||||
for(int i = 0; i < len; i++)
|
||||
ret = ret * 10 + s[i] - wxT('0');
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool ParseJoy(const wxChar *s, int len, int &mod, int &key, int &joy)
|
||||
{
|
||||
mod = key = joy = 0;
|
||||
if(!len)
|
||||
return false;
|
||||
|
||||
wxCriticalSectionLocker lk(recs);
|
||||
size_t b, l;
|
||||
if(!joyre.Matches(s) || !joyre.GetMatch(&b, &l) || b)
|
||||
return false;
|
||||
const wxChar *p = s + l;
|
||||
int alen = len - l;
|
||||
joyre.GetMatch(&b, &l, 1);
|
||||
joy = simple_atoi(s + b, l);
|
||||
#define is_ctrl(re) re.Matches(p) && re.GetMatch(&b, &l) && l == alen && !b
|
||||
if(is_ctrl(axre)) {
|
||||
axre.GetMatch(&b, &l, 1);
|
||||
key = simple_atoi(p + b, l);
|
||||
axre.GetMatch(&b, &l, 2);
|
||||
mod = p[b] == wxT('+') ? WXJB_AXIS_PLUS : WXJB_AXIS_MINUS;
|
||||
} else if(is_ctrl(butre)) {
|
||||
butre.GetMatch(&b, &l, 1);
|
||||
key = simple_atoi(p + b, l);
|
||||
mod = WXJB_BUTTON;
|
||||
} else if(is_ctrl(hatre)) {
|
||||
hatre.GetMatch(&b, &l, 1);
|
||||
key = simple_atoi(p + b, l);
|
||||
#define check_dir(n, d) else if(hatre.GetMatch(&b, &l, n) && l > 0) mod = WXJB_HAT_##d
|
||||
if(0);
|
||||
check_dir(3, N);
|
||||
check_dir(4, S);
|
||||
check_dir(5, E);
|
||||
check_dir(6, W);
|
||||
check_dir(7, NE);
|
||||
check_dir(8, SE);
|
||||
check_dir(9, SW);
|
||||
check_dir(10, NW);
|
||||
} else {
|
||||
joy = 0;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool wxJoyKeyTextCtrl::ParseString(const wxChar *s, int len, int &mod, int &key, int &joy)
|
||||
{
|
||||
if(ParseJoy(s, len, mod, key, joy))
|
||||
return true;
|
||||
return wxKeyTextCtrl::ParseString(s, len, mod, key);
|
||||
}
|
||||
|
||||
bool wxJoyKeyTextCtrl::FromString(const wxString &s, int &mod, int &key, int &joy)
|
||||
{
|
||||
return ParseString(s.c_str(), s.size(), mod, key, joy);
|
||||
}
|
||||
|
||||
wxJoyKeyBinding_v wxJoyKeyTextCtrl::FromString(const wxString &s, wxChar sep)
|
||||
{
|
||||
wxJoyKeyBinding_v ret, empty;
|
||||
int mod, key, joy;
|
||||
int len = s.size();
|
||||
if(!len)
|
||||
return empty;
|
||||
|
||||
for(int lastkey = len - 1; (lastkey = s.rfind(sep, lastkey)) != wxString::npos; lastkey--) {
|
||||
if(lastkey == len - 1) {
|
||||
// sep as accel
|
||||
if(!lastkey)
|
||||
break;
|
||||
if(s[lastkey - 1] == wxT('-') || s[lastkey - 1] == wxT('+') ||
|
||||
s[lastkey - 1] == sep)
|
||||
continue;
|
||||
}
|
||||
if(!ParseString(s.c_str() + lastkey + 1, len - lastkey - 1, mod, key, joy))
|
||||
return empty;
|
||||
wxJoyKeyBinding jb = { key, mod, joy };
|
||||
ret.insert(ret.begin(), jb);
|
||||
len = lastkey;
|
||||
}
|
||||
if(!ParseString(s.c_str(), len, mod, key, joy))
|
||||
return empty;
|
||||
wxJoyKeyBinding jb = { key, mod, joy };
|
||||
ret.insert(ret.begin(), jb);
|
||||
return ret;
|
||||
}
|
||||
|
||||
IMPLEMENT_CLASS(wxJoyKeyValidator, wxValidator)
|
||||
|
||||
bool wxJoyKeyValidator::TransferToWindow()
|
||||
{
|
||||
if(!val)
|
||||
return false;
|
||||
wxJoyKeyTextCtrl *jk = wxDynamicCast(GetWindow(), wxJoyKeyTextCtrl);
|
||||
if(!jk)
|
||||
return false;
|
||||
jk->SetValue(wxJoyKeyTextCtrl::ToString(*val));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool wxJoyKeyValidator::TransferFromWindow()
|
||||
{
|
||||
if(!val)
|
||||
return false;
|
||||
wxJoyKeyTextCtrl *jk = wxDynamicCast(GetWindow(), wxJoyKeyTextCtrl);
|
||||
if(!jk)
|
||||
return false;
|
||||
*val = wxJoyKeyTextCtrl::FromString(jk->GetValue());
|
||||
return true;
|
||||
}
|
|
@ -0,0 +1,228 @@
|
|||
#include "wx/keyedit.h"
|
||||
|
||||
IMPLEMENT_DYNAMIC_CLASS(wxKeyTextCtrl, wxTextCtrl)
|
||||
|
||||
BEGIN_EVENT_TABLE(wxKeyTextCtrl, wxTextCtrl)
|
||||
EVT_KEY_DOWN(wxKeyTextCtrl::OnKeyDown)
|
||||
EVT_KEY_UP(wxKeyTextCtrl::OnKeyUp)
|
||||
END_EVENT_TABLE()
|
||||
|
||||
void wxKeyTextCtrl::OnKeyDown(wxKeyEvent &event)
|
||||
{
|
||||
lastmod = event.GetModifiers();
|
||||
lastkey = event.GetKeyCode();
|
||||
}
|
||||
|
||||
void wxKeyTextCtrl::OnKeyUp(wxKeyEvent &event)
|
||||
{
|
||||
int mod = lastmod;
|
||||
int key = lastkey;
|
||||
lastmod = lastkey = 0;
|
||||
// key is only 0 if we missed the keydown event
|
||||
// or if we are being shipped pseudo keypress events
|
||||
// either way, just ignore
|
||||
if(!key)
|
||||
return;
|
||||
// use unmodified backspace to clear last key, if enabled
|
||||
// if blank or backspace is modified, add normally instead
|
||||
if(clearable && !mod && key == WXK_BACK && !GetValue().empty())
|
||||
{
|
||||
wxString val = GetValue();
|
||||
int lastkey = val.rfind(multikey);
|
||||
if(lastkey && lastkey != wxString::npos) {
|
||||
// if this was actually a ,-accel, delete instead
|
||||
if(lastkey == val.size() - 1) {
|
||||
lastkey = val.rfind(multikey, lastkey-1);
|
||||
if(lastkey == wxString::npos)
|
||||
lastkey = 0;
|
||||
}
|
||||
val.resize(lastkey);
|
||||
SetValue(val);
|
||||
} else
|
||||
Clear();
|
||||
} else {
|
||||
wxString nv = ToString(mod, key);
|
||||
if(nv.empty())
|
||||
return;
|
||||
if(multikey) {
|
||||
wxString ov = GetValue();
|
||||
if(!ov.empty())
|
||||
nv = ov + multikey + nv;
|
||||
}
|
||||
SetValue(nv);
|
||||
}
|
||||
if(keyenter)
|
||||
Navigate();
|
||||
}
|
||||
|
||||
wxString wxKeyTextCtrl::ToString(int mod, int key)
|
||||
{
|
||||
// wx ignores non-alnum printable chars
|
||||
// actually, wx gives an assertion error, so it's best to filter out
|
||||
// before passing to ToString()
|
||||
bool char_override = key > 32 && key < WXK_START && !wxIsalnum(key);
|
||||
// wx also ignores modifiers (and does not report meta at all)
|
||||
bool mod_override = key == WXK_SHIFT || key == WXK_CONTROL || key == WXK_ALT;
|
||||
wxAcceleratorEntry ae(mod, char_override || mod_override ? WXK_F1 : key);
|
||||
// Note: wx translates unconditionally (2.8.12, 2.9.1)!
|
||||
// So any strings added below must also be translated unconditionally
|
||||
wxString s = ae.ToString();
|
||||
if(char_override || mod_override) {
|
||||
int l = s.rfind(wxT('-'));
|
||||
if(l == wxString::npos)
|
||||
l = 0;
|
||||
else l++;
|
||||
s.erase(l);
|
||||
switch(key) {
|
||||
case WXK_SHIFT:
|
||||
s.append(_("SHIFT"));
|
||||
break;
|
||||
case WXK_ALT:
|
||||
s.append(_("ALT"));
|
||||
break;
|
||||
case WXK_CONTROL:
|
||||
s.append(_("CTRL"));
|
||||
break;
|
||||
default:
|
||||
s.append((wxChar)key);
|
||||
}
|
||||
}
|
||||
|
||||
// on Mac, ctrl/meta become xctrl/cmd
|
||||
// on other, meta is ignored
|
||||
#ifndef __WXMAC__
|
||||
if(mod & wxMOD_META) {
|
||||
s.insert(0, _("Meta-"));
|
||||
}
|
||||
#endif
|
||||
if(s.empty() || (key != wxT('-') && s[s.size()-1] == wxT('-')))
|
||||
// bad key combo; probably also generates an assertion in wx
|
||||
return wxEmptyString;
|
||||
return s;
|
||||
}
|
||||
|
||||
wxString wxKeyTextCtrl::ToString(wxAcceleratorEntry_v keys, wxChar sep)
|
||||
{
|
||||
wxString ret;
|
||||
for(int i = 0; i < keys.size(); i++) {
|
||||
if(i > 0)
|
||||
ret += sep;
|
||||
wxString key = ToString(keys[i].GetFlags(), keys[i].GetKeyCode());
|
||||
if(key.empty())
|
||||
return wxEmptyString;
|
||||
ret += key;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool wxKeyTextCtrl::ParseString(const wxChar *s, int len, int &mod, int &key)
|
||||
{
|
||||
mod = key = 0;
|
||||
if(!s || !len)
|
||||
return false;
|
||||
wxString a = wxT('\t');
|
||||
a.append(s, len);
|
||||
wxAcceleratorEntry ae;
|
||||
#ifndef __WXMAC__
|
||||
#define check_meta(str) do { \
|
||||
wxString meta = str; \
|
||||
for(int ml = 0; (ml = a.find(meta, ml)) != wxString::npos; ml++) { \
|
||||
if(!ml || a[ml-1] == wxT('-') || a[ml-1] == wxT('+')) { \
|
||||
mod |= wxMOD_META; \
|
||||
a.erase(ml, meta.size()); \
|
||||
ml = -1; \
|
||||
} \
|
||||
} \
|
||||
} while(0)
|
||||
check_meta(wxT("Meta-"));
|
||||
check_meta(wxT("Meta+"));
|
||||
check_meta(_("Meta-"));
|
||||
check_meta(_("Meta+"));
|
||||
#endif
|
||||
// wx disallows standalone modifiers
|
||||
// unlike ToString(), this generates a debug message rather than
|
||||
// an assertion error, so it's easy to ignore and expensive to avoid
|
||||
// beforehand. Instead, check for them on failure
|
||||
if(!ae.FromString(a)) {
|
||||
a.MakeUpper();
|
||||
#define chk_str(n, k) do { \
|
||||
wxString t = n; \
|
||||
if(a.size() > t.size() && a.substr(a.size() - t.size()) == t) { \
|
||||
a.replace(a.size() - t.size(), t.size(), wxT("F1")); \
|
||||
wxString ss(s); \
|
||||
if(ae.FromString(a)) { \
|
||||
mod |= ae.GetFlags(); \
|
||||
key = k; \
|
||||
return true; \
|
||||
} \
|
||||
a.replace(a.size() - 2, 2, n); \
|
||||
} \
|
||||
} while(0)
|
||||
chk_str(wxT("ALT"), WXK_ALT);
|
||||
chk_str(wxT("SHIFT"), WXK_SHIFT);
|
||||
chk_str(wxT("CTRL"), WXK_CONTROL);
|
||||
chk_str(wxT("CONTROL"), WXK_CONTROL);
|
||||
chk_str(_("ALT"), WXK_ALT);
|
||||
chk_str(_("SHIFT"), WXK_SHIFT);
|
||||
chk_str(_("CTRL"), WXK_CONTROL);
|
||||
chk_str(_("CONTROL"), WXK_CONTROL);
|
||||
return false;
|
||||
}
|
||||
mod |= ae.GetFlags();
|
||||
key = ae.GetKeyCode();
|
||||
// wx makes key lower-case, but key events return upper case
|
||||
if(key < WXK_START && wxIslower(key))
|
||||
key = wxToupper(key);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool wxKeyTextCtrl::FromString(const wxString &s, int &mod, int &key)
|
||||
{
|
||||
return ParseString(s.c_str(), s.size(), mod, key);
|
||||
}
|
||||
|
||||
wxAcceleratorEntry_v wxKeyTextCtrl::FromString(const wxString &s, wxChar sep)
|
||||
{
|
||||
wxAcceleratorEntry_v ret, empty;
|
||||
int mod, key;
|
||||
int len = s.size();
|
||||
|
||||
for(int lastkey = len - 1; (lastkey = s.rfind(sep, lastkey)) != wxString::npos; lastkey--) {
|
||||
if(lastkey == len - 1) {
|
||||
// sep as accel
|
||||
if(!lastkey)
|
||||
break;
|
||||
if(s[lastkey - 1] == wxT('-') || s[lastkey - 1] == wxT('+') ||
|
||||
s[lastkey - 1] == sep)
|
||||
continue;
|
||||
}
|
||||
if(!ParseString(s.c_str() + lastkey + 1, len - lastkey - 1, mod, key))
|
||||
return empty;
|
||||
ret.insert(ret.begin(), wxAcceleratorEntry(mod, key));
|
||||
len = lastkey;
|
||||
}
|
||||
if(!ParseString(s.c_str(), len, mod, key))
|
||||
return empty;
|
||||
ret.insert(ret.begin(), wxAcceleratorEntry(mod, key));
|
||||
return ret;
|
||||
}
|
||||
|
||||
IMPLEMENT_CLASS(wxKeyValidator, wxValidator)
|
||||
|
||||
bool wxKeyValidator::TransferToWindow()
|
||||
{
|
||||
wxKeyTextCtrl *k = wxDynamicCast(GetWindow(), wxKeyTextCtrl);
|
||||
if(!k)
|
||||
return false;
|
||||
k->SetValue(wxKeyTextCtrl::ToString(*val));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool wxKeyValidator::TransferFromWindow()
|
||||
{
|
||||
wxKeyTextCtrl *k = wxDynamicCast(GetWindow(), wxKeyTextCtrl);
|
||||
if(!k)
|
||||
return false;
|
||||
*val = wxKeyTextCtrl::FromString(k->GetValue());
|
||||
return true;
|
||||
}
|
|
@ -0,0 +1,159 @@
|
|||
#include "wx/sdljoy.h"
|
||||
#include <SDL/SDL.h>
|
||||
#include <SDL/SDL_joystick.h>
|
||||
#include <wx/window.h>
|
||||
|
||||
DEFINE_EVENT_TYPE(wxEVT_SDLJOY)
|
||||
|
||||
struct wxSDLJoyState {
|
||||
SDL_Joystick *dev;
|
||||
int nax, nhat, nbut;
|
||||
short *curval;
|
||||
~wxSDLJoyState() { if(dev) SDL_JoystickClose(dev); }
|
||||
};
|
||||
|
||||
wxSDLJoy::wxSDLJoy(bool analog) : wxTimer(), digital(!analog),
|
||||
evthandler(0), joystate(0), nosticks(true)
|
||||
{
|
||||
// Start up joystick if not already started
|
||||
// FIXME: check for errors
|
||||
SDL_Init(SDL_INIT_JOYSTICK);
|
||||
// but we'll have to manage it manually
|
||||
SDL_JoystickEventState(SDL_IGNORE);
|
||||
// now query joystick config and open joysticks
|
||||
// there is no way to reread this later (e.g. if joystick plugged in),
|
||||
// since SDL won't
|
||||
njoy = SDL_NumJoysticks();
|
||||
if(!njoy)
|
||||
return;
|
||||
joystate = new wxSDLJoyState_t[njoy];
|
||||
memset(joystate, 0, njoy * sizeof(*joystate));
|
||||
for(int i = 0; i < njoy; i++) {
|
||||
SDL_Joystick *dev = joystate[i].dev = SDL_JoystickOpen(i);
|
||||
int nctrl = 0;
|
||||
nctrl += joystate[i].nax = SDL_JoystickNumAxes(dev);
|
||||
nctrl += joystate[i].nhat = SDL_JoystickNumHats(dev);
|
||||
nctrl += joystate[i].nbut = SDL_JoystickNumButtons(dev);
|
||||
joystate[i].curval = new short[nctrl];
|
||||
memset(joystate[i].curval, 0, sizeof(short) * nctrl);
|
||||
}
|
||||
}
|
||||
|
||||
wxSDLJoy::~wxSDLJoy()
|
||||
{
|
||||
delete[] joystate;
|
||||
// SDL_QuitSubSystem(SDL_INIT_JOYSTICK);
|
||||
}
|
||||
|
||||
wxEvtHandler *wxSDLJoy::Attach(wxEvtHandler *handler)
|
||||
{
|
||||
wxEvtHandler *prev = evthandler;
|
||||
evthandler = handler;
|
||||
return prev;
|
||||
}
|
||||
|
||||
void wxSDLJoy::Add(int joy)
|
||||
{
|
||||
if(joy >= njoy || !njoy)
|
||||
return;
|
||||
if(joy < 0) {
|
||||
for(int i = 0; i < njoy; i++)
|
||||
Add(i);
|
||||
return;
|
||||
}
|
||||
if(!joystate[joy].dev)
|
||||
joystate[joy].dev = SDL_JoystickOpen(joy);
|
||||
if(nosticks && joystate[joy].dev) {
|
||||
Start(50);
|
||||
nosticks = false;
|
||||
}
|
||||
}
|
||||
|
||||
void wxSDLJoy::Remove(int joy)
|
||||
{
|
||||
if(joy >= njoy || !njoy)
|
||||
return;
|
||||
if(joy < 0) {
|
||||
for(int i = 0; i < njoy; i++)
|
||||
if(joystate[i].dev) {
|
||||
SDL_JoystickClose(joystate[i].dev);
|
||||
joystate[i].dev = NULL;
|
||||
}
|
||||
Stop();
|
||||
nosticks = true;
|
||||
return;
|
||||
}
|
||||
if(!joystate[joy].dev)
|
||||
return;
|
||||
SDL_JoystickClose(joystate[joy].dev);
|
||||
joystate[joy].dev = NULL;
|
||||
for(int i = 0; i < njoy; i++)
|
||||
if(joystate[i].dev)
|
||||
return;
|
||||
Stop();
|
||||
nosticks = true;
|
||||
}
|
||||
|
||||
void wxSDLJoy::Notify()
|
||||
{
|
||||
if(nosticks)
|
||||
return;
|
||||
SDL_JoystickUpdate();
|
||||
wxEvtHandler *handler = evthandler ? evthandler : wxWindow::FindFocus();
|
||||
for(int i = 0; i < njoy; i++) {
|
||||
SDL_Joystick *dev = joystate[i].dev;
|
||||
if(dev) {
|
||||
int nax = joystate[i].nax, nhat = joystate[i].nhat,
|
||||
nbut = joystate[i].nbut;
|
||||
int ctrl = 0;
|
||||
short val;
|
||||
for(int j = 0; j < nax; j++) {
|
||||
val = SDL_JoystickGetAxis(dev, j);
|
||||
if(digital) {
|
||||
if(val > 0x3fff)
|
||||
val = 0x7fff;
|
||||
else if(val <= -0x3fff)
|
||||
val = -0x7fff;
|
||||
else
|
||||
val = 0;
|
||||
}
|
||||
if(handler && val != joystate[i].curval[j]) {
|
||||
wxSDLJoyEvent ev(wxEVT_SDLJOY, GetId());
|
||||
ev.joy = i;
|
||||
ev.ctrl_type = WXSDLJOY_AXIS;
|
||||
ev.ctrl_idx = j;
|
||||
ev.ctrl_val = val;
|
||||
ev.prev_val = joystate[i].curval[j];
|
||||
handler->ProcessEvent(ev);
|
||||
}
|
||||
joystate[i].curval[j] = val;
|
||||
}
|
||||
for(int j = 0; j < nhat; j++) {
|
||||
val = SDL_JoystickGetHat(dev, j);
|
||||
if(handler && val != joystate[i].curval[nax + j]) {
|
||||
wxSDLJoyEvent ev(wxEVT_SDLJOY, GetId());
|
||||
ev.joy = i;
|
||||
ev.ctrl_type = WXSDLJOY_HAT;
|
||||
ev.ctrl_idx = j;
|
||||
ev.ctrl_val = val;
|
||||
ev.prev_val = joystate[i].curval[nax + j];
|
||||
handler->ProcessEvent(ev);
|
||||
}
|
||||
joystate[i].curval[nax + j] = val;
|
||||
}
|
||||
for(int j = 0; j < nbut; j++) {
|
||||
val = SDL_JoystickGetButton(dev, j);
|
||||
if(handler && val != joystate[i].curval[nax + nhat + j]) {
|
||||
wxSDLJoyEvent ev(wxEVT_SDLJOY, GetId());
|
||||
ev.joy = i;
|
||||
ev.ctrl_type = WXSDLJOY_BUTTON;
|
||||
ev.ctrl_idx = j;
|
||||
ev.ctrl_val = val;
|
||||
ev.prev_val = joystate[i].curval[nax + nhat + j];
|
||||
handler->ProcessEvent(ev);
|
||||
}
|
||||
joystate[i].curval[nax + nhat + j] = val;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,182 @@
|
|||
/////////////////////////////////////////////////////////////////////////////
|
||||
// Name: checkedlistctrl.h
|
||||
// Purpose: wxCheckedListCtrl
|
||||
// Author: Uknown ? (found at http://wiki.wxwidgets.org/wiki.pl?WxListCtrl)
|
||||
// Modified by: Francesco Montorsi
|
||||
// Created: 2005/06/29
|
||||
// RCS-ID: $Id$
|
||||
// Copyright: (c) 2005 Francesco Montorsi
|
||||
// Licence: wxWidgets licence
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
#ifndef _WX_CHECKEDLISTCTRL_H_
|
||||
#define _WX_CHECKEDLISTCTRL_H_
|
||||
|
||||
// wxWidgets headers
|
||||
#include "wx/webupdatedef.h" // for the WXDLLIMPEXP_WEBUPDATE macro
|
||||
#include <wx/listctrl.h>
|
||||
#include <wx/imaglist.h>
|
||||
|
||||
#if wxUSE_CHECKEDLISTCTRL
|
||||
|
||||
// image indexes (used internally by wxCheckedListCtrl)
|
||||
#define wxCLC_UNCHECKED_IMGIDX 0 // unchecked & enabled
|
||||
#define wxCLC_CHECKED_IMGIDX 1 // checked & enabled
|
||||
#define wxCLC_DISABLED_UNCHECKED_IMGIDX 2 // unchecked & disabled
|
||||
#define wxCLC_DISABLED_CHECKED_IMGIDX 3 // checked & disabled
|
||||
|
||||
// additional state flags (wx's defines should end at 0x0100; see listbase.h)
|
||||
#define wxLIST_STATE_CHECKED 0x010000
|
||||
#define wxLIST_STATE_ENABLED 0x100000
|
||||
|
||||
// additional wxCheckedListCtrl style flags
|
||||
// (wx's defines should at 0x8000; see listbase.h)
|
||||
#define wxCLC_CHECK_WHEN_SELECTING 0x10000
|
||||
|
||||
|
||||
// -------------------------
|
||||
// wxCheckedListCtrl events
|
||||
// -------------------------
|
||||
|
||||
DECLARE_EXPORTED_EVENT_TYPE(WXDLLIMPEXP_WEBUPDATE, wxEVT_COMMAND_LIST_ITEM_CHECKED, -1);
|
||||
DECLARE_EXPORTED_EVENT_TYPE(WXDLLIMPEXP_WEBUPDATE, wxEVT_COMMAND_LIST_ITEM_UNCHECKED, -1);
|
||||
|
||||
#define EVT_LIST_ITEM_CHECKED(id, fn) \
|
||||
DECLARE_EVENT_TABLE_ENTRY( \
|
||||
wxEVT_COMMAND_LIST_ITEM_CHECKED, id, -1, \
|
||||
(wxObjectEventFunction)(wxEventFunction)(wxListEventFunction)&fn, \
|
||||
(wxObject *) NULL \
|
||||
),
|
||||
|
||||
#define EVT_LIST_ITEM_UNCHECKED(id, fn) \
|
||||
DECLARE_EVENT_TABLE_ENTRY( \
|
||||
wxEVT_COMMAND_LIST_ITEM_UNCHECKED, id, -1, \
|
||||
(wxObjectEventFunction)(wxEventFunction)(wxListEventFunction)&fn, \
|
||||
(wxObject *) NULL \
|
||||
),
|
||||
|
||||
|
||||
|
||||
//! This is the class which performs all transactions with the server.
|
||||
//! It uses the wxSocket facilities.
|
||||
class WXDLLIMPEXP_WEBUPDATE wxCheckedListCtrl : public wxListCtrl
|
||||
{
|
||||
protected:
|
||||
|
||||
// we have to keep a different array to keep track of the additional
|
||||
// states we support....
|
||||
wxArrayInt m_stateList;
|
||||
|
||||
// our set of checkbox images...
|
||||
wxImageList m_imageList;
|
||||
|
||||
public:
|
||||
wxCheckedListCtrl()
|
||||
#if CLC_VBAM_USAGE && CLC_USE_SYSICONS
|
||||
: wxListCtrl(), m_imageList() {}
|
||||
#else
|
||||
: wxListCtrl(), m_imageList(16, 16, TRUE) {}
|
||||
#endif
|
||||
|
||||
wxCheckedListCtrl(wxWindow *parent, wxWindowID id = -1,
|
||||
const wxPoint& pt = wxDefaultPosition,
|
||||
const wxSize& sz = wxDefaultSize,
|
||||
long style = wxCLC_CHECK_WHEN_SELECTING,
|
||||
const wxValidator& validator = wxDefaultValidator,
|
||||
const wxString& name = wxListCtrlNameStr)
|
||||
#if CLC_VBAM_USAGE && CLC_USE_SYSICONS
|
||||
: wxListCtrl(), m_imageList()
|
||||
#else
|
||||
: wxListCtrl(), m_imageList(16, 16, TRUE)
|
||||
#endif
|
||||
{ Create(parent, id, pt, sz, style, validator, name); }
|
||||
|
||||
bool Create(wxWindow *parent, wxWindowID id = -1,
|
||||
const wxPoint& pt = wxDefaultPosition,
|
||||
const wxSize& sz = wxDefaultSize,
|
||||
long style = wxCLC_CHECK_WHEN_SELECTING,
|
||||
const wxValidator& validator = wxDefaultValidator,
|
||||
const wxString& name = wxListCtrlNameStr);
|
||||
|
||||
#ifdef CLC_VBAM_USAGE
|
||||
// for xrc usage, a separate init function is needed.
|
||||
bool Init();
|
||||
#endif
|
||||
virtual ~wxCheckedListCtrl() {}
|
||||
|
||||
|
||||
public: // utilities
|
||||
|
||||
// core overloads (i.e. the most generic overloads)
|
||||
bool GetItem(wxListItem& info) const;
|
||||
bool SetItem(wxListItem& info);
|
||||
long InsertItem(wxListItem& info);
|
||||
bool DeleteItem(long item);
|
||||
bool DeleteAllItems()
|
||||
{ m_stateList.Clear(); return wxListCtrl::DeleteAllItems(); }
|
||||
|
||||
bool SortItems(wxListCtrlCompare, long)
|
||||
{ wxASSERT_MSG(0, wxT("Not implemented yet ! sorry... ")); return FALSE; }
|
||||
|
||||
// shortcuts to the SetItemState function
|
||||
void Check(long item, bool checked);
|
||||
void Enable(long item, bool enable);
|
||||
void CheckAll(bool checked = true);
|
||||
void EnableAll(bool enable = true);
|
||||
|
||||
// this needs to be redeclared otherwise it's hidden by our other Enable() function.
|
||||
// However you should use #EnableAll instead of this function if you want to get
|
||||
// good graphics (try to understand)
|
||||
virtual bool Enable(bool enable = true)
|
||||
{ return wxListCtrl::Enable(enable); }
|
||||
|
||||
// shortcuts to the GetItemState function
|
||||
bool IsChecked(long item) const
|
||||
{ return GetItemState(item, wxLIST_STATE_CHECKED) != 0; }
|
||||
bool IsEnabled(long item) const
|
||||
{ return GetItemState(item, wxLIST_STATE_ENABLED) != 0; }
|
||||
|
||||
// this needs to be redeclared otherwise it's hidden by our other IsEnabled() function.
|
||||
bool IsEnabled() const
|
||||
{ return wxWindow::IsEnabled(); }
|
||||
|
||||
//! Returns the number of checked items in the control.
|
||||
int GetCheckedItemCount() const;
|
||||
|
||||
// we overload these so we are sure they will use our
|
||||
// #GetItem and #SetItem functions...
|
||||
bool SetItemState(long item, long state, long stateMask);
|
||||
int GetItemState(long item, long stateMask) const;
|
||||
long InsertItem( long index, const wxString& label, int imageIndex = -1);
|
||||
long SetItem(long index, int col, const wxString& label, int imageId = -1);
|
||||
|
||||
// the image associated with an element is already in used by wxCheckedListCtrl
|
||||
// itself to show the checkbox and it cannot be handled by the user !
|
||||
bool SetItemImage(long, int)
|
||||
{ wxASSERT_MSG(0, wxT("This function cannot be used with wxCheckedListCtrl !")); return FALSE; }
|
||||
|
||||
protected: // event handlers
|
||||
|
||||
void OnMouseEvent(wxMouseEvent& event);
|
||||
|
||||
protected: // internal utilities
|
||||
|
||||
static int GetItemImageFromAdditionalState(int addstate);
|
||||
static int GetAndRemoveAdditionalState(long *state, int statemask);
|
||||
wxColour GetBgColourFromAdditionalState(int additionalstate);
|
||||
|
||||
private:
|
||||
#if CLC_VBAM_USAGE
|
||||
DECLARE_DYNAMIC_CLASS(wxCheckedListCtrl)
|
||||
#else
|
||||
DECLARE_CLASS(wxCheckedListCtrl)
|
||||
#endif
|
||||
DECLARE_EVENT_TABLE()
|
||||
};
|
||||
|
||||
|
||||
#endif // wxUSE_CHECKEDLISTCTRL
|
||||
|
||||
#endif // _WX_CHECKEDLISTCTRL_H_
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
#ifndef _WX_JOYKEYTEXT_H
|
||||
#define _WX_JOYKEYTEXT_H
|
||||
|
||||
// wxJoyKeyTextCtrl: a wxTextCtrl which stores/acts on key presses and joystick
|
||||
// The value is the symbolic name of the key pressed
|
||||
// Supports manual clearing (bs), multiple keys in widget, automatic tab on key
|
||||
|
||||
#include "wx/keyedit.h"
|
||||
#include "wx/sdljoy.h"
|
||||
|
||||
typedef struct wxJoyKeyBinding {
|
||||
int key; // key code; listed first for easy static init
|
||||
int mod; // modifier flags
|
||||
int joy; // joystick # (starting at 1)
|
||||
// if joy is non-0, key = control number, and mod = control type
|
||||
} wxJoyKeyBinding;
|
||||
|
||||
typedef std::vector<wxJoyKeyBinding> wxJoyKeyBinding_v;
|
||||
|
||||
// joystick control types
|
||||
// mod for joysticks
|
||||
enum {
|
||||
WXJB_AXIS_PLUS, WXJB_AXIS_MINUS, WXJB_BUTTON, WXJB_HAT_FIRST,
|
||||
WXJB_HAT_N = WXJB_HAT_FIRST, WXJB_HAT_S, WXJB_HAT_W, WXJB_HAT_E, WXJB_HAT_NW,
|
||||
WXJB_HAT_NE, WXJB_HAT_SW, WXJB_HAT_SE, WXJB_HAT_LAST = WXJB_HAT_SE
|
||||
};
|
||||
|
||||
class wxJoyKeyTextCtrl : public wxKeyTextCtrl
|
||||
{
|
||||
public:
|
||||
// default constructor; required for use with xrc
|
||||
// FIXME: clearable and keyenter should be style flags
|
||||
wxJoyKeyTextCtrl() : wxKeyTextCtrl() {}
|
||||
virtual ~wxJoyKeyTextCtrl() {};
|
||||
|
||||
// key is event.GetControlIndex(), and joy is event.GetJoy() + 1
|
||||
// mod is derived from GetControlValue() and GetControlType():
|
||||
// convert wxSDLJoyEvent's type+val into mod (WXJB_*)
|
||||
static int DigitalButton(wxSDLJoyEvent &event);
|
||||
// convert mod+key to accel string, separated by -
|
||||
static wxString ToString(int mod, int key, int joy);
|
||||
// convert multiple keys, separated by multikey
|
||||
static wxString ToString(wxJoyKeyBinding_v keys, wxChar sep = wxT(','));
|
||||
// parses single key string into mod+key
|
||||
static bool FromString(const wxString &s, int &mod, int &key, int &joy);
|
||||
// parse multi-key string into array
|
||||
// returns empty array on parse errors
|
||||
static wxJoyKeyBinding_v FromString(const wxString &s, wxChar sep = wxT(','));
|
||||
// parse a single key in given wxChar array up to given len
|
||||
static bool ParseString(const wxChar *s, int len, int &mod, int &key, int &joy);
|
||||
|
||||
protected:
|
||||
void OnJoy(wxSDLJoyEvent &);
|
||||
|
||||
DECLARE_DYNAMIC_CLASS();
|
||||
DECLARE_EVENT_TABLE();
|
||||
};
|
||||
|
||||
// A simple copy-only validator
|
||||
class wxJoyKeyValidator : public wxValidator
|
||||
{
|
||||
public:
|
||||
wxJoyKeyValidator(wxJoyKeyBinding_v *v) : wxValidator(), val(v) {}
|
||||
wxJoyKeyValidator(const wxJoyKeyValidator &v) : wxValidator(), val(v.val) {}
|
||||
wxObject *Clone() const { return new wxJoyKeyValidator(val); }
|
||||
bool TransferToWindow();
|
||||
bool TransferFromWindow();
|
||||
bool Validate(wxWindow *p) { return true; }
|
||||
protected:
|
||||
wxJoyKeyBinding_v *val;
|
||||
|
||||
DECLARE_CLASS(wxJoyKeyValidator)
|
||||
};
|
||||
|
||||
#endif /* WX_JOYKEYTEXT_H */
|
|
@ -0,0 +1,75 @@
|
|||
#ifndef _WX_KEYTEXT_H
|
||||
#define _WX_KEYTEXT_H
|
||||
|
||||
// wxKeyTextCtrl: a wxTextCtrl which stores/acts on key presses
|
||||
// The value is the symbolic name of the key pressed
|
||||
// Supports manual clearing (bs), multiple keys in widget, automatic tab on key
|
||||
|
||||
#include <wx/textctrl.h>
|
||||
#include <wx/accel.h>
|
||||
#include <vector>
|
||||
|
||||
typedef std::vector<wxAcceleratorEntry> wxAcceleratorEntry_v;
|
||||
|
||||
class wxKeyTextCtrl : public wxTextCtrl
|
||||
{
|
||||
public:
|
||||
// default constructor; required for use with xrc
|
||||
// FIXME: clearable and keyenter should be style flags
|
||||
wxKeyTextCtrl() : wxTextCtrl(), clearable(true), multikey(wxT(',')),
|
||||
keyenter(true), lastmod(0), lastkey(0) {};
|
||||
virtual ~wxKeyTextCtrl() {};
|
||||
|
||||
void SetClearable(bool set = true) { clearable = set; }
|
||||
void SetMultikey(wxChar c = wxT(',')) { multikey = c; }
|
||||
void SetKeyEnter(bool set = true) { keyenter = set; }
|
||||
|
||||
bool GetClearable() { return clearable; }
|
||||
wxChar GetMultikey() { return multikey; }
|
||||
bool GetKeyEnter() { return keyenter; }
|
||||
|
||||
// convert mod+key to accel string, separated by -
|
||||
static wxString ToString(int mod, int key);
|
||||
// convert multiple keys, separated by multikey
|
||||
static wxString ToString(wxAcceleratorEntry_v keys, wxChar sep = wxT(','));
|
||||
// parses single key string into mod+key
|
||||
static bool FromString(const wxString &s, int &mod, int &key);
|
||||
// parse multi-key string into accelentry array
|
||||
// note that meta flag may be set in accelentry array item even
|
||||
// where not supported for accelerators (i.e. non-mac)
|
||||
// returns empty array on parse errors
|
||||
static wxAcceleratorEntry_v FromString(const wxString &s, wxChar sep = wxT(','));
|
||||
// parse a single key in given wxChar array up to given len
|
||||
static bool ParseString(const wxChar *s, int len, int &mod, int &key);
|
||||
|
||||
protected:
|
||||
void OnKeyDown(wxKeyEvent &);
|
||||
void OnKeyUp(wxKeyEvent &);
|
||||
|
||||
bool clearable;
|
||||
wxChar multikey;
|
||||
bool keyenter;
|
||||
// the last keydown event received; this is processed on next keyup
|
||||
int lastmod, lastkey;
|
||||
|
||||
DECLARE_DYNAMIC_CLASS();
|
||||
DECLARE_EVENT_TABLE();
|
||||
};
|
||||
|
||||
// A simple copy-only validator
|
||||
class wxKeyValidator : public wxValidator
|
||||
{
|
||||
public:
|
||||
wxKeyValidator(wxAcceleratorEntry_v *v) : wxValidator(), val(v) {}
|
||||
wxKeyValidator(const wxKeyValidator &v) : wxValidator(), val(v.val) {}
|
||||
wxObject *Clone() const { return new wxKeyValidator(val); }
|
||||
bool TransferToWindow();
|
||||
bool TransferFromWindow();
|
||||
bool Validate(wxWindow *p) { return true; }
|
||||
protected:
|
||||
wxAcceleratorEntry_v *val;
|
||||
|
||||
DECLARE_CLASS(wxKeyValidator)
|
||||
};
|
||||
|
||||
#endif /* WX_KEYTEXT_H */
|
|
@ -0,0 +1,102 @@
|
|||
#ifndef JOYEVT_H
|
||||
#define JOYEVT_H
|
||||
|
||||
// This is my own SDL-based joystick handler, since wxJoystick is brain-dead.
|
||||
// It's geared towards keyboard emulation
|
||||
|
||||
// To use, create a wxSDLJoy object Add() the joysticks you want to monitor.
|
||||
// wxSDLJoy derives from wxTimer, so you can pause it with Stop() and
|
||||
// resume with Start().
|
||||
//
|
||||
// The target window will receive EVT_SDLJOY events of type wxSDLJoyEvent.
|
||||
|
||||
#include <wx/event.h>
|
||||
#include <wx/timer.h>
|
||||
|
||||
typedef struct wxSDLJoyState wxSDLJoyState_t;
|
||||
|
||||
class wxSDLJoy : public wxTimer
|
||||
{
|
||||
public:
|
||||
// if analog, send events for all axis movement
|
||||
// otherwise, only send events when values cross the 50% mark
|
||||
// and max out values
|
||||
wxSDLJoy(bool analog = false);
|
||||
// but flag can be set later
|
||||
void SetAnalog(bool analog = true) { digital = !analog; };
|
||||
// send events to this handler
|
||||
// If NULL (default), send to window with keyboard focus
|
||||
wxEvtHandler *Attach(wxEvtHandler *);
|
||||
// add another joystick to the list of polled sticks
|
||||
// -1 == add all
|
||||
// If joy > # of joysticks, it is ignored
|
||||
// This will start polling if a valid joystick is selected
|
||||
void Add(int joy = -1);
|
||||
// remove a joystick from the polled sticks
|
||||
// -1 == remove all
|
||||
// If joy > # of joysticks, it is ignored
|
||||
// This will stop polling if all joysticks are disabled
|
||||
void Remove(int joy = -1);
|
||||
// query if a stick is being polled
|
||||
bool IsPolling(int joy);
|
||||
// query # of joysticks
|
||||
int GetNumJoysticks() { return njoy; }
|
||||
// query # of axes on given joystick
|
||||
// 0 is returned if joy is invalid
|
||||
int GetNumAxes(int joy);
|
||||
// query # of hats on given joystick
|
||||
// 0 is returned if joy is invalid
|
||||
int GetNumHats(int joy);
|
||||
// query # of buttons on given joystick
|
||||
// 0 is returned if joy is invalid
|
||||
int GetNumButtons(int joy);
|
||||
|
||||
virtual ~wxSDLJoy();
|
||||
protected:
|
||||
bool digital;
|
||||
int njoy;
|
||||
wxSDLJoyState_t *joystate;
|
||||
wxEvtHandler *evthandler;
|
||||
bool nosticks;
|
||||
void Notify();
|
||||
};
|
||||
|
||||
enum {
|
||||
// The types of supported controls
|
||||
// values are signed-16 for axis, 0/1 for button
|
||||
// hat is bitmask NESW/URDL
|
||||
WXSDLJOY_AXIS, WXSDLJOY_HAT, WXSDLJOY_BUTTON
|
||||
};
|
||||
|
||||
class wxSDLJoyEvent : public wxCommandEvent
|
||||
{
|
||||
friend class wxSDLJoy;
|
||||
public:
|
||||
// Default constructor
|
||||
wxSDLJoyEvent(wxEventType commandType = wxEVT_NULL, int id = 0) :
|
||||
wxCommandEvent(commandType, id) {}
|
||||
// accessors
|
||||
unsigned short GetJoy() { return joy; }
|
||||
unsigned short GetControlType() { return ctrl_type; }
|
||||
unsigned short GetControlIndex() { return ctrl_idx; }
|
||||
short GetControlValue() { return ctrl_val; }
|
||||
short GetControlPrevValue() { return prev_val; }
|
||||
// required for PostEvent, apparently
|
||||
wxEvent *Clone();
|
||||
protected:
|
||||
unsigned short joy;
|
||||
unsigned short ctrl_type;
|
||||
unsigned short ctrl_idx;
|
||||
short ctrl_val;
|
||||
short prev_val;
|
||||
};
|
||||
|
||||
// Note: this means sdljoy can't be part of a library w/o extra work
|
||||
DECLARE_LOCAL_EVENT_TYPE(wxEVT_SDLJOY, -1)
|
||||
typedef void (wxEvtHandler::*wxSDLJoyEventFunction)(wxSDLJoyEvent &);
|
||||
#define EVT_SDLJOY(fn) \
|
||||
DECLARE_EVENT_TABLE_ENTRY(wxEVT_SDLJOY, wxID_ANY, wxID_ANY, \
|
||||
(wxObjectEventFunction)(wxEventFunction)(wxCommandEventFunction) \
|
||||
wxStaticCastEvent(wxSDLJoyEventFunction, &fn), (wxObject *)NULL), \
|
||||
|
||||
#endif /* JOYEVT_H */
|
|
@ -0,0 +1,14 @@
|
|||
// dummy webupdatedef.h for checklistctrl
|
||||
#ifndef WXDLLIMPEXP_WEBUPDATE
|
||||
// this will never be part of a DLL
|
||||
#define WXDLLIMPEXP_WEBUPDATE
|
||||
// why would I include it and not enable it?
|
||||
#define wxUSE_CHECKEDLISTCTRL 1
|
||||
// enable customizations:
|
||||
// - include wx/settings.h (bugfix; always enabled)
|
||||
// - make a dynamic class so it can be subclass in xrc
|
||||
// - only make col0 checkable
|
||||
// - use "native" checkbox (also requires CLC_USE_SYSICONS)
|
||||
#define CLC_VBAM_USAGE 1
|
||||
#define CLC_USE_SYSICONS 1
|
||||
#endif
|
|
@ -0,0 +1,195 @@
|
|||
#ifndef WX_MISC_H
|
||||
#define WX_MISC_H
|
||||
// utility widgets
|
||||
|
||||
// simple radio button not under the same parent window
|
||||
// note that it must be checkbox, as wx radio buttons have rigid behavior
|
||||
class wxFarRadio : public wxCheckBox
|
||||
{
|
||||
public:
|
||||
wxFarRadio();
|
||||
virtual ~wxFarRadio();
|
||||
void SetValue(bool val);
|
||||
// join this group with widget(s) in grp
|
||||
void SetGroup(class wxFarRadio *grp);
|
||||
// turn into a singleton
|
||||
void BreakGroup();
|
||||
// iterate over members in group (ring)
|
||||
wxFarRadio *GetNext();
|
||||
protected:
|
||||
void UpdatedValue();
|
||||
void UpdateEvt(wxCommandEvent &ev);
|
||||
wxFarRadio *Next;
|
||||
DECLARE_DYNAMIC_CLASS()
|
||||
DECLARE_EVENT_TABLE()
|
||||
};
|
||||
|
||||
// boolean copy-only validator that uses a constant int
|
||||
// may be attached to radio button or checkbox
|
||||
class wxBoolIntValidator : public wxValidator
|
||||
{
|
||||
public:
|
||||
wxBoolIntValidator(int *_vptr, int _val, int _mask = ~0) : wxValidator(),
|
||||
vptr(_vptr), val(_val), mask(_mask) {}
|
||||
wxBoolIntValidator(const wxBoolIntValidator &v) : wxValidator(),
|
||||
vptr(v.vptr), val(v.val), mask(v.mask) {}
|
||||
wxObject *Clone() const { return new wxBoolIntValidator(vptr, val, mask); }
|
||||
bool TransferToWindow();
|
||||
bool TransferFromWindow();
|
||||
bool Validate(wxWindow *p) { return true; }
|
||||
protected:
|
||||
int val, mask, *vptr;
|
||||
};
|
||||
|
||||
// boolean copy-only validator with reversed value
|
||||
// may be attached to radio button or checkbox
|
||||
class wxBoolRevValidator : public wxValidator
|
||||
{
|
||||
public:
|
||||
wxBoolRevValidator(bool *_vptr) : wxValidator(), vptr(_vptr) {}
|
||||
wxBoolRevValidator(const wxBoolRevValidator &v) : wxValidator(), vptr(v.vptr) {}
|
||||
wxObject *Clone() const { return new wxBoolRevValidator(vptr); }
|
||||
bool TransferToWindow();
|
||||
bool TransferFromWindow();
|
||||
bool Validate(wxWindow *p) { return true; }
|
||||
protected:
|
||||
bool *vptr;
|
||||
};
|
||||
|
||||
// wxFilePickerCtrl/wxDirPickerCtrl copy-only vvalidator
|
||||
class wxFileDirPickerValidator : public wxValidator
|
||||
{
|
||||
public:
|
||||
wxFileDirPickerValidator(wxString *_vptr) : wxValidator(), vptr(_vptr) {}
|
||||
wxFileDirPickerValidator(const wxFileDirPickerValidator &v) : wxValidator(),
|
||||
vptr(v.vptr) {}
|
||||
wxObject *Clone() const { return new wxFileDirPickerValidator(vptr); }
|
||||
bool TransferToWindow();
|
||||
bool TransferFromWindow();
|
||||
bool Validate(wxWindow *p) { return true; }
|
||||
protected:
|
||||
wxString *vptr;
|
||||
};
|
||||
|
||||
|
||||
// color copy-only validator that supports either 32-bit or 16-bit color
|
||||
// value (but not endianness..)
|
||||
// FIXME: only supported formats are RGB888 and BGR555
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
class wxColorValidator : public wxValidator
|
||||
{
|
||||
public:
|
||||
wxColorValidator(uint32_t *vptr) : wxValidator(), ptr32(vptr), ptr16(0) {}
|
||||
wxColorValidator(uint16_t *vptr) : wxValidator(), ptr16(vptr), ptr32(0) {}
|
||||
wxColorValidator(const wxColorValidator &v) : wxValidator(),
|
||||
ptr32(v.ptr32), ptr16(v.ptr16) {}
|
||||
wxObject *Clone() const { return new wxColorValidator(*this); }
|
||||
bool TransferToWindow();
|
||||
bool TransferFromWindow();
|
||||
bool Validate(wxWindow *p) { return true; }
|
||||
protected:
|
||||
uint32_t *ptr32;
|
||||
uint16_t *ptr16;
|
||||
};
|
||||
|
||||
// Copy-only validators for checkboxes and radio buttons that enables a set
|
||||
// of dependent widgets
|
||||
// Requires an event handler during run-time
|
||||
|
||||
#include <vector>
|
||||
#include <wx/valgen.h>
|
||||
|
||||
// there's probably a standard wxWindowList or some such, but it's
|
||||
// undocumented and I prefer arrays
|
||||
typedef std::vector<wxWindow *>wxWindow_v;
|
||||
|
||||
class wxBoolEnValidator : public wxGenericValidator
|
||||
{
|
||||
public:
|
||||
wxBoolEnValidator(bool *vptr) : wxGenericValidator(vptr) {}
|
||||
wxBoolEnValidator(bool *vptr, wxWindow_v &cnt, std::vector<bool>rev = std::vector<bool>()) :
|
||||
wxGenericValidator(vptr), controls(cnt), reverse(rev) {}
|
||||
wxBoolEnValidator(const wxBoolEnValidator &v) : wxGenericValidator(v),
|
||||
controls(v.controls), reverse(v.reverse) {}
|
||||
wxObject *Clone() const { return new wxBoolEnValidator(*this); }
|
||||
// set these after init, rather than in constructor
|
||||
wxWindow_v controls;
|
||||
// set reverse entries to true if disabled when checkbox checked
|
||||
// controls past the end of the reverse array are not reversed
|
||||
std::vector<bool> reverse;
|
||||
// inherit validate, xferfrom from parent
|
||||
bool TransferToWindow();
|
||||
};
|
||||
|
||||
class wxBoolIntEnValidator : public wxBoolIntValidator
|
||||
{
|
||||
public:
|
||||
wxBoolIntEnValidator(int *vptr, int val, int mask = ~0) :
|
||||
wxBoolIntValidator(vptr, val, mask) {}
|
||||
wxBoolIntEnValidator(int *vptr, int val, int mask, wxWindow_v &cnt,
|
||||
std::vector<bool>rev = std::vector<bool>()) :
|
||||
wxBoolIntValidator(vptr, val, mask), controls(cnt), reverse(rev) {}
|
||||
wxBoolIntEnValidator(const wxBoolIntEnValidator &v) :
|
||||
wxBoolIntValidator(v), controls(v.controls), reverse(v.reverse) {}
|
||||
wxObject *Clone() const { return new wxBoolIntEnValidator(*this); }
|
||||
// set these after init, rather than in constructor
|
||||
wxWindow_v controls;
|
||||
// set reverse entries to true if disabled when checkbox checked
|
||||
// controls past the end of the reverse array are not reversed
|
||||
std::vector<bool> reverse;
|
||||
// inherit validate, xferfrom from parent
|
||||
bool TransferToWindow();
|
||||
};
|
||||
|
||||
class wxBoolRevEnValidator : public wxBoolRevValidator
|
||||
{
|
||||
public:
|
||||
wxBoolRevEnValidator(bool *vptr) : wxBoolRevValidator(vptr) {}
|
||||
wxBoolRevEnValidator(bool *vptr, wxWindow_v &cnt, std::vector<bool>rev = std::vector<bool>()) :
|
||||
wxBoolRevValidator(vptr), controls(cnt), reverse(rev) {}
|
||||
wxBoolRevEnValidator(const wxBoolRevEnValidator &v) : wxBoolRevValidator(v),
|
||||
controls(v.controls), reverse(v.reverse) {}
|
||||
wxObject *Clone() const { return new wxBoolRevEnValidator(*this); }
|
||||
// set these after init, rather than in constructor
|
||||
wxWindow_v controls;
|
||||
// set reverse entries to true if disabled when checkbox checked
|
||||
// controls past the end of the reverse array are not reversed
|
||||
std::vector<bool> reverse;
|
||||
// inherit validate, xferfrom from parent
|
||||
bool TransferToWindow();
|
||||
};
|
||||
|
||||
// and here's an event handler that can be attached to the widget or a parent
|
||||
// of the widget
|
||||
// It is a descendent of wxEvtHandler so its methods can be used as
|
||||
// event handlers
|
||||
class wxBoolEnHandler : public wxEvtHandler
|
||||
{
|
||||
public:
|
||||
wxWindow_v controls;
|
||||
std::vector<bool> reverse;
|
||||
void ToggleCheck(wxCommandEvent &ev);
|
||||
void Enable(wxCommandEvent &ev);
|
||||
void Disable(wxCommandEvent &ev);
|
||||
};
|
||||
|
||||
// use this to connect to the handler of id in win to obj
|
||||
#define wxCBBoolEnHandlerConnect(win, id, obj) \
|
||||
(win)->Connect(id, wxEVT_COMMAND_CHECKBOX_CLICKED, \
|
||||
wxCommandEventHandler(wxBoolEnHandler::ToggleCheck), NULL, \
|
||||
&obj)
|
||||
|
||||
#define wxRBBoolEnHandlerConnect(win, id, obj, f) \
|
||||
(win)->Connect(id, wxEVT_COMMAND_RADIOBUTTON_SELECTED, \
|
||||
wxCommandEventHandler(wxBoolEnHandler::f), NULL, \
|
||||
&obj)
|
||||
|
||||
#define wxRBEBoolEnHandlerConnect(win, id, obj) wxRBBoolEnHandlerConnect(win, id, obj, Enable)
|
||||
#define wxRBDBoolEnHandlerConnect(win, id, obj) wxRBBoolEnHandlerConnect(win, id, obj, Disable)
|
||||
|
||||
// for wxTextValidator include lists
|
||||
extern const wxArrayString val_hexdigits, val_sigdigits, val_unsdigits;
|
||||
|
||||
#endif /* WX_MISC_H */
|
|
@ -0,0 +1,322 @@
|
|||
// utility widgets
|
||||
#include <wx/wx.h>
|
||||
#include "wx/wxmisc.h"
|
||||
|
||||
wxFarRadio::wxFarRadio()
|
||||
: wxCheckBox()
|
||||
{
|
||||
Next = this;
|
||||
}
|
||||
|
||||
wxFarRadio::~wxFarRadio()
|
||||
{
|
||||
BreakGroup();
|
||||
}
|
||||
|
||||
void wxFarRadio::SetValue(bool val)
|
||||
{
|
||||
wxCheckBox::SetValue(val);
|
||||
UpdatedValue();
|
||||
}
|
||||
|
||||
void wxFarRadio::SetGroup(class wxFarRadio *grp)
|
||||
{
|
||||
if(grp == this)
|
||||
return;
|
||||
wxFarRadio *checked = GetValue() ? this : NULL;
|
||||
for(wxFarRadio *gp = Next; gp != this; gp = gp->Next) {
|
||||
if(gp = grp)
|
||||
return;
|
||||
if(gp->GetValue())
|
||||
checked = gp;
|
||||
}
|
||||
wxFarRadio *link = Next;
|
||||
Next = grp;
|
||||
bool clear_checked = false;
|
||||
wxFarRadio *gp;
|
||||
for(gp = grp; gp->Next != grp; gp = gp->Next) {
|
||||
if(checked && GetValue())
|
||||
clear_checked = true;
|
||||
}
|
||||
gp->Next = link;
|
||||
if(checked && gp->GetValue())
|
||||
clear_checked = true;
|
||||
if(clear_checked)
|
||||
checked->SetValue(false);
|
||||
int l;
|
||||
for(l = 1, gp = Next; gp != this; gp = gp->Next, l++);
|
||||
}
|
||||
|
||||
void wxFarRadio::BreakGroup() {
|
||||
wxFarRadio **gp;
|
||||
for(gp = &Next; *gp != this; gp = &(*gp)->Next);
|
||||
*gp = Next;
|
||||
Next = this;
|
||||
}
|
||||
|
||||
wxFarRadio *wxFarRadio::GetNext()
|
||||
{
|
||||
return Next;
|
||||
}
|
||||
|
||||
void wxFarRadio::UpdatedValue()
|
||||
{
|
||||
if(!GetValue()) {
|
||||
wxFarRadio *gp;
|
||||
// just like system wx, ensure at least one always checked
|
||||
for(gp = Next; gp != this; gp = gp->Next)
|
||||
if(gp->GetValue())
|
||||
break;
|
||||
if(gp == this) {
|
||||
SetValue(true);
|
||||
return;
|
||||
}
|
||||
} else
|
||||
for(wxFarRadio *gp = Next; gp != this; gp = gp->Next)
|
||||
gp->SetValue(false);
|
||||
}
|
||||
|
||||
void wxFarRadio::UpdateEvt(wxCommandEvent &ev)
|
||||
{
|
||||
UpdatedValue();
|
||||
ev.Skip();
|
||||
}
|
||||
|
||||
IMPLEMENT_DYNAMIC_CLASS(wxFarRadio, wxCheckBox);
|
||||
BEGIN_EVENT_TABLE(wxFarRadio, wxCheckBox)
|
||||
EVT_CHECKBOX(wxID_ANY, wxFarRadio::UpdateEvt)
|
||||
END_EVENT_TABLE()
|
||||
|
||||
bool wxBoolIntValidator::TransferToWindow()
|
||||
{
|
||||
if(!vptr)
|
||||
return false;
|
||||
bool checked = (*vptr & mask) == val;
|
||||
wxCheckBox *cb = wxDynamicCast(GetWindow(), wxCheckBox);
|
||||
if(cb) {
|
||||
cb->SetValue(checked);
|
||||
return true;
|
||||
}
|
||||
wxRadioButton *rb = wxDynamicCast(GetWindow(), wxRadioButton);
|
||||
if(rb) {
|
||||
rb->SetValue(checked);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool wxBoolIntValidator::TransferFromWindow()
|
||||
{
|
||||
if(!vptr)
|
||||
return false;
|
||||
bool nv = false;
|
||||
wxCheckBox *cb = wxDynamicCast(GetWindow(), wxCheckBox);
|
||||
if(cb)
|
||||
nv = cb->GetValue();
|
||||
else {
|
||||
wxRadioButton *rb = wxDynamicCast(GetWindow(), wxRadioButton);
|
||||
if(rb)
|
||||
nv = rb->GetValue();
|
||||
else
|
||||
return false;
|
||||
}
|
||||
if(mask == ~0 && !nv && *vptr != val)
|
||||
return true;
|
||||
*vptr = (*vptr & ~mask) | (nv ? val : 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool wxBoolRevValidator::TransferToWindow()
|
||||
{
|
||||
if(!vptr)
|
||||
return false;
|
||||
wxCheckBox *cb = wxDynamicCast(GetWindow(), wxCheckBox);
|
||||
if(cb) {
|
||||
cb->SetValue(!*vptr);
|
||||
return true;
|
||||
}
|
||||
wxRadioButton *rb = wxDynamicCast(GetWindow(), wxRadioButton);
|
||||
if(rb) {
|
||||
rb->SetValue(!*vptr);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool wxBoolRevValidator::TransferFromWindow()
|
||||
{
|
||||
if(!vptr)
|
||||
return false;
|
||||
wxCheckBox *cb = wxDynamicCast(GetWindow(), wxCheckBox);
|
||||
if(cb)
|
||||
*vptr = !cb->GetValue();
|
||||
else {
|
||||
wxRadioButton *rb = wxDynamicCast(GetWindow(), wxRadioButton);
|
||||
if(rb)
|
||||
*vptr = !rb->GetValue();
|
||||
else
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#include <wx/filepicker.h>
|
||||
|
||||
bool wxFileDirPickerValidator::TransferToWindow()
|
||||
{
|
||||
if(!vptr)
|
||||
return false;
|
||||
wxFilePickerCtrl *fp = wxDynamicCast(GetWindow(), wxFilePickerCtrl);
|
||||
if(fp) {
|
||||
fp->SetPath(*vptr);
|
||||
return true;
|
||||
}
|
||||
wxDirPickerCtrl *dp = wxDynamicCast(GetWindow(), wxDirPickerCtrl);
|
||||
if(dp) {
|
||||
dp->SetPath(*vptr);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool wxFileDirPickerValidator::TransferFromWindow()
|
||||
{
|
||||
if(!vptr)
|
||||
return false;
|
||||
wxFilePickerCtrl *fp = wxDynamicCast(GetWindow(), wxFilePickerCtrl);
|
||||
if(fp) {
|
||||
*vptr = fp->GetPath();
|
||||
return true;
|
||||
}
|
||||
wxDirPickerCtrl *dp = wxDynamicCast(GetWindow(), wxDirPickerCtrl);
|
||||
if(dp) {
|
||||
*vptr = dp->GetPath();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#include <wx/clrpicker.h>
|
||||
|
||||
bool wxColorValidator::TransferToWindow()
|
||||
{
|
||||
if(!ptr32 && !ptr16)
|
||||
return false;
|
||||
wxColourPickerCtrl *cp = wxDynamicCast(GetWindow(), wxColourPickerCtrl);
|
||||
if(!cp)
|
||||
return false;
|
||||
if(ptr32)
|
||||
cp->SetColour(wxColor(((*ptr32 >> 16) & 0xff),
|
||||
((*ptr32 >> 8) & 0xff),
|
||||
((*ptr32) & 0xff)));
|
||||
else
|
||||
cp->SetColour(wxColor(((*ptr16 << 3) & 0xf8),
|
||||
((*ptr16 >> 2) & 0xf8),
|
||||
((*ptr16 >> 7) & 0xf8)));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool wxColorValidator::TransferFromWindow()
|
||||
{
|
||||
if(!ptr32 && !ptr16)
|
||||
return false;
|
||||
wxColourPickerCtrl *cp = wxDynamicCast(GetWindow(), wxColourPickerCtrl);
|
||||
if(!cp)
|
||||
return false;
|
||||
wxColor c = cp->GetColour();
|
||||
if(ptr32)
|
||||
*ptr32 = ((c.Red() & 0xff) << 16) +
|
||||
((c.Green() & 0xff) << 8) +
|
||||
((c.Blue() & 0xff));
|
||||
else
|
||||
*ptr16 = ((c.Red() & 0xf8) >> 3) +
|
||||
((c.Green() & 0xf8) << 2) +
|
||||
((c.Blue() & 0xf8) << 7);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void enable(wxWindow_v controls, std::vector<bool>reverse, bool en)
|
||||
{
|
||||
for(int i = 0; i < controls.size(); i++)
|
||||
controls[i]->Enable(reverse.size() <= i || !reverse[i] ? en : !en);
|
||||
}
|
||||
|
||||
#define boolen(r) do { \
|
||||
bool en; \
|
||||
wxCheckBox *cb = wxDynamicCast(GetWindow(), wxCheckBox); \
|
||||
if(cb) \
|
||||
en = cb->GetValue(); \
|
||||
else { \
|
||||
wxRadioButton *rb = wxDynamicCast(GetWindow(), wxRadioButton); \
|
||||
if(!rb) \
|
||||
return false; \
|
||||
en = rb->GetValue(); \
|
||||
} \
|
||||
enable(controls, reverse, r ? !en : en); \
|
||||
return true; \
|
||||
} while(0)
|
||||
|
||||
bool wxBoolEnValidator::TransferToWindow()
|
||||
{
|
||||
if(!wxGenericValidator::TransferToWindow())
|
||||
return false;
|
||||
boolen(false);
|
||||
}
|
||||
|
||||
bool wxBoolIntEnValidator::TransferToWindow()
|
||||
{
|
||||
if(!wxBoolIntValidator::TransferToWindow())
|
||||
return false;
|
||||
boolen(false);
|
||||
}
|
||||
|
||||
bool wxBoolRevEnValidator::TransferToWindow()
|
||||
{
|
||||
if(!wxBoolRevValidator::TransferToWindow())
|
||||
return false;
|
||||
boolen(true);
|
||||
}
|
||||
|
||||
void wxBoolEnHandler::ToggleCheck(wxCommandEvent &ev)
|
||||
{
|
||||
enable(controls, reverse, ev.IsChecked());
|
||||
ev.Skip();
|
||||
}
|
||||
|
||||
void wxBoolEnHandler::Enable(wxCommandEvent &ev)
|
||||
{
|
||||
enable(controls, reverse, true);
|
||||
ev.Skip();
|
||||
}
|
||||
|
||||
void wxBoolEnHandler::Disable(wxCommandEvent &ev)
|
||||
{
|
||||
enable(controls, reverse, false);
|
||||
ev.Skip();
|
||||
}
|
||||
|
||||
static const wxChar * /* const */ val_hexdigits_s[] = {
|
||||
wxT("0"), wxT("1"), wxT("2"), wxT("3"), wxT("4"), wxT("5"), wxT("6"),
|
||||
wxT("7"), wxT("8"), wxT("9"), wxT("A"), wxT("B"), wxT("C"), wxT("D"),
|
||||
wxT("E"), wxT("F"), wxT("a"), wxT("b"), wxT("c"), wxT("d"), wxT("e"),
|
||||
wxT("f")
|
||||
};
|
||||
|
||||
const wxArrayString val_hexdigits(sizeof(val_hexdigits_s)/sizeof(val_hexdigits_s[0]),
|
||||
val_hexdigits_s);
|
||||
|
||||
static const wxChar * /* const */ val_sigdigits_s[] = {
|
||||
wxT("0"), wxT("1"), wxT("2"), wxT("3"), wxT("4"), wxT("5"), wxT("6"),
|
||||
wxT("7"), wxT("8"), wxT("9"), wxT("-"), wxT("+")
|
||||
};
|
||||
|
||||
const wxArrayString val_sigdigits(sizeof(val_sigdigits_s)/sizeof(val_sigdigits_s[0]),
|
||||
val_sigdigits_s);
|
||||
|
||||
static const wxChar * /* const */ val_unsdigits_s[] = {
|
||||
wxT("0"), wxT("1"), wxT("2"), wxT("3"), wxT("4"), wxT("5"), wxT("6"),
|
||||
wxT("7"), wxT("8"), wxT("9")
|
||||
};
|
||||
|
||||
const wxArrayString val_unsdigits(sizeof(val_unsdigits_s)/sizeof(val_unsdigits_s[0]),
|
||||
val_unsdigits_s);
|
|
@ -0,0 +1,115 @@
|
|||
#ifndef WX_WXHEAD_H
|
||||
#define WX_WXHEAD_H
|
||||
|
||||
// For compilers that support precompilation, includes <wx/wx.h>.
|
||||
#include <wx/wxprec.h>
|
||||
|
||||
#ifdef __BORLANDC__
|
||||
#pragma hdrstop
|
||||
#endif
|
||||
|
||||
// for all others, include the necessary headers (this file is usually all you
|
||||
// need because it includes almost all "standard" wxWidgets headers)
|
||||
#ifndef WX_PRECOMP
|
||||
#include <wx/wx.h>
|
||||
#endif
|
||||
|
||||
// The following are not pulled in by wx.h
|
||||
|
||||
// for some reason, mingw32 wx.h doesn't pull in listctrl by default
|
||||
#include <wx/listctrl.h>
|
||||
#include <wx/treectrl.h>
|
||||
#include <wx/xrc/xmlres.h>
|
||||
#include <wx/config.h>
|
||||
#include <wx/fileconf.h>
|
||||
#include <wx/display.h>
|
||||
#include <wx/stdpaths.h>
|
||||
// filehistory.h is separate only in 2.9+
|
||||
#include <wx/docview.h>
|
||||
|
||||
#ifndef NO_OGL
|
||||
// glcanvas must be included before SFML for MacOSX
|
||||
// originally, this was confined to drawing.h.
|
||||
#include <wx/glcanvas.h>
|
||||
// besides that, other crap gets #defined
|
||||
#ifdef Status
|
||||
#undef Status
|
||||
#endif
|
||||
#ifdef BadRequest
|
||||
#undef BadRequest
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// compatibility with MacOSX 10.5
|
||||
#if !wxCHECK_VERSION(2,8,8)
|
||||
#define AddFileWithMimeType(a, b, c, m) AddFile(a, b, c)
|
||||
#define GetItemLabel GetText
|
||||
#define SetItemLabel SetText
|
||||
#define GetMenuLabel GetLabelTop
|
||||
#define GetItemLabelText GetLabel
|
||||
#endif
|
||||
|
||||
// compatibility with wx-2.9
|
||||
// The only reason I use wxTRANSLATE at all is to get wxT as a side effect.
|
||||
#if wxCHECK_VERSION(2,9,0)
|
||||
#undef wxTRANSLATE
|
||||
#define wxTRANSLATE wxT
|
||||
#endif
|
||||
|
||||
// wxGTK (2.8.8+, at least) doesn't store the actual menu item text in m_text.
|
||||
// This breaks GetText, SetText, GetAccel, SetAccel, and GetLabel;
|
||||
// GetItemLabel() works, though.
|
||||
// GetText, SetText, and GetLabel are deprecated, so that's not a problem
|
||||
// GetAccel is inefficent anyway (often I don't want to convert to wxAccEnt)
|
||||
// This is a working replacement for SetAccel, at least.
|
||||
|
||||
#include "wx/keyedit.h"
|
||||
|
||||
static inline void DoSetAccel(wxMenuItem *mi, wxAcceleratorEntry *acc)
|
||||
{
|
||||
wxString lab = mi->GetItemLabel();
|
||||
size_t tab = lab.find(wxT('\t'));
|
||||
// following short circuit returns are to avoid UI update on no change
|
||||
if(tab == wxString::npos && !acc)
|
||||
return;
|
||||
wxString accs;
|
||||
if(acc)
|
||||
// actually, use keyedit's ToString(), as it is more reliable
|
||||
// and doesn't generate wx assertions
|
||||
// accs = acc->ToString();
|
||||
accs = wxKeyTextCtrl::ToString(acc->GetFlags(), acc->GetKeyCode());
|
||||
if(tab != wxString::npos && accs == lab.substr(tab + 1))
|
||||
return;
|
||||
if(tab != wxString::npos)
|
||||
lab.resize(tab);
|
||||
if(acc) {
|
||||
lab.append(wxT('\t'));
|
||||
lab.append(accs);
|
||||
}
|
||||
mi->SetItemLabel(lab);
|
||||
}
|
||||
|
||||
// wxrc helpers (for dynamic strings instead of constant)
|
||||
#define XRCID_D(str) wxXmlResource::GetXRCID(str)
|
||||
//#define XRCCTRL_D(win, id, type) (wxStaticCast((win).FindWindow(XRCID_D(id)), type))
|
||||
//#define XRCCTRL_I(win, id, type) (wxStaticCast((win).FindWindow(id), type))
|
||||
// XRCCTRL is broken.
|
||||
// In debug mode, it uses wxDynamicCast, which numerous wx classes fail on
|
||||
// due to not correctly specifying parents in CLASS() declarations
|
||||
// In standard mode, it does a static cast, which is unsafe for user input
|
||||
// So instead I'll always do a (slow, possibly unportable) dynamic_cast().
|
||||
// If your compiler doesn't support rtti, there are other pieces of code where
|
||||
// I bypassed wx's stuff to use real dynamic_cast as well, so get a better
|
||||
// compiler.
|
||||
#undef XRCCTRL
|
||||
#define XRCCTRL_I(win, id, type) (dynamic_cast<type *>((win).FindWindow(id)))
|
||||
#define XRCCTRL(win, id, type) XRCCTRL_I(win, XRCID(id), type)
|
||||
#define XRCCTRL_D(win, id, type) XRCCTRL_I(win, XRCID_D(id), type)
|
||||
|
||||
// wxWidgets provides fn_str(), but no mb_fn_str() or equiv.
|
||||
#define mb_fn_str() mb_str(wxConvFile)
|
||||
|
||||
// by default, only 9 recent items
|
||||
#define wxID_FILE10 (wxID_FILE9 + 1)
|
||||
|
||||
#endif /* WX_WXHEAD_H */
|
|
@ -0,0 +1,122 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>English</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>${MACOSX_BUNDLE_EXECUTABLE_NAME}</string>
|
||||
<key>CFBundleGetInfoString</key>
|
||||
<string>${MACOSX_BUNDLE_INFO_STRING}</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>${MACOSX_BUNDLE_ICON_FILE}</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>${MACOSX_BUNDLE_GUI_IDENTIFIER}</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleLongVersionString</key>
|
||||
<string>${MACOSX_BUNDLE_LONG_VERSION_STRING}</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>${MACOSX_BUNDLE_BUNDLE_NAME}</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>${MACOSX_BUNDLE_SHORT_VERSION_STRING}</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>${MACOSX_BUNDLE_BUNDLE_VERSION}</string>
|
||||
<key>CSResourcesFileMapped</key>
|
||||
<true/>
|
||||
<key>LSRequiresCarbon</key>
|
||||
<true/>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>${MACOSX_BUNDLE_COPYRIGHT}</string>
|
||||
<key>UTExportedTypeDeclarations</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>UTTypeIdentifier</key>
|
||||
<string>public.gbarom</string>
|
||||
<key>UTTypeReferenceURL</key>
|
||||
<string>http://www.vba-m.com/</string>
|
||||
<key>UTTypeDescription</key>
|
||||
<string>GameBoy Advance ROM</string>
|
||||
<key>UTTypeIconFile</key>
|
||||
<string>wxvbam.icns</string>
|
||||
<key>UTTypeConformsTo</key>
|
||||
<array>
|
||||
<string>public.data</string>
|
||||
</array>
|
||||
<key>UTTypeTagSpecification</key>
|
||||
<dict>
|
||||
<key>public.filename-extension</key>
|
||||
<array>
|
||||
<string>agb</string>
|
||||
<string>agb.gz</string>
|
||||
<string>agb.z</string>
|
||||
<string>gba</string>
|
||||
<string>gba.gz</string>
|
||||
<string>gba.z</string>
|
||||
<!-- this is probably too generic
|
||||
<string>bin</string>
|
||||
<string>bin.gz</string>
|
||||
<string>bin.z</string> -->
|
||||
<!-- slightly less generic, but still..
|
||||
<string>elf</string>
|
||||
<string>elf.gz</string>
|
||||
<string>elf.z</string>
|
||||
-->
|
||||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>UTTypeIdentifier</key>
|
||||
<string>public.gbrom</string>
|
||||
<key>UTTypeReferenceURL</key>
|
||||
<string>http://www.vba-m.com/</string>
|
||||
<key>UTTypeDescription</key>
|
||||
<string>GameBoy ROM</string>
|
||||
<key>UTTypeIconFile</key>
|
||||
<string>wxvbam.icns</string>
|
||||
<key>UTTypeConformsTo</key>
|
||||
<array>
|
||||
<string>public.data</string>
|
||||
</array>
|
||||
<key>UTTypeTagSpecification</key>
|
||||
<dict>
|
||||
<key>public.filename-extension</key>
|
||||
<array>
|
||||
<string>gb</string>
|
||||
<string>gb.gz</string>
|
||||
<string>gb.z</string>
|
||||
<string>cgb</string>
|
||||
<string>cgb.gz</string>
|
||||
<string>cgb.z</string>
|
||||
<string>gbc</string>
|
||||
<string>gbc.gz</string>
|
||||
<string>gbc.z</string>
|
||||
<string>sgb</string>
|
||||
<string>sgb.gz</string>
|
||||
<string>sgb.z</string>
|
||||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundleDocumentTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleTypeName</key>
|
||||
<string>Cartridge</string>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Viewer</string>
|
||||
<key>LSItemContentTypes</key>
|
||||
<array>
|
||||
<string>public.gbarom</string>
|
||||
<string>public.gbrom</string>
|
||||
</array>
|
||||
<key>LSHandlerRank</key>
|
||||
<string>Alternate</string>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
|
@ -0,0 +1,669 @@
|
|||
|
||||
// mainline:
|
||||
// parse cmd line
|
||||
// load xrc file (guiinit.cpp does most of instantiation)
|
||||
// create & display main frame
|
||||
|
||||
#include "wxvbam.h"
|
||||
#include <wx/filesys.h>
|
||||
#include <wx/fs_mem.h>
|
||||
#include <wx/fs_arc.h>
|
||||
#include <wx/file.h>
|
||||
#include <wx/wfstream.h>
|
||||
#include <wx/mstream.h>
|
||||
#include <wx/sstream.h>
|
||||
#include <wx/cmdline.h>
|
||||
#include <wx/regex.h>
|
||||
|
||||
// The built-in xrc file
|
||||
#include "builtin-xrc.h"
|
||||
|
||||
// The built-in vba-over.ini
|
||||
#include "builtin-over.h"
|
||||
|
||||
IMPLEMENT_APP(wxvbamApp)
|
||||
IMPLEMENT_DYNAMIC_CLASS(MainFrame, wxFrame)
|
||||
|
||||
// generate config file path
|
||||
static void get_config_path(wxPathList &path, bool exists = true)
|
||||
{
|
||||
// local config dir first, then global
|
||||
// locale-specific res first, then main
|
||||
wxStandardPathsBase& stdp = wxStandardPaths::Get();
|
||||
#define add_path(p) do { \
|
||||
const wxString& s = stdp.p; \
|
||||
if(!exists || wxDirExists(s)) \
|
||||
path.Add(s); \
|
||||
} while(0)
|
||||
// NOTE: this does not support XDG (freedesktop.org) paths
|
||||
add_path(GetUserLocalDataDir());
|
||||
add_path(GetUserDataDir());
|
||||
add_path(GetLocalizedResourcesDir(wxGetApp().locale.GetCanonicalName()));
|
||||
add_path(GetResourcesDir());
|
||||
add_path(GetDataDir());
|
||||
add_path(GetLocalDataDir());
|
||||
}
|
||||
|
||||
static void tack_full_path(wxString &s, const wxString &app = wxEmptyString)
|
||||
{
|
||||
// regenerate path, including nonexistent dirs this time
|
||||
wxPathList full_config_path;
|
||||
get_config_path(full_config_path, false);
|
||||
for(int i = 0; i < full_config_path.size(); i++)
|
||||
s += wxT("\n\t") + full_config_path[i] + app;
|
||||
}
|
||||
|
||||
bool wxvbamApp::OnInit()
|
||||
{
|
||||
// use consistent names for config
|
||||
SetAppName(_T("wxvbam"));
|
||||
|
||||
// load system default locale, if available
|
||||
locale.Init();
|
||||
locale.AddCatalog(_T("wxvbam"));
|
||||
|
||||
// make built-in xrc file available
|
||||
// this has to be done before parent OnInit() so xrc dump works
|
||||
wxFileSystem::AddHandler(new wxMemoryFSHandler);
|
||||
wxFileSystem::AddHandler(new wxArchiveFSHandler);
|
||||
wxMemoryFSHandler::AddFileWithMimeType(wxT("wxvbam.xrs"), builtin_xrs, sizeof(builtin_xrs), wxT("application/zip"));
|
||||
|
||||
if ( !wxApp::OnInit() )
|
||||
return false;
|
||||
|
||||
// prepare for loading xrc files
|
||||
wxXmlResource* xr = wxXmlResource::Get();
|
||||
// note: if linking statically, next 2 pull in lot of unused code
|
||||
// maybe in future if not wxSHARED, load only builtin-needed handlers
|
||||
xr->InitAllHandlers();
|
||||
wxInitAllImageHandlers();
|
||||
|
||||
get_config_path(config_path);
|
||||
|
||||
// first, load override xrcs
|
||||
// this can only override entire root nodes
|
||||
// 2.9 has LoadAllFiles(), but this is 2.8, so we'll do it manually
|
||||
wxString cwd = wxGetCwd();
|
||||
for(int i = 0; i < config_path.size(); i++)
|
||||
if(wxSetWorkingDirectory(config_path[i])) {
|
||||
// *.xr[cs] doesn't work (double the number of scans)
|
||||
// 2.9 gives errors for no files found, so manual precheck needed
|
||||
// (yet another double the number of scans)
|
||||
if(!wxFindFirstFile(wxT("*.xrc")).empty())
|
||||
xr->Load(wxT("*.xrc"));
|
||||
if(!wxFindFirstFile(wxT("*.xrs")).empty())
|
||||
xr->Load(wxT("*.xrs"));
|
||||
}
|
||||
wxSetWorkingDirectory(cwd);
|
||||
|
||||
// finally, load built-in xrc
|
||||
xr->Load(wxT("memory:wxvbam.xrs"));
|
||||
|
||||
// set up config file
|
||||
// this needs to be in a subdir to support other config as well
|
||||
// but subdir flag behaves differently 2.8 vs. 2.9. Oh well.
|
||||
// NOTE: this does not support XDG (freedesktop.org) paths
|
||||
cfg = new wxConfig(wxEmptyString, wxEmptyString, wxEmptyString,
|
||||
wxEmptyString,
|
||||
// style =
|
||||
wxCONFIG_USE_GLOBAL_FILE|wxCONFIG_USE_LOCAL_FILE|
|
||||
wxCONFIG_USE_SUBDIR);
|
||||
// set global config for e.g. Windows font mapping
|
||||
wxConfig::Set(cfg);
|
||||
// yet another bug/deficiency in wxConfig: dirs are not created if needed
|
||||
// since a default config is always written, dirs are always needed
|
||||
// Can't figure out statically if using wxFileConfig w/o duplicating wx's
|
||||
// logic, so do it at run-time
|
||||
// wxFileConfig *f = wxDynamicCast(cfg, wxFileConfig);
|
||||
// wxConfigBase does not derive from wxObject!!! so no wxDynamicCast
|
||||
wxFileConfig *f = dynamic_cast<wxFileConfig *>(cfg);
|
||||
if(f) {
|
||||
wxFileName s(wxFileConfig::GetLocalFileName(GetAppName()));
|
||||
// at least up to 2.8.12, GetLocalFileName returns the dir if
|
||||
// SUBDIR is specified instead of actual file name
|
||||
// and SUBDIR only affects UNIX
|
||||
#if defined(__UNIX__) && !wxCHECK_VERSION(2,9,0)
|
||||
s.AppendDir(s.GetFullName());
|
||||
#endif
|
||||
// only the path part gets created
|
||||
// note that 0777 is default (assumes umask will do og-w)
|
||||
s.Mkdir(0777, wxPATH_MKDIR_FULL);
|
||||
}
|
||||
load_opts();
|
||||
// process command-line options
|
||||
for(int i = 0; i < pending_optset.size(); i++) {
|
||||
wxString p = pending_optset[i];
|
||||
size_t eqat = p.find(wxT('='));
|
||||
p[eqat] = 0;
|
||||
opt_set(p.c_str(), p.c_str() + eqat + 1);
|
||||
}
|
||||
pending_optset.clear();
|
||||
|
||||
// load vba-over.ini
|
||||
// rather than dealing with wxConfig's broken search path, just use
|
||||
// the same one that the xrc overrides use
|
||||
// this also allows us to override a group at a time, add commments, and
|
||||
// add the file from which the group came
|
||||
wxMemoryInputStream mis(builtin_over, sizeof(builtin_over));
|
||||
overrides = new wxFileConfig(mis);
|
||||
wxRegEx cmtre;
|
||||
// not the most efficient thing to do: read entire file into a string
|
||||
// just to parse the comments out
|
||||
wxString bovs((const char *)builtin_over, wxConvUTF8, sizeof(builtin_over));
|
||||
bool cont;
|
||||
wxString s;
|
||||
long grp_idx;
|
||||
for(cont = overrides->GetFirstGroup(s, grp_idx); cont;
|
||||
cont = overrides->GetNextGroup(s, grp_idx)) {
|
||||
// apparently even MacOSX sometimes uses the old \r by itself
|
||||
#define CMT_RE_START wxT("(^|[\n\r])# ?([^\n\r]*)(\r?\n|\r)\\[")
|
||||
wxString cmt(CMT_RE_START);
|
||||
cmt += s + wxT("\\]");
|
||||
if(cmtre.Compile(cmt) && cmtre.Matches(bovs))
|
||||
cmt = cmtre.GetMatch(bovs, 2);
|
||||
else
|
||||
cmt = wxEmptyString;
|
||||
overrides->Write(s + wxT("/comment"), cmt);
|
||||
}
|
||||
for(int i = config_path.size() - 1; i >= 0 ; i--) {
|
||||
wxFileName fn(config_path[i], wxT("vba-over.ini"));
|
||||
if(!fn.IsFileReadable())
|
||||
continue;
|
||||
wxStringOutputStream sos;
|
||||
wxFileInputStream fis(fn.GetFullPath());
|
||||
// not the most efficient thing to do: read entire file into a string
|
||||
// just to parse the comments out
|
||||
fis.Read(sos);
|
||||
// rather than assuming the file is seekable, use the string we just
|
||||
// read as an input stream
|
||||
wxStringInputStream sis(sos.GetString());
|
||||
wxFileConfig ov(sis);
|
||||
|
||||
for(cont = ov.GetFirstGroup(s, grp_idx); cont;
|
||||
cont = ov.GetNextGroup(s, grp_idx)) {
|
||||
overrides->DeleteGroup(s);
|
||||
overrides->SetPath(s);
|
||||
ov.SetPath(s);
|
||||
overrides->Write(wxT("path"), config_path[i]);
|
||||
// apparently even MacOSX sometimes uses \r by itself
|
||||
wxString cmt(CMT_RE_START);
|
||||
cmt += s + wxT("\\]");
|
||||
if(cmtre.Compile(cmt) && cmtre.Matches(sos.GetString()))
|
||||
cmt = cmtre.GetMatch(sos.GetString(), 2);
|
||||
else
|
||||
cmt = wxEmptyString;
|
||||
overrides->Write(wxT("comment"), cmt);
|
||||
long ent_idx;
|
||||
for(cont = ov.GetFirstEntry(s, ent_idx); cont;
|
||||
cont = ov.GetNextEntry(s, ent_idx))
|
||||
overrides->Write(s, ov.Read(s, wxEmptyString));
|
||||
ov.SetPath(wxT("/"));
|
||||
overrides->SetPath(wxT("/"));
|
||||
}
|
||||
}
|
||||
|
||||
// create the main window
|
||||
frame = wxDynamicCast(xr->LoadFrame(NULL, wxT("MainFrame")), MainFrame);
|
||||
if(!frame) {
|
||||
wxLogError(_("Could not create main window"));
|
||||
return false;
|
||||
}
|
||||
// Create() cannot be overridden easily
|
||||
if(!frame->InitMore())
|
||||
return false;
|
||||
|
||||
frame->Show(true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void wxvbamApp::OnInitCmdLine(wxCmdLineParser &cl)
|
||||
{
|
||||
wxApp::OnInitCmdLine(cl);
|
||||
|
||||
cl.SetLogo(wxT("VisualBoyAdvance-M " VERSION "\n"));
|
||||
|
||||
// 2.9 decided to change all of these from wxChar to char for some
|
||||
// reason
|
||||
#if wxCHECK_VERSION(2,9,0)
|
||||
// N_(x) returns x
|
||||
#define t(x) x
|
||||
#else
|
||||
// N_(x) returns wxT(x)
|
||||
#define t(x) wxT(x)
|
||||
#endif
|
||||
// while I would rather the long options be translated, there is merit
|
||||
// to the idea that command-line syntax should not change based on
|
||||
// locale
|
||||
static wxCmdLineEntryDesc opttab[] = {
|
||||
{ wxCMD_LINE_OPTION, NULL, t("save-xrc"),
|
||||
N_("Save built-in XRC file and exit") },
|
||||
{ wxCMD_LINE_OPTION, NULL, t("save-over"),
|
||||
N_("Save built-in vba-over.ini and exit") },
|
||||
{ wxCMD_LINE_SWITCH, NULL, t("print-cfg-path"),
|
||||
N_("Print configuration path and exit") },
|
||||
{ wxCMD_LINE_SWITCH, t("f"), t("fullscreen"),
|
||||
N_("Start in full-screen mode") },
|
||||
#if !defined(NO_LINK) && !defined(__WXMSW__)
|
||||
{ wxCMD_LINE_SWITCH, t("s"), t("delete-shared-state"),
|
||||
N_("Delete shared link state first, if it exists") },
|
||||
#endif
|
||||
// stupid wx cmd line parser doesn't support duplicate options
|
||||
// { wxCMD_LINE_OPTION, t("o"), t("option"),
|
||||
// _("Set configuration option; <opt>=<value> or help for list"),
|
||||
{ wxCMD_LINE_SWITCH, t("o"), t("list-options"),
|
||||
N_("List all settable options and exit") },
|
||||
{ wxCMD_LINE_PARAM, NULL, NULL,
|
||||
N_("ROM file"), wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL },
|
||||
{ wxCMD_LINE_PARAM, NULL, NULL,
|
||||
N_("<config>=<value>"), wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_MULTIPLE|wxCMD_LINE_PARAM_OPTIONAL },
|
||||
{ wxCMD_LINE_NONE }
|
||||
};
|
||||
// 2.9 automatically translates desc, but 2.8 doesn't
|
||||
#if !wxCHECK_VERSION(2,9,0)
|
||||
for(int i = 0; opttab[i].kind != wxCMD_LINE_NONE; i++)
|
||||
opttab[i].description = wxGetTranslation(opttab[i].description);
|
||||
#endif
|
||||
|
||||
// note that "SetDesc" actually Adds rather than replaces
|
||||
// so system standard (port-dependent) options are still included:
|
||||
// -h/--help --verbose --theme --mode
|
||||
cl.SetDesc(opttab);
|
||||
}
|
||||
|
||||
bool wxvbamApp::OnCmdLineParsed(wxCmdLineParser &cl)
|
||||
{
|
||||
if(!wxApp::OnCmdLineParsed(cl))
|
||||
return false;
|
||||
wxString s;
|
||||
if(cl.Found(_("save-xrc"), &s)) {
|
||||
// This was most likely done on a command line, so use
|
||||
// stderr instead of gui for messages
|
||||
wxLog::SetActiveTarget(new wxLogStderr);
|
||||
|
||||
wxFileSystem fs;
|
||||
wxFSFile *f = fs.OpenFile(wxT("memory:wxvbam.xrs#zip:wxvbam.xrs$wxvbam.xrc"));
|
||||
if(!f) {
|
||||
wxLogError(_("Configuration/build error: can't find built-in xrc"));
|
||||
return false;
|
||||
}
|
||||
wxFileOutputStream os(s);
|
||||
os.Write(*f->GetStream());
|
||||
delete f;
|
||||
wxString lm;
|
||||
lm.Printf(_("Wrote built-in configuration to %s.\n"
|
||||
"To override, remove all but changed root node(s). "
|
||||
"First found root node of correct name in any .xrc or "
|
||||
".xrs files in following search path overrides built-in:"),
|
||||
s.c_str());
|
||||
tack_full_path(lm);
|
||||
wxLogMessage(lm);
|
||||
return false;
|
||||
}
|
||||
if(cl.Found(_("print-cfg-path"))) {
|
||||
// This was most likely done on a command line, so use
|
||||
// stderr instead of gui for messages
|
||||
wxLog::SetActiveTarget(new wxLogStderr);
|
||||
|
||||
wxString lm(_("Configuration is read from, in order:"));
|
||||
tack_full_path(lm);
|
||||
wxLogMessage(lm);
|
||||
return false;
|
||||
}
|
||||
if(cl.Found(_("save-over"), &s)) {
|
||||
// This was most likely done on a command line, so use
|
||||
// stderr instead of gui for messages
|
||||
wxLog::SetActiveTarget(new wxLogStderr);
|
||||
|
||||
wxFileOutputStream os(s);
|
||||
os.Write(builtin_over, sizeof(builtin_over));
|
||||
wxString lm;
|
||||
lm.Printf(_("Wrote built-in override file to %s\n"
|
||||
"To override, delete all but changed section. First found section is used from search path:"), s.c_str());
|
||||
wxString oi = wxFileName::GetPathSeparator();
|
||||
oi += wxT("vba-over.ini");
|
||||
tack_full_path(lm, oi);
|
||||
lm.append(_("\n\tbuilt-in"));
|
||||
wxLogMessage(lm);
|
||||
return false;
|
||||
}
|
||||
if(cl.Found(wxT("f"))) {
|
||||
pending_fullscreen = true;
|
||||
}
|
||||
if(cl.Found(wxT("o"))) {
|
||||
wxPrintf(_("Options set from the command line are saved if any"
|
||||
" configuration changes are made in the user interface.\n\n"
|
||||
"For flag options, true and false are specified as 1 and 0, respectively.\n\n"));
|
||||
for(int i = 0; i < num_opts; i++) {
|
||||
wxPrintf(wxT("%s (%s"), opts[i].opt,
|
||||
opts[i].boolopt ? (const wxChar *)_("flag") :
|
||||
opts[i].stropt ? (const wxChar *)_("string") :
|
||||
opts[i].enumvals ? opts[i].enumvals :
|
||||
opts[i].intopt ? (const wxChar *)_("int") :
|
||||
(const wxChar *)_("string"));
|
||||
if(opts[i].enumvals) {
|
||||
const wxChar *evx = wxGetTranslation(opts[i].enumvals);
|
||||
if(wxStrcmp(evx, opts[i].enumvals))
|
||||
wxPrintf(wxT(" = %s"), evx);
|
||||
}
|
||||
wxPrintf(wxT(")\n\t%s\n\n"), opts[i].desc);
|
||||
if(opts[i].enumvals)
|
||||
opts[i].enumvals = wxGetTranslation(opts[i].enumvals);
|
||||
}
|
||||
wxPrintf(_("The commands available for the Keyboard/* option are:\n\n"));
|
||||
for(int i = 0; i < ncmds; i++)
|
||||
wxPrintf(wxT("%s (%s)\n"), cmdtab[i].cmd, cmdtab[i].name);
|
||||
return false;
|
||||
}
|
||||
#if !defined(NO_LINK) && !defined(__WXMSW__)
|
||||
if(cl.Found(wxT("s"))) {
|
||||
CleanLocalLink();
|
||||
}
|
||||
#endif
|
||||
int nparm = cl.GetParamCount();
|
||||
bool complained = false, gotfile = false;
|
||||
for(int i = 0; i < nparm; i++) {
|
||||
wxString p = cl.GetParam(i);
|
||||
size_t eqat = p.find(wxT('='));
|
||||
if(eqat != wxString::npos) {
|
||||
p[eqat] = 0;
|
||||
if(!opt_set(p.c_str(), p.c_str() + eqat + 1)) {
|
||||
p[eqat] = wxT('=');
|
||||
eqat = wxString::npos;
|
||||
} else
|
||||
p[eqat] = wxT('=');
|
||||
pending_optset.push_back(p);
|
||||
}
|
||||
if(eqat == wxString::npos) {
|
||||
if(!gotfile) {
|
||||
pending_load = p;
|
||||
gotfile = true;
|
||||
} else {
|
||||
if(!complained) {
|
||||
wxFprintf(stderr, _("Bad configuration option or multiple ROM files given:\n"));
|
||||
wxFprintf(stderr, wxT("%s\n"), pending_load.c_str());
|
||||
complained = true;
|
||||
}
|
||||
wxFprintf(stderr, wxT("%s\n"), p.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
MainFrame::MainFrame() : wxFrame(), did_link_init(false), focused(false),
|
||||
paused(false), menus_opened(0), dialog_opened(0) {}
|
||||
|
||||
MainFrame::~MainFrame()
|
||||
{
|
||||
if(did_link_init)
|
||||
CloseLink();
|
||||
}
|
||||
|
||||
BEGIN_EVENT_TABLE(MainFrame, wxFrame)
|
||||
#include "cmd-evtable.h"
|
||||
EVT_CONTEXT_MENU(MainFrame::OnMenu)
|
||||
// this is the main window focus? Or EVT_SET_FOCUS/EVT_KILL_FOCUS?
|
||||
EVT_ACTIVATE(MainFrame::OnActivate)
|
||||
// requires DragAcceptFiles(true); even then may not do anything
|
||||
EVT_DROP_FILES(MainFrame::OnDropFile)
|
||||
// pause game if menu pops up
|
||||
EVT_MENU_OPEN(MainFrame::MenuPopped)
|
||||
EVT_MENU_CLOSE(MainFrame::MenuPopped)
|
||||
END_EVENT_TABLE()
|
||||
|
||||
void MainFrame::OnActivate(wxActivateEvent &event)
|
||||
{
|
||||
focused = event.GetActive();
|
||||
if(panel && focused)
|
||||
panel->SetFocus();
|
||||
}
|
||||
|
||||
void MainFrame::OnDropFile(wxDropFilesEvent &event)
|
||||
{
|
||||
int n = event.GetNumberOfFiles();
|
||||
wxString *f = event.GetFiles();
|
||||
// ignore all but last
|
||||
wxGetApp().pending_load = f[event.GetNumberOfFiles() - 1];
|
||||
}
|
||||
|
||||
void MainFrame::OnMenu(wxContextMenuEvent &event)
|
||||
{
|
||||
if(IsFullScreen() && ctx_menu) {
|
||||
wxPoint p(event.GetPosition());
|
||||
#if 0 // wx actually recommends ignoring the position
|
||||
if(p != wxDefaultPosition)
|
||||
p = ScreenToClient(p);
|
||||
#endif
|
||||
PopupMenu(ctx_menu, p);
|
||||
}
|
||||
}
|
||||
|
||||
void MainFrame::SetJoystick()
|
||||
{
|
||||
bool anyjoy = false;
|
||||
joy.Remove();
|
||||
if(!emulating)
|
||||
return;
|
||||
for(int i = 0; i < 4; i++)
|
||||
for(int j = 0; j < NUM_KEYS; j++) {
|
||||
wxJoyKeyBinding_v b = gopts.joykey_bindings[i][j];
|
||||
for(int k = 0; k < b.size(); k++) {
|
||||
int jn = b[k].joy;
|
||||
if(jn) {
|
||||
if(!anyjoy) {
|
||||
anyjoy = true;
|
||||
joy.Attach(panel);
|
||||
}
|
||||
joy.Add(jn - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MainFrame::enable_menus()
|
||||
{
|
||||
for(int i = 0; i < ncmds; i++)
|
||||
if(cmdtab[i].mask_flags && cmdtab[i].mi)
|
||||
cmdtab[i].mi->Enable((cmdtab[i].mask_flags & cmd_enable) != 0);
|
||||
if(cmd_enable & CMDEN_SAVST)
|
||||
for(int i = 0; i < 10; i++)
|
||||
if(loadst_mi[i])
|
||||
loadst_mi[i]->Enable(state_ts[i].IsValid());
|
||||
}
|
||||
|
||||
void MainFrame::SetRecentAccels()
|
||||
{
|
||||
for(int i = 0; i <= 10; i++) {
|
||||
wxMenuItem *mi = recent->FindItem(i + wxID_FILE1);
|
||||
if(!mi)
|
||||
break;
|
||||
// if command is 0, there is no accel
|
||||
DoSetAccel(mi, recent_accel[i].GetCommand() ? &recent_accel[i] : NULL);
|
||||
}
|
||||
}
|
||||
|
||||
void MainFrame::update_state_ts(bool force)
|
||||
{
|
||||
bool any_states = false;
|
||||
for(int i = 0; i < 10; i++) {
|
||||
if(force)
|
||||
state_ts[i] = wxInvalidDateTime;
|
||||
if(panel->game_type() != IMAGE_UNKNOWN) {
|
||||
wxString fn;
|
||||
fn.Printf(SAVESLOT_FMT, panel->game_name().c_str(), i + 1);
|
||||
wxFileName fp(panel->state_dir(), fn);
|
||||
wxDateTime ts; // = wxInvalidDateTime
|
||||
if(fp.IsFileReadable()) {
|
||||
ts = fp.GetModificationTime();
|
||||
any_states = true;
|
||||
}
|
||||
// if(ts != state_ts[i] || (force && !ts.IsValid())) {
|
||||
// to prevent assertions (stupid wx):
|
||||
if(ts.IsValid() != state_ts[i].IsValid() ||
|
||||
(ts.IsValid() && ts != state_ts[i]) ||
|
||||
(force && !ts.IsValid())) {
|
||||
// wx has no easy way of doing the -- bit independent
|
||||
// of locale
|
||||
// so use a real date and substitute all digits
|
||||
wxDateTime fts = ts.IsValid() ? ts : wxDateTime::Now();
|
||||
wxString df = fts.Format(wxT("0&0 %x %X"));
|
||||
if(!ts.IsValid())
|
||||
for(int j = 0; j < df.size(); j++)
|
||||
if(wxIsdigit(df[j]))
|
||||
df[j] = wxT('-');
|
||||
df[0] = i == 9 ? wxT('1') : wxT(' ');
|
||||
df[2] = wxT('0') + (i + 1) % 10;
|
||||
if(loadst_mi[i]) {
|
||||
wxString lab = loadst_mi[i]->GetItemLabel();
|
||||
size_t tab = lab.find(wxT('\t')), dflen = df.size();
|
||||
if(tab != wxString::npos)
|
||||
df.append(lab.substr(tab));
|
||||
loadst_mi[i]->SetItemLabel(df);
|
||||
loadst_mi[i]->Enable(ts.IsValid());
|
||||
if(tab != wxString::npos)
|
||||
df.resize(dflen);
|
||||
}
|
||||
if(savest_mi[i]) {
|
||||
wxString lab = savest_mi[i]->GetItemLabel();
|
||||
size_t tab = lab.find(wxT('\t'));
|
||||
if(tab != wxString::npos)
|
||||
df.append(lab.substr(tab));
|
||||
savest_mi[i]->SetItemLabel(df);
|
||||
}
|
||||
}
|
||||
state_ts[i] = ts;
|
||||
}
|
||||
}
|
||||
int cmd_flg = any_states ? CMDEN_SAVST : 0;
|
||||
if((cmd_enable & CMDEN_SAVST) != cmd_flg) {
|
||||
cmd_enable = (cmd_enable & ~CMDEN_SAVST) | cmd_flg;
|
||||
enable_menus();
|
||||
}
|
||||
}
|
||||
|
||||
int MainFrame::oldest_state_slot()
|
||||
{
|
||||
wxDateTime ot;
|
||||
int os = -1;
|
||||
for(int i = 0; i < 10; i++) {
|
||||
if(!state_ts[i].IsValid())
|
||||
return i + 1;
|
||||
if(os < 0 || state_ts[i] < ot) {
|
||||
os = i;
|
||||
ot = state_ts[i];
|
||||
}
|
||||
}
|
||||
return os + 1;
|
||||
}
|
||||
|
||||
int MainFrame::newest_state_slot()
|
||||
{
|
||||
wxDateTime nt;
|
||||
int ns = -1;
|
||||
for(int i = 0; i < 10; i++) {
|
||||
if(!state_ts[i].IsValid())
|
||||
continue;
|
||||
if(ns < 0 || state_ts[i] > nt) {
|
||||
ns = i;
|
||||
nt = state_ts[i];
|
||||
}
|
||||
}
|
||||
return ns + 1;
|
||||
}
|
||||
|
||||
// disable emulator loop while menus are popped up
|
||||
// not sure how to match up w/ down other than counting...
|
||||
// only msw is guaranteed to only give one up & one down event for entire
|
||||
// menu browsing
|
||||
// if there is ever a mismatch, the game will freeze and there is no way
|
||||
// to detect if it's doing that
|
||||
|
||||
// FIXME: this does not work.
|
||||
// Not all open events are followed by close events.
|
||||
// Removing the nesting counter may help, but on wxGTK I still get lockups.
|
||||
void MainFrame::MenuPopped(wxMenuEvent &evt)
|
||||
{
|
||||
bool popped = evt.GetEventType() == wxEVT_MENU_OPEN;
|
||||
|
||||
#if 0
|
||||
if(popped)
|
||||
++menus_opened;
|
||||
else
|
||||
--menus_opened;
|
||||
if(menus_opened < 0) // how could this ever be???
|
||||
menus_opened = 0;
|
||||
#else
|
||||
if(popped)
|
||||
menus_opened = 1;
|
||||
else
|
||||
menus_opened = 0;
|
||||
#endif
|
||||
// workaround for lack of wxGTK mouse motion events: unblank
|
||||
// pointer when menu popped up
|
||||
// of course this does not help in finding the menus in the first place
|
||||
// the user is better off clicking in the window or entering/
|
||||
// exiting the window (which does generate a mouse event)
|
||||
// it will auto-hide again once game resumes
|
||||
if(popped)
|
||||
panel->ShowPointer();
|
||||
if(menus_opened)
|
||||
panel->Pause();
|
||||
else if(!IsPaused())
|
||||
panel->Resume();
|
||||
}
|
||||
|
||||
// ShowModal that also disables emulator loop
|
||||
// uses dialog_opened as a nesting counter
|
||||
int MainFrame::ShowModal(wxDialog *dlg)
|
||||
{
|
||||
StartModal();
|
||||
int ret = dlg->ShowModal();
|
||||
StopModal();
|
||||
return ret;
|
||||
}
|
||||
|
||||
void MainFrame::StartModal()
|
||||
{
|
||||
// workaround for lack of wxGTK mouse motion events: unblank
|
||||
// pointer when dialog popped up
|
||||
// it will auto-hide again once game resumes
|
||||
panel->ShowPointer();
|
||||
panel->Pause();
|
||||
++dialog_opened;
|
||||
}
|
||||
|
||||
void MainFrame::StopModal()
|
||||
{
|
||||
if(!dialog_opened) // technically an error in the caller
|
||||
return;
|
||||
--dialog_opened;
|
||||
if(!IsPaused())
|
||||
panel->Resume();
|
||||
}
|
||||
|
||||
// global event filter
|
||||
// apparently required for win32; just setting accel table still misses
|
||||
// a few keys (e.g. only ctrl-x works for exit, but not esc & ctrl-q;
|
||||
// ctrl-w does not work for close). It's possible another entity is
|
||||
// grabbing those keys, but I can't track it down.
|
||||
// FIXME: move this to MainFrame
|
||||
int wxvbamApp::FilterEvent(wxEvent &event)
|
||||
{
|
||||
if(frame && frame->IsPaused(true))
|
||||
return -1;
|
||||
if(event.GetEventType() == wxEVT_KEY_DOWN) {
|
||||
wxKeyEvent &ke = (wxKeyEvent &)event;
|
||||
|
||||
for(int i = 0; i < accels.size(); i++) {
|
||||
if(accels[i].GetKeyCode() == ke.GetKeyCode() &&
|
||||
accels[i].GetFlags() == ke.GetModifiers()) {
|
||||
wxCommandEvent ev(wxEVT_COMMAND_MENU_SELECTED, accels[i].GetCommand());
|
||||
ev.SetEventObject(this);
|
||||
frame->GetEventHandler()->ProcessEvent(ev);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
[Desktop Entry]
|
||||
Version=1.0
|
||||
Encoding=UTF-8
|
||||
Type=Application
|
||||
Name=VBA-M
|
||||
GenericName=GameBoy Advance Emulator
|
||||
Comment=Nindendo GameBoy Advance Emulator
|
||||
Exec=wxvbam
|
||||
Icon=vbam
|
||||
Categories=Application;Game;Emulator
|
|
@ -0,0 +1,566 @@
|
|||
#ifndef WX_WXVBAM_H
|
||||
#define WX_WXVBAM_H
|
||||
|
||||
#include "wxhead.h"
|
||||
#include "wx/sdljoy.h"
|
||||
#include "wx/joyedit.h"
|
||||
#include "wx/keyedit.h"
|
||||
#include "wx/wxmisc.h"
|
||||
#ifndef NO_FFMPEG
|
||||
#include "../common/ffmpeg.h"
|
||||
#endif
|
||||
#include <list>
|
||||
|
||||
/* yeah, they aren't needed globally, but I'm too lazy to limit where needed */
|
||||
#include "../System.h"
|
||||
#include "../gba/Globals.h"
|
||||
#include "../gb/gbGlobals.h"
|
||||
#include "../gb/gbSound.h"
|
||||
#include "../gb/gb.h"
|
||||
#include "../gba/Sound.h"
|
||||
#include "../Util.h"
|
||||
#include "../gba/GBALink.h"
|
||||
#include "../gb/gbCheats.h"
|
||||
#include "../gba/Cheats.h"
|
||||
|
||||
class MainFrame;
|
||||
|
||||
class wxvbamApp : public wxApp
|
||||
{
|
||||
public:
|
||||
wxvbamApp() : wxApp(), pending_fullscreen(false) {}
|
||||
virtual bool OnInit();
|
||||
virtual void OnInitCmdLine(wxCmdLineParser &);
|
||||
virtual bool OnCmdLineParsed(wxCmdLineParser &);
|
||||
// name of a file to load at earliest opportunity
|
||||
wxString pending_load;
|
||||
// list of options to set after config file loaded
|
||||
wxArrayString pending_optset;
|
||||
// set fullscreen mode after init
|
||||
bool pending_fullscreen;
|
||||
#if __WXMAC__
|
||||
// I suppose making this work will require tweaking the bundle
|
||||
void MacOpenFile(const wxString&f) { pending_load = f; };
|
||||
#endif
|
||||
// without this, global accels don't always work
|
||||
int FilterEvent(wxEvent &event);
|
||||
wxAcceleratorEntry_v accels;
|
||||
|
||||
// the main configuration
|
||||
wxConfig *cfg;
|
||||
// vba-over.ini
|
||||
wxFileConfig *overrides;
|
||||
MainFrame *frame;
|
||||
// use this to get ms since program lauch
|
||||
wxStopWatch timer;
|
||||
// append log messages to this for the log viewer
|
||||
wxString log;
|
||||
// there's no way to retrieve "current" locale, so this is public
|
||||
wxLocale locale;
|
||||
private:
|
||||
wxPathList config_path;
|
||||
};
|
||||
|
||||
DECLARE_APP(wxvbamApp);
|
||||
|
||||
// menu event handlers are declared with these macros in mainwind.cpp
|
||||
// and mainwind-handlers.h, mainwind-cmd.cpp, and mainwind-evt.cpp are
|
||||
// auto-generated from them
|
||||
#define EVT_HANDLER(n, x) \
|
||||
void MainFrame::On##n(wxCommandEvent& WXUNUSED(event))
|
||||
// some commands are masked under some conditions
|
||||
#define EVT_HANDLER_MASK(n, x, m) \
|
||||
void MainFrame::On##n(wxCommandEvent& WXUNUSED(event)) \
|
||||
{ \
|
||||
if(!(cmd_enable & (m))) \
|
||||
return; \
|
||||
Do##n(); \
|
||||
} \
|
||||
void MainFrame::Do##n() \
|
||||
|
||||
// here are those conditions
|
||||
enum {
|
||||
CMDEN_GB = (1<<0), // GB ROM loaded
|
||||
CMDEN_GBA = (1<<1), // GBA ROM loaded
|
||||
// the rest imply the above, unless:
|
||||
// _ANY -> does not imply either
|
||||
// _GBA -> only implies GBA
|
||||
CMDEN_REWIND = (1<<2), // rewind states available
|
||||
CMDEN_SREC = (1<<3), // sound recording in progress
|
||||
CMDEN_NSREC = (1<<4), // no sound recording
|
||||
CMDEN_VREC = (1<<5), // video recording
|
||||
CMDEN_NVREC = (1<<6), // no video recording
|
||||
CMDEN_GREC = (1<<7), // game recording
|
||||
CMDEN_NGREC = (1<<8), // no game recording
|
||||
CMDEN_GPLAY = (1<<9), // game playback
|
||||
CMDEN_NGPLAY = (1<<10), // no game playback
|
||||
CMDEN_SAVST = (1<<11), // any save states
|
||||
CMDEN_GDB = (1<<12), // gdb connected
|
||||
CMDEN_NGDB_GBA = (1<<13), // gdb not connected
|
||||
CMDEN_NGDB_ANY = (1<<14), // gdb not connected
|
||||
CMDEN_NREC_ANY = (1<<15), // not a/v recording
|
||||
CMDEN_LINK_ANY = (1<<16), // link enabled
|
||||
|
||||
CMDEN_NEVER = (1<<31) // never (for NOOP)
|
||||
};
|
||||
#define ONLOAD_CMDEN (CMDEN_NSREC|CMDEN_NVREC|CMDEN_NGREC|CMDEN_NGPLAY)
|
||||
#define UNLOAD_CMDEN_KEEP (CMDEN_NGDB_ANY|CMDEN_NREC_ANY|CMDEN_LINK_ANY)
|
||||
|
||||
struct checkable_mi_t {
|
||||
int cmd;
|
||||
wxMenuItem *mi;
|
||||
bool *boolopt;
|
||||
int *intopt, mask, val;
|
||||
};
|
||||
|
||||
// wxArray is only for small types
|
||||
typedef std::vector<checkable_mi_t> checkable_mi_array_t;
|
||||
|
||||
typedef std::list<wxDialog *> dialog_list_t;
|
||||
|
||||
class GameArea;
|
||||
|
||||
class LogDialog;
|
||||
|
||||
// true if pause should happen at next frame
|
||||
extern bool pause_next;
|
||||
|
||||
class MainFrame : public wxFrame
|
||||
{
|
||||
public:
|
||||
MainFrame();
|
||||
~MainFrame();
|
||||
|
||||
bool InitMore(void);
|
||||
|
||||
void SetJoystick(void);
|
||||
|
||||
GameArea *GetPanel() { return panel; }
|
||||
|
||||
// wxMSW pauses the game for menu popups and modal dialogs, but wxGTK
|
||||
// does not. It's probably desirable to pause the game. To do this for
|
||||
// dialogs, use this function instead of dlg->ShowModal()
|
||||
int ShowModal(wxDialog *dlg);
|
||||
// and here are the wrapper functions for use when ShowModal() isn't
|
||||
// possible
|
||||
void StartModal();
|
||||
void StopModal();
|
||||
// however, adding a handler for open/close menu to do the same is unsafe.
|
||||
// there is no guarantee every show will be matched by a hide.
|
||||
void MenuPopped(wxMenuEvent &evt);
|
||||
|
||||
// flags for enabling commands
|
||||
int cmd_enable;
|
||||
|
||||
// adjust menus based on current cmd_enable
|
||||
void enable_menus();
|
||||
|
||||
// adjust menus based on available save game states
|
||||
void update_state_ts(bool force = false);
|
||||
|
||||
// retrieve oldest/newest slot
|
||||
// returns lowest-numbered slot on ties
|
||||
int oldest_state_slot(); // or empty slot if available
|
||||
int newest_state_slot(); // or 0 if none
|
||||
|
||||
// system-defined accelerators
|
||||
wxAcceleratorEntry_v sys_accels;
|
||||
// adjust recent menu with accelerators
|
||||
void SetRecentAccels();
|
||||
// merge sys accels with user-defined accels (user overrides sys)
|
||||
wxAcceleratorEntry_v get_accels(wxAcceleratorEntry_v user_accels);
|
||||
// update menu and global accelerator table with sys+user accel defs
|
||||
// probably not the quickest way to add/remove individual accels
|
||||
void set_global_accels();
|
||||
|
||||
// 2.8 has no HasFocus(), and FindFocus() doesn't work right
|
||||
bool HasFocus() { return focused; }
|
||||
|
||||
// The various viewer popups; these can be popped up as often as
|
||||
// desired
|
||||
void Disassemble();
|
||||
void IOViewer();
|
||||
void MapViewer();
|
||||
void MemViewer();
|
||||
void OAMViewer();
|
||||
void PaletteViewer();
|
||||
void TileViewer();
|
||||
|
||||
// since they will all have to be destroyed on game unload:
|
||||
dialog_list_t popups;
|
||||
|
||||
// this won't actually be destroyed, but it needs to be tracked so only
|
||||
// one is ever up and it needs to be pinged when new messages arrive
|
||||
LogDialog *logdlg;
|
||||
|
||||
// the cheat search dialog isn't destroyed or tracked, but it needs
|
||||
// to be cleared between games
|
||||
void ResetCheatSearch();
|
||||
|
||||
// call this to update the viewers once a frame:
|
||||
void UpdateViewers();
|
||||
|
||||
// required for building from xrc
|
||||
DECLARE_DYNAMIC_CLASS();
|
||||
// required for event handling
|
||||
DECLARE_EVENT_TABLE();
|
||||
private:
|
||||
bool did_link_init;
|
||||
GameArea *panel;
|
||||
|
||||
// the various reasons the game might be paused
|
||||
bool paused;
|
||||
int menus_opened, dialog_opened;
|
||||
public:
|
||||
bool IsPaused(bool incendental = false)
|
||||
{
|
||||
return (paused && !pause_next && !incendental) || menus_opened || dialog_opened;
|
||||
}
|
||||
private:
|
||||
|
||||
bool autoLoadMostRecent;
|
||||
// copy of top-level menu bar as a context menu
|
||||
wxMenu *ctx_menu;
|
||||
// load/save states menu items
|
||||
wxMenuItem *loadst_mi[10];
|
||||
wxMenuItem *savest_mi[10];
|
||||
wxDateTime state_ts[10];
|
||||
// checkable menu items
|
||||
checkable_mi_array_t checkable_mi;
|
||||
// recent menu item accels
|
||||
wxMenu *recent;
|
||||
wxAcceleratorEntry recent_accel[10];
|
||||
// joystick reader
|
||||
wxSDLJoy joy;
|
||||
|
||||
// helper function for adding menu to accel editor
|
||||
void add_menu_accels(wxTreeCtrl *tc, wxTreeItemId &parent, wxMenu *menu);
|
||||
|
||||
// for detecting window focus
|
||||
void OnActivate(wxActivateEvent&);
|
||||
// quicker & more accurate than FindFocus() != NULL
|
||||
bool focused;
|
||||
// may work, may not... if so, load dropped file
|
||||
void OnDropFile(wxDropFilesEvent&);
|
||||
// pop up menu in fullscreen mode
|
||||
void OnMenu(wxContextMenuEvent &);
|
||||
#include "cmdhandlers.h"
|
||||
};
|
||||
|
||||
// a helper class to avoid forgetting StopModal()
|
||||
class ModalPause
|
||||
{
|
||||
public:
|
||||
ModalPause() { wxGetApp().frame->StartModal(); }
|
||||
~ModalPause() { wxGetApp().frame->StopModal(); }
|
||||
};
|
||||
|
||||
enum showspeed {
|
||||
// this order must match order of option enum and selector widget
|
||||
SS_NONE, SS_PERCENT, SS_DETAILED
|
||||
};
|
||||
|
||||
enum filtfunc {
|
||||
// this order must match order of option enum and selector widget
|
||||
FF_NONE, FF_2XSAI, FF_SUPER2XSAI, FF_SUPEREAGLE, FF_PIXELATE,
|
||||
FF_ADVMAME, FF_BILINEAR, FF_BILINEARPLUS, FF_SCANLINES, FF_TV,
|
||||
FF_HQ2X, FF_LQ2X, FF_SIMPLE2X, FF_SIMPLE3X, FF_HQ3X, FF_SIMPLE4X,
|
||||
FF_HQ4X, FF_PLUGIN // plugin must always be last
|
||||
};
|
||||
#define builtin_ff_scale(x) \
|
||||
((x == FF_HQ4X || x == FF_SIMPLE4X) ? 4 : \
|
||||
(x == FF_HQ3X || x == FF_SIMPLE3X) ? 3 : \
|
||||
x == FF_PLUGIN ? 0 : x == FF_NONE ? 1 : 2)
|
||||
|
||||
enum ifbfunc {
|
||||
// this order must match order of option enum and selector widget
|
||||
IFB_NONE, IFB_SMART, IFB_MOTION_BLUR
|
||||
};
|
||||
|
||||
// make sure and keep this in sync with opts.cpp!
|
||||
enum renderer {
|
||||
RND_SIMPLE, RND_OPENGL, RND_CAIRO, RND_DIRECT3D
|
||||
};
|
||||
|
||||
// likewise
|
||||
enum audioapi {
|
||||
AUD_SDL, AUD_OPENAL, AUD_DIRECTSOUND, AUD_XAUDIO2
|
||||
};
|
||||
|
||||
// an unfortunate legacy default; should have a non-digit preceding %d
|
||||
// the only reason to keep it is that user can set slotdir to old dir
|
||||
// otoh, we already make some name changes (double ext strip), and
|
||||
// rename is not that hard (esp. under UNIX), so I'm going ahead with a new
|
||||
// name.
|
||||
//#define SAVESLOT_FMT wxT("%s%d.sgm")
|
||||
#define SAVESLOT_FMT wxT("%s-%02d.sgm")
|
||||
|
||||
// display time, in ms
|
||||
#define OSD_TIME 3000
|
||||
|
||||
class DrawingPanel;
|
||||
|
||||
class GameArea : public wxPanel
|
||||
{
|
||||
public:
|
||||
GameArea();
|
||||
virtual ~GameArea();
|
||||
|
||||
// set to game title + link info
|
||||
void SetFrameTitle();
|
||||
|
||||
void LoadGame(const wxString &name);
|
||||
void UnloadGame(bool destruct = false);
|
||||
|
||||
IMAGE_TYPE game_type() { return loaded; }
|
||||
u32 game_size() { return rom_size; }
|
||||
wxString game_dir() { return loaded_game.GetPath(); }
|
||||
wxString game_name() { return loaded_game.GetFullName(); }
|
||||
wxString bat_dir() { return batdir; }
|
||||
wxString state_dir() { return statedir; }
|
||||
void recompute_dirs();
|
||||
|
||||
bool LoadState();
|
||||
bool LoadState(int slot);
|
||||
bool LoadState(const wxFileName &fname);
|
||||
|
||||
bool SaveState();
|
||||
bool SaveState(int slot);
|
||||
bool SaveState(const wxFileName &fname);
|
||||
|
||||
// save to default location
|
||||
void SaveBattery(bool quiet = false);
|
||||
|
||||
// true if file at default location may not match memory
|
||||
bool cheats_dirty;
|
||||
|
||||
static const int GBWidth = 160, GBHeight = 144,
|
||||
SGBWidth = 256, SGBHeight = 224,
|
||||
GBAWidth = 240, GBAHeight = 160;
|
||||
void AddBorder();
|
||||
void DelBorder();
|
||||
// Delete() & set to NULL to force reinit
|
||||
DrawingPanel *panel;
|
||||
struct EmulatedSystem *emusys;
|
||||
|
||||
// pause game or signal a long operation, similar to pausing
|
||||
void Pause();
|
||||
void Resume();
|
||||
|
||||
// true if paused since last reset of flag
|
||||
bool was_paused;
|
||||
|
||||
// osdstat is always displayed at top-left of screen
|
||||
wxString osdstat;
|
||||
|
||||
// osdtext is displayed for 3 seconds after osdtime, and then cleared
|
||||
wxString osdtext;
|
||||
u32 osdtime;
|
||||
|
||||
// Rewind: count down to 0 and rewind
|
||||
u32 rewind_time;
|
||||
// Rewind: flag to OnIdle to do a rewind
|
||||
bool do_rewind;
|
||||
// Rewind: rewind states
|
||||
char *rewind_mem; // should be u8, really
|
||||
int num_rewind_states;
|
||||
int next_rewind_state;
|
||||
// FIXME: size this properly
|
||||
#define REWIND_SIZE 400000
|
||||
// FIXME: make this a config option
|
||||
#define NUM_REWINDS 8
|
||||
|
||||
void ShowFullScreen(bool full);
|
||||
bool IsFullScreen() { return fullscreen; }
|
||||
// set size of frame & panel to scaled screen size
|
||||
void AdjustSize(bool force);
|
||||
#ifndef NO_FFMPEG
|
||||
void StartSoundRecording(const wxString &fname);
|
||||
void StopSoundRecording();
|
||||
void StartVidRecording(const wxString &fname);
|
||||
void StopVidRecording();
|
||||
void AddFrame(const u8 *data); // video
|
||||
void AddFrame(const u16 *data, int length); // audio
|
||||
bool IsRecording() { return snd_rec.IsRecording() || vid_rec.IsRecording(); }
|
||||
#endif
|
||||
void StartGameRecording(const wxString &fname);
|
||||
void StopGameRecording();
|
||||
void StartGamePlayback(const wxString &fname);
|
||||
void StopGamePlayback();
|
||||
|
||||
protected:
|
||||
// set minsize of frame & panel to unscaled screen size
|
||||
void LowerMinSize();
|
||||
// set minsize of frame & panel to scaled screen size
|
||||
void AdjustMinSize();
|
||||
|
||||
IMAGE_TYPE loaded;
|
||||
u32 rom_size;
|
||||
wxFileName loaded_game;
|
||||
wxString batdir, statedir;
|
||||
|
||||
int basic_width, basic_height;
|
||||
bool fullscreen;
|
||||
|
||||
bool paused;
|
||||
void OnIdle(wxIdleEvent &);
|
||||
void OnKeyDown(wxKeyEvent &ev);
|
||||
void OnKeyUp(wxKeyEvent &ev);
|
||||
void OnSDLJoy(wxSDLJoyEvent &ev);
|
||||
|
||||
#ifndef NO_FFMPEG
|
||||
MediaRecorder snd_rec, vid_rec;
|
||||
#endif
|
||||
|
||||
public:
|
||||
void ShowPointer();
|
||||
void HidePointer();
|
||||
protected:
|
||||
void MouseEvent(wxMouseEvent &) { ShowPointer(); }
|
||||
bool pointer_blanked;
|
||||
u32 mouse_active_time;
|
||||
|
||||
DECLARE_DYNAMIC_CLASS()
|
||||
DECLARE_EVENT_TABLE()
|
||||
};
|
||||
|
||||
// wxString version of OSD message
|
||||
void systemScreenMessage(const wxString &msg);
|
||||
|
||||
// List of all commands with their descriptions
|
||||
// sorted by cmd field for binary searching
|
||||
// filled in by copy-events.cmake
|
||||
extern struct cmditem {
|
||||
const wxChar *cmd, *name;
|
||||
int cmd_id;
|
||||
int mask_flags; // if non-0, one of the flags must be turned on in win
|
||||
// to enable this command
|
||||
wxMenuItem *mi; // the menu item to invoke command, if present
|
||||
} cmdtab[];
|
||||
extern const int ncmds;
|
||||
// for binary search
|
||||
extern bool cmditem_lt(const struct cmditem &cmd1, const struct cmditem &cmd2);
|
||||
|
||||
#include <wx/dynlib.h>
|
||||
#include "../win32/rpi.h"
|
||||
|
||||
class FilterThread;
|
||||
|
||||
class DrawingPanel : public wxObject
|
||||
{
|
||||
public:
|
||||
DrawingPanel(int _width, int _height);
|
||||
~DrawingPanel();
|
||||
void DrawArea(u8 **pixels);
|
||||
// the following wouldn't be necessary with virtual inheritance
|
||||
virtual wxWindow *GetWindow() = 0;
|
||||
virtual void Delete() = 0;
|
||||
|
||||
protected:
|
||||
virtual void DrawArea(wxWindowDC&) = 0;
|
||||
virtual void DrawOSD(wxWindowDC&);
|
||||
int width, height, scale;
|
||||
u8 *todraw;
|
||||
u8 *pixbuf1, *pixbuf2;
|
||||
FilterThread *threads;
|
||||
int nthreads;
|
||||
wxSemaphore filt_done;
|
||||
wxDynamicLibrary filt_plugin;
|
||||
const RENDER_PLUGIN_INFO *rpi; // also flag indicating plugin loaded
|
||||
// largest buffer required is 32-bit * (max width + 1) * (max height + 2)
|
||||
u8 delta[257 * 4 * 226];
|
||||
|
||||
// following can't work in 2.8 as intended
|
||||
// inheriting from wxEvtHandler is required, but also breaks subclasses
|
||||
// due to lack of virtual inheritance (2.9 drops wxEvtHandler req)
|
||||
// so each child must have a paint event handler (not override of this,
|
||||
// but it's not virtual anyway, so that won't happen) that calls this
|
||||
void PaintEv(wxPaintEvent &ev);
|
||||
|
||||
DECLARE_ABSTRACT_CLASS()
|
||||
};
|
||||
|
||||
class LogDialog : public wxDialog
|
||||
{
|
||||
public:
|
||||
LogDialog();
|
||||
void Update();
|
||||
private:
|
||||
wxTextCtrl *log;
|
||||
void Save(wxCommandEvent &ev);
|
||||
void Clear(wxCommandEvent &ev);
|
||||
|
||||
DECLARE_EVENT_TABLE()
|
||||
};
|
||||
|
||||
#include "opts.h"
|
||||
|
||||
class SoundDriver;
|
||||
extern SoundDriver *newOpenAL();
|
||||
// I should add this to SoundDriver, but wxArrayString is wx-specific
|
||||
// I suppose I could make subclass wxSoundDriver. maybe later.
|
||||
extern bool GetOALDevices(wxArrayString &names, wxArrayString &ids);
|
||||
#ifdef __WXMSW__
|
||||
extern SoundDriver *newDirectSound();
|
||||
extern bool GetDSDevices(wxArrayString &names, wxArrayString &ids);
|
||||
extern SoundDriver *newXAudio2_Output();
|
||||
extern bool GetXA2Devices(wxArrayString &names, wxArrayString &ids);
|
||||
#endif
|
||||
|
||||
extern bool debugger;
|
||||
extern void (*dbgMain)();
|
||||
extern void (*dbgSignal)(int, int);
|
||||
extern void (*dbgOutput)(const char *, u32);
|
||||
extern void remoteStubMain();
|
||||
extern void remoteCleanUp();
|
||||
extern void remoteStubSignal(int, int);
|
||||
extern void remoteOutput(const char *, u32);
|
||||
|
||||
extern bool debugOpenPty();
|
||||
extern const wxString &debugGetSlavePty();
|
||||
extern bool debugWaitPty();
|
||||
extern bool debugStartListen(int port);
|
||||
extern bool debugWaitSocket();
|
||||
|
||||
// perhaps these functions should not be called systemXXX
|
||||
// perhaps they should move to panel.cpp/GameArea
|
||||
// but they must integrate with systemReadJoypad
|
||||
void systemStartGameRecording(const wxString &fname);
|
||||
void systemStopGameRecording();
|
||||
void systemStartGamePlayback(const wxString &fname);
|
||||
void systemStopGamePlayback();
|
||||
|
||||
// true if turbo mode (like pressing turbo button constantly)
|
||||
extern bool turbo;
|
||||
|
||||
// mask of key press flags; see below
|
||||
extern int joypress[4], autofire;
|
||||
|
||||
// FIXME: these defines should be global to project and used instead of raw numbers
|
||||
#define KEYM_A (1<<0)
|
||||
#define KEYM_B (1<<1)
|
||||
#define KEYM_SELECT (1<<2)
|
||||
#define KEYM_START (1<<3)
|
||||
#define KEYM_RIGHT (1<<4)
|
||||
#define KEYM_LEFT (1<<5)
|
||||
#define KEYM_UP (1<<6)
|
||||
#define KEYM_DOWN (1<<7)
|
||||
#define KEYM_R (1<<8)
|
||||
#define KEYM_L (1<<9)
|
||||
#define KEYM_SPEED (1<<10)
|
||||
#define KEYM_CAPTURE (1<<11)
|
||||
#define KEYM_GS (1<<12)
|
||||
|
||||
// actually, the wx port adds the following, which could be local:
|
||||
#define REALKEY_MASK ((1<<13)-1)
|
||||
|
||||
#define KEYM_AUTO_A (1<<13)
|
||||
#define KEYM_AUTO_B (1<<14)
|
||||
#define KEYM_MOTION_UP (1<<15)
|
||||
#define KEYM_MOTION_DOWN (1<<16)
|
||||
#define KEYM_MOTION_LEFT (1<<17)
|
||||
#define KEYM_MOTION_RIGHT (1<<18)
|
||||
|
||||
#include "filters.h"
|
||||
|
||||
#endif /* WX_WXVBAM_H */
|
|
@ -0,0 +1,42 @@
|
|||
IDI_MAINICON ICON "../win32/res/VBA.ico"
|
||||
|
||||
#include "wx/msw/wx.rc"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Version
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 1,8,0,0
|
||||
PRODUCTVERSION 1,8,0,0
|
||||
FILEFLAGSMASK 0x17L
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
#else
|
||||
FILEFLAGS 0x0L
|
||||
#endif
|
||||
FILEOS 0x4L
|
||||
FILETYPE 0x1L
|
||||
FILESUBTYPE 0x0L
|
||||
BEGIN
|
||||
BLOCK "StringFileInfo"
|
||||
BEGIN
|
||||
BLOCK "040904b0"
|
||||
BEGIN
|
||||
VALUE "Comments", "VBA-M comes with NO WARRANTY. Use it at your own risk."
|
||||
VALUE "CompanyName", "http://vba-m.com/"
|
||||
VALUE "FileDescription", "VisualBoyAdvance-M"
|
||||
VALUE "FileVersion", "1, 8, 0, 0"
|
||||
VALUE "InternalName", "VBA-M"
|
||||
VALUE "LegalCopyright", "Copyright © 2008-2009 VBA-M development team"
|
||||
VALUE "OriginalFilename", "VisualBoyAdvance-M.exe"
|
||||
VALUE "ProductName", "GB/C/A emulator for Windows"
|
||||
VALUE "ProductVersion", "1, 8, 0, 0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
BEGIN
|
||||
VALUE "Translation", 0x409, 1200
|
||||
END
|
||||
END
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,439 @@
|
|||
#ifndef NO_XAUDIO2
|
||||
|
||||
// Application
|
||||
#include "wxvbam.h"
|
||||
#include <stdio.h>
|
||||
|
||||
// Interface
|
||||
#include "../common/SoundDriver.h"
|
||||
|
||||
// XAudio2
|
||||
#include <XAudio2.h>
|
||||
|
||||
// Internals
|
||||
#include "../System.h" // for systemMessage()
|
||||
#include "../gba/Globals.h"
|
||||
|
||||
|
||||
// Synchronization Event
|
||||
class XAudio2_BufferNotify : public IXAudio2VoiceCallback
|
||||
{
|
||||
public:
|
||||
HANDLE hBufferEndEvent;
|
||||
|
||||
XAudio2_BufferNotify() {
|
||||
hBufferEndEvent = NULL;
|
||||
hBufferEndEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
|
||||
assert( hBufferEndEvent != NULL );
|
||||
}
|
||||
|
||||
~XAudio2_BufferNotify() {
|
||||
CloseHandle( hBufferEndEvent );
|
||||
hBufferEndEvent = NULL;
|
||||
}
|
||||
|
||||
STDMETHOD_( void, OnBufferEnd ) ( void *pBufferContext ) {
|
||||
assert( hBufferEndEvent != NULL );
|
||||
SetEvent( hBufferEndEvent );
|
||||
}
|
||||
|
||||
|
||||
// dummies:
|
||||
STDMETHOD_( void, OnVoiceProcessingPassStart ) ( UINT32 BytesRequired ) {}
|
||||
STDMETHOD_( void, OnVoiceProcessingPassEnd ) () {}
|
||||
STDMETHOD_( void, OnStreamEnd ) () {}
|
||||
STDMETHOD_( void, OnBufferStart ) ( void *pBufferContext ) {}
|
||||
STDMETHOD_( void, OnLoopEnd ) ( void *pBufferContext ) {}
|
||||
STDMETHOD_( void, OnVoiceError ) ( void *pBufferContext, HRESULT Error ) {};
|
||||
};
|
||||
|
||||
|
||||
// Class Declaration
|
||||
class XAudio2_Output
|
||||
: public SoundDriver
|
||||
{
|
||||
public:
|
||||
XAudio2_Output();
|
||||
~XAudio2_Output();
|
||||
|
||||
// Initialization
|
||||
bool init(long sampleRate);
|
||||
|
||||
// Sound Data Feed
|
||||
void write(u16 * finalWave, int length);
|
||||
|
||||
// Play Control
|
||||
void pause();
|
||||
void resume();
|
||||
void reset();
|
||||
|
||||
// Configuration Changes
|
||||
void setThrottle( unsigned short throttle );
|
||||
|
||||
private:
|
||||
bool failed;
|
||||
bool initialized;
|
||||
bool playing;
|
||||
UINT32 freq;
|
||||
UINT32 bufferCount;
|
||||
BYTE *buffers;
|
||||
int currentBuffer;
|
||||
int soundBufferLen;
|
||||
|
||||
IXAudio2 *xaud;
|
||||
IXAudio2MasteringVoice *mVoice; // listener
|
||||
IXAudio2SourceVoice *sVoice; // sound source
|
||||
XAUDIO2_BUFFER buf;
|
||||
XAUDIO2_VOICE_STATE vState;
|
||||
XAudio2_BufferNotify notify; // buffer end notification
|
||||
};
|
||||
|
||||
|
||||
// Class Implementation
|
||||
XAudio2_Output::XAudio2_Output()
|
||||
{
|
||||
failed = false;
|
||||
initialized = false;
|
||||
playing = false;
|
||||
freq = 0;
|
||||
bufferCount = gopts.audio_buffers;
|
||||
buffers = NULL;
|
||||
currentBuffer = 0;
|
||||
|
||||
xaud = NULL;
|
||||
mVoice = NULL;
|
||||
sVoice = NULL;
|
||||
ZeroMemory( &buf, sizeof( buf ) );
|
||||
ZeroMemory( &vState, sizeof( vState ) );
|
||||
}
|
||||
|
||||
|
||||
XAudio2_Output::~XAudio2_Output()
|
||||
{
|
||||
initialized = false;
|
||||
|
||||
if( sVoice ) {
|
||||
if( playing ) {
|
||||
HRESULT hr = sVoice->Stop( 0 );
|
||||
assert( hr == S_OK );
|
||||
}
|
||||
sVoice->DestroyVoice();
|
||||
}
|
||||
|
||||
if( buffers ) {
|
||||
free( buffers );
|
||||
buffers = NULL;
|
||||
}
|
||||
|
||||
if( mVoice ) {
|
||||
mVoice->DestroyVoice();
|
||||
}
|
||||
|
||||
if( xaud ) {
|
||||
xaud->Release();
|
||||
xaud = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int XA2GetDev(IXAudio2 *);
|
||||
|
||||
|
||||
bool XAudio2_Output::init(long sampleRate)
|
||||
{
|
||||
if( failed || initialized ) return false;
|
||||
|
||||
HRESULT hr;
|
||||
|
||||
// Initialize XAudio2
|
||||
UINT32 flags = 0;
|
||||
#ifdef _DEBUG
|
||||
flags = XAUDIO2_DEBUG_ENGINE;
|
||||
#endif
|
||||
|
||||
hr = XAudio2Create( &xaud, flags );
|
||||
if( hr != S_OK ) {
|
||||
wxLogError( _("The XAudio2 interface failed to initialize!") );
|
||||
failed = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
freq = sampleRate;
|
||||
|
||||
// calculate the number of samples per frame first
|
||||
// then multiply it with the size of a sample frame (16 bit * stereo)
|
||||
soundBufferLen = ( freq / 60 ) * 4;
|
||||
|
||||
// create own buffers to store sound data because it must not be
|
||||
// manipulated while the voice plays from it
|
||||
buffers = (BYTE *)malloc( ( bufferCount + 1 ) * soundBufferLen );
|
||||
// + 1 because we need one temporary buffer when all others are in use
|
||||
|
||||
WAVEFORMATEX wfx;
|
||||
ZeroMemory( &wfx, sizeof( wfx ) );
|
||||
wfx.wFormatTag = WAVE_FORMAT_PCM;
|
||||
wfx.nChannels = 2;
|
||||
wfx.nSamplesPerSec = freq;
|
||||
wfx.wBitsPerSample = 16;
|
||||
wfx.nBlockAlign = wfx.nChannels * ( wfx.wBitsPerSample / 8 );
|
||||
wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign;
|
||||
|
||||
|
||||
// create sound receiver
|
||||
hr = xaud->CreateMasteringVoice(
|
||||
&mVoice,
|
||||
XAUDIO2_DEFAULT_CHANNELS,
|
||||
XAUDIO2_DEFAULT_SAMPLERATE,
|
||||
0,
|
||||
XA2GetDev(xaud),
|
||||
NULL );
|
||||
if( hr != S_OK ) {
|
||||
wxLogError( _("XAudio2: Creating mastering voice failed!") );
|
||||
failed = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// create sound emitter
|
||||
hr = xaud->CreateSourceVoice( &sVoice, &wfx, 0, 4.0f, ¬ify );
|
||||
if( hr != S_OK ) {
|
||||
wxLogError( _("XAudio2: Creating source voice failed!") );
|
||||
failed = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
if( gopts.upmix ) {
|
||||
// set up stereo upmixing
|
||||
XAUDIO2_DEVICE_DETAILS dd;
|
||||
ZeroMemory( &dd, sizeof( dd ) );
|
||||
hr = xaud->GetDeviceDetails( 0, &dd );
|
||||
assert( hr == S_OK );
|
||||
float *matrix = NULL;
|
||||
matrix = (float*)malloc( sizeof( float ) * 2 * dd.OutputFormat.Format.nChannels );
|
||||
if( matrix == NULL ) return false;
|
||||
bool matrixAvailable = true;
|
||||
switch( dd.OutputFormat.Format.nChannels ) {
|
||||
case 4: // 4.0
|
||||
//Speaker \ Left Source Right Source
|
||||
/*Front L*/ matrix[0] = 1.0000f; matrix[1] = 0.0000f;
|
||||
/*Front R*/ matrix[2] = 0.0000f; matrix[3] = 1.0000f;
|
||||
/*Back L*/ matrix[4] = 1.0000f; matrix[5] = 0.0000f;
|
||||
/*Back R*/ matrix[6] = 0.0000f; matrix[7] = 1.0000f;
|
||||
break;
|
||||
case 5: // 5.0
|
||||
//Speaker \ Left Source Right Source
|
||||
/*Front L*/ matrix[0] = 1.0000f; matrix[1] = 0.0000f;
|
||||
/*Front R*/ matrix[2] = 0.0000f; matrix[3] = 1.0000f;
|
||||
/*Front C*/ matrix[4] = 0.7071f; matrix[5] = 0.7071f;
|
||||
/*Side L*/ matrix[6] = 1.0000f; matrix[7] = 0.0000f;
|
||||
/*Side R*/ matrix[8] = 0.0000f; matrix[9] = 1.0000f;
|
||||
break;
|
||||
case 6: // 5.1
|
||||
//Speaker \ Left Source Right Source
|
||||
/*Front L*/ matrix[0] = 1.0000f; matrix[1] = 0.0000f;
|
||||
/*Front R*/ matrix[2] = 0.0000f; matrix[3] = 1.0000f;
|
||||
/*Front C*/ matrix[4] = 0.7071f; matrix[5] = 0.7071f;
|
||||
/*LFE */ matrix[6] = 0.0000f; matrix[7] = 0.0000f;
|
||||
/*Side L*/ matrix[8] = 1.0000f; matrix[9] = 0.0000f;
|
||||
/*Side R*/ matrix[10] = 0.0000f; matrix[11] = 1.0000f;
|
||||
break;
|
||||
case 7: // 6.1
|
||||
//Speaker \ Left Source Right Source
|
||||
/*Front L*/ matrix[0] = 1.0000f; matrix[1] = 0.0000f;
|
||||
/*Front R*/ matrix[2] = 0.0000f; matrix[3] = 1.0000f;
|
||||
/*Front C*/ matrix[4] = 0.7071f; matrix[5] = 0.7071f;
|
||||
/*LFE */ matrix[6] = 0.0000f; matrix[7] = 0.0000f;
|
||||
/*Side L*/ matrix[8] = 1.0000f; matrix[9] = 0.0000f;
|
||||
/*Side R*/ matrix[10] = 0.0000f; matrix[11] = 1.0000f;
|
||||
/*Back C*/ matrix[12] = 0.7071f; matrix[13] = 0.7071f;
|
||||
break;
|
||||
case 8: // 7.1
|
||||
//Speaker \ Left Source Right Source
|
||||
/*Front L*/ matrix[0] = 1.0000f; matrix[1] = 0.0000f;
|
||||
/*Front R*/ matrix[2] = 0.0000f; matrix[3] = 1.0000f;
|
||||
/*Front C*/ matrix[4] = 0.7071f; matrix[5] = 0.7071f;
|
||||
/*LFE */ matrix[6] = 0.0000f; matrix[7] = 0.0000f;
|
||||
/*Back L*/ matrix[8] = 1.0000f; matrix[9] = 0.0000f;
|
||||
/*Back R*/ matrix[10] = 0.0000f; matrix[11] = 1.0000f;
|
||||
/*Side L*/ matrix[12] = 1.0000f; matrix[13] = 0.0000f;
|
||||
/*Side R*/ matrix[14] = 0.0000f; matrix[15] = 1.0000f;
|
||||
break;
|
||||
default:
|
||||
matrixAvailable = false;
|
||||
break;
|
||||
}
|
||||
if( matrixAvailable ) {
|
||||
hr = sVoice->SetOutputMatrix( NULL, 2, dd.OutputFormat.Format.nChannels, matrix );
|
||||
assert( hr == S_OK );
|
||||
}
|
||||
free( matrix );
|
||||
matrix = NULL;
|
||||
}
|
||||
|
||||
|
||||
hr = sVoice->Start( 0 );
|
||||
assert( hr == S_OK );
|
||||
playing = true;
|
||||
|
||||
|
||||
initialized = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void XAudio2_Output::write(u16 * finalWave, int length)
|
||||
{
|
||||
if( !initialized || failed ) return;
|
||||
|
||||
while( true ) {
|
||||
sVoice->GetState( &vState );
|
||||
|
||||
assert( vState.BuffersQueued <= bufferCount );
|
||||
|
||||
if( vState.BuffersQueued < bufferCount ) {
|
||||
if( vState.BuffersQueued == 0 ) {
|
||||
// buffers ran dry
|
||||
if( systemVerbose & VERBOSE_SOUNDOUTPUT ) {
|
||||
static unsigned int i = 0;
|
||||
log( "XAudio2: Buffers were not refilled fast enough (i=%i)\n", i++ );
|
||||
}
|
||||
}
|
||||
// there is at least one free buffer
|
||||
break;
|
||||
} else {
|
||||
// the maximum number of buffers is currently queued
|
||||
if( synchronize && !speedup && !gopts.throttle ) {
|
||||
// wait for one buffer to finish playing
|
||||
WaitForSingleObject( notify.hBufferEndEvent, INFINITE );
|
||||
} else {
|
||||
// drop current audio frame
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// copy & protect the audio data in own memory area while playing it
|
||||
CopyMemory( &buffers[ currentBuffer * soundBufferLen ], finalWave, soundBufferLen );
|
||||
|
||||
buf.AudioBytes = soundBufferLen;
|
||||
buf.pAudioData = &buffers[ currentBuffer * soundBufferLen ];
|
||||
|
||||
currentBuffer++;
|
||||
currentBuffer %= ( bufferCount + 1 ); // + 1 because we need one temporary buffer
|
||||
|
||||
HRESULT hr = sVoice->SubmitSourceBuffer( &buf ); // send buffer to queue
|
||||
assert( hr == S_OK );
|
||||
}
|
||||
|
||||
|
||||
void XAudio2_Output::pause()
|
||||
{
|
||||
if( !initialized || failed ) return;
|
||||
|
||||
if( playing ) {
|
||||
HRESULT hr = sVoice->Stop( 0 );
|
||||
assert( hr == S_OK );
|
||||
playing = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void XAudio2_Output::resume()
|
||||
{
|
||||
if( !initialized || failed ) return;
|
||||
|
||||
if( !playing ) {
|
||||
HRESULT hr = sVoice->Start( 0 );
|
||||
assert( hr == S_OK );
|
||||
playing = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void XAudio2_Output::reset()
|
||||
{
|
||||
if( !initialized || failed ) return;
|
||||
|
||||
if( playing ) {
|
||||
HRESULT hr = sVoice->Stop( 0 );
|
||||
assert( hr == S_OK );
|
||||
}
|
||||
|
||||
sVoice->FlushSourceBuffers();
|
||||
sVoice->Start( 0 );
|
||||
playing = true;
|
||||
}
|
||||
|
||||
|
||||
void XAudio2_Output::setThrottle( unsigned short throttle )
|
||||
{
|
||||
if( !initialized || failed ) return;
|
||||
|
||||
if( throttle == 0 ) throttle = 100;
|
||||
HRESULT hr = sVoice->SetFrequencyRatio( (float)throttle / 100.0f );
|
||||
assert( hr == S_OK );
|
||||
}
|
||||
|
||||
SoundDriver *newXAudio2_Output()
|
||||
{
|
||||
return new XAudio2_Output();
|
||||
}
|
||||
|
||||
int GetXA2Devices(IXAudio2 *xa, wxArrayString *names, wxArrayString *ids,
|
||||
const wxString *match)
|
||||
{
|
||||
HRESULT hr;
|
||||
UINT32 dev_count = 0;
|
||||
hr = xa->GetDeviceCount( &dev_count );
|
||||
if( hr != S_OK ) {
|
||||
wxLogError(_("XAudio2: Enumerating devices failed!"));
|
||||
return true;
|
||||
} else {
|
||||
XAUDIO2_DEVICE_DETAILS dd;
|
||||
for( UINT32 i = 0; i < dev_count; i++ ) {
|
||||
hr = xa->GetDeviceDetails( i, &dd );
|
||||
if( hr != S_OK ) {
|
||||
continue;
|
||||
} else {
|
||||
if(ids) {
|
||||
ids->push_back(dd.DeviceID);
|
||||
names->push_back( dd.DisplayName );
|
||||
} else if(*match == dd.DeviceID)
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool GetXA2Devices(wxArrayString &names, wxArrayString &ids)
|
||||
{
|
||||
HRESULT hr;
|
||||
IXAudio2 *xa = NULL;
|
||||
UINT32 flags = 0;
|
||||
#ifdef _DEBUG
|
||||
flags = XAUDIO2_DEBUG_ENGINE;
|
||||
#endif
|
||||
|
||||
hr = XAudio2Create( &xa, flags );
|
||||
if( hr != S_OK ) {
|
||||
wxLogError( _("The XAudio2 interface failed to initialize!") );
|
||||
return false;
|
||||
}
|
||||
GetXA2Devices(xa, &names, &ids, NULL);
|
||||
xa->Release();
|
||||
return true;
|
||||
}
|
||||
|
||||
static int XA2GetDev(IXAudio2 *xa)
|
||||
{
|
||||
if(gopts.audio_dev.empty())
|
||||
return 0;
|
||||
else {
|
||||
int ret = GetXA2Devices(xa, NULL, NULL, &gopts.audio_dev);
|
||||
return ret < 0 ? 0 : ret;
|
||||
}
|
||||
}
|
||||
#endif // #ifndef NO_XAUDIO2
|
Loading…
Reference in New Issue